diff --git a/.travis.yml b/.travis.yml index 665b195..42436b5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,3 +8,4 @@ branches: script: - "npm run cover" - "npm run lint" + - "npm run format-check" diff --git a/lib/caller.js b/lib/caller.js index 8eae627..7614a36 100644 --- a/lib/caller.js +++ b/lib/caller.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; /* * Copy of Erik's caller module with node 8 support added. -*/ + */ /** * Module wrapper of @substack's `caller.js` @@ -23,7 +23,7 @@ const caller = function (depth) { return stack; }; - const stack = (new Error()).stack.slice(2); + const stack = new Error().stack.slice(2); let file; diff --git a/lib/index.js b/lib/index.js index e467278..35ea6a3 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,34 +1,39 @@ -'use strict'; - -const Package = require('../package.json'); -const Joi = require('joi'); -const Hoek = require('@hapi/hoek'); -const Caller = require('./caller'); -const Path = require('path'); -const Parser = require('swagger-parser'); -const Utils = require('./utils'); -const Routes = require('./routes'); -const Yaml = require('js-yaml'); -const Fs = require('fs'); -const Util = require('util'); +"use strict"; + +const Package = require("../package.json"); +const Joi = require("joi"); +const Hoek = require("@hapi/hoek"); +const Caller = require("./caller"); +const Path = require("path"); +const Parser = require("swagger-parser"); +const Utils = require("./utils"); +const Routes = require("./routes"); +const Yaml = require("js-yaml"); +const Fs = require("fs"); +const Util = require("util"); const CALLER_DIR = Path.resolve(Path.dirname(Caller())); const optionsSchema = Joi.object({ api: Joi.alternatives(Joi.string(), Joi.object().unknown(true)), //deprecated - docspath: Joi.string().default('/api-docs'), + docspath: Joi.string().default("/api-docs"), docs: Joi.object({ - path: Joi.string().default('/api-docs'), + path: Joi.string().default("/api-docs"), auth: Joi.alternatives().try(Joi.object(), Joi.boolean()).allow(null), stripExtensions: Joi.boolean().default(true), - prefixBasePath: Joi.boolean().default(true) + prefixBasePath: Joi.boolean().default(true), }).default(), cors: Joi.alternatives().try(Joi.object(), Joi.boolean()).default(true), vhost: Joi.string().allow(null), - handlers: Joi.alternatives().try(Joi.string().default(Path.join(CALLER_DIR, 'routes')), Joi.object()).allow(null), - extensions: Joi.array().items(Joi.string()).default(['js']), - outputvalidation: Joi.boolean().default(false) + handlers: Joi.alternatives() + .try( + Joi.string().default(Path.join(CALLER_DIR, "routes")), + Joi.object() + ) + .allow(null), + extensions: Joi.array().items(Joi.string()).default(["js"]), + outputvalidation: Joi.boolean().default(false), }).required(); const stripVendorExtensions = function (obj) { @@ -61,8 +66,7 @@ const requireApi = function (path) { if (path.match(/\.ya?ml?/)) { const file = Fs.readFileSync(path); document = Yaml.load(file); - } - else { + } else { document = require(path); } @@ -70,24 +74,29 @@ const requireApi = function (path) { }; const register = async function (server, options, next) { - //Validator needs to be explicitly declared for Hapi v19.* - server.validator(require('joi')); + server.validator(require("joi")); const validation = optionsSchema.validate(options); Hoek.assert(!validation.error, validation.error); - const { api, cors, vhost, handlers, extensions, outputvalidation } = validation.value; + const { api, cors, vhost, handlers, extensions, outputvalidation } = + validation.value; let { docs, docspath } = validation.value; const spec = await Parser.validate(api); // Cannot use conflicting url pathnames, so opting to mount the first url pathname if (spec.openapi) { - spec.basePath = new URL(Hoek.reach(spec, ['servers', 0, 'url'])).pathname; + spec.basePath = new URL( + Hoek.reach(spec, ["servers", 0, "url"]) + ).pathname; } - spec.basePath = Utils.unsuffix(Utils.prefix(spec.basePath || '/', '/'), '/'); + spec.basePath = Utils.unsuffix( + Utils.prefix(spec.basePath || "/", "/"), + "/" + ); //Expose plugin api server.expose({ @@ -96,36 +105,37 @@ const register = async function (server, options, next) { }, setHost: function setHost(host) { spec.host = host; - } + }, }); let basedir; let apiDocument; - if (typeof api === 'string') { + if (typeof api === "string") { apiDocument = requireApi(api); basedir = Path.dirname(Path.resolve(api)); - } - else { + } else { apiDocument = api; basedir = CALLER_DIR; } - if (spec['x-hapi-auth-schemes']) { - for (const [name, path] of Object.entries(spec['x-hapi-auth-schemes'])) { + if (spec["x-hapi-auth-schemes"]) { + for (const [name, path] of Object.entries( + spec["x-hapi-auth-schemes"] + )) { const scheme = require(Path.resolve(Path.join(basedir, path))); await server.register({ plugin: scheme, options: { - name - } + name, + }, }); } } let securitySchemes; - + if (spec.swagger && spec.securityDefinitions) { securitySchemes = spec.securityDefinitions; } @@ -136,8 +146,10 @@ const register = async function (server, options, next) { if (securitySchemes) { for (const [name, security] of Object.entries(securitySchemes)) { - if (security['x-hapi-auth-strategy']) { - const strategy = require(Path.resolve(Path.join(basedir, security['x-hapi-auth-strategy']))); + if (security["x-hapi-auth-strategy"]) { + const strategy = require(Path.resolve( + Path.join(basedir, security["x-hapi-auth-strategy"]) + )); await server.register({ plugin: strategy, @@ -145,25 +157,25 @@ const register = async function (server, options, next) { name, scheme: security.type, lookup: security.name, - where: security.in - } + where: security.in, + }, }); } } } - if (docspath !== '/api-docs' && docs.path === '/api-docs') { - server.log(['warn'], 'docspath is deprecated. Use docs instead.'); + if (docspath !== "/api-docs" && docs.path === "/api-docs") { + server.log(["warn"], "docspath is deprecated. Use docs instead."); docs = { path: docspath, - prefixBasePath: docs.prefixBasePath + prefixBasePath: docs.prefixBasePath, }; } let apiPath = docs.path; - if (docs.prefixBasePath){ - docs.path = Utils.prefix(docs.path, '/'); - docs.path = Utils.unsuffix(docs.path, '/'); + if (docs.prefixBasePath) { + docs.path = Utils.prefix(docs.path, "/"); + docs.path = Utils.unsuffix(docs.path, "/"); apiPath = spec.basePath + docs.path; } @@ -173,26 +185,39 @@ const register = async function (server, options, next) { //API docs route server.route({ - method: 'GET', + method: "GET", path: apiPath, config: { handler(request, h) { return apiDocument; }, cors, - id: `${apiPath.replace(/\//g, '_')}`, - description: 'The OpenAPI document.', - tags: ['api', 'documentation'], - auth: docs.auth + id: `${apiPath.replace(/\//g, "_")}`, + description: "The OpenAPI document.", + tags: ["api", "documentation"], + auth: docs.auth, }, - vhost + vhost, }); - const routes = await Routes.create(server, { api: spec, basedir, cors, vhost, handlers, extensions, outputvalidation }); + const routes = await Routes.create(server, { + api: spec, + basedir, + cors, + vhost, + handlers, + extensions, + outputvalidation, + }); for (const route of routes) { server.route(route); } }; -module.exports.plugin = { register, name: 'openapi', version: Package.version, multiple: true }; +module.exports.plugin = { + register, + name: "openapi", + version: Package.version, + multiple: true, +}; diff --git a/lib/routes.js b/lib/routes.js index e4cb2b0..137642b 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -1,33 +1,38 @@ -'use strict'; +"use strict"; -const ObjectFiles = require('merge-object-files'); -const Validators = require('./validators'); -const Hoek = require('@hapi/hoek'); -const Utils = require('./utils'); -const Path = require('path'); -const Props = require('dot-prop'); +const ObjectFiles = require("merge-object-files"); +const Validators = require("./validators"); +const Hoek = require("@hapi/hoek"); +const Utils = require("./utils"); +const Path = require("path"); +const Props = require("dot-prop"); -const SEPARATOR = '/'; +const SEPARATOR = "/"; -const create = async function (server, { api, basedir, cors, vhost, handlers, extensions, outputvalidation }) { +const create = async function ( + server, + { api, basedir, cors, vhost, handlers, extensions, outputvalidation } +) { const routes = []; const validator = Validators.create({ api }); - if (typeof handlers === 'string') { + if (typeof handlers === "string") { handlers = await ObjectFiles.merge(handlers, extensions); } //Support x-hapi-handler when no handlers set. if (!handlers) { for (const [path, operations] of Object.entries(api.paths)) { - if (operations['x-hapi-handler']) { - const pathnames = path.split('/').slice(1).join('.'); + if (operations["x-hapi-handler"]) { + const pathnames = path.split("/").slice(1).join("."); if (!handlers) { handlers = {}; } - const xhandler = require(Path.resolve(Path.join(basedir, operations['x-hapi-handler']))); + const xhandler = require(Path.resolve( + Path.join(basedir, operations["x-hapi-handler"]) + )); Props.set(handlers, pathnames, xhandler); } @@ -35,30 +40,46 @@ const create = async function (server, { api, basedir, cors, vhost, handlers, ex } for (const [path, operations] of Object.entries(api.paths)) { - const pathnames = Utils.unsuffix(path, '/').split('/').slice(1).join(SEPARATOR); + const pathnames = Utils.unsuffix(path, "/") + .split("/") + .slice(1) + .join(SEPARATOR); for (const [method, operation] of Object.entries(operations)) { const pathsearch = `${pathnames}${SEPARATOR}${method}`; - const handler = Hoek.reach(handlers, pathsearch, { separator: SEPARATOR }); - const xoptions = operation['x-hapi-options'] || {}; + const handler = Hoek.reach(handlers, pathsearch, { + separator: SEPARATOR, + }); + const xoptions = operation["x-hapi-options"] || {}; if (!handler) { continue; } const customTags = operation.tags || []; - const options = Object.assign({ - cors, - id: operation.operationId, - // hapi does not support empty descriptions - description: operation.description !== '' ? operation.description : undefined, - tags: ['api', ...customTags] - }, xoptions); + const options = Object.assign( + { + cors, + id: operation.operationId, + // hapi does not support empty descriptions + description: + operation.description !== "" + ? operation.description + : undefined, + tags: ["api", ...customTags], + }, + xoptions + ); options.handler = handler; if (Utils.canCarry(method)) { - options.payload = options.payload ? Hoek.applyToDefaults({ allow: operation.consumes || api.consumes }, options.payload) : { allow: operation.consumes || api.consumes }; + options.payload = options.payload + ? Hoek.applyToDefaults( + { allow: operation.consumes || api.consumes }, + options.payload + ) + : { allow: operation.consumes || api.consumes }; } if (Array.isArray(handler)) { @@ -66,28 +87,44 @@ const create = async function (server, { api, basedir, cors, vhost, handlers, ex for (let i = 0; i < handler.length - 1; ++i) { options.pre.push({ - assign: handler[i].name || 'p' + (i + 1), - method: handler[i] + assign: handler[i].name || "p" + (i + 1), + method: handler[i], }); } options.handler = handler[handler.length - 1]; } - const skipValidation = options.payload && options.payload.parse === false; - - if ((operation.parameters || operation.requestBody) && !skipValidation) { - const allowUnknownProperties = xoptions.validate && xoptions.validate.options && xoptions.validate.options.allowUnknown === true; - const v = validator.makeAll(operation.parameters, operation.requestBody, operation.consumes || api.consumes, api.openapi, allowUnknownProperties); + const skipValidation = + options.payload && options.payload.parse === false; + + if ( + (operation.parameters || operation.requestBody) && + !skipValidation + ) { + const allowUnknownProperties = + xoptions.validate && + xoptions.validate.options && + xoptions.validate.options.allowUnknown === true; + const v = validator.makeAll( + operation.parameters, + operation.requestBody, + operation.consumes || api.consumes, + api.openapi, + allowUnknownProperties + ); options.validate = v.validate; options.ext = { - onPreAuth: { method: v.routeExt } + onPreAuth: { method: v.routeExt }, }; } if (outputvalidation && operation.responses) { options.response = {}; - options.response.status = validator.makeResponseValidator(operation.responses, api.openapi); + options.response.status = validator.makeResponseValidator( + operation.responses, + api.openapi + ); } if (operation.security === undefined && api.security) { @@ -98,30 +135,46 @@ const create = async function (server, { api, basedir, cors, vhost, handlers, ex for (const secdef of operation.security) { const securitySchemes = Object.keys(secdef); if (!securitySchemes.length) { - options.auth = options.auth || { access: {}, mode: 'optional' }; - options.auth.mode = 'optional'; + options.auth = options.auth || { + access: {}, + mode: "optional", + }; + options.auth.mode = "optional"; } for (const securityDefinitionName of securitySchemes) { let securityDefinition; if (api.swagger) { - securityDefinition = api.securityDefinitions[securityDefinitionName]; + securityDefinition = + api.securityDefinitions[securityDefinitionName]; } if (api.openapi) { - securityDefinition = api.components.securitySchemes[securityDefinitionName]; + securityDefinition = + api.components.securitySchemes[ + securityDefinitionName + ]; } - Hoek.assert(securityDefinition, 'Security scheme not defined.'); - - options.auth = options.auth || { access: {}, mode: 'required' }; - options.auth.access.scope = options.auth.access.scope || []; - options.auth.access.scope.push(...secdef[securityDefinitionName]); + Hoek.assert( + securityDefinition, + "Security scheme not defined." + ); + + options.auth = options.auth || { + access: {}, + mode: "required", + }; + options.auth.access.scope = + options.auth.access.scope || []; + options.auth.access.scope.push( + ...secdef[securityDefinitionName] + ); options.auth.strategies = options.auth.strategies || []; options.auth.strategies.push(securityDefinitionName); } } - + if (options.auth.access.scope.length === 0) { options.auth.access.scope = false; } @@ -131,7 +184,7 @@ const create = async function (server, { api, basedir, cors, vhost, handlers, ex method, path: api.basePath + path, options, - vhost + vhost, }); } } diff --git a/lib/utils.js b/lib/utils.js index 65535fc..6784557 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,25 +1,24 @@ -'use strict'; +"use strict"; const utils = { - verbs: [ - 'get', - 'post', - 'put', - 'delete', - 'head', - 'options', - 'trace', - 'connect', - 'patch' + "get", + "post", + "put", + "delete", + "head", + "options", + "trace", + "connect", + "patch", ], canCarry: function (method) { switch (method) { - case 'post': - case 'put': - case 'patch': - case 'delete': + case "post": + case "put": + case "patch": + case "delete": return true; default: return false; @@ -27,7 +26,10 @@ const utils = { }, isHttpMethod: function (method) { - return (typeof method === 'string') && !!~utils.verbs.indexOf(method.toLowerCase()); + return ( + typeof method === "string" && + !!~utils.verbs.indexOf(method.toLowerCase()) + ); }, endsWith: function (haystack, needle) { @@ -43,7 +45,7 @@ const utils = { }, prefix: function (str, pre) { - str = str || ''; + str = str || ""; if (str.indexOf(pre) === 0) { return str; } @@ -53,7 +55,7 @@ const utils = { }, unprefix: function (str, pre) { - str = str || ''; + str = str || ""; if (str.indexOf(pre) === 0) { str = str.substr(pre.length); return str; @@ -63,7 +65,7 @@ const utils = { }, suffix: function (str, suff) { - str = str || ''; + str = str || ""; if (this.endsWith(str, suff)) { return str; } @@ -73,14 +75,14 @@ const utils = { }, unsuffix: function (str, suff) { - str = str || ''; + str = str || ""; if (this.endsWith(str, suff)) { str = str.substr(0, str.length - suff.length); return str; } return str; - } + }, }; module.exports = utils; diff --git a/lib/validators.js b/lib/validators.js index 8a44f6b..73c15e1 100644 --- a/lib/validators.js +++ b/lib/validators.js @@ -1,35 +1,41 @@ -'use strict'; +"use strict"; -const Enjoi = require('enjoi'); -const Joi = require('joi'); +const Enjoi = require("enjoi"); +const Joi = require("joi"); const extensions = [ - { type: 'int64', base: Joi.string().regex(/^\d+$/) }, - { type: 'byte', base: Joi.string().base64() }, - { type: 'date-time', base: Joi.date().iso() }, + { type: "int64", base: Joi.string().regex(/^\d+$/) }, + { type: "byte", base: Joi.string().base64() }, + { type: "date-time", base: Joi.date().iso() }, { - type: 'file', + type: "file", base: Joi.object({ value: Joi.binary().required(true), - consumes: Joi.array().items( - Joi.string().regex(/multipart\/form-data|application\/x-www-form-urlencoded/) - ).required(true), - in: Joi.string().regex(/formData/).required(true) - }) - } + consumes: Joi.array() + .items( + Joi.string().regex( + /multipart\/form-data|application\/x-www-form-urlencoded/ + ) + ) + .required(true), + in: Joi.string() + .regex(/formData/) + .required(true), + }), + }, ]; const refineType = function (type, format) { - if (type === 'integer') { - type = 'number'; + if (type === "integer") { + type = "number"; } switch (format) { - case 'int64': - case 'byte': - case 'binary': - case 'date': - case 'date-time': + case "int64": + case "byte": + case "binary": + case "date": + case "date-time": return format; default: return type; @@ -47,18 +53,25 @@ const refineSchema = function (joiSchema, jsonSchema) { const enjoi = Enjoi.defaults({ extensions, refineType, refineSchema }); const create = function (options = {}) { - const makeValidator = function (parameter, consumes, openapi, allowUnknownProperties = false) { + const makeValidator = function ( + parameter, + consumes, + openapi, + allowUnknownProperties = false + ) { const coerce = coercion(parameter, consumes); let schema; - if ((parameter.in === 'body' || parameter.in === 'formData') && parameter.schema) { + if ( + (parameter.in === "body" || parameter.in === "formData") && + parameter.schema + ) { schema = enjoi.schema(parameter.schema); - if (schema.type === 'object') { + if (schema.type === "object") { schema = schema.unknown(allowUnknownProperties); } - } - else { + } else { let template = { required: parameter.required, enum: parameter.enum, @@ -78,20 +91,19 @@ const create = function (options = {}) { maxItems: parameter.maxItems, minItems: parameter.minItems, uniqueItems: parameter.uniqueItems, - multipleOf: parameter.multipleOf + multipleOf: parameter.multipleOf, }; if (openapi) { template = { ...template, ...parameter.schema }; - } - else { + } else { template.schema = parameter.schema; } schema = enjoi.schema(template); } - if (parameter.type === 'array') { + if (parameter.type === "array") { schema = schema.single(true); } @@ -99,17 +111,20 @@ const create = function (options = {}) { schema = schema.required(); } - if (parameter.in !== 'body' && parameter.allowEmptyValue) { - schema = schema.allow('').optional(); + if (parameter.in !== "body" && parameter.allowEmptyValue) { + schema = schema.allow("").optional(); } return { parameter, schema, routeExt: function (request, h) { - const p = parameter.in === 'query' ? 'query' : 'params'; + const p = parameter.in === "query" ? "query" : "params"; if (request[p][parameter.name] !== undefined) { - request[p][parameter.name] = coerce && request[p][parameter.name] && coerce(request[p][parameter.name]); + request[p][parameter.name] = + coerce && + request[p][parameter.name] && + coerce(request[p][parameter.name]); } return h.continue; @@ -119,11 +134,16 @@ const create = function (options = {}) { const result = schema.validate(data); if (result && result.error) { - - result.error.message = result.error.message.replace('value', parameter.name); + result.error.message = result.error.message.replace( + "value", + parameter.name + ); result.error.details.forEach((detail) => { - detail.message = detail.message.replace('value', parameter.name); + detail.message = detail.message.replace( + "value", + parameter.name + ); detail.path = [parameter.name]; }); @@ -131,7 +151,7 @@ const create = function (options = {}) { } return result.value; - } + }, }; }; @@ -149,14 +169,13 @@ const create = function (options = {}) { break; } } - } - else { + } else { schemaDesc = response.schema; } if (schemaDesc) { const schema = enjoi.schema(schemaDesc); - if (schemaDesc === 'array') { + if (schemaDesc === "array") { schema.single(true); } @@ -168,7 +187,13 @@ const create = function (options = {}) { return schemas; }; - const makeAll = function (parameters = [], requestBody, consumes, openapi, allowUnknownProperties = false) { + const makeAll = function ( + parameters = [], + requestBody, + consumes, + openapi, + allowUnknownProperties = false + ) { const routeExt = []; const validate = {}; const formValidators = {}; @@ -181,7 +206,7 @@ const create = function (options = {}) { throw result.error; } - return this.parameter.type === 'file' ? result.value : result; + return this.parameter.type === "file" ? result.value : result; }; if (openapi && requestBody && requestBody.content) { @@ -189,8 +214,17 @@ const create = function (options = {}) { for (const mediaType of consumes) { // Applying first available schema to all media types if (requestBody.content[mediaType].schema) { - const parameter = { in: 'body', schema: requestBody.content[mediaType].schema, name: 'body' }; - const validator = makeValidator(parameter, consumes, openapi, allowUnknownProperties); + const parameter = { + in: "body", + schema: requestBody.content[mediaType].schema, + name: "body", + }; + const validator = makeValidator( + parameter, + consumes, + openapi, + allowUnknownProperties + ); validate.payload = validator.validate; break; } @@ -198,35 +232,45 @@ const create = function (options = {}) { } for (const parameter of parameters) { - const validator = makeValidator(parameter, consumes, openapi, allowUnknownProperties); + const validator = makeValidator( + parameter, + consumes, + openapi, + allowUnknownProperties + ); switch (validator.parameter.in) { - case 'header': + case "header": headers = headers || {}; headers[validator.parameter.name] = validator.schema; break; - case 'query': + case "query": validate.query = validate.query || {}; validate.query[validator.parameter.name] = validator.schema; routeExt.push(validator.routeExt); break; - case 'path': + case "path": validate.params = validate.params || {}; - validate.params[validator.parameter.name.replace(/(\*[0-9]*|\?)$/, '')] = validator.schema; + validate.params[ + validator.parameter.name.replace(/(\*[0-9]*|\?)$/, "") + ] = validator.schema; routeExt.push(validator.routeExt); break; - case 'body': + case "body": validate.payload = validator.validate; break; - case 'formData': - formValidators[validator.parameter.name] = formValidator.bind(validator); + case "formData": + formValidators[validator.parameter.name] = + formValidator.bind(validator); break; default: break; } if (headers && Object.keys(headers).length > 0) { - validate.headers = Joi.object(headers).options({ allowUnknown: true }); + validate.headers = Joi.object(headers).options({ + allowUnknown: true, + }); } if (!validate.payload && Object.keys(formValidators).length > 0) { @@ -243,36 +287,36 @@ const create = function (options = {}) { } for (const [key, value] of Object.entries(validate)) { - if (typeof value === 'object' && !Joi.isSchema(value)) { + if (typeof value === "object" && !Joi.isSchema(value)) { validate[key] = Joi.object(value); } } return { validate, - routeExt + routeExt, }; }; return { makeAll, makeValidator, - makeResponseValidator + makeResponseValidator, }; }; const pathsep = function (format) { switch (format) { - case 'csv': - return ','; - case 'ssv': - return ' '; - case 'tsv': - return '\t'; - case 'pipes': - return '|'; - case 'multi': - return '&'; + case "csv": + return ","; + case "ssv": + return " "; + case "tsv": + return "\t"; + case "pipes": + return "|"; + case "multi": + return "&"; } }; @@ -280,48 +324,48 @@ const coercion = function (parameter, consumes) { let fn; switch (parameter.type) { - case 'array': + case "array": fn = function (data) { if (Array.isArray(data)) { return data; } - const sep = pathsep(parameter.collectionFormat || 'csv'); + const sep = pathsep(parameter.collectionFormat || "csv"); return data.split(sep); }; break; - case 'integer': - case 'number': + case "integer": + case "number": fn = function (data) { - if (parameter.format === 'int64') { + if (parameter.format === "int64") { return data; } - + return Number(data); }; break; - case 'string': + case "string": //TODO: handle date, date-time, binary, byte formats. fn = String; break; - case 'boolean': + case "boolean": fn = function (data) { - return (data === 'true') || (data === '1') || (data === true); + return data === "true" || data === "1" || data === true; }; break; - case 'file': { + case "file": { fn = function (data) { return { value: data, consumes, - in: parameter.in + in: parameter.in, }; }; - + break; } } diff --git a/package.json b/package.json index edd9c11..9370c4f 100644 --- a/package.json +++ b/package.json @@ -32,12 +32,15 @@ "eslint-config-hapi": "^12.0.0", "eslint-plugin-hapi": "^4.1.0", "nyc": "^15.0.0", + "prettier": "^2.3.0", "tape": "^5.0.0" }, "scripts": { "test": "tape test/*.js", "cover": "nyc npm test", - "lint": "eslint lib" + "lint": "eslint lib", + "format": "prettier -w lib test", + "format-check": "prettier --check lib test" }, "license": "Apache-2.0", "main": "./lib/index", diff --git a/test/fixtures/defs/form.json b/test/fixtures/defs/form.json index 42b0298..8712e11 100644 --- a/test/fixtures/defs/form.json +++ b/test/fixtures/defs/form.json @@ -7,21 +7,13 @@ }, "host": "example.com", "basePath": "/v1/forms", - "schemes": [ - "http" - ], - "consumes": [ - "application/x-www-form-urlencoded" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/x-www-form-urlencoded"], + "produces": ["application/json"], "paths": { "/upload": { "post": { - "consumes": [ - "application/x-www-form-urlencoded" - ], + "consumes": ["application/x-www-form-urlencoded"], "operationId": "uploadFile", "description": "uploads a file", "parameters": [ @@ -55,4 +47,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/fixtures/defs/form_xoptions.json b/test/fixtures/defs/form_xoptions.json index dc58e0b..ecd8368 100644 --- a/test/fixtures/defs/form_xoptions.json +++ b/test/fixtures/defs/form_xoptions.json @@ -7,21 +7,13 @@ }, "host": "example.com", "basePath": "/v1/forms", - "schemes": [ - "http" - ], - "consumes": [ - "application/x-www-form-urlencoded" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/x-www-form-urlencoded"], + "produces": ["application/json"], "paths": { "/upload": { "post": { - "consumes": [ - "application/x-www-form-urlencoded" - ], + "consumes": ["application/x-www-form-urlencoded"], "operationId": "uploadFile", "description": "uploads a file", "parameters": [ @@ -58,4 +50,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/fixtures/defs/pets.json b/test/fixtures/defs/pets.json index efb617e..fcced0a 100644 --- a/test/fixtures/defs/pets.json +++ b/test/fixtures/defs/pets.json @@ -14,19 +14,13 @@ "name": "MIT", "url": "http://opensource.org/licenses/MIT" }, - "x-meta" : "test" + "x-meta": "test" }, "host": "petstore.swagger.io", "basePath": "/v1/petstore", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": { "/path.with.period": { "get": { @@ -92,9 +86,7 @@ "post": { "description": "Creates a new pet in the store. Duplicates are allowed", "operationId": "addPet", - "produces": [ - "application/json" - ], + "produces": ["application/json"], "parameters": [ { "name": "pet", @@ -187,10 +179,7 @@ "definitions": { "pet": { "type": "object", - "required": [ - "id", - "name" - ], + "required": ["id", "name"], "properties": { "id": { "type": "integer", @@ -206,9 +195,7 @@ }, "newPet": { "type": "object", - "required": [ - "name" - ], + "required": ["name"], "properties": { "id": { "type": "integer", @@ -224,10 +211,7 @@ }, "errorModel": { "type": "object", - "required": [ - "code", - "message" - ], + "required": ["code", "message"], "properties": { "code": { "type": "integer", @@ -239,4 +223,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/fixtures/defs/pets.yaml b/test/fixtures/defs/pets.yaml index b5a28b7..af39d59 100644 --- a/test/fixtures/defs/pets.yaml +++ b/test/fixtures/defs/pets.yaml @@ -1,161 +1,161 @@ -swagger: '2.0' +swagger: "2.0" info: - version: 1.0.0 - title: Swagger Petstore (Simple) - description: >- - A sample API that uses a petstore as an example to demonstrate features in - the swagger-2.0 specification - termsOfService: 'http://helloreverb.com/terms/' - contact: - name: Swagger API team - email: foo@example.com - url: 'http://swagger.io' - license: - name: MIT - url: 'http://opensource.org/licenses/MIT' + version: 1.0.0 + title: Swagger Petstore (Simple) + description: >- + A sample API that uses a petstore as an example to demonstrate features in + the swagger-2.0 specification + termsOfService: "http://helloreverb.com/terms/" + contact: + name: Swagger API team + email: foo@example.com + url: "http://swagger.io" + license: + name: MIT + url: "http://opensource.org/licenses/MIT" host: petstore.swagger.io basePath: /v1/petstore schemes: - - http + - http consumes: - - application/json + - application/json produces: - - application/json + - application/json paths: - /pets: - get: - description: Returns all pets from the system that the user has access to - operationId: findPets - produces: - - application/json - - application/xml - - text/xml - - text/html - parameters: - - name: tags - in: query - description: tags to filter by - required: false - type: array - items: - type: string - collectionFormat: csv - - name: limit - in: query - description: maximum number of results to return - required: false - type: integer - format: int32 - responses: - '200': - description: pet response - schema: - type: array - items: - $ref: '#/definitions/pet' - default: - description: unexpected error - schema: - $ref: '#/definitions/errorModel' - post: - description: Creates a new pet in the store. Duplicates are allowed - operationId: addPet - produces: - - application/json - parameters: - - name: pet - in: body - description: Pet to add to the store - required: true - schema: - $ref: '#/definitions/newPet' - responses: - '200': - description: pet response - schema: - $ref: '#/definitions/pet' - default: - description: unexpected error - schema: - $ref: '#/definitions/errorModel' - '/pets/{id}': - get: - description: >- - Returns a user based on a single ID, if the user does not have access to - the pet - operationId: findPetById - produces: - - application/json - - application/xml - - text/xml - - text/html - parameters: - - name: id - in: path - description: ID of pet to fetch - required: true - type: integer - format: int64 - responses: - '200': - description: pet response - schema: - $ref: '#/definitions/pet' - default: - description: unexpected error - schema: - $ref: '#/definitions/errorModel' - delete: - description: deletes a single pet based on the ID supplied - operationId: deletePet - parameters: - - name: id - in: path - description: ID of pet to delete - required: true - type: integer - format: int64 - responses: - '204': - description: pet deleted - default: - description: unexpected error - schema: - $ref: '#/definitions/errorModel' + /pets: + get: + description: Returns all pets from the system that the user has access to + operationId: findPets + produces: + - application/json + - application/xml + - text/xml + - text/html + parameters: + - name: tags + in: query + description: tags to filter by + required: false + type: array + items: + type: string + collectionFormat: csv + - name: limit + in: query + description: maximum number of results to return + required: false + type: integer + format: int32 + responses: + "200": + description: pet response + schema: + type: array + items: + $ref: "#/definitions/pet" + default: + description: unexpected error + schema: + $ref: "#/definitions/errorModel" + post: + description: Creates a new pet in the store. Duplicates are allowed + operationId: addPet + produces: + - application/json + parameters: + - name: pet + in: body + description: Pet to add to the store + required: true + schema: + $ref: "#/definitions/newPet" + responses: + "200": + description: pet response + schema: + $ref: "#/definitions/pet" + default: + description: unexpected error + schema: + $ref: "#/definitions/errorModel" + "/pets/{id}": + get: + description: >- + Returns a user based on a single ID, if the user does not have access to + the pet + operationId: findPetById + produces: + - application/json + - application/xml + - text/xml + - text/html + parameters: + - name: id + in: path + description: ID of pet to fetch + required: true + type: integer + format: int64 + responses: + "200": + description: pet response + schema: + $ref: "#/definitions/pet" + default: + description: unexpected error + schema: + $ref: "#/definitions/errorModel" + delete: + description: deletes a single pet based on the ID supplied + operationId: deletePet + parameters: + - name: id + in: path + description: ID of pet to delete + required: true + type: integer + format: int64 + responses: + "204": + description: pet deleted + default: + description: unexpected error + schema: + $ref: "#/definitions/errorModel" definitions: - pet: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - newPet: - type: object - required: - - name - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - errorModel: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string + pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + newPet: + type: object + required: + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + errorModel: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/fixtures/defs/pets_authed.json b/test/fixtures/defs/pets_authed.json index cd4e7aa..4241440 100644 --- a/test/fixtures/defs/pets_authed.json +++ b/test/fixtures/defs/pets_authed.json @@ -17,28 +17,18 @@ }, "host": "petstore.swagger.io", "basePath": "/v1/petstore", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": { "/pets": { "get": { "security": [ { - "api_key": [ - "api1:read" - ] + "api_key": ["api1:read"] }, { - "api_key2": [ - "api2:read" - ] + "api_key2": ["api2:read"] }, {} ], @@ -99,21 +89,15 @@ "post": { "security": [ { - "api_key": [ - "api1:read" - ] + "api_key": ["api1:read"] }, { - "api_key2": [ - "api2:read" - ] + "api_key2": ["api2:read"] } ], "description": "Creates a new pet in the store. Duplicates are allowed", "operationId": "addPet", - "produces": [ - "application/json" - ], + "produces": ["application/json"], "parameters": [ { "name": "pet", @@ -206,10 +190,7 @@ "definitions": { "pet": { "type": "object", - "required": [ - "id", - "name" - ], + "required": ["id", "name"], "properties": { "id": { "type": "integer", @@ -225,9 +206,7 @@ }, "newPet": { "type": "object", - "required": [ - "name" - ], + "required": ["name"], "properties": { "id": { "type": "integer", @@ -243,10 +222,7 @@ }, "errorModel": { "type": "object", - "required": [ - "code", - "message" - ], + "required": ["code", "message"], "properties": { "code": { "type": "integer", diff --git a/test/fixtures/defs/pets_root_authed.json b/test/fixtures/defs/pets_root_authed.json index 1329819..f3e147f 100644 --- a/test/fixtures/defs/pets_root_authed.json +++ b/test/fixtures/defs/pets_root_authed.json @@ -17,32 +17,21 @@ }, "host": "petstore.swagger.io", "basePath": "/v1/petstore", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "security": [ { - "api_key": [ - "api1:read" - ] + "api_key": ["api1:read"] }, { - "api_key2": [ - "api2:read" - ] + "api_key2": ["api2:read"] } ], "paths": { "/pets": { "get": { - "parameters": [ - ], + "parameters": [], "responses": { "200": { "description": "pet response", @@ -66,10 +55,7 @@ "definitions": { "pet": { "type": "object", - "required": [ - "id", - "name" - ], + "required": ["id", "name"], "properties": { "id": { "type": "integer", @@ -85,10 +71,7 @@ }, "errorModel": { "type": "object", - "required": [ - "code", - "message" - ], + "required": ["code", "message"], "properties": { "code": { "type": "integer", diff --git a/test/fixtures/defs/pets_xauthed.json b/test/fixtures/defs/pets_xauthed.json index f018d96..d78944f 100644 --- a/test/fixtures/defs/pets_xauthed.json +++ b/test/fixtures/defs/pets_xauthed.json @@ -17,23 +17,15 @@ }, "host": "petstore.swagger.io", "basePath": "/v1/petstore", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": { "/pets": { "get": { "security": [ { - "api_key": [ - "read" - ] + "api_key": ["read"] } ], "description": "Returns all pets from the system that the user has access to", @@ -93,9 +85,7 @@ "post": { "description": "Creates a new pet in the store. Duplicates are allowed", "operationId": "addPet", - "produces": [ - "application/json" - ], + "produces": ["application/json"], "parameters": [ { "name": "pet", @@ -188,10 +178,7 @@ "definitions": { "pet": { "type": "object", - "required": [ - "id", - "name" - ], + "required": ["id", "name"], "properties": { "id": { "type": "integer", @@ -207,9 +194,7 @@ }, "newPet": { "type": "object", - "required": [ - "name" - ], + "required": ["name"], "properties": { "id": { "type": "integer", @@ -225,10 +210,7 @@ }, "errorModel": { "type": "object", - "required": [ - "code", - "message" - ], + "required": ["code", "message"], "properties": { "code": { "type": "integer", @@ -251,4 +233,4 @@ "in": "header" } } -} \ No newline at end of file +} diff --git a/test/fixtures/defs/pets_xhandlers.json b/test/fixtures/defs/pets_xhandlers.json index 8db6c2a..3189079 100644 --- a/test/fixtures/defs/pets_xhandlers.json +++ b/test/fixtures/defs/pets_xhandlers.json @@ -17,15 +17,9 @@ }, "host": "petstore.swagger.io", "basePath": "/v1/petstore", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": { "/pets": { "x-hapi-handler": "../handlers/pets.js", @@ -80,9 +74,7 @@ "post": { "description": "Creates a new pet in the store. Duplicates are allowed", "operationId": "addPet", - "produces": [ - "application/json" - ], + "produces": ["application/json"], "parameters": [ { "name": "pet", @@ -176,10 +168,7 @@ "definitions": { "pet": { "type": "object", - "required": [ - "id", - "name" - ], + "required": ["id", "name"], "properties": { "id": { "type": "integer", @@ -195,9 +184,7 @@ }, "newPet": { "type": "object", - "required": [ - "name" - ], + "required": ["name"], "properties": { "id": { "type": "integer", @@ -213,10 +200,7 @@ }, "errorModel": { "type": "object", - "required": [ - "code", - "message" - ], + "required": ["code", "message"], "properties": { "code": { "type": "integer", @@ -228,4 +212,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/fixtures/handlers/path.with.period.js b/test/fixtures/handlers/path.with.period.js index d4d56eb..c5713de 100644 --- a/test/fixtures/handlers/path.with.period.js +++ b/test/fixtures/handlers/path.with.period.js @@ -1,7 +1,7 @@ -'use strict'; +"use strict"; module.exports = { get: function (req, h) { - return null - } + return null; + }, }; diff --git a/test/fixtures/handlers/pets.js b/test/fixtures/handlers/pets.js index 346e1b9..9ed2a10 100644 --- a/test/fixtures/handlers/pets.js +++ b/test/fixtures/handlers/pets.js @@ -1,6 +1,6 @@ -'use strict'; +"use strict"; -const Store = require('../lib/store'); +const Store = require("../lib/store"); module.exports = { get: function (req, h) { @@ -8,5 +8,5 @@ module.exports = { }, post: function (req, h) { return Store.get(Store.put(req.payload)); - } + }, }; diff --git a/test/fixtures/handlers/pets/{id}.js b/test/fixtures/handlers/pets/{id}.js index 6739946..474f032 100644 --- a/test/fixtures/handlers/pets/{id}.js +++ b/test/fixtures/handlers/pets/{id}.js @@ -1,6 +1,6 @@ -'use strict'; +"use strict"; -const Store = require('../../lib/store'); +const Store = require("../../lib/store"); module.exports = { get: [ @@ -9,10 +9,10 @@ module.exports = { }, function handler(req, h) { return req.pre.p1; - } + }, ], delete: function (req, h) { Store.delete(req.params.id); return Store.all(); - } + }, }; diff --git a/test/fixtures/lib/store.js b/test/fixtures/lib/store.js index 5b54f3c..ab9e627 100644 --- a/test/fixtures/lib/store.js +++ b/test/fixtures/lib/store.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; var store = []; @@ -15,5 +15,5 @@ module.exports = { }, all: function () { return store; - } + }, }; diff --git a/test/fixtures/lib/stub-auth-token-scheme.js b/test/fixtures/lib/stub-auth-token-scheme.js index 70b52dc..7e9f9ff 100644 --- a/test/fixtures/lib/stub-auth-token-scheme.js +++ b/test/fixtures/lib/stub-auth-token-scheme.js @@ -1,30 +1,32 @@ -'use strict'; +"use strict"; -var Boom = require('@hapi/boom'); +var Boom = require("@hapi/boom"); const register = function (server, options) { - server.auth.scheme('stub-auth-token', function (server, options) { + server.auth.scheme("stub-auth-token", function (server, options) { const scheme = { authenticate: async function (request, h) { const token = request.headers.authorization; if (!token) { - throw Boom.unauthorized(null, 'stub-auth-token'); + throw Boom.unauthorized(null, "stub-auth-token"); } try { - const { credentials, artifacts } = await options.validateFunc(token); + const { credentials, artifacts } = + await options.validateFunc(token); if (!credentials) { - throw Boom.unauthorized(null, 'stub-auth-token', { credentials }); + throw Boom.unauthorized(null, "stub-auth-token", { + credentials, + }); } return h.authenticated({ credentials, artifacts }); - } - catch (error) { + } catch (error) { throw error; } - } + }, }; return scheme; @@ -33,13 +35,12 @@ const register = function (server, options) { const buildValidateFunc = function (allowedToken) { return async function (token) { - if (token === allowedToken) { - return { credentials: { scope: [ 'api1:read' ] }, artifacts: { }}; + return { credentials: { scope: ["api1:read"] }, artifacts: {} }; } return {}; - } + }; }; -module.exports = { register, name: 'stub-auth-token', buildValidateFunc}; +module.exports = { register, name: "stub-auth-token", buildValidateFunc }; diff --git a/test/fixtures/lib/xauth-scheme.js b/test/fixtures/lib/xauth-scheme.js index ffc90be..4dcfaf8 100644 --- a/test/fixtures/lib/xauth-scheme.js +++ b/test/fixtures/lib/xauth-scheme.js @@ -1,15 +1,15 @@ -'use strict'; +"use strict"; -var Boom = require('@hapi/boom'); +var Boom = require("@hapi/boom"); -const register = function (server, { name }) { +const register = function (server, { name }) { server.auth.scheme(name /*apiKey*/, (server, { validate }) => { return { authenticate: async function (request, h) { return h.authenticated(await validate(request)); - } + }, }; }); }; -module.exports = { register, name: 'x-auth-scheme' }; +module.exports = { register, name: "x-auth-scheme" }; diff --git a/test/fixtures/lib/xauth-strategy.js b/test/fixtures/lib/xauth-strategy.js index 814d6e3..ce5eade 100644 --- a/test/fixtures/lib/xauth-strategy.js +++ b/test/fixtures/lib/xauth-strategy.js @@ -1,19 +1,22 @@ -'use strict'; +"use strict"; -const Boom = require('@hapi/boom'); +const Boom = require("@hapi/boom"); const register = function (server, { name, scheme, where, lookup }) { server.auth.strategy(name, scheme, { validate: async function (request) { const token = request.headers[lookup]; - if (token === '12345') { - return { credentials: { scope: ['read'] }, artifacts: { token } }; + if (token === "12345") { + return { + credentials: { scope: ["read"] }, + artifacts: { token }, + }; } throw Boom.unauthorized(); - } + }, }); }; -module.exports = { register, name: 'x-auth-strategy' }; +module.exports = { register, name: "x-auth-strategy" }; diff --git a/test/fixtures/openapi3/defs/pets-types.yaml b/test/fixtures/openapi3/defs/pets-types.yaml index 296d3da..bf0c61b 100644 --- a/test/fixtures/openapi3/defs/pets-types.yaml +++ b/test/fixtures/openapi3/defs/pets-types.yaml @@ -1,219 +1,221 @@ openapi: 3.0.1 info: - title: Swagger Petstore (Simple) - description: A sample API that uses a petstore as an example to demonstrate features - in the swagger-2.0 specification - termsOfService: http://helloreverb.com/terms/ - contact: - name: Swagger API team - url: http://swagger.io - email: foo@example.com - license: - name: MIT - url: http://opensource.org/licenses/MIT - version: 1.0.0 + title: Swagger Petstore (Simple) + description: + A sample API that uses a petstore as an example to demonstrate features + in the swagger-2.0 specification + termsOfService: http://helloreverb.com/terms/ + contact: + name: Swagger API team + url: http://swagger.io + email: foo@example.com + license: + name: MIT + url: http://opensource.org/licenses/MIT + version: 1.0.0 servers: -- url: http://petstore.swagger.io/v1/petstore + - url: http://petstore.swagger.io/v1/petstore paths: - /pets: - get: - description: Returns all pets from the system that the user has access to - operationId: findPets - parameters: - - name: tags - in: query - description: tags to filter by - style: form - explode: false - schema: - type: array - items: - type: string - - name: limit - in: query - description: maximum number of results to return - schema: - type: integer - format: int32 - responses: - 200: - description: pet response - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/pet' - application/xml: - schema: - type: array - items: - $ref: '#/components/schemas/pet' - text/xml: - schema: - type: array - items: - $ref: '#/components/schemas/pet' - text/html: - schema: - type: array - items: - $ref: '#/components/schemas/pet' - default: - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/errorModel' - application/xml: - schema: - $ref: '#/components/schemas/errorModel' - text/xml: - schema: - $ref: '#/components/schemas/errorModel' - text/html: - schema: - $ref: '#/components/schemas/errorModel' - post: - description: Creates a new pet in the store. Duplicates are allowed - operationId: addPet - requestBody: - description: Pet to add to the store - content: - application/json: - schema: - $ref: '#/components/schemas/newPet' - required: true - responses: - 200: - description: pet response - content: - application/json: - schema: - $ref: '#/components/schemas/pet' - default: - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/errorModel' - x-codegen-request-body-name: pet - /pets/{id}: - get: - description: Returns a user based on a single ID, if the user does not have - access to the pet - operationId: findPetById - parameters: - - name: id - in: path - description: ID of pet to fetch - required: true - schema: - type: integer - format: int64 - responses: - 200: - description: pet response - content: - application/json: - schema: - $ref: '#/components/schemas/pet' - application/xml: - schema: - $ref: '#/components/schemas/pet' - text/xml: - schema: - $ref: '#/components/schemas/pet' - text/html: - schema: - $ref: '#/components/schemas/pet' - default: - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/errorModel' - application/xml: - schema: - $ref: '#/components/schemas/errorModel' - text/xml: - schema: - $ref: '#/components/schemas/errorModel' - text/html: - schema: - $ref: '#/components/schemas/errorModel' - delete: - description: deletes a single pet based on the ID supplied - operationId: deletePet - parameters: - - name: id - in: path - description: ID of pet to delete - required: true - schema: - type: integer - format: int64 - responses: - 204: - description: pet deleted - content: {} - default: - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/errorModel' + /pets: + get: + description: Returns all pets from the system that the user has access to + operationId: findPets + parameters: + - name: tags + in: query + description: tags to filter by + style: form + explode: false + schema: + type: array + items: + type: string + - name: limit + in: query + description: maximum number of results to return + schema: + type: integer + format: int32 + responses: + 200: + description: pet response + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/pet" + application/xml: + schema: + type: array + items: + $ref: "#/components/schemas/pet" + text/xml: + schema: + type: array + items: + $ref: "#/components/schemas/pet" + text/html: + schema: + type: array + items: + $ref: "#/components/schemas/pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/errorModel" + application/xml: + schema: + $ref: "#/components/schemas/errorModel" + text/xml: + schema: + $ref: "#/components/schemas/errorModel" + text/html: + schema: + $ref: "#/components/schemas/errorModel" + post: + description: Creates a new pet in the store. Duplicates are allowed + operationId: addPet + requestBody: + description: Pet to add to the store + content: + application/json: + schema: + $ref: "#/components/schemas/newPet" + required: true + responses: + 200: + description: pet response + content: + application/json: + schema: + $ref: "#/components/schemas/pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/errorModel" + x-codegen-request-body-name: pet + /pets/{id}: + get: + description: + Returns a user based on a single ID, if the user does not have + access to the pet + operationId: findPetById + parameters: + - name: id + in: path + description: ID of pet to fetch + required: true + schema: + type: integer + format: int64 + responses: + 200: + description: pet response + content: + application/json: + schema: + $ref: "#/components/schemas/pet" + application/xml: + schema: + $ref: "#/components/schemas/pet" + text/xml: + schema: + $ref: "#/components/schemas/pet" + text/html: + schema: + $ref: "#/components/schemas/pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/errorModel" + application/xml: + schema: + $ref: "#/components/schemas/errorModel" + text/xml: + schema: + $ref: "#/components/schemas/errorModel" + text/html: + schema: + $ref: "#/components/schemas/errorModel" + delete: + description: deletes a single pet based on the ID supplied + operationId: deletePet + parameters: + - name: id + in: path + description: ID of pet to delete + required: true + schema: + type: integer + format: int64 + responses: + 204: + description: pet deleted + content: {} + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/errorModel" components: - schemas: - pet: - required: - - id - - name - type: object - properties: - id: - type: integer - format: int64 - name: - type: string - birthday: - type: string - format: date - nullable: true - coupon: - oneOf: - - type: integer - - type: string - tag: - type: string - newPet: - required: - - name - type: object - properties: - id: - type: integer - format: int64 - name: - type: string - birthday: - type: string - format: date - nullable: true - coupon: - oneOf: - - type: integer - - type: string - tag: - type: string - errorModel: - required: - - code - - message - type: object - properties: - code: - type: integer - format: int32 - message: - type: string + schemas: + pet: + required: + - id + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + birthday: + type: string + format: date + nullable: true + coupon: + oneOf: + - type: integer + - type: string + tag: + type: string + newPet: + required: + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + birthday: + type: string + format: date + nullable: true + coupon: + oneOf: + - type: integer + - type: string + tag: + type: string + errorModel: + required: + - code + - message + type: object + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/fixtures/openapi3/defs/pets.yaml b/test/fixtures/openapi3/defs/pets.yaml index 1e88c02..1f7f8ed 100644 --- a/test/fixtures/openapi3/defs/pets.yaml +++ b/test/fixtures/openapi3/defs/pets.yaml @@ -1,203 +1,205 @@ openapi: 3.0.1 info: - title: Swagger Petstore (Simple) - description: A sample API that uses a petstore as an example to demonstrate features - in the swagger-2.0 specification - termsOfService: http://helloreverb.com/terms/ - contact: - name: Swagger API team - url: http://swagger.io - email: foo@example.com - license: - name: MIT - url: http://opensource.org/licenses/MIT - version: 1.0.0 + title: Swagger Petstore (Simple) + description: + A sample API that uses a petstore as an example to demonstrate features + in the swagger-2.0 specification + termsOfService: http://helloreverb.com/terms/ + contact: + name: Swagger API team + url: http://swagger.io + email: foo@example.com + license: + name: MIT + url: http://opensource.org/licenses/MIT + version: 1.0.0 servers: -- url: http://petstore.swagger.io/v1/petstore + - url: http://petstore.swagger.io/v1/petstore paths: - /pets: - get: - description: Returns all pets from the system that the user has access to - operationId: findPets - parameters: - - name: tags - in: query - description: tags to filter by - style: form - explode: false - schema: - type: array - items: - type: string - - name: limit - in: query - description: maximum number of results to return - schema: - type: integer - format: int32 - responses: - 200: - description: pet response - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/pet' - application/xml: - schema: - type: array - items: - $ref: '#/components/schemas/pet' - text/xml: - schema: - type: array - items: - $ref: '#/components/schemas/pet' - text/html: - schema: - type: array - items: - $ref: '#/components/schemas/pet' - default: - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/errorModel' - application/xml: - schema: - $ref: '#/components/schemas/errorModel' - text/xml: - schema: - $ref: '#/components/schemas/errorModel' - text/html: - schema: - $ref: '#/components/schemas/errorModel' - post: - description: Creates a new pet in the store. Duplicates are allowed - operationId: addPet - requestBody: - description: Pet to add to the store - content: - application/json: - schema: - $ref: '#/components/schemas/newPet' - required: true - responses: - 200: - description: pet response - content: - application/json: - schema: - $ref: '#/components/schemas/pet' - default: - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/errorModel' - x-codegen-request-body-name: pet - /pets/{id}: - get: - description: Returns a user based on a single ID, if the user does not have - access to the pet - operationId: findPetById - parameters: - - name: id - in: path - description: ID of pet to fetch - required: true - schema: - type: integer - format: int64 - responses: - 200: - description: pet response - content: - application/json: - schema: - $ref: '#/components/schemas/pet' - application/xml: - schema: - $ref: '#/components/schemas/pet' - text/xml: - schema: - $ref: '#/components/schemas/pet' - text/html: - schema: - $ref: '#/components/schemas/pet' - default: - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/errorModel' - application/xml: - schema: - $ref: '#/components/schemas/errorModel' - text/xml: - schema: - $ref: '#/components/schemas/errorModel' - text/html: - schema: - $ref: '#/components/schemas/errorModel' - delete: - description: deletes a single pet based on the ID supplied - operationId: deletePet - parameters: - - name: id - in: path - description: ID of pet to delete - required: true - schema: - type: integer - format: int64 - responses: - 204: - description: pet deleted - content: {} - default: - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/errorModel' + /pets: + get: + description: Returns all pets from the system that the user has access to + operationId: findPets + parameters: + - name: tags + in: query + description: tags to filter by + style: form + explode: false + schema: + type: array + items: + type: string + - name: limit + in: query + description: maximum number of results to return + schema: + type: integer + format: int32 + responses: + 200: + description: pet response + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/pet" + application/xml: + schema: + type: array + items: + $ref: "#/components/schemas/pet" + text/xml: + schema: + type: array + items: + $ref: "#/components/schemas/pet" + text/html: + schema: + type: array + items: + $ref: "#/components/schemas/pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/errorModel" + application/xml: + schema: + $ref: "#/components/schemas/errorModel" + text/xml: + schema: + $ref: "#/components/schemas/errorModel" + text/html: + schema: + $ref: "#/components/schemas/errorModel" + post: + description: Creates a new pet in the store. Duplicates are allowed + operationId: addPet + requestBody: + description: Pet to add to the store + content: + application/json: + schema: + $ref: "#/components/schemas/newPet" + required: true + responses: + 200: + description: pet response + content: + application/json: + schema: + $ref: "#/components/schemas/pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/errorModel" + x-codegen-request-body-name: pet + /pets/{id}: + get: + description: + Returns a user based on a single ID, if the user does not have + access to the pet + operationId: findPetById + parameters: + - name: id + in: path + description: ID of pet to fetch + required: true + schema: + type: integer + format: int64 + responses: + 200: + description: pet response + content: + application/json: + schema: + $ref: "#/components/schemas/pet" + application/xml: + schema: + $ref: "#/components/schemas/pet" + text/xml: + schema: + $ref: "#/components/schemas/pet" + text/html: + schema: + $ref: "#/components/schemas/pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/errorModel" + application/xml: + schema: + $ref: "#/components/schemas/errorModel" + text/xml: + schema: + $ref: "#/components/schemas/errorModel" + text/html: + schema: + $ref: "#/components/schemas/errorModel" + delete: + description: deletes a single pet based on the ID supplied + operationId: deletePet + parameters: + - name: id + in: path + description: ID of pet to delete + required: true + schema: + type: integer + format: int64 + responses: + 204: + description: pet deleted + content: {} + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/errorModel" components: - schemas: - pet: - required: - - id - - name - type: object - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - newPet: - required: - - name - type: object - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - errorModel: - required: - - code - - message - type: object - properties: - code: - type: integer - format: int32 - message: - type: string + schemas: + pet: + required: + - id + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + newPet: + required: + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + errorModel: + required: + - code + - message + type: object + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/fixtures/openapi3/defs/pets_authed.yaml b/test/fixtures/openapi3/defs/pets_authed.yaml index 8e576ba..f6fea4b 100644 --- a/test/fixtures/openapi3/defs/pets_authed.yaml +++ b/test/fixtures/openapi3/defs/pets_authed.yaml @@ -1,216 +1,218 @@ openapi: 3.0.1 info: - title: Swagger Petstore (Simple) - description: A sample API that uses a petstore as an example to demonstrate features - in the swagger-2.0 specification - termsOfService: http://helloreverb.com/terms/ - contact: - name: Swagger API team - url: http://swagger.io - email: foo@example.com - license: - name: MIT - url: http://opensource.org/licenses/MIT - version: 1.0.0 + title: Swagger Petstore (Simple) + description: + A sample API that uses a petstore as an example to demonstrate features + in the swagger-2.0 specification + termsOfService: http://helloreverb.com/terms/ + contact: + name: Swagger API team + url: http://swagger.io + email: foo@example.com + license: + name: MIT + url: http://opensource.org/licenses/MIT + version: 1.0.0 servers: -- url: http://petstore.swagger.io/v1/petstore + - url: http://petstore.swagger.io/v1/petstore paths: - /pets: - get: - description: Returns all pets from the system that the user has access to - operationId: findPets - parameters: - - name: tags - in: query - description: tags to filter by - style: form - explode: false - schema: - type: array - items: - type: string - - name: limit - in: query - description: maximum number of results to return - schema: - type: integer - format: int32 - responses: - 200: - description: pet response - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/pet' - application/xml: - schema: - type: array - items: - $ref: '#/components/schemas/pet' - text/xml: - schema: - type: array - items: - $ref: '#/components/schemas/pet' - text/html: - schema: - type: array - items: - $ref: '#/components/schemas/pet' - default: - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/errorModel' - application/xml: - schema: - $ref: '#/components/schemas/errorModel' - text/xml: - schema: - $ref: '#/components/schemas/errorModel' - text/html: - schema: - $ref: '#/components/schemas/errorModel' - security: - - api_key: [] - post: - description: Creates a new pet in the store. Duplicates are allowed - operationId: addPet - requestBody: - description: Pet to add to the store - content: - application/json: - schema: - $ref: '#/components/schemas/newPet' - required: true - responses: - 200: - description: pet response - content: - application/json: - schema: - $ref: '#/components/schemas/pet' - default: - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/errorModel' - security: - - api_key: [] - x-codegen-request-body-name: pet - /pets/{id}: - get: - description: Returns a user based on a single ID, if the user does not have - access to the pet - operationId: findPetById - parameters: - - name: id - in: path - description: ID of pet to fetch - required: true - schema: - type: integer - format: int64 - responses: - 200: - description: pet response - content: - application/json: - schema: - $ref: '#/components/schemas/pet' - application/xml: - schema: - $ref: '#/components/schemas/pet' - text/xml: - schema: - $ref: '#/components/schemas/pet' - text/html: - schema: - $ref: '#/components/schemas/pet' - default: - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/errorModel' - application/xml: - schema: - $ref: '#/components/schemas/errorModel' - text/xml: - schema: - $ref: '#/components/schemas/errorModel' - text/html: - schema: - $ref: '#/components/schemas/errorModel' - security: - - api_key: [] - delete: - description: deletes a single pet based on the ID supplied - operationId: deletePet - parameters: - - name: id - in: path - description: ID of pet to delete - required: true - schema: - type: integer - format: int64 - responses: - 204: - description: pet deleted - content: {} - default: - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/errorModel' - security: - - api_key: [] + /pets: + get: + description: Returns all pets from the system that the user has access to + operationId: findPets + parameters: + - name: tags + in: query + description: tags to filter by + style: form + explode: false + schema: + type: array + items: + type: string + - name: limit + in: query + description: maximum number of results to return + schema: + type: integer + format: int32 + responses: + 200: + description: pet response + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/pet" + application/xml: + schema: + type: array + items: + $ref: "#/components/schemas/pet" + text/xml: + schema: + type: array + items: + $ref: "#/components/schemas/pet" + text/html: + schema: + type: array + items: + $ref: "#/components/schemas/pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/errorModel" + application/xml: + schema: + $ref: "#/components/schemas/errorModel" + text/xml: + schema: + $ref: "#/components/schemas/errorModel" + text/html: + schema: + $ref: "#/components/schemas/errorModel" + security: + - api_key: [] + post: + description: Creates a new pet in the store. Duplicates are allowed + operationId: addPet + requestBody: + description: Pet to add to the store + content: + application/json: + schema: + $ref: "#/components/schemas/newPet" + required: true + responses: + 200: + description: pet response + content: + application/json: + schema: + $ref: "#/components/schemas/pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/errorModel" + security: + - api_key: [] + x-codegen-request-body-name: pet + /pets/{id}: + get: + description: + Returns a user based on a single ID, if the user does not have + access to the pet + operationId: findPetById + parameters: + - name: id + in: path + description: ID of pet to fetch + required: true + schema: + type: integer + format: int64 + responses: + 200: + description: pet response + content: + application/json: + schema: + $ref: "#/components/schemas/pet" + application/xml: + schema: + $ref: "#/components/schemas/pet" + text/xml: + schema: + $ref: "#/components/schemas/pet" + text/html: + schema: + $ref: "#/components/schemas/pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/errorModel" + application/xml: + schema: + $ref: "#/components/schemas/errorModel" + text/xml: + schema: + $ref: "#/components/schemas/errorModel" + text/html: + schema: + $ref: "#/components/schemas/errorModel" + security: + - api_key: [] + delete: + description: deletes a single pet based on the ID supplied + operationId: deletePet + parameters: + - name: id + in: path + description: ID of pet to delete + required: true + schema: + type: integer + format: int64 + responses: + 204: + description: pet deleted + content: {} + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/errorModel" + security: + - api_key: [] components: - schemas: - pet: - required: - - id - - name - type: object - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - newPet: - required: - - name - type: object - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - errorModel: - required: - - code - - message - type: object - properties: - code: - type: integer - format: int32 - message: - type: string - securitySchemes: - api_key: - type: apiKey - name: Authorization - in: header + schemas: + pet: + required: + - id + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + newPet: + required: + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + errorModel: + required: + - code + - message + type: object + properties: + code: + type: integer + format: int32 + message: + type: string + securitySchemes: + api_key: + type: apiKey + name: Authorization + in: header diff --git a/test/test-auth.js b/test/test-auth.js index 8b5ed5a..4ca7c83 100644 --- a/test/test-auth.js +++ b/test/test-auth.js @@ -1,13 +1,13 @@ -'use strict'; +"use strict"; -const Test = require('tape'); -const Path = require('path'); -const OpenAPI = require('../lib'); -const Hapi = require('@hapi/hapi'); -const StubAuthTokenScheme = require('./fixtures/lib/stub-auth-token-scheme'); +const Test = require("tape"); +const Path = require("path"); +const OpenAPI = require("../lib"); +const Hapi = require("@hapi/hapi"); +const StubAuthTokenScheme = require("./fixtures/lib/stub-auth-token-scheme"); -Test('authentication', function (t) { - t.test('token authentication', async function (t) { +Test("authentication", function (t) { + t.test("token authentication", async function (t) { t.plan(3); const server = new Hapi.Server(); @@ -15,57 +15,71 @@ Test('authentication', function (t) { try { await server.register({ plugin: StubAuthTokenScheme }); - server.auth.strategy('api_key', 'stub-auth-token', { - validateFunc: StubAuthTokenScheme.buildValidateFunc('12345') + server.auth.strategy("api_key", "stub-auth-token", { + validateFunc: StubAuthTokenScheme.buildValidateFunc("12345"), }); - server.auth.strategy('api_key2', 'stub-auth-token', { - validateFunc: StubAuthTokenScheme.buildValidateFunc('98765') + server.auth.strategy("api_key2", "stub-auth-token", { + validateFunc: StubAuthTokenScheme.buildValidateFunc("98765"), }); await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets_authed.json'), - handlers: Path.join(__dirname, './fixtures/handlers') - } + api: Path.join( + __dirname, + "./fixtures/defs/pets_authed.json" + ), + handlers: Path.join(__dirname, "./fixtures/handlers"), + }, }); let response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets' + method: "GET", + url: "/v1/petstore/pets", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} unauthenticated.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} unauthenticated.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Cat' - } + id: "0", + name: "Cat", + }, }); - t.strictEqual(response.statusCode, 401, `${response.request.path} unauthenticated.`); + t.strictEqual( + response.statusCode, + 401, + `${response.request.path} unauthenticated.` + ); response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets', + method: "GET", + url: "/v1/petstore/pets", headers: { - authorization: '12345', - 'custom-header': 'Hello' - } + authorization: "12345", + "custom-header": "Hello", + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK when authorized and authenticated.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK when authorized and authenticated.` + ); + } catch (error) { t.fail(error.message); } }); - t.test('unauthorized', async function (t) { + t.test("unauthorized", async function (t) { t.plan(1); const server = new Hapi.Server(); @@ -73,40 +87,49 @@ Test('authentication', function (t) { try { await server.register({ plugin: StubAuthTokenScheme }); - server.auth.strategy('api_key', 'stub-auth-token', { + server.auth.strategy("api_key", "stub-auth-token", { validateFunc: async function (token) { - return { credentials: { scope: [ 'api3:read' ] }, artifacts: { }}; - } + return { + credentials: { scope: ["api3:read"] }, + artifacts: {}, + }; + }, }); - server.auth.strategy('api_key2', 'stub-auth-token', { - validateFunc: () => ({ isValid: true }) + server.auth.strategy("api_key2", "stub-auth-token", { + validateFunc: () => ({ isValid: true }), }); await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets_authed.json'), - handlers: Path.join(__dirname, './fixtures/handlers') - } + api: Path.join( + __dirname, + "./fixtures/defs/pets_authed.json" + ), + handlers: Path.join(__dirname, "./fixtures/handlers"), + }, }); const response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets', + method: "GET", + url: "/v1/petstore/pets", headers: { - authorization: '12345' - } + authorization: "12345", + }, }); - t.strictEqual(response.statusCode, 403, `${response.request.path} unauthorized.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 403, + `${response.request.path} unauthorized.` + ); + } catch (error) { t.fail(error.message); } }); - t.test('with root auth', async function (t) { + t.test("with root auth", async function (t) { t.plan(1); const server = new Hapi.Server(); @@ -114,43 +137,51 @@ Test('authentication', function (t) { try { await server.register({ plugin: StubAuthTokenScheme }); - server.auth.strategy('api_key', 'stub-auth-token', { + server.auth.strategy("api_key", "stub-auth-token", { validateFunc: async function (token) { - return { credentials: { scope: [ 'api3:read' ] }, artifacts: { }}; - } + return { + credentials: { scope: ["api3:read"] }, + artifacts: {}, + }; + }, }); - server.auth.strategy('api_key2', 'stub-auth-token', { - validateFunc: () => ({ isValid: true }) + server.auth.strategy("api_key2", "stub-auth-token", { + validateFunc: () => ({ isValid: true }), }); await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets_root_authed.json'), - handlers: Path.join(__dirname, './fixtures/handlers') - } + api: Path.join( + __dirname, + "./fixtures/defs/pets_root_authed.json" + ), + handlers: Path.join(__dirname, "./fixtures/handlers"), + }, }); const response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets', + method: "GET", + url: "/v1/petstore/pets", headers: { - authorization: '12345' - } + authorization: "12345", + }, }); - t.strictEqual(response.statusCode, 403, `${response.request.path} unauthorized.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 403, + `${response.request.path} unauthorized.` + ); + } catch (error) { t.fail(error.message); } }); }); -Test('authentication with x-auth', function (t) { - - t.test('authenticated', async function (t) { +Test("authentication with x-auth", function (t) { + t.test("authenticated", async function (t) { t.plan(2); const server = new Hapi.Server(); @@ -159,31 +190,40 @@ Test('authentication with x-auth', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets_xauthed.json'), - handlers: Path.join(__dirname, './fixtures/handlers') - } + api: Path.join( + __dirname, + "./fixtures/defs/pets_xauthed.json" + ), + handlers: Path.join(__dirname, "./fixtures/handlers"), + }, }); let response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets' + method: "GET", + url: "/v1/petstore/pets", }); - t.strictEqual(response.statusCode, 401, `${response.request.path} unauthenticated.`); + t.strictEqual( + response.statusCode, + 401, + `${response.request.path} unauthenticated.` + ); response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets', + method: "GET", + url: "/v1/petstore/pets", headers: { - authorization: '12345' - } + authorization: "12345", + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK when authorized and authenticated.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK when authorized and authenticated.` + ); + } catch (error) { t.fail(error.message); } }); - }); diff --git a/test/test-forms.js b/test/test-forms.js index d8b8fa6..aaa95fb 100644 --- a/test/test-forms.js +++ b/test/test-forms.js @@ -1,14 +1,12 @@ -'use strict'; +"use strict"; -const Test = require('tape'); -const Path = require('path'); -const OpenAPI = require('../lib'); -const Hapi = require('@hapi/hapi'); +const Test = require("tape"); +const Path = require("path"); +const OpenAPI = require("../lib"); +const Hapi = require("@hapi/hapi"); - -Test('form data', function (t) { - - t.test('upload', async function (t) { +Test("form data", function (t) { + t.test("upload", async function (t) { t.plan(1); try { @@ -17,38 +15,40 @@ Test('form data', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/form.json'), + api: Path.join(__dirname, "./fixtures/defs/form.json"), handlers: { upload: { post: function (req, h) { - return { - upload: req.payload.toString() + return { + upload: req.payload.toString(), }; - } - } + }, + }, }, - outputvalidation: true - } + outputvalidation: true, + }, }); const response = await server.inject({ - method: 'POST', - url: '/v1/forms/upload', + method: "POST", + url: "/v1/forms/upload", headers: { - 'content-type': 'application/x-www-form-urlencoded' + "content-type": "application/x-www-form-urlencoded", }, - payload: 'name=thing&upload=data' + payload: "name=thing&upload=data", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } - }); - t.test('bad content type', async function (t) { + t.test("bad content type", async function (t) { t.plan(1); try { @@ -57,33 +57,34 @@ Test('form data', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/form.json'), + api: Path.join(__dirname, "./fixtures/defs/form.json"), handlers: { upload: { post: function (req, h) { - return ''; - } - } - } - } + return ""; + }, + }, + }, + }, }); const response = await server.inject({ - method: 'POST', - url: '/v1/forms/upload', - payload: 'name=thing&upload=data' + method: "POST", + url: "/v1/forms/upload", + payload: "name=thing&upload=data", }); - t.strictEqual(response.statusCode, 415, `${response.request.path} unsupported media type.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 415, + `${response.request.path} unsupported media type.` + ); + } catch (error) { t.fail(error.message); } - }); - - t.test('invalid payload', async function (t) { + t.test("invalid payload", async function (t) { t.plan(1); try { @@ -92,32 +93,33 @@ Test('form data', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/form.json'), + api: Path.join(__dirname, "./fixtures/defs/form.json"), handlers: { upload: { post: function (req, h) { return; - } - } - } - } + }, + }, + }, + }, }); const response = await server.inject({ - method: 'POST', - url: '/v1/forms/upload', + method: "POST", + url: "/v1/forms/upload", headers: { - 'content-type': 'application/x-www-form-urlencoded' + "content-type": "application/x-www-form-urlencoded", }, - payload: 'name=thing&upload=' + payload: "name=thing&upload=", }); - t.strictEqual(response.statusCode, 400, `${response.request.path} validation error.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 400, + `${response.request.path} validation error.` + ); + } catch (error) { t.fail(error.message); } - }); - }); diff --git a/test/test-hapi-openapi.js b/test/test-hapi-openapi.js index 59260c7..f7e67f9 100644 --- a/test/test-hapi-openapi.js +++ b/test/test-hapi-openapi.js @@ -1,13 +1,12 @@ -'use strict'; +"use strict"; -const Test = require('tape'); -const Path = require('path'); -const OpenAPI = require('../lib'); -const Hapi = require('@hapi/hapi'); +const Test = require("tape"); +const Path = require("path"); +const OpenAPI = require("../lib"); +const Hapi = require("@hapi/hapi"); -Test('test plugin', function (t) { - - t.test('register', async function (t) { +Test("test plugin", function (t) { + t.test("register", async function (t) { t.plan(3); const server = new Hapi.Server(); @@ -16,24 +15,32 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), - handlers: Path.join(__dirname, './fixtures/handlers') - } + api: Path.join(__dirname, "./fixtures/defs/pets.json"), + handlers: Path.join(__dirname, "./fixtures/handlers"), + }, }); - t.ok(server.plugins.openapi.getApi, 'server.plugins.openapi.api exists.'); - t.ok(server.plugins.openapi.setHost, 'server.plugins.openapi.setHost exists.'); - - server.plugins.openapi.setHost('api.paypal.com'); - - t.strictEqual(server.plugins.openapi.getApi().host, 'api.paypal.com', 'server.plugins.openapi.setHost set host.'); - } - catch (error) { + t.ok( + server.plugins.openapi.getApi, + "server.plugins.openapi.api exists." + ); + t.ok( + server.plugins.openapi.setHost, + "server.plugins.openapi.setHost exists." + ); + + server.plugins.openapi.setHost("api.paypal.com"); + + t.strictEqual( + server.plugins.openapi.getApi().host, + "api.paypal.com", + "server.plugins.openapi.setHost set host." + ); + } catch (error) { t.fail(error.message); } - }); - t.test('register with cors options', async function (t) { + t.test("register with cors options", async function (t) { t.plan(3); const server = new Hapi.Server(); @@ -42,30 +49,43 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), - handlers: Path.join(__dirname, './fixtures/handlers'), + api: Path.join(__dirname, "./fixtures/defs/pets.json"), + handlers: Path.join(__dirname, "./fixtures/handlers"), cors: { origin: ["*"], maxAge: 86400, - headers: ["Accept", "Authorization", "Content-Type", "If-None-Match"], - exposedHeaders: ["x-count", "link"] - } - } + headers: [ + "Accept", + "Authorization", + "Content-Type", + "If-None-Match", + ], + exposedHeaders: ["x-count", "link"], + }, + }, }); - t.ok(server.plugins.openapi.getApi, 'server.plugins.openapi.api exists.'); - t.ok(server.plugins.openapi.setHost, 'server.plugins.openapi.setHost exists.'); - - server.plugins.openapi.setHost('api.paypal.com'); - - t.strictEqual(server.plugins.openapi.getApi().host, 'api.paypal.com', 'server.plugins.openapi.setHost set host.'); - } - catch (error) { + t.ok( + server.plugins.openapi.getApi, + "server.plugins.openapi.api exists." + ); + t.ok( + server.plugins.openapi.setHost, + "server.plugins.openapi.setHost exists." + ); + + server.plugins.openapi.setHost("api.paypal.com"); + + t.strictEqual( + server.plugins.openapi.getApi().host, + "api.paypal.com", + "server.plugins.openapi.setHost set host." + ); + } catch (error) { t.fail(error.message); } - }); - t.test('register with boolean cors', async function (t) { + t.test("register with boolean cors", async function (t) { t.plan(3); const server = new Hapi.Server(); @@ -74,54 +94,58 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), - handlers: Path.join(__dirname, './fixtures/handlers'), - cors: true - } + api: Path.join(__dirname, "./fixtures/defs/pets.json"), + handlers: Path.join(__dirname, "./fixtures/handlers"), + cors: true, + }, }); - t.ok(server.plugins.openapi.getApi, 'server.plugins.openapi.api exists.'); - t.ok(server.plugins.openapi.setHost, 'server.plugins.openapi.setHost exists.'); - - server.plugins.openapi.setHost('api.paypal.com'); - - t.strictEqual(server.plugins.openapi.getApi().host, 'api.paypal.com', 'server.plugins.openapi.setHost set host.'); - } - catch (error) { + t.ok( + server.plugins.openapi.getApi, + "server.plugins.openapi.api exists." + ); + t.ok( + server.plugins.openapi.setHost, + "server.plugins.openapi.setHost exists." + ); + + server.plugins.openapi.setHost("api.paypal.com"); + + t.strictEqual( + server.plugins.openapi.getApi().host, + "api.paypal.com", + "server.plugins.openapi.setHost set host." + ); + } catch (error) { t.fail(error.message); } - }); - t.test('register with object api', async function (t) { + t.test("register with object api", async function (t) { t.plan(3); const server = new Hapi.Server(); const api = { - swagger: '2.0', + swagger: "2.0", info: { - title: 'Test Object API', - version: '1.0.0' + title: "Test Object API", + version: "1.0.0", }, - host: 'example.com', - consumes: [ - 'application/json' - ], - produces: [ - 'application/json' - ], + host: "example.com", + consumes: ["application/json"], + produces: ["application/json"], paths: { - '/test': { + "/test": { get: { - operationId: 'testGet', + operationId: "testGet", responses: { 200: { - description: 'default response' - } - } - } - } - } + description: "default response", + }, + }, + }, + }, + }, }; try { @@ -133,85 +157,98 @@ Test('test plugin', function (t) { test: { get(request, h) { return; - } - } - } - } + }, + }, + }, + }, }); - t.ok(server.plugins.openapi.getApi, 'server.plugins.openapi.api exists.'); - t.ok(server.plugins.openapi.setHost, 'server.plugins.openapi.setHost exists.'); - - server.plugins.openapi.setHost('api.paypal.com'); - - t.strictEqual(server.plugins.openapi.getApi().host, 'api.paypal.com', 'server.plugins.openapi.setHost set host.'); - } - catch (error) { - console.log(error.stack) + t.ok( + server.plugins.openapi.getApi, + "server.plugins.openapi.api exists." + ); + t.ok( + server.plugins.openapi.setHost, + "server.plugins.openapi.setHost exists." + ); + + server.plugins.openapi.setHost("api.paypal.com"); + + t.strictEqual( + server.plugins.openapi.getApi().host, + "api.paypal.com", + "server.plugins.openapi.setHost set host." + ); + } catch (error) { + console.log(error.stack); t.fail(error.message); } - }); - t.test('register with optional query parameters does not change "request.orig"', async function (t) { - t.plan(1); + t.test( + 'register with optional query parameters does not change "request.orig"', + async function (t) { + t.plan(1); - const server = new Hapi.Server(); + const server = new Hapi.Server(); - const api = { - swagger: '2.0', - info: { - title: 'Test Optional Query Params', - version: '1.0.0' - }, - paths: { - '/test': { - get: { - parameters: [ - { - name: 'optionalParameter', - in: 'query', - required: false, - type: 'string', + const api = { + swagger: "2.0", + info: { + title: "Test Optional Query Params", + version: "1.0.0", + }, + paths: { + "/test": { + get: { + parameters: [ + { + name: "optionalParameter", + in: "query", + required: false, + type: "string", + }, + ], + responses: { + 200: { + description: "OK", + }, }, - ], - responses: { - 200: { - description: 'OK' - } - } - } - } - } - }; + }, + }, + }, + }; - try { - await server.register({ - plugin: OpenAPI, - options: { - api, - handlers: { - test: { - get(request, h) { - return request.orig; - } - } - } - } - }); + try { + await server.register({ + plugin: OpenAPI, + options: { + api, + handlers: { + test: { + get(request, h) { + return request.orig; + }, + }, + }, + }, + }); - const { result } = await server.inject({ - method: 'GET', - url: '/test' - }); - t.ok(Object.entries(result.query).length === 0, 'request.orig was not modified'); - } - catch (error) { - t.fail(error.message); + const { result } = await server.inject({ + method: "GET", + url: "/test", + }); + t.ok( + Object.entries(result.query).length === 0, + "request.orig was not modified" + ); + } catch (error) { + t.fail(error.message); + } } - }); + ); - t.test('api docs', async function (t) { + t.test("api docs", async function (t) { t.plan(3); const server = new Hapi.Server(); @@ -220,29 +257,36 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), - handlers: Path.join(__dirname, './fixtures/handlers') - } + api: Path.join(__dirname, "./fixtures/defs/pets.json"), + handlers: Path.join(__dirname, "./fixtures/handlers"), + }, }); const response = await server.inject({ - method: 'GET', - url: '/v1/petstore/api-docs' + method: "GET", + url: "/v1/petstore/api-docs", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); const body = JSON.parse(response.payload); - t.equal(body.info['x-meta'], undefined, 'stripped x-'); - t.equal(body.paths['/pets'].get.parameters[0]['x-meta'], undefined, 'stripped x- from array.'); - } - catch (error) { + t.equal(body.info["x-meta"], undefined, "stripped x-"); + t.equal( + body.paths["/pets"].get.parameters[0]["x-meta"], + undefined, + "stripped x- from array." + ); + } catch (error) { t.fail(error.message); } }); - t.test('api docs strip vendor extensions false', async function (t) { + t.test("api docs strip vendor extensions false", async function (t) { t.plan(3); const server = new Hapi.Server(); @@ -251,32 +295,35 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), - handlers: Path.join(__dirname, './fixtures/handlers'), + api: Path.join(__dirname, "./fixtures/defs/pets.json"), + handlers: Path.join(__dirname, "./fixtures/handlers"), docs: { - stripExtensions: false - } - } + stripExtensions: false, + }, + }, }); const response = await server.inject({ - method: 'GET', - url: '/v1/petstore/api-docs' + method: "GET", + url: "/v1/petstore/api-docs", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); const body = JSON.parse(response.payload); - t.equal(body.info['x-meta'], 'test'); - t.equal(body.paths['/pets'].get.parameters[0]['x-meta'], 'test'); - } - catch (error) { + t.equal(body.info["x-meta"], "test"); + t.equal(body.paths["/pets"].get.parameters[0]["x-meta"], "test"); + } catch (error) { t.fail(error.message); } }); - t.test('api docs auth false', async function (t) { + t.test("api docs auth false", async function (t) { t.plan(1); const server = new Hapi.Server(); @@ -285,27 +332,30 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), - handlers: Path.join(__dirname, './fixtures/handlers'), + api: Path.join(__dirname, "./fixtures/defs/pets.json"), + handlers: Path.join(__dirname, "./fixtures/handlers"), docs: { - auth: false - } - } + auth: false, + }, + }, }); const response = await server.inject({ - method: 'GET', - url: '/v1/petstore/api-docs' + method: "GET", + url: "/v1/petstore/api-docs", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } }); - t.test('api docs change path', async function (t) { + t.test("api docs change path", async function (t) { t.plan(1); const server = new Hapi.Server(); @@ -314,57 +364,66 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), - handlers: Path.join(__dirname, './fixtures/handlers'), + api: Path.join(__dirname, "./fixtures/defs/pets.json"), + handlers: Path.join(__dirname, "./fixtures/handlers"), docs: { - path: '/spec' - } - } + path: "/spec", + }, + }, }); const response = await server.inject({ - method: 'GET', - url: '/v1/petstore/spec' + method: "GET", + url: "/v1/petstore/spec", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } }); - t.test('api docs change path (with no basepath prefix)', async function (t) { - t.plan(1); + t.test( + "api docs change path (with no basepath prefix)", + async function (t) { + t.plan(1); - const server = new Hapi.Server(); + const server = new Hapi.Server(); - try { - await server.register({ - plugin: OpenAPI, - options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), - handlers: Path.join(__dirname, './fixtures/handlers'), - docs: { - path: '/spec', - prefixBasePath: false - } - } - }); + try { + await server.register({ + plugin: OpenAPI, + options: { + api: Path.join(__dirname, "./fixtures/defs/pets.json"), + handlers: Path.join(__dirname, "./fixtures/handlers"), + docs: { + path: "/spec", + prefixBasePath: false, + }, + }, + }); - const response = await server.inject({ - method: 'GET', - url: '/spec' - }); + const response = await server.inject({ + method: "GET", + url: "/spec", + }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - } - catch (error) { - t.fail(error.message); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { + t.fail(error.message); + } } - }); + ); - t.test('api docs change path old way', async function (t) { + t.test("api docs change path old way", async function (t) { t.plan(1); const server = new Hapi.Server(); @@ -373,46 +432,49 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), - handlers: Path.join(__dirname, './fixtures/handlers'), - docspath: '/spec' - } + api: Path.join(__dirname, "./fixtures/defs/pets.json"), + handlers: Path.join(__dirname, "./fixtures/handlers"), + docspath: "/spec", + }, }); const response = await server.inject({ - method: 'GET', - url: '/v1/petstore/spec' + method: "GET", + url: "/v1/petstore/spec", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } }); - t.test('minimal api spec support', async function (t) { + t.test("minimal api spec support", async function (t) { t.plan(1); const server = new Hapi.Server(); const api = { - swagger: '2.0', + swagger: "2.0", info: { - title: 'Minimal', - version: '1.0.0' + title: "Minimal", + version: "1.0.0", }, paths: { - '/test': { + "/test": { get: { responses: { 200: { - description: 'default response' - } - } - } - } - } + description: "default response", + }, + }, + }, + }, + }, }; try { @@ -423,49 +485,50 @@ Test('test plugin', function (t) { handlers: { test: { get(request, h) { - return 'test'; - } - } - } - } + return "test"; + }, + }, + }, + }, }); let response = await server.inject({ - method: 'GET', - url: '/test' + method: "GET", + url: "/test", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } - }); - t.test('trailing slashes', async function (t) { + t.test("trailing slashes", async function (t) { t.plan(1); const server = new Hapi.Server(); const api = { - swagger: '2.0', + swagger: "2.0", info: { - title: 'Minimal', - version: '1.0.0' + title: "Minimal", + version: "1.0.0", }, paths: { - '/test/': { + "/test/": { get: { responses: { 200: { - description: 'default response' - } - } - } - } - } + description: "default response", + }, + }, + }, + }, + }, }; try { @@ -476,28 +539,29 @@ Test('test plugin', function (t) { handlers: { test: { get(request, h) { - return 'test'; - } - } - } - } + return "test"; + }, + }, + }, + }, }); let response = await server.inject({ - method: 'GET', - url: '/test/' + method: "GET", + url: "/test/", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } - }); - t.test('routes', async function (t) { + t.test("routes", async function (t) { t.plan(6); const server = new Hapi.Server(); @@ -506,68 +570,89 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), - handlers: Path.join(__dirname, './fixtures/handlers') - } + api: Path.join(__dirname, "./fixtures/defs/pets.json"), + handlers: Path.join(__dirname, "./fixtures/handlers"), + }, }); let response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets' + method: "GET", + url: "/v1/petstore/pets", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Cat' - } + id: "0", + name: "Cat", + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - name: 123 - } + name: 123, + }, }); - t.strictEqual(response.statusCode, 400, `${response.request.path} payload bad.`); + t.strictEqual( + response.statusCode, + 400, + `${response.request.path} payload bad.` + ); response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets/0' + method: "GET", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'DELETE', - url: '/v1/petstore/pets/0' + method: "DELETE", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'GET', - url: '/v1/petstore/path.with.period' + method: "GET", + url: "/v1/petstore/path.with.period", }); - t.strictEqual(response.statusCode, 204, `${response.request.path} OK.`); - - } - catch (error) { + t.strictEqual( + response.statusCode, + 204, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } - }); - t.test('routes with output validation', async function (t) { + t.test("routes with output validation", async function (t) { t.plan(5); const server = new Hapi.Server(); @@ -576,62 +661,79 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), - handlers: Path.join(__dirname, './fixtures/handlers'), - outputvalidation: true - } + api: Path.join(__dirname, "./fixtures/defs/pets.json"), + handlers: Path.join(__dirname, "./fixtures/handlers"), + outputvalidation: true, + }, }); let response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets' + method: "GET", + url: "/v1/petstore/pets", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Cat' - } + id: "0", + name: "Cat", + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - name: 123 - } + name: 123, + }, }); - t.strictEqual(response.statusCode, 400, `${response.request.path} payload bad.`); + t.strictEqual( + response.statusCode, + 400, + `${response.request.path} payload bad.` + ); response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets/0' + method: "GET", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'DELETE', - url: '/v1/petstore/pets/0' + method: "DELETE", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } - }); - t.test('output validation fails', async function (t) { + t.test("output validation fails", async function (t) { t.plan(1); const server = new Hapi.Server(); @@ -640,35 +742,36 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), + api: Path.join(__dirname, "./fixtures/defs/pets.json"), handlers: { pets: { - '{id}': { + "{id}": { get(req, h) { - return 'bad response type'; - } - } - } + return "bad response type"; + }, + }, + }, }, - outputvalidation: true - } + outputvalidation: true, + }, }); const response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets/0' + method: "GET", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 500, `${response.request.path} failed.`); - - } - catch (error) { + t.strictEqual( + response.statusCode, + 500, + `${response.request.path} failed.` + ); + } catch (error) { t.fail(error.message); } - }); - t.test('routes x-handler', async function (t) { + t.test("routes x-handler", async function (t) { t.plan(4); const server = new Hapi.Server(); @@ -677,89 +780,105 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets_xhandlers.json') - } + api: Path.join( + __dirname, + "./fixtures/defs/pets_xhandlers.json" + ), + }, }); let response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets' + method: "GET", + url: "/v1/petstore/pets", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Cat' - } + id: "0", + name: "Cat", + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets/0' + method: "GET", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'DELETE', - url: '/v1/petstore/pets/0' + method: "DELETE", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } - }); - - t.test('query validation', async function (t) { - + t.test("query validation", async function (t) { const server = new Hapi.Server(); try { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.json'), - handlers: Path.join(__dirname, './fixtures/handlers') - } + api: Path.join(__dirname, "./fixtures/defs/pets.json"), + handlers: Path.join(__dirname, "./fixtures/handlers"), + }, }); const queryStringToStatusCode = { - 'limit=2': 200, - 'tags=some_tag&tags=some_other_tag': 200, - 'tags=single_tag': 200, - 'limit=2&tags=some_tag&tags=some_other_tag': 200, - 'limit=a_string': 400 - } + "limit=2": 200, + "tags=some_tag&tags=some_other_tag": 200, + "tags=single_tag": 200, + "limit=2&tags=some_tag&tags=some_other_tag": 200, + "limit=a_string": 400, + }; for (const queryString in queryStringToStatusCode) { const response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets?' + queryString + method: "GET", + url: "/v1/petstore/pets?" + queryString, }); - t.strictEqual(response.statusCode, queryStringToStatusCode[queryString], queryString); + t.strictEqual( + response.statusCode, + queryStringToStatusCode[queryString], + queryString + ); } t.end(); - } - catch (error) { + } catch (error) { t.fail(error.message); } }); - t.test('query validation with arrays', async function (t) { - + t.test("query validation with arrays", async function (t) { const server = new Hapi.Server(); try { @@ -767,65 +886,72 @@ Test('test plugin', function (t) { plugin: OpenAPI, options: { api: { - swagger: '2.0', + swagger: "2.0", info: { - title: 'Minimal', - version: '1.0.0' + title: "Minimal", + version: "1.0.0", }, paths: { - '/test': { + "/test": { get: { - description: '', + description: "", parameters: [ { - name: 'tags', - in: 'query', + name: "tags", + in: "query", required: false, - type: 'array', + type: "array", items: { - type: 'string' + type: "string", }, - collectionFormat: 'csv' - } + collectionFormat: "csv", + }, ], responses: { 200: { - description: 'default response' - } - } - } - } - } + description: "default response", + }, + }, + }, + }, + }, }, handlers: { test: { get(request, h) { - t.ok(request.query.tags, 'query exists.'); - t.equal(request.query.tags.length, 2, 'two array elements.'); - t.equal(request.query.tags[0], 'some_tag', 'values correct.'); - return 'test'; - } - } - } - } + t.ok(request.query.tags, "query exists."); + t.equal( + request.query.tags.length, + 2, + "two array elements." + ); + t.equal( + request.query.tags[0], + "some_tag", + "values correct." + ); + return "test"; + }, + }, + }, + }, }); const response = await server.inject({ - method: 'GET', - url: '/test?tags=some_tag,some_other_tag' + method: "GET", + url: "/test?tags=some_tag,some_other_tag", }); - t.strictEqual(response.statusCode, 200, 'csv format supported.'); + t.strictEqual(response.statusCode, 200, "csv format supported."); t.end(); - } - catch (error) { + } catch (error) { t.fail(error.message); } }); - t.test('parse description from api definition', async function (t) { - t.test('do not break with empty descriptions', async function (t) { + t.test("parse description from api definition", async function (t) { + t.test("do not break with empty descriptions", async function (t) { t.plan(1); const server = new Hapi.Server(); @@ -835,32 +961,32 @@ Test('test plugin', function (t) { plugin: OpenAPI, options: { api: { - swagger: '2.0', + swagger: "2.0", info: { - title: 'Minimal', - version: '1.0.0' + title: "Minimal", + version: "1.0.0", }, paths: { - '/test': { + "/test": { get: { - description: '', + description: "", responses: { 200: { - description: 'default response' - } - } - } - } - } + description: "default response", + }, + }, + }, + }, + }, }, handlers: { test: { get(request, h) { - return 'test'; - } - } - } - } + return "test"; + }, + }, + }, + }, }); t.pass(); @@ -869,93 +995,104 @@ Test('test plugin', function (t) { } }); - t.test('create the right description for the route', async function (t) { - t.plan(1); - - const server = new Hapi.Server(); - - try { - await server.register({ - plugin: OpenAPI, - options: { - api: { - swagger: '2.0', - info: { - title: 'Minimal', - version: '1.0.0' + t.test( + "create the right description for the route", + async function (t) { + t.plan(1); + + const server = new Hapi.Server(); + + try { + await server.register({ + plugin: OpenAPI, + options: { + api: { + swagger: "2.0", + info: { + title: "Minimal", + version: "1.0.0", + }, + paths: { + "/test": { + get: { + description: + "A simple description for the route", + responses: { + 200: { + description: + "default response", + }, + }, + }, + }, + }, + }, + handlers: { + test: { + get(request, h) { + return "test"; + }, + }, }, - paths: { - '/test': { - get: { - description: 'A simple description for the route', - responses: { - 200: { - description: 'default response' - } - } - } - } - } }, - handlers: { - test: { - get(request, h) { - return 'test'; - } - } - } - } - }); - - const response = await server.inject({ method: 'GET', url: '/test' }); - t.strictEqual(response.request.route.settings.description, 'A simple description for the route'); - } catch (error) { - t.fail(error.message); + }); + + const response = await server.inject({ + method: "GET", + url: "/test", + }); + t.strictEqual( + response.request.route.settings.description, + "A simple description for the route" + ); + } catch (error) { + t.fail(error.message); + } } - }); + ); }); - t.test('hapi payload options (assert via parse:false)', async function (t) { + t.test("hapi payload options (assert via parse:false)", async function (t) { t.plan(1); const server = new Hapi.Server(); const api = { - swagger: '2.0', + swagger: "2.0", info: { - title: 'Minimal', - version: '1.0.0' + title: "Minimal", + version: "1.0.0", }, paths: { - '/test': { + "/test": { post: { - 'x-hapi-options': { + "x-hapi-options": { payload: { - parse: false - } + parse: false, + }, }, parameters: [ { - name: 'thing', - in: 'body', + name: "thing", + in: "body", schema: { - type: 'object', + type: "object", properties: { id: { - type: 'string' - } - } - } - } + type: "string", + }, + }, + }, + }, ], responses: { 200: { - description: 'default response' - } - } - } - } - } + description: "default response", + }, + }, + }, + }, + }, }; try { @@ -966,73 +1103,74 @@ Test('test plugin', function (t) { handlers: { test: { post() { - return 'test'; - } - } - } - } + return "test"; + }, + }, + }, + }, }); let response = await server.inject({ - method: 'POST', - url: '/test', + method: "POST", + url: "/test", payload: { - id: 1 //won't fail because parse is false - } + id: 1, //won't fail because parse is false + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } - }); - t.test('hapi allowUnknown request payload properties', async function (t) { + t.test("hapi allowUnknown request payload properties", async function (t) { t.plan(1); const server = new Hapi.Server(); const api = { - swagger: '2.0', + swagger: "2.0", info: { - title: 'Minimal', - version: '1.0.0' + title: "Minimal", + version: "1.0.0", }, paths: { - '/test': { + "/test": { post: { - 'x-hapi-options': { + "x-hapi-options": { validate: { options: { - allowUnknown: true - } - } + allowUnknown: true, + }, + }, }, parameters: [ { - name: 'thing', - in: 'body', + name: "thing", + in: "body", schema: { - type: 'object', + type: "object", properties: { id: { - type: 'string' - } - } - } - } + type: "string", + }, + }, + }, + }, ], responses: { 200: { - description: 'default response' - } - } - } - } - } + description: "default response", + }, + }, + }, + }, + }, }; try { @@ -1043,73 +1181,74 @@ Test('test plugin', function (t) { handlers: { test: { post() { - return 'test'; - } - } - } - } + return "test"; + }, + }, + }, + }, }); let response = await server.inject({ - method: 'POST', - url: '/test', + method: "POST", + url: "/test", payload: { - id: 'string-id', - excessive: 42 - } + id: "string-id", + excessive: 42, + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } - }); - t.test('hapi array parameters', async function (t) { + t.test("hapi array parameters", async function (t) { t.plan(1); const server = new Hapi.Server(); const api = { - swagger: '2.0', + swagger: "2.0", info: { - title: 'Minimal', - version: '1.0.0' + title: "Minimal", + version: "1.0.0", }, paths: { - '/test': { + "/test": { post: { parameters: [ { - name: 'body', - in: 'body', + name: "body", + in: "body", schema: { type: "array", items: { type: "object", properties: { name: { - type: "string" + type: "string", }, breed: { - type: "string" - } - } - } - } - } + type: "string", + }, + }, + }, + }, + }, ], responses: { 200: { - description: 'default response' - } - } - } - } - } + description: "default response", + }, + }, + }, + }, + }, }; try { @@ -1120,65 +1259,63 @@ Test('test plugin', function (t) { handlers: { test: { post() { - return 'test'; - } - } - } - } + return "test"; + }, + }, + }, + }, }); let response = await server.inject({ - method: 'POST', - url: '/test', + method: "POST", + url: "/test", payload: [ { - name: 'Fido', - breed: 'Pointer' + name: "Fido", + breed: "Pointer", }, { - name: 'Frodo', - breed: 'Beagle' - } - ] + name: "Frodo", + breed: "Beagle", + }, + ], }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } - }); - t.test('hapi operation tags', async function (t) { + t.test("hapi operation tags", async function (t) { t.plan(1); const server = new Hapi.Server(); const api = { - swagger: '2.0', + swagger: "2.0", info: { - title: 'Minimal', - version: '1.0.0' + title: "Minimal", + version: "1.0.0", }, paths: { - '/test': { + "/test": { get: { - tags: [ - 'sample1', - 'sample2' - ], + tags: ["sample1", "sample2"], responses: { 200: { - description: 'default response' - } - } - } - } - } + description: "default response", + }, + }, + }, + }, + }, }; - const expectedTags = ['api', 'sample1', 'sample2'] + const expectedTags = ["api", "sample1", "sample2"]; try { await server.register({ @@ -1188,52 +1325,53 @@ Test('test plugin', function (t) { handlers: { test: { get() { - return 'test'; - } - } - } - } + return "test"; + }, + }, + }, + }, }); let response = await server.inject({ - method: 'GET', - url: '/test' + method: "GET", + url: "/test", }); - const responsteTags = response.request.route.settings.tags - - t.deepEqual(responsteTags, expectedTags, 'additional tags successfully configured'); - - } - catch (error) { + const responsteTags = response.request.route.settings.tags; + + t.deepEqual( + responsteTags, + expectedTags, + "additional tags successfully configured" + ); + } catch (error) { t.fail(error.message); } - }); - t.test('hapi operation tags omitted', async function (t) { + t.test("hapi operation tags omitted", async function (t) { t.plan(1); const server = new Hapi.Server(); const api = { - swagger: '2.0', + swagger: "2.0", info: { - title: 'Minimal', - version: '1.0.0' + title: "Minimal", + version: "1.0.0", }, paths: { - '/test': { + "/test": { get: { responses: { 200: { - description: 'default response' - } - } - } - } - } + description: "default response", + }, + }, + }, + }, + }, }; - const expectedDefaultTags = ['api'] + const expectedDefaultTags = ["api"]; try { await server.register({ @@ -1243,73 +1381,72 @@ Test('test plugin', function (t) { handlers: { test: { get() { - return 'test'; - } - } - } - } + return "test"; + }, + }, + }, + }, }); let response = await server.inject({ - method: 'GET', - url: '/test' + method: "GET", + url: "/test", }); - const responsteTags = response.request.route.settings.tags - - t.deepEqual(responsteTags, expectedDefaultTags, 'returned default tags'); - - } - catch (error) { + const responsteTags = response.request.route.settings.tags; + + t.deepEqual( + responsteTags, + expectedDefaultTags, + "returned default tags" + ); + } catch (error) { t.fail(error.message); } - }); - }); -Test('multi-register', function (t) { - +Test("multi-register", function (t) { const api1 = { - swagger: '2.0', + swagger: "2.0", info: { - title: 'API 1', - version: '1.0.0' + title: "API 1", + version: "1.0.0", }, - basePath: '/api1', + basePath: "/api1", paths: { - '/test': { + "/test": { get: { responses: { 200: { - description: 'default response' - } - } - } - } - } + description: "default response", + }, + }, + }, + }, + }, }; const api2 = { - swagger: '2.0', + swagger: "2.0", info: { - title: 'API 2', - version: '1.0.0' + title: "API 2", + version: "1.0.0", }, - basePath: '/api2', + basePath: "/api2", paths: { - '/test': { + "/test": { get: { responses: { 200: { - description: 'default response' - } - } - } - } - } + description: "default response", + }, + }, + }, + }, + }, }; - t.test('support register multiple', async function (t) { + t.test("support register multiple", async function (t) { t.plan(2); const server = new Hapi.Server(); @@ -1323,11 +1460,11 @@ Test('multi-register', function (t) { handlers: { test: { get(request, h) { - return 'test'; - } - } - } - } + return "test"; + }, + }, + }, + }, }, { plugin: OpenAPI, @@ -1336,36 +1473,41 @@ Test('multi-register', function (t) { handlers: { test: { get(request, h) { - return 'test'; - } - } - } - } - } + return "test"; + }, + }, + }, + }, + }, ]); let response = await server.inject({ - method: 'GET', - url: '/api1/test' + method: "GET", + url: "/api1/test", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'GET', - url: '/api2/test' + method: "GET", + url: "/api2/test", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } - }); - t.test('support fail on conflicts', async function (t) { + t.test("support fail on conflicts", async function (t) { t.plan(1); const server = new Hapi.Server(); @@ -1377,47 +1519,44 @@ Test('multi-register', function (t) { options: { api: api1, docs: { - path: 'docs1' + path: "docs1", }, handlers: { test: { get(request, h) { - return 'test'; - } - } - } - } + return "test"; + }, + }, + }, + }, }, { plugin: OpenAPI, options: { api: api1, docs: { - path: 'docs2' + path: "docs2", }, handlers: { test: { get(request, h) { - return 'test'; - } - } - } - } - } + return "test"; + }, + }, + }, + }, + }, ]); - t.fail('should have errored'); - } - catch (error) { - t.pass('expected failure'); + t.fail("should have errored"); + } catch (error) { + t.pass("expected failure"); } - }); - }); -Test('yaml support', function (t) { - t.test('register', async function (t) { +Test("yaml support", function (t) { + t.test("register", async function (t) { t.plan(3); const server = new Hapi.Server(); @@ -1426,24 +1565,32 @@ Test('yaml support', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/pets.yaml'), - handlers: Path.join(__dirname, './fixtures/handlers') - } + api: Path.join(__dirname, "./fixtures/defs/pets.yaml"), + handlers: Path.join(__dirname, "./fixtures/handlers"), + }, }); - t.ok(server.plugins.openapi.getApi, 'server.plugins.openapi.getApi exists.'); - t.ok(server.plugins.openapi.setHost, 'server.plugins.openapi.setHost exists.'); + t.ok( + server.plugins.openapi.getApi, + "server.plugins.openapi.getApi exists." + ); + t.ok( + server.plugins.openapi.setHost, + "server.plugins.openapi.setHost exists." + ); const response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets' + method: "GET", + url: "/v1/petstore/pets", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } - }); }); diff --git a/test/test-hapi-openapi3.js b/test/test-hapi-openapi3.js index afa90f2..979aff4 100644 --- a/test/test-hapi-openapi3.js +++ b/test/test-hapi-openapi3.js @@ -1,13 +1,13 @@ -'use strict'; +"use strict"; -const Test = require('tape'); -const Path = require('path'); -const OpenAPI = require('../lib'); -const Hapi = require('@hapi/hapi'); -const StubAuthTokenScheme = require('./fixtures/lib/stub-auth-token-scheme'); +const Test = require("tape"); +const Path = require("path"); +const OpenAPI = require("../lib"); +const Hapi = require("@hapi/hapi"); +const StubAuthTokenScheme = require("./fixtures/lib/stub-auth-token-scheme"); -Test('test plugin', function (t) { - t.test('basic API', async function (t) { +Test("test plugin", function (t) { + t.test("basic API", async function (t) { t.plan(8); const server = new Hapi.Server(); @@ -16,65 +16,97 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/openapi3/defs/pets.yaml'), - handlers: Path.join(__dirname, './fixtures/handlers') - } + api: Path.join( + __dirname, + "./fixtures/openapi3/defs/pets.yaml" + ), + handlers: Path.join(__dirname, "./fixtures/handlers"), + }, }); - t.ok(server.plugins.openapi.getApi, 'server.plugins.openapi.api exists.'); - t.ok(server.plugins.openapi.setHost, 'server.plugins.openapi.setHost exists.'); - - server.plugins.openapi.setHost('api.paypal.com'); - - t.strictEqual(server.plugins.openapi.getApi().host, 'api.paypal.com', 'server.plugins.openapi.setHost set host.'); + t.ok( + server.plugins.openapi.getApi, + "server.plugins.openapi.api exists." + ); + t.ok( + server.plugins.openapi.setHost, + "server.plugins.openapi.setHost exists." + ); + + server.plugins.openapi.setHost("api.paypal.com"); + + t.strictEqual( + server.plugins.openapi.getApi().host, + "api.paypal.com", + "server.plugins.openapi.setHost set host." + ); let response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets' + method: "GET", + url: "/v1/petstore/pets", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Cat' - } + id: "0", + name: "Cat", + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - name: 123 - } + name: 123, + }, }); - t.strictEqual(response.statusCode, 400, `${response.request.path} payload bad.`); + t.strictEqual( + response.statusCode, + 400, + `${response.request.path} payload bad.` + ); response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets/0' + method: "GET", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'DELETE', - url: '/v1/petstore/pets/0' + method: "DELETE", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } }); - t.test('routes with output validation', async function (t) { + t.test("routes with output validation", async function (t) { t.plan(5); const server = new Hapi.Server(); @@ -83,61 +115,82 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/openapi3/defs/pets.yaml'), - handlers: Path.join(__dirname, './fixtures/handlers'), - outputvalidation: true - } + api: Path.join( + __dirname, + "./fixtures/openapi3/defs/pets.yaml" + ), + handlers: Path.join(__dirname, "./fixtures/handlers"), + outputvalidation: true, + }, }); let response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets' + method: "GET", + url: "/v1/petstore/pets", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Cat' - } + id: "0", + name: "Cat", + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - name: 123 - } + name: 123, + }, }); - t.strictEqual(response.statusCode, 400, `${response.request.path} payload bad.`); + t.strictEqual( + response.statusCode, + 400, + `${response.request.path} payload bad.` + ); response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets/0' + method: "GET", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'DELETE', - url: '/v1/petstore/pets/0' + method: "DELETE", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } }); - t.test('output validation fails', async function (t) { + t.test("output validation fails", async function (t) { t.plan(1); const server = new Hapi.Server(); @@ -146,33 +199,39 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/openapi3/defs/pets.yaml'), + api: Path.join( + __dirname, + "./fixtures/openapi3/defs/pets.yaml" + ), handlers: { pets: { - '{id}': { + "{id}": { get(req, h) { - return 'bad response type'; - } - } - } + return "bad response type"; + }, + }, + }, }, - outputvalidation: true - } + outputvalidation: true, + }, }); const response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets/0' + method: "GET", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 500, `${response.request.path} failed.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 500, + `${response.request.path} failed.` + ); + } catch (error) { t.fail(error.message); } }); - t.test('additional type properties', async function (t) { + t.test("additional type properties", async function (t) { t.plan(11); const server = new Hapi.Server(); @@ -181,130 +240,176 @@ Test('test plugin', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/openapi3/defs/pets-types.yaml'), - handlers: Path.join(__dirname, './fixtures/handlers'), - outputvalidation: true - } + api: Path.join( + __dirname, + "./fixtures/openapi3/defs/pets-types.yaml" + ), + handlers: Path.join(__dirname, "./fixtures/handlers"), + outputvalidation: true, + }, }); let response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets' + method: "GET", + url: "/v1/petstore/pets", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Cat', - coupon: 97 - } + id: "0", + name: "Cat", + coupon: 97, + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Cat', - coupon: 'Welcome' - } + id: "0", + name: "Cat", + coupon: "Welcome", + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Dog', - coupon: false - } + id: "0", + name: "Dog", + coupon: false, + }, }); - t.strictEqual(response.statusCode, 400, `${response.request.path} payload bad.`); - + t.strictEqual( + response.statusCode, + 400, + `${response.request.path} payload bad.` + ); + response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Cat', - birthday: '2006-01-02' - } + id: "0", + name: "Cat", + birthday: "2006-01-02", + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Dog', - birthday: null - } + id: "0", + name: "Dog", + birthday: null, + }, }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets' + method: "GET", + url: "/v1/petstore/pets", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Dog', - birthday: '' - } + id: "0", + name: "Dog", + birthday: "", + }, }); - t.strictEqual(response.statusCode, 400, `${response.request.path} payload bad.`); + t.strictEqual( + response.statusCode, + 400, + `${response.request.path} payload bad.` + ); response = await server.inject({ - method: 'POST', - url: '/v1/petstore/pets', + method: "POST", + url: "/v1/petstore/pets", payload: { - id: '0', - name: 'Dog', - birthday: 'yesterday' - } + id: "0", + name: "Dog", + birthday: "yesterday", + }, }); - t.strictEqual(response.statusCode, 400, `${response.request.path} payload bad.`); + t.strictEqual( + response.statusCode, + 400, + `${response.request.path} payload bad.` + ); response = await server.inject({ - method: 'GET', - url: '/v1/petstore/pets/0' + method: "GET", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); response = await server.inject({ - method: 'DELETE', - url: '/v1/petstore/pets/0' + method: "DELETE", + url: "/v1/petstore/pets/0", }); - t.strictEqual(response.statusCode, 200, `${response.request.path} OK.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 200, + `${response.request.path} OK.` + ); + } catch (error) { t.fail(error.message); } }); - t.test('security object', async function (t) { + t.test("security object", async function (t) { t.plan(3); const server = new Hapi.Server(); @@ -312,30 +417,42 @@ Test('test plugin', function (t) { try { await server.register({ plugin: StubAuthTokenScheme }); - server.auth.strategy('api_key', 'stub-auth-token', { - validateFunc: StubAuthTokenScheme.buildValidateFunc('12345') + server.auth.strategy("api_key", "stub-auth-token", { + validateFunc: StubAuthTokenScheme.buildValidateFunc("12345"), }); - server.auth.strategy('api_key2', 'stub-auth-token', { - validateFunc: StubAuthTokenScheme.buildValidateFunc('98765') + server.auth.strategy("api_key2", "stub-auth-token", { + validateFunc: StubAuthTokenScheme.buildValidateFunc("98765"), }); await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/openapi3/defs/pets_authed.yaml'), - handlers: Path.join(__dirname, './fixtures/handlers') - } + api: Path.join( + __dirname, + "./fixtures/openapi3/defs/pets_authed.yaml" + ), + handlers: Path.join(__dirname, "./fixtures/handlers"), + }, }); - t.ok(server.plugins.openapi.getApi, 'server.plugins.openapi.api exists.'); - t.ok(server.plugins.openapi.setHost, 'server.plugins.openapi.setHost exists.'); - - server.plugins.openapi.setHost('api.paypal.com'); - - t.strictEqual(server.plugins.openapi.getApi().host, 'api.paypal.com', 'server.plugins.openapi.setHost set host.'); - } - catch (error) { + t.ok( + server.plugins.openapi.getApi, + "server.plugins.openapi.api exists." + ); + t.ok( + server.plugins.openapi.setHost, + "server.plugins.openapi.setHost exists." + ); + + server.plugins.openapi.setHost("api.paypal.com"); + + t.strictEqual( + server.plugins.openapi.getApi().host, + "api.paypal.com", + "server.plugins.openapi.setHost set host." + ); + } catch (error) { t.fail(error.message); } - }); -}); \ No newline at end of file + }); +}); diff --git a/test/test-utils.js b/test/test-utils.js index e69a2f1..6a96434 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -1,89 +1,88 @@ -'use strict'; +"use strict"; -const Test = require('tape'); -const Utils = require('../lib/utils'); +const Test = require("tape"); +const Utils = require("../lib/utils"); -Test('utils', function (t) { - - t.test('prefix', function (t) { +Test("utils", function (t) { + t.test("prefix", function (t) { t.plan(3); - var str = 'foobar'; + var str = "foobar"; - str = Utils.prefix(str, 'foo'); + str = Utils.prefix(str, "foo"); - t.equal(str, 'foobar', 'string had prefix so is the same.'); + t.equal(str, "foobar", "string had prefix so is the same."); - str = 'bar'; + str = "bar"; - str = Utils.prefix(str, 'foo'); + str = Utils.prefix(str, "foo"); - t.equal(str, 'foobar', 'string did not have prefix so was changed.'); + t.equal(str, "foobar", "string did not have prefix so was changed."); - t.equal(Utils.prefix(undefined, 'foo'), 'foo', 'handled undefined.'); + t.equal(Utils.prefix(undefined, "foo"), "foo", "handled undefined."); }); - t.test('unprefix', function (t) { + t.test("unprefix", function (t) { t.plan(3); - var str = 'foobar'; + var str = "foobar"; - str = Utils.unprefix(str, 'foo'); + str = Utils.unprefix(str, "foo"); - t.equal(str, 'bar', 'string had prefix so is changed.'); + t.equal(str, "bar", "string had prefix so is changed."); - str = 'bar'; + str = "bar"; - str = Utils.unprefix(str, 'foo'); + str = Utils.unprefix(str, "foo"); - t.equal(str, 'bar', 'string did not have prefix so was not changed.'); + t.equal(str, "bar", "string did not have prefix so was not changed."); - t.equal(Utils.unprefix(undefined, 'foo'), '', 'handled undefined.'); + t.equal(Utils.unprefix(undefined, "foo"), "", "handled undefined."); }); - t.test('suffix', function (t) { + t.test("suffix", function (t) { t.plan(3); - var str = 'foobar'; + var str = "foobar"; - str = Utils.suffix(str, 'bar'); + str = Utils.suffix(str, "bar"); - t.equal(str, 'foobar', 'string had suffix so is the same.'); + t.equal(str, "foobar", "string had suffix so is the same."); - str = 'foo'; + str = "foo"; - str = Utils.suffix(str, 'bar'); + str = Utils.suffix(str, "bar"); - t.equal(str, 'foobar', 'string did not have suffix so was changed.'); + t.equal(str, "foobar", "string did not have suffix so was changed."); - t.equal(Utils.suffix(undefined, 'foo'), 'foo', 'handled undefined.'); + t.equal(Utils.suffix(undefined, "foo"), "foo", "handled undefined."); }); - t.test('unsuffix', function (t) { + t.test("unsuffix", function (t) { t.plan(3); - var str = 'foobar'; + var str = "foobar"; - str = Utils.unsuffix(str, 'bar'); + str = Utils.unsuffix(str, "bar"); - t.equal(str, 'foo', 'string had suffix so is changed.'); + t.equal(str, "foo", "string had suffix so is changed."); - str = 'foo'; + str = "foo"; - str = Utils.unsuffix(str, 'bar'); + str = Utils.unsuffix(str, "bar"); - t.equal(str, 'foo', 'string did not have suffix so was not changed.'); + t.equal(str, "foo", "string did not have suffix so was not changed."); - t.equal(Utils.unsuffix(undefined, 'foo'), '', 'handled undefined.'); + t.equal(Utils.unsuffix(undefined, "foo"), "", "handled undefined."); }); - t.test('ends with', function (t) { + t.test("ends with", function (t) { t.plan(2); - t.ok(Utils.endsWith('foobar', 'bar'), 'foobar ends with bar'); - t.ok(!Utils.endsWith('foobar', 'x'), 'foobar doesn\'t end with x'); + t.ok(Utils.endsWith("foobar", "bar"), "foobar ends with bar"); + t.ok(!Utils.endsWith("foobar", "x"), "foobar doesn't end with x"); }); - t.test('is httpMethod', function (t) { + t.test("is httpMethod", function (t) { const verbs = Utils.verbs; t.plan(verbs.length + 1); @@ -92,7 +91,6 @@ Test('utils', function (t) { t.ok(Utils.isHttpMethod(verb), `${verb} is an http method.`); } - t.ok(!Utils.isHttpMethod('Blerg'), 'Blerg is not an http method.'); + t.ok(!Utils.isHttpMethod("Blerg"), "Blerg is not an http method."); }); - }); diff --git a/test/test-validators.js b/test/test-validators.js index 581b5d8..e6c45c0 100644 --- a/test/test-validators.js +++ b/test/test-validators.js @@ -1,143 +1,155 @@ -const Test = require('tape'); -const Validators = require('../lib/validators'); - -Test('validator special types', function(t) { - const api = { - swagger: '2.0', - info: { - title: 'Minimal', - version: '1.0.0' - }, - paths: { - '/test': { - get: { - description: '', - parameters: [ - { - name: 'dateTime', - in: 'query', - required: false, - type: 'string', - format: 'date-time' - } - ], - responses: { - 200: { - description: 'default response' - } - } +const Test = require("tape"); +const Validators = require("../lib/validators"); + +Test("validator special types", function (t) { + const api = { + swagger: "2.0", + info: { + title: "Minimal", + version: "1.0.0", }, - post: { - description: '', - parameters: [ - { - name: 'payload', - in: 'body', - required: true, - schema: { - type: 'object', - required: ['requiredProperty'], - properties: { - requiredProperty: { - type: 'string' - } - } - } - } - ] + paths: { + "/test": { + get: { + description: "", + parameters: [ + { + name: "dateTime", + in: "query", + required: false, + type: "string", + format: "date-time", + }, + ], + responses: { + 200: { + description: "default response", + }, + }, + }, + post: { + description: "", + parameters: [ + { + name: "payload", + in: "body", + required: true, + schema: { + type: "object", + required: ["requiredProperty"], + properties: { + requiredProperty: { + type: "string", + }, + }, + }, + }, + ], + }, + }, + "/test/{foo*}": { + get: { + description: "", + parameters: [ + { + name: "foo*", + in: "path", + required: true, + type: "string", + }, + ], + responses: { + 200: { + description: "default response", + }, + }, + }, + }, + }, + }; + + const validator = Validators.create(api); + + t.test("valid date-time", async function (t) { + t.plan(1); + + const { validate } = validator.makeValidator( + api.paths["/test"].get.parameters[0] + ); + + try { + validate("1995-09-07T10:40:52Z"); + t.pass("valid date-time"); + } catch (error) { + t.fail(error.message); + } + }); + + t.test("invalid date-time", async function (t) { + t.plan(1); + + const { validate } = validator.makeValidator( + api.paths["/test"].get.parameters[0] + ); + + const timestamp = Date.now(); + + try { + validate(timestamp); + t.fail(`${timestamp} should be invalid.`); + } catch (error) { + t.pass(`${timestamp} is invalid.`); + } + }); + + t.test("validate multi-segment paths", async function (t) { + t.plan(1); + + const v = validator.makeAll(api.paths["/test/{foo*}"].get.parameters); + + const keys = Object.keys(v.validate.params.describe().keys); + + if (keys.length === 1 && keys[0] === "foo") { + return t.pass(`${keys.join(", ")} are valid.`); + } + t.fail(`${keys.join(", ")} are invalid.`); + }); + + t.test("validate missing body parameter", async function (t) { + t.plan(1); + + const { validate } = validator.makeValidator( + api.paths["/test"].post.parameters[0] + ); + + try { + validate(); + t.fail('"undefined" should be invalid'); + } catch (error) { + t.equal( + error.message, + '"payload" is required', + "received expected payload error message" + ); } - }, - '/test/{foo*}': { - get: { - description: '', - parameters: [ - { - name: 'foo*', - in: 'path', - required: true, - type: 'string' - } - ], - responses: { - 200: { - description: 'default response' - } - } + }); + + t.test("validate empty object with required property", async function (t) { + t.plan(1); + + const { validate } = validator.makeValidator( + api.paths["/test"].post.parameters[0] + ); + + try { + validate({}); + t.fail('"undefined" should be invalid'); + } catch (error) { + t.match( + error.message, + /"requiredProperty" is required/, + "received expected property error message" + ); } - } - } - }; - - const validator = Validators.create(api); - - t.test('valid date-time', async function(t) { - t.plan(1); - - const { validate } = validator.makeValidator( - api.paths['/test'].get.parameters[0] - ); - - try { - validate('1995-09-07T10:40:52Z'); - t.pass('valid date-time'); - } catch (error) { - t.fail(error.message); - } - }); - - t.test('invalid date-time', async function(t) { - t.plan(1); - - const { validate } = validator.makeValidator( - api.paths['/test'].get.parameters[0] - ); - - const timestamp = Date.now(); - - try { - validate(timestamp); - t.fail(`${timestamp} should be invalid.`); - } catch (error) { - t.pass(`${timestamp} is invalid.`); - } - }); - - t.test('validate multi-segment paths', async function(t) { - t.plan(1); - - const v = validator.makeAll(api.paths['/test/{foo*}'].get.parameters); - - const keys = Object.keys(v.validate.params.describe().keys); - - if (keys.length === 1 && keys[0] === 'foo') { - return t.pass(`${keys.join(', ')} are valid.`); - } - t.fail(`${keys.join(', ')} are invalid.`); - }); - - t.test('validate missing body parameter', async function(t) { - t.plan(1); - - const { validate } = validator.makeValidator(api.paths['/test'].post.parameters[0]); - - try { - validate(); - t.fail('"undefined" should be invalid'); - } catch (error) { - t.equal(error.message, '"payload" is required', "received expected payload error message"); - } - }); - - t.test('validate empty object with required property', async function(t) { - t.plan(1); - - const { validate } = validator.makeValidator(api.paths['/test'].post.parameters[0]); - - try { - validate({}); - t.fail('"undefined" should be invalid'); - } catch (error) { - t.match(error.message, /"requiredProperty" is required/, "received expected property error message"); - } - }) + }); }); diff --git a/test/test_xoptions.js b/test/test_xoptions.js index 2872bb5..c725c18 100644 --- a/test/test_xoptions.js +++ b/test/test_xoptions.js @@ -1,14 +1,12 @@ -'use strict'; +"use strict"; -const Test = require('tape'); -const Path = require('path'); -const OpenAPI = require('../lib'); -const Hapi = require('@hapi/hapi'); +const Test = require("tape"); +const Path = require("path"); +const OpenAPI = require("../lib"); +const Hapi = require("@hapi/hapi"); - -Test('x-hapi-options', function (t) { - - t.test('overrides', async function (t) { +Test("x-hapi-options", function (t) { + t.test("overrides", async function (t) { t.plan(1); try { @@ -17,34 +15,38 @@ Test('x-hapi-options', function (t) { await server.register({ plugin: OpenAPI, options: { - api: Path.join(__dirname, './fixtures/defs/form_xoptions.json'), + api: Path.join( + __dirname, + "./fixtures/defs/form_xoptions.json" + ), handlers: { upload: { post: function (req, h) { - return { - upload: req.payload.toString() + return { + upload: req.payload.toString(), }; - } - } - } - } + }, + }, + }, + }, }); const response = await server.inject({ - method: 'POST', - url: '/v1/forms/upload', + method: "POST", + url: "/v1/forms/upload", headers: { - 'content-type': 'application/x-www-form-urlencoded' + "content-type": "application/x-www-form-urlencoded", }, - payload: 'name=thing&upload=data' + payload: "name=thing&upload=data", }); - t.strictEqual(response.statusCode, 404, `${response.request.path} not found due to isInternal.`); - } - catch (error) { + t.strictEqual( + response.statusCode, + 404, + `${response.request.path} not found due to isInternal.` + ); + } catch (error) { t.fail(error.message); } - }); - });