@cap-js/ord 1.1.0 → 1.3.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/LICENSE +1 -1
- package/README.md +98 -8
- package/cds-plugin.js +17 -8
- package/data/well-known.json +2 -2
- package/lib/authentication.js +153 -0
- package/lib/build.js +53 -0
- package/lib/constants.js +106 -16
- package/lib/date.js +17 -0
- package/lib/defaults.js +73 -46
- package/lib/es-module.mjs +6 -0
- package/lib/extendOrdWithCustom.js +23 -18
- package/lib/index.js +5 -3
- package/lib/logger.js +17 -0
- package/lib/metaData.js +28 -13
- package/lib/ord-service.cds +3 -0
- package/lib/ord-service.mjs +32 -0
- package/lib/ord.js +246 -104
- package/lib/templates.js +306 -81
- package/package.json +16 -7
- package/lib/plugin.js +0 -33
package/lib/templates.js
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
1
|
-
const defaults = require("./defaults");
|
|
2
1
|
const cds = require("@sap/cds");
|
|
2
|
+
const defaults = require("./defaults");
|
|
3
3
|
const _ = require("lodash");
|
|
4
|
-
const {
|
|
4
|
+
const {
|
|
5
|
+
AUTHENTICATION_TYPE,
|
|
6
|
+
DATA_PRODUCT_ANNOTATION,
|
|
7
|
+
DATA_PRODUCT_TYPE,
|
|
8
|
+
DESCRIPTION_PREFIX,
|
|
9
|
+
ENTITY_RELATIONSHIP_ANNOTATION,
|
|
10
|
+
LEVEL,
|
|
11
|
+
ORD_EXTENSIONS_PREFIX,
|
|
12
|
+
ORD_ODM_ENTITY_NAME_ANNOTATION,
|
|
13
|
+
ORD_RESOURCE_TYPE,
|
|
14
|
+
RESOURCE_VISIBILITY,
|
|
15
|
+
SEM_VERSION_REGEX,
|
|
16
|
+
SHORT_DESCRIPTION_PREFIX,
|
|
17
|
+
CONTENT_MERGE_KEY,
|
|
18
|
+
CDS_ELEMENT_KIND,
|
|
19
|
+
} = require("./constants");
|
|
20
|
+
const { Logger } = require("./logger");
|
|
5
21
|
|
|
6
22
|
function unflatten(flattedObject) {
|
|
7
|
-
let result = {}
|
|
23
|
+
let result = {};
|
|
8
24
|
_.keys(flattedObject).forEach(function (key) {
|
|
9
|
-
_.set(result, key, flattedObject[key])
|
|
10
|
-
})
|
|
11
|
-
return result
|
|
25
|
+
_.set(result, key, flattedObject[key]);
|
|
26
|
+
});
|
|
27
|
+
return result;
|
|
12
28
|
}
|
|
13
29
|
|
|
14
30
|
function readORDExtensions(model) {
|
|
@@ -29,7 +45,6 @@ function readORDExtensions(model) {
|
|
|
29
45
|
* @param {Object} srvDefinition The service definition object.
|
|
30
46
|
* @returns {Array} An array containing paths and it's kind.
|
|
31
47
|
*/
|
|
32
|
-
|
|
33
48
|
const _generatePaths = (srv, srvDefinition) => {
|
|
34
49
|
const srvObj = { name: srv, definition: srvDefinition };
|
|
35
50
|
const protocols = cds.service.protocols;
|
|
@@ -40,12 +55,12 @@ const _generatePaths = (srv, srvDefinition) => {
|
|
|
40
55
|
//removing instances of graphql protocol from paths
|
|
41
56
|
for (var index = paths.length - 1; index >= 0; index--) {
|
|
42
57
|
if (paths[index].kind === "graphql") {
|
|
43
|
-
|
|
58
|
+
Logger.warn("Graphql protocol is not supported.");
|
|
44
59
|
paths.splice(index, 1);
|
|
45
60
|
}
|
|
46
61
|
}
|
|
47
62
|
|
|
48
|
-
//putting
|
|
63
|
+
//putting OData as default in case of non-supported protocol
|
|
49
64
|
if (paths.length === 0) {
|
|
50
65
|
srvDefinition["@odata"] = true;
|
|
51
66
|
paths.push({ kind: "odata", path: protocols.path4(srvDefinition) });
|
|
@@ -55,20 +70,32 @@ const _generatePaths = (srv, srvDefinition) => {
|
|
|
55
70
|
};
|
|
56
71
|
|
|
57
72
|
/**
|
|
58
|
-
* This is a template function to create
|
|
73
|
+
* This is a template function to create item of entityTypeMappings array.
|
|
59
74
|
*
|
|
60
|
-
* @param {string} entity The
|
|
61
|
-
* @returns {Object} An
|
|
75
|
+
* @param {string} entity The entity definition.
|
|
76
|
+
* @returns {Object} An entry of the entityTypeMappings array.
|
|
62
77
|
*/
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
78
|
+
const createEntityTypeMappingsItemTemplate = (entity) => {
|
|
79
|
+
if (entity[ORD_ODM_ENTITY_NAME_ANNOTATION]) {
|
|
80
|
+
return {
|
|
81
|
+
ordId: `sap.odm:entityType:${entity[ORD_ODM_ENTITY_NAME_ANNOTATION]}:v1`,
|
|
82
|
+
entityName: entity[ORD_ODM_ENTITY_NAME_ANNOTATION],
|
|
83
|
+
...entity,
|
|
84
|
+
};
|
|
85
|
+
} else if (entity[ENTITY_RELATIONSHIP_ANNOTATION]) {
|
|
86
|
+
const ordIdParts = entity[ENTITY_RELATIONSHIP_ANNOTATION].split(":");
|
|
87
|
+
const namespace = ordIdParts[0];
|
|
88
|
+
const entityName = ordIdParts[1];
|
|
89
|
+
const version = ordIdParts[2] || "v1";
|
|
90
|
+
return {
|
|
91
|
+
ordId: `${namespace}:entityType:${entityName}:${version}`,
|
|
92
|
+
entityName,
|
|
93
|
+
...entity,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
function _getGroupID(fullyQualifiedName, groupTypeId = defaults.groupTypeId, appConfig) {
|
|
72
99
|
return `${groupTypeId}:${appConfig.ordNamespace}:${fullyQualifiedName}`;
|
|
73
100
|
}
|
|
74
101
|
|
|
@@ -88,6 +115,49 @@ function _getTitleFromServiceName(srv) {
|
|
|
88
115
|
}
|
|
89
116
|
}
|
|
90
117
|
|
|
118
|
+
/**
|
|
119
|
+
* This is a function to get the version of the entity,
|
|
120
|
+
* validate it and log if it is not a valid semantical version.
|
|
121
|
+
*
|
|
122
|
+
* @param {object} entity An entity object.
|
|
123
|
+
* @returns Version of the entity with '1.0.0' as fallback value.
|
|
124
|
+
*/
|
|
125
|
+
function _getEntityVersion(entity) {
|
|
126
|
+
const entityVersion = entity.ordId.split(":").pop();
|
|
127
|
+
const version = entityVersion.replace("v", "") + ".0.0"; // TODO: version can be stated/overwritten by annotation
|
|
128
|
+
if (!SEM_VERSION_REGEX.test(version)) {
|
|
129
|
+
Logger.warn(`Entity version "${version}" is not a valid semantic version.`);
|
|
130
|
+
}
|
|
131
|
+
return version;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* This is a function to create a resource definition object.
|
|
136
|
+
* @param {string} resourceType The type of the resource.
|
|
137
|
+
* @param {string} mediaType The media type of the resource.
|
|
138
|
+
* @param {string} ordId The ordId of the resource.
|
|
139
|
+
* @param {string} serviceName The name of the service.
|
|
140
|
+
* @param {string} fileExtension The file extension of the resource.
|
|
141
|
+
* @param {Array} accessStrategies The array of accessStrategies objects
|
|
142
|
+
* @returns {Object} A resource definition object.
|
|
143
|
+
* @private
|
|
144
|
+
*/
|
|
145
|
+
function _getResourceDefinition(
|
|
146
|
+
resourceType,
|
|
147
|
+
mediaType,
|
|
148
|
+
ordId,
|
|
149
|
+
serviceName,
|
|
150
|
+
fileExtension,
|
|
151
|
+
accessStrategies = [{ type: AUTHENTICATION_TYPE.Open }],
|
|
152
|
+
) {
|
|
153
|
+
return {
|
|
154
|
+
type: resourceType,
|
|
155
|
+
mediaType: `application/${mediaType}`,
|
|
156
|
+
url: `/ord/v1/${ordId}/${serviceName}.${fileExtension}`,
|
|
157
|
+
accessStrategies,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
91
161
|
/**
|
|
92
162
|
* This is a template function to create group object of a service for groups array in ORD doc.
|
|
93
163
|
*
|
|
@@ -100,128 +170,283 @@ const createGroupsTemplateForService = (serviceName, serviceDefinition, appConfi
|
|
|
100
170
|
const ordExtensions = readORDExtensions(serviceDefinition);
|
|
101
171
|
|
|
102
172
|
if (!serviceDefinition) {
|
|
103
|
-
|
|
104
|
-
return undefined
|
|
173
|
+
Logger.warn("Unable to find service definition:", serviceName);
|
|
174
|
+
return undefined;
|
|
105
175
|
}
|
|
106
176
|
|
|
107
177
|
const groupId = _getGroupID(serviceName, defaults.groupTypeId, appConfig);
|
|
108
178
|
return {
|
|
109
179
|
groupId: groupId,
|
|
110
180
|
groupTypeId: defaults.groupTypeId,
|
|
111
|
-
title: ordExtensions.title ?? _getTitleFromServiceName(serviceName)
|
|
181
|
+
title: ordExtensions.title ?? _getTitleFromServiceName(serviceName),
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* This is a template function to create EntityType object for EntityTypes Array.
|
|
187
|
+
* Ensures correct visibility assignment based on referenced resources.
|
|
188
|
+
*
|
|
189
|
+
* @param { object } appConfig The configuration object.
|
|
190
|
+
* @param { Array } packageIds The available package identifiers.
|
|
191
|
+
* @param { object } entity The entity definition.
|
|
192
|
+
* @returns { object } An object for the EntityType.
|
|
193
|
+
*/
|
|
194
|
+
const createEntityTypeTemplate = (appConfig, packageIds, entity) => {
|
|
195
|
+
const ordExtensions = readORDExtensions(entity);
|
|
196
|
+
const visibility = ordExtensions.visibility || RESOURCE_VISIBILITY.public;
|
|
197
|
+
|
|
198
|
+
if (visibility === RESOURCE_VISIBILITY.private) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const packageId = _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.entityType, visibility);
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
ordId: entity.ordId,
|
|
206
|
+
localId: entity.entityName,
|
|
207
|
+
title: entity["@title"] ?? entity["@Common.Label"] ?? entity.entityName,
|
|
208
|
+
shortDescription: SHORT_DESCRIPTION_PREFIX + entity.entityName,
|
|
209
|
+
description: DESCRIPTION_PREFIX + entity.entityName,
|
|
210
|
+
version: _getEntityVersion(entity),
|
|
211
|
+
lastUpdate: appConfig.lastUpdate,
|
|
212
|
+
visibility,
|
|
213
|
+
partOfPackage: packageId,
|
|
214
|
+
releaseStatus: "active",
|
|
215
|
+
level: entity["@ObjectModel.compositionRoot"] || entity["@ODM.root"] ? LEVEL.rootEntity : LEVEL.subEntity,
|
|
216
|
+
extensible: { supported: "no" },
|
|
217
|
+
...ordExtensions,
|
|
112
218
|
};
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
function _handleVisibility(ordExtensions, definition) {
|
|
222
|
+
let visibility;
|
|
223
|
+
if (ordExtensions.visibility) {
|
|
224
|
+
visibility = ordExtensions.visibility;
|
|
225
|
+
} else if (definition[ORD_EXTENSIONS_PREFIX + "visibility"]) {
|
|
226
|
+
visibility = definition[ORD_EXTENSIONS_PREFIX + "visibility"];
|
|
227
|
+
} else {
|
|
228
|
+
visibility = RESOURCE_VISIBILITY.public;
|
|
229
|
+
}
|
|
230
|
+
return visibility;
|
|
113
231
|
}
|
|
114
232
|
|
|
115
233
|
/**
|
|
116
234
|
* This is a template function to create API Resource object for API Resource Array.
|
|
117
|
-
*
|
|
235
|
+
* Properties of an API resource can be overwritten by the ORD extensions. Example: visibility.
|
|
236
|
+
* Ensures proper visibility compliance by checking associated EntityTypes.
|
|
118
237
|
* @param {string} serviceName The name of the service.
|
|
119
238
|
* @param {object} serviceDefinition The definition of the service
|
|
239
|
+
* @param {object} appConfig - The application configuration.
|
|
240
|
+
* @param {Array} packageIds - The available package identifiers.
|
|
241
|
+
* @param {Array} accessStrategies The array of accessStrategies objects
|
|
120
242
|
* @returns {Array} An array of objects for the API Resources.
|
|
121
243
|
*/
|
|
122
|
-
const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, packageIds) => {
|
|
244
|
+
const createAPIResourceTemplate = (serviceName, serviceDefinition, appConfig, packageIds, accessStrategies) => {
|
|
123
245
|
const ordExtensions = readORDExtensions(serviceDefinition);
|
|
246
|
+
const visibility = _handleVisibility(ordExtensions, serviceDefinition);
|
|
247
|
+
const packageId = _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.api, visibility);
|
|
248
|
+
|
|
124
249
|
const paths = _generatePaths(serviceName, serviceDefinition);
|
|
125
250
|
const apiResources = [];
|
|
251
|
+
const ordId = `${appConfig.ordNamespace}:apiResource:${serviceName}:v1`;
|
|
126
252
|
|
|
127
253
|
paths.forEach((generatedPath) => {
|
|
128
254
|
let resourceDefinitions = [
|
|
129
|
-
|
|
130
|
-
type: "openapi-v3",
|
|
131
|
-
mediaType: "application/json",
|
|
132
|
-
url: `/.well-known/open-resource-discovery/v1/api-metadata/${serviceName}.oas3.json`,
|
|
133
|
-
accessStrategies: [{ type: "open" }],
|
|
134
|
-
},
|
|
255
|
+
_getResourceDefinition("openapi-v3", "json", ordId, serviceName, "oas3.json", accessStrategies),
|
|
135
256
|
];
|
|
136
257
|
|
|
137
258
|
if (generatedPath.kind !== "rest") {
|
|
138
259
|
//edmx resource definition is not generated in case of 'rest' protocol
|
|
139
|
-
resourceDefinitions.push(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
url: `/.well-known/open-resource-discovery/v1/api-metadata/${serviceName}.edmx`,
|
|
143
|
-
accessStrategies: [{ type: "open" }],
|
|
144
|
-
});
|
|
260
|
+
resourceDefinitions.push(
|
|
261
|
+
_getResourceDefinition("edmx", "xml", ordId, serviceName, "edmx", accessStrategies),
|
|
262
|
+
);
|
|
145
263
|
}
|
|
146
264
|
|
|
265
|
+
const entityTypeMappings = _getEntityTypeMappings(serviceDefinition);
|
|
266
|
+
|
|
147
267
|
let obj = {
|
|
148
|
-
ordId
|
|
149
|
-
title:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
serviceName,
|
|
153
|
-
shortDescription: serviceName,
|
|
154
|
-
description:
|
|
155
|
-
serviceDefinition["@Core.Description"] ??
|
|
156
|
-
serviceName,
|
|
268
|
+
ordId,
|
|
269
|
+
title: serviceDefinition["@title"] ?? serviceDefinition["@Common.Label"] ?? serviceName,
|
|
270
|
+
shortDescription: SHORT_DESCRIPTION_PREFIX + serviceName,
|
|
271
|
+
description: serviceDefinition["@Core.Description"] ?? DESCRIPTION_PREFIX + serviceName,
|
|
157
272
|
version: "1.0.0",
|
|
158
|
-
|
|
159
|
-
|
|
273
|
+
lastUpdate: appConfig.lastUpdate,
|
|
274
|
+
visibility,
|
|
275
|
+
partOfPackage: packageId,
|
|
160
276
|
partOfGroups: [_getGroupID(serviceName, defaults.groupTypeId, appConfig)],
|
|
161
277
|
releaseStatus: "active",
|
|
162
|
-
apiProtocol:
|
|
163
|
-
generatedPath.kind === "odata" ? "odata-v4" : generatedPath.kind,
|
|
278
|
+
apiProtocol: generatedPath.kind === "odata" ? "odata-v4" : generatedPath.kind,
|
|
164
279
|
resourceDefinitions: resourceDefinitions,
|
|
165
280
|
entryPoints: [generatedPath.path],
|
|
166
281
|
extensible: {
|
|
167
282
|
supported: "no",
|
|
168
283
|
},
|
|
169
|
-
entityTypeMappings
|
|
170
|
-
|
|
284
|
+
...(entityTypeMappings ? { entityTypeMappings } : {}),
|
|
171
285
|
...ordExtensions,
|
|
172
286
|
};
|
|
173
287
|
|
|
174
|
-
|
|
288
|
+
if (serviceDefinition[DATA_PRODUCT_ANNOTATION] === DATA_PRODUCT_TYPE.primary) {
|
|
289
|
+
obj.apiProtocol = "rest";
|
|
290
|
+
obj.visibility = RESOURCE_VISIBILITY.internal;
|
|
291
|
+
obj.direction = "outbound";
|
|
292
|
+
obj.implementationStandard = "sap.dp:data-subscription-api:v1";
|
|
293
|
+
obj.entryPoints = [];
|
|
294
|
+
obj.resourceDefinitions = [
|
|
295
|
+
_getResourceDefinition(
|
|
296
|
+
"sap-csn-interop-effective-v1",
|
|
297
|
+
"json",
|
|
298
|
+
ordId,
|
|
299
|
+
serviceName,
|
|
300
|
+
"csn.json",
|
|
301
|
+
accessStrategies,
|
|
302
|
+
),
|
|
303
|
+
];
|
|
304
|
+
|
|
305
|
+
apiResources.push(obj);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (obj.visibility !== RESOURCE_VISIBILITY.private) {
|
|
309
|
+
apiResources.push(obj);
|
|
310
|
+
}
|
|
175
311
|
});
|
|
176
312
|
|
|
177
|
-
|
|
313
|
+
return apiResources;
|
|
178
314
|
};
|
|
179
315
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
316
|
+
/**
|
|
317
|
+
* This is a template function to create Event Resource object for Event Resource Array.
|
|
318
|
+
* There can be only one event resource per service because all events are using the same protocol, they are always Cloud Events.
|
|
319
|
+
* Properties of an event resource can be overwritten by the ORD extensions. Example: visibility.
|
|
320
|
+
* Ensures proper visibility compliance by checking associated EntityTypes.
|
|
321
|
+
*
|
|
322
|
+
* @param {string} serviceName The name of the service.
|
|
323
|
+
* @param {object} serviceDefinition The definition of the service
|
|
324
|
+
* @param {object} appConfig - The application configuration.
|
|
325
|
+
* @param {Array} packageIds - The available package identifiers.
|
|
326
|
+
* @param {Array} accessStrategies The array of accessStrategies objects
|
|
327
|
+
* @returns {Array} An single-item array of objects for the Event Resources.
|
|
328
|
+
*/
|
|
329
|
+
const createEventResourceTemplate = (serviceName, serviceDefinition, appConfig, packageIds, accessStrategies) => {
|
|
330
|
+
const ordExtensions = readORDExtensions(serviceDefinition);
|
|
331
|
+
const visibility = _handleVisibility(ordExtensions, serviceDefinition);
|
|
332
|
+
const packageId = _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.event, visibility);
|
|
333
|
+
const ordId = `${appConfig.ordNamespace}:eventResource:${serviceName}:v1`;
|
|
334
|
+
const entityTypeMappings = _getEntityTypeMappings(serviceDefinition);
|
|
335
|
+
|
|
336
|
+
let obj = {
|
|
337
|
+
ordId,
|
|
184
338
|
title:
|
|
185
|
-
|
|
186
|
-
|
|
339
|
+
serviceDefinition["@title"] ??
|
|
340
|
+
serviceDefinition["@Common.Label"] ??
|
|
187
341
|
`ODM ${appConfig.appName.replace(/[^a-zA-Z0-9]/g, "")} Events`,
|
|
188
342
|
shortDescription: `${serviceName} event resource`,
|
|
189
343
|
description:
|
|
190
|
-
|
|
344
|
+
serviceDefinition["@description"] ??
|
|
345
|
+
serviceDefinition["@Core.Description"] ??
|
|
191
346
|
"CAP Event resource describing events / messages.",
|
|
192
347
|
version: "1.0.0",
|
|
348
|
+
lastUpdate: appConfig.lastUpdate,
|
|
193
349
|
releaseStatus: "active",
|
|
194
|
-
partOfPackage:
|
|
350
|
+
partOfPackage: packageId,
|
|
195
351
|
partOfGroups: [_getGroupID(serviceName, defaults.groupTypeId, appConfig)],
|
|
196
|
-
visibility
|
|
352
|
+
visibility,
|
|
197
353
|
resourceDefinitions: [
|
|
198
|
-
|
|
199
|
-
type: "asyncapi-v2",
|
|
200
|
-
mediaType: "application/json",
|
|
201
|
-
url: `/.well-known/open-resource-discovery/v1/api-metadata/${serviceName}.asyncapi2.json`,
|
|
202
|
-
accessStrategies: [
|
|
203
|
-
{
|
|
204
|
-
type: "open",
|
|
205
|
-
},
|
|
206
|
-
],
|
|
207
|
-
},
|
|
354
|
+
_getResourceDefinition("asyncapi-v2", "json", ordId, serviceName, "asyncapi2.json", accessStrategies),
|
|
208
355
|
],
|
|
209
356
|
extensible: { supported: "no" },
|
|
210
|
-
|
|
211
|
-
...ordExtensions
|
|
357
|
+
...(entityTypeMappings ? { entityTypeMappings } : {}),
|
|
358
|
+
...ordExtensions,
|
|
212
359
|
};
|
|
360
|
+
|
|
361
|
+
return obj.visibility === RESOURCE_VISIBILITY.public || obj.visibility === RESOURCE_VISIBILITY.internal
|
|
362
|
+
? [obj]
|
|
363
|
+
: [];
|
|
213
364
|
};
|
|
214
365
|
|
|
215
|
-
function
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
366
|
+
function _getEntityTypeMappings(definitionObj) {
|
|
367
|
+
if (!definitionObj.entities) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const entities = Object.values(definitionObj.entities)
|
|
371
|
+
.flatMap((entity) => _flattenEntityGraph(entity))
|
|
372
|
+
.map(createEntityTypeMappingsItemTemplate);
|
|
373
|
+
const entityTypeTargets = _.uniqBy(entities, CONTENT_MERGE_KEY)
|
|
374
|
+
.filter((entity) => entity !== undefined)
|
|
375
|
+
.map(({ ordId }) => ({
|
|
376
|
+
ordId,
|
|
377
|
+
}));
|
|
378
|
+
if (entityTypeTargets.length > 0) {
|
|
379
|
+
return [{ entityTypeTargets }];
|
|
380
|
+
} else {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function _flattenEntityGraph(currentEntity, processedEntities = []) {
|
|
386
|
+
if (!currentEntity.associations) {
|
|
387
|
+
return [currentEntity];
|
|
388
|
+
}
|
|
389
|
+
const entityAssociationTargets = Object.values(currentEntity.associations).map((association) => ({
|
|
390
|
+
target: association.target,
|
|
391
|
+
entity: association._target,
|
|
392
|
+
}));
|
|
393
|
+
|
|
394
|
+
const assertionsTodo = [];
|
|
395
|
+
entityAssociationTargets.forEach(({ target, entity }) => {
|
|
396
|
+
if (processedEntities.includes(target)) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
processedEntities = [...processedEntities, target];
|
|
400
|
+
assertionsTodo.push(entity);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
return [currentEntity, ...assertionsTodo.flatMap((entity) => _flattenEntityGraph(entity, processedEntities))];
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function _getPackageID(namespace, packageIds, resourceType, visibility = RESOURCE_VISIBILITY.public) {
|
|
407
|
+
if (!packageIds) return;
|
|
408
|
+
|
|
409
|
+
if (resourceType) {
|
|
410
|
+
return (
|
|
411
|
+
packageIds.find((id) => {
|
|
412
|
+
if (visibility === RESOURCE_VISIBILITY.public) {
|
|
413
|
+
return id.includes(resourceType) && !id.includes("-internal") && !id.includes("-private");
|
|
414
|
+
} else {
|
|
415
|
+
return id.includes(`${resourceType}-${visibility}`);
|
|
416
|
+
}
|
|
417
|
+
}) || packageIds.find((id) => id.includes(namespace))
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return packageIds.find((id) => id.includes(`-${resourceType}-`)) || packageIds.find((id) => id.includes(namespace));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function _propagateORDVisibility(model) {
|
|
425
|
+
for (const [name, def] of Object.entries(model.definitions)) {
|
|
426
|
+
if (def.kind === CDS_ELEMENT_KIND.service && def[ORD_EXTENSIONS_PREFIX + "visibility"]) {
|
|
427
|
+
const serviceName = name;
|
|
428
|
+
const serviceVisibility = def[ORD_EXTENSIONS_PREFIX + "visibility"];
|
|
429
|
+
for (const serviceChildrenDef of model.definitions) {
|
|
430
|
+
if (
|
|
431
|
+
serviceChildrenDef.name.startsWith(serviceName + ".") &&
|
|
432
|
+
!serviceChildrenDef[ORD_EXTENSIONS_PREFIX + "visibility"]
|
|
433
|
+
) {
|
|
434
|
+
serviceChildrenDef[ORD_EXTENSIONS_PREFIX + "visibility"] = serviceVisibility;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return model;
|
|
220
441
|
}
|
|
221
442
|
|
|
222
443
|
module.exports = {
|
|
223
444
|
createEntityTypeTemplate,
|
|
445
|
+
createEntityTypeMappingsItemTemplate,
|
|
224
446
|
createGroupsTemplateForService,
|
|
225
447
|
createAPIResourceTemplate,
|
|
226
448
|
createEventResourceTemplate,
|
|
449
|
+
_getPackageID,
|
|
450
|
+
_getEntityTypeMappings,
|
|
451
|
+
_propagateORDVisibility,
|
|
227
452
|
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js/ord",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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)",
|
|
7
7
|
"homepage": "https://cap.cloud.sap/",
|
|
8
|
-
"license": "
|
|
8
|
+
"license": "Apache-2.0",
|
|
9
9
|
"main": "cds-plugin.js",
|
|
10
10
|
"files": [
|
|
11
11
|
"lib",
|
|
@@ -20,15 +20,24 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"eslint": "^8",
|
|
23
|
-
"jest": "^29.7.0"
|
|
23
|
+
"jest": "^29.7.0",
|
|
24
|
+
"prettier": "3.5.3"
|
|
24
25
|
},
|
|
25
26
|
"peerDependencies": {
|
|
26
|
-
"@cap-js/asyncapi": "^1.0.0",
|
|
27
|
-
"@cap-js/openapi": "^1.0.2",
|
|
28
27
|
"@sap/cds": "^8.1.1",
|
|
29
|
-
"@sap/cds-
|
|
28
|
+
"@sap/cds-dk": "^8.9.3"
|
|
30
29
|
},
|
|
31
30
|
"dependencies": {
|
|
31
|
+
"@cap-js/asyncapi": "^1.0.3",
|
|
32
|
+
"@cap-js/openapi": "^1.2.1",
|
|
33
|
+
"bcrypt": "^5.1.1",
|
|
32
34
|
"lodash": "^4.17.21"
|
|
35
|
+
},
|
|
36
|
+
"cds": {
|
|
37
|
+
"requires": {
|
|
38
|
+
"SAP ORD Service": {
|
|
39
|
+
"model": "@cap-js/ord/lib/ord-service"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
33
42
|
}
|
|
34
|
-
}
|
|
43
|
+
}
|
package/lib/plugin.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
const { ord, getMetadata, defaults } = require("./");
|
|
2
|
-
const cds = require("@sap/cds");
|
|
3
|
-
|
|
4
|
-
cds.on("bootstrap", (app) => {
|
|
5
|
-
app.use("/.well-known/open-resource-discovery", async (req, res) => {
|
|
6
|
-
if (req.url === "/") {
|
|
7
|
-
res.status(200).send(defaults.baseTemplate);
|
|
8
|
-
} else {
|
|
9
|
-
try {
|
|
10
|
-
const { contentType, response } = await getMetadata(req.url);
|
|
11
|
-
res.status(200).contentType(contentType).send(response);
|
|
12
|
-
} catch (error) {
|
|
13
|
-
console.log(error);
|
|
14
|
-
console.log('Error while generating metadata');
|
|
15
|
-
res.status(500).send(error.message);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
app.get("/open-resource-discovery/v1/documents/1", async (req, res) => {
|
|
21
|
-
try {
|
|
22
|
-
const csn = await cds.load(cds.env.folders.srv);
|
|
23
|
-
const data = ord(csn);
|
|
24
|
-
return res.status(200).send(data);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
console.log(error);
|
|
27
|
-
console.log('Error while creating ORD document');
|
|
28
|
-
return res.status(500).send(error.message);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
module.exports = cds.server;
|