@cap-js/ord 1.3.8 → 1.3.10
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 +3 -1
- package/lib/constants.js +3 -0
- package/lib/defaults.js +39 -1
- package/lib/templates.js +79 -4
- package/package.json +3 -3
package/lib/build.js
CHANGED
|
@@ -10,7 +10,9 @@ module.exports = class OrdBuildPlugin extends cds_dk.build.Plugin {
|
|
|
10
10
|
static taskDefaults = { src: cds.env.folders.srv };
|
|
11
11
|
|
|
12
12
|
init() {
|
|
13
|
-
this.task.dest
|
|
13
|
+
if (this.task.dest === undefined) {
|
|
14
|
+
this.task.dest = path.join(cds.root, BUILD_DEFAULT_PATH);
|
|
15
|
+
}
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
async _writeResourcesFiles(resObj, model, promises) {
|
package/lib/constants.js
CHANGED
|
@@ -44,6 +44,8 @@ const CONTENT_MERGE_KEY = "ordId";
|
|
|
44
44
|
|
|
45
45
|
const DATA_PRODUCT_ANNOTATION = "@DataIntegration.dataProduct.type";
|
|
46
46
|
|
|
47
|
+
const DATA_PRODUCT_SIMPLE_ANNOTATION = "@data.product";
|
|
48
|
+
|
|
47
49
|
const DATA_PRODUCT_TYPE = Object.freeze({
|
|
48
50
|
primary: "primary",
|
|
49
51
|
});
|
|
@@ -106,6 +108,7 @@ module.exports = {
|
|
|
106
108
|
COMPILER_TYPES,
|
|
107
109
|
CONTENT_MERGE_KEY,
|
|
108
110
|
DATA_PRODUCT_ANNOTATION,
|
|
111
|
+
DATA_PRODUCT_SIMPLE_ANNOTATION,
|
|
109
112
|
DATA_PRODUCT_TYPE,
|
|
110
113
|
DESCRIPTION_PREFIX,
|
|
111
114
|
ENTITY_RELATIONSHIP_ANNOTATION,
|
package/lib/defaults.js
CHANGED
|
@@ -12,6 +12,26 @@ const packageTypes = [
|
|
|
12
12
|
{ tag: "-dataProduct", type: "Data Products" },
|
|
13
13
|
];
|
|
14
14
|
|
|
15
|
+
const stringTypeCheck = (value) => typeof value === "string";
|
|
16
|
+
const arrayTypeCheck = (value) => Array.isArray(value);
|
|
17
|
+
|
|
18
|
+
// see ref: https://pages.github.tools.sap/CentralEngineering/open-resource-discovery-specification/spec-v1/interfaces/document#package
|
|
19
|
+
const packageTypeChecks = {
|
|
20
|
+
policyLevels: arrayTypeCheck,
|
|
21
|
+
packageLinks: arrayTypeCheck,
|
|
22
|
+
links: arrayTypeCheck,
|
|
23
|
+
licenseType: stringTypeCheck,
|
|
24
|
+
supportInfo: stringTypeCheck,
|
|
25
|
+
vendor: stringTypeCheck,
|
|
26
|
+
countries: arrayTypeCheck,
|
|
27
|
+
lineOfBusiness: arrayTypeCheck,
|
|
28
|
+
industry: arrayTypeCheck,
|
|
29
|
+
runtimeRestriction: stringTypeCheck,
|
|
30
|
+
tags: arrayTypeCheck,
|
|
31
|
+
labels: arrayTypeCheck,
|
|
32
|
+
documentationLabels: arrayTypeCheck,
|
|
33
|
+
};
|
|
34
|
+
|
|
15
35
|
const regexWithRemoval = (name) => {
|
|
16
36
|
return name?.replace(/[^a-zA-Z0-9]/g, "");
|
|
17
37
|
};
|
|
@@ -36,6 +56,22 @@ const generateUniquePackageOrdId = (ordNamespace, name, tag, visibility) => {
|
|
|
36
56
|
return `${ordNamespace}:package:${regexWithRemoval(name)}${tag}${visibilitySuffix}:v1`;
|
|
37
57
|
};
|
|
38
58
|
|
|
59
|
+
const mapEnvPackageInfo = (packageConfig) => {
|
|
60
|
+
if (!packageConfig) {
|
|
61
|
+
return { vendor: undefined };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const filteredEnv = {};
|
|
65
|
+
for (const key in packageConfig) {
|
|
66
|
+
const value = packageConfig[key];
|
|
67
|
+
const checkFunction = packageTypeChecks[key];
|
|
68
|
+
if (value && checkFunction && checkFunction(value)) {
|
|
69
|
+
filteredEnv[key] = value;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return filteredEnv;
|
|
73
|
+
};
|
|
74
|
+
|
|
39
75
|
/**
|
|
40
76
|
* Module containing default configuration for ORD Document.
|
|
41
77
|
* @module defaults
|
|
@@ -58,7 +94,7 @@ module.exports = {
|
|
|
58
94
|
const name = appConfig.appName;
|
|
59
95
|
const ordNamespace = appConfig.ordNamespace;
|
|
60
96
|
const productsOrdId = appConfig.existingProductORDId || appConfig.products?.[0]?.ordId;
|
|
61
|
-
const vendor = appConfig
|
|
97
|
+
const { vendor, ...envValues } = mapEnvPackageInfo(appConfig?.env?.packages?.[0]);
|
|
62
98
|
if (hasSAPPolicyLevel(appConfig.policyLevels)) {
|
|
63
99
|
return _.uniqBy(
|
|
64
100
|
packageTypes.flatMap(({ tag, type }) =>
|
|
@@ -69,6 +105,7 @@ module.exports = {
|
|
|
69
105
|
version: "1.0.0",
|
|
70
106
|
...(productsOrdId && { partOfProducts: [productsOrdId || defaultProductOrdId(name)] }),
|
|
71
107
|
vendor: vendor || "customer:vendor:Customer:",
|
|
108
|
+
...envValues,
|
|
72
109
|
})),
|
|
73
110
|
),
|
|
74
111
|
"ordId",
|
|
@@ -82,6 +119,7 @@ module.exports = {
|
|
|
82
119
|
version: "1.0.0",
|
|
83
120
|
...(productsOrdId && { partOfProducts: [productsOrdId || defaultProductOrdId(name)] }),
|
|
84
121
|
vendor: vendor || "customer:vendor:Customer:",
|
|
122
|
+
...envValues,
|
|
85
123
|
},
|
|
86
124
|
];
|
|
87
125
|
}
|
package/lib/templates.js
CHANGED
|
@@ -5,6 +5,7 @@ const _ = require("lodash");
|
|
|
5
5
|
const {
|
|
6
6
|
AUTHENTICATION_TYPE,
|
|
7
7
|
DATA_PRODUCT_ANNOTATION,
|
|
8
|
+
DATA_PRODUCT_SIMPLE_ANNOTATION,
|
|
8
9
|
DATA_PRODUCT_TYPE,
|
|
9
10
|
DESCRIPTION_PREFIX,
|
|
10
11
|
ENTITY_RELATIONSHIP_ANNOTATION,
|
|
@@ -189,6 +190,13 @@ const createGroupsTemplateForService = (serviceName, serviceDefinition, appConfi
|
|
|
189
190
|
return undefined;
|
|
190
191
|
}
|
|
191
192
|
|
|
193
|
+
const visibility = _handleVisibility(ordExtensions, serviceDefinition, appConfig.env?.defaultVisibility);
|
|
194
|
+
if (visibility === RESOURCE_VISIBILITY.private) {
|
|
195
|
+
// If the visibility of the service is private, do not create a group
|
|
196
|
+
Logger.debug("Skipping group creation for private service:", serviceName);
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
192
200
|
const groupId = _getGroupID(serviceDefinition, defaults.groupTypeId, appConfig);
|
|
193
201
|
return {
|
|
194
202
|
groupId: groupId,
|
|
@@ -257,7 +265,13 @@ function _handleVisibility(ordExtensions, definition, defaultVisibility = RESOUR
|
|
|
257
265
|
let visibility;
|
|
258
266
|
//check for supported custom visibility value in defaultVisibility variable
|
|
259
267
|
if (!ALLOWED_VISIBILITY.includes(defaultVisibility)) {
|
|
260
|
-
Logger.warn(
|
|
268
|
+
Logger.warn(
|
|
269
|
+
"Default visibility",
|
|
270
|
+
defaultVisibility,
|
|
271
|
+
"is not supported. Using",
|
|
272
|
+
RESOURCE_VISIBILITY.public,
|
|
273
|
+
"as fallback.",
|
|
274
|
+
);
|
|
261
275
|
defaultVisibility = RESOURCE_VISIBILITY.public;
|
|
262
276
|
}
|
|
263
277
|
// Determine visibility
|
|
@@ -295,7 +309,34 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
|
|
|
295
309
|
|
|
296
310
|
const paths = _generatePaths(serviceName, serviceDefinition);
|
|
297
311
|
const apiResources = [];
|
|
298
|
-
|
|
312
|
+
|
|
313
|
+
// Handle version suffix extraction for primary data product services
|
|
314
|
+
let cleanServiceName,
|
|
315
|
+
version,
|
|
316
|
+
semanticVersion,
|
|
317
|
+
extracted = null;
|
|
318
|
+
if (isPrimaryDataProductService(serviceDefinition)) {
|
|
319
|
+
extracted = _extractVersionFromServiceName(serviceDefinition.name);
|
|
320
|
+
if (extracted) {
|
|
321
|
+
// Create a temporary service definition with the clean name for namespace processing
|
|
322
|
+
const cleanServiceDefinition = { ...serviceDefinition, name: extracted.cleanName };
|
|
323
|
+
cleanServiceName = _getGroupNameWithNestedNamespace(cleanServiceDefinition, appConfig);
|
|
324
|
+
version = extracted.version;
|
|
325
|
+
semanticVersion = extracted.semanticVersion;
|
|
326
|
+
} else {
|
|
327
|
+
// Invalid pattern - use current behavior
|
|
328
|
+
cleanServiceName = _getGroupNameWithNestedNamespace(serviceDefinition, appConfig);
|
|
329
|
+
version = "v1";
|
|
330
|
+
semanticVersion = "1.0.0";
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
// Non-data product - use current behavior
|
|
334
|
+
cleanServiceName = _getGroupNameWithNestedNamespace(serviceDefinition, appConfig);
|
|
335
|
+
version = "v1";
|
|
336
|
+
semanticVersion = "1.0.0";
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const ordId = `${appConfig.ordNamespace}:apiResource:${cleanServiceName}:${version}`;
|
|
299
340
|
|
|
300
341
|
paths.forEach((generatedPath) => {
|
|
301
342
|
let resourceDefinitions = [
|
|
@@ -317,7 +358,7 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
|
|
|
317
358
|
title: serviceDefinition["@title"] ?? serviceDefinition["@Common.Label"] ?? serviceName,
|
|
318
359
|
shortDescription: SHORT_DESCRIPTION_PREFIX + serviceName,
|
|
319
360
|
description: serviceDefinition["@Core.Description"] ?? DESCRIPTION_PREFIX + serviceName,
|
|
320
|
-
version:
|
|
361
|
+
version: semanticVersion,
|
|
321
362
|
lastUpdate: appConfig.lastUpdate,
|
|
322
363
|
visibility,
|
|
323
364
|
partOfPackage: packageId,
|
|
@@ -339,6 +380,10 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
|
|
|
339
380
|
obj.direction = "outbound";
|
|
340
381
|
obj.implementationStandard = "sap.dp:data-subscription-api:v1";
|
|
341
382
|
obj.entryPoints = [];
|
|
383
|
+
if (extracted) {
|
|
384
|
+
// Overwrite partOfGroups
|
|
385
|
+
obj.partOfGroups = [`${defaults.groupTypeId}:${appConfig.ordNamespace}:${cleanServiceName}`];
|
|
386
|
+
}
|
|
342
387
|
obj.resourceDefinitions = [
|
|
343
388
|
_getResourceDefinition(
|
|
344
389
|
"sap-csn-interop-effective-v1",
|
|
@@ -412,7 +457,10 @@ const createEventResourceTemplate = (serviceName, serviceDefinition, appConfig,
|
|
|
412
457
|
};
|
|
413
458
|
|
|
414
459
|
function isPrimaryDataProductService(serviceDefinition) {
|
|
415
|
-
return
|
|
460
|
+
return (
|
|
461
|
+
serviceDefinition[DATA_PRODUCT_ANNOTATION] === DATA_PRODUCT_TYPE.primary ||
|
|
462
|
+
!!serviceDefinition[DATA_PRODUCT_SIMPLE_ANNOTATION]
|
|
463
|
+
);
|
|
416
464
|
}
|
|
417
465
|
|
|
418
466
|
function _getEntityTypeMappings(definitionObj) {
|
|
@@ -483,6 +531,32 @@ function _flattenEntityGraph(currentEntity, processedEntities = []) {
|
|
|
483
531
|
return [currentEntity, ...assertionsTodo.flatMap((entity) => _flattenEntityGraph(entity, processedEntities))];
|
|
484
532
|
}
|
|
485
533
|
|
|
534
|
+
/**
|
|
535
|
+
* Extracts version suffix from service name for data product services.
|
|
536
|
+
* Only accepts pattern: .v<number> (e.g., .v0, .v1, .v2, .v10)
|
|
537
|
+
* Rejects patterns like: .v1.1, .v1.0, .version1, .beta
|
|
538
|
+
*
|
|
539
|
+
* @param {string} serviceName The full service name
|
|
540
|
+
* @returns {Object|null} Object with cleanName, version, and semanticVersion, or null if invalid pattern
|
|
541
|
+
*/
|
|
542
|
+
function _extractVersionFromServiceName(serviceName) {
|
|
543
|
+
// Only match pattern: .v<number> (where number is 1 or more digits)
|
|
544
|
+
const versionPattern = /\.v(\d+)$/;
|
|
545
|
+
const match = serviceName.match(versionPattern);
|
|
546
|
+
|
|
547
|
+
if (!match) {
|
|
548
|
+
return null; // No valid version suffix found
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const versionNumber = parseInt(match[1], 10);
|
|
552
|
+
|
|
553
|
+
return {
|
|
554
|
+
cleanName: serviceName.replace(versionPattern, ""),
|
|
555
|
+
version: `v${versionNumber}`,
|
|
556
|
+
semanticVersion: `${versionNumber}.0.0`,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
486
560
|
function _getPackageID(namespace, packageIds, resourceType, visibility = RESOURCE_VISIBILITY.public) {
|
|
487
561
|
if (!packageIds) return;
|
|
488
562
|
|
|
@@ -531,4 +605,5 @@ module.exports = {
|
|
|
531
605
|
_getExposedEntityTypes,
|
|
532
606
|
_propagateORDVisibility,
|
|
533
607
|
_handleVisibility,
|
|
608
|
+
isPrimaryDataProductService,
|
|
534
609
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js/ord",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.10",
|
|
4
4
|
"description": "CAP Plugin for generating ORD document.",
|
|
5
5
|
"repository": "cap-js/ord",
|
|
6
6
|
"author": "SAP SE (https://www.sap.com)",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"eslint": "^9.2.0",
|
|
28
|
-
"jest": "^
|
|
29
|
-
"prettier": "3.
|
|
28
|
+
"jest": "^30.0.0",
|
|
29
|
+
"prettier": "3.6.2"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"@sap/cds": ">=8.9.4",
|