@cap-js/ord 1.7.0 → 1.9.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 +10 -5
- package/lib/auth/authentication.js +30 -33
- package/lib/auth/cf-mtls.js +5 -5
- package/lib/build.js +1 -3
- package/lib/common/placeholders.js +6 -0
- package/lib/common/slice.js +72 -0
- package/lib/common/utils.js +190 -0
- package/lib/configuration.js +33 -88
- package/lib/constants.js +2 -32
- package/lib/defaults.js +27 -139
- package/lib/extend-ord-with-custom.js +82 -51
- package/lib/interop-csn.js +23 -11
- package/lib/logger.js +1 -3
- package/lib/meta-data.js +14 -26
- package/lib/ord.js +55 -123
- package/lib/protocol-resolver.js +26 -96
- package/lib/services/ord-service.js +30 -6
- package/lib/templates/api-resource.js +283 -0
- package/lib/templates/entity-type.js +121 -0
- package/lib/templates/event-resource.js +161 -0
- package/lib/templates/group.js +50 -0
- package/lib/templates/integration-dependency.js +98 -0
- package/lib/templates/package.js +91 -0
- package/lib/templates/product.js +57 -0
- package/package.json +7 -5
- package/data/well-known.json +0 -14
- package/lib/access-strategies.js +0 -172
- package/lib/date.js +0 -17
- package/lib/integration-dependency.js +0 -125
- package/lib/templates.js +0 -613
package/lib/configuration.js
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
const cds = require("@sap/cds");
|
|
3
|
-
const _ = require("lodash");
|
|
4
3
|
const { readFileSync } = require("fs");
|
|
5
4
|
|
|
6
5
|
const Logger = require("./logger");
|
|
7
|
-
const { getRFC3339Date } = require("./date");
|
|
8
6
|
const defaults = require("./defaults");
|
|
9
|
-
const {
|
|
10
|
-
const {
|
|
7
|
+
const { getRFC3339Date } = require("./common/utils");
|
|
8
|
+
const { createAuthConfig } = require("./auth/authentication");
|
|
9
|
+
const { resolveAccessStrategies } = require("./common/utils");
|
|
10
|
+
const { CDS_ELEMENT_KIND } = require("./constants");
|
|
11
11
|
|
|
12
12
|
module.exports = class Configuration {
|
|
13
13
|
constructor(csn) {
|
|
14
14
|
const env = cds.env["ord"];
|
|
15
|
-
const
|
|
15
|
+
const authConfig = createAuthConfig();
|
|
16
|
+
const packageName = env?.packageName ?? this._loadNameFromPackageJson();
|
|
16
17
|
const eventApplicationNamespace = cds.env?.export?.asyncapi?.applicationNamespace;
|
|
17
|
-
const ordNamespace =
|
|
18
|
-
const internalNamespace = env?.internalNamespace;
|
|
18
|
+
const ordNamespace = env?.namespace || `customer.${packageName.replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
19
19
|
|
|
20
20
|
if (eventApplicationNamespace && ordNamespace !== eventApplicationNamespace) {
|
|
21
21
|
Logger.warn("ORD and AsyncAPI namespaces should be the same.");
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
if (authConfig.error) {
|
|
25
|
+
throw new Error(`Authentication configuration error: ${authConfig.error}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
this._env = env;
|
|
25
29
|
this._packageName = packageName;
|
|
26
30
|
this._ordNamespace = ordNamespace;
|
|
27
|
-
this._internalNamespace = internalNamespace;
|
|
28
31
|
this._lastUpdate = getRFC3339Date();
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
31
|
-
this._apiResourceNames = this._resolveApiResourceNames(csn);
|
|
32
|
-
this._entityTypeTargets = this._resolveEntityTypeTargets(csn);
|
|
33
|
-
this._eventServiceNames = this._resolveEventServiceNames(csn);
|
|
32
|
+
this._csn = this._propagateORDVisibility(cds.linked(csn));
|
|
33
|
+
this._accessStrategies = resolveAccessStrategies(authConfig);
|
|
34
34
|
this._appName = packageName.replace(/^@/, "").replace(/[@/]/g, "-");
|
|
35
35
|
this._policyLevels = env?.policyLevels || (env?.policyLevel && [env?.policyLevel]) || defaults.policyLevels;
|
|
36
36
|
}
|
|
@@ -39,6 +39,10 @@ module.exports = class Configuration {
|
|
|
39
39
|
return this._env;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
get csn() {
|
|
43
|
+
return this._csn;
|
|
44
|
+
}
|
|
45
|
+
|
|
42
46
|
get appName() {
|
|
43
47
|
return this._appName;
|
|
44
48
|
}
|
|
@@ -51,32 +55,20 @@ module.exports = class Configuration {
|
|
|
51
55
|
return this._packageName;
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
get serviceNames() {
|
|
55
|
-
return this._serviceNames;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
58
|
get ordNamespace() {
|
|
59
59
|
return this._ordNamespace;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
get internalNamespace() {
|
|
63
|
-
return this._internalNamespace;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
62
|
get policyLevels() {
|
|
67
63
|
return this._policyLevels;
|
|
68
64
|
}
|
|
69
65
|
|
|
70
|
-
get
|
|
71
|
-
return this.
|
|
66
|
+
get accessStrategies() {
|
|
67
|
+
return this._accessStrategies;
|
|
72
68
|
}
|
|
73
69
|
|
|
74
|
-
get
|
|
75
|
-
return this.
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
get eventServiceNames() {
|
|
79
|
-
return this._eventServiceNames;
|
|
70
|
+
get internalNamespace() {
|
|
71
|
+
return this._env?.internalNamespace;
|
|
80
72
|
}
|
|
81
73
|
|
|
82
74
|
get hasSAPPolicyLevel() {
|
|
@@ -84,12 +76,7 @@ module.exports = class Configuration {
|
|
|
84
76
|
}
|
|
85
77
|
|
|
86
78
|
get existingProductORDId() {
|
|
87
|
-
return this.
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
_resolveServiceNames(csn) {
|
|
91
|
-
return Object.keys(csn.definitions) //
|
|
92
|
-
.filter((name) => this._isValidService(name, csn.definitions[name]));
|
|
79
|
+
return this._env?.existingProductORDId;
|
|
93
80
|
}
|
|
94
81
|
|
|
95
82
|
_loadNameFromPackageJson() {
|
|
@@ -102,60 +89,18 @@ module.exports = class Configuration {
|
|
|
102
89
|
return JSON.parse(readFileSync(packageJsonPath, "utf-8")).name;
|
|
103
90
|
}
|
|
104
91
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
.some((blocked) => name.includes(blocked));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
_resolveApiResourceNames(csn) {
|
|
111
|
-
return Object.keys(csn.definitions)
|
|
112
|
-
.filter((name) => this._isValidService(name, csn.definitions[name]))
|
|
113
|
-
.filter((name) => !this._serviceOnlyContainsEvents(csn.definitions[name]));
|
|
114
|
-
}
|
|
92
|
+
_propagateORDVisibility(csn) {
|
|
93
|
+
const annotation = "@ORD.Extensions.visibility";
|
|
115
94
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
CONTENT_MERGE_KEY,
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
_resolveEventServiceNames(csn) {
|
|
129
|
-
const serviceNames = this._resolveServiceNames(csn);
|
|
130
|
-
|
|
131
|
-
return [
|
|
132
|
-
...new Set(
|
|
133
|
-
Object.keys(csn.definitions)
|
|
134
|
-
.filter((name) => !this._isBlockedServiceName(name))
|
|
135
|
-
.filter((name) => csn.definitions[name].kind === CDS_ELEMENT_KIND.event)
|
|
136
|
-
.filter((name) => csn.definitions[name]["_service"]?.["@protocol"] !== "none")
|
|
137
|
-
.filter((name) => serviceNames.some((serviceName) => name.startsWith(`${serviceName}.`)))
|
|
138
|
-
.map((name) => serviceNames.find((serviceName) => name.startsWith(`${serviceName}.`))),
|
|
139
|
-
),
|
|
140
|
-
];
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
_isValidService(key, definition) {
|
|
144
|
-
const isExternalService = Object.keys(cds).includes("requires") && Object.keys(cds.requires).includes(key);
|
|
145
|
-
|
|
146
|
-
return (
|
|
147
|
-
definition.kind === CDS_ELEMENT_KIND.service &&
|
|
148
|
-
!definition["@cds.external"] &&
|
|
149
|
-
definition["@protocol"] !== "none" &&
|
|
150
|
-
!isExternalService &&
|
|
151
|
-
!this._isBlockedServiceName(key)
|
|
152
|
-
);
|
|
153
|
-
}
|
|
95
|
+
Object.values(csn.definitions)
|
|
96
|
+
.filter((service) => Boolean(service[annotation]))
|
|
97
|
+
.filter((definition) => definition.kind === CDS_ELEMENT_KIND.service)
|
|
98
|
+
.forEach((service) => {
|
|
99
|
+
Object.values(csn.definitions)
|
|
100
|
+
.filter((definition) => definition.name.startsWith(`${service.name}.`))
|
|
101
|
+
.forEach((definition) => (definition[annotation] ??= service[annotation]));
|
|
102
|
+
});
|
|
154
103
|
|
|
155
|
-
|
|
156
|
-
return (
|
|
157
|
-
Object.keys(definition.events || {}).length > 0 &&
|
|
158
|
-
["actions", "entities", "functions"].every((key) => Object.keys(definition[key] || {}).length === 0)
|
|
159
|
-
);
|
|
104
|
+
return csn;
|
|
160
105
|
}
|
|
161
106
|
};
|
package/lib/constants.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
const AUTHENTICATION_TYPE = Object.freeze({
|
|
2
|
-
Open: "open",
|
|
3
|
-
Basic: "basic",
|
|
4
|
-
CfMtls: "cf-mtls",
|
|
5
|
-
});
|
|
6
|
-
|
|
7
1
|
const BASIC_AUTH_HEADER_KEY = "authorization";
|
|
8
2
|
|
|
9
3
|
const BUILD_DEFAULT_PATH = "gen/ord";
|
|
@@ -11,13 +5,8 @@ const BUILD_DEFAULT_PATH = "gen/ord";
|
|
|
11
5
|
const ORD_ACCESS_STRATEGY = Object.freeze({
|
|
12
6
|
Open: "open",
|
|
13
7
|
Basic: "basic-auth",
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const AUTH_TYPE_ORD_ACCESS_STRATEGY_MAP = Object.freeze({
|
|
18
|
-
[AUTHENTICATION_TYPE.Open]: ORD_ACCESS_STRATEGY.Open,
|
|
19
|
-
[AUTHENTICATION_TYPE.Basic]: ORD_ACCESS_STRATEGY.Basic,
|
|
20
|
-
[AUTHENTICATION_TYPE.CfMtls]: ORD_ACCESS_STRATEGY.CfMtls,
|
|
8
|
+
CmpMtls: "sap:cmp-mtls:v1",
|
|
9
|
+
BahMtls: "sap.businesshub:mtls:v1",
|
|
21
10
|
});
|
|
22
11
|
|
|
23
12
|
// CF mTLS default header names
|
|
@@ -42,15 +31,6 @@ const CDS_ELEMENT_KIND = Object.freeze({
|
|
|
42
31
|
type: "type",
|
|
43
32
|
});
|
|
44
33
|
|
|
45
|
-
const COMPILER_TYPES = Object.freeze({
|
|
46
|
-
oas3: "oas3",
|
|
47
|
-
asyncapi2: "asyncapi2",
|
|
48
|
-
edmx: "edmx",
|
|
49
|
-
csn: "csn",
|
|
50
|
-
mcp: "mcp",
|
|
51
|
-
graphql: "graphql",
|
|
52
|
-
});
|
|
53
|
-
|
|
54
34
|
const CONTENT_MERGE_KEY = "ordId";
|
|
55
35
|
|
|
56
36
|
const DATA_PRODUCT_ANNOTATION = "@DataIntegration.dataProduct.type";
|
|
@@ -116,8 +96,6 @@ const EXTERNAL_DP_ORD_ID_ANNOTATION = "@cds.dp.ordId";
|
|
|
116
96
|
|
|
117
97
|
const EXTERNAL_SERVICE_ANNOTATION = "@cds.external";
|
|
118
98
|
|
|
119
|
-
const INTEGRATION_DEPENDENCY_RESOURCE_NAME = "externalDependencies";
|
|
120
|
-
|
|
121
99
|
// ORD apiProtocol values
|
|
122
100
|
const ORD_API_PROTOCOL = Object.freeze({
|
|
123
101
|
ODATA_V4: "odata-v4",
|
|
@@ -149,9 +127,6 @@ const ORD_ONLY_PROTOCOLS = Object.freeze({
|
|
|
149
127
|
},
|
|
150
128
|
});
|
|
151
129
|
|
|
152
|
-
// Protocols that the ORD plugin cannot currently generate definitions for
|
|
153
|
-
const PLUGIN_UNSUPPORTED_PROTOCOLS = Object.freeze([]);
|
|
154
|
-
|
|
155
130
|
// CF mTLS Error Reasons
|
|
156
131
|
const CF_MTLS_ERROR_REASON = Object.freeze({
|
|
157
132
|
NO_HEADERS: "NO_HEADERS",
|
|
@@ -183,14 +158,11 @@ const DOCUMENT_PERSPECTIVES = Object.freeze({
|
|
|
183
158
|
const LOCAL_TENANT_ID_HEADER_KEY = "local-tenant-id";
|
|
184
159
|
|
|
185
160
|
module.exports = {
|
|
186
|
-
AUTHENTICATION_TYPE,
|
|
187
|
-
AUTH_TYPE_ORD_ACCESS_STRATEGY_MAP,
|
|
188
161
|
BASIC_AUTH_HEADER_KEY,
|
|
189
162
|
BUILD_DEFAULT_PATH,
|
|
190
163
|
CAP_TO_ORD_PROTOCOL_MAP,
|
|
191
164
|
CDS_ELEMENT_KIND,
|
|
192
165
|
CF_MTLS_HEADERS,
|
|
193
|
-
COMPILER_TYPES,
|
|
194
166
|
CONTENT_MERGE_KEY,
|
|
195
167
|
DATA_PRODUCT_ANNOTATION,
|
|
196
168
|
DATA_PRODUCT_SIMPLE_ANNOTATION,
|
|
@@ -199,7 +171,6 @@ module.exports = {
|
|
|
199
171
|
ENTITY_RELATIONSHIP_ANNOTATION,
|
|
200
172
|
EXTERNAL_DP_ORD_ID_ANNOTATION,
|
|
201
173
|
EXTERNAL_SERVICE_ANNOTATION,
|
|
202
|
-
INTEGRATION_DEPENDENCY_RESOURCE_NAME,
|
|
203
174
|
LEVEL,
|
|
204
175
|
MCP_RESOURCE_DEFINITION_TYPE,
|
|
205
176
|
OPENAPI_SERVERS_ANNOTATION,
|
|
@@ -212,7 +183,6 @@ module.exports = {
|
|
|
212
183
|
ORD_ONLY_PROTOCOLS,
|
|
213
184
|
ORD_RESOURCE_TYPE,
|
|
214
185
|
ORD_SERVICE_NAME,
|
|
215
|
-
PLUGIN_UNSUPPORTED_PROTOCOLS,
|
|
216
186
|
RESOURCE_VISIBILITY,
|
|
217
187
|
ALLOWED_VISIBILITY,
|
|
218
188
|
IMPLEMENTATIONSTANDARD_VERSIONS,
|
package/lib/defaults.js
CHANGED
|
@@ -1,75 +1,12 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
|
-
const _ = require("lodash");
|
|
3
2
|
const cds = require("@sap/cds");
|
|
4
3
|
const { join } = require("path");
|
|
5
4
|
|
|
6
|
-
const {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
RESOURCE_VISIBILITY,
|
|
10
|
-
DOCUMENT_PERSPECTIVES,
|
|
11
|
-
CONTENT_MERGE_KEY,
|
|
12
|
-
} = require("./constants");
|
|
5
|
+
const { slice } = require("./common/slice");
|
|
6
|
+
const { DOCUMENT_PERSPECTIVES } = require("./constants");
|
|
7
|
+
const { resolveAccessStrategies } = require("./common/utils");
|
|
13
8
|
|
|
14
|
-
const
|
|
15
|
-
const arrayTypeCheck = (value) => Array.isArray(value);
|
|
16
|
-
|
|
17
|
-
// see ref: https://pages.github.tools.sap/CentralEngineering/open-resource-discovery-specification/spec-v1/interfaces/document#package
|
|
18
|
-
const packageTypeChecks = {
|
|
19
|
-
policyLevels: arrayTypeCheck,
|
|
20
|
-
packageLinks: arrayTypeCheck,
|
|
21
|
-
links: arrayTypeCheck,
|
|
22
|
-
licenseType: stringTypeCheck,
|
|
23
|
-
supportInfo: stringTypeCheck,
|
|
24
|
-
vendor: stringTypeCheck,
|
|
25
|
-
countries: arrayTypeCheck,
|
|
26
|
-
lineOfBusiness: arrayTypeCheck,
|
|
27
|
-
industry: arrayTypeCheck,
|
|
28
|
-
runtimeRestriction: stringTypeCheck,
|
|
29
|
-
tags: arrayTypeCheck,
|
|
30
|
-
labels: arrayTypeCheck,
|
|
31
|
-
documentationLabels: arrayTypeCheck,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const regexWithRemoval = (name) => {
|
|
35
|
-
return name?.replace(/[^a-zA-Z0-9]/g, "");
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const nameWithDot = (name) => {
|
|
39
|
-
return regexWithRemoval(name.charAt(0)) + name.slice(1, name.length).replace(/[^a-zA-Z0-9]/g, ".");
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const nameWithSpaces = (name) => {
|
|
43
|
-
return regexWithRemoval(name.charAt(0)) + name.slice(1, name.length).replace(/[^a-zA-Z0-9]/g, " ");
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const defaultProductOrdId = (name) => `customer:product:${nameWithDot(name)}:`;
|
|
47
|
-
|
|
48
|
-
const generatePackageDescriptions = (name, type, visibility) => ({
|
|
49
|
-
shortDescription: `Package containing ${visibility} ${type}`,
|
|
50
|
-
description: `This package contains ${visibility} ${type} for ${nameWithSpaces(name)}.`,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const generateUniquePackageOrdId = (ordNamespace, name, tag, visibility) => {
|
|
54
|
-
const visibilitySuffix = visibility === RESOURCE_VISIBILITY.public ? "" : `-${visibility}`;
|
|
55
|
-
return `${ordNamespace}:package:${regexWithRemoval(name)}${tag}${visibilitySuffix}:v1`;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const mapEnvPackageInfo = (packageConfig) => {
|
|
59
|
-
if (!packageConfig) {
|
|
60
|
-
return { vendor: undefined };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const filteredEnv = {};
|
|
64
|
-
for (const key in packageConfig) {
|
|
65
|
-
const value = packageConfig[key];
|
|
66
|
-
const checkFunction = packageTypeChecks[key];
|
|
67
|
-
if (value && checkFunction && checkFunction(value)) {
|
|
68
|
-
filteredEnv[key] = value;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return filteredEnv;
|
|
72
|
-
};
|
|
9
|
+
const sizeLimit = 2000000; // 2mb
|
|
73
10
|
|
|
74
11
|
/**
|
|
75
12
|
* Module containing default configuration for ORD Document.
|
|
@@ -77,86 +14,37 @@ const mapEnvPackageInfo = (packageConfig) => {
|
|
|
77
14
|
*/
|
|
78
15
|
module.exports = {
|
|
79
16
|
$schema: "https://open-resource-discovery.github.io/specification/spec-v1/interfaces/Document.schema.json",
|
|
80
|
-
openResourceDiscovery: "1.
|
|
17
|
+
openResourceDiscovery: "1.16",
|
|
81
18
|
policyLevels: ["none"],
|
|
82
19
|
groupTypeId: "sap.cds:service",
|
|
83
20
|
description: "this is an application description",
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
title: nameWithSpaces(name),
|
|
88
|
-
shortDescription: SHORT_DESCRIPTION_PREFIX + nameWithSpaces(name),
|
|
89
|
-
vendor: "customer:vendor:Customer:",
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
packages: (appConfig, products) => {
|
|
93
|
-
const name = appConfig.appName;
|
|
94
|
-
const ordNamespace = appConfig.ordNamespace;
|
|
95
|
-
const productsOrdId = appConfig.existingProductORDId || products?.[0]?.ordId;
|
|
96
|
-
const { vendor, ...envValues } = mapEnvPackageInfo(appConfig?.env?.packages?.[0]);
|
|
97
|
-
const visibilities = !appConfig.hasSAPPolicyLevel
|
|
98
|
-
? [RESOURCE_VISIBILITY.public]
|
|
99
|
-
: Object.values(RESOURCE_VISIBILITY);
|
|
100
|
-
const packageTypes = !appConfig.hasSAPPolicyLevel
|
|
101
|
-
? [{ tag: "", type: "General" }]
|
|
102
|
-
: [
|
|
103
|
-
{ tag: `-${ORD_RESOURCE_TYPE.api}`, type: "APIs" },
|
|
104
|
-
{ tag: `-${ORD_RESOURCE_TYPE.event}`, type: "Events" },
|
|
105
|
-
{ tag: `-${ORD_RESOURCE_TYPE.entityType}`, type: "Entity Types" },
|
|
106
|
-
{ tag: `-${ORD_RESOURCE_TYPE.dataProduct}`, type: "Data Products" },
|
|
107
|
-
{ tag: `-${ORD_RESOURCE_TYPE.integrationDependency}`, type: "Integration Dependencies" },
|
|
108
|
-
];
|
|
109
|
-
|
|
110
|
-
return _.uniqBy(
|
|
111
|
-
packageTypes.flatMap(({ tag, type }) =>
|
|
112
|
-
visibilities.map((visibility) => ({
|
|
113
|
-
ordId: generateUniquePackageOrdId(ordNamespace, name, tag, visibility),
|
|
114
|
-
title: nameWithSpaces(name),
|
|
115
|
-
...generatePackageDescriptions(name, type, visibility),
|
|
116
|
-
version: "1.0.0",
|
|
117
|
-
...(productsOrdId && { partOfProducts: [productsOrdId || defaultProductOrdId(name)] }),
|
|
118
|
-
vendor: vendor || "customer:vendor:Customer:",
|
|
119
|
-
...envValues,
|
|
120
|
-
})),
|
|
121
|
-
),
|
|
122
|
-
CONTENT_MERGE_KEY,
|
|
123
|
-
);
|
|
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
|
-
baseTemplate: (authConfig, tenant) => {
|
|
137
|
-
// Get access strategies from the provided authConfig
|
|
138
|
-
// If auth config is not available, fall back to empty array
|
|
139
|
-
const toggles = cds.env.requires.toggles;
|
|
140
|
-
const extensibility = cds.env.requires.extensibility;
|
|
141
|
-
const accessStrategies = authConfig?.accessStrategies || [];
|
|
21
|
+
sizeLimit: sizeLimit,
|
|
22
|
+
baseTemplate: (authConfig, document, tenantDocument) => {
|
|
23
|
+
const accessStrategies = resolveAccessStrategies(authConfig);
|
|
142
24
|
|
|
143
25
|
return {
|
|
144
26
|
openResourceDiscoveryV1: {
|
|
145
27
|
documents: [
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
28
|
+
...Array(slice(document, sizeLimit).length)
|
|
29
|
+
.fill(0)
|
|
30
|
+
.map((_, i) => {
|
|
31
|
+
return {
|
|
32
|
+
url: `/ord/v1/documents/ord-document?part=${i}`,
|
|
33
|
+
perspective: DOCUMENT_PERSPECTIVES.SystemVersion,
|
|
34
|
+
accessStrategies,
|
|
35
|
+
};
|
|
36
|
+
}),
|
|
37
|
+
...(!tenantDocument
|
|
152
38
|
? []
|
|
153
|
-
:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
39
|
+
: Array(slice(tenantDocument, sizeLimit).length)
|
|
40
|
+
.fill(0)
|
|
41
|
+
.map((_, i) => {
|
|
42
|
+
return {
|
|
43
|
+
url: `/ord/v1/documents/ord-document?part=${i}&perspective=${encodeURIComponent(DOCUMENT_PERSPECTIVES.SystemInstance)}`,
|
|
44
|
+
perspective: DOCUMENT_PERSPECTIVES.SystemInstance,
|
|
45
|
+
accessStrategies,
|
|
46
|
+
};
|
|
47
|
+
})),
|
|
160
48
|
],
|
|
161
49
|
},
|
|
162
50
|
};
|
|
@@ -1,68 +1,99 @@
|
|
|
1
|
-
const { CONTENT_MERGE_KEY } = require("./constants");
|
|
2
1
|
const cds = require("@sap/cds");
|
|
3
2
|
const fs = require("fs");
|
|
4
3
|
const Logger = require("./logger");
|
|
5
4
|
const path = require("path");
|
|
6
5
|
const _ = require("lodash");
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
for (const key in obj) {
|
|
10
|
-
if (obj[key] === null) {
|
|
11
|
-
delete obj[key];
|
|
12
|
-
} else if (typeof obj[key] === "object") {
|
|
13
|
-
cleanNullProperties(obj[key]);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return obj;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function patchGeneratedOrdResources(destinationObj, sourceObj) {
|
|
20
|
-
const destObj = _.keyBy(destinationObj, CONTENT_MERGE_KEY);
|
|
21
|
-
const srcObj = _.keyBy(sourceObj, CONTENT_MERGE_KEY);
|
|
7
|
+
const SCALAR_TYPES = Object.freeze(["number", "string", "boolean"]);
|
|
22
8
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
9
|
+
const MERGE_STRATEGIES = Object.freeze({
|
|
10
|
+
// Directly replace scalar values and scalar value arrays
|
|
11
|
+
$schema: (current, custom) => custom,
|
|
12
|
+
baseUrl: (current, custom) => custom,
|
|
13
|
+
description: (current, custom) => custom,
|
|
14
|
+
perspective: (current, custom) => custom,
|
|
15
|
+
policyLevel: (current, custom) => custom,
|
|
16
|
+
customPolicyLevel: (current, custom) => custom,
|
|
17
|
+
policyLevels: (current, custom) => prune(custom),
|
|
18
|
+
openResourceDiscovery: (current, custom) => custom,
|
|
29
19
|
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
// Perform simple merge for objects
|
|
21
|
+
describedSystemType: (current, custom) => prune(_.assign(current, custom)),
|
|
22
|
+
describedSystemVersion: (current, custom) => prune(_.assign(current, custom)),
|
|
23
|
+
describedSystemInstance: (current, custom) => prune(_.assign(current, custom)),
|
|
32
24
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
25
|
+
// Perform smart merge for arrays of objects
|
|
26
|
+
agents: (current, custom) => merge(current, custom, ["ordId"]),
|
|
27
|
+
vendors: (current, custom) => merge(current, custom, ["ordId"]),
|
|
28
|
+
overlays: (current, custom) => merge(current, custom, ["ordId"]),
|
|
29
|
+
products: (current, custom) => merge(current, custom, ["ordId"]),
|
|
30
|
+
packages: (current, custom) => merge(current, custom, ["ordId"]),
|
|
31
|
+
groups: (current, custom) => merge(current, custom, ["groupId"]),
|
|
32
|
+
entityTypes: (current, custom) => merge(current, custom, ["ordId"]),
|
|
33
|
+
apiResources: (current, custom) => merge(current, custom, ["ordId"]),
|
|
34
|
+
capabilities: (current, custom) => merge(current, custom, ["ordId"]),
|
|
35
|
+
dataProducts: (current, custom) => merge(current, custom, ["ordId"]),
|
|
36
|
+
eventResources: (current, custom) => merge(current, custom, ["ordId"]),
|
|
37
|
+
groupTypes: (current, custom) => merge(current, custom, ["groupTypeId"]),
|
|
38
|
+
consumptionBundles: (current, custom) => merge(current, custom, ["ordId"]),
|
|
39
|
+
integrationDependencies: (current, custom) => merge(current, custom, ["ordId"]),
|
|
40
|
+
tombstones: (current, custom) => merge(current, custom, ["ordId", "groupId", "groupTypeId"]),
|
|
41
|
+
});
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
clonedOrdContent[key] = customORDContent[key];
|
|
50
|
-
}
|
|
43
|
+
function prune(entity) {
|
|
44
|
+
if (Array.isArray(entity)) {
|
|
45
|
+
return entity
|
|
46
|
+
.filter((value) => ![null, undefined].includes(value))
|
|
47
|
+
.map((value) => (SCALAR_TYPES.includes(typeof value) ? value : prune(value)));
|
|
51
48
|
}
|
|
52
|
-
|
|
49
|
+
|
|
50
|
+
return Object.fromEntries(
|
|
51
|
+
Object.entries(entity)
|
|
52
|
+
.filter(([, value]) => ![null, undefined].includes(value))
|
|
53
|
+
.map(([key, value]) => [key, SCALAR_TYPES.includes(typeof value) ? value : prune(value)]),
|
|
54
|
+
);
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
function merge(target, source, keys) {
|
|
58
|
+
const iteratee = (entity) => keys.map((key) => entity[key] || "").join(".");
|
|
59
|
+
const sources = _.keyBy(source || [], iteratee);
|
|
60
|
+
const targets = _.keyBy(target || [], iteratee);
|
|
61
|
+
|
|
62
|
+
return Array.from(new Set([...Object.keys(targets), ...Object.keys(sources)])) //
|
|
63
|
+
.map((key) => _.assign(structuredClone(targets[key]), structuredClone(sources[key])))
|
|
64
|
+
.map(prune);
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
module.exports = {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
MERGE_STRATEGIES,
|
|
69
|
+
getCustomORDContent: (configuration) => {
|
|
70
|
+
if (!configuration.env?.customOrdContentFile) return {};
|
|
71
|
+
|
|
72
|
+
const file = path.join(cds.root, configuration.env?.customOrdContentFile);
|
|
73
|
+
if (!fs.existsSync(file)) {
|
|
74
|
+
Logger.error("Custom ORD content file not found at", file);
|
|
75
|
+
return {};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
79
|
+
},
|
|
80
|
+
compareAndHandleCustomORDContentWithExistingContent: (ordContent, customORDContent) => {
|
|
81
|
+
return Object.fromEntries([
|
|
82
|
+
// Process elements found only in 'ordContent'
|
|
83
|
+
...Object.entries(ordContent)
|
|
84
|
+
.filter(([key]) => !(key in customORDContent))
|
|
85
|
+
.map(([key, value]) => [key, structuredClone(value)]),
|
|
86
|
+
|
|
87
|
+
// Process elements found only in 'customORDContent'
|
|
88
|
+
...Object.entries(customORDContent)
|
|
89
|
+
.filter(([key]) => !(key in ordContent))
|
|
90
|
+
.map(([key, value]) => [key, structuredClone(value)]),
|
|
91
|
+
|
|
92
|
+
// Process elements found in both 'ordContent' and 'customORDContent'
|
|
93
|
+
...Object.entries(customORDContent)
|
|
94
|
+
.filter(([key]) => key in ordContent)
|
|
95
|
+
.filter(([, value]) => ![null, undefined].includes(value))
|
|
96
|
+
.map(([key, value]) => [key, MERGE_STRATEGIES[key](structuredClone(ordContent[key]), value)]),
|
|
97
|
+
]);
|
|
98
|
+
},
|
|
68
99
|
};
|