From 9fb1c3b5baf3be592e453d01aeb96bf570a10f6f Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Thu, 24 Oct 2024 15:45:34 +0200 Subject: [PATCH 01/15] initial commit --- lib/templates.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/templates.js b/lib/templates.js index 8e31e1d..b55ee25 100644 --- a/lib/templates.js +++ b/lib/templates.js @@ -193,7 +193,7 @@ const createEventResourceTemplate = (serviceName, eventDefinition, appConfig, pa releaseStatus: "active", partOfPackage: _getPackageID(appConfig.ordNamespace, packageIds, 'event'), partOfGroups: [_getGroupID(serviceName, defaults.groupTypeId, appConfig)], - visibility: "public", + visibility: "public", // TODO: visibility is always set to public resourceDefinitions: [ { type: "asyncapi-v2", From 07aa35a5f3a97ead0009cfd02fe5f4d6592388f8 Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Wed, 6 Nov 2024 10:12:49 +0100 Subject: [PATCH 02/15] filter non-public api resources --- lib/constants.js | 44 ++++++++++++++++++++++++------------------- lib/ord.js | 21 ++++++++++----------- lib/templates.js | 21 ++++++++++++--------- xmpl/srv/services.cds | 5 +++-- 4 files changed, 50 insertions(+), 41 deletions(-) diff --git a/lib/constants.js b/lib/constants.js index 04faf71..2d3bf1f 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,33 +1,39 @@ -const ORD_RESOURCE_TYPE = Object.freeze( - { - "service": "service", - "entity": "entity", - "event": "event", - "api": "api" - }); - -const COMPILER_TYPES = Object.freeze( - { - "oas3": "oas3", - "asyncapi2": "asyncapi2", - "edmx": "edmx" - }); +const COMPILER_TYPES = Object.freeze({ + oas3: "oas3", + asyncapi2: "asyncapi2", + edmx: "edmx", +}); const CONTENT_MERGE_KEY = "ordId"; -const ORD_EXTENSIONS_PREFIX = "@ORD.Extensions."; +const DESCRIPTION_PREFIX = "Description for "; const OPEN_RESOURCE_DISCOVERY_VERSION = "1.9"; +const ORD_EXTENSIONS_PREFIX = "@ORD.Extensions."; + +const ORD_RESOURCE_TYPE = Object.freeze({ + service: "service", + entity: "entity", + event: "event", + api: "api", +}); + +const RESOURCE_VISIBILITY = Object.freeze({ + public: "public", + internal: "internal", + private: "private", +}); + const SHORT_DESCRIPTION_PREFIX = "Short description for "; -const DESCRIPTION_PREFIX = "Description for "; module.exports = { - ORD_RESOURCE_TYPE, COMPILER_TYPES, CONTENT_MERGE_KEY, - ORD_EXTENSIONS_PREFIX, + DESCRIPTION_PREFIX, OPEN_RESOURCE_DISCOVERY_VERSION, + ORD_EXTENSIONS_PREFIX, + ORD_RESOURCE_TYPE, + RESOURCE_VISIBILITY, SHORT_DESCRIPTION_PREFIX, - DESCRIPTION_PREFIX }; diff --git a/lib/ord.js b/lib/ord.js index d4ef871..4238adc 100644 --- a/lib/ord.js +++ b/lib/ord.js @@ -1,17 +1,17 @@ -const path = require("path"); -const cds = require("@sap/cds"); -const { exists } = cds.utils; -const _ = require("lodash"); -const defaults = require("./defaults"); +const { ORD_RESOURCE_TYPE, RESOURCE_VISIBILITY } = require('./constants'); const { createAPIResourceTemplate, + createEntityTypeTemplate, createEventResourceTemplate, - createGroupsTemplateForService, - createEntityTypeTemplate + createGroupsTemplateForService } = require('./templates'); +const cds = require("@sap/cds"); +const { exists } = cds.utils; +const defaults = require("./defaults"); const { extendCustomORDContentIfExists } = require('./extendOrdWithCustom'); -const { ORD_RESOURCE_TYPE } = require('./constants'); -const { getRFC3339Date } = require('./date') +const { getRFC3339Date } = require('./date'); +const _ = require("lodash"); +const path = require("path"); const logger = cds.log('ord-plugin'); @@ -100,8 +100,7 @@ const _getPackages = (policyLevel, appConfig) => const _getAPIResources = (csn, appConfig, packageIds) => { return appConfig.serviceNames - .flatMap((serviceName) => createAPIResourceTemplate(serviceName, csn.definitions[serviceName], appConfig, packageIds) || []) - .filter((resource) => !!resource); + .flatMap((serviceName) => createAPIResourceTemplate(serviceName, csn.definitions[serviceName], appConfig, packageIds)) }; const _getEventResources = (csn, appConfig, packageIds) => { diff --git a/lib/templates.js b/lib/templates.js index a668dc7..7d96401 100644 --- a/lib/templates.js +++ b/lib/templates.js @@ -1,9 +1,12 @@ -const defaults = require("./defaults"); const cds = require("@sap/cds"); +const defaults = require("./defaults"); const _ = require("lodash"); -const { ORD_EXTENSIONS_PREFIX, - SHORT_DESCRIPTION_PREFIX, - DESCRIPTION_PREFIX } = require("./constants"); +const { + DESCRIPTION_PREFIX, + ORD_EXTENSIONS_PREFIX, + RESOURCE_VISIBILITY, + SHORT_DESCRIPTION_PREFIX +} = require("./constants"); function unflatten(flattedObject) { let result = {} @@ -158,7 +161,7 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa DESCRIPTION_PREFIX + serviceName, version: "1.0.0", lastUpdate: appConfig.lastUpdate, - visibility: "public", + visibility: RESOURCE_VISIBILITY.public, // default visibility partOfPackage: _getPackageID(appConfig.ordNamespace, packageIds, "api"), partOfGroups: [_getGroupID(serviceName, defaults.groupTypeId, appConfig)], releaseStatus: "active", @@ -173,11 +176,11 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa ...ordExtensions, }; - - apiResources.push(obj); + // visibility can also be set as ORD extension, in which case it overwrites the default visibility + if (obj.visibility === RESOURCE_VISIBILITY.public) apiResources.push(obj); }); - if (apiResources.length > 0) return apiResources; + return apiResources; }; const createEventResourceTemplate = (serviceName, eventDefinition, appConfig, packageIds) => { @@ -197,7 +200,7 @@ const createEventResourceTemplate = (serviceName, eventDefinition, appConfig, pa releaseStatus: "active", partOfPackage: _getPackageID(appConfig.ordNamespace, packageIds, 'event'), partOfGroups: [_getGroupID(serviceName, defaults.groupTypeId, appConfig)], - visibility: "public", // TODO: visibility is always set to public + visibility: "public", resourceDefinitions: [ { type: "asyncapi-v2", diff --git a/xmpl/srv/services.cds b/xmpl/srv/services.cds index 4e134c3..e1c896c 100644 --- a/xmpl/srv/services.cds +++ b/xmpl/srv/services.cds @@ -9,7 +9,7 @@ namespace sap.capire.incidents; annotate ProcessorService with @ORD.Extensions: { title : 'This is Processor Service title', shortDescription: 'short description for Processor Service', - visibility : 'public', + visibility : 'private', extensible : {supported: 'no'} }; @@ -37,7 +37,8 @@ annotate AdminService with @ORD.Extensions: { 'Retail', 'Consumer Products' ], - lineOfBusiness: ['Sales'] + lineOfBusiness: ['Sales'], + visibility : 'internal', }; annotate sap.capire.incidents.Customers with @ODM.entityName: 'Customers'; From fdb7932e4cf823c174af1c8c0e95e80a24a04016 Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Wed, 6 Nov 2024 16:57:36 +0100 Subject: [PATCH 03/15] handle visibility on events, apis and packages; fix some tests as well; also, minor refactoring done --- __tests__/__snapshots__/ordCdsrc.test.js.snap | 100 ++--------- .../__snapshots__/ordPackageJson.test.js.snap | 86 ---------- .../__snapshots__/templates.test.js.snap | 157 +++++------------- __tests__/unittest/templates.test.js | 18 +- lib/extendOrdWithCustom.js | 4 +- lib/ord.js | 21 ++- lib/templates.js | 39 +++-- xmpl/srv/services.cds | 5 +- 8 files changed, 98 insertions(+), 332 deletions(-) diff --git a/__tests__/__snapshots__/ordCdsrc.test.js.snap b/__tests__/__snapshots__/ordCdsrc.test.js.snap index dc23623..3898f66 100644 --- a/__tests__/__snapshots__/ordCdsrc.test.js.snap +++ b/__tests__/__snapshots__/ordCdsrc.test.js.snap @@ -4,55 +4,6 @@ exports[`Tests for default ORD document when .cdsrc.json is present Successfully { "$schema": "https://sap.github.io/open-resource-discovery/spec-v1/interfaces/Document.schema.json", "apiResources": [ - { - "apiProtocol": "odata-v4", - "description": "Description for AdminService", - "entityTypeMappings": [ - { - "entityTypeTargets": [ - { - "ordId": "sap.odm:entityType:BusinessPartner:v1", - }, - ], - }, - ], - "entryPoints": [ - "/odata/v4/admin", - ], - "extensible": { - "supported": "yes", - }, - "lastUpdate": "2024-11-04T14:33:25+01:00", - "ordId": "sap.test.cdsrc.sample:apiResource:AdminService:v1", - "partOfPackage": "sap.test.cdsrc.sample:package:capirebookshopordsample-api:v1", - "releaseStatus": "active", - "resourceDefinitions": [ - { - "accessStrategies": [ - { - "type": "open", - }, - ], - "mediaType": "application/json", - "type": "openapi-v3", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/AdminService.oas3.json", - }, - { - "accessStrategies": [ - { - "type": "open", - }, - ], - "mediaType": "application/xml", - "type": "edmx", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/AdminService.edmx", - }, - ], - "shortDescription": "short description for test AdminService", - "title": "This is test AdminService title", - "version": "2.0.0", - "visibility": "private", - }, { "apiProtocol": "odata-v4", "description": "Description for CatalogService", @@ -105,6 +56,21 @@ exports[`Tests for default ORD document when .cdsrc.json is present Successfully "version": "1.0.0", "visibility": "public", }, + { + "entityTypeMappings": [ + { + "entityTypeTargets": [ + { + "ordId": "sap.odm:entityType:BusinessPartner:v1", + }, + ], + }, + ], + "extensible": { + "supported": "yes", + }, + "ordId": "sap.test.cdsrc.sample:apiResource:AdminService:v1", + }, ], "consumptionBundles": [ { @@ -144,42 +110,6 @@ exports[`Tests for default ORD document when .cdsrc.json is present Successfully ], "description": "this is my custom description", "eventResources": [ - { - "description": "CAP Event resource describing events / messages.", - "entityTypeMappings": { - "entityTypeTargets": [ - { - "ordId": "sap.odm:entityType:test-from-extension:v1", - }, - ], - }, - "extensible": { - "supported": "yes", - }, - "lastUpdate": "2024-11-04T14:33:25+01:00", - "ordId": "sap.test.cdsrc.sample:eventResource:AdminService:v1", - "partOfGroups": [ - "sap.cds:service:sap.test.cdsrc.sample:AdminService", - ], - "partOfPackage": "sap.test.cdsrc.sample:package:capirebookshopordsample-event:v1", - "releaseStatus": "active", - "resourceDefinitions": [ - { - "accessStrategies": [ - { - "type": "open", - }, - ], - "mediaType": "application/json", - "type": "asyncapi-v2", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/AdminService.asyncapi2.json", - }, - ], - "shortDescription": "short description for test AdminService", - "title": "This is test AdminService title", - "version": "2.0.0", - "visibility": "private", - }, { "description": "CAP Event resource describing events / messages.", "extensible": { diff --git a/__tests__/__snapshots__/ordPackageJson.test.js.snap b/__tests__/__snapshots__/ordPackageJson.test.js.snap index 6de0fb4..182bddd 100644 --- a/__tests__/__snapshots__/ordPackageJson.test.js.snap +++ b/__tests__/__snapshots__/ordPackageJson.test.js.snap @@ -4,56 +4,6 @@ exports[`Tests for default ORD document when .cdsrc.json is not present Successf { "$schema": "https://sap.github.io/open-resource-discovery/spec-v1/interfaces/Document.schema.json", "apiResources": [ - { - "apiProtocol": "odata-v4", - "description": "Description for AdminService", - "entityTypeMappings": { - "entityTypeTargets": [ - { - "ordId": "sap.odm:entityType:test-from-extension:v1", - }, - ], - }, - "entryPoints": [ - "/odata/v4/admin", - ], - "extensible": { - "supported": "yes", - }, - "lastUpdate": "2024-11-04T14:33:25+01:00", - "ordId": "customer.capirebookshopordsample:apiResource:AdminService:v1", - "partOfGroups": [ - "sap.cds:service:customer.capirebookshopordsample:AdminService", - ], - "partOfPackage": "customer.capirebookshopordsample:package:capirebookshopordsample:v1", - "releaseStatus": "active", - "resourceDefinitions": [ - { - "accessStrategies": [ - { - "type": "open", - }, - ], - "mediaType": "application/json", - "type": "openapi-v3", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/AdminService.oas3.json", - }, - { - "accessStrategies": [ - { - "type": "open", - }, - ], - "mediaType": "application/xml", - "type": "edmx", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/AdminService.edmx", - }, - ], - "shortDescription": "short description for test AdminService", - "title": "This is test AdminService title", - "version": "2.0.0", - "visibility": "private", - }, { "apiProtocol": "odata-v4", "description": "Description for CatalogService", @@ -119,42 +69,6 @@ exports[`Tests for default ORD document when .cdsrc.json is not present Successf ], "description": "this is an application description", "eventResources": [ - { - "description": "CAP Event resource describing events / messages.", - "entityTypeMappings": { - "entityTypeTargets": [ - { - "ordId": "sap.odm:entityType:test-from-extension:v1", - }, - ], - }, - "extensible": { - "supported": "yes", - }, - "lastUpdate": "2024-11-04T14:33:25+01:00", - "ordId": "customer.capirebookshopordsample:eventResource:AdminService:v1", - "partOfGroups": [ - "sap.cds:service:customer.capirebookshopordsample:AdminService", - ], - "partOfPackage": "customer.capirebookshopordsample:package:capirebookshopordsample:v1", - "releaseStatus": "active", - "resourceDefinitions": [ - { - "accessStrategies": [ - { - "type": "open", - }, - ], - "mediaType": "application/json", - "type": "asyncapi-v2", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/AdminService.asyncapi2.json", - }, - ], - "shortDescription": "short description for test AdminService", - "title": "This is test AdminService title", - "version": "2.0.0", - "visibility": "private", - }, { "description": "CAP Event resource describing events / messages.", "extensible": { diff --git a/__tests__/unittest/__snapshots__/templates.test.js.snap b/__tests__/unittest/__snapshots__/templates.test.js.snap index f705de3..13e0a4e 100644 --- a/__tests__/unittest/__snapshots__/templates.test.js.snap +++ b/__tests__/unittest/__snapshots__/templates.test.js.snap @@ -54,91 +54,18 @@ exports[`templates createAPIResourceTemplate should create API resource template `; exports[`templates createEventResourceTemplate should create event resource template correctly 1`] = ` -{ - "description": "CAP Event resource describing events / messages.", - "extensible": { - "supported": "no", - }, - "lastUpdate": "2022-12-19T15:47:04+00:00", - "ordId": "customer.testNamespace:eventResource:MyService:v1", - "partOfGroups": [ - "sap.cds:service:customer.testNamespace:MyService", - ], - "partOfPackage": "sap.test.cdsrc.sample:package:test-event:v1", - "releaseStatus": "active", - "resourceDefinitions": [ - { - "accessStrategies": [ - { - "type": "open", - }, - ], - "mediaType": "application/json", - "type": "asyncapi-v2", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/MyService.asyncapi2.json", - }, - ], - "shortDescription": "MyService event resource", - "title": "ODM testAppName Events", - "version": "1.0.0", - "visibility": "public", -} -`; - -exports[`templates createEventResourceTemplate should create event resource template correctly with packageIds including namespace 1`] = ` -{ - "description": "CAP Event resource describing events / messages.", - "extensible": { - "supported": "no", - }, - "lastUpdate": "2022-12-19T15:47:04+00:00", - "ordId": "customer.testNamespace:eventResource:MyService:v1", - "partOfGroups": [ - "sap.cds:service:customer.testNamespace:MyService", - ], - "partOfPackage": "customer.testNamespace:package:test:v1", - "releaseStatus": "active", - "resourceDefinitions": [ - { - "accessStrategies": [ - { - "type": "open", - }, - ], - "mediaType": "application/json", - "type": "asyncapi-v2", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/MyService.asyncapi2.json", - }, - ], - "shortDescription": "MyService event resource", - "title": "ODM testAppName Events", - "version": "1.0.0", - "visibility": "public", -} -`; - -exports[`templates ordExtension should add apiResources with ord extensions correctly 1`] = ` [ { - "apiProtocol": "odata-v4", - "description": "Description for MyService", - "entityTypeMappings": [ - { - "entityTypeTargets": "sap.odm:entityType:test:v1", - }, - ], - "entryPoints": [ - "/odata/v4/my", - ], + "description": "CAP Event resource describing events / messages.", "extensible": { - "supported": "yes", + "supported": "no", }, "lastUpdate": "2022-12-19T15:47:04+00:00", - "ordId": "customer.testNamespace:apiResource:MyService:v1", + "ordId": "customer.testNamespace:eventResource:MyService:v1", "partOfGroups": [ "sap.cds:service:customer.testNamespace:MyService", ], - "partOfPackage": "sap.test.cdsrc.sample:package:test-other:v1", + "partOfPackage": "sap.test.cdsrc.sample:package:test-event:v1", "releaseStatus": "active", "resourceDefinitions": [ { @@ -148,56 +75,52 @@ exports[`templates ordExtension should add apiResources with ord extensions corr }, ], "mediaType": "application/json", - "type": "openapi-v3", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/MyService.oas3.json", + "type": "asyncapi-v2", + "url": "/.well-known/open-resource-discovery/v1/api-metadata/MyService.asyncapi2.json", }, + ], + "shortDescription": "MyService event resource", + "title": "ODM testAppName Events", + "version": "1.0.0", + "visibility": "public", + }, +] +`; + +exports[`templates createEventResourceTemplate should create event resource template correctly with packageIds including namespace 1`] = ` +[ + { + "description": "CAP Event resource describing events / messages.", + "extensible": { + "supported": "no", + }, + "lastUpdate": "2022-12-19T15:47:04+00:00", + "ordId": "customer.testNamespace:eventResource:MyService:v1", + "partOfGroups": [ + "sap.cds:service:customer.testNamespace:MyService", + ], + "partOfPackage": "customer.testNamespace:package:test:v1", + "releaseStatus": "active", + "resourceDefinitions": [ { "accessStrategies": [ { "type": "open", }, ], - "mediaType": "application/xml", - "type": "edmx", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/MyService.edmx", + "mediaType": "application/json", + "type": "asyncapi-v2", + "url": "/.well-known/open-resource-discovery/v1/api-metadata/MyService.asyncapi2.json", }, ], - "shortDescription": "short description for test MyService apiResource", - "title": "This is test MyService apiResource title", - "version": "2.0.0", - "visibility": "private", + "shortDescription": "MyService event resource", + "title": "ODM testAppName Events", + "version": "1.0.0", + "visibility": "public", }, ] `; -exports[`templates ordExtension should add events with ord extensions correctly 1`] = ` -{ - "description": "CAP Event resource describing events / messages.", - "extensible": { - "supported": "yes", - }, - "lastUpdate": "2022-12-19T15:47:04+00:00", - "ordId": "customer.testNamespace:eventResource:MyService:v1", - "partOfGroups": [ - "sap.cds:service:customer.testNamespace:MyService", - ], - "partOfPackage": "sap.test.cdsrc.sample:package:test-event:v1", - "releaseStatus": "active", - "resourceDefinitions": [ - { - "accessStrategies": [ - { - "type": "open", - }, - ], - "mediaType": "application/json", - "type": "asyncapi-v2", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/MyService.asyncapi2.json", - }, - ], - "shortDescription": "short description for test MyService event", - "title": "This is test MyService event title", - "version": "2.0.0", - "visibility": "private", -} -`; +exports[`templates ordExtension should add apiResources with ord extensions correctly 1`] = `[]`; + +exports[`templates ordExtension should add events with ord extensions correctly 1`] = `[]`; diff --git a/__tests__/unittest/templates.test.js b/__tests__/unittest/templates.test.js index 265d05f..8d973a2 100644 --- a/__tests__/unittest/templates.test.js +++ b/__tests__/unittest/templates.test.js @@ -43,9 +43,7 @@ describe('templates', () => { it('should create API resource template correctly', () => { const serviceName = 'MyService'; const srvDefinition = linkedModel - const packageIds = new Set() - packageIds.add('sap.test.cdsrc.sample:package:test-event:v1'); - packageIds.add('sap.test.cdsrc.sample:package:test-api:v1'); + const packageIds = ['sap.test.cdsrc.sample:package:test-event:v1', 'sap.test.cdsrc.sample:package:test-api:v1']; expect(templates.createAPIResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); }); }); @@ -54,17 +52,14 @@ describe('templates', () => { it('should create event resource template correctly', () => { const serviceName = 'MyService'; const srvDefinition = linkedModel - const packageIds = new Set() - packageIds.add('sap.test.cdsrc.sample:package:test-event:v1'); - packageIds.add('sap.test.cdsrc.sample:package:test-api:v1'); + const packageIds = ['sap.test.cdsrc.sample:package:test-event:v1', 'sap.test.cdsrc.sample:package:test-api:v1']; expect(templates.createEventResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); }); it('should create event resource template correctly with packageIds including namespace', () => { const serviceName = 'MyService'; const srvDefinition = linkedModel - const packageIds = new Set(); - packageIds.add('customer.testNamespace:package:test:v1'); + const packageIds = ['customer.testNamespace:package:test:v1']; expect(templates.createEventResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); }); }); @@ -90,9 +85,7 @@ describe('templates', () => { }; `); const srvDefinition = linkedModel.definitions[serviceName]; - const packageIds = new Set() - packageIds.add('sap.test.cdsrc.sample:package:test-event:v1'); - packageIds.add('sap.test.cdsrc.sample:package:test-api:v1'); + const packageIds = ['sap.test.cdsrc.sample:package:test-event:v1', 'sap.test.cdsrc.sample:package:test-api:v1']; expect(templates.createEventResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); }); @@ -123,8 +116,7 @@ describe('templates', () => { `); const srvDefinition = linkedModel.definitions[serviceName]; appConfig['odmEntity'] = 'sap.odm:entityType:test:v1' - const packageIds = new Set(); - packageIds.add('customer.testNamespace:package:test:v1'); + const packageIds = ['customer.testNamespace:package:test:v1']; expect(templates.createAPIResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); }); }); diff --git a/lib/extendOrdWithCustom.js b/lib/extendOrdWithCustom.js index 1efdc92..7223a37 100644 --- a/lib/extendOrdWithCustom.js +++ b/lib/extendOrdWithCustom.js @@ -1,4 +1,4 @@ -const path = require("path"); +const { join } = require("path"); const cds = require("@sap/cds"); const _ = require('lodash'); const { CONTENT_MERGE_KEY } = require('./constants'); @@ -47,7 +47,7 @@ function compareAndHandleCustomORDContentWithExistingContent(ordContent, customO function getCustomORDContent(appConfig) { if (appConfig.env?.customOrdContentFile) { - const customORDContent = require(path.join(cds.root, appConfig.env.customOrdContentFile)); + const customORDContent = require(join(cds.root, appConfig.env.customOrdContentFile)); return customORDContent; } return {}; diff --git a/lib/ord.js b/lib/ord.js index 4238adc..451823c 100644 --- a/lib/ord.js +++ b/lib/ord.js @@ -1,4 +1,4 @@ -const { ORD_RESOURCE_TYPE, RESOURCE_VISIBILITY } = require('./constants'); +const { ORD_RESOURCE_TYPE } = require('./constants'); const { createAPIResourceTemplate, createEntityTypeTemplate, @@ -6,20 +6,19 @@ const { createGroupsTemplateForService } = require('./templates'); const cds = require("@sap/cds"); -const { exists } = cds.utils; const defaults = require("./defaults"); const { extendCustomORDContentIfExists } = require('./extendOrdWithCustom'); const { getRFC3339Date } = require('./date'); const _ = require("lodash"); -const path = require("path"); +const { join } = require("path"); const logger = cds.log('ord-plugin'); const initializeAppConfig = (csn) => { - let packagejsonPath = path.join(cds.root, 'package.json') + let packageJsonPath = join(cds.root, 'package.json') let packageJson; - if (exists(packagejsonPath)) { - packageJson = require(packagejsonPath); + if (cds.utils.exists(packageJsonPath)) { + packageJson = require(packageJsonPath); } else { throw new Error(`package.json not found in the project root directory`); } @@ -31,8 +30,8 @@ const initializeAppConfig = (csn) => { const lastUpdate = getRFC3339Date(); let odmEntity = []; - // namespace variable value if present in cdsrc.json take it there or else from package.json - //if cdsrc.json does not have applicationNamespace, then use just the namespace + // use "namespace" from package.json as fallback in case it's not given in cdsrc.json + // if cdsrc.json does not have "applicationNamespace", use the "namespace" const vendorNamespace = "customer"; const ordNamespace = cds.env["ord"]?.namespace || `${vendorNamespace}.${packageName.replace(/[^a-zA-Z0-9]/g, "")}`; const eventApplicationNamespace = cds.env?.export?.asyncapi?.applicationNamespace; @@ -107,7 +106,7 @@ const _getEventResources = (csn, appConfig, packageIds) => { if (appConfig.events.length === 0) return []; return appConfig.serviceNames .filter((serviceName) => appConfig.events.some((eventName) => eventName.startsWith(serviceName))) - .flatMap((serviceName) => createEventResourceTemplate(serviceName, csn.definitions[serviceName], appConfig, packageIds) || []); + .flatMap((serviceName) => createEventResourceTemplate(serviceName, csn.definitions[serviceName], appConfig, packageIds)); }; function _getOpenResourceDiscovery(appConfig) { @@ -150,9 +149,9 @@ function createDefaultORDDocument(linkedCsn, appConfig) { } function extractPackageIds(ordDocument) { - const packageIds = new Set(); + const packageIds = []; if (ordDocument.packages) { - ordDocument.packages.map((pkg) => packageIds.add(pkg.ordId)); + ordDocument.packages.map((pkg) => packageIds.push(pkg.ordId)); } return packageIds; } diff --git a/lib/templates.js b/lib/templates.js index 7d96401..573b8c8 100644 --- a/lib/templates.js +++ b/lib/templates.js @@ -4,6 +4,7 @@ const _ = require("lodash"); const { DESCRIPTION_PREFIX, ORD_EXTENSIONS_PREFIX, + ORD_RESOURCE_TYPE, RESOURCE_VISIBILITY, SHORT_DESCRIPTION_PREFIX } = require("./constants"); @@ -162,7 +163,7 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa version: "1.0.0", lastUpdate: appConfig.lastUpdate, visibility: RESOURCE_VISIBILITY.public, // default visibility - partOfPackage: _getPackageID(appConfig.ordNamespace, packageIds, "api"), + partOfPackage: _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.api), partOfGroups: [_getGroupID(serviceName, defaults.groupTypeId, appConfig)], releaseStatus: "active", apiProtocol: @@ -176,31 +177,40 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa ...ordExtensions, }; - // visibility can also be set as ORD extension, in which case it overwrites the default visibility + // visibility can also be set as an ORD extension; in this case, it overwrites the default visibility if (obj.visibility === RESOURCE_VISIBILITY.public) apiResources.push(obj); }); return apiResources; }; -const createEventResourceTemplate = (serviceName, eventDefinition, appConfig, packageIds) => { - const ordExtensions = readORDExtensions(eventDefinition); - return { +/** + * This is a template function to create Event Resource object for Event Resource Array. + * There can be only one event resource per service because all events are using the same protocol, they are always Cloud Events. + * + * @param {string} serviceName The name of the service. + * @param {object} serviceDefinition The definition of the service + * @returns {Array} An single-item array of objects for the Event Resources. + */ +const createEventResourceTemplate = (serviceName, serviceDefinition, appConfig, packageIds) => { + const ordExtensions = readORDExtensions(serviceDefinition); + if (!!ordExtensions.visibility && ordExtensions.visibility !== RESOURCE_VISIBILITY.public) return []; + return [{ ordId: `${appConfig.ordNamespace}:eventResource:${serviceName}:v1`, title: - eventDefinition["@title"] ?? - eventDefinition["@Common.Label"] ?? + serviceDefinition["@title"] ?? + serviceDefinition["@Common.Label"] ?? `ODM ${appConfig.appName.replace(/[^a-zA-Z0-9]/g, "")} Events`, shortDescription: `${serviceName} event resource`, description: - eventDefinition['@description'] ?? eventDefinition['@Core.Description'] ?? + serviceDefinition['@description'] ?? serviceDefinition['@Core.Description'] ?? "CAP Event resource describing events / messages.", version: "1.0.0", lastUpdate: appConfig.lastUpdate, releaseStatus: "active", - partOfPackage: _getPackageID(appConfig.ordNamespace, packageIds, 'event'), + partOfPackage: _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.event), partOfGroups: [_getGroupID(serviceName, defaults.groupTypeId, appConfig)], - visibility: "public", + visibility: RESOURCE_VISIBILITY.public,//default value that can be overwritten by ORD extension resourceDefinitions: [ { type: "asyncapi-v2", @@ -216,14 +226,13 @@ const createEventResourceTemplate = (serviceName, eventDefinition, appConfig, pa extensible: { supported: "no" }, ...ordExtensions - }; + }] }; function _getPackageID(namespace, packageIds, resourceType) { - //TODO: without this check, it will be failed in ordPackageJson.test.js, need to check why it is failing - if (!(packageIds instanceof Set)) return; - const packageIdsArray = Array.from(packageIds); - return packageIdsArray.find((pkg) => pkg.includes("-" + resourceType)) || packageIdsArray.find((pkg) => pkg.includes(namespace)); + if (!packageIds) return; + + return packageIds.find((id) => id.includes("-" + resourceType)) || packageIds.find((id) => id.includes(namespace)); } module.exports = { diff --git a/xmpl/srv/services.cds b/xmpl/srv/services.cds index e1c896c..4e134c3 100644 --- a/xmpl/srv/services.cds +++ b/xmpl/srv/services.cds @@ -9,7 +9,7 @@ namespace sap.capire.incidents; annotate ProcessorService with @ORD.Extensions: { title : 'This is Processor Service title', shortDescription: 'short description for Processor Service', - visibility : 'private', + visibility : 'public', extensible : {supported: 'no'} }; @@ -37,8 +37,7 @@ annotate AdminService with @ORD.Extensions: { 'Retail', 'Consumer Products' ], - lineOfBusiness: ['Sales'], - visibility : 'internal', + lineOfBusiness: ['Sales'] }; annotate sap.capire.incidents.Customers with @ODM.entityName: 'Customers'; From 6d378e59eb228d2f551f73220ab1c1432ed6347a Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Thu, 7 Nov 2024 11:47:37 +0100 Subject: [PATCH 04/15] removal of logger propagation --- __tests__/unittest/extendOrdWithCustom.test.js | 17 ++++++++++------- lib/extendOrdWithCustom.js | 12 ++++++------ lib/ord.js | 4 +--- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/__tests__/unittest/extendOrdWithCustom.test.js b/__tests__/unittest/extendOrdWithCustom.test.js index 2a55b06..d293f4e 100644 --- a/__tests__/unittest/extendOrdWithCustom.test.js +++ b/__tests__/unittest/extendOrdWithCustom.test.js @@ -1,3 +1,4 @@ +const cds = require("@sap/cds"); const path = require('path'); const { extendCustomORDContentIfExists } = require('../../lib/extendOrdWithCustom'); @@ -11,19 +12,16 @@ jest.mock("@sap/cds", () => { ...actualCds, env: {}, log: jest.fn(() => ({ - warn: jest.fn(), + warn: jest.fn(() => console.warn('Mocked warning')), })), }; }); -const cds = require("@sap/cds"); - describe('extendOrdWithCustom', () => { + let appConfig = {}; - let logger; beforeEach(() => { - logger = { warn: jest.fn() }; appConfig = { env: { customOrdContentFile: 'customOrdContentFile.json', @@ -33,6 +31,7 @@ describe('extendOrdWithCustom', () => { afterEach(() => { jest.resetModules(); + jest.clearAllMocks(); }); describe('extendCustomORDContentIfExists', () => { @@ -45,9 +44,13 @@ describe('extendOrdWithCustom', () => { 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, logger); - expect(logger.warn).toHaveBeenCalledTimes(3); + const result = extendCustomORDContentIfExists(appConfig, ordContent); + + expect(warningSpy).toHaveBeenCalledTimes(3); + expect(warningSpy).toHaveBeenCalledWith('Mocked warning'); + expect(result).toMatchSnapshot(); }); diff --git a/lib/extendOrdWithCustom.js b/lib/extendOrdWithCustom.js index 7223a37..969028e 100644 --- a/lib/extendOrdWithCustom.js +++ b/lib/extendOrdWithCustom.js @@ -1,5 +1,5 @@ +const { root, log } = require("@sap/cds"); const { join } = require("path"); -const cds = require("@sap/cds"); const _ = require('lodash'); const { CONTENT_MERGE_KEY } = require('./constants'); @@ -28,12 +28,12 @@ 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.'); + log('ord-plugin').warn('Found ord top level primitive ord property in customOrdFile:', key, ', please define it in .cdsrc.json.'); continue; } if (key in ordContent) { @@ -47,17 +47,17 @@ function compareAndHandleCustomORDContentWithExistingContent(ordContent, customO function getCustomORDContent(appConfig) { if (appConfig.env?.customOrdContentFile) { - const customORDContent = require(join(cds.root, appConfig.env.customOrdContentFile)); + const customORDContent = require(join(root, appConfig.env.customOrdContentFile)); return customORDContent; } return {}; } -function extendCustomORDContentIfExists(appConfig, ordContent, logger) { +function extendCustomORDContentIfExists(appConfig, ordContent) { const customORDContent = getCustomORDContent(appConfig); if (customORDContent) { - ordContent = compareAndHandleCustomORDContentWithExistingContent(ordContent, customORDContent, logger); + ordContent = compareAndHandleCustomORDContentWithExistingContent(ordContent, customORDContent); } return ordContent; } diff --git a/lib/ord.js b/lib/ord.js index 451823c..eb6e24b 100644 --- a/lib/ord.js +++ b/lib/ord.js @@ -12,8 +12,6 @@ const { getRFC3339Date } = require('./date'); const _ = require("lodash"); const { join } = require("path"); -const logger = cds.log('ord-plugin'); - const initializeAppConfig = (csn) => { let packageJsonPath = join(cds.root, 'package.json') let packageJson; @@ -170,7 +168,7 @@ module.exports = (csn) => { ordDocument.eventResources = eventResources; } - ordDocument = extendCustomORDContentIfExists(appConfig, ordDocument, logger); + ordDocument = extendCustomORDContentIfExists(appConfig, ordDocument); return ordDocument; }; From 128e49423f5f518dc93595de575f632265d3717f Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Thu, 7 Nov 2024 11:57:06 +0100 Subject: [PATCH 05/15] private and internal resource are not considered --- __tests__/ordCdsrc.test.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/__tests__/ordCdsrc.test.js b/__tests__/ordCdsrc.test.js index 53b8f80..6895cbb 100644 --- a/__tests__/ordCdsrc.test.js +++ b/__tests__/ordCdsrc.test.js @@ -10,7 +10,7 @@ jest.mock("@sap/cds", () => { }); jest.mock("../lib/date", () => ({ - getRFC3339Date: jest.fn(() => "2024-11-04T14:33:25+01:00") + getRFC3339Date: jest.fn(() => "2024-11-04T14:33:25+01:00"), })); const cds = require("@sap/cds"); @@ -29,7 +29,8 @@ describe("Tests for default ORD document when .cdsrc.json is present", () => { describe("apiResources", () => { // eslint-disable-next-line no-useless-escape - const PACKAGE_ID_REGEX = /^([a-z0-9]+(?:[.][a-z0-9]+)*):(package):([a-zA-Z0-9._\-]+):(v0|v[1-9][0-9]*)$/; + const PACKAGE_ID_REGEX = + /^([a-z0-9]+(?:[.][a-z0-9]+)*):(package):([a-zA-Z0-9._\-]+):(v0|v[1-9][0-9]*)$/; let document; @@ -39,12 +40,16 @@ describe("Tests for default ORD document when .cdsrc.json is present", () => { test("PartOfPackage values are valid ORD IDs ", () => { for (const apiResource of document.apiResources) { + if (!apiResource.partOfPackage) continue; + expect(apiResource.partOfPackage).toMatch(PACKAGE_ID_REGEX); } }); test("The partOfPackage references an existing package", () => { for (const apiResource of document.apiResources) { + if (!apiResource.partOfPackage) continue; + expect( document.packages.find( (pck) => pck.ordId === apiResource.partOfPackage @@ -56,7 +61,8 @@ describe("Tests for default ORD document when .cdsrc.json is present", () => { describe("eventResources", () => { // eslint-disable-next-line no-useless-escape - const GROUP_ID_REGEX = /^([a-z0-9-]+(?:[.][a-z0-9-]+)*):([a-zA-Z0-9._\-/]+):([a-z0-9-]+(?:[.][a-z0-9-]+)*):(?[a-zA-Z0-9._\-/]+)$/; + const GROUP_ID_REGEX = + /^([a-z0-9-]+(?:[.][a-z0-9-]+)*):([a-zA-Z0-9._\-/]+):([a-z0-9-]+(?:[.][a-z0-9-]+)*):(?[a-zA-Z0-9._\-/]+)$/; let document; From b0bd69e2498b1090f2b766cc69cd350a77ce8adc Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Thu, 7 Nov 2024 11:59:01 +0100 Subject: [PATCH 06/15] minor change --- __tests__/ordCdsrc.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/__tests__/ordCdsrc.test.js b/__tests__/ordCdsrc.test.js index 6895cbb..ab21a3d 100644 --- a/__tests__/ordCdsrc.test.js +++ b/__tests__/ordCdsrc.test.js @@ -29,8 +29,7 @@ describe("Tests for default ORD document when .cdsrc.json is present", () => { describe("apiResources", () => { // eslint-disable-next-line no-useless-escape - const PACKAGE_ID_REGEX = - /^([a-z0-9]+(?:[.][a-z0-9]+)*):(package):([a-zA-Z0-9._\-]+):(v0|v[1-9][0-9]*)$/; + const PACKAGE_ID_REGEX = /^([a-z0-9]+(?:[.][a-z0-9]+)*):(package):([a-zA-Z0-9._\-]+):(v0|v[1-9][0-9]*)$/; let document; From c99a4b3fb1a44f78eee22cd9486bb069847f93b2 Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Thu, 7 Nov 2024 12:04:29 +0100 Subject: [PATCH 07/15] minor change --- __tests__/ordCdsrc.test.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/__tests__/ordCdsrc.test.js b/__tests__/ordCdsrc.test.js index ab21a3d..ab1b89d 100644 --- a/__tests__/ordCdsrc.test.js +++ b/__tests__/ordCdsrc.test.js @@ -10,7 +10,7 @@ jest.mock("@sap/cds", () => { }); jest.mock("../lib/date", () => ({ - getRFC3339Date: jest.fn(() => "2024-11-04T14:33:25+01:00"), + getRFC3339Date: jest.fn(() => "2024-11-04T14:33:25+01:00") })); const cds = require("@sap/cds"); @@ -60,8 +60,7 @@ describe("Tests for default ORD document when .cdsrc.json is present", () => { describe("eventResources", () => { // eslint-disable-next-line no-useless-escape - const GROUP_ID_REGEX = - /^([a-z0-9-]+(?:[.][a-z0-9-]+)*):([a-zA-Z0-9._\-/]+):([a-z0-9-]+(?:[.][a-z0-9-]+)*):(?[a-zA-Z0-9._\-/]+)$/; + const GROUP_ID_REGEX = /^([a-z0-9-]+(?:[.][a-z0-9-]+)*):([a-zA-Z0-9._\-/]+):([a-z0-9-]+(?:[.][a-z0-9-]+)*):(?[a-zA-Z0-9._\-/]+)$/; let document; From 4f4150c16b8039379cc5bb55838ad297e25c66c6 Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Fri, 8 Nov 2024 10:45:49 +0100 Subject: [PATCH 08/15] add unit tests for private and internal resources --- .../__snapshots__/templates.test.js.snap | 95 +++++++++- __tests__/unittest/templates.test.js | 168 ++++++++++++++++-- 2 files changed, 246 insertions(+), 17 deletions(-) diff --git a/__tests__/unittest/__snapshots__/templates.test.js.snap b/__tests__/unittest/__snapshots__/templates.test.js.snap index 13e0a4e..499a01c 100644 --- a/__tests__/unittest/__snapshots__/templates.test.js.snap +++ b/__tests__/unittest/__snapshots__/templates.test.js.snap @@ -121,6 +121,97 @@ exports[`templates createEventResourceTemplate should create event resource temp ] `; -exports[`templates ordExtension should add apiResources with ord extensions correctly 1`] = `[]`; +exports[`templates ordExtension should add apiResources with ORD Extension "visibility=public" 1`] = ` +[ + { + "apiProtocol": "odata-v4", + "description": "Description for MyService", + "entityTypeMappings": [ + { + "entityTypeTargets": "sap.odm:entityType:test:v1", + }, + ], + "entryPoints": [ + "/odata/v4/my", + ], + "extensible": { + "supported": "yes", + }, + "lastUpdate": "2022-12-19T15:47:04+00:00", + "ordId": "customer.testNamespace:apiResource:MyService:v1", + "partOfGroups": [ + "sap.cds:service:customer.testNamespace:MyService", + ], + "partOfPackage": "sap.test.cdsrc.sample:package:test-other:v1", + "releaseStatus": "active", + "resourceDefinitions": [ + { + "accessStrategies": [ + { + "type": "open", + }, + ], + "mediaType": "application/json", + "type": "openapi-v3", + "url": "/.well-known/open-resource-discovery/v1/api-metadata/MyService.oas3.json", + }, + { + "accessStrategies": [ + { + "type": "open", + }, + ], + "mediaType": "application/xml", + "type": "edmx", + "url": "/.well-known/open-resource-discovery/v1/api-metadata/MyService.edmx", + }, + ], + "shortDescription": "short description for test MyService apiResource", + "title": "This is test MyService apiResource title", + "version": "2.0.0", + "visibility": "public", + }, +] +`; + +exports[`templates ordExtension should add events with ORD Extension "visibility=public" 1`] = ` +[ + { + "description": "CAP Event resource describing events / messages.", + "extensible": { + "supported": "yes", + }, + "lastUpdate": "2022-12-19T15:47:04+00:00", + "ordId": "customer.testNamespace:eventResource:MyService:v1", + "partOfGroups": [ + "sap.cds:service:customer.testNamespace:MyService", + ], + "partOfPackage": "sap.test.cdsrc.sample:package:test-event:v1", + "releaseStatus": "active", + "resourceDefinitions": [ + { + "accessStrategies": [ + { + "type": "open", + }, + ], + "mediaType": "application/json", + "type": "asyncapi-v2", + "url": "/.well-known/open-resource-discovery/v1/api-metadata/MyService.asyncapi2.json", + }, + ], + "shortDescription": "short description for test MyService event", + "title": "This is test MyService event title", + "version": "2.0.0", + "visibility": "public", + }, +] +`; + +exports[`templates ordExtension should not add apiResources with ORD Extension "visibility=internal" 1`] = `[]`; + +exports[`templates ordExtension should not add apiResources with ORD Extension "visibility=private" 1`] = `[]`; + +exports[`templates ordExtension should not add events with ORD Extension "visibility=internal" 1`] = `[]`; -exports[`templates ordExtension should add events with ord extensions correctly 1`] = `[]`; +exports[`templates ordExtension should not add events with ORD Extension "visibility=private" 1`] = `[]`; diff --git a/__tests__/unittest/templates.test.js b/__tests__/unittest/templates.test.js index 8d973a2..3190f81 100644 --- a/__tests__/unittest/templates.test.js +++ b/__tests__/unittest/templates.test.js @@ -1,15 +1,21 @@ const cds = require('@sap/cds'); -const templates = require('../../lib/templates'); +const { + createEntityTypeTemplate, + createGroupsTemplateForService, + createAPIResourceTemplate, + createEventResourceTemplate +} = require('../../lib/templates'); describe('templates', () => { let linkedModel; + const appConfig = { ordNamespace: 'customer.testNamespace', appName: 'testAppName', lastUpdate: '2022-12-19T15:47:04+00:00' }; - beforeEach(() => { + beforeAll(() => { linkedModel = cds.linked(` namespace customer.testNamespace123; entity Books { @@ -21,7 +27,7 @@ describe('templates', () => { describe('createEntityTypeTemplate', () => { it('should return default value', () => { - expect(templates.createEntityTypeTemplate(linkedModel)).toEqual({ + expect(createEntityTypeTemplate(linkedModel)).toEqual({ ordId: 'sap.odm:entityType:undefined:v1' }); }); @@ -35,7 +41,7 @@ describe('templates', () => { groupTypeId: 'sap.cds:service', title: 'test Service' }; - expect(templates.createGroupsTemplateForService(testServiceName, linkedModel, appConfig)).toEqual(testResult); + expect(createGroupsTemplateForService(testServiceName, linkedModel, appConfig)).toEqual(testResult); }); }); @@ -44,7 +50,7 @@ describe('templates', () => { const serviceName = 'MyService'; const srvDefinition = linkedModel const packageIds = ['sap.test.cdsrc.sample:package:test-event:v1', 'sap.test.cdsrc.sample:package:test-api:v1']; - expect(templates.createAPIResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); + expect(createAPIResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); }); }); @@ -53,19 +59,19 @@ describe('templates', () => { const serviceName = 'MyService'; const srvDefinition = linkedModel const packageIds = ['sap.test.cdsrc.sample:package:test-event:v1', 'sap.test.cdsrc.sample:package:test-api:v1']; - expect(templates.createEventResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); + expect(createEventResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); }); it('should create event resource template correctly with packageIds including namespace', () => { const serviceName = 'MyService'; const srvDefinition = linkedModel const packageIds = ['customer.testNamespace:package:test:v1']; - expect(templates.createEventResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); + expect(createEventResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); }); }); describe('ordExtension', () => { - it('should add events with ord extensions correctly', () => { + it('should add apiResources with ORD Extension "visibility=public"', () => { const serviceName = 'MyService'; linkedModel = cds.linked(` service MyService { @@ -74,22 +80,67 @@ describe('templates', () => { title: String; } } + @ODM.entityName: 'testOdmEntity' + entity Books { + key ID: UUID; + title: String; + } annotate MyService with @ORD.Extensions : { - title : 'This is test MyService event title', - shortDescription: 'short description for test MyService event', - visibility : 'private', + title : 'This is test MyService apiResource title', + shortDescription: 'short description for test MyService apiResource', + visibility : 'public', version : '2.0.0', + partOfPackage : 'sap.test.cdsrc.sample:package:test-other:v1', extensible : { supported : 'yes' } }; `); const srvDefinition = linkedModel.definitions[serviceName]; - const packageIds = ['sap.test.cdsrc.sample:package:test-event:v1', 'sap.test.cdsrc.sample:package:test-api:v1']; - expect(templates.createEventResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); + appConfig['odmEntity'] = 'sap.odm:entityType:test:v1' + const packageIds = ['customer.testNamespace:package:test:v1']; + const apiResourceTemplate = createAPIResourceTemplate(serviceName, srvDefinition, appConfig, packageIds); + + expect(apiResourceTemplate).toBeInstanceOf(Array); + expect(apiResourceTemplate).toMatchSnapshot(); }); - it('should add apiResources with ord extensions correctly', () => { + it('should not add apiResources with ORD Extension "visibility=internal"', () => { + const serviceName = 'MyService'; + linkedModel = cds.linked(` + service MyService { + entity Books { + key ID: UUID; + title: String; + } + } + @ODM.entityName: 'testOdmEntity' + entity Books { + key ID: UUID; + title: String; + } + annotate MyService with @ORD.Extensions : { + title : 'This is test MyService apiResource title', + shortDescription: 'short description for test MyService apiResource', + visibility : 'internal', + version : '2.0.0', + partOfPackage : 'sap.test.cdsrc.sample:package:test-other:v1', + extensible : { + supported : 'yes' + } + }; + `); + const srvDefinition = linkedModel.definitions[serviceName]; + appConfig['odmEntity'] = 'sap.odm:entityType:test:v1' + const packageIds = ['customer.testNamespace:package:test:v1']; + const apiResourceTemplate = createAPIResourceTemplate(serviceName, srvDefinition, appConfig, packageIds); + + expect(apiResourceTemplate).toBeInstanceOf(Array); + expect(apiResourceTemplate).toMatchSnapshot(); + expect(apiResourceTemplate).toEqual([]); + }); + + it('should not add apiResources with ORD Extension "visibility=private"', () => { const serviceName = 'MyService'; linkedModel = cds.linked(` service MyService { @@ -117,7 +168,94 @@ describe('templates', () => { const srvDefinition = linkedModel.definitions[serviceName]; appConfig['odmEntity'] = 'sap.odm:entityType:test:v1' const packageIds = ['customer.testNamespace:package:test:v1']; - expect(templates.createAPIResourceTemplate(serviceName, srvDefinition, appConfig, packageIds)).toMatchSnapshot(); + const apiResourceTemplate = createAPIResourceTemplate(serviceName, srvDefinition, appConfig, packageIds); + + expect(apiResourceTemplate).toBeInstanceOf(Array); + expect(apiResourceTemplate).toMatchSnapshot(); + expect(apiResourceTemplate).toEqual([]); + }); + + it('should add events with ORD Extension "visibility=public"', () => { + const serviceName = 'MyService'; + linkedModel = cds.linked(` + service MyService { + entity Books { + key ID: UUID; + title: String; + } + } + annotate MyService with @ORD.Extensions : { + title : 'This is test MyService event title', + shortDescription: 'short description for test MyService event', + visibility : 'public', + version : '2.0.0', + extensible : { + supported : 'yes' + } + }; + `); + const srvDefinition = linkedModel.definitions[serviceName]; + const packageIds = ['sap.test.cdsrc.sample:package:test-event:v1', 'sap.test.cdsrc.sample:package:test-api:v1']; + const eventResourceTemplate = createEventResourceTemplate(serviceName, srvDefinition, appConfig, packageIds); + + expect(eventResourceTemplate).toBeInstanceOf(Array); + expect(eventResourceTemplate).toMatchSnapshot(); + }); + + it('should not add events with ORD Extension "visibility=internal"', () => { + const serviceName = 'MyService'; + linkedModel = cds.linked(` + service MyService { + entity Books { + key ID: UUID; + title: String; + } + } + annotate MyService with @ORD.Extensions : { + title : 'This is test MyService event title', + shortDescription: 'short description for test MyService event', + visibility : 'internal', + version : '2.0.0', + extensible : { + supported : 'yes' + } + }; + `); + const srvDefinition = linkedModel.definitions[serviceName]; + const packageIds = ['sap.test.cdsrc.sample:package:test-event:v1', 'sap.test.cdsrc.sample:package:test-api:v1']; + const eventResourceTemplate = createEventResourceTemplate(serviceName, srvDefinition, appConfig, packageIds); + + expect(eventResourceTemplate).toBeInstanceOf(Array); + expect(eventResourceTemplate).toMatchSnapshot(); + expect(eventResourceTemplate).toEqual([]); + }); + + it('should not add events with ORD Extension "visibility=private"', () => { + const serviceName = 'MyService'; + linkedModel = cds.linked(` + service MyService { + entity Books { + key ID: UUID; + title: String; + } + } + annotate MyService with @ORD.Extensions : { + title : 'This is test MyService event title', + shortDescription: 'short description for test MyService event', + visibility : 'private', + version : '2.0.0', + extensible : { + supported : 'yes' + } + }; + `); + const srvDefinition = linkedModel.definitions[serviceName]; + const packageIds = ['sap.test.cdsrc.sample:package:test-event:v1', 'sap.test.cdsrc.sample:package:test-api:v1']; + const eventResourceTemplate = createEventResourceTemplate(serviceName, srvDefinition, appConfig, packageIds); + + expect(eventResourceTemplate).toBeInstanceOf(Array); + expect(eventResourceTemplate).toMatchSnapshot(); + expect(eventResourceTemplate).toEqual([]); }); }); }); From 67a004d53e94c9dba87b955f5aecbb1e14f7710d Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Fri, 8 Nov 2024 10:57:59 +0100 Subject: [PATCH 09/15] change the visibility of Admin and Catalog services; regenerate snapshots --- __tests__/__snapshots__/ordCdsrc.test.js.snap | 61 ++++++++----------- .../__snapshots__/ordPackageJson.test.js.snap | 59 ++++++++++-------- __tests__/bookshop/srv/admin-service.cds | 2 +- __tests__/bookshop/srv/cat-service.cds | 8 +++ 4 files changed, 66 insertions(+), 64 deletions(-) diff --git a/__tests__/__snapshots__/ordCdsrc.test.js.snap b/__tests__/__snapshots__/ordCdsrc.test.js.snap index 3898f66..3372c8c 100644 --- a/__tests__/__snapshots__/ordCdsrc.test.js.snap +++ b/__tests__/__snapshots__/ordCdsrc.test.js.snap @@ -6,27 +6,24 @@ exports[`Tests for default ORD document when .cdsrc.json is present Successfully "apiResources": [ { "apiProtocol": "odata-v4", - "description": "Description for CatalogService", + "description": "Description for AdminService", "entityTypeMappings": [ { "entityTypeTargets": [ { - "ordId": "sap.odm:entityType:odm.bookshop.Authors:v1", + "ordId": "sap.odm:entityType:BusinessPartner:v1", }, ], }, ], "entryPoints": [ - "/browse", + "/odata/v4/admin", ], "extensible": { - "supported": "no", + "supported": "yes", }, "lastUpdate": "2024-11-04T14:33:25+01:00", - "ordId": "sap.test.cdsrc.sample:apiResource:CatalogService:v1", - "partOfGroups": [ - "sap.cds:service:sap.test.cdsrc.sample:CatalogService", - ], + "ordId": "sap.test.cdsrc.sample:apiResource:AdminService:v1", "partOfPackage": "sap.test.cdsrc.sample:package:capirebookshopordsample-api:v1", "releaseStatus": "active", "resourceDefinitions": [ @@ -38,7 +35,7 @@ exports[`Tests for default ORD document when .cdsrc.json is present Successfully ], "mediaType": "application/json", "type": "openapi-v3", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/CatalogService.oas3.json", + "url": "/.well-known/open-resource-discovery/v1/api-metadata/AdminService.oas3.json", }, { "accessStrategies": [ @@ -48,29 +45,14 @@ exports[`Tests for default ORD document when .cdsrc.json is present Successfully ], "mediaType": "application/xml", "type": "edmx", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/CatalogService.edmx", + "url": "/.well-known/open-resource-discovery/v1/api-metadata/AdminService.edmx", }, ], - "shortDescription": "Short description for CatalogService", - "title": "CatalogService", - "version": "1.0.0", + "shortDescription": "short description for test AdminService", + "title": "This is test AdminService title", + "version": "2.0.0", "visibility": "public", }, - { - "entityTypeMappings": [ - { - "entityTypeTargets": [ - { - "ordId": "sap.odm:entityType:BusinessPartner:v1", - }, - ], - }, - ], - "extensible": { - "supported": "yes", - }, - "ordId": "sap.test.cdsrc.sample:apiResource:AdminService:v1", - }, ], "consumptionBundles": [ { @@ -112,13 +94,20 @@ exports[`Tests for default ORD document when .cdsrc.json is present Successfully "eventResources": [ { "description": "CAP Event resource describing events / messages.", + "entityTypeMappings": { + "entityTypeTargets": [ + { + "ordId": "sap.odm:entityType:test-from-extension:v1", + }, + ], + }, "extensible": { - "supported": "no", + "supported": "yes", }, "lastUpdate": "2024-11-04T14:33:25+01:00", - "ordId": "sap.test.cdsrc.sample:eventResource:CatalogService:v1", + "ordId": "sap.test.cdsrc.sample:eventResource:AdminService:v1", "partOfGroups": [ - "sap.cds:service:sap.test.cdsrc.sample:CatalogService", + "sap.cds:service:sap.test.cdsrc.sample:AdminService", ], "partOfPackage": "sap.test.cdsrc.sample:package:capirebookshopordsample-event:v1", "releaseStatus": "active", @@ -131,12 +120,12 @@ exports[`Tests for default ORD document when .cdsrc.json is present Successfully ], "mediaType": "application/json", "type": "asyncapi-v2", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/CatalogService.asyncapi2.json", + "url": "/.well-known/open-resource-discovery/v1/api-metadata/AdminService.asyncapi2.json", }, ], - "shortDescription": "CatalogService event resource", - "title": "ODM capirebookshopordsample Events", - "version": "1.0.0", + "shortDescription": "short description for test AdminService", + "title": "This is test AdminService title", + "version": "2.0.0", "visibility": "public", }, ], @@ -149,7 +138,7 @@ exports[`Tests for default ORD document when .cdsrc.json is present Successfully { "groupId": "sap.cds:service:sap.test.cdsrc.sample:CatalogService", "groupTypeId": "sap.cds:service", - "title": "Catalog Service", + "title": "This is test Catalog Service title", }, ], "openResourceDiscovery": "1.10", diff --git a/__tests__/__snapshots__/ordPackageJson.test.js.snap b/__tests__/__snapshots__/ordPackageJson.test.js.snap index 182bddd..5a40632 100644 --- a/__tests__/__snapshots__/ordPackageJson.test.js.snap +++ b/__tests__/__snapshots__/ordPackageJson.test.js.snap @@ -6,26 +6,24 @@ exports[`Tests for default ORD document when .cdsrc.json is not present Successf "apiResources": [ { "apiProtocol": "odata-v4", - "description": "Description for CatalogService", - "entityTypeMappings": [ - { - "entityTypeTargets": [ - { - "ordId": "sap.odm:entityType:odm.bookshop.Authors:v1", - }, - ], - }, - ], + "description": "Description for AdminService", + "entityTypeMappings": { + "entityTypeTargets": [ + { + "ordId": "sap.odm:entityType:test-from-extension:v1", + }, + ], + }, "entryPoints": [ - "/browse", + "/odata/v4/admin", ], "extensible": { - "supported": "no", + "supported": "yes", }, "lastUpdate": "2024-11-04T14:33:25+01:00", - "ordId": "customer.capirebookshopordsample:apiResource:CatalogService:v1", + "ordId": "customer.capirebookshopordsample:apiResource:AdminService:v1", "partOfGroups": [ - "sap.cds:service:customer.capirebookshopordsample:CatalogService", + "sap.cds:service:customer.capirebookshopordsample:AdminService", ], "partOfPackage": "customer.capirebookshopordsample:package:capirebookshopordsample:v1", "releaseStatus": "active", @@ -38,7 +36,7 @@ exports[`Tests for default ORD document when .cdsrc.json is not present Successf ], "mediaType": "application/json", "type": "openapi-v3", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/CatalogService.oas3.json", + "url": "/.well-known/open-resource-discovery/v1/api-metadata/AdminService.oas3.json", }, { "accessStrategies": [ @@ -48,12 +46,12 @@ exports[`Tests for default ORD document when .cdsrc.json is not present Successf ], "mediaType": "application/xml", "type": "edmx", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/CatalogService.edmx", + "url": "/.well-known/open-resource-discovery/v1/api-metadata/AdminService.edmx", }, ], - "shortDescription": "Short description for CatalogService", - "title": "CatalogService", - "version": "1.0.0", + "shortDescription": "short description for test AdminService", + "title": "This is test AdminService title", + "version": "2.0.0", "visibility": "public", }, ], @@ -71,13 +69,20 @@ exports[`Tests for default ORD document when .cdsrc.json is not present Successf "eventResources": [ { "description": "CAP Event resource describing events / messages.", + "entityTypeMappings": { + "entityTypeTargets": [ + { + "ordId": "sap.odm:entityType:test-from-extension:v1", + }, + ], + }, "extensible": { - "supported": "no", + "supported": "yes", }, "lastUpdate": "2024-11-04T14:33:25+01:00", - "ordId": "customer.capirebookshopordsample:eventResource:CatalogService:v1", + "ordId": "customer.capirebookshopordsample:eventResource:AdminService:v1", "partOfGroups": [ - "sap.cds:service:customer.capirebookshopordsample:CatalogService", + "sap.cds:service:customer.capirebookshopordsample:AdminService", ], "partOfPackage": "customer.capirebookshopordsample:package:capirebookshopordsample:v1", "releaseStatus": "active", @@ -90,12 +95,12 @@ exports[`Tests for default ORD document when .cdsrc.json is not present Successf ], "mediaType": "application/json", "type": "asyncapi-v2", - "url": "/.well-known/open-resource-discovery/v1/api-metadata/CatalogService.asyncapi2.json", + "url": "/.well-known/open-resource-discovery/v1/api-metadata/AdminService.asyncapi2.json", }, ], - "shortDescription": "CatalogService event resource", - "title": "ODM capirebookshopordsample Events", - "version": "1.0.0", + "shortDescription": "short description for test AdminService", + "title": "This is test AdminService title", + "version": "2.0.0", "visibility": "public", }, ], @@ -108,7 +113,7 @@ exports[`Tests for default ORD document when .cdsrc.json is not present Successf { "groupId": "sap.cds:service:customer.capirebookshopordsample:CatalogService", "groupTypeId": "sap.cds:service", - "title": "Catalog Service", + "title": "This is test Catalog Service title", }, ], "openResourceDiscovery": "1.9", diff --git a/__tests__/bookshop/srv/admin-service.cds b/__tests__/bookshop/srv/admin-service.cds index 4d1ff55..0af736a 100644 --- a/__tests__/bookshop/srv/admin-service.cds +++ b/__tests__/bookshop/srv/admin-service.cds @@ -25,7 +25,7 @@ service AdminService @(requires: 'authenticated-user') { annotate AdminService with @ORD.Extensions: { title : 'This is test AdminService title', shortDescription : 'short description for test AdminService', - visibility : 'private', + visibility : 'public', version : '2.0.0', extensible : {supported: 'yes'}, entityTypeMappings: {entityTypeTargets: [{ordId: 'sap.odm:entityType:test-from-extension:v1'}]}, diff --git a/__tests__/bookshop/srv/cat-service.cds b/__tests__/bookshop/srv/cat-service.cds index d8d8c0b..739ff07 100644 --- a/__tests__/bookshop/srv/cat-service.cds +++ b/__tests__/bookshop/srv/cat-service.cds @@ -30,3 +30,11 @@ service CatalogService @(path: '/browse') { title : String @title: 'Title'; } } + +annotate CatalogService with @ORD.Extensions: { + title : 'This is test Catalog Service title', + shortDescription : 'short description for test CatalogService', + visibility : 'internal', + version : '2.0.0', + extensible : {supported: 'yes'} +}; \ No newline at end of file From bfc8ae2507e6e50c8e1a73a2a6fe07f0a073dee1 Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Fri, 8 Nov 2024 11:09:52 +0100 Subject: [PATCH 10/15] add internal cinema-service --- __tests__/__snapshots__/ordCdsrc.test.js.snap | 5 +++ .../__snapshots__/ordPackageJson.test.js.snap | 5 +++ __tests__/bookshop/db/schema.cds | 8 ++++ __tests__/bookshop/srv/cat-service.cds | 2 +- __tests__/bookshop/srv/cinema-service.cds | 40 +++++++++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 __tests__/bookshop/srv/cinema-service.cds diff --git a/__tests__/__snapshots__/ordCdsrc.test.js.snap b/__tests__/__snapshots__/ordCdsrc.test.js.snap index 3372c8c..1449d55 100644 --- a/__tests__/__snapshots__/ordCdsrc.test.js.snap +++ b/__tests__/__snapshots__/ordCdsrc.test.js.snap @@ -140,6 +140,11 @@ exports[`Tests for default ORD document when .cdsrc.json is present Successfully "groupTypeId": "sap.cds:service", "title": "This is test Catalog Service title", }, + { + "groupId": "sap.cds:service:sap.test.cdsrc.sample:CinemaService", + "groupTypeId": "sap.cds:service", + "title": "This is test Cinema Service title", + }, ], "openResourceDiscovery": "1.10", "packages": [ diff --git a/__tests__/__snapshots__/ordPackageJson.test.js.snap b/__tests__/__snapshots__/ordPackageJson.test.js.snap index 5a40632..7c72444 100644 --- a/__tests__/__snapshots__/ordPackageJson.test.js.snap +++ b/__tests__/__snapshots__/ordPackageJson.test.js.snap @@ -115,6 +115,11 @@ exports[`Tests for default ORD document when .cdsrc.json is not present Successf "groupTypeId": "sap.cds:service", "title": "This is test Catalog Service title", }, + { + "groupId": "sap.cds:service:customer.capirebookshopordsample:CinemaService", + "groupTypeId": "sap.cds:service", + "title": "This is test Cinema Service title", + }, ], "openResourceDiscovery": "1.9", "packages": [ diff --git a/__tests__/bookshop/db/schema.cds b/__tests__/bookshop/db/schema.cds index ea9a431..57cd392 100644 --- a/__tests__/bookshop/db/schema.cds +++ b/__tests__/bookshop/db/schema.cds @@ -32,3 +32,11 @@ entity Genres : sap.common.CodeList { children : Composition of many Genres on children.parent = $self; } + +entity Movies : managed { + key ID : Integer; + title : localized String(111); + descr : localized String(1111); + author : Association to Authors; + genre : Association to Genres; +} diff --git a/__tests__/bookshop/srv/cat-service.cds b/__tests__/bookshop/srv/cat-service.cds index 739ff07..bd71923 100644 --- a/__tests__/bookshop/srv/cat-service.cds +++ b/__tests__/bookshop/srv/cat-service.cds @@ -34,7 +34,7 @@ service CatalogService @(path: '/browse') { annotate CatalogService with @ORD.Extensions: { title : 'This is test Catalog Service title', shortDescription : 'short description for test CatalogService', - visibility : 'internal', + visibility : 'private', version : '2.0.0', extensible : {supported: 'yes'} }; \ No newline at end of file diff --git a/__tests__/bookshop/srv/cinema-service.cds b/__tests__/bookshop/srv/cinema-service.cds new file mode 100644 index 0000000..97e9cbe --- /dev/null +++ b/__tests__/bookshop/srv/cinema-service.cds @@ -0,0 +1,40 @@ +using {sap.capire.bookshop as my} from '../db/schema'; + +service CinemaService @(path: '/browse') { + + @readonly + entity Movies as + select from my.Movies { + *, + author.name as author + } + excluding { + createdBy, + modifiedBy + }; + + @requires: 'authenticated-user' + action submitOrder(book : Movies:ID, quantity : Integer); + + event MovieCreated : { + ID : Integer; + title : String @title: 'Title'; + }; + + event MovieDeleted : { + ID : Integer; + }; + + event MovieUpdated : { + ID : Integer; + title : String @title: 'Title'; + } +} + +annotate CinemaService with @ORD.Extensions: { + title : 'This is test Cinema Service title', + shortDescription : 'short description for test CinemaService', + visibility : 'internal', + version : '1.0.0', + extensible : {supported: 'yes'} +}; \ No newline at end of file From 992b48b9c3a2cc4aab18d6ce8daf0e64150ac61f Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Fri, 8 Nov 2024 11:14:56 +0100 Subject: [PATCH 11/15] remove the check if the package is undefined --- __tests__/ordCdsrc.test.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/__tests__/ordCdsrc.test.js b/__tests__/ordCdsrc.test.js index ab1b89d..53b8f80 100644 --- a/__tests__/ordCdsrc.test.js +++ b/__tests__/ordCdsrc.test.js @@ -39,16 +39,12 @@ describe("Tests for default ORD document when .cdsrc.json is present", () => { test("PartOfPackage values are valid ORD IDs ", () => { for (const apiResource of document.apiResources) { - if (!apiResource.partOfPackage) continue; - expect(apiResource.partOfPackage).toMatch(PACKAGE_ID_REGEX); } }); test("The partOfPackage references an existing package", () => { for (const apiResource of document.apiResources) { - if (!apiResource.partOfPackage) continue; - expect( document.packages.find( (pck) => pck.ordId === apiResource.partOfPackage From 6c36856d5c7e2a14b44f389d12d64b0d3f8bce7c Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Fri, 8 Nov 2024 12:17:12 +0100 Subject: [PATCH 12/15] minor change: remove un-needed comment --- lib/ord.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/ord.js b/lib/ord.js index eb6e24b..5f5dd34 100644 --- a/lib/ord.js +++ b/lib/ord.js @@ -28,8 +28,6 @@ const initializeAppConfig = (csn) => { const lastUpdate = getRFC3339Date(); let odmEntity = []; - // use "namespace" from package.json as fallback in case it's not given in cdsrc.json - // if cdsrc.json does not have "applicationNamespace", use the "namespace" const vendorNamespace = "customer"; const ordNamespace = cds.env["ord"]?.namespace || `${vendorNamespace}.${packageName.replace(/[^a-zA-Z0-9]/g, "")}`; const eventApplicationNamespace = cds.env?.export?.asyncapi?.applicationNamespace; From 8d9bf18bede820a0623ccf00d3332f0872c34c9b Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Fri, 8 Nov 2024 12:18:06 +0100 Subject: [PATCH 13/15] minor change: remove un-needed comment --- lib/templates.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/templates.js b/lib/templates.js index 573b8c8..9f8dee4 100644 --- a/lib/templates.js +++ b/lib/templates.js @@ -162,7 +162,7 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa DESCRIPTION_PREFIX + serviceName, version: "1.0.0", lastUpdate: appConfig.lastUpdate, - visibility: RESOURCE_VISIBILITY.public, // default visibility + visibility: RESOURCE_VISIBILITY.public, partOfPackage: _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.api), partOfGroups: [_getGroupID(serviceName, defaults.groupTypeId, appConfig)], releaseStatus: "active", From 6cdf905d42750e3d3bf6f07f4660d1076b48baf0 Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Fri, 8 Nov 2024 17:28:46 +0100 Subject: [PATCH 14/15] minor change: address review comment https://github.com/cap-js/ord/pull/82#discussion_r1834598322 --- lib/templates.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/templates.js b/lib/templates.js index 9f8dee4..a244152 100644 --- a/lib/templates.js +++ b/lib/templates.js @@ -120,7 +120,8 @@ const createGroupsTemplateForService = (serviceName, serviceDefinition, appConfi /** * This is a template function to create API Resource object for API Resource Array. - * + * Properties of an API resource can be overwritten by the ORD extensions. Example: visibility. + * @param {string} serviceName The name of the service. * @param {object} serviceDefinition The definition of the service * @returns {Array} An array of objects for the API Resources. @@ -174,10 +175,9 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa supported: "no", }, entityTypeMappings: [{ entityTypeTargets: appConfig.odmEntity }], - ...ordExtensions, }; - // visibility can also be set as an ORD extension; in this case, it overwrites the default visibility + if (obj.visibility === RESOURCE_VISIBILITY.public) apiResources.push(obj); }); @@ -187,6 +187,7 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa /** * This is a template function to create Event Resource object for Event Resource Array. * There can be only one event resource per service because all events are using the same protocol, they are always Cloud Events. + * Properties of an event resource can be overwritten by the ORD extensions. Example: visibility. * * @param {string} serviceName The name of the service. * @param {object} serviceDefinition The definition of the service @@ -210,7 +211,7 @@ const createEventResourceTemplate = (serviceName, serviceDefinition, appConfig, releaseStatus: "active", partOfPackage: _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.event), partOfGroups: [_getGroupID(serviceName, defaults.groupTypeId, appConfig)], - visibility: RESOURCE_VISIBILITY.public,//default value that can be overwritten by ORD extension + visibility: RESOURCE_VISIBILITY.public, resourceDefinitions: [ { type: "asyncapi-v2", From 2820f9eb99505b6413a09f6d838d21cbd92f1090 Mon Sep 17 00:00:00 2001 From: Albin Ramovic Date: Mon, 11 Nov 2024 17:08:18 +0100 Subject: [PATCH 15/15] address review comments --- __tests__/ordCdsrc.test.js | 6 +++--- __tests__/ordPackageJson.test.js | 12 ++++++------ __tests__/unittest/extendOrdWithCustom.test.js | 2 +- lib/extendOrdWithCustom.js | 17 +++++++++-------- lib/ord.js | 11 ++++++----- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/__tests__/ordCdsrc.test.js b/__tests__/ordCdsrc.test.js index 53b8f80..dbf2e0e 100644 --- a/__tests__/ordCdsrc.test.js +++ b/__tests__/ordCdsrc.test.js @@ -1,11 +1,12 @@ +const cds = require("@sap/cds"); const ord = require("../lib/ord"); const path = require("path"); // Mock the @sap/cds module jest.mock("@sap/cds", () => { - const { join } = require("path"); + const path = require("path"); let originalCds = jest.requireActual("@sap/cds"); - originalCds.root = join(__dirname, "bookshop"); + originalCds.root = path.join(__dirname, "bookshop"); return originalCds; }); @@ -13,7 +14,6 @@ jest.mock("../lib/date", () => ({ getRFC3339Date: jest.fn(() => "2024-11-04T14:33:25+01:00") })); -const cds = require("@sap/cds"); describe("Tests for default ORD document when .cdsrc.json is present", () => { let csn; diff --git a/__tests__/ordPackageJson.test.js b/__tests__/ordPackageJson.test.js index 32d5fa0..0b0558d 100644 --- a/__tests__/ordPackageJson.test.js +++ b/__tests__/ordPackageJson.test.js @@ -1,11 +1,13 @@ +const cds = require("@sap/cds"); const ord = require("../lib/ord"); -const { join } = require("path"); +const path = require("path"); + // Mock the @sap/cds module jest.mock("@sap/cds", () => { - const { join } = require("path"); + const path = require("path"); let originalCds = jest.requireActual("@sap/cds"); - originalCds.root = join(__dirname, "bookshop"); + originalCds.root = path.join(__dirname, "bookshop"); return originalCds; }); @@ -13,14 +15,12 @@ jest.mock("../lib/date", () => ({ getRFC3339Date: jest.fn(() => "2024-11-04T14:33:25+01:00") })); -const cds = require("@sap/cds"); - describe("Tests for default ORD document when .cdsrc.json is not present", () => { let csn; beforeAll(async () => { cds.env["ord"] = ""; - csn = await cds.load(join(__dirname, "bookshop", "srv")); + csn = await cds.load(path.join(__dirname, "bookshop", "srv")); }); diff --git a/__tests__/unittest/extendOrdWithCustom.test.js b/__tests__/unittest/extendOrdWithCustom.test.js index d293f4e..94d6dfd 100644 --- a/__tests__/unittest/extendOrdWithCustom.test.js +++ b/__tests__/unittest/extendOrdWithCustom.test.js @@ -46,7 +46,7 @@ describe('extendOrdWithCustom', () => { const ordContent = {}; const warningSpy = jest.spyOn(console, 'warn'); prepareTestEnvironment({ namespace: "sap.sample" }, appConfig, 'testCustomORDContentFileThrowErrors.json'); - const result = extendCustomORDContentIfExists(appConfig, ordContent); + const result = extendCustomORDContentIfExists(appConfig, ordContent, cds.log()); expect(warningSpy).toHaveBeenCalledTimes(3); expect(warningSpy).toHaveBeenCalledWith('Mocked warning'); diff --git a/lib/extendOrdWithCustom.js b/lib/extendOrdWithCustom.js index 969028e..981d5c2 100644 --- a/lib/extendOrdWithCustom.js +++ b/lib/extendOrdWithCustom.js @@ -1,7 +1,8 @@ -const { root, log } = require("@sap/cds"); -const { join } = require("path"); -const _ = require('lodash'); const { CONTENT_MERGE_KEY } = require('./constants'); +const cds = require("@sap/cds"); +const path = require("path"); +const _ = require('lodash'); + function cleanNullProperties(obj) { @@ -28,12 +29,12 @@ function patchGeneratedOrdResources(destinationObj, sourceObj) { return cleanNullProperties(Object.values(destObj)); } -function compareAndHandleCustomORDContentWithExistingContent(ordContent, customORDContent) { +function compareAndHandleCustomORDContentWithExistingContent(ordContent, customORDContent, logger) { const clonedOrdContent = structuredClone(ordContent); for (const key in customORDContent) { const propertyType = typeof customORDContent[key]; if (propertyType !== 'object' && propertyType !== 'array') { - log('ord-plugin').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,17 +48,17 @@ function compareAndHandleCustomORDContentWithExistingContent(ordContent, customO function getCustomORDContent(appConfig) { if (appConfig.env?.customOrdContentFile) { - const customORDContent = require(join(root, appConfig.env.customOrdContentFile)); + const customORDContent = require(path.join(cds.root, appConfig.env.customOrdContentFile)); return customORDContent; } return {}; } -function extendCustomORDContentIfExists(appConfig, ordContent) { +function extendCustomORDContentIfExists(appConfig, ordContent, logger) { const customORDContent = getCustomORDContent(appConfig); if (customORDContent) { - ordContent = compareAndHandleCustomORDContentWithExistingContent(ordContent, customORDContent); + ordContent = compareAndHandleCustomORDContentWithExistingContent(ordContent, customORDContent, logger); } return ordContent; } diff --git a/lib/ord.js b/lib/ord.js index 5f5dd34..9fe8f0d 100644 --- a/lib/ord.js +++ b/lib/ord.js @@ -5,15 +5,16 @@ const { createEventResourceTemplate, createGroupsTemplateForService } = require('./templates'); -const cds = require("@sap/cds"); -const defaults = require("./defaults"); const { extendCustomORDContentIfExists } = require('./extendOrdWithCustom'); const { getRFC3339Date } = require('./date'); const _ = require("lodash"); -const { join } = require("path"); +const cds = require("@sap/cds"); +const defaults = require("./defaults"); +const logger = cds.log('ord-plugin'); +const path = require("path"); const initializeAppConfig = (csn) => { - let packageJsonPath = join(cds.root, 'package.json') + let packageJsonPath = path.join(cds.root, 'package.json') let packageJson; if (cds.utils.exists(packageJsonPath)) { packageJson = require(packageJsonPath); @@ -166,7 +167,7 @@ module.exports = (csn) => { ordDocument.eventResources = eventResources; } - ordDocument = extendCustomORDContentIfExists(appConfig, ordDocument); + ordDocument = extendCustomORDContentIfExists(appConfig, ordDocument, logger); return ordDocument; };