Final GSOC Submission for the Moira Project: OpenAPI Description and Client Libraries of the Moira API
Name: Michael Okoko
Organization: Moira Alert
Project: https://summerofcode.withgoogle.com/projects/#5733016854331392/
Proposal: https://drive.google.com/file/d/14HpvkkBGcfaP676FWr-5qECOI7Gebkbp/view?usp=sharing/
The goal of the project was to:
- Develop an up-to-date OpenAPI/Swagger description of the endpoints exposed by the API.
- Generate language-specific client libraries from the API description file.
- Setup automated API tests to test the API specification (contract) against the actual implementation.
In my initial proposal, I had planned to document the API as code comments and transform them to an OpenAPI spec file with go-swagger. However, after discussions with my mentors, I realized writing the yml
files by hand was better because it does not clutter the code base, and it provides more flexibility in the long run.
To document the APIs, I generated all the method/path pairs available in the Moira API using chi-walker, and created a folder per route group. For instance, methods, requests and responses relating to the /contact
route group are kept in the contact folder
. This will hopefully make maintaining/updating the documentation easier.
- idoqo/moira-oas: Repository containing OpenAPI
yml
files that covers all of Moira's endpoints. - I am currently working with mentors to integrate the documentation into moira-alert/doc. Some of the related PRs include:
To make the API documentation accessible to Moira users, it is published to a public SwaggerHub repository. The multiple yml
files are bundled together using the swagger-cli
tool, and validated to ensure that the schema is correctly written. Schema validation is handled by the amazing openapi-generator-cli
tool.
The build-validate-publish process is automatically executed as part of the CI flow. In my proposal, I planned on using TravisCI but later realized Github Actions can do the job just fine so I settled for that instead. The CI pipelines are executed as follows:
- On PR to master branch: Merge all the
yml
files into a singleopenapi.yml
file and validate the schema. - On Merge to master branch: Merge the
yml
files, validate the schema and publish the schema to SwaggerHub as "version master". - On Push to feature branches (
feature/*
e.g.,feature/add-teams
) and version branches (v[0-9]+.[0-9]+.[0-9]+
e.g.,v2.5.13
), merge theyml
files, validate the schema, and publish to SwaggerHub - using the branch name as the version name.
- https://app.swaggerhub.com/apis/Moira/moira-alert/: Moira-owned SwaggerHub page containing all the documentation integrated so far.
- https://app.swaggerhub.com/apis/idoko/moira-alert/: OpenAPI specification on SwaggerHub containing all of Moira API endpoints.
- https://hub.docker.com/r/idoko/moira-oas/: Public Docker image to view Moira API documentation (using SwaggerUI or ReDoc).
- PR #59 - improve pipeline actions for Pull Requests
- PR #55 - set up GitHub Actions workflow
With a valid OpenAPI spec file, it is possible to generate client libraries for different languages using openapi-generator-cli
. The languages it supports are listed here and luckily, it supports the languages most important for the Moira team (i.e Python, C#, TypeScript, and Golang).
I wrote Makefile rules to generate the clients and work is ongoing to check the code quality of the auto-generated code.
Ideally, the generated clients will be published to their respective package repositories (i.e Python => Pypi, TypeScript => npm, C# => Nuget, Go => pkg.go.dev).
- idoqo/moira-clients: Repository containing auto-generated Moira client libraries for Python, C#, TypeScript and Golang.
- PR #70 - ci: add client libraries generation to GitHub Actions CI
It is one thing to have an API documentation, ensuring it is correct is another thing, especially as the documentation will be updated when the core Moira APIs changes too. To help this, I set up a process to test the OpenAPI spec against the real Moira API and add it to the CI pipelines.
I looked at Dredd and Schemathesis and in the end, settled on using Schemathesis because it had better support for OpenAPI 3, and it can use OpenAPI links to improve how tests are run.
Schemathesis works by generating request payload (i.e request bodies, query parameters, and path parameters) from the examples in an OpenAPI document, making the request to the paths and comparing the response it gets with what is defined in the document. For example, given this OpenAPI schema,
ContactRequest:
type: object
description: "Format of the request body for POST/PUT requests to `/contacts` and related sub-paths."
properties:
type:
type: string
example: "mail"
value:
type: string
example: "devops@example.com"
The request payload below will be generated.
{
"type": "mail",
"value": "devops@example.com"
}
It also makes it possible to specify "fixed" request data using hooks (such as when data in the database needs to be re-used).
My mentor suggested an alternative that involved setting up our own custom script with dummy data, that way, we can easily reproduce the tests at any time. It is currently a work in progress.
PR #68 - add test rule to Makefile and integrate with CI
I hope to see the OpenAPI documentation fully integrated into Moira and see it updated as changes are made to the core Moira API.
We could also explore using a stricter validator (such as IBM/openapi-validator or Redocly/openapi-cli) as they supports a lot more rules for validation e.g missing descriptions and missing examples).
Working on this project taught me a lot about how important API documentation is. I also learnt a lot about how OpenAPI works and the ecosystem around it (mock servers, library generation, converters, etc).
My challenges mostly revolved around testing API contracts and working with Github Actions (and CI pipelines), especially as Actions is a relatively new tool and there are not much resources online.
In the end though, I'm thankful to Emil, Alexey, and Nixolay - my mentors - for always being there to help and making it easier than it would have been.