From f97701e21728ee2d62a5c9d9c5602402a0e5f663 Mon Sep 17 00:00:00 2001 From: Dipto-at-sap Date: Mon, 14 Oct 2024 15:05:05 +0530 Subject: [PATCH 1/2] Add --openapi:config-file option for openapi compile and test cases for the same --- CHANGELOG.md | 1 + lib/compile/index.js | 60 +++++++++++++++++---------- test/lib/compile/data/configFile.json | 13 ++++++ test/lib/compile/openapi.test.js | 29 +++++++++++++ 4 files changed, 82 insertions(+), 21 deletions(-) create mode 100644 test/lib/compile/data/configFile.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 2126d09..6b75617 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,3 +63,4 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Added - Initial release +- Introduced --openapi:config-file option to incorporate all the options for cds compile command in a JSON configuration file, inline options take precedence over those defined in the configuration file. diff --git a/lib/compile/index.js b/lib/compile/index.js index f10bd66..b7040bb 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -1,29 +1,30 @@ const csdl2openapi = require('./csdl2openapi') const cds = require('@sap/cds/lib'); +const fs = require("fs"); module.exports = function processor(csn, options = {}) { - const edmOptions = Object.assign({ - odataOpenapiHints: true, // hint to cds-compiler - edm4OpenAPI: true, // downgrades certain OData errors to warnings in cds-compiler - to: 'openapi' // hint to cds.compile.to.edm (usually set by CLI, but also do this in programmatic usages) - }, options) - - // must not be part of function* otherwise thrown errors are swallowed - const csdl = cds.compile.to.edm(csn, edmOptions); - let openApiDocs = {}; - - if (csdl[Symbol.iterator]) { // generator function means multiple services - openApiDocs = _getOpenApiForMultipleServices(csdl, csn, options) - } else { - const openApiOptions = toOpenApiOptions(csdl, csn, options) - openApiDocs = _getOpenApi(csdl, openApiOptions); - } + const edmOptions = Object.assign({ + odataOpenapiHints: true, // hint to cds-compiler + edm4OpenAPI: true, // downgrades certain OData errors to warnings in cds-compiler + to: 'openapi' // hint to cds.compile.to.edm (usually set by CLI, but also do this in programmatic usages) + }, options) + + // must not be part of function* otherwise thrown errors are swallowed + const csdl = cds.compile.to.edm(csn, edmOptions); + let openApiDocs = {}; - if(Object.keys(openApiDocs).length == 1){ - return openApiDocs[Object.keys(openApiDocs)[0]]; - } + if (csdl[Symbol.iterator]) { // generator function means multiple services + openApiDocs = _getOpenApiForMultipleServices(csdl, csn, options) + } else { + const openApiOptions = toOpenApiOptions(csdl, csn, options) + openApiDocs = _getOpenApi(csdl, openApiOptions); + } + + if (Object.keys(openApiDocs).length == 1) { + return openApiDocs[Object.keys(openApiDocs)[0]]; + } - return _iterate(openApiDocs); + return _iterate(openApiDocs); } function _getOpenApiForMultipleServices(csdl, csn, options) { @@ -88,6 +89,23 @@ function toOpenApiOptions(csdl, csn, options = {}) { } } + if (result["config-file"]) { + if (fs.existsSync(result["config-file"])) { + const fileContent = require(result["config-file"]); + Object.keys(fileContent).forEach((key) => { + if (key === "odata-version" && !result["odataVersion"]) { + result["odataVersion"] = fileContent[key]; + } else if (key === "diagram") { + result["diagram"] = !result["diagram"] && fileContent[key] === "true"; + } else if (!(key in result)) { // inline options take precedence + result[key] = JSON.stringify(fileContent[key]); + } + }); + } else { + throw new Error("Error while parsing the openapi configuration file"); + } + } + const protocols = _getProtocols(csdl, csn); if (result.url) { @@ -158,4 +176,4 @@ function _servicePath(csdl, csn, protocols) { return paths; } -} +} \ No newline at end of file diff --git a/test/lib/compile/data/configFile.json b/test/lib/compile/data/configFile.json new file mode 100644 index 0000000..d3b95f4 --- /dev/null +++ b/test/lib/compile/data/configFile.json @@ -0,0 +1,13 @@ +{ + "url": "http://foo.bar:3000", + "servers": [ + { + "url": "http://foo.bar:8080" + }, + { + "url": "http://foo.bar:8080/a/foo" + } + ], + "odata-version": "4.1", + "diagram":"true" +} \ No newline at end of file diff --git a/test/lib/compile/openapi.test.js b/test/lib/compile/openapi.test.js index 5b3ad90..4de5032 100644 --- a/test/lib/compile/openapi.test.js +++ b/test/lib/compile/openapi.test.js @@ -1,3 +1,4 @@ +const { path } = require('@sap/cds/lib/utils/cds-utils'); const toOpenApi = require('../../../lib/compile'); const cds = require('@sap/cds') @@ -238,6 +239,34 @@ describe('OpenAPI export', () => { } }); + test('options: config-file - without inline options', () => { + const csn = cds.compile.to.csn(` + service A {entity E { key ID : UUID; };};` + ); + const openapi = toOpenApi(csn, { 'openapi:config-file': path.resolve("./test/lib/compile/data/configFile.json") }); + expect(openapi.servers).toBeTruthy(); + expect(openapi).toMatchObject({ servers: [{ url: 'http://foo.bar:8080/rest/A' }, { url: "http://foo.bar:8080/a/foo/rest/A" }] }); + expect(openapi.info.description).toMatch(/yuml.*diagram/i); + expect(openapi['x-odata-version']).toMatch('4.1'); + }); + + test('options: config-file - with inline options, inline options given precedence', () => { + const csn = cds.compile.to.csn(` + service A {entity E { key ID : UUID; };};` + ); + const options = { + 'openapi:config-file': path.resolve("./test/lib/compile/data/configFile.json"), + 'openapi:url': "http://example.com:8080", + 'odata-version': '4.0', + 'openapi:diagram': "false" + } + const openapi = toOpenApi(csn, options); + expect(openapi.info.description).toMatch(/http:\/\/example.com:8080/i); + expect(openapi.info.description).not.toMatch(/yuml.*diagram/i); + expect(openapi['x-odata-version']).toMatch('4.0'); + expect(openapi).toMatchObject({ servers: [{ url: 'http://foo.bar:8080/odata/v4/A' }, { url: "http://foo.bar:8080/a/foo/odata/v4/A" }] }); + }); + test('annotations: root entity property', () => { const csn = cds.compile.to.csn(` namespace sap.odm.test; From 1cb8550d7a5ac33b6f1769ae26e3d2808f5dc987 Mon Sep 17 00:00:00 2001 From: Dipto-at-sap Date: Tue, 15 Oct 2024 11:35:59 +0530 Subject: [PATCH 2/2] Update test case--> options: config-file - with inline options, inline options given precedence --- test/lib/compile/openapi.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/lib/compile/openapi.test.js b/test/lib/compile/openapi.test.js index 106bbe2..e9b0656 100644 --- a/test/lib/compile/openapi.test.js +++ b/test/lib/compile/openapi.test.js @@ -245,13 +245,13 @@ describe('OpenAPI export', () => { const openapi = toOpenApi(csn, { 'openapi:config-file': path.resolve("./test/lib/compile/data/configFile.json") }); expect(openapi.servers).toBeTruthy(); expect(openapi).toMatchObject({ servers: [{ url: 'http://foo.bar:8080/rest/A' }, { url: "http://foo.bar:8080/a/foo/rest/A" }] }); - expect(openapi.info.description).toMatch(/yuml.*diagram/i); + expect(openapi.info.description).toMatch(/yuml.*diagram/i); expect(openapi['x-odata-version']).toMatch('4.1'); }); test('options: config-file - with inline options, inline options given precedence', () => { const csn = cds.compile.to.csn(` - service A {entity E { key ID : UUID; };};` + @title:'It is located at http://example.com:8080' service A {entity E { key ID : UUID;};};` ); const options = { 'openapi:config-file': path.resolve("./test/lib/compile/data/configFile.json"), @@ -260,7 +260,7 @@ describe('OpenAPI export', () => { 'openapi:diagram': "false" } const openapi = toOpenApi(csn, options); - expect(openapi.info.description).toMatch(/http:\/\/example.com:8080/i); + expect(openapi.info.title).toMatch(/http:\/\/example.com:8080/i) expect(openapi.info.description).not.toMatch(/yuml.*diagram/i); expect(openapi['x-odata-version']).toMatch('4.0'); expect(openapi).toMatchObject({ servers: [{ url: 'http://foo.bar:8080/odata/v4/A' }, { url: "http://foo.bar:8080/a/foo/odata/v4/A" }] });