@cap-js/ord 1.3.2 → 1.3.4

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/defaults.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const { OPEN_RESOURCE_DISCOVERY_VERSION, SHORT_DESCRIPTION_PREFIX, RESOURCE_VISIBILITY } = require("./constants");
2
+ const { hasSAPPolicyLevel } = require("./utils");
2
3
  const { getAuthConfig } = require("./authentication");
3
4
  const _ = require("lodash");
4
5
 
@@ -35,15 +36,6 @@ const generateUniquePackageOrdId = (ordNamespace, name, tag, visibility) => {
35
36
  return `${ordNamespace}:package:${regexWithRemoval(name)}${tag}${visibilitySuffix}:v1`;
36
37
  };
37
38
 
38
- /**
39
- * Checks if at least one policy level in the array is SAP.
40
- *
41
- * @param {string[]} policyLevels - Array of policy levels.
42
- */
43
- function hasSAPPolicyLevel(policyLevels) {
44
- return policyLevels.some((policyLevel) => policyLevel.split(":")[0].toLowerCase() === "sap");
45
- }
46
-
47
39
  /**
48
40
  * Module containing default configuration for ORD Document.
49
41
  * @module defaults
@@ -58,16 +50,16 @@ module.exports = {
58
50
  ordId: defaultProductOrdId(name),
59
51
  title: nameWithSpaces(name),
60
52
  shortDescription: SHORT_DESCRIPTION_PREFIX + nameWithSpaces(name),
61
- vendor: "customer:vendor:customer:",
53
+ vendor: "customer:vendor:Customer:",
62
54
  },
63
55
  ],
64
56
  groupTypeId: "sap.cds:service",
65
- packages: function getPackageData(appConfig, policyLevels) {
57
+ packages: function getPackageData(appConfig) {
66
58
  const name = appConfig.appName;
67
59
  const ordNamespace = appConfig.ordNamespace;
68
60
  const productsOrdId = appConfig.existingProductORDId || appConfig.products?.[0]?.ordId;
69
61
  const vendor = appConfig.env?.packages?.[0]?.vendor;
70
- if (hasSAPPolicyLevel(policyLevels)) {
62
+ if (hasSAPPolicyLevel(appConfig.policyLevels)) {
71
63
  return _.uniqBy(
72
64
  packageTypes.flatMap(({ tag, type }) =>
73
65
  Object.values(RESOURCE_VISIBILITY).map((visibility) => ({
package/lib/ord.js CHANGED
@@ -34,14 +34,15 @@ const initializeAppConfig = (csn) => {
34
34
 
35
35
  _validateNamespaces(ordNamespace, eventApplicationNamespace);
36
36
 
37
- const { serviceNames, apiResourceNames, apiEndpoints, eventNames, entityTypeTargets } = _triageCsnDefinitions(csn);
37
+ const { serviceNames, apiResourceNames, apiEndpoints, eventServiceNames, entityTypeTargets } =
38
+ _triageCsnDefinitions(csn);
38
39
 
39
40
  return {
40
41
  env: cds.env["ord"],
41
42
  lastUpdate,
42
43
  appName,
43
44
  apiEndpoints: Array.from(apiEndpoints),
44
- eventNames,
45
+ eventServiceNames,
45
46
  serviceNames,
46
47
  apiResourceNames,
47
48
  entityTypeTargets: _.uniqBy(entityTypeTargets, CONTENT_MERGE_KEY),
@@ -77,9 +78,12 @@ function _validateNamespaces(ordNamespace, eventApplicationNamespace) {
77
78
  function _triageCsnDefinitions(csn) {
78
79
  const pendingApiResourceNames = [];
79
80
  const apiEndpoints = new Set();
80
- const pendingEventNames = [];
81
+ const pendingEventServiceNames = new Set();
81
82
  const entityTypeTargets = [];
82
83
  const pendingServiceNames = [];
84
+ const serviceNames = Object.keys(csn.definitions).filter(
85
+ (key) => csn.definitions[key].kind === CDS_ELEMENT_KIND.service,
86
+ );
83
87
 
84
88
  for (const definitionKey of Object.keys(csn.definitions)) {
85
89
  const definitionObj = csn.definitions[definitionKey];
@@ -103,8 +107,8 @@ function _triageCsnDefinitions(csn) {
103
107
  break;
104
108
  }
105
109
  case CDS_ELEMENT_KIND.event: {
106
- const event = _handleEvent(definitionKey);
107
- if (event) pendingEventNames.push(event);
110
+ const event = _handleEvent(serviceNames, definitionKey);
111
+ if (event) pendingEventServiceNames.add(event);
108
112
  break;
109
113
  }
110
114
  case CDS_ELEMENT_KIND.action:
@@ -120,7 +124,7 @@ function _triageCsnDefinitions(csn) {
120
124
  serviceNames: pendingServiceNames,
121
125
  apiResourceNames: pendingApiResourceNames,
122
126
  apiEndpoints: Array.from(apiEndpoints),
123
- eventNames: pendingEventNames,
127
+ eventServiceNames: [...pendingEventServiceNames],
124
128
  entityTypeTargets,
125
129
  };
126
130
  }
@@ -156,8 +160,12 @@ function _handleEntity(key, keyDefinition) {
156
160
  return null;
157
161
  }
158
162
 
159
- function _handleEvent(key) {
160
- return key;
163
+ function _handleEvent(serviceNames, key) {
164
+ for (const serviceName of serviceNames) {
165
+ if (key.startsWith(serviceName + ".")) {
166
+ return serviceName;
167
+ }
168
+ }
161
169
  }
162
170
 
163
171
  function _handleActionOrFunction(key) {
@@ -177,14 +185,14 @@ const _getGroups = (csn, appConfig) => {
177
185
  .filter((resource) => !!resource);
178
186
  };
179
187
 
180
- const _getPackages = (policyLevels, appConfig) => {
181
- return defaults.packages(appConfig, policyLevels);
188
+ const _getPackages = (appConfig) => {
189
+ return defaults.packages(appConfig);
182
190
  };
183
191
 
184
192
  const _getEntityTypes = (appConfig, packageIds) => {
185
193
  if (!appConfig.entityTypeTargets?.length) return [];
186
194
 
187
- return appConfig.entityTypeTargets.map((entity) => createEntityTypeTemplate(appConfig, packageIds, entity));
195
+ return appConfig.entityTypeTargets.flatMap((entity) => createEntityTypeTemplate(appConfig, packageIds, entity));
188
196
  };
189
197
 
190
198
  const _getAPIResources = (csn, appConfig, packageIds, accessStrategies) => {
@@ -200,7 +208,7 @@ const _getAPIResources = (csn, appConfig, packageIds, accessStrategies) => {
200
208
  };
201
209
 
202
210
  const _getEventResources = (csn, appConfig, packageIds, accessStrategies) => {
203
- return appConfig.serviceNames.flatMap((serviceName) =>
211
+ return appConfig.eventServiceNames.flatMap((serviceName) =>
204
212
  createEventResourceTemplate(serviceName, csn.definitions[serviceName], appConfig, packageIds, accessStrategies),
205
213
  );
206
214
  };
@@ -230,10 +238,11 @@ const _getProducts = (appConfig) => {
230
238
  };
231
239
 
232
240
  function createDefaultORDDocument(linkedCsn, appConfig) {
241
+ appConfig.policyLevels = _getPolicyLevels(appConfig);
233
242
  let ordDocument = {
234
243
  $schema: "https://open-resource-discovery.github.io/specification/spec-v1/interfaces/Document.schema.json",
235
244
  openResourceDiscovery: _getOpenResourceDiscovery(appConfig),
236
- policyLevels: _getPolicyLevels(appConfig),
245
+ policyLevels: appConfig.policyLevels,
237
246
  description: _getDescription(appConfig),
238
247
  consumptionBundles: _getConsumptionBundles(appConfig),
239
248
  };
@@ -248,7 +257,7 @@ function createDefaultORDDocument(linkedCsn, appConfig) {
248
257
  ordDocument.products = [_getProducts(appConfig)[0]];
249
258
  }
250
259
 
251
- ordDocument.packages = _getPackages(ordDocument.policyLevels, appConfig);
260
+ ordDocument.packages = _getPackages(appConfig);
252
261
 
253
262
  return ordDocument;
254
263
  }
@@ -295,7 +304,7 @@ module.exports = (csn) => {
295
304
  ordDocument.apiResources = apiResources;
296
305
  }
297
306
  }
298
- if (appConfig.eventNames.length) {
307
+ if (appConfig.eventServiceNames.length) {
299
308
  const eventResources = _getEventResources(linkedCsn, appConfig, packageIds, accessStrategies);
300
309
  if (eventResources.length) {
301
310
  ordDocument.eventResources = eventResources;
package/lib/templates.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const cds = require("@sap/cds");
2
+ const { hasSAPPolicyLevel } = require("./utils");
2
3
  const defaults = require("./defaults");
3
4
  const _ = require("lodash");
4
5
  const {
@@ -80,6 +81,7 @@ const createEntityTypeMappingsItemTemplate = (entity) => {
80
81
  return {
81
82
  ordId: `sap.odm:entityType:${entity[ORD_ODM_ENTITY_NAME_ANNOTATION]}:v1`,
82
83
  entityName: entity[ORD_ODM_ENTITY_NAME_ANNOTATION],
84
+ isODMMapping: true,
83
85
  ...entity,
84
86
  };
85
87
  } else if (entity[ENTITY_RELATIONSHIP_ANNOTATION]) {
@@ -95,8 +97,19 @@ const createEntityTypeMappingsItemTemplate = (entity) => {
95
97
  }
96
98
  };
97
99
 
98
- function _getGroupID(fullyQualifiedName, groupTypeId = defaults.groupTypeId, appConfig) {
99
- return `${groupTypeId}:${appConfig.ordNamespace}:${fullyQualifiedName}`;
100
+ function _getGroupID(serviceDefinition, groupTypeId = defaults.groupTypeId, appConfig) {
101
+ return `${groupTypeId}:${appConfig.ordNamespace}:${_getGroupNameWithNestedNamespace(serviceDefinition, appConfig)}`;
102
+ }
103
+
104
+ function _getGroupNameWithNestedNamespace({ name }, appConfig) {
105
+ if (!name.startsWith(appConfig.ordNamespace)) {
106
+ return name;
107
+ }
108
+ let sortedName = name.substring(appConfig.ordNamespace.length);
109
+ if (sortedName.startsWith(".")) {
110
+ sortedName = sortedName.substring(1);
111
+ }
112
+ return sortedName;
100
113
  }
101
114
 
102
115
  /**
@@ -174,7 +187,7 @@ const createGroupsTemplateForService = (serviceName, serviceDefinition, appConfi
174
187
  return undefined;
175
188
  }
176
189
 
177
- const groupId = _getGroupID(serviceName, defaults.groupTypeId, appConfig);
190
+ const groupId = _getGroupID(serviceDefinition, defaults.groupTypeId, appConfig);
178
191
  return {
179
192
  groupId: groupId,
180
193
  groupTypeId: defaults.groupTypeId,
@@ -192,11 +205,20 @@ const createGroupsTemplateForService = (serviceName, serviceDefinition, appConfi
192
205
  * @returns { object } An object for the EntityType.
193
206
  */
194
207
  const createEntityTypeTemplate = (appConfig, packageIds, entity) => {
208
+ if (entity.isODMMapping) {
209
+ // ODM mappings are not created as entity types, they are only used in entityTypeMappings
210
+ return [];
211
+ }
212
+ if (hasSAPPolicyLevel(appConfig.policyLevels)) {
213
+ // If SAP policy level is present, don't create entity type, they must be in the central repository
214
+ return [];
215
+ }
216
+
195
217
  const ordExtensions = readORDExtensions(entity);
196
218
  const visibility = ordExtensions.visibility || RESOURCE_VISIBILITY.public;
197
219
 
198
220
  if (visibility === RESOURCE_VISIBILITY.private) {
199
- return null;
221
+ return [];
200
222
  }
201
223
 
202
224
  const packageId = _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.entityType, visibility);
@@ -220,7 +242,9 @@ const createEntityTypeTemplate = (appConfig, packageIds, entity) => {
220
242
 
221
243
  function _handleVisibility(ordExtensions, definition) {
222
244
  let visibility;
223
- if (ordExtensions.visibility) {
245
+ if (isPrimaryDataProductService(definition)) {
246
+ visibility = RESOURCE_VISIBILITY.internal;
247
+ } else if (ordExtensions.visibility) {
224
248
  visibility = ordExtensions.visibility;
225
249
  } else if (definition[ORD_EXTENSIONS_PREFIX + "visibility"]) {
226
250
  visibility = definition[ORD_EXTENSIONS_PREFIX + "visibility"];
@@ -248,7 +272,7 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
248
272
 
249
273
  const paths = _generatePaths(serviceName, serviceDefinition);
250
274
  const apiResources = [];
251
- const ordId = `${appConfig.ordNamespace}:apiResource:${serviceName}:v1`;
275
+ const ordId = `${appConfig.ordNamespace}:apiResource:${_getGroupNameWithNestedNamespace(serviceDefinition, appConfig)}:v1`;
252
276
 
253
277
  paths.forEach((generatedPath) => {
254
278
  let resourceDefinitions = [
@@ -273,7 +297,7 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
273
297
  lastUpdate: appConfig.lastUpdate,
274
298
  visibility,
275
299
  partOfPackage: packageId,
276
- partOfGroups: [_getGroupID(serviceName, defaults.groupTypeId, appConfig)],
300
+ partOfGroups: [_getGroupID(serviceDefinition, defaults.groupTypeId, appConfig)],
277
301
  releaseStatus: "active",
278
302
  apiProtocol: generatedPath.kind === "odata" ? "odata-v4" : generatedPath.kind,
279
303
  resourceDefinitions: resourceDefinitions,
@@ -285,9 +309,8 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
285
309
  ...ordExtensions,
286
310
  };
287
311
 
288
- if (serviceDefinition[DATA_PRODUCT_ANNOTATION] === DATA_PRODUCT_TYPE.primary) {
312
+ if (isPrimaryDataProductService(serviceDefinition)) {
289
313
  obj.apiProtocol = "rest";
290
- obj.visibility = RESOURCE_VISIBILITY.internal;
291
314
  obj.direction = "outbound";
292
315
  obj.implementationStandard = "sap.dp:data-subscription-api:v1";
293
316
  obj.entryPoints = [];
@@ -301,8 +324,6 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
301
324
  accessStrategies,
302
325
  ),
303
326
  ];
304
-
305
- apiResources.push(obj);
306
327
  }
307
328
 
308
329
  if (obj.visibility !== RESOURCE_VISIBILITY.private) {
@@ -330,7 +351,7 @@ const createEventResourceTemplate = (serviceName, serviceDefinition, appConfig,
330
351
  const ordExtensions = readORDExtensions(serviceDefinition);
331
352
  const visibility = _handleVisibility(ordExtensions, serviceDefinition);
332
353
  const packageId = _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.event, visibility);
333
- const ordId = `${appConfig.ordNamespace}:eventResource:${serviceName}:v1`;
354
+ const ordId = `${appConfig.ordNamespace}:eventResource:${_getGroupNameWithNestedNamespace(serviceDefinition, appConfig)}:v1`;
334
355
  const entityTypeMappings = _getEntityTypeMappings(serviceDefinition);
335
356
 
336
357
  let obj = {
@@ -348,7 +369,7 @@ const createEventResourceTemplate = (serviceName, serviceDefinition, appConfig,
348
369
  lastUpdate: appConfig.lastUpdate,
349
370
  releaseStatus: "active",
350
371
  partOfPackage: packageId,
351
- partOfGroups: [_getGroupID(serviceName, defaults.groupTypeId, appConfig)],
372
+ partOfGroups: [_getGroupID(serviceDefinition, defaults.groupTypeId, appConfig)],
352
373
  visibility,
353
374
  resourceDefinitions: [
354
375
  _getResourceDefinition("asyncapi-v2", "json", ordId, serviceName, "asyncapi2.json", accessStrategies),
@@ -363,13 +384,18 @@ const createEventResourceTemplate = (serviceName, serviceDefinition, appConfig,
363
384
  : [];
364
385
  };
365
386
 
387
+ function isPrimaryDataProductService(serviceDefinition) {
388
+ return serviceDefinition[DATA_PRODUCT_ANNOTATION] === DATA_PRODUCT_TYPE.primary;
389
+ }
390
+
366
391
  function _getEntityTypeMappings(definitionObj) {
367
392
  if (!definitionObj.entities) {
368
393
  return;
369
394
  }
370
- const entities = Object.values(definitionObj.entities)
371
- .flatMap((entity) => _flattenEntityGraph(entity))
372
- .map(createEntityTypeMappingsItemTemplate);
395
+ const entities = Object.values(definitionObj.entities).flatMap((entity) => {
396
+ const entityData = _flattenEntityGraph(entity).map(createEntityTypeMappingsItemTemplate);
397
+ return _.uniqBy(entityData, CONTENT_MERGE_KEY);
398
+ });
373
399
  const entityTypeTargets = _.uniqBy(entities, CONTENT_MERGE_KEY)
374
400
  .filter((entity) => entity !== undefined)
375
401
  .map(({ ordId }) => ({
@@ -396,8 +422,15 @@ function _flattenEntityGraph(currentEntity, processedEntities = []) {
396
422
  if (processedEntities.includes(target)) {
397
423
  return;
398
424
  }
399
- processedEntities = [...processedEntities, target];
400
425
  assertionsTodo.push(entity);
426
+
427
+ /*
428
+ the next line operates on heap memory so that the same entity is not processed again
429
+ heap memory is used so that the check is the same in:
430
+ a > b > c > b scenario (runs first)
431
+ a > d > c > ... scenario (runs later and does not process c again)
432
+ */
433
+ processedEntities.push(target);
401
434
  });
402
435
 
403
436
  return [currentEntity, ...assertionsTodo.flatMap((entity) => _flattenEntityGraph(entity, processedEntities))];
package/lib/utils.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Checks if at least one policy level in the array is SAP.
3
+ *
4
+ * @param {string[]} policyLevels - Array of policy levels.
5
+ */
6
+ function hasSAPPolicyLevel(policyLevels) {
7
+ return policyLevels.some((policyLevel) => policyLevel.split(":")[0].toLowerCase() === "sap");
8
+ }
9
+
10
+ module.exports = {
11
+ hasSAPPolicyLevel,
12
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/ord",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "CAP Plugin for generating ORD document.",
5
5
  "repository": "cap-js/ord",
6
6
  "author": "SAP SE (https://www.sap.com)",