-
Notifications
You must be signed in to change notification settings - Fork 83
feat: add atx fes integration for transform profiles #2439
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
8bc6db0
7fe3ac7
d80d7d7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| import { | ||
| CancellationToken, | ||
| CredentialsProvider, | ||
| GetConfigurationFromServerParams, | ||
| InitializeParams, | ||
| Logging, | ||
| LSPErrorCodes, | ||
| ResponseError, | ||
| Server, | ||
| BearerCredentials, | ||
| } from '@aws/language-server-runtimes/server-interface' | ||
| import { AmazonQDeveloperProfile } from '../../shared/amazonQServiceManager/qDeveloperProfiles' | ||
| import { ElasticGumbyFrontendClient, ListAvailableProfilesCommand } from '@amazon/elastic-gumby-frontend-client' | ||
| import { DEFAULT_ATX_FES_ENDPOINT_URL } from '../../shared/constants' | ||
| import { getBearerTokenFromProvider } from '../../shared/utils' | ||
|
|
||
| // Transform Configuration Sections | ||
| export const TRANSFORM_PROFILES_CONFIGURATION_SECTION = 'aws.transformProfiles' | ||
|
|
||
| /** | ||
| * Transform Configuration Server - standalone server for ATX FES profile management | ||
| * Completely separate from qConfigurationServer to maintain clean RTS/ATX FES separation | ||
| */ | ||
| export class TransformConfigurationServer { | ||
| private atxClient: ElasticGumbyFrontendClient | null = null | ||
|
|
||
| constructor( | ||
| private readonly logging: Logging, | ||
| private readonly credentialsProvider: CredentialsProvider | ||
| ) {} | ||
|
|
||
| /** | ||
| * Initialize as standalone LSP server | ||
| */ | ||
| async initialize(params: InitializeParams): Promise<any> { | ||
| return { | ||
| capabilities: {}, | ||
| awsServerCapabilities: { | ||
| configurationProvider: { | ||
| sections: [TRANSFORM_PROFILES_CONFIGURATION_SECTION], | ||
| }, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Handle configuration requests for Transform profiles | ||
| */ | ||
| async getConfiguration(params: GetConfigurationFromServerParams, token: CancellationToken): Promise<any> { | ||
| this.logging.log(`TransformConfigurationServer: Configuration requested for section: ${params.section}`) | ||
|
|
||
| switch (params.section) { | ||
| case TRANSFORM_PROFILES_CONFIGURATION_SECTION: | ||
| const profiles = await this.listAvailableProfiles(token) | ||
| return profiles | ||
| default: | ||
| throw new ResponseError( | ||
| LSPErrorCodes.RequestFailed, | ||
| `TransformConfigurationServer: Unsupported configuration section: ${params.section}` | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Initialize ATX FES client with bearer token authentication | ||
| */ | ||
| private async initializeAtxClient(): Promise<boolean> { | ||
| try { | ||
| if (!this.credentialsProvider?.hasCredentials('bearer')) { | ||
| return false | ||
| } | ||
|
|
||
| const credentials = (await this.credentialsProvider.getCredentials('bearer')) as BearerCredentials | ||
| if (!credentials?.token) { | ||
| return false | ||
| } | ||
|
|
||
| // Initialize ATX FES client | ||
| this.atxClient = new ElasticGumbyFrontendClient({ | ||
| region: 'us-east-1', | ||
| endpoint: DEFAULT_ATX_FES_ENDPOINT_URL, | ||
| }) | ||
|
|
||
| return true | ||
| } catch (error) { | ||
| this.logging.error(`TransformConfigurationServer: Failed to initialize ATX client: ${error}`) | ||
|
||
| return false | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Add bearer token authentication to ATX FES command | ||
| */ | ||
| private async addBearerTokenToCommand(command: any): Promise<void> { | ||
| const credentials = (await this.credentialsProvider.getCredentials('bearer')) as BearerCredentials | ||
| if (!credentials?.token) { | ||
| throw new Error('No bearer token available for ATX FES authentication') | ||
| } | ||
|
|
||
| command.middlewareStack?.add( | ||
| (next: any) => async (args: any) => { | ||
| args.request.headers = { | ||
| ...args.request.headers, | ||
| Authorization: `Bearer ${credentials.token}`, | ||
| } | ||
| return next(args) | ||
| }, | ||
| { step: 'build', priority: 'high' } | ||
| ) | ||
| } | ||
|
|
||
| /** | ||
| * List available Transform profiles using ATX FES ListAvailableProfiles API | ||
| */ | ||
| async listAvailableProfiles(token: CancellationToken): Promise<AmazonQDeveloperProfile[]> { | ||
| try { | ||
| if (!this.atxClient && !(await this.initializeAtxClient())) { | ||
| this.logging.error('TransformConfigurationServer: Failed to initialize ATX FES client') | ||
| return [] | ||
| } | ||
|
|
||
| const command = new ListAvailableProfilesCommand({ | ||
| maxResults: 100, | ||
| }) | ||
|
|
||
| await this.addBearerTokenToCommand(command) | ||
| const response = await this.atxClient!.send(command) | ||
|
|
||
| this.logging.log( | ||
| `TransformConfigurationServer: ATX FES returned ${response.profiles?.length || 0} profiles` | ||
| ) | ||
|
|
||
| // Convert ATX FES profiles to AmazonQDeveloperProfile format | ||
| const transformProfiles: AmazonQDeveloperProfile[] = (response.profiles || []).map((profile: any) => { | ||
| const convertedProfile = { | ||
| arn: profile.arn || '', | ||
| name: profile.profileName || profile.applicationUrl || 'Unnamed Transform Profile', | ||
| applicationUrl: (profile.applicationUrl || '').replace(/\/$/, ''), // Strip trailing slash | ||
| identityDetails: { | ||
| region: profile.region || 'us-east-1', | ||
| accountId: profile.accountId || '', | ||
| }, | ||
| } | ||
|
|
||
| return convertedProfile | ||
| }) | ||
|
|
||
| return transformProfiles | ||
| } catch (error) { | ||
| this.logging.error(`TransformConfigurationServer: ListAvailableProfiles failed: ${error}`) | ||
|
||
| return [] | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Transform Configuration Server Token - creates standalone Transform configuration server | ||
| */ | ||
| export const TransformConfigurationServerToken = (): Server => { | ||
| return ({ credentialsProvider, lsp, logging }) => { | ||
| let transformConfigurationServer: TransformConfigurationServer | ||
|
|
||
| lsp.addInitializer(async params => { | ||
| transformConfigurationServer = new TransformConfigurationServer(logging, credentialsProvider) | ||
| return transformConfigurationServer.initialize(params) | ||
| }) | ||
|
|
||
| lsp.extensions.onGetConfigurationFromServer( | ||
| async (params: GetConfigurationFromServerParams, token: CancellationToken) => { | ||
| return transformConfigurationServer.getConfiguration(params, token) | ||
| } | ||
| ) | ||
|
|
||
| return () => {} | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| import { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets add unit tests for this new class. |
||
| CancellationToken, | ||
| ExecuteCommandParams, | ||
| InitializeParams, | ||
| Server, | ||
| } from '@aws/language-server-runtimes/server-interface' | ||
| import { AtxTokenServiceManager } from '../../shared/amazonQServiceManager/AtxTokenServiceManager' | ||
| import { ATXTransformHandler } from './atxTransformHandler' | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :nit non-blocking. some variables use Atx and some use ATX. can we use only one convention |
||
|
|
||
| // ATX FES Commands | ||
| const AtxListAvailableProfilesCommand = 'aws/atxNetTransform/listAvailableProfiles' | ||
|
|
||
| // TODO: Phase 2 - Add remaining ATX FES APIs | ||
| // const AtxVerifySessionCommand = 'aws/atxNetTransform/verifySession' // LSP-only implementation | ||
| // const AtxListWorkspacesCommand = 'aws/atxNetTransform/listWorkspaces' | ||
| // const AtxCreateWorkspaceCommand = 'aws/atxNetTransform/createWorkspace' | ||
| // const AtxCreateJobCommand = 'aws/atxNetTransform/createJob' | ||
| // const AtxStartJobCommand = 'aws/atxNetTransform/startJob' | ||
| // const AtxGetJobCommand = 'aws/atxNetTransform/getJob' | ||
| // const AtxStopJobCommand = 'aws/atxNetTransform/stopJob' | ||
| // const AtxCreateUploadArtifactURLCommand = 'aws/atxNetTransform/createUploadArtifactURL' | ||
| // const AtxCompleteUploadArtifactURLCommand = 'aws/atxNetTransform/completeUploadArtifactURL' | ||
| // const AtxCreateDownloadArtifactURLCommand = 'aws/atxNetTransform/createDownloadArtifactURL' | ||
| // const AtxListArtifactsCommand = 'aws/atxNetTransform/listArtifacts' | ||
| // const AtxListJobStepPlansCommand = 'aws/atxNetTransform/listJobStepPlans' | ||
|
|
||
| // TODO: Phase 2 - Add remaining ATX FES APIs | ||
| export const AtxNetTransformServerToken = | ||
| (): Server => | ||
| ({ workspace, logging, lsp, telemetry, runtime }) => { | ||
| let atxTokenServiceManager: AtxTokenServiceManager | ||
| let atxTransformHandler: ATXTransformHandler | ||
|
|
||
| const runAtxTransformCommand = async (params: ExecuteCommandParams, _token: CancellationToken) => { | ||
| try { | ||
| switch (params.command) { | ||
| case AtxListAvailableProfilesCommand: { | ||
| const maxResults = (params as any).maxResults || 100 | ||
| const response = await atxTransformHandler.listAvailableProfiles(maxResults) | ||
| return response | ||
| } | ||
| default: { | ||
| throw new Error(`Unknown ATX FES command: ${params.command}`) | ||
| } | ||
| } | ||
| } catch (e: any) { | ||
| logging.log(`ATX FES: Error executing command ${params.command}: ${e}`) | ||
| throw e | ||
| } | ||
| } | ||
|
|
||
| const onExecuteCommandHandler = async ( | ||
| params: ExecuteCommandParams, | ||
| _token: CancellationToken | ||
| ): Promise<any> => { | ||
| return runAtxTransformCommand(params, _token) | ||
| } | ||
|
|
||
| const onInitializeHandler = async (params: InitializeParams) => { | ||
| return { | ||
| capabilities: { | ||
| executeCommandProvider: { | ||
| commands: [ | ||
| AtxListAvailableProfilesCommand, | ||
| // TODO: Phase 2: Add remaining ATX FES APIs | ||
| // AtxVerifySessionCommand, // LSP-only implementation | ||
| // AtxListWorkspacesCommand, | ||
| // AtxCreateWorkspaceCommand, | ||
| // AtxCreateJobCommand, | ||
| // AtxStartJobCommand, | ||
| // AtxGetJobCommand, | ||
| // AtxStopJobCommand, | ||
| // AtxCreateUploadArtifactURLCommand, | ||
| // AtxCompleteUploadArtifactURLCommand, | ||
| // AtxCreateDownloadArtifactURLCommand, | ||
| // AtxListArtifactsCommand, | ||
| // AtxListJobStepPlansCommand, | ||
| ], | ||
| }, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| const onInitializedHandler = () => { | ||
| atxTokenServiceManager = AtxTokenServiceManager.getInstance() | ||
| atxTransformHandler = new ATXTransformHandler(atxTokenServiceManager, workspace, logging, runtime) | ||
|
|
||
| logging.log('ATX FES Server: Initialized') | ||
| } | ||
|
|
||
| lsp.addInitializer(onInitializeHandler) | ||
| lsp.onInitialized(onInitializedHandler) | ||
| lsp.onExecuteCommand(onExecuteCommandHandler) | ||
|
|
||
| return () => {} | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this region be dynamic? for example- if user has profile in FRA do we override client region somewhere else?