From f5db98533f0f9593905d32e908c943d3ce519af8 Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Wed, 26 Apr 2017 10:51:20 +0200 Subject: [PATCH 01/24] improved naming convention for importers --- configs/paw/importers/Importer.js | 2 +- configs/paw/importers/postman2/api-flow-config.js | 1 + configs/paw/importers/raml1/api-flow-config.js | 1 + configs/paw/importers/swagger/api-flow-config.js | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/configs/paw/importers/Importer.js b/configs/paw/importers/Importer.js index f755bc5..d23dbb5 100644 --- a/configs/paw/importers/Importer.js +++ b/configs/paw/importers/Importer.js @@ -11,7 +11,7 @@ const methods = {} @registerImporter // eslint-disable-line class SwaggerImporter { static identifier = source.identifier - static title = source.title + static title = source.humanTitle static fileExtensions = []; diff --git a/configs/paw/importers/postman2/api-flow-config.js b/configs/paw/importers/postman2/api-flow-config.js index 3490b2f..e4aeb26 100644 --- a/configs/paw/importers/postman2/api-flow-config.js +++ b/configs/paw/importers/postman2/api-flow-config.js @@ -21,6 +21,7 @@ export const serializers = [ export const source = { identifier: 'com.luckymarmot.PawExtensions.PostmanCollectionV2Importer', title: 'PostmanCollectionV2Importer', + humanTitle: 'Postman Collection v2.0 Importer', format: PostmanCollectionV2Parser.__meta__.format, version: PostmanCollectionV2Parser.__meta__.version } diff --git a/configs/paw/importers/raml1/api-flow-config.js b/configs/paw/importers/raml1/api-flow-config.js index 0c3077c..e79032a 100644 --- a/configs/paw/importers/raml1/api-flow-config.js +++ b/configs/paw/importers/raml1/api-flow-config.js @@ -21,6 +21,7 @@ export const serializers = [ export const source = { identifier: 'com.luckymarmot.PawExtensions.RAML1Importer', title: 'RAML1Importer', + humanTitle: 'RAML v1.0 Importer', format: RAMLV1Parser.__meta__.format, version: RAMLV1Parser.__meta__.version } diff --git a/configs/paw/importers/swagger/api-flow-config.js b/configs/paw/importers/swagger/api-flow-config.js index bd52df7..aa8ec8b 100644 --- a/configs/paw/importers/swagger/api-flow-config.js +++ b/configs/paw/importers/swagger/api-flow-config.js @@ -21,6 +21,7 @@ export const serializers = [ export const source = { identifier: 'com.luckymarmot.PawExtensions.SwaggerImporter', title: 'SwaggerImporter', + humanTitle: 'Swagger v2.0 Importer', format: SwaggerV2Parser.__meta__.format, version: SwaggerV2Parser.__meta__.version } From 901148e7bfe5de61a98faef6e4d654c0d14f96c5 Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Wed, 26 Apr 2017 10:55:45 +0200 Subject: [PATCH 02/24] removed v prefix for versions --- configs/paw/generators/postman2/api-flow-config.js | 2 +- configs/paw/generators/raml1/api-flow-config.js | 2 +- configs/paw/generators/swagger/api-flow-config.js | 2 +- configs/paw/importers/postman2/api-flow-config.js | 2 +- configs/paw/importers/raml1/api-flow-config.js | 2 +- configs/paw/importers/swagger/api-flow-config.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/paw/generators/postman2/api-flow-config.js b/configs/paw/generators/postman2/api-flow-config.js index e36f7e6..5b8e04f 100644 --- a/configs/paw/generators/postman2/api-flow-config.js +++ b/configs/paw/generators/postman2/api-flow-config.js @@ -21,7 +21,7 @@ export const serializers = [ export const target = { identifier: 'com.luckymarmot.PawExtensions.PostmanCollectionGenerator', title: 'PostmanCollectionGenerator', - humanTitle: 'Postman Collection v2.0', + humanTitle: 'Postman Collection 2.0', format: PostmanV2Serializer.__meta__.format, version: PostmanV2Serializer.__meta__.version } diff --git a/configs/paw/generators/raml1/api-flow-config.js b/configs/paw/generators/raml1/api-flow-config.js index 1ffb2ba..b8034e6 100644 --- a/configs/paw/generators/raml1/api-flow-config.js +++ b/configs/paw/generators/raml1/api-flow-config.js @@ -21,7 +21,7 @@ export const serializers = [ export const target = { identifier: 'com.luckymarmot.PawExtensions.RAML1Generator', title: 'RAML1Generator', - humanTitle: 'RAML v1.0', + humanTitle: 'RAML 1.0', format: RAMLV1Serializer.__meta__.format, version: RAMLV1Serializer.__meta__.version } diff --git a/configs/paw/generators/swagger/api-flow-config.js b/configs/paw/generators/swagger/api-flow-config.js index 8c16845..548c48e 100644 --- a/configs/paw/generators/swagger/api-flow-config.js +++ b/configs/paw/generators/swagger/api-flow-config.js @@ -21,7 +21,7 @@ export const serializers = [ export const target = { identifier: 'com.luckymarmot.PawExtensions.SwaggerGenerator', title: 'SwaggerGenerator', - humanTitle: 'Swagger v2.0', + humanTitle: 'Swagger 2.0', format: SwaggerV2Serializer.__meta__.format, version: SwaggerV2Serializer.__meta__.version } diff --git a/configs/paw/importers/postman2/api-flow-config.js b/configs/paw/importers/postman2/api-flow-config.js index e4aeb26..a0f1ffb 100644 --- a/configs/paw/importers/postman2/api-flow-config.js +++ b/configs/paw/importers/postman2/api-flow-config.js @@ -21,7 +21,7 @@ export const serializers = [ export const source = { identifier: 'com.luckymarmot.PawExtensions.PostmanCollectionV2Importer', title: 'PostmanCollectionV2Importer', - humanTitle: 'Postman Collection v2.0 Importer', + humanTitle: 'Postman Collection 2.0 Importer', format: PostmanCollectionV2Parser.__meta__.format, version: PostmanCollectionV2Parser.__meta__.version } diff --git a/configs/paw/importers/raml1/api-flow-config.js b/configs/paw/importers/raml1/api-flow-config.js index e79032a..41f85e7 100644 --- a/configs/paw/importers/raml1/api-flow-config.js +++ b/configs/paw/importers/raml1/api-flow-config.js @@ -21,7 +21,7 @@ export const serializers = [ export const source = { identifier: 'com.luckymarmot.PawExtensions.RAML1Importer', title: 'RAML1Importer', - humanTitle: 'RAML v1.0 Importer', + humanTitle: 'RAML 1.0 Importer', format: RAMLV1Parser.__meta__.format, version: RAMLV1Parser.__meta__.version } diff --git a/configs/paw/importers/swagger/api-flow-config.js b/configs/paw/importers/swagger/api-flow-config.js index aa8ec8b..daf9cbc 100644 --- a/configs/paw/importers/swagger/api-flow-config.js +++ b/configs/paw/importers/swagger/api-flow-config.js @@ -21,7 +21,7 @@ export const serializers = [ export const source = { identifier: 'com.luckymarmot.PawExtensions.SwaggerImporter', title: 'SwaggerImporter', - humanTitle: 'Swagger v2.0 Importer', + humanTitle: 'Swagger 2.0 Importer', format: SwaggerV2Parser.__meta__.format, version: SwaggerV2Parser.__meta__.version } From 20ed8cd89999f090dc6202d650e0d414873b9ad9 Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Wed, 26 Apr 2017 15:37:21 +0200 Subject: [PATCH 03/24] fixed packing error --- scripts/pack.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/pack.sh b/scripts/pack.sh index 5e7f6aa..8b231c6 100644 --- a/scripts/pack.sh +++ b/scripts/pack.sh @@ -11,9 +11,13 @@ do echo "extension: $extension" if [ -d "$extension" ] then + cd "$extension/.." + echo "in $extension --- $(ls)" package=$(echo "$extension" | sed -E 's-.*/([^/]+)-\1-') - zip -r "$package.zip" "$extension/"; + echo "package --- $package" + zip -r "$package.zip" "./$package"; mv "./$package.zip" "$base/releases/paw/"; + cd "$base" fi; done; cd "$base" From 55416ccde256647708ec803a906ed7319510c77f Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Wed, 26 Apr 2017 15:59:10 +0200 Subject: [PATCH 04/24] fix empty paths bug --- src/parsers/swagger/v2.0/Parser.js | 8 ++++++-- src/parsers/swagger/v2.0/__tests__/Parser.spec.js | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/parsers/swagger/v2.0/Parser.js b/src/parsers/swagger/v2.0/Parser.js index 28c9557..99a4923 100644 --- a/src/parsers/swagger/v2.0/Parser.js +++ b/src/parsers/swagger/v2.0/Parser.js @@ -89,7 +89,7 @@ methods.handleUnkownFormat = () => { * @returns {void} */ methods.handleInvalidSwagger = () => { - const message = 'Invalid Swagger File (invalid schema / version < 2.0)' + const message = 'Invalid Swagger File (invalid schema / version < 2.0)\n' + tv4.error const error = new __errors__.NotASwaggerV2(message) throw error } @@ -893,7 +893,11 @@ methods.getRequestsForResource = (store, security, resourceObject) => { * @param {Entry} entry: a operation object, as an entry * @returns {URL} the updated path endpoint */ -methods.updatePathWithParametersFromOperations = (store, path, { key, value }) => { +methods.updatePathWithParametersFromOperations = (store, path, { key, value } = {}) => { + if (!key && !value) { + return path + } + const container = methods.getParameterContainerForOperation(store, value, key) const pathParams = container.get('path') diff --git a/src/parsers/swagger/v2.0/__tests__/Parser.spec.js b/src/parsers/swagger/v2.0/__tests__/Parser.spec.js index 5c88d19..cd2469d 100644 --- a/src/parsers/swagger/v2.0/__tests__/Parser.spec.js +++ b/src/parsers/swagger/v2.0/__tests__/Parser.spec.js @@ -2189,7 +2189,9 @@ describe('parsers/swagger/v2.0/Parser.js', () => { }) : new ParameterContainer() }) + /* eslint-disable no-undefined */ const inputs = [ + [ new Store(), new URL(), undefined ], [ new Store(), new URL(), {} ], [ new Store(), new URL(), { key: 'abc', value: 123 } ], [ @@ -2214,8 +2216,10 @@ describe('parsers/swagger/v2.0/Parser.js', () => { { key: 'userId', value: 123 } ] ] + /* eslint-enable no-undefined */ const expected = [ + new URL(), new URL(), new URL(), new URL({ url: 'https://echo.paw.cloud/user/123' }), From 2cd15edf2da371668be8d547973c4717db39083f Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Wed, 26 Apr 2017 16:23:32 +0200 Subject: [PATCH 05/24] fixed a bug where implicit host would fail to be correctly replaced when used with local files --- src/loaders/swagger/Loader.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/loaders/swagger/Loader.js b/src/loaders/swagger/Loader.js index 44b392e..ff66f33 100644 --- a/src/loaders/swagger/Loader.js +++ b/src/loaders/swagger/Loader.js @@ -159,9 +159,22 @@ methods.fixRemotePaths = (options, uri, swagger) => { }) } +methods.fixImplicitHost = (uri) => { + if (!uri) { + return 'localhost' + } + + const host = parse(uri).host + if (!host) { + return 'localhost' + } + + return host +} + methods.fixImplicitUriReferences = (options, uri, swagger) => { if (!swagger.host) { - swagger.host = uri ? parse(uri).host : 'localhost' + swagger.host = methods.fixImplicitHost(uri) } if (!swagger.schemes || !swagger.schemes.length) { From 3f404453ce87c092362c50e9fcfb15e85b456819 Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Wed, 26 Apr 2017 16:28:20 +0200 Subject: [PATCH 06/24] added potential culprit to tv4 validation --- src/parsers/swagger/v2.0/Parser.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/parsers/swagger/v2.0/Parser.js b/src/parsers/swagger/v2.0/Parser.js index 99a4923..a2f9043 100644 --- a/src/parsers/swagger/v2.0/Parser.js +++ b/src/parsers/swagger/v2.0/Parser.js @@ -89,7 +89,9 @@ methods.handleUnkownFormat = () => { * @returns {void} */ methods.handleInvalidSwagger = () => { - const message = 'Invalid Swagger File (invalid schema / version < 2.0)\n' + tv4.error + const message = 'Invalid Swagger File (invalid schema / version < 2.0)\n' + + tv4.error + '\n' + + 'Potential culprit: ' + tv4.error.dataPath const error = new __errors__.NotASwaggerV2(message) throw error } From a4ec0b0df53f7b2669a7dc7b76d71a9160e19bf8 Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Tue, 2 May 2017 15:47:01 +0200 Subject: [PATCH 07/24] fixed a bug where required was ignored. fixed a bug where schema default was ignored. fixed a bug where paths were too expanded for the import of a single request. --- src/parsers/paw/Parser.js | 47 ++++++++-- src/parsers/paw/__tests__/Parser.spec.js | 112 +++++++++++++++++++++++ 2 files changed, 152 insertions(+), 7 deletions(-) diff --git a/src/parsers/paw/Parser.js b/src/parsers/paw/Parser.js index dd9f2e9..59e75a3 100644 --- a/src/parsers/paw/Parser.js +++ b/src/parsers/paw/Parser.js @@ -354,17 +354,45 @@ methods.updateHostKeyWithLongestCommonPathname = ({ entries, lcPathname }, key) } } +/** + * converts a PawRequest into an array of entries of size 1 where the key is the extracted origin of + * the urlBase of the requests. + * @param {Array} request: the requests to group by host + * @returns {Array>} the corresponding sequence of entries. + */ +methods.convertSingleRequestIntoRequestEntry = (request) => { + const baseUrl = request.getUrlBase() + const numberOfSlashes = parse(baseUrl).slashes ? 3 : 1 + const origin = baseUrl.split('/').slice(0, numberOfSlashes).join('/') + return [ + { key: origin, value: request } + ] +} + +/** + * converts an array of PawRequests into an array of entries where the keys are the urlBase of the + * requests, except if there is only one request. + * @param {Array} requests: the requests to group by host + * @returns {Array>} the corresponding sequence of entries. + */ +methods.convertRequestsIntoRequestEntries = (requests) => { + if (requests.length === 1) { + return methods.convertSingleRequestIntoRequestEntry(requests[0]) + } + + return requests.map(request => { + return { key: request.getUrlBase(), value: request } + }) +} + /** * extracts common hosts from a list of requests, and assigns each request to its corresponding host * @param {Array} requests: the requests to group by host * @returns {Seq>} the corresponding sequence of entries. */ methods.extractCommonHostsFromRequests = (requests) => { - const hosts = requests - .map((request) => { - return { key: request.getUrlBase(), value: request } - }) - .reduce(methods.addHostEntryToHostMap, {}) + const requestEntries = methods.convertRequestsIntoRequestEntries(requests) + const hosts = requestEntries.reduce(methods.addHostEntryToHostMap, {}) return new OrderedMap(hosts).map(methods.updateHostKeyWithLongestCommonPathname).valueSeq() } @@ -944,7 +972,11 @@ methods.convertRequestVariableDVIntoParameter = ( }) } } - const { name, value, schema, type, description } = variable + const { name, value, required, schema, type, description } = variable + + const defaultValue = typeof (schema || {}).default !== 'undefined' ? + schema.default : + value.getEvaluatedString() const param = new Parameter({ in: location, @@ -952,7 +984,8 @@ methods.convertRequestVariableDVIntoParameter = ( name: name || paramName, type: type || 'string', description: description || null, - default: value.getEvaluatedString(), + required: required || false, + default: defaultValue, constraints: List([ new Constraint.JSONSchema(schema) ]), diff --git a/src/parsers/paw/__tests__/Parser.spec.js b/src/parsers/paw/__tests__/Parser.spec.js index 24ed9f0..0c9479e 100644 --- a/src/parsers/paw/__tests__/Parser.spec.js +++ b/src/parsers/paw/__tests__/Parser.spec.js @@ -499,8 +499,52 @@ describe('parsers/paw/Parser.js', () => { }) }) + describe('@convertSingleRequestIntoRequestEntry', () => { + it('should work', () => { + const inputs = [ + { getUrlBase: () => '' }, + { getUrlBase: () => 'http://paw.cloud/base/users' }, + { getUrlBase: () => 'file:///base/users' }, + { getUrlBase: () => 'urn:paw.cloud/base/users' } + ] + const expected = [ + [ { key: '', value: { getUrlBase: () => null } } ], + [ { key: 'http://paw.cloud', value: { getUrlBase: () => 'http://paw.cloud/base/users' } } ], + [ { key: 'file://', value: { getUrlBase: () => 'file:///base/users' } } ], + [ { key: 'urn:paw.cloud', value: { getUrlBase: () => 'urn:paw.cloud/base/users' } } ] + ] + const actual = inputs.map(input => __internals__.convertSingleRequestIntoRequestEntry(input)) + expect(JSON.stringify(actual, null, 2)).toEqual(JSON.stringify(expected, null, 2)) + }) + }) + + describe('@convertRequestsIntoRequestEntries', () => { + it('should work', () => { + spyOn(__internals__, 'convertSingleRequestIntoRequestEntry').andCall(r => { + return [ { key: r.getUrlBase() * 2, value: r } ] + }) + + const inputs = [ + [], + [ { getUrlBase: () => 123 } ], + [ { getUrlBase: () => 123 }, { getUrlBase: () => 234 } ] + ] + const expected = [ + [], + [ { key: 123 * 2, value: { getUrlBase: () => 123 } } ], + [ + { key: 123, value: { getUrlBase: () => 123 } }, + { key: 234, value: { getUrlBase: () => 234 } } + ] + ] + const actual = inputs.map(input => __internals__.convertRequestsIntoRequestEntries(input)) + expect(JSON.stringify(actual, null, 2)).toEqual(JSON.stringify(expected, null, 2)) + }) + }) + describe('@extractCommonHostsFromRequests', () => { it('should work if underlying methods are correct', () => { + spyOn(__internals__, 'convertRequestsIntoRequestEntries').andCall(v => v) spyOn(__internals__, 'addHostEntryToHostMap').andReturn({ a: 321, b: 321, c: 321 }) spyOn(__internals__, 'updateHostKeyWithLongestCommonPathname').andReturn(123) const req1 = { getUrlBase: () => {} } @@ -1313,6 +1357,74 @@ describe('parsers/paw/Parser.js', () => { ) expect(actual).toEqual(expected) }) + + it('should prefer schema default over value', () => { + const variable = { + name: 'userId', + value: { getEvaluatedString: () => 123 }, + schema: { default: '' }, + type: 345 + } + const request = { getVariableById: () => variable } + const location = 'queries' + const contexts = List([ 567, 567 ]) + const paramName = 'UserId' + const input = { variableUUID: 678 } + const expected = { + key: 'UserId', + value: new Parameter({ + in: 'queries', + key: 'userId', + name: 'userId', + type: 345, + description: null, + default: '', + constraints: List([ + new Constraint.JSONSchema({ default: '' }) + ]), + applicableContexts: List([ 567, 567 ]) + }) + } + + const actual = __internals__.convertRequestVariableDVIntoParameter( + request, location, contexts, input, paramName + ) + expect(actual).toEqual(expected) + }) + + it('should work if no schema', () => { + const variable = { + name: 'userId', + value: { getEvaluatedString: () => 123 }, + schema: null, + type: 345 + } + const request = { getVariableById: () => variable } + const location = 'queries' + const contexts = List([ 567, 567 ]) + const paramName = 'UserId' + const input = { variableUUID: 678 } + const expected = { + key: 'UserId', + value: new Parameter({ + in: 'queries', + key: 'userId', + name: 'userId', + type: 345, + description: null, + default: 123, + constraints: List([ + new Constraint.JSONSchema(null) + ]), + applicableContexts: List([ 567, 567 ]) + }) + } + + const actual = __internals__.convertRequestVariableDVIntoParameter( + request, location, contexts, input, paramName + ) + expect(actual).toEqual(expected) + }) }) describe('@convertRequestVariableDSIntoParameter', () => { From d3d21fb4ce4913988eeaf8da33e8ec10b2cb0097 Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Tue, 2 May 2017 15:47:42 +0200 Subject: [PATCH 08/24] fixed a bug where paths could start by another character than / --- src/serializers/swagger/v2.0/Serializer.js | 7 ++++++- .../swagger/v2.0/__tests__/Serializer.spec.js | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/serializers/swagger/v2.0/Serializer.js b/src/serializers/swagger/v2.0/Serializer.js index 30c4aa9..29f7c26 100644 --- a/src/serializers/swagger/v2.0/Serializer.js +++ b/src/serializers/swagger/v2.0/Serializer.js @@ -918,6 +918,11 @@ methods.getTagDefinitions = (api) => { */ methods.getPathFromResource = (resource) => { const path = resource.get('path').toURLObject(List([ '{', '}' ])).pathname + + if (path[0] !== '/') { + return '/' + path + } + return path } @@ -1329,7 +1334,7 @@ methods.convertRequestToOperationObject = (store, { consumes, produces }, reques } const value = operation - return { key, value } + return { key: key.toLowerCase(), value } } /* eslint-enable max-statements */ diff --git a/src/serializers/swagger/v2.0/__tests__/Serializer.spec.js b/src/serializers/swagger/v2.0/__tests__/Serializer.spec.js index 8fce06f..f6343e1 100644 --- a/src/serializers/swagger/v2.0/__tests__/Serializer.spec.js +++ b/src/serializers/swagger/v2.0/__tests__/Serializer.spec.js @@ -10,6 +10,7 @@ import Reference from '../../../../models/Reference' import Parameter from '../../../../models/Parameter' import Resource from '../../../../models/Resource' import URL from '../../../../models/URL' +import URLComponent from '../../../../models/URLComponent' import Store from '../../../../models/Store' import Constraint from '../../../../models/Constraint' import ParameterContainer from '../../../../models/ParameterContainer' @@ -1650,6 +1651,24 @@ describe('serializers/swagger/v2.0/Serializer.js', () => { expect(actual).toEqual(expected) }) + + it('should work even if path not starting with /', () => { + const input = new Resource({ + path: new URL().set( + 'pathname', + new URLComponent({ + componentName: 'pathname', + string: 'my/path/{pathId}', + variableDelimiters: List([ '{', '}' ]) + }) + ) + }) + + const expected = '/my/path/{pathId}' + const actual = __internals__.getPathFromResource(input) + + expect(actual).toEqual(expected) + }) }) describe('@convertInterfaceToTagString', () => { From 60d674eeeaef86f46e4f1d73d10aeaf0351177b2 Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Tue, 2 May 2017 16:11:48 +0200 Subject: [PATCH 09/24] added drafter.js to dependencies --- package.json | 1 + yarn.lock | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/package.json b/package.json index 44dcad5..2070b35 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "babel-plugin-transform-flow-strip-types": "6.3.15", "babel-polyfill": "6.3.14", "babel-runtime": "6.3.19", + "drafter.js": "^2.6.6", "immutable": "^3.8.1", "js-yaml": "3.5.5", "json-schema-faker": "^0.3.7", diff --git a/yarn.lock b/yarn.lock index d138830..8955699 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1629,6 +1629,10 @@ dot-case@^1.1.0: dependencies: sentence-case "^1.1.2" +drafter.js: + version "2.6.6" + resolved "https://registry.yarnpkg.com/drafter.js/-/drafter.js-2.6.6.tgz#6706978eb11afedec4de61ad8b90e006d68e3a3c" + duplexify@^3.2.0: version "3.5.0" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.0.tgz#1aa773002e1578457e9d9d4a50b0ccaaebcbd604" From 4061a654d3780f35c948d971f06cba40886da39a Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Tue, 9 May 2017 17:46:16 +0200 Subject: [PATCH 10/24] added api-blueprint serializer, with documentation. still needs tests. WILL FAIL --- src/api-flow-config.js | 4 +- .../api-blueprint/1A/Serializer.js | 1308 ++++ .../e2e/internal-apiblueprint-1a/e2e.spec.js | 91 + .../test-case-0/input.json | 5711 +++++++++++++++++ .../test-case-0/output.json | 441 ++ .../test-case-1/input.json | 3418 ++++++++++ .../test-case-1/output.json | 0 7 files changed, 10972 insertions(+), 1 deletion(-) create mode 100644 src/serializers/api-blueprint/1A/Serializer.js create mode 100644 testing/e2e/internal-apiblueprint-1a/e2e.spec.js create mode 100644 testing/e2e/internal-apiblueprint-1a/test-case-0/input.json create mode 100644 testing/e2e/internal-apiblueprint-1a/test-case-0/output.json create mode 100644 testing/e2e/internal-apiblueprint-1a/test-case-1/input.json create mode 100644 testing/e2e/internal-apiblueprint-1a/test-case-1/output.json diff --git a/src/api-flow-config.js b/src/api-flow-config.js index f18ccd6..f7500aa 100644 --- a/src/api-flow-config.js +++ b/src/api-flow-config.js @@ -14,6 +14,7 @@ import SwaggerV2Serializer from './serializers/swagger/v2.0/Serializer' import RAMLV1Serializer from './serializers/raml/v1.0/Serializer' import InternalSerializer from './serializers/internal/Serializer' import PostmanV2Serializer from './serializers/postman/v2.0/Serializer' +import ApiBlueprint1ASerializer from './serializers/api-blueprint/1A/Serializer' export const loaders = [ SwaggerLoader, @@ -33,7 +34,8 @@ export const serializers = [ SwaggerV2Serializer, RAMLV1Serializer, InternalSerializer, - PostmanV2Serializer + PostmanV2Serializer, + ApiBlueprint1ASerializer ] export const environment = Environment diff --git a/src/serializers/api-blueprint/1A/Serializer.js b/src/serializers/api-blueprint/1A/Serializer.js new file mode 100644 index 0000000..64233c9 --- /dev/null +++ b/src/serializers/api-blueprint/1A/Serializer.js @@ -0,0 +1,1308 @@ +/** + * An Api Blueprint 1A serializer. + * This implementation has the following limitations: + * - TODO: write the limitations of this serializer (wrt to the api-blueprint spec) + * + * NOTE: we allow use of undefined in this file as it works nicely with JSON.stringify, which drops + * keys with a value of undefined. + * ``` + * const swagger = { info, security } + * ``` + * is easier to read than + * ``` + * const swagger = { info } + * if (security) { + * swagger.security = security + * } + * ``` + * + */ + /* eslint-disable no-undefined */ +import { List, OrderedMap } from 'immutable' + +import Reference from '../../../models/Reference' + +import { flatten } from '../../../utils/fp-utils' + +const __meta__ = { + format: 'api-bluepint', + version: '1A' +} + +const methods = {} + +/** + * A Serializer to convert Api Records into Api Blueprint 1A. + */ +export class ApiBlueprintSerializer { + static __meta__ = __meta__ + + /** + * serializes an Api into an Api Blueprint 1A formatted string + * @param {Api} api: the api to convert + * @returns {string} the corresponding api blueprint, as a string + */ + static serialize(api) { + return methods.serialize(api) + } + + /** + * returns a quality score for a content string wrt. to the collection v2 format. + * @param {String} content: the content of the file to analyze + * @returns {number} the quality of the content + */ + static validate(content) { + return methods.validate(content) + } +} + +/** + * returns a quality score for a content string wrt. to the collection v2 format. + * @param {String} content: the content of the file to analyze + * @returns {number} the quality of the content + */ +methods.validate = () => { + return 0 +} + +/** + * extracts an endpoint from the endpoint store, if any exists + * @param {Api} api: the api to get the store from + * @returns {URL|null} the corresponding endpoint + */ +methods.getRootHostFromEndpoint = (api) => { + const endpoint = api.getIn([ 'store', 'endpoint' ]).valueSeq().get(0) + return endpoint || null +} + +/** + * extracts an endpoint from the variable store, if any exists + * @param {Api} api: the api to get the store from + * @returns {URL|null} the corresponding endpoint + */ +methods.getRootHostFromVariable = (api) => { + const variable = api.getIn([ 'store', 'variable' ]).valueSeq().get(0) + if (!variable) { + return null + } + + const values = variable.get('values') + if (!values) { + return null + } + + const value = values.valueSeq().get(0) + if (!value) { + return null + } + + return new URL({ url: value }) +} + +/** + * extracts a root endpoint from the Api (from either the endpoint store or from the variable store) + * @param {Api} api: the api to get the store from + * @returns {URL|null} the corresponding root endpoint + */ +methods.getRootHostForApi = (api) => { + const host = methods.getRootHostFromEndpoint(api) || + methods.getRootHostFromVariable(api) + + return host +} + +/** + * creates a format version section + * @returns {ContentSection} the corresponding section that represents the format version + */ +methods.createFormatVersionSection = () => { + const section = { + type: 'content', + value: 'FORMAT: 1A' + } + + return section +} + +/** + * create a format host section + * @param {Api} api: the api from which to get a root endpoint + * @returns {ContentSection|null} the corresponding section that represents the host of the api, + * if it exists. + */ +methods.createFormatHostSection = (api) => { + const host = methods.getRootHostForApi(api) + + if (!host) { + return null + } + + const rootUrl = host.generate() + + if (!rootUrl) { + return null + } + + return { + type: 'content', + value: 'HOST: ' + rootUrl + } +} + +/** + * creates the metadata section of an ABP from an api. + * @param {Api} api: the api from which to get the metadata pertaining to the ABP file + * @returns {AbstractSection} the corresponding section that contains all the metadata about the ABP + * file. + */ +methods.createMetadataSection = (api) => { + const section = { + type: 'metadata', + abstract: true, + content: [ + methods.createFormatVersionSection(api), + methods.createFormatHostSection(api) + ].filter(v => !!v), + separator: '\n' + } + + return section +} + +/** + * creates the root title section of an ABP + * @param {Api} api: the api whose title needs to be extracted + * @returns {HeaderSection<1>} the corresponding section describing the title of the Api. + */ +methods.createRootTitleSection = (api) => { + const title = api.getIn([ 'info', 'title' ]) + + return { + type: 'header', + depth: 1, + value: { + abstract: true, + content: [ title || 'Unnamed API' ], + separator: '' + } + } +} + +/** + * creates a root description section of an ABP + * @param {Api} api: the api whose description needs to be extracted + * @returns {ContentSection|null} the corresponding section describing the api, if such description + * exists + */ +methods.createRootDescriptionSection = (api) => { + const description = api.getIn([ 'info', 'description' ]) + + if (!description) { + return null + } + + return { + type: 'content', + value: description + } +} + +/** + * creates an Api Name and Overview Section + * @param {Api} api: the api from which to get the title and description + * @returns {AbstractSection} the corresponding section containing the name and description of the + * Api + */ +methods.createApiNameAndOverviewSection = (api) => { + const section = { + type: 'overview', + abstract: true, + content: [ + methods.createRootTitleSection(api), + methods.createRootDescriptionSection(api) + ].filter(v => !!v), + separator: '\n' + } + + return section +} + +/** + * creates the resource group header section for an ABP. + * We use the strategy of grouping all the resources together inside a single group, as truncating + * a hierarchy of groups at the depth 1 only has a lot of meaning if the api was already grouped + * based on semantics instead of paths, which is definitely not true for RAML. + * @returns {HeaderSection} the corresponding section describing the header for the Group Resources + */ +methods.createResourceGroupHeaderSection = () => { + return { + type: 'header', + depth: 2, + value: { + abstract: true, + content: [ 'Group Resources' ], + separator: '' + } + } +} + +/** + * converts a resource into a resource entry based on the path value of the resource. + * The idea is to use the path to group together resources that have the same paths. + * @param {Resource} resource: the resource to convert into a resource entry + * @returns {Entry} the corresponding resource as an entry + */ +methods.convertResourceIntoResourceEntryBasedOnPath = (resource) => { + return { + key: resource.get('path').generate(List([ '{', '}' ])), + value: resource + } +} + +/** + * merges a resource entry in an accumulator based on whether the key/path is already present in + * the object. If the key/path is already present in the accumulator, then we merge the `methods` + * field of the resource contained in the accumulator with the new resource. + * @param {Object} acc: the accumulator to update with the entry + * @param {Entry} entry: the entry to merge with the accumulator + * @param {string} entry.key: the path of the resource. This is used to merge resources that share + * the same key + * @param {Resource} entry.value: the resource to merge + * @returns {Object} the updated accumulator + */ +methods.mergeResourceEntriesBasedOnKey = (acc, { key, value }) => { + if (!acc[key]) { + acc[key] = value + return acc + } + + const $methods = OrderedMap().merge(acc[key].get('methods'), value.get('methods')) + acc[key] = acc[key].set('methods', $methods) + return acc +} + +/** + * merges the resources of an api together based on their paths. + * @param {Api} api: the api from which to get the resource Map + * @returns {OrderedMap} the updated resource Map with merged resources based on + * their path + */ +methods.getMergedResourcesBasedOnPathFromApi = (api) => { + const resources = api.get('resources') + .map(methods.convertResourceIntoResourceEntryBasedOnPath) + .reduce(methods.mergeResourceEntriesBasedOnKey, {}) + + return OrderedMap(resources) +} + +/** + * extracts a QueryString section from a resource + * @param {Api} api: the api containing the store to use to resolve shared parameters if needed + * @param {Resource} resource: the resource to get the QueryString from + * @returns {AbstractSection|null} the corresponding section that contains all the query parameters + * as a string respecting ABP format + */ +methods.extractQueryStringFromResource = (api, resource) => { + const store = api.get('store') + const queryParams = resource.get('methods') + .map(request => request.get('parameters')) + .map(paramContainer => paramContainer.resolve(store)) + .map(paramContainer => paramContainer.get('queries').valueSeq().toArray()) + .reduce(flatten, []) + .map(param => param.get('key')) + .filter(v => !!v) + + const params = Array.from(new Set(queryParams)) + + if (!params.length) { + return null + } + + return { + type: 'query-params', + abstract: true, + content: [ + '{?', + { + abstract: true, + content: params, + separator: ',' + }, + '}' + ], + separator: '' + } +} + +/** + * creates a Resource Title Section from a Resource. + * @param {Api} api: the api to use to resolve shared parameters + * @param {Resource} resource: the resource to get the title from + * @returns {HeaderSection|null} the corresponding section that represents the title of the + * resource, which is composed of the name of the resource, its path, and queryString, if they + * exist. + */ +methods.createResourceTitleSection = (api, resource) => { + const title = resource.get('name') + const path = resource.get('path').generate(List([ '{', '}' ])) + const queryString = methods.extractQueryStringFromResource(api, resource) + + const section = { + type: 'header', + depth: 3, + value: { + abstract: true, + content: [ + title, + title ? ' [' : null, + path, + queryString, + title ? ']' : null + ].filter(v => !!v), + separator: '' + } + } + + if (!section.value.content.length) { + return null + } + + return section +} + +/** + * creates a resource description section from a Resource + * @param {Resource} resource: the resource whose description needs extraction + * @returns {ContentSection|null} the section containing the description of the resource, if it + * exist. + */ +methods.createResourceDescriptionSection = (resource) => { + const description = resource.get('description') + + if (!description) { + return null + } + + return { + type: 'content', + value: description + } +} + +/** + * creates an operation title section from an Operation/Request + * @param {Request} operation: the operation from which to extract a title + * @returns {HeaderSection|null} the section describing the title of the operation, which contains + * the name and method of the operation + */ +methods.createOperationTitleSection = (operation) => { + const name = operation.get('name') + const method = operation.get('method').toUpperCase() + + const section = { + type: 'header', + depth: 4, + value: { + abstract: true, + content: [ + name, + name ? ' [' : null, + method, + name ? ']' : null + ].filter(v => !!v), + separator: '' + } + } + + if (!section.value.content.length) { + return null + } + + return section +} + +/** + * creates an operation description section from an Operation/Request. + * @param {Request} operation: the request from which to get a description + * @returns {ContentSection|null} the corresponding content section containing the description of + * the operation, if it exists. + */ +methods.createOperationDescriptionSection = (operation) => { + const description = operation.get('description') + + if (!description) { + return null + } + + return { + type: 'content', + value: description + } +} + +/** + * extracts the key of a Parameter as a content section. + * @param {Parameter} parameter: the parameter to extract the key section from + * @returns {ContentSection|null} the corresponding content section containing the key of the + * parameter. + */ +methods.createParameterKeySegment = (parameter) => { + const key = parameter.get('key') + + if (!key) { + return null + } + + const section = { + abstract: true, + content: [ key ], + separator: '' + } + + return section +} + +/** + * creates a section that contains the optional fields used in the description of a parameter + * @param {JSONSchema} schema: the JSON Schema representing the parameter + * @param {Parameter} parameter: the parameter itself + * @returns {AbstractSection|null} the section containing the optional fields of the description of + * a parameter, in the expected format. + */ +methods.createParameterOptionalSegment = (schema, parameter) => { + const type = schema.type || parameter.get('type') + const optionalText = parameter.get('required') ? 'required' : 'optional' + + const optionalData = [ + type, optionalText + ].filter(v => !!v) + + if (!optionalData.length) { + return null + } + + const section = { + abstract: true, + content: [ + '(', + { + abstract: true, + content: optionalData, + separator: ', ' + }, + ')' + ], + separator: '' + } + + return section +} + +/** + * creates a section containing the description of a parameter. + * @param {JSONSchema} schema: the schema representing the parameter + * @param {Parameter} parameter: the parameter itself + * @returns {AbstractSection|null} the corresponding description section + */ +methods.createParameterDescriptionSegment = (schema, parameter) => { + const description = schema.description || parameter.get('description') + + if (!description) { + return null + } + + const section = { + abstract: true, + content: [ '-', description ], + separator: ' ' + } + + return section +} + +/** + * creates the Payload section of a Parameter + * @param {JSONSchema} schema: the JSON Schema representing a parameter + * @param {Parameter} parameter: the parameter itself + * @returns {AbstractSection|null} the section describing the payload associated with a Parameter + */ +methods.createParameterPayloadSection = (schema, parameter) => { + const section = { + abstract: true, + content: [ + methods.createParameterKeySegment(parameter), + methods.createParameterOptionalSegment(schema, parameter), + methods.createParameterDescriptionSegment(schema, parameter) + ].filter(v => !!v), + separator: ' ' + } + + if (!section.content.length) { + return null + } + + return section +} + +/** + * converts a Parameter into a section + * @param {Parameter} parameter: the parameter to convert + * @returns {AbstractSection} the section that contains all the information relative to the + * parameter + */ +methods.convertParameterIntoParamSection = (parameter) => { + if (!parameter) { + return null + } + + const schema = parameter.getJSONSchema() + + const section = { + type: 'parameter', + abstract: true, + content: [ + methods.createParameterPayloadSection(schema, parameter), + methods.createOperationRequestSchemaAssetSection(schema) + ].filter(v => !!v), + separator: '\n' + } + + if (!section.content.length) { + return null + } + + return section +} + +/** + * extracts path parameters name from a Path + * @param {Api} api: the api to use to resolve shared parameters + * @param {URL} path: the path to extract the parameters from + * @returns {Array} the corresponding array that contains all the path parameters. + */ +methods.extractPathParamsFromPath = (api, path) => { + if (path.getIn([ 'parameter', 'superType' ]) !== 'sequence') { + return [] + } + + const sequence = path.getIn([ 'parameter', 'value' ]) + if (!sequence || !sequence.size) { + return [] + } + + return sequence + .filter(param => param.get('key')) + .valueSeq() + .toJS() +} + +/** + * creates the Parameters section for an operation + * @param {Api} api: the api to use to resolve shared parameters + * @param {URL} path: the path from which to get the path parameters + * @param {ParameterContainer} container: the ParameterContainer that holds all the query + * parameters. This container is already resolved and filtered based on a set of constraints. + * @returns {AbstractSection|null} the corresponding section that contains all the information + * pertaining to the query and path parameters + */ +methods.createOperationParametersSection = (api, path, container) => { + const queryParamSections = container + .get('queries') + .map(methods.convertParameterIntoParamSection) + .valueSeq() + .toJS() + + const pathParamSections = methods.extractPathParamsFromPath(api, path) + .map(methods.convertParameterIntoParamSection) + + const paramSections = [ ...queryParamSections, ...pathParamSections ] + + if (!paramSections.length) { + return null + } + + const section = { + type: 'list-item', + abstract: true, + content: [ + 'Parameters', + { + type: 'list', + depth: 2, + value: paramSections + } + ], + separator: '\n' + } + + return section +} + +/** + * extract a content type value from a list of constraints + * @param {List} constraints: the list of constraints from which to extract the content + * type parameters + * @returns {string} the corresponding content type default value, iff there exists one content type + * parameter. + */ +methods.extractContentTypeFromConstraints = (constraints) => { + const contentTypeParams = constraints + .filter(param => param.get('key') === 'Content-Type') + + if (contentTypeParams.size !== 1) { + return null + } + + return contentTypeParams.valueSeq().getIn([ 0, 'default' ]) || null +} + +/** + * creates the section associated with the title of an Operation Request section + * @param {List} constraints: the constraints from which to extract the content-type + * @returns {AbstractSection|ContentSection} the corresponding section that contains the title of + * the Request section, with the associated content-type if it exists. + */ +methods.createOperationRequestTitleSection = (constraints) => { + const contentType = methods.extractContentTypeFromConstraints(constraints) + + if (!contentType) { + return { + type: 'content', + value: 'Request' + } + } + + return { + abstract: true, + content: [ + 'Request (', + contentType, + ')' + ], + separator: '' + } +} + +/** + * creates a partial asset section from a header Parameter. + * @param {Parameter} parameter: the header to convert into a partial asset + * @returns {string} the partial asset that describe the header. + */ +methods.convertHeaderParameterIntoHeaderSection = (parameter) => { + const name = parameter.get('key') + const schema = parameter.getJSONSchema() + const value = parameter.get('default') || schema.default || (schema.enum || [])[0] || null + + return name + ': ' + value +} + +/** + * creates the Headers Section and its payload from an Operation/Request ParameterContainer + * @param {Api} api: the api used to resolve shared object, such as authentication methods + * @param {List} constraints: the constraints used filter the ParameterContainer + * @param {ParameterContainer} container: the ParameterContainer containing all the header + * parameters. + * @returns {ListSection} the section that contains all the information pertaining to the headers + * of the operation + */ +methods.createOperationRequestHeaderSection = (api, constraints, container) => { + const headers = container.get('headers') + + if (!headers.size) { + return null + } + + const headersSection = headers.map(methods.convertHeaderParameterIntoHeaderSection) + .valueSeq() + .toJS() + .join('\n') + // TODO add authentication header + + return { + type: 'list', + depth: 2, + value: [ + { + abstract: true, + content: [ + 'Headers', + { + type: 'asset', + depth: 4, + value: headersSection + } + ], + separator: '\n' + } + ] + } +} + +/** + * creates an asset section representing a schema for an operation request section + * @param {JSONSchema} schema: the JSON schema representing a parameter + * @returns {AssetSection} the corresponding asset section that represent a JSON Schema + */ +methods.createOperationRequestSchemaAssetSection = (schema) => { + const section = { + type: 'asset', + depth: 4, + value: schema + } + + return section +} + +/** + * extracts the JSON Schema for a single body parameter + * @param {OrderedMap} bodyParams: a map of Parameters, that should be of size 1. + * @returns {JSONSchema} the corresponding JSON Schema + */ +methods.getSchemaForSingleBodyParameter = (bodyParams) => { + const schema = bodyParams.valueSeq().get(0).getJSONSchema() + + return schema +} + +/** + * extracts the JSON Schema for multiple body parameter + * @param {OrderedMap} bodyParams: a map of Parameters to get the JSON Schemas + * of. + * @returns {JSONSchema} a JSONSchema describing an object with all the JSON Schemas as properties + */ +methods.getSchemaForMultipleBodyParameters = (bodyParams) => { + const properties = bodyParams + .map(param => param.getJSONSchema()) + .reduce(($properties, schema, key) => { + const title = schema.title || schema['x-title'] || key + $properties[title] = schema + return $properties + }, {}) + + return { type: 'object', properties } +} + +/** + * extracts a JSON Schema from a Map of Parameters + * @param {OrderedMap} bodyParams: a Map of Parameters to convert into a single + * JSON Schema + * @returns {JSONSchema|null} the corresponding JSON Schema + */ +methods.getSchemaFromBodyParameters = (bodyParams) => { + if (bodyParams.size === 1) { + return methods.getSchemaForSingleBodyParameter(bodyParams) + } + + if (bodyParams.size > 1) { + return methods.getSchemaForMultipleBodyParameters(bodyParams) + } + + return null +} + +/** + * create a Schema Section for an Operation Request + * @param {ParameterContainer} container: a ParameterContainer from which to get the body parameters + * @returns {ListSection} the corresponding section with its associated payload section that + * describe the schema of the body of a request. + */ +methods.createOperationRequestSchemaSection = (container) => { + const bodyParams = container.get('body') + + const schema = methods.getSchemaFromBodyParameters(bodyParams) + + if (!schema) { + return null + } + + const section = { + type: 'list', + depth: 2, + value: [ + { + abstract: true, + content: [ + 'Schema', + methods.createOperationRequestSchemaAssetSection(schema) + ], + separator: '\n' + } + ] + } + + return section +} + +/** + * creates an action section from an Operation/Request + * @param {Api} api: the api used to resolve shared objects + * @param {List} constraints: the list of constraints used to filter the + * ParameterContainer block of the Operation + * @param {ParameterContainer} container: the parameter container, resolved and filtered based on + * the list of constraints. + * @param {Request} operation: the operation to use to create the Request action section. + * @returns {AbstractSection|null} the corresponding `Request` section + */ +methods.createOperationRequestSection = (api, constraints, container, operation) => { + const section = { + type: 'list-item', + abstract: true, + content: [ + methods.createOperationRequestTitleSection(constraints), + methods.createOperationRequestHeaderSection(api, constraints, container, operation), + methods.createOperationRequestSchemaSection(container) + ].filter(v => !!v), + separator: '\n' + } + + if (section.content.length < 2) { + return null + } + + return section +} + +/** + * creates a Response title section from a Response and a List of constraints + * @param {Response} response: the response to use to extract the information pertaining to the + * title of the Response action section + * @param {List} constraints: the list of constraints used as a view for this response + * @returns {AbstractSection|ContentSection} the corresponding title section + */ +methods.createOperationResponseTitleSection = (response, constraints) => { + const rawCode = parseInt(response.get('code'), 10) + const code = rawCode ? rawCode : 200 + const contentType = methods.extractContentTypeFromConstraints(constraints) + + if (!contentType) { + return { + type: 'content', + value: 'Response ' + code + } + } + + return { + abstract: true, + content: [ + 'Response ', + code, + ' (', + contentType, + ')' + ], + separator: '' + } +} + +/** + * creates a response action section from a response + * @param {Api} api: the api used to resolve shared parameters + * @param {Response} response: the response to convert into a Response action section + * @returns {AbstractSection|null} the corresponding response action section + */ +methods.createOperationResponseSection = (api, response) => { + if (!response) { + return null + } + + const constraints = response.getIn([ 'contexts', 0, 'constraints' ]) || List() + const container = response.get('parameters').resolve(api.get('store')).filter(constraints) + + const section = { + type: 'request', + abstract: true, + content: [ + methods.createOperationResponseTitleSection(response, constraints), + methods.createOperationRequestHeaderSection(api, constraints, container, null), + methods.createOperationRequestSchemaSection(container) + ].filter(v => !!v), + separator: '\n' + } + + return section +} + +/** + * creates a default response section for requests that do not have responses + * @returns {Array} the corresponding array of response sections + */ +methods.createDefaultResponseSection = () => { + const section = { + type: 'content', + value: 'Response 200' + } + + return [ section ] +} + +/** + * creates an array of response action sections from an Operation/Request + * @param {Api} api: the api used to resolve shared objects + * @param {Request} operation: the request from which to get the Responses + * @returns {Array} the corresponding array of sections describing + * the possible responses to an operation + */ +methods.createOperationResponseSections = (api, operation) => { + const responses = operation.get('responses') + + const responseSections = responses + .map(response => { + if (response instanceof Reference) { + return api.getIn([ 'store', 'response', response.get('uuid') ]) + } + + return response + }) + .map(response => methods.createOperationResponseSection(api, response)) + .filter(v => !!v) + .toJS() + + if (!responseSections.length) { + return methods.createDefaultResponseSection() + } + + return responseSections +} + +/** + * creates the payload section associated with an Operation/Request + * @param {Api} api: the api to use to resolve shared objects + * @param {URL} path: the path of the operation. This is used to extract Parameters associated with + * an operation + * @param {Request} operation: the request to extract a payload section from + * @returns {ListSection|null} the corresponding list section describing an Operation + */ +methods.createOperationContentSection = (api, path, operation) => { + const contextConstraints = operation.getIn([ 'contexts', 0, 'constraints' ]) || List() + const container = operation.get('parameters').resolve(api.get('store')).filter(contextConstraints) + + const section = { + type: 'list', + depth: 0, + value: [ + methods.createOperationParametersSection(api, path, container), + methods.createOperationRequestSection(api, contextConstraints, container, operation), + ...methods.createOperationResponseSections(api, operation) + ].filter(v => !!v) + } + + if (!section.value.length) { + return null + } + + return section +} + +/** + * converts an Operation/Request into a section + * @param {Api} api: the api used to resolve shared objects + * @param {URL} path: the path associated with this operation + * @param {Request} operation: the request itself + * @returns {AbstractSection} the corresponding section + */ +methods.createOperationSection = (api, path, operation) => { + const section = { + type: 'operation', + abstract: true, + content: [ + methods.createOperationTitleSection(operation), + methods.createOperationDescriptionSection(operation), + methods.createOperationContentSection(api, path, operation) + ].filter(v => !!v), + separator: '\n\n' + } + + if (!section.content.length) { + return null + } + + return section +} + +/** + * creates an array of sections describing all the operations of a resource + * @param {Api} api: the api used to resolve shared objects + * @param {Resource} resource: the resource to get all the methods from + * @returns {Array} the corresponding array of sections + */ +methods.createResourceOperationSections = (api, resource) => { + const path = resource.get('path') + return resource.get('methods') + .map(operation => methods.createOperationSection(api, path, operation)) + .filter(v => !!v) + .valueSeq() + .toJS() +} + +/** + * converts a Resource into a section + * @param {Api} api: the api used to resolve shared objects + * @param {Resource} resource: the resource to convert + * @returns {AbstractSection|null} the corresponding section + */ +methods.createResourceSection = (api, resource) => { + const section = { + type: 'resource', + abstract: true, + content: [ + methods.createResourceTitleSection(api, resource), + methods.createResourceDescriptionSection(resource), + ...methods.createResourceOperationSections(api, resource) + ].filter(v => !!v), + separator: '\n\n' + } + + if (!section.content.length) { + return null + } + + return section +} + +/** + * extracts an array of sections representing all the resources associated with an Api. + * @param {Api} api: the api to get the resources from + * @returns {Array} the corresponding array of sections + */ +methods.createResourceSections = (api) => { + const resources = methods.getMergedResourcesBasedOnPathFromApi(api) + + const resourcesSections = resources + .map((resource) => methods.createResourceSection(api, resource)) + .valueSeq() + .toJS() + + return resourcesSections +} + +/** + * creates the resource group section from an api + * @param {Api} api: the api to get the resource group from + * @returns {AbstractSection} the corresponding section + */ +methods.createResourceGroupSection = (api) => { + const section = { + type: 'resourceGroups', + abstract: true, + content: [ + methods.createResourceGroupHeaderSection(), + ...methods.createResourceSections(api) + ], + separator: '\n\n' + } + + return section +} + +/** + * creates the header section for the Data Structures section + * @returns {HeaderSection} the corresponding header section + */ +methods.createDataStructuresHeaderSection = () => { + const section = { + type: 'header', + depth: 1, + value: 'Data Structures' + } + + return section +} + +/** + * converts each shared constraint into a Data Structure Section + * @param {Api} api: the api from which to get the shared constraints + * @returns {Array} the corresponding sections + */ +methods.createDataStructureSections = (api) => { + const constraints = api.getIn([ 'store', 'constraint' ]) + + if (!constraints) { + return [] + } + + const sections = constraints + .map((constraint, key) => ({ key, value: constraint.toJSONSchema() })) + .map(({ key, value }) => { + const header = { + type: 'header', + depth: 2, + value: { + abstract: true, + content: [ + key, + ' (', + value.type || 'object', + ')' + ], + separator: '' + } + } + + const schema = { + type: 'asset', + depth: 2, + value: value + } + + const section = { + abstract: true, + content: [ + header, + schema + ], + separator: '\n' + } + + return section + }) + .valueSeq() + .toJS() + + return sections +} + +methods.createDataStructuresSection = (api) => { + const sections = methods.createDataStructureSections(api) + + if (!sections.length) { + return null + } + + const section = { + type: 'dataStructures', + abstract: true, + content: [ + methods.createDataStructuresHeaderSection(), + ...sections + ], + separator: '\n\n' + } + + return section +} + +/** + * converts an Api into a section + * @param {Api} api: the api to convert + * @returns {AbstractSection} the corresponding section + */ +methods.convertApiIntoSection = (api) => { + const sections = [ + methods.createMetadataSection(api), + methods.createApiNameAndOverviewSection(api), + methods.createResourceGroupSection(api), + methods.createDataStructuresSection(api) + ].filter(v => !!v) + + const section = { + abstract: true, + content: sections, + separator: '\n\n' + } + + return section +} + +/** + * stringifies an AbstractSection + * @param {AbstractSection} section: the section to convert into a string + * @returns {string} the corresponding string + */ +methods.stringifyAbstractSection = (section) => { + const sections = section.content.map(methods.stringifySection) + return sections.join(section.separator) +} + +/** + * stringifies a HeaderSection + * @param {HeaderSection} section: the section to convert into a string + * @returns {string} the corresponding string + */ +methods.stringifyHeaderSection = (section) => { + const header = methods.stringifySection(section.value) + + return (new Array(section.depth + 1)).join('#') + ' ' + header +} + +/** + * stringifies a ListSection + * @param {ListSection} section: the section to convert into a string + * @returns {string} the corresponding string + */ +methods.stringifyListSection = (section) => { + const prefix = (new Array(section.depth + 1)).join(' ') + '+ ' + const list = section.value + .map(methods.stringifySection) + .map(listItemString => prefix + listItemString) + + return list.join('\n\n') +} + +/** + * stringifies an AssetSection + * @param {AssetSection} section: the section to convert into a string + * @returns {string} the corresponding string + */ +methods.stringifyAssetSection = (section) => { + const content = typeof section.value === 'string' ? + section.value : + JSON.stringify(section.value, null, 2) + + const offset = new Array(section.depth + 1).join(' ') + const offsetContent = content.split('\n') + .map(line => offset + line) + .join('\n') + return '```\n' + offsetContent + '\n```' +} + +/* eslint-disable max-statements */ +/** + * stringifies a Section + * @param {string|AbstractSection|ContentSection|HeaderSection|ListSection|AssetSection} section: + * the section to convert into a string + * @returns {string} the corresponding string + */ +methods.stringifySection = (section) => { + if (typeof section === 'string') { + return section + } + + if (section.abstract) { + return methods.stringifyAbstractSection(section) + } + + if (section.type === 'content') { + return section.value + } + + if (section.type === 'header') { + return methods.stringifyHeaderSection(section) + } + + if (section.type === 'list') { + return methods.stringifyListSection(section) + } + + if (section.type === 'asset') { + return methods.stringifyAssetSection(section) + } + + return JSON.stringify(section, null, 2) +} +/* eslint-enable max-statements */ + +/** + * serializes an Api into an api-blueprint formatted string + * @param {Api} api: the api to convert + * @returns {string} the corresponding swagger object, as a string + */ +methods.serialize = ({ api }) => { + const section = methods.convertApiIntoSection(api) + const serialized = methods.stringifySection(section) + + return serialized + '\n' +} + +export const __internals__ = methods +export default ApiBlueprintSerializer +/* eslint-enable no-undefined */ diff --git a/testing/e2e/internal-apiblueprint-1a/e2e.spec.js b/testing/e2e/internal-apiblueprint-1a/e2e.spec.js new file mode 100644 index 0000000..aa670d5 --- /dev/null +++ b/testing/e2e/internal-apiblueprint-1a/e2e.spec.js @@ -0,0 +1,91 @@ +/* eslint-disable require-jsdoc */ +require('colors') +import fs from 'fs' +import { resolve } from 'path' + +import expect from 'expect' +const diff = require('diff') + +import ApiFlow from '../../../src/api-flow' + +const compare = (actual, expected = '{}') => { + const delta = diff.diffJson(actual, expected) + if ( + delta.length === 1 && + typeof delta[0].removed === 'undefined' && + typeof delta[0].added === 'undefined' + ) { + return true + } + + /* eslint-disable no-console */ + console.log('\x1b[42m' + + (new Array(6)).join('-------------\n') + '\x1b[0m') + delta.forEach(part => { + let color = 'grey' + if (part.added) { + color = 'green' + } + else if (part.removed) { + color = 'red' + } + process.stderr.write(part.value[color]) + }) + /* eslint-enable no-console */ + + return false +} + +const fixDiff = (actual, index) => { + if (process.env.FIX === 'internal-v1.0--apiblueprint-1A') { + /* eslint-disable no-console */ + console.log('updating spec') + /* eslint-enable no-console */ + fs.writeFileSync(resolve(__dirname, './test-case-' + index + '/output.json'), actual) + } +} + +describe('internal -> api-bluepint 1A', () => { + for (let index = 0; index < 2; index += 1) { + it('should match expected output for test case #' + index, (done) => { + const output = fs.readFileSync( + resolve(__dirname, './test-case-' + index + '/output.json'), + 'utf-8' + ).toString() + // const item = { content: input } + /* eslint-disable no-console */ + try { + const options = ApiFlow.setup({ + options: { + source: { format: 'internal', version: 'v1.0' }, + target: { format: 'api-bluepint', version: '1A' } + } + }) + + ApiFlow + .transform({ + options, + uri: 'file://' + resolve(__dirname, './test-case-' + index + '/input.json') + }) + .then(serialized => { + const success = compare(serialized, output) + if (!success) { + done(new Error('found differences')) + return fixDiff(serialized, index) + } + done() + }, e => { + console.error('got err:\n', e.stack) + done() + }) + .catch() + } + catch (e) { + console.error(e.stack) + expect(true).toEqual(false) + done() + } + /* eslint-enable no-console */ + }) + } +}) diff --git a/testing/e2e/internal-apiblueprint-1a/test-case-0/input.json b/testing/e2e/internal-apiblueprint-1a/test-case-0/input.json new file mode 100644 index 0000000..c6624b2 --- /dev/null +++ b/testing/e2e/internal-apiblueprint-1a/test-case-0/input.json @@ -0,0 +1,5711 @@ +{ + "_model": { + "name": "api.core.models", + "version": "0.1.0" + }, + "resources": { + "/pets": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": "/pets", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + null + ], + "slashes": null, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": "/pets", + "path": "/pets", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/pets", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/pets", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + }, + "methods": { + "post": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "addPet", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + "https:" + ], + "slashes": true, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": null, + "path": null, + "pathname": null, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + } + } + }, + "name": "Add a new pet to the store", + "description": "This is a fairly long description about the API request /pets with Method POST", + "method": "post", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the request", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "body-body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "request", + "uuid": "body-body", + "key": null, + "name": "body", + "description": "Pet object that needs to be added to the store", + "examples": [], + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/Pet" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "petstore_auth", + "overlay": { + "_model": { + "name": "oauth-2.auth.models", + "version": "0.1.0" + }, + "description": null, + "authName": null, + "flow": null, + "authorizationUrl": null, + "tokenUrl": null, + "scopes": [ + { + "key": "write_pets" + }, + { + "key": "read_pets" + } + ] + } + } + ], + "responses": { + "405": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "405", + "description": "Invalid input", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "pet": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "pet", + "uuid": "pet", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + }, + "put": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "updatePet", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Update an existing pet", + "description": null, + "method": "put", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the request", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "body-body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "request", + "uuid": "body-body", + "key": null, + "name": "body", + "description": "Pet object that needs to be added to the store", + "examples": [], + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/Pet" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "petstore_auth", + "overlay": { + "_model": { + "name": "oauth-2.auth.models", + "version": "0.1.0" + }, + "description": null, + "authName": null, + "flow": null, + "authorizationUrl": null, + "tokenUrl": null, + "scopes": [ + { + "key": "write_pets" + }, + { + "key": "read_pets" + } + ] + } + } + ], + "responses": { + "400": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "400", + "description": "Invalid ID supplied", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "globalProduces": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalProduces", + "overlay": null + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "404": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "404", + "description": "Pet not found", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "globalProduces": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalProduces", + "overlay": null + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "405": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "405", + "description": "Validation exception", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "globalProduces": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalProduces", + "overlay": null + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "pet": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "pet", + "uuid": "pet", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + } + }, + "description": null, + "interfaces": {} + }, + "/pets/findByStatus": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": "/pets/findByStatus", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + null + ], + "slashes": null, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": "/pets/findByStatus", + "path": "/pets/findByStatus", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/pets/findByStatus", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/pets/findByStatus", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + }, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "findPetsByStatus", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Finds Pets by status", + "description": "Multiple status values can be provided with comma seperated strings", + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": {}, + "queries": { + "status-query": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "queries", + "usedIn": "request", + "uuid": "status-query", + "key": "status", + "name": "status", + "description": "Status values that need to be considered for filter", + "examples": [], + "type": "array", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + }, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "petstore_auth", + "overlay": { + "_model": { + "name": "oauth-2.auth.models", + "version": "0.1.0" + }, + "description": null, + "authName": null, + "flow": null, + "authorizationUrl": null, + "tokenUrl": null, + "scopes": [ + { + "key": "write_pets" + }, + { + "key": "read_pets" + } + ] + } + } + ], + "responses": { + "200": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "200", + "description": "successful operation", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "response", + "uuid": "body", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "400": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "400", + "description": "Invalid status value", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "pet": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "pet", + "uuid": "pet", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + } + }, + "description": null, + "interfaces": {} + }, + "/pets/findByTags": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": "/pets/findByTags", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + null + ], + "slashes": null, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": "/pets/findByTags", + "path": "/pets/findByTags", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/pets/findByTags", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/pets/findByTags", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + }, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "findPetsByTags", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.", + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": {}, + "queries": { + "tags-query": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "queries", + "usedIn": "request", + "uuid": "tags-query", + "key": "tags", + "name": "tags", + "description": "Tags to filter by", + "examples": [], + "type": "array", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + }, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "petstore_auth", + "overlay": { + "_model": { + "name": "oauth-2.auth.models", + "version": "0.1.0" + }, + "description": null, + "authName": null, + "flow": null, + "authorizationUrl": null, + "tokenUrl": null, + "scopes": [ + { + "key": "write_pets" + }, + { + "key": "read_pets" + } + ] + } + } + ], + "responses": { + "200": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "200", + "description": "successful operation", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "response", + "uuid": "body", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "400": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "400", + "description": "Invalid tag value", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "pet": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "pet", + "uuid": "pet", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + } + }, + "description": null, + "interfaces": {} + }, + "/pets/{petId}": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": "/pets/{petId}", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + null + ], + "slashes": null, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": "/pets/{petId}", + "path": "/pets/{petId}", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/pets/{petId}", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": "sequence", + "value": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/pets/", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "path", + "usedIn": "request", + "uuid": "petId-path", + "key": "petId", + "name": "petId", + "description": "ID of pet that needs to be fetched", + "examples": [], + "type": "integer", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + ], + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + }, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "getPetById", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Find pet by ID", + "description": "Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions", + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": {}, + "queries": {}, + "body": {}, + "path": { + "petId-path": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "path", + "usedIn": "request", + "uuid": "petId-path", + "key": "petId", + "name": "petId", + "description": "ID of pet that needs to be fetched", + "examples": [], + "type": "integer", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + } + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "api_key", + "overlay": null + }, + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "petstore_auth", + "overlay": { + "_model": { + "name": "oauth-2.auth.models", + "version": "0.1.0" + }, + "description": null, + "authName": null, + "flow": null, + "authorizationUrl": null, + "tokenUrl": null, + "scopes": [ + { + "key": "write_pets" + }, + { + "key": "read_pets" + } + ] + } + } + ], + "responses": { + "200": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "200", + "description": "successful operation", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "response", + "uuid": "body", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/Pet" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "400": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "400", + "description": "Invalid ID supplied", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "404": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "404", + "description": "Pet not found", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "pet": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "pet", + "uuid": "pet", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + }, + "post": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "updatePetWithForm", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Updates a pet in the store with form data", + "description": null, + "method": "post", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the request", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/x-www-form-urlencoded" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "name-formData": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "request", + "uuid": "name-formData", + "key": "name", + "name": "name", + "description": "Updated name of the pet", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/x-www-form-urlencoded", + "multipart/form-data" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + ], + "interfaces": {} + }, + "status-formData": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "request", + "uuid": "status-formData", + "key": "status", + "name": "status", + "description": "Updated status of the pet", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/x-www-form-urlencoded", + "multipart/form-data" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + ], + "interfaces": {} + } + }, + "path": { + "petId-path": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "path", + "usedIn": "request", + "uuid": "petId-path", + "key": "petId", + "name": "petId", + "description": "ID of pet that needs to be updated", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + } + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "api_key", + "overlay": null + }, + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "petstore_auth", + "overlay": { + "_model": { + "name": "oauth-2.auth.models", + "version": "0.1.0" + }, + "description": null, + "authName": null, + "flow": null, + "authorizationUrl": null, + "tokenUrl": null, + "scopes": [ + { + "key": "write_pets" + }, + { + "key": "read_pets" + } + ] + } + } + ], + "responses": { + "405": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "405", + "description": "Invalid input", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "pet": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "pet", + "uuid": "pet", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + }, + "delete": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "deletePet", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Deletes a pet", + "description": null, + "method": "delete", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "api_key-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": "api_key-header", + "key": "api_key", + "name": "api_key", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "globalConsumes": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalConsumes", + "overlay": null + } + }, + "queries": {}, + "body": {}, + "path": { + "petId-path": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "path", + "usedIn": "request", + "uuid": "petId-path", + "key": "petId", + "name": "petId", + "description": "Pet id to delete", + "examples": [], + "type": "integer", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + } + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "petstore_auth", + "overlay": { + "_model": { + "name": "oauth-2.auth.models", + "version": "0.1.0" + }, + "description": null, + "authName": null, + "flow": null, + "authorizationUrl": null, + "tokenUrl": null, + "scopes": [ + { + "key": "write_pets" + }, + { + "key": "read_pets" + } + ] + } + } + ], + "responses": { + "400": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "400", + "description": "Invalid pet value", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "pet": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "pet", + "uuid": "pet", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + } + }, + "description": null, + "interfaces": {} + }, + "/stores/order": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": "/stores/order", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + null + ], + "slashes": null, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": "/stores/order", + "path": "/stores/order", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/stores/order", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/stores/order", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + }, + "methods": { + "post": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "placeOrder", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Place an order for a pet", + "description": null, + "method": "post", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "globalConsumes": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalConsumes", + "overlay": null + } + }, + "queries": {}, + "body": { + "body-body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "request", + "uuid": "body-body", + "key": null, + "name": "body", + "description": "order placed for purchasing the pet", + "examples": [], + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/Order" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "auths": [], + "responses": { + "200": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "200", + "description": "successful operation", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "response", + "uuid": "body", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/Order" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "400": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "400", + "description": "Invalid Order", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "store": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "store", + "uuid": "store", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + } + }, + "description": null, + "interfaces": {} + }, + "/stores/order/{orderId}": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": "/stores/order/{orderId}", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + null + ], + "slashes": null, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": "/stores/order/{orderId}", + "path": "/stores/order/{orderId}", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/stores/order/{orderId}", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": "sequence", + "value": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/stores/order/", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "path", + "usedIn": "request", + "uuid": "orderId-path", + "key": "orderId", + "name": "orderId", + "description": "ID of pet that needs to be fetched", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + ], + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + }, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "getOrderById", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Find purchase order by ID", + "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": {}, + "queries": {}, + "body": {}, + "path": { + "orderId-path": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "path", + "usedIn": "request", + "uuid": "orderId-path", + "key": "orderId", + "name": "orderId", + "description": "ID of pet that needs to be fetched", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + } + }, + "contexts": [], + "auths": [], + "responses": { + "200": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "200", + "description": "successful operation", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "response", + "uuid": "body", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/Order" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "400": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "400", + "description": "Invalid ID supplied", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "404": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "404", + "description": "Order not found", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "store": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "store", + "uuid": "store", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + }, + "delete": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "deleteOrder", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Delete purchase order by ID", + "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "method": "delete", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "globalConsumes": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalConsumes", + "overlay": null + } + }, + "queries": {}, + "body": {}, + "path": { + "orderId-path": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "path", + "usedIn": "request", + "uuid": "orderId-path", + "key": "orderId", + "name": "orderId", + "description": "ID of the order that needs to be deleted", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + } + }, + "contexts": [], + "auths": [], + "responses": { + "400": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "400", + "description": "Invalid ID supplied", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "404": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "404", + "description": "Order not found", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "store": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "store", + "uuid": "store", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + } + }, + "description": null, + "interfaces": {} + }, + "/users": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": "/users", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + null + ], + "slashes": null, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": "/users", + "path": "/users", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/users", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/users", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + }, + "methods": { + "post": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "createUser", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Create user", + "description": "This can only be done by the logged in user.", + "method": "post", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "globalConsumes": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalConsumes", + "overlay": null + } + }, + "queries": {}, + "body": { + "body-body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "request", + "uuid": "body-body", + "key": null, + "name": "body", + "description": "Created user object", + "examples": [], + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/User" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "auths": [], + "responses": { + "default": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "default", + "description": "successful operation", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "user": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "user", + "uuid": "user", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + } + }, + "description": null, + "interfaces": {} + }, + "/users/createWithArray": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": "/users/createWithArray", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + null + ], + "slashes": null, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": "/users/createWithArray", + "path": "/users/createWithArray", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/users/createWithArray", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/users/createWithArray", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + }, + "methods": { + "post": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "createUsersWithArrayInput", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Creates list of users with given input array", + "description": null, + "method": "post", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "globalConsumes": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalConsumes", + "overlay": null + } + }, + "queries": {}, + "body": { + "body-body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "request", + "uuid": "body-body", + "key": null, + "name": "body", + "description": "List of user object", + "examples": [], + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "auths": [], + "responses": { + "default": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "default", + "description": "successful operation", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "user": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "user", + "uuid": "user", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + } + }, + "description": null, + "interfaces": {} + }, + "/users/createWithList": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": "/users/createWithList", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + null + ], + "slashes": null, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": "/users/createWithList", + "path": "/users/createWithList", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/users/createWithList", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/users/createWithList", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + }, + "methods": { + "post": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "createUsersWithListInput", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Creates list of users with given input array", + "description": null, + "method": "post", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "globalConsumes": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalConsumes", + "overlay": null + } + }, + "queries": {}, + "body": { + "body-body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "request", + "uuid": "body-body", + "key": null, + "name": "body", + "description": "List of user object", + "examples": [], + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "auths": [], + "responses": { + "default": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "default", + "description": "successful operation", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "user": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "user", + "uuid": "user", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + } + }, + "description": null, + "interfaces": {} + }, + "/users/login": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": "/users/login", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + null + ], + "slashes": null, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": "/users/login", + "path": "/users/login", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/users/login", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/users/login", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + }, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "loginUser", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Logs user into the system", + "description": null, + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": {}, + "queries": { + "username-query": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "queries", + "usedIn": "request", + "uuid": "username-query", + "key": "username", + "name": "username", + "description": "The user name for login", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "password-query": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "queries", + "usedIn": "request", + "uuid": "password-query", + "key": "password", + "name": "password", + "description": "The password for login in clear text", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + }, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [], + "responses": { + "200": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "200", + "description": "successful operation", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "response", + "uuid": "body", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "400": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "400", + "description": "Invalid username/password supplied", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "user": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "user", + "uuid": "user", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + } + }, + "description": null, + "interfaces": {} + }, + "/users/logout": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": "/users/logout", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + null + ], + "slashes": null, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": "/users/logout", + "path": "/users/logout", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/users/logout", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/users/logout", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + }, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "logoutUser", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Logs out current logged in user session", + "description": null, + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": {}, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [], + "responses": { + "default": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "default", + "description": "successful operation", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "user": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "user", + "uuid": "user", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + } + }, + "description": null, + "interfaces": {} + }, + "/users/{username}": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": "/users/{username}", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + null + ], + "slashes": null, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": "/users/{username}", + "path": "/users/{username}", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/users/{username}", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": "sequence", + "value": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/users/", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "path", + "usedIn": "request", + "uuid": "username-path", + "key": "username", + "name": "username", + "description": "The name that needs to be fetched. Use user1 for testing.", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + ], + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + }, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "getUserByName", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Get user by user name", + "description": null, + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": {}, + "queries": {}, + "body": {}, + "path": { + "username-path": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "path", + "usedIn": "request", + "uuid": "username-path", + "key": "username", + "name": "username", + "description": "The name that needs to be fetched. Use user1 for testing.", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + } + }, + "contexts": [], + "auths": [], + "responses": { + "200": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "200", + "description": "successful operation", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "response", + "uuid": "body", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/User" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "400": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "400", + "description": "Invalid username supplied", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "404": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "404", + "description": "User not found", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "user": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "user", + "uuid": "user", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + }, + "put": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "updateUser", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Updated user", + "description": "This can only be done by the logged in user.", + "method": "put", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "globalConsumes": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalConsumes", + "overlay": null + } + }, + "queries": {}, + "body": { + "body-body": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "request", + "uuid": "body-body", + "key": null, + "name": "body", + "description": "Updated user object", + "examples": [], + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/User" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "path": { + "username-path": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "path", + "usedIn": "request", + "uuid": "username-path", + "key": "username", + "name": "username", + "description": "name that need to be deleted", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + } + }, + "contexts": [], + "auths": [], + "responses": { + "400": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "400", + "description": "Invalid user supplied", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "404": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "404", + "description": "User not found", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "user": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "user", + "uuid": "user", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + }, + "delete": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": "deleteUser", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": "Delete user", + "description": "This can only be done by the logged in user.", + "method": "delete", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "globalConsumes": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalConsumes", + "overlay": null + } + }, + "queries": {}, + "body": {}, + "path": { + "username-path": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "path", + "usedIn": "request", + "uuid": "username-path", + "key": "username", + "name": "username", + "description": "The name that needs to be deleted", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + } + }, + "contexts": [], + "auths": [], + "responses": { + "400": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "400", + "description": "Invalid username supplied", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + }, + "404": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "404", + "description": "User not found", + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type-header": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "Content-Type-header", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": { + "user": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "user", + "uuid": "user", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + } + }, + "description": null, + "interfaces": {} + } + }, + "group": { + "_model": { + "name": "group.models", + "version": "0.1.0" + }, + "id": null, + "name": null, + "description": "All the requests", + "children": { + "/pets": "/pets", + "/pets/findByStatus": "/pets/findByStatus", + "/pets/findByTags": "/pets/findByTags", + "/pets/{petId}": "/pets/{petId}", + "/stores/order": "/stores/order", + "/stores/order/{orderId}": "/stores/order/{orderId}", + "/users": "/users", + "/users/createWithArray": "/users/createWithArray", + "/users/createWithList": "/users/createWithList", + "/users/login": "/users/login", + "/users/logout": "/users/logout", + "/users/{username}": "/users/{username}" + } + }, + "store": { + "_model": { + "name": "store.models", + "version": "0.1.0" + }, + "variable": {}, + "constraint": { + "User": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "pet": { + "$ref": "#/definitions/Pet" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + } + } + }, + "Category": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + } + }, + "Pet": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "owner": { + "$ref": "#/definitions/User" + }, + "photoUrls": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store" + } + } + } + }, + "Tag": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + } + }, + "Order": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status" + }, + "complete": { + "type": "boolean" + } + } + } + } + }, + "endpoint": { + "base": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "uuid": "base", + "protocol": [ + "http:", + "https:" + ], + "slashes": true, + "auth": null, + "host": "petstore.swagger.wordnik.com", + "port": null, + "hostname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "hostname", + "string": "petstore.swagger.wordnik.com", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "hostname", + "name": "hostname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "petstore.swagger.wordnik.com", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "href": "http://petstore.swagger.wordnik.com/v2", + "path": "/v2", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/v2", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/v2", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "secure": true, + "variableDelimiters": [ + "{", + "}" + ] + } + }, + "parameter": { + "globalConsumes": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": "globalConsumes", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the request", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json" + ] + } + ], + "applicableContexts": [], + "interfaces": { + "apiRequestMediaType": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "interface", + "uuid": "apiRequestMediaType", + "overlay": null + } + } + }, + "globalProduces": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": "globalProduces", + "key": "Content-Type", + "name": "Content Type Header", + "description": "describes the media type of the response", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": true, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": { + "apiResponseMediaType": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "interface", + "uuid": "apiResponseMediaType", + "overlay": null + } + } + } + }, + "response": {}, + "auth": { + "api_key": { + "_model": { + "name": "api-key.auth.models", + "version": "0.1.0" + }, + "description": null, + "authName": "api_key", + "name": "api_key", + "in": "header", + "key": null + }, + "petstore_auth": { + "_model": { + "name": "oauth-2.auth.models", + "version": "0.1.0" + }, + "description": "the authentication method for the Petstore API", + "authName": "petstore_auth", + "flow": "implicit", + "authorizationUrl": "http://petstore.swagger.wordnik.com/api/oauth/dialog", + "tokenUrl": null, + "scopes": [ + { + "key": "write_pets", + "value": "modify pets in your account" + }, + { + "key": "read_pets", + "value": "read your pets" + } + ] + } + }, + "interface": { + "apiRequestMediaType": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "apiRequestMediaType", + "uuid": "apiRequestMediaType", + "level": "request", + "required": false, + "description": "defines the common media type of requests in the API.", + "underlay": null + }, + "apiResponseMediaType": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "apiResponseMediaType", + "uuid": "apiResponseMediaType", + "level": "response", + "required": false, + "description": "defines the common media type of responses in the API.", + "underlay": null + }, + "pet": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "pet", + "uuid": "pet", + "level": "request", + "required": false, + "description": null, + "underlay": null + }, + "store": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "store", + "uuid": "store", + "level": "request", + "required": false, + "description": null, + "underlay": null + }, + "user": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "user", + "uuid": "user", + "level": "request", + "required": false, + "description": null, + "underlay": null + } + } + }, + "info": { + "_model": { + "name": "info.utils.models", + "version": "0.1.0" + }, + "title": "Swagger Petstore", + "description": "This is a sample server Petstore server.\n\n[Learn about Swagger](http://swagger.wordnik.com) or join the IRC channel `#swagger` on irc.freenode.net.\n\nFor this sample, you can use the api key `special-key` to test the authorization filters\n", + "tos": "http://helloreverb.com/terms/", + "contact": { + "_model": { + "name": "contact.utils.models", + "version": "0.1.0" + }, + "name": "apiteam@wordnik.com", + "url": null, + "email": null + }, + "license": { + "_model": { + "name": "license.utils.models", + "version": "0.1.0" + }, + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0.0" + } +} diff --git a/testing/e2e/internal-apiblueprint-1a/test-case-0/output.json b/testing/e2e/internal-apiblueprint-1a/test-case-0/output.json new file mode 100644 index 0000000..2b0593b --- /dev/null +++ b/testing/e2e/internal-apiblueprint-1a/test-case-0/output.json @@ -0,0 +1,441 @@ +FORMAT: 1A +HOST: https://petstore.swagger.wordnik.com/v2 + +# Swagger Petstore +This is a sample server Petstore server. + +[Learn about Swagger](http://swagger.wordnik.com) or join the IRC channel `#swagger` on irc.freenode.net. + +For this sample, you can use the api key `special-key` to test the authorization filters + + +## Group Resources + +### /pets + +#### Add a new pet to the store [POST] + +This is a fairly long description about the API request /pets with Method POST + ++ Request + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "$ref": "#/definitions/Pet" + } +``` + ++ Response 200 + +#### Update an existing pet [PUT] + ++ Request + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "$ref": "#/definitions/Pet" + } +``` + ++ Response 200 + +### /pets/findByStatus{?status} + +#### Finds Pets by status [GET] + +Multiple status values can be provided with comma seperated strings + ++ Parameters + + status (array, optional) - Status values that need to be considered for filter +``` + { + "type": "array", + "x-title": "status", + "items": { + "type": "string" + } + } +``` + ++ Response 200 + +### /pets/findByTags{?tags} + +#### Finds Pets by tags [GET] + +Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing. + ++ Parameters + + tags (array, optional) - Tags to filter by +``` + { + "type": "array", + "x-title": "tags", + "items": { + "type": "string" + } + } +``` + ++ Response 200 + +### /pets/{petId} + +#### Find pet by ID [GET] + +Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions + ++ Response 200 + +#### Updates a pet in the store with form data [POST] + ++ Request + + Headers +``` + Content-Type: application/x-www-form-urlencoded +``` + + Schema +``` + { + "type": "object", + "properties": { + "name": { + "type": "string", + "x-title": "name" + }, + "status": { + "type": "string", + "x-title": "status" + } + } + } +``` + ++ Response 200 + +#### Deletes a pet [DELETE] + ++ Request + + Headers +``` + api_key: null + Content-Type: application/json +``` + ++ Response 200 + +### /stores/order + +#### Place an order for a pet [POST] + ++ Request + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "$ref": "#/definitions/Order" + } +``` + ++ Response 200 + +### /stores/order/{orderId} + +#### Find purchase order by ID [GET] + +For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions + ++ Response 200 + +#### Delete purchase order by ID [DELETE] + +For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + ++ Request + + Headers +``` + Content-Type: application/json +``` + ++ Response 200 + +### /users + +#### Create user [POST] + +This can only be done by the logged in user. + ++ Request + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "$ref": "#/definitions/User" + } +``` + ++ Response 200 + +### /users/createWithArray + +#### Creates list of users with given input array [POST] + ++ Request + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } +``` + ++ Response 200 + +### /users/createWithList + +#### Creates list of users with given input array [POST] + ++ Request + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } +``` + ++ Response 200 + +### /users/login{?username,password} + +#### Logs user into the system [GET] + ++ Parameters + + username (string, optional) - The user name for login +``` + { + "type": "string", + "x-title": "username" + } +``` + + + password (string, optional) - The password for login in clear text +``` + { + "type": "string", + "x-title": "password" + } +``` + ++ Response 200 + +### /users/logout + +#### Logs out current logged in user session [GET] + ++ Response 200 + +### /users/{username} + +#### Get user by user name [GET] + ++ Response 200 + +#### Updated user [PUT] + +This can only be done by the logged in user. + ++ Request + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "$ref": "#/definitions/User" + } +``` + ++ Response 200 + +#### Delete user [DELETE] + +This can only be done by the logged in user. + ++ Request + + Headers +``` + Content-Type: application/json +``` + ++ Response 200 + +# Data Structures + +## User (object) +``` + { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "pet": { + "$ref": "#/definitions/Pet" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + } + } +``` + +## Category (object) +``` + { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + } +``` + +## Pet (object) +``` + { + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "owner": { + "$ref": "#/definitions/User" + }, + "photoUrls": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store" + } + } + } +``` + +## Tag (object) +``` + { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + } +``` + +## Order (object) +``` + { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status" + }, + "complete": { + "type": "boolean" + } + } + } +``` diff --git a/testing/e2e/internal-apiblueprint-1a/test-case-1/input.json b/testing/e2e/internal-apiblueprint-1a/test-case-1/input.json new file mode 100644 index 0000000..97942d6 --- /dev/null +++ b/testing/e2e/internal-apiblueprint-1a/test-case-1/input.json @@ -0,0 +1,3418 @@ +{ + "_model": { + "name": "api.core.models", + "version": "0.1.0" + }, + "resources": { + "http://{environment}.musicapi.com/{version}/api": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": "/api", + "uuid": "http://{environment}.musicapi.com/{version}/api", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "uuid": null, + "protocol": [], + "slashes": true, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": null, + "path": null, + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/api", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/api", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "secure": false, + "variableDelimiters": [], + "description": null + }, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": null, + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": null, + "description": "This was loaded from api-library", + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalMediaType", + "overlay": null + } + }, + "queries": { + "queryString": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "queries", + "usedIn": "request", + "uuid": "queryString", + "key": "queryString", + "name": "queryString", + "description": null, + "examples": [], + "type": "object", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "start": { + "type": "number" + }, + "page-size": { + "type": "number" + } + } + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "ApiLib.basic_api", + "overlay": null + } + ], + "responses": {}, + "timeout": null, + "tags": [], + "interfaces": {} + }, + "post": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": null, + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": null, + "description": "This is an awesome description", + "method": "post", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "application/json": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "request", + "uuid": "application/json", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/ApiLib.RamlDataType" + } + } + ], + "applicableContexts": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + ], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [ + { + "_model": { + "name": "context.core.models", + "version": "0.1.0" + }, + "constraints": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": null, + "format": null, + "default": "application/json", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + ], + "type": null, + "implements": {} + } + ], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "custom_scheme", + "overlay": null + } + ], + "responses": {}, + "timeout": null, + "tags": [], + "interfaces": { + "trait_ApiLib.described": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "interface", + "uuid": "trait_ApiLib.described", + "overlay": null + } + } + } + }, + "description": null, + "interfaces": { + "resourceType_ApiLib.collectionFromApiLib": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "interface", + "uuid": "resourceType_ApiLib.collectionFromApiLib", + "overlay": null + } + } + }, + "http://{environment}.musicapi.com/{version}/entry": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": "/entry", + "uuid": "http://{environment}.musicapi.com/{version}/entry", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "uuid": null, + "protocol": [], + "slashes": true, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": null, + "path": null, + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/entry", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/entry", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "secure": false, + "variableDelimiters": [], + "description": null + }, + "methods": { + "post": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": null, + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": null, + "description": null, + "method": "post", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalMediaType", + "overlay": null + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "custom_scheme", + "overlay": null + } + ], + "responses": { + "200": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "200", + "description": null, + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "application/json": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "response", + "uuid": "application/json", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/AnotherEntry" + } + } + ], + "applicableContexts": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + ], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [ + { + "_model": { + "name": "context.core.models", + "version": "0.1.0" + }, + "constraints": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": null, + "format": null, + "default": "application/json", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + ], + "type": null, + "implements": {} + } + ], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": {} + }, + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": null, + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": null, + "description": "returns a list of entry", + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalMediaType", + "overlay": null + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "custom_scheme", + "overlay": null + } + ], + "responses": { + "200": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "200", + "description": null, + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "application/json": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "response", + "uuid": "application/json", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "object", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object" + } + } + ], + "applicableContexts": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + ], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [ + { + "_model": { + "name": "context.core.models", + "version": "0.1.0" + }, + "constraints": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": null, + "format": null, + "default": "application/json", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + ], + "type": null, + "implements": {} + } + ], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": {} + } + }, + "description": null, + "interfaces": { + "resourceType_collection": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "interface", + "uuid": "resourceType_collection", + "overlay": null + } + } + }, + "http://{environment}.musicapi.com/{version}/songs": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": "/songs", + "uuid": "http://{environment}.musicapi.com/{version}/songs", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "uuid": null, + "protocol": [], + "slashes": true, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": null, + "path": null, + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/songs", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/songs", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "secure": false, + "variableDelimiters": [], + "description": null + }, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": null, + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": null, + "description": "This was loaded from songs-library", + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalMediaType", + "overlay": null + } + }, + "queries": { + "genre": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "queries", + "usedIn": "request", + "uuid": "genre", + "key": "genre", + "name": "genre", + "description": "filter the songs by genre", + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "description": "filter the songs by genre" + } + } + ], + "applicableContexts": [], + "interfaces": {} + }, + "access_token": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "queries", + "usedIn": "request", + "uuid": "access_token", + "key": "access_token", + "name": "access_token", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "oauth_2_0", + "overlay": null + }, + null + ], + "responses": {}, + "timeout": null, + "tags": [], + "interfaces": {} + }, + "post": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": null, + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": null, + "description": null, + "method": "post", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalMediaType", + "overlay": null + } + }, + "queries": { + "access_token": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "queries", + "usedIn": "request", + "uuid": "access_token", + "key": "access_token", + "name": "access_token", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "custom_scheme", + "overlay": null + } + ], + "responses": {}, + "timeout": null, + "tags": [], + "interfaces": {} + } + }, + "description": "Access to all songs inside the music world library.", + "interfaces": { + "resourceType_SongsLib.collectionfromSongsLib": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "interface", + "uuid": "resourceType_SongsLib.collectionfromSongsLib", + "overlay": null + } + } + }, + "http://{environment}.musicapi.com/{version}/songs/{songId}": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": "/songs/{songId}", + "uuid": "http://{environment}.musicapi.com/{version}/songs/{songId}", + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "path": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "uuid": null, + "protocol": [], + "slashes": true, + "auth": null, + "host": null, + "port": null, + "hostname": null, + "href": null, + "path": null, + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/songs/{songId}", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": "sequence", + "value": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/songs/", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "path", + "usedIn": "request", + "uuid": null, + "key": "songId", + "name": "songId", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string" + } + } + ], + "applicableContexts": [], + "interfaces": {} + }, + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + ], + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "secure": false, + "variableDelimiters": [], + "description": null + }, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": null, + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": null, + "description": null, + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "parameter", + "uuid": "globalMediaType", + "overlay": null + } + }, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "custom_scheme", + "overlay": null + } + ], + "responses": { + "200": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "200", + "description": null, + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json", + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "application/json": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "response", + "uuid": "application/json", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/SongsLib.Song" + } + } + ], + "applicableContexts": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + ], + "interfaces": {} + }, + "application/xml": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "response", + "uuid": "application/xml", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": null, + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "x-xml": "\n\n \n \n \n \n \n \n \n \n\n", + "x-examples": [ + "\n\n str1234\n str1234\n\n" + ] + } + } + ], + "applicableContexts": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/xml" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + ], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [ + { + "_model": { + "name": "context.core.models", + "version": "0.1.0" + }, + "constraints": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": null, + "format": null, + "default": "application/json", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + ], + "type": null, + "implements": {} + }, + { + "_model": { + "name": "context.core.models", + "version": "0.1.0" + }, + "constraints": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": null, + "format": null, + "default": "application/xml", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + ], + "type": null, + "implements": {} + } + ], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": {} + } + }, + "description": null, + "interfaces": {} + } + }, + "group": { + "_model": { + "name": "group.models", + "version": "0.1.0" + }, + "id": null, + "name": null, + "description": null, + "children": { + "/api": { + "_model": { + "name": "group.models", + "version": "0.1.0" + }, + "id": "/api", + "name": "/api", + "description": null, + "children": { + "http://{environment}.musicapi.com/{version}/api": "http://{environment}.musicapi.com/{version}/api" + } + }, + "/entry": { + "_model": { + "name": "group.models", + "version": "0.1.0" + }, + "id": "/entry", + "name": "/entry", + "description": null, + "children": { + "http://{environment}.musicapi.com/{version}/entry": "http://{environment}.musicapi.com/{version}/entry" + } + }, + "/songs": { + "_model": { + "name": "group.models", + "version": "0.1.0" + }, + "id": "/songs", + "name": "/songs", + "description": null, + "children": { + "/songs/{songId}": { + "_model": { + "name": "group.models", + "version": "0.1.0" + }, + "id": "/songs/{songId}", + "name": "/{songId}", + "description": null, + "children": { + "http://{environment}.musicapi.com/{version}/songs/{songId}": "http://{environment}.musicapi.com/{version}/songs/{songId}" + } + }, + "http://{environment}.musicapi.com/{version}/songs": "http://{environment}.musicapi.com/{version}/songs" + } + } + } + }, + "store": { + "_model": { + "name": "store.models", + "version": "0.1.0" + }, + "variable": {}, + "constraint": { + "Entry": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/Song" + } + } + }, + "Song": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "artist": { + "type": "string" + } + } + } + }, + "AnotherEntry": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "$ref": "#/definitions/Entry", + "description": "# This is in Markdown\nThis is to see what the representation of this ~~Markdown~~ string is.\n## SubTitle\nThis is just another entry to **simulate** that you can add facets also on JSON\nschema defined types. Although you can only add documentation-based facets.\n" + } + }, + "SpecialEntry": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "pattern": "^.{12}$" + } + }, + "Toy": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "minProperties": 2, + "maxProperties": 8, + "discriminator": "name", + "properties": { + "name": { + "type": "string" + }, + "soft": { + "type": "boolean", + "x-examples": [ + true + ] + }, + "squiky": { + "type": "boolean" + }, + "heavy": { + "type": "boolean" + }, + "clean": { + "type": "boolean" + }, + "purchaseDate": { + "type": "string", + "$ref": "#/definitions/$DateOnly" + }, + "more": { + "type": "string", + "$ref": "#/definitions/$DateTime" + }, + "final": { + "type": "string", + "$ref": "#/definitions/$DateTimeOnly" + } + }, + "required": [ + "name", + "soft", + "squiky", + "heavy", + "clean", + "purchaseDate", + "more", + "final" + ] + } + }, + "$DateOnly": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "description": "full-date as defined in RFC#3339" + } + }, + "$DateTime": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "description": "datetime" + } + }, + "$DateTimeOnly": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|20|21|22|23):[0-5][0-9]:([0-5][0-9]|60)(.[0-9]+)?$", + "description": "full-time as defined in RFC#3339" + } + }, + "Ball": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "allOf": [ + { + "$ref": "#/definitions/Toy" + } + ], + "discriminatorValue": "ball", + "properties": { + "bouncinessFactor": { + "type": "number", + "minimum": 0 + } + }, + "required": [ + "bouncinessFactor" + ] + } + }, + "Rope": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "allOf": [ + { + "$ref": "#/definitions/Toy" + } + ], + "discriminatorValue": "rope", + "properties": { + "length": { + "type": "integer", + "enum": [ + "10", + "20", + "50", + "100" + ], + "x-examples": [ + 10 + ] + } + }, + "required": [ + "length" + ] + } + }, + "CommonToy": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "anyOf": [ + { + "$ref": "#/definitions/Ball" + }, + { + "$ref": "#/definitions/Rope" + } + ] + } + }, + "Treat": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "tastiness": { + "type": "number", + "minimum": 0, + "maximum": 1 + } + }, + "required": [ + "name", + "tastiness" + ] + } + }, + "Pet": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "claws": { + "type": "number", + "minimum": 0, + "maximum": 4 + }, + "color": { + "type": "string", + "minLength": 7, + "maxLength": 7, + "pattern": "^#[0-9a-f]{6}$" + }, + "size": { + "type": "number", + "multipleOf": 5 + }, + "weigth": { + "type": "number" + }, + "mostActiveAt": { + "type": "string", + "$ref": "#/definitions/$TimeOnly" + }, + "owner": { + "$ref": "#/definitions/User" + }, + "toys": { + "type": "array", + "items": { + "$ref": "#/definitions/Toy" + } + } + }, + "required": [ + "claws", + "color", + "size", + "weigth", + "mostActiveAt", + "owner", + "toys" + ] + } + }, + "$TimeOnly": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "pattern": "^([01][0-9]|20|21|22|23):[0-5][0-9]:([0-5][0-9]|60)(.[0-9]+)?$", + "description": "full-time as defined in RFC#3339" + } + }, + "Resident": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "address": { + "type": "string" + } + }, + "required": [ + "address" + ] + } + }, + "Wild": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "location": { + "type": "string" + } + }, + "required": [ + "location" + ] + } + }, + "FarmPet": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "allOf": [ + { + "$ref": "#/definitions/Pet" + }, + { + "anyOf": [ + { + "$ref": "#/definitions/Resident" + }, + { + "$ref": "#/definitions/Wild" + } + ] + } + ], + "title": "Farm Pet" + } + }, + "ToyBox": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "array", + "uniqueItems": false, + "minItems": 1, + "maxItems": 15, + "items": { + "anyOf": [ + { + "$ref": "#/definitions/Toy" + }, + { + "$ref": "#/definitions/Treat" + } + ] + } + } + }, + "BigToyBox": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "allOf": [ + { + "$ref": "#/definitions/ToyBox" + } + ], + "maxItems": 100 + } + }, + "Photo": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "$ref": "#/definitions/$File" + } + }, + "$File": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "description": "file", + "pattern": "^[^\u0000]*\u0000$" + } + }, + "Album": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/Photo" + } + } + }, + "Weird": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": {} + }, + "Habitats": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/Resident" + }, + { + "$ref": "#/definitions/Wild" + } + ] + } + } + }, + "SeparateKinds": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/Resident" + } + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/Wild" + } + } + ] + } + }, + "User": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "firstname": { + "type": "string" + }, + "lastname": { + "type": "string", + "x-examples": [ + "Doe" + ] + }, + "pets": { + "type": "array", + "items": { + "$ref": "#/definitions/FarmPet" + } + }, + "box": { + "$ref": "#/definitions/ToyBox" + }, + "albums": { + "type": "array", + "items": { + "$ref": "#/definitions/Album" + } + } + }, + "required": [ + "firstname" + ], + "x-examples": [ + { + "firstname": "John" + } + ] + } + }, + "SongsLib.Song": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "length": { + "type": "number" + } + }, + "required": [ + "title", + "length" + ], + "x-examples": [ + { + "title": "My Song", + "length": 12 + }, + { + "title": "Last", + "length": 3 + } + ] + } + }, + "SongsLib.Album": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "songs": { + "type": "array", + "items": { + "$ref": "#/definitions/SongsLib.Song" + } + } + }, + "required": [ + "title", + "songs" + ] + } + }, + "SongsLib.Musician": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "discography": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/SongsLib.Song" + }, + { + "$ref": "#/definitions/SongsLib.Album" + } + ] + } + } + }, + "required": [ + "name", + "discography" + ] + } + }, + "ApiLib.RamlDataType": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "propString": { + "type": "string" + }, + "propStringArray1": { + "type": "array", + "items": { + "type": "string" + } + }, + "ideas": { + "type": "array" + }, + "extIdeas": { + "properties": { + "createdBy": { + "type": "string" + } + }, + "required": [ + "createdBy" + ] + }, + "feedback": { + "type": "string", + "minLength": 1, + "maxLength": 255, + "pattern": "[a-zA-Z\\s]*", + "x-examples": [ + "very well made" + ] + }, + "propNumber": { + "type": "number", + "minimum": 0, + "maximum": 32, + "multipleOf": 2 + }, + "propInteger": { + "type": "integer", + "minimum": 3, + "maximum": 5, + "multipleOf": 1 + }, + "propBoolean": { + "type": "boolean" + }, + "propDate": { + "type": "string", + "$ref": "#/definitions/ApiLib.$DateOnly", + "x-examples": [ + "2015-05-23" + ] + }, + "userPicture": { + "type": "string", + "$ref": "#/definitions/ApiLib.$File" + }, + "NilValue": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "comment": { + "$ref": "#/definitions/ApiLib.string?" + } + }, + "required": [ + "name", + "comment" + ] + }, + "CatOrDog": { + "anyOf": [ + { + "$ref": "#/definitions/ApiLib.Cat" + }, + { + "$ref": "#/definitions/ApiLib.Dog" + } + ] + }, + "CatAndDog": { + "$ref": "#/definitions/ApiLib.Dog" + }, + "PossibleMeetingDate": { + "type": "string", + "$ref": "#/definitions/ApiLib.$DateOnly" + } + }, + "required": [ + "propString", + "propStringArray1", + "ideas", + "extIdeas", + "feedback", + "propNumber", + "propInteger", + "propBoolean", + "propDate", + "userPicture", + "NilValue", + "CatOrDog", + "CatAndDog", + "CatAndDog", + "PossibleMeetingDate" + ] + } + }, + "ApiLib.$DateOnly": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "description": "full-date as defined in RFC#3339" + } + }, + "ApiLib.$File": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "description": "file", + "pattern": "^[^\u0000]*\u0000$" + } + }, + "ApiLib.Cat": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "color": { + "type": "string" + } + }, + "required": [ + "name", + "color" + ] + } + }, + "ApiLib.Dog": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "fangs": { + "type": "string" + } + }, + "required": [ + "name", + "fangs" + ] + } + }, + "ApiLib.CustomDate": { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "$ref": "#/definitions/ApiLib.$DateOnly" + } + } + }, + "endpoint": { + "base": { + "_model": { + "name": "url.models", + "version": "0.1.0" + }, + "protocol": [ + "http:", + "https:" + ], + "slashes": true, + "auth": null, + "host": "{environment}.musicapi.com", + "port": null, + "hostname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "hostname", + "string": "{environment}.musicapi.com", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "hostname", + "name": "hostname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": "sequence", + "value": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "hostname", + "usedIn": "request", + "uuid": "environment", + "key": "environment", + "name": "environment", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "enum": [ + "stg", + "dev", + "test", + "prod" + ] + } + } + ], + "applicableContexts": [], + "interfaces": {} + }, + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": ".musicapi.com", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + ], + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "href": "http://{environment}.musicapi.com/{version}", + "path": "/{version}", + "pathname": { + "_model": { + "name": "url-component.models", + "version": "0.1.0" + }, + "componentName": "pathname", + "string": "/{version}", + "parameter": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "pathname", + "name": "pathname", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": "sequence", + "value": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "/", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "pathname", + "usedIn": "request", + "uuid": "version", + "key": "version", + "name": "version", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string", + "enum": [ + "v1" + ] + } + } + ], + "applicableContexts": [], + "interfaces": {} + }, + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": "", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + ], + "constraints": [], + "applicableContexts": [], + "interfaces": {} + }, + "variableDelimiters": [ + "{", + "}" + ] + }, + "query": null, + "search": null, + "hash": null, + "variableDelimiters": [ + "{", + "}" + ] + } + }, + "parameter": { + "globalMediaType": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "response": {}, + "auth": { + "oauth_1_0": { + "_model": { + "name": "oauth-1.auth.models", + "version": "0.1.0" + }, + "description": "OAuth 1.0 continues to be supported for all API requests, but OAuth 2.0 is now preferred.\n", + "authName": "oauth_1_0", + "callback": null, + "consumerSecret": null, + "tokenSecret": null, + "consumerKey": null, + "algorithm": null, + "nonce": null, + "additionalParameters": null, + "timestamp": null, + "token": null, + "version": null, + "signature": "HMAC-SHA1", + "tokenCredentialsUri": "https://api.mysampleapi.com/1/oauth/access_token", + "requestTokenUri": "https://api.mysampleapi.com/1/oauth/request_token", + "authorizationUri": "https://api.mysampleapi.com/1/oauth/authorize" + }, + "oauth_2_0": { + "_model": { + "name": "oauth-2.auth.models", + "version": "0.1.0" + }, + "description": "Dropbox supports OAuth 2.0 for authenticating all API requests.\n", + "authName": "oauth_2_0", + "flow": "accessCode", + "authorizationUrl": "https://www.dropbox.com/1/oauth2/authorize", + "tokenUrl": "https://api.dropbox.com/1/oauth2/token", + "scopes": [] + }, + "ApiLib.basic_api": { + "_model": { + "name": "basic.auth.models", + "version": "0.1.0" + }, + "description": "This is our super secure api auth", + "authName": "basic_api", + "username": null, + "password": null, + "raw": null, + "interfaces": {} + } + }, + "interface": { + "resourceType_SongsLib.collectionfromSongsLib": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "SongsLib.collectionfromSongsLib", + "uuid": "resourceType_SongsLib.collectionfromSongsLib", + "level": "resource", + "required": false, + "description": null, + "underlay": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": null, + "endpoints": {}, + "path": null, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": null, + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": null, + "description": "This was loaded from songs-library", + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": {}, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [], + "responses": {}, + "timeout": null, + "tags": [], + "interfaces": {} + } + }, + "description": null, + "interfaces": {} + } + }, + "resourceType_ApiLib.collectionFromApiLib": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "ApiLib.collectionFromApiLib", + "uuid": "resourceType_ApiLib.collectionFromApiLib", + "level": "resource", + "required": false, + "description": null, + "underlay": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": null, + "endpoints": {}, + "path": null, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": null, + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": null, + "description": "This was loaded from api-library", + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": {}, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [], + "responses": {}, + "timeout": null, + "tags": [], + "interfaces": {} + } + }, + "description": null, + "interfaces": {} + } + }, + "resourceType_collection": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "collection", + "uuid": "resourceType_collection", + "level": "resource", + "required": false, + "description": null, + "underlay": { + "_model": { + "name": "resource.models", + "version": "0.1.0" + }, + "name": null, + "uuid": null, + "endpoints": {}, + "path": null, + "methods": { + "get": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": null, + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": null, + "description": "returns a list of <>", + "method": "get", + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": {}, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "custom_scheme", + "overlay": null + } + ], + "responses": { + "200": { + "_model": { + "name": "response.core.models", + "version": "0.1.0" + }, + "code": "200", + "description": null, + "examples": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": { + "Content-Type": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "response", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "queries": {}, + "body": { + "application/json": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "body", + "usedIn": "response", + "uuid": "application/json", + "key": null, + "name": null, + "description": null, + "examples": [], + "type": "object", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "object" + } + } + ], + "applicableContexts": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": null, + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "enum.constraint.models", + "version": "0.1.0" + }, + "name": "enum", + "value": [ + "application/json" + ] + } + ], + "applicableContexts": [], + "interfaces": {} + } + ], + "interfaces": {} + } + }, + "path": {} + }, + "contexts": [ + { + "_model": { + "name": "context.core.models", + "version": "0.1.0" + }, + "constraints": [ + { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "headers", + "usedIn": "request", + "uuid": null, + "key": "Content-Type", + "name": "Content-Type", + "description": null, + "examples": [], + "type": null, + "format": null, + "default": "application/json", + "required": false, + "superType": null, + "value": null, + "constraints": [], + "applicableContexts": [], + "interfaces": {} + } + ], + "type": null, + "implements": {} + } + ], + "interfaces": {} + } + }, + "timeout": null, + "tags": [], + "interfaces": {} + } + }, + "description": null, + "interfaces": {} + } + }, + "trait_ApiLib.described": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "ApiLib.described", + "uuid": "trait_ApiLib.described", + "level": "request", + "required": false, + "description": null, + "underlay": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": null, + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": null, + "description": "This is an awesome description", + "method": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": {}, + "queries": {}, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [], + "responses": {}, + "timeout": null, + "tags": [], + "interfaces": {} + } + }, + "trait_secured": { + "_model": { + "name": "interface.models", + "version": "0.1.0" + }, + "name": "secured", + "uuid": "trait_secured", + "level": "request", + "required": false, + "description": "This trait can be used to apply an access token query parameter\nto any resources or HTTP methods.\n", + "underlay": { + "_model": { + "name": "request.models", + "version": "0.1.0" + }, + "id": null, + "endpoints": { + "base": { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "endpoint", + "uuid": "base", + "overlay": null + } + }, + "name": null, + "description": null, + "method": null, + "parameters": { + "_model": { + "name": "parameter-container.core.models", + "version": "0.1.0" + }, + "headers": {}, + "queries": { + "access_token": { + "_model": { + "name": "parameter.core.models", + "version": "0.1.0" + }, + "in": "queries", + "usedIn": "request", + "uuid": "access_token", + "key": "access_token", + "name": "access_token", + "description": null, + "examples": [], + "type": "string", + "format": null, + "default": null, + "required": false, + "superType": null, + "value": null, + "constraints": [ + { + "_model": { + "name": "json.constraint.models", + "version": "0.1.0" + }, + "name": "json", + "value": { + "type": "string" + } + } + ], + "applicableContexts": [], + "interfaces": {} + } + }, + "body": {}, + "path": {} + }, + "contexts": [], + "auths": [ + { + "_model": { + "name": "reference.models", + "version": "0.1.0" + }, + "type": "auth", + "uuid": "custom_scheme", + "overlay": null + } + ], + "responses": {}, + "timeout": null, + "tags": [], + "interfaces": {} + } + } + } + }, + "info": { + "_model": { + "name": "info.utils.models", + "version": "0.1.0" + }, + "title": "World Music API", + "description": "This is an example of a music API.", + "tos": null, + "contact": null, + "license": null, + "version": "v1" + } +} diff --git a/testing/e2e/internal-apiblueprint-1a/test-case-1/output.json b/testing/e2e/internal-apiblueprint-1a/test-case-1/output.json new file mode 100644 index 0000000..e69de29 From 1a838fa7d09785bb40f7dc27f907b2fb0f0e03cb Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Mon, 15 May 2017 15:24:58 +0200 Subject: [PATCH 11/24] added tests for api-blueprint serializer --- .../api-blueprint/1A/Serializer.js | 267 ++- .../1A/__tests__/Serializer.spec.js | 2086 +++++++++++++++++ .../test-case-0/output.json | 296 ++- .../test-case-1/output.json | 861 +++++++ testing/mocha-runner.js | 4 +- 5 files changed, 3405 insertions(+), 109 deletions(-) create mode 100644 src/serializers/api-blueprint/1A/__tests__/Serializer.spec.js diff --git a/src/serializers/api-blueprint/1A/Serializer.js b/src/serializers/api-blueprint/1A/Serializer.js index 64233c9..2d95556 100644 --- a/src/serializers/api-blueprint/1A/Serializer.js +++ b/src/serializers/api-blueprint/1A/Serializer.js @@ -21,6 +21,8 @@ import { List, OrderedMap } from 'immutable' import Reference from '../../../models/Reference' +import Variable from '../../../models/Variable' +import URL from '../../../models/URL' import { flatten } from '../../../utils/fp-utils' @@ -82,7 +84,7 @@ methods.getRootHostFromEndpoint = (api) => { */ methods.getRootHostFromVariable = (api) => { const variable = api.getIn([ 'store', 'variable' ]).valueSeq().get(0) - if (!variable) { + if (!variable || !(variable instanceof Variable)) { return null } @@ -137,7 +139,7 @@ methods.createFormatHostSection = (api) => { return null } - const rootUrl = host.generate() + const rootUrl = host.generate(List([ '{', '}' ])) if (!rootUrl) { return null @@ -295,14 +297,9 @@ methods.getMergedResourcesBasedOnPathFromApi = (api) => { return OrderedMap(resources) } -/** - * extracts a QueryString section from a resource - * @param {Api} api: the api containing the store to use to resolve shared parameters if needed - * @param {Resource} resource: the resource to get the QueryString from - * @returns {AbstractSection|null} the corresponding section that contains all the query parameters - * as a string respecting ABP format - */ -methods.extractQueryStringFromResource = (api, resource) => { +methods.deduplicateArray = (array) => Array.from(new Set(array)) + +methods.extractQueryParametersFromResourceWithDuplicates = (api, resource) => { const store = api.get('store') const queryParams = resource.get('methods') .map(request => request.get('parameters')) @@ -312,7 +309,25 @@ methods.extractQueryStringFromResource = (api, resource) => { .map(param => param.get('key')) .filter(v => !!v) - const params = Array.from(new Set(queryParams)) + return queryParams +} + +methods.extractQueryParametersFromResource = (api, resource) => { + const queryParams = methods.extractQueryParametersFromResourceWithDuplicates(api, resource) + const params = methods.deduplicateArray(queryParams) + + return params +} + +/** + * extracts a QueryString section from a resource + * @param {Api} api: the api containing the store to use to resolve shared parameters if needed + * @param {Resource} resource: the resource to get the QueryString from + * @returns {AbstractSection|null} the corresponding section that contains all the query parameters + * as a string respecting ABP format + */ +methods.extractQueryStringFromResource = (api, resource) => { + const params = methods.extractQueryParametersFromResource(api, resource) if (!params.length) { return null @@ -334,6 +349,25 @@ methods.extractQueryStringFromResource = (api, resource) => { } } +methods.createResourceTitleSectionContent = (api, resource) => { + const title = resource.get('name') + const path = resource.get('path').generate(List([ '{', '}' ])) + const queryString = methods.extractQueryStringFromResource(api, resource) + + let fixedTitle = title + if (!title || title[0] === '/') { + fixedTitle = null + } + + return [ + fixedTitle, + fixedTitle ? ' [' : null, + path ? path.replace(/\/+/, '/') : '/', + queryString, + fixedTitle ? ']' : null + ].filter(v => !!v) +} + /** * creates a Resource Title Section from a Resource. * @param {Api} api: the api to use to resolve shared parameters @@ -343,30 +377,22 @@ methods.extractQueryStringFromResource = (api, resource) => { * exist. */ methods.createResourceTitleSection = (api, resource) => { - const title = resource.get('name') - const path = resource.get('path').generate(List([ '{', '}' ])) - const queryString = methods.extractQueryStringFromResource(api, resource) + const content = methods.createResourceTitleSectionContent(api, resource) + + if (!content.length) { + return null + } const section = { type: 'header', depth: 3, value: { abstract: true, - content: [ - title, - title ? ' [' : null, - path, - queryString, - title ? ']' : null - ].filter(v => !!v), + content: content, separator: '' } } - if (!section.value.content.length) { - return null - } - return section } @@ -389,6 +415,22 @@ methods.createResourceDescriptionSection = (resource) => { } } +methods.createOperationTitleSectionContent = (operation) => { + const name = operation.get('name') + const method = operation.get('method') + + if (!method) { + return [] + } + + return [ + name, + name ? ' [' : null, + method.toUpperCase(), + name ? ']' : null + ].filter(v => !!v) +} + /** * creates an operation title section from an Operation/Request * @param {Request} operation: the operation from which to extract a title @@ -396,28 +438,22 @@ methods.createResourceDescriptionSection = (resource) => { * the name and method of the operation */ methods.createOperationTitleSection = (operation) => { - const name = operation.get('name') - const method = operation.get('method').toUpperCase() + const content = methods.createOperationTitleSectionContent(operation) + + if (!content.length) { + return null + } const section = { type: 'header', depth: 4, value: { abstract: true, - content: [ - name, - name ? ' [' : null, - method, - name ? ']' : null - ].filter(v => !!v), + content: content, separator: '' } } - if (!section.value.content.length) { - return null - } - return section } @@ -462,6 +498,13 @@ methods.createParameterKeySegment = (parameter) => { return section } +methods.createParameterOptionalSegmentContent = (schema, parameter) => { + const type = schema.type || parameter.get('type') + const optionalText = parameter.get('required') ? 'required' : 'optional' + + return [ type, optionalText ].filter(v => !!v) +} + /** * creates a section that contains the optional fields used in the description of a parameter * @param {JSONSchema} schema: the JSON Schema representing the parameter @@ -470,14 +513,9 @@ methods.createParameterKeySegment = (parameter) => { * a parameter, in the expected format. */ methods.createParameterOptionalSegment = (schema, parameter) => { - const type = schema.type || parameter.get('type') - const optionalText = parameter.get('required') ? 'required' : 'optional' - - const optionalData = [ - type, optionalText - ].filter(v => !!v) + const content = methods.createParameterOptionalSegmentContent(schema, parameter) - if (!optionalData.length) { + if (!content.length) { return null } @@ -487,7 +525,7 @@ methods.createParameterOptionalSegment = (schema, parameter) => { '(', { abstract: true, - content: optionalData, + content: content, separator: ', ' }, ')' @@ -576,16 +614,15 @@ methods.convertParameterIntoParamSection = (parameter) => { /** * extracts path parameters name from a Path - * @param {Api} api: the api to use to resolve shared parameters * @param {URL} path: the path to extract the parameters from * @returns {Array} the corresponding array that contains all the path parameters. */ -methods.extractPathParamsFromPath = (api, path) => { - if (path.getIn([ 'parameter', 'superType' ]) !== 'sequence') { +methods.extractPathParamsFromPath = (path) => { + if (path.getIn([ 'pathname', 'parameter', 'superType' ]) !== 'sequence') { return [] } - const sequence = path.getIn([ 'parameter', 'value' ]) + const sequence = path.getIn([ 'pathname', 'parameter', 'value' ]) if (!sequence || !sequence.size) { return [] } @@ -593,29 +630,32 @@ methods.extractPathParamsFromPath = (api, path) => { return sequence .filter(param => param.get('key')) .valueSeq() + .toArray() +} + +methods.createOperationParametersSectionContent = (path, container) => { + const queryParamSections = container + .get('queries') + .map(methods.convertParameterIntoParamSection) + .valueSeq() .toJS() + + const pathParamSections = methods.extractPathParamsFromPath(path) + .map(methods.convertParameterIntoParamSection) + + return [ ...queryParamSections, ...pathParamSections ] } /** * creates the Parameters section for an operation - * @param {Api} api: the api to use to resolve shared parameters * @param {URL} path: the path from which to get the path parameters * @param {ParameterContainer} container: the ParameterContainer that holds all the query * parameters. This container is already resolved and filtered based on a set of constraints. * @returns {AbstractSection|null} the corresponding section that contains all the information * pertaining to the query and path parameters */ -methods.createOperationParametersSection = (api, path, container) => { - const queryParamSections = container - .get('queries') - .map(methods.convertParameterIntoParamSection) - .valueSeq() - .toJS() - - const pathParamSections = methods.extractPathParamsFromPath(api, path) - .map(methods.convertParameterIntoParamSection) - - const paramSections = [ ...queryParamSections, ...pathParamSections ] +methods.createOperationParametersSection = (path, container) => { + const paramSections = methods.createOperationParametersSectionContent(path, container) if (!paramSections.length) { return null @@ -696,19 +736,40 @@ methods.convertHeaderParameterIntoHeaderSection = (parameter) => { return name + ': ' + value } +methods.getHeadersForOperationRequest = (constraints, container, method) => { + const headers = container.get('headers') + + if (!headers.size) { + return null + } + + const contentType = methods.extractContentTypeFromConstraints(constraints) + if (!contentType && (method || '').toLowerCase() !== 'get') { + return headers + } + + const filteredHeaders = headers.filter(parameter => parameter.get('key') !== 'Content-Type') + if (!filteredHeaders.size) { + return null + } + + return filteredHeaders +} + /** * creates the Headers Section and its payload from an Operation/Request ParameterContainer * @param {Api} api: the api used to resolve shared object, such as authentication methods * @param {List} constraints: the constraints used filter the ParameterContainer * @param {ParameterContainer} container: the ParameterContainer containing all the header * parameters. + * @param {string?} method: the method associated with this operation request. * @returns {ListSection} the section that contains all the information pertaining to the headers * of the operation */ -methods.createOperationRequestHeaderSection = (api, constraints, container) => { - const headers = container.get('headers') +methods.createOperationRequestHeaderSection = (api, constraints, container, method) => { + const headers = methods.getHeadersForOperationRequest(constraints, container, method) - if (!headers.size) { + if (!headers) { return null } @@ -844,12 +905,13 @@ methods.createOperationRequestSchemaSection = (container) => { * @returns {AbstractSection|null} the corresponding `Request` section */ methods.createOperationRequestSection = (api, constraints, container, operation) => { + const method = operation.get('method') const section = { type: 'list-item', abstract: true, content: [ methods.createOperationRequestTitleSection(constraints), - methods.createOperationRequestHeaderSection(api, constraints, container, operation), + methods.createOperationRequestHeaderSection(api, constraints, container, method), methods.createOperationRequestSchemaSection(container) ].filter(v => !!v), separator: '\n' @@ -955,6 +1017,7 @@ methods.createOperationResponseSections = (api, operation) => { }) .map(response => methods.createOperationResponseSection(api, response)) .filter(v => !!v) + .valueSeq() .toJS() if (!responseSections.length) { @@ -980,7 +1043,7 @@ methods.createOperationContentSection = (api, path, operation) => { type: 'list', depth: 0, value: [ - methods.createOperationParametersSection(api, path, container), + methods.createOperationParametersSection(path, container), methods.createOperationRequestSection(api, contextConstraints, container, operation), ...methods.createOperationResponseSections(api, operation) ].filter(v => !!v) @@ -1108,6 +1171,40 @@ methods.createDataStructuresHeaderSection = () => { return section } +methods.createDataStructuresSectionContent = ({ key, value }) => { + const header = { + type: 'header', + depth: 2, + value: { + abstract: true, + content: [ + key, + ' (', + value.type || 'object', + ')' + ], + separator: '' + } + } + + const schema = { + type: 'asset', + depth: 2, + value: value + } + + const section = { + abstract: true, + content: [ + header, + schema + ], + separator: '\n' + } + + return section +} + /** * converts each shared constraint into a Data Structure Section * @param {Api} api: the api from which to get the shared constraints @@ -1116,45 +1213,13 @@ methods.createDataStructuresHeaderSection = () => { methods.createDataStructureSections = (api) => { const constraints = api.getIn([ 'store', 'constraint' ]) - if (!constraints) { + if (!constraints || !constraints.size) { return [] } const sections = constraints .map((constraint, key) => ({ key, value: constraint.toJSONSchema() })) - .map(({ key, value }) => { - const header = { - type: 'header', - depth: 2, - value: { - abstract: true, - content: [ - key, - ' (', - value.type || 'object', - ')' - ], - separator: '' - } - } - - const schema = { - type: 'asset', - depth: 2, - value: value - } - - const section = { - abstract: true, - content: [ - header, - schema - ], - separator: '\n' - } - - return section - }) + .map(methods.createDataStructuresSectionContent) .valueSeq() .toJS() @@ -1221,7 +1286,7 @@ methods.stringifyAbstractSection = (section) => { methods.stringifyHeaderSection = (section) => { const header = methods.stringifySection(section.value) - return (new Array(section.depth + 1)).join('#') + ' ' + header + return (new Array(Math.max(section.depth, 1) + 1)).join('#') + ' ' + header } /** diff --git a/src/serializers/api-blueprint/1A/__tests__/Serializer.spec.js b/src/serializers/api-blueprint/1A/__tests__/Serializer.spec.js new file mode 100644 index 0000000..5e6d74c --- /dev/null +++ b/src/serializers/api-blueprint/1A/__tests__/Serializer.spec.js @@ -0,0 +1,2086 @@ +/* eslint-disable max-nested-callbacks */ +import expect, { spyOn, restoreSpies } from 'expect' +import { OrderedMap, List } from 'immutable' + +import Api from '../../../../models/Api' +import Store from '../../../../models/Store' +import URL from '../../../../models/URL' +import URLComponent from '../../../../models/URLComponent' +import Variable from '../../../../models/Variable' +import Parameter from '../../../../models/Parameter' +import Constraint from '../../../../models/Constraint' +import Info from '../../../../models/Info' +import Resource from '../../../../models/Resource' +import Request from '../../../../models/Request' +import Reference from '../../../../models/Reference' +import ParameterContainer from '../../../../models/ParameterContainer' +import Response from '../../../../models/Response' +import Context from '../../../../models/Context' + +/* +import Group from '../../../../models/Group' +import Auth from '../../../../models/Auth' +*/ + +import Serializer, { __internals__ } from '../Serializer' + +describe('serializers/api-blueprint/1A/Serializer.js', () => { + afterEach(() => restoreSpies()) + describe('{ Serializer }', () => { + describe('@serialize', () => { + it('should call __internals__.serialize', () => { + const expected = 1234 + spyOn(__internals__, 'serialize').andReturn(expected) + + const actual = Serializer.serialize() + + expect(__internals__.serialize).toHaveBeenCalled() + expect(actual).toEqual(expected) + }) + + it('should call __internals__.serialize with the correct arguments', () => { + const expected = 1234 + spyOn(__internals__, 'serialize').andReturn(expected) + + const input = '123412312' + const actual = Serializer.serialize(input) + + expect(__internals__.serialize).toHaveBeenCalledWith(input) + expect(actual).toEqual(expected) + }) + }) + + describe('@validate', () => { + it('should call __internals__.validate', () => { + const expected = 1234 + spyOn(__internals__, 'validate').andReturn(expected) + + const actual = Serializer.validate() + + expect(__internals__.validate).toHaveBeenCalled() + expect(actual).toEqual(expected) + }) + + it('should call __internals__.validate with the correct arguments', () => { + const expected = 1234 + spyOn(__internals__, 'validate').andReturn(expected) + + const input = '123412312' + const actual = Serializer.validate(input) + + expect(__internals__.validate).toHaveBeenCalledWith(input) + expect(actual).toEqual(expected) + }) + }) + }) + + describe('@validate', () => { + it('should work', () => { + const inputs = [ + null + ] + const expected = [ + 0 + ] + const actual = inputs.map(input => __internals__.validate(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@getRootHostFromEndpoint', () => { + it('should work', () => { + const inputs = [ + new Api(), + new Api({ + store: new Store({ + endpoint: OrderedMap({ + a: new URL({ + url: 'https://echo.paw.cloud', + variableDelimiters: List([ '{', '}' ]) + }) + }) + }) + }) + ] + const expected = [ + null, + new URL({ + url: 'https://echo.paw.cloud', + variableDelimiters: List([ '{', '}' ]) + }) + ] + const actual = inputs.map(input => __internals__.getRootHostFromEndpoint(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@getRootHostFromVariable', () => { + it('should work', () => { + const inputs = [ + new Api(), + new Api({ + store: new Store({ + variable: OrderedMap({ + a: new Variable() + }) + }) + }), + new Api({ + store: new Store({ + variable: OrderedMap({ + a: new Variable({ + values: null + }) + }) + }) + }), + new Api({ + store: new Store({ + variable: OrderedMap({ + a: new Variable({ + values: OrderedMap({ + default: 'https://echo.paw.cloud/' + }) + }) + }) + }) + }) + ] + const expected = [ + null, + null, + null, + new URL({ url: 'https://echo.paw.cloud' }) + ] + const actual = inputs.map(input => __internals__.getRootHostFromVariable(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@getRootHostForApi', () => { + it('should work', () => { + spyOn(__internals__, 'getRootHostFromEndpoint').andCall(({ e }) => e || null) + spyOn(__internals__, 'getRootHostFromVariable').andCall(({ v }) => v || null) + + const inputs = [ + { e: 123 }, + { v: 234 } + ] + const expected = [ + 123, + 234 + ] + const actual = inputs.map(input => __internals__.getRootHostForApi(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createFormatVersionSection', () => { + it('should work', () => { + const inputs = [ + null + ] + const expected = [ + { + type: 'content', + value: 'FORMAT: 1A' + } + ] + const actual = inputs.map(input => __internals__.createFormatVersionSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createFormatHostSection', () => { + it('should work', () => { + spyOn(__internals__, 'getRootHostForApi').andCall(({ host }) => host) + const inputs = [ + { host: null }, + { + host: new URL({ + url: 'https://echo.paw.cloud/{version}', + variableDelimiters: List([ '{', '}' ]) + }) + }, + { + host: new URL({ + url: 'https://echo.paw.cloud/{version}', + variableDelimiters: List([ '{', '}' ]) + }).set('pathname', new URLComponent({ + componentName: 'pathname', + string: '/{version}', + parameter: new Parameter({ + key: 'pathname', + name: 'pathname', + superType: 'sequence', + type: 'string', + value: List([ + new Parameter({ + type: 'string', + default: '/' + }), + new Parameter({ + key: 'version', + type: 'string', + constraints: List([ + new Constraint.Enum([ '2.1', '2.3' ]) + ]) + }) + ]) + }), + variableDelimiters: List([ '{', '}' ]) + })) + }, + { + host: new URL({ + url: 'https://echo.paw.cloud/{version}', + variableDelimiters: List([ '{', '}' ]) + }).set('pathname', new URLComponent({ + componentName: 'pathname', + string: '/{version}', + parameter: new Parameter({ + key: 'pathname', + name: 'pathname', + superType: 'sequence', + type: 'string', + value: List([ + new Parameter({ + type: 'string', + default: '/' + }), + new Parameter({ + key: 'version', + type: 'string', + default: '2.3', + constraints: List([ + new Constraint.Enum([ '2.1', '2.3' ]) + ]) + }) + ]) + }), + variableDelimiters: List([ '{', '}' ]) + })) + }, + { + host: new URL().set('slashes', false) + } + ] + const expected = [ + null, + { + type: 'content', + value: 'HOST: https://echo.paw.cloud/{version}' + }, + { + type: 'content', + value: 'HOST: https://echo.paw.cloud/{version}' + }, + { + type: 'content', + value: 'HOST: https://echo.paw.cloud/{version}' + }, + null + ] + const actual = inputs.map(input => __internals__.createFormatHostSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createMetadataSection', () => { + it('should work', () => { + spyOn(__internals__, 'createFormatVersionSection').andCall(v => v % 2 ? v * 2 : null) + spyOn(__internals__, 'createFormatHostSection').andCall(v => v < 300 ? v * 3 : null) + const inputs = [ + 123, + 234, + 345 + ] + const expected = [ + { + type: 'metadata', + abstract: true, + content: [ 123 * 2, 123 * 3 ], + separator: '\n' + }, + { + type: 'metadata', + abstract: true, + content: [ 234 * 3 ], + separator: '\n' + }, + { + type: 'metadata', + abstract: true, + content: [ 345 * 2 ], + separator: '\n' + } + ] + const actual = inputs.map(input => __internals__.createMetadataSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createRootTitleSection', () => { + it('should work', () => { + const inputs = [ + new Api(), + new Api({ + info: new Info({ title: 'some title' }) + }) + ] + const expected = [ + { + type: 'header', + depth: 1, + value: { + abstract: true, + content: [ 'Unnamed API' ], + separator: '' + } + }, + { + type: 'header', + depth: 1, + value: { + abstract: true, + content: [ 'some title' ], + separator: '' + } + } + ] + const actual = inputs.map(input => __internals__.createRootTitleSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createRootDescriptionSection', () => { + it('should work', () => { + const inputs = [ + new Api(), + new Api({ + info: new Info({ + description: 'some description' + }) + }) + ] + const expected = [ + null, + { + type: 'content', + value: 'some description' + } + ] + const actual = inputs.map(input => __internals__.createRootDescriptionSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createApiNameAndOverviewSection', () => { + it('should work', () => { + spyOn(__internals__, 'createRootTitleSection').andCall(v => v % 2 ? v * 2 : null) + spyOn(__internals__, 'createRootDescriptionSection').andCall(v => v < 300 ? v * 3 : null) + + const inputs = [ + 123, + 234, + 345 + ] + const expected = [ + { + type: 'overview', + abstract: true, + content: [ 123 * 2, 123 * 3 ], + separator: '\n' + }, + { + type: 'overview', + abstract: true, + content: [ 234 * 3 ], + separator: '\n' + }, + { + type: 'overview', + abstract: true, + content: [ 345 * 2 ], + separator: '\n' + } + ] + const actual = inputs.map(input => __internals__.createApiNameAndOverviewSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createResourceGroupHeaderSection', () => { + it('should work', () => { + const inputs = [ + null + ] + const expected = [ + { + type: 'header', + depth: 2, + value: { + abstract: true, + content: [ 'Group Resources' ], + separator: '' + } + } + ] + const actual = inputs.map(input => __internals__.createResourceGroupHeaderSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@convertResourceIntoResourceEntryBasedOnPath', () => { + it('should work', () => { + const inputs = [ + new Resource({ + path: new URL({ + url: 'https://echo.paw.cloud/{{version}}', + variableDelimiters: List([ '{{', '}}' ]) + }) + }) + ] + const expected = [ + { + key: 'https://echo.paw.cloud/{version}', + value: new Resource({ + path: new URL({ + url: 'https://echo.paw.cloud/{{version}}', + variableDelimiters: List([ '{{', '}}' ]) + }) + }) + } + ] + + const actual = inputs.map( + input => __internals__.convertResourceIntoResourceEntryBasedOnPath(input) + ) + expect(actual).toEqual(expected) + }) + }) + + describe('@mergeResourceEntriesBasedOnKey', () => { + it('should work', () => { + const inputs = [ + [ {}, { key: 'abc', value: 123 } ], + [ + { + abc: new Resource({ + description: 'preserved', + methods: OrderedMap({ + a: 123 + }) + }) + }, + { + key: 'abc', value: new Resource({ + description: 'dropped', + methods: OrderedMap({ + b: 234 + }) + }) + } + ], + [ + { + abc: new Resource({ + description: 'preserved', + methods: OrderedMap({ + a: 123, + b: 234 + }) + }) + }, + { + key: 'abc', value: new Resource({ + description: 'dropped', + methods: OrderedMap({ + b: 345, + c: 456 + }) + }) + } + ] + ] + const expected = [ + { abc: 123 }, + { abc: new Resource({ + description: 'preserved', + methods: OrderedMap({ + a: 123, + b: 234 + }) + }) }, + { abc: new Resource({ + description: 'preserved', + methods: OrderedMap({ + a: 123, + b: 345, + c: 456 + }) + }) } + ] + const actual = inputs.map(input => __internals__.mergeResourceEntriesBasedOnKey(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@getMergedResourcesBasedOnPathFromApi', () => { + it('should work', () => { + spyOn(__internals__, 'convertResourceIntoResourceEntryBasedOnPath').andCall(v => v * 2) + spyOn(__internals__, 'mergeResourceEntriesBasedOnKey') + .andCall((a, v, k) => Object.assign(a, { [k]: v })) + + const inputs = [ + new Api(), + new Api({ + resources: OrderedMap({ + a: 123, + b: 234, + c: 345 + }) + }) + ] + const expected = [ + OrderedMap(), + OrderedMap({ + a: 123 * 2, + b: 234 * 2, + c: 345 * 2 + }) + ] + const actual = inputs.map(input => __internals__.getMergedResourcesBasedOnPathFromApi(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@extractQueryParametersFromResourceWithDuplicates', () => { + it('should work', () => { + const inputs = [ + [ new Api(), new Resource() ], + [ new Api(), new Resource({ + methods: OrderedMap({ + a: new Request(), + b: new Request() + }) + }) ], + [ new Api(), new Resource({ + methods: OrderedMap({ + a: new Request({ + parameters: new ParameterContainer({ + headers: OrderedMap({ + a1: new Parameter({ key: 'a1' }), + a2: new Parameter({ key: 'a2' }) + }) + }) + }), + b: new Request() + }) + }) ], + [ new Api(), new Resource({ + methods: OrderedMap({ + a: new Request({ + parameters: new ParameterContainer({ + queries: OrderedMap({ + a1: new Parameter({ key: 'a1' }), + a2: new Parameter({ key: 'a2' }) + }) + }) + }), + b: new Request({ + parameters: new ParameterContainer({ + queries: OrderedMap({ + b1: new Parameter({ key: 'b1' }), + b2: new Parameter({ key: 'b2' }) + }), + headers: OrderedMap({ + b3: new Parameter({ key: 'b3' }) + }) + }) + }) + }) + }) ], + [ new Api(), new Resource({ + methods: OrderedMap({ + a: new Request({ + parameters: new ParameterContainer({ + queries: OrderedMap({ + a1: new Parameter({ key: 'a1' }), + a2: new Parameter({ key: 'a2' }), + // checks if duplicates are removed + s1: new Parameter({ key: 's1' }) + }) + }) + }), + b: new Request({ + parameters: new ParameterContainer({ + queries: OrderedMap({ + b1: new Parameter({ key: 'b1' }), + b2: new Parameter({ key: 'b2' }), + s1: new Parameter({ key: 's1' }) + }), + headers: OrderedMap({ + b3: new Parameter({ key: 'b3' }) + }) + }) + }) + }) + }) ], + [ new Api({ + store: new Store({ + parameter: OrderedMap({ + s2: new Parameter({ key: 's2' }), + s3: new Parameter({ key: 's3' }), + s4: new Parameter({ key: 's4' }), + s5: new Parameter({ key: 's5' }) + }) + }) + }), new Resource({ + methods: OrderedMap({ + a: new Request({ + parameters: new ParameterContainer({ + queries: OrderedMap({ + a1: new Parameter({ key: 'a1' }), + a2: new Parameter({ key: 'a2' }), + s1: new Parameter({ key: 's1' }), + // checks if Reference are resolved correctly + s2: new Reference({ type: 'parameter', uuid: 's2' }), + // checks if multiple references pointing to the same param are not duplicated + s3: new Reference({ type: 'parameter', uuid: 's3' }), + // checks if a Param & Reference with the same resolved `key` are not duplicated + s4: new Parameter({ key: 's4' }) + }) + }) + }), + b: new Request({ + parameters: new ParameterContainer({ + queries: OrderedMap({ + b1: new Parameter({ key: 'b1' }), + b2: new Parameter({ key: 'b2' }), + s1: new Parameter({ key: 's1' }), + s3: new Reference({ type: 'parameter', uuid: 's3' }), + s4: new Reference({ type: 'parameter', uuid: 's4' }) + }), + headers: OrderedMap({ + b3: new Parameter({ key: 'b3' }) + }) + }) + }) + }) + }) ] + ] + const expected = [ + [], + [], + [], + [ 'a1', 'a2', 'b1', 'b2' ], + [ 'a1', 'a2', 's1', 'b1', 'b2', 's1' ], + [ 'a1', 'a2', 's1', 's2', 's3', 's4', 'b1', 'b2', 's1', 's3', 's4' ] + ] + const actual = inputs.map( + input => __internals__.extractQueryParametersFromResourceWithDuplicates(...input) + ) + expect(actual).toEqual(expected) + }) + }) + + describe('@deduplicateArray', () => { + it('should work', () => { + const inputs = [ + [ 123, 123, 234, 345, 123, 234, 345, 234 ] + ] + const expected = [ + [ 123, 234, 345 ] + ] + const actual = inputs.map(input => __internals__.deduplicateArray(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@extractQueryParametersFromResource', () => { + it('should work', () => { + spyOn(__internals__, 'extractQueryParametersFromResourceWithDuplicates') + .andCall((a, r) => [ a * 2, r * 2 ]) + spyOn(__internals__, 'deduplicateArray').andCall(a => a.map(v => v * 3)) + const inputs = [ + [ 123, 234 ] + ] + const expected = [ + [ 123 * 2 * 3, 234 * 2 * 3 ] + ] + const actual = inputs.map(input => __internals__.extractQueryParametersFromResource(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@extractQueryStringFromResource', () => { + it('should work', () => { + spyOn(__internals__, 'extractQueryParametersFromResource') + .andCall((a, r) => a ? [ a * 2, r * 2 ] : []) + const inputs = [ + [ null, null ], + [ 123, 234 ] + ] + const expected = [ + null, + { + type: 'query-params', + abstract: true, + content: [ + '{?', + { + abstract: true, + content: [ 123 * 2, 234 * 2 ], + separator: ',' + }, + '}' + ], + separator: '' + } + ] + const actual = inputs.map(input => __internals__.extractQueryStringFromResource(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createResourceTitleSectionContent', () => { + it('should work', () => { + spyOn(__internals__, 'extractQueryStringFromResource').andCall(a => a) + + const inputs = [ + [ null, new Resource({ + name: null, + path: new URL({ url: '/users/{userId}', variableDelimiters: List([ '{', '}' ]) }) + }) ], + [ null, new Resource({ + name: '/customers/{userId}', + path: new URL({ url: '/users/{userId}', variableDelimiters: List([ '{', '}' ]) }) + }) ], + [ null, new Resource({ + name: 'User Operations', + path: new URL({ url: '/users/{userId}', variableDelimiters: List([ '{', '}' ]) }) + }) ], + [ '{?graphql}', new Resource({ + name: 'User Operations', + path: new URL({ url: '/users/{userId}', variableDelimiters: List([ '{', '}' ]) }) + }) ], + [ '{?graphql}', new Resource({ + path: new URL({ url: '/users/{userId}', variableDelimiters: List([ '{', '}' ]) }) + }) ], + [ '{?graphql}', new Resource({ + path: (new URL()).set('slashes', false) + }) ] + ] + const expected = [ + [ '/users/{userId}' ], + [ '/users/{userId}' ], + [ 'User Operations', ' [', '/users/{userId}', ']' ], + [ 'User Operations', ' [', '/users/{userId}', '{?graphql}', ']' ], + [ '/users/{userId}', '{?graphql}' ], + [ '/', '{?graphql}' ] + ] + const actual = inputs.map(input => __internals__.createResourceTitleSectionContent(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createResourceTitleSection', () => { + it('should work', () => { + spyOn(__internals__, 'createResourceTitleSectionContent') + .andCall((a, r) => a ? [ a * 2, r * 2 ] : []) + + const inputs = [ + [ null, null ], + [ 123, 234 ] + ] + const expected = [ + null, + { + type: 'header', + depth: 3, + value: { + abstract: true, + content: [ 123 * 2, 234 * 2 ], + separator: '' + } + } + ] + const actual = inputs.map(input => __internals__.createResourceTitleSection(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createResourceDescriptionSection', () => { + it('should work', () => { + const inputs = [ + new Resource(), + new Resource({ + description: 'abc' + }) + ] + const expected = [ + null, + { + type: 'content', + value: 'abc' + } + ] + const actual = inputs.map(input => __internals__.createResourceDescriptionSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationTitleSectionContent', () => { + it('should work', () => { + const inputs = [ + new Request(), + new Request({ + name: 'get user by id' + }), + new Request({ + method: 'get' + }), + new Request({ + name: 'get user by id', + method: 'get' + }) + ] + const expected = [ + [], + [], + [ 'GET' ], + [ 'get user by id', ' [', 'GET', ']' ] + ] + const actual = inputs.map(input => __internals__.createOperationTitleSectionContent(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationTitleSection', () => { + it('should work', () => { + spyOn(__internals__, 'createOperationTitleSectionContent') + .andCall(o => o.map(v => v * 2)) + + const inputs = [ + [], + [ 123 ] + ] + const expected = [ + null, + { + type: 'header', + depth: 4, + value: { + abstract: true, + content: [ 123 * 2 ], + separator: '' + } + } + ] + const actual = inputs.map(input => __internals__.createOperationTitleSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationDescriptionSection', () => { + it('should work', () => { + const inputs = [ + new Request(), + new Request({ + description: 'abc' + }) + ] + const expected = [ + null, + { type: 'content', value: 'abc' } + ] + const actual = inputs.map(input => __internals__.createOperationDescriptionSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createParameterKeySegment', () => { + it('should work', () => { + const inputs = [ + new Parameter(), + new Parameter({ + key: 'limit' + }) + ] + const expected = [ + null, + { + abstract: true, + content: [ 'limit' ], + separator: '' + } + ] + const actual = inputs.map(input => __internals__.createParameterKeySegment(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createParameterOptionalSegmentContent', () => { + it('should work', () => { + const inputs = [ + [ {}, new Parameter() ], + [ {}, new Parameter({ type: 'string' }) ], + [ { type: 'number' }, new Parameter({ type: 'string' }) ], + [ { type: 'number' }, new Parameter({ type: 'string', required: true }) ] + ] + const expected = [ + [ 'optional' ], + [ 'string', 'optional' ], + [ 'number', 'optional' ], + [ 'number', 'required' ] + ] + const actual = inputs.map( + input => __internals__.createParameterOptionalSegmentContent(...input) + ) + expect(actual).toEqual(expected) + }) + }) + + describe('@createParameterOptionalSegment', () => { + it('should work', () => { + spyOn(__internals__, 'createParameterOptionalSegmentContent') + .andCall((s, p) => s ? [ s * 2, p * 2 ] : []) + const inputs = [ + [ null, null ], + [ 123, 234 ] + ] + const expected = [ + null, + { + abstract: true, + content: [ + '(', + { + abstract: true, + content: [ 123 * 2, 234 * 2 ], + separator: ', ' + }, + ')' + ], + separator: '' + } + ] + const actual = inputs.map(input => __internals__.createParameterOptionalSegment(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createParameterDescriptionSegment', () => { + it('should work', () => { + const inputs = [ + [ {}, new Parameter() ], + [ {}, new Parameter({ description: 'abc' }) ], + [ { description: 'def' }, new Parameter({ description: 'abc' }) ] + ] + const expected = [ + null, + { + abstract: true, + content: [ '-', 'abc' ], + separator: ' ' + }, + { + abstract: true, + content: [ '-', 'def' ], + separator: ' ' + } + ] + const actual = inputs.map(input => __internals__.createParameterDescriptionSegment(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createParameterPayloadSection', () => { + it('should work', () => { + spyOn(__internals__, 'createParameterKeySegment').andCall(p => p ? p * 2 : null) + spyOn(__internals__, 'createParameterOptionalSegment').andCall((s, p) => s ? s + p : null) + spyOn(__internals__, 'createParameterDescriptionSegment').andCall((s, p) => s ? s * p : null) + + const inputs = [ + [ null, null ], + [ 123, 234 ] + ] + const expected = [ + null, + { + abstract: true, + content: [ + 234 * 2, + 123 + 234, + 123 * 234 + ].filter(v => !!v), + separator: ' ' + } + ] + const actual = inputs.map(input => __internals__.createParameterPayloadSection(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@convertParameterIntoParamSection', () => { + it('should work', () => { + spyOn(__internals__, 'createParameterPayloadSection') + .andCall(s => s['x-title'] || null) + spyOn(__internals__, 'createOperationRequestSchemaAssetSection') + .andCall(s => s['x-title'] ? s['x-title'] * 2 : null) + + const inputs = [ + null, + new Parameter(), + new Parameter({ key: 123 }) + ] + const expected = [ + null, + null, + { + type: 'parameter', + abstract: true, + content: [ + 123, + 123 * 2 + ], + separator: '\n' + } + ] + const actual = inputs.map(input => __internals__.convertParameterIntoParamSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@extractPathParamsFromPath', () => { + it('should work', () => { + const inputs = [ + new URL({ url: '/users', variableDelimiters: List([ '{', '}' ]) }), + new URL({ url: '/users/{userId}', variableDelimiters: List([ '{', '}' ]) }), + new URL({ url: '/users/{userId}', variableDelimiters: List([ '{', '}' ]) }) + .set('pathname', new URLComponent({ + parameter: new Parameter({ superType: 'sequence' }) + }) + ), + new URL({ url: '/users/{userId}', variableDelimiters: List([ '{', '}' ]) }) + .set('pathname', new URLComponent({ + parameter: new Parameter({ superType: 'sequence', value: List() }) + }) + ) + ] + const expected = [ + [], + [ new Parameter({ + key: 'userId', + name: 'userId', + type: 'string', + default: 'userId' + }) ], + [], + [] + ] + const actual = inputs.map(input => __internals__.extractPathParamsFromPath(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationParametersSectionContent', () => { + it('should work', () => { + spyOn(__internals__, 'convertParameterIntoParamSection') + .andCall(v => v * 2) + spyOn(__internals__, 'extractPathParamsFromPath').andCall(a => a.map(v => v * 3)) + + const inputs = [ + [ [], new ParameterContainer() ], + [ [], new ParameterContainer({ + queries: OrderedMap({ + a: 123, + b: 234 + }) + }) ], + [ [ 345 ], new ParameterContainer({ + queries: OrderedMap({ + a: 123, + b: 234 + }) + }) ] + ] + + const expected = [ + [], + [ 123 * 2, 234 * 2 ], + [ 123 * 2, 234 * 2, 345 * 2 * 3 ] + ] + + const actual = inputs.map( + input => __internals__.createOperationParametersSectionContent(...input) + ) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationParametersSection', () => { + it('should work', () => { + spyOn(__internals__, 'createOperationParametersSectionContent') + .andCall((p, c) => p ? [ p * 2, c * 2 ] : []) + + const inputs = [ + [ null, null ], + [ 123, 234 ] + ] + const expected = [ + null, + { + type: 'list-item', + abstract: true, + content: [ + 'Parameters', + { + type: 'list', + depth: 2, + value: [ 123 * 2, 234 * 2 ] + } + ], + separator: '\n' + } + ] + + const actual = inputs.map(input => __internals__.createOperationParametersSection(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@extractContentTypeFromConstraints', () => { + it('should work', () => { + const inputs = [ + List(), + List([ + new Parameter({ key: 'Accept', default: 'application/json' }) + ]), + List([ + new Parameter({ key: 'Accept', default: 'application/json' }), + new Parameter({ key: 'Content-Type', default: 'application/json' }) + ]), + List([ + new Parameter({ key: 'Accept', default: 'application/json' }), + new Parameter({ key: 'Content-Type', default: 'application/json' }), + new Parameter({ key: 'Content-Type', default: 'application/xml' }) + ]), + List([ + new Parameter({ key: 'Accept', default: 'application/json' }), + new Parameter({ key: 'Content-Type' }) + ]) + ] + const expected = [ + null, + null, + 'application/json', + null, + null + ] + const actual = inputs.map(input => __internals__.extractContentTypeFromConstraints(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationRequestTitleSection', () => { + it('should work', () => { + spyOn(__internals__, 'extractContentTypeFromConstraints').andCall(c => c ? c * 2 : null) + + const inputs = [ + null, + 123 + ] + + const expected = [ + { + type: 'content', + value: 'Request' + }, + { + abstract: true, + content: [ + 'Request (', + 123 * 2, + ')' + ], + separator: '' + } + ] + + const actual = inputs.map(input => __internals__.createOperationRequestTitleSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@convertHeaderParameterIntoHeaderSection', () => { + it('should work', () => { + const inputs = [ + new Parameter(), + new Parameter({ + key: 'Content-Type', + default: 'application/json', + constraints: List([ + new Constraint.JSONSchema({ type: 'string', pattern: 'application/.*+?json' }) + ]) + }), + new Parameter({ + key: 'Content-Type', + constraints: List([ + new Constraint.JSONSchema({ type: 'string', pattern: 'application/.*+?json' }) + ]) + }), + new Parameter({ + key: 'Content-Type', + constraints: List([ + new Constraint.JSONSchema({ + type: 'string', + default: 'application/json', + pattern: 'application/.*+?json' + }) + ]) + }), + new Parameter({ + key: 'Content-Type', + constraints: List([ + new Constraint.JSONSchema({ + type: 'string', + enum: [ 'application/json', 'application/xml' ] + }) + ]) + }) + ] + const expected = [ + 'null: null', + 'Content-Type: application/json', + 'Content-Type: null', + 'Content-Type: application/json', + 'Content-Type: application/json' + ] + const actual = inputs.map( + input => __internals__.convertHeaderParameterIntoHeaderSection(input) + ) + expect(actual).toEqual(expected) + }) + }) + + describe('@getHeadersForOperationRequest', () => { + it('should work', () => { + spyOn(__internals__, 'extractContentTypeFromConstraints').andCall(v => v) + const inputs = [ + [ null, new ParameterContainer(), null ], + [ null, new ParameterContainer({ + headers: OrderedMap({ + a: new Parameter({ key: 'X-Origin' }), + b: new Parameter({ key: 'Content-Type' }) + }) + }), null ], + [ 'application/json', new ParameterContainer({ + headers: OrderedMap({ + a: new Parameter({ key: 'X-Origin' }), + b: new Parameter({ key: 'Content-Type' }) + }) + }), null ], + [ null, new ParameterContainer({ + headers: OrderedMap({ + a: new Parameter({ key: 'X-Origin' }), + b: new Parameter({ key: 'Content-Type' }) + }) + }), 'get' ], + [ 'application/json', new ParameterContainer({ + headers: OrderedMap({ + a: new Parameter({ key: 'X-Origin' }), + b: new Parameter({ key: 'Content-Type' }) + }) + }), 'post' ], + [ 'application/json', new ParameterContainer({ + headers: OrderedMap({ + b: new Parameter({ key: 'Content-Type' }) + }) + }), 'post' ] + ] + const expected = [ + null, + OrderedMap({ + a: new Parameter({ key: 'X-Origin' }), + b: new Parameter({ key: 'Content-Type' }) + }), + OrderedMap({ + a: new Parameter({ key: 'X-Origin' }) + }), + OrderedMap({ + a: new Parameter({ key: 'X-Origin' }) + }), + OrderedMap({ + a: new Parameter({ key: 'X-Origin' }) + }), + null + ] + const actual = inputs.map(input => __internals__.getHeadersForOperationRequest(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationRequestHeaderSection', () => { + it('should work', () => { + spyOn(__internals__, 'getHeadersForOperationRequest').andCall((cs, c, m) => { + return cs ? List([ cs * 2, c * 2, m * 2 ]) : null + }) + + spyOn(__internals__, 'convertHeaderParameterIntoHeaderSection').andCall(v => v * 3) + + const inputs = [ + [ null, null, null, null ], + [ 123, 234, 345, 456 ] + ] + + const expected = [ + null, + { + type: 'list', + depth: 2, + value: [ + { + abstract: true, + content: [ + 'Headers', + { + type: 'asset', + depth: 4, + value: [ 234 * 2 * 3, 345 * 2 * 3, 456 * 2 * 3 ].join('\n') + } + ], + separator: '\n' + } + ] + } + ] + const actual = inputs.map( + input => __internals__.createOperationRequestHeaderSection(...input) + ) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationRequestSchemaAssetSection', () => { + it('should work', () => { + const inputs = [ + 123 + ] + const expected = [ + { + type: 'asset', + depth: 4, + value: 123 + } + ] + + const actual = inputs.map( + input => __internals__.createOperationRequestSchemaAssetSection(input) + ) + expect(actual).toEqual(expected) + }) + }) + + describe('@getSchemaForSingleBodyParameter', () => { + it('should work', () => { + const inputs = [ + OrderedMap({ + a: new Parameter({ + constraints: List([ + new Constraint.JSONSchema({ + type: 'string', + pattern: '[0-9a-f]{16}' + }) + ]) + }) + }) + ] + const expected = [ + { + type: 'string', + pattern: '[0-9a-f]{16}' + } + ] + const actual = inputs.map(input => __internals__.getSchemaForSingleBodyParameter(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@getSchemaForMultipleBodyParameters', () => { + it('should work', () => { + const inputs = [ + OrderedMap(), + OrderedMap({ + a: new Parameter({ + constraints: List([ new Constraint.JSONSchema({ pattern: '[0-9a-f]{16}' }) ]) + }), + b: new Parameter({ + key: 'b2', + constraints: List([ new Constraint.JSONSchema({ pattern: '[0-9a-f]{10}' }) ]) + }), + c: new Parameter({ + constraints: List([ + new Constraint.JSONSchema({ title: 'c2', pattern: '[0-9a-f]{13}' }) + ]) + }) + }) + ] + + const expected = [ + { type: 'object', properties: {} }, + { type: 'object', properties: { + a: { pattern: '[0-9a-f]{16}' }, + b2: { pattern: '[0-9a-f]{10}', 'x-title': 'b2' }, + c2: { pattern: '[0-9a-f]{13}', title: 'c2' } + } } + ] + const actual = inputs.map(input => __internals__.getSchemaForMultipleBodyParameters(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@getSchemaFromBodyParameters', () => { + it('should work', () => { + spyOn(__internals__, 'getSchemaForSingleBodyParameter') + .andCall(p => p.map(v => v * 2).valueSeq().toJS()) + spyOn(__internals__, 'getSchemaForMultipleBodyParameters') + .andCall(p => p.map(v => v * 3).valueSeq().toJS()) + + const inputs = [ + OrderedMap(), + OrderedMap({ a: 123 }), + OrderedMap({ a: 123, b: 234 }) + ] + const expected = [ + null, + [ 123 * 2 ], + [ 123 * 3, 234 * 3 ] + ] + const actual = inputs.map(input => __internals__.getSchemaFromBodyParameters(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationRequestSchemaSection', () => { + it('should work', () => { + spyOn(__internals__, 'getSchemaFromBodyParameters') + .andCall(p => p.valueSeq().get(0) * 2) + spyOn(__internals__, 'createOperationRequestSchemaAssetSection') + .andCall(v => v * 3) + const inputs = [ + new ParameterContainer(), + new ParameterContainer({ + body: OrderedMap({ a: 123 }) + }) + ] + + const expected = [ + null, + { + type: 'list', + depth: 2, + value: [ + { + abstract: true, + content: [ + 'Schema', + 123 * 2 * 3 + ], + separator: '\n' + } + ] + } + ] + const actual = inputs.map(input => __internals__.createOperationRequestSchemaSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationRequestSection', () => { + it('should work', () => { + spyOn(__internals__, 'createOperationRequestTitleSection').andCall(c => c ? c * 2 : null) + spyOn(__internals__, 'createOperationRequestHeaderSection') + .andCall((a, cs, c, m) => a ? a + cs + c + m : null) + spyOn(__internals__, 'createOperationRequestSchemaSection') + .andCall(c => c ? c * 3 : null) + + const inputs = [ + [ null, null, null, new Request() ], + [ null, 234, null, new Request({ method: 456 }) ], + [ 123, 234, 345, new Request({ method: 456 }) ] + ] + const expected = [ + null, + null, + { + type: 'list-item', + abstract: true, + content: [ + 234 * 2, + 123 + 234 + 345 + 456, + 345 * 3 + ].filter(v => !!v), + separator: '\n' + } + ] + const actual = inputs.map(input => __internals__.createOperationRequestSection(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationResponseTitleSection', () => { + it('should work', () => { + spyOn(__internals__, 'extractContentTypeFromConstraints').andCall(v => v) + + const inputs = [ + [ new Response(), null ], + [ new Response({ code: 404 }), null ], + [ new Response({ code: 404 }), 'application/json' ] + ] + const expected = [ + { + type: 'content', + value: 'Response 200' + }, + { + type: 'content', + value: 'Response 404' + }, + { + abstract: true, + content: [ 'Response ', 404, ' (', 'application/json', ')' ], + separator: '' + } + ] + const actual = inputs.map( + input => __internals__.createOperationResponseTitleSection(...input) + ) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationResponseSection', () => { + it('should work', () => { + spyOn(__internals__, 'createOperationResponseTitleSection') + .andCall((r, l) => l.size ? l.toJS().join(',') : null) + spyOn(__internals__, 'createOperationRequestHeaderSection') + .andCall((a, cs) => cs.size ? cs.map(v => v * 2).toJS().join(',') : null) + spyOn(__internals__, 'createOperationRequestSchemaSection') + .andReturn(456) + const inputs = [ + [ new Api(), null ], + [ new Api(), new Response() ], + [ new Api(), new Response({ + contexts: List([ + new Context({ + constraints: List([ 123, 234, 345 ]) + }) + ]) + }) ] + ] + const expected = [ + null, + { + type: 'request', + abstract: true, + content: [ 456 ], + separator: '\n' + }, + { + type: 'request', + abstract: true, + content: [ '123,234,345', '246,468,690', 456 ], + separator: '\n' + } + ] + const actual = inputs.map(input => __internals__.createOperationResponseSection(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createDefaultResponseSection', () => { + it('should work', () => { + const inputs = [ + null + ] + const expected = [ + [ { + type: 'content', + value: 'Response 200' + } ] + ] + const actual = inputs.map(input => __internals__.createDefaultResponseSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationResponseSections', () => { + it('should work', () => { + spyOn(__internals__, 'createDefaultResponseSection').andReturn([ 123 ]) + spyOn(__internals__, 'createOperationResponseSection') + .andCall((a, r) => r ? r.get('code') * 2 : null) + + const inputs = [ + [ new Api(), new Request() ], + [ new Api({ + store: new Store({ + response: OrderedMap({ + '500': new Response({ code: 500 }) + }) + }) + }), new Request({ + responses: OrderedMap({ + '200': new Response({ code: 200 }), + '404': new Response({ code: 404 }), + '500': new Reference({ type: 'response', uuid: '500' }), + '600': new Reference({ type: 'response', uuid: '600' }) + }) + }) ] + ] + const expected = [ + [ 123 ], + [ 400, 808, 1000 ] + ] + const actual = inputs.map(input => __internals__.createOperationResponseSections(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationContentSection', () => { + it('should work', () => { + spyOn(__internals__, 'createOperationParametersSection').andCall(p => p ? p * 2 : null) + spyOn(__internals__, 'createOperationRequestSection') + .andCall((a, cs, c, o) => o.get('name') ? o.get('name') * 3 : null) + spyOn(__internals__, 'createOperationResponseSections') + .andCall((a, o) => o.get('name') ? [ o.get('name') * 4 ] : []) + + const inputs = [ + [ new Api(), null, new Request() ], + [ new Api(), 123, new Request({ + name: 200, + contexts: List([ new Context({ constraints: List([ new Parameter({ + key: 'Content-Type', + default: 'application/json' + }) ]) }) ]) + }) ] + ] + const expected = [ + null, + { + type: 'list', + depth: 0, + value: [ 123 * 2, 200 * 3, 200 * 4 ] + } + ] + + const actual = inputs.map(input => __internals__.createOperationContentSection(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createOperationSection', () => { + it('should work', () => { + spyOn(__internals__, 'createOperationTitleSection').andCall(o => o ? o * 2 : null) + spyOn(__internals__, 'createOperationDescriptionSection').andCall(o => o ? o * 3 : null) + spyOn(__internals__, 'createOperationContentSection') + .andCall((a, p, o) => a ? a + p + o : null) + + const inputs = [ + [ null, null, null ], + [ 123, 234, 345 ] + ] + const expected = [ + null, + { + type: 'operation', + abstract: true, + content: [ 345 * 2, 345 * 3, 123 + 234 + 345 ], + separator: '\n\n' + } + ] + const actual = inputs.map(input => __internals__.createOperationSection(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createResourceOperationSections', () => { + it('should work', () => { + spyOn(__internals__, 'createOperationSection').andCall((a, p, o) => a ? o + p : null) + + const inputs = [ + [ null, new Resource() ], + [ + 123, + new Resource({ + path: 234, + methods: OrderedMap({ a: 345, b: 456 }) + }) + ] + ] + const expected = [ + [], + [ 234 + 345, 234 + 456 ] + ] + const actual = inputs.map(input => __internals__.createResourceOperationSections(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createResourceSection', () => { + it('should work', () => { + spyOn(__internals__, 'createResourceTitleSection') + .andCall((a, r) => a ? a + r : null) + spyOn(__internals__, 'createResourceDescriptionSection') + .andCall(r => r * 2) + spyOn(__internals__, 'createResourceOperationSections') + .andCall(a => a ? [ a * 2 ] : []) + + const inputs = [ + [ null, null ], + [ 123, 234 ] + ] + const expected = [ + null, + { + type: 'resource', + abstract: true, + content: [ + 123 + 234, + 234 * 2, + 123 * 2 + ].filter(v => !!v), + separator: '\n\n' + } + ] + const actual = inputs.map(input => __internals__.createResourceSection(...input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createResourceSections', () => { + it('should work', () => { + spyOn(__internals__, 'getMergedResourcesBasedOnPathFromApi').andCall(v => v) + spyOn(__internals__, 'createResourceSection').andCall((a, r) => r * 2) + + const inputs = [ + OrderedMap({ a: 123, b: 234, c: 345 }) + ] + const expected = [ + [ 123 * 2, 234 * 2, 345 * 2 ] + ] + const actual = inputs.map(input => __internals__.createResourceSections(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createResourceGroupSection', () => { + it('should work', () => { + spyOn(__internals__, 'createResourceGroupHeaderSection') + .andReturn(123) + spyOn(__internals__, 'createResourceSections') + .andCall(a => [ a ]) + + const inputs = [ + 234 + ] + + const expected = [ + { + type: 'resourceGroups', + abstract: true, + content: [ + 123, + 234 + ], + separator: '\n\n' + } + ] + const actual = inputs.map(input => __internals__.createResourceGroupSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createDataStructuresHeaderSection', () => { + it('should work', () => { + const inputs = [ + null + ] + const expected = [ + { + type: 'header', + depth: 1, + value: 'Data Structures' + } + ] + const actual = inputs.map(input => __internals__.createDataStructuresHeaderSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createDataStructuresSectionContent', () => { + it('should work', () => { + const inputs = [ + { key: 'User', value: {} }, + { key: 'address', value: { type: 'string' } } + ] + const expected = [ + { + abstract: true, + content: [ + { + type: 'header', + depth: 2, + value: { + abstract: true, + content: [ + 'User', + ' (', + 'object', + ')' + ], + separator: '' + } + }, + { + type: 'asset', + depth: 2, + value: {} + } + ], + separator: '\n' + }, + { + abstract: true, + content: [ + { + type: 'header', + depth: 2, + value: { + abstract: true, + content: [ + 'address', + ' (', + 'string', + ')' + ], + separator: '' + } + }, + { + type: 'asset', + depth: 2, + value: { type: 'string' } + } + ], + separator: '\n' + } + ] + const actual = inputs.map(input => __internals__.createDataStructuresSectionContent(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createDataStructureSections', () => { + it('should work', () => { + spyOn(__internals__, 'createDataStructuresSectionContent') + .andCall(({ value }) => value.default * 2) + + const inputs = [ + new Api(), + new Api({ + store: new Store({ + constraint: null + }) + }), + new Api({ + store: new Store({ + constraint: OrderedMap({ + a: new Constraint.JSONSchema({ type: 'number', default: 123 }), + b: new Constraint.JSONSchema({ type: 'number', default: 234 }) + }) + }) + }) + ] + const expected = [ + [], + [], + [ 123 * 2, 234 * 2 ] + ] + + const actual = inputs.map(input => __internals__.createDataStructureSections(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@createDataStructuresSection', () => { + it('should work', () => { + spyOn(__internals__, 'createDataStructureSections') + .andCall(a => a ? [ a * 2 ] : []) + spyOn(__internals__, 'createDataStructuresHeaderSection') + .andReturn(234) + + const inputs = [ + null, + 123 + ] + const expected = [ + null, + { + type: 'dataStructures', + abstract: true, + content: [ 234, 123 * 2 ], + separator: '\n\n' + } + ] + const actual = inputs.map(input => __internals__.createDataStructuresSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@convertApiIntoSection', () => { + it('should work', () => { + spyOn(__internals__, 'createMetadataSection').andCall(a => a ? a * 2 : null) + spyOn(__internals__, 'createApiNameAndOverviewSection').andCall(a => a ? a * 3 : null) + spyOn(__internals__, 'createResourceGroupSection').andCall(a => a ? a * 4 : null) + spyOn(__internals__, 'createDataStructuresSection').andCall(a => a ? a * 5 : null) + + const inputs = [ + null, + 123 + ] + const expected = [ + { + abstract: true, + content: [], + separator: '\n\n' + }, + { + abstract: true, + content: [ 123 * 2, 123 * 3, 123 * 4, 123 * 5 ], + separator: '\n\n' + } + ] + const actual = inputs.map(input => __internals__.convertApiIntoSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@stringifyAbstractSection', () => { + it('should work', () => { + spyOn(__internals__, 'stringifySection').andCall(v => v * 2) + + const inputs = [ + { content: [], separator: '**' }, + { content: [ 123, 234 ], separator: '**' } + ] + const expected = [ + '', + '246**468' + ] + const actual = inputs.map(input => __internals__.stringifyAbstractSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@stringifyHeaderSection', () => { + it('should work', () => { + spyOn(__internals__, 'stringifySection').andCall(v => v * 2) + + const inputs = [ + { value: 123, depth: 1 }, + { value: 234, depth: 3 }, + { value: 234, depth: 0 } + ] + const expected = [ + '# 246', + '### 468', + '# 468' + ] + const actual = inputs.map(input => __internals__.stringifyHeaderSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@stringifyListSection', () => { + it('should work', () => { + const inputs = [ + { depth: 0, value: [ 123, 234 ] }, + { depth: 1, value: [ 123, 234 ] }, + { depth: 2, value: [ 123, 234 ] } + ] + const expected = [ + '+ 123\n\n' + + '+ 234', + ' + 123\n\n' + + ' + 234', + ' + 123\n\n' + + ' + 234' + ] + const actual = inputs.map(input => __internals__.stringifyListSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@stringifyAssetSection', () => { + it('should work', () => { + const inputs = [ + { value: 'abc', depth: 0 }, + { value: { a: 123 }, depth: 2 } + ] + const expected = [ + '```\n' + 'abc' + '\n```', + '```\n' + + ' {\n' + + ' "a": 123\n' + + ' }' + + '\n```' + ] + const actual = inputs.map(input => __internals__.stringifyAssetSection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@stringifySection', () => { + it('should work', () => { + spyOn(__internals__, 'stringifyAbstractSection').andCall(v => v.content * 2) + spyOn(__internals__, 'stringifyHeaderSection').andCall(v => v.value * 4) + spyOn(__internals__, 'stringifyListSection').andCall(v => v.value * 5) + spyOn(__internals__, 'stringifyAssetSection').andCall(v => v.value * 6) + + const inputs = [ + 'abc', + { abstract: true, content: 123 }, + { type: 'content', value: 234 }, + { type: 'header', value: 345 }, + { type: 'list', value: 456 }, + { type: 'asset', value: 567 }, + { type: 'weird', value: 678 } + ] + const expected = [ + 'abc', + 123 * 2, 234, 345 * 4, 456 * 5, 567 * 6, + JSON.stringify({ type: 'weird', value: 678 }, null, 2) + ] + const actual = inputs.map(input => __internals__.stringifySection(input)) + expect(actual).toEqual(expected) + }) + }) + + describe('@serialize', () => { + it('should work', () => { + spyOn(__internals__, 'convertApiIntoSection').andCall(v => v * 2) + spyOn(__internals__, 'stringifySection').andCall(v => v * 3) + + const inputs = [ + { api: 123 } + ] + const expected = [ + 123 * 2 * 3 + '\n' + ] + const actual = inputs.map(input => __internals__.serialize(input)) + expect(actual).toEqual(expected) + }) + }) +}) diff --git a/testing/e2e/internal-apiblueprint-1a/test-case-0/output.json b/testing/e2e/internal-apiblueprint-1a/test-case-0/output.json index 2b0593b..6073096 100644 --- a/testing/e2e/internal-apiblueprint-1a/test-case-0/output.json +++ b/testing/e2e/internal-apiblueprint-1a/test-case-0/output.json @@ -29,7 +29,11 @@ This is a fairly long description about the API request /pets with Method POST } ``` -+ Response 200 ++ Response 405 + + Headers +``` + Content-Type: application/json +``` #### Update an existing pet [PUT] @@ -45,7 +49,23 @@ This is a fairly long description about the API request /pets with Method POST } ``` -+ Response 200 ++ Response 400 + + Headers +``` + Content-Type: application/xml +``` + ++ Response 404 + + Headers +``` + Content-Type: application/xml +``` + ++ Response 405 + + Headers +``` + Content-Type: application/xml +``` ### /pets/findByStatus{?status} @@ -66,6 +86,25 @@ Multiple status values can be provided with comma seperated strings ``` + Response 200 + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } +``` + ++ Response 400 + + Headers +``` + Content-Type: application/json +``` ### /pets/findByTags{?tags} @@ -86,6 +125,25 @@ Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 ``` + Response 200 + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } +``` + ++ Response 400 + + Headers +``` + Content-Type: application/json +``` ### /pets/{petId} @@ -93,10 +151,50 @@ Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions ++ Parameters + + petId (integer, required) - ID of pet that needs to be fetched +``` + { + "type": "integer", + "x-title": "petId" + } +``` + + Response 200 + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "$ref": "#/definitions/Pet" + } +``` + ++ Response 400 + + Headers +``` + Content-Type: application/json +``` + ++ Response 404 + + Headers +``` + Content-Type: application/json +``` #### Updates a pet in the store with form data [POST] ++ Parameters + + petId (integer, required) - ID of pet that needs to be fetched +``` + { + "type": "integer", + "x-title": "petId" + } +``` + + Request + Headers ``` @@ -119,10 +217,23 @@ Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error cond } ``` -+ Response 200 ++ Response 405 + + Headers +``` + Content-Type: application/json +``` #### Deletes a pet [DELETE] ++ Parameters + + petId (integer, required) - ID of pet that needs to be fetched +``` + { + "type": "integer", + "x-title": "petId" + } +``` + + Request + Headers ``` @@ -130,7 +241,11 @@ Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error cond Content-Type: application/json ``` -+ Response 200 ++ Response 400 + + Headers +``` + Content-Type: application/json +``` ### /stores/order @@ -149,6 +264,22 @@ Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error cond ``` + Response 200 + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "$ref": "#/definitions/Order" + } +``` + ++ Response 400 + + Headers +``` + Content-Type: application/json +``` ### /stores/order/{orderId} @@ -156,19 +287,69 @@ Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error cond For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions ++ Parameters + + orderId (string, required) - ID of pet that needs to be fetched +``` + { + "type": "string", + "x-title": "orderId" + } +``` + + Response 200 + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "$ref": "#/definitions/Order" + } +``` + ++ Response 400 + + Headers +``` + Content-Type: application/json +``` + ++ Response 404 + + Headers +``` + Content-Type: application/json +``` #### Delete purchase order by ID [DELETE] For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors ++ Parameters + + orderId (string, required) - ID of pet that needs to be fetched +``` + { + "type": "string", + "x-title": "orderId" + } +``` + + Request + Headers ``` Content-Type: application/json ``` -+ Response 200 ++ Response 400 + + Headers +``` + Content-Type: application/json +``` + ++ Response 404 + + Headers +``` + Content-Type: application/json +``` ### /users @@ -189,6 +370,10 @@ This can only be done by the logged in user. ``` + Response 200 + + Headers +``` + Content-Type: application/json +``` ### /users/createWithArray @@ -210,6 +395,10 @@ This can only be done by the logged in user. ``` + Response 200 + + Headers +``` + Content-Type: application/json +``` ### /users/createWithList @@ -231,6 +420,10 @@ This can only be done by the logged in user. ``` + Response 200 + + Headers +``` + Content-Type: application/json +``` ### /users/login{?username,password} @@ -254,23 +447,83 @@ This can only be done by the logged in user. ``` + Response 200 + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "type": "string" + } +``` + ++ Response 400 + + Headers +``` + Content-Type: application/json +``` ### /users/logout #### Logs out current logged in user session [GET] + Response 200 + + Headers +``` + Content-Type: application/json +``` ### /users/{username} #### Get user by user name [GET] ++ Parameters + + username (string, required) - The name that needs to be fetched. Use user1 for testing. +``` + { + "type": "string", + "x-title": "username" + } +``` + + Response 200 + + Headers +``` + Content-Type: application/json +``` + + Schema +``` + { + "$ref": "#/definitions/User" + } +``` + ++ Response 400 + + Headers +``` + Content-Type: application/json +``` + ++ Response 404 + + Headers +``` + Content-Type: application/json +``` #### Updated user [PUT] This can only be done by the logged in user. ++ Parameters + + username (string, required) - The name that needs to be fetched. Use user1 for testing. +``` + { + "type": "string", + "x-title": "username" + } +``` + + Request + Headers ``` @@ -283,19 +536,48 @@ This can only be done by the logged in user. } ``` -+ Response 200 ++ Response 400 + + Headers +``` + Content-Type: application/json +``` + ++ Response 404 + + Headers +``` + Content-Type: application/json +``` #### Delete user [DELETE] This can only be done by the logged in user. ++ Parameters + + username (string, required) - The name that needs to be fetched. Use user1 for testing. +``` + { + "type": "string", + "x-title": "username" + } +``` + + Request + Headers ``` Content-Type: application/json ``` -+ Response 200 ++ Response 400 + + Headers +``` + Content-Type: application/json +``` + ++ Response 404 + + Headers +``` + Content-Type: application/json +``` # Data Structures diff --git a/testing/e2e/internal-apiblueprint-1a/test-case-1/output.json b/testing/e2e/internal-apiblueprint-1a/test-case-1/output.json index e69de29..873c342 100644 --- a/testing/e2e/internal-apiblueprint-1a/test-case-1/output.json +++ b/testing/e2e/internal-apiblueprint-1a/test-case-1/output.json @@ -0,0 +1,861 @@ +FORMAT: 1A +HOST: http://{environment}.musicapi.com/{version} + +# World Music API +This is an example of a music API. + +## Group Resources + +### /api{?queryString} + +#### GET + +This was loaded from api-library + ++ Parameters + + queryString (object, optional) +``` + { + "type": "object", + "properties": { + "start": { + "type": "number" + }, + "page-size": { + "type": "number" + } + }, + "x-title": "queryString" + } +``` + ++ Response 200 + +#### POST + +This is an awesome description + ++ Request (application/json) + + Schema +``` + { + "$ref": "#/definitions/ApiLib.RamlDataType" + } +``` + ++ Response 200 + +### /entry + +#### POST + ++ Request + + Headers +``` + Content-Type: application/json +``` + ++ Response 200 (application/json) + + Schema +``` + { + "$ref": "#/definitions/AnotherEntry" + } +``` + +#### GET + +returns a list of entry + ++ Response 200 (application/json) + + Schema +``` + { + "type": "object" + } +``` + +### /songs{?genre,access_token} + +Access to all songs inside the music world library. + +#### GET + +This was loaded from songs-library + ++ Parameters + + genre (string, optional) - filter the songs by genre +``` + { + "type": "string", + "description": "filter the songs by genre", + "x-title": "genre" + } +``` + + + access_token (string, optional) +``` + { + "type": "string", + "x-title": "access_token" + } +``` + ++ Response 200 + +#### POST + ++ Parameters + + access_token (string, optional) +``` + { + "type": "string", + "x-title": "access_token" + } +``` + ++ Request + + Headers +``` + Content-Type: application/json +``` + ++ Response 200 + +### /songs/{songId} + +#### GET + ++ Parameters + + songId (string, optional) +``` + { + "type": "string", + "x-title": "songId" + } +``` + ++ Response 200 (application/json) + + Schema +``` + { + "$ref": "#/definitions/SongsLib.Song" + } +``` + +# Data Structures + +## Entry (array) +``` + { + "type": "array", + "items": { + "$ref": "#/definitions/Song" + } + } +``` + +## Song (object) +``` + { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "artist": { + "type": "string" + } + } + } +``` + +## AnotherEntry (object) +``` + { + "$ref": "#/definitions/Entry", + "description": "# This is in Markdown\nThis is to see what the representation of this ~~Markdown~~ string is.\n## SubTitle\nThis is just another entry to **simulate** that you can add facets also on JSON\nschema defined types. Although you can only add documentation-based facets.\n" + } +``` + +## SpecialEntry (string) +``` + { + "type": "string", + "pattern": "^.{12}$" + } +``` + +## Toy (object) +``` + { + "type": "object", + "minProperties": 2, + "maxProperties": 8, + "discriminator": "name", + "properties": { + "name": { + "type": "string" + }, + "soft": { + "type": "boolean", + "x-examples": [ + true + ] + }, + "squiky": { + "type": "boolean" + }, + "heavy": { + "type": "boolean" + }, + "clean": { + "type": "boolean" + }, + "purchaseDate": { + "type": "string", + "$ref": "#/definitions/$DateOnly" + }, + "more": { + "type": "string", + "$ref": "#/definitions/$DateTime" + }, + "final": { + "type": "string", + "$ref": "#/definitions/$DateTimeOnly" + } + }, + "required": [ + "name", + "soft", + "squiky", + "heavy", + "clean", + "purchaseDate", + "more", + "final" + ] + } +``` + +## $DateOnly (string) +``` + { + "type": "string", + "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "description": "full-date as defined in RFC#3339" + } +``` + +## $DateTime (string) +``` + { + "type": "string", + "description": "datetime" + } +``` + +## $DateTimeOnly (string) +``` + { + "type": "string", + "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|20|21|22|23):[0-5][0-9]:([0-5][0-9]|60)(.[0-9]+)?$", + "description": "full-time as defined in RFC#3339" + } +``` + +## Ball (object) +``` + { + "allOf": [ + { + "$ref": "#/definitions/Toy" + } + ], + "discriminatorValue": "ball", + "properties": { + "bouncinessFactor": { + "type": "number", + "minimum": 0 + } + }, + "required": [ + "bouncinessFactor" + ] + } +``` + +## Rope (object) +``` + { + "allOf": [ + { + "$ref": "#/definitions/Toy" + } + ], + "discriminatorValue": "rope", + "properties": { + "length": { + "type": "integer", + "enum": [ + "10", + "20", + "50", + "100" + ], + "x-examples": [ + 10 + ] + } + }, + "required": [ + "length" + ] + } +``` + +## CommonToy (object) +``` + { + "anyOf": [ + { + "$ref": "#/definitions/Ball" + }, + { + "$ref": "#/definitions/Rope" + } + ] + } +``` + +## Treat (object) +``` + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "tastiness": { + "type": "number", + "minimum": 0, + "maximum": 1 + } + }, + "required": [ + "name", + "tastiness" + ] + } +``` + +## Pet (object) +``` + { + "type": "object", + "properties": { + "claws": { + "type": "number", + "minimum": 0, + "maximum": 4 + }, + "color": { + "type": "string", + "minLength": 7, + "maxLength": 7, + "pattern": "^#[0-9a-f]{6}$" + }, + "size": { + "type": "number", + "multipleOf": 5 + }, + "weigth": { + "type": "number" + }, + "mostActiveAt": { + "type": "string", + "$ref": "#/definitions/$TimeOnly" + }, + "owner": { + "$ref": "#/definitions/User" + }, + "toys": { + "type": "array", + "items": { + "$ref": "#/definitions/Toy" + } + } + }, + "required": [ + "claws", + "color", + "size", + "weigth", + "mostActiveAt", + "owner", + "toys" + ] + } +``` + +## $TimeOnly (string) +``` + { + "type": "string", + "pattern": "^([01][0-9]|20|21|22|23):[0-5][0-9]:([0-5][0-9]|60)(.[0-9]+)?$", + "description": "full-time as defined in RFC#3339" + } +``` + +## Resident (object) +``` + { + "type": "object", + "properties": { + "address": { + "type": "string" + } + }, + "required": [ + "address" + ] + } +``` + +## Wild (object) +``` + { + "type": "object", + "properties": { + "location": { + "type": "string" + } + }, + "required": [ + "location" + ] + } +``` + +## FarmPet (object) +``` + { + "allOf": [ + { + "$ref": "#/definitions/Pet" + }, + { + "anyOf": [ + { + "$ref": "#/definitions/Resident" + }, + { + "$ref": "#/definitions/Wild" + } + ] + } + ], + "title": "Farm Pet" + } +``` + +## ToyBox (array) +``` + { + "type": "array", + "uniqueItems": false, + "minItems": 1, + "maxItems": 15, + "items": { + "anyOf": [ + { + "$ref": "#/definitions/Toy" + }, + { + "$ref": "#/definitions/Treat" + } + ] + } + } +``` + +## BigToyBox (object) +``` + { + "allOf": [ + { + "$ref": "#/definitions/ToyBox" + } + ], + "maxItems": 100 + } +``` + +## Photo (string) +``` + { + "type": "string", + "$ref": "#/definitions/$File" + } +``` + +## $File (string) +``` + { + "type": "string", + "description": "file", + "pattern": "^[^\u0000]*\u0000$" + } +``` + +## Album (array) +``` + { + "type": "array", + "items": { + "$ref": "#/definitions/Photo" + } + } +``` + +## Weird (object) +``` + {} +``` + +## Habitats (array) +``` + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/Resident" + }, + { + "$ref": "#/definitions/Wild" + } + ] + } + } +``` + +## SeparateKinds (object) +``` + { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/Resident" + } + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/Wild" + } + } + ] + } +``` + +## User (object) +``` + { + "type": "object", + "properties": { + "firstname": { + "type": "string" + }, + "lastname": { + "type": "string", + "x-examples": [ + "Doe" + ] + }, + "pets": { + "type": "array", + "items": { + "$ref": "#/definitions/FarmPet" + } + }, + "box": { + "$ref": "#/definitions/ToyBox" + }, + "albums": { + "type": "array", + "items": { + "$ref": "#/definitions/Album" + } + } + }, + "required": [ + "firstname" + ], + "x-examples": [ + { + "firstname": "John" + } + ] + } +``` + +## SongsLib.Song (object) +``` + { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "length": { + "type": "number" + } + }, + "required": [ + "title", + "length" + ], + "x-examples": [ + { + "title": "My Song", + "length": 12 + }, + { + "title": "Last", + "length": 3 + } + ] + } +``` + +## SongsLib.Album (object) +``` + { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "songs": { + "type": "array", + "items": { + "$ref": "#/definitions/SongsLib.Song" + } + } + }, + "required": [ + "title", + "songs" + ] + } +``` + +## SongsLib.Musician (object) +``` + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "discography": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/SongsLib.Song" + }, + { + "$ref": "#/definitions/SongsLib.Album" + } + ] + } + } + }, + "required": [ + "name", + "discography" + ] + } +``` + +## ApiLib.RamlDataType (object) +``` + { + "type": "object", + "properties": { + "propString": { + "type": "string" + }, + "propStringArray1": { + "type": "array", + "items": { + "type": "string" + } + }, + "ideas": { + "type": "array" + }, + "extIdeas": { + "properties": { + "createdBy": { + "type": "string" + } + }, + "required": [ + "createdBy" + ] + }, + "feedback": { + "type": "string", + "minLength": 1, + "maxLength": 255, + "pattern": "[a-zA-Z\\s]*", + "x-examples": [ + "very well made" + ] + }, + "propNumber": { + "type": "number", + "minimum": 0, + "maximum": 32, + "multipleOf": 2 + }, + "propInteger": { + "type": "integer", + "minimum": 3, + "maximum": 5, + "multipleOf": 1 + }, + "propBoolean": { + "type": "boolean" + }, + "propDate": { + "type": "string", + "$ref": "#/definitions/ApiLib.$DateOnly", + "x-examples": [ + "2015-05-23" + ] + }, + "userPicture": { + "type": "string", + "$ref": "#/definitions/ApiLib.$File" + }, + "NilValue": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "comment": { + "$ref": "#/definitions/ApiLib.string?" + } + }, + "required": [ + "name", + "comment" + ] + }, + "CatOrDog": { + "anyOf": [ + { + "$ref": "#/definitions/ApiLib.Cat" + }, + { + "$ref": "#/definitions/ApiLib.Dog" + } + ] + }, + "CatAndDog": { + "$ref": "#/definitions/ApiLib.Dog" + }, + "PossibleMeetingDate": { + "type": "string", + "$ref": "#/definitions/ApiLib.$DateOnly" + } + }, + "required": [ + "propString", + "propStringArray1", + "ideas", + "extIdeas", + "feedback", + "propNumber", + "propInteger", + "propBoolean", + "propDate", + "userPicture", + "NilValue", + "CatOrDog", + "CatAndDog", + "CatAndDog", + "PossibleMeetingDate" + ] + } +``` + +## ApiLib.$DateOnly (string) +``` + { + "type": "string", + "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "description": "full-date as defined in RFC#3339" + } +``` + +## ApiLib.$File (string) +``` + { + "type": "string", + "description": "file", + "pattern": "^[^\u0000]*\u0000$" + } +``` + +## ApiLib.Cat (object) +``` + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "color": { + "type": "string" + } + }, + "required": [ + "name", + "color" + ] + } +``` + +## ApiLib.Dog (object) +``` + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "fangs": { + "type": "string" + } + }, + "required": [ + "name", + "fangs" + ] + } +``` + +## ApiLib.CustomDate (string) +``` + { + "type": "string", + "$ref": "#/definitions/ApiLib.$DateOnly" + } +``` diff --git a/testing/mocha-runner.js b/testing/mocha-runner.js index feccc77..cc01974 100644 --- a/testing/mocha-runner.js +++ b/testing/mocha-runner.js @@ -38,13 +38,15 @@ function runSuite() { } } +const blob = process.env.BLOB || 'src/**/*.spec.js' + /** * Chokidar watches all the files for any kind of change and calls the run function * from above. Read more: https://github.com/paulmillr/chokidar * @param {string} glob: a glob of files to watch * @param {object} settings */ -chokidar.watch('src/**/*.spec.js', { persistent: true }) +chokidar.watch(blob, { persistent: true }) .on('add', path => fileList.push(path)) .on('change', () => runSuite()) .on('ready', () => runSuite()) From 9237c76329e1cea0fe6d44b46e7b56cd6dc0689d Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Mon, 15 May 2017 15:36:07 +0200 Subject: [PATCH 12/24] removed todos --- src/serializers/api-blueprint/1A/Serializer.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/serializers/api-blueprint/1A/Serializer.js b/src/serializers/api-blueprint/1A/Serializer.js index 2d95556..d6af0b5 100644 --- a/src/serializers/api-blueprint/1A/Serializer.js +++ b/src/serializers/api-blueprint/1A/Serializer.js @@ -1,7 +1,12 @@ /** * An Api Blueprint 1A serializer. * This implementation has the following limitations: - * - TODO: write the limitations of this serializer (wrt to the api-blueprint spec) + * - it does not link between data Structures and attributes, as attributes need to be in MSON + * format + * - attributes are not supported. + * - authentication methods are not included because they require an example header, which is a mess + * to implement and interpret + * - no grouping * * NOTE: we allow use of undefined in this file as it works nicely with JSON.stringify, which drops * keys with a value of undefined. @@ -777,7 +782,6 @@ methods.createOperationRequestHeaderSection = (api, constraints, container, meth .valueSeq() .toJS() .join('\n') - // TODO add authentication header return { type: 'list', From e785df675e1006270e80100bfc76b2fce1a05f15 Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Mon, 15 May 2017 15:56:13 +0200 Subject: [PATCH 13/24] fixes around methods and url-encoded body --- src/serializers/swagger/v2.0/Serializer.js | 5 +- .../swagger/v2.0/__tests__/Serializer.spec.js | 65 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/serializers/swagger/v2.0/Serializer.js b/src/serializers/swagger/v2.0/Serializer.js index 29f7c26..5737cef 100644 --- a/src/serializers/swagger/v2.0/Serializer.js +++ b/src/serializers/swagger/v2.0/Serializer.js @@ -693,7 +693,7 @@ methods.isBodyParameter = (parameter) => { const isUrlEncoded = parameter.isValid( new Parameter({ key: 'Content-Type', - default: 'multipart/form-data' + default: 'application/x-www-form-urlencoded' }) ) @@ -1334,7 +1334,8 @@ methods.convertRequestToOperationObject = (store, { consumes, produces }, reques } const value = operation - return { key: key.toLowerCase(), value } + const method = request.get('method') || key + return { key: method.toLowerCase(), value } } /* eslint-enable max-statements */ diff --git a/src/serializers/swagger/v2.0/__tests__/Serializer.spec.js b/src/serializers/swagger/v2.0/__tests__/Serializer.spec.js index f6343e1..e5765f9 100644 --- a/src/serializers/swagger/v2.0/__tests__/Serializer.spec.js +++ b/src/serializers/swagger/v2.0/__tests__/Serializer.spec.js @@ -2275,6 +2275,71 @@ describe('serializers/swagger/v2.0/Serializer.js', () => { expect(actual).toEqual(expected) }) + + it('should work if underlying methods are correct and with request method', () => { + spyOn(__internals__, 'getTagStrings').andReturn([ 'pet', 'store' ]) + spyOn(__internals__, 'getKeysFromRecord').andReturn({ + summary: 'update a Pet', + description: 'updates a pet with some params', + operationId: 'updatePet' + }) + + spyOn(__internals__, 'getConsumesEntry').andReturn([ 'application/json' ]) + spyOn(__internals__, 'getProducesEntry').andReturn([ 'application/xml' ]) + spyOn(__internals__, 'getParametersFromRequest').andReturn([ { + in: 'query', + name: 'petId', + type: 'string' + } ]) + spyOn(__internals__, 'getSchemesFromRequestEndpointOverlay').andReturn([ 'https' ]) + spyOn(__internals__, 'getSecurityRequirementsFromRequest').andReturn([ + { + petstore_auth: [ 'write:self' ] + } + ]) + spyOn(__internals__, 'getResponsesFromRequest').andReturn({ + '200': { + description: 'this method should return 200' + } + }) + + const store = new Store() + const globalInfo = {} + const request = new Request({ method: 'Get' }) + const key = 'put' + + const expectedValue = { + tags: [ 'pet', 'store' ], + summary: 'update a Pet', + description: 'updates a pet with some params', + operationId: 'updatePet', + consumes: [ 'application/json' ], + produces: [ 'application/xml' ], + parameters: [ + { + in: 'query', + name: 'petId', + type: 'string' + } + ], + schemes: [ 'https' ], + security: [ + { + petstore_auth: [ 'write:self' ] + } + ], + responses: { + '200': { + description: 'this method should return 200' + } + } + } + + const expected = { key: 'get', value: expectedValue } + const actual = __internals__.convertRequestToOperationObject(store, globalInfo, request, key) + + expect(actual).toEqual(expected) + }) /* eslint-enable max-statements */ }) From 54b3988374045f7466b71d9fe34c82e66f36d1fd Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Wed, 17 May 2017 11:39:23 +0200 Subject: [PATCH 14/24] fixed a bug where swagger contentTypes could include charsets data --- src/serializers/swagger/v2.0/Serializer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serializers/swagger/v2.0/Serializer.js b/src/serializers/swagger/v2.0/Serializer.js index 5737cef..77b27e9 100644 --- a/src/serializers/swagger/v2.0/Serializer.js +++ b/src/serializers/swagger/v2.0/Serializer.js @@ -1479,11 +1479,11 @@ methods.isProducesHeader = (param) => { methods.extractContentTypesFromParam = (param) => { const schema = param.getJSONSchema(false) if (schema.enum) { - return schema.enum + return schema.enum.map(contentType => contentType.split(';')[0]) } if (schema.default) { - return [ schema.default ] + return [ schema.default ].map(contentType => contentType.split(';')[0]) } return [] From b1ec796c1ba7d182b163cca12d1dabf8bb12a963 Mon Sep 17 00:00:00 2001 From: Jonathan Montane Date: Fri, 4 Aug 2017 10:48:45 +0200 Subject: [PATCH 15/24] Added link to documentation in README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index cba8277..4e0b4f2 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,9 @@ API-Flow is one of the main components of [Console.REST](https://github.com/luck PRs are welcomed! Our sole requirement is that organizations that want to extend API-Flow to support their format write both a parser and a serializer, and not simply a serializer. +## Documentation +You can find more information about the internal structure of API-Flow in [src](https://github.com/luckymarmot/API-Flow/tree/develop/src). We've also created a set of templates to help speed up the extension process: [loader](https://github.com/luckymarmot/API-Flow/tree/develop/src/loaders/template/v1.0), [parser](https://github.com/luckymarmot/API-Flow/tree/develop/src/parsers/template/v1.0/), and [environment](https://github.com/luckymarmot/API-Flow/tree/develop/src/environments/template) + ## License This repository is released under the [MIT License](LICENSE). Feel free to fork, and modify! From 97c44d0eab5db0fcb34767b3a3a322e1e5df3337 Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Wed, 16 Aug 2017 10:41:52 +0200 Subject: [PATCH 16/24] fixed a bug where a single slash between two components was removed from paths --- src/parsers/paw/Parser.js | 28 +++++++++++++++++++----- src/parsers/paw/__tests__/Parser.spec.js | 21 +++++++++++++++--- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/parsers/paw/Parser.js b/src/parsers/paw/Parser.js index 59e75a3..321e05b 100644 --- a/src/parsers/paw/Parser.js +++ b/src/parsers/paw/Parser.js @@ -421,10 +421,25 @@ methods.isPartOfBaseUrl = (defaultUrl, defaultSecureUrl, urlPart) => { return defaultUrl.indexOf(urlPart) >= 0 || defaultSecureUrl.indexOf(urlPart) >= 0 } -// NOTE: we assume that the urlPart is after the protocol -methods.findIntersection = (defaultUrl, urlPart) => { - const match = (defaultUrl + '####' + urlPart).match(/^.*?(.*)####\1(.*)$/) +/** + * finds the intersection between two strings, and returns the intersection, as well as the right- + * most exclusion. This is used to find the overlap between a host url and a part of a url + * associated with that host. + * @param {string} defaultUrl: the default url to test against. + * @param {string} defaultSecureUrl: the default secure url to test against. + * @param {string} urlPart: the part of url to test + * @returns {{ inside: string, outside: string }} + * + * Note: this assumes Paw only supports http and https. + * Note: this may work incorrectly if url is as follow: http://example.com/example.com/ (not tested) + */ +methods.findIntersection = (defaultUrl, defaultSecureUrl, urlPart) => { + let baseUrl = defaultUrl + if (urlPart.match(/^[^:]*s:\/\//)) { + baseUrl = defaultSecureUrl + } + const match = (baseUrl + '####' + urlPart).match(/^.*?(.*)####\1(.*)$/) // always matches return { inside: match[1], outside: match[2] } } @@ -452,7 +467,10 @@ methods.addComponentToBaseOrPath = ( { baseComponents, pathComponents }, { key: urlPart, value: component } ) => { - if (methods.isPartOfBaseUrl(defaultUrl, defaultSecureUrl, urlPart)) { + if ( + pathComponents.length === 0 && + methods.isPartOfBaseUrl(defaultUrl, defaultSecureUrl, urlPart) + ) { // component is member of base url baseComponents.push({ key: urlPart, value: component }) return { baseComponents, pathComponents } @@ -460,7 +478,7 @@ methods.addComponentToBaseOrPath = ( if (pathComponents.length === 0) { // component may be split between base url and path - const { inside, outside } = methods.findIntersection(defaultUrl, urlPart) + const { inside, outside } = methods.findIntersection(defaultUrl, defaultSecureUrl, urlPart) baseComponents.push({ key: inside, value: inside }) pathComponents.push({ key: outside, value: outside }) } diff --git a/src/parsers/paw/__tests__/Parser.spec.js b/src/parsers/paw/__tests__/Parser.spec.js index 0c9479e..a56da6f 100644 --- a/src/parsers/paw/__tests__/Parser.spec.js +++ b/src/parsers/paw/__tests__/Parser.spec.js @@ -595,13 +595,28 @@ describe('parsers/paw/Parser.js', () => { describe('@findIntersection', () => { it('should work', () => { const defaultUrl = 'http://echo.paw.cloud/pets' - const inputs = [ '/pets', '/pets/234', '/234' ] + const defaultSecureUrl = 'https://echo.paw.cloud/pets' + const inputs = [ + '/pets', + '/pets/234', + '/234', + // this one should be outside, because our intersection method tries to place strings so + // that they overlap from the right + 'http://echo.paw.cloud', + 'http://echo.paw.cloud/pets', + 'https://echo.paw.cloud/pets' + ] const expected = [ { inside: '/pets', outside: '' }, { inside: '/pets', outside: '/234' }, - { inside: '', outside: '/234' } + { inside: '', outside: '/234' }, + { inside: '', outside: 'http://echo.paw.cloud' }, + { inside: 'http://echo.paw.cloud/pets', outside: '' }, + { inside: 'https://echo.paw.cloud/pets', outside: '' } ] - const actual = inputs.map(input => __internals__.findIntersection(defaultUrl, input)) + const actual = inputs.map( + input => __internals__.findIntersection(defaultUrl, defaultSecureUrl, input) + ) expect(actual).toEqual(expected) }) }) From 753ee23ee4e0045377195de4976a203ad90ac281 Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Sun, 20 Aug 2017 11:21:31 +0200 Subject: [PATCH 17/24] added support for custom headers for api key auths --- .gitignore | 1 + src/serializers/paw/Serializer.js | 21 ++++++++++------ .../paw/__tests__/Serializer.spec.js | 25 +++++++++++++++---- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index c07f7e1..0859556 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,4 @@ lib/ .DS_Store deploy-script.sh coverage/ +.nyc_output/ diff --git a/src/serializers/paw/Serializer.js b/src/serializers/paw/Serializer.js index 23297a1..a5f0ff1 100644 --- a/src/serializers/paw/Serializer.js +++ b/src/serializers/paw/Serializer.js @@ -442,7 +442,7 @@ methods.convertAuthIntoDynamicValue = (auth) => { * @param {Environment} environment: the environment in which this auth value is applicable. * @param {Auth} auth: the auth to add to the domain * @param {string} key: the name of the auth - * @returns {EnvironmentVariable} the newly created environment variable. + * @returns {{ variable: EnvironmentVariable, auth: Auth }} the newly created environment variable. */ methods.addAuthToDomain = (domain, environment, auth, key) => { const variable = domain.createEnvironmentVariable(key) @@ -450,7 +450,7 @@ methods.addAuthToDomain = (domain, environment, auth, key) => { const ds = methods.wrapDV(dv) variable.setValue(ds, environment) - return variable + return { variable, auth } } /** @@ -1041,7 +1041,7 @@ methods.convertAuthFromReference = (store, reference) => { * converts a reference or an auth into a DynamicString Entry. * @param {Store} store: the store used to resolve references * @param {Auth|Reference} authOrReference: the record to convert into a DynamicString - * @returns {DynamicString} the corresponding DynamicString + * @returns {{ variable: DynamicString, auth: Auth }} the corresponding DynamicString */ methods.convertReferenceOrAuthToDsEntry = (store, authOrReference) => { if (authOrReference instanceof Reference) { @@ -1049,18 +1049,25 @@ methods.convertReferenceOrAuthToDsEntry = (store, authOrReference) => { } const dv = methods.convertAuthIntoDynamicValue(authOrReference) - return methods.wrapDV(dv) + return { variable: methods.wrapDV(dv), auth: authOrReference } } // TODO create Variable DS that has enum with all auth possible /** * sets the Auth DynamicString as am Authorization Header. * @param {PawRequest} pawRequest: the paw request to update - * @param {DynamicString} auth: the DynamicString representing an auth + * @param {Objecti} authData: the object containing the auth representation as a DynamicString and + * the original auth Record. + * @param {DynamicString} variable: the DynamicString representing an auth + * @param {Auth} auth: the original auth * @returns {PawRequest} the update paw request */ -methods.addAuthToRequest = (pawRequest, auth) => { - pawRequest.setHeader('Authorization', auth) +methods.addAuthToRequest = (pawRequest, { variable, auth }) => { + let authName = 'Authorization' + if (auth instanceof Auth.ApiKey) { + authName = auth.get('name') + } + pawRequest.setHeader(authName, variable) return pawRequest } diff --git a/src/serializers/paw/__tests__/Serializer.spec.js b/src/serializers/paw/__tests__/Serializer.spec.js index ccd6baf..1eb1c31 100644 --- a/src/serializers/paw/__tests__/Serializer.spec.js +++ b/src/serializers/paw/__tests__/Serializer.spec.js @@ -746,7 +746,7 @@ describe('serializers/paw/Serializer.js', () => { spyOn(__internals__, 'convertAuthIntoDynamicValue').andReturn(123) spyOn(__internals__, 'wrapDV').andReturn('123') - const expected = variable + const expected = { variable, auth } const actual = __internals__.addAuthToDomain(domain, environment, auth, key) expect(domain.createEnvironmentVariable).toHaveBeenCalledWith(key) @@ -2040,7 +2040,7 @@ describe('serializers/paw/Serializer.js', () => { const store = new Store() const auth = new Auth.Basic() - const expected = '123' + const expected = { variable: '123', auth } const actual = __internals__.convertReferenceOrAuthToDsEntry(store, auth) expect(actual).toEqual(expected) @@ -2051,12 +2051,27 @@ describe('serializers/paw/Serializer.js', () => { it('should work', () => { const pawReq = { setHeader: () => {} } spyOn(pawReq, 'setHeader').andReturn(123) - const auth = 'some dynamic string' + const authData = { variable: 'some dynamic string', auth: new Auth.Basic() } + + const expected = pawReq + const actual = __internals__.addAuthToRequest(pawReq, authData) + + expect(pawReq.setHeader).toHaveBeenCalledWith('Authorization', authData.variable) + expect(actual).toEqual(expected) + }) + + it('should work with ApiKeys custom headers', () => { + const pawReq = { setHeader: () => {} } + spyOn(pawReq, 'setHeader').andReturn(123) + const authData = { + variable: 'some dynamic string', + auth: new Auth.ApiKey({ name: 'X-Auth-Token' }) + } const expected = pawReq - const actual = __internals__.addAuthToRequest(pawReq, auth) + const actual = __internals__.addAuthToRequest(pawReq, authData) - expect(pawReq.setHeader).toHaveBeenCalledWith('Authorization', auth) + expect(pawReq.setHeader).toHaveBeenCalledWith('X-Auth-Token', authData.variable) expect(actual).toEqual(expected) }) }) From 64f5527b2e16ba20c0f1286b52045382ad2965bc Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Thu, 14 Sep 2017 10:49:15 +0200 Subject: [PATCH 18/24] fixed pathname bug for very weird urls --- src/parsers/postman/v2.0/Parser.js | 2 +- .../postman/v2.0/__tests__/Parser.spec.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/parsers/postman/v2.0/Parser.js b/src/parsers/postman/v2.0/Parser.js index 52b8980..401546f 100644 --- a/src/parsers/postman/v2.0/Parser.js +++ b/src/parsers/postman/v2.0/Parser.js @@ -478,7 +478,7 @@ methods.addHostEntryToHostMap = (hostMap, { key, value }) => { const hostname = key.get('hostname') ? key.get('hostname').generate(List([ '{{', '}}' ])) : '' const port = key.get('port') ? ':' + key.get('port').generate(List([ '{{', '}}' ])) : '' const host = hostname + port - const pathname = key.get('pathname').generate(List([ '{{', '}}' ])) + const pathname = key.get('pathname') ? key.get('pathname').generate(List([ '{{', '}}' ])) : '' if (!hostMap[host]) { hostMap[host] = { entries: [], lcPathname: pathname.split('/') } diff --git a/src/parsers/postman/v2.0/__tests__/Parser.spec.js b/src/parsers/postman/v2.0/__tests__/Parser.spec.js index dd36787..b2cb0c1 100644 --- a/src/parsers/postman/v2.0/__tests__/Parser.spec.js +++ b/src/parsers/postman/v2.0/__tests__/Parser.spec.js @@ -960,6 +960,13 @@ describe('parsers/postman/v2.0/Parser.js', () => { variableDelimiters: List([ '{{', '}}', ':' ]) }), value: 321 + }, + { + key: new URL({ + url: 'staging.paw.cloud', + variableDelimiters: List([ '{{', '}}', ':' ]) + }), + value: 123 } ] const expected = { @@ -1017,6 +1024,18 @@ describe('parsers/postman/v2.0/Parser.js', () => { } ], lcPathname: [ '', 'users', '321' ] + }, + 'staging.paw.cloud': { + entries: [ + { + key: new URL({ + url: 'staging.paw.cloud', + variableDelimiters: List([ '{{', '}}', ':' ]) + }), + value: 123 + } + ], + lcPathname: [ '' ] } } From 1118c263c104740b1557bec7948a77dc621773dd Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Thu, 5 Oct 2017 23:40:15 +0200 Subject: [PATCH 19/24] fixed missing postman loader, parser, and serializers from configs --- configs/node/api-flow-config.js | 12 +++++++++--- configs/web/api-flow-config.js | 12 +++++++++--- configs/webworker/api-flow-config.js | 12 +++++++++--- src/serializers/postman/v2.0/Serializer.js | 2 +- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/configs/node/api-flow-config.js b/configs/node/api-flow-config.js index 97b2d80..f6cad0f 100644 --- a/configs/node/api-flow-config.js +++ b/configs/node/api-flow-config.js @@ -2,28 +2,34 @@ import Environment from '../../src/environments/node/Environment' import SwaggerLoader from '../../src/loaders/swagger/Loader' import RAMLLoader from '../../src/loaders/raml/Loader' +import PostmanV2Loader from '../../src/loaders/postman/v2.0/Loader' import SwaggerV2Parser from '../../src/parsers/swagger/v2.0/Parser' import RAMLV1Parser from '../../src/parsers/raml/v1.0/Parser' +import PostmanV2Parser from '../../src/loaders/postman/v2.0/Parser' import SwaggerV2Serializer from '../../src/serializers/swagger/v2.0/Serializer' import RAMLV1Serializer from '../../src/serializers/raml/v1.0/Serializer' +import PostmanV2Serializer from '../../src/serializers/postman/v2.0/Serializer' import InternalSerializer from '../../src/serializers/internal/Serializer' export const loaders = [ SwaggerLoader, - RAMLLoader + RAMLLoader, + PostmanV2Loader ] export const parsers = [ SwaggerV2Parser, - RAMLV1Parser + RAMLV1Parser, + PostmanV2Parser ] export const serializers = [ SwaggerV2Serializer, RAMLV1Serializer, - InternalSerializer + InternalSerializer, + PostmanV2Serializer ] export const environment = Environment diff --git a/configs/web/api-flow-config.js b/configs/web/api-flow-config.js index 81aa211..12e25e9 100644 --- a/configs/web/api-flow-config.js +++ b/configs/web/api-flow-config.js @@ -2,28 +2,34 @@ import Environment from '../../src/environments/web/Environment' import SwaggerLoader from '../../src/loaders/swagger/Loader' import RAMLLoader from '../../src/loaders/raml/Loader' +import PostmanV2Loader from '../../src/loaders/postman/v2.0/Loader' import SwaggerV2Parser from '../../src/parsers/swagger/v2.0/Parser' import RAMLV1Parser from '../../src/parsers/raml/v1.0/Parser' +import PostmanV2Parser from '../../src/loaders/postman/v2.0/Parser' import SwaggerV2Serializer from '../../src/serializers/swagger/v2.0/Serializer' import RAMLV1Serializer from '../../src/serializers/raml/v1.0/Serializer' +import PostmanV2Serializer from '../../src/serializers/postman/v2.0/Serializer' import InternalSerializer from '../../src/serializers/internal/Serializer' export const loaders = [ SwaggerLoader, - RAMLLoader + RAMLLoader, + PostmanV2Loader ] export const parsers = [ SwaggerV2Parser, - RAMLV1Parser + RAMLV1Parser, + PostmanV2Parser ] export const serializers = [ SwaggerV2Serializer, RAMLV1Serializer, - InternalSerializer + InternalSerializer, + PostmanV2Serializer ] export const environment = Environment diff --git a/configs/webworker/api-flow-config.js b/configs/webworker/api-flow-config.js index 81aa211..12e25e9 100644 --- a/configs/webworker/api-flow-config.js +++ b/configs/webworker/api-flow-config.js @@ -2,28 +2,34 @@ import Environment from '../../src/environments/web/Environment' import SwaggerLoader from '../../src/loaders/swagger/Loader' import RAMLLoader from '../../src/loaders/raml/Loader' +import PostmanV2Loader from '../../src/loaders/postman/v2.0/Loader' import SwaggerV2Parser from '../../src/parsers/swagger/v2.0/Parser' import RAMLV1Parser from '../../src/parsers/raml/v1.0/Parser' +import PostmanV2Parser from '../../src/loaders/postman/v2.0/Parser' import SwaggerV2Serializer from '../../src/serializers/swagger/v2.0/Serializer' import RAMLV1Serializer from '../../src/serializers/raml/v1.0/Serializer' +import PostmanV2Serializer from '../../src/serializers/postman/v2.0/Serializer' import InternalSerializer from '../../src/serializers/internal/Serializer' export const loaders = [ SwaggerLoader, - RAMLLoader + RAMLLoader, + PostmanV2Loader ] export const parsers = [ SwaggerV2Parser, - RAMLV1Parser + RAMLV1Parser, + PostmanV2Parser ] export const serializers = [ SwaggerV2Serializer, RAMLV1Serializer, - InternalSerializer + InternalSerializer, + PostmanV2Serializer ] export const environment = Environment diff --git a/src/serializers/postman/v2.0/Serializer.js b/src/serializers/postman/v2.0/Serializer.js index 1920695..70f68f1 100644 --- a/src/serializers/postman/v2.0/Serializer.js +++ b/src/serializers/postman/v2.0/Serializer.js @@ -35,7 +35,7 @@ import Auth from '../../../models/Auth' import { convertEntryListInMap } from '../../../utils/fp-utils' const __meta__ = { - format: 'postman', + format: 'postman-collection', version: 'v2.0' } From 809235eca339e407f4c5132f0770b26d176fd8f5 Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Thu, 5 Oct 2017 23:56:42 +0200 Subject: [PATCH 20/24] loaders now defaults to fsResolution instead of httpResolution --- src/loaders/internal/Loader.js | 7 ++++--- src/loaders/postman/v2.0/Loader.js | 6 +++--- src/loaders/swagger/Loader.js | 7 ++++--- src/loaders/template/v1.0/Loader.js | 6 +++--- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/loaders/internal/Loader.js b/src/loaders/internal/Loader.js index efdf747..cabd751 100644 --- a/src/loaders/internal/Loader.js +++ b/src/loaders/internal/Loader.js @@ -70,11 +70,12 @@ methods.parseJSON = (str) => { methods.resolve = (options, uri, { $ref = '' } = {}) => { const uriToLoad = resolve(uri, $ref) - if (parse(uriToLoad).protocol === 'file:') { - return options.fsResolver.resolve(uriToLoad.split('#')[0]) + const protocol = parse(uriToLoad).protocol + if (protocol && protocol.substr(0,4) === 'http') { + return options.httpResolver.resolve(uriToLoad.split('#')[0]) } - return options.httpResolver.resolve(uriToLoad.split('#')[0]) + return options.fsResolver.resolve(uriToLoad.split('#')[0]) } diff --git a/src/loaders/postman/v2.0/Loader.js b/src/loaders/postman/v2.0/Loader.js index 237398d..8865593 100644 --- a/src/loaders/postman/v2.0/Loader.js +++ b/src/loaders/postman/v2.0/Loader.js @@ -87,11 +87,11 @@ methods.isParsable = (content) => { methods.resolve = (options, uri, { $ref = '' } = {}) => { const uriToLoad = resolve(uri, $ref) const protocol = parse(uriToLoad).protocol - if (protocol === 'file:' || protocol === 'file') { - return options.fsResolver.resolve(uriToLoad.split('#')[0]) + if (protocol && protocol.substr(0,4) === 'http') { + return options.httpResolver.resolve(uriToLoad.split('#')[0]) } - return options.httpResolver.resolve(uriToLoad.split('#')[0]) + return options.fsResolver.resolve(uriToLoad.split('#')[0]) } methods.normalizeRequestItem = (item) => { diff --git a/src/loaders/swagger/Loader.js b/src/loaders/swagger/Loader.js index ff66f33..0bfd644 100644 --- a/src/loaders/swagger/Loader.js +++ b/src/loaders/swagger/Loader.js @@ -122,11 +122,12 @@ methods.traverse = (content, { $ref = '#/' } = {}) => { methods.resolve = (options, uri, { $ref = '' } = {}) => { const uriToLoad = resolve(uri, $ref) - if (parse(uriToLoad).protocol === 'file:') { - return options.fsResolver.resolve(uriToLoad.split('#')[0]) + const protocol = parse(uriToLoad).protocol + if (protocol && protocol.substr(0,4) === 'http') { + return options.httpResolver.resolve(uriToLoad.split('#')[0]) } - return options.httpResolver.resolve(uriToLoad.split('#')[0]) + return options.fsResolver.resolve(uriToLoad.split('#')[0]) } methods.objectMap = (obj, func) => { diff --git a/src/loaders/template/v1.0/Loader.js b/src/loaders/template/v1.0/Loader.js index 1509f5d..4846aa6 100644 --- a/src/loaders/template/v1.0/Loader.js +++ b/src/loaders/template/v1.0/Loader.js @@ -125,11 +125,11 @@ methods.isParsable = (content) => { methods.resolve = (options, uri, { $ref = '' } = {}) => { const uriToLoad = resolve(uri, $ref) const protocol = parse(uriToLoad).protocol - if (protocol === 'file:' || protocol === 'file' || !protocol) { - return options.fsResolver.resolve(uriToLoad.split('#')[0]) + if (protocol && protocol.substr(0,4) === 'http') { + return options.httpResolver.resolve(uriToLoad.split('#')[0]) } - return options.httpResolver.resolve(uriToLoad.split('#')[0]) + return options.fsResolver.resolve(uriToLoad.split('#')[0]) } /** From c46ad2f4244c8641c47c5d57e7135a42f72ed8d3 Mon Sep 17 00:00:00 2001 From: JonathanMontane Date: Fri, 6 Oct 2017 00:06:58 +0200 Subject: [PATCH 21/24] linting --- src/loaders/internal/Loader.js | 2 +- src/loaders/postman/v2.0/Loader.js | 2 +- src/loaders/swagger/Loader.js | 2 +- src/loaders/template/v1.0/Loader.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/loaders/internal/Loader.js b/src/loaders/internal/Loader.js index cabd751..c4e033c 100644 --- a/src/loaders/internal/Loader.js +++ b/src/loaders/internal/Loader.js @@ -71,7 +71,7 @@ methods.parseJSON = (str) => { methods.resolve = (options, uri, { $ref = '' } = {}) => { const uriToLoad = resolve(uri, $ref) const protocol = parse(uriToLoad).protocol - if (protocol && protocol.substr(0,4) === 'http') { + if (protocol && protocol.substr(0, 4) === 'http') { return options.httpResolver.resolve(uriToLoad.split('#')[0]) } diff --git a/src/loaders/postman/v2.0/Loader.js b/src/loaders/postman/v2.0/Loader.js index 8865593..96f8481 100644 --- a/src/loaders/postman/v2.0/Loader.js +++ b/src/loaders/postman/v2.0/Loader.js @@ -87,7 +87,7 @@ methods.isParsable = (content) => { methods.resolve = (options, uri, { $ref = '' } = {}) => { const uriToLoad = resolve(uri, $ref) const protocol = parse(uriToLoad).protocol - if (protocol && protocol.substr(0,4) === 'http') { + if (protocol && protocol.substr(0, 4) === 'http') { return options.httpResolver.resolve(uriToLoad.split('#')[0]) } diff --git a/src/loaders/swagger/Loader.js b/src/loaders/swagger/Loader.js index 0bfd644..159e516 100644 --- a/src/loaders/swagger/Loader.js +++ b/src/loaders/swagger/Loader.js @@ -123,7 +123,7 @@ methods.traverse = (content, { $ref = '#/' } = {}) => { methods.resolve = (options, uri, { $ref = '' } = {}) => { const uriToLoad = resolve(uri, $ref) const protocol = parse(uriToLoad).protocol - if (protocol && protocol.substr(0,4) === 'http') { + if (protocol && protocol.substr(0, 4) === 'http') { return options.httpResolver.resolve(uriToLoad.split('#')[0]) } diff --git a/src/loaders/template/v1.0/Loader.js b/src/loaders/template/v1.0/Loader.js index 4846aa6..7ca25c7 100644 --- a/src/loaders/template/v1.0/Loader.js +++ b/src/loaders/template/v1.0/Loader.js @@ -125,7 +125,7 @@ methods.isParsable = (content) => { methods.resolve = (options, uri, { $ref = '' } = {}) => { const uriToLoad = resolve(uri, $ref) const protocol = parse(uriToLoad).protocol - if (protocol && protocol.substr(0,4) === 'http') { + if (protocol && protocol.substr(0, 4) === 'http') { return options.httpResolver.resolve(uriToLoad.split('#')[0]) } From 210f6b2dc0a5873f34c46dce52ff4d134bceb3a5 Mon Sep 17 00:00:00 2001 From: Micha Mazaheri Date: Thu, 2 Nov 2017 13:16:05 +0900 Subject: [PATCH 22/24] Fix convertAuthFromReference in Paw serializer --- src/serializers/paw/Serializer.js | 6 +++--- src/serializers/paw/__tests__/Serializer.spec.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/serializers/paw/Serializer.js b/src/serializers/paw/Serializer.js index a5f0ff1..52032d2 100644 --- a/src/serializers/paw/Serializer.js +++ b/src/serializers/paw/Serializer.js @@ -1029,12 +1029,12 @@ methods.getContainerFromRequest = (request) => { * converts an auth into a DynamicString from a reference. * @param {Store} store: the store to use to resolve the reference * @param {Reference} reference: the reference to an EnvironmentVariable representing an Auth. - * @returns {DynamicString} the corresponding DynamicString + * @returns {{ variable: DynamicString, auth: Auth }} the corresponding DynamicString */ methods.convertAuthFromReference = (store, reference) => { - const variable = store.getIn([ 'auth', reference.get('uuid') ]) + const { variable, auth } = store.getIn([ 'auth', reference.get('uuid') ]) const ds = variable.createDynamicString() - return ds + return { variable: ds, auth: auth } } /** diff --git a/src/serializers/paw/__tests__/Serializer.spec.js b/src/serializers/paw/__tests__/Serializer.spec.js index 1eb1c31..2f12552 100644 --- a/src/serializers/paw/__tests__/Serializer.spec.js +++ b/src/serializers/paw/__tests__/Serializer.spec.js @@ -2010,12 +2010,12 @@ describe('serializers/paw/Serializer.js', () => { spyOn(variable, 'createDynamicString').andReturn(123) const store = new Store({ - auth: OrderedMap({ a: variable }) + auth: OrderedMap({ a: { variable: variable, auth: 'my-auth' } }) }) const ref = new Reference({ uuid: 'a' }) - const expected = 123 + const expected = { auth: 'my-auth', variable: 123 } const actual = __internals__.convertAuthFromReference(store, ref) expect(actual).toEqual(expected) From 5870855e408ad18ff2e577cb04d684e62d52e812 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Mon, 11 Dec 2017 17:18:20 -0500 Subject: [PATCH 23/24] Support example and x-example in Swagger Properties --- src/parsers/swagger/v2.0/Parser.js | 6 +++ .../swagger/v2.0/__tests__/Parser.spec.js | 37 ++++++++++++++++++- testing/e2e/swagger2-internal/input.json | 5 ++- testing/e2e/swagger2-internal/output.json | 8 ++-- 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/parsers/swagger/v2.0/Parser.js b/src/parsers/swagger/v2.0/Parser.js index a2f9043..836c12d 100644 --- a/src/parsers/swagger/v2.0/Parser.js +++ b/src/parsers/swagger/v2.0/Parser.js @@ -1252,6 +1252,12 @@ methods.convertParameterObjectIntoParameter = (parameterEntry) => { applicableContexts } + if (parameter.example) { + paramInstance.examples = List([parameter.example]) + } else if (parameter['x-example']) { + paramInstance.examples = List([parameter['x-example']]) + } + if (parameter.type === 'array' && parameter.items) { const { value } = methods.convertParameterObjectIntoParameter({ key: null, diff --git a/src/parsers/swagger/v2.0/__tests__/Parser.spec.js b/src/parsers/swagger/v2.0/__tests__/Parser.spec.js index cd2469d..f97b998 100644 --- a/src/parsers/swagger/v2.0/__tests__/Parser.spec.js +++ b/src/parsers/swagger/v2.0/__tests__/Parser.spec.js @@ -2772,7 +2772,8 @@ describe('parsers/swagger/v2.0/Parser.js', () => { maximum: 321, minimum: 123, multipleOf: 5, - default: 100 + default: 100, + example: 12345 } } @@ -2787,6 +2788,7 @@ describe('parsers/swagger/v2.0/Parser.js', () => { required: true, type: 'integer', default: 100, + examples: List([12345]), constraints: List([ new Constraint.Maximum(321), new Constraint.Minimum(123), @@ -2816,7 +2818,8 @@ describe('parsers/swagger/v2.0/Parser.js', () => { maximum: 321, minimum: 123, multipleOf: 5, - default: 100 + default: 100, + example: 12345 } } } @@ -2838,6 +2841,7 @@ describe('parsers/swagger/v2.0/Parser.js', () => { required: true, type: 'integer', default: 100, + examples: List([12345]), constraints: List([ new Constraint.Maximum(321), new Constraint.Minimum(123), @@ -2920,6 +2924,35 @@ describe('parsers/swagger/v2.0/Parser.js', () => { const actual = inputs.map(input => __internals__.convertParameterObjectIntoParameter(input)) expect(actual).toEqual(expected) }) + + it('should respect x-example too', () => { + const entry = { + key: 'UserId', + value: { + name: 'userId', + in: 'query', + type: 'integer', + required: true, + 'x-example': 23456 + } + } + + const expected = { + key: 'UserId', + value: new Parameter({ + key: 'userId', + name: 'userId', + in: 'queries', + uuid: 'UserId', + required: true, + type: 'integer', + examples: List([23456]), + constraints: List() + }) + } + + const actual = __internals__.convertParameterObjectIntoParameter(entry) + }) }) describe('@convertParameterObjectArrayIntoParameterMap', () => { diff --git a/testing/e2e/swagger2-internal/input.json b/testing/e2e/swagger2-internal/input.json index ad83045..0dbf4ed 100644 --- a/testing/e2e/swagger2-internal/input.json +++ b/testing/e2e/swagger2-internal/input.json @@ -236,7 +236,8 @@ "description": "ID of pet that needs to be fetched", "required": true, "type": "integer", - "format": "int64" + "format": "int64", + "x-example": 12356789 } ], "responses": { @@ -414,7 +415,7 @@ { "in": "path", "name": "orderId", - "description": "ID of pet that needs to be fetched", + "description": "ID of purchase order that needs to be fetched", "required": true, "type": "string" } diff --git a/testing/e2e/swagger2-internal/output.json b/testing/e2e/swagger2-internal/output.json index 6bf5b10..353bc58 100644 --- a/testing/e2e/swagger2-internal/output.json +++ b/testing/e2e/swagger2-internal/output.json @@ -1347,7 +1347,7 @@ "key": "petId", "name": "petId", "description": "ID of pet that needs to be fetched", - "examples": [], + "examples": [123456789], "type": "integer", "format": null, "default": null, @@ -2633,7 +2633,7 @@ "uuid": "orderId-path", "key": "orderId", "name": "orderId", - "description": "ID of pet that needs to be fetched", + "description": "ID of purchase order that needs to be fetched", "examples": [], "type": "string", "format": null, @@ -2725,7 +2725,7 @@ "uuid": "orderId-path", "key": "orderId", "name": "orderId", - "description": "ID of pet that needs to be fetched", + "description": "ID of purchase order that needs to be fetched", "examples": [], "type": "string", "format": null, @@ -5715,4 +5715,4 @@ }, "version": "1.0.0" } -} \ No newline at end of file +} From d9ec4da89cffc1b23f8eacdc0f0f1a1d844fddc3 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Mon, 11 Dec 2017 18:09:31 -0500 Subject: [PATCH 24/24] Swagger Examples E2E tests fail... --- testing/e2e/swagger2-internal/input.json | 8 +++++--- testing/e2e/swagger2-internal/output.json | 20 +++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/testing/e2e/swagger2-internal/input.json b/testing/e2e/swagger2-internal/input.json index 0dbf4ed..a06dc47 100644 --- a/testing/e2e/swagger2-internal/input.json +++ b/testing/e2e/swagger2-internal/input.json @@ -237,7 +237,7 @@ "required": true, "type": "integer", "format": "int64", - "x-example": 12356789 + "x-example": 123456789 } ], "responses": { @@ -345,7 +345,8 @@ "description": "Pet id to delete", "required": true, "type": "integer", - "format": "int64" + "format": "int64", + "x-example": 12345 } ], "responses": { @@ -417,7 +418,8 @@ "name": "orderId", "description": "ID of purchase order that needs to be fetched", "required": true, - "type": "string" + "type": "string", + "example": "15" } ], "responses": { diff --git a/testing/e2e/swagger2-internal/output.json b/testing/e2e/swagger2-internal/output.json index 353bc58..8b57f5d 100644 --- a/testing/e2e/swagger2-internal/output.json +++ b/testing/e2e/swagger2-internal/output.json @@ -1347,7 +1347,9 @@ "key": "petId", "name": "petId", "description": "ID of pet that needs to be fetched", - "examples": [123456789], + "examples": [ + 123456789 + ], "type": "integer", "format": null, "default": null, @@ -1439,7 +1441,9 @@ "key": "petId", "name": "petId", "description": "ID of pet that needs to be fetched", - "examples": [], + "examples": [ + 123456789 + ], "type": "integer", "format": null, "default": null, @@ -2100,7 +2104,9 @@ "key": "petId", "name": "petId", "description": "Pet id to delete", - "examples": [], + "examples": [ + 12345 + ], "type": "integer", "format": null, "default": null, @@ -2634,7 +2640,9 @@ "key": "orderId", "name": "orderId", "description": "ID of purchase order that needs to be fetched", - "examples": [], + "examples": [ + "15" + ], "type": "string", "format": null, "default": null, @@ -2726,7 +2734,9 @@ "key": "orderId", "name": "orderId", "description": "ID of purchase order that needs to be fetched", - "examples": [], + "examples": [ + "15" + ], "type": "string", "format": null, "default": null,