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 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | 29x 29x 29x 29x 40x 40x 9057x 9057x 5337x 3720x 117x 117x 117x 117x 3603x 1x 3603x 3603x 9017x 4x 2x 118x 119x 118x 2x 2x 2x 2x 117x 118x 117x 113x 4x 4x 4x 4x 2x 2x 4x 2x 2x 4x 40x 40x | // Assertions are in the following order ( bailing as soon as we hit the first assertion ) // Assertation typo // If a paramter with `in: formdata` exists, warn about typo ( it should be formData ) // Assertation 1 // If a paramter with `in: formData` exists, a param with `in: body` cannot // Assertation 2: // If a parameter with `type: file` exists // - It must have `in: formData` // - The consumes property must have `multipart/form-data` // Assertation 3: // If a parameter with `in: formData` exists a consumes property ( inherited or inline ) my contain `application/x-www-form-urlencoded` or `multipart/form-data` // Assertation 3 checked with Spectral rule oas2-operation-formData-consume-check const isPlainObject = require('lodash/isPlainObject'); const getIn = require('lodash/get'); const MessageCarrier = require('../../../utils/messageCarrier'); module.exports.validate = function({ resolvedSpec }) { const messages = new MessageCarrier(); Iif (!isPlainObject(resolvedSpec)) { return; } // Looking for... // Parameters ( /paths/{method}/parameters or /parameters) // - in: formData // - type: file function walk(obj, path) { path = path || []; if (typeof obj !== 'object' || obj === null) { return; } // Inside a parameter array ( under an operation or pathitem ) // NOTE: What if we want to add a body, with multipart/form-data? Not possible right? if ( (path[0] === 'paths' || path[0] === 'pathitems') && path[path.length - 1] === 'parameters' && Array.isArray(obj) ) { const opPath = path.slice(0, path.length - 1); const opItem = getIn(resolvedSpec, opPath); // Check for formdata ( typos ) assertationTypo(obj, path); return ( // assertationOne(obj, path) assertationTwo(obj, path, opItem) ); } // Parameters under the root `/parameters` property if (path[0] === 'parameters' && path.length === 2 && Array.isArray(obj)) { // Check for formdata ( typos ) assertationTypo(obj, path); // return assertationOne(obj, path) } // Continue to walk the object tree const keys = Object.keys(obj); Eif (keys) { return keys.map(k => walk(obj[k], [...path, k])); } else { return null; } } // Checks the operation for the presences of a consumes function hasConsumes(operation, consumes) { return ( isPlainObject(operation) && Array.isArray(operation.consumes) && operation.consumes.some(c => c === consumes) ); } // Warn about a typo, formdata => formData function assertationTypo(params, path) { const formDataWithTypos = params.filter( p => isPlainObject(p) && p['in'] === 'formdata' ); if (formDataWithTypos.length) { params.forEach((param, i) => { Iif (param['in'] !== 'formdata') { return; } messages.addMessage( `${path.join('.')}.${i}`, 'The form data value for `in` must be camelCase (formData)', 'error' ); }); return; } } // If a paramter with `in: formData` exists, a param with `in: body` cannot // eslint-disable-next-line no-unused-vars function assertationOne(params, path) { // Assertion 1 const inBodyIndex = params.findIndex( p => isPlainObject(p) && p['in'] === 'body' ); const formData = params.filter( p => isPlainObject(p) && p['in'] === 'formData' ); const hasFormData = !!formData.length; if (~inBodyIndex && hasFormData) { // We"ll blame the `in: body` parameter const pathStr = `${path.join('.')}.${inBodyIndex}`; messages.addMessage( pathStr, 'Parameters cannot have `in` values of both "body" and "formData", as "formData" _will_ be the body', 'error' ); return; } } // If a parameter with `type: file` exists // - a. It must have `in: formData` // - b. The consumes property must have `multipart/form-data` function assertationTwo(params, path, operation) { const typeFileIndex = params.findIndex( p => isPlainObject(p) && p.type === 'file' ); // No type: file? if (!~typeFileIndex) { return; } let hasErrors = false; const param = params[typeFileIndex]; const pathStr = [...path, typeFileIndex].join('.'); // a - must have formData if (param['in'] !== 'formData') { messages.addMessage( pathStr, 'Parameters with `type` "file" must have `in` be "formData"', 'error' ); hasErrors = true; } // - b. The consumes property must have `multipart/form-data` if (!hasConsumes(operation, 'multipart/form-data')) { messages.addMessage( pathStr, 'Operations with Parameters of `type` "file" must include "multipart/form-data" in their "consumes" property', 'error' ); hasErrors = true; } return hasErrors; } walk(resolvedSpec, []); return messages; }; |