Schema
An endpoint that automatically creates a swagger doc for the application
- Overview
- url
- schema_format
- request_methods
- response_headers
- security_headers
- schema_configuration
- 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:
- A healthcheck
- A schema endpoint
- A callable endpoint
- 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.