openapi-generator
2024-10-12
Parse OpenAPI into CLOS object for client generation
1Common Lisp OpenAPI Generator (openapi-generator)
OpenAPI-generator is an api client system generator.
1.1Features
- Generation of OpenAPI ASDF/Quicklisp-loadable projects within one command
- Support for path, (arbitrary) query, (arbitrary) header, (json) content parameters
- Each system has dynamic variables for changing default server, query, headers,authentication, cookie, parse
- Multiple function alias options (operation-id, path, summary, description)
- Supported file formats / inputs: JSON, YAML, URL, local file, project-id on apis.guru.
- Conversion of an OpenAPI spec into CLOS object -> explore API within inspector
- Conversion of OpenAPI-2.0 or YAML files to OpenAPI-3.0 JSON with [[https://converter.swagger.io/][swaggerconverter]] (network connection required)
1.2Codeberg
The main repository of this library is hosted on Codeberg.If you are viewing this anywhere else, it is just a mirror. Please use theCodeberg repository for collaboration (issues, pull requests, discussions,...).Thank you!1.3Warning
- This software is still ALPHA quality. The APIs will be likely to change.
- Make sure to review the openapi specification file if it is from an untrustedsource or environment file before using openapi-generator, especially beforeusing the generated system. This is to mitigate potential security risks suchas code injection. For security vulnerabilities, please contact the maintainer of the library.
1.4Quickstart
Load openapi-generator from Ultralisp via ql:quickload, or from local projectfolder via asdf:load-system (ql:quickload :openapi-generator)
Then put this in the REPL:
(openapi-generator:make-openapi-client
"codeberg"
:url "https://codeberg.org/swagger.v1.json"
:server "https://codeberg.org/api/v1"
:parse t)
This creates a client system to access the codeberg api in the projects folder within openapi-generator library and loads it (by default). Now you can use e.g.:
(codeberg:repo-get "kilianmh" "openapi-generator")
1.5Interface
1.5.1parse-openapi
parse-openapi (name &key url collection-id source-directory content (dereference *dereference*))
Parse an OpenAPI to a openapi class object. This might be useful for exploration of the API specification, or you can pass it to the make-openapi-client function.
(openapi-generator:parse-openapi "codeberg" :url "https://codeberg.org/swagger.v1.json")
1.5.1.1name
name of the openapi-file which is opened/created1.5.1.2url
url to a openapi specification file1.5.1.3collection-id
apis-guru-id from apis.guru1.5.1.4source-directory
source-directory (name will be inserted from name parameter)1.5.1.5content
openapi spec file string1.5.1.6dereference
If set to true, pointers within openapi spec are dereferenced before parsing intoopenapi CLOS object. This improves discoverability for humans (and algorithms).Can be turned off for faster parsing.1.5.2make-openapi-client
make-openapi-client (system-name
&key
(server *server*) (parse *parse*) (query *query*) (headers *headers*)
(authorization *authorization*) (cookie *cookie*)
(alias (list :operation-id)) (system-directory :temporary) (load-system t)
openapi (api-name system-name) url source-directory collection-id
content (dereference *dereference*))
1.5.2.1parameters
1.5.2.1.1input same as parse-openapi
1.5.2.1.2openapi
openapi-object1.5.2.1.3api-name
Name of file in openapi-generator/data folder1.5.2.1.4system-directory
Options:- pathname-object -> pathname pathname-object + system-name
- :temporary -> default, (uiop:temporary-directory)
- :library -> openapi-generator/projects
1.5.2.1.5parse
default: nil, alternative: t, :json. (openapi-generator:make-openapi-client
"wikitionary-api"
:url "https://github.com/shreyashag/openapi-specs/raw/main/thesaurus.yaml"
:parse t)
(wikitionary-api:find-word "oligopoly")
1.5.2.1.6server
set default server variable in system (e.g. if server not/incorrect in specfile)1.5.2.1.7query
set default query parameters1.5.2.1.8headers
set default headers (e.g. for api-tokens that have to be supplied often)1.5.2.1.9authorization
set default authorization value1.5.2.1.10cookie
set default cookie value1.5.2.1.11Alias
system exported functions: (multiple options possible):- :operation-id (param-cased operation-id) (default if there is are operation-id specified)
- :summary (param-cased summary)
- :description (param-case description)
- :path (operation-type + path) (default if no operation-id specified)
1.5.2.1.12load-system
Load system after making it (default: t)1.5.2.1.13dereference
If set to true (default), then the openapi spec is fully dereferenced before parsing intothe CLOS object. This might be necessary to properly generate the system, e.g.if pointers are used for schemas. Can be turned off for faster system generation.1.5.2.2examples
1.5.2.2.1content
The request body content can be supplied either as "raw" Json or as lisp datastructures. Both are type-checked at run-time. Look at JZON documentation for the type mappinghttps://github.com/Zulu-Inuoe/jzon#type-mappings.You can supply the body-request to generated functions with the content keyword(default) or alternatively if existing in there is a title in the spec to theparam-cased title keyword request-body id.Additionally for objects, the first level properties exist as functionparameters. (openapi-generator:make-openapi-client
"stacks"
:url "https://raw.githubusercontent.com/hirosystems/stacks-blockchain-api/gh-pages/openapi.resolved.yaml")
;; make-openapi-client stacks as described in quickstart
(stacks:call-read-only-function
"SP187Y7NRSG3T9Z9WTSWNEN3XRV1YSJWS81C7JKV7" "imaginary-friends-zebras" "get-token-uri"
:content
"{
\"sender\": \"STM9EQRAB3QAKF8NKTP15WJT7VHH4EWG3DJB4W29\",
\"arguments\":
[
\"0x0100000000000000000000000000000095\"
]
}")
;; make-openapi-client stacks as described in quickstart
(stacks:call-read-only-function
"SP187Y7NRSG3T9Z9WTSWNEN3XRV1YSJWS81C7JKV7" "imaginary-friends-zebras" "get-token-uri"
:read-only-function-args ;; request-body title (only if existing in spec)
(serapeum:dict
"sender" "STM9EQRAB3QAKF8NKTP15WJT7VHH4EWG3DJB4W29"
"arguments" (list "0x0100000000000000000000000000000095"))
:parse t)
;; first level of the request-body objects can be supplied as lisp datastructures
(stacks:call-read-only-function
"SP187Y7NRSG3T9Z9WTSWNEN3XRV1YSJWS81C7JKV7" "imaginary-friends-zebras" "get-token-uri"
:sender "STM9EQRAB3QAKF8NKTP15WJT7VHH4EWG3DJB4W29"
:arguments (list "0x0100000000000000000000000000000095"))
;; first level of the request-body objects can be supplied as JSON strings
(stacks:call-read-only-function
"SP187Y7NRSG3T9Z9WTSWNEN3XRV1YSJWS81C7JKV7" "imaginary-friends-zebras" "get-token-uri"
:sender "STM9EQRAB3QAKF8NKTP15WJT7VHH4EWG3DJB4W29"
:arguments "[\"0x0100000000000000000000000000000095\"]")
1.5.2.2.2header
;; This example only works if you generate a valid apikey and insert it after Bearer
;; in the headers list
(openapi-generator:make-openapi-client
"openai"
:url "https://raw.githubusercontent.com/openai/openai-openapi/master/openapi.yaml"
:bearer "<YOUR-API-KEY>"
:system-directory :temporary)
You have to first open an account and generate an api-key for using this api.If you supply value of authorization during client-creation, it will be saveddirectly in the file as variable. Beware and dont use this if in an untrustedenvironment. ;; only working with valid API-KEY
(openai:retrieve-engine "davinci")
You can also add add :authorization "Bearer <YOUR-API-KEY>" to each functioncall. This is equivalent to adding it to the headers. (openai:list-engines
:authorization "Bearer <YOUR-API-KEY>" ;; -> if not supplied during system generation
)
1.5.2.2.3collection-id
(openapi-generator:make-openapi-client
"opendatasoft"
:collection-id "opendatasoft.com"
:parse nil)
This creates the api client for opendatasoft by accessing apis.guru forthe URL.Here an example query: (opendatasoft:get-dataset "geonames-all-cities-with-a-population-1000")
1.5.2.2.4from openapi data folder
Each time you load an api, a loadable json is stored in the openapi-generator/datafolder. ELse you can put a file in the this folder manually. ;; file with that name has to be present in the folder openapi-generator/data
(openapi-generator:make-openapi-client "codeberg")
1.5.3convert-to-openapi-3
convert-to-openapi-3 (&key url content pathname (content-type "json"))
Conversion from Openapi 2.0 YAML/JSON to OpenAPI 3.0 JSON. (openapi-generator:convert-to-openapi-3 :url "https://converter.swagger.io/api/openapi.json")
1.5.4*dereference*
Global variable that determines whether openapi spec is dereferenced beforeparsing into a CLOS object. Set to true by default. Can be overwritten in eachcall to parse-openapi / make-openapi-client.1.6Possible Future Improvements
- modularize the project (e.g. separate systems for parsing, functiongeneration, system generation)
- extensibility with custom classes
- Auto-generation of request body classes for parsing them into CLOS objects
- Response validation & access functions for response content
- websocket support
- integrate JSON-Schema to create an expanded API-Object
- generate client from command line interface (CLI)
- integration in workflows (CI/CD, etc.)
- more regression tests
- support multiple implementations
- offline openapi-spec conversion
- integrate other api standards: json:api, raml, postman collection, har, OData,GraphQL, gRPC
1.7License on generated code
Generated code is intentionally not subject to this project license.Code generated shall be considered AS IS and owned by the user.There are no warranties--expressed or implied--for generated code.You can do what you wish with it, and once generated, the code is yourresponsibility and subject to the licensing terms that you deem appropriate.1.8Call for collaboration
Feel free to contribute by opening issues, pull request, feature requests etc.Your help is much appreciated.1.9Copyright
(C) 2023 Kilian M. Haemmerle (kilian.haemmerle@protonmail.com)
1.10License
Licensed under the AGPLv3+ License.