Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | 29x 29x 29x 29x 69x 69x 69x 69x 3581x 125x 125x 125x 125x 125x 123x 225x 1x 224x 1x 123x 13x 110x 2x 110x 116x 9x 107x 1x 69x 125x 299x 225x 125x 299x 299x 125x 299x 299x 96x 101x 86x 86x 86x 6x | // Assertation 1:
// The Responses Object MUST contain at least one response code
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#responsesObject
// Assertation 2:
// At least one response "SHOULD be the response for a successful operation call"
// Assertation 3:
// A 204 response MUST not define a response body
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5
// Assertation 4:
// A non-204 success response should define a response body
// Assertation 5. Response bodies with application/json content should not use schema
// type: string, format: binary.
const { walk, isResponseObject } = require('../../../utils');
const MessageCarrier = require('../../../utils/messageCarrier');
const findOctetSequencePaths = require('../../../utils/findOctetSequencePaths')
.findOctetSequencePaths;
module.exports.validate = function({ resolvedSpec }, config) {
const messages = new MessageCarrier();
const configSchemas = config.schemas;
config = config.responses;
walk(resolvedSpec, [], function(obj, path) {
// because we are using the resolved spec here,
// and only want to validate the responses inside of operations,
// check that we are within the `paths` object
if (isResponseObject(path) && path[0] === 'paths') {
const [statusCodes, successCodes] = getResponseCodes(obj);
const binaryStringStatus = configSchemas.json_or_param_binary_string;
Eif (binaryStringStatus !== 'off') {
validateNoBinaryStringsInResponse(
obj,
messages,
path,
binaryStringStatus
);
}
if (statusCodes.length) {
for (const statusCode of statusCodes) {
// default warnings for discouraged status code per IBM API Handbook
if (statusCode === '422') {
messages.addMessage(
path.concat(['422']),
'Should use status code 400 instead of 422 for invalid request payloads.',
config.ibm_status_code_guidelines,
'ibm_status_code_guidelines'
);
} else if (statusCode === '302') {
messages.addMessage(
path.concat(['302']),
'Should use status codes 303 or 307 instead of 302.',
config.ibm_status_code_guidelines,
'ibm_status_code_guidelines'
);
}
}
// validate all success codes
if (!successCodes.length && !('101' in obj)) {
messages.addMessage(
path,
'Each `responses` object SHOULD have at least one code for a successful response.',
config.no_success_response_codes,
'no_success_response_codes'
);
} else {
if (successCodes.length && '101' in obj) {
messages.addMessage(
path,
'A `responses` object MUST NOT support 101 and any success (2xx) code.',
config.protocol_switching_and_success_code,
'protocol_switching_and_success_code'
);
}
for (const statusCode of successCodes) {
if (statusCode !== '204' && !obj[statusCode].content) {
messages.addMessage(
path.concat([statusCode]),
`A ${statusCode} response should include a response body. Use 204 for responses without content.`,
config.no_response_body,
'no_response_body'
);
} else if (statusCode === '204' && obj[statusCode].content) {
messages.addMessage(
path.concat(['204', 'content']),
`A 204 response MUST NOT include a message-body.`,
'error'
);
}
}
}
}
}
});
return messages;
};
function getResponseCodes(responseObj) {
const statusCodes = Object.keys(responseObj).filter(code =>
isStatusCode(code)
);
const successCodes = statusCodes.filter(code => code.slice(0, 1) === '2');
return [statusCodes, successCodes];
}
function isStatusCode(code) {
const allowedFirstDigits = ['1', '2', '3', '4', '5'];
return code.length === 3 && allowedFirstDigits.includes(code.slice(0, 1));
}
function validateNoBinaryStringsInResponse(
responseObj,
messages,
path,
binaryStringStatus
) {
Object.keys(responseObj).forEach(function(responseCode) {
const responseBodyContent = responseObj[responseCode].content;
if (responseBodyContent) {
Object.keys(responseBodyContent).forEach(function(mimeType) {
if (mimeType === 'application/json') {
const schemaPath = path.concat([
responseCode,
'content',
mimeType,
'schema'
]);
const octetSequencePaths = findOctetSequencePaths(
responseBodyContent[mimeType].schema,
schemaPath
);
for (const p of octetSequencePaths) {
messages.addMessage(
p,
'JSON request/response bodies should not contain binary (type: string, format: binary) values.',
binaryStringStatus,
'json_or_param_binary_string'
);
}
}
});
}
});
}
|