generated from SAP/repository-template
-
Notifications
You must be signed in to change notification settings - Fork 55
feat: Prompt for credentials when not available in VSCode for adp generator #4052
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+549
−11
Merged
Changes from 9 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
3c05011
feat: check system for missing credentials
IvoSG 5942c79
feat: add prompt for store credentials and integrate credentials prom…
IvoSG ff7a2a4
test: cover new functionallity with unit tests
IvoSG f43948d
fix: lint errors
IvoSG 483d222
chore:add cset
IvoSG 84bdaec
fix: move store credential to adp-tooling, remove redundant tests and…
IvoSG e0de534
test: check for storeCredentials
IvoSG 1e1fa8c
fix: use ConfigAnswers type
IvoSG 1fa049e
test: adapt tests to the ConfigAnswers type
IvoSG 01da720
fix: change label text
IvoSG cd695da
Merge branch 'main' into feat/adp-prompt-for-credentials
IvoSG 53f54fb
Merge branch 'main' into feat/adp-prompt-for-credentials
IvoSG 6c83750
test: enhanced coverage
IvoSG 8e3ea78
Add userDisplayName to backend system configuration
IvoSG d8c8c5f
Merge branch 'main' into feat/adp-prompt-for-credentials
IvoSG 6c5b6a0
Merge branch 'main' into feat/adp-prompt-for-credentials
IvoSG 5823709
Merge branch 'main' into feat/adp-prompt-for-credentials
IvoSG 679441a
Merge branch 'main' into feat/adp-prompt-for-credentials
IvoSG File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| '@sap-ux/generator-adp': patch | ||
| '@sap-ux/adp-tooling': patch | ||
| --- | ||
|
|
||
| feat: Prompt for credentials when not available in VSCode for adp generator |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import { getService, BackendSystem, BackendSystemKey, SystemType } from '@sap-ux/store'; | ||
| import type { SystemLookup } from '../source'; | ||
| import type { ToolsLogger } from '@sap-ux/logger'; | ||
| import type { ConfigAnswers } from '../types'; | ||
|
|
||
| /** | ||
| * Stores system credentials securely using the @sap-ux/store service. | ||
| * Only stores credentials for ABAP environments when all required fields are provided. | ||
| * | ||
| * @param {ConfigAnswers} configAnswers - Configuration answers containing credentials and system info | ||
| * @param {SystemLookup} systemLookup - System lookup service for retrieving endpoint details | ||
| * @param {ToolsLogger} logger - Logger for informational and warning messages | ||
| * @returns {Promise<void>} Promise that resolves when credentials are stored or operation completes | ||
| */ | ||
| export async function storeCredentials( | ||
| configAnswers: ConfigAnswers, | ||
| systemLookup: SystemLookup, | ||
| logger: ToolsLogger | ||
| ): Promise<void> { | ||
| if (!configAnswers.username || !configAnswers.password) { | ||
| return; | ||
| } | ||
|
|
||
| try { | ||
| const systemEndpoint = await systemLookup.getSystemByName(configAnswers.system); | ||
| if (!systemEndpoint?.Url) { | ||
| logger.warn('Cannot store credentials: system endpoint or URL not found.'); | ||
| return; | ||
| } | ||
|
|
||
| const systemService = await getService<BackendSystem, BackendSystemKey>({ | ||
| entityName: 'system' | ||
| }); | ||
|
|
||
| const backendSystemKey = new BackendSystemKey({ | ||
| url: systemEndpoint.Url, | ||
| client: systemEndpoint.Client | ||
| }); | ||
|
|
||
| const existingSystem = await systemService.read(backendSystemKey); | ||
|
|
||
| const backendSystem = new BackendSystem({ | ||
| name: configAnswers.system, | ||
| url: systemEndpoint.Url, | ||
| client: systemEndpoint.Client, | ||
| username: configAnswers.username, | ||
| password: configAnswers.password, | ||
| systemType: (systemEndpoint.SystemType as SystemType) || SystemType.AbapOnPrem, | ||
| connectionType: 'abap_catalog' | ||
| }); | ||
|
|
||
| await systemService.write(backendSystem, { force: !!existingSystem }); | ||
|
|
||
| logger.info('System credentials have been stored securely.'); | ||
| } catch (error) { | ||
| logger.error(`Failed to store credentials: ${error instanceof Error ? error.message : String(error)}`); | ||
| logger.debug(error); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
141 changes: 141 additions & 0 deletions
141
packages/adp-tooling/test/unit/base/credentials.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| import { getService, SystemType } from '@sap-ux/store'; | ||
| import { storeCredentials } from '../../../src'; | ||
| import type { SystemLookup } from '../../../src'; | ||
| import type { ToolsLogger } from '@sap-ux/logger'; | ||
|
|
||
| jest.mock('@sap-ux/store'); | ||
|
|
||
| describe('Credential Storage Logic', () => { | ||
| let mockSystemService: any; | ||
| let mockLogger: ToolsLogger; | ||
| let mockSystemLookup: SystemLookup; | ||
| const getServiceMock = getService as jest.Mock; | ||
|
|
||
| beforeEach(() => { | ||
| mockSystemService = { | ||
| read: jest.fn(), | ||
| write: jest.fn() | ||
| }; | ||
|
|
||
| mockLogger = { | ||
| debug: jest.fn(), | ||
| info: jest.fn(), | ||
| warn: jest.fn(), | ||
| error: jest.fn() | ||
| } as any; | ||
|
|
||
| mockSystemLookup = { | ||
| getSystemByName: jest.fn() | ||
| } as any; | ||
|
|
||
| getServiceMock.mockResolvedValue(mockSystemService); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| jest.clearAllMocks(); | ||
| }); | ||
|
|
||
| describe('storeCredentials function', () => { | ||
| it('should store credentials when credentials are provided', async () => { | ||
| const configAnswers = { | ||
| system: 'SystemA', | ||
| username: 'user1', | ||
| password: 'pass1', | ||
| application: {} as any | ||
| }; | ||
|
|
||
| (mockSystemLookup.getSystemByName as jest.Mock).mockResolvedValue({ | ||
| Name: 'SystemA', | ||
| Client: '010', | ||
| Url: 'https://example.com', | ||
| SystemType: 'OnPrem' | ||
| }); | ||
|
|
||
| mockSystemService.read.mockResolvedValue(null); | ||
|
|
||
| await storeCredentials(configAnswers, mockSystemLookup, mockLogger); | ||
|
|
||
| expect(getServiceMock).toHaveBeenCalledWith({ entityName: 'system' }); | ||
| expect(mockSystemService.read).toHaveBeenCalled(); | ||
| expect(mockSystemService.write).toHaveBeenCalledWith(expect.any(Object), { force: false }); | ||
| expect(mockLogger.info).toHaveBeenCalledWith('System credentials have been stored securely.'); | ||
| }); | ||
|
|
||
| it('should update existing credentials when system already exists in store', async () => { | ||
| const configAnswers = { | ||
| system: 'SystemA', | ||
| username: 'user1', | ||
| password: 'pass1', | ||
| application: {} as any | ||
| }; | ||
|
|
||
| (mockSystemLookup.getSystemByName as jest.Mock).mockResolvedValue({ | ||
| Name: 'SystemA', | ||
| Client: '010', | ||
| Url: 'https://example.com', | ||
| SystemType: 'OnPrem' | ||
| }); | ||
|
|
||
| mockSystemService.read.mockResolvedValue({ name: 'SystemA', url: 'https://example.com' }); | ||
|
|
||
| await storeCredentials(configAnswers, mockSystemLookup, mockLogger); | ||
|
|
||
| expect(mockSystemService.write).toHaveBeenCalledWith(expect.any(Object), { force: true }); | ||
| expect(mockLogger.info).toHaveBeenCalledWith('System credentials have been stored securely.'); | ||
| }); | ||
|
|
||
| it('should not store credentials when password is missing', async () => { | ||
| const configAnswers = { | ||
| system: 'SystemA', | ||
| username: 'user1', | ||
| password: '', | ||
| application: {} as any | ||
| } as any; | ||
|
|
||
| await storeCredentials(configAnswers, mockSystemLookup, mockLogger); | ||
|
|
||
| expect(getServiceMock).not.toHaveBeenCalled(); | ||
| expect(mockSystemService.write).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('should warn when system endpoint is not found', async () => { | ||
| const configAnswers = { | ||
| system: 'SystemA', | ||
| username: 'user1', | ||
| password: 'pass1', | ||
| application: {} as any | ||
| }; | ||
|
|
||
| (mockSystemLookup.getSystemByName as jest.Mock).mockResolvedValue(undefined); | ||
|
|
||
| await storeCredentials(configAnswers, mockSystemLookup, mockLogger); | ||
|
|
||
| expect(mockLogger.warn).toHaveBeenCalledWith('Cannot store credentials: system endpoint or URL not found.'); | ||
| expect(mockSystemService.write).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('should handle credential storage errors gracefully', async () => { | ||
| const configAnswers = { | ||
| system: 'SystemA', | ||
| username: 'user1', | ||
| password: 'pass1', | ||
| application: {} as any | ||
| }; | ||
|
|
||
| (mockSystemLookup.getSystemByName as jest.Mock).mockResolvedValue({ | ||
| Name: 'SystemA', | ||
| Client: '010', | ||
| Url: 'https://example.com', | ||
| SystemType: 'OnPrem' | ||
| }); | ||
|
|
||
| const error = new Error('Storage failed'); | ||
| mockSystemService.write.mockRejectedValue(error); | ||
|
|
||
| await storeCredentials(configAnswers, mockSystemLookup, mockLogger); | ||
|
|
||
| expect(mockLogger.error).toHaveBeenCalledWith('Failed to store credentials: Storage failed'); | ||
| expect(mockLogger.debug).toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
| }); | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.