Open API AWS Tool
Open API-spec AWS tool for recording, linting & comparing API responses; building and deploying a spec to AWS API Gateway.
- Installation
- Usage
- Recording
- Linting
- Building
- Comparing
- Validating
- Deploying (Coming soon)
- Config file
npm install -g oaat
# Display help
oaat --help
Note: Node 12.3 or higher runtime required.
This tool does 5 things:
oaat record
records API responses to requests specified inx-examples
fields in an OpenAPI 3.x spec file.oaat lint
lints an OpenAPI 3.x spec file (basic formatting; tools like speccy provide more capability, but don't do formatting).oaat build
creates an OpenAPI 3.x spec file with API Gateway headers, optionally with mock responses for the APIs.oaat compare
compares the earlier-recorded responses to the last responses for endpoints in an OpenAPI 3.x spec file.oaat validate
validates that a spec-file is compliant with the OpenAPI 3.x specification.- (Coming soon)
oaat deploy
deploys an OpenAPI 3.x spec file that has the API Gateway headers to API Gateway.
This tool provides the capability to record responses by making requests to the real API endpoints (oaat record
),
and optionally use them as mock responses later (oaat deploy
),
by reading an Open API spec file (v3.x) in JSON format.
$ oaat record --help
Usage: oaat record [options] <jsonFile> [serverUrl]
Record the responses of API spec file endpoint requests (optionally use a different server to make requests)
Options:
-o, --output <file> Output file (if different to jsonFile)
-c, --config <file> Config file to override default config
-s, --sec-tokens <key=val,> Pass security token(s) matching the "key" in spec.securitySchemes, with a "value"
-q, --quiet No logging
-v, --verbose Verbose logging
-d, --dry-run Dry run (no changes made)
-h, --help display help for command
To get valid example responses - to use for mocking & as documentation - we need to add some custom properties to the OpenAPI spec file.
The x-examples
object is a custom field that allows for the description of multiple examples
of inputs, and stores the corresponding response (for use in mocks and testing).
x-examples
is an object, with each child-property being the name of an example. There must be at-least one
child-property example-name for x-examples
.
Each example-object can have the following properties:
parameters
- optionalrequestBody
- optionalresponseFile
- generated when recording a response
This property is required whenever an API has a non-empty path.method.parameters
array.
The elements in x-examples[example-name].parameters
correspond to the elements in parameters
.
The order of each parameters
object is significant.
Each parameters
object has either a value
or script
property:
value
can be any data type (string, number, array, object or null)script
is a reference to a JavaScript file. The JavaScript file must export a function which returns a value which can be used in the correspondingparameters
argument. The function can be asynchronous. This is useful for situations where an endpoint relies on the result of another endpoint.
This property is required whenever an API has a non-empty path.method.requestBody
property.
The requestBody
object has either a value
or script
property - same as the x-examples[exampleName].parameters
(above).
The responseFile
property points to a mock-response file.
When an API has no parameters, the responseFile
property is generated for the 200 response automatically.
For all other response codes, the responseFile
property will need to be added
manually, along with the mock file for that response.
"paths": {
"/posts/{id}": {
"post": {
"tags": ["posts"],
"summary": "Get specific post",
"parameters": [
{
"name": "id",
"in": "path",
"description": "The ID of the post to retrieve",
"required": true,
"schema": {
"type": "integer"
}
}
],
"requestBody": {
"description": "Optional description in *Markdown*",
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"responses": {
"200": {
"description": "successful operation",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/Post"
}
}
},
"x-examples": {
"id_1": {
"parameters": [{ "value": "1" }],
"requestBody": { "script": "scripts/getIdFromDatabase.js" }
}
}
}
}
}
}
}
In the above example, the POST /posts/{id}
endpoint requires two parameters - a
path
parameter ({id}
) and a requestBody
parameter. The corresponding
x-examples[exampleName]
object has two properties: parameters
, which uses the value
property to specify the id
parameter; the requestBody
property uses the script
property to specify
a JS file which will produce the value for the requestBody
parameter.
JS script example:
const queueWrapper = require('../src/queueWrapper');
/**
* Returns the post data
* @param {string} serverUrl
* @param {string} path E.g. "/post/{id}"
* @return {Promise<*>}
*/
function doAsyncThing({ serverUrl }) {
return new Promise(resolve => {
setTimeout(() => resolve('async value 1'), 100);
});
}
module.exports = queueWrapper(doAsyncThing);
The above JavaScript module exports an async function which returns an object asynchronously.
The queueWrapper()
function is there to combine multiple requests into a single request, in scenarios where
multiple endpoint-examples require the same async-value.
APIs are often protected with security tokens and headers. To call these APIs, the security token can
be passed to the command line via -s securityHeaderType=123someValue,nextToken=nextValue,...
. Alternatively,
the security token values can be specified in the securitySchemes
section of configuration file.
By default, securitySchemes
contains no keys.
oaat
will always pick the first security scheme when there are multiple security schemes available for an endpoint.
Sometimes it may be necessary to disable the recording of certain endpoints, while keeping the endpoint in the spec.
Add the field paths[path][method].x-ignore
with a value of true
to disable the recording (and comparing) of this endpoint.
Add the field paths[path][method].responses[status].x-ignore
with a value of true
to disable the recording (and comparing) of this endpoint response.
The x-test-ignore-paths
property is an array of paths (in Lodash path format (see example)) to be ignored.
For example, the foo.correlationId
property may change in every request. When comparing a new request
against the mock-file, the objects will never match. To overcome this, we can use the x-test-ignore-paths
property to ignore this field when comparing the response to the mock-file:
"paths": {
"/foo/bar": {
"get": {
"summary": "Get foo's bars",
"produces": ["application/json"],
"parameters": [],
"responses": {
"200": {
"schema": {
"$ref": "#/definitions/Cart"
},
"x-test-ignore-paths": [
"foo.correlationId"
]
}
}
}
}
}
There are lots of good tools that can check the syntax and style of Open API Sepc 3.x files:
oaat
compliments these tools, as it lints things that the others don't:
- sort
paths
alphabetically (true) - sort
components.schemas
alphabetically (true) - copy
x-examples
examples intoparameter.examples
andrequestBody.examples
$ oaat lint --help
Usage: oaat lint [options] <jsonFile> [serverUrl]
Tidy the API Spec up a bit
Options:
-o, --output <file> Output file (if different to jsonFile)
-c, --config <file> Config file to override default config
-s, --sec-tokens <key=val,> Pass security token(s) matching the "key" in spec.securitySchemes, with a "value"
-q, --quiet no logging
-v, --verbose verbose logging
-h, --help display help for command
Note: serverUrl
and sec-tokens
are only required when the config.lint.syncExamples
is true
.
See the configuration file for further options.
Creates an OpenAPI 3.x spec file with API Gateway headers, optionally with mock responses for the APIs.
$ oaat build --help
Usage: oaat build [options] <jsonFile> <outputJsonFile> [serverUrl]
Adds custom headers & Swagger UI endpoint to allow deployment of spec file to AWS API Gateway with documentation
Options:
-c, --config <file> Config file to override default config
-m, --mock Uses the recorded responses as mock responses
-q, --quiet No logging
-v, --verbose Verbose logging
-d, --dry-run Dry run (no changes made)
-h, --help display help for command
API Gateway supports different kinds of integrations. One integration-type is "mock", whereby static responses are returned for any API requests. Mock integrations are useful for testing, documentation, or as a backup for the real API when things go wrong.
To create mock responses, add the x-mock-file
property to each endpoint and specify the -m
flag in the command.
When using the
-m
option, the schema, security and requestBody information is not present in the generated spec file. This is due to limitations within API Gateway's handling of specific Open API Spec 3.x features (removing the schemas makes most of the issues disappear). However, the Swagger UI still loads the original spec file, so that the documentation is correct.
x-mock-file
is either a string
or an object
, associating an endpoint with a response file.
When x-mock-file
is a string
, the value is a path to a response file, and the response-data is used
regardless of the path-parameters supplied in the request.
When x-mock-file
is an object
, the property-key is the API-path that is generated, and the value
is a path to a response file (as above). In this mode, it is possible to generate multiple mock responses
by specifying multiple API paths as property keys (see example below).
"paths": {
"/posts/{id}": {
"post": {
"responses": {
"200": {
"description": "String example",
"x-mock-file": "mock/foo.json"
}
}
}
},
"/users/{id}": {
"post": {
"responses": {
"200": {
"description": "Object example:",
"x-mock-file": {
"/users/alexa": "mocks/alexa.json",
"/users/david": "mocks/david.json"
}
}
}
}
}
}
Compares the earlier-recorded responses to the last responses for endpoints in an OpenAPI 3.x spec file. This command can be used to do integration testing, by treating the recorded responses as snapshots, and comparing those to the latest responses.
Usage: oaat compare [options] <jsonFile> [serverUrl]
Compares recorded responses (referenced by the spec file) to the latest responses
Options:
-c, --config <file> Config file to override default config
-s, --sec-tokens <key=val,> Pass security token(s) matching the "key" in spec.securitySchemes, with a "value"
-m --compare-mode <mode> Comparison mode: "value" (default), "type", "schema"
-q, --quiet no logging
-v, --verbose verbose logging
-h, --help display help for command
This command validates the JSON spec file against the Open API 3.x schema. Any errors are listed in the output.
$ oaat validate --help
Usage: oaat validate [options] <jsonFile>
Validate the API spec file against the OAS 3.x schema
Options:
-h, --help display help for command
oaat has a default configuration which can be overridden using a config file. The config file can be JSON or a CommonJS module which exports an object.
Example:
module.exports = {
// Shared configuration for all commands
global: {
// The number of simultaneous requests HTTP requests to make. Increasing this value
// can lead to inconsistent results.
simultaneousRequests: 15,
},
// Configuration for the `oaat record` command:
record: {
// Path to a subdirectory (relative to the spec file) that contains the response files
// If you do not wish to put response files into a subdirectory, removeUnsedResponses is
// automatically set to false to avoid deleting files from your specfile folder!
responseBasePath: 'responses/',
// After renaming examples and generating new response files, old response files may no longer
// be being used. Set this to true to remove the unused (un-referenced by the spec file) response files.
removeUnusedResponses: true,
// A function to generate the name for each mock file.
// This is the default naming function:
responseFilenameFn(apiData) {
// console.log(apiData); // Print the apiData structure to see what is available
const exampleName =
apiData.exampleIndex === 0
? `DEFAULT${apiData.exampleName === 'default' ? '' : `_${apiData.exampleName}`}`
: apiData.exampleName;
return `${apiData.config.method.toUpperCase()}_${apiData.path.slice(1).replace(/\//g, '_')}-${
apiData.statusCode
}_${exampleName}.json`;
},
// Whether to update the response file when the new response is an inexact match.
// For example, if a date field in the response is being ignored (because it always changes)
// but everything else in the response matches the previous response, should the response
// file be updated?
updateResponseWhenInexactMatch: true,
// Whether to update the response file when the new response's data-types match the
// old response's data types, but the values are different.
updateResponseWhenTypesMatch: true,
// Lint the spec file as well (true)
andLint: true,
},
// Configuration for the `lint` command
lint: {
// Sort the spec file's paths alphabetically (true)
sortPathsAlphabetically: true,
// Sort the spec file's component.schemas alphabetically (true)
sortComponentsAlphabetically: true,
// Updates the parameter and requestBody examples using the x-examples from the 200 response
// Requires a API server to be in the spec or specified in the command line if any paramaters come from scripts
// Requires config.securitySchemes (or `sec-tokens` on the command line) to be specified if any APIs specify the "security" property.
syncExamples: true
},
// Configuration for the `build` command
build: {
// The path that will serve the Swagger UI
specUIEndpoint: '/',
// The path that will serve the original spec itself (not the API Gateway version of the spec)
specFileEndpoint: '/open-api-spec.json',
// The value for the <title> element
webTitle: 'My Company',
// A logo for the website (shown in the top-left corner)
webLogoUrl: 'https://www.elitedangerous.com/img/logo-elite-dangerous-icon.c7206b1e.svg',
// A URI (URL or data:image/x-icon;base64 encode image) for the favicon
webFaviconHref: 'url or data:image/x-icon;base64',
},
// If you wish to record your security tokens here, you may.
securitySchemes: {
schemeName1: { value: 'static value' },
schemeName2: { script: 'path/to/script.js' }
}
};