openapi-snippet
Advanced tools
Comparing version 0.11.0 to 0.12.0
89
index.js
@@ -31,25 +31,23 @@ /** | ||
const har = OpenAPIToHar.getEndpoint(openApi, path, method, values); | ||
const hars = OpenAPIToHar.getEndpoint(openApi, path, method, values); | ||
const snippet = new HTTPSnippet(har); | ||
const snippets = []; | ||
for (let j in targets) { | ||
const target = formatTarget(targets[j]); | ||
if (!target) throw new Error('Invalid target: ' + targets[j]); | ||
snippets.push({ | ||
id: targets[j], | ||
title: target.title, | ||
content: snippet.convert( | ||
target.language, | ||
typeof target.library !== 'undefined' ? target.library : null | ||
), | ||
}); | ||
for (const har of hars) { | ||
const snippet = new HTTPSnippet(har); | ||
snippets.push( | ||
...getSnippetsForTargets( | ||
targets, | ||
snippet, | ||
har.comment ? har.comment : undefined | ||
) | ||
); | ||
} | ||
// use first element since method, url, and description | ||
// are the same for all elements | ||
return { | ||
method: har.method, | ||
url: har.url, | ||
description: har.description, | ||
resource: getResourceName(har.url), | ||
method: hars[0].method, | ||
url: hars[0].url, | ||
description: hars[0].description, | ||
resource: getResourceName(hars[0].url), | ||
snippets: snippets, | ||
@@ -67,29 +65,19 @@ }; | ||
const getSnippets = function (openApi, targets) { | ||
const harList = OpenAPIToHar.getAll(openApi); | ||
const endpointHarInfoList = OpenAPIToHar.getAll(openApi); | ||
const results = []; | ||
for (let i in harList) { | ||
for (let i in endpointHarInfoList) { | ||
// create HTTPSnippet object: | ||
const har = harList[i]; | ||
const snippet = new HTTPSnippet(har.har); | ||
const harInfo = endpointHarInfoList[i]; | ||
const snippets = []; | ||
for (let j in targets) { | ||
const target = formatTarget(targets[j]); | ||
if (!target) throw new Error('Invalid target: ' + targets[j]); | ||
snippets.push({ | ||
id: targets[j], | ||
title: target.title, | ||
content: snippet.convert( | ||
target.language, | ||
typeof target.library !== 'undefined' ? target.library : null | ||
), | ||
}); | ||
for (const har of harInfo.hars) { | ||
const snippet = new HTTPSnippet(har); | ||
snippets.push(...getSnippetsForTargets(targets, snippet, har.comment)); | ||
} | ||
results.push({ | ||
method: har.method, | ||
url: har.url, | ||
description: har.description, | ||
resource: getResourceName(har.url), | ||
method: harInfo.method, | ||
url: harInfo.url, | ||
description: harInfo.description, | ||
resource: getResourceName(harInfo.url), | ||
snippets, | ||
@@ -200,2 +188,27 @@ }); | ||
/** | ||
* Generate code snippets for each of the supplied targets | ||
* | ||
* @param targets {array} List of language targets to generate code for | ||
* @param snippet {Object} Snippet object from httpsnippet to convert into the target objects | ||
* @param mimeType {string | undefined} Additional information to add uniqueness to the produced snippets | ||
*/ | ||
const getSnippetsForTargets = function (targets, snippet, mimeType) { | ||
const snippets = []; | ||
for (let j in targets) { | ||
const target = formatTarget(targets[j]); | ||
if (!target) throw new Error('Invalid target: ' + targets[j]); | ||
snippets.push({ | ||
id: targets[j], | ||
...(mimeType !== undefined && { mimeType: mimeType }), | ||
title: target.title, | ||
content: snippet.convert( | ||
target.language, | ||
typeof target.library !== 'undefined' ? target.library : null | ||
), | ||
}); | ||
} | ||
return snippets; | ||
}; | ||
const capitalizeFirstLetter = function (string) { | ||
@@ -202,0 +215,0 @@ return string.charAt(0).toUpperCase() + string.slice(1); |
@@ -31,3 +31,3 @@ /** | ||
* @param {Object} queryParamValues Optional: Values for the query parameters if present | ||
* @return {Object} HAR Request object | ||
* @return {array} List of HAR Request objects for the endpoint | ||
*/ | ||
@@ -42,3 +42,3 @@ const createHar = function (openApi, path, method, queryParamValues) { | ||
const har = { | ||
const baseHar = { | ||
method: method.toUpperCase(), | ||
@@ -54,7 +54,25 @@ url: baseUrl + getFullPath(openApi, path, method), | ||
let hars = []; | ||
// get payload data, if available: | ||
const postData = getPayload(openApi, path, method); | ||
if (postData) har.postData = postData; | ||
const postDatas = getPayloads(openApi, path, method); | ||
return har; | ||
// For each postData create a snippet | ||
if (postDatas.length > 0) { | ||
for (let i in postDatas) { | ||
const postData = postDatas[i]; | ||
const copiedHar = JSON.parse(JSON.stringify(baseHar)); | ||
copiedHar.postData = postData; | ||
copiedHar.comment = postData.mimeType; | ||
copiedHar.headers.push({ | ||
name: 'content-type', | ||
value: postData.mimeType, | ||
}); | ||
hars.push(copiedHar); | ||
} | ||
} else { | ||
hars = [baseHar]; | ||
} | ||
return hars; | ||
}; | ||
@@ -70,5 +88,5 @@ | ||
* @param {string} method | ||
* @return {object} | ||
* @return {array} A list of payload objects | ||
*/ | ||
const getPayload = function (openApi, path, method) { | ||
const getPayloads = function (openApi, path, method) { | ||
if (typeof openApi.paths[path][method].parameters !== 'undefined') { | ||
@@ -88,6 +106,8 @@ for (let i in openApi.paths[path][method].parameters) { | ||
); | ||
return { | ||
mimeType: 'application/json', | ||
text: JSON.stringify(sample), | ||
}; | ||
return [ | ||
{ | ||
mimeType: 'application/json', | ||
text: JSON.stringify(sample), | ||
}, | ||
]; | ||
} catch (err) { | ||
@@ -111,2 +131,3 @@ console.log(err); | ||
const payloads = []; | ||
if ( | ||
@@ -116,55 +137,53 @@ openApi.paths[path][method].requestBody && | ||
) { | ||
if ( | ||
openApi.paths[path][method].requestBody.content['application/json'] && | ||
openApi.paths[path][method].requestBody.content['application/json'].schema | ||
) { | ||
const sample = OpenAPISampler.sample( | ||
openApi.paths[path][method].requestBody.content['application/json'] | ||
.schema, | ||
{ skipReadOnly: true }, | ||
openApi | ||
); | ||
return { | ||
mimeType: 'application/json', | ||
text: JSON.stringify(sample), | ||
}; | ||
} | ||
[ | ||
'application/json', | ||
'application/x-www-form-urlencoded', | ||
'multipart/form-data', | ||
].forEach((type) => { | ||
const content = openApi.paths[path][method].requestBody.content[type]; | ||
if (content && content.schema) { | ||
const sample = OpenAPISampler.sample( | ||
content.schema, | ||
{ skipReadOnly: true }, | ||
openApi | ||
); | ||
if (type === 'application/json') { | ||
payloads.push({ | ||
mimeType: type, | ||
text: JSON.stringify(sample), | ||
}); | ||
} else if (type === 'multipart/form-data') { | ||
if (sample !== undefined) { | ||
const params = Object.keys(sample).reduce( | ||
(acc, key) => acc.concat([{ name: key, value: sample[key] }]), | ||
[] | ||
); | ||
payloads.push({ | ||
mimeType: type, | ||
params: params, | ||
}); | ||
} | ||
} else if (type == 'application/x-www-form-urlencoded') { | ||
if (sample === undefined) return null; | ||
if ( | ||
openApi.paths[path][method].requestBody.content[ | ||
'application/x-www-form-urlencoded' | ||
] && | ||
openApi.paths[path][method].requestBody.content[ | ||
'application/x-www-form-urlencoded' | ||
].schema | ||
) { | ||
const sample = OpenAPISampler.sample( | ||
openApi.paths[path][method].requestBody.content[ | ||
'application/x-www-form-urlencoded' | ||
].schema, | ||
{ skipReadOnly: true }, | ||
openApi | ||
); | ||
const params = []; | ||
Object.keys(sample).map((key) => | ||
params.push({ | ||
name: encodeURIComponent(key).replace(/\%20/g, '+'), | ||
value: encodeURIComponent(sample[key]).replace(/\%20/g, '+'), | ||
}) | ||
); | ||
if (sample === undefined) return null; | ||
const params = []; | ||
Object.keys(sample).map((key) => | ||
params.push({ | ||
name: encodeURIComponent(key).replace(/\%20/g, '+'), | ||
value: encodeURIComponent(sample[key]).replace(/\%20/g, '+'), | ||
}) | ||
); | ||
return { | ||
mimeType: 'application/x-www-form-urlencoded', | ||
params: params, | ||
text: Object.keys(params) | ||
.map((key) => key + '=' + sample[key]) | ||
.join('&'), | ||
}; | ||
} | ||
payloads.push({ | ||
mimeType: 'application/x-www-form-urlencoded', | ||
params: params, | ||
text: Object.keys(params) | ||
.map((key) => key + '=' + sample[key]) | ||
.join('&'), | ||
}); | ||
} | ||
} | ||
}); | ||
} | ||
return null; | ||
return payloads; | ||
}; | ||
@@ -202,5 +221,5 @@ | ||
* Gets an object describing the the paremeters (header or query) in a given OpenAPI method | ||
* @param {Object} param parameter values to use in snippet | ||
* @param {Object} values Optional: query parameter values to use in the snippet if present | ||
* @returns {Object} Object describing the parameters in a given OpenAPI method | ||
* @param {Object} param parameter values to use in snippet | ||
* @param {Object} values Optional: query parameter values to use in the snippet if present | ||
* @return {Object} Object describing the parameters in a given OpenAPI method or path | ||
*/ | ||
@@ -230,2 +249,40 @@ const getParameterValues = function (param, values) { | ||
/** | ||
* Parse parameter object into query string objects | ||
* | ||
* @param {Object} openApi OpenApi document | ||
* @param {Object} parameters Objects described in the document to parse into the query string | ||
* @param {Object} values Optional: query parameter values to use in the snippet if present | ||
* @return {Object} Object describing the parameters for a method or path | ||
*/ | ||
const parseParametersToQuery = function (openApi, parameters, values) { | ||
const queryStrings = {}; | ||
for (let i in parameters) { | ||
let param = parameters[i]; | ||
if (typeof param['$ref'] === 'string' && /^#/.test(param['$ref'])) { | ||
param = resolveRef(openApi, param['$ref']); | ||
} | ||
if (typeof param.schema !== 'undefined') { | ||
if ( | ||
typeof param.schema['$ref'] === 'string' && | ||
/^#/.test(param.schema['$ref']) | ||
) { | ||
param.schema = resolveRef(openApi, param.schema['$ref']); | ||
if (typeof param.schema.type === 'undefined') { | ||
// many schemas don't have an explicit type | ||
param.schema.type = 'object'; | ||
} | ||
} | ||
} | ||
if (typeof param.in !== 'undefined' && param.in.toLowerCase() === 'query') { | ||
// param.name is a safe key, because the spec defines | ||
// that name MUST be unique | ||
queryStrings[param.name] = getParameterValues(param, values); | ||
} | ||
} | ||
return queryStrings; | ||
}; | ||
/** | ||
* Get array of objects describing the query parameters for a path and method | ||
@@ -246,32 +303,29 @@ * pair described in the given OpenAPI document. | ||
const queryStrings = []; | ||
let pathQueryStrings = {}; | ||
let methodQueryStrings = {}; | ||
// First get any parameters from the path | ||
if (typeof openApi.paths[path].parameters !== 'undefined') { | ||
pathQueryStrings = parseParametersToQuery( | ||
openApi, | ||
openApi.paths[path].parameters, | ||
values | ||
); | ||
} | ||
if (typeof openApi.paths[path][method].parameters !== 'undefined') { | ||
for (let i in openApi.paths[path][method].parameters) { | ||
let param = openApi.paths[path][method].parameters[i]; | ||
if (typeof param['$ref'] === 'string' && /^#/.test(param['$ref'])) { | ||
param = resolveRef(openApi, param['$ref']); | ||
} | ||
if (typeof param.schema !== 'undefined') { | ||
if ( | ||
typeof param.schema['$ref'] === 'string' && | ||
/^#/.test(param.schema['$ref']) | ||
) { | ||
param.schema = resolveRef(openApi, param.schema['$ref']); | ||
if (typeof param.schema.type === 'undefined') { | ||
// many schemas don't have an explicit type | ||
param.schema.type = 'object'; | ||
} | ||
} | ||
} | ||
if ( | ||
typeof param.in !== 'undefined' && | ||
param.in.toLowerCase() === 'query' | ||
) { | ||
queryStrings.push(getParameterValues(param, values)); | ||
} | ||
} | ||
methodQueryStrings = parseParametersToQuery( | ||
openApi, | ||
openApi.paths[path][method].parameters, | ||
values | ||
); | ||
} | ||
return queryStrings; | ||
// Merge query strings, with method overriding path | ||
// from the spec: | ||
// If a parameter is already defined at the Path Item, the new definition will override | ||
// it but can never remove it. | ||
// https://swagger.io/specification/ | ||
const queryStrings = Object.assign(pathQueryStrings, methodQueryStrings); | ||
return Object.values(queryStrings); | ||
}; | ||
@@ -336,23 +390,2 @@ | ||
// 'content-type' header: | ||
if (typeof pathObj.produces !== 'undefined') { | ||
for (let j in pathObj.produces) { | ||
const type2 = pathObj.produces[j]; | ||
headers.push({ | ||
name: 'content-type', | ||
value: type2, | ||
}); | ||
} | ||
} | ||
// v3 'content-type' header: | ||
if (pathObj.requestBody && pathObj.requestBody.content) { | ||
for (const type3 of Object.keys(pathObj.requestBody.content)) { | ||
headers.push({ | ||
name: 'content-type', | ||
value: type3, | ||
}); | ||
} | ||
} | ||
// headers defined in path object: | ||
@@ -483,3 +516,4 @@ if (typeof pathObj.parameters !== 'undefined') { | ||
const url = getBaseUrl(openApi, path, method) + path; | ||
const har = createHar(openApi, path, method); | ||
const hars = createHar(openApi, path, method); | ||
// need to push multiple here | ||
harList.push({ | ||
@@ -491,3 +525,3 @@ method: method.toUpperCase(), | ||
'No description available', | ||
har: har, | ||
hars: hars, | ||
}); | ||
@@ -494,0 +528,0 @@ } |
{ | ||
"name": "openapi-snippet", | ||
"version": "0.11.0", | ||
"version": "0.12.0", | ||
"description": "Generates code snippets from Open API (previously Swagger) documents.", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -76,2 +76,3 @@ # OpenAPI Snippet | ||
"id": "node", | ||
"mimeType": "application/json", // Only set for methods with a request body | ||
"title": "Node + Native", | ||
@@ -78,0 +79,0 @@ "content": "var http = require(\"https\");\n\nvar options = {..." |
@@ -22,6 +22,3 @@ { | ||
"type": "object", | ||
"required": [ | ||
"id", | ||
"secret" | ||
], | ||
"required": ["id", "secret"], | ||
"properties": { | ||
@@ -28,0 +25,0 @@ "id": { |
@@ -78,2 +78,112 @@ { | ||
} | ||
}, | ||
"/animals": { | ||
"parameters": [ | ||
{ | ||
"name": "tags", | ||
"in": "query", | ||
"description": "tags to filter by", | ||
"required": false, | ||
"style": "form", | ||
"example": ["dog", "cat"], | ||
"schema": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
} | ||
}, | ||
{ | ||
"name": "limit", | ||
"in": "query", | ||
"description": "maximum number of results to return", | ||
"example": 10, | ||
"required": false, | ||
"schema": { | ||
"type": "integer", | ||
"format": "int32" | ||
} | ||
} | ||
], | ||
"get": { | ||
"description": "Get Pets from store", | ||
"operationId": "getPet", | ||
"responses": { | ||
"200": { | ||
"description": "pet response", | ||
"content": { | ||
"application/json": { | ||
"schema": { | ||
"$ref": "#/components/schemas/Pet" | ||
} | ||
} | ||
} | ||
}, | ||
"default": { | ||
"description": "unexpected error", | ||
"content": { | ||
"application/json": { | ||
"schema": { | ||
"$ref": "#/components/schemas/Error" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
"/species": { | ||
"parameters": [ | ||
{ | ||
"name": "id", | ||
"in": "query", | ||
"description": "the species id", | ||
"required": false, | ||
"example": 1, | ||
"schema": { | ||
"type": "integer" | ||
} | ||
} | ||
], | ||
"get": { | ||
"description": "Get Pets from store", | ||
"operationId": "getPet", | ||
"parameters": [ | ||
{ | ||
"name": "id", | ||
"in": "query", | ||
"description": "A comma-seperated list of species IDs", | ||
"required": false, | ||
"example": [1, 2], | ||
"schema": { | ||
"type": "array", | ||
"items": { | ||
"type": "integer" | ||
} | ||
} | ||
} | ||
], | ||
"responses": { | ||
"200": { | ||
"description": "pet response", | ||
"content": { | ||
"application/json": { | ||
"schema": { | ||
"$ref": "#/components/schemas/Species" | ||
} | ||
} | ||
} | ||
}, | ||
"default": { | ||
"description": "unexpected error", | ||
"content": { | ||
"application/json": { | ||
"schema": { | ||
"$ref": "#/components/schemas/Error" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
@@ -110,2 +220,16 @@ }, | ||
}, | ||
"Species": { | ||
"items": { | ||
"$ref": "#/components/thing" | ||
}, | ||
"type": "array" | ||
}, | ||
"Thing": { | ||
"required": ["id"], | ||
"properties": { | ||
"id": { | ||
"type": "integer" | ||
} | ||
} | ||
}, | ||
"Error": { | ||
@@ -112,0 +236,0 @@ "required": ["code", "message"], |
@@ -15,3 +15,5 @@ 'use strict'; | ||
const ParameterExampleReferenceAPI = require('./parameter_example_swagger.json'); | ||
const FormDataExampleReferenceAPI = require('./form_data_example.json'); | ||
const FormUrlencodedExampleAPI = require('./form_urlencoded_example.json'); | ||
const MultipleRequestContentReferenceAPI = require('./multiple_request_content.json'); | ||
@@ -202,2 +204,84 @@ test('Getting snippets should not result in error or undefined', function (t) { | ||
test('Generate snippet with multipart/form-data', function (t) { | ||
const result = OpenAPISnippets.getEndpointSnippets( | ||
FormDataExampleReferenceAPI, | ||
'/pets', | ||
'patch', | ||
['node_request'] | ||
); | ||
const snippet = result.snippets[0].content; | ||
t.true(/boundary=---011000010111000001101001/.test(snippet)); | ||
t.true( | ||
/formData: {'pet\[name\]': 'string', 'pet\[tag\]': 'string'}/.test(snippet) | ||
); | ||
t.end(); | ||
}); | ||
test('Generate snippets with multiple content types', function (t) { | ||
const result = OpenAPISnippets.getEndpointSnippets( | ||
MultipleRequestContentReferenceAPI, | ||
'/pets', | ||
'patch', | ||
['node_request'] | ||
); | ||
t.equal(result.snippets.length, 2); | ||
for (const snippet of result.snippets) { | ||
if (snippet.mimeType === 'application/json') { | ||
t.true( | ||
/headers: {'content-type': 'application\/json'}/.test(snippet.content) | ||
); | ||
t.true(/body: {name: 'string', tag: 'string'}/.test(snippet.content)); | ||
} else if (snippet.mimeType === 'multipart/form-data') { | ||
t.true( | ||
/headers: {'content-type': 'multipart\/form-data; boundary=---011000010111000001101001'}/.test( | ||
snippet.content | ||
) | ||
); | ||
t.true( | ||
/formData: {'pet\[name\]': 'string', 'pet\[tag\]': 'string', 'pet\[picture\]': 'string'}/.test( | ||
snippet.content | ||
) | ||
); | ||
} | ||
} | ||
t.end(); | ||
}); | ||
test('Query Params Defined for all methods should be resolved', function (t) { | ||
const result = OpenAPISnippets.getEndpointSnippets( | ||
ParameterExampleReferenceAPI, | ||
'/animals', | ||
'get', | ||
['node_request'] | ||
); | ||
const snippet = result.snippets[0].content; | ||
t.true(/ {tags: 'dog,cat', limit: '10'}/.test(snippet)); | ||
t.false(/SOME_INTEGER_VALUE/.test(snippet)); | ||
t.end(); | ||
}); | ||
test('Query Params Defined for all methods are overriden by method definitions', function (t) { | ||
const result = OpenAPISnippets.getEndpointSnippets( | ||
ParameterExampleReferenceAPI, | ||
'/species', | ||
'get', | ||
['node_request'] | ||
); | ||
const snippet = result.snippets[0].content; | ||
t.true(/ qs: {id: '1,2'}/.test(snippet)); | ||
t.end(); | ||
}); | ||
test('Snippet for Get with no parameters should work', function (t) { | ||
const result = OpenAPISnippets.getEndpointSnippets( | ||
InstagramOpenAPI, | ||
'/media/popular', | ||
'get', | ||
['node_request'] | ||
); | ||
const snippet = result.snippets[0].content; | ||
t.false(/qs/.test(snippet)); | ||
t.end(); | ||
}); | ||
test('Testing the application/x-www-form-urlencoded example case', function (t) { | ||
@@ -204,0 +288,0 @@ t.plan(2); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1223617
21
41375
116