@cap-js/ord 1.0.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/ord.js CHANGED
@@ -1,202 +1,168 @@
1
- const path = require("path");
2
- const cds = require("@sap/cds");
3
- const { exists } = cds.utils;
4
- const defaults = require("./defaults");
1
+ const { ORD_RESOURCE_TYPE } = require('./constants');
5
2
  const {
6
- fCreateAPIResourceTemplate,
7
- fCreateEventResourceTemplate,
8
- fCreateGroupsTemplateForService,
9
- fCreateGroupsTemplateForEvent,
10
- fCreateEntityTypeTemplate
3
+ createAPIResourceTemplate,
4
+ createEntityTypeTemplate,
5
+ createEventResourceTemplate,
6
+ createGroupsTemplateForService
11
7
  } = require('./templates');
8
+ const { extendCustomORDContentIfExists } = require('./extendOrdWithCustom');
9
+ const { getRFC3339Date } = require('./date');
10
+ const { Logger } = require('./logger');
11
+ const _ = require("lodash");
12
+ const cds = require("@sap/cds");
13
+ const defaults = require("./defaults");
14
+ const path = require("path");
12
15
 
13
- /**
14
- * Initializes global object based on package.json and CSN object.
15
- * @param {Object} csn object
16
- * @returns {Object} An object containing global variables.
17
- */
18
- const fInitializeGlobal = (csn) => {
19
- let packagejsonPath = path.join(cds.root,'package.json')
16
+ const initializeAppConfig = (csn) => {
17
+ let packageJsonPath = path.join(cds.root, 'package.json')
20
18
  let packageJson;
21
- if (exists(packagejsonPath)) {
22
- packageJson = require(packagejsonPath);
19
+ if (cds.utils.exists(packageJsonPath)) {
20
+ packageJson = require(packageJsonPath);
23
21
  } else {
24
22
  throw new Error(`package.json not found in the project root directory`);
25
23
  }
24
+ const packageName = packageJson.name;
26
25
  const appName = packageJson.name.replace(/^[@]/, "").replace(/[@/]/g, "-");
27
- const aModelKeys = Object.keys(csn.definitions);
28
- const aEvents = [];
29
- const aServices = [];
30
- const aODMEntity = [];
31
-
32
- const capNamespace = csn.namespace;
33
- // namespace variable value if present in cdsrc.json take it there or else from package.json
34
- //if cdsrc.json does not have applicationNamespace, then use just the namespace
35
- const namespace = cds.env["ord"]?.namespace || `customer.${appName}`;
36
- const applicationNamespace = cds.env?.export?.asyncapi?.applicationNamespace;
37
-
38
- if (applicationNamespace && fGetNamespaceComponents(namespace) !== fGetNamespaceComponents(applicationNamespace)) {
39
- console.warn('ORD and AsyncAPI namespaces should be the same.');
40
- }
41
-
42
- const fEventFilter = (keyDefinition) => keyDefinition.kind === "event";
43
- const fServicesFilter = (keyDefinition) => keyDefinition.kind === "service" && !keyDefinition['@cds.external'];
44
- const fODMEntityFilter = (key, keyDefinition) => {
45
- return keyDefinition.kind === "entity"
46
- && key.includes(capNamespace)
47
- && !key.includes(".texts")
48
- && keyDefinition["@ODM.entityName"];
26
+ const modelKeys = Object.keys(csn.definitions);
27
+ const events = [];
28
+ const serviceNames = [];
29
+ const lastUpdate = getRFC3339Date();
30
+ let odmEntity = [];
31
+
32
+ const vendorNamespace = "customer";
33
+ const ordNamespace = cds.env["ord"]?.namespace || `${vendorNamespace}.${packageName.replace(/[^a-zA-Z0-9]/g, "")}`;
34
+ const eventApplicationNamespace = cds.env?.export?.asyncapi?.applicationNamespace;
35
+
36
+ if (eventApplicationNamespace && ordNamespace !== eventApplicationNamespace) {
37
+ Logger.warn('ORD and AsyncAPI namespaces should be the same.');
49
38
  }
50
39
 
51
- aModelKeys.forEach((key) => {
40
+ for (const key of modelKeys) {
52
41
  const keyDefinition = csn.definitions[key];
53
- if(fServicesFilter(keyDefinition)){
54
- aServices.push(key);
55
- } else if(fODMEntityFilter(key, keyDefinition)) {
56
- aODMEntity.push(fCreateEntityTypeTemplate(keyDefinition));
57
- } else if(fEventFilter(keyDefinition)){
58
- aEvents.push(key);
42
+ switch (keyDefinition.kind) {
43
+ case ORD_RESOURCE_TYPE.service:
44
+ if (!keyDefinition["@cds.external"]) {
45
+ serviceNames.push(key);
46
+ }
47
+ break;
48
+ // TODO: should be rewritten
49
+ case ORD_RESOURCE_TYPE.entity:
50
+ if (!key.includes(".texts") && keyDefinition["@ODM.entityName"]) {
51
+ odmEntity.push(createEntityTypeTemplate(keyDefinition));
52
+ }
53
+ break;
54
+ case ORD_RESOURCE_TYPE.event:
55
+ events.push(key);
56
+ break;
59
57
  }
60
- });
58
+ }
61
59
 
60
+ odmEntity = _.uniqBy(odmEntity, "ordId");
62
61
 
63
62
  return {
64
63
  env: cds.env["ord"],
65
- capNamespace,
64
+ lastUpdate,
66
65
  appName,
67
- aEvents,
68
- aServices,
69
- aODMEntity,
70
- namespace,
71
- applicationNamespace
72
- }
73
- }
66
+ events,
67
+ serviceNames,
68
+ odmEntity,
69
+ ordNamespace,
70
+ eventApplicationNamespace,
71
+ packageName,
72
+ };
73
+ };
74
+
75
+ const _getPolicyLevel = (appConfig) =>
76
+ appConfig.env?.policyLevel || defaults.policyLevel;
74
77
 
75
- /**
76
- * Retrieves first two components of the namespace.
77
- * @param {string} namespace
78
- * @returns {string} The first two components of the namespace.
79
- */
80
- const fGetNamespaceComponents = (namespace) => namespace.split('.').slice(0, 2).join('.');
81
-
82
- /**
83
- * Retrieves the policy level.
84
- * Hierarchy to check data: cdsrc.json > defaults
85
- * @returns {string} The policy level.
86
- */
87
- const fGetPolicyLevel = (global) => global.env?.policyLevel || defaults.policyLevel;
88
-
89
- /**
90
- * Retrieves the root level ORD document description.
91
- * Hierarchy to check data: cdsrc.json > defaults
92
- * @returns {string} The ORD document description.
93
- */
94
- const fGetDescription = (global) => global.env?.description || defaults.description;
95
-
96
- /**
97
- * Retrieves the products.
98
- * Hierarchy to check data: cdsrc.json > defaults
99
- * @returns {Array<object>} The products array.
100
- * if global.namespace is defined in cdsrc.json, then use it, else use the appName from package.json
101
- */
102
- const fGetProducts = (global) => global.env?.products || defaults.products(global.namespace);
103
-
104
- /**
105
- * Retrieves the groups that services belongs to.
106
- * Gets list of groups from CDS runtime object.
107
- * @param {Object} csn object
108
- * @returns {Array<object>} The groups array.
109
- */
110
- const fGetGroups = (csn, global) => {
111
- // storing the group ids in a set to avoid duplicates
112
- let groupIds = new Set();
113
-
114
- let serviceGroups = global.aServices
115
- .map((srv) => fCreateGroupsTemplateForService(srv, csn.definitions[srv], groupIds))
116
- .filter((resource) => resource !== null && resource !== undefined);
117
- let eventGroups = global.aEvents
118
- .map((event) => fCreateGroupsTemplateForEvent(event, csn.definitions[event], groupIds))
119
- .filter((resource) => resource !== null && resource !== undefined);
120
-
121
- return [...serviceGroups, ...eventGroups];
78
+ const _getDescription = (appConfig) =>
79
+ appConfig.env?.description || defaults.description;
80
+
81
+ const _getProducts = (appConfig) =>
82
+ appConfig.env?.products || defaults.products(appConfig.packageName);
83
+
84
+ const _getGroups = (csn, appConfig) => {
85
+ return appConfig.serviceNames
86
+ .flatMap((serviceName) => createGroupsTemplateForService(serviceName, csn.definitions[serviceName], appConfig))
87
+ .filter((resource) => !!resource);
122
88
  };
123
89
 
124
- /**
125
- * Retrieves the packages.
126
- * Hierarchy to check data: cdsrc.json > defaults
127
- * @returns {Array<object>} The packages array.
128
- */
129
- const fGetPackages = (policyLevel,global) => global.env?.packages || (global.aEvents.length) ? defaults.packages(global.namespace,policyLevel,global.capNamespace) : defaults.packages(global.namespace,policyLevel,global.capNamespace).slice(0, 1)
130
-
131
- /**
132
- * Retrieves the consumption bundles.
133
- * Hierarchy to check data: cdsrc.json > defaults
134
- * @returns {Array<object>} The consumption bundles array.
135
- */
136
- const fGetConsumptionBundles = (global) => global.env?.consumptionBundles || (global.aEvents.length) ? defaults.consumptionBundles(global.capNamespace) : defaults.consumptionBundles(global.capNamespace).slice(0,1)
137
-
138
- /**
139
- * Retrieves the API Resources
140
- * Gets list of services from CSN object
141
- * @param {Object} csn object
142
- * @returns {Array<object>} The API Resources array.
143
- */
144
- const fGetAPIResources = (csn, global,packageIds) => {
145
- const apiResources = [];
146
- global.aServices.forEach((srv) => {
147
- fCreateAPIResourceTemplate(srv, csn.definitions[srv], global,packageIds)?.forEach(
148
- (resource) => {
149
- if (resource !== null && resource !== undefined) {
150
- apiResources.push(resource);
151
- }
152
- }
153
- );
154
- });
155
- return apiResources;
90
+ const _getPackages = (policyLevel, appConfig) =>
91
+ appConfig.env?.packages || appConfig.events.length
92
+ ? defaults.packages(appConfig.appName, policyLevel, appConfig.ordNamespace)
93
+ : defaults
94
+ .packages(appConfig.appName, policyLevel, appConfig.ordNamespace)
95
+ .slice(0, 1);
96
+
97
+ const _getAPIResources = (csn, appConfig, packageIds) => {
98
+ return appConfig.serviceNames
99
+ .flatMap((serviceName) => createAPIResourceTemplate(serviceName, csn.definitions[serviceName], appConfig, packageIds))
156
100
  };
157
101
 
158
- /**
159
- * Retrieves the Event Resources
160
- * Gets list of event from CSN object
161
- * @param {Object} csn object
162
- * @returns {Array<object>} The Event Resources array.
163
- */
164
- const fGetEventResources = (csn, global,packageIds) => global.aEvents.map((srv) => fCreateEventResourceTemplate(srv, csn.definitions[srv], global, packageIds)).filter((resource) => resource !== null && resource !== undefined);
102
+ const _getEventResources = (csn, appConfig, packageIds) => {
103
+ if (appConfig.events.length === 0) return [];
104
+ return appConfig.serviceNames
105
+ .filter((serviceName) => appConfig.events.some((eventName) => eventName.startsWith(serviceName)))
106
+ .flatMap((serviceName) => createEventResourceTemplate(serviceName, csn.definitions[serviceName], appConfig, packageIds));
107
+ };
165
108
 
166
- module.exports = (csn) => {
167
- const linkedCsn = cds.linked(csn);
168
- Object.assign(global, fInitializeGlobal(linkedCsn));
169
- const validateSystemNamespace = new RegExp(`^${global.applicationNamespace}\\.[^.]+\\..+$`);
170
- if (global.namespace === undefined && !validateSystemNamespace.test(global.namespace)) {
171
- let error = new Error(`Namespace is not defined in cdsrc.json or it is not in the format of ${global.applicationNamespace}.<appName>.<service>`);
172
- console.error('Namespace error:', error.message);
109
+ function _getOpenResourceDiscovery(appConfig) {
110
+ return appConfig.env?.openResourceDiscovery || defaults.openResourceDiscovery;
111
+ }
112
+
113
+ function _getConsumptionBundles(appConfig) {
114
+ return appConfig.env?.consumptionBundles || defaults.consumptionBundles(appConfig);
115
+ }
116
+
117
+ function validateNamespace(appConfig) {
118
+ const validateSystemNamespace = new RegExp(`^${appConfig.eventApplicationNamespace}\\.[^.]+\\..+$`);
119
+ if (
120
+ appConfig.ordNamespace === undefined &&
121
+ !validateSystemNamespace.test(appConfig.ordNamespace)
122
+ ) {
123
+ let error = new Error(
124
+ `Namespace is not defined in cdsrc.json or it is not in the format of ${appConfig.eventApplicationNamespace}.<appName>.<service>`
125
+ );
126
+ Logger.error('Namespace error:', error.message);
173
127
  throw error;
174
128
  }
129
+ }
175
130
 
176
- let oReturn = {openResourceDiscovery: "1.9",
177
- policyLevel: fGetPolicyLevel(global),
178
- description: fGetDescription(global),
179
- products: fGetProducts(global),
180
- groups: fGetGroups(linkedCsn, global),
131
+ function createDefaultORDDocument(linkedCsn, appConfig) {
132
+ let ordDocument = {
133
+ $schema: "https://sap.github.io/open-resource-discovery/spec-v1/interfaces/Document.schema.json",
134
+ openResourceDiscovery: _getOpenResourceDiscovery(appConfig),
135
+ policyLevel: _getPolicyLevel(appConfig),
136
+ description: _getDescription(appConfig),
137
+ products: _getProducts(appConfig),
138
+ groups: _getGroups(linkedCsn, appConfig),
139
+ consumptionBundles: _getConsumptionBundles(appConfig),
181
140
  };
182
- if(fGetAPIResources(linkedCsn, global).length > 0 && (fGetEventResources(linkedCsn, global).length>0)){
183
- oReturn.packages = fGetPackages(oReturn.policyLevel, global);
184
- }
185
141
 
186
- // check if oReturn.packages is defined and extract the ordId from the packages and store in a set
187
- let packageIds = new Set();
188
- if(oReturn.packages){
189
- oReturn.packages.forEach((pkg) => {
190
- packageIds.add(pkg.ordId);
191
- });
142
+ if (_getAPIResources(linkedCsn, appConfig).length && _getEventResources(linkedCsn, appConfig).length) {
143
+ ordDocument.packages = _getPackages(ordDocument.policyLevel, appConfig);
192
144
  }
193
- oReturn = {
194
- ...oReturn,
195
- consumptionBundles: fGetConsumptionBundles(global),
196
- apiResources: fGetAPIResources(linkedCsn, global,packageIds),
197
- };
198
- if(fGetEventResources(linkedCsn, global,packageIds).length > 0) {
199
- oReturn.eventResources= fGetEventResources(linkedCsn, global,packageIds);
145
+ return ordDocument;
146
+ }
147
+
148
+ function extractPackageIds(ordDocument) {
149
+ const packageIds = [];
150
+ if (ordDocument.packages) {
151
+ ordDocument.packages.map((pkg) => packageIds.push(pkg.ordId));
200
152
  }
201
- return oReturn;
153
+ return packageIds;
202
154
  }
155
+
156
+ module.exports = (csn) => {
157
+ const linkedCsn = cds.linked(csn);
158
+ const appConfig = initializeAppConfig(linkedCsn);
159
+ validateNamespace(appConfig);
160
+
161
+ let ordDocument = createDefaultORDDocument(linkedCsn, appConfig);
162
+ const packageIds = extractPackageIds(ordDocument);
163
+ ordDocument.apiResources = _getAPIResources(linkedCsn, appConfig, packageIds);
164
+ ordDocument.eventResources = _getEventResources(linkedCsn, appConfig, packageIds);
165
+ ordDocument = extendCustomORDContentIfExists(appConfig, ordDocument);
166
+
167
+ return ordDocument;
168
+ };
package/lib/plugin.js CHANGED
@@ -1,5 +1,7 @@
1
- const { ord, getMetadata, defaults } = require("./");
2
1
  const cds = require("@sap/cds");
2
+ const { Logger } = require("./logger");
3
+ const { ord, getMetadata, defaults } = require("./");
4
+
3
5
 
4
6
  cds.on("bootstrap", (app) => {
5
7
  app.use("/.well-known/open-resource-discovery", async (req, res) => {
@@ -10,8 +12,7 @@ cds.on("bootstrap", (app) => {
10
12
  const { contentType, response } = await getMetadata(req.url);
11
13
  res.status(200).contentType(contentType).send(response);
12
14
  } catch (error) {
13
- console.log(error);
14
- console.log('Error while generating metadata');
15
+ Logger.error(error, 'Error while generating metadata');
15
16
  res.status(500).send(error.message);
16
17
  }
17
18
  }
@@ -23,8 +24,7 @@ cds.on("bootstrap", (app) => {
23
24
  const data = ord(csn);
24
25
  return res.status(200).send(data);
25
26
  } catch (error) {
26
- console.log(error);
27
- console.log('Error while creating ORD document');
27
+ Logger.error(error, 'Error while creating ORD document');
28
28
  return res.status(500).send(error.message);
29
29
  }
30
30
  });