@cap-js/ord 1.7.0 → 1.8.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/defaults.js +0 -11
- package/lib/integration-dependency.js +3 -2
- package/lib/ord.js +9 -14
- package/lib/protocol-resolver.js +3 -16
- package/lib/templates.js +97 -85
- package/package.json +2 -2
package/lib/defaults.js
CHANGED
|
@@ -122,17 +122,6 @@ module.exports = {
|
|
|
122
122
|
CONTENT_MERGE_KEY,
|
|
123
123
|
);
|
|
124
124
|
},
|
|
125
|
-
consumptionBundles: (appConfig) => [
|
|
126
|
-
{
|
|
127
|
-
ordId: `${regexWithRemoval(appConfig.appName)}:consumptionBundle:noAuth:v1`,
|
|
128
|
-
version: "1.0.0",
|
|
129
|
-
lastUpdate: appConfig.lastUpdate,
|
|
130
|
-
title: "Unprotected resources",
|
|
131
|
-
shortDescription: "If we have another protected API then it will be another object",
|
|
132
|
-
description:
|
|
133
|
-
"This Consumption Bundle contains all resources of the reference app which are unprotected and do not require authentication",
|
|
134
|
-
},
|
|
135
|
-
],
|
|
136
125
|
baseTemplate: (authConfig, tenant) => {
|
|
137
126
|
// Get access strategies from the provided authConfig
|
|
138
127
|
// If auth config is not available, fall back to empty array
|
|
@@ -89,11 +89,12 @@ function createIntegrationDependency(externalServices, appConfig, packageIds) {
|
|
|
89
89
|
|
|
90
90
|
// Read IntegrationDependency level config from cdsrc
|
|
91
91
|
const integrationDepConfig = appConfig.env?.integrationDependency || {};
|
|
92
|
+
const version = integrationDepConfig.version || "1.0.0";
|
|
92
93
|
|
|
93
94
|
return {
|
|
94
|
-
ordId: `${appConfig.ordNamespace}:${ORD_RESOURCE_TYPE.integrationDependency}:${INTEGRATION_DEPENDENCY_RESOURCE_NAME}:
|
|
95
|
+
ordId: `${appConfig.ordNamespace}:${ORD_RESOURCE_TYPE.integrationDependency}:${INTEGRATION_DEPENDENCY_RESOURCE_NAME}:v${version.split(".")[0]}`,
|
|
95
96
|
title: "External Dependencies",
|
|
96
|
-
version:
|
|
97
|
+
version: version,
|
|
97
98
|
releaseStatus: "active",
|
|
98
99
|
visibility: RESOURCE_VISIBILITY.public,
|
|
99
100
|
mandatory: false,
|
package/lib/ord.js
CHANGED
|
@@ -20,26 +20,21 @@ const {
|
|
|
20
20
|
|
|
21
21
|
const _getGroups = (csn, appConfig) => {
|
|
22
22
|
return appConfig.serviceNames
|
|
23
|
-
.
|
|
23
|
+
.map((name) => csn.definitions[name])
|
|
24
|
+
.flatMap((srvDefinition) => createGroupsTemplateForService(srvDefinition, appConfig))
|
|
24
25
|
.filter((resource) => !!resource);
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
const _getAPIResources = (csn, appConfig, packageIds, accessStrategies) => {
|
|
28
|
-
return appConfig.apiResourceNames
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
csn.definitions[apiResourceName],
|
|
32
|
-
appConfig,
|
|
33
|
-
packageIds,
|
|
34
|
-
accessStrategies,
|
|
35
|
-
),
|
|
36
|
-
);
|
|
29
|
+
return appConfig.apiResourceNames
|
|
30
|
+
.map((name) => csn.definitions[name])
|
|
31
|
+
.flatMap((srvDefinition) => createAPIResourceTemplate(srvDefinition, appConfig, packageIds, accessStrategies));
|
|
37
32
|
};
|
|
38
33
|
|
|
39
34
|
const _getEventResources = (csn, appConfig, packageIds, accessStrategies) => {
|
|
40
|
-
return appConfig.eventServiceNames
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
return appConfig.eventServiceNames
|
|
36
|
+
.map((name) => csn.definitions[name])
|
|
37
|
+
.flatMap((srvDefinition) => createEventResourceTemplate(srvDefinition, appConfig, packageIds, accessStrategies));
|
|
43
38
|
};
|
|
44
39
|
|
|
45
40
|
const _getProducts = (appConfig) => {
|
|
@@ -75,7 +70,6 @@ const _createDefaultORDDocument = (linkedCsn, appConfig, authConfig) => {
|
|
|
75
70
|
packages: packages,
|
|
76
71
|
description: appConfig.env?.description || defaults.description,
|
|
77
72
|
openResourceDiscovery: appConfig.env?.openResourceDiscovery || defaults.openResourceDiscovery,
|
|
78
|
-
consumptionBundles: appConfig.env?.consumptionBundles || defaults.consumptionBundles(appConfig),
|
|
79
73
|
|
|
80
74
|
// Conditionally added top-level properties
|
|
81
75
|
...(!entityTypes.length ? {} : { entityTypes: entityTypes }),
|
|
@@ -84,6 +78,7 @@ const _createDefaultORDDocument = (linkedCsn, appConfig, authConfig) => {
|
|
|
84
78
|
...(appConfig.existingProductORDId ? {} : { products: [products[0]] }),
|
|
85
79
|
...(!appConfig.serviceNames.length ? {} : { groups: _getGroups(linkedCsn, appConfig) }),
|
|
86
80
|
...(!integrationDependencies.length ? {} : { integrationDependencies: integrationDependencies }),
|
|
81
|
+
...(!appConfig.env?.consumptionBundles?.length ? {} : { consumptionBundles: appConfig.env.consumptionBundles }),
|
|
87
82
|
};
|
|
88
83
|
};
|
|
89
84
|
|
package/lib/protocol-resolver.js
CHANGED
|
@@ -7,18 +7,6 @@ const {
|
|
|
7
7
|
} = require("./constants");
|
|
8
8
|
const Logger = require("./logger");
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* Gets CAP endpoints for a service using CDS endpoints4().
|
|
12
|
-
*
|
|
13
|
-
* @param {string} serviceName The service name.
|
|
14
|
-
* @param {Object} srvDefinition The service definition object.
|
|
15
|
-
* @returns {Array} Raw endpoints from CDS.
|
|
16
|
-
*/
|
|
17
|
-
function _getCapEndpoints(serviceName, srvDefinition) {
|
|
18
|
-
const srvObj = { name: serviceName, definition: srvDefinition };
|
|
19
|
-
return cds.service.protocols.endpoints4(srvObj);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
10
|
/**
|
|
23
11
|
* Reads the explicit @protocol annotation from service definition.
|
|
24
12
|
*
|
|
@@ -42,13 +30,12 @@ function _getExplicitProtocol(srvDefinition) {
|
|
|
42
30
|
* - Rule B: Only fallback to OData when no explicit protocol
|
|
43
31
|
* - Rule C: Never produce [null] in entryPoints
|
|
44
32
|
*
|
|
45
|
-
* @param {string} serviceName The service name.
|
|
46
33
|
* @param {Object} srvDefinition The service definition object.
|
|
47
34
|
* @param {Object} options Configuration options.
|
|
48
35
|
* @param {Function} options.isPrimaryDataProduct Strategy function to check if service is primary data product.
|
|
49
36
|
* @returns {Array} Array with single {apiProtocol, entryPoints, hasResourceDefinitions} object, or empty array.
|
|
50
37
|
*/
|
|
51
|
-
function resolveApiResourceProtocol(
|
|
38
|
+
function resolveApiResourceProtocol(srvDefinition, options = {}) {
|
|
52
39
|
const { isPrimaryDataProduct = () => false } = options;
|
|
53
40
|
|
|
54
41
|
// 1. Primary Data Product - early return
|
|
@@ -62,7 +49,7 @@ function resolveApiResourceProtocol(serviceName, srvDefinition, options = {}) {
|
|
|
62
49
|
];
|
|
63
50
|
}
|
|
64
51
|
|
|
65
|
-
const capEndpoints =
|
|
52
|
+
const capEndpoints = cds.service.protocols.endpoints4({ name: srvDefinition.name, definition: srvDefinition });
|
|
66
53
|
const ordProtocols = [];
|
|
67
54
|
for (const endpoint of capEndpoints) {
|
|
68
55
|
if (PLUGIN_UNSUPPORTED_PROTOCOLS.includes(endpoint.kind)) {
|
|
@@ -107,7 +94,7 @@ function resolveApiResourceProtocol(serviceName, srvDefinition, options = {}) {
|
|
|
107
94
|
|
|
108
95
|
// 4. Handle explicit protocol with no CAP endpoint (Rule A)
|
|
109
96
|
if (!ordProtocols.some((p) => p.apiProtocol === protocol) && !CAP_TO_ORD_PROTOCOL_MAP[protocol]) {
|
|
110
|
-
Logger.warn(`Unknown protocol '${protocol}' is not supported, and skipped for service '${
|
|
97
|
+
Logger.warn(`Unknown protocol '${protocol}' is not supported, and skipped for service '${srvDefinition.name}'.`);
|
|
111
98
|
}
|
|
112
99
|
}
|
|
113
100
|
|
package/lib/templates.js
CHANGED
|
@@ -32,11 +32,11 @@ function unflatten(flattedObject) {
|
|
|
32
32
|
return result;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function readORDExtensions(model) {
|
|
35
|
+
function readORDExtensions(model, prefix = ORD_EXTENSIONS_PREFIX) {
|
|
36
36
|
const ordExtensions = {};
|
|
37
37
|
for (const key in model) {
|
|
38
|
-
if (key.startsWith(
|
|
39
|
-
const ordKey = key.replace(
|
|
38
|
+
if (key.startsWith(prefix)) {
|
|
39
|
+
const ordKey = key.replace(prefix, "");
|
|
40
40
|
ordExtensions[ordKey] = model[key];
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -63,9 +63,9 @@ const createEntityTypeMappingsItemTemplate = (entity) => {
|
|
|
63
63
|
const ordIdParts = entity[ENTITY_RELATIONSHIP_ANNOTATION].split(":");
|
|
64
64
|
const namespace = ordIdParts[0];
|
|
65
65
|
const entityName = ordIdParts[1];
|
|
66
|
-
const version = ordIdParts[2] || "
|
|
66
|
+
const version = entity[`${ORD_EXTENSIONS_PREFIX}version`] || ordIdParts[2]?.substring(1) || "1";
|
|
67
67
|
results.push({
|
|
68
|
-
ordId: `${namespace}:entityType:${entityName}
|
|
68
|
+
ordId: `${namespace}:entityType:${entityName}:v${version.split(".")[0]}`,
|
|
69
69
|
entityName,
|
|
70
70
|
...entity,
|
|
71
71
|
});
|
|
@@ -74,8 +74,8 @@ const createEntityTypeMappingsItemTemplate = (entity) => {
|
|
|
74
74
|
return results;
|
|
75
75
|
};
|
|
76
76
|
|
|
77
|
-
function _getGroupID(
|
|
78
|
-
return `${groupTypeId}:${appConfig.ordNamespace}:${_getCleanServiceName(
|
|
77
|
+
function _getGroupID(appConfig, srvDefinition) {
|
|
78
|
+
return `${defaults.groupTypeId}:${appConfig.ordNamespace}:${_getCleanServiceName(srvDefinition.name, appConfig)}`;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
function _startsWithNamespace(name, namespace) {
|
|
@@ -84,7 +84,7 @@ function _startsWithNamespace(name, namespace) {
|
|
|
84
84
|
return rest === "" || rest.startsWith(".");
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
function _getCleanServiceName(
|
|
87
|
+
function _getCleanServiceName(name, appConfig) {
|
|
88
88
|
let sortedName = name;
|
|
89
89
|
if (appConfig.internalNamespace && _startsWithNamespace(name, appConfig.internalNamespace)) {
|
|
90
90
|
sortedName = name.substring(appConfig.internalNamespace.length);
|
|
@@ -122,7 +122,7 @@ function _getTitleFromServiceName(srv) {
|
|
|
122
122
|
*/
|
|
123
123
|
function _getEntityVersion(entity) {
|
|
124
124
|
const entityVersion = entity.ordId.split(":").pop();
|
|
125
|
-
const version = entityVersion.replace("v", "") + ".0.0";
|
|
125
|
+
const version = entityVersion.replace("v", "") + ".0.0";
|
|
126
126
|
if (!SEM_VERSION_REGEX.test(version)) {
|
|
127
127
|
Logger.warn("Entity version", version, "is not a valid semantic version.");
|
|
128
128
|
}
|
|
@@ -162,31 +162,24 @@ function _getResourceDefinition(resourceType, mediaType, ordId, serviceName, fil
|
|
|
162
162
|
/**
|
|
163
163
|
* This is a template function to create group object of a service for groups array in ORD doc.
|
|
164
164
|
*
|
|
165
|
-
* @param {
|
|
166
|
-
* @param {object} serviceDefinition The definition of the service
|
|
165
|
+
* @param {object} srvDefinition The definition of the service
|
|
167
166
|
* @param {object} appConfig - The application configuration.
|
|
168
167
|
* @returns {Object} A group object.
|
|
169
168
|
*/
|
|
170
|
-
const createGroupsTemplateForService = (
|
|
171
|
-
const ordExtensions = readORDExtensions(
|
|
172
|
-
|
|
173
|
-
if (!serviceDefinition) {
|
|
174
|
-
Logger.warn("Unable to find service definition:", serviceName);
|
|
175
|
-
return undefined;
|
|
176
|
-
}
|
|
169
|
+
const createGroupsTemplateForService = (srvDefinition, appConfig) => {
|
|
170
|
+
const ordExtensions = readORDExtensions(srvDefinition);
|
|
171
|
+
const visibility = _handleVisibility(ordExtensions, srvDefinition, appConfig.env?.defaultVisibility);
|
|
177
172
|
|
|
178
|
-
const visibility = _handleVisibility(ordExtensions, serviceDefinition, appConfig.env?.defaultVisibility);
|
|
179
173
|
if (visibility === RESOURCE_VISIBILITY.private) {
|
|
180
174
|
// If the visibility of the service is private, do not create a group
|
|
181
|
-
Logger.info("Skipping group creation for private service:",
|
|
175
|
+
Logger.info("Skipping group creation for private service:", srvDefinition.name);
|
|
182
176
|
return undefined;
|
|
183
177
|
}
|
|
184
178
|
|
|
185
|
-
const groupId = _getGroupID(serviceDefinition, defaults.groupTypeId, appConfig);
|
|
186
179
|
return {
|
|
187
|
-
groupId:
|
|
180
|
+
groupId: _getGroupID(appConfig, srvDefinition),
|
|
188
181
|
groupTypeId: defaults.groupTypeId,
|
|
189
|
-
title: ordExtensions.title ?? _getTitleFromServiceName(
|
|
182
|
+
title: ordExtensions.title ?? _getTitleFromServiceName(srvDefinition.name),
|
|
190
183
|
};
|
|
191
184
|
};
|
|
192
185
|
|
|
@@ -221,7 +214,7 @@ const createEntityTypeTemplate = (appConfig, packageIds, entity) => {
|
|
|
221
214
|
return {
|
|
222
215
|
ordId: entity.ordId,
|
|
223
216
|
localId: entity.entityName,
|
|
224
|
-
title: entity["@title"] ?? entity["@Common.Label"] ?? entity.entityName,
|
|
217
|
+
title: entity["@title"] ?? entity["@Common.Label"] ?? entity["@EndUserText.label"] ?? entity.entityName,
|
|
225
218
|
shortDescription: SHORT_DESCRIPTION_PREFIX + entity.entityName,
|
|
226
219
|
description: DESCRIPTION_PREFIX + entity.entityName,
|
|
227
220
|
version: _getEntityVersion(entity),
|
|
@@ -280,26 +273,24 @@ function _handleVisibility(ordExtensions, definition, defaultVisibility = RESOUR
|
|
|
280
273
|
* This is a template function to create API Resource object for API Resource Array.
|
|
281
274
|
* Properties of an API resource can be overwritten by the ORD extensions. Example: visibility.
|
|
282
275
|
* Ensures proper visibility compliance by checking associated EntityTypes.
|
|
283
|
-
* @param {
|
|
284
|
-
* @param {object} serviceDefinition The definition of the service
|
|
276
|
+
* @param {object} srvDefinition The definition of the service
|
|
285
277
|
* @param {object} appConfig - The application configuration.
|
|
286
278
|
* @param {Array} packageIds - The available package identifiers.
|
|
287
279
|
* @param {Array} accessStrategies The array of accessStrategies objects
|
|
288
280
|
* @returns {Array} An array of objects for the API Resources.
|
|
289
281
|
*/
|
|
290
|
-
const createAPIResourceTemplate = (
|
|
291
|
-
const ordExtensions = readORDExtensions(
|
|
292
|
-
const visibility = _handleVisibility(ordExtensions,
|
|
282
|
+
const createAPIResourceTemplate = (srvDefinition, appConfig, packageIds, accessStrategies) => {
|
|
283
|
+
const ordExtensions = readORDExtensions(srvDefinition);
|
|
284
|
+
const visibility = _handleVisibility(ordExtensions, srvDefinition, appConfig.env?.defaultVisibility);
|
|
293
285
|
const packageId = _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.api, visibility);
|
|
294
|
-
|
|
295
|
-
const protocolResults = resolveApiResourceProtocol(serviceName, serviceDefinition, {
|
|
286
|
+
const protocolResults = resolveApiResourceProtocol(srvDefinition, {
|
|
296
287
|
isPrimaryDataProduct: isPrimaryDataProductService,
|
|
297
288
|
});
|
|
298
289
|
const apiResources = [];
|
|
299
290
|
|
|
300
291
|
// If no protocols were generated, skip this service
|
|
301
292
|
if (protocolResults.length === 0) {
|
|
302
|
-
Logger.info(`No supported protocols for service '${
|
|
293
|
+
Logger.info(`No supported protocols for service '${srvDefinition.name}', skipping API resource generation.`);
|
|
303
294
|
return apiResources;
|
|
304
295
|
}
|
|
305
296
|
|
|
@@ -308,31 +299,26 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
|
|
|
308
299
|
version,
|
|
309
300
|
semanticVersion,
|
|
310
301
|
extracted = null;
|
|
311
|
-
if (isPrimaryDataProductService(
|
|
312
|
-
extracted = _extractVersionFromServiceName(
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
version = extracted.version;
|
|
318
|
-
semanticVersion = extracted.semanticVersion;
|
|
319
|
-
} else {
|
|
320
|
-
// Invalid pattern - use current behavior
|
|
321
|
-
cleanServiceName = _getCleanServiceName(serviceDefinition, appConfig);
|
|
322
|
-
version = "v1";
|
|
323
|
-
semanticVersion = "1.0.0";
|
|
324
|
-
}
|
|
302
|
+
if (isPrimaryDataProductService(srvDefinition)) {
|
|
303
|
+
extracted = _extractVersionFromServiceName(srvDefinition.name);
|
|
304
|
+
|
|
305
|
+
cleanServiceName = _getCleanServiceName(extracted?.cleanName || srvDefinition.name, appConfig);
|
|
306
|
+
semanticVersion = ordExtensions.version || extracted?.semanticVersion || "1.0.0";
|
|
307
|
+
version = `v${semanticVersion.split(".")[0]}`;
|
|
325
308
|
} else {
|
|
326
309
|
// Non-data product - use current behavior
|
|
327
|
-
cleanServiceName = _getCleanServiceName(
|
|
328
|
-
|
|
329
|
-
|
|
310
|
+
cleanServiceName = _getCleanServiceName(srvDefinition.name, appConfig);
|
|
311
|
+
semanticVersion = ordExtensions.version || "1.0.0";
|
|
312
|
+
version = `v${semanticVersion.split(".")[0]}`;
|
|
330
313
|
}
|
|
331
314
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
protocolResults.forEach((protocolResult) => {
|
|
315
|
+
protocolResults.forEach((protocolResult, index) => {
|
|
335
316
|
const { apiProtocol, entryPoints, hasResourceDefinitions } = protocolResult;
|
|
317
|
+
const protocolExtensions = readORDExtensions(srvDefinition, `@protocol('${apiProtocol}').ORD.Extensions.`);
|
|
318
|
+
const ordId =
|
|
319
|
+
protocolExtensions.ordId ||
|
|
320
|
+
ordExtensions.ordId ||
|
|
321
|
+
`${appConfig.ordNamespace}:apiResource:${cleanServiceName}${index === 0 ? "" : `-${apiProtocol}`}:${version}`;
|
|
336
322
|
|
|
337
323
|
// Build resource definitions based on protocol
|
|
338
324
|
let resourceDefinitions = [];
|
|
@@ -344,7 +330,7 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
|
|
|
344
330
|
"sap-csn-interop-effective-v1",
|
|
345
331
|
"json",
|
|
346
332
|
ordId,
|
|
347
|
-
|
|
333
|
+
srvDefinition.name,
|
|
348
334
|
"csn.json",
|
|
349
335
|
accessStrategies,
|
|
350
336
|
),
|
|
@@ -352,7 +338,14 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
|
|
|
352
338
|
} else if (apiProtocol === ORD_API_PROTOCOL.REST) {
|
|
353
339
|
// REST only has OpenAPI, no EDMX
|
|
354
340
|
resourceDefinitions = [
|
|
355
|
-
_getResourceDefinition(
|
|
341
|
+
_getResourceDefinition(
|
|
342
|
+
"openapi-v3",
|
|
343
|
+
"json",
|
|
344
|
+
ordId,
|
|
345
|
+
srvDefinition.name,
|
|
346
|
+
"oas3.json",
|
|
347
|
+
accessStrategies,
|
|
348
|
+
),
|
|
356
349
|
];
|
|
357
350
|
} else if (apiProtocol === ORD_API_PROTOCOL.MCP) {
|
|
358
351
|
resourceDefinitions = [
|
|
@@ -360,7 +353,7 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
|
|
|
360
353
|
MCP_RESOURCE_DEFINITION_TYPE,
|
|
361
354
|
"json",
|
|
362
355
|
ordId,
|
|
363
|
-
|
|
356
|
+
srvDefinition.name,
|
|
364
357
|
"mcp.json",
|
|
365
358
|
accessStrategies,
|
|
366
359
|
),
|
|
@@ -371,38 +364,49 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
|
|
|
371
364
|
{
|
|
372
365
|
type: "graphql-sdl",
|
|
373
366
|
mediaType: "text/plain",
|
|
374
|
-
url: `/ord/v1/${ordId}/${
|
|
367
|
+
url: `/ord/v1/${ordId}/${srvDefinition.name}.graphql`,
|
|
375
368
|
accessStrategies: ensureAccessStrategies(accessStrategies, {
|
|
376
|
-
resourceName: `${
|
|
369
|
+
resourceName: `${srvDefinition.name} (graphql-sdl)`,
|
|
377
370
|
}),
|
|
378
371
|
},
|
|
379
372
|
];
|
|
380
373
|
} else if (apiProtocol === ORD_API_PROTOCOL.ODATA_V2) {
|
|
381
374
|
// openapi-v3 is not supported for OData V2, only EDMX
|
|
382
375
|
resourceDefinitions = [
|
|
383
|
-
_getResourceDefinition("edmx", "xml", ordId,
|
|
376
|
+
_getResourceDefinition("edmx", "xml", ordId, srvDefinition.name, "edmx", accessStrategies),
|
|
384
377
|
];
|
|
385
378
|
} else {
|
|
386
379
|
// odata-v4 and others have both OpenAPI and EDMX
|
|
387
380
|
resourceDefinitions = [
|
|
388
|
-
_getResourceDefinition(
|
|
389
|
-
|
|
381
|
+
_getResourceDefinition(
|
|
382
|
+
"openapi-v3",
|
|
383
|
+
"json",
|
|
384
|
+
ordId,
|
|
385
|
+
srvDefinition.name,
|
|
386
|
+
"oas3.json",
|
|
387
|
+
accessStrategies,
|
|
388
|
+
),
|
|
389
|
+
_getResourceDefinition("edmx", "xml", ordId, srvDefinition.name, "edmx", accessStrategies),
|
|
390
390
|
];
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
-
const exposedEntityTypes = _getExposedEntityTypes(
|
|
394
|
+
const exposedEntityTypes = _getExposedEntityTypes(srvDefinition);
|
|
395
395
|
|
|
396
396
|
let obj = {
|
|
397
397
|
ordId,
|
|
398
|
-
title:
|
|
399
|
-
|
|
400
|
-
|
|
398
|
+
title:
|
|
399
|
+
srvDefinition["@title"] ??
|
|
400
|
+
srvDefinition["@Common.Label"] ??
|
|
401
|
+
srvDefinition["@EndUserText.label"] ??
|
|
402
|
+
srvDefinition.name,
|
|
403
|
+
shortDescription: SHORT_DESCRIPTION_PREFIX + srvDefinition.name,
|
|
404
|
+
description: srvDefinition["@Core.Description"] ?? DESCRIPTION_PREFIX + srvDefinition.name,
|
|
401
405
|
version: semanticVersion,
|
|
402
406
|
lastUpdate: appConfig.lastUpdate,
|
|
403
407
|
visibility,
|
|
404
408
|
partOfPackage: packageId,
|
|
405
|
-
partOfGroups: [_getGroupID(
|
|
409
|
+
partOfGroups: [_getGroupID(appConfig, srvDefinition)],
|
|
406
410
|
releaseStatus: "active",
|
|
407
411
|
apiProtocol,
|
|
408
412
|
resourceDefinitions,
|
|
@@ -412,10 +416,11 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
|
|
|
412
416
|
},
|
|
413
417
|
...(exposedEntityTypes ? { exposedEntityTypes } : []),
|
|
414
418
|
...ordExtensions,
|
|
419
|
+
...protocolExtensions,
|
|
415
420
|
};
|
|
416
421
|
|
|
417
422
|
// Special handling for data product services
|
|
418
|
-
if (isPrimaryDataProductService(
|
|
423
|
+
if (isPrimaryDataProductService(srvDefinition)) {
|
|
419
424
|
obj.direction = "outbound";
|
|
420
425
|
if (extracted) {
|
|
421
426
|
// Overwrite partOfGroups
|
|
@@ -437,39 +442,47 @@ const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, pa
|
|
|
437
442
|
* Properties of an event resource can be overwritten by the ORD extensions. Example: visibility.
|
|
438
443
|
* Ensures proper visibility compliance by checking associated EntityTypes.
|
|
439
444
|
*
|
|
440
|
-
* @param {
|
|
441
|
-
* @param {object} serviceDefinition The definition of the service
|
|
445
|
+
* @param {object} srvDefinition The definition of the service
|
|
442
446
|
* @param {object} appConfig - The application configuration.
|
|
443
447
|
* @param {Array} packageIds - The available package identifiers.
|
|
444
448
|
* @param {Array} accessStrategies The array of accessStrategies objects
|
|
445
449
|
* @returns {Array} An single-item array of objects for the Event Resources.
|
|
446
450
|
*/
|
|
447
|
-
const createEventResourceTemplate = (
|
|
448
|
-
const ordExtensions = readORDExtensions(
|
|
449
|
-
const visibility = _handleVisibility(ordExtensions,
|
|
451
|
+
const createEventResourceTemplate = (srvDefinition, appConfig, packageIds, accessStrategies) => {
|
|
452
|
+
const ordExtensions = readORDExtensions(srvDefinition);
|
|
453
|
+
const visibility = _handleVisibility(ordExtensions, srvDefinition, appConfig.env?.defaultVisibility);
|
|
454
|
+
const version = ordExtensions.version || "1.0.0";
|
|
450
455
|
const packageId = _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.event, visibility);
|
|
451
|
-
const ordId = `${appConfig.ordNamespace}:eventResource:${_getCleanServiceName(
|
|
452
|
-
const exposedEntityTypes = _getExposedEntityTypes(
|
|
456
|
+
const ordId = `${appConfig.ordNamespace}:eventResource:${_getCleanServiceName(srvDefinition.name, appConfig)}:v${version.split(".")[0]}`;
|
|
457
|
+
const exposedEntityTypes = _getExposedEntityTypes(srvDefinition);
|
|
453
458
|
|
|
454
459
|
let obj = {
|
|
455
460
|
ordId,
|
|
456
461
|
title:
|
|
457
|
-
|
|
458
|
-
|
|
462
|
+
srvDefinition["@title"] ??
|
|
463
|
+
srvDefinition["@Common.Label"] ??
|
|
464
|
+
srvDefinition["@EndUserText.label"] ??
|
|
459
465
|
`ODM ${appConfig.appName.replace(/[^a-zA-Z0-9]/g, "")} Events`,
|
|
460
|
-
shortDescription: `${
|
|
466
|
+
shortDescription: `${srvDefinition.name} event resource`,
|
|
461
467
|
description:
|
|
462
|
-
|
|
463
|
-
|
|
468
|
+
srvDefinition["@description"] ??
|
|
469
|
+
srvDefinition["@Core.Description"] ??
|
|
464
470
|
"CAP Event resource describing events / messages.",
|
|
465
|
-
version:
|
|
471
|
+
version: version,
|
|
466
472
|
lastUpdate: appConfig.lastUpdate,
|
|
467
473
|
releaseStatus: "active",
|
|
468
474
|
partOfPackage: packageId,
|
|
469
|
-
partOfGroups: [_getGroupID(
|
|
475
|
+
partOfGroups: [_getGroupID(appConfig, srvDefinition)],
|
|
470
476
|
visibility,
|
|
471
477
|
resourceDefinitions: [
|
|
472
|
-
_getResourceDefinition(
|
|
478
|
+
_getResourceDefinition(
|
|
479
|
+
"asyncapi-v2",
|
|
480
|
+
"json",
|
|
481
|
+
ordId,
|
|
482
|
+
srvDefinition.name,
|
|
483
|
+
"asyncapi2.json",
|
|
484
|
+
accessStrategies,
|
|
485
|
+
),
|
|
473
486
|
],
|
|
474
487
|
extensible: { supported: "no" },
|
|
475
488
|
...(exposedEntityTypes ? { exposedEntityTypes } : []),
|
|
@@ -497,9 +510,9 @@ function _getExposedEntityTypes(definitionObj) {
|
|
|
497
510
|
return _.uniqBy(entityData, CONTENT_MERGE_KEY);
|
|
498
511
|
});
|
|
499
512
|
const exposedEntityTypes = _.uniqBy(entities, CONTENT_MERGE_KEY)
|
|
500
|
-
.filter((entity) => entity
|
|
501
|
-
.map((
|
|
502
|
-
ordId,
|
|
513
|
+
.filter((entity) => !!entity)
|
|
514
|
+
.map((entity) => ({
|
|
515
|
+
ordId: entity.isODMMapping ? entity.ordId : entity[`${ORD_EXTENSIONS_PREFIX}ordId`] || entity.ordId,
|
|
503
516
|
}));
|
|
504
517
|
|
|
505
518
|
if (exposedEntityTypes.length > 0) {
|
|
@@ -556,7 +569,6 @@ function _extractVersionFromServiceName(serviceName) {
|
|
|
556
569
|
|
|
557
570
|
return {
|
|
558
571
|
cleanName: serviceName.replace(versionPattern, ""),
|
|
559
|
-
version: `v${versionNumber}`,
|
|
560
572
|
semanticVersion: `${versionNumber}.0.0`,
|
|
561
573
|
};
|
|
562
574
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js/ord",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "CAP Plugin for generating ORD document.",
|
|
5
5
|
"repository": "cap-js/ord",
|
|
6
6
|
"author": "SAP SE (https://www.sap.com)",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@cap-js/sqlite": "^2",
|
|
34
34
|
"@sap/cds-dk": ">=8.9.5",
|
|
35
35
|
"eslint": "^10.0.0",
|
|
36
|
-
"express": "^
|
|
36
|
+
"express": "^5.0.0",
|
|
37
37
|
"jest": "^30.0.0",
|
|
38
38
|
"prettier": "3.8.3",
|
|
39
39
|
"supertest": "^7.0.0"
|