@sap-ux/adp-tooling 0.18.115 → 0.18.117
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/dist/btp/api.d.ts +9 -1
- package/dist/btp/api.js +37 -3
- package/dist/cf/project/yaml.d.ts +11 -0
- package/dist/cf/project/yaml.js +39 -0
- package/dist/cf/services/destinations.d.ts +11 -0
- package/dist/cf/services/destinations.js +79 -0
- package/dist/cf/services/index.d.ts +1 -0
- package/dist/cf/services/index.js +1 -0
- package/dist/prompts/add-new-model/index.d.ts +22 -2
- package/dist/prompts/add-new-model/index.js +211 -107
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/index.js +3 -1
- package/dist/translations/adp-tooling.i18n.json +16 -0
- package/dist/types.d.ts +34 -12
- package/dist/types.js +7 -1
- package/dist/writer/cf.d.ts +1 -1
- package/dist/writer/cf.js +3 -2
- package/dist/writer/changes/writers/new-model-writer.d.ts +7 -0
- package/dist/writer/changes/writers/new-model-writer.js +54 -17
- package/package.json +6 -6
package/dist/btp/api.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ToolsLogger } from '@sap-ux/logger';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Destinations } from '@sap-ux/btp-utils';
|
|
3
|
+
import type { Uaa, BtpDestinationConfig, CfDestinationServiceCredentials } from '../types';
|
|
3
4
|
/**
|
|
4
5
|
* Obtain an OAuth2 access token using the client credentials grant.
|
|
5
6
|
*
|
|
@@ -19,4 +20,11 @@ export declare function getToken(uaa: Uaa, logger?: ToolsLogger): Promise<string
|
|
|
19
20
|
* @returns The destinationConfiguration object (e.g. Name, ProxyType, URL, Authentication) or undefined on failure.
|
|
20
21
|
*/
|
|
21
22
|
export declare function getBtpDestinationConfig(uri: string, token: string, destinationName: string, logger?: ToolsLogger): Promise<BtpDestinationConfig | undefined>;
|
|
23
|
+
/**
|
|
24
|
+
* Lists all subaccount destinations from the BTP Destination Configuration API.
|
|
25
|
+
*
|
|
26
|
+
* @param {CfDestinationServiceCredentials} credentials - Destination service credentials.
|
|
27
|
+
* @returns {Promise<Destinations>} Map of destination name to Destination object.
|
|
28
|
+
*/
|
|
29
|
+
export declare function listBtpDestinations(credentials: CfDestinationServiceCredentials): Promise<Destinations>;
|
|
22
30
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/btp/api.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getToken = getToken;
|
|
7
7
|
exports.getBtpDestinationConfig = getBtpDestinationConfig;
|
|
8
|
+
exports.listBtpDestinations = listBtpDestinations;
|
|
8
9
|
const axios_1 = __importDefault(require("axios"));
|
|
9
10
|
const i18n_1 = require("../i18n");
|
|
10
11
|
/**
|
|
@@ -30,8 +31,8 @@ async function getToken(uaa, logger) {
|
|
|
30
31
|
return response.data['access_token'];
|
|
31
32
|
}
|
|
32
33
|
catch (e) {
|
|
33
|
-
logger?.error(`Failed to obtain OAuth token from ${uri}: ${e.message}`);
|
|
34
|
-
throw new Error((0, i18n_1.t)('error.failedToGetAuthKey', { error: e.message }));
|
|
34
|
+
logger?.error(`Failed to obtain OAuth token from ${uri}: ${e instanceof Error ? e.message : String(e)}`);
|
|
35
|
+
throw new Error((0, i18n_1.t)('error.failedToGetAuthKey', { error: e instanceof Error ? e.message : String(e) }));
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
@@ -56,8 +57,41 @@ async function getBtpDestinationConfig(uri, token, destinationName, logger) {
|
|
|
56
57
|
return config;
|
|
57
58
|
}
|
|
58
59
|
catch (e) {
|
|
59
|
-
logger?.error(`Failed to fetch destination config for "${destinationName}": ${e.message}`);
|
|
60
|
+
logger?.error(`Failed to fetch destination config for "${destinationName}": ${e instanceof Error ? e.message : String(e)}`);
|
|
60
61
|
return undefined;
|
|
61
62
|
}
|
|
62
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Lists all subaccount destinations from the BTP Destination Configuration API.
|
|
66
|
+
*
|
|
67
|
+
* @param {CfDestinationServiceCredentials} credentials - Destination service credentials.
|
|
68
|
+
* @returns {Promise<Destinations>} Map of destination name to Destination object.
|
|
69
|
+
*/
|
|
70
|
+
async function listBtpDestinations(credentials) {
|
|
71
|
+
const uaa = 'uaa' in credentials
|
|
72
|
+
? credentials.uaa
|
|
73
|
+
: { clientid: credentials.clientid, clientsecret: credentials.clientsecret, url: credentials.url };
|
|
74
|
+
const token = await getToken(uaa);
|
|
75
|
+
const url = `${credentials.uri}/destination-configuration/v1/subaccountDestinations`;
|
|
76
|
+
try {
|
|
77
|
+
const response = await axios_1.default.get(url, {
|
|
78
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
79
|
+
});
|
|
80
|
+
const configs = Array.isArray(response.data) ? response.data : [];
|
|
81
|
+
return configs.reduce((acc, config) => {
|
|
82
|
+
acc[config.Name] = {
|
|
83
|
+
Name: config.Name,
|
|
84
|
+
Host: config.URL,
|
|
85
|
+
Type: config.Type,
|
|
86
|
+
Authentication: config.Authentication,
|
|
87
|
+
ProxyType: config.ProxyType,
|
|
88
|
+
Description: config.Description ?? ''
|
|
89
|
+
};
|
|
90
|
+
return acc;
|
|
91
|
+
}, {});
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
throw new Error((0, i18n_1.t)('error.failedToListBtpDestinations', { error: e instanceof Error ? e.message : String(e) }));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
63
97
|
//# sourceMappingURL=api.js.map
|
|
@@ -18,6 +18,17 @@ interface AdjustMtaYamlParams {
|
|
|
18
18
|
* @returns {boolean} True if the selected path is a MTA project, false otherwise.
|
|
19
19
|
*/
|
|
20
20
|
export declare function isMtaProject(selectedPath: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Adds a connectivity service resource to the project's mta.yaml if not already present,
|
|
23
|
+
* creates the CF service instance and generates a service key for it.
|
|
24
|
+
* Only applies to MTA projects. Required when the selected CF destination is OnPremise
|
|
25
|
+
* so the AppRouter can proxy requests through the Cloud Connector.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} projectPath - The root path of the project.
|
|
28
|
+
* @param {Editor} memFs - The mem-fs editor instance.
|
|
29
|
+
* @param {ToolsLogger} [logger] - Optional logger.
|
|
30
|
+
*/
|
|
31
|
+
export declare function addConnectivityServiceToMta(projectPath: string, memFs: Editor, logger?: ToolsLogger): Promise<void>;
|
|
21
32
|
/**
|
|
22
33
|
* Gets the SAP Cloud Service.
|
|
23
34
|
*
|
package/dist/cf/project/yaml.js
CHANGED
|
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.isMtaProject = isMtaProject;
|
|
40
|
+
exports.addConnectivityServiceToMta = addConnectivityServiceToMta;
|
|
40
41
|
exports.getSAPCloudService = getSAPCloudService;
|
|
41
42
|
exports.getRouterType = getRouterType;
|
|
42
43
|
exports.getAppParamsFromUI5Yaml = getAppParamsFromUI5Yaml;
|
|
@@ -60,6 +61,44 @@ const SAP_APPLICATION_CONTENT = 'com.sap.application.content';
|
|
|
60
61
|
function isMtaProject(selectedPath) {
|
|
61
62
|
return node_fs_1.default.existsSync(path.join(selectedPath, 'mta.yaml'));
|
|
62
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Adds a connectivity service resource to the project's mta.yaml if not already present,
|
|
66
|
+
* creates the CF service instance and generates a service key for it.
|
|
67
|
+
* Only applies to MTA projects. Required when the selected CF destination is OnPremise
|
|
68
|
+
* so the AppRouter can proxy requests through the Cloud Connector.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} projectPath - The root path of the project.
|
|
71
|
+
* @param {Editor} memFs - The mem-fs editor instance.
|
|
72
|
+
* @param {ToolsLogger} [logger] - Optional logger.
|
|
73
|
+
*/
|
|
74
|
+
async function addConnectivityServiceToMta(projectPath, memFs, logger) {
|
|
75
|
+
if (!isMtaProject(projectPath)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const mtaYamlPath = path.join(projectPath, 'mta.yaml');
|
|
79
|
+
const yamlContent = (0, yaml_loader_1.getYamlContent)(mtaYamlPath);
|
|
80
|
+
if (!yamlContent) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const projectName = yamlContent.ID.toLowerCase();
|
|
84
|
+
const connectivityResourceName = `${projectName}-connectivity`;
|
|
85
|
+
if (yamlContent.resources?.some((r) => r.name === connectivityResourceName)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
await (0, api_1.createServiceInstance)('lite', connectivityResourceName, 'connectivity', { logger });
|
|
89
|
+
await (0, api_1.getOrCreateServiceInstanceKeys)({ names: [connectivityResourceName] }, logger);
|
|
90
|
+
yamlContent.resources = yamlContent.resources ?? [];
|
|
91
|
+
yamlContent.resources.push({
|
|
92
|
+
name: connectivityResourceName,
|
|
93
|
+
type: CF_MANAGED_SERVICE,
|
|
94
|
+
parameters: {
|
|
95
|
+
service: 'connectivity',
|
|
96
|
+
'service-plan': 'lite',
|
|
97
|
+
'service-name': connectivityResourceName
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
memFs.write(mtaYamlPath, js_yaml_1.default.dump(yamlContent, { lineWidth: -1 }));
|
|
101
|
+
}
|
|
63
102
|
/**
|
|
64
103
|
* Gets the SAP Cloud Service.
|
|
65
104
|
*
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Destinations } from '@sap-ux/btp-utils';
|
|
2
|
+
/**
|
|
3
|
+
* Returns the list of available BTP destinations from the logged-in CF subaccount.
|
|
4
|
+
* Reads the destination service credentials from the CF project's service keys
|
|
5
|
+
* and calls the BTP Destination Configuration API directly.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} projectPath - The root path of the CF app project.
|
|
8
|
+
* @returns {Promise<Destinations>} Map of destination name to Destination object.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getBtpDestinations(projectPath: string): Promise<Destinations>;
|
|
11
|
+
//# sourceMappingURL=destinations.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getBtpDestinations = getBtpDestinations;
|
|
37
|
+
const path = __importStar(require("node:path"));
|
|
38
|
+
const api_1 = require("./api");
|
|
39
|
+
const api_2 = require("../../btp/api");
|
|
40
|
+
const yaml_loader_1 = require("../project/yaml-loader");
|
|
41
|
+
const i18n_1 = require("../../i18n");
|
|
42
|
+
/**
|
|
43
|
+
* Finds the name of the destination service instance declared in the MTA project's mta.yaml.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} projectPath - The root path of the app project.
|
|
46
|
+
* @returns {string} The CF service instance name.
|
|
47
|
+
* @throws {Error} When the destination service instance is not found or mta.yaml cannot be read.
|
|
48
|
+
*/
|
|
49
|
+
function getDestinationServiceName(projectPath) {
|
|
50
|
+
try {
|
|
51
|
+
const yamlContent = (0, yaml_loader_1.getYamlContent)(path.join(path.dirname(projectPath), 'mta.yaml'));
|
|
52
|
+
const name = yamlContent?.resources?.find((r) => r.parameters?.service === 'destination')?.name;
|
|
53
|
+
if (!name) {
|
|
54
|
+
throw new Error((0, i18n_1.t)('error.destinationServiceNotFoundInMtaYaml'));
|
|
55
|
+
}
|
|
56
|
+
return name;
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
throw e instanceof Error ? e : new Error((0, i18n_1.t)('error.destinationServiceNotFoundInMtaYaml'));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Returns the list of available BTP destinations from the logged-in CF subaccount.
|
|
64
|
+
* Reads the destination service credentials from the CF project's service keys
|
|
65
|
+
* and calls the BTP Destination Configuration API directly.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} projectPath - The root path of the CF app project.
|
|
68
|
+
* @returns {Promise<Destinations>} Map of destination name to Destination object.
|
|
69
|
+
*/
|
|
70
|
+
async function getBtpDestinations(projectPath) {
|
|
71
|
+
const destinationServiceName = getDestinationServiceName(projectPath);
|
|
72
|
+
const serviceInfo = await (0, api_1.getOrCreateServiceInstanceKeys)({ names: [destinationServiceName] });
|
|
73
|
+
if (!serviceInfo?.serviceKeys?.length) {
|
|
74
|
+
throw new Error((0, i18n_1.t)('error.noServiceKeysFoundForDestination', { serviceInstanceName: destinationServiceName }));
|
|
75
|
+
}
|
|
76
|
+
const credentials = serviceInfo.serviceKeys[0].credentials;
|
|
77
|
+
return (0, api_2.listBtpDestinations)(credentials);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=destinations.js.map
|
|
@@ -17,5 +17,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./api"), exports);
|
|
18
18
|
__exportStar(require("./ssh"), exports);
|
|
19
19
|
__exportStar(require("./cli"), exports);
|
|
20
|
+
__exportStar(require("./destinations"), exports);
|
|
20
21
|
__exportStar(require("./manifest"), exports);
|
|
21
22
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
import type { YUIQuestion } from '@sap-ux/inquirer-common';
|
|
2
2
|
import type { UI5FlexLayer } from '@sap-ux/project-access';
|
|
3
|
-
import {
|
|
3
|
+
import type { ToolsLogger } from '@sap-ux/logger';
|
|
4
|
+
import { ServiceType, type DescriptorVariant, type NewModelAnswers, type NewModelData } from '../../types';
|
|
5
|
+
/**
|
|
6
|
+
* Returns the OData version string for use in change content based on the selected service type.
|
|
7
|
+
* Returns undefined for HTTP service type as it has no OData version.
|
|
8
|
+
*
|
|
9
|
+
* @param {ServiceType} serviceType - The selected service type.
|
|
10
|
+
* @returns {string | undefined} The OData version string ('2.0' or '4.0'), or undefined for HTTP.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getODataVersionFromServiceType(serviceType: ServiceType): string | undefined;
|
|
4
13
|
/**
|
|
5
14
|
* Gets the prompts for adding the new model.
|
|
6
15
|
*
|
|
7
16
|
* @param {string} projectPath - The root path of the project.
|
|
8
17
|
* @param {UI5FlexLayer} layer - UI5 Flex layer.
|
|
18
|
+
* @param {ToolsLogger} [logger] - Optional logger.
|
|
9
19
|
* @returns {YUIQuestion<NewModelAnswers>[]} The questions/prompts.
|
|
10
20
|
*/
|
|
11
|
-
export declare function getPrompts(projectPath: string, layer: UI5FlexLayer): Promise<YUIQuestion<NewModelAnswers>[]>;
|
|
21
|
+
export declare function getPrompts(projectPath: string, layer: UI5FlexLayer, logger?: ToolsLogger): Promise<YUIQuestion<NewModelAnswers>[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Builds the NewModelData object from the prompts answers.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} projectPath - The root path of the project.
|
|
26
|
+
* @param {DescriptorVariant} variant - The descriptor variant of the adaptation project.
|
|
27
|
+
* @param {NewModelAnswers} answers - The answers to the prompts.
|
|
28
|
+
* @param {ToolsLogger} [logger] - Optional logger instance.
|
|
29
|
+
* @returns {Promise<NewModelData>} The data required by NewModelWriter.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createNewModelData(projectPath: string, variant: DescriptorVariant, answers: NewModelAnswers, logger?: ToolsLogger): Promise<NewModelData>;
|
|
12
32
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,13 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getODataVersionFromServiceType = getODataVersionFromServiceType;
|
|
3
4
|
exports.getPrompts = getPrompts;
|
|
5
|
+
exports.createNewModelData = createNewModelData;
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const btp_utils_1 = require("@sap-ux/btp-utils");
|
|
9
|
+
const yeoman_ui_types_1 = require("@sap-devx/yeoman-ui-types");
|
|
4
10
|
const i18n_1 = require("../../i18n");
|
|
5
11
|
const change_utils_1 = require("../../base/change-utils");
|
|
12
|
+
const destinations_1 = require("../../cf/services/destinations");
|
|
13
|
+
const types_1 = require("../../types");
|
|
6
14
|
const cf_1 = require("../../base/cf");
|
|
15
|
+
const helper_1 = require("../../base/helper");
|
|
7
16
|
const project_input_validator_1 = require("@sap-ux/project-input-validator");
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Reads the routes array from the xs-app.json file in the project's webapp folder.
|
|
19
|
+
* Returns an empty array if the file does not exist or cannot be parsed.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} projectPath - The root path of the project.
|
|
22
|
+
* @returns {XsAppRoute[]} The existing routes.
|
|
23
|
+
*/
|
|
24
|
+
function readXsAppRoutes(projectPath) {
|
|
25
|
+
try {
|
|
26
|
+
const xsAppPath = (0, node_path_1.join)(projectPath, 'webapp', 'xs-app.json');
|
|
27
|
+
const content = JSON.parse((0, node_fs_1.readFileSync)(xsAppPath, 'utf-8'));
|
|
28
|
+
return Array.isArray(content?.routes) ? content.routes : [];
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const serviceTypeChoices = [
|
|
35
|
+
{ name: types_1.ServiceType.ODATA_V2, value: types_1.ServiceType.ODATA_V2 },
|
|
36
|
+
{ name: types_1.ServiceType.ODATA_V4, value: types_1.ServiceType.ODATA_V4 },
|
|
37
|
+
{ name: types_1.ServiceType.HTTP, value: types_1.ServiceType.HTTP }
|
|
11
38
|
];
|
|
12
39
|
/**
|
|
13
40
|
* Exucute generic validation for input.
|
|
@@ -23,6 +50,9 @@ function validatePromptInput(value) {
|
|
|
23
50
|
return validationResult;
|
|
24
51
|
}
|
|
25
52
|
}
|
|
53
|
+
if (!/[a-zA-Z0-9]$/.test(value)) {
|
|
54
|
+
return (0, i18n_1.t)('validators.errorInputMustEndWithAlphanumeric');
|
|
55
|
+
}
|
|
26
56
|
return true;
|
|
27
57
|
}
|
|
28
58
|
/**
|
|
@@ -64,18 +94,17 @@ function validatePromptJSON(value) {
|
|
|
64
94
|
* Validates the OData Service name prompt.
|
|
65
95
|
*
|
|
66
96
|
* @param value The value to validate.
|
|
67
|
-
* @param answers The answers object.
|
|
68
97
|
* @param isCustomerBase Whether the validation is for customer usage.
|
|
69
98
|
* @param changeFiles The list of existing change files to check against.
|
|
70
99
|
* @returns {boolean | string} True if no duplication is found, or an error message if validation fails.
|
|
71
100
|
*/
|
|
72
|
-
function validatePromptODataName(value,
|
|
101
|
+
function validatePromptODataName(value, isCustomerBase, changeFiles) {
|
|
73
102
|
let validationResult = validatePromptInput(value);
|
|
74
103
|
if (typeof validationResult === 'string') {
|
|
75
104
|
return validationResult;
|
|
76
105
|
}
|
|
77
106
|
if (isCustomerBase) {
|
|
78
|
-
validationResult = validateCustomerValue(value, 'prompts.
|
|
107
|
+
validationResult = validateCustomerValue(value, 'prompts.modelAndDatasourceNameLabel');
|
|
79
108
|
if (typeof validationResult === 'string') {
|
|
80
109
|
return validationResult;
|
|
81
110
|
}
|
|
@@ -83,149 +112,198 @@ function validatePromptODataName(value, answers, isCustomerBase, changeFiles) {
|
|
|
83
112
|
if ((0, project_input_validator_1.hasContentDuplication)(value, 'dataSource', changeFiles)) {
|
|
84
113
|
return (0, i18n_1.t)('validators.errorDuplicatedValueOData');
|
|
85
114
|
}
|
|
86
|
-
if (answers.addAnnotationMode && value === answers.dataSourceName) {
|
|
87
|
-
return (0, i18n_1.t)('validators.errorDuplicateNamesOData');
|
|
88
|
-
}
|
|
89
115
|
return true;
|
|
90
116
|
}
|
|
91
117
|
/**
|
|
92
|
-
* Validates the OData
|
|
118
|
+
* Validates the OData Source URI prompt.
|
|
93
119
|
*
|
|
94
120
|
* @param value The value to validate.
|
|
95
|
-
* @
|
|
96
|
-
* @param isCustomerBase Whether the validation is for customer usage.
|
|
97
|
-
* @param changeFiles The list of existing change files to check against.
|
|
98
|
-
* @returns {boolean | string} True if no duplication is found, or an error message if validation fails.
|
|
121
|
+
* @returns {boolean | string} True if the URI is valid, or an error message if validation fails.
|
|
99
122
|
*/
|
|
100
|
-
function
|
|
101
|
-
|
|
123
|
+
function validatePromptURI(value) {
|
|
124
|
+
const validationResult = (0, project_input_validator_1.validateEmptyString)(value);
|
|
102
125
|
if (typeof validationResult === 'string') {
|
|
103
126
|
return validationResult;
|
|
104
127
|
}
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
if (typeof validationResult === 'string') {
|
|
108
|
-
return validationResult;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if ((0, project_input_validator_1.hasContentDuplication)(value, 'dataSource', changeFiles)) {
|
|
112
|
-
return (0, i18n_1.t)('validators.errorDuplicatedValueOData');
|
|
113
|
-
}
|
|
114
|
-
if (value === answers.name) {
|
|
115
|
-
return (0, i18n_1.t)('validators.errorDuplicateNamesOData');
|
|
128
|
+
if (!(0, project_input_validator_1.isDataSourceURI)(value)) {
|
|
129
|
+
return (0, i18n_1.t)('validators.errorInvalidDataSourceURI');
|
|
116
130
|
}
|
|
117
131
|
return true;
|
|
118
132
|
}
|
|
119
133
|
/**
|
|
120
|
-
*
|
|
134
|
+
* Builds the full resulting service URL from a destination URL and a service URI.
|
|
135
|
+
* Returns undefined if either value is absent or the URI fails basic validation.
|
|
121
136
|
*
|
|
122
|
-
* @param
|
|
123
|
-
* @param
|
|
124
|
-
* @
|
|
125
|
-
* @returns {boolean | string} True if no duplication is found, or an error message if validation fails.
|
|
137
|
+
* @param {string | undefined} destinationUrl - The destination base URL.
|
|
138
|
+
* @param {string | undefined} serviceUri - The relative service URI from the prompt.
|
|
139
|
+
* @returns {string | undefined} The concatenated URL, or undefined if it cannot be formed.
|
|
126
140
|
*/
|
|
127
|
-
function
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return validationResult;
|
|
141
|
+
function buildResultingServiceUrl(destinationUrl, serviceUri) {
|
|
142
|
+
if (!destinationUrl || !serviceUri || validatePromptURI(serviceUri) !== true) {
|
|
143
|
+
return undefined;
|
|
131
144
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
145
|
+
return destinationUrl.replace(/\/$/, '') + serviceUri;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Returns the OData version string for use in change content based on the selected service type.
|
|
149
|
+
* Returns undefined for HTTP service type as it has no OData version.
|
|
150
|
+
*
|
|
151
|
+
* @param {ServiceType} serviceType - The selected service type.
|
|
152
|
+
* @returns {string | undefined} The OData version string ('2.0' or '4.0'), or undefined for HTTP.
|
|
153
|
+
*/
|
|
154
|
+
function getODataVersionFromServiceType(serviceType) {
|
|
155
|
+
if (serviceType === types_1.ServiceType.ODATA_V2) {
|
|
156
|
+
return '2.0';
|
|
157
|
+
}
|
|
158
|
+
if (serviceType === types_1.ServiceType.ODATA_V4) {
|
|
159
|
+
return '4.0';
|
|
160
|
+
}
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Resolves the backend base URL for ABAP (non-CF) projects.
|
|
165
|
+
* For VS Code projects the URL is read directly from the `target.url` field in ui5.yaml.
|
|
166
|
+
* For BAS projects the destination name is read from `target.destination` and the URL
|
|
167
|
+
* is resolved via the BAS destination service.
|
|
168
|
+
*
|
|
169
|
+
* @param {string} projectPath - The root path of the project.
|
|
170
|
+
* @returns {Promise<string | undefined>} The resolved base URL, or undefined if it cannot be determined.
|
|
171
|
+
*/
|
|
172
|
+
async function getAbapServiceUrl(projectPath) {
|
|
173
|
+
try {
|
|
174
|
+
const { target } = (await (0, helper_1.getAdpConfig)(projectPath, 'ui5.yaml'));
|
|
175
|
+
if (!target) {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
if (target.url) {
|
|
179
|
+
return target.url;
|
|
180
|
+
}
|
|
181
|
+
if (target.destination) {
|
|
182
|
+
const destinations = await (0, btp_utils_1.listDestinations)();
|
|
183
|
+
return destinations[target.destination]?.Host;
|
|
136
184
|
}
|
|
137
185
|
}
|
|
138
|
-
|
|
139
|
-
|
|
186
|
+
catch {
|
|
187
|
+
// Message will not be shown
|
|
140
188
|
}
|
|
141
|
-
return
|
|
189
|
+
return undefined;
|
|
142
190
|
}
|
|
143
191
|
/**
|
|
144
|
-
*
|
|
192
|
+
* Fetches destination choices for CF environments.
|
|
193
|
+
* Returns the choices and a generic UI error message if the fetch fails, logging the original error.
|
|
145
194
|
*
|
|
146
|
-
* @param
|
|
147
|
-
* @
|
|
195
|
+
* @param {string} projectPath - The root path of the project.
|
|
196
|
+
* @param {ToolsLogger} [logger] - Optional logger for error details.
|
|
197
|
+
* @returns {Promise<{ choices: { name: string; value: Destination }[]; error?: string }>} The destination choices and an optional error message.
|
|
148
198
|
*/
|
|
149
|
-
function
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
199
|
+
async function getDestinationChoices(projectPath, logger) {
|
|
200
|
+
try {
|
|
201
|
+
const destinations = await (0, destinations_1.getBtpDestinations)(projectPath);
|
|
202
|
+
const choices = Object.entries(destinations).map(([name, dest]) => ({
|
|
203
|
+
name,
|
|
204
|
+
value: dest
|
|
205
|
+
}));
|
|
206
|
+
return { choices };
|
|
153
207
|
}
|
|
154
|
-
|
|
155
|
-
|
|
208
|
+
catch (e) {
|
|
209
|
+
logger?.error(e.message);
|
|
210
|
+
return { choices: [], error: (0, i18n_1.t)('error.errorFetchingDestinations') };
|
|
156
211
|
}
|
|
157
|
-
return true;
|
|
158
212
|
}
|
|
159
213
|
/**
|
|
160
214
|
* Gets the prompts for adding the new model.
|
|
161
215
|
*
|
|
162
216
|
* @param {string} projectPath - The root path of the project.
|
|
163
217
|
* @param {UI5FlexLayer} layer - UI5 Flex layer.
|
|
218
|
+
* @param {ToolsLogger} [logger] - Optional logger.
|
|
164
219
|
* @returns {YUIQuestion<NewModelAnswers>[]} The questions/prompts.
|
|
165
220
|
*/
|
|
166
|
-
async function getPrompts(projectPath, layer) {
|
|
221
|
+
async function getPrompts(projectPath, layer, logger) {
|
|
167
222
|
const isCustomerBase = "CUSTOMER_BASE" /* FlexLayer.CUSTOMER_BASE */ === layer;
|
|
168
223
|
const defaultSeviceName = isCustomerBase ? "customer." /* NamespacePrefix.CUSTOMER */ : "" /* NamespacePrefix.EMPTY */;
|
|
169
224
|
const isCFEnv = await (0, cf_1.isCFEnvironment)(projectPath);
|
|
170
|
-
const
|
|
225
|
+
const abapServiceUrl = isCFEnv ? undefined : await getAbapServiceUrl(projectPath);
|
|
226
|
+
const changeFiles = [
|
|
227
|
+
...(0, change_utils_1.getChangesByType)(projectPath, "appdescr_ui5_addNewModel" /* ChangeType.ADD_NEW_MODEL */),
|
|
228
|
+
...(0, change_utils_1.getChangesByType)(projectPath, "appdescr_app_addNewDataSource" /* ChangeType.ADD_NEW_DATA_SOURCE */)
|
|
229
|
+
];
|
|
230
|
+
let destinationError;
|
|
231
|
+
let destinationChoices;
|
|
232
|
+
if (isCFEnv) {
|
|
233
|
+
({ choices: destinationChoices, error: destinationError } = await getDestinationChoices(projectPath, logger));
|
|
234
|
+
}
|
|
235
|
+
const buildResultingUrlMessage = (i18nKey, uri, previousAnswers) => {
|
|
236
|
+
const destinationUrl = isCFEnv ? previousAnswers?.destination?.Host : abapServiceUrl;
|
|
237
|
+
const resultingUrl = buildResultingServiceUrl(destinationUrl, uri);
|
|
238
|
+
if (!resultingUrl) {
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
message: (0, i18n_1.t)(i18nKey, { url: resultingUrl, interpolation: { escapeValue: false } }),
|
|
243
|
+
severity: yeoman_ui_types_1.Severity.information
|
|
244
|
+
};
|
|
245
|
+
};
|
|
171
246
|
return [
|
|
172
247
|
{
|
|
173
|
-
type: '
|
|
174
|
-
name: '
|
|
175
|
-
message: (0, i18n_1.t)('prompts.
|
|
176
|
-
|
|
248
|
+
type: 'list',
|
|
249
|
+
name: 'serviceType',
|
|
250
|
+
message: (0, i18n_1.t)('prompts.serviceTypeLabel'),
|
|
251
|
+
choices: isCFEnv ? serviceTypeChoices : serviceTypeChoices.filter((c) => c.value !== types_1.ServiceType.HTTP),
|
|
177
252
|
store: false,
|
|
178
|
-
validate:
|
|
179
|
-
return validatePromptODataName(value, answers, isCustomerBase, changeFiles);
|
|
180
|
-
},
|
|
253
|
+
validate: project_input_validator_1.validateEmptyString,
|
|
181
254
|
guiOptions: {
|
|
182
255
|
mandatory: true,
|
|
183
|
-
hint: (0, i18n_1.t)('prompts.
|
|
256
|
+
hint: (0, i18n_1.t)('prompts.serviceTypeTooltip')
|
|
184
257
|
}
|
|
185
258
|
},
|
|
259
|
+
{
|
|
260
|
+
type: 'list',
|
|
261
|
+
name: 'destination',
|
|
262
|
+
message: (0, i18n_1.t)('prompts.destinationLabel'),
|
|
263
|
+
choices: () => destinationChoices ?? [],
|
|
264
|
+
when: () => isCFEnv,
|
|
265
|
+
guiOptions: {
|
|
266
|
+
mandatory: true,
|
|
267
|
+
hint: (0, i18n_1.t)('prompts.destinationTooltip')
|
|
268
|
+
},
|
|
269
|
+
validate: (value) => destinationError ?? (0, project_input_validator_1.validateEmptyString)(value?.Name)
|
|
270
|
+
},
|
|
186
271
|
{
|
|
187
272
|
type: 'input',
|
|
188
273
|
name: 'uri',
|
|
189
|
-
message: (0, i18n_1.t)('prompts.
|
|
274
|
+
message: (0, i18n_1.t)('prompts.serviceUriLabel'),
|
|
190
275
|
guiOptions: {
|
|
191
276
|
mandatory: true,
|
|
192
277
|
hint: (0, i18n_1.t)('prompts.oDataServiceUriTooltip')
|
|
193
278
|
},
|
|
194
|
-
validate:
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
type: 'list',
|
|
199
|
-
name: 'version',
|
|
200
|
-
message: (0, i18n_1.t)('prompts.oDataServiceVersionLabel'),
|
|
201
|
-
choices: oDataVersions,
|
|
202
|
-
default: (answers) => {
|
|
203
|
-
if (answers.uri?.startsWith(isCFEnv ? '/odata/v4/' : '/sap/opu/odata4/')) {
|
|
204
|
-
return oDataVersions[1].value;
|
|
279
|
+
validate: (value) => {
|
|
280
|
+
const uriResult = validatePromptURI(value);
|
|
281
|
+
if (typeof uriResult === 'string') {
|
|
282
|
+
return uriResult;
|
|
205
283
|
}
|
|
206
|
-
|
|
284
|
+
if (isCFEnv && readXsAppRoutes(projectPath).some((r) => r.target === `${value}$1`)) {
|
|
285
|
+
return (0, i18n_1.t)('validators.errorRouteAlreadyExists');
|
|
286
|
+
}
|
|
287
|
+
return true;
|
|
207
288
|
},
|
|
208
289
|
store: false,
|
|
209
|
-
|
|
210
|
-
guiOptions: {
|
|
211
|
-
mandatory: true,
|
|
212
|
-
hint: (0, i18n_1.t)('prompts.oDataServiceVersionTooltip'),
|
|
213
|
-
applyDefaultWhenDirty: true
|
|
214
|
-
}
|
|
290
|
+
additionalMessages: (uri, previousAnswers) => buildResultingUrlMessage('prompts.resultingServiceUrl', uri, previousAnswers)
|
|
215
291
|
},
|
|
216
292
|
{
|
|
217
293
|
type: 'input',
|
|
218
|
-
name: '
|
|
219
|
-
message: (
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
hint: (0, i18n_1.t)('prompts.oDataServiceModelNameTooltip')
|
|
223
|
-
},
|
|
294
|
+
name: 'modelAndDatasourceName',
|
|
295
|
+
message: (answers) => answers.serviceType === types_1.ServiceType.HTTP
|
|
296
|
+
? (0, i18n_1.t)('prompts.datasourceNameLabel')
|
|
297
|
+
: (0, i18n_1.t)('prompts.modelAndDatasourceNameLabel'),
|
|
224
298
|
default: defaultSeviceName,
|
|
299
|
+
store: false,
|
|
225
300
|
validate: (value) => {
|
|
226
|
-
return
|
|
301
|
+
return validatePromptODataName(value, isCustomerBase, changeFiles);
|
|
227
302
|
},
|
|
228
|
-
|
|
303
|
+
guiOptions: {
|
|
304
|
+
mandatory: true,
|
|
305
|
+
hint: (0, i18n_1.t)('prompts.modelAndDatasourceNameTooltip')
|
|
306
|
+
}
|
|
229
307
|
},
|
|
230
308
|
{
|
|
231
309
|
type: 'editor',
|
|
@@ -233,6 +311,7 @@ async function getPrompts(projectPath, layer) {
|
|
|
233
311
|
message: (0, i18n_1.t)('prompts.oDataServiceModelSettingsLabel'),
|
|
234
312
|
store: false,
|
|
235
313
|
validate: validatePromptJSON,
|
|
314
|
+
when: (answers) => answers.serviceType !== types_1.ServiceType.HTTP,
|
|
236
315
|
guiOptions: {
|
|
237
316
|
hint: (0, i18n_1.t)('prompts.oDataServiceModelSettingsTooltip')
|
|
238
317
|
}
|
|
@@ -241,22 +320,8 @@ async function getPrompts(projectPath, layer) {
|
|
|
241
320
|
type: 'confirm',
|
|
242
321
|
name: 'addAnnotationMode',
|
|
243
322
|
message: 'Do you want to add annotation?',
|
|
244
|
-
default: false
|
|
245
|
-
|
|
246
|
-
{
|
|
247
|
-
type: 'input',
|
|
248
|
-
name: 'dataSourceName',
|
|
249
|
-
message: (0, i18n_1.t)('prompts.oDataAnnotationDataSourceNameLabel'),
|
|
250
|
-
validate: (value, answers) => {
|
|
251
|
-
return validatePromptODataAnnotationsName(value, answers, isCustomerBase, changeFiles);
|
|
252
|
-
},
|
|
253
|
-
default: defaultSeviceName,
|
|
254
|
-
store: false,
|
|
255
|
-
guiOptions: {
|
|
256
|
-
mandatory: true,
|
|
257
|
-
hint: (0, i18n_1.t)('prompts.oDataAnnotationDataSourceNameTooltip')
|
|
258
|
-
},
|
|
259
|
-
when: (answers) => answers.addAnnotationMode
|
|
323
|
+
default: false,
|
|
324
|
+
when: (answers) => answers.serviceType !== types_1.ServiceType.HTTP
|
|
260
325
|
},
|
|
261
326
|
{
|
|
262
327
|
type: 'input',
|
|
@@ -268,7 +333,8 @@ async function getPrompts(projectPath, layer) {
|
|
|
268
333
|
mandatory: true,
|
|
269
334
|
hint: (0, i18n_1.t)('prompts.oDataAnnotationDataSourceUriTooltip')
|
|
270
335
|
},
|
|
271
|
-
when: (answers) => answers.addAnnotationMode
|
|
336
|
+
when: (answers) => answers.addAnnotationMode,
|
|
337
|
+
additionalMessages: (uri, previousAnswers) => buildResultingUrlMessage('prompts.resultingAnnotationUrl', uri, previousAnswers)
|
|
272
338
|
},
|
|
273
339
|
{
|
|
274
340
|
type: 'editor',
|
|
@@ -283,4 +349,42 @@ async function getPrompts(projectPath, layer) {
|
|
|
283
349
|
}
|
|
284
350
|
];
|
|
285
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Builds the NewModelData object from the prompts answers.
|
|
354
|
+
*
|
|
355
|
+
* @param {string} projectPath - The root path of the project.
|
|
356
|
+
* @param {DescriptorVariant} variant - The descriptor variant of the adaptation project.
|
|
357
|
+
* @param {NewModelAnswers} answers - The answers to the prompts.
|
|
358
|
+
* @param {ToolsLogger} [logger] - Optional logger instance.
|
|
359
|
+
* @returns {Promise<NewModelData>} The data required by NewModelWriter.
|
|
360
|
+
*/
|
|
361
|
+
async function createNewModelData(projectPath, variant, answers, logger) {
|
|
362
|
+
const { modelAndDatasourceName, uri, serviceType, modelSettings, addAnnotationMode } = answers;
|
|
363
|
+
const isCloudFoundry = await (0, cf_1.isCFEnvironment)(projectPath);
|
|
364
|
+
return {
|
|
365
|
+
variant,
|
|
366
|
+
serviceType,
|
|
367
|
+
isCloudFoundry,
|
|
368
|
+
destinationName: isCloudFoundry ? answers.destination?.Name : undefined,
|
|
369
|
+
...(isCloudFoundry &&
|
|
370
|
+
answers.destination && {
|
|
371
|
+
isOnPremiseDestination: (0, btp_utils_1.isOnPremiseDestination)(answers.destination)
|
|
372
|
+
}),
|
|
373
|
+
logger,
|
|
374
|
+
service: {
|
|
375
|
+
name: modelAndDatasourceName,
|
|
376
|
+
uri,
|
|
377
|
+
modelName: serviceType === types_1.ServiceType.HTTP ? undefined : modelAndDatasourceName,
|
|
378
|
+
version: getODataVersionFromServiceType(serviceType),
|
|
379
|
+
modelSettings
|
|
380
|
+
},
|
|
381
|
+
...(addAnnotationMode && {
|
|
382
|
+
annotation: {
|
|
383
|
+
dataSourceName: `${modelAndDatasourceName}.annotation`,
|
|
384
|
+
dataSourceURI: answers.dataSourceURI,
|
|
385
|
+
settings: answers.annotationSettings
|
|
386
|
+
}
|
|
387
|
+
})
|
|
388
|
+
};
|
|
389
|
+
}
|
|
286
390
|
//# sourceMappingURL=index.js.map
|
package/dist/prompts/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { getPrompts as getPromptsForChangeDataSource } from './change-data-source';
|
|
2
2
|
export { getPrompts as getPromptsForAddComponentUsages } from './add-component-usages';
|
|
3
|
-
export { getPrompts as getPromptsForNewModel } from './add-new-model';
|
|
3
|
+
export { getPrompts as getPromptsForNewModel, getODataVersionFromServiceType, createNewModelData } from './add-new-model';
|
|
4
4
|
export { getPrompts as getPromptsForChangeInbound } from './change-inbound';
|
|
5
5
|
export { getPrompts as getPromptsForAddAnnotationsToOData } from './add-annotations-to-odata';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/prompts/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPromptsForAddAnnotationsToOData = exports.getPromptsForChangeInbound = exports.getPromptsForNewModel = exports.getPromptsForAddComponentUsages = exports.getPromptsForChangeDataSource = void 0;
|
|
3
|
+
exports.getPromptsForAddAnnotationsToOData = exports.getPromptsForChangeInbound = exports.createNewModelData = exports.getODataVersionFromServiceType = exports.getPromptsForNewModel = exports.getPromptsForAddComponentUsages = exports.getPromptsForChangeDataSource = void 0;
|
|
4
4
|
var change_data_source_1 = require("./change-data-source");
|
|
5
5
|
Object.defineProperty(exports, "getPromptsForChangeDataSource", { enumerable: true, get: function () { return change_data_source_1.getPrompts; } });
|
|
6
6
|
var add_component_usages_1 = require("./add-component-usages");
|
|
7
7
|
Object.defineProperty(exports, "getPromptsForAddComponentUsages", { enumerable: true, get: function () { return add_component_usages_1.getPrompts; } });
|
|
8
8
|
var add_new_model_1 = require("./add-new-model");
|
|
9
9
|
Object.defineProperty(exports, "getPromptsForNewModel", { enumerable: true, get: function () { return add_new_model_1.getPrompts; } });
|
|
10
|
+
Object.defineProperty(exports, "getODataVersionFromServiceType", { enumerable: true, get: function () { return add_new_model_1.getODataVersionFromServiceType; } });
|
|
11
|
+
Object.defineProperty(exports, "createNewModelData", { enumerable: true, get: function () { return add_new_model_1.createNewModelData; } });
|
|
10
12
|
var change_inbound_1 = require("./change-inbound");
|
|
11
13
|
Object.defineProperty(exports, "getPromptsForChangeInbound", { enumerable: true, get: function () { return change_inbound_1.getPrompts; } });
|
|
12
14
|
var add_annotations_to_odata_1 = require("./add-annotations-to-odata");
|
|
@@ -13,10 +13,20 @@
|
|
|
13
13
|
"filePathLabel": "Annotation File path",
|
|
14
14
|
"filePathTooltip": "Select the annotation file from your workspace",
|
|
15
15
|
"addAnnotationOdataSourceTooltip": "Select the OData service you want to add the annotation file to",
|
|
16
|
+
"destinationLabel": "Destination",
|
|
17
|
+
"destinationTooltip": "Select a destination for the service.",
|
|
18
|
+
"serviceTypeLabel": "Service Type",
|
|
19
|
+
"serviceTypeTooltip": "Select the type of service you want to add.",
|
|
16
20
|
"oDataServiceNameLabel": "OData Service Name",
|
|
17
21
|
"oDataServiceNameTooltip": "Enter a name for the OData service you want to add",
|
|
18
22
|
"oDataServiceUriLabel": "OData Service URI",
|
|
19
23
|
"oDataServiceUriTooltip": "Enter the URI for the OData service you want to add. The URI must start and end with '/' and must not contain any whitespaces or parameters",
|
|
24
|
+
"serviceUriLabel": "Service URI",
|
|
25
|
+
"resultingServiceUrl": "Resulting Service URL: {{url}}",
|
|
26
|
+
"resultingAnnotationUrl": "Resulting Annotation URL: {{url}}",
|
|
27
|
+
"modelAndDatasourceNameLabel": "Model and Data Source Name",
|
|
28
|
+
"modelAndDatasourceNameTooltip": "Enter a name for the data source and model.",
|
|
29
|
+
"datasourceNameLabel": "Data Source Name",
|
|
20
30
|
"oDataServiceVersionLabel": "OData Version",
|
|
21
31
|
"oDataServiceVersionTooltip": "Select the version of OData of the service you want to add",
|
|
22
32
|
"oDataServiceModelNameLabel": "OData Service SAPUI5 Model Name",
|
|
@@ -67,7 +77,9 @@
|
|
|
67
77
|
"errorDuplicateNamesOData": "An OData Service Name must be different from an OData Annotation Data Source Name. Rename and try again.",
|
|
68
78
|
"errorInputInvalidValuePrefix": "{{value}} must start with '{{prefix}}'.",
|
|
69
79
|
"errorCustomerEmptyValue": "{{value}} must contain at least one character in addition to '{{prefix}}'.",
|
|
80
|
+
"errorInputMustEndWithAlphanumeric": "The input must end with an alphanumeric character.",
|
|
70
81
|
"errorInvalidDataSourceURI": "Invalid URI. The URI must start and end with '/' and contain no spaces.",
|
|
82
|
+
"errorRouteAlreadyExists": "A route with the same Service URI already exists in xs-app.json. Use a different URI.",
|
|
71
83
|
"appDoesNotSupportManifest": "The selected application is not supported by SAPUI5 Adaptation Project because it does not have a `manifest.json` file. Please select a different application.",
|
|
72
84
|
"ui5VersionNotReachableError": "The URL of the SAPUI5 version you have selected is not reachable. The <URL> URL must be made accessible through the cloud connector and the destination configuration so it can be consumed within the SAPUI5 adaptation project and its SAPUI5 Adaptation Editor.",
|
|
73
85
|
"ui5VersionOutdatedError": "The SAPUI5 version you have selected is not compatible with the SAPUI5 Adaptation Editor. Please select a different version.",
|
|
@@ -115,6 +127,10 @@
|
|
|
115
127
|
"noServiceKeysFoundForInstance": "No service keys found for service instance: {{serviceInstanceName}}",
|
|
116
128
|
"couldNotFetchServiceKeys": "Cannot fetch the service keys. Error: {{error}}",
|
|
117
129
|
"metadataFetchingNotSupportedForCF": "Metadata fetching is not supported for Cloud Foundry projects.",
|
|
130
|
+
"failedToListBtpDestinations": "Failed to list BTP destinations. Error: {{error}}",
|
|
131
|
+
"destinationServiceNotFoundInMtaYaml": "Destination service instance not found in mta.yaml. Ensure a resource with 'service: destination' is declared.",
|
|
132
|
+
"noServiceKeysFoundForDestination": "No service keys found for destination service instance '{{serviceInstanceName}}'. Ensure the service is provisioned and try again.",
|
|
133
|
+
"errorFetchingDestinations": "Error fetching destinations. Check log for details.",
|
|
118
134
|
"cfPushFailed": "cf push failed for the '{{appName}}' app: {{error}}",
|
|
119
135
|
"cfEnableSshFailed": "cf enable-ssh failed for the '{{appName}}' app: {{error}}",
|
|
120
136
|
"cfRestartFailed": "cf restart failed for the '{{appName}}' app: {{error}}"
|
package/dist/types.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { Editor } from 'mem-fs-editor';
|
|
|
6
6
|
import type { Destination } from '@sap-ux/btp-utils';
|
|
7
7
|
import type { YUIQuestion } from '@sap-ux/inquirer-common';
|
|
8
8
|
import type AdmZip from 'adm-zip';
|
|
9
|
+
import type { ToolsLogger } from '@sap-ux/logger';
|
|
9
10
|
import type { SupportedProject } from './source';
|
|
10
11
|
export type DataSources = Record<string, ManifestNamespace.DataSource>;
|
|
11
12
|
/**
|
|
@@ -481,12 +482,19 @@ export interface IWriter<T> {
|
|
|
481
482
|
*/
|
|
482
483
|
export declare const enum ChangeType {
|
|
483
484
|
ADD_NEW_MODEL = "appdescr_ui5_addNewModel",
|
|
485
|
+
ADD_NEW_DATA_SOURCE = "appdescr_app_addNewDataSource",
|
|
484
486
|
ADD_ANNOTATIONS_TO_ODATA = "appdescr_app_addAnnotationsToOData",
|
|
485
487
|
CHANGE_DATA_SOURCE = "appdescr_app_changeDataSource",
|
|
486
488
|
ADD_COMPONENT_USAGES = "appdescr_ui5_addComponentUsages",
|
|
487
489
|
ADD_LIBRARY_REFERENCE = "appdescr_ui5_addLibraries",
|
|
488
490
|
CHANGE_INBOUND = "appdescr_app_changeInbound"
|
|
489
491
|
}
|
|
492
|
+
export declare const ServiceType: {
|
|
493
|
+
readonly ODATA_V2: "OData v2";
|
|
494
|
+
readonly ODATA_V4: "OData v4";
|
|
495
|
+
readonly HTTP: "HTTP";
|
|
496
|
+
};
|
|
497
|
+
export type ServiceType = (typeof ServiceType)[keyof typeof ServiceType];
|
|
490
498
|
/**
|
|
491
499
|
* A mapping of ChangeType values to their respective change names.
|
|
492
500
|
*/
|
|
@@ -571,15 +579,25 @@ export type AddComponentUsageAnswersBase = {
|
|
|
571
579
|
export type AddComponentUsageAnswers = AddComponentUsageAnswersBase & (AddComponentUsageAnswersWithoutLibrary | addComponentUsageAnswersWithLibrary);
|
|
572
580
|
export interface NewModelDataBase {
|
|
573
581
|
variant: DescriptorVariant;
|
|
582
|
+
/** The type of service being added. Determines dataSource type and whether a model entry is created. */
|
|
583
|
+
serviceType: ServiceType;
|
|
584
|
+
/** Whether the project is deployed on Cloud Foundry. Affects URI construction and model settings. */
|
|
585
|
+
isCloudFoundry?: boolean;
|
|
586
|
+
/** Name of the BTP destination. Required when isCloudFoundry is true to write the xs-app.json route. */
|
|
587
|
+
destinationName?: string;
|
|
588
|
+
/** True when the selected CF destination is OnPremise (ProxyType: 'OnPremise'). Triggers connectivity service in mta.yaml. */
|
|
589
|
+
isOnPremiseDestination?: boolean;
|
|
590
|
+
/** Optional logger passed to the writer for use during the write operation. */
|
|
591
|
+
logger?: ToolsLogger;
|
|
574
592
|
service: {
|
|
575
593
|
/** Name of the OData service. */
|
|
576
594
|
name: string;
|
|
577
595
|
/** URI of the OData service. */
|
|
578
596
|
uri: string;
|
|
579
|
-
/** Name of the OData service model. */
|
|
580
|
-
modelName
|
|
581
|
-
/** Version of OData used. */
|
|
582
|
-
version
|
|
597
|
+
/** Name of the OData service model. Optional for absent for HTTP service type. */
|
|
598
|
+
modelName?: string;
|
|
599
|
+
/** Version of OData used. Optional for HTTP service type. */
|
|
600
|
+
version?: string;
|
|
583
601
|
/** Settings for the OData service model. */
|
|
584
602
|
modelSettings?: string;
|
|
585
603
|
};
|
|
@@ -596,21 +614,19 @@ export interface NewModelDataWithAnnotations extends NewModelDataBase {
|
|
|
596
614
|
}
|
|
597
615
|
export type NewModelData = NewModelDataBase | NewModelDataWithAnnotations;
|
|
598
616
|
export interface NewModelAnswersBase {
|
|
599
|
-
/**
|
|
600
|
-
|
|
617
|
+
/** Selected BTP destination. Only relevant for CF projects. */
|
|
618
|
+
destination?: Destination;
|
|
619
|
+
/** Type of service to add. */
|
|
620
|
+
serviceType: ServiceType;
|
|
621
|
+
/** Name used for both the OData service datasource and the SAPUI5 model. */
|
|
622
|
+
modelAndDatasourceName: string;
|
|
601
623
|
/** URI of the OData service. */
|
|
602
624
|
uri: string;
|
|
603
|
-
/** Name of the OData service model. */
|
|
604
|
-
modelName: string;
|
|
605
|
-
/** Version of OData used. */
|
|
606
|
-
version: string;
|
|
607
625
|
/** Settings for the OData service model. */
|
|
608
626
|
modelSettings: string;
|
|
609
627
|
}
|
|
610
628
|
export interface NewModelAnswersWithAnnotations extends NewModelAnswersBase {
|
|
611
629
|
addAnnotationMode: true;
|
|
612
|
-
/** Name of the OData annotation data source. */
|
|
613
|
-
dataSourceName: string;
|
|
614
630
|
/** Optional URI of the OData annotation data source. */
|
|
615
631
|
dataSourceURI?: string;
|
|
616
632
|
/** Optional settings for the OData annotation. */
|
|
@@ -794,6 +810,12 @@ export interface Uaa {
|
|
|
794
810
|
clientsecret: string;
|
|
795
811
|
url: string;
|
|
796
812
|
}
|
|
813
|
+
export type CfDestinationServiceCredentials = {
|
|
814
|
+
uri: string;
|
|
815
|
+
uaa: Uaa;
|
|
816
|
+
} | ({
|
|
817
|
+
uri: string;
|
|
818
|
+
} & Uaa);
|
|
797
819
|
export interface CfAppParams {
|
|
798
820
|
appName: string;
|
|
799
821
|
appVersion: string;
|
package/dist/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.cfServicesPromptNames = exports.AppRouterType = exports.ChangeTypeMap = exports.ApplicationType = void 0;
|
|
3
|
+
exports.cfServicesPromptNames = exports.AppRouterType = exports.ChangeTypeMap = exports.ServiceType = exports.ApplicationType = void 0;
|
|
4
4
|
var ApplicationType;
|
|
5
5
|
(function (ApplicationType) {
|
|
6
6
|
ApplicationType["FIORI_ELEMENTS"] = "FioriElements";
|
|
@@ -8,11 +8,17 @@ var ApplicationType;
|
|
|
8
8
|
ApplicationType["FREE_STYLE"] = "FreeStyle";
|
|
9
9
|
ApplicationType["NONE"] = "";
|
|
10
10
|
})(ApplicationType || (exports.ApplicationType = ApplicationType = {}));
|
|
11
|
+
exports.ServiceType = {
|
|
12
|
+
ODATA_V2: 'OData v2',
|
|
13
|
+
ODATA_V4: 'OData v4',
|
|
14
|
+
HTTP: 'HTTP'
|
|
15
|
+
};
|
|
11
16
|
/**
|
|
12
17
|
* A mapping of ChangeType values to their respective change names.
|
|
13
18
|
*/
|
|
14
19
|
exports.ChangeTypeMap = {
|
|
15
20
|
["appdescr_ui5_addNewModel" /* ChangeType.ADD_NEW_MODEL */]: 'addNewModel',
|
|
21
|
+
["appdescr_app_addNewDataSource" /* ChangeType.ADD_NEW_DATA_SOURCE */]: 'addNewDataSource',
|
|
16
22
|
["appdescr_app_addAnnotationsToOData" /* ChangeType.ADD_ANNOTATIONS_TO_ODATA */]: 'addAnnotationsToOData',
|
|
17
23
|
["appdescr_app_changeDataSource" /* ChangeType.CHANGE_DATA_SOURCE */]: 'changeDataSource',
|
|
18
24
|
["appdescr_ui5_addComponentUsages" /* ChangeType.ADD_COMPONENT_USAGES */]: 'addComponentUsages',
|
package/dist/writer/cf.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Editor } from 'mem-fs-editor';
|
|
2
2
|
import { type ToolsLogger } from '@sap-ux/logger';
|
|
3
|
-
import type { CfAdpWriterConfig,
|
|
3
|
+
import type { CfAdpWriterConfig, CfConfig, CfUi5AppInfo } from '../types';
|
|
4
4
|
/**
|
|
5
5
|
* Writes the CF adp-project template to the mem-fs-editor instance.
|
|
6
6
|
*
|
package/dist/writer/cf.js
CHANGED
|
@@ -16,8 +16,9 @@ const source_1 = require("../source");
|
|
|
16
16
|
const manifest_1 = require("./manifest");
|
|
17
17
|
const project_utils_1 = require("./project-utils");
|
|
18
18
|
const i18n_1 = require("./i18n");
|
|
19
|
-
const helper_1 = require("../base/helper");
|
|
20
19
|
const project_builder_1 = require("../base/project-builder");
|
|
20
|
+
const helper_1 = require("../base/helper");
|
|
21
|
+
const discovery_1 = require("../cf/app/discovery");
|
|
21
22
|
/**
|
|
22
23
|
* Writes the CF adp-project template to the mem-fs-editor instance.
|
|
23
24
|
*
|
|
@@ -118,7 +119,7 @@ async function setupCfPreview(basePath, yamlPath, cfConfig, logger) {
|
|
|
118
119
|
throw new Error(`No service keys found for service instance: ${serviceInstanceName}`);
|
|
119
120
|
}
|
|
120
121
|
const appId = await (0, helper_1.getBaseAppId)(basePath);
|
|
121
|
-
const appHostIds = (0,
|
|
122
|
+
const appHostIds = (0, discovery_1.getAppHostIds)(serviceInfo.serviceKeys);
|
|
122
123
|
const ui5AppInfo = await (0, cf_1.getCfUi5AppInfo)(appId, appHostIds, cfConfig, logger);
|
|
123
124
|
if (appHostIds.length === 0) {
|
|
124
125
|
throw new Error('No app host IDs found in service keys.');
|
|
@@ -25,5 +25,12 @@ export declare class NewModelWriter implements IWriter<NewModelData> {
|
|
|
25
25
|
* @returns {Promise<void>} A promise that resolves when the change writing process is completed.
|
|
26
26
|
*/
|
|
27
27
|
write(data: NewModelData): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Creates or updates the xs-app.json in the webapp folder with a new AppRouter route
|
|
30
|
+
* for the added OData service.
|
|
31
|
+
*
|
|
32
|
+
* @param {NewModelData} data - The new model data containing service name, URI and destination name.
|
|
33
|
+
*/
|
|
34
|
+
private writeXsAppRoute;
|
|
28
35
|
}
|
|
29
36
|
//# sourceMappingURL=new-model-writer.d.ts.map
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.NewModelWriter = void 0;
|
|
4
|
+
const node_path_1 = require("node:path");
|
|
5
|
+
const logger_1 = require("@sap-ux/logger");
|
|
6
|
+
const types_1 = require("../../../types");
|
|
4
7
|
const change_utils_1 = require("../../../base/change-utils");
|
|
8
|
+
const yaml_1 = require("../../../cf/project/yaml");
|
|
9
|
+
const ssh_1 = require("../../../cf/services/ssh");
|
|
5
10
|
/**
|
|
6
11
|
* Handles the creation and writing of new sapui5 model data changes for a project.
|
|
7
12
|
*/
|
|
@@ -23,29 +28,35 @@ class NewModelWriter {
|
|
|
23
28
|
* @returns {object} The constructed content object for the new model change.
|
|
24
29
|
*/
|
|
25
30
|
constructContent(data) {
|
|
26
|
-
const { service } = data;
|
|
31
|
+
const { service, isCloudFoundry, serviceType } = data;
|
|
32
|
+
const isHttp = serviceType === types_1.ServiceType.HTTP;
|
|
33
|
+
const uri = isCloudFoundry ? `${service.name.replaceAll('.', '/')}${service.uri}` : service.uri;
|
|
34
|
+
const dataSourceEntry = {
|
|
35
|
+
uri,
|
|
36
|
+
type: isHttp ? 'http' : 'OData',
|
|
37
|
+
settings: {}
|
|
38
|
+
};
|
|
39
|
+
if (service.version) {
|
|
40
|
+
dataSourceEntry.settings.odataVersion = service.version;
|
|
41
|
+
}
|
|
27
42
|
const content = {
|
|
28
43
|
dataSource: {
|
|
29
|
-
[service.name]:
|
|
30
|
-
uri: service.uri,
|
|
31
|
-
type: 'OData',
|
|
32
|
-
settings: {
|
|
33
|
-
odataVersion: service.version
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
model: {
|
|
38
|
-
[service.modelName]: {
|
|
39
|
-
dataSource: service.name
|
|
40
|
-
}
|
|
44
|
+
[service.name]: dataSourceEntry
|
|
41
45
|
}
|
|
42
46
|
};
|
|
43
|
-
if (
|
|
44
|
-
content.model
|
|
47
|
+
if (!isHttp && service.modelName) {
|
|
48
|
+
content.model = {
|
|
49
|
+
[service.modelName]: {
|
|
50
|
+
dataSource: service.name,
|
|
51
|
+
...(service.modelSettings?.length ? { settings: (0, change_utils_1.parseStringToObject)(service.modelSettings) } : {})
|
|
52
|
+
}
|
|
53
|
+
};
|
|
45
54
|
}
|
|
46
55
|
if ('annotation' in data) {
|
|
47
56
|
const { annotation } = data;
|
|
48
|
-
content.dataSource[service.name].settings.annotations = [
|
|
57
|
+
content.dataSource[service.name].settings.annotations = [
|
|
58
|
+
`${annotation.dataSourceName}`
|
|
59
|
+
];
|
|
49
60
|
content.dataSource[annotation.dataSourceName] = {
|
|
50
61
|
uri: annotation.dataSourceURI,
|
|
51
62
|
type: 'ODataAnnotation'
|
|
@@ -64,9 +75,35 @@ class NewModelWriter {
|
|
|
64
75
|
*/
|
|
65
76
|
async write(data) {
|
|
66
77
|
const timestamp = Date.now();
|
|
78
|
+
const isHttp = data.serviceType === types_1.ServiceType.HTTP;
|
|
67
79
|
const content = this.constructContent(data);
|
|
68
|
-
const change = (0, change_utils_1.getChange)(data.variant, timestamp, content, "appdescr_ui5_addNewModel" /* ChangeType.ADD_NEW_MODEL */);
|
|
80
|
+
const change = (0, change_utils_1.getChange)(data.variant, timestamp, content, isHttp ? "appdescr_app_addNewDataSource" /* ChangeType.ADD_NEW_DATA_SOURCE */ : "appdescr_ui5_addNewModel" /* ChangeType.ADD_NEW_MODEL */);
|
|
69
81
|
await (0, change_utils_1.writeChangeToFolder)(this.projectPath, change, this.fs);
|
|
82
|
+
if (data.isCloudFoundry) {
|
|
83
|
+
this.writeXsAppRoute(data);
|
|
84
|
+
}
|
|
85
|
+
if (data.isOnPremiseDestination) {
|
|
86
|
+
await (0, yaml_1.addConnectivityServiceToMta)((0, node_path_1.dirname)(this.projectPath), this.fs);
|
|
87
|
+
await (0, ssh_1.ensureTunnelAppExists)(ssh_1.DEFAULT_TUNNEL_APP_NAME, data.logger ?? new logger_1.ToolsLogger());
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Creates or updates the xs-app.json in the webapp folder with a new AppRouter route
|
|
92
|
+
* for the added OData service.
|
|
93
|
+
*
|
|
94
|
+
* @param {NewModelData} data - The new model data containing service name, URI and destination name.
|
|
95
|
+
*/
|
|
96
|
+
writeXsAppRoute(data) {
|
|
97
|
+
const xsAppPath = (0, node_path_1.join)(this.projectPath, 'webapp', 'xs-app.json');
|
|
98
|
+
const source = `^/${data.service.name.replaceAll('.', '/')}${data.service.uri}(.*)`;
|
|
99
|
+
const newRoute = {
|
|
100
|
+
source,
|
|
101
|
+
target: `${data.service.uri}$1`,
|
|
102
|
+
destination: data.destinationName
|
|
103
|
+
};
|
|
104
|
+
const existing = this.fs.readJSON(xsAppPath, { routes: [] });
|
|
105
|
+
existing.routes.push(newRoute);
|
|
106
|
+
this.fs.writeJSON(xsAppPath, existing);
|
|
70
107
|
}
|
|
71
108
|
}
|
|
72
109
|
exports.NewModelWriter = NewModelWriter;
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"bugs": {
|
|
10
10
|
"url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Aadp-tooling"
|
|
11
11
|
},
|
|
12
|
-
"version": "0.18.
|
|
12
|
+
"version": "0.18.117",
|
|
13
13
|
"license": "Apache-2.0",
|
|
14
14
|
"author": "@SAP/ux-tools-team",
|
|
15
15
|
"main": "dist/index.js",
|
|
@@ -36,17 +36,17 @@
|
|
|
36
36
|
"prompts": "2.4.2",
|
|
37
37
|
"sanitize-filename": "1.6.4",
|
|
38
38
|
"uuid": "11.1.0",
|
|
39
|
-
"@sap-ux/axios-extension": "1.25.
|
|
40
|
-
"@sap-ux/btp-utils": "1.1.
|
|
39
|
+
"@sap-ux/axios-extension": "1.25.31",
|
|
40
|
+
"@sap-ux/btp-utils": "1.1.14",
|
|
41
41
|
"@sap-ux/i18n": "0.3.10",
|
|
42
|
-
"@sap-ux/inquirer-common": "0.11.
|
|
42
|
+
"@sap-ux/inquirer-common": "0.11.36",
|
|
43
43
|
"@sap-ux/logger": "0.8.5",
|
|
44
|
-
"@sap-ux/nodejs-utils": "0.2.
|
|
44
|
+
"@sap-ux/nodejs-utils": "0.2.21",
|
|
45
45
|
"@sap-ux/odata-service-writer": "0.31.7",
|
|
46
46
|
"@sap-ux/project-access": "1.35.20",
|
|
47
47
|
"@sap-ux/project-input-validator": "0.6.76",
|
|
48
48
|
"@sap-ux/store": "1.5.13",
|
|
49
|
-
"@sap-ux/system-access": "0.7.
|
|
49
|
+
"@sap-ux/system-access": "0.7.7",
|
|
50
50
|
"@sap-ux/ui5-config": "0.30.3",
|
|
51
51
|
"@sap-ux/ui5-info": "0.13.19"
|
|
52
52
|
},
|