Schema

An endpoint that automatically creates a swagger doc for the application

  1. Overview
  2. url
  3. schema_format
  4. request_methods
  5. response_headers
  6. security_headers
  7. schema_configuration
  8. authentication

Overview

The schema endpoint must always be attached to an endpoint group. It will document all endpoints attached to its parent endpoint group.

Keep in mind that the routing in the endpoint group is greedy and goes from top-down. As a result, since the schema endpoint (typically) has a specific URL, it’s usually best for it to be at the top of your endpoint list. The following example builds an application with two endpoint groups, each of which has a schema endpoint:

import clearskies
from clearskies.validators import Required, Unique
from clearskies import columns


class User(clearskies.Model):
    id_column_name = "id"
    backend = clearskies.backends.MemoryBackend()

    id = columns.Uuid()
    name = columns.String(validators=[Required()])
    username = columns.String(
        validators=[
            Required(),
            Unique(),
        ]
    )
    age = columns.Integer(validators=[Required()])
    company_name = columns.String()
    created_at = columns.Created()
    updated_at = columns.Updated()


readable_column_names = [
    "id",
    "name",
    "username",
    "age",
    "company_name",
    "created_at",
    "updated_at",
]
writeable_user_column_names = ["name", "username", "age", "company_name"]
users_api = clearskies.EndpointGroup(
    [
        clearskies.endpoints.Schema(url="schema"),
        clearskies.endpoints.RestfulApi(
            url="users",
            model_class=User,
            readable_column_names=readable_column_names,
            writeable_column_names=writeable_user_column_names,
            sortable_column_names=readable_column_names,
            searchable_column_names=readable_column_names,
            default_sort_column_name="name",
        )
    ],
    url="/users",
)

class SomeThing(clearskies.Model):
    id_column_name = "id"
    backend = clearskies.backends.MemoryBackend()

    id = clearskies.columns.Uuid()
    thing_1 = clearskies.columns.String(validators=[Required()])
    thing_2 = clearskies.columns.String(validators=[Unique()])

more_endpoints = clearskies.EndpointGroup(
    [
        clearskies.endpoints.HealthCheck(url="health"),
        clearskies.endpoints.Schema(url="schema"),
        clearskies.endpoints.Callable(
            lambda request_data, some_things: some_things.create(request_data),
            model_class=SomeThing,
            readable_column_names=["id", "thing_1", "thing_2"],
            writeable_column_names=["thing_1", "thing_2"],
            request_methods=["POST"],
            url="some_thing",
        ),
        users_api,
    ]
)

wsgi = clearskies.contexts.WsgiRef(more_endpoints)
wsgi()

We attach the more_endpoints endpoint group to our context, and this contains 4 endpoints:

  1. A healthcheck
  2. A schema endpoint
  3. A callable endpoint
  4. The users_api endpoint group.

The users_api endpoint group then contains it’s own schema endpoint and a restful api endpoint with all our standard user CRUD operations. As a result, we can fetch two different schema endpoints:

curl 'http://localhost/schema'

curl 'http://localhost/users/schema'

The former documents all endpoints in the system. The latter only documents the endpoints under the /users path provided by the users_api endpoint group.

url

Required

Set the URL for the endpoint

When an endpoint is attached directly to a context, then the endpoint’s URL becomes the exact URL to invoke the endpoint. If it is instead attached to an endpoint group, then the URL of the endpoint becomes a suffix on the URL of the group. This is described in more detail in the documentation for endpoint groups, so here’s an example of attaching endpoints directly and setting the URL:

import clearskies

endpoint = clearskies.endpoints.Callable(
    lambda: {"hello": "World"},
    url="/hello/world",
)

wsgi = clearskies.contexts.WsgiRef(endpoint)
wsgi()

Which then acts as expected:

$ curl 'http://localhost:8080/hello/asdf' | jq
{
    "status": "client_error",
    "error": "Not Found",
    "data": [],
    "pagination": {},
    "input_errors": {}
}

$ curl 'http://localhost:8080/hello/world' | jq
{
    "status": "success",
    "error": "",
    "data": {
        "hello": "world"
    },
    "pagination": {},
    "input_errors": {}
}

Some endpoints allow or require the use of named routing parameters. Named routing paths are created using either the /{name}/ syntax or /:name/. These parameters can be injected into any callable via the routing_data dependency injection name, as well as via their name:

import clearskies

endpoint = clearskies.endpoints.Callable(
    lambda first_name, last_name: {"hello": f"{first_name} {last_name}"},
    url="/hello/:first_name/{last_name}",
)

wsgi = clearskies.contexts.WsgiRef(endpoint)
wsgi()

Which you can then invoke in the usual way:

$ curl 'http://localhost:8080/hello/bob/brown' | jq
{
    "status": "success",
    "error": "",
    "data": {
        "hello": "bob brown"
    },
    "pagination": {},
    "input_errors": {}
}

schema_format

Optional

The doc builder class/format to use

request_methods

Optional

The allowed request methods for this endpoint.

By default, only GET is allowed.

import clearskies

endpoint = clearskies.endpoints.Callable(
    lambda: {"hello": "world"},
    request_methods=["POST"],
)

wsgi = clearskies.contexts.WsgiRef(endpoint)
wsgi()

And to execute:

$ curl 'http://localhost:8080/' -X POST | jq
{
    "status": "success",
    "error": "",
    "data": {
        "hello": "world"
    },
    "pagination": {},
    "input_errors": {}
}

$ curl 'http://localhost:8080/' -X GET | jq
{
    "status": "client_error",
    "error": "Not Found",
    "data": [],
    "pagination": {},
    "input_errors": {}
}

response_headers

Optional

Set some response headers that should be returned for this endpoint.

Provide a list of response headers to return to the caller when this endpoint is executed. This should be given a list containing a combination of strings or callables that return a list of strings. The strings in question should be headers formatted as “key: value”. If you attach a callable, it can accept any of the standard dependencies or context-specific values like any other callable in a clearskies application:

def custom_headers(query_parameters):
    some_value = "yes" if query_parameters.get("stuff") else "no"
    return [f"x-custom: {some_value}", "content-type: application/custom"]

endpoint = clearskies.endpoints.Callable(
    lambda: {"hello": "world"},
    response_headers=custom_headers,
)

wsgi = clearskies.contexts.WsgiRef(endpoint)
wsgi()

security_headers

Optional

Configure standard security headers to be sent along in the response from this endpoint.

Note that, with CORS, you generally only have to specify the origin. The routing system will automatically add in the appropriate HTTP verbs, and the authorization classes will add in the appropriate headers.

import clearskies

hello_world = clearskies.endpoints.Callable(
    lambda: {"hello": "world"},
    request_methods=["PATCH", "POST"],
    authentication=clearskies.authentication.SecretBearer(environment_key="MY_SECRET"),
    security_headers=[
        clearskies.security_headers.Hsts(),
        clearskies.security_headers.Cors(origin="https://example.com"),
    ],
)

wsgi = clearskies.contexts.WsgiRef(hello_world)
wsgi()

And then execute the options endpoint to see all the security headers:

$ curl -v http://localhost:8080 -X OPTIONS
* Host localhost:8080 was resolved.
< HTTP/1.0 200 Ok
< Server: WSGIServer/0.2 CPython/3.11.6
< ACCESS-CONTROL-ALLOW-METHODS: PATCH, POST
< ACCESS-CONTROL-ALLOW-HEADERS: Authorization
< ACCESS-CONTROL-MAX-AGE: 5
< ACCESS-CONTROL-ALLOW-ORIGIN: https://example.com
< STRICT-TRANSPORT-SECURITY: max-age=31536000 ;
< CONTENT-TYPE: application/json; charset=UTF-8
< Content-Length: 0
<
* Closing connection

schema_configuration

Optional

Addiional data to inject into the schema doc.

This is typically used for setting info/server settings in the resultant swagger doc. Anything in this dictionary is injected into the “root” of the generated documentation file.

authentication

Optional

The authentication for this endpoint (default is public)

Use this to attach an instance of clearskies.authentication.Authentication to an endpoint, which enforces authentication. For more details, see the dedicated documentation section on authentication and authorization. By default, all endpoints are public.