-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from seriousme/openapiv3
Openapi V3 support
- Loading branch information
Showing
21 changed files
with
3,454 additions
and
947 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
// a class for parsing openapi v3 data into config data for fastify | ||
|
||
class parserV3 { | ||
constructor() { | ||
this.config = { generic: {}, routes: [] }; | ||
} | ||
|
||
makeOperationId(operation, path) { | ||
// make a nice camelCase operationID | ||
// e.g. get /user/{name} becomes getUserByName | ||
const firstUpper = str => str.substr(0, 1).toUpperCase() + str.substr(1); | ||
const by = (matched, p1) => "By" + firstUpper(p1); | ||
const parts = path.split("/").slice(1); | ||
const opId = parts | ||
.map((item, i) => (i > 0 ? firstUpper(item) : item)) | ||
.join("") | ||
.replace(/{(\w+)}/g, by) | ||
.replace(/[^a-z]/gi, ""); | ||
return opId; | ||
} | ||
|
||
makeURL(path) { | ||
// fastify wants 'path/:param' instead of openapis 'path/{param}' | ||
return path.replace(/{(\w+)}/g, ":$1"); | ||
} | ||
|
||
copyProps(source, target, list) { | ||
list.forEach(item => { | ||
if (source[item]) target[item] = source[item]; | ||
}); | ||
} | ||
|
||
parseParams(data) { | ||
const params = { | ||
type: "object", | ||
properties: {} | ||
}; | ||
const required = []; | ||
data.forEach(item => { | ||
// item.type "file" breaks ajv, so treat is as a special here | ||
if (item.type === "file") { | ||
item.type = "string"; | ||
item.isFile = true; | ||
} | ||
// | ||
params.properties[item.name] = item.schema; | ||
this.copyProps(item, params.properties[item.name], ["description"]); | ||
// ajv wants "required" to be an array, which seems to be too strict | ||
// see https://github.com/json-schema/json-schema/wiki/Properties-and-required | ||
if (item.required) { | ||
required.push(item.name); | ||
} | ||
}); | ||
if (required.length > 0) { | ||
params.required = required; | ||
} | ||
return params; | ||
} | ||
|
||
parseParameters(schema, data) { | ||
const params = []; | ||
const querystring = []; | ||
const headers = []; | ||
// const formData = []; | ||
data.forEach(item => { | ||
switch (item.in) { | ||
// case "body": | ||
// schema.body = item.schema; | ||
// break; | ||
// case "formData": | ||
// formData.push(item); | ||
// break; | ||
case "path": | ||
params.push(item); | ||
break; | ||
case "query": | ||
querystring.push(item); | ||
break; | ||
case "header": | ||
headers.push(item); | ||
break; | ||
} | ||
}); | ||
if (params.length > 0) schema.params = this.parseParams(params); | ||
if (querystring.length > 0) | ||
schema.querystring = this.parseParams(querystring); | ||
if (headers.length > 0) schema.headers = this.parseParams(headers); | ||
} | ||
|
||
parseBody(data) { | ||
let schema; | ||
if (data && data.content) { | ||
for (let mimeType in data.content) { | ||
if (mimeType !== "application/json") { | ||
console.log(`body type: ${mimeType} found`); | ||
} | ||
schema = data.content[mimeType].schema; | ||
} | ||
} | ||
return schema; | ||
} | ||
|
||
parseResponses(responses) { | ||
const result = {}; | ||
let hasResponse = false; | ||
for (let httpCode in responses) { | ||
const body = this.parseBody(responses[httpCode]); | ||
if (body !== undefined) { | ||
result[httpCode] = body; | ||
hasResponse = true; | ||
} | ||
} | ||
return hasResponse ? result : null; | ||
} | ||
|
||
makeSchema(data) { | ||
const schema = {}; | ||
const copyItems = ["tags", "summary", "operationId"]; | ||
this.copyProps(data, schema, copyItems); | ||
if (data.parameters) this.parseParameters(schema, data.parameters); | ||
const body = this.parseBody(data.requestBody); | ||
if (body) schema.body = body; | ||
const response = this.parseResponses(data.responses); | ||
if (response) schema.response = response; | ||
return schema; | ||
} | ||
|
||
processOperation(path, operation, data) { | ||
const route = { | ||
method: operation.toUpperCase(), | ||
url: this.makeURL(path), | ||
schema: this.makeSchema(data), | ||
operationId: data.operationId || this.makeOperationId(operation, path), | ||
openapiSource: data | ||
}; | ||
this.config.routes.push(route); | ||
} | ||
|
||
processPaths(paths) { | ||
for (let path in paths) { | ||
for (let operation in paths[path]) { | ||
this.processOperation(path, operation, paths[path][operation]); | ||
} | ||
} | ||
} | ||
|
||
parse(spec) { | ||
for (let item in spec) { | ||
switch (item) { | ||
case "paths": | ||
this.processPaths(spec.paths); | ||
break; | ||
default: | ||
this.config.generic[item] = spec[item]; | ||
} | ||
} | ||
return this.config; | ||
} | ||
} | ||
|
||
module.exports = parserV3; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.