API standards
This page outlines the API standards that must be enforced in Autologyx API.
Data types
The data in the API is categorized by type.
| Type | Notes |
|---|---|
| int | Integer |
| uuid | UUID |
| string | |
| url | |
| date | |
| datetime | |
| enum | Consists of predefined values |
| set | |
| bool | |
| json | JSON |
| user | Complex object of system user attributes |
| user_group | Complex object describing user_group |
| owners | Complex object describing owners |
| permissions | Complex object describing permissions |
Data structures
If the response schema is complex, the following data structures can be used for categorization. The contents should later be described in a separate table.
In case of less complex nested structures, standard nested.property syntax can be used instead.
| Type | Notes |
|---|---|
| Array | A JSON array |
| Object | A JSON object |
User type
Example:
{
"id": 23,
"first_name": "John",
"last_name": "Smith",
"username": "j.smith@autologyx.com",
"company_name": "Autologyx",
"is_deleted": false,
"account_type": "super_admin"
}Owners type
Type used for resources managed by owners. Value is complex object contains:
- total number of owners
- first assigned owner, by added at date and time (user type)
Sample:
{
"total_number": 1,
"first": {
"id": 13,
"first_name": "John",
"last_name": "Smith",
"username": "j.smith@autologyx.com",
"company_name": "Autologyx",
"is_deleted": false,
"account_type": "full"
}
}Insufficient permissions
| Error | Response code | Message |
|---|---|---|
| User is authenticated but has not enough permissions to perform method on an endpoint | 403 Forbidden | "detail": "You do not have permission to perform this action." |
| User has not permission to given resource's instance PK used as value for an | 400 Bad Request | "{attribute}": [ "You do not have permission to use this {resource name}." ] |
POST standards
Standard response content
201 Created status code is returned on success.
Error handling
Invalid payload properties
- Common
| Error | Response code | Message |
|---|---|---|
| {attribute} is missing | 400 Bad Request | "{attribute}": [ "This field is required." ] |
| {attribute} is empty string | 400 Bad Request | "{attribute}": [ "This field may not be blank." ] |
| {attribute} is null | 400 Bad Request | "{attribute}": [ "This field may not be null." ] |
| {attribute} has > {number} chars | 400 Bad Request | "{attribute}": [ "Ensure this field has no more than {number} characters." ] |
| {attribute} is not unique | 400 Bad Request | "{attribute}": [ "This field must be unique." ] |
| {attribute} is null | 400 Bad Request | "{attribute}": [ "This field may not be null." ] |
| Value for integer field is different type than int | 400 Bad Request | "{attribute}": [ "A valid integer is required."] |
| Value of different type than list given for list | 400 Bad Request | "{attribute}": [ "Expected a list of items but got type "{type}"." ] |
| Primary key value for enum {attribute} is different {type} than int | 400 Bad Request | "{attribute}": [ "Incorrect type. Expected pk value, received {type}." ] |
| {resource} instance for given primary key {value} of enum {attribute} does not exist | 400 Bad Request | "{attribute}": [ "Invalid pk \"{value}\" - {resource} does not exist." ] |
| User has not perm perm to given resource's instance | 400 Bad Request | "{attribute}": [ "You do not have permission to use this {resource name}." ] |
| {value} for {attribute} is out of choices | 400 Bad Request | "{attribute}": [ "\"{value}\" is not a valid choice." ] |
| Max {number} of {resource} exceeded | 400 Bad Request | "detail": "Limit of {number} {resource} has been exceeded.", "error_code": "ERR_LIMIT_EXCEEDED" |
| Max {number} {sub-resource} per {resource} exceeded | 400 Bad Request | "detail": [ "Limit of {number} {sub-resource} for the {resource} has been exceeded." ] |
| Insufficient permissions | 403 Forbidden | "detail": "You do not have permission to perform this action." |
| Resource's instance with "id" does not exist | 404 Not Found | "detail": "Not found." |
- Permission fields
| Error | Response code | Message |
|---|---|---|
| Invalid {resource} key for permissions | 400 Bad Request | "{attribute}": [ "Invalid resource "{resource}"." ] |
| Invalid {action} given for resource | 400 Bad Request | "{attribute}": [ ""{action}" is not a valid choice." ] |
| Value of different type than list given for resource | 400 Bad Request | "{attribute}": [ "Expected a list of items but got type "{type}"." ] |
- File fields
| Error | Response code | Message |
|---|---|---|
| Invalid file's extension | 400 Bad Request | "detail": [ "File extension “{file_extension}” is not allowed. Allowed extensions are: {allowed_extensions}." ] |
| Missing header with filename | 400 Bad Request | "detail": [ "Missing filename. Request should include a Content-Disposition header with a filename parameter." ] |
| File size is over {max} size | 400 Bad Request | "detail": [ "Max file size is {max} MB." ] |
Non-modifiable properties
If value for non-modifiable property is given in a request's body, then it is silently ignored.
URL standards
- URL should contain the dash symbol
-instead of underscore_f.e./api/endpoint/action-name/
PATCH standards
Standard response content
200 OK status code is returned on success.
Error handling
Invalid IDs in the url
404 Not Found status code is returned on not matching id/uuid.
Invalid payload properties
- Common
| Error | Response code | Message |
|---|---|---|
| {attribute} is empty string | 400 Bad Request | "{attribute}": [ "This field may not be blank." ] |
| {attribute} is null | 400 Bad Request | "{attribute}": [ "This field may not be null." ] |
| {attribute} has > {number} chars | 400 Bad Request | "{attribute}": [ "Ensure this field has no more than {number} characters." ] |
| {attribute} is not unique | 400 Bad Request | "{attribute}": [ "This field must be unique." ] |
| {attribute} is null | 400 Bad Request | "{attribute}": [ "This field may not be null." ] |
| Value for integer field is different type than int | 400 Bad Request | "{attribute}": [ "A valid integer is required."] |
| Value of different type than list given for list | 400 Bad Request | "{attribute}": [ "Expected a list of items but got type "{type}"." ] |
| Primary key value for enum {attribute} is different {type} than int | 400 Bad Request | "{attribute}": [ "Incorrect type. Expected pk value, received {type}." ] |
| {resource} instance for given primary key {value} of enum {attribute} does not exist | 400 Bad Request | "{attribute}": [ "Invalid pk \"{value}\" - {resource} does not exist." ] |
| User has not perm perm to given resource's instance | 400 Bad Request | "{attribute}": [ "You do not have permission to use this {resource name}." ] |
| {value} for {attribute} is out of choices | 400 Bad Request | "{attribute}": [ "\"{value}\" is not a valid choice." ] |
| Max {number} of {resource} exceeded | 400 Bad Request | "detail": "Limit of {number} {resource} has been exceeded.", "error_code": "ERR_LIMIT_EXCEEDED" |
| Max {number} {sub-resource} per {resource} exceeded | 400 Bad Request | "detail": [ "Limit of {number} {sub-resource} for the {resource} has been exceeded." ] |
| Insufficient permissions | 403 Forbidden | "detail": "You do not have permission to perform this action." |
| Resource's instance with "id" does not exist | 404 Not Found | "detail": "Not found." |
- Permission fields
| Error | Response code | Message |
|---|---|---|
| Invalid {resource} key for permissions | 400 Bad Request | "{attribute}": [ "Invalid resource "{resource}"." ] |
| Invalid {action} given for resource | 400 Bad Request | "{attribute}": [ ""{action}" is not a valid choice." ] |
| Value of different type than list given for resource | 400 Bad Request | "{attribute}": [ "Expected a list of items but got type "{type}"." ] |
- File fields
| Error | Response code | Message |
|---|---|---|
| Invalid file type | 400 Bad Request | "{attribute}": [ "Invalid file type." ] |
Non-modifiable properties
If value for non-modifiable property is given in a request's body, then it is silently ignored.
Preventing some fields to be updated
Some fields can be set during creation, but they cannot be changed via PATCH.
Instead of handling errors on fields used with Create request we should use apps.api.mixins.CreateOnlyFieldsMixin.
By adding it as a mixing to a serializer, we force silently ignore this field in the update request.
class ObjectClassFormDetailSerializer(CreateOnlyFieldsMixin, ...):
...
class Meta:
...
create_only_fields = (
'object_class',
'type',
)GET standards
In most cases, the GET method can return:
- list of items
- single item (detail, with extended set of attributes)
Common (list and detail)
Serializer's text fields with empty values
Text fields, like description should allow empty string (""). NULL values are not accepted, even if model accepts them. Existing NULL values should be serialized as empty strings.
description = serializers.CharField(..., default="")Labels
If list request (like /api/task-templates/) data for given property of type set or enum is dispalyed in response json as a list of integers (idents of related objects):
"task_templates": [1, 2],then details request (like /api/task-templates/{id}/) data for given property of type set or enum is displayed in the same format as a list of integers(idents of related objects):
"task_templates": [1, 2],API will deliver details in additional helper parameter _meta
"_meta": {
"labels": {
"task_templates": [
"task template name 1",
"task template name 2",
]
}
}List standards
Filtering predicates
Legend:
+- operator available for specific data typec.ins.- case insensitive
| Predicate | Operator | int | uuid | string | url | date | datetime | enum | set | bool | phone | user | owners | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Equal | = | + | + | + | + | + | + | + | + | + | + | + | + | + |
| Equal (c.ins.) | iexact= | + | + | + | + | |||||||||
| Is Empty | isempty= | + | + | + | + | |||||||||
| Contains | contains= | + | + | + | + | |||||||||
| Contains (c.ins.) | icontains= | + | + | + | + | |||||||||
| Starts with | startswith= | + | + | + | + | |||||||||
| Starts with (c.ins.) | istartswith= | + | + | + | + | |||||||||
| Ends with | endswith= | + | + | + | + | |||||||||
| Ends with (c.ins.) | iendswith= | + | + | + | + | |||||||||
| Is NULL | isnull= | + | + | + | + | + | + | + | ||||||
| Lower than | lt= | + | + | + | ||||||||||
| Lower/equal than | lte= | + | + | + | ||||||||||
| Greater than | gt= | + | + | + | ||||||||||
| Greater/equal than | gte= | + | + | + | ||||||||||
| Range | range= | + | + | + | ||||||||||
| Is in | in= | + | + | + | ||||||||||
| Contains all | containsall= | + | ||||||||||||
| Contains some | containssome= | + |
Filtering by primary key of integer type does not support isnull operator due to primary key cannot be NULL.
Relational complex fields like created_by, modified_by will not be sortable because we expect performance decrease. Fields are annotated to response queryset with complex database operations.
Operator should be used with attribute's name:
name=name__startswith=created_at__gte=
To negate an operator, please use
!
name!=name__endswith!=count__lt!=
Sample: /api/object-classes/?id__gte=2005
Error handling
| Error | Response code | Message |
|---|---|---|
| Value for filtering by enum {parameter} is out of choices | 400 Bad Request | "{parameter}": [ "Select a valid choice. That choice is not one of the available choices." ] |
| {value} for filtering by set {parameter} is out of choices | 400 Bad Request | "{parameter}": [ "“"{value}"” is not a valid value." ] |
| Value for filtering by date/time {parameter} is invalid | 400 Bad Request | "{parameter}": [ "Enter a valid date/time." ] |
| {value} for ordering is out of available parameters | 400 Bad Request | "ordering": [ "Select a valid choice. {value} is not one of the available choices." ] |
Detail standards
Invalid IDs in the url
404 Not Found status code is returned on not matching id/uuid.
DELETE standards
Standard response content
204 No Content response status code.
Response body is empty.
Error handling
Invalid IDs in the url
404 Not Found status code is returned on not matching id/uuid.
OPTIONS standards
Status code
200 OK status code is returned on success.
Response structure
Response is a JSON object with following attributes:
- list
- details
- restrictions
{
"list": {
"columns": [...]
},
"details": {
"schema": [...]
},
"restrictions": {
"limit_items": 10000
}
}Each of the above attributes is optional, it's presence depends on API endpoint being implemented (list or details) and having any limitations (restrictions).
list attribute
Contains information related to API list view e.g. /api/task-templates/
list.columns attribute
It’s a list of fields available for the listing view with information if sorting or filtering is possible for given field or not.
"columns": [
{
"alias": <string>,
"type": <type>,
"predicates": [<string>, ...],
"sort_ok": <bool>
},
...
]Each field (irrespectively of type) is represented by JSON object with following attributes:
alias– field alias used to build filtering and sorting queriestype– field typepredicates– list with available predicates for filtering, if empty then filtering is not allowedsort_ok– boolean flag determining if sorting is allowed
Additionally, fields of enum and set types may have additional keys:
values– list of JSON objects with predefined values for the field:
"values": [
{
"value": <value>,
"text": <label>
},
...
]autocomplete– relative URL to API endpoint where available values can be obtained
Sample:
"columns": [
{
"alias": "id",
"type": "int",
"predicates": [
"exact",
"gt",
"gte",
"lt",
"lte",
"range"
],
"sort_ok": true
},
{
"alias": "task_group_templates",
"type": "enum",
"predicates": [
"isnull",
"containsall",
"containssome"
],
"sort_ok": false,
"autocomplete": "/api/task-group-templates/autocomplete/?text__icontains="
},
{
"alias": "status",
"type": "enum",
"predicates": [
"exact"
],
"sort_ok": false,
"values": [
{
"value": 1,
"text": "Active"
},
{
"value": 2,
"text": "Archived"
}
]
},
]details attribute
Contains information related to API detail view e.g. /api/task-templates/{id}/.
details.schema attribute
It’s a list of JSON objects that can be of two types:
- Field – represents single field
- Nested schema – represents multiple fields as nested group (more can be found in Task Templates - JSON schema for OPTIONS request )
Fields
Field object contain requirements around the field for the create and detail views.
"schema": [
{
"alias": <string>,
"type": <type>,
"required": <bool>
},
...
]Each field (irrespectively of type) is represented by JSON object with following attributes:
alias– field aliastype– field typerequired- boolean flag determining if field is required
Optionally following attributes may be used:
validators– list of validators that checks if value is correct:
"validators": [<json>, <json>, ...]Where each validator has at least one attribute type and depending on it’s type additional attributes:
{
"type": <string>,
...
}Supported validators:
| Type | Options | Description |
|---|---|---|
| min_length | minimal number of characters | Validates min length of string |
| max_length | maximum number of characters | Validates max length of string |
| min_value | minimal allowed value | Validates min value of number |
| max_value | maximum allowed value | Validates max value of number |
| date_in_future | Validates if date is in the future |
Additionally, fields of type enum and set may have:
values– list of JSON objects with predefined values for the field:
"values": [
{
"value": <value>,
"text": <string>
},
...
]autocomplete- relative URL to API endpoint where available values can be obtained.
Sample:
"schema": [
{
"alias": "name",
"type": "string",
"required": true,
"validators": [
{
"type": "min_length",
"length": 1
},
{
"type": "max_length",
"length": 255
}
]
},
{
"alias": "time_unit",
"type": "enum",
"required": true,
"values": [
{
"value": "days",
"text": "days"
},
{
"value": "weeks",
"text": "weeks"
},
{
"value": "months",
"text": "months"
}
]
},
...
]Nested schema
"schema": [
{
"alias": <string>,
"schema": [<json>, ...]
},
...
]Aggregates multiple fields as nested JSON object. Always contains at least two attributes:
alias– alias of a groupschema– list of fields or another nested schema
Optionally following attributes may be used:
schema_by_<alias>– similar to schema attribute but used when schema depends on value of another field.validators– list of validators that checks if entire group of fields is correct.
Schema by value from different field
schema_by_<alias> - alias is a name of a field on which value a schema depends.
Define the schema of a field when schema depends on another field's value.
At least two obligatory attributes are present:
alias- field name which value affects the schema ("task_type" in the below example)schema– list of fields in the schema
Sample:
{
"alias": "plugin_config",
"schema": [
{
"alias": "task_type",
"type": "string",
"required": true,
"values": [
{
"value": "tt_form",
"text": "Form"
}
]
}
],
"schema_by_type": [
{
"task_type": "tt_form",
"schema": [
{
"alias": "task_type",
"schema": [
{
"alias": "data_type",
"type": "string",
"required": true,
"values": [
{
"value": "t_form",
"text": "Form"
}
]
}
]
}
]
}
]
}Related validators
If a validator apply to only some Fields in the group an apply_to attribute is used to point which Fields are affected.
Supported validators:
| Type | Options | Description |
|---|---|---|
| max_length | maximal number of items in the group | Validates maximal number of items in the group |
Example:
{
"alias": "reminders",
"schema": [
{
"alias": "notice_type",
"type": "enum",
"required": true,
"values": [
{
"value": "task_complete_by",
"text": "Task to Complete by"
},
{
"value": "task_overdue",
"text": "Task Overdue"
},
...
]
}
],
"validators": [
{
"type": "max_length",
"length": 1,
"apply_to": {
"alias": "notice_type",
"value": "task_complete_by"
}
},
{
"type": "max_length",
"length": 10,
"apply_to": {
"alias": "notice_type",
"value": "task_overdue"
}
},
...
]
}For above example:
- Only 1 reminder of notice_type=”task_complete_by” is allowed
- Maximum 10 reminders of notice_type=”task_overdue” are allowed
restrictions attribute
Contains all kinds of limitations that are enforced for particular endpoint.
Autocomplete
Some related api endpoint uses data stored as an enum or set type. OPTIONS request will contain an autocomplete relative url like: /api/users/autocomplete/. Only GET request to this endpoint is avaliable
Restrictions based on limits
When some endpoint is limited by number of objects to create, the response should be:
HTTP 403 Forbiddenwith response body:
{
"detail": "Limit of {limit} {object_name} has been exceeded.",
"error_code": "ERR_LIMIT_EXCEEDED"
}