Skip to content
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

feat: add cds logger #93

Merged
merged 9 commits into from
Nov 15, 2024
3 changes: 2 additions & 1 deletion __tests__/bookshop/.cdsrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
"description": "this is my custom description",
"policyLevel": "sap:core:v1",
"customOrdContentFile": "./ord/custom.ord.json"
}
},
"DEBUG": false
}
25 changes: 12 additions & 13 deletions __tests__/unittest/extendOrdWithCustom.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand All @@ -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');
Expand Down Expand Up @@ -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}`);
}
15 changes: 15 additions & 0 deletions __tests__/unittest/templates.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
31 changes: 14 additions & 17 deletions lib/extendOrdWithCustom.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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) {
Expand All @@ -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 = {
Expand Down
11 changes: 11 additions & 0 deletions lib/logger.js
Original file line number Diff line number Diff line change
@@ -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,
};
7 changes: 4 additions & 3 deletions lib/metaData.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(".");
Expand All @@ -16,23 +17,23 @@ 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;
case COMPILER_TYPES.asyncapi2:
try {
responseFile = asyncapi(csn, options);
} catch (error) {
console.error("AsyncApi error:", error.message);
Logger.error('AsyncApi error:', error.message);
throw error;
}
break;
case COMPILER_TYPES.edmx:
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;
}
}
Expand Down
8 changes: 4 additions & 4 deletions lib/ord.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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) {
Expand Down Expand Up @@ -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}.<appName>.<service>`
);
console.error("Namespace error:", error.message);
Logger.error('Namespace error:', error.message);
throw error;
}
}
Expand Down Expand Up @@ -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;
};
10 changes: 5 additions & 5 deletions lib/plugin.js
Original file line number Diff line number Diff line change
@@ -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) => {
Expand All @@ -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);
}
}
Expand All @@ -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);
}
});
Expand Down
7 changes: 4 additions & 3 deletions lib/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const {
RESOURCE_VISIBILITY,
SHORT_DESCRIPTION_PREFIX
} = require("./constants");
const { Logger } = require("./logger");

function unflatten(flattedObject) {
let result = {}
Expand Down Expand Up @@ -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) });
Expand Down Expand Up @@ -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
}

Expand Down