@sap-ux/adp-tooling 0.15.38 → 0.16.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.
- package/dist/cf/app/discovery.d.ts +19 -0
- package/dist/cf/app/discovery.js +42 -0
- package/dist/cf/app/html5-repo.d.ts +36 -0
- package/dist/cf/app/html5-repo.js +137 -0
- package/dist/cf/app/index.d.ts +3 -0
- package/dist/cf/app/index.js +19 -0
- package/dist/cf/core/auth.d.ts +18 -0
- package/dist/cf/core/auth.js +38 -0
- package/dist/cf/core/config.d.ts +10 -0
- package/dist/cf/core/config.js +68 -0
- package/dist/cf/core/index.d.ts +3 -0
- package/dist/cf/core/index.js +19 -0
- package/dist/cf/index.d.ts +6 -0
- package/dist/cf/index.js +22 -0
- package/dist/cf/project/index.d.ts +4 -0
- package/dist/cf/project/index.js +20 -0
- package/dist/cf/project/mta.d.ts +57 -0
- package/dist/cf/project/mta.js +175 -0
- package/dist/cf/project/yaml-loader.d.ts +24 -0
- package/dist/cf/project/yaml-loader.js +54 -0
- package/dist/cf/project/yaml.d.ts +51 -0
- package/dist/cf/project/yaml.js +436 -0
- package/dist/cf/services/api.d.ts +65 -0
- package/dist/cf/services/api.js +291 -0
- package/dist/cf/services/cli.d.ts +31 -0
- package/dist/cf/services/cli.js +93 -0
- package/dist/cf/services/index.d.ts +3 -0
- package/dist/cf/services/index.js +19 -0
- package/dist/cf/utils/index.d.ts +2 -0
- package/dist/cf/utils/index.js +18 -0
- package/dist/cf/utils/validation.d.ts +37 -0
- package/dist/cf/utils/validation.js +134 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/source/manifest.d.ts +7 -0
- package/dist/source/manifest.js +10 -0
- package/dist/translations/adp-tooling.i18n.json +33 -0
- package/dist/types.d.ts +410 -1
- package/dist/types.js +15 -1
- package/dist/writer/cf.d.ts +14 -0
- package/dist/writer/cf.js +65 -0
- package/dist/writer/project-utils.d.ts +24 -1
- package/dist/writer/project-utils.js +104 -0
- package/dist/writer/writer-config.d.ts +8 -1
- package/dist/writer/writer-config.js +47 -0
- package/package.json +17 -12
- package/templates/cf/_gitignore +6 -0
- package/templates/cf/approuter/package.json +13 -0
- package/templates/cf/approuter/xs-app.json +15 -0
- package/templates/cf/i18n/i18n.properties +3 -0
- package/templates/cf/package.json +30 -0
- package/templates/cf/ui5.yaml +19 -0
- package/templates/cf/xs-security.json +7 -0
|
@@ -0,0 +1,291 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.getBusinessServiceKeys = getBusinessServiceKeys;
|
|
40
|
+
exports.getFDCRequestArguments = getFDCRequestArguments;
|
|
41
|
+
exports.getFDCApps = getFDCApps;
|
|
42
|
+
exports.createService = createService;
|
|
43
|
+
exports.createServices = createServices;
|
|
44
|
+
exports.getServiceInstanceKeys = getServiceInstanceKeys;
|
|
45
|
+
const fs = __importStar(require("node:fs"));
|
|
46
|
+
const axios_1 = __importDefault(require("axios"));
|
|
47
|
+
const path = __importStar(require("node:path"));
|
|
48
|
+
const CFToolsCli = require("@sap/cf-tools/out/src/cli");
|
|
49
|
+
const btp_utils_1 = require("@sap-ux/btp-utils");
|
|
50
|
+
const i18n_1 = require("../../i18n");
|
|
51
|
+
const project_1 = require("../project");
|
|
52
|
+
const cli_1 = require("./cli");
|
|
53
|
+
const PARAM_MAP = new Map([
|
|
54
|
+
['spaceGuids', 'space_guids'],
|
|
55
|
+
['planNames', 'service_plan_names'],
|
|
56
|
+
['names', 'names']
|
|
57
|
+
]);
|
|
58
|
+
/**
|
|
59
|
+
* Get the business service keys.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} businessService - The business service.
|
|
62
|
+
* @param {CfConfig} config - The CF config.
|
|
63
|
+
* @param {ToolsLogger} logger - The logger.
|
|
64
|
+
* @returns {Promise<ServiceKeys | null>} The service keys.
|
|
65
|
+
*/
|
|
66
|
+
async function getBusinessServiceKeys(businessService, config, logger) {
|
|
67
|
+
const serviceKeys = await getServiceInstanceKeys({
|
|
68
|
+
spaceGuids: [config.space.GUID],
|
|
69
|
+
names: [businessService]
|
|
70
|
+
}, logger);
|
|
71
|
+
logger?.log(`Available service key instance : ${JSON.stringify(serviceKeys?.serviceInstance)}`);
|
|
72
|
+
return serviceKeys;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get the FDC request arguments.
|
|
76
|
+
*
|
|
77
|
+
* @param {CfConfig} cfConfig - The CF config.
|
|
78
|
+
* @returns {RequestArguments} The request arguments.
|
|
79
|
+
*/
|
|
80
|
+
function getFDCRequestArguments(cfConfig) {
|
|
81
|
+
const fdcUrl = 'https://ui5-flexibility-design-and-configuration.';
|
|
82
|
+
const cfApiEndpoint = `https://api.cf.${cfConfig.url}`;
|
|
83
|
+
const endpointParts = /https:\/\/api\.cf(?:\.([^-.]*)(-\d+)?(\.hana\.ondemand\.com)|(.*))/.exec(cfApiEndpoint);
|
|
84
|
+
const options = {
|
|
85
|
+
withCredentials: true,
|
|
86
|
+
headers: {
|
|
87
|
+
'Content-Type': 'application/json'
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
let url;
|
|
91
|
+
if (endpointParts?.[3]) {
|
|
92
|
+
// Public cloud - use mTLS enabled domain with "cert" prefix
|
|
93
|
+
const region = endpointParts[1];
|
|
94
|
+
url = `${fdcUrl}cert.cfapps.${region}.hana.ondemand.com`;
|
|
95
|
+
}
|
|
96
|
+
else if (endpointParts?.[4]?.endsWith('.cn')) {
|
|
97
|
+
// China has a special URL pattern
|
|
98
|
+
const parts = endpointParts[4].split('.');
|
|
99
|
+
parts.splice(2, 0, 'apps');
|
|
100
|
+
url = `${fdcUrl}sapui5flex${parts.join('.')}`;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
url = `${fdcUrl}sapui5flex.cfapps${endpointParts?.[4]}`;
|
|
104
|
+
}
|
|
105
|
+
// Add authorization token for non-BAS environments or private cloud
|
|
106
|
+
// For BAS environments with mTLS, the certificate authentication is handled automatically
|
|
107
|
+
if (!(0, btp_utils_1.isAppStudio)() || !endpointParts?.[3]) {
|
|
108
|
+
options.headers['Authorization'] = `Bearer ${cfConfig.token}`;
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
url: url,
|
|
112
|
+
options
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get the FDC apps.
|
|
117
|
+
*
|
|
118
|
+
* @param {string[]} appHostIds - The app host ids.
|
|
119
|
+
* @param {CfConfig} cfConfig - The CF config.
|
|
120
|
+
* @param {ToolsLogger} logger - The logger.
|
|
121
|
+
* @returns {Promise<FDCResponse>} The FDC apps.
|
|
122
|
+
*/
|
|
123
|
+
async function getFDCApps(appHostIds, cfConfig, logger) {
|
|
124
|
+
const requestArguments = getFDCRequestArguments(cfConfig);
|
|
125
|
+
logger?.log(`App Hosts: ${JSON.stringify(appHostIds)}, request arguments: ${JSON.stringify(requestArguments)}`);
|
|
126
|
+
const appHostIdParams = appHostIds.map((id) => `appHostId=${encodeURIComponent(id)}`).join('&');
|
|
127
|
+
const url = `${requestArguments.url}/api/business-service/discovery?${appHostIdParams}`;
|
|
128
|
+
try {
|
|
129
|
+
const response = await axios_1.default.get(url, requestArguments.options);
|
|
130
|
+
if (response.status === 200) {
|
|
131
|
+
logger?.log(`Retrieved FDC apps with request url: ${JSON.stringify(response.data)}`);
|
|
132
|
+
return response.data.results;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
throw new Error((0, i18n_1.t)('error.failedToConnectToFDCService', { status: response.status }));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
logger?.error(`Getting FDC apps failed. Request url: ${url}. ${error}`);
|
|
140
|
+
throw new Error((0, i18n_1.t)('error.failedToGetFDCApps', { error: error.message }));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Creates a service.
|
|
145
|
+
*
|
|
146
|
+
* @param {string} spaceGuid - The space GUID.
|
|
147
|
+
* @param {string} plan - The plan.
|
|
148
|
+
* @param {string} serviceInstanceName - The service instance name.
|
|
149
|
+
* @param {string[]} tags - The tags.
|
|
150
|
+
* @param {string} [serviceName] - The service name.
|
|
151
|
+
* @param {object} [security] - Security configuration.
|
|
152
|
+
* @param {string | null} security.filePath - The security file path.
|
|
153
|
+
* @param {string} [security.xsappname] - The XS app name.
|
|
154
|
+
* @param {ToolsLogger} [logger] - The logger.
|
|
155
|
+
*/
|
|
156
|
+
async function createService(spaceGuid, plan, serviceInstanceName, tags, serviceName, security, logger) {
|
|
157
|
+
try {
|
|
158
|
+
const { filePath, xsappname } = security ?? {};
|
|
159
|
+
if (!serviceName) {
|
|
160
|
+
const json = await (0, cli_1.requestCfApi)(`/v3/service_offerings?per_page=1000&space_guids=${spaceGuid}`);
|
|
161
|
+
const serviceOffering = json?.resources?.find((resource) => resource.tags && tags.every((tag) => resource.tags?.includes(tag)));
|
|
162
|
+
serviceName = serviceOffering?.name;
|
|
163
|
+
}
|
|
164
|
+
logger?.log(`Creating service instance '${serviceInstanceName}' of service '${serviceName}' with '${plan}' plan`);
|
|
165
|
+
const commandParameters = ['create-service', serviceName ?? '', plan, serviceInstanceName];
|
|
166
|
+
if (filePath) {
|
|
167
|
+
let xsSecurity = null;
|
|
168
|
+
try {
|
|
169
|
+
const filePath = path.resolve(__dirname, '../../../templates/cf/xs-security.json');
|
|
170
|
+
const xsContent = fs.readFileSync(filePath, 'utf-8');
|
|
171
|
+
xsSecurity = JSON.parse(xsContent);
|
|
172
|
+
xsSecurity.xsappname = xsappname;
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
logger?.error(`Failed to parse xs-security.json file: ${err}`);
|
|
176
|
+
throw new Error((0, i18n_1.t)('error.xsSecurityJsonCouldNotBeParsed'));
|
|
177
|
+
}
|
|
178
|
+
commandParameters.push('-c', JSON.stringify(xsSecurity));
|
|
179
|
+
}
|
|
180
|
+
await CFToolsCli.Cli.execute(commandParameters);
|
|
181
|
+
logger?.log(`Service instance '${serviceInstanceName}' created successfully`);
|
|
182
|
+
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
logger?.error(e);
|
|
185
|
+
throw new Error((0, i18n_1.t)('error.failedToCreateServiceInstance', { serviceInstanceName, error: e.message }));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Creates the services.
|
|
190
|
+
*
|
|
191
|
+
* @param {string} projectPath - The project path.
|
|
192
|
+
* @param {MtaYaml} yamlContent - The YAML content.
|
|
193
|
+
* @param {string[]} initialServices - The initial services.
|
|
194
|
+
* @param {string} timestamp - The timestamp.
|
|
195
|
+
* @param {string} spaceGuid - The space GUID.
|
|
196
|
+
* @param {ToolsLogger} logger - The logger.
|
|
197
|
+
* @returns {Promise<void>} The promise.
|
|
198
|
+
*/
|
|
199
|
+
async function createServices(projectPath, yamlContent, initialServices, timestamp, spaceGuid, logger) {
|
|
200
|
+
const excludeServices = new Set([...initialServices, 'portal', 'html5-apps-repo']);
|
|
201
|
+
const xsSecurityPath = path.join(projectPath, 'xs-security.json');
|
|
202
|
+
const xsSecurityProjectName = (0, project_1.getProjectNameForXsSecurity)(yamlContent, timestamp);
|
|
203
|
+
for (const resource of yamlContent.resources ?? []) {
|
|
204
|
+
if (!excludeServices.has(resource?.parameters?.service ?? '')) {
|
|
205
|
+
if (resource?.parameters?.service === 'xsuaa') {
|
|
206
|
+
await createService(spaceGuid, resource.parameters['service-plan'] ?? '', resource.parameters['service-name'] ?? '', [], resource.parameters.service, { filePath: xsSecurityPath, xsappname: xsSecurityProjectName }, logger);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
await createService(spaceGuid, resource.parameters['service-plan'] ?? '', resource.parameters['service-name'] ?? '', [], resource.parameters.service, { filePath: null, xsappname: xsSecurityProjectName }, logger);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Gets the service instance keys.
|
|
216
|
+
*
|
|
217
|
+
* @param {GetServiceInstanceParams} serviceInstanceQuery - The service instance query.
|
|
218
|
+
* @param {ToolsLogger} logger - The logger.
|
|
219
|
+
* @returns {Promise<ServiceKeys | null>} The service instance keys.
|
|
220
|
+
*/
|
|
221
|
+
async function getServiceInstanceKeys(serviceInstanceQuery, logger) {
|
|
222
|
+
try {
|
|
223
|
+
const serviceInstances = await getServiceInstance(serviceInstanceQuery);
|
|
224
|
+
if (serviceInstances?.length > 0) {
|
|
225
|
+
// We can use any instance in the list to connect to HTML5 Repo
|
|
226
|
+
logger?.log(`Use '${serviceInstances[0].name}' HTML5 Repo instance`);
|
|
227
|
+
return {
|
|
228
|
+
credentials: await getOrCreateServiceKeys(serviceInstances[0], logger),
|
|
229
|
+
serviceInstance: serviceInstances[0]
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
catch (e) {
|
|
235
|
+
const errorMessage = (0, i18n_1.t)('error.failedToGetServiceInstanceKeys', { error: e.message });
|
|
236
|
+
logger?.error(errorMessage);
|
|
237
|
+
throw new Error(errorMessage);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Gets the service instance.
|
|
242
|
+
*
|
|
243
|
+
* @param {GetServiceInstanceParams} params - The service instance parameters.
|
|
244
|
+
* @returns {Promise<ServiceInstance[]>} The service instance.
|
|
245
|
+
*/
|
|
246
|
+
async function getServiceInstance(params) {
|
|
247
|
+
const parameters = Object.entries(params)
|
|
248
|
+
.filter(([_, value]) => value?.length > 0)
|
|
249
|
+
.map(([key, value]) => `${PARAM_MAP.get(key)}=${value.join(',')}`);
|
|
250
|
+
const uriParameters = parameters.length > 0 ? `?${parameters.join('&')}` : '';
|
|
251
|
+
const uri = `/v3/service_instances` + uriParameters;
|
|
252
|
+
try {
|
|
253
|
+
const json = await (0, cli_1.requestCfApi)(uri);
|
|
254
|
+
if (!json?.resources || !Array.isArray(json.resources)) {
|
|
255
|
+
throw new Error((0, i18n_1.t)('error.noValidJsonForServiceInstance'));
|
|
256
|
+
}
|
|
257
|
+
return json.resources.map((service) => ({
|
|
258
|
+
name: service.name,
|
|
259
|
+
guid: service.guid
|
|
260
|
+
}));
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
throw new Error((0, i18n_1.t)('error.failedToGetServiceInstance', { uriParameters, error: e.message }));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Gets the service instance keys.
|
|
268
|
+
*
|
|
269
|
+
* @param {ServiceInstance} serviceInstance - The service instance.
|
|
270
|
+
* @param {ToolsLogger} logger - The logger.
|
|
271
|
+
* @returns {Promise<ServiceKeys | null>} The service instance keys.
|
|
272
|
+
*/
|
|
273
|
+
async function getOrCreateServiceKeys(serviceInstance, logger) {
|
|
274
|
+
const serviceInstanceName = serviceInstance.name;
|
|
275
|
+
try {
|
|
276
|
+
const credentials = await (0, cli_1.getServiceKeys)(serviceInstance.guid);
|
|
277
|
+
if (credentials?.length > 0) {
|
|
278
|
+
return credentials;
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
const serviceKeyName = serviceInstanceName + '_key';
|
|
282
|
+
logger?.log(`Creating service key '${serviceKeyName}' for service instance '${serviceInstanceName}'`);
|
|
283
|
+
await (0, cli_1.createServiceKey)(serviceInstanceName, serviceKeyName);
|
|
284
|
+
return (0, cli_1.getServiceKeys)(serviceInstance.guid);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
catch (e) {
|
|
288
|
+
throw new Error((0, i18n_1.t)('error.failedToGetOrCreateServiceKeys', { serviceInstanceName, error: e.message }));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ToolsLogger } from '@sap-ux/logger';
|
|
2
|
+
import type { CfCredentials } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Checks if Cloud Foundry is installed.
|
|
5
|
+
*
|
|
6
|
+
* @param {ToolsLogger} logger - The logger.
|
|
7
|
+
* @returns {Promise<boolean>} True if CF is installed, false otherwise.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isCfInstalled(logger: ToolsLogger): Promise<boolean>;
|
|
10
|
+
/**
|
|
11
|
+
* Gets the service instance credentials.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} serviceInstanceGuid - The service instance GUID.
|
|
14
|
+
* @returns {Promise<CfCredentials[]>} The service instance credentials.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getServiceKeys(serviceInstanceGuid: string): Promise<CfCredentials[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a service key.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} serviceInstanceName - The service instance name.
|
|
21
|
+
* @param {string} serviceKeyName - The service key name.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createServiceKey(serviceInstanceName: string, serviceKeyName: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Request CF API.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} url - The URL.
|
|
28
|
+
* @returns {Promise<T>} The response.
|
|
29
|
+
*/
|
|
30
|
+
export declare function requestCfApi<T = unknown>(url: string): Promise<T>;
|
|
31
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isCfInstalled = isCfInstalled;
|
|
4
|
+
exports.getServiceKeys = getServiceKeys;
|
|
5
|
+
exports.createServiceKey = createServiceKey;
|
|
6
|
+
exports.requestCfApi = requestCfApi;
|
|
7
|
+
const CFLocal = require("@sap/cf-tools/out/src/cf-local");
|
|
8
|
+
const CFToolsCli = require("@sap/cf-tools/out/src/cli");
|
|
9
|
+
const types_1 = require("@sap/cf-tools/out/src/types");
|
|
10
|
+
const i18n_1 = require("../../i18n");
|
|
11
|
+
const ENV = { env: { 'CF_COLOR': 'false' } };
|
|
12
|
+
/**
|
|
13
|
+
* Checks if Cloud Foundry is installed.
|
|
14
|
+
*
|
|
15
|
+
* @param {ToolsLogger} logger - The logger.
|
|
16
|
+
* @returns {Promise<boolean>} True if CF is installed, false otherwise.
|
|
17
|
+
*/
|
|
18
|
+
async function isCfInstalled(logger) {
|
|
19
|
+
try {
|
|
20
|
+
const response = await CFToolsCli.Cli.execute(['version'], ENV);
|
|
21
|
+
if (response.exitCode !== 0) {
|
|
22
|
+
throw new Error(response.stderr);
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
logger.error((0, i18n_1.t)('error.cfNotInstalled', { error: e.message }));
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Gets the service instance credentials.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} serviceInstanceGuid - The service instance GUID.
|
|
35
|
+
* @returns {Promise<CfCredentials[]>} The service instance credentials.
|
|
36
|
+
*/
|
|
37
|
+
async function getServiceKeys(serviceInstanceGuid) {
|
|
38
|
+
try {
|
|
39
|
+
return await CFLocal.cfGetInstanceCredentials({
|
|
40
|
+
filters: [
|
|
41
|
+
{
|
|
42
|
+
value: serviceInstanceGuid,
|
|
43
|
+
// key: eFilters.service_instance_guid
|
|
44
|
+
key: types_1.eFilters.service_instance_guid
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
throw new Error((0, i18n_1.t)('error.cfGetInstanceCredentialsFailed', { serviceInstanceGuid, error: e.message }));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Creates a service key.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} serviceInstanceName - The service instance name.
|
|
57
|
+
* @param {string} serviceKeyName - The service key name.
|
|
58
|
+
*/
|
|
59
|
+
async function createServiceKey(serviceInstanceName, serviceKeyName) {
|
|
60
|
+
try {
|
|
61
|
+
const cliResult = await CFToolsCli.Cli.execute(['create-service-key', serviceInstanceName, serviceKeyName], ENV);
|
|
62
|
+
if (cliResult.exitCode !== 0) {
|
|
63
|
+
throw new Error(cliResult.stderr);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
throw new Error((0, i18n_1.t)('error.createServiceKeyFailed', { serviceInstanceName, error: e.message }));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Request CF API.
|
|
72
|
+
*
|
|
73
|
+
* @param {string} url - The URL.
|
|
74
|
+
* @returns {Promise<T>} The response.
|
|
75
|
+
*/
|
|
76
|
+
async function requestCfApi(url) {
|
|
77
|
+
try {
|
|
78
|
+
const response = await CFToolsCli.Cli.execute(['curl', url], ENV);
|
|
79
|
+
if (response.exitCode === 0) {
|
|
80
|
+
try {
|
|
81
|
+
return JSON.parse(response.stdout);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
throw new Error((0, i18n_1.t)('error.failedToParseCFAPIResponse', { error: e.message }));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
throw new Error(response.stderr);
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
throw new Error((0, i18n_1.t)('error.failedToRequestCFAPI', { error: e.message }));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./api"), exports);
|
|
18
|
+
__exportStar(require("./cli"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./validation"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type AdmZip from 'adm-zip';
|
|
2
|
+
import type { ToolsLogger } from '@sap-ux/logger';
|
|
3
|
+
import type { Manifest } from '@sap-ux/project-access';
|
|
4
|
+
import type { CfCredentials, XsApp } from '../../types';
|
|
5
|
+
/**
|
|
6
|
+
* Validate the smart template application.
|
|
7
|
+
*
|
|
8
|
+
* @param {Manifest} manifest - The manifest.
|
|
9
|
+
* @returns {Promise<void>} The messages.
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateSmartTemplateApplication(manifest: Manifest): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Generic function to extract and parse JSON from zip entries.
|
|
14
|
+
*
|
|
15
|
+
* @param {AdmZip.IZipEntry[]} zipEntries - The zip entries.
|
|
16
|
+
* @param {string} fileName - The file name to find (e.g., 'manifest.json', 'xs-app.json').
|
|
17
|
+
* @param {string} errorKey - The i18n error key for parsing failures.
|
|
18
|
+
* @returns {T | undefined} The parsed JSON object.
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractJsonFromZip<T>(zipEntries: AdmZip.IZipEntry[], fileName: string, errorKey: string): T | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Extract the xs-app.json from the zip entries.
|
|
23
|
+
*
|
|
24
|
+
* @param {AdmZip.IZipEntry[]} zipEntries - The zip entries.
|
|
25
|
+
* @returns {XsApp | undefined} The xs-app.json.
|
|
26
|
+
*/
|
|
27
|
+
export declare function extractXSApp(zipEntries: AdmZip.IZipEntry[]): XsApp | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Validate the OData endpoints, data sources and routes.
|
|
30
|
+
*
|
|
31
|
+
* @param {AdmZip.IZipEntry[]} zipEntries - The zip entries.
|
|
32
|
+
* @param {CfCredentials[]} credentials - The credentials.
|
|
33
|
+
* @param {ToolsLogger} logger - The logger.
|
|
34
|
+
* @returns {Promise<string[]>} The messages.
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateODataEndpoints(zipEntries: AdmZip.IZipEntry[], credentials: CfCredentials[], logger: ToolsLogger): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateSmartTemplateApplication = validateSmartTemplateApplication;
|
|
4
|
+
exports.extractJsonFromZip = extractJsonFromZip;
|
|
5
|
+
exports.extractXSApp = extractXSApp;
|
|
6
|
+
exports.validateODataEndpoints = validateODataEndpoints;
|
|
7
|
+
const i18n_1 = require("../../i18n");
|
|
8
|
+
const manifest_1 = require("../../source/manifest");
|
|
9
|
+
/**
|
|
10
|
+
* Normalize the xs-app route regex.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} value - The value.
|
|
13
|
+
* @returns {RegExp} The normalized route regex.
|
|
14
|
+
*/
|
|
15
|
+
function normalizeRouteRegex(value) {
|
|
16
|
+
return new RegExp(value.replace('^/', '^(/)*').replace('/(.*)$', '(/)*(.*)$'));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Validate the smart template application.
|
|
20
|
+
*
|
|
21
|
+
* @param {Manifest} manifest - The manifest.
|
|
22
|
+
* @returns {Promise<void>} The messages.
|
|
23
|
+
*/
|
|
24
|
+
async function validateSmartTemplateApplication(manifest) {
|
|
25
|
+
const appType = (0, manifest_1.getApplicationType)(manifest);
|
|
26
|
+
if (!(0, manifest_1.isSupportedAppTypeForAdp)(appType)) {
|
|
27
|
+
throw new Error((0, i18n_1.t)('error.adpDoesNotSupportSelectedApplication'));
|
|
28
|
+
}
|
|
29
|
+
if (manifest['sap.ui5'] && manifest['sap.ui5'].flexEnabled === false) {
|
|
30
|
+
throw new Error((0, i18n_1.t)('error.appDoesNotSupportFlexibility'));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generic function to extract and parse JSON from zip entries.
|
|
35
|
+
*
|
|
36
|
+
* @param {AdmZip.IZipEntry[]} zipEntries - The zip entries.
|
|
37
|
+
* @param {string} fileName - The file name to find (e.g., 'manifest.json', 'xs-app.json').
|
|
38
|
+
* @param {string} errorKey - The i18n error key for parsing failures.
|
|
39
|
+
* @returns {T | undefined} The parsed JSON object.
|
|
40
|
+
*/
|
|
41
|
+
function extractJsonFromZip(zipEntries, fileName, errorKey) {
|
|
42
|
+
const entry = zipEntries.find((item) => item.entryName.endsWith(fileName));
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(entry?.getData().toString('utf8') ?? '');
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
throw new Error((0, i18n_1.t)(errorKey, { error: e.message }));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Extract the xs-app.json from the zip entries.
|
|
52
|
+
*
|
|
53
|
+
* @param {AdmZip.IZipEntry[]} zipEntries - The zip entries.
|
|
54
|
+
* @returns {XsApp | undefined} The xs-app.json.
|
|
55
|
+
*/
|
|
56
|
+
function extractXSApp(zipEntries) {
|
|
57
|
+
return extractJsonFromZip(zipEntries, 'xs-app.json', 'error.failedToParseXsAppJson');
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extract the manifest.json from the zip entries.
|
|
61
|
+
*
|
|
62
|
+
* @param {AdmZip.IZipEntry[]} zipEntries - The zip entries.
|
|
63
|
+
* @returns {Manifest | undefined} The manifest.
|
|
64
|
+
*/
|
|
65
|
+
function extractManifest(zipEntries) {
|
|
66
|
+
return extractJsonFromZip(zipEntries, 'manifest.json', 'error.failedToParseManifestJson');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Match the routes and data sources.
|
|
70
|
+
*
|
|
71
|
+
* @param {Record<string, ManifestNamespace.DataSource>} dataSources - The data sources from manifest.json.
|
|
72
|
+
* @param {XsAppRoute[]} routes - The routes from xs-app.json.
|
|
73
|
+
* @param {string[]} serviceKeyEndpoints - The service key endpoints.
|
|
74
|
+
* @returns {string[]} The messages.
|
|
75
|
+
*/
|
|
76
|
+
function matchRoutesAndDatasources(dataSources, routes, serviceKeyEndpoints) {
|
|
77
|
+
const messages = [];
|
|
78
|
+
for (const route of routes) {
|
|
79
|
+
if (route.endpoint && !serviceKeyEndpoints.includes(route.endpoint)) {
|
|
80
|
+
messages.push(`Route endpoint '${route.endpoint}' doesn't match a corresponding OData endpoint`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const dataSourceName of Object.keys(dataSources ?? {})) {
|
|
84
|
+
if (!routes.some((route) => dataSources?.[dataSourceName].uri?.match(normalizeRouteRegex(route.source)))) {
|
|
85
|
+
messages.push(`Data source '${dataSourceName}' doesn't match a corresponding route in xs-app.json routes`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return messages;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Validate the OData endpoints, data sources and routes.
|
|
92
|
+
*
|
|
93
|
+
* @param {AdmZip.IZipEntry[]} zipEntries - The zip entries.
|
|
94
|
+
* @param {CfCredentials[]} credentials - The credentials.
|
|
95
|
+
* @param {ToolsLogger} logger - The logger.
|
|
96
|
+
* @returns {Promise<string[]>} The messages.
|
|
97
|
+
*/
|
|
98
|
+
async function validateODataEndpoints(zipEntries, credentials, logger) {
|
|
99
|
+
const messages = [];
|
|
100
|
+
let xsApp;
|
|
101
|
+
try {
|
|
102
|
+
xsApp = extractXSApp(zipEntries);
|
|
103
|
+
logger?.log(`ODATA endpoints: ${JSON.stringify(xsApp)}`);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
messages.push(error);
|
|
107
|
+
}
|
|
108
|
+
let manifest;
|
|
109
|
+
try {
|
|
110
|
+
manifest = extractManifest(zipEntries);
|
|
111
|
+
logger?.log(`Extracted manifest: ${JSON.stringify(manifest)}`);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
messages.push(error);
|
|
115
|
+
}
|
|
116
|
+
const dataSources = manifest?.['sap.app']?.dataSources;
|
|
117
|
+
const routes = xsApp?.routes;
|
|
118
|
+
if (dataSources && routes) {
|
|
119
|
+
const serviceKeyEndpoints = [].concat(...credentials.map((item) => (item.endpoints ? Object.keys(item.endpoints) : [])));
|
|
120
|
+
messages.push(...matchRoutesAndDatasources(dataSources, routes, serviceKeyEndpoints));
|
|
121
|
+
}
|
|
122
|
+
else if (routes && !dataSources) {
|
|
123
|
+
messages.push("Base app manifest.json doesn't contain data sources specified in xs-app.json");
|
|
124
|
+
}
|
|
125
|
+
else if (!routes && dataSources) {
|
|
126
|
+
messages.push("Base app xs-app.json doesn't contain data sources routes specified in manifest.json");
|
|
127
|
+
}
|
|
128
|
+
if (messages.length > 0) {
|
|
129
|
+
const errorMessages = messages.join('\n');
|
|
130
|
+
logger?.error(`OData endpoints validation failed:\n${errorMessages}`);
|
|
131
|
+
throw new Error((0, i18n_1.t)('error.oDataEndpointsValidationFailed'));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=validation.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -5,12 +5,14 @@ export * from './abap';
|
|
|
5
5
|
export * from './source';
|
|
6
6
|
export * from './ui5';
|
|
7
7
|
export * from './base/cf';
|
|
8
|
+
export * from './cf';
|
|
8
9
|
export * from './base/helper';
|
|
9
10
|
export * from './base/constants';
|
|
10
11
|
export * from './base/project-builder';
|
|
11
12
|
export * from './base/abap/manifest-service';
|
|
12
13
|
export { promptGeneratorInput, PromptDefaults } from './base/prompt';
|
|
13
14
|
export * from './preview/adp-preview';
|
|
15
|
+
export * from './writer/cf';
|
|
14
16
|
export * from './writer/manifest';
|
|
15
17
|
export * from './writer/writer-config';
|
|
16
18
|
export { generate, migrate } from './writer';
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ __exportStar(require("./abap"), exports);
|
|
|
22
22
|
__exportStar(require("./source"), exports);
|
|
23
23
|
__exportStar(require("./ui5"), exports);
|
|
24
24
|
__exportStar(require("./base/cf"), exports);
|
|
25
|
+
__exportStar(require("./cf"), exports);
|
|
25
26
|
__exportStar(require("./base/helper"), exports);
|
|
26
27
|
__exportStar(require("./base/constants"), exports);
|
|
27
28
|
__exportStar(require("./base/project-builder"), exports);
|
|
@@ -29,6 +30,7 @@ __exportStar(require("./base/abap/manifest-service"), exports);
|
|
|
29
30
|
var prompt_1 = require("./base/prompt");
|
|
30
31
|
Object.defineProperty(exports, "promptGeneratorInput", { enumerable: true, get: function () { return prompt_1.promptGeneratorInput; } });
|
|
31
32
|
__exportStar(require("./preview/adp-preview"), exports);
|
|
33
|
+
__exportStar(require("./writer/cf"), exports);
|
|
32
34
|
__exportStar(require("./writer/manifest"), exports);
|
|
33
35
|
__exportStar(require("./writer/writer-config"), exports);
|
|
34
36
|
var writer_1 = require("./writer");
|