@sap-ux/create 1.3.27 → 1.4.2
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/README.md +4 -2
- package/dist/cli/add/system.js +18 -8
- package/dist/cli/get/system.js +7 -2
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +4 -1
- package/dist/cli/remove/system.js +7 -3
- package/dist/cli/update/system.js +51 -13
- package/dist/cli/utils/system-connection.d.ts +26 -20
- package/dist/cli/utils/system-connection.js +91 -27
- package/dist/cli/utils/system-lookup.d.ts +17 -0
- package/dist/cli/utils/system-lookup.js +94 -0
- package/dist/cli/utils/system-prompts.d.ts +20 -11
- package/dist/cli/utils/system-prompts.js +108 -87
- package/dist/i18n.d.ts +15 -0
- package/dist/i18n.js +41 -0
- package/dist/index.js +2 -1
- package/dist/translations/ux-create.i18n.json +68 -0
- package/package.json +16 -15
package/README.md
CHANGED
|
@@ -314,7 +314,8 @@ Options:
|
|
|
314
314
|
- `--connection-type <string>` - Connection type (abap_catalog | generic_host | odata_service) _(default: `abap_catalog`)_
|
|
315
315
|
- `--username <string>` - Username for basic authentication
|
|
316
316
|
- `--password <string>` - To avoid plain-text credentials in the shell's history, pass an env reference: --password env:MY_VAR
|
|
317
|
-
- `--skip-
|
|
317
|
+
- `--skip-credentials-prompt` - Skip credential prompts. No credentials will be saved, but may be added later if required
|
|
318
|
+
- `--skip-connection-validation` - Skip connection verification before saving
|
|
318
319
|
|
|
319
320
|
--------------------------------
|
|
320
321
|
|
|
@@ -456,7 +457,8 @@ Options:
|
|
|
456
457
|
- `--username <string>` - New username
|
|
457
458
|
- `--password <string>` - To avoid plain-text credentials in the shell's history, pass an env reference: --password env:MY_VAR
|
|
458
459
|
- `--clear-credentials` - Remove stored credentials from the system
|
|
459
|
-
- `--skip-
|
|
460
|
+
- `--skip-connection-validation` - Skip connection verification before saving
|
|
461
|
+
- `--skip-check` - (Deprecated: use --skip-connection-validation) Skip connection verification before saving
|
|
460
462
|
|
|
461
463
|
--------------------------------
|
|
462
464
|
|
package/dist/cli/add/system.js
CHANGED
|
@@ -6,6 +6,7 @@ import { validateClient } from '@sap-ux/project-input-validator';
|
|
|
6
6
|
import { getLogger } from '../../tracing/index.js';
|
|
7
7
|
import { promptForSystemConfig } from '../utils/system-prompts.js';
|
|
8
8
|
import { checkConnectionOrPrompt } from '../utils/system-connection.js';
|
|
9
|
+
import { t } from '../../i18n.js';
|
|
9
10
|
/**
|
|
10
11
|
* Add the "add system" subcommand to a passed command.
|
|
11
12
|
* Adds a new backend system to the saved systems store (~/.fioritools).
|
|
@@ -31,7 +32,8 @@ Example:
|
|
|
31
32
|
.option('--connection-type <string>', `Connection type (${Object.values(ConnectionType).join(' | ')})`, ConnectionType.AbapCatalog)
|
|
32
33
|
.option('--username <string>', 'Username for basic authentication')
|
|
33
34
|
.option('--password <string>', "To avoid plain-text credentials in the shell's history, pass an env reference: --password env:MY_VAR")
|
|
34
|
-
.option('--skip-
|
|
35
|
+
.option('--skip-credentials-prompt', 'Skip credential prompts. No credentials will be saved, but may be added later if required')
|
|
36
|
+
.option('--skip-connection-validation', 'Skip connection verification before saving')
|
|
35
37
|
.action(async (options) => {
|
|
36
38
|
loadEnvConfig();
|
|
37
39
|
await addSystem({
|
|
@@ -43,7 +45,8 @@ Example:
|
|
|
43
45
|
connectionType: options.connectionType,
|
|
44
46
|
username: options.username,
|
|
45
47
|
password: options.password,
|
|
46
|
-
|
|
48
|
+
skipCredentialsPrompt: !!options.skipCredentialsPrompt,
|
|
49
|
+
skipConnectionValidation: !!options.skipConnectionValidation
|
|
47
50
|
});
|
|
48
51
|
});
|
|
49
52
|
}
|
|
@@ -62,7 +65,7 @@ Example:
|
|
|
62
65
|
*/
|
|
63
66
|
function validateSystemConfig(config, logger) {
|
|
64
67
|
if (!config.name || !config.url || !config.systemType || !config.authenticationType || !config.connectionType) {
|
|
65
|
-
logger.error('Missing required fields.
|
|
68
|
+
logger.error('Missing required fields.');
|
|
66
69
|
return false;
|
|
67
70
|
}
|
|
68
71
|
if (config.name.trim().length === 0) {
|
|
@@ -83,6 +86,8 @@ function validateSystemConfig(config, logger) {
|
|
|
83
86
|
return false;
|
|
84
87
|
}
|
|
85
88
|
}
|
|
89
|
+
// Validate that provided type values are valid enum members
|
|
90
|
+
// (TypeScript types don't prevent invalid runtime values from CLI flags)
|
|
86
91
|
const validSystemTypes = Object.values(SystemType);
|
|
87
92
|
if (!validSystemTypes.includes(config.systemType)) {
|
|
88
93
|
logger.error(`Invalid system type '${config.systemType}'. Valid values: ${validSystemTypes.join(', ')}`);
|
|
@@ -137,7 +142,8 @@ async function checkForDuplicates(config, service, logger) {
|
|
|
137
142
|
* @param params.connectionType - connection type
|
|
138
143
|
* @param params.username - optional username for basic auth
|
|
139
144
|
* @param params.password - optional password for basic auth
|
|
140
|
-
* @param params.
|
|
145
|
+
* @param params.skipCredentialsPrompt - skip credential prompts entirely
|
|
146
|
+
* @param params.skipConnectionValidation - skip connection verification
|
|
141
147
|
*/
|
|
142
148
|
async function addSystem(params) {
|
|
143
149
|
const logger = getLogger();
|
|
@@ -154,14 +160,17 @@ async function addSystem(params) {
|
|
|
154
160
|
authenticationType: params.authenticationType,
|
|
155
161
|
connectionType: params.connectionType,
|
|
156
162
|
username: params.username,
|
|
157
|
-
password: params.password
|
|
163
|
+
password: params.password,
|
|
164
|
+
skipCredentialsPrompt: params.skipCredentialsPrompt
|
|
158
165
|
});
|
|
159
166
|
replaceEnvVariables(config);
|
|
160
167
|
if (!validateSystemConfig(config, logger)) {
|
|
168
|
+
logger.info(t('systemActions.systemNotAddedInvalidConfig'));
|
|
161
169
|
return;
|
|
162
170
|
}
|
|
163
171
|
const service = await getService({ entityName: 'system' });
|
|
164
172
|
if (!(await checkForDuplicates(config, service, logger))) {
|
|
173
|
+
logger.info(t('systemActions.systemNotAddedDuplicate'));
|
|
165
174
|
return;
|
|
166
175
|
}
|
|
167
176
|
const shouldSave = await checkConnectionOrPrompt({
|
|
@@ -169,11 +178,12 @@ async function addSystem(params) {
|
|
|
169
178
|
client: config.client,
|
|
170
179
|
systemType: config.systemType,
|
|
171
180
|
authenticationType: config.authenticationType,
|
|
181
|
+
connectionType: config.connectionType,
|
|
172
182
|
username: config.username,
|
|
173
183
|
password: config.password
|
|
174
|
-
}, params.
|
|
184
|
+
}, params.skipConnectionValidation || false);
|
|
175
185
|
if (!shouldSave) {
|
|
176
|
-
logger.info('
|
|
186
|
+
logger.info(t('systemActions.systemNotSaved'));
|
|
177
187
|
return;
|
|
178
188
|
}
|
|
179
189
|
const system = new BackendSystem({
|
|
@@ -187,7 +197,7 @@ async function addSystem(params) {
|
|
|
187
197
|
password: config.password
|
|
188
198
|
});
|
|
189
199
|
await service.write(system);
|
|
190
|
-
logger.info(
|
|
200
|
+
logger.info(t('systemActions.systemAdded', { name: config.name }));
|
|
191
201
|
}
|
|
192
202
|
catch (error) {
|
|
193
203
|
logger.error(error.message);
|
package/dist/cli/get/system.js
CHANGED
|
@@ -2,6 +2,7 @@ import { isAppStudio } from '@sap-ux/btp-utils';
|
|
|
2
2
|
import { getService, BackendSystemKey } from '@sap-ux/store';
|
|
3
3
|
import { NullTransport, ToolsLogger } from '@sap-ux/logger';
|
|
4
4
|
import { getLogger } from '../../tracing/index.js';
|
|
5
|
+
import { findSystemByUrl } from '../utils/system-lookup.js';
|
|
5
6
|
/**
|
|
6
7
|
* Add the "get system" subcommand to a passed command.
|
|
7
8
|
* Retrieves details of a saved backend system identified by its URL and optional client.
|
|
@@ -45,9 +46,10 @@ async function getSystem(url, client, asJson) {
|
|
|
45
46
|
entityName: 'system',
|
|
46
47
|
logger: storeLogger
|
|
47
48
|
});
|
|
48
|
-
|
|
49
|
-
const system = await service
|
|
49
|
+
// Use smart lookup to handle client mismatch scenarios
|
|
50
|
+
const system = await findSystemByUrl(url, client, service);
|
|
50
51
|
if (!system) {
|
|
52
|
+
const key = new BackendSystemKey({ url, client });
|
|
51
53
|
logger.error(`System not found: ${key.getId()}`);
|
|
52
54
|
return;
|
|
53
55
|
}
|
|
@@ -78,6 +80,9 @@ async function getSystem(url, client, asJson) {
|
|
|
78
80
|
if (system.hasSensitiveData) {
|
|
79
81
|
logger.info(`Credentials stored securely.`);
|
|
80
82
|
}
|
|
83
|
+
else {
|
|
84
|
+
logger.info(`No credentials stored.`);
|
|
85
|
+
}
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
88
|
catch (error) {
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Command } from 'commander';
|
|
|
4
4
|
*
|
|
5
5
|
* @param argv - arguments, typically 'process.argv'
|
|
6
6
|
*/
|
|
7
|
-
export declare function handleCreateFioriCommand(argv: string[]): void
|
|
7
|
+
export declare function handleCreateFioriCommand(argv: string[]): Promise<void>;
|
|
8
8
|
/**
|
|
9
9
|
* Generates the full MCP specification for a top-level commander program.
|
|
10
10
|
*
|
package/dist/cli/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
4
4
|
import { Command } from 'commander';
|
|
5
5
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
6
|
import { getLogger } from '../tracing/index.js';
|
|
7
|
+
import { initI18n } from '../i18n.js';
|
|
7
8
|
import { getAddCommands } from './add/index.js';
|
|
8
9
|
import { getRemoveCommands } from './remove/index.js';
|
|
9
10
|
import { getGenerateCommands } from './generate/index.js';
|
|
@@ -24,12 +25,14 @@ import { getUpdateCommands } from './update/index.js';
|
|
|
24
25
|
*
|
|
25
26
|
* @param argv - arguments, typically 'process.argv'
|
|
26
27
|
*/
|
|
27
|
-
export function handleCreateFioriCommand(argv) {
|
|
28
|
+
export async function handleCreateFioriCommand(argv) {
|
|
28
29
|
const logger = getLogger();
|
|
29
30
|
if (!Array.isArray(argv) || argv.length < 2) {
|
|
30
31
|
throw Error(`This function must be called from command line interface (cli). Or provide meaningful arguments.`);
|
|
31
32
|
}
|
|
32
33
|
try {
|
|
34
|
+
// Initialize i18n before executing commands
|
|
35
|
+
await initI18n();
|
|
33
36
|
const program = getCommanderProgram();
|
|
34
37
|
program.parse(argv);
|
|
35
38
|
}
|
|
@@ -2,6 +2,8 @@ import { isAppStudio } from '@sap-ux/btp-utils';
|
|
|
2
2
|
import { getService, BackendSystemKey } from '@sap-ux/store';
|
|
3
3
|
import { getLogger } from '../../tracing/index.js';
|
|
4
4
|
import { promptForSystemIdentifier, promptForRemoveConfirmation } from '../utils/system-prompts.js';
|
|
5
|
+
import { findSystemByUrl } from '../utils/system-lookup.js';
|
|
6
|
+
import { t } from '../../i18n.js';
|
|
5
7
|
/**
|
|
6
8
|
* Add the "remove system" subcommand to a passed command.
|
|
7
9
|
* Removes a saved backend system from the store (~/.fioritools) and deletes its credentials from the OS keychain.
|
|
@@ -39,9 +41,10 @@ async function removeSystem(url, client, force) {
|
|
|
39
41
|
// Prompt for system identifier if not provided
|
|
40
42
|
const identifier = await promptForSystemIdentifier({ url, client });
|
|
41
43
|
const service = await getService({ entityName: 'system' });
|
|
42
|
-
|
|
43
|
-
const system = await service
|
|
44
|
+
// Use smart lookup to handle client mismatch scenarios
|
|
45
|
+
const system = await findSystemByUrl(identifier.url, identifier.client, service);
|
|
44
46
|
if (!system) {
|
|
47
|
+
const key = new BackendSystemKey({ url: identifier.url, client: identifier.client });
|
|
45
48
|
logger.error(`System not found: ${key.getId()}`);
|
|
46
49
|
return;
|
|
47
50
|
}
|
|
@@ -55,9 +58,10 @@ async function removeSystem(url, client, force) {
|
|
|
55
58
|
}
|
|
56
59
|
const deleted = await service.delete(system);
|
|
57
60
|
if (deleted) {
|
|
58
|
-
logger.info(
|
|
61
|
+
logger.info(t('systemActions.systemRemoved', { name: system.name }));
|
|
59
62
|
}
|
|
60
63
|
else {
|
|
64
|
+
const key = new BackendSystemKey({ url: system.url, client: system.client });
|
|
61
65
|
logger.error(`Failed to remove system: ${key.getId()}`);
|
|
62
66
|
}
|
|
63
67
|
}
|
|
@@ -3,8 +3,10 @@ import { getService, BackendSystemKey, isSystemNameInUse } from '@sap-ux/store';
|
|
|
3
3
|
import { replaceEnvVariables } from '@sap-ux/ui5-config';
|
|
4
4
|
import { config as loadEnvConfig } from 'dotenv';
|
|
5
5
|
import { getLogger } from '../../tracing/index.js';
|
|
6
|
-
import { promptForSystemIdentifier, promptForUpdateFields, promptForFieldUpdates } from '../utils/system-prompts.js';
|
|
6
|
+
import { promptForSystemIdentifier, promptForUpdateFields, promptForFieldUpdates, ClearCredentialsCancelledError } from '../utils/system-prompts.js';
|
|
7
7
|
import { checkConnectionOrPrompt } from '../utils/system-connection.js';
|
|
8
|
+
import { findSystemByUrl } from '../utils/system-lookup.js';
|
|
9
|
+
import { t } from '../../i18n.js';
|
|
8
10
|
/**
|
|
9
11
|
* Add the "update system" subcommand to a passed command.
|
|
10
12
|
* Updates an existing backend system in the saved systems store (~/.fioritools).
|
|
@@ -26,7 +28,8 @@ Example:
|
|
|
26
28
|
.option('--username <string>', 'New username')
|
|
27
29
|
.option('--password <string>', "To avoid plain-text credentials in the shell's history, pass an env reference: --password env:MY_VAR")
|
|
28
30
|
.option('--clear-credentials', 'Remove stored credentials from the system')
|
|
29
|
-
.option('--skip-
|
|
31
|
+
.option('--skip-connection-validation', 'Skip connection verification before saving')
|
|
32
|
+
.option('--skip-check', '(Deprecated: use --skip-connection-validation) Skip connection verification before saving')
|
|
30
33
|
.action(async (options) => {
|
|
31
34
|
loadEnvConfig();
|
|
32
35
|
await updateSystem({
|
|
@@ -36,7 +39,7 @@ Example:
|
|
|
36
39
|
username: options.username,
|
|
37
40
|
password: options.password,
|
|
38
41
|
clearCredentials: !!options.clearCredentials,
|
|
39
|
-
|
|
42
|
+
skipConnectionValidation: !!options.skipConnectionValidation || !!options.skipCheck
|
|
40
43
|
});
|
|
41
44
|
});
|
|
42
45
|
}
|
|
@@ -103,7 +106,25 @@ async function determinePatch(params, existing, logger) {
|
|
|
103
106
|
return await buildPatchFromParams(params, existing, logger);
|
|
104
107
|
}
|
|
105
108
|
const fieldsToUpdate = await promptForUpdateFields(existing);
|
|
106
|
-
|
|
109
|
+
let updateValues;
|
|
110
|
+
try {
|
|
111
|
+
updateValues = await promptForFieldUpdates(fieldsToUpdate, existing);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
// User cancelled credential clearing operation
|
|
115
|
+
if (err instanceof ClearCredentialsCancelledError) {
|
|
116
|
+
logger.info(t('systemActions.operationCancelled'));
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
121
|
+
// Check if clearCredentials was selected in interactive mode
|
|
122
|
+
if (updateValues.clearCredentials) {
|
|
123
|
+
updateValues.username = '';
|
|
124
|
+
updateValues.password = '';
|
|
125
|
+
delete updateValues.clearCredentials;
|
|
126
|
+
}
|
|
127
|
+
return updateValues;
|
|
107
128
|
}
|
|
108
129
|
/**
|
|
109
130
|
* Verifies connection if credentials are being updated.
|
|
@@ -112,22 +133,35 @@ async function determinePatch(params, existing, logger) {
|
|
|
112
133
|
* @param existing - existing system
|
|
113
134
|
* @param params - update parameters
|
|
114
135
|
* @param params.clearCredentials - whether credentials are being cleared
|
|
115
|
-
* @param params.
|
|
136
|
+
* @param params.skipConnectionValidation - whether to skip connection check
|
|
116
137
|
* @returns true if should proceed, false otherwise
|
|
117
138
|
*/
|
|
118
139
|
async function verifyCredentialsUpdate(patch, existing, params) {
|
|
119
|
-
|
|
120
|
-
|
|
140
|
+
// Check if credentials are being updated (set to new values or cleared)
|
|
141
|
+
const hasUsernameChange = patch.username !== undefined;
|
|
142
|
+
const hasPasswordChange = patch.password !== undefined;
|
|
143
|
+
const updatingCredentials = hasUsernameChange || hasPasswordChange;
|
|
144
|
+
// Skip verification if:
|
|
145
|
+
// 1. No credential changes at all, OR
|
|
146
|
+
// 2. Explicitly clearing credentials (--clear-credentials flag), OR
|
|
147
|
+
// 3. Setting both to empty strings (clearing via interactive prompts)
|
|
148
|
+
if (!updatingCredentials) {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
const clearingCredentials = params.clearCredentials || (patch.username === '' && patch.password === '');
|
|
152
|
+
if (clearingCredentials) {
|
|
121
153
|
return true;
|
|
122
154
|
}
|
|
155
|
+
// Otherwise, verify the new credentials work
|
|
123
156
|
return await checkConnectionOrPrompt({
|
|
124
157
|
url: existing.url,
|
|
125
158
|
client: existing.client,
|
|
126
159
|
systemType: existing.systemType,
|
|
127
160
|
authenticationType: existing.authenticationType || 'basic',
|
|
161
|
+
connectionType: existing.connectionType,
|
|
128
162
|
username: patch.username ?? existing.username,
|
|
129
163
|
password: patch.password ?? existing.password
|
|
130
|
-
}, params.
|
|
164
|
+
}, params.skipConnectionValidation || false);
|
|
131
165
|
}
|
|
132
166
|
/**
|
|
133
167
|
* Updates a backend system in the saved systems store.
|
|
@@ -139,7 +173,7 @@ async function verifyCredentialsUpdate(patch, existing, params) {
|
|
|
139
173
|
* @param params.username - optional new username
|
|
140
174
|
* @param params.password - optional new password
|
|
141
175
|
* @param params.clearCredentials - if true, clears stored credentials
|
|
142
|
-
* @param params.
|
|
176
|
+
* @param params.skipConnectionValidation - skip connection verification
|
|
143
177
|
*/
|
|
144
178
|
async function updateSystem(params) {
|
|
145
179
|
const logger = getLogger();
|
|
@@ -153,28 +187,32 @@ async function updateSystem(params) {
|
|
|
153
187
|
client: params.client
|
|
154
188
|
});
|
|
155
189
|
const service = await getService({ entityName: 'system' });
|
|
156
|
-
|
|
157
|
-
const existing = await service
|
|
190
|
+
// Use smart lookup to handle client mismatch scenarios
|
|
191
|
+
const existing = await findSystemByUrl(identifier.url, identifier.client, service);
|
|
158
192
|
if (!existing) {
|
|
193
|
+
const key = new BackendSystemKey({ url: identifier.url, client: identifier.client });
|
|
159
194
|
logger.error(`System not found: ${key.getId()}`);
|
|
160
195
|
return;
|
|
161
196
|
}
|
|
162
197
|
const patchRecord = await determinePatch(params, existing, logger);
|
|
163
198
|
if (!patchRecord) {
|
|
199
|
+
logger.info(t('systemActions.systemNotUpdated'));
|
|
164
200
|
return;
|
|
165
201
|
}
|
|
166
202
|
replaceEnvVariables(patchRecord);
|
|
167
203
|
if (!Object.keys(patchRecord).length) {
|
|
168
204
|
logger.error('No fields to update. Provide at least one of: --name, --username, --password, --clear-credentials');
|
|
205
|
+
logger.info(t('systemActions.systemNotUpdated'));
|
|
169
206
|
return;
|
|
170
207
|
}
|
|
171
208
|
const patch = patchRecord;
|
|
172
209
|
if (!(await verifyCredentialsUpdate(patch, existing, params))) {
|
|
173
|
-
logger.info('
|
|
210
|
+
logger.info(t('systemActions.systemNotUpdated'));
|
|
174
211
|
return;
|
|
175
212
|
}
|
|
213
|
+
const key = new BackendSystemKey({ url: existing.url, client: existing.client });
|
|
176
214
|
await service.partialUpdate(key, patch);
|
|
177
|
-
logger.info(
|
|
215
|
+
logger.info(t('systemActions.systemUpdated', { name: existing.name }));
|
|
178
216
|
}
|
|
179
217
|
catch (error) {
|
|
180
218
|
logger.error(error.message);
|
|
@@ -1,20 +1,24 @@
|
|
|
1
|
+
import type { SystemType } from '@sap-ux/store';
|
|
2
|
+
import { AuthenticationType, ConnectionType } from '@sap-ux/store';
|
|
1
3
|
/**
|
|
2
|
-
* Checks connection to a backend system.
|
|
4
|
+
* Checks connection to a backend system by validating based on the connection type.
|
|
3
5
|
*
|
|
4
6
|
* @param config - System configuration to test
|
|
5
|
-
* @param config.url
|
|
6
|
-
* @param config.client
|
|
7
|
-
* @param config.systemType
|
|
8
|
-
* @param config.authenticationType
|
|
9
|
-
* @param config.
|
|
10
|
-
* @param config.
|
|
7
|
+
* @param config.url - System URL
|
|
8
|
+
* @param config.client - SAP client (optional)
|
|
9
|
+
* @param config.systemType - System type (OnPrem, AbapCloud, etc.)
|
|
10
|
+
* @param config.authenticationType - Authentication type
|
|
11
|
+
* @param config.connectionType - Connection type (determines validation method)
|
|
12
|
+
* @param config.username - Username for basic auth (optional)
|
|
13
|
+
* @param config.password - Password for basic auth (optional)
|
|
11
14
|
* @returns Connection check result with success status and optional error message
|
|
12
15
|
*/
|
|
13
16
|
export declare function checkSystemConnection(config: {
|
|
14
17
|
url: string;
|
|
15
18
|
client?: string;
|
|
16
|
-
systemType:
|
|
17
|
-
authenticationType:
|
|
19
|
+
systemType: SystemType;
|
|
20
|
+
authenticationType: AuthenticationType;
|
|
21
|
+
connectionType: ConnectionType;
|
|
18
22
|
username?: string;
|
|
19
23
|
password?: string;
|
|
20
24
|
}): Promise<{
|
|
@@ -23,24 +27,26 @@ export declare function checkSystemConnection(config: {
|
|
|
23
27
|
}>;
|
|
24
28
|
/**
|
|
25
29
|
* Checks connection to a backend system, or prompts user whether to save anyway if check fails.
|
|
26
|
-
* If
|
|
30
|
+
* If skipConnectionValidation is true, always returns true without checking.
|
|
27
31
|
*
|
|
28
32
|
* @param config - System configuration to test
|
|
29
|
-
* @param config.url
|
|
30
|
-
* @param config.client
|
|
31
|
-
* @param config.systemType
|
|
32
|
-
* @param config.authenticationType
|
|
33
|
-
* @param config.
|
|
34
|
-
* @param config.
|
|
35
|
-
* @param
|
|
33
|
+
* @param config.url - System URL
|
|
34
|
+
* @param config.client - SAP client (optional)
|
|
35
|
+
* @param config.systemType - System type (OnPrem, AbapCloud, etc.)
|
|
36
|
+
* @param config.authenticationType - Authentication type
|
|
37
|
+
* @param config.connectionType - Connection type (determines validation method)
|
|
38
|
+
* @param config.username - Username for basic auth (optional)
|
|
39
|
+
* @param config.password - Password for basic auth (optional)
|
|
40
|
+
* @param skipConnectionValidation - If true, skip the connection check
|
|
36
41
|
* @returns True if connection succeeded or user chose to save anyway, false if user chose not to save
|
|
37
42
|
*/
|
|
38
43
|
export declare function checkConnectionOrPrompt(config: {
|
|
39
44
|
url: string;
|
|
40
45
|
client?: string;
|
|
41
|
-
systemType:
|
|
42
|
-
authenticationType:
|
|
46
|
+
systemType: SystemType;
|
|
47
|
+
authenticationType: AuthenticationType;
|
|
48
|
+
connectionType: ConnectionType;
|
|
43
49
|
username?: string;
|
|
44
50
|
password?: string;
|
|
45
|
-
},
|
|
51
|
+
}, skipConnectionValidation: boolean): Promise<boolean>;
|
|
46
52
|
//# sourceMappingURL=system-connection.d.ts.map
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import prompts from 'prompts';
|
|
2
|
+
import { createAbapServiceProvider } from '@sap-ux/system-access';
|
|
3
|
+
import { ErrorHandler } from '@sap-ux/inquirer-common';
|
|
4
|
+
import { AuthenticationType, ConnectionType } from '@sap-ux/store';
|
|
5
|
+
import { ODataVersion } from '@sap-ux/axios-extension';
|
|
2
6
|
import { getLogger } from '../../tracing/index.js';
|
|
7
|
+
import { t } from '../../i18n.js';
|
|
3
8
|
/**
|
|
4
|
-
* Checks connection to a backend system.
|
|
9
|
+
* Checks connection to a backend system by validating based on the connection type.
|
|
5
10
|
*
|
|
6
11
|
* @param config - System configuration to test
|
|
7
|
-
* @param config.url
|
|
8
|
-
* @param config.client
|
|
9
|
-
* @param config.systemType
|
|
10
|
-
* @param config.authenticationType
|
|
11
|
-
* @param config.
|
|
12
|
-
* @param config.
|
|
12
|
+
* @param config.url - System URL
|
|
13
|
+
* @param config.client - SAP client (optional)
|
|
14
|
+
* @param config.systemType - System type (OnPrem, AbapCloud, etc.)
|
|
15
|
+
* @param config.authenticationType - Authentication type
|
|
16
|
+
* @param config.connectionType - Connection type (determines validation method)
|
|
17
|
+
* @param config.username - Username for basic auth (optional)
|
|
18
|
+
* @param config.password - Password for basic auth (optional)
|
|
13
19
|
* @returns Connection check result with success status and optional error message
|
|
14
20
|
*/
|
|
15
21
|
export async function checkSystemConnection(config) {
|
|
@@ -18,44 +24,102 @@ export async function checkSystemConnection(config) {
|
|
|
18
24
|
new URL(config.url);
|
|
19
25
|
}
|
|
20
26
|
catch {
|
|
21
|
-
return { success: false, error:
|
|
27
|
+
return { success: false, error: t('systemConnection.invalidUrl', { url: config.url }) };
|
|
28
|
+
}
|
|
29
|
+
// Attempt actual connection check
|
|
30
|
+
try {
|
|
31
|
+
const logger = getLogger();
|
|
32
|
+
// Build target configuration for system-access
|
|
33
|
+
const target = {
|
|
34
|
+
url: config.url,
|
|
35
|
+
client: config.client,
|
|
36
|
+
authenticationType: config.authenticationType
|
|
37
|
+
};
|
|
38
|
+
// Build request options with auth if provided
|
|
39
|
+
// For basic auth with credentials, include them
|
|
40
|
+
// For reentranceTicket/oauth2, omit auth (will get 401 but proves reachability)
|
|
41
|
+
const requestOptions = config.authenticationType === AuthenticationType.Basic && config.username && config.password
|
|
42
|
+
? {
|
|
43
|
+
auth: {
|
|
44
|
+
username: config.username,
|
|
45
|
+
password: config.password
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
: undefined;
|
|
49
|
+
// Create service provider using system-access utilities
|
|
50
|
+
// prompt=false because we're in non-interactive connection check mode
|
|
51
|
+
const service = await createAbapServiceProvider(target, requestOptions, false, logger);
|
|
52
|
+
// Validate based on connection type
|
|
53
|
+
if (config.connectionType === ConnectionType.AbapCatalog) {
|
|
54
|
+
// For ABAP catalog connections, request catalog services
|
|
55
|
+
const catalog = service.catalog(ODataVersion.v2);
|
|
56
|
+
const services = await catalog.listServices();
|
|
57
|
+
logger.info(t('systemConnection.catalogServicesFound', { count: services.length }));
|
|
58
|
+
return { success: true };
|
|
59
|
+
}
|
|
60
|
+
else if (config.connectionType === ConnectionType.ODataService) {
|
|
61
|
+
// For OData service URLs, attempt metadata request
|
|
62
|
+
const response = await service.get('/$metadata', { timeout: 5000 });
|
|
63
|
+
if (response.status === 200) {
|
|
64
|
+
logger.info(t('systemConnection.metadataRequestSuccessful'));
|
|
65
|
+
return { success: true };
|
|
66
|
+
}
|
|
67
|
+
return { success: false, error: t('systemConnection.unexpectedStatus', { status: response.status }) };
|
|
68
|
+
}
|
|
69
|
+
else if (config.connectionType === ConnectionType.GenericHost) {
|
|
70
|
+
// For generic host, basic connectivity check
|
|
71
|
+
await service.get('/', { timeout: 5000 });
|
|
72
|
+
return { success: true };
|
|
73
|
+
}
|
|
74
|
+
return { success: true };
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// 401 means system is reachable but requires auth - treat as success
|
|
78
|
+
if (error.response?.status === 401) {
|
|
79
|
+
return { success: true };
|
|
80
|
+
}
|
|
81
|
+
// Use ErrorHandler for comprehensive error analysis
|
|
82
|
+
const errorHandler = new ErrorHandler(getLogger(), false);
|
|
83
|
+
const errorMsg = errorHandler.logErrorMsgs(error, undefined, false);
|
|
84
|
+
// For other errors, provide detailed message
|
|
85
|
+
return {
|
|
86
|
+
success: false,
|
|
87
|
+
error: errorMsg || t('systemConnection.unknownError')
|
|
88
|
+
};
|
|
22
89
|
}
|
|
23
|
-
// For now, we just validate the URL format
|
|
24
|
-
// A real implementation would attempt to connect to the backend
|
|
25
|
-
// using the provided credentials and check if the system is reachable
|
|
26
|
-
return { success: true };
|
|
27
90
|
}
|
|
28
91
|
/**
|
|
29
92
|
* Checks connection to a backend system, or prompts user whether to save anyway if check fails.
|
|
30
|
-
* If
|
|
93
|
+
* If skipConnectionValidation is true, always returns true without checking.
|
|
31
94
|
*
|
|
32
95
|
* @param config - System configuration to test
|
|
33
|
-
* @param config.url
|
|
34
|
-
* @param config.client
|
|
35
|
-
* @param config.systemType
|
|
36
|
-
* @param config.authenticationType
|
|
37
|
-
* @param config.
|
|
38
|
-
* @param config.
|
|
39
|
-
* @param
|
|
96
|
+
* @param config.url - System URL
|
|
97
|
+
* @param config.client - SAP client (optional)
|
|
98
|
+
* @param config.systemType - System type (OnPrem, AbapCloud, etc.)
|
|
99
|
+
* @param config.authenticationType - Authentication type
|
|
100
|
+
* @param config.connectionType - Connection type (determines validation method)
|
|
101
|
+
* @param config.username - Username for basic auth (optional)
|
|
102
|
+
* @param config.password - Password for basic auth (optional)
|
|
103
|
+
* @param skipConnectionValidation - If true, skip the connection check
|
|
40
104
|
* @returns True if connection succeeded or user chose to save anyway, false if user chose not to save
|
|
41
105
|
*/
|
|
42
|
-
export async function checkConnectionOrPrompt(config,
|
|
106
|
+
export async function checkConnectionOrPrompt(config, skipConnectionValidation) {
|
|
43
107
|
const logger = getLogger();
|
|
44
|
-
if (
|
|
45
|
-
logger.info(
|
|
108
|
+
if (skipConnectionValidation) {
|
|
109
|
+
logger.info(t('systemConnection.skippingCheck'));
|
|
46
110
|
return true;
|
|
47
111
|
}
|
|
48
|
-
logger.info('
|
|
112
|
+
logger.info(t('systemConnection.verifying'));
|
|
49
113
|
const result = await checkSystemConnection(config);
|
|
50
114
|
if (result.success) {
|
|
51
|
-
logger.info('
|
|
115
|
+
logger.info(t('systemConnection.connectionSuccessful'));
|
|
52
116
|
return true;
|
|
53
117
|
}
|
|
54
|
-
logger.warn(
|
|
118
|
+
logger.warn(t('systemConnection.connectionFailed', { error: result.error ?? t('systemConnection.unknownError') }));
|
|
55
119
|
const answer = await prompts({
|
|
56
120
|
type: 'confirm',
|
|
57
121
|
name: 'saveAnyway',
|
|
58
|
-
message: '
|
|
122
|
+
message: t('systemConnection.saveAnywayPrompt'),
|
|
59
123
|
initial: false
|
|
60
124
|
});
|
|
61
125
|
return answer.saveAnyway === true;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BackendSystem, BackendSystemKey, Service } from '@sap-ux/store';
|
|
2
|
+
/**
|
|
3
|
+
* Finds a backend system by URL with smart matching when client is not specified or doesn't match.
|
|
4
|
+
*
|
|
5
|
+
* Logic:
|
|
6
|
+
* 1. Find all systems with the same URL (across all connection types).
|
|
7
|
+
* 2. If exactly one system found, use it automatically.
|
|
8
|
+
* 3. If multiple systems found, try exact match first, then prompt user to select if no exact match.
|
|
9
|
+
* 4. If none found, return undefined.
|
|
10
|
+
*
|
|
11
|
+
* @param url - URL of the backend system
|
|
12
|
+
* @param client - optional SAP client
|
|
13
|
+
* @param service - backend system service
|
|
14
|
+
* @returns the matched system, or undefined if not found or user cancelled
|
|
15
|
+
*/
|
|
16
|
+
export declare function findSystemByUrl(url: string, client: string | undefined, service: Service<BackendSystem, BackendSystemKey>): Promise<BackendSystem | undefined>;
|
|
17
|
+
//# sourceMappingURL=system-lookup.d.ts.map
|