From ec0d891aec8b3e86e2bd8fddbef8622354d68031 Mon Sep 17 00:00:00 2001 From: Amar Zavery Date: Mon, 26 Jun 2017 10:38:47 -0700 Subject: [PATCH] initial commit to support yaml format --- lib/commands/generate-wireformat.js | 11 +++- lib/httpTemplate.js | 81 ++++++++++++++++++++++++----- lib/validate.js | 8 +-- lib/wireFormatGenerator.js | 8 +-- 4 files changed, 87 insertions(+), 21 deletions(-) diff --git a/lib/commands/generate-wireformat.js b/lib/commands/generate-wireformat.js index 21fca248..de828a4f 100644 --- a/lib/commands/generate-wireformat.js +++ b/lib/commands/generate-wireformat.js @@ -24,6 +24,12 @@ exports.builder = { 'need to be transformed. If operationIds are not provided then the entire spec will be processed. ' + 'Example: "StorageAccounts_Create, StorageAccounts_List, Usages_List".', string: true + }, + y: { + alias: 'inYaml', + describe: 'A boolean flag when provided will indicate the tool to ' + + 'generate wireformat in a yaml doc. Default is a markdown doc.', + boolean: true } }; @@ -33,12 +39,13 @@ exports.handler = function (argv) { let operationIds = argv.operationIds; let outDir = argv.outDir; let vOptions = {}; + let emitYaml = argv.inYaml; vOptions.consoleLogLevel = argv.logLevel; vOptions.logFilepath = argv.f; if (specPath.match(/.*composite.*/ig) !== null) { - return validate.generateWireFormatInCompositeSpec(specPath, vOptions); + return validate.generateWireFormatInCompositeSpec(specPath, outDir, emitYaml, vOptions); } else { - return validate.generateWireFormat(specPath, outDir, operationIds, vOptions); + return validate.generateWireFormat(specPath, outDir, emitYaml, operationIds, vOptions); } } diff --git a/lib/httpTemplate.js b/lib/httpTemplate.js index 863bb189..3a73ef4f 100644 --- a/lib/httpTemplate.js +++ b/lib/httpTemplate.js @@ -7,9 +7,10 @@ const url = require('url'), class HttpTemplate { - constructor(request, responses) { + constructor(request, responses, emitYaml) { this.request = request; this.responses = responses; + this.emitYaml = emitYaml || false; } getHost() { @@ -22,15 +23,16 @@ class HttpTemplate { getRequestHeaders() { let result = ``; + let padding = this.emitYaml ? ` ` : ``; if (this.request.body) { - result += `Content-Length: ${JSON.stringify(this.request.body).length}\n`; + result += `${padding}Content-Length: ${JSON.stringify(this.request.body).length}\n`; } if (this.request.headers) { let headers = Object.keys(this.request.headers); for (let i = 0; i < headers.length; i++) { let headerName = headers[i]; - result += `${headerName}: ${this.request.headers[headerName]}`; + result += `${padding}${headerName}: ${this.request.headers[headerName]}`; if (i !== headers.length - 1) { result += `\n`; } @@ -41,15 +43,16 @@ class HttpTemplate { getCurlRequestHeaders() { let result = ``; + let padding = this.emitYaml ? ` ` : ``; if (this.request.body) { - result += ` \n-H 'Content-Length: ${JSON.stringify(this.request.body).length}' \\`; + result += `\n${padding}-H 'Content-Length: ${JSON.stringify(this.request.body).length}' \\`; } if (this.request.headers) { let headers = Object.keys(this.request.headers); for (let i = 0; i < headers.length; i++) { let headerName = headers[i]; - result += `\n-H '${headerName}: ${this.request.headers[headerName]}' \\`; + result += `\n${padding}-H '${headerName}: ${this.request.headers[headerName]}' \\`; } } return result; @@ -63,18 +66,23 @@ class HttpTemplate { return body; } + //The format for request body in Curl has been inspired from the following links: + // - https://stackoverflow.com/questions/34847981/curl-with-multiline-of-json + // - https://ok-b.org/t/34847981/curl-with-multiline-of-json getCurlRequestBody() { let body = ``; + let padding = this.emitYaml ? ` ` : ``; if (this.request && this.request.body !== null && this.request.body !== undefined) { - body = `\n-d ${JSON.stringify(this.request.body, null, 2).split('\n').join(' \\\n')}`; + body = `\n${padding}-d @- << EOF\n${padding}${JSON.stringify(this.request.body, null, 2).split(`\n`).join(`\n${padding}`)}\n${padding}EOF`; } return body; } getResponseHeaders(response) { let result = ``; + let padding = this.emitYaml ? ` ` : ``; if (response.body) { - result += `Content-Length: ${JSON.stringify(response.body).length}\n`; + result += `${padding}Content-Length: ${JSON.stringify(response.body).length}\n`; } let gotContentType = false; if (response.headers) { @@ -82,14 +90,14 @@ class HttpTemplate { for (let i = 0; i < headers.length; i++) { let headerName = headers[i]; if (headerName.match(/^Content-Type$/ig) !== null) gotContentType = true; - result += `${headerName}: ${response.headers[headerName]}`; + result += `${padding}${headerName}: ${response.headers[headerName]}`; if (i !== headers.length - 1) { result += `\n`; } } } if (!gotContentType) { - result += `Content-Type: application/json; charset=utf-8` + result += `${padding}Content-Type: application/json; charset=utf-8` } return result; } @@ -103,7 +111,8 @@ class HttpTemplate { } populateRequest() { - let requestTemplate = + let requestTemplate = ``; + let requestTemplateMD = `## Request \`\`\`http @@ -117,13 +126,30 @@ ${this.getRequestBody()} \`\`\`\ `; + + + let requestTemplateYAML = + `#Request +request: | + ${this.request.method} ${this.request.url} HTTP/1.1 + Authorization: Bearer +${this.getRequestHeaders()} + host: ${this.getHost()} + Connection: close + + ${this.getRequestBody()} +`; + + requestTemplate = this.emitYaml ? requestTemplateYAML : requestTemplateMD; return requestTemplate; } populateResponse(response, responseType) { if (!responseType) responseType = 'Response'; let responseGuid = uuid.v4(); - let responseTemplate = ` + let responseTemplate = ``; + + let responseTemplateMD = ` ## ${responseType} #### StatusCode: ${response.statusCode} @@ -145,17 +171,48 @@ Connection: close ${this.getResponseBody(response)} \`\`\` `; + + + let responseTemplateYAML = ` +#${responseType} +response: + #${response.statusCode} + ${response.statusCode}: | + HTTP 1.1 ${response.statusCode} + Cache-Control: no-cache + Pragma: no-cache + Expires: -1 + x-ms-ratelimit-remaining-subscription-writes: 1199 + x-ms-request-id: ${responseGuid} + x-ms-correlation-request-id: ${responseGuid} + x-ms-routing-request-id: WESTUS2:${new Date().toISOString().replace(/(\W)/ig, '')}:${responseGuid} + Strict-Transport-Security: max-age=31536000; includeSubDomains +${this.getResponseHeaders(response)} + Date: ${new Date().toUTCString()} + Connection: close + + ${this.getResponseBody(response)} +`; + responseTemplate = this.emitYaml ? responseTemplateYAML : responseTemplateMD; return responseTemplate; } populateCurl() { - let template = + let template = ``; + let templateMD = `\n## Curl \`\`\`bash curl -X ${this.request.method} '${this.request.url}' \\\n-H 'authorization: bearer ' \\${this.getCurlRequestHeaders()}${this.getCurlRequestBody()} \`\`\` `; + + let templateYAML = + `\n#Curl +curl: | + curl -X ${this.request.method} '${this.request.url}' \\\n -H 'authorization: bearer ' \\${this.getCurlRequestHeaders()}${this.getCurlRequestBody()} +`; + template = this.emitYaml ? templateYAML : templateMD; return template; } diff --git a/lib/validate.js b/lib/validate.js index c0a9333e..8c6bded9 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -164,11 +164,11 @@ exports.resolveCompositeSpec = function resolveCompositeSpec(specPath, outputDir }; -exports.generateWireFormat = function generateWireFormat(specPath, outDir, operationIds, options) { +exports.generateWireFormat = function generateWireFormat(specPath, outDir, emitYaml, operationIds, options) { if (!options) options = {}; log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel; log.filepath = options.logFilepath || log.filepath; - let wfGenerator = new WireFormatGenerator(specPath, null, outDir); + let wfGenerator = new WireFormatGenerator(specPath, null, outDir, emitYaml); return wfGenerator.initialize().then(function () { log.info(`Generating wire format request and responses for swagger spec: "${specPath}":\n`); wfGenerator.processOperations(operationIds); @@ -179,7 +179,7 @@ exports.generateWireFormat = function generateWireFormat(specPath, outDir, opera }); }; -exports.generateWireFormatInCompositeSpec = function generateWireFormatInCompositeSpec(compositeSpecPath, outDir, options) { +exports.generateWireFormatInCompositeSpec = function generateWireFormatInCompositeSpec(compositeSpecPath, outDir, emitYaml, options) { if (!options) options = {}; log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel; log.filepath = options.logFilepath || log.filepath; @@ -187,7 +187,7 @@ exports.generateWireFormatInCompositeSpec = function generateWireFormatInComposi options.consoleLogLevel = log.consoleLogLevel; options.logFilepath = log.filepath; let promiseFactories = docs.map(function (doc) { - return function () { return exports.generateWireFormat(doc, outDir, null, options); } + return function () { return exports.generateWireFormat(doc, outDir, emitYaml, null, options); } }); return utils.executePromisesSequentially(promiseFactories); }).catch(function (err) { diff --git a/lib/wireFormatGenerator.js b/lib/wireFormatGenerator.js index d6e7768d..ddd03bc3 100644 --- a/lib/wireFormatGenerator.js +++ b/lib/wireFormatGenerator.js @@ -19,7 +19,7 @@ const ErrorCodes = Constants.ErrorCodes; class WireFormatGenerator { - constructor(specPath, specInJson, wireFormatDir) { + constructor(specPath, specInJson, wireFormatDir, emitYaml) { if (specPath === null || specPath === undefined || typeof specPath.valueOf() !== 'string' || !specPath.trim().length) { throw new Error('specPath is a required parameter of type string and it cannot be an empty string.') } @@ -37,6 +37,7 @@ class WireFormatGenerator { if (!fs.existsSync(this.wireFormatDir)) { fs.mkdirSync(this.wireFormatDir); } + this.emitYaml = emitYaml || false; this.specInJson = specInJson; this.specResolver = null; this.swaggerApi = null; @@ -248,9 +249,10 @@ class WireFormatGenerator { let sampleRequest = self.processRequest(operation, xmsExample.parameters); let sampleResponses = self.processXmsExampleResponses(operation, xmsExample.responses); let exampleFileName = path.basename(xmsExamples[scenario].filePath); - let wireformatFileName = `${exampleFileName.substring(0, exampleFileName.indexOf(path.extname(exampleFileName)))}.md`; + let wireformatFileName = `${exampleFileName.substring(0, exampleFileName.indexOf(path.extname(exampleFileName)))}.`; + wireformatFileName += self.emitYaml ? 'yml' : 'md'; let fileName = path.join(self.wireFormatDir, wireformatFileName); - let httpTempl = new HttpTemplate(sampleRequest, sampleResponses); + let httpTempl = new HttpTemplate(sampleRequest, sampleResponses, self.emitYaml); let sampleData = httpTempl.populate(); fs.writeFileSync(fileName, sampleData, { encoding: 'utf8' }); }