Skip to content

Commit

Permalink
feat: flag operations with non-form request bodies that do not specif…
Browse files Browse the repository at this point in the history
…y a name
  • Loading branch information
dpopp07 committed Apr 29, 2019
1 parent cf64905 commit a229fc0
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 9 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ The supported rules are described below:
| no_array_responses | Flag any operations with a top-level array response. | shared |
| parameter_order | Flag any operations with optional parameters before a required param. | shared |
| no_request_body_content | [Flag any operations with a `requestBody` that does not have a `content` field.][3] | oas3 |
| no_request_body_name | Flag any operations with a non-form `requestBody` that does not have a name set with `x-codegen-request-body-name`. | oas3 |

##### parameters
| Rule | Description | Spec |
Expand Down Expand Up @@ -307,6 +308,7 @@ The default values for each rule are described below.
| Rule | Default |
| --------------------------- | --------|
| no_request_body_content | error |
| no_request_body_name | error |

###### parameters
| Rule | Default |
Expand Down
3 changes: 2 additions & 1 deletion src/.defaultsForValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ const defaults = {
},
'oas3': {
'operations': {
'no_request_body_content': 'error'
'no_request_body_content': 'error',
'no_request_body_name': 'warning'
},
'parameters': {
'no_in_property': 'error',
Expand Down
36 changes: 34 additions & 2 deletions src/plugins/validation/oas3/semantic-validators/operations.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Assertation 1. Request body objects must have a `content` property
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#requestBodyObject

// Assertation 2. Operations with non-form request bodies should set the `x-codegen-request-body-name`
// annotation (for code generation purposes)

const pick = require('lodash/pick');
const each = require('lodash/each');

Expand All @@ -11,6 +14,8 @@ module.exports.validate = function({ resolvedSpec }, config) {

config = config.operations;

const REQUEST_BODY_NAME = 'x-codegen-request-body-name';

// get, head, and delete are not in this list because they are not allowed
// to have request bodies
const allowedOps = ['post', 'put', 'patch', 'options', 'trace'];
Expand All @@ -24,18 +29,45 @@ module.exports.validate = function({ resolvedSpec }, config) {
// Assertation 1
if (op.requestBody) {
const requestBodyContent = op.requestBody.content;
if (!requestBodyContent || !Object.keys(requestBodyContent).length) {
const requestBodyMimeTypes =
op.requestBody.content && Object.keys(requestBodyContent);
if (!requestBodyContent || !requestBodyMimeTypes.length) {
const checkStatus = config.no_request_body_content;
if (checkStatus !== 'off') {
result.error.push({
result[checkStatus].push({
path: `paths.${pathName}.${opName}.requestBody`,
message: 'Request bodies MUST specify a `content` property'
});
}
} else {
// request body has content
const firstMimeType = requestBodyMimeTypes[0]; // code generation uses the first mime type
const hasRequestBodyName =
op[REQUEST_BODY_NAME] && op[REQUEST_BODY_NAME].trim().length;
if (isBodyParameter(firstMimeType) && !hasRequestBodyName) {
const checkStatus = config.no_request_body_name;
if (checkStatus != 'off') {
const message =
'Operations with non-form request bodies should set a name with the x-codegen-request-body-name annotation.';
result[checkStatus].push({
path: `paths.${pathName}.${opName}`,
message
});
}
}
}
}
});
});

return { errors: result.error, warnings: result.warning };
};

function isBodyParameter(mimeType) {
const formDataMimeTypes = [
'multipart/form-data',
'application/x-www-form-urlencoded',
'application/octet-stream'
];
return !formDataMimeTypes.includes(mimeType);
}
99 changes: 93 additions & 6 deletions test/plugins/validation/oas3/operations.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@ const expect = require('expect');
const {
validate
} = require('../../../../src/plugins/validation/oas3/semantic-validators/operations');
const config = require('../../../../src/.defaultsForValidator').defaults.oas3;

describe('validation plugin - semantic - operations - oas3', function() {
it('should complain about a request body not having a content field', function() {
const config = {
operations: {
no_request_body_content: 'error'
}
};

const spec = {
paths: {
'/pets': {
Expand All @@ -33,4 +28,96 @@ describe('validation plugin - semantic - operations - oas3', function() {
);
expect(res.warnings.length).toEqual(0);
});

it('should warn about an operation with a non-form request body that does not set a name', function() {
const spec = {
paths: {
'/pets': {
post: {
summary: 'this is a summary',
operationId: 'operationId',
requestBody: {
description: 'body for request',
content: {
'application/json': {
schema: {
type: 'string'
}
}
}
}
}
}
}
};

const res = validate({ resolvedSpec: spec }, config);
expect(res.warnings.length).toEqual(1);
expect(res.warnings[0].path).toEqual('paths./pets.post');
expect(res.warnings[0].message).toEqual(
'Operations with non-form request bodies should set a name with the x-codegen-request-body-name annotation.'
);
expect(res.errors.length).toEqual(0);
});

it('should not warn about an operation with a non-form request body that sets a name', function() {
const spec = {
paths: {
'/pets': {
post: {
'x-codegen-request-body-name': 'goodRequestBody',
summary: 'this is a summary',
operationId: 'operationId',
requestBody: {
description: 'body for request',
content: {
'application/json': {
schema: {
type: 'string'
}
}
}
}
}
}
}
};

const res = validate({ resolvedSpec: spec }, config);
expect(res.warnings.length).toEqual(0);
expect(res.errors.length).toEqual(0);
});

// should not warn about a form request body
it('should not warn about an operation with a form request body that does not set a name', function() {
const spec = {
paths: {
'/pets': {
post: {
summary: 'this is a summary',
operationId: 'operationId',
requestBody: {
description: 'body for request',
content: {
'multipart/form-data': {
schema: {
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
}
}
}
}
}
}
};

const res = validate({ resolvedSpec: spec }, config);
expect(res.warnings.length).toEqual(0);
expect(res.errors.length).toEqual(0);
});
});

0 comments on commit a229fc0

Please sign in to comment.