@cap-js/ord 1.8.0 → 1.9.1

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.
@@ -0,0 +1,98 @@
1
+ const { createPackages } = require("./package");
2
+ const placeholders = require("../common/placeholders");
3
+ const { readORDExtensions, isExternalDataProduct } = require("../common/utils");
4
+ const { RESOURCE_VISIBILITY, EXTERNAL_DP_ORD_ID_ANNOTATION } = require("../constants");
5
+ const _ = require("lodash");
6
+
7
+ const RESOLVERS = Object.freeze({
8
+ ordId: (appConfig) => {
9
+ const major = RESOLVERS.version(appConfig).split(".")[0];
10
+
11
+ return (
12
+ placeholders.replace(appConfig.env?.integrationDependency?.ordId, {
13
+ type: "integrationDependency",
14
+ namespace: appConfig.ordNamespace,
15
+ }) ?? `${appConfig.ordNamespace}:integrationDependency:externalDependencies:v${major}`
16
+ );
17
+ },
18
+ version: (appConfig) => {
19
+ return appConfig.env?.integrationDependency?.version || "1.0.0";
20
+ },
21
+ aspects: (appConfig) => {
22
+ return (
23
+ appConfig.env?.integrationDependency?.aspects ??
24
+ Object.values(appConfig.csn.definitions)
25
+ .filter((definition) => isExternalDataProduct(definition))
26
+ .filter((service) => "apiResource" === service[EXTERNAL_DP_ORD_ID_ANNOTATION].split(":")[1])
27
+ .map((service) => {
28
+ const version = service[EXTERNAL_DP_ORD_ID_ANNOTATION].split(":")[3] ?? "v1";
29
+
30
+ return {
31
+ title: service.name,
32
+ mandatory: false,
33
+ apiResources: [
34
+ {
35
+ ordId: service[EXTERNAL_DP_ORD_ID_ANNOTATION],
36
+ minVersion: version.replace("v", "") + ".0.0",
37
+ },
38
+ ],
39
+
40
+ ...readORDExtensions(service), // Allow customization via @ORD.Extensions on the service
41
+ };
42
+ })
43
+ );
44
+ },
45
+ visibility: (appConfig) => {
46
+ return appConfig.env?.integrationDependency?.visibility || RESOURCE_VISIBILITY.public;
47
+ },
48
+ partOfPackage: (appConfig) => {
49
+ const visibility = RESOLVERS.visibility(appConfig);
50
+ const name = appConfig.appName?.replace(/[^a-zA-Z0-9]/g, "");
51
+ const packages = createPackages(appConfig).map((pkg) => pkg.ordId);
52
+ const suffix = visibility === RESOURCE_VISIBILITY.public ? "" : `-${visibility}`;
53
+
54
+ return (
55
+ placeholders.replace(appConfig.env?.integrationDependency?.partOfPackage, {
56
+ type: "package",
57
+ namespace: appConfig.ordNamespace,
58
+ }) ??
59
+ [
60
+ `${appConfig.ordNamespace}:package:${name}-integrationDependency${suffix}:v1`,
61
+ `${appConfig.ordNamespace}:package:${name}:v1`,
62
+ ].find((candidate) => packages.includes(candidate))
63
+ );
64
+ },
65
+ });
66
+
67
+ /**
68
+ * Creates a single IntegrationDependency with one aspect per external service.
69
+ * @param {Object} appConfig - The application configuration
70
+ * @returns {Object} IntegrationDependency object
71
+ */
72
+ function createIntegrationDependency(appConfig) {
73
+ return {
74
+ mandatory: false,
75
+ releaseStatus: "active",
76
+ title: "External Dependencies",
77
+
78
+ ordId: RESOLVERS.ordId(appConfig),
79
+ version: RESOLVERS.version(appConfig),
80
+ aspects: RESOLVERS.aspects(appConfig),
81
+ visibility: RESOLVERS.visibility(appConfig),
82
+ partOfPackage: RESOLVERS.partOfPackage(appConfig),
83
+
84
+ // Allow customization via cdsrc
85
+ ..._.omit(appConfig.env?.integrationDependency ?? {}, Object.keys(RESOLVERS)),
86
+ };
87
+ }
88
+
89
+ function createIntegrationDependencies(appConfig) {
90
+ return [createIntegrationDependency(appConfig)] //
91
+ .filter((dependency) => dependency.aspects?.length > 0);
92
+ }
93
+
94
+ module.exports = {
95
+ createIntegrationDependencies,
96
+ createIntegrationDependency,
97
+ RESOLVERS,
98
+ };
@@ -0,0 +1,91 @@
1
+ const _ = require("lodash");
2
+
3
+ const { prune } = require("../common/utils");
4
+ const { createProducts } = require("./product");
5
+ const placeholders = require("../common/placeholders");
6
+ const { ORD_RESOURCE_TYPE, RESOURCE_VISIBILITY } = require("../constants");
7
+
8
+ const RESOLVERS = Object.freeze({
9
+ title: (appConfig) => {
10
+ return appConfig?.env?.packages?.[0]?.title ?? appConfig.appName.replace(/[^a-zA-Z0-9]/g, " ").trim();
11
+ },
12
+ vendor: (appConfig) => {
13
+ return appConfig?.env?.packages?.[0]?.vendor ?? "customer:vendor:Customer:";
14
+ },
15
+ version: (appConfig) => {
16
+ return appConfig?.env?.packages?.[0]?.version ?? "1.0.0";
17
+ },
18
+ partOfProducts: (appConfig, products) => {
19
+ return appConfig?.env?.packages?.[0]?.partOfProducts ?? products;
20
+ },
21
+ description: (appConfig, label, visibility) => {
22
+ return (
23
+ appConfig?.env?.packages?.[0]?.description ??
24
+ `This package contains ${visibility} ${label} for ${RESOLVERS.title(appConfig)}.`
25
+ );
26
+ },
27
+ shortDescription: (appConfig, label, visibility) => {
28
+ return appConfig?.env?.packages?.[0]?.shortDescription ?? `Package containing ${visibility} ${label}`;
29
+ },
30
+ ordId: (appConfig, label, visibility, resourceType) => {
31
+ const tag = !resourceType ? "" : `-${resourceType}`;
32
+ const technicalName = appConfig.appName.replace(/[^a-zA-Z0-9]/g, "");
33
+ const suffix = visibility === RESOURCE_VISIBILITY.public ? "" : `-${visibility}`;
34
+
35
+ return (
36
+ placeholders.replace(appConfig?.env?.packages?.[0]?.ordId, {
37
+ type: "package",
38
+ namespace: appConfig.ordNamespace,
39
+ }) ?? `${appConfig.ordNamespace}:package:${technicalName}${tag}${suffix}:v1`
40
+ );
41
+ },
42
+ });
43
+
44
+ function createPackage(appConfig, { label, visibility, products, resourceType }) {
45
+ return prune({
46
+ title: RESOLVERS.title(appConfig),
47
+ vendor: RESOLVERS.vendor(appConfig),
48
+ version: RESOLVERS.version(appConfig),
49
+ partOfProducts: RESOLVERS.partOfProducts(appConfig, products),
50
+ description: RESOLVERS.description(appConfig, label, visibility),
51
+ ordId: RESOLVERS.ordId(appConfig, label, visibility, resourceType),
52
+ shortDescription: RESOLVERS.shortDescription(appConfig, label, visibility),
53
+
54
+ ..._.omit(appConfig?.env?.packages?.[0], Object.keys(RESOLVERS)),
55
+ });
56
+ }
57
+
58
+ function createPackages(appConfig) {
59
+ const products = appConfig.existingProductORDId
60
+ ? [appConfig.existingProductORDId]
61
+ : createProducts(appConfig)?.map((p) => p.ordId);
62
+ const visibilities = !appConfig.hasSAPPolicyLevel
63
+ ? [RESOURCE_VISIBILITY.public]
64
+ : Object.values(RESOURCE_VISIBILITY);
65
+ const packageTypes = !appConfig.hasSAPPolicyLevel
66
+ ? [{ label: "General" }]
67
+ : [
68
+ { label: "APIs", resourceType: ORD_RESOURCE_TYPE.api },
69
+ { label: "Events", resourceType: ORD_RESOURCE_TYPE.event },
70
+ { label: "Entity Types", resourceType: ORD_RESOURCE_TYPE.entityType },
71
+ { label: "Data Products", resourceType: ORD_RESOURCE_TYPE.dataProduct },
72
+ { label: "Integration Dependencies", resourceType: ORD_RESOURCE_TYPE.integrationDependency },
73
+ ];
74
+
75
+ return packageTypes.flatMap(({ label, resourceType }) =>
76
+ visibilities.map((visibility) =>
77
+ createPackage(appConfig, {
78
+ label,
79
+ visibility,
80
+ products,
81
+ resourceType,
82
+ }),
83
+ ),
84
+ );
85
+ }
86
+
87
+ module.exports = {
88
+ createPackages,
89
+ createPackage,
90
+ RESOLVERS,
91
+ };
@@ -0,0 +1,57 @@
1
+ const _ = require("lodash");
2
+
3
+ const Logger = require("../logger");
4
+ const placeholders = require("../common/placeholders");
5
+
6
+ const RESOLVERS = Object.freeze({
7
+ ordId: (appConfig, overrides) => {
8
+ const name = appConfig.packageName
9
+ .split(/[^a-zA-Z0-9]/)
10
+ .filter(Boolean)
11
+ .join(".");
12
+
13
+ return (
14
+ placeholders.replace(overrides?.ordId, { type: "product", namespace: appConfig.ordNamespace }) ??
15
+ `customer:product:${name}:`
16
+ );
17
+ },
18
+ title: (appConfig, overrides) => {
19
+ return overrides?.title ?? appConfig.packageName.replace(/[^a-zA-Z0-9]/g, " ").trim();
20
+ },
21
+ vendor: (appConfig, overrides) => {
22
+ return overrides?.vendor ?? "customer:vendor:Customer:";
23
+ },
24
+ shortDescription: (appConfig, overrides) => {
25
+ return overrides?.shortDescription ?? `Short description of ${RESOLVERS.title(appConfig, overrides)}`;
26
+ },
27
+ });
28
+
29
+ function createProduct(appConfig) {
30
+ let overrides = appConfig?.env?.products?.[0] ?? {};
31
+
32
+ if (overrides?.ordId?.toLowerCase().startsWith("sap")) {
33
+ overrides = {}; // disable overrides, misconfiguration detected
34
+ Logger.error(
35
+ "Detected sap product ordId, which should not be defined for custom products, use default value instead. Please check ORD global registry.",
36
+ );
37
+ }
38
+
39
+ return {
40
+ title: RESOLVERS.title(appConfig, overrides),
41
+ ordId: RESOLVERS.ordId(appConfig, overrides),
42
+ vendor: RESOLVERS.vendor(appConfig, overrides),
43
+ shortDescription: RESOLVERS.shortDescription(appConfig, overrides),
44
+
45
+ ..._.omit(overrides, Object.keys(RESOLVERS)),
46
+ };
47
+ }
48
+
49
+ function createProducts(appConfig) {
50
+ return appConfig.existingProductORDId ? [] : [createProduct(appConfig)];
51
+ }
52
+
53
+ module.exports = {
54
+ createProducts,
55
+ createProduct,
56
+ RESOLVERS,
57
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/ord",
3
- "version": "1.8.0",
3
+ "version": "1.9.1",
4
4
  "description": "CAP Plugin for generating ORD document.",
5
5
  "repository": "cap-js/ord",
6
6
  "author": "SAP SE (https://www.sap.com)",
@@ -22,20 +22,22 @@
22
22
  "lint": "npx eslint .",
23
23
  "test": "jest __tests__/unit --ci --collectCoverage",
24
24
  "test:integration:basic": "jest __tests__/integration/basic-auth.test.js --testPathIgnorePatterns=/node_modules/ --forceExit",
25
- "test:integration:mtls": "jest __tests__/integration/mtls-auth.test.js --testPathIgnorePatterns=/node_modules/ --forceExit",
25
+ "test:integration:mtls": "jest __tests__/integration/mtls-auth-*.test.js --testPathIgnorePatterns=/node_modules/ --forceExit",
26
26
  "test:integration:build": "jest __tests__/integration/cds-build.test.js --testPathIgnorePatterns=/node_modules/ --forceExit",
27
27
  "update-snapshot": "jest --ci --updateSnapshot",
28
28
  "cds:version": "cds v -i",
29
29
  "test:all": "npm run test && npm run test:integration:basic && npm run test:integration:mtls && npm run test:integration:build"
30
30
  },
31
31
  "devDependencies": {
32
+ "@cap-js/cds-test": "0.4.1",
32
33
  "@cap-js/graphql": "^0.14.0",
33
34
  "@cap-js/sqlite": "^2",
35
+ "@open-resource-discovery/specification": "1.16.2",
34
36
  "@sap/cds-dk": ">=8.9.5",
35
37
  "eslint": "^10.0.0",
36
38
  "express": "^5.0.0",
37
39
  "jest": "^30.0.0",
38
- "prettier": "3.8.3",
40
+ "prettier": "3.8.4",
39
41
  "supertest": "^7.0.0"
40
42
  },
41
43
  "peerDependencies": {
@@ -53,7 +55,7 @@
53
55
  "node": ">=20 <25"
54
56
  },
55
57
  "overrides": {
56
- "@sap/cds-compiler": "6.9.2"
58
+ "@sap/cds-compiler": "6.9.3"
57
59
  },
58
60
  "cds": {
59
61
  "requires": {
@@ -1,14 +0,0 @@
1
- {
2
- "openResourceDiscoveryV1": {
3
- "documents": [
4
- {
5
- "url": "/ord/v1/documents/ord-document",
6
- "accessStrategies": [
7
- {
8
- "type": "open"
9
- }
10
- ]
11
- }
12
- ]
13
- }
14
- }
@@ -1,172 +0,0 @@
1
- const cds = require("@sap/cds");
2
- const { AUTHENTICATION_TYPE, ORD_ACCESS_STRATEGY } = require("./constants");
3
- const Logger = require("./logger");
4
-
5
- /**
6
- * Mapping from internal authentication types to ORD access strategy values.
7
- * This is the single source of truth for auth type to ORD document mapping.
8
- *
9
- * @private
10
- */
11
- const AUTH_TYPE_ORD_ACCESS_STRATEGY_MAP = Object.freeze({
12
- [AUTHENTICATION_TYPE.Open]: ORD_ACCESS_STRATEGY.Open,
13
- [AUTHENTICATION_TYPE.Basic]: ORD_ACCESS_STRATEGY.Basic,
14
- [AUTHENTICATION_TYPE.CfMtls]: ORD_ACCESS_STRATEGY.CfMtls,
15
- });
16
-
17
- /**
18
- * Derives ORD access strategies from authentication configuration.
19
- * This function is the main entry point for converting auth config to ORD document format.
20
- *
21
- * @param {Object} authConfig - Authentication configuration object
22
- * @param {string[]} authConfig.types - Array of authentication types (from AUTHENTICATION_TYPE)
23
- * @returns {Array<{type: string}>} Array of access strategy objects for ORD document
24
- *
25
- * @example
26
- * // With Basic auth configured
27
- * const authConfig = { types: ['basic'] };
28
- * const strategies = getAccessStrategiesFromAuthConfig(authConfig);
29
- * // Returns: [{ type: 'basic-auth' }]
30
- *
31
- * @example
32
- * // With multiple auth types
33
- * const authConfig = { types: ['basic', 'cf-mtls'] };
34
- * const strategies = getAccessStrategiesFromAuthConfig(authConfig);
35
- * // Returns: [{ type: 'basic-auth' }, { type: 'sap:cmp-mtls:v1' }]
36
- */
37
- function getAccessStrategiesFromAuthConfig(authConfig) {
38
- if (!authConfig || !Array.isArray(authConfig.types)) {
39
- Logger.warn("getAccessStrategiesFromAuthConfig:", "Invalid authConfig, defaulting to 'open'");
40
- return [{ type: ORD_ACCESS_STRATEGY.Open }];
41
- }
42
-
43
- const strategies = authConfig.types
44
- .map((type) => {
45
- const ordType = AUTH_TYPE_ORD_ACCESS_STRATEGY_MAP[type];
46
- if (!ordType) {
47
- Logger.warn("getAccessStrategiesFromAuthConfig:", `Unknown auth type '${type}', skipping`);
48
- return null;
49
- }
50
- return { type: ordType };
51
- })
52
- .filter(Boolean); // Remove null entries
53
-
54
- // If no valid strategies found, default to open
55
- if (strategies.length === 0) {
56
- return [{ type: ORD_ACCESS_STRATEGY.Open }];
57
- }
58
-
59
- return strategies;
60
- }
61
-
62
- /**
63
- * Checks if access strategies contain any non-open strategies.
64
- *
65
- * @param {Array<{type: string}>} accessStrategies - Array of access strategy objects
66
- * @returns {boolean} True if any non-open strategy is present
67
- */
68
- function hasNonOpenStrategies(accessStrategies) {
69
- if (!Array.isArray(accessStrategies)) {
70
- return false;
71
- }
72
- return accessStrategies.some((s) => s.type !== ORD_ACCESS_STRATEGY.Open);
73
- }
74
-
75
- /**
76
- * Validates that 'open' strategy does not coexist with non-open strategies.
77
- * According to ORD specification, 'open' should not be mixed with authenticated strategies.
78
- *
79
- * @param {Array<{type: string}>} accessStrategies - Array of access strategy objects
80
- * @throws {Error} If 'open' coexists with non-open strategies
81
- */
82
- function ensureNoOpenWhenNonOpenPresent(accessStrategies) {
83
- if (!Array.isArray(accessStrategies) || accessStrategies.length === 0) {
84
- return;
85
- }
86
-
87
- const hasOpen = accessStrategies.some((s) => s.type === ORD_ACCESS_STRATEGY.Open);
88
- const hasNonOpen = hasNonOpenStrategies(accessStrategies);
89
-
90
- if (hasOpen && hasNonOpen) {
91
- throw new Error(
92
- "Invalid access strategies: 'open' cannot coexist with authenticated strategies (basic-auth, sap:cmp-mtls:v1)",
93
- );
94
- }
95
- }
96
-
97
- /**
98
- * Ensures access strategies are valid and present, with configurable strict mode.
99
- * In non-strict mode (default), missing/empty strategies fallback to 'open' with error log.
100
- * In strict mode, missing/empty strategies throw an error.
101
- *
102
- * @param {Array<{type: string}>|undefined} accessStrategies - Array of access strategy objects
103
- * @param {Object} options - Validation options
104
- * @param {string} [options.resourceName] - Name of the resource (for error messages)
105
- * @param {boolean} [options.strict] - If true, throw error instead of fallback (default: reads from cds.env.ord.strictAccessStrategies)
106
- * @returns {Array<{type: string}>} Validated access strategies array
107
- * @throws {Error} In strict mode, if accessStrategies is missing or empty
108
- *
109
- * @example
110
- * // Non-strict mode (default) - fallback to open
111
- * const strategies = ensureAccessStrategies(undefined, { resourceName: 'MyAPI' });
112
- * // Logs error and returns: [{ type: 'open' }]
113
- *
114
- * @example
115
- * // Strict mode - throws error
116
- * const strategies = ensureAccessStrategies(undefined, {
117
- * resourceName: 'MyAPI',
118
- * strict: true
119
- * });
120
- * // Throws: Error with message about missing accessStrategies
121
- */
122
- function ensureAccessStrategies(accessStrategies, options = {}) {
123
- const { resourceName = "unknown resource", strict } = options;
124
-
125
- // Determine strict mode: explicit parameter > config > default false
126
- const isStrict = strict !== undefined ? strict : cds.env.ord?.strictAccessStrategies === true;
127
-
128
- if (!Array.isArray(accessStrategies) || accessStrategies.length === 0) {
129
- const message = `[ORD] accessStrategies missing or empty for resource "${resourceName}"`;
130
-
131
- if (isStrict) {
132
- throw new Error(`${message}. Strict mode is enabled.`);
133
- } else {
134
- Logger.error("ensureAccessStrategies:", `${message}. Falling back to 'open'.`);
135
- return [{ type: ORD_ACCESS_STRATEGY.Open }];
136
- }
137
- }
138
-
139
- // Validate no mixing of 'open' with non-open strategies
140
- ensureNoOpenWhenNonOpenPresent(accessStrategies);
141
-
142
- return accessStrategies;
143
- }
144
-
145
- /**
146
- * Validates that access strategies array contains only known ORD access strategy types.
147
- *
148
- * @param {Array<{type: string}>} accessStrategies - Array of access strategy objects
149
- * @returns {boolean} True if all strategies are valid
150
- */
151
- function isValidAccessStrategies(accessStrategies) {
152
- if (!Array.isArray(accessStrategies) || accessStrategies.length === 0) {
153
- return false;
154
- }
155
-
156
- const validTypes = Object.values(ORD_ACCESS_STRATEGY);
157
- return accessStrategies.every((s) => s.type && validTypes.includes(s.type));
158
- }
159
-
160
- module.exports = {
161
- // Main API
162
- getAccessStrategiesFromAuthConfig,
163
- ensureAccessStrategies,
164
-
165
- // Helper functions
166
- hasNonOpenStrategies,
167
- ensureNoOpenWhenNonOpenPresent,
168
- isValidAccessStrategies,
169
-
170
- // Constants (re-exported for convenience)
171
- AUTH_TYPE_ORD_ACCESS_STRATEGY_MAP,
172
- };
package/lib/date.js DELETED
@@ -1,17 +0,0 @@
1
- function getRFC3339Date() {
2
- const now = new Date();
3
- const year = now.getUTCFullYear();
4
- const month = String(now.getUTCMonth() + 1).padStart(2, "0");
5
- const day = String(now.getUTCDate()).padStart(2, "0");
6
- const hours = String(now.getUTCHours()).padStart(2, "0");
7
- const minutes = String(now.getUTCMinutes()).padStart(2, "0");
8
- const seconds = String(now.getUTCSeconds()).padStart(2, "0");
9
- const offsetHours = "01";
10
- const offsetMinutes = "00";
11
- const offsetSign = "+";
12
- return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}${offsetSign}${offsetHours}:${offsetMinutes}`;
13
- }
14
-
15
- module.exports = {
16
- getRFC3339Date,
17
- };
@@ -1,126 +0,0 @@
1
- const {
2
- DATA_PRODUCT_SIMPLE_ANNOTATION,
3
- EXTERNAL_DP_ORD_ID_ANNOTATION,
4
- EXTERNAL_SERVICE_ANNOTATION,
5
- INTEGRATION_DEPENDENCY_RESOURCE_NAME,
6
- ORD_RESOURCE_TYPE,
7
- RESOURCE_VISIBILITY,
8
- } = require("./constants");
9
- const { readORDExtensions, _getPackageID } = require("./templates");
10
-
11
- /**
12
- * Parses @cds.dp.ordId annotation to extract resource information.
13
- * @param {string} ordId - e.g., "sap.sai:apiResource:Supplier:v1"
14
- * @returns {Object} { namespace, resourceType, resourceName, version }
15
- */
16
- function parseDataProductOrdId(ordId) {
17
- const [namespace, resourceType, resourceName, version = "v1"] = ordId.split(":");
18
- return { namespace, resourceType, resourceName, version };
19
- }
20
-
21
- /**
22
- * Checks if a CSN definition is an external Data Product.
23
- * @param {Object} definition - CSN definition object
24
- * @returns {boolean}
25
- */
26
- function isExternalDataProduct(definition) {
27
- return !!(
28
- definition.kind === "service" &&
29
- definition[EXTERNAL_SERVICE_ANNOTATION] &&
30
- definition[DATA_PRODUCT_SIMPLE_ANNOTATION] &&
31
- definition[EXTERNAL_DP_ORD_ID_ANNOTATION]
32
- );
33
- }
34
-
35
- /**
36
- * Collects external services from CSN definitions.
37
- * Returns a flat list of external services (one per service, no namespace grouping).
38
- * @param {Object} csn - The CSN definitions object
39
- * @returns {Array} Array of external service objects
40
- */
41
- function collectExternalServices(csn) {
42
- const externalServices = [];
43
-
44
- for (const [serviceName, definition] of Object.entries(csn.definitions)) {
45
- if (!isExternalDataProduct(definition)) continue;
46
-
47
- const dpOrdId = definition[EXTERNAL_DP_ORD_ID_ANNOTATION];
48
- const { resourceType, version } = parseDataProductOrdId(dpOrdId);
49
-
50
- // Only support apiResource for now
51
- if (resourceType !== "apiResource") continue;
52
-
53
- externalServices.push({
54
- serviceName,
55
- ordId: dpOrdId,
56
- minVersion: version.replace("v", "") + ".0.0",
57
- definition,
58
- });
59
- }
60
-
61
- return externalServices;
62
- }
63
-
64
- /**
65
- * Creates a single IntegrationDependency with one aspect per external service.
66
- * @param {Array} externalServices - Array of external service objects
67
- * @param {Object} appConfig - The application configuration
68
- * @param {Array} packageIds - The available package identifiers
69
- * @returns {Object} IntegrationDependency object
70
- */
71
- function createIntegrationDependency(externalServices, appConfig, packageIds) {
72
- const packageId = _getPackageID(
73
- appConfig.ordNamespace,
74
- packageIds,
75
- ORD_RESOURCE_TYPE.integrationDependency,
76
- RESOURCE_VISIBILITY.public,
77
- );
78
-
79
- // Create one aspect per external service
80
- const aspects = externalServices.map((service) => {
81
- const ordExtensions = readORDExtensions(service.definition || {});
82
- return {
83
- title: service.serviceName,
84
- mandatory: false,
85
- apiResources: [{ ordId: service.ordId, minVersion: service.minVersion }],
86
- ...ordExtensions, // Allow customization via @ORD.Extensions on the service
87
- };
88
- });
89
-
90
- // Read IntegrationDependency level config from cdsrc
91
- const integrationDepConfig = appConfig.env?.integrationDependency || {};
92
- const version = integrationDepConfig.version || "1.0.0";
93
-
94
- return {
95
- ordId: `${appConfig.ordNamespace}:${ORD_RESOURCE_TYPE.integrationDependency}:${INTEGRATION_DEPENDENCY_RESOURCE_NAME}:v${version.split(".")[0]}`,
96
- title: "External Dependencies",
97
- version: version,
98
- releaseStatus: "active",
99
- visibility: RESOURCE_VISIBILITY.public,
100
- mandatory: false,
101
- partOfPackage: packageId,
102
- aspects,
103
- ...integrationDepConfig, // Allow customization via cdsrc
104
- };
105
- }
106
-
107
- /**
108
- * Generates a single IntegrationDependency for all external services.
109
- * @param {Object} csn - The CSN definitions object
110
- * @param {Object} appConfig - The application configuration
111
- * @param {Array} packageIds - The available package identifiers
112
- * @returns {Array} Array containing single IntegrationDependency or empty array
113
- */
114
- function getIntegrationDependencies(csn, appConfig, packageIds) {
115
- const externalServices = collectExternalServices(csn);
116
- if (externalServices.length === 0) return [];
117
-
118
- return [createIntegrationDependency(externalServices, appConfig, packageIds)];
119
- }
120
-
121
- module.exports = {
122
- getIntegrationDependencies,
123
- // Exported for testing
124
- collectExternalServices,
125
- createIntegrationDependency,
126
- };