-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/#22 open api 3 spec #63
Changes from 13 commits
9b0192b
0c0db5d
346430d
5b66d12
4e02436
60dcd64
15e26b8
243ef97
d27748e
45fbfd0
5f356d8
9f2f8a7
78826ce
866341e
cd24492
4c53dc4
a2aa633
5daa90b
762b693
5387df5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,4 +5,6 @@ build | |
.idea | ||
*.iml | ||
test_spec.json | ||
test_spec_v3.json | ||
api-spec.json | ||
api-spec_v3.json | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,8 +10,10 @@ const fs = require('fs'); | |
const path = require('path'); | ||
const swaggerUi = require('swagger-ui-express'); | ||
const utils = require('./lib/utils'); | ||
|
||
const { generateMongooseModelsSpec } = require('./lib/mongoose'); | ||
const { generateTagsSpec, matchingTags } = require('./lib/tags'); | ||
const { convertOpenApiVersionToV3, getSpecByVersion, versions } = require('./lib/openapi'); | ||
const processors = require('./lib/processors'); | ||
const listEndpoints = require('express-list-endpoints'); | ||
|
||
|
@@ -97,11 +99,55 @@ function updateSpecFromPackage() { | |
} | ||
|
||
/** | ||
* @description serve the openAPI docs with swagger at a specified path / url | ||
* @description Builds api spec middleware | ||
* | ||
* @returns Middleware | ||
*/ | ||
function apiSpecMiddleware(specV2, version) { | ||
return (req, res) => { | ||
getSpecByVersion(specV2, version, (err, openApiSpec) => { | ||
if (!err) { | ||
res.setHeader('Content-Type', 'application/json'); | ||
res.send(JSON.stringify(openApiSpec, null, 2)); | ||
} | ||
}); | ||
}; | ||
} | ||
|
||
/** | ||
* @description Builds swagger serve middleware | ||
* | ||
* @returns Middleware | ||
*/ | ||
function swaggerServeMiddleware(specV2, version) { | ||
return (req, res) => { | ||
getSpecByVersion(specV2, version, (err, openApiSpec) => { | ||
if (!err) { | ||
res.setHeader('Content-Type', 'text/html'); | ||
swaggerUi.setup(openApiSpec)(req, res); | ||
} | ||
}); | ||
}; | ||
} | ||
|
||
/** | ||
* @description Applies spec middlewares | ||
*/ | ||
function applySpecMiddlewares(spec, version = '') { | ||
|
||
const apiSpecBasePath = packageInfo.baseUrlPath.concat('/api-spec'); | ||
const baseSwaggerServePath = packageInfo.baseUrlPath.concat('/' + swaggerUiServePath); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, it is! Before initializing the middlewares |
||
app.use(apiSpecBasePath.concat('/' + version), apiSpecMiddleware(spec, version)); | ||
app.use(baseSwaggerServePath.concat('/' + version), swaggerUi.serve, swaggerServeMiddleware(spec, version)); | ||
} | ||
|
||
/** | ||
* @description Prepares spec to be served | ||
* | ||
* @returns void | ||
*/ | ||
function serveApiDocs() { | ||
function prepareSpec() { | ||
spec = { swagger: '2.0', paths: {} }; | ||
|
||
const endpoints = listEndpoints(app); | ||
|
@@ -141,15 +187,22 @@ function serveApiDocs() { | |
spec.definitions = mongooseModelsSpecs || {}; | ||
updateSpecFromPackage(); | ||
spec = patchSpec(predefinedSpec); | ||
app.use(packageInfo.baseUrlPath + '/api-spec', (req, res, next) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to have next line like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
||
res.setHeader('Content-Type', 'application/json'); | ||
res.send(JSON.stringify(patchSpec(predefinedSpec), null, 2)); | ||
next(); | ||
}); | ||
app.use(packageInfo.baseUrlPath + '/' + swaggerUiServePath, swaggerUi.serve, (req, res) => { | ||
res.setHeader('Content-Type', 'text/html'); | ||
swaggerUi.setup(patchSpec(predefinedSpec))(req, res); | ||
}); | ||
} | ||
|
||
/** | ||
* @description serve the openAPI docs with swagger at a specified path / url | ||
* | ||
* @returns void | ||
*/ | ||
function serveApiDocs() { | ||
prepareSpec(); | ||
|
||
applySpecMiddlewares(spec, versions.OPEN_API_V2); | ||
|
||
applySpecMiddlewares(spec, versions.OPEN_API_V3); | ||
|
||
// Base path middleware should be applied after specific versions | ||
applySpecMiddlewares(spec); | ||
} | ||
|
||
/** | ||
|
@@ -275,7 +328,7 @@ function handleResponses(expressApp, | |
|
||
updateDefinitionsSpec(options.mongooseModels); | ||
updateTagsSpec(options.tags || options.mongooseModels); | ||
|
||
/** middleware to handle RESPONSES */ | ||
app.use((req, res, next) => { | ||
try { | ||
|
@@ -289,17 +342,21 @@ function handleResponses(expressApp, | |
firstResponseProcessing = false; | ||
lastRecordTime = ts; | ||
|
||
fs.writeFile(specOutputPath, JSON.stringify(spec, null, 2), 'utf8', err => { | ||
const fullPath = path.resolve(specOutputPath); | ||
|
||
if (err) { | ||
/** | ||
* TODO - this is broken - the error will be caught and ignored in the catch below. | ||
* See https://github.com/mpashkovskiy/express-oas-generator/pull/39#discussion_r340026645 | ||
*/ | ||
throw new Error(`Cannot store the specification into ${fullPath} because of ${err.message}`); | ||
fs.writeFileSync(specOutputPath, JSON.stringify(spec, null, 2), 'utf8'); | ||
|
||
convertOpenApiVersionToV3(spec, (err, specV3) => { | ||
if (!err) { | ||
const parsedSpecOutputPath = path.parse(specOutputPath); | ||
const {name, ext} = parsedSpecOutputPath; | ||
parsedSpecOutputPath.base = name.concat('_').concat(versions.OPEN_API_V3).concat(ext); | ||
|
||
const v3Path = path.format(parsedSpecOutputPath); | ||
|
||
fs.writeFileSync(v3Path, JSON.stringify(specV3), 'utf8'); | ||
} | ||
}); | ||
/** TODO - Log that open api v3 could not be generated */ | ||
}); | ||
|
||
} | ||
} catch (e) { | ||
/** TODO - shouldn't we do something here? */ | ||
|
@@ -381,6 +438,13 @@ const getSpec = () => { | |
return patchSpec(predefinedSpec); | ||
}; | ||
|
||
/** | ||
* @type { typeof import('./index').getSpecV3 } | ||
*/ | ||
const getSpecV3 = callback => { | ||
convertOpenApiVersionToV3(getSpec(), callback); | ||
}; | ||
|
||
/** | ||
* @type { typeof import('./index').setPackageInfoPath } | ||
* | ||
|
@@ -395,5 +459,6 @@ module.exports = { | |
handleRequests, | ||
init, | ||
getSpec, | ||
getSpecV3, | ||
setPackageInfoPath | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
const openApiVersionConverter = require('swagger2openapi'); | ||
|
||
module.exports.versions = { | ||
OPEN_API_V2:'v2', | ||
OPEN_API_V3:'v3' | ||
}; | ||
|
||
module.exports.getSpecByVersion = (specV2, version, callback) => { | ||
const defaultSpec = (specV2, callback) => callback(null, specV2); | ||
const availableSpecs = { | ||
[this.versions.OPEN_API_V2]: defaultSpec, | ||
[this.versions.OPEN_API_V3]: this.convertOpenApiVersionToV3, | ||
}; | ||
const specByVersion = availableSpecs[version] || defaultSpec; | ||
|
||
return specByVersion(specV2, callback); | ||
}; | ||
|
||
module.exports.convertOpenApiVersionToV3 = (specV2, callback) => { | ||
const options = {patch: true, warnOnly: true}; | ||
|
||
openApiVersionConverter.convertObj(specV2, options, function(err, results) { | ||
callback(err, results && results.openapi); | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd just add
test_spec*.json
once instead. Same withapi-spec*.json
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!