Use cases for making things more dynamic
We have a lot of dynamic things we want to do in our API definitions. I want to look at use cases and examples that show how one might use Geneva to solve them.
A few notes about the examples below:
- The variables are values defined before the definition is executed.
- The definition is the code, in a sense
- The result is what the code produced when it runs
- The examples I show are truncated—they aren't valid or complete OpenAPI or AsyncAPI definitions
Everything but the results are editable. Have fun!
We want reusability
As noted, we've added keywords like $ref
to specifications to help people make things reusable. We want to define once and reuse all over. Geneva does this by way of ref:
.
variables (edit)info:contact:name: API Supporturl: http://www.example.com/supportemail: support@example.comdefinition (edit)openapi: 3.0.0info: ref:inforesultopenapi: 3.0.0info:contact:name: API Supporturl: http://www.example.com/supportemail: support@example.com
This differs from $ref
that we find in OpenAPI and JSON Schema. The ref
here is referencing a variable in the current scope. Geneva lets you define variables and reuse them through the entire definition. This means Geneva's ref
and $ref
could be used together.
variables (edit)schemas:customer:$ref: https://example.com/customer.ymldefinition (edit)openapi: 3.0.0components:schemas:Customer: ref:schemas.customerresultopenapi: 3.0.0components:schemas:Customer:$ref: https://example.com/customer.yml
This could be a way to pass in different schemas for different builds.
We want to compose files together
Left untended, YAML documents grow in complexity and size. It's not easy to read or edit a 2,000 line YAML document. The next step is to break files like this into multiple files.
Here I create a mock file system that I reference in the definition. Geneva allows for passing in the normal file system modules, too, though this might not be safe.
files (edit)infoFile: |contact:name: API Supporturl: http://www.example.com/supportemail: support@example.comdefinition (edit)openapi: 3.0.0info:fn:include: infoFileresultopenapi: 3.0.0info:contact:name: API Supporturl: http://www.example.com/supportemail: support@example.com
Imagine including Markdown files that technical writers put together. Or adding in security definitions managed by the security team. Or pulling in operations defined in a separate file. You could include many different kinds of files without bloating a specification with that functionality.
We want localization
Similarly, people may want to add localization to their OpenAPI or AsyncAPI definition. Instead of adding localization to the specification, people could pass in different languages to build separate final OpenAPI definitions.
variables (edit)lang: enlocale:userSignedupDescription:en: An event describing that a user just signed up.getLocale:fn:lambda:- term- fn:path:- [ref:term, ref:lang]- ref:localedefinition (edit)asyncapi: 2.0.0info:title: Exampleversion: 0.1.0channels:user/signedup:subscribe:message:description:fn:getLocale: userSignedupDescriptionresultasyncapi: 2.0.0info:title: Exampleversion: 0.1.0channels:user/signedup:subscribe:message:description: An event describing that a user just signed up.
If you look, you'll see that I use fn:lambda
, which is a function definition. I'm making my own function to use throughout my definition. I'll let you decide if this is a great or horrible idea.
We want base definitions
Sometimes we want to add certain status codes to every response in an OpenAPI definition. This happens for error codes that all return the same error format.
variables (edit)postResponses:fn:lambda:- responses- fn:mergeDeepLeft:- responses:"405":description: Method Not Allowedcontent:application/json: {}- ref:responsesdefinition (edit)openapi: 3.0.0paths:/customers:post:fn:postResponses:responses:"200":description: Customer addedcontent:application/json: {}resultopenapi: 3.0.0paths:/customers:post:responses:"200":description: Customer addedcontent:application/json: {}"405":description: Method Not Allowedcontent:application/json: {}
We want a way to use templates
We saw where AWS requires people to use a !Join
function to create strings. Maybe a template would be better.
Geneva provides a fn:template
function that allows for mustache templates. This is a simple example to show how templates work. You can use mustache features in the template or use other functions of Geneva to create values.
variables (edit)env: devdefinition (edit)openapi: 3.0.0info:description:fn:template: |This is in the {{env}} environment.resultopenapi: 3.0.0info:description: |This is in the dev environment.