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())