@cap-js/ord 1.0.3 → 1.2.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/README.md +21 -11
- package/lib/constants.js +39 -0
- package/lib/date.js +22 -0
- package/lib/defaults.js +74 -53
- package/lib/extendOrdWithCustom.js +66 -0
- package/lib/logger.js +11 -0
- package/lib/metaData.js +29 -30
- package/lib/ord.js +137 -171
- package/lib/plugin.js +5 -5
- package/lib/templates.js +171 -280
- package/package.json +33 -26
package/lib/ord.js
CHANGED
|
@@ -1,202 +1,168 @@
|
|
|
1
|
-
const
|
|
2
|
-
const cds = require("@sap/cds");
|
|
3
|
-
const { exists } = cds.utils;
|
|
4
|
-
const defaults = require("./defaults");
|
|
1
|
+
const { ORD_RESOURCE_TYPE } = require('./constants');
|
|
5
2
|
const {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
fCreateEntityTypeTemplate
|
|
3
|
+
createAPIResourceTemplate,
|
|
4
|
+
createEntityTypeTemplate,
|
|
5
|
+
createEventResourceTemplate,
|
|
6
|
+
createGroupsTemplateForService
|
|
11
7
|
} = require('./templates');
|
|
8
|
+
const { extendCustomORDContentIfExists } = require('./extendOrdWithCustom');
|
|
9
|
+
const { getRFC3339Date } = require('./date');
|
|
10
|
+
const { Logger } = require('./logger');
|
|
11
|
+
const _ = require("lodash");
|
|
12
|
+
const cds = require("@sap/cds");
|
|
13
|
+
const defaults = require("./defaults");
|
|
14
|
+
const path = require("path");
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* @param {Object} csn object
|
|
16
|
-
* @returns {Object} An object containing global variables.
|
|
17
|
-
*/
|
|
18
|
-
const fInitializeGlobal = (csn) => {
|
|
19
|
-
let packagejsonPath = path.join(cds.root,'package.json')
|
|
16
|
+
const initializeAppConfig = (csn) => {
|
|
17
|
+
let packageJsonPath = path.join(cds.root, 'package.json')
|
|
20
18
|
let packageJson;
|
|
21
|
-
if (exists(
|
|
22
|
-
packageJson = require(
|
|
19
|
+
if (cds.utils.exists(packageJsonPath)) {
|
|
20
|
+
packageJson = require(packageJsonPath);
|
|
23
21
|
} else {
|
|
24
22
|
throw new Error(`package.json not found in the project root directory`);
|
|
25
23
|
}
|
|
24
|
+
const packageName = packageJson.name;
|
|
26
25
|
const appName = packageJson.name.replace(/^[@]/, "").replace(/[@/]/g, "-");
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
console.warn('ORD and AsyncAPI namespaces should be the same.');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const fEventFilter = (keyDefinition) => keyDefinition.kind === "event";
|
|
43
|
-
const fServicesFilter = (keyDefinition) => keyDefinition.kind === "service" && !keyDefinition['@cds.external'];
|
|
44
|
-
const fODMEntityFilter = (key, keyDefinition) => {
|
|
45
|
-
return keyDefinition.kind === "entity"
|
|
46
|
-
&& key.includes(capNamespace)
|
|
47
|
-
&& !key.includes(".texts")
|
|
48
|
-
&& keyDefinition["@ODM.entityName"];
|
|
26
|
+
const modelKeys = Object.keys(csn.definitions);
|
|
27
|
+
const events = [];
|
|
28
|
+
const serviceNames = [];
|
|
29
|
+
const lastUpdate = getRFC3339Date();
|
|
30
|
+
let odmEntity = [];
|
|
31
|
+
|
|
32
|
+
const vendorNamespace = "customer";
|
|
33
|
+
const ordNamespace = cds.env["ord"]?.namespace || `${vendorNamespace}.${packageName.replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
34
|
+
const eventApplicationNamespace = cds.env?.export?.asyncapi?.applicationNamespace;
|
|
35
|
+
|
|
36
|
+
if (eventApplicationNamespace && ordNamespace !== eventApplicationNamespace) {
|
|
37
|
+
Logger.warn('ORD and AsyncAPI namespaces should be the same.');
|
|
49
38
|
}
|
|
50
39
|
|
|
51
|
-
|
|
40
|
+
for (const key of modelKeys) {
|
|
52
41
|
const keyDefinition = csn.definitions[key];
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
42
|
+
switch (keyDefinition.kind) {
|
|
43
|
+
case ORD_RESOURCE_TYPE.service:
|
|
44
|
+
if (!keyDefinition["@cds.external"]) {
|
|
45
|
+
serviceNames.push(key);
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
// TODO: should be rewritten
|
|
49
|
+
case ORD_RESOURCE_TYPE.entity:
|
|
50
|
+
if (!key.includes(".texts") && keyDefinition["@ODM.entityName"]) {
|
|
51
|
+
odmEntity.push(createEntityTypeTemplate(keyDefinition));
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
case ORD_RESOURCE_TYPE.event:
|
|
55
|
+
events.push(key);
|
|
56
|
+
break;
|
|
59
57
|
}
|
|
60
|
-
}
|
|
58
|
+
}
|
|
61
59
|
|
|
60
|
+
odmEntity = _.uniqBy(odmEntity, "ordId");
|
|
62
61
|
|
|
63
62
|
return {
|
|
64
63
|
env: cds.env["ord"],
|
|
65
|
-
|
|
64
|
+
lastUpdate,
|
|
66
65
|
appName,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
66
|
+
events,
|
|
67
|
+
serviceNames,
|
|
68
|
+
odmEntity,
|
|
69
|
+
ordNamespace,
|
|
70
|
+
eventApplicationNamespace,
|
|
71
|
+
packageName,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const _getPolicyLevel = (appConfig) =>
|
|
76
|
+
appConfig.env?.policyLevel || defaults.policyLevel;
|
|
74
77
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
* @returns {string} The policy level.
|
|
86
|
-
*/
|
|
87
|
-
const fGetPolicyLevel = (global) => global.env?.policyLevel || defaults.policyLevel;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Retrieves the root level ORD document description.
|
|
91
|
-
* Hierarchy to check data: cdsrc.json > defaults
|
|
92
|
-
* @returns {string} The ORD document description.
|
|
93
|
-
*/
|
|
94
|
-
const fGetDescription = (global) => global.env?.description || defaults.description;
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Retrieves the products.
|
|
98
|
-
* Hierarchy to check data: cdsrc.json > defaults
|
|
99
|
-
* @returns {Array<object>} The products array.
|
|
100
|
-
* if global.namespace is defined in cdsrc.json, then use it, else use the appName from package.json
|
|
101
|
-
*/
|
|
102
|
-
const fGetProducts = (global) => global.env?.products || defaults.products(global.namespace);
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Retrieves the groups that services belongs to.
|
|
106
|
-
* Gets list of groups from CDS runtime object.
|
|
107
|
-
* @param {Object} csn object
|
|
108
|
-
* @returns {Array<object>} The groups array.
|
|
109
|
-
*/
|
|
110
|
-
const fGetGroups = (csn, global) => {
|
|
111
|
-
// storing the group ids in a set to avoid duplicates
|
|
112
|
-
let groupIds = new Set();
|
|
113
|
-
|
|
114
|
-
let serviceGroups = global.aServices
|
|
115
|
-
.map((srv) => fCreateGroupsTemplateForService(srv, csn.definitions[srv], groupIds))
|
|
116
|
-
.filter((resource) => resource !== null && resource !== undefined);
|
|
117
|
-
let eventGroups = global.aEvents
|
|
118
|
-
.map((event) => fCreateGroupsTemplateForEvent(event, csn.definitions[event], groupIds))
|
|
119
|
-
.filter((resource) => resource !== null && resource !== undefined);
|
|
120
|
-
|
|
121
|
-
return [...serviceGroups, ...eventGroups];
|
|
78
|
+
const _getDescription = (appConfig) =>
|
|
79
|
+
appConfig.env?.description || defaults.description;
|
|
80
|
+
|
|
81
|
+
const _getProducts = (appConfig) =>
|
|
82
|
+
appConfig.env?.products || defaults.products(appConfig.packageName);
|
|
83
|
+
|
|
84
|
+
const _getGroups = (csn, appConfig) => {
|
|
85
|
+
return appConfig.serviceNames
|
|
86
|
+
.flatMap((serviceName) => createGroupsTemplateForService(serviceName, csn.definitions[serviceName], appConfig))
|
|
87
|
+
.filter((resource) => !!resource);
|
|
122
88
|
};
|
|
123
89
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
* @returns {Array<object>} The consumption bundles array.
|
|
135
|
-
*/
|
|
136
|
-
const fGetConsumptionBundles = (global) => global.env?.consumptionBundles || (global.aEvents.length) ? defaults.consumptionBundles(global.capNamespace) : defaults.consumptionBundles(global.capNamespace).slice(0,1)
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Retrieves the API Resources
|
|
140
|
-
* Gets list of services from CSN object
|
|
141
|
-
* @param {Object} csn object
|
|
142
|
-
* @returns {Array<object>} The API Resources array.
|
|
143
|
-
*/
|
|
144
|
-
const fGetAPIResources = (csn, global,packageIds) => {
|
|
145
|
-
const apiResources = [];
|
|
146
|
-
global.aServices.forEach((srv) => {
|
|
147
|
-
fCreateAPIResourceTemplate(srv, csn.definitions[srv], global,packageIds)?.forEach(
|
|
148
|
-
(resource) => {
|
|
149
|
-
if (resource !== null && resource !== undefined) {
|
|
150
|
-
apiResources.push(resource);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
);
|
|
154
|
-
});
|
|
155
|
-
return apiResources;
|
|
90
|
+
const _getPackages = (policyLevel, appConfig) =>
|
|
91
|
+
appConfig.env?.packages || appConfig.events.length
|
|
92
|
+
? defaults.packages(appConfig.appName, policyLevel, appConfig.ordNamespace)
|
|
93
|
+
: defaults
|
|
94
|
+
.packages(appConfig.appName, policyLevel, appConfig.ordNamespace)
|
|
95
|
+
.slice(0, 1);
|
|
96
|
+
|
|
97
|
+
const _getAPIResources = (csn, appConfig, packageIds) => {
|
|
98
|
+
return appConfig.serviceNames
|
|
99
|
+
.flatMap((serviceName) => createAPIResourceTemplate(serviceName, csn.definitions[serviceName], appConfig, packageIds))
|
|
156
100
|
};
|
|
157
101
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const fGetEventResources = (csn, global,packageIds) => global.aEvents.map((srv) => fCreateEventResourceTemplate(srv, csn.definitions[srv], global, packageIds)).filter((resource) => resource !== null && resource !== undefined);
|
|
102
|
+
const _getEventResources = (csn, appConfig, packageIds) => {
|
|
103
|
+
if (appConfig.events.length === 0) return [];
|
|
104
|
+
return appConfig.serviceNames
|
|
105
|
+
.filter((serviceName) => appConfig.events.some((eventName) => eventName.startsWith(serviceName)))
|
|
106
|
+
.flatMap((serviceName) => createEventResourceTemplate(serviceName, csn.definitions[serviceName], appConfig, packageIds));
|
|
107
|
+
};
|
|
165
108
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
109
|
+
function _getOpenResourceDiscovery(appConfig) {
|
|
110
|
+
return appConfig.env?.openResourceDiscovery || defaults.openResourceDiscovery;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function _getConsumptionBundles(appConfig) {
|
|
114
|
+
return appConfig.env?.consumptionBundles || defaults.consumptionBundles(appConfig);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function validateNamespace(appConfig) {
|
|
118
|
+
const validateSystemNamespace = new RegExp(`^${appConfig.eventApplicationNamespace}\\.[^.]+\\..+$`);
|
|
119
|
+
if (
|
|
120
|
+
appConfig.ordNamespace === undefined &&
|
|
121
|
+
!validateSystemNamespace.test(appConfig.ordNamespace)
|
|
122
|
+
) {
|
|
123
|
+
let error = new Error(
|
|
124
|
+
`Namespace is not defined in cdsrc.json or it is not in the format of ${appConfig.eventApplicationNamespace}.<appName>.<service>`
|
|
125
|
+
);
|
|
126
|
+
Logger.error('Namespace error:', error.message);
|
|
173
127
|
throw error;
|
|
174
128
|
}
|
|
129
|
+
}
|
|
175
130
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
131
|
+
function createDefaultORDDocument(linkedCsn, appConfig) {
|
|
132
|
+
let ordDocument = {
|
|
133
|
+
$schema: "https://sap.github.io/open-resource-discovery/spec-v1/interfaces/Document.schema.json",
|
|
134
|
+
openResourceDiscovery: _getOpenResourceDiscovery(appConfig),
|
|
135
|
+
policyLevel: _getPolicyLevel(appConfig),
|
|
136
|
+
description: _getDescription(appConfig),
|
|
137
|
+
products: _getProducts(appConfig),
|
|
138
|
+
groups: _getGroups(linkedCsn, appConfig),
|
|
139
|
+
consumptionBundles: _getConsumptionBundles(appConfig),
|
|
181
140
|
};
|
|
182
|
-
if(fGetAPIResources(linkedCsn, global).length > 0 && (fGetEventResources(linkedCsn, global).length>0)){
|
|
183
|
-
oReturn.packages = fGetPackages(oReturn.policyLevel, global);
|
|
184
|
-
}
|
|
185
141
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if(oReturn.packages){
|
|
189
|
-
oReturn.packages.forEach((pkg) => {
|
|
190
|
-
packageIds.add(pkg.ordId);
|
|
191
|
-
});
|
|
142
|
+
if (_getAPIResources(linkedCsn, appConfig).length && _getEventResources(linkedCsn, appConfig).length) {
|
|
143
|
+
ordDocument.packages = _getPackages(ordDocument.policyLevel, appConfig);
|
|
192
144
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if(
|
|
199
|
-
|
|
145
|
+
return ordDocument;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function extractPackageIds(ordDocument) {
|
|
149
|
+
const packageIds = [];
|
|
150
|
+
if (ordDocument.packages) {
|
|
151
|
+
ordDocument.packages.map((pkg) => packageIds.push(pkg.ordId));
|
|
200
152
|
}
|
|
201
|
-
return
|
|
153
|
+
return packageIds;
|
|
202
154
|
}
|
|
155
|
+
|
|
156
|
+
module.exports = (csn) => {
|
|
157
|
+
const linkedCsn = cds.linked(csn);
|
|
158
|
+
const appConfig = initializeAppConfig(linkedCsn);
|
|
159
|
+
validateNamespace(appConfig);
|
|
160
|
+
|
|
161
|
+
let ordDocument = createDefaultORDDocument(linkedCsn, appConfig);
|
|
162
|
+
const packageIds = extractPackageIds(ordDocument);
|
|
163
|
+
ordDocument.apiResources = _getAPIResources(linkedCsn, appConfig, packageIds);
|
|
164
|
+
ordDocument.eventResources = _getEventResources(linkedCsn, appConfig, packageIds);
|
|
165
|
+
ordDocument = extendCustomORDContentIfExists(appConfig, ordDocument);
|
|
166
|
+
|
|
167
|
+
return ordDocument;
|
|
168
|
+
};
|
package/lib/plugin.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
const { ord, getMetadata, defaults } = require("./");
|
|
2
1
|
const cds = require("@sap/cds");
|
|
2
|
+
const { Logger } = require("./logger");
|
|
3
|
+
const { ord, getMetadata, defaults } = require("./");
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
cds.on("bootstrap", (app) => {
|
|
5
7
|
app.use("/.well-known/open-resource-discovery", async (req, res) => {
|
|
@@ -10,8 +12,7 @@ cds.on("bootstrap", (app) => {
|
|
|
10
12
|
const { contentType, response } = await getMetadata(req.url);
|
|
11
13
|
res.status(200).contentType(contentType).send(response);
|
|
12
14
|
} catch (error) {
|
|
13
|
-
|
|
14
|
-
console.log('Error while generating metadata');
|
|
15
|
+
Logger.error(error, 'Error while generating metadata');
|
|
15
16
|
res.status(500).send(error.message);
|
|
16
17
|
}
|
|
17
18
|
}
|
|
@@ -23,8 +24,7 @@ cds.on("bootstrap", (app) => {
|
|
|
23
24
|
const data = ord(csn);
|
|
24
25
|
return res.status(200).send(data);
|
|
25
26
|
} catch (error) {
|
|
26
|
-
|
|
27
|
-
console.log('Error while creating ORD document');
|
|
27
|
+
Logger.error(error, 'Error while creating ORD document');
|
|
28
28
|
return res.status(500).send(error.message);
|
|
29
29
|
}
|
|
30
30
|
});
|