Integrate api manual for your web project
Project description
APIMAN
APIMAN provide a easy way to integrate api manual/document for your web project
Features
- Out of the box for Flask and Starlette and Django
- Whole OpenAPI 2 and 3 specification support
- Configurable SwaggerUI and RedocUI
- Request(json body、query、header...) validation by API specification
Install
pip install -U apiman
Usage
create openapi basic file
openapi: "3.0.0"
info:
title: 'APIMAN'
version: '0.1'
description: 'API manual for example'
create apiman instance
from starlette.applications import Starlette
from apiman.starlette import Extension
# from flask import Flask
# from apiman.flask import Extension
apiman = Extension(template="./docs/openapi.yml")
register apiman
# app = Flask(__name__)
app = Starlette()
openapi.init_app(app)
add document for api endpoint
by docstring:
@app.route("/hello/", methods=["GET"])
async def hello(req: Request):
"""
There is normal docstring, Below is OpenAPI specification
---
summary: hello api
tags:
- test
parameters:
- name: name
in: query
required: True
schema:
type: string
responses:
"200":
description: OK
"""
return Response(f"hello")
by yaml content:
@app.route("/hello/", methods=["GET"])
@openapi.from_yaml(
"""
summary: hello api
tags:
- test
parameters:
- name: name
in: query
required: True
schema:
type: string
responses:
"200":
description: OK
"""
)
async def hello(req: Request):
return Response(f"hello")
(why yaml content? for my usage, just for f-string support)
by file:
@app.route("/hello/", methods=["GET"])
@openapi.from_file("./docs/hello.yml")
async def hello(req: Request):
return Response(f"hello")
@app.route("/hello/", methods=["GET"])
@openapi.from_file("./docs/hello.json")
async def hello(req: Request):
return Response(f"hello")
run app and browse swagger ui at server:port/apiman/swagger
or server:port/apiman/redoc
More Usage
Config apiman
Config apiman's ui url, title and ui html template
apiman = Extension(
title="OpenAPI document",
specification_url="/apiman/specification/",
swagger_url="/apiman/swagger/",
redoc_url="/apiman/redoc/",
swagger_template="swagger.html",
redoc_template="redoc.html",
template="template.yaml",
)
reuseable schema
We can define some OpenAPI schema or parameters for config usage, in openapi.yml:
openapi: "3.0.0"
info:
title: 'APIMAN'
version: '0.1'
description: 'API manual for example'
definitions:
Cat:
type: object
required:
- name
properties:
name:
type: string
age:
type: integer
format: int32
minimum: 0
or by code:
openapi.add_schema(
"Cat",
{
"properties": {
"name": {"type": "string"},
"age": {"type": "integer", "minimum": 0},
},
"type": "object",
},
)
(All specification data store in apiman.specification
), then use it by OpenAPI way:
@openapi.from_yaml(
"""
responses:
"200":
description: OK
schema:
$ref: '#definitions/Cat'
"""
)
request validation
valide request by validate_request
@app.route("/hello/", methods=["POST"])
@openapi.from_yaml(
"""
summary: hello api
tags:
- test
parameters:
- name: name
in: query
required: True
schema:
type: string
- name: x-custom-param
schema:
type: string
in: header
required: True
- name: cookie_param
schema:
type: string
in: cookie
requesteBody:
required: true
content:
application/json:
schema:
type: object
properties:
key:
type: integer
required: ["key"]
responses:
"200":
description: OK
"""
)
async def get(self, req: Request):
await req.json()
openapi.validate_request(req)
# get validated params
request.query_params
request.cookies
request.headers
request.json()
...
(for starlette, call await req.json()
first to load json data)
This method will find this request's OpenAPI specification and request params(query, path, cookie, header, json body) then validate it, we can get validate req params by origin way or raise Exceptoin(by jsonschema_rs)
limit
type limit
All request params type is origin type, so query/header/cookie params is always string, we should define this params type to string or we will get Exception(flask's path params can be int original).
But if we want some integer param for string type you can set regex pattern
in specification, eg:
id:
type: string
pattern: '^\d+$'
or just use json body for rich format
Examples
Let's see a Starlette example app:
"""OpenAPI2(Swagger) with Starlette
"""
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.endpoints import HTTPEndpoint
from uvicorn import run
from openapi_spec_validator import validate_v2_spec
from starlette.testclient import TestClient
from apiman.starlette import Extension
app = Starlette()
openapi = Extension(template="./examples/docs/cat_template.yml")
openapi.init_app(app)
# define data
CATS = {
1: {"id": 1, "name": "DangDang", "age": 2},
2: {"id": 2, "name": "DingDing", "age": 1},
}
# add schema definition
openapi.add_schema(
"Cat",
{
"properties": {
"id": {"description": "global unique", "type": "integer"},
"name": {"type": "string"},
"age": {"type": "integer"},
},
"type": "object",
},
)
# define routes and schema(in doc string)
@app.route("/cat/")
class Cat(HTTPEndpoint):
"""
Declare multi method
---
get:
summary: Get single cat
tags:
- cat
parameters:
- name: id
type: string
in: path
required: True
- name: x-client-version
type: string
in: header
required: True
responses:
"200":
description: OK
schema:
$ref: '#/definitions/Cat'
"404":
description: Not found
"""
def get(self, req: Request):
# validate params in path query header and cookie by schema (only support string type)
openapi.validate_request(req)
return JSONResponse(CATS[int(req.path_params["id"])])
def delete(self, req: Request):
"""
Declare single method
---
summary: Delete single cat
tags:
- cat
parameters:
- name: id
type: integer
in: path
required: True
responses:
"204":
description: OK
schema:
$ref: '#/definitions/Cat'
"404":
description: Not found
"""
cat = CATS.pop(int(req.path_params["id"]))
return JSONResponse(cat)
# define doc by yaml or json file
@app.route("/cats/", methods=["GET"])
@openapi.from_file("./examples/docs/cats_get.yml")
def list_cats(req: Request):
return JSONResponse(list(CATS.values()))
@app.route("/cats/", methods=["POST"])
@openapi.from_file("./examples/docs/cats_post.json")
async def list_cats(req: Request):
await req.json()
# validate json body
openapi.validate_request(req)
cat = await req.json()
CATS[cat["id"]] = cat
return JSONResponse(cat)
if __name__ == "__main__":
run(app)
Then we get swagger web page at http://localhost:8000/apiman/swagger/:
See examples/ for more examples
How it works
- Provide a base class("OpenApi") to handle api specification's collection
- Provide extensions to extract api specification and register http endpoints to show UI web page and specification
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.