Skip to content

Commit

Permalink
fix(build): add support for x-mock-file property in the build command
Browse files Browse the repository at this point in the history
  • Loading branch information
uglow committed Sep 15, 2020
1 parent bb5a656 commit 5389b55
Show file tree
Hide file tree
Showing 17 changed files with 236 additions and 151 deletions.
74 changes: 69 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![Coverage Status][coveralls-image]][coveralls-url]
[![Downloads][downloads-image]][npm-url]

Open API Spec tool for recording, linting & comparing API responses; building and deploying a spec to AWS API Gateway.
**O**pen **A**PI-spec **A**WS **t**ool for recording, linting & comparing API responses; building and deploying a spec to AWS API Gateway.

## Table of Contents

Expand Down Expand Up @@ -70,7 +70,7 @@ 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 object, with each child-property being the name of an example. There must be at-least one
`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:
Expand Down Expand Up @@ -284,6 +284,61 @@ Options:
-h, --help display help for command
```

### Mocking

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.

#### `path.method.responses.statusCode["x-mock-file"]`

`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).

##### Example

```json
"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"
}
}
}
}
}
}
```


## Comparing

Compares the earlier-recorded responses to the last responses for endpoints in an OpenAPI 3.x spec file.
Expand Down Expand Up @@ -325,7 +380,7 @@ module.exports = {
record: {

// Path to a subdirectory (relative to the spec file) that contains the response files
// If you do not wish to put mock files into a subdirectory, removeUnsedResponses is
// 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/',

Expand Down Expand Up @@ -373,14 +428,23 @@ module.exports = {
syncExamples: true
},

// Configuration for the `apig` command
apig: {
// Configuration for the `build` command
build: {

// The path that will server the Swagger UI
specUIEndpoint: '/',

// The path that will serve the spec itself
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',
},
};
```
Expand Down
3 changes: 3 additions & 0 deletions fixtures/lintExamples.json
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@
},
"components": {
"schemas": {
"Info": {
"type": "object"
},
"Post": {
"type": "object",
"properties": {
Expand Down
39 changes: 1 addition & 38 deletions fixtures/output/buildMock1.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,6 @@
}
}
}
},
"x-examples": {
"default": {
"parameters": [
{
"value": null
},
{
"value": null
}
]
}
}
}
},
Expand Down Expand Up @@ -112,22 +100,6 @@
"$ref": "#/components/schemas/Post"
}
}
},
"x-examples": {
"id_1": {
"parameters": [
{
"value": "1"
}
]
},
"id_2": {
"parameters": [
{
"value": "2"
}
]
}
}
}
},
Expand Down Expand Up @@ -174,15 +146,6 @@
"$ref": "#/components/schemas/NotFoundError"
}
}
},
"x-examples": {
"badParam": {
"parameters": [
{
"value": "wrong-param"
}
]
}
}
}
},
Expand Down Expand Up @@ -246,7 +209,7 @@
"default": {
"statusCode": "200",
"responseTemplates": {
"application/json": "{\"openapi\":\"3.0.0\",\"info\":{\"description\":\"Fake Online REST API for Testing and Prototyping\",\"version\":\"1.0.0\",\"title\":\"JSON Placeholder\"},\"tags\":[{\"name\":\"posts\"}],\"servers\":[{\"url\":\"https://jsonplaceholder.typicode.com\"}],\"paths\":{\"/posts\":{\"get\":{\"tags\":[\"posts\"],\"summary\":\"Get all available posts\",\"parameters\":[{\"name\":\"id\",\"in\":\"query\",\"description\":\"Filter by post ID\",\"required\":false,\"schema\":{\"type\":\"integer\"}},{\"name\":\"userId\",\"in\":\"query\",\"description\":\"Filter by user ID\",\"required\":false,\"schema\":{\"type\":\"integer\"}}],\"responses\":{\"200\":{\"description\":\"successful operation\",\"content\":{\"application/json\":{\"schema\":{\"type\":\"array\",\"items\":{\"\\$ref\":\"#/components/schemas/Post\"}}}},\"x-examples\":{\"default\":{\"parameters\":[{\"value\":null},{\"value\":null}]}}}}}},\"/posts/{id}\":{\"get\":{\"tags\":[\"posts\"],\"summary\":\"Get specific post\",\"parameters\":[{\"name\":\"id\",\"in\":\"path\",\"description\":\"The ID of the post to retrieve\",\"required\":true,\"schema\":{\"type\":\"integer\"}}],\"responses\":{\"200\":{\"description\":\"successful operation\",\"content\":{\"application/json\":{\"schema\":{\"\\$ref\":\"#/components/schemas/Post\"}}},\"x-examples\":{\"id_1\":{\"parameters\":[{\"value\":\"1\"}]},\"id_2\":{\"parameters\":[{\"value\":\"2\"}]}}}}}},\"/posts/{id}/404\":{\"get\":{\"tags\":[\"posts\"],\"summary\":\"Get specific post\",\"parameters\":[{\"name\":\"id\",\"in\":\"path\",\"description\":\"The ID of the post to retrieve\",\"required\":true,\"schema\":{\"type\":\"integer\"}}],\"responses\":{\"404\":{\"description\":\"not found\",\"content\":{\"application/json\":{\"schema\":{\"\\$ref\":\"#/components/schemas/NotFoundError\"}}},\"x-examples\":{\"badParam\":{\"parameters\":[{\"value\":\"wrong-param\"}]}}}}}}},\"components\":{\"schemas\":{\"Post\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"userId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"},\"body\":{\"type\":\"string\"}}},\"Comment\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"postId\":{\"type\":\"integer\",\"format\":\"int64\"},\"name\":{\"type\":\"string\"},\"email\":{\"type\":\"string\",\"format\":\"email\"},\"body\":{\"type\":\"string\"}}},\"Album\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"userId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"}}},\"Photo\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"albumId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"},\"url\":{\"type\":\"string\",\"format\":\"uri\"},\"thumbnailUrl\":{\"type\":\"string\",\"format\":\"uri\"}}},\"Todo\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"userId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"},\"completed\":{\"type\":\"boolean\"}}},\"User\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"name\":{\"type\":\"string\"},\"username\":{\"type\":\"string\"},\"email\":{\"type\":\"string\",\"format\":\"email\"},\"phone\":{\"type\":\"string\"},\"website\":{\"type\":\"string\"},\"company\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"},\"catchPhrase\":{\"type\":\"string\"},\"bs\":{\"type\":\"string\"}}},\"address\":{\"type\":\"object\",\"properties\":{\"street\":{\"type\":\"string\"},\"suite\":{\"type\":\"string\"},\"city\":{\"type\":\"string\"},\"zipcode\":{\"type\":\"string\"},\"geo\":{\"type\":\"object\",\"properties\":{\"lat\":{\"type\":\"string\"},\"lng\":{\"type\":\"string\"}}}}}}},\"NotFoundError\":{\"type\":\"object\"},\"Empty\":{\"type\":\"object\",\"title\":\"Empty response model for AWS + CORS use\"},\"StringResponse\":{\"type\":\"string\",\"title\":\"String-type workaround for API Gateway\"}}}}"
"application/json": "{\"openapi\":\"3.0.0\",\"info\":{\"description\":\"Fake Online REST API for Testing and Prototyping\",\"version\":\"1.0.0\",\"title\":\"JSON Placeholder\"},\"tags\":[{\"name\":\"posts\"}],\"servers\":[{\"url\":\"https://jsonplaceholder.typicode.com\"}],\"paths\":{\"/posts\":{\"get\":{\"tags\":[\"posts\"],\"summary\":\"Get all available posts\",\"parameters\":[{\"name\":\"id\",\"in\":\"query\",\"description\":\"Filter by post ID\",\"required\":false,\"schema\":{\"type\":\"integer\"}},{\"name\":\"userId\",\"in\":\"query\",\"description\":\"Filter by user ID\",\"required\":false,\"schema\":{\"type\":\"integer\"}}],\"responses\":{\"200\":{\"description\":\"successful operation\",\"content\":{\"application/json\":{\"schema\":{\"type\":\"array\",\"items\":{\"\\$ref\":\"#/components/schemas/Post\"}}}}}}}},\"/posts/{id}\":{\"get\":{\"tags\":[\"posts\"],\"summary\":\"Get specific post\",\"parameters\":[{\"name\":\"id\",\"in\":\"path\",\"description\":\"The ID of the post to retrieve\",\"required\":true,\"schema\":{\"type\":\"integer\"}}],\"responses\":{\"200\":{\"description\":\"successful operation\",\"content\":{\"application/json\":{\"schema\":{\"\\$ref\":\"#/components/schemas/Post\"}}}}}}},\"/posts/{id}/404\":{\"get\":{\"tags\":[\"posts\"],\"summary\":\"Get specific post\",\"parameters\":[{\"name\":\"id\",\"in\":\"path\",\"description\":\"The ID of the post to retrieve\",\"required\":true,\"schema\":{\"type\":\"integer\"}}],\"responses\":{\"404\":{\"description\":\"not found\",\"content\":{\"application/json\":{\"schema\":{\"\\$ref\":\"#/components/schemas/NotFoundError\"}}}}}}}},\"components\":{\"schemas\":{\"Post\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"userId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"},\"body\":{\"type\":\"string\"}}},\"Comment\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"postId\":{\"type\":\"integer\",\"format\":\"int64\"},\"name\":{\"type\":\"string\"},\"email\":{\"type\":\"string\",\"format\":\"email\"},\"body\":{\"type\":\"string\"}}},\"Album\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"userId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"}}},\"Photo\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"albumId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"},\"url\":{\"type\":\"string\",\"format\":\"uri\"},\"thumbnailUrl\":{\"type\":\"string\",\"format\":\"uri\"}}},\"Todo\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"userId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"},\"completed\":{\"type\":\"boolean\"}}},\"User\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"name\":{\"type\":\"string\"},\"username\":{\"type\":\"string\"},\"email\":{\"type\":\"string\",\"format\":\"email\"},\"phone\":{\"type\":\"string\"},\"website\":{\"type\":\"string\"},\"company\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"},\"catchPhrase\":{\"type\":\"string\"},\"bs\":{\"type\":\"string\"}}},\"address\":{\"type\":\"object\",\"properties\":{\"street\":{\"type\":\"string\"},\"suite\":{\"type\":\"string\"},\"city\":{\"type\":\"string\"},\"zipcode\":{\"type\":\"string\"},\"geo\":{\"type\":\"object\",\"properties\":{\"lat\":{\"type\":\"string\"},\"lng\":{\"type\":\"string\"}}}}}}},\"NotFoundError\":{\"type\":\"object\"},\"Empty\":{\"type\":\"object\",\"title\":\"Empty response model for AWS + CORS use\"},\"StringResponse\":{\"type\":\"string\",\"title\":\"String-type workaround for API Gateway\"}}}}"
},
"responseParameters": {
"method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS'",
Expand Down
18 changes: 1 addition & 17 deletions fixtures/output/buildMock2.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,6 @@
"$ref": "#/components/schemas/Post"
}
}
},
"x-examples": {
"id_1": {
"parameters": [
{
"value": "1"
}
]
},
"id_2": {
"parameters": [
{
"value": "2"
}
]
}
}
}
},
Expand Down Expand Up @@ -121,7 +105,7 @@
"default": {
"statusCode": "200",
"responseTemplates": {
"application/json": "{\"openapi\":\"3.0.0\",\"info\":{\"description\":\"Fake Online REST API for Testing and Prototyping\",\"version\":\"1.0.0\",\"title\":\"JSON Placeholder\"},\"tags\":[{\"name\":\"posts\"}],\"servers\":[{\"url\":\"https://jsonplaceholder.typicode.com\"}],\"paths\":{\"/posts/{id}\":{\"get\":{\"tags\":[\"posts\"],\"summary\":\"Get specific post\",\"parameters\":[{\"name\":\"id\",\"in\":\"path\",\"description\":\"The ID of the post to retrieve\",\"required\":true,\"schema\":{\"type\":\"integer\"}}],\"responses\":{\"200\":{\"description\":\"successful operation\",\"content\":{\"application/json\":{\"schema\":{\"\\$ref\":\"#/components/schemas/Post\"}}},\"x-examples\":{\"id_1\":{\"parameters\":[{\"value\":\"1\"}]},\"id_2\":{\"parameters\":[{\"value\":\"2\"}]}}}}}}},\"components\":{\"schemas\":{\"Post\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"userId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"},\"body\":{\"type\":\"string\"}}},\"Comment\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"postId\":{\"type\":\"integer\",\"format\":\"int64\"},\"name\":{\"type\":\"string\"},\"email\":{\"type\":\"string\",\"format\":\"email\"},\"body\":{\"type\":\"string\"}}},\"Album\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"userId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"}}},\"Photo\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"albumId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"},\"url\":{\"type\":\"string\",\"format\":\"uri\"},\"thumbnailUrl\":{\"type\":\"string\",\"format\":\"uri\"}}},\"Todo\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"userId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"},\"completed\":{\"type\":\"boolean\"}}},\"User\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"name\":{\"type\":\"string\"},\"username\":{\"type\":\"string\"},\"email\":{\"type\":\"string\",\"format\":\"email\"},\"phone\":{\"type\":\"string\"},\"website\":{\"type\":\"string\"},\"company\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"},\"catchPhrase\":{\"type\":\"string\"},\"bs\":{\"type\":\"string\"}}},\"address\":{\"type\":\"object\",\"properties\":{\"street\":{\"type\":\"string\"},\"suite\":{\"type\":\"string\"},\"city\":{\"type\":\"string\"},\"zipcode\":{\"type\":\"string\"},\"geo\":{\"type\":\"object\",\"properties\":{\"lat\":{\"type\":\"string\"},\"lng\":{\"type\":\"string\"}}}}}}},\"NotFoundError\":{\"type\":\"object\"},\"Empty\":{\"type\":\"object\",\"title\":\"Empty response model for AWS + CORS use\"},\"StringResponse\":{\"type\":\"string\",\"title\":\"String-type workaround for API Gateway\"}}}}"
"application/json": "{\"openapi\":\"3.0.0\",\"info\":{\"description\":\"Fake Online REST API for Testing and Prototyping\",\"version\":\"1.0.0\",\"title\":\"JSON Placeholder\"},\"tags\":[{\"name\":\"posts\"}],\"servers\":[{\"url\":\"https://jsonplaceholder.typicode.com\"}],\"paths\":{\"/posts/{id}\":{\"get\":{\"tags\":[\"posts\"],\"summary\":\"Get specific post\",\"parameters\":[{\"name\":\"id\",\"in\":\"path\",\"description\":\"The ID of the post to retrieve\",\"required\":true,\"schema\":{\"type\":\"integer\"}}],\"responses\":{\"200\":{\"description\":\"successful operation\",\"content\":{\"application/json\":{\"schema\":{\"\\$ref\":\"#/components/schemas/Post\"}}}}}}}},\"components\":{\"schemas\":{\"Post\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"userId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"},\"body\":{\"type\":\"string\"}}},\"Comment\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"postId\":{\"type\":\"integer\",\"format\":\"int64\"},\"name\":{\"type\":\"string\"},\"email\":{\"type\":\"string\",\"format\":\"email\"},\"body\":{\"type\":\"string\"}}},\"Album\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"userId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"}}},\"Photo\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"albumId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"},\"url\":{\"type\":\"string\",\"format\":\"uri\"},\"thumbnailUrl\":{\"type\":\"string\",\"format\":\"uri\"}}},\"Todo\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"userId\":{\"type\":\"integer\",\"format\":\"int64\"},\"title\":{\"type\":\"string\"},\"completed\":{\"type\":\"boolean\"}}},\"User\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int64\"},\"name\":{\"type\":\"string\"},\"username\":{\"type\":\"string\"},\"email\":{\"type\":\"string\",\"format\":\"email\"},\"phone\":{\"type\":\"string\"},\"website\":{\"type\":\"string\"},\"company\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"},\"catchPhrase\":{\"type\":\"string\"},\"bs\":{\"type\":\"string\"}}},\"address\":{\"type\":\"object\",\"properties\":{\"street\":{\"type\":\"string\"},\"suite\":{\"type\":\"string\"},\"city\":{\"type\":\"string\"},\"zipcode\":{\"type\":\"string\"},\"geo\":{\"type\":\"object\",\"properties\":{\"lat\":{\"type\":\"string\"},\"lng\":{\"type\":\"string\"}}}}}}},\"NotFoundError\":{\"type\":\"object\"},\"Empty\":{\"type\":\"object\",\"title\":\"Empty response model for AWS + CORS use\"},\"StringResponse\":{\"type\":\"string\",\"title\":\"String-type workaround for API Gateway\"}}}}"
},
"responseParameters": {
"method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS'",
Expand Down
3 changes: 3 additions & 0 deletions fixtures/output/lint2.json
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@
}
}
},
"Info": {
"type": "object"
},
"NotFoundError": {
"type": "object"
},
Expand Down
Loading

0 comments on commit 5389b55

Please sign in to comment.