@sap-ux/repo-app-import-sub-generator 1.2.90 → 1.2.92

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.
@@ -10,7 +10,8 @@ export default class extends Generator {
10
10
  private readonly vscode?;
11
11
  private readonly appRootPath;
12
12
  private readonly answers;
13
- private readonly downloadType;
13
+ private downloadType;
14
+ private readonly downloadTypeExplicit;
14
15
  private readonly prompts;
15
16
  options: RepoAppDownloadOptions;
16
17
  private projectPath;
@@ -4,7 +4,7 @@ import { AppWizard, Prompts, MessageType } from '@sap-devx/yeoman-ui-types';
4
4
  import { isInternalFeaturesSettingEnabled } from '@sap-ux/feature-toggle';
5
5
  import { extractedFilePath, generatorName, defaultAnswers, qfaJsonFileName, downloadTypeConfig } from '../utils/constants.js';
6
6
  import { t } from '../utils/i18n.js';
7
- import { extractZip } from '../utils/download-utils.js';
7
+ import { extractZip, hasQfaJson } from '../utils/download-utils.js';
8
8
  import { EventName } from '../telemetryEvents/index.js';
9
9
  import { getDefaultTargetFolder, generateAppGenInfo, sendTelemetry, TelemetryHelper, isCli, setYeomanEnvConflicterForce, getFloorplanLabel } from '@sap-ux/fiori-generator-shared';
10
10
  import { AppDownloadType, PromptNames } from './types.js';
@@ -37,6 +37,7 @@ export default class extends Generator {
37
37
  appRootPath;
38
38
  answers = defaultAnswers;
39
39
  downloadType;
40
+ downloadTypeExplicit;
40
41
  prompts;
41
42
  options;
42
43
  projectPath;
@@ -58,6 +59,7 @@ export default class extends Generator {
58
59
  this.appRootPath = opts?.appRootPath ?? getDefaultTargetFolder(this.vscode) ?? this.destinationRoot();
59
60
  this.options = opts;
60
61
  this.downloadType = opts.data?.appDownloadType ?? AppDownloadType.ADTQuickDeploy;
62
+ this.downloadTypeExplicit = opts.data?.appDownloadType !== undefined;
61
63
  // Configure logging
62
64
  RepoAppDownloadLogger.configureLogging(this.rootGeneratorName(), this.log, this.options.logWrapper, this.options.logLevel, this.options.logger, this.vscode);
63
65
  this.prompts = new Prompts(getYUIDetails(this.downloadType));
@@ -110,8 +112,14 @@ export default class extends Generator {
110
112
  * Writes the configuration files for the project, including deployment config, and README.
111
113
  */
112
114
  async writing() {
115
+ // hasQfaJson() is the authoritative signal: qfa.json is only present in ADT Quick Deploy apps.
116
+ // When downloadType was not explicitly set (CLI invocation defaults to ADTQuickDeploy),
117
+ // use qfa.json presence to decide which flow to run.
118
+ const isAdtQuickDeploy = this.downloadType === AppDownloadType.ADTQuickDeploy && (this.downloadTypeExplicit || hasQfaJson());
119
+ // Resolve the effective type so all post-writing phases (README, telemetry, success msg) are consistent.
120
+ this.downloadType = isAdtQuickDeploy ? AppDownloadType.ADTQuickDeploy : AppDownloadType.AbapRepository;
113
121
  try {
114
- if (this.downloadType === AppDownloadType.AbapRepository) {
122
+ if (!isAdtQuickDeploy) {
115
123
  await this._generateAbapRepositoryApp();
116
124
  }
117
125
  else {
@@ -69,18 +69,21 @@ export const formatAppChoices = (appList) => {
69
69
  */
70
70
  /**
71
71
  * Fallback for older ABAP systems that reject sourceTemplate/type search params with HTTP 400.
72
- * Returns all apps unfiltered — old systems won't have ADT-deployed apps anyway.
72
+ * Fetches the full unfiltered list and then applies client-side appId filtering if provided,
73
+ * since older systems cannot handle server-side field filtering.
73
74
  *
74
75
  * @param appIndex - The app index instance to search against.
75
- * @returns All apps without filtering, or an empty array if the retry also fails.
76
+ * @param appId - Optional app ID to filter results client-side.
77
+ * @returns Matching apps (or all apps when appId is omitted), or an empty array if the retry also fails.
76
78
  */
77
- async function retryAbapAppListFetch(appIndex) {
79
+ async function retryAppListFetchForOlderSystems(appIndex, appId) {
78
80
  try {
79
81
  // sap.app/type=application is also dropped — older systems may reject it too.
80
82
  // Non-application entries in the list are acceptable; they will fail at the download step.
81
83
  const results = await appIndex.search({}, appListFieldsWithoutSourceTemplate);
82
- RepoAppDownloadLogger.logger?.debug(`Retry succeeded: ${results.length} results`);
83
- return results;
84
+ const filtered = appId ? results.filter((app) => app['sap.app/id'] === appId) : results;
85
+ RepoAppDownloadLogger.logger?.debug(`Retry succeeded: ${filtered.length} results`);
86
+ return filtered;
84
87
  }
85
88
  catch (retryError) {
86
89
  const message = retryError instanceof Error ? retryError.message : String(retryError);
@@ -134,13 +137,16 @@ async function getAppList(provider, appId, downloadType = AppDownloadType.ADTQui
134
137
  return await searchAdtQuickDeployApps(appIndex, appId);
135
138
  }
136
139
  catch (error) {
137
- if (downloadType === AppDownloadType.AbapRepository &&
138
- error?.response?.status === 400) {
139
- // Older systems may not support sourceTemplateIdField or the sap.app/type search param.
140
- RepoAppDownloadLogger.logger?.debug(`Retrying without ${sourceTemplateIdField} and search params`);
141
- return retryAbapAppListFetch(appIndex);
142
- }
140
+ const status = error?.response?.status;
143
141
  const message = error instanceof Error ? error.message : String(error);
142
+ // Older systems rejects unknown fields either as HTTP 400 or with an "unknown column" error message
143
+ const isOlderSystemError = status === 400 || /column.*unknown|unknown.*column/i.test(message);
144
+ if (isOlderSystemError) {
145
+ // Older systems reject sap.app/sourceTemplate/id as an unknown column.
146
+ // Both flows send it — ADTQuickDeploy as a search param, AbapRepository as a result field.
147
+ RepoAppDownloadLogger.logger?.debug(`Retrying without ${sourceTemplateIdField} and search params (original error: ${message})`);
148
+ return retryAppListFetchForOlderSystems(appIndex, appId);
149
+ }
144
150
  RepoAppDownloadLogger.logger?.error(t('error.applicationListFetchError', { error: message }));
145
151
  return [];
146
152
  }
@@ -10,7 +10,9 @@
10
10
  "writingPhase": "An error occurred in the writing phase: {{- error}}. For more information, view the logs.",
11
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
+ "appDownloadFailed": "The application could not be downloaded. The ABAP system does not support any available download method.",
14
+ "adtFallbackDownload": "ZIP download is not supported on this system. Falling back to downloading from the ADT file store.",
15
+ "adtFallbackDownloadFailed": "Downloading from the ADT file store failed: {{- error}}",
14
16
  "validationErrors": {
15
17
  "invalidMetadataPackage": "Invalid or missing package in the metadata. Check the metadata.",
16
18
  "invalidServiceName": "Invalid or missing `serviceName` in `serviceBindingDetails`. Check the `serviceName`.",
@@ -78,6 +78,7 @@ export const appListResultFields = [
78
78
  'fileType',
79
79
  'url'
80
80
  ];
81
- // Fallback for older systems that reject sap.app/sourceTemplate/id with HTTP 400.
82
- export const appListFieldsWithoutSourceTemplate = appListResultFields.filter((field) => field !== sourceTemplateIdField);
81
+ // Fallback for older systems (e.g. ABAP 731) that reject unknown columns with HTTP 400.
82
+ // Drops sourceTemplateIdField and fileType, which older app index versions do not support.
83
+ export const appListFieldsWithoutSourceTemplate = appListResultFields.filter((field) => field !== sourceTemplateIdField && field !== 'fileType');
83
84
  //# sourceMappingURL=constants.js.map
@@ -24,6 +24,8 @@ export declare function hasQfaJson(): boolean;
24
24
  export declare function extractZip(extractedProjectPath: string, fs: Editor): Promise<void>;
25
25
  /**
26
26
  * Downloads application files from the ABAP repository.
27
+ * First attempts download via ABAP_REPOSITORY_SRV (requires SAP_UI 754+).
28
+ * Falls back to ADT FileStoreService on older systems.
27
29
  *
28
30
  * @param {string} repoName - The repository name of the application.
29
31
  * @returns `{ success: true }` on success, or `{ success: false, errorMessage }` if the server
@@ -3,7 +3,7 @@ import { PromptState } from '../prompts/prompt-state.js';
3
3
  import { t } from './i18n.js';
4
4
  import RepoAppDownloadLogger from '../utils/logger.js';
5
5
  import { qfaJsonFileName } from './constants.js';
6
- import { TransportChecksService } from '@sap-ux/axios-extension';
6
+ import { TransportChecksService, FileStoreService } from '@sap-ux/axios-extension';
7
7
  import { restoreServiceProviderLoggers } from '@sap-ux/fiori-generator-shared';
8
8
  /**
9
9
  * Fetches the metadata of a given service from the provided ABAP service provider.
@@ -57,6 +57,8 @@ export async function extractZip(extractedProjectPath, fs) {
57
57
  }
58
58
  /**
59
59
  * Downloads application files from the ABAP repository.
60
+ * First attempts download via ABAP_REPOSITORY_SRV (requires SAP_UI 754+).
61
+ * Falls back to ADT FileStoreService on older systems.
60
62
  *
61
63
  * @param {string} repoName - The repository name of the application.
62
64
  * @returns `{ success: true }` on success, or `{ success: false, errorMessage }` if the server
@@ -64,10 +66,30 @@ export async function extractZip(extractedProjectPath, fs) {
64
66
  */
65
67
  export async function downloadApp(repoName) {
66
68
  const serviceProvider = PromptState.systemSelection?.connectedSystem?.serviceProvider;
67
- const ui5AbapRepository = await serviceProvider.getUi5AbapRepository();
69
+ const ui5AbapRepository = serviceProvider.getUi5AbapRepository();
68
70
  ui5AbapRepository.log = RepoAppDownloadLogger.logger;
69
71
  RepoAppDownloadLogger.logger?.debug(`App download started: ${repoName}`);
70
- const downloadedAppPackage = await ui5AbapRepository.downloadFiles(repoName);
72
+ let downloadedAppPackage;
73
+ try {
74
+ downloadedAppPackage = await ui5AbapRepository.downloadFiles(repoName);
75
+ }
76
+ catch (error) {
77
+ // ABAP_REPOSITORY_SRV may not be available on older systems (e.g. ABAP 731 / SAP_UI < 754).
78
+ // Fall through to the ADT FileStoreService fallback below.
79
+ RepoAppDownloadLogger.logger?.debug(`ABAP_REPOSITORY_SRV unavailable, falling back to ADT: ${error.message}`);
80
+ }
81
+ if (!downloadedAppPackage || downloadedAppPackage.length === 0) {
82
+ RepoAppDownloadLogger.logger?.debug(t('error.adtFallbackDownload'));
83
+ try {
84
+ const fileStoreService = await serviceProvider.getAdtService(FileStoreService);
85
+ if (fileStoreService) {
86
+ downloadedAppPackage = await ui5AbapRepository.downloadFilesViaAdt(repoName, fileStoreService);
87
+ }
88
+ }
89
+ catch (error) {
90
+ RepoAppDownloadLogger.logger?.error(t('error.adtFallbackDownloadFailed', { error: error.message }));
91
+ }
92
+ }
71
93
  if (!downloadedAppPackage || downloadedAppPackage.length === 0) {
72
94
  // Legacy ABAP systems may return empty content rather than an error response.
73
95
  const errorMessage = t('error.appDownloadFailed');
@@ -75,7 +97,6 @@ export async function downloadApp(repoName) {
75
97
  return { success: false, errorMessage };
76
98
  }
77
99
  RepoAppDownloadLogger.logger?.debug(`App download completed: ${repoName}`);
78
- // store downloaded package in prompt state
79
100
  PromptState.admZip = downloadedAppPackage;
80
101
  return { success: true };
81
102
  }
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.90",
4
+ "version": "1.2.92",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/SAP/open-ux-tools.git",
@@ -25,24 +25,24 @@
25
25
  "i18next": "26.3.6",
26
26
  "inquirer": "8.2.7",
27
27
  "yeoman-generator": "5.10.0",
28
- "@sap-ux/feature-toggle": "1.0.5",
29
- "@sap-ux/fiori-generator-shared": "1.2.23",
30
- "@sap-ux/inquirer-common": "1.0.49",
31
- "@sap-ux/project-access": "2.3.1",
32
- "@sap-ux/odata-service-inquirer": "3.2.27",
33
- "@sap-ux/fiori-elements-writer": "3.1.85",
34
- "@sap-ux/fiori-freestyle-writer": "3.0.128",
28
+ "@sap-ux/fiori-generator-shared": "1.2.24",
29
+ "@sap-ux/inquirer-common": "1.0.50",
30
+ "@sap-ux/odata-service-inquirer": "3.2.28",
31
+ "@sap-ux/project-access": "2.3.2",
32
+ "@sap-ux/fiori-elements-writer": "3.1.87",
33
+ "@sap-ux/fiori-freestyle-writer": "3.0.130",
35
34
  "@sap-ux/logger": "1.0.3",
36
- "@sap-ux/project-input-validator": "1.0.24",
37
- "@sap-ux/launch-config": "1.0.25",
35
+ "@sap-ux/project-input-validator": "1.0.25",
36
+ "@sap-ux/launch-config": "1.0.26",
38
37
  "@sap-ux/fiori-tools-settings": "1.1.1",
39
- "@sap-ux/abap-deploy-config-writer": "1.0.33",
38
+ "@sap-ux/abap-deploy-config-writer": "1.0.34",
40
39
  "@sap-ux/btp-utils": "2.0.8",
41
40
  "@sap-ux/ui5-info": "1.0.7",
42
- "@sap-ux/axios-extension": "2.1.3",
41
+ "@sap-ux/axios-extension": "2.1.4",
42
+ "@sap-ux/system-access": "1.0.18",
43
43
  "@sap-ux/store": "2.0.7",
44
- "@sap-ux/system-access": "1.0.17",
45
- "@sap-ux/guided-answers-helper": "1.0.2"
44
+ "@sap-ux/guided-answers-helper": "1.0.2",
45
+ "@sap-ux/feature-toggle": "1.0.5"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@jest/globals": "30.4.1",
@@ -67,7 +67,7 @@
67
67
  "unionfs": "4.6.0",
68
68
  "yeoman-environment": "3.19.3",
69
69
  "yeoman-test": "6.3.0",
70
- "@sap-ux/nodejs-utils": "1.0.12",
70
+ "@sap-ux/nodejs-utils": "1.0.13",
71
71
  "@sap-ux/store": "2.0.7",
72
72
  "@sap-ux/ui5-config": "1.0.10"
73
73
  },