Package: apiman
APIMANAPIMAN provide a easy way to integrate OPENAPI manual/document for python web projectFeaturesOut of the box for Starlette, Flask, Django, Bottle, Tornado and FalconWhole OpenAPI 2 and 3 specification supportConfigurableSwaggerUIandRedocUIRequest data(json/xml/form body, query, header, cookie, path args) validation by API specificationInstallpipinstall-UapimanUsagecreate openapi basic fileopenapi: "3.0.0"
info:
  title: 'APIMAN'
  version: '0.1'
  description: 'API manual for example'create apiman instancefromstarlette.applicationsimportStarlettefromapiman.starletteimportApiman# from flask import Flask# from apiman.flask import Apimanapiman=Apiman(template="./docs/openapi.yml")register apiman# app = Flask(__name__)app=Starlette()apiman.init_app(app)add document for api endpointby docstring:@app.route("/hello/",methods=["GET"])asyncdefhello(req:Request):"""There is normal docstring, Below is OpenAPI specification---summary: hello apitags:- testparameters:- name: namein: queryrequired: Trueschema:type: stringresponses:"200":description: OK"""returnResponse(f"hello")by yaml content:@app.route("/hello/",methods=["GET"])@apiman.from_yaml("""summary: hello apitags:- testparameters:- name: namein: queryrequired: Trueschema:type: stringresponses:"200":description: OK""")asyncdefhello(req:Request):returnResponse(f"hello")(why yaml content? for my usage, just for f-string support)by file:@app.route("/hello/",methods=["GET"])@apiman.from_file("./docs/hello.yml")asyncdefhello(req:Request):returnResponse(f"hello")@app.route("/hello/",methods=["GET"])@apiman.from_file("./docs/hello.json")asyncdefhello(req:Request):returnResponse(f"hello")validate defined OPENAPI specificationapiman.validate_specification()run app and browse swagger ui atserver:port/apiman/swaggerorserver:port/apiman/redocMore UsageConfig apimanConfig apiman's ui url, title and ui html templateapiman=Apiman(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 schemaWe 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: 0or by code:apiman.add_schema("Cat",{"properties":{"name":{"type":"string"},"age":{"type":"integer","minimum":0},},"type":"object",},)(All specification data store inapiman.specification), then use it by OpenAPI way:@apiman.from_yaml("""responses:"200":description: OKschema:$ref: '#definitions/Cat'""")request validationvalide request byvalidate_request@app.route("/hello/",methods=["POST"])@apiman.from_yaml("""summary: hello apitags:- testparameters:- name: namein: queryrequired: Trueschema:type: string- name: x-custom-paramschema:type: stringin: headerrequired: True- name: cookie_paramschema:type: stringin: cookierequesteBody:required: truecontent:application/json:schema:type: objectproperties:key:type: integerrequired: ["key"]responses:"200":description: OK""")asyncdefget(self,req:Request):# await req.json()# apiman.validate_request(req)awaitapiman.async_validate_request(req)# get validated params by original waysrequest.query_paramsrequest.cookiesrequest.headersrequest.json()...(for sync code, callapiman.validate_request(req))This method will find this request's OpenAPI specification and request params(query, path, cookie, header, body) then validate it, we can assess validated req params by origin way or raise validation exception.(byjsonschema_rs)limittype limitAll request params type isorigin type, so query/header/cookie params is alwaysstring, we should define this params type to string or we will get validation Exception(eg, flask's path params can be int type original).But if we want some integer param for string type, set regexpatternin specification, eg:id:
  type: string
  pattern: '^\d+$'or just use body data for rich formatExamplesLet's see a Starlette example app:"""OpenAPI2(Swagger) with Starlette"""fromstarlette.applicationsimportStarlettefromstarlette.requestsimportRequestfromstarlette.responsesimportJSONResponsefromstarlette.endpointsimportHTTPEndpointfromuvicornimportrunfromstarlette.testclientimportTestClientfromapiman.starletteimportApimanapp=Starlette()apiman=Apiman(template="./examples/docs/cat_template.yml")apiman.init_app(app)# define dataCATS={1:{"id":1,"name":"DangDang","age":2},2:{"id":2,"name":"DingDing","age":1},}# add schema definitionapiman.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/")classCat(HTTPEndpoint):"""Declare multi method---get:summary: Get single cattags:- catparameters:- name: idtype: stringin: pathrequired: True- name: x-client-versiontype: stringin: headerrequired: Trueresponses:"200":description: OKschema:$ref: '#/definitions/Cat'"404":description: Not found"""defget(self,req:Request):# validate params in path query header and cookie by schema (only support string type)apiman.validate_request(req)returnJSONResponse(CATS[int(req.path_params["id"])])defdelete(self,req:Request):"""Declare single method---summary: Delete single cattags:- catparameters:- name: idtype: integerin: pathrequired: Trueresponses:"204":description: OKschema:$ref: '#/definitions/Cat'"404":description: Not found"""cat=CATS.pop(int(req.path_params["id"]))returnJSONResponse(cat)# define doc by yaml or json file@app.route("/cats/",methods=["GET"])@apiman.from_file("./examples/docs/cats_get.yml")deflist_cats(req:Request):returnJSONResponse(list(CATS.values()))@app.route("/cats/",methods=["POST"])@apiman.from_file("./examples/docs/cats_post.json")asyncdeflist_cats(req:Request):awaitreq.json()# validate json bodyapiman.validate_request(req)cat=awaitreq.json()CATS[cat["id"]]=catreturnJSONResponse(cat)if__name__=="__main__":apiman.validate_specification()run(app)Then we get swagger web page athttp://localhost:8000/apiman/swagger/:Seeexamples/for more examplesHow it worksProvide a base class to handle api specification's collectionProvide extension for every web framework to extract api specification and register http endpoints to show UI web page and specification