-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto registration as CAP plugin (#87)
- The middleware registers itself when loaded as a CAP plugin - Declarative configuration through `package.json` / `cds-rc.json` - Programmatic registration is still possible, but disables auto mode then - Make dependency to `@sap/cds-dk` optional. If running with the `cds` executable during development, the dependency is fulfilled anyways. Only when running as part of a deployed appi with `cds-serve`, then such a dependency is needed.
- Loading branch information
Showing
13 changed files
with
732 additions
and
169 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
const cds = require ('@sap/cds') | ||
const DEBUG = cds.debug('swagger') | ||
const { registered } = require ('./lib/etc') | ||
|
||
cds.on ('bootstrap', app => { | ||
const { swagger } = cds.env | ||
if (!swagger) { | ||
return DEBUG?.('Plugin disabled by configuration') | ||
} | ||
|
||
if (cds[registered]) { | ||
return DEBUG?.('Plugin disabled: already registered programmatically') | ||
} | ||
|
||
DEBUG?.('Plugin with options', swagger) | ||
const cds_swagger = require('./lib/middleware') | ||
app.use(cds_swagger(swagger)) | ||
|
||
}) |
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 |
---|---|---|
@@ -1,63 +1,10 @@ | ||
const cds = require('@sap/cds-dk') | ||
const swaggerUi = require('swagger-ui-express') | ||
const express = require('express') | ||
const { join } = require('path') | ||
const LOG = cds.log('swagger') | ||
const cds = require ('@sap/cds') | ||
const DEBUG = cds.debug('swagger') | ||
|
||
DEBUG?.('Programmatic registration') | ||
const { registered } = require ('./lib/etc') | ||
cds[registered] = true | ||
|
||
/** | ||
* Creates express middleware to serve CDS services in the Swagger UI | ||
* | ||
* @param {CdsSwaggerOptions} options - options for cds-swagger-ui itself | ||
* @param {swaggerUi.SwaggerUiOptions} swaggerUiOptions - options passed to swagger-ui-express | ||
* @returns {express.RequestHandler} - an express middleware | ||
*/ | ||
module.exports = (options={}, swaggerUiOptions={}) => { | ||
options = Object.assign({ basePath: '/$api-docs', apiPath: '' }, options) | ||
const router = express.Router() | ||
module.exports = require('./lib/middleware') | ||
|
||
cds.on('serving', service => { | ||
const apiPath = options.basePath + service.path | ||
const mount = apiPath.replace('$', '[\\$]') | ||
LOG._debug && LOG.debug('serving Swagger UI for ', { service: service.name, at: apiPath }) | ||
|
||
const uiOptions = Object.assign({ customSiteTitle: `${service.name} - Swagger UI` }, swaggerUiOptions) | ||
router.use(mount, (req, _, next) => { | ||
req.swaggerDoc = toOpenApiDoc(service, options) | ||
next() | ||
}, swaggerUi.serveFiles(), swaggerUi.setup(null, uiOptions)) | ||
|
||
addLinkToIndexHtml(service, apiPath) | ||
}) | ||
return router | ||
} | ||
|
||
const cache = {} | ||
function toOpenApiDoc (service, options = {}) { | ||
if (!cache[service.name]) { | ||
cache[service.name] = cds.compile.to.openapi(service.model, { | ||
service: service.name, | ||
'odata-version': options.odataVersion, | ||
'openapi:url': join('/', options.apiPath, service.path), | ||
'openapi:diagram': ('diagram' in options ? options.diagram : true), | ||
to: 'openapi' // workaround needed for cds-dk 7.4 | ||
}) | ||
} | ||
return cache[service.name] | ||
} | ||
|
||
function addLinkToIndexHtml (service, apiPath) { | ||
const provider = (entity) => { | ||
if (entity) return // avoid link on entity level, looks too messy | ||
return { href: apiPath, name: 'Open API Preview', title: 'Show in Swagger UI' } | ||
} | ||
service.$linkProviders ? service.$linkProviders.push(provider) : service.$linkProviders = [provider] | ||
} | ||
|
||
/** | ||
* @typedef {Object} CdsSwaggerOptions | ||
* @property {string} basePath - the root path to mount the middleware on | ||
* @property {string} apiPath - the root path for the services (useful if behind a reverse proxy) | ||
* @property {boolean} diagram - whether to render the YUML diagram | ||
* @property {string} odataVersion - the OData version used to compile the OpenAPI specs. Defaults to 4.01 | ||
*/ | ||
// important: this file must not be loaded when running as plugin ('auto' registration mode) |
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 @@ | ||
module.exports.registered = Symbol.for('swaggerui_registered') |
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,82 @@ | ||
const cds = requireCdsOpenAPI() | ||
const LOG = cds.log('swagger') | ||
|
||
const swaggerUi = require('swagger-ui-express') | ||
const express = require('express') | ||
const { join } = require('path') | ||
|
||
/** | ||
* Creates express middleware to serve CDS services in the Swagger UI | ||
* | ||
* @param {CdsSwaggerOptions} options - options for cds-swagger-ui itself | ||
* @param {swaggerUi.SwaggerUiOptions} swaggerUiOptions - options passed to swagger-ui-express | ||
* @returns {express.RequestHandler} - an express middleware | ||
*/ | ||
module.exports = (options={}, swaggerUiOptions={}) => { | ||
options = Object.assign({ basePath: '/$api-docs', apiPath: '' }, options) | ||
|
||
const router = express.Router() | ||
|
||
cds.on('serving', service => { | ||
const apiPath = options.basePath + service.path | ||
const mount = apiPath.replace('$', '[\\$]') | ||
LOG._debug && LOG.debug('serving Swagger UI for', { service: service.name, at: apiPath }) | ||
|
||
const uiOptions = Object.assign({ customSiteTitle: `${service.name} - Swagger UI` }, swaggerUiOptions) | ||
router.use(mount, (req, _, next) => { | ||
req.swaggerDoc = toOpenApiDoc(service, options) | ||
next() | ||
}, swaggerUi.serveFiles(), swaggerUi.setup(null, uiOptions)) | ||
|
||
addLinkToIndexHtml(service, apiPath) | ||
}) | ||
return router | ||
} | ||
|
||
const cache = {} | ||
function toOpenApiDoc (service, options = {}) { | ||
if (!cache[service.name]) { | ||
cache[service.name] = cds.compile.to.openapi(service.model, { | ||
service: service.name, | ||
'odata-version': options.odataVersion, | ||
'openapi:url': join('/', options.apiPath, service.path), | ||
'openapi:diagram': ('diagram' in options ? options.diagram : true), | ||
to: 'openapi' // workaround needed for cds-dk 7.4 | ||
}) | ||
} | ||
return cache[service.name] | ||
} | ||
|
||
function addLinkToIndexHtml (service, apiPath) { | ||
const provider = (entity) => { | ||
if (entity) return // avoid link on entity level, looks too messy | ||
return { href: apiPath, name: 'Open API Preview', title: 'Show in Swagger UI' } | ||
} | ||
service.$linkProviders ? service.$linkProviders.push(provider) : service.$linkProviders = [provider] | ||
} | ||
|
||
/** | ||
* Loads the compile.to.openapi function from @sap/cds-dk or throws an error if not installed. | ||
* Will get simpler in the future when we have a dedicated openapi plugin. | ||
* | ||
* @returns { import('@sap/cds-dk') } | ||
*/ | ||
function requireCdsOpenAPI () { | ||
try { | ||
return require('@sap/cds-dk') | ||
} catch (err) { | ||
const cds = require('@sap/cds') | ||
if (!cds.compile.to.openapi) { | ||
throw new Error(`'@sap/cds-dk' is not installed. Add it as a (dev) dependency to use this plugin`, { cause: err }) | ||
} | ||
return cds | ||
} | ||
} | ||
|
||
/** | ||
* @typedef {Object} CdsSwaggerOptions | ||
* @property {string} basePath - the root path to mount the middleware on | ||
* @property {string} apiPath - the root path for the services (useful if behind a reverse proxy) | ||
* @property {boolean} diagram - whether to render the YUML diagram | ||
* @property {string} odataVersion - the OData version used to compile the OpenAPI specs. Defaults to 4.01 | ||
*/ |
Oops, something went wrong.