Fork me on GitHub

This documentation is currently under active development.

Please follow the warnings or errors that the openapi-eller tool provides, and if something is missing from the documentation, please open an issue on GitHub.

1. Introduction to openapi-eller 0.3.3

Documentation is still to be done. Sorry!

2. Creating Targets

2.1. Basic requirements for a conforming target

2.1.1. Getting Started

A target is the recipe for generating API code for a given platform. It may generate either a client or a server depending on its need (and the name of the target will make it obvious, assume client by default).

A target template is usually one Handlebars template, with any number of partials necessary to make a fully functional tool.

A target generator can be configured with a JSON or YAML file of key-value parameters that can be accessed from the code generation templates.

Each target’s entry point is an index.ts file implementing the Target type.

2.1.2. Basic API

A conforming implementation consists of at the very least:

  • A top level class—or equivalent—holding methods for each endpoint for a given service

  • A top level enum-like class—or equivalent—for holding known server endpoints, configurable for the defined variables of that server endpoint

  • At least one constructor taking a base URL as a string, and preferably another constructor accepting the enum-like class type for servers if it makes sense for the provided language

  • Each endpoint provides a return type of an Observable or a Promise/Future, preferring the most used solution for the platform, and falling back to Rx where that is not specified or is simpler

  • Generates RFC-compliant handlers for OAuth 2 where required

  • Handles all types supported by OpenAPI v3, and preferrably has a mechanism for handling well-known formats, and custom extensions for those formats

  • Generates interceptors for each type of authentication supported by OpenAPI

2.1.3. Nested Objects

OpenAPI specifications tend to have nested objects inside other objects, as is common with all JSON documents. In order to handle this effectively, depending on the placement of the object and whether or not it is anonymously defined (does not appear in the top-level schemas list), a subobject might be generated.

Some languages like Swift or Kotlin will allow nesting a class inside another class definition, however some others do not. In the case where your language does not allow nested classes, it is recommended that you generate these classes at the top level of your generated models, with the nesting generated as part of the name of the class.

For instance, if you have an object Foo, with an anonymous field bar which would ordinarily generate the nested type Bar and be accessed in Kotlin as Foo.Bar, in your flat language you may need to name the type Foo_Bar or some other underutilised delimiter in order to guarantee namespace integrity.

2.1.4. Documentation

Several targets require some set-up in order to function. Instead of generating entire demo projects, only two things are needed for fully supporting a target and assisting those people who use them:

  • At the beginning for the file, clearly mark it as generated

  • Below that, document any requirements for using dependencies, such as the version requirements, package managers required, imports to be declared in other configurations, etc. You may make this copy and pastable chunks if you desire to be super-nice.

  • The intro documentation should also include supported configuration elements from the JSON, such as { "es6": true } enabling ES6 modules for ECMAScript, which would otherwise only be discoverable by reading the target source code.

  • Documenting dependencies for specific elements on those elements, such as the requirement for OAuth activities in Android to specify some things in the XML manifest. This should be as copy-pastable as possible.

  • Generate the relevant documentation fields for all endpoints, models and fields. This makes the API autocompletable and discoverable in an IDE, or allow documentation to be generated with a relevant generator.

2.1.5. Reserved keywords

Each target must handle reserved keywords in a way that would be considered acceptable for a given platform. A general approach is to append an underscore (_) to a variable or class name. Users have the option of providing field and class name overrides in the configuration passed to the generator, so close enough is good enough is the approach taken.

Even if a language supports a special way of accessing keywords, such as Swift’s backtick notation, avoid doing so. It makes actually using the API more painful than it needs to be.

2.1.6. Interceptors

All targets must implement an interceptor pattern for handling the various API key, OAuth and other header and query string mangling operations that affect all endpoints.

The basic principle is that in your language, it should be possible to provide the necessary hooks to modify the headers and query parameters to support authentication and other requirements for a given API.

Where possible, it is considered highly useful to be able to inject a logging interceptor, though with some implementations this may be difficult if not impossible. Try to design your target so that this is achievable.

2.1.7. Formatters and Types

Targets should—but are not required to—provide a mechanism for handling user-defined formats and parsing them to correct types. This is distinct from actually implementing those formats however. It should be sufficient to provide as part of the service constructors to provide parsing hook functions keyed by the type and format arguments.

In Kotlin, the @Format annotation is used for this purpose and provides a hook. The Gson library can then be provided type handler implementations in the constructor in order to make parsing those formats to the correct data types possible.

Consider for example an object of type string with a format uint64. It is reasonable for this type to therefore be generated to Long and using the @Format("int64") annotation, causing the string to be correctly parsed into the most correct datatype.

2.1.8. Configuration

openapi-eller supports the direct manipulation of targets using a provided config file.

Some keys are reserved for use by the generator itself (such as fieldRenames) but may be extended with custom keys for use by targets and are accessible within the templates via the config key. Examples of this in use include the ECMAScript target, where different module export styles are used whether or not es6 or commonjs is set to true.

At this stage, configuration is quite rudimentary. There is no support for disjoint values, and no automatically derived variables. This is an area of development going forward, and suggestions are highly welcomed.

2.1.9. Dependency policy

Until 1.0.0, this policy does not apply. Experiment at will!

All dependencies must be permissively licensed. Once a target is accepted into the tree, dependencies may not be changed except for minor version changes, except in cases of major bugs. Only in major versions will this requirement be relaxed.

3. Installation

4. Quickstart

5. Targets

All targets support a certain amount of customization of the end result. Those customizations are specific per target and are specified in the config file you can pass to the generator.

Configuration files are JSON or YAML files matching the relevant key-value pairs.

5.1. Kotlin

5.1.2. Configuration options

  • package (string): when available name the package

  • imports (string[]): a list of imports

5.2. Swift

5.2.2. Configuration options

  • prefix (string): an optional prefix to the names of classes, structs, enums, etc

5.3. TypeScript

5.3.1. Target aliases

  • typescript

  • ts

5.3.2. Configuration options

  • dev (boolean): when true, will generate development imports and functions.