From fcaf2053a521d12f26248688ad405672511fcc02 Mon Sep 17 00:00:00 2001 From: Marco Friso Date: Wed, 22 Apr 2020 17:44:00 +0200 Subject: [PATCH 1/2] test(oas3): add server variables object validation Closes APIARY-6482 --- .../lib/parser/oas/parseServerObject.js | 35 ++++++++ .../unit/parser/oas/parseServerObject-test.js | 81 +++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/packages/fury-adapter-oas3-parser/lib/parser/oas/parseServerObject.js b/packages/fury-adapter-oas3-parser/lib/parser/oas/parseServerObject.js index 453a284cd..824e76b06 100644 --- a/packages/fury-adapter-oas3-parser/lib/parser/oas/parseServerObject.js +++ b/packages/fury-adapter-oas3-parser/lib/parser/oas/parseServerObject.js @@ -15,6 +15,38 @@ const parseServerVariableObject = require('./parseServerVariableObject'); const name = 'Server Object'; const requiredKeys = ['url']; +const validateVariableInURL = (context, object) => { + const url = object.getValue('url'); + const variables = object.get('variables'); + const parseResult = new context.namespace.elements.ParseResult(); + + const variablesKeys = variables.content.map(variable => variable.key.toValue()); + const urlVariables = url + .match(/{(.*?)}/g) + .map(x => x.replace(/[{}]/g, '')); + + // if you define a variable that is not in URL it warns (and the variable is discarded). + variablesKeys.forEach((key) => { + if (urlVariables.indexOf(key) === -1) { + parseResult.push(createWarning(context.namespace, + `Server variable '${key}' is not present in the URL and will be discarted`, variables)); + + variables.remove(key); + } + }); + + // if you place a variable in the URL and its not in variables you get a warning that the variable is missing. + urlVariables.forEach((key) => { + if (!variables.hasKey(key)) { + parseResult.push(createWarning(context.namespace, + `URL variable '${key}' is missing within the server variables`, variables)); + } + }); + + parseResult.push(object); + return parseResult; +}; + const parseMember = context => R.cond([ [hasKey('description'), parseString(context, name, false)], [hasKey('url'), parseString(context, name, true)], @@ -23,6 +55,8 @@ const parseMember = context => R.cond([ [R.T, createInvalidMemberWarning(context.namespace, name)], ]); +const hasVariables = object => object.hasKey('variables'); + /** * Parse the OpenAPI 'Server Object' (`#/server`) * @see http://spec.openapis.org/oas/v3.0.3#server-object @@ -32,6 +66,7 @@ const parseMember = context => R.cond([ const parseServerObject = context => pipeParseResult(context.namespace, R.unless(isObject, createWarning(context.namespace, `'${name}' is not an object`)), parseObject(context, name, parseMember(context), requiredKeys, [], true), + R.when(hasVariables, R.curry(validateVariableInURL)(context)), (object) => { const resource = new context.namespace.elements.Resource(); diff --git a/packages/fury-adapter-oas3-parser/test/unit/parser/oas/parseServerObject-test.js b/packages/fury-adapter-oas3-parser/test/unit/parser/oas/parseServerObject-test.js index 34b485956..4059a3f84 100644 --- a/packages/fury-adapter-oas3-parser/test/unit/parser/oas/parseServerObject-test.js +++ b/packages/fury-adapter-oas3-parser/test/unit/parser/oas/parseServerObject-test.js @@ -102,6 +102,87 @@ describe('#parseServerObject', () => { expect(parseResult).to.contain.warning("'Server Object' 'variables' is not an object"); }); + it("warns when a variable in server 'variables' is not defined in the URL and removes it", () => { + const server = new namespace.elements.Object({ + url: 'https://{username}.gigantic-server.com/{version}/', + variables: { + username: { + default: 'Mario', + description: 'API user name', + }, + version: { + default: '1.0', + }, + location: { + default: 'Prague', + }, + }, + }); + + const parseResult = parse(context)(server); + expect(parseResult.length).to.equal(2); + expect(parseResult).to.contain.annotations; + expect(parseResult).to.contain.warning("Server variable 'location' is not present in the URL and will be discarted"); + + const resource = parseResult.get(0); + expect(resource).to.be.instanceof(namespace.elements.Resource); + + const { hrefVariables } = resource; + const firstHrefVariable = hrefVariables.content.content[0]; + const secondHrefVariable = hrefVariables.content.content[1]; + + expect(hrefVariables).to.be.instanceof(namespace.elements.HrefVariables); + expect(hrefVariables.length).to.equal(2); + + expect(firstHrefVariable).to.be.instanceof(namespace.elements.Member); + expect(firstHrefVariable.key.toValue()).to.equal('username'); + expect(firstHrefVariable.value.default).to.equal('Mario'); + expect(firstHrefVariable.value.description.toValue()).to.equal('API user name'); + + expect(secondHrefVariable).to.be.instanceof(namespace.elements.Member); + expect(secondHrefVariable.key.toValue()).to.equal('version'); + expect(secondHrefVariable.value.default).to.equal('1.0'); + }); + + it("warns when a URL defined variable is missing from 'variables'", () => { + const server = new namespace.elements.Object({ + url: 'https://{username}.{server}/{version}/', + variables: { + username: { + default: 'Mario', + description: 'API user name', + }, + version: { + default: '1.0', + }, + }, + }); + + const parseResult = parse(context)(server); + expect(parseResult.length).to.equal(2); + expect(parseResult).to.contain.annotations; + expect(parseResult).to.contain.warning("URL variable 'server' is missing within the server variables"); + + const resource = parseResult.get(0); + expect(resource).to.be.instanceof(namespace.elements.Resource); + + const { hrefVariables } = resource; + const firstHrefVariable = hrefVariables.content.content[0]; + const secondHrefVariable = hrefVariables.content.content[1]; + + expect(hrefVariables).to.be.instanceof(namespace.elements.HrefVariables); + expect(hrefVariables.length).to.equal(2); + + expect(firstHrefVariable).to.be.instanceof(namespace.elements.Member); + expect(firstHrefVariable.key.toValue()).to.equal('username'); + expect(firstHrefVariable.value.default).to.equal('Mario'); + expect(firstHrefVariable.value.description.toValue()).to.equal('API user name'); + + expect(secondHrefVariable).to.be.instanceof(namespace.elements.Member); + expect(secondHrefVariable.key.toValue()).to.equal('version'); + expect(secondHrefVariable.value.default).to.equal('1.0'); + }); + it('parse server object with variables', () => { const server = new namespace.elements.Object({ url: 'https://{username}.gigantic-server.com/{version}', From a39f6347797b8989ad1e39dc2de72181d4fc62b5 Mon Sep 17 00:00:00 2001 From: Marco Friso Date: Thu, 23 Apr 2020 15:52:17 +0200 Subject: [PATCH 2/2] refactor(oas3): modify parseServerObject --- .../lib/parser/oas/parseServerObject.js | 15 +++++++-------- .../unit/parser/oas/parseServerObject-test.js | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/fury-adapter-oas3-parser/lib/parser/oas/parseServerObject.js b/packages/fury-adapter-oas3-parser/lib/parser/oas/parseServerObject.js index 824e76b06..6be7e6a8a 100644 --- a/packages/fury-adapter-oas3-parser/lib/parser/oas/parseServerObject.js +++ b/packages/fury-adapter-oas3-parser/lib/parser/oas/parseServerObject.js @@ -15,21 +15,20 @@ const parseServerVariableObject = require('./parseServerVariableObject'); const name = 'Server Object'; const requiredKeys = ['url']; -const validateVariableInURL = (context, object) => { +const validateVariablesInURL = (context, object) => { const url = object.getValue('url'); const variables = object.get('variables'); const parseResult = new context.namespace.elements.ParseResult(); - const variablesKeys = variables.content.map(variable => variable.key.toValue()); const urlVariables = url .match(/{(.*?)}/g) .map(x => x.replace(/[{}]/g, '')); - // if you define a variable that is not in URL it warns (and the variable is discarded). - variablesKeys.forEach((key) => { - if (urlVariables.indexOf(key) === -1) { + // if you define a variable that is not in URL it warns (and the variable is ignored). + variables.keys().forEach((key) => { + if (!urlVariables.includes(key)) { parseResult.push(createWarning(context.namespace, - `Server variable '${key}' is not present in the URL and will be discarted`, variables)); + `Server variable '${key}' is not present in the URL and will be ignored`, variables)); variables.remove(key); } @@ -39,7 +38,7 @@ const validateVariableInURL = (context, object) => { urlVariables.forEach((key) => { if (!variables.hasKey(key)) { parseResult.push(createWarning(context.namespace, - `URL variable '${key}' is missing within the server variables`, variables)); + `URL variable '${key}' is missing within the server variables`, object.get('url'))); } }); @@ -66,7 +65,7 @@ const hasVariables = object => object.hasKey('variables'); const parseServerObject = context => pipeParseResult(context.namespace, R.unless(isObject, createWarning(context.namespace, `'${name}' is not an object`)), parseObject(context, name, parseMember(context), requiredKeys, [], true), - R.when(hasVariables, R.curry(validateVariableInURL)(context)), + R.when(hasVariables, R.curry(validateVariablesInURL)(context)), (object) => { const resource = new context.namespace.elements.Resource(); diff --git a/packages/fury-adapter-oas3-parser/test/unit/parser/oas/parseServerObject-test.js b/packages/fury-adapter-oas3-parser/test/unit/parser/oas/parseServerObject-test.js index 4059a3f84..6afbee286 100644 --- a/packages/fury-adapter-oas3-parser/test/unit/parser/oas/parseServerObject-test.js +++ b/packages/fury-adapter-oas3-parser/test/unit/parser/oas/parseServerObject-test.js @@ -122,7 +122,7 @@ describe('#parseServerObject', () => { const parseResult = parse(context)(server); expect(parseResult.length).to.equal(2); expect(parseResult).to.contain.annotations; - expect(parseResult).to.contain.warning("Server variable 'location' is not present in the URL and will be discarted"); + expect(parseResult).to.contain.warning("Server variable 'location' is not present in the URL and will be ignored"); const resource = parseResult.get(0); expect(resource).to.be.instanceof(namespace.elements.Resource);