@sap-ux/repo-app-import-sub-generator 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +16 -0
- package/generators/app/app-config.d.ts +25 -0
- package/generators/app/app-config.js +122 -0
- package/generators/app/index.d.ts +72 -0
- package/generators/app/index.js +291 -0
- package/generators/app/types.d.ts +136 -0
- package/generators/app/types.js +13 -0
- package/generators/prompts/prompt-helpers.d.ts +40 -0
- package/generators/prompts/prompt-helpers.js +108 -0
- package/generators/prompts/prompt-state.d.ts +48 -0
- package/generators/prompts/prompt-state.js +65 -0
- package/generators/prompts/prompts.d.ts +10 -0
- package/generators/prompts/prompts.js +111 -0
- package/generators/telemetryEvents/index.d.ts +7 -0
- package/generators/telemetryEvents/index.js +11 -0
- package/generators/translations/repo-app-import-sub-generator.i18n.json +67 -0
- package/generators/utils/constants.d.ts +14 -0
- package/generators/utils/constants.js +44 -0
- package/generators/utils/download-utils.d.ts +17 -0
- package/generators/utils/download-utils.js +48 -0
- package/generators/utils/event-hook.d.ts +18 -0
- package/generators/utils/event-hook.js +34 -0
- package/generators/utils/file-helpers.d.ts +19 -0
- package/generators/utils/file-helpers.js +52 -0
- package/generators/utils/i18n.d.ts +14 -0
- package/generators/utils/i18n.js +34 -0
- package/generators/utils/logger.d.ts +33 -0
- package/generators/utils/logger.js +41 -0
- package/generators/utils/updates.d.ts +23 -0
- package/generators/utils/updates.js +90 -0
- package/generators/utils/validators.d.ts +17 -0
- package/generators/utils/validators.js +94 -0
- package/package.json +84 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PromptNames = void 0;
|
|
7
|
+
const yeoman_generator_1 = __importDefault(require("yeoman-generator"));
|
|
8
|
+
const logger_1 = __importDefault(require("../utils/logger"));
|
|
9
|
+
const yeoman_ui_types_1 = require("@sap-devx/yeoman-ui-types");
|
|
10
|
+
const feature_toggle_1 = require("@sap-ux/feature-toggle");
|
|
11
|
+
const constants_1 = require("../utils/constants");
|
|
12
|
+
const i18n_1 = require("../utils/i18n");
|
|
13
|
+
const download_utils_1 = require("../utils/download-utils");
|
|
14
|
+
const telemetryEvents_1 = require("../telemetryEvents");
|
|
15
|
+
const fiori_generator_shared_1 = require("@sap-ux/fiori-generator-shared");
|
|
16
|
+
const prompts_1 = require("../prompts/prompts");
|
|
17
|
+
const fiori_elements_writer_1 = require("@sap-ux/fiori-elements-writer");
|
|
18
|
+
const path_1 = require("path");
|
|
19
|
+
const os_1 = require("os");
|
|
20
|
+
const event_hook_1 = require("../utils/event-hook");
|
|
21
|
+
const ui5_info_1 = require("@sap-ux/ui5-info");
|
|
22
|
+
const launch_config_1 = require("@sap-ux/launch-config");
|
|
23
|
+
const btp_utils_1 = require("@sap-ux/btp-utils");
|
|
24
|
+
const odata_service_inquirer_1 = require("@sap-ux/odata-service-inquirer");
|
|
25
|
+
const fiori_tools_settings_1 = require("@sap-ux/fiori-tools-settings");
|
|
26
|
+
const abap_deploy_config_writer_1 = require("@sap-ux/abap-deploy-config-writer");
|
|
27
|
+
const prompt_state_1 = require("../prompts/prompt-state");
|
|
28
|
+
const types_1 = require("./types");
|
|
29
|
+
Object.defineProperty(exports, "PromptNames", { enumerable: true, get: function () { return types_1.PromptNames; } });
|
|
30
|
+
const app_config_1 = require("./app-config");
|
|
31
|
+
const file_helpers_1 = require("../utils/file-helpers");
|
|
32
|
+
const updates_1 = require("../utils/updates");
|
|
33
|
+
const prompt_helpers_1 = require("../prompts/prompt-helpers");
|
|
34
|
+
const validators_1 = require("../utils/validators");
|
|
35
|
+
const project_access_1 = require("@sap-ux/project-access");
|
|
36
|
+
/**
|
|
37
|
+
* Generator class for downloading a basic app from a repository.
|
|
38
|
+
* This class handles the process of app selection, downloading the app and generating a fiori app from the downloaded app
|
|
39
|
+
*/
|
|
40
|
+
class default_1 extends yeoman_generator_1.default {
|
|
41
|
+
appWizard;
|
|
42
|
+
vscode;
|
|
43
|
+
appRootPath;
|
|
44
|
+
prompts;
|
|
45
|
+
answers = constants_1.defaultAnswers;
|
|
46
|
+
options;
|
|
47
|
+
projectPath;
|
|
48
|
+
extractedProjectPath;
|
|
49
|
+
setPromptsCallback;
|
|
50
|
+
/**
|
|
51
|
+
* Constructor for Downloading App.
|
|
52
|
+
*
|
|
53
|
+
* @param args - arguments passed to the generator
|
|
54
|
+
* @param opts - options passed to the generator
|
|
55
|
+
*/
|
|
56
|
+
constructor(args, opts) {
|
|
57
|
+
super(args, opts);
|
|
58
|
+
// Initialise properties from options
|
|
59
|
+
this.appWizard = opts.appWizard ?? yeoman_ui_types_1.AppWizard.create(opts);
|
|
60
|
+
this.vscode = opts.vscode;
|
|
61
|
+
this.appRootPath = opts?.appRootPath ?? (0, fiori_generator_shared_1.getDefaultTargetFolder)(this.vscode) ?? this.destinationRoot();
|
|
62
|
+
this.options = opts;
|
|
63
|
+
// Configure logging
|
|
64
|
+
logger_1.default.configureLogging(this.rootGeneratorName(), this.log, this.options.logWrapper, this.options.logLevel, this.options.logger, this.vscode);
|
|
65
|
+
// Initialise prompts and callbacks if not launched as a subgenerator
|
|
66
|
+
this.appWizard.setHeaderTitle(constants_1.generatorTitle);
|
|
67
|
+
this.prompts = new yeoman_ui_types_1.Prompts((0, prompt_helpers_1.getYUIDetails)());
|
|
68
|
+
this.setPromptsCallback = (fn) => {
|
|
69
|
+
if (this.prompts) {
|
|
70
|
+
this.prompts.setCallback(fn);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Initialises necessary settings and telemetry for the generator.
|
|
76
|
+
*/
|
|
77
|
+
async initializing() {
|
|
78
|
+
if (this.env.conflicter) {
|
|
79
|
+
this.env.conflicter.force = this.options.force ?? true;
|
|
80
|
+
}
|
|
81
|
+
// Initialise telemetry settings
|
|
82
|
+
await fiori_generator_shared_1.TelemetryHelper.initTelemetrySettings({
|
|
83
|
+
consumerModule: {
|
|
84
|
+
name: constants_1.generatorName,
|
|
85
|
+
version: this.rootGeneratorVersion()
|
|
86
|
+
},
|
|
87
|
+
internalFeature: (0, feature_toggle_1.isInternalFeaturesSettingEnabled)(),
|
|
88
|
+
watchTelemetrySettingStore: false
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Prompts the user for application details and downloads the app.
|
|
93
|
+
*/
|
|
94
|
+
async prompting() {
|
|
95
|
+
const quickDeployedAppConfig = this.options?.data?.quickDeployedAppConfig;
|
|
96
|
+
const questions = await (0, prompts_1.getPrompts)(this.appRootPath, quickDeployedAppConfig);
|
|
97
|
+
const answers = await this.prompt(questions);
|
|
98
|
+
const { targetFolder } = answers;
|
|
99
|
+
if (quickDeployedAppConfig?.appId) {
|
|
100
|
+
// Handle quick deployed app download where prompts for system selection and app selection are not displayed
|
|
101
|
+
// Only target folder prompt is shown
|
|
102
|
+
this.answers.targetFolder = targetFolder;
|
|
103
|
+
this.answers.systemSelection = prompt_state_1.PromptState.systemSelection;
|
|
104
|
+
this.answers.selectedApp = answers.selectedApp;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// Handle app download where prompts for system selection and app selection are shown
|
|
108
|
+
Object.assign(this.answers, answers);
|
|
109
|
+
}
|
|
110
|
+
if ((0, validators_1.isValidPromptState)(this.answers.targetFolder, this.answers.selectedApp.appId)) {
|
|
111
|
+
this.projectPath = (0, path_1.join)(this.answers.targetFolder, this.answers.selectedApp.appId);
|
|
112
|
+
this.extractedProjectPath = (0, path_1.join)(this.projectPath, constants_1.extractedFilePath);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Writes the configuration files for the project, including deployment config, and README.
|
|
117
|
+
*/
|
|
118
|
+
async writing() {
|
|
119
|
+
// Extract downloaded app
|
|
120
|
+
const archive = prompt_state_1.PromptState.downloadedAppPackage;
|
|
121
|
+
await (0, download_utils_1.extractZip)(this.extractedProjectPath, archive, this.fs);
|
|
122
|
+
// Check if the qfa.json file
|
|
123
|
+
const qfaJsonFilePath = (0, path_1.join)(this.extractedProjectPath, constants_1.qfaJsonFileName);
|
|
124
|
+
if (this.fs.exists(qfaJsonFilePath)) {
|
|
125
|
+
const qfaJson = (0, file_helpers_1.makeValidJson)(qfaJsonFilePath, this.fs);
|
|
126
|
+
// Generate project files
|
|
127
|
+
(0, validators_1.validateQfaJsonFile)(qfaJson);
|
|
128
|
+
// Generate app config
|
|
129
|
+
const config = await (0, app_config_1.getAppConfig)(this.answers.selectedApp, this.extractedProjectPath, qfaJson, this.fs);
|
|
130
|
+
await (0, fiori_elements_writer_1.generate)(this.projectPath, config, this.fs);
|
|
131
|
+
// Generate deploy config
|
|
132
|
+
const deployConfig = (0, app_config_1.getAbapDeployConfig)(this.answers.selectedApp, qfaJson);
|
|
133
|
+
await (0, abap_deploy_config_writer_1.generate)(this.projectPath, deployConfig, undefined, this.fs);
|
|
134
|
+
if (this.vscode) {
|
|
135
|
+
// Generate Fiori launch config
|
|
136
|
+
const fioriOptions = this._getLaunchConfig(config);
|
|
137
|
+
// Create launch configuration
|
|
138
|
+
await (0, launch_config_1.createLaunchConfig)(this.projectPath, fioriOptions, this.fs, logger_1.default.logger);
|
|
139
|
+
(0, fiori_tools_settings_1.writeApplicationInfoSettings)(this.projectPath);
|
|
140
|
+
}
|
|
141
|
+
// Generate README
|
|
142
|
+
const readMeConfig = this._getReadMeConfig(config);
|
|
143
|
+
(0, fiori_generator_shared_1.generateReadMe)(this.projectPath, readMeConfig, this.fs);
|
|
144
|
+
// Replace webapp files with downloaded app files
|
|
145
|
+
await (0, updates_1.replaceWebappFiles)(this.projectPath, this.extractedProjectPath, this.fs);
|
|
146
|
+
await (0, updates_1.validateAndUpdateManifestUI5Version)((0, path_1.join)(this.projectPath, project_access_1.DirName.Webapp, project_access_1.FileName.Manifest), this.fs);
|
|
147
|
+
// Clean up extracted project files
|
|
148
|
+
this.fs.delete(this.extractedProjectPath);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
logger_1.default.logger?.error((0, i18n_1.t)('error.qfaJsonNotFound', { jsonFileName: constants_1.qfaJsonFileName }));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Returns the configuration for the README file.
|
|
156
|
+
*
|
|
157
|
+
* @param config - The app configuration object.
|
|
158
|
+
* @returns {ReadMe} The configuration for generating the README.
|
|
159
|
+
*/
|
|
160
|
+
_getReadMeConfig(config) {
|
|
161
|
+
const readMeConfig = {
|
|
162
|
+
appName: config.app.id,
|
|
163
|
+
appTitle: config.app.title ?? '',
|
|
164
|
+
appNamespace: config.app.id.substring(0, config.app.id.lastIndexOf('.')),
|
|
165
|
+
appDescription: (0, i18n_1.t)('readMe.appDescription'),
|
|
166
|
+
ui5Theme: (0, ui5_info_1.getDefaultUI5Theme)(config.ui5?.version),
|
|
167
|
+
generatorName: constants_1.generatorName,
|
|
168
|
+
generatorVersion: this.rootGeneratorVersion(),
|
|
169
|
+
ui5Version: config.ui5?.version ?? '',
|
|
170
|
+
template: fiori_elements_writer_1.TemplateType.ListReportObjectPage,
|
|
171
|
+
serviceUrl: config.service.url,
|
|
172
|
+
launchText: (0, i18n_1.t)('readMe.launchText')
|
|
173
|
+
};
|
|
174
|
+
return readMeConfig;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Returns the configuration for launching the app with Fiori options.
|
|
178
|
+
*
|
|
179
|
+
* @param config - The app configuration object.
|
|
180
|
+
* @returns {FioriOptions} The launch configuration options.
|
|
181
|
+
*/
|
|
182
|
+
_getLaunchConfig(config) {
|
|
183
|
+
const debugOptions = {
|
|
184
|
+
vscode: this.vscode,
|
|
185
|
+
addStartCmd: true,
|
|
186
|
+
sapClientParam: prompt_state_1.PromptState.sapClient,
|
|
187
|
+
flpAppId: config.app.flpAppId ?? config.app.id,
|
|
188
|
+
flpSandboxAvailable: true,
|
|
189
|
+
isAppStudio: (0, btp_utils_1.isAppStudio)(),
|
|
190
|
+
odataVersion: config.service.version === odata_service_inquirer_1.OdataVersion.v2 ? '2.0' : '4.0'
|
|
191
|
+
};
|
|
192
|
+
const fioriOptions = {
|
|
193
|
+
name: config.app.id,
|
|
194
|
+
projectRoot: this.projectPath,
|
|
195
|
+
/**
|
|
196
|
+
* The `enableVSCodeReload` property is set to `false` to prevent automatic reloading of the VS Code workspace
|
|
197
|
+
* after the app generation process. This is necessary to ensure that the `.vscode/launch-config.json` file is
|
|
198
|
+
* written to disk before the workspace reload occurs. See {@link _handlePostAppGeneration} for details.
|
|
199
|
+
*/
|
|
200
|
+
enableVSCodeReload: false,
|
|
201
|
+
debugOptions
|
|
202
|
+
};
|
|
203
|
+
return fioriOptions;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Installs npm dependencies for the project.
|
|
207
|
+
*/
|
|
208
|
+
async install() {
|
|
209
|
+
if (!this.options.skipInstall) {
|
|
210
|
+
try {
|
|
211
|
+
await this._runNpmInstall(this.projectPath);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
logger_1.default.logger?.error((0, i18n_1.t)('error.installationErrors.npmInstall', { error }));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
logger_1.default.logger?.info((0, i18n_1.t)('info.installationErrors.skippedInstallation'));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Runs npm install in the specified path.
|
|
223
|
+
*
|
|
224
|
+
* @param path - The path to run npm install.
|
|
225
|
+
*/
|
|
226
|
+
async _runNpmInstall(path) {
|
|
227
|
+
const npm = (0, os_1.platform)() === 'win32' ? 'npm.cmd' : 'npm';
|
|
228
|
+
// install dependencies
|
|
229
|
+
await this.spawnCommand(npm, ['install', '--no-audit', '--no-fund', '--silent', '--prefer-offline', '--no-progress'], {
|
|
230
|
+
cwd: path
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Responsible for updating workspace folders and running post-generation commands if defined.
|
|
235
|
+
*/
|
|
236
|
+
async _handlePostAppGeneration() {
|
|
237
|
+
/**
|
|
238
|
+
* `enableVSCodeReload` is set to false when generating launch config here {@link _getLaunchConfig}.
|
|
239
|
+
* This prevents issues where the `.vscode/launch-config.json` file may not be written to disk due to the timing of mem-fs commits.
|
|
240
|
+
*
|
|
241
|
+
* In Yeoman, a commit occurs between the writing phase and the end phase. If no workspace is open in VS Code and the generated
|
|
242
|
+
* app is added to the workspace, VS Code automatically reloads the window. However, by this point in the end phase, the in-memory file system
|
|
243
|
+
* (mem-fs) has written all files except for `.vscode/launch-config.json`, because the commit happens before the end phase
|
|
244
|
+
* causing it to be missed when the workspace reload occurs.
|
|
245
|
+
*
|
|
246
|
+
* Workflow:
|
|
247
|
+
* 1. **Workspace URI**: The `updateWorkspaceFolders` object is created with the project folder's path, the project name,
|
|
248
|
+
* and the VS Code instance to handle workspace folder updates.
|
|
249
|
+
* 2. **Update Workspace Folders**: The `updateWorkspaceFoldersIfNeeded` function is called to update the workspace folders,
|
|
250
|
+
* if necessary. See {@link updateWorkspaceFoldersIfNeeded} for details.
|
|
251
|
+
* 3. **Run Post-Generation Commands**: If defined, post-generation commands from `options.data?.postGenCommands` are executed
|
|
252
|
+
* using the `runPostAppGenHook` function. This allows for additional setup or configuration tasks to be performed after
|
|
253
|
+
* the app generation process.
|
|
254
|
+
*/
|
|
255
|
+
if (this.vscode) {
|
|
256
|
+
const updateWorkspaceFolders = {
|
|
257
|
+
uri: this.vscode?.Uri?.file((0, path_1.join)(this.projectPath)),
|
|
258
|
+
projectName: (0, path_1.basename)(this.projectPath),
|
|
259
|
+
vscode: this.vscode
|
|
260
|
+
};
|
|
261
|
+
(0, launch_config_1.updateWorkspaceFoldersIfNeeded)(updateWorkspaceFolders);
|
|
262
|
+
}
|
|
263
|
+
if (this.options.data?.postGenCommand) {
|
|
264
|
+
await (0, event_hook_1.runPostAppGenHook)({
|
|
265
|
+
path: this.projectPath,
|
|
266
|
+
vscodeInstance: this.vscode,
|
|
267
|
+
postGenCommand: this.options.data?.postGenCommand
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Finalises the generator process by creating launch configurations and running post-generation hooks.
|
|
273
|
+
*/
|
|
274
|
+
async end() {
|
|
275
|
+
try {
|
|
276
|
+
this.appWizard.showInformation((0, i18n_1.t)('info.repoAppDownloadCompleteMsg'), yeoman_ui_types_1.MessageType.notification);
|
|
277
|
+
await (0, fiori_generator_shared_1.sendTelemetry)(telemetryEvents_1.EventName.GENERATION_SUCCESS, fiori_generator_shared_1.TelemetryHelper.createTelemetryData({
|
|
278
|
+
appType: 'repo-app-import-sub-generator',
|
|
279
|
+
...this.options.telemetryData
|
|
280
|
+
}) ?? {}).catch((error) => {
|
|
281
|
+
logger_1.default.logger?.error((0, i18n_1.t)('error.telemetry', { error: error.message }));
|
|
282
|
+
});
|
|
283
|
+
await this._handlePostAppGeneration();
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
logger_1.default.logger?.error((0, i18n_1.t)('error.endPhase', { error: error.message }));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
exports.default = default_1;
|
|
291
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type Generator from 'yeoman-generator';
|
|
2
|
+
import type { AppWizard } from '@sap-devx/yeoman-ui-types';
|
|
3
|
+
import type { VSCodeInstance, TelemetryData, LogWrapper } from '@sap-ux/fiori-generator-shared';
|
|
4
|
+
import type { Destination } from '@sap-ux/btp-utils';
|
|
5
|
+
import type { BackendSystem } from '@sap-ux/store';
|
|
6
|
+
import type { AbapServiceProvider, AppIndex } from '@sap-ux/axios-extension';
|
|
7
|
+
import type { YUIQuestion } from '@sap-ux/inquirer-common';
|
|
8
|
+
import type { AutocompleteQuestionOptions } from 'inquirer-autocomplete-prompt';
|
|
9
|
+
/**
|
|
10
|
+
* Quick deploy app config are applicable only in scenarios where an application
|
|
11
|
+
* deployed via ADT Quick Deploy is being downloaded from a repository.
|
|
12
|
+
*/
|
|
13
|
+
export interface QuickDeployedAppConfig {
|
|
14
|
+
/** application Id to be downloaded. */
|
|
15
|
+
appId: string;
|
|
16
|
+
/** appUrl is the URL pointing to the application */
|
|
17
|
+
appUrl?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Information about the system from which the application is to be downloaded.
|
|
20
|
+
*/
|
|
21
|
+
serviceProviderInfo?: {
|
|
22
|
+
/**
|
|
23
|
+
* The base URL of the system providing the application.
|
|
24
|
+
*/
|
|
25
|
+
serviceUrl?: string;
|
|
26
|
+
/**
|
|
27
|
+
* The name of the system providing the application.
|
|
28
|
+
*/
|
|
29
|
+
name: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Options for downloading an application from repository.
|
|
34
|
+
*/
|
|
35
|
+
export interface RepoAppDownloadOptions extends Generator.GeneratorOptions {
|
|
36
|
+
/** VSCode instance for interacting with the VSCode environment. */
|
|
37
|
+
vscode?: VSCodeInstance;
|
|
38
|
+
/** The quick deploy config is provided only when an ADT quick deployed app is being downloaded */
|
|
39
|
+
quickDeployedAppConfig?: QuickDeployedAppConfig;
|
|
40
|
+
/** AppWizard instance for managing the application download flow. */
|
|
41
|
+
appWizard?: AppWizard;
|
|
42
|
+
/** Path to the application root where the Fiori launchpad configuration will be added. */
|
|
43
|
+
appRootPath?: string;
|
|
44
|
+
/** Telemetry data for tracking events post deployment configuration. */
|
|
45
|
+
telemetryData?: TelemetryData;
|
|
46
|
+
/** Logger instance for logging operations. */
|
|
47
|
+
logWrapper?: LogWrapper;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Answers related to system selection in the application download process.
|
|
51
|
+
*/
|
|
52
|
+
export interface SystemSelectionAnswers {
|
|
53
|
+
/**
|
|
54
|
+
* Details of the connected system allowing downstream consumers to access it without creating new connections.
|
|
55
|
+
*/
|
|
56
|
+
connectedSystem?: {
|
|
57
|
+
/** Service provider for the connected ABAP system. */
|
|
58
|
+
serviceProvider: AbapServiceProvider;
|
|
59
|
+
/**
|
|
60
|
+
* Persistable backend system representation of the connected service provider.
|
|
61
|
+
* `newOrUpdated` is true if the system was newly created or updated during connection validation.
|
|
62
|
+
*/
|
|
63
|
+
backendSystem?: BackendSystem & {
|
|
64
|
+
newOrUpdated?: boolean;
|
|
65
|
+
};
|
|
66
|
+
/** Destination details of the connected system. */
|
|
67
|
+
destination?: Destination;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Represents a question in the app download process.
|
|
72
|
+
* Extends `YUIQuestion` with optional autocomplete functionality.
|
|
73
|
+
*/
|
|
74
|
+
export type RepoAppDownloadQuestions = YUIQuestion<RepoAppDownloadAnswers> & Partial<Pick<AutocompleteQuestionOptions, 'source'>>;
|
|
75
|
+
export type AppItem = AppIndex extends (infer U)[] ? U : never;
|
|
76
|
+
export interface AppInfo {
|
|
77
|
+
appId: string;
|
|
78
|
+
title: string;
|
|
79
|
+
description: string;
|
|
80
|
+
repoName: string;
|
|
81
|
+
url: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Enum representing the names of prompts used in the application download process.
|
|
85
|
+
*/
|
|
86
|
+
export declare enum PromptNames {
|
|
87
|
+
selectedApp = "selectedApp",
|
|
88
|
+
systemSelection = "systemSelection",
|
|
89
|
+
targetFolder = "targetFolder"
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Structure of answers provided by the user for application download prompts.
|
|
93
|
+
*/
|
|
94
|
+
export interface RepoAppDownloadAnswers {
|
|
95
|
+
/** Selected backend system connection details. */
|
|
96
|
+
[PromptNames.systemSelection]: SystemSelectionAnswers;
|
|
97
|
+
/** Information about the selected application for download. */
|
|
98
|
+
[PromptNames.selectedApp]: AppInfo;
|
|
99
|
+
/** Target folder where the application will be generated. */
|
|
100
|
+
[PromptNames.targetFolder]: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Interface representing the configuration of a QFA JSON file.
|
|
104
|
+
* This QFA JSON file is used for configuring the application download process
|
|
105
|
+
* and contains user inputs.
|
|
106
|
+
*/
|
|
107
|
+
export interface QfaJsonConfig {
|
|
108
|
+
metadata: {
|
|
109
|
+
package: string;
|
|
110
|
+
masterLanguage?: string;
|
|
111
|
+
};
|
|
112
|
+
serviceBindingDetails: {
|
|
113
|
+
name?: string;
|
|
114
|
+
serviceName: string;
|
|
115
|
+
serviceVersion: string;
|
|
116
|
+
mainEntityName: string;
|
|
117
|
+
navigationEntity?: string;
|
|
118
|
+
};
|
|
119
|
+
projectAttribute: {
|
|
120
|
+
moduleName: string;
|
|
121
|
+
applicationTitle?: string;
|
|
122
|
+
minimumUi5Version?: string;
|
|
123
|
+
template?: string;
|
|
124
|
+
};
|
|
125
|
+
deploymentDetails: {
|
|
126
|
+
repositoryName: string;
|
|
127
|
+
repositoryDescription?: string;
|
|
128
|
+
};
|
|
129
|
+
fioriLaunchpadConfiguration: {
|
|
130
|
+
semanticObject: string;
|
|
131
|
+
action: string;
|
|
132
|
+
title: string;
|
|
133
|
+
subtitle?: string;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PromptNames = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Enum representing the names of prompts used in the application download process.
|
|
6
|
+
*/
|
|
7
|
+
var PromptNames;
|
|
8
|
+
(function (PromptNames) {
|
|
9
|
+
PromptNames["selectedApp"] = "selectedApp";
|
|
10
|
+
PromptNames["systemSelection"] = "systemSelection";
|
|
11
|
+
PromptNames["targetFolder"] = "targetFolder";
|
|
12
|
+
})(PromptNames || (exports.PromptNames = PromptNames = {}));
|
|
13
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { AbapServiceProvider, AppIndex } from '@sap-ux/axios-extension';
|
|
2
|
+
import type { AppInfo, AppItem } from '../app/types';
|
|
3
|
+
/**
|
|
4
|
+
* Returns the details for the YUI prompt.
|
|
5
|
+
*
|
|
6
|
+
* @returns step details
|
|
7
|
+
*/
|
|
8
|
+
export declare function getYUIDetails(): {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
}[];
|
|
12
|
+
/**
|
|
13
|
+
* Returns the prompt details for the selected application.
|
|
14
|
+
*
|
|
15
|
+
* @param {AppItem} app - The application item to extract details from.
|
|
16
|
+
* @returns {{ name: string; value: AppInfo }} The extracted details including name and value.
|
|
17
|
+
*/
|
|
18
|
+
export declare const extractAppData: (app: AppItem) => {
|
|
19
|
+
name: string;
|
|
20
|
+
value: AppInfo;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Formats the application list into selectable choices.
|
|
24
|
+
*
|
|
25
|
+
* @param {AppIndex} appList - List of applications retrieved from the system.
|
|
26
|
+
* @returns {Array<{ name: string; value: AppInfo }>} The formatted choices for selection.
|
|
27
|
+
*/
|
|
28
|
+
export declare const formatAppChoices: (appList: AppIndex) => Array<{
|
|
29
|
+
name: string;
|
|
30
|
+
value: AppInfo;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Fetches the application list for the selected system.
|
|
34
|
+
*
|
|
35
|
+
* @param {AbapServiceProvider} serviceProvider - The ABAP service provider.
|
|
36
|
+
* @param {string} appId - Application ID to be downloaded.
|
|
37
|
+
* @returns {Promise<AppIndex>} A list of applications filtered by source template.
|
|
38
|
+
*/
|
|
39
|
+
export declare function fetchAppListForSelectedSystem(serviceProvider: AbapServiceProvider, appId?: string): Promise<AppIndex>;
|
|
40
|
+
//# sourceMappingURL=prompt-helpers.d.ts.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fetchAppListForSelectedSystem = exports.formatAppChoices = exports.extractAppData = exports.getYUIDetails = void 0;
|
|
7
|
+
const constants_1 = require("../utils/constants");
|
|
8
|
+
const prompt_state_1 = require("./prompt-state");
|
|
9
|
+
const i18n_1 = require("../utils/i18n");
|
|
10
|
+
const logger_1 = __importDefault(require("../utils/logger"));
|
|
11
|
+
/**
|
|
12
|
+
* Returns the details for the YUI prompt.
|
|
13
|
+
*
|
|
14
|
+
* @returns step details
|
|
15
|
+
*/
|
|
16
|
+
function getYUIDetails() {
|
|
17
|
+
return [
|
|
18
|
+
{
|
|
19
|
+
name: constants_1.generatorTitle,
|
|
20
|
+
description: constants_1.generatorDescription
|
|
21
|
+
}
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
exports.getYUIDetails = getYUIDetails;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the prompt details for the selected application.
|
|
27
|
+
*
|
|
28
|
+
* @param {AppItem} app - The application item to extract details from.
|
|
29
|
+
* @returns {{ name: string; value: AppInfo }} The extracted details including name and value.
|
|
30
|
+
*/
|
|
31
|
+
const extractAppData = (app) => {
|
|
32
|
+
// cast to string because TypeScript doesn't automatically know at the point that these fields are defined
|
|
33
|
+
// after filtering out invalid apps.
|
|
34
|
+
const id = app['sap.app/id'];
|
|
35
|
+
const title = app['sap.app/title'];
|
|
36
|
+
const description = (app['sap.app/description'] ?? '');
|
|
37
|
+
const repoName = app.repoName;
|
|
38
|
+
const url = app.url;
|
|
39
|
+
return {
|
|
40
|
+
name: id,
|
|
41
|
+
value: {
|
|
42
|
+
appId: id,
|
|
43
|
+
title,
|
|
44
|
+
description,
|
|
45
|
+
repoName,
|
|
46
|
+
url
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
exports.extractAppData = extractAppData;
|
|
51
|
+
/**
|
|
52
|
+
* Formats the application list into selectable choices.
|
|
53
|
+
*
|
|
54
|
+
* @param {AppIndex} appList - List of applications retrieved from the system.
|
|
55
|
+
* @returns {Array<{ name: string; value: AppInfo }>} The formatted choices for selection.
|
|
56
|
+
*/
|
|
57
|
+
const formatAppChoices = (appList) => {
|
|
58
|
+
return appList
|
|
59
|
+
.filter((app) => {
|
|
60
|
+
const hasRequiredFields = app['sap.app/id'] && app['sap.app/title'] && app['repoName'] && app['url'];
|
|
61
|
+
if (!hasRequiredFields) {
|
|
62
|
+
logger_1.default.logger?.error((0, i18n_1.t)('error.requiredFieldsMissing', { app: JSON.stringify(app) }));
|
|
63
|
+
}
|
|
64
|
+
return hasRequiredFields;
|
|
65
|
+
})
|
|
66
|
+
.map((app) => (0, exports.extractAppData)(app));
|
|
67
|
+
};
|
|
68
|
+
exports.formatAppChoices = formatAppChoices;
|
|
69
|
+
/**
|
|
70
|
+
* Fetches a list of deployed applications from the ABAP repository.
|
|
71
|
+
*
|
|
72
|
+
* @param {AbapServiceProvider} provider - The ABAP service provider.
|
|
73
|
+
* @param {string} appId - Application ID to filter the list.
|
|
74
|
+
* @returns {Promise<AppIndex>} A list of applications filtered by source template.
|
|
75
|
+
*/
|
|
76
|
+
async function getAppList(provider, appId) {
|
|
77
|
+
try {
|
|
78
|
+
const searchParams = appId
|
|
79
|
+
? {
|
|
80
|
+
...constants_1.appListSearchParams,
|
|
81
|
+
'sap.app/id': appId
|
|
82
|
+
}
|
|
83
|
+
: constants_1.appListSearchParams;
|
|
84
|
+
return await provider.getAppIndex().search(searchParams, constants_1.appListResultFields);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
logger_1.default.logger?.error((0, i18n_1.t)('error.applicationListFetchError', { error: error.message }));
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Fetches the application list for the selected system.
|
|
93
|
+
*
|
|
94
|
+
* @param {AbapServiceProvider} serviceProvider - The ABAP service provider.
|
|
95
|
+
* @param {string} appId - Application ID to be downloaded.
|
|
96
|
+
* @returns {Promise<AppIndex>} A list of applications filtered by source template.
|
|
97
|
+
*/
|
|
98
|
+
async function fetchAppListForSelectedSystem(serviceProvider, appId) {
|
|
99
|
+
if (serviceProvider) {
|
|
100
|
+
prompt_state_1.PromptState.systemSelection = {
|
|
101
|
+
connectedSystem: { serviceProvider }
|
|
102
|
+
};
|
|
103
|
+
return await getAppList(serviceProvider, appId);
|
|
104
|
+
}
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
exports.fetchAppListForSelectedSystem = fetchAppListForSelectedSystem;
|
|
108
|
+
//# sourceMappingURL=prompt-helpers.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { SystemSelectionAnswers } from '../app/types';
|
|
3
|
+
/**
|
|
4
|
+
* Much of the values returned by the app downloader prompting are derived from prompt answers and are not direct answer values.
|
|
5
|
+
* Since inquirer does not provide a way to return values that are not direct answers from prompts, this class will maintain the derived values
|
|
6
|
+
* across prompts statically for the lifespan of the prompting session.
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
export declare class PromptState {
|
|
10
|
+
private static _systemSelection;
|
|
11
|
+
private static _downloadedAppPackage?;
|
|
12
|
+
/**
|
|
13
|
+
* Returns the current state of the service config.
|
|
14
|
+
*
|
|
15
|
+
* @returns {SystemSelectionAnswers} service config
|
|
16
|
+
*/
|
|
17
|
+
static get systemSelection(): SystemSelectionAnswers;
|
|
18
|
+
/**
|
|
19
|
+
* Set the state of the system selection.
|
|
20
|
+
*
|
|
21
|
+
* @param {SystemSelectionAnswers} value - system selection value
|
|
22
|
+
*/
|
|
23
|
+
static set systemSelection(value: Partial<SystemSelectionAnswers>);
|
|
24
|
+
/**
|
|
25
|
+
* Set the downloaded app package.
|
|
26
|
+
*/
|
|
27
|
+
static set downloadedAppPackage(archive: Buffer);
|
|
28
|
+
/**
|
|
29
|
+
* Returns the downloaded app package.
|
|
30
|
+
*
|
|
31
|
+
* @returns {Buffer} downloaded app package
|
|
32
|
+
*/
|
|
33
|
+
static get downloadedAppPackage(): Buffer;
|
|
34
|
+
/**
|
|
35
|
+
* Get the baseURL from the connected system's service provider defaults.
|
|
36
|
+
*
|
|
37
|
+
* @returns {string | undefined} baseURL
|
|
38
|
+
*/
|
|
39
|
+
static get baseURL(): string | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Get the sap-client parameter from the connected system's service provider defaults.
|
|
42
|
+
*
|
|
43
|
+
* @returns {string | undefined} sap-client
|
|
44
|
+
*/
|
|
45
|
+
static get sapClient(): string | undefined;
|
|
46
|
+
static reset(): void;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=prompt-state.d.ts.map
|