Specification#
pjrpc
has built-in OpenAPI and OpenRPC
specification generation support implemented by pjrpc.server.specs.openapi.OpenAPI
and pjrpc.server.specs.openrpc.OpenRPC
respectively.
To enable schema generation you should pass specification generator instance to the JSON-RPC application.
json_rpc = integration.JsonRPC(
'/api/v1',
spec=specs.OpenAPI(
info=specs.Info(version="1.0.0", title="User storage"),
servers=[
specs.Server(
url='http://127.0.0.1:8080',
),
],
security_schemes=dict(
basic=specs.SecurityScheme(
type=specs.SecuritySchemeType.HTTP,
scheme='basic',
),
),
schema_extractor=extractors.pydantic.PydanticSchemaExtractor(),
ui=specs.SwaggerUI(),
),
)
OpenAPI specification will be available on /api/v1/openapi.json
path. Path suffix can be overridden
by passing path
parameter to a specification generator.
For more information about the specification see OpenAPI Specification.
OpenRPC specification generation looks pretty the same:
json_rpc = integration.JsonRPC(
'/api/v1',
spec=specs.OpenRPC(
info=specs.Info(version="1.0.0", title="User storage"),
servers=[
specs.Server(
name='test',
url='http://127.0.0.1:8080/api/v1/',
summary='test server',
),
],
schema_extractor=extractors.pydantic.PydanticSchemaExtractor(),
),
)
OpenRPC specification will be available on /api/v1/openrpc.json
path.
Method description, tags, errors, examples, parameters and return value schemas can be provided by hand
using pjrpc.server.specs.openapi.annotate()
decorator or automatically extracted using schema extractor.
pjrpc
provides two schema extractors: pjrpc.server.specs.extractors.pydantic.PydanticSchemaExtractor
and pjrpc.server.specs.extractors.docstring.DocstringSchemaExtractor
.
They uses pydantic models or python docstrings for method summary,
description, errors, examples and schema extraction respectively. You can implement your own schema extractor
inheriting it from pjrpc.server.specs.extractors.BaseSchemaExtractor
and implementing abstract methods.
Multiple schema extractors could be used simultaneously using parameter schema_extractors
. Final schema
will be merged using that extractors in reverse order (former extractor rewrites the result of the later ones).
@specs.annotate(
tags=['users'],
errors=[AlreadyExistsError],
examples=[
specs.MethodExample(
summary="Simple example",
params=dict(
user={
'name': 'John',
'surname': 'Doe',
'age': 25,
},
),
result={
'id': 'c47726c6-a232-45f1-944f-60b98966ff1b',
'name': 'John',
'surname': 'Doe',
'age': 25,
},
),
],
)
@methods.add
@validator.validate
def add_user(user: UserIn) -> UserOut:
"""
Creates a user.
:param object user: user data
:return object: registered user
:raise AlreadyExistsError: user already exists
"""
for existing_user in flask.current_app.users_db.values():
if user.name == existing_user.name:
raise AlreadyExistsError()
user_id = uuid.uuid4().hex
flask.current_app.users_db[user_id] = user
return UserOut(id=user_id, **user.dict())