@cap-js/ord 1.3.5 → 1.3.6

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/build.js CHANGED
@@ -3,6 +3,7 @@ const cds_dk = require("@sap/cds-dk");
3
3
  const path = require("path");
4
4
  const _ = require("lodash");
5
5
  const { ord, getMetadata } = require("./index");
6
+ const cliProgress = require("cli-progress");
6
7
  const { BUILD_DEFAULT_PATH, ORD_SERVICE_NAME, ORD_DOCUMENT_FILE_NAME } = require("./constants");
7
8
 
8
9
  module.exports = class OrdBuildPlugin extends cds_dk.build.Plugin {
@@ -13,27 +14,58 @@ module.exports = class OrdBuildPlugin extends cds_dk.build.Plugin {
13
14
  }
14
15
 
15
16
  async _writeResourcesFiles(resObj, model, promises) {
16
- for (const resource of resObj) {
17
- if (resource.ordId.includes(ORD_SERVICE_NAME) || !resource.resourceDefinitions) {
18
- continue;
17
+ let totalFiles = resObj.reduce((total, resource) => {
18
+ if (!resource.ordId.includes(ORD_SERVICE_NAME) && resource.resourceDefinitions) {
19
+ return total + resource.resourceDefinitions.length;
19
20
  }
21
+ return total;
22
+ }, 0);
20
23
 
21
- for (const resourceDefinition of resource.resourceDefinitions) {
22
- const url = resourceDefinition.url;
23
- const fileName = path.join(resource.ordId, url.split("/").pop());
24
- try {
25
- const { _, response } = await getMetadata(url, model); // eslint-disable-line no-unused-vars
26
- promises.push(
27
- this.write(response)
28
- .to(fileName.replace(/:/g, "_"))
29
- .catch((err) => {
30
- console.log("Error", `Failed to write file ${fileName}: ${err.message}`);
31
- }),
32
- );
33
- } catch (error) {
34
- console.log("Error", `Failed to get metadata for ${url}: ${error.message}`);
24
+ const progressBar = new cliProgress.SingleBar({
25
+ format: "Processing resourcesFiles [{bar}] {percentage}% | {value}/{total} | ETA: {eta}s",
26
+ barCompleteChar: "",
27
+ barIncompleteChar: "░",
28
+ stopOnComplete: true,
29
+ });
30
+
31
+ let warnings = [];
32
+ let completed = 0;
33
+ progressBar.start(totalFiles, 0);
34
+
35
+ try {
36
+ for (const resource of resObj) {
37
+ if (resource.ordId.includes(ORD_SERVICE_NAME) || !resource.resourceDefinitions) continue;
38
+ for (const resourceDefinition of resource.resourceDefinitions) {
39
+ try {
40
+ const { _, response } = await getMetadata(resourceDefinition.url, model); // eslint-disable-line no-unused-vars
41
+ const fileName = path
42
+ .join(resource.ordId, resourceDefinition.url.split("/").pop())
43
+ .replace(/:/g, "_");
44
+ promises.push(
45
+ this.write(response)
46
+ .to(fileName)
47
+ .catch((err) => {
48
+ warnings.push(`Error writing file ${fileName}: ${err.message}`);
49
+ }),
50
+ );
51
+ completed++;
52
+ progressBar.update(completed);
53
+ } catch (error) {
54
+ completed++;
55
+ progressBar.update(completed);
56
+ warnings.push(`Error getting metadata for ${resourceDefinition.url}: ${error.message}`);
57
+ } finally {
58
+ completed++;
59
+ progressBar.update(completed);
60
+ }
35
61
  }
36
62
  }
63
+ await Promise.all(promises);
64
+ } catch (error) {
65
+ warnings.push("Failed to process resources: " + error.message);
66
+ throw error;
67
+ } finally {
68
+ progressBar.stop();
37
69
  }
38
70
  }
39
71
 
@@ -79,7 +111,6 @@ module.exports = class OrdBuildPlugin extends cds_dk.build.Plugin {
79
111
  if (ordDocument.eventResources && ordDocument.eventResources.length > 0) {
80
112
  await this._writeResourcesFiles(ordDocument.eventResources, model, promises);
81
113
  }
82
-
83
114
  return Promise.all(promises);
84
115
  }
85
116
  };
package/lib/constants.js CHANGED
@@ -83,6 +83,14 @@ const RESOURCE_VISIBILITY = Object.freeze({
83
83
  private: "private",
84
84
  });
85
85
 
86
+ const ALLOWED_VISIBILITY = Object.values(RESOURCE_VISIBILITY);
87
+
88
+ const IMPLEMENTATIONSTANDARD_VERSIONS = Object.freeze({
89
+ v1: "sap:ord-document-api:v1",
90
+ });
91
+
92
+ const SUPPORTED_IMPLEMENTATIONSTANDARD_VERSIONS = Object.values(IMPLEMENTATIONSTANDARD_VERSIONS);
93
+
86
94
  const SHORT_DESCRIPTION_PREFIX = "Short description of ";
87
95
 
88
96
  const SEM_VERSION_REGEX =
@@ -111,6 +119,9 @@ module.exports = {
111
119
  ORD_RESOURCE_TYPE,
112
120
  ORD_SERVICE_NAME,
113
121
  RESOURCE_VISIBILITY,
122
+ ALLOWED_VISIBILITY,
123
+ IMPLEMENTATIONSTANDARD_VERSIONS,
124
+ SUPPORTED_IMPLEMENTATIONSTANDARD_VERSIONS,
114
125
  SHORT_DESCRIPTION_PREFIX,
115
126
  SEM_VERSION_REGEX,
116
127
  };
package/lib/templates.js CHANGED
@@ -13,6 +13,8 @@ const {
13
13
  ORD_ODM_ENTITY_NAME_ANNOTATION,
14
14
  ORD_RESOURCE_TYPE,
15
15
  RESOURCE_VISIBILITY,
16
+ ALLOWED_VISIBILITY,
17
+ SUPPORTED_IMPLEMENTATIONSTANDARD_VERSIONS,
16
18
  SEM_VERSION_REGEX,
17
19
  SHORT_DESCRIPTION_PREFIX,
18
20
  CONTENT_MERGE_KEY,
@@ -139,7 +141,7 @@ function _getEntityVersion(entity) {
139
141
  const entityVersion = entity.ordId.split(":").pop();
140
142
  const version = entityVersion.replace("v", "") + ".0.0"; // TODO: version can be stated/overwritten by annotation
141
143
  if (!SEM_VERSION_REGEX.test(version)) {
142
- Logger.warn(`Entity version "${version}" is not a valid semantic version.`);
144
+ Logger.warn("Entity version", version, "is not a valid semantic version.");
143
145
  }
144
146
  return version;
145
147
  }
@@ -240,16 +242,37 @@ const createEntityTypeTemplate = (appConfig, packageIds, entity) => {
240
242
  };
241
243
  };
242
244
 
243
- function _handleVisibility(ordExtensions, definition) {
245
+ /**
246
+ * Determines the visibility of a resource based on provided extensions, definition, and default visibility.
247
+ *
248
+ * The function checks for custom visibility values, validates the default visibility,
249
+ * and applies specific rules based on the resource's definition and extensions.
250
+ *
251
+ * @param {Object} ordExtensions - Extensions object containing resource metadata.
252
+ * @param {Object} definition - The resource definition object.
253
+ * @param {string} [defaultVisibility=RESOURCE_VISIBILITY.public] - The default visibility value.
254
+ * @returns {string} The resolved visibility value for the resource.
255
+ */
256
+ function _handleVisibility(ordExtensions, definition, defaultVisibility = RESOURCE_VISIBILITY.public) {
244
257
  let visibility;
258
+ //check for supported custom visibility value in defaultVisibility variable
259
+ if (!ALLOWED_VISIBILITY.includes(defaultVisibility)) {
260
+ Logger.warn("Default visibility", defaultVisibility, "is not supported. Using", RESOURCE_VISIBILITY.public, "as fallback.");
261
+ defaultVisibility = RESOURCE_VISIBILITY.public;
262
+ }
263
+ // Determine visibility
245
264
  if (isPrimaryDataProductService(definition)) {
246
265
  visibility = RESOURCE_VISIBILITY.internal;
247
266
  } else if (ordExtensions.visibility) {
248
267
  visibility = ordExtensions.visibility;
249
268
  } else if (definition[ORD_EXTENSIONS_PREFIX + "visibility"]) {
250
269
  visibility = definition[ORD_EXTENSIONS_PREFIX + "visibility"];
251
- } else {
270
+ } else if (SUPPORTED_IMPLEMENTATIONSTANDARD_VERSIONS.includes(ordExtensions.implementationStandard)) {
271
+ // if the implementationStandard is for example sap:ord-document-api:v1, it should be public by default
252
272
  visibility = RESOURCE_VISIBILITY.public;
273
+ } else if (ALLOWED_VISIBILITY.includes(defaultVisibility)) {
274
+ // Default visibility from config file
275
+ visibility = defaultVisibility;
253
276
  }
254
277
  return visibility;
255
278
  }
@@ -267,7 +290,7 @@ function _handleVisibility(ordExtensions, definition) {
267
290
  */
268
291
  const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, packageIds, accessStrategies) => {
269
292
  const ordExtensions = readORDExtensions(serviceDefinition);
270
- const visibility = _handleVisibility(ordExtensions, serviceDefinition);
293
+ const visibility = _handleVisibility(ordExtensions, serviceDefinition, appConfig.env?.defaultVisibility);
271
294
  const packageId = _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.api, visibility);
272
295
 
273
296
  const paths = _generatePaths(serviceName, serviceDefinition);
@@ -287,6 +310,7 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
287
310
  }
288
311
 
289
312
  const entityTypeMappings = _getEntityTypeMappings(serviceDefinition);
313
+ const exposedEntityTypes = _getExposedEntityTypes(serviceDefinition);
290
314
 
291
315
  let obj = {
292
316
  ordId,
@@ -306,6 +330,7 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
306
330
  supported: "no",
307
331
  },
308
332
  ...(entityTypeMappings ? { entityTypeMappings } : {}),
333
+ ...(exposedEntityTypes ? { exposedEntityTypes } : []),
309
334
  ...ordExtensions,
310
335
  };
311
336
 
@@ -349,10 +374,11 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
349
374
  */
350
375
  const createEventResourceTemplate = (serviceName, serviceDefinition, appConfig, packageIds, accessStrategies) => {
351
376
  const ordExtensions = readORDExtensions(serviceDefinition);
352
- const visibility = _handleVisibility(ordExtensions, serviceDefinition);
377
+ const visibility = _handleVisibility(ordExtensions, serviceDefinition, appConfig.env?.defaultVisibility);
353
378
  const packageId = _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.event, visibility);
354
379
  const ordId = `${appConfig.ordNamespace}:eventResource:${_getGroupNameWithNestedNamespace(serviceDefinition, appConfig)}:v1`;
355
380
  const entityTypeMappings = _getEntityTypeMappings(serviceDefinition);
381
+ const exposedEntityTypes = _getExposedEntityTypes(serviceDefinition);
356
382
 
357
383
  let obj = {
358
384
  ordId,
@@ -376,6 +402,7 @@ const createEventResourceTemplate = (serviceName, serviceDefinition, appConfig,
376
402
  ],
377
403
  extensible: { supported: "no" },
378
404
  ...(entityTypeMappings ? { entityTypeMappings } : {}),
405
+ ...(exposedEntityTypes ? { exposedEntityTypes } : []),
379
406
  ...ordExtensions,
380
407
  };
381
408
 
@@ -408,6 +435,26 @@ function _getEntityTypeMappings(definitionObj) {
408
435
  }
409
436
  }
410
437
 
438
+ function _getExposedEntityTypes(definitionObj) {
439
+ if (!definitionObj.entities) {
440
+ return;
441
+ }
442
+ const entities = Object.values(definitionObj.entities).flatMap((entity) => {
443
+ const entityData = _flattenEntityGraph(entity).map(createEntityTypeMappingsItemTemplate);
444
+ return _.uniqBy(entityData, CONTENT_MERGE_KEY);
445
+ });
446
+ const exposedEntityTypes = _.uniqBy(entities, CONTENT_MERGE_KEY)
447
+ .filter((entity) => entity !== undefined)
448
+ .map(({ ordId }) => ({
449
+ ordId,
450
+ }));
451
+ if (exposedEntityTypes.length > 0) {
452
+ return exposedEntityTypes;
453
+ } else {
454
+ return;
455
+ }
456
+ }
457
+
411
458
  function _flattenEntityGraph(currentEntity, processedEntities = []) {
412
459
  if (!currentEntity.associations) {
413
460
  return [currentEntity];
@@ -481,5 +528,7 @@ module.exports = {
481
528
  createEventResourceTemplate,
482
529
  _getPackageID,
483
530
  _getEntityTypeMappings,
531
+ _getExposedEntityTypes,
484
532
  _propagateORDVisibility,
533
+ _handleVisibility,
485
534
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/ord",
3
- "version": "1.3.5",
3
+ "version": "1.3.6",
4
4
  "description": "CAP Plugin for generating ORD document.",
5
5
  "repository": "cap-js/ord",
6
6
  "author": "SAP SE (https://www.sap.com)",
@@ -35,12 +35,16 @@
35
35
  "@cap-js/asyncapi": "^1.0.3",
36
36
  "@cap-js/openapi": "^1.2.1",
37
37
  "bcrypt": "^5.1.1",
38
+ "cli-progress": "^3.12.0",
38
39
  "lodash": "^4.17.21"
39
40
  },
40
41
  "cds": {
41
42
  "requires": {
42
43
  "SAP ORD Service": {
43
44
  "model": "@cap-js/ord/lib/ord-service"
45
+ },
46
+ "[java]": {
47
+ "SAP ORD Service": false
44
48
  }
45
49
  }
46
50
  }