Self-documenting JSON REST API

This API implementation offers all the required functionality to support specified features and data access capabilities.

Basic Terminology

API Endpoint Types

Identifying endpoints are used when actions are targeting specified entity and generi endpoints when the identity is either not relevant (search) or not available (create new entity, for example).

Most CRUD (Create, Read, Update, Delete) actions deal with identified entities (/api/entity/<int:id>), except POST ("create") action. Only the service can give the new entity instance a valid and non-conflicting ID.

A GET Request ("read") action can access either identified entity (making it "fetch" type), or to entity generic (making it a "search" request).

Why are Primary Keys not considered Entity Attributes?

Idiotic teachings in almost all universities, in their database courses, tell us to use "some immutable attribute of the entity as a primary key". This is horseshit! If you are so utterly clueless and stupid to actually do that, I would have fired you from my company, on the spot, and been miffed about hiring you in the first place. You will NEVER use actual entity data in schema because there is no such thing as "immutable attributes" in real-life. Not even back account numbers or social security number. There is no such thing at all!

Thus it is paramount to separate data from schema and give each entity an autogenerated primary key that simply cannot face real-world pressure to change its value. Thus, primary keys created by this principle, ARE NOT ATTRIBUTES OF AN ENTITY!

REST API Principles

This implementation follows number of principles.

HTTP Methods

Method Action Idempontence
GET search/fetch Idempotent
POST create Non-idempotent
PUT update Idempotent
PATCH (special) Non-Idempotent
DELETE delete Idempotent

HTTP Responses

This implementation strives to follow these principles when ever possible. Most important of the response code usages involved signaling if the requested resource is available at all. For resource (endpoint) + method combinations and their logical meaning, following table should be used:

    +---------------+---------+--------+---------+--------+--------+
    | Resource      | POST    | GET    | PUT     | PATCH  | DELETE |
    +---------------+---------+--------+---------+--------+--------+
    | /entity       | Create  | Search | 405     | 405    | 405    |
    | /entity/<:id> | 405     | Fetch  | Replace | Update | Delete |
    +---------------+---------+--------+---------+--------+--------+

Reply 405 Method Not Allowed should be the only 4xx response used to indicate unavailable method + endpoint combinations.

HTTP Response Codes Acknowledged by this API

HTTP Transactions (Request - Response)

GET

Idempotent
Verb: Fetch / Search (SELECT)
Query Parameters: FETCH: maybe, SEARCH: always
Accesses: FETCH: Entity Identifying Endpoint (/api/entity/<int:id>), SEARCH: Generic Endpoint (/api/entity)
Send Payload: None
Get Payload: FETCH: entity object, SEARCH: list of entity objects (0 ... N)

First, the more obvious, is be called the "fetch" type. These GET requests access Entity Identifying Endpoints and expect to receive one entity object in JSON format.

Second type is the "search" type, which accesses Generic Entity Endpoint with any number of search/filtering criteria in Query Parameters. These requests expect to receive a list JSON, containing zero to N entity objects.

Fetch type GET queries usually do not support Query parameters, but if deemed necessary, an implementation may choose to define them.

When "fetch" yields no results, response "404 Not Found" is returned. When "search" type request yields no results, an empty list is returned with code "200 OK".

Search GET : Possible Replies

    Code                    Payload                 Description
    200 OK                  'data': [{},...]        Success. List of entities returned.
    400 Bad Request         None                    Data/structure error.
    401 Unauthorized        None                    Access/authorization error.
    405 Method not allowed  None                    Endpoint + method combo not supported.
    406 Not Acceptable      None                    Argument error.

Fetch GET : Possible Replies

    Code                    Payload                 Description
    200 OK                  'data': {}              Success. Entity is returned.
    400 Bad Request         None                    Data/structure error.
    401 Unauthorized        None                    Access/authorization error.
    404 Not Found           None                    Entity by ID not found.
    405 Method not allowed  None                    Endpoint + method combo not supported.
    406 Not Acceptable      None                    Argument error.

POST

Non-idempotent
Verb: Create (INSERT)
Query Parameters: None
Access: Generic Endpoint (/api/entity)
Send Payload: Complete entity object JSON
Get Payload: Entity ID: { 'id': <int:id>}

POST Request accesses Generic Endpoint (/api/entity) with entity object JSON and the server creates a new database record (row). Response will always contain the ID of the created entity . Client may use or discard this information at its discretion.

Possible Replies

    Code                    Payload                 Description
    200 OK                  'data': {'id': <int>}   Success. New entity created.
    202 Accepted            'data': {'id': <int>}   Asyncronous action queued.
    400 Bad Request         None                    Data/structure error.
    401 Unauthorized        None                    Access/authorization error.
    405 Method not allowed  None                    Endpoint + method combo not supported.
    406 Not Acceptable      None                    Argument error.

Response code 202 usage should be carefully considered. If one API endpoint accessed with one method, does not return both asyncronous and immediate response "values", code 200 should be used instead.

PUT

Idempotent
Verb: Update (UPDATE)
Query Parameters: None
Access: Entity Identifying Endpoint (/api/entity/<int:id>)
Send Payload: Complete or partial entity object JSON
Get Payload: None

PUT Request accesses Entity Identifying Endpoint (api/entity/<int:id>) with entity object JSON payload, containing those attributes that need to be updated and their new values. Successul response will have no payload.

Possible Replies

    Code                    Payload                 Description
    200 OK                  'data': {'id': <int>}   Success. Entity updated.
    400 Bad Request         None                    Data/structure error.
    401 Unauthorized        None                    Access/authorization error.
    404 Not Found           None                    Entity by ID not found.
    405 Method not allowed  None                    Endpoint + method combo not supported.
    406 Not Acceptable      None                    Argument error.

PATCH

Non-idemptent
Verb: N/A
Query Parameters: None
Access: Either Entity Identifying Endpoint (api/entity/<int:id>) OR Generic Endpoint (/api/entity)
Send Payload: Implementation specific
Get Payload: Implementation specific

PATCH Request accesses Entity Identifying Endpoint (api/entity/<int:id>) OR Generic Endpoint (/api/entity) (depending which one makes logical sense) with special JSON payload (specifications up to the implementation). The special payload contains instructions or commands (as allowed by the implementation) that cannot be expressed simply as an entity attribute update.

An example might be an archive command to api/entity with payload { 'archive': {'olderthan': '2019-01-01'} }, which would cause the server to archive entities older than the specified date (possibly moving to a compressed .tar archive, maybe).

Alternatively, PATCH Request may limit its effects to the entity, but requests an action that is not idempotent, such as "increment usage count" (something which you never want to implement as fetch-update action, to avoid race conditions).

Need for response payload and its content are also entirely implementation specific.

Possible Replies

    Code                    Payload                 Description
    200 OK                  'data': {'id': <int>}   Success. Entity updated.
    400 Bad Request         None                    Data/structure error.
    401 Unauthorized        None                    Access/authorization error.
    405 Method not allowed  None                    Endpoint + method combo not supported.
    406 Not Acceptable      None                    Argument error.

DELETE

Idempotent
Verb: Delete (DELETE)
Query Parameters: None Access: Entity Identifying Endpoint (/api/entity/<int:id>)
Send Payload: None
Get Payload: None

DELETE Request accesses Entity Identifying Endpoint (api/entity/<int:id>) and carries no payload. Response will neither carry payload.

At first, DELETE might not seem like idempotent, but the fact that you can delete a specific entity only once and thenafter only receive 404 Not Found replies, effectively means that no matter how many times the same DELETE is repeated, the database state has the sone end result.

Possible Replies

    Code                    Payload                 Description
    200 OK                  None                    Success.
    401 Unauthorized        None                    Access/authorization error.
    404 Not Found           None                    Entity by ID not found.
    405 Method not allowed  None                    Endpoint + method combo not supported.
    406 Not Acceptable      None                    Foreign key violation, or other reason.

List of Flask routes and Endpoints

ServiceMethodsEndpointDocumentation
send_uiGET/Send static content (HTML/CSS/JS/images/...).
send_uiGET/<path:path>Send static content (HTML/CSS/JS/images/...).
api_not_implementedPOST,PUT,GET,DELETE,PATCH/apiCatch-all route for '/api*' access attempts that do not match any defined routes.
"405 Method Not Allowed" JSON reply is returned.
api_docGET/api.htmlJSON API Documentation.
Generates API document from the available endpoints. This functionality
relies on PEP 257 (https://www.python.org/dev/peps/pep-0257/) convention
for docstrings and Flask micro framework route ('rule') mapping to
generate basic information listing on all the available REST API functions.
This call takes no arguments.

GET /sys/api

List of API endpoints is returned in JSON.

GET /api.html

The README.md from /api is prefixed to HTML content. List of API endpoints
is included as a table.
api_not_implementedPOST,PUT,GET,DELETE,PATCH/api/Catch-all route for '/api*' access attempts that do not match any defined routes.
"405 Method Not Allowed" JSON reply is returned.
api_not_implementedPOST,PUT,GET,DELETE,PATCH/api/<path:path>Catch-all route for '/api*' access attempts that do not match any defined routes.
"405 Method Not Allowed" JSON reply is returned.
api_fileGET/api/fileList of downloadable file, with optional type filtering. Allowed types are "vm" and "usb".

GET /api/file
GET /api/file/usb
GET /api/file/vm
Query parameters:
(none implemented)
API returns 200 OK and:
{
...,
"data" : [
{
TBA
},
...
],
...
}

api_fileGET/api/file/<any("vm","usb"):ftype>List of downloadable file, with optional type filtering. Allowed types are "vm" and "usb".

GET /api/file
GET /api/file/usb
GET /api/file/vm
Query parameters:
(none implemented)
API returns 200 OK and:
{
...,
"data" : [
{
TBA
},
...
],
...
}

api_file_idPUT,GET/api/file/<int:id>Database table row endpoint. Retrieve (GET) or update (PUT) record.
flow_chunk_uploadPOST,GET/api/file/flowReturn 200 if given chunk already exists, return 204 if not.
api_file_ownedGET/api/file/ownedReturn JSON listing of files owned by currently authenticated person. Slightly 'special' endpoint that accepts only GET method and no parameters of any kind. Data is returned based on the SSO session role. Specially created for Upload and Manage UI, to list user's files.
api_file_schemaGET/api/file/schemaCreate data schema JSON for client FORM creation.
sso_stateGET/api/ssoReturns a sigle item JSON: { "role": "[anonymous|student|teacher]" }. This also implicitly indicates the authentication state (anonymous = not authenticated).
sso_loginGET/api/sso/loginThis is the landing URI from SSO login page. SSO REST API is re-queried and session is updated accordingly. Finally, 'destination' URL parameter is used to redirect the broser to the final location - persumably the page from where the "login" link/button was pressed.
sso_logoutGET/api/sso/logoutThis endpoint sets UID to None and ROLE to 'anonymous' in the session, thus effectively logging the user out.
downloadGET/download/<path:path>None
flow_process_statusGET/sse/flow-upload-statusSSE endpoint to supply data to event listener. Required URL parameters: 'filename' and 'flowid'.
RETURN CODES
200 OK no name conflict (GET) / successful assembly (POST)
400 BadRequest Malformed requests (no 'filename' and/or 'flowid').
401 Unauthorized Not an active teacher

api_docGET/sys/apiJSON API Documentation.
Generates API document from the available endpoints. This functionality
relies on PEP 257 (https://www.python.org/dev/peps/pep-0257/) convention
for docstrings and Flask micro framework route ('rule') mapping to
generate basic information listing on all the available REST API functions.
This call takes no arguments.

GET /sys/api

List of API endpoints is returned in JSON.

GET /api.html

The README.md from /api is prefixed to HTML content. List of API endpoints
is included as a table.
show_flask_configGET/sys/cfgMiddleware (Flask application) configuration. Sensitive entries are
censored.