From 8b0f6010a587bb8f742a749722d201fc64e5aec6 Mon Sep 17 00:00:00 2001 From: Lothar Bender Date: Thu, 11 Nov 2021 11:55:28 +0100 Subject: [PATCH 1/5] add custom build task contribution sample --- bookshop/package.json | 3 ++ cds-plugin-openapi/index.js | 51 +++++++++++++++++++++++++++++++++ cds-plugin-openapi/package.json | 9 ++++++ package.json | 5 ++-- 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 cds-plugin-openapi/index.js create mode 100644 cds-plugin-openapi/package.json diff --git a/bookshop/package.json b/bookshop/package.json index e7125251..b02bee72 100644 --- a/bookshop/package.json +++ b/bookshop/package.json @@ -8,6 +8,9 @@ "express": "^4.17.1", "passport": "0.4.1" }, + "devDependencies": { + "@cds/cds-plugin-openapi": "*" + }, "scripts": { "genres": "cds serve test/genres.cds", "start": "cds run", diff --git a/cds-plugin-openapi/index.js b/cds-plugin-openapi/index.js new file mode 100644 index 00000000..1ac184ed --- /dev/null +++ b/cds-plugin-openapi/index.js @@ -0,0 +1,51 @@ +/* eslint-disable require-await */ +const path = require("path") +const fs = require("fs") +const cds = require('@sap/cds'), { BuildTaskProvider, BuildTaskHandler } = cds.build +const cdsdk = require('@sap/cds-dk') +const { path4 } = cds.serve +const logger = cds.log("build") + +module.exports.activate = () => { + cds.build.registerProvider( + new (class extends BuildTaskProvider { + get id() { + return 'openapi' + } + get dependents() { + return ['node-cf', 'java-cf'] + } + applyTaskDefaults(task) { + task.src = task.src || cds.env.folders.srv.replace(/\/$/, '') + } + async lookupTasks() { + if (process.env.NODE_ENV === 'production') { + return [{ for: this.id }] + } + } + get handler() { + return class extends BuildTaskHandler { + async clean() { + fs.rm(path.join(this.task.dest, "gen/docs"), { recursive: true, force: true }, (err) => logger.error(err)) + } + + async build() { + const model = await this.model() + const { options } = this.task + + // generate openapi files for all services + await Promise.all(cds.linked(model).services.map(service => { + const openApi = cdsdk.compile.to.openapi(model, { + service: service.name, + 'openapi:url': path4(service).replace(/\.[^.]+$/, ''), + 'openapi:diagram': options.diagram + }) + + this.write(openApi).to(`gen/docs/${service.name}.openapi3.json`) + })) + } + } + } + })() + ) +} \ No newline at end of file diff --git a/cds-plugin-openapi/package.json b/cds-plugin-openapi/package.json new file mode 100644 index 00000000..b7cd003e --- /dev/null +++ b/cds-plugin-openapi/package.json @@ -0,0 +1,9 @@ +{ + "name": "@sap/cds-plugin-openapi", + "version": "1.0.0", + "description": "OpenAPI service specification build plugin", + "main": "index.js", + "dependencies": { + "@sap/cds-dk": "^4" + } +} diff --git a/package.json b/package.json index f7821d41..f21560f4 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,15 @@ "@capire/media": "./media", "@capire/orders": "./orders", "@capire/reviews": "./reviews", - "@sap/cds": "^5.1.5" + "@sap/cds": "git+https://github.tools.sap/cap/cds#add/customBuildTaskProviders" }, "devDependencies": { "cds-swagger-ui-express": "^0.2.0", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "chai-subset": "^1.6.0", - "sqlite3": "^5.0.0" + "sqlite3": "^5.0.0", + "@cds/cds-plugin-openapi": "./cds-plugin-openapi" }, "scripts": { "cleanup": "rm -rf node_modules && rm -rf */node_modules && rm -rf */*/node_modules", From 69126b94afcf4ba5cf30946ba42d711da8ba3268 Mon Sep 17 00:00:00 2001 From: Lothar Bender Date: Wed, 24 Nov 2021 09:57:25 +0100 Subject: [PATCH 2/5] lookupTasks becomes optional - change output dir --- cds-plugin-openapi/index.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/cds-plugin-openapi/index.js b/cds-plugin-openapi/index.js index 1ac184ed..f41ab193 100644 --- a/cds-plugin-openapi/index.js +++ b/cds-plugin-openapi/index.js @@ -3,7 +3,6 @@ const path = require("path") const fs = require("fs") const cds = require('@sap/cds'), { BuildTaskProvider, BuildTaskHandler } = cds.build const cdsdk = require('@sap/cds-dk') -const { path4 } = cds.serve const logger = cds.log("build") module.exports.activate = () => { @@ -18,15 +17,10 @@ module.exports.activate = () => { applyTaskDefaults(task) { task.src = task.src || cds.env.folders.srv.replace(/\/$/, '') } - async lookupTasks() { - if (process.env.NODE_ENV === 'production') { - return [{ for: this.id }] - } - } get handler() { return class extends BuildTaskHandler { async clean() { - fs.rm(path.join(this.task.dest, "gen/docs"), { recursive: true, force: true }, (err) => logger.error(err)) + fs.rm(path.join(this.task.dest, "openapi-docs"), { recursive: true, force: true }, (err) => err ? logger.error(err) : '') } async build() { @@ -37,15 +31,23 @@ module.exports.activate = () => { await Promise.all(cds.linked(model).services.map(service => { const openApi = cdsdk.compile.to.openapi(model, { service: service.name, - 'openapi:url': path4(service).replace(/\.[^.]+$/, ''), - 'openapi:diagram': options.diagram + 'openapi:diagram': String(options.diagram) === 'true' }) - - this.write(openApi).to(`gen/docs/${service.name}.openapi3.json`) + this.write(openApi).to(`openapi-docs/${service.name}.openapi3.json`) })) } } } + /** + * Additional constraints can be defined, e.g. generate openapi service specification in production builds only. + * > cds build --production + * > cds build --for node-cf --production + */ + // async lookupTasks() { + // if (process.env.NODE_ENV === 'production') { + // return [{ for: this.id }] + // } + // } })() ) } \ No newline at end of file From a7ab8b3bdae46f2647fe9ef0d2c1f5ccab71eacb Mon Sep 17 00:00:00 2001 From: Lothar Bender Date: Wed, 24 Nov 2021 17:59:43 +0100 Subject: [PATCH 3/5] optional plugin.activate() method --- cds-plugin-openapi/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cds-plugin-openapi/index.js b/cds-plugin-openapi/index.js index f41ab193..15b054ea 100644 --- a/cds-plugin-openapi/index.js +++ b/cds-plugin-openapi/index.js @@ -5,7 +5,7 @@ const cds = require('@sap/cds'), { BuildTaskProvider, BuildTaskHandler } = cds.b const cdsdk = require('@sap/cds-dk') const logger = cds.log("build") -module.exports.activate = () => { +// module.exports.activate = () => { cds.build.registerProvider( new (class extends BuildTaskProvider { get id() { @@ -50,4 +50,4 @@ module.exports.activate = () => { // } })() ) -} \ No newline at end of file +// } \ No newline at end of file From 8852c9140654cb6ff6350f89f26c9d83e7758e5a Mon Sep 17 00:00:00 2001 From: Lothar Bender Date: Wed, 24 Nov 2021 18:30:30 +0100 Subject: [PATCH 4/5] rename applyTaskDefaults --- cds-plugin-openapi/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cds-plugin-openapi/index.js b/cds-plugin-openapi/index.js index 15b054ea..5a81af5a 100644 --- a/cds-plugin-openapi/index.js +++ b/cds-plugin-openapi/index.js @@ -14,7 +14,7 @@ const logger = cds.log("build") get dependents() { return ['node-cf', 'java-cf'] } - applyTaskDefaults(task) { + applyDefaults(task) { task.src = task.src || cds.env.folders.srv.replace(/\/$/, '') } get handler() { From 9a6f4b71b342070768b0ea4289ae82d5c6127842 Mon Sep 17 00:00:00 2001 From: Lothar Bender Date: Thu, 9 Dec 2021 13:04:08 +0100 Subject: [PATCH 5/5] adopt latest custom build task changes - single BuildTaskHandler instead of BuildTaskProvider/BuildTaskHandler approach - static build task handler config moved to meta - default build task properties are now returned as name/value pairs using property config - runWith to declare dependencies across build task handlers, ensuring correct dependency graph - optional getTasks method to implement build task specific constraints --- cds-plugin-openapi/index.js | 74 +++++++++++++------------------------ 1 file changed, 25 insertions(+), 49 deletions(-) diff --git a/cds-plugin-openapi/index.js b/cds-plugin-openapi/index.js index 5a81af5a..bdb7c8bf 100644 --- a/cds-plugin-openapi/index.js +++ b/cds-plugin-openapi/index.js @@ -1,53 +1,29 @@ /* eslint-disable require-await */ -const path = require("path") -const fs = require("fs") -const cds = require('@sap/cds'), { BuildTaskProvider, BuildTaskHandler } = cds.build +const cds = require('@sap/cds'), { BuildTaskHandler } = cds.build const cdsdk = require('@sap/cds-dk') -const logger = cds.log("build") -// module.exports.activate = () => { - cds.build.registerProvider( - new (class extends BuildTaskProvider { - get id() { - return 'openapi' - } - get dependents() { - return ['node-cf', 'java-cf'] - } - applyDefaults(task) { - task.src = task.src || cds.env.folders.srv.replace(/\/$/, '') - } - get handler() { - return class extends BuildTaskHandler { - async clean() { - fs.rm(path.join(this.task.dest, "openapi-docs"), { recursive: true, force: true }, (err) => err ? logger.error(err) : '') - } +cds.build.register(class OpenApiHandler extends BuildTaskHandler { + static get meta() { + return { + id: 'openapi', + runWith: ['node-cf', 'java-cf'], + config: { src: cds.env.folders.srv.replace(/\/$/, '') } + } + } + async clean() { + return this.remove('openapi-docs') + } + async build() { + const model = await this.model() + const { options } = this.task - async build() { - const model = await this.model() - const { options } = this.task - - // generate openapi files for all services - await Promise.all(cds.linked(model).services.map(service => { - const openApi = cdsdk.compile.to.openapi(model, { - service: service.name, - 'openapi:diagram': String(options.diagram) === 'true' - }) - this.write(openApi).to(`openapi-docs/${service.name}.openapi3.json`) - })) - } - } - } - /** - * Additional constraints can be defined, e.g. generate openapi service specification in production builds only. - * > cds build --production - * > cds build --for node-cf --production - */ - // async lookupTasks() { - // if (process.env.NODE_ENV === 'production') { - // return [{ for: this.id }] - // } - // } - })() - ) -// } \ No newline at end of file + // generate openapi files for all services + await Promise.all(cds.linked(model).services.map(service => { + const openApi = cdsdk.compile.to.openapi(model, { + service: service.name, + 'openapi:diagram': String(options.diagram) === 'true' + }) + this.write(openApi).to(`openapi-docs/${service.name}.openapi3.json`) + })) + } +}) \ No newline at end of file