@sap-ux/repo-app-import-sub-generator 1.2.60 → 1.2.62
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/generators/prompts/prompt-helpers.js +29 -12
- package/generators/prompts/prompts.js +2 -3
- package/generators/telemetryEvents/index.d.ts +1 -0
- package/generators/telemetryEvents/index.js +1 -0
- package/generators/translations/repo-app-import-sub-generator.i18n.json +2 -1
- package/generators/utils/constants.d.ts +3 -1
- package/generators/utils/constants.js +14 -9
- package/generators/utils/download-utils.d.ts +2 -1
- package/generators/utils/download-utils.js +6 -0
- package/generators/utils/validators.d.ts +1 -1
- package/generators/utils/validators.js +20 -2
- package/package.json +6 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { appListResultFields, downloadTypeConfig, generatorTitleConfig, adtSourceTemplateId } from '../utils/constants.js';
|
|
1
|
+
import { appListResultFields, downloadTypeConfig, generatorTitleConfig, adtSourceTemplateId, appListFieldsWithoutSourceTemplate, sourceTemplateIdField } from '../utils/constants.js';
|
|
2
2
|
import { AppDownloadType } from '../app/types.js';
|
|
3
3
|
import { PromptState } from './prompt-state.js';
|
|
4
4
|
import { t } from '../utils/i18n.js';
|
|
@@ -64,27 +64,44 @@ export const formatAppChoices = (appList) => {
|
|
|
64
64
|
*
|
|
65
65
|
* @param {AbapServiceProvider} provider - The ABAP service provider.
|
|
66
66
|
* @param {string} appId - Application ID to filter the list.
|
|
67
|
-
* @param {AppDownloadType} downloadType - The download type determining which search
|
|
68
|
-
* @returns {Promise<AppIndex>} A list of applications
|
|
67
|
+
* @param {AppDownloadType} downloadType - The download type determining which search parameters to use.
|
|
68
|
+
* @returns {Promise<AppIndex>} A list of deployed applications. For the ADTQuickDeploy flow, only ADT-deployed apps are returned. For the AbapRepository flow on systems that support it, ADT-deployed apps are excluded.
|
|
69
69
|
*/
|
|
70
70
|
async function getAppList(provider, appId, downloadType = AppDownloadType.ADTQuickDeploy) {
|
|
71
|
+
const baseSearchParams = downloadTypeConfig[downloadType].searchParams;
|
|
72
|
+
const searchParams = appId ? { ...baseSearchParams, 'sap.app/id': appId } : baseSearchParams;
|
|
71
73
|
try {
|
|
72
|
-
const baseSearchParams = downloadTypeConfig[downloadType].searchParams;
|
|
73
|
-
const searchParams = appId
|
|
74
|
-
? {
|
|
75
|
-
...baseSearchParams,
|
|
76
|
-
'sap.app/id': appId
|
|
77
|
-
}
|
|
78
|
-
: baseSearchParams;
|
|
79
74
|
const results = await provider.getAppIndex().search(searchParams, appListResultFields);
|
|
80
75
|
if (downloadType === AppDownloadType.AbapRepository) {
|
|
81
76
|
// For ABAP Repository downloads, filter out apps with the ADT source template as they follow the quick deploy app download flow.
|
|
82
|
-
|
|
77
|
+
const filtered = results.filter((app) => app[sourceTemplateIdField] !== adtSourceTemplateId);
|
|
78
|
+
RepoAppDownloadLogger.logger?.debug(`App list fetched: ${results.length} total, ${filtered.length} after filtering out ADT-deployed apps`);
|
|
79
|
+
return filtered;
|
|
83
80
|
}
|
|
84
81
|
return results;
|
|
85
82
|
}
|
|
86
83
|
catch (error) {
|
|
87
|
-
|
|
84
|
+
if (downloadType === AppDownloadType.AbapRepository &&
|
|
85
|
+
error?.response?.status === 400) {
|
|
86
|
+
// Older systems may not support sourceTemplateIdField or the sap.app/type search param.
|
|
87
|
+
// Retry with no search params and without sourceTemplateIdField — return all results as-is
|
|
88
|
+
// since old systems won't have ADT-deployed apps to filter out anyway.
|
|
89
|
+
RepoAppDownloadLogger.logger?.debug(`Retrying without ${sourceTemplateIdField} and search params`);
|
|
90
|
+
try {
|
|
91
|
+
// sap.app/type=application is also dropped — older systems may reject it too.
|
|
92
|
+
// Non-application entries in the list are acceptable; they will fail at the download step.
|
|
93
|
+
const retryResults = await provider.getAppIndex().search({}, appListFieldsWithoutSourceTemplate);
|
|
94
|
+
RepoAppDownloadLogger.logger?.debug(`Retry succeeded: ${retryResults.length} results`);
|
|
95
|
+
return retryResults;
|
|
96
|
+
}
|
|
97
|
+
catch (retryError) {
|
|
98
|
+
const retryMessage = retryError instanceof Error ? retryError.message : String(retryError);
|
|
99
|
+
RepoAppDownloadLogger.logger?.error(t('error.applicationListFetchError', { error: retryMessage }));
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
104
|
+
RepoAppDownloadLogger.logger?.error(t('error.applicationListFetchError', { error: message }));
|
|
88
105
|
return [];
|
|
89
106
|
}
|
|
90
107
|
}
|
|
@@ -25,9 +25,8 @@ const getTargetFolderPrompt = (appRootPath, appId) => {
|
|
|
25
25
|
if (appId) {
|
|
26
26
|
return true;
|
|
27
27
|
}
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
return Boolean(answers?.selectedApp?.appId);
|
|
28
|
+
// Only show after a successful download — admZip is set by downloadApp on success.
|
|
29
|
+
return Boolean(answers?.selectedApp?.appId) && !!PromptState.admZip;
|
|
31
30
|
},
|
|
32
31
|
guiOptions: {
|
|
33
32
|
applyDefaultWhenDirty: true,
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
export declare const EventName: {
|
|
5
5
|
readonly ABAP_REPO_DOWNLOAD_SUCCESS: "ABAP_REPO_DOWNLOAD_SUCCESS";
|
|
6
6
|
readonly ABAP_REPO_DOWNLOAD_FAIL: "ABAP_REPO_DOWNLOAD_FAIL";
|
|
7
|
+
readonly ABAP_REPO_DOWNLOAD_NO_FILES_RETURNED: "ABAP_REPO_DOWNLOAD_NO_FILES_RETURNED";
|
|
7
8
|
readonly ADT_QUICK_DEPLOY_DOWNLOAD_SUCCESS: "ADT_QUICK_DEPLOY_DOWNLOAD_SUCCESS";
|
|
8
9
|
readonly ADT_QUICK_DEPLOY_DOWNLOAD_FAIL: "ADT_QUICK_DEPLOY_DOWNLOAD_FAIL";
|
|
9
10
|
readonly ABAP_REPO_DOWNLOAD_MIGRATION_COMPLETED: "ABAP_REPO_DOWNLOAD_MIGRATION_COMPLETED";
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
export const EventName = {
|
|
5
5
|
ABAP_REPO_DOWNLOAD_SUCCESS: 'ABAP_REPO_DOWNLOAD_SUCCESS',
|
|
6
6
|
ABAP_REPO_DOWNLOAD_FAIL: 'ABAP_REPO_DOWNLOAD_FAIL',
|
|
7
|
+
ABAP_REPO_DOWNLOAD_NO_FILES_RETURNED: 'ABAP_REPO_DOWNLOAD_NO_FILES_RETURNED',
|
|
7
8
|
ADT_QUICK_DEPLOY_DOWNLOAD_SUCCESS: 'ADT_QUICK_DEPLOY_DOWNLOAD_SUCCESS',
|
|
8
9
|
ADT_QUICK_DEPLOY_DOWNLOAD_FAIL: 'ADT_QUICK_DEPLOY_DOWNLOAD_FAIL',
|
|
9
10
|
ABAP_REPO_DOWNLOAD_MIGRATION_COMPLETED: 'ABAP_REPO_DOWNLOAD_MIGRATION_COMPLETED'
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
"appConfigGenError": "An error occurred when generating the application configuration: {{- error}}. For more information, view the logs.",
|
|
9
9
|
"endPhase": "An error occurred in the end phase: {{- error}}. For more information, view the logs.",
|
|
10
10
|
"writingPhase": "An error occurred in the writing phase: {{- error}}. For more information, view the logs.",
|
|
11
|
-
"errorProcessingJsonFile": "An error occurred when processing the JSON file: {{- error}}.
|
|
11
|
+
"errorProcessingJsonFile": "An error occurred when processing the JSON file: {{- error}}. Ensure that the file is valid.",
|
|
12
12
|
"noAppsDeployed": "No applications deployed to this system can be downloaded.",
|
|
13
|
+
"appDownloadFailed": "The application was found but no ZIP archive was returned by the system. The ABAP system may not support this download method.",
|
|
13
14
|
"validationErrors": {
|
|
14
15
|
"invalidMetadataPackage": "Invalid or missing package in the metadata. Check the metadata.",
|
|
15
16
|
"invalidServiceName": "Invalid or missing `serviceName` in `serviceBindingDetails`. Check the `serviceName`.",
|
|
@@ -7,8 +7,9 @@ export interface DownloadTypeConfig {
|
|
|
7
7
|
searchParams: Record<string, string>;
|
|
8
8
|
}
|
|
9
9
|
export declare const adtSourceTemplateId = "@sap.adt.sevicebinding.deploy:lrop";
|
|
10
|
+
export declare const sourceTemplateIdField = "sap.app/sourceTemplate/id";
|
|
10
11
|
/**
|
|
11
|
-
* Title and description
|
|
12
|
+
* Title and configuration of the description per download flow, used in the constructor
|
|
12
13
|
*/
|
|
13
14
|
export declare const generatorTitleConfig: Record<AppDownloadType, {
|
|
14
15
|
title: string;
|
|
@@ -25,4 +26,5 @@ export declare const qfaJsonFileName = "qfa.json";
|
|
|
25
26
|
export declare const defaultAnswers: RepoAppDownloadAnswers;
|
|
26
27
|
export declare const extractedFilePath = "extractedFiles";
|
|
27
28
|
export declare const appListResultFields: string[];
|
|
29
|
+
export declare const appListFieldsWithoutSourceTemplate: string[];
|
|
28
30
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -4,8 +4,10 @@ import { AppDownloadType } from '../app/types.js';
|
|
|
4
4
|
import { t } from './i18n.js';
|
|
5
5
|
// The source template ID used for filtering the apps in the repository
|
|
6
6
|
export const adtSourceTemplateId = '@sap.adt.sevicebinding.deploy:lrop';
|
|
7
|
+
// The app index field name for the source template ID
|
|
8
|
+
export const sourceTemplateIdField = 'sap.app/sourceTemplate/id';
|
|
7
9
|
/**
|
|
8
|
-
* Title and description
|
|
10
|
+
* Title and configuration of the description per download flow, used in the constructor
|
|
9
11
|
*/
|
|
10
12
|
export const generatorTitleConfig = {
|
|
11
13
|
[AppDownloadType.ADTQuickDeploy]: {
|
|
@@ -61,14 +63,17 @@ export const defaultAnswers = {
|
|
|
61
63
|
};
|
|
62
64
|
// Path for storing the extracted files from repository
|
|
63
65
|
export const extractedFilePath = 'extractedFiles';
|
|
64
|
-
// Fields to retrieve from the app list, useful for displaying app metadata
|
|
66
|
+
// Fields to retrieve from the app list, useful for displaying app metadata.
|
|
67
|
+
// Includes sap.app/sourceTemplate/id so ADT-deployed apps can be filtered out on modern systems.
|
|
65
68
|
export const appListResultFields = [
|
|
66
|
-
'sap.app/id',
|
|
67
|
-
'sap.app/title',
|
|
68
|
-
'sap.app/description',
|
|
69
|
-
|
|
70
|
-
'repoName',
|
|
71
|
-
'fileType',
|
|
72
|
-
'url'
|
|
69
|
+
'sap.app/id',
|
|
70
|
+
'sap.app/title',
|
|
71
|
+
'sap.app/description',
|
|
72
|
+
sourceTemplateIdField,
|
|
73
|
+
'repoName',
|
|
74
|
+
'fileType',
|
|
75
|
+
'url'
|
|
73
76
|
];
|
|
77
|
+
// Fallback for older systems that reject sap.app/sourceTemplate/id with HTTP 400.
|
|
78
|
+
export const appListFieldsWithoutSourceTemplate = appListResultFields.filter((field) => field !== sourceTemplateIdField);
|
|
74
79
|
//# sourceMappingURL=constants.js.map
|
|
@@ -26,8 +26,9 @@ export declare function extractZip(extractedProjectPath: string, fs: Editor): Pr
|
|
|
26
26
|
* Downloads application files from the ABAP repository.
|
|
27
27
|
*
|
|
28
28
|
* @param {string} repoName - The repository name of the application.
|
|
29
|
+
* @returns {Promise<boolean>} - Resolves to false if no data was returned (such as on a legacy ABAP system), true otherwise.
|
|
29
30
|
*/
|
|
30
|
-
export declare function downloadApp(repoName: string): Promise<
|
|
31
|
+
export declare function downloadApp(repoName: string): Promise<boolean>;
|
|
31
32
|
/**
|
|
32
33
|
* Resolve a transport request for the given app/package.
|
|
33
34
|
*
|
|
@@ -59,6 +59,7 @@ export async function extractZip(extractedProjectPath, fs) {
|
|
|
59
59
|
* Downloads application files from the ABAP repository.
|
|
60
60
|
*
|
|
61
61
|
* @param {string} repoName - The repository name of the application.
|
|
62
|
+
* @returns {Promise<boolean>} - Resolves to false if no data was returned (such as on a legacy ABAP system), true otherwise.
|
|
62
63
|
*/
|
|
63
64
|
export async function downloadApp(repoName) {
|
|
64
65
|
const serviceProvider = PromptState.systemSelection?.connectedSystem?.serviceProvider;
|
|
@@ -66,9 +67,14 @@ export async function downloadApp(repoName) {
|
|
|
66
67
|
ui5AbapRepository.log = RepoAppDownloadLogger.logger;
|
|
67
68
|
RepoAppDownloadLogger.logger?.debug(`App download started: ${repoName}`);
|
|
68
69
|
const downloadedAppPackage = await ui5AbapRepository.downloadFiles(repoName);
|
|
70
|
+
if (!downloadedAppPackage || downloadedAppPackage.length === 0) {
|
|
71
|
+
RepoAppDownloadLogger.logger?.error(t('error.appDownloadFailed'));
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
69
74
|
RepoAppDownloadLogger.logger?.debug(`App download completed: ${repoName}`);
|
|
70
75
|
// store downloaded package in prompt state
|
|
71
76
|
PromptState.admZip = downloadedAppPackage;
|
|
77
|
+
return true;
|
|
72
78
|
}
|
|
73
79
|
/**
|
|
74
80
|
* Resolve a transport request for the given app/package.
|
|
@@ -19,7 +19,7 @@ export declare const validateQfaJsonFile: (config: QfaJsonConfig) => boolean;
|
|
|
19
19
|
*/
|
|
20
20
|
export declare const isValidPromptState: (targetFolder: string, appId?: string) => boolean;
|
|
21
21
|
/**
|
|
22
|
-
* Validates the app selection and handles app download if applicable.
|
|
22
|
+
* Validates the app selection and handles the app download, if applicable.
|
|
23
23
|
*
|
|
24
24
|
* @param answers - The selected app information.
|
|
25
25
|
* @param appList - The list of available apps.
|
|
@@ -7,6 +7,8 @@ import { AppDownloadType } from '../app/types.js';
|
|
|
7
7
|
import { downloadApp, hasQfaJson } from '../utils/download-utils.js';
|
|
8
8
|
import { MessageType } from '@sap-devx/yeoman-ui-types';
|
|
9
9
|
import { qfaJsonFileName } from '../utils/constants.js';
|
|
10
|
+
import { sendTelemetry, TelemetryHelper } from '@sap-ux/fiori-generator-shared';
|
|
11
|
+
import { EventName } from '../telemetryEvents/index.js';
|
|
10
12
|
/**
|
|
11
13
|
* Validates the metadata section of the app configuration.
|
|
12
14
|
*
|
|
@@ -98,7 +100,15 @@ async function generateAppNotFoundHelpLink() {
|
|
|
98
100
|
return ErrorHandler.getHelpLink(HELP_NODES.ADT_APP_NOT_FOUND_ERROR, ERROR_TYPE.INTERNAL_SERVER_ERROR, t('error.noAppsDeployed'));
|
|
99
101
|
}
|
|
100
102
|
/**
|
|
101
|
-
*
|
|
103
|
+
* Generates a help link for the "App Download Failed" error.
|
|
104
|
+
*
|
|
105
|
+
* @returns {Promise<ValidationLink>} - A promise resolving to a validation link for the error.
|
|
106
|
+
*/
|
|
107
|
+
async function generateAppDownloadFailedHelpLink() {
|
|
108
|
+
return ErrorHandler.getHelpLink(HELP_NODES.ABAP_REPO_APP_DOWNLOAD_FAILED, ERROR_TYPE.INTERNAL_SERVER_ERROR, t('error.appDownloadFailed'));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Validates the app selection and handles the app download, if applicable.
|
|
102
112
|
*
|
|
103
113
|
* @param answers - The selected app information.
|
|
104
114
|
* @param appList - The list of available apps.
|
|
@@ -119,7 +129,15 @@ export async function validateAppSelection(answers, appList, quickDeployedAppCon
|
|
|
119
129
|
// Valid app selected, try to download
|
|
120
130
|
if (answers?.appId) {
|
|
121
131
|
try {
|
|
122
|
-
await downloadApp(answers.repoName);
|
|
132
|
+
const downloadSucceeded = await downloadApp(answers.repoName);
|
|
133
|
+
if (!downloadSucceeded) {
|
|
134
|
+
if (downloadType === AppDownloadType.AbapRepository) {
|
|
135
|
+
await sendTelemetry(EventName.ABAP_REPO_DOWNLOAD_NO_FILES_RETURNED, TelemetryHelper.createTelemetryData({}) ?? {}).catch(() => {
|
|
136
|
+
// telemetry errors are non-fatal
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return await generateAppDownloadFailedHelpLink();
|
|
140
|
+
}
|
|
123
141
|
const isQfaJsonPresent = hasQfaJson();
|
|
124
142
|
if (!isQfaJsonPresent && downloadType === AppDownloadType.ADTQuickDeploy) {
|
|
125
143
|
appWizard?.showError(t('error.qfaJsonNotFound', { jsonFileName: qfaJsonFileName }), MessageType.notification);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap-ux/repo-app-import-sub-generator",
|
|
3
3
|
"description": "Generator to download LROP Fiori applications deployed from an ABAP repository.",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.62",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/SAP/open-ux-tools.git",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"yeoman-generator": "5.10.0",
|
|
28
28
|
"@sap-ux/feature-toggle": "1.0.5",
|
|
29
29
|
"@sap-ux/fiori-generator-shared": "1.2.16",
|
|
30
|
-
"@sap-ux/inquirer-common": "1.0.
|
|
30
|
+
"@sap-ux/inquirer-common": "1.0.42",
|
|
31
31
|
"@sap-ux/project-access": "2.2.0",
|
|
32
|
-
"@sap-ux/odata-service-inquirer": "3.2.
|
|
33
|
-
"@sap-ux/fiori-elements-writer": "3.1.
|
|
34
|
-
"@sap-ux/fiori-freestyle-writer": "3.0.
|
|
32
|
+
"@sap-ux/odata-service-inquirer": "3.2.19",
|
|
33
|
+
"@sap-ux/fiori-elements-writer": "3.1.59",
|
|
34
|
+
"@sap-ux/fiori-freestyle-writer": "3.0.104",
|
|
35
35
|
"@sap-ux/logger": "1.0.3",
|
|
36
36
|
"@sap-ux/project-input-validator": "1.0.19",
|
|
37
37
|
"@sap-ux/launch-config": "1.0.21",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@sap-ux/axios-extension": "2.1.0",
|
|
43
43
|
"@sap-ux/store": "2.0.7",
|
|
44
44
|
"@sap-ux/system-access": "1.0.14",
|
|
45
|
-
"@sap-ux/guided-answers-helper": "1.0.
|
|
45
|
+
"@sap-ux/guided-answers-helper": "1.0.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@jest/globals": "30.4.1",
|