diff --git a/__tests__/bookshop/.cdsrc.json b/__tests__/bookshop/.cdsrc.json index 765bae6..759a00c 100644 --- a/__tests__/bookshop/.cdsrc.json +++ b/__tests__/bookshop/.cdsrc.json @@ -5,5 +5,6 @@ "description": "this is my custom description", "policyLevel": "sap:core:v1", "customOrdContentFile": "./ord/custom.ord.json" - } + }, + "DEBUG": false } \ No newline at end of file diff --git a/__tests__/unittest/extendOrdWithCustom.test.js b/__tests__/unittest/extendOrdWithCustom.test.js index 94d6dfd..4052a98 100644 --- a/__tests__/unittest/extendOrdWithCustom.test.js +++ b/__tests__/unittest/extendOrdWithCustom.test.js @@ -2,23 +2,20 @@ const cds = require("@sap/cds"); const path = require('path'); const { extendCustomORDContentIfExists } = require('../../lib/extendOrdWithCustom'); -jest.mock('path', () => ({ - join: jest.fn(), -})); - jest.mock("@sap/cds", () => { const actualCds = jest.requireActual('@sap/cds'); return { ...actualCds, env: {}, log: jest.fn(() => ({ + levels: { DEBUG: 0, WARN: 1 }, warn: jest.fn(() => console.warn('Mocked warning')), + error: jest.fn(() => console.error('Mocked error')), })), }; }); describe('extendOrdWithCustom', () => { - let appConfig = {}; beforeEach(() => { @@ -29,24 +26,26 @@ describe('extendOrdWithCustom', () => { }; }); - afterEach(() => { - jest.resetModules(); - jest.clearAllMocks(); - }); - describe('extendCustomORDContentIfExists', () => { - it('should skip if there is no custom ord file', () => { + it('should skip if there is no customOrdContentFile property in the .cdsrc.json', () => { const ordContent = {}; appConfig.env.customOrdContentFile = undefined; const result = extendCustomORDContentIfExists(appConfig, ordContent); expect(result).toEqual(ordContent); }); + it('should skip if customOrdContentFile property in the .cdsrc.json points to NON-EXISTING custom ord file', () => { + const ordContent = {}; + appConfig.env.customOrdContentFile = "./ord/NotExistingCustom.ord.json"; + const result = extendCustomORDContentIfExists(appConfig, ordContent); + expect(result).toEqual(ordContent); + }); + it('should ignore and log warn if found ord top-level primitive property in customOrdFile', () => { const ordContent = {}; const warningSpy = jest.spyOn(console, 'warn'); prepareTestEnvironment({ namespace: "sap.sample" }, appConfig, 'testCustomORDContentFileThrowErrors.json'); - const result = extendCustomORDContentIfExists(appConfig, ordContent, cds.log()); + const result = extendCustomORDContentIfExists(appConfig, ordContent); expect(warningSpy).toHaveBeenCalledTimes(3); expect(warningSpy).toHaveBeenCalledWith('Mocked warning'); @@ -120,5 +119,5 @@ describe('extendOrdWithCustom', () => { function prepareTestEnvironment(ordEnvVariables, appConfig, testFileName) { cds.env["ord"] = ordEnvVariables; appConfig.env.customOrdContentFile = testFileName; - path.join.mockReturnValue(`${__dirname}/utils/${testFileName}`); + jest.spyOn(path, 'join').mockReturnValueOnce(`${__dirname}/utils/${testFileName}`); } diff --git a/__tests__/unittest/templates.test.js b/__tests__/unittest/templates.test.js index 3190f81..db86572 100644 --- a/__tests__/unittest/templates.test.js +++ b/__tests__/unittest/templates.test.js @@ -43,6 +43,21 @@ describe('templates', () => { }; expect(createGroupsTemplateForService(testServiceName, linkedModel, appConfig)).toEqual(testResult); }); + + it('should return default value with a proper Service title when "Service" keyword is missing', () => { + const testServiceName = 'testServName'; + const testResult = { + groupId: 'sap.cds:service:customer.testNamespace:testServName', + groupTypeId: 'sap.cds:service', + title: 'testServName Service' + }; + expect(createGroupsTemplateForService(testServiceName, linkedModel, appConfig)).toEqual(testResult); + }); + + it('should return undefined when no service definition', () => { + const testServiceName = 'testServiceName'; + expect(createGroupsTemplateForService(testServiceName, null, appConfig)).not.toBeDefined(); + }); }); describe('createAPIResourceTemplate', () => { diff --git a/lib/extendOrdWithCustom.js b/lib/extendOrdWithCustom.js index bbdd16c..3d07528 100644 --- a/lib/extendOrdWithCustom.js +++ b/lib/extendOrdWithCustom.js @@ -1,9 +1,9 @@ -const { CONTENT_MERGE_KEY } = require('./constants'); +const { CONTENT_MERGE_KEY } = require("./constants"); const cds = require("@sap/cds"); +const fs = require("fs"); +const { Logger } = require("./logger"); const path = require("path"); -const _ = require('lodash'); - - +const _ = require("lodash"); function cleanNullProperties(obj) { for (const key in obj) { @@ -29,12 +29,13 @@ function patchGeneratedOrdResources(destinationObj, sourceObj) { return cleanNullProperties(Object.values(destObj)); } -function compareAndHandleCustomORDContentWithExistingContent(ordContent, customORDContent, logger) { +function compareAndHandleCustomORDContentWithExistingContent(ordContent, customORDContent) { const clonedOrdContent = structuredClone(ordContent); for (const key in customORDContent) { const propertyType = typeof customORDContent[key]; if (propertyType !== 'object' && propertyType !== 'array') { - logger.warn('Found ord top level primitive ord property in customOrdFile:', key, ', please define it in .cdsrc.json.'); + Logger.warn('Found ord top level primitive ord property in customOrdFile:', key, '. Please define it in .cdsrc.json.'); + continue; } if (key in ordContent) { @@ -47,21 +48,17 @@ function compareAndHandleCustomORDContentWithExistingContent(ordContent, customO } function getCustomORDContent(appConfig) { - let customORDContent; - - if (appConfig.env?.customOrdContentFile) { - customORDContent = require(path.join(cds.root, appConfig.env.customOrdContentFile)); + if (!appConfig.env?.customOrdContentFile) return; + const pathToCustomORDContent = path.join(cds.root, appConfig.env?.customOrdContentFile); + if (fs.existsSync(pathToCustomORDContent)) { + Logger.error('Custom ORD content file not found at', pathToCustomORDContent); + return require(pathToCustomORDContent); } - return customORDContent; } -function extendCustomORDContentIfExists(appConfig, ordContent, logger) { +function extendCustomORDContentIfExists(appConfig, ordContent) { const customORDContent = getCustomORDContent(appConfig); - - if (customORDContent) { - ordContent = compareAndHandleCustomORDContentWithExistingContent(ordContent, customORDContent, logger); - } - return ordContent; + return customORDContent ? compareAndHandleCustomORDContentWithExistingContent(ordContent, customORDContent) : ordContent; } module.exports = { diff --git a/lib/logger.js b/lib/logger.js new file mode 100644 index 0000000..c94a23a --- /dev/null +++ b/lib/logger.js @@ -0,0 +1,11 @@ +const cds = require("@sap/cds"); + +const Logger = cds.log("ord-plugin", { + level: cds.env.DEBUG || process.env.DEBUG + ? cds.log.levels?.DEBUG + : cds.log.levels?.WARN, +}); + +module.exports = { + Logger, +}; diff --git a/lib/metaData.js b/lib/metaData.js index 4bee72c..650775c 100644 --- a/lib/metaData.js +++ b/lib/metaData.js @@ -2,6 +2,7 @@ const cds = require('@sap/cds/lib'); const { compile: openapi } = require('@cap-js/openapi') const { compile: asyncapi } = require('@cap-js/asyncapi'); const { COMPILER_TYPES } = require('./constants'); +const { Logger } = require('./logger'); module.exports = async (data) => { const parts = data?.split("/").pop().replace(/\.json$/, '').split("."); @@ -16,7 +17,7 @@ module.exports = async (data) => { try { responseFile = openapi(csn, options); } catch (error) { - console.error("OpenApi error:", error.message); + Logger.error('OpenApi error:', error.message); throw error; } break; @@ -24,7 +25,7 @@ module.exports = async (data) => { try { responseFile = asyncapi(csn, options); } catch (error) { - console.error("AsyncApi error:", error.message); + Logger.error('AsyncApi error:', error.message); throw error; } break; @@ -32,7 +33,7 @@ module.exports = async (data) => { try { responseFile = await cds.compile(csn).to["edmx"](options); } catch (error) { - console.error("Edmx error:", error.message); + Logger.error('Edmx error:', error.message); throw error; } } diff --git a/lib/ord.js b/lib/ord.js index 760c63b..c8132bb 100644 --- a/lib/ord.js +++ b/lib/ord.js @@ -7,10 +7,10 @@ const { } = require('./templates'); const { extendCustomORDContentIfExists } = require('./extendOrdWithCustom'); const { getRFC3339Date } = require('./date'); +const { Logger } = require('./logger'); const _ = require("lodash"); const cds = require("@sap/cds"); const defaults = require("./defaults"); -const logger = cds.log('ord-plugin'); const path = require("path"); const initializeAppConfig = (csn) => { @@ -34,7 +34,7 @@ const initializeAppConfig = (csn) => { const eventApplicationNamespace = cds.env?.export?.asyncapi?.applicationNamespace; if (eventApplicationNamespace && ordNamespace !== eventApplicationNamespace) { - console.warn("ORD and AsyncAPI namespaces should be the same."); + Logger.warn('ORD and AsyncAPI namespaces should be the same.'); } for (const key of modelKeys) { @@ -123,7 +123,7 @@ function validateNamespace(appConfig) { let error = new Error( `Namespace is not defined in cdsrc.json or it is not in the format of ${appConfig.eventApplicationNamespace}..` ); - console.error("Namespace error:", error.message); + Logger.error('Namespace error:', error.message); throw error; } } @@ -162,7 +162,7 @@ module.exports = (csn) => { const packageIds = extractPackageIds(ordDocument); ordDocument.apiResources = _getAPIResources(linkedCsn, appConfig, packageIds); ordDocument.eventResources = _getEventResources(linkedCsn, appConfig, packageIds); - ordDocument = extendCustomORDContentIfExists(appConfig, ordDocument, logger); + ordDocument = extendCustomORDContentIfExists(appConfig, ordDocument); return ordDocument; }; diff --git a/lib/plugin.js b/lib/plugin.js index 4dd6fcf..b2028e4 100644 --- a/lib/plugin.js +++ b/lib/plugin.js @@ -1,5 +1,7 @@ -const { ord, getMetadata, defaults } = require("./"); const cds = require("@sap/cds"); +const { Logger } = require("./logger"); +const { ord, getMetadata, defaults } = require("./"); + cds.on("bootstrap", (app) => { app.use("/.well-known/open-resource-discovery", async (req, res) => { @@ -10,8 +12,7 @@ cds.on("bootstrap", (app) => { const { contentType, response } = await getMetadata(req.url); res.status(200).contentType(contentType).send(response); } catch (error) { - console.log(error); - console.log('Error while generating metadata'); + Logger.error(error, 'Error while generating metadata'); res.status(500).send(error.message); } } @@ -23,8 +24,7 @@ cds.on("bootstrap", (app) => { const data = ord(csn); return res.status(200).send(data); } catch (error) { - console.log(error); - console.log('Error while creating ORD document'); + Logger.error(error, 'Error while creating ORD document'); return res.status(500).send(error.message); } }); diff --git a/lib/templates.js b/lib/templates.js index a244152..6515cf3 100644 --- a/lib/templates.js +++ b/lib/templates.js @@ -8,6 +8,7 @@ const { RESOURCE_VISIBILITY, SHORT_DESCRIPTION_PREFIX } = require("./constants"); +const { Logger } = require("./logger"); function unflatten(flattedObject) { let result = {} @@ -46,12 +47,12 @@ const _generatePaths = (srv, srvDefinition) => { //removing instances of graphql protocol from paths for (var index = paths.length - 1; index >= 0; index--) { if (paths[index].kind === "graphql") { - console.warn("Graphql protocol is not supported."); + Logger.warn('Graphql protocol is not supported.'); paths.splice(index, 1); } } - //putting default as odata in case no supported protcol is there + //putting OData as default in case of non-supported protocol if (paths.length === 0) { srvDefinition["@odata"] = true; paths.push({ kind: "odata", path: protocols.path4(srvDefinition) }); @@ -106,7 +107,7 @@ const createGroupsTemplateForService = (serviceName, serviceDefinition, appConfi const ordExtensions = readORDExtensions(serviceDefinition); if (!serviceDefinition) { - console.warn("Unable to find service definition:", serviceName) + Logger.warn('Unable to find service definition:', serviceName) return undefined }