@cap-js/ord 1.5.0 → 1.7.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/cds-plugin.js +4 -14
- package/lib/auth/mtls-endpoint-service.js +1 -1
- package/lib/build.js +6 -10
- package/lib/common/register-compile-targets.js +31 -0
- package/lib/configuration.js +161 -0
- package/lib/constants.js +11 -10
- package/lib/defaults.js +73 -42
- package/lib/{extendOrdWithCustom.js → extend-ord-with-custom.js} +2 -8
- package/lib/index.js +1 -1
- package/lib/{integrationDependency.js → integration-dependency.js} +0 -2
- package/lib/{interopCsn.js → interop-csn.js} +4 -4
- package/lib/meta-data.js +90 -0
- package/lib/ord.js +60 -290
- package/lib/services/mtx-ord-provider-service.cds +21 -0
- package/lib/services/mtx-ord-provider-service.js +43 -0
- package/lib/services/ord-service.js +115 -0
- package/lib/templates.js +35 -44
- package/lib/threads/compile.js +4 -1
- package/package.json +12 -6
- package/lib/metaData.js +0 -108
- package/lib/ord-service.js +0 -54
- package/lib/utils.js +0 -12
- /package/lib/{ord-service.cds → services/ord-service.cds} +0 -0
package/cds-plugin.js
CHANGED
|
@@ -4,17 +4,7 @@ if (cds.cli.command === "build") {
|
|
|
4
4
|
cds.build?.register?.("ord", require("./lib/build"));
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const registerORDCompileTarget = () => {
|
|
14
|
-
Object.defineProperty(cds.compile.to, "ord", {
|
|
15
|
-
get: _lazyRegisterCompileTarget,
|
|
16
|
-
configurable: true,
|
|
17
|
-
});
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
registerORDCompileTarget();
|
|
7
|
+
Object.defineProperty(cds.compile.to, "ord", {
|
|
8
|
+
configurable: true,
|
|
9
|
+
get: () => (model) => require("./lib/index").ord(model, []),
|
|
10
|
+
});
|
package/lib/build.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
const os = require("os");
|
|
1
2
|
const path = require("path");
|
|
2
3
|
const cds = require("@sap/cds");
|
|
3
4
|
const _ = require("lodash");
|
|
4
5
|
const Piscina = require("piscina");
|
|
5
6
|
const cliProgress = require("cli-progress");
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const defaults = require("./defaults.js");
|
|
8
9
|
const { ord } = require("./index");
|
|
9
|
-
const
|
|
10
|
+
const registerCompileTargets = require("./common/register-compile-targets");
|
|
11
|
+
const { BUILD_DEFAULT_PATH, ORD_DOCUMENT_FILE_NAME, DOCUMENT_PERSPECTIVES } = require("./constants");
|
|
10
12
|
|
|
11
13
|
module.exports = class OrdBuildPlugin extends cds.build.Plugin {
|
|
12
14
|
static taskDefaults = { src: cds.env.folders.srv };
|
|
@@ -16,13 +18,7 @@ module.exports = class OrdBuildPlugin extends cds.build.Plugin {
|
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
async build() {
|
|
19
|
-
|
|
20
|
-
// but cds build rebuilds cds.env afterward, losing both registrations.
|
|
21
|
-
// Re-apply so endpoints4() and cds.compile.to.graphql work during build.
|
|
22
|
-
if ("@cap-js/graphql" in cds.env.plugins && !cds.env.protocols?.graphql) {
|
|
23
|
-
cds.env.protocols.graphql = { path: "/graphql", impl: "@cap-js/graphql" };
|
|
24
|
-
require("@cap-js/graphql/lib/api").registerCompileTargets();
|
|
25
|
-
}
|
|
21
|
+
registerCompileTargets();
|
|
26
22
|
|
|
27
23
|
return this.model()
|
|
28
24
|
.then((model) => ({ model, document: ord(model) }))
|
|
@@ -58,7 +54,7 @@ module.exports = class OrdBuildPlugin extends cds.build.Plugin {
|
|
|
58
54
|
resourceDefinition.url = this._createRelativePath(resourceDefinition.url);
|
|
59
55
|
});
|
|
60
56
|
|
|
61
|
-
return clone;
|
|
57
|
+
return defaults.adjustForPerspective(clone, DOCUMENT_PERSPECTIVES.SystemVersion);
|
|
62
58
|
}
|
|
63
59
|
|
|
64
60
|
_createRelativePath(url) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const cds = require("@sap/cds");
|
|
2
|
+
const Logger = require("../logger");
|
|
3
|
+
|
|
4
|
+
const PROTOCOL_PROVIDERS = Object.freeze({
|
|
5
|
+
["@cap-js/mcp"]: "mcp",
|
|
6
|
+
["@cap-js/graphql"]: "graphql",
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
module.exports = () => {
|
|
10
|
+
// Plugins like @cap-js/mcp and @cap-js/graphql register protocols and compile targets
|
|
11
|
+
// at runtime via cds-plugin.js, but cds build rebuilds cds.env afterward, losing both
|
|
12
|
+
// registrations. Re-apply so that compilation works accordingly during build and runtime.
|
|
13
|
+
|
|
14
|
+
Object.keys(cds.env.plugins || {})
|
|
15
|
+
.filter((plugin) => !!PROTOCOL_PROVIDERS[plugin])
|
|
16
|
+
.forEach((plugin) => {
|
|
17
|
+
try {
|
|
18
|
+
const protocol = PROTOCOL_PROVIDERS[plugin];
|
|
19
|
+
|
|
20
|
+
require(`${plugin}/lib/api`)?.registerCompileTargets?.();
|
|
21
|
+
cds.env.protocols = {
|
|
22
|
+
...cds.env.protocols,
|
|
23
|
+
...(!protocol || cds.env.protocols?.[protocol]
|
|
24
|
+
? {}
|
|
25
|
+
: { [protocol]: { path: `/${protocol}`, impl: plugin } }),
|
|
26
|
+
};
|
|
27
|
+
} catch (error) {
|
|
28
|
+
Logger.warn(`Failed to register compile targets for ${plugin}: ${error}`);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const cds = require("@sap/cds");
|
|
3
|
+
const _ = require("lodash");
|
|
4
|
+
const { readFileSync } = require("fs");
|
|
5
|
+
|
|
6
|
+
const Logger = require("./logger");
|
|
7
|
+
const { getRFC3339Date } = require("./date");
|
|
8
|
+
const defaults = require("./defaults");
|
|
9
|
+
const { createEntityTypeMappingsItemTemplate } = require("./templates");
|
|
10
|
+
const { CONTENT_MERGE_KEY, CDS_ELEMENT_KIND } = require("./constants");
|
|
11
|
+
|
|
12
|
+
module.exports = class Configuration {
|
|
13
|
+
constructor(csn) {
|
|
14
|
+
const env = cds.env["ord"];
|
|
15
|
+
const packageName = this._loadNameFromPackageJson();
|
|
16
|
+
const eventApplicationNamespace = cds.env?.export?.asyncapi?.applicationNamespace;
|
|
17
|
+
const ordNamespace = cds.env["ord"]?.namespace || `customer.${packageName.replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
18
|
+
const internalNamespace = env?.internalNamespace;
|
|
19
|
+
|
|
20
|
+
if (eventApplicationNamespace && ordNamespace !== eventApplicationNamespace) {
|
|
21
|
+
Logger.warn("ORD and AsyncAPI namespaces should be the same.");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this._env = env;
|
|
25
|
+
this._packageName = packageName;
|
|
26
|
+
this._ordNamespace = ordNamespace;
|
|
27
|
+
this._internalNamespace = internalNamespace;
|
|
28
|
+
this._lastUpdate = getRFC3339Date();
|
|
29
|
+
this._serviceNames = this._resolveServiceNames(csn);
|
|
30
|
+
this._existingProductORDId = env?.existingProductORDId;
|
|
31
|
+
this._apiResourceNames = this._resolveApiResourceNames(csn);
|
|
32
|
+
this._entityTypeTargets = this._resolveEntityTypeTargets(csn);
|
|
33
|
+
this._eventServiceNames = this._resolveEventServiceNames(csn);
|
|
34
|
+
this._appName = packageName.replace(/^@/, "").replace(/[@/]/g, "-");
|
|
35
|
+
this._policyLevels = env?.policyLevels || (env?.policyLevel && [env?.policyLevel]) || defaults.policyLevels;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get env() {
|
|
39
|
+
return this._env;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get appName() {
|
|
43
|
+
return this._appName;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get lastUpdate() {
|
|
47
|
+
return this._lastUpdate;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get packageName() {
|
|
51
|
+
return this._packageName;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get serviceNames() {
|
|
55
|
+
return this._serviceNames;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get ordNamespace() {
|
|
59
|
+
return this._ordNamespace;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get internalNamespace() {
|
|
63
|
+
return this._internalNamespace;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get policyLevels() {
|
|
67
|
+
return this._policyLevels;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get apiResourceNames() {
|
|
71
|
+
return this._apiResourceNames;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get entityTypeTargets() {
|
|
75
|
+
return this._entityTypeTargets;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get eventServiceNames() {
|
|
79
|
+
return this._eventServiceNames;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get hasSAPPolicyLevel() {
|
|
83
|
+
return this.policyLevels.some((policyLevel) => policyLevel.split(":")[0].toLowerCase() === "sap");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get existingProductORDId() {
|
|
87
|
+
return this._existingProductORDId;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
_resolveServiceNames(csn) {
|
|
91
|
+
return Object.keys(csn.definitions) //
|
|
92
|
+
.filter((name) => this._isValidService(name, csn.definitions[name]));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_loadNameFromPackageJson() {
|
|
96
|
+
const packageJsonPath = path.join(cds.root, "package.json");
|
|
97
|
+
|
|
98
|
+
if (!cds.utils.exists(packageJsonPath)) {
|
|
99
|
+
throw new Error("package.json not found in the project root directory");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return JSON.parse(readFileSync(packageJsonPath, "utf-8")).name;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
_isBlockedServiceName(name) {
|
|
106
|
+
return ["cds.xt.MTXServices", "MtxOrdProviderService", "OpenResourceDiscoveryService"] //
|
|
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
|
+
}
|
|
115
|
+
|
|
116
|
+
_resolveEntityTypeTargets(csn) {
|
|
117
|
+
return _.uniqBy(
|
|
118
|
+
Object.keys(csn.definitions)
|
|
119
|
+
.filter((name) => !name.includes(".texts"))
|
|
120
|
+
.filter((name) => !this._isBlockedServiceName(name))
|
|
121
|
+
.filter((name) => csn.definitions[name].kind === CDS_ELEMENT_KIND.entity)
|
|
122
|
+
.filter((name) => csn.definitions[name]["_service"]?.["@protocol"] !== "none")
|
|
123
|
+
.flatMap((name) => createEntityTypeMappingsItemTemplate(csn.definitions[name]) || []),
|
|
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
|
+
}
|
|
154
|
+
|
|
155
|
+
_serviceOnlyContainsEvents(definition) {
|
|
156
|
+
return (
|
|
157
|
+
Object.keys(definition.events || {}).length > 0 &&
|
|
158
|
+
["actions", "entities", "functions"].every((key) => Object.keys(definition[key] || {}).length === 0)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
};
|
package/lib/constants.js
CHANGED
|
@@ -8,11 +8,6 @@ const BASIC_AUTH_HEADER_KEY = "authorization";
|
|
|
8
8
|
|
|
9
9
|
const BUILD_DEFAULT_PATH = "gen/ord";
|
|
10
10
|
|
|
11
|
-
const BLOCKED_SERVICE_NAME = Object.freeze({
|
|
12
|
-
MTXServices: "cds.xt.MTXServices",
|
|
13
|
-
OpenResourceDiscoveryService: "OpenResourceDiscoveryService",
|
|
14
|
-
});
|
|
15
|
-
|
|
16
11
|
const ORD_ACCESS_STRATEGY = Object.freeze({
|
|
17
12
|
Open: "open",
|
|
18
13
|
Basic: "basic-auth",
|
|
@@ -76,8 +71,6 @@ const LEVEL = Object.freeze({
|
|
|
76
71
|
subEntity: "sub-entity",
|
|
77
72
|
});
|
|
78
73
|
|
|
79
|
-
const OPEN_RESOURCE_DISCOVERY_VERSION = "1.12";
|
|
80
|
-
|
|
81
74
|
const OPENAPI_SERVERS_ANNOTATION = "@OpenAPI.servers";
|
|
82
75
|
|
|
83
76
|
const ORD_EXTENSIONS_PREFIX = "@ORD.Extensions.";
|
|
@@ -89,8 +82,9 @@ const ORD_EXISTING_PRODUCT_PROPERTY = "existingProductORDId";
|
|
|
89
82
|
const ORD_RESOURCE_TYPE = Object.freeze({
|
|
90
83
|
api: "api",
|
|
91
84
|
event: "event",
|
|
92
|
-
integrationDependency: "integrationDependency",
|
|
93
85
|
entityType: "entityType",
|
|
86
|
+
dataProduct: "dataProduct",
|
|
87
|
+
integrationDependency: "integrationDependency",
|
|
94
88
|
});
|
|
95
89
|
|
|
96
90
|
const ORD_SERVICE_NAME = "OpenResourceDiscoveryService";
|
|
@@ -181,12 +175,18 @@ const AUTH_STRINGS = Object.freeze({
|
|
|
181
175
|
WWW_AUTHENTICATE_REALM: 'Basic realm="401"',
|
|
182
176
|
});
|
|
183
177
|
|
|
178
|
+
const DOCUMENT_PERSPECTIVES = Object.freeze({
|
|
179
|
+
SystemVersion: "system-version",
|
|
180
|
+
SystemInstance: "system-instance",
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const LOCAL_TENANT_ID_HEADER_KEY = "local-tenant-id";
|
|
184
|
+
|
|
184
185
|
module.exports = {
|
|
185
186
|
AUTHENTICATION_TYPE,
|
|
186
187
|
AUTH_TYPE_ORD_ACCESS_STRATEGY_MAP,
|
|
187
188
|
BASIC_AUTH_HEADER_KEY,
|
|
188
189
|
BUILD_DEFAULT_PATH,
|
|
189
|
-
BLOCKED_SERVICE_NAME,
|
|
190
190
|
CAP_TO_ORD_PROTOCOL_MAP,
|
|
191
191
|
CDS_ELEMENT_KIND,
|
|
192
192
|
CF_MTLS_HEADERS,
|
|
@@ -202,7 +202,6 @@ module.exports = {
|
|
|
202
202
|
INTEGRATION_DEPENDENCY_RESOURCE_NAME,
|
|
203
203
|
LEVEL,
|
|
204
204
|
MCP_RESOURCE_DEFINITION_TYPE,
|
|
205
|
-
OPEN_RESOURCE_DISCOVERY_VERSION,
|
|
206
205
|
OPENAPI_SERVERS_ANNOTATION,
|
|
207
206
|
ORD_ACCESS_STRATEGY,
|
|
208
207
|
ORD_API_PROTOCOL,
|
|
@@ -223,4 +222,6 @@ module.exports = {
|
|
|
223
222
|
CF_MTLS_ERROR_REASON,
|
|
224
223
|
HTTP_CONFIG,
|
|
225
224
|
AUTH_STRINGS,
|
|
225
|
+
DOCUMENT_PERSPECTIVES,
|
|
226
|
+
LOCAL_TENANT_ID_HEADER_KEY,
|
|
226
227
|
};
|
package/lib/defaults.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
const
|
|
2
|
-
const { hasSAPPolicyLevel } = require("./utils");
|
|
1
|
+
const fs = require("fs");
|
|
3
2
|
const _ = require("lodash");
|
|
3
|
+
const cds = require("@sap/cds");
|
|
4
|
+
const { join } = require("path");
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
];
|
|
6
|
+
const {
|
|
7
|
+
ORD_RESOURCE_TYPE,
|
|
8
|
+
SHORT_DESCRIPTION_PREFIX,
|
|
9
|
+
RESOURCE_VISIBILITY,
|
|
10
|
+
DOCUMENT_PERSPECTIVES,
|
|
11
|
+
CONTENT_MERGE_KEY,
|
|
12
|
+
} = require("./constants");
|
|
13
13
|
|
|
14
14
|
const stringTypeCheck = (value) => typeof value === "string";
|
|
15
15
|
const arrayTypeCheck = (value) => Array.isArray(value);
|
|
@@ -77,8 +77,9 @@ const mapEnvPackageInfo = (packageConfig) => {
|
|
|
77
77
|
*/
|
|
78
78
|
module.exports = {
|
|
79
79
|
$schema: "https://open-resource-discovery.github.io/specification/spec-v1/interfaces/Document.schema.json",
|
|
80
|
-
openResourceDiscovery:
|
|
80
|
+
openResourceDiscovery: "1.14",
|
|
81
81
|
policyLevels: ["none"],
|
|
82
|
+
groupTypeId: "sap.cds:service",
|
|
82
83
|
description: "this is an application description",
|
|
83
84
|
products: (name) => [
|
|
84
85
|
{
|
|
@@ -88,40 +89,38 @@ module.exports = {
|
|
|
88
89
|
vendor: "customer:vendor:Customer:",
|
|
89
90
|
},
|
|
90
91
|
],
|
|
91
|
-
|
|
92
|
-
packages: function getPackageData(appConfig) {
|
|
92
|
+
packages: (appConfig, products) => {
|
|
93
93
|
const name = appConfig.appName;
|
|
94
94
|
const ordNamespace = appConfig.ordNamespace;
|
|
95
|
-
const productsOrdId = appConfig.existingProductORDId ||
|
|
95
|
+
const productsOrdId = appConfig.existingProductORDId || products?.[0]?.ordId;
|
|
96
96
|
const { vendor, ...envValues } = mapEnvPackageInfo(appConfig?.env?.packages?.[0]);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
{
|
|
115
|
-
ordId: generateUniquePackageOrdId(ordNamespace, name, "", RESOURCE_VISIBILITY.public),
|
|
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),
|
|
116
114
|
title: nameWithSpaces(name),
|
|
117
|
-
...generatePackageDescriptions(name,
|
|
115
|
+
...generatePackageDescriptions(name, type, visibility),
|
|
118
116
|
version: "1.0.0",
|
|
119
117
|
...(productsOrdId && { partOfProducts: [productsOrdId || defaultProductOrdId(name)] }),
|
|
120
118
|
vendor: vendor || "customer:vendor:Customer:",
|
|
121
119
|
...envValues,
|
|
122
|
-
},
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
})),
|
|
121
|
+
),
|
|
122
|
+
CONTENT_MERGE_KEY,
|
|
123
|
+
);
|
|
125
124
|
},
|
|
126
125
|
consumptionBundles: (appConfig) => [
|
|
127
126
|
{
|
|
@@ -134,23 +133,55 @@ module.exports = {
|
|
|
134
133
|
"This Consumption Bundle contains all resources of the reference app which are unprotected and do not require authentication",
|
|
135
134
|
},
|
|
136
135
|
],
|
|
137
|
-
|
|
138
|
-
apiResources: [],
|
|
139
|
-
eventResources: [],
|
|
140
|
-
entityTypes: [],
|
|
141
|
-
baseTemplate: (authConfig) => {
|
|
136
|
+
baseTemplate: (authConfig, tenant) => {
|
|
142
137
|
// Get access strategies from the provided authConfig
|
|
143
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;
|
|
144
141
|
const accessStrategies = authConfig?.accessStrategies || [];
|
|
142
|
+
|
|
145
143
|
return {
|
|
146
144
|
openResourceDiscoveryV1: {
|
|
147
145
|
documents: [
|
|
148
146
|
{
|
|
149
147
|
url: "/ord/v1/documents/ord-document",
|
|
148
|
+
perspective: DOCUMENT_PERSPECTIVES.SystemVersion,
|
|
150
149
|
accessStrategies,
|
|
151
150
|
},
|
|
151
|
+
...(!tenant || !(toggles || extensibility)
|
|
152
|
+
? []
|
|
153
|
+
: [
|
|
154
|
+
{
|
|
155
|
+
url: `/ord/v1/documents/ord-document?perspective=${encodeURIComponent(DOCUMENT_PERSPECTIVES.SystemInstance)}`,
|
|
156
|
+
perspective: DOCUMENT_PERSPECTIVES.SystemInstance,
|
|
157
|
+
accessStrategies,
|
|
158
|
+
},
|
|
159
|
+
]),
|
|
152
160
|
],
|
|
153
161
|
},
|
|
154
162
|
};
|
|
155
163
|
},
|
|
164
|
+
adjustForPerspective: (document, perspective) => {
|
|
165
|
+
document.perspective = perspective;
|
|
166
|
+
|
|
167
|
+
if (perspective === DOCUMENT_PERSPECTIVES.SystemVersion) {
|
|
168
|
+
document.describedSystemVersion = cds.env["ord"]?.describedSystemVersion ?? {
|
|
169
|
+
version: JSON.parse(fs.readFileSync(join(cds.root, "package.json"), "utf-8")).version,
|
|
170
|
+
};
|
|
171
|
+
} else if (perspective === DOCUMENT_PERSPECTIVES.SystemInstance) {
|
|
172
|
+
(document.apiResources || []).forEach((apiResource) => {
|
|
173
|
+
(apiResource.resourceDefinitions || []).forEach((apiResourceDefinition) => {
|
|
174
|
+
apiResourceDefinition.url += `?perspective=${encodeURIComponent(perspective)}`;
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
(document.eventResources || []).forEach((eventResource) => {
|
|
179
|
+
(eventResource.resourceDefinitions || []).forEach((eventResourceDefinition) => {
|
|
180
|
+
eventResourceDefinition.url += `?perspective=${encodeURIComponent(perspective)}`;
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return document;
|
|
186
|
+
},
|
|
156
187
|
};
|
|
@@ -62,13 +62,7 @@ function getCustomORDContent(appConfig) {
|
|
|
62
62
|
return require(pathToCustomORDContent);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
function extendCustomORDContentIfExists(appConfig, ordContent) {
|
|
66
|
-
const customORDContent = getCustomORDContent(appConfig);
|
|
67
|
-
return customORDContent
|
|
68
|
-
? compareAndHandleCustomORDContentWithExistingContent(ordContent, customORDContent)
|
|
69
|
-
: ordContent;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
65
|
module.exports = {
|
|
73
|
-
|
|
66
|
+
getCustomORDContent,
|
|
67
|
+
compareAndHandleCustomORDContentWithExistingContent,
|
|
74
68
|
};
|
package/lib/index.js
CHANGED
|
@@ -54,7 +54,7 @@ function add_i18n_texts(csn) {
|
|
|
54
54
|
if (n.startsWith("@")) {
|
|
55
55
|
let annoVal = o[n];
|
|
56
56
|
if (typeof annoVal === "string" && annoVal.startsWith("{i18n>")) {
|
|
57
|
-
let [, x] = annoVal.match(/^\{i18n>(.*)
|
|
57
|
+
let [, x] = annoVal.match(/^\{i18n>(.*)}$/) || [];
|
|
58
58
|
if (x) keys.add(x);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
@@ -73,7 +73,7 @@ function remove_localized_assoc(csn) {
|
|
|
73
73
|
// - name is "localized"
|
|
74
74
|
// - type is "cds.Association"
|
|
75
75
|
// - target name is source name + ".texts"
|
|
76
|
-
// - there is an ON condition
|
|
76
|
+
// - there is an ON condition
|
|
77
77
|
for (let n1 in csn.definitions) {
|
|
78
78
|
let def = csn.definitions[n1];
|
|
79
79
|
if (def.kind === "entity")
|
|
@@ -141,7 +141,7 @@ function add_meta_info(csn) {
|
|
|
141
141
|
for (let k in csn.meta) {
|
|
142
142
|
if (!["flavor", "document", "features"].includes(k) && !k.startsWith("__")) {
|
|
143
143
|
delete csn.meta[k];
|
|
144
|
-
}
|
|
144
|
+
}
|
|
145
145
|
}
|
|
146
146
|
csn.meta.flavor = "effective";
|
|
147
147
|
|
|
@@ -165,5 +165,5 @@ function add_meta_info(csn) {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
module.exports = {
|
|
168
|
-
interopCSN
|
|
168
|
+
interopCSN
|
|
169
169
|
};
|
package/lib/meta-data.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const cds = require("@sap/cds/lib");
|
|
3
|
+
const assert = require("node:assert");
|
|
4
|
+
const cdsc = require("@sap/cds-compiler/lib/main");
|
|
5
|
+
const { compile: openapi } = require("@cap-js/openapi");
|
|
6
|
+
const { compile: asyncapi } = require("@cap-js/asyncapi");
|
|
7
|
+
|
|
8
|
+
const Logger = require("./logger");
|
|
9
|
+
const { interopCSN } = require("./interop-csn.js");
|
|
10
|
+
const { COMPILER_TYPES, OPENAPI_SERVERS_ANNOTATION } = require("./constants");
|
|
11
|
+
|
|
12
|
+
function extractServiceName(url) {
|
|
13
|
+
return path.basename(url, ".json").split(".").slice(0, -1).join(".");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function extractCompilerType(url) {
|
|
17
|
+
return path.basename(url, ".json").split(".").pop();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const compilers = Object.freeze({
|
|
21
|
+
[COMPILER_TYPES.csn]: function (csn, options) {
|
|
22
|
+
return {
|
|
23
|
+
contentType: "application/json",
|
|
24
|
+
response: interopCSN(
|
|
25
|
+
cdsc.for.effective(csn, { beta: { effectiveCsn: true }, effectiveServiceName: options.service }),
|
|
26
|
+
),
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
[COMPILER_TYPES.mcp]: async function (csn, options) {
|
|
30
|
+
return {
|
|
31
|
+
contentType: "application/json",
|
|
32
|
+
response: cds.compile(csn).to["mcp"]({ ...options, ...(cds.env.ord?.compileOptions?.mcp || {}) }),
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
[COMPILER_TYPES.oas3]: function (csn, options) {
|
|
36
|
+
// Check for service-level @OpenAPI.servers annotation
|
|
37
|
+
const servers = csn?.definitions?.[options.service]?.[OPENAPI_SERVERS_ANNOTATION];
|
|
38
|
+
const openapiOptions = { ...options, ...(cds.env?.ord?.compileOptions?.openapi || {}) };
|
|
39
|
+
|
|
40
|
+
// Service-level annotation takes precedence over global config
|
|
41
|
+
if (Array.isArray(servers) && servers.length) {
|
|
42
|
+
openapiOptions["openapi:servers"] = JSON.stringify(servers);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
contentType: "application/json",
|
|
47
|
+
response: openapi(csn, openapiOptions),
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
[COMPILER_TYPES.edmx]: function (csn, options) {
|
|
51
|
+
return {
|
|
52
|
+
contentType: "application/xml",
|
|
53
|
+
response: cds
|
|
54
|
+
.compile(csn)
|
|
55
|
+
.to["edmx"]({ ...options, ...(cds.env?.ord?.compileOptions?.edmx || {}) }),
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
[COMPILER_TYPES.graphql]: function (csn, options) {
|
|
59
|
+
const { generateSchema4 } = require("@cap-js/graphql/lib/schema");
|
|
60
|
+
const { printSchema, lexicographicSortSchema } = require("graphql");
|
|
61
|
+
const srv = new cds.ApplicationService(options.service, cds.linked(csn));
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
contentType: "text/plain",
|
|
65
|
+
response: printSchema(lexicographicSortSchema(generateSchema4({ [options.service]: srv }))),
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
[COMPILER_TYPES.asyncapi2]: function (csn, options) {
|
|
69
|
+
return {
|
|
70
|
+
contentType: "application/json",
|
|
71
|
+
response: asyncapi(csn, { ...options, ...(cds.env?.ord?.compileOptions?.asyncapi || {}) }),
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
module.exports = async (url, model = null) => {
|
|
77
|
+
const name = extractServiceName(url);
|
|
78
|
+
const type = extractCompilerType(url);
|
|
79
|
+
const csn = model || cds.services[name]?.model;
|
|
80
|
+
const options = { service: name, as: "str", messages: [] };
|
|
81
|
+
|
|
82
|
+
assert(Object.hasOwn(compilers, type), `Unsupported format: ${type}`);
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
return await compilers[type](csn, options);
|
|
86
|
+
} catch(error) {
|
|
87
|
+
Logger.error(`Compilation failed for service ${name} (compiler: ${type}) - ${error.message}`);
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
};
|