Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/core/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
"AWS.stepFunctions.executeStateMachine.info.executing": "Starting execution of '{0}' in {1}...",
"AWS.stepFunctions.executeStateMachine.info.started": "Execution started",
"AWS.stepFunctions.executeStateMachine.error.failedToStart": "There was an error starting execution for '{0}', check AWS Toolkit logs for more information.",
"AWS.stepfunctions.viewExecutionDetailsByExecutionARN": "View Execution Details by Execution ARN",
"AWS.stepFunctions.asl.format.enable.desc": "Enables the default formatter used with Amazon States Language files",
"AWS.stepFunctions.asl.maxItemsComputed.desc": "The maximum number of outline symbols and folding regions computed (limited for performance reasons).",
"AWS.stepFunctions.workflowStudio.actions.progressMessage": "Opening asl file in Workflow Studio",
Expand Down Expand Up @@ -229,6 +228,7 @@
"AWS.command.stepFunctions.createStateMachineFromTemplate": "Create a new Step Functions state machine",
"AWS.command.stepFunctions.publishStateMachine": "Publish state machine to Step Functions",
"AWS.command.stepFunctions.openWithWorkflowStudio": "Open with Workflow Studio",
"AWS.command.stepFunctions.viewExecutionDetailsByExecutionARN": "View Execution Details by Execution ARN",
"AWS.command.cdk.previewStateMachine": "Render state machine graph from CDK application",
"AWS.command.copyLogResource": "Copy Log Stream or Group",
"AWS.command.saveCurrentLogDataContent": "Save Log to File",
Expand Down
47 changes: 47 additions & 0 deletions packages/core/src/shared/clients/stepFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,34 @@ import {
CreateStateMachineCommand,
CreateStateMachineCommandInput,
CreateStateMachineCommandOutput,
DescribeExecutionCommand,
DescribeExecutionCommandInput,
DescribeExecutionCommandOutput,
DescribeMapRunCommand,
DescribeMapRunCommandInput,
DescribeMapRunCommandOutput,
DescribeStateMachineCommand,
DescribeStateMachineCommandInput,
DescribeStateMachineCommandOutput,
DescribeStateMachineForExecutionCommand,
DescribeStateMachineForExecutionCommandInput,
DescribeStateMachineForExecutionCommandOutput,
GetExecutionHistoryCommand,
GetExecutionHistoryCommandInput,
GetExecutionHistoryCommandOutput,
ListStateMachinesCommand,
ListStateMachinesCommandInput,
ListStateMachinesCommandOutput,
RedriveExecutionCommand,
RedriveExecutionCommandInput,
RedriveExecutionCommandOutput,
SFNClient,
StartExecutionCommand,
StartExecutionCommandInput,
StartExecutionCommandOutput,
StateMachineListItem,
StopExecutionCommand,
StopExecutionCommandInput,
TestStateCommand,
TestStateCommandInput,
TestStateCommandOutput,
Expand All @@ -26,6 +43,8 @@ import {
UpdateStateMachineCommandOutput,
} from '@aws-sdk/client-sfn'
import { ClientWrapper } from './clientWrapper'
import { StopAccessLoggingOutput } from 'aws-sdk/clients/mediastore'
// import { StopExecutionInput } from 'aws-sdk/clients/stepfunctions'

export class StepFunctionsClient extends ClientWrapper<SFNClient> {
public constructor(regionCode: string) {
Expand All @@ -50,10 +69,38 @@ export class StepFunctionsClient extends ClientWrapper<SFNClient> {
return this.makeRequest(DescribeStateMachineCommand, request)
}

public async getStateMachineDetailsForExecution(
request: DescribeStateMachineForExecutionCommandInput
): Promise<DescribeStateMachineForExecutionCommandOutput> {
return this.makeRequest(DescribeStateMachineForExecutionCommand, request)
}

public async getExecutionDetails(request: DescribeExecutionCommandInput): Promise<DescribeExecutionCommandOutput> {
return this.makeRequest(DescribeExecutionCommand, request)
}

public async getMapRunDetails(request: DescribeMapRunCommandInput): Promise<DescribeMapRunCommandOutput> {
return this.makeRequest(DescribeMapRunCommand, request)
}

public async getExecutionHistory(
request: GetExecutionHistoryCommandInput
): Promise<GetExecutionHistoryCommandOutput> {
return this.makeRequest(GetExecutionHistoryCommand, request)
}

public async reDriveExecution(request: RedriveExecutionCommandInput): Promise<RedriveExecutionCommandOutput> {
return this.makeRequest(RedriveExecutionCommand, request)
}

public async executeStateMachine(request: StartExecutionCommandInput): Promise<StartExecutionCommandOutput> {
return this.makeRequest(StartExecutionCommand, request)
}

public async stopExecution(request: StopExecutionCommandInput): Promise<StopAccessLoggingOutput> {
return this.makeRequest(StopExecutionCommand, request)
}

public async createStateMachine(request: CreateStateMachineCommandInput): Promise<CreateStateMachineCommandOutput> {
return this.makeRequest(CreateStateMachineCommand, request)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { isTreeNode } from '../../../shared/treeview/resourceTreeDataProvider'
import { unboxTreeNode } from '../../../shared/treeview/utils'
import { Commands } from '../../../shared/vscode/commands2'
import { PreviewStateMachineCDKWizard } from '../../wizards/previewStateMachineCDKWizard'
import { WorkflowMode } from '../../workflowStudio/types'
import { WorkflowMode } from '../../messageHandlers/types'
import { WorkflowStudioEditorProvider } from '../../workflowStudio/workflowStudioEditorProvider'
import { getStateMachineDefinitionFromCfnTemplate } from './getStateMachineDefinitionFromCfnTemplate'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import { getLogger } from '../../shared/logger/logger'
import request from '../../shared/request'
import { ToolkitError } from '../../shared/errors'
import { i18n } from '../../shared/i18n-helper'
import { ComponentType } from '../workflowStudio/types'
import { ComponentType } from '../messageHandlers/types'
import { isLocalDev, localhost, cdn } from '../constants/webviewResources'
import { handleMessage } from './handleMessage'
import { ExecutionDetailsContext } from '../messageHandlers/types'

/**
* Provider for Execution Details panels.
Expand Down Expand Up @@ -98,11 +100,18 @@ export class ExecutionDetailProvider {

// Set up the content
panel.webview.html = await this.getWebviewContent()
const context: ExecutionDetailsContext = {
stateMachineName: executionArn.split(':').pop() || 'Unknown',
panel,
loaderNotification: undefined,
fileId: executionArn,
executionArn,
}

// Handle messages from the webview
panel.webview.onDidReceiveMessage(async (message) => {
this.logger.debug('Received message from execution details webview: %O', message)
// Add message handlers as needed
await handleMessage(message, context)
})
} catch (err) {
void vscode.window.showErrorMessage(i18n('AWS.stepFunctions.executionDetails.failed'))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import {
Command,
Message,
MessageType,
ExecutionDetailsContext,
ApiCallRequestMessage,
InitResponseMessage,
} from '../messageHandlers/types'
import {
loadStageMessageHandler,
handleUnsupportedMessage,
apiCallMessageHandler,
} from '../messageHandlers/handleMessageHelpers'

/**
* Handles messages received from the ExecutionDetails webview. Depending on the message type and command,
* calls the appropriate handler function
* @param message The message received from the webview
* @param context The context object containing information about the execution details webview environment
*/
export async function handleMessage(message: Message, context: ExecutionDetailsContext) {
const { command, messageType } = message
if (messageType === MessageType.REQUEST) {
switch (command) {
case Command.INIT:
void initMessageHandler(context)
break
case Command.API_CALL:
void apiCallMessageHandler(message as ApiCallRequestMessage, context)
break
default:
void handleUnsupportedMessage(context, message)
break
}
} else if (messageType === MessageType.BROADCAST) {
switch (command) {
case Command.LOAD_STAGE:
void loadStageMessageHandler(context)
break
default:
void handleUnsupportedMessage(context, message)
break
}
} else {
void handleUnsupportedMessage(context, message)
}
}

/**
* Handler for when the webview is ready.
* This handler is used to initialize the webview with execution details.
* @param context The context object containing the necessary information for the webview.
*/
async function initMessageHandler(context: ExecutionDetailsContext) {
try {
await context.panel.webview.postMessage({
messageType: MessageType.BROADCAST,
command: Command.INIT,
executionArn: context.executionArn,
})
} catch (e) {
await context.panel.webview.postMessage({
messageType: MessageType.RESPONSE,
command: Command.INIT,
isSuccess: false,
failureReason: (e as Error).message,
} as InitResponseMessage)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import {
Command,
Message,
MessageType,
WebviewContext,
ExecutionDetailsContext,
ApiCallRequestMessage,
UnsupportedMessage,
} from './types'
import { stepFunctionApiHandler } from './stepFunctionApiHandler'
import globals from '../../shared/extensionGlobals'
import { getLogger } from '../../shared/logger/logger'

/**
* Handler for managing webview stage load, which updates load notifications.
* @param context The context object containing the necessary information for the webview.
*/
export async function loadStageMessageHandler(context: WebviewContext | ExecutionDetailsContext) {
context.loaderNotification?.progress.report({ increment: 25 })
setTimeout(() => {
context.loaderNotification?.resolve()
}, 100)
}

/**
* Handler for making API calls from the webview and returning the response.
* @param request The request message containing the API to call and the parameters
* @param context The webview context used for returning the API response to the webview
*/
export function apiCallMessageHandler(
request: ApiCallRequestMessage,
context: WebviewContext | ExecutionDetailsContext
) {
const logger = getLogger('stepfunctions')
const apiHandler = new stepFunctionApiHandler(globals.awsContext.getCredentialDefaultRegion(), context)
apiHandler.performApiCall(request).catch((error) => logger.error('%s API call failed: %O', request.apiName, error))
}

/**
* Handles unsupported or unrecognized messages by sending a response to the webview. Ensures compatibility with future
* commands and message types, preventing issues if the user has an outdated extension version.
* @param context The context object containing information about the webview environment
* @param command The command received from the webview
* @param messageType The type of the message received
*/
export async function handleUnsupportedMessage(
context: WebviewContext | ExecutionDetailsContext,
originalMessage: Message
) {
await context.panel.webview.postMessage({
messageType: MessageType.RESPONSE,
command: Command.UNSUPPORTED_COMMAND,
originalMessage,
} as UnsupportedMessage)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,21 @@
import * as StepFunctions from '@aws-sdk/client-sfn'
import { IamClient, IamRole } from '../../shared/clients/iam'
import { StepFunctionsClient } from '../../shared/clients/stepFunctions'
import { ApiAction, ApiCallRequestMessage, Command, MessageType, WebviewContext } from './types'
import {
ApiAction,
ApiCallRequestMessage,
Command,
MessageType,
WebviewContext,
ExecutionDetailsContext,
} from './types'
import { telemetry } from '../../shared/telemetry/telemetry'
import { ListRolesRequest } from '@aws-sdk/client-iam'

export class WorkflowStudioApiHandler {
export class stepFunctionApiHandler {
public constructor(
region: string,
private readonly context: WebviewContext,
private readonly context: WebviewContext | ExecutionDetailsContext,
private readonly clients = {
sfn: new StepFunctionsClient(region),
iam: new IamClient(region),
Expand All @@ -33,6 +40,30 @@ export class WorkflowStudioApiHandler {
case ApiAction.SFNTestState:
response = await this.testState(params)
break
case ApiAction.SFNDescribeStateMachine:
response = await this.clients.sfn.getStateMachineDetails(params)
break
case ApiAction.SFNDescribeStateMachineForExecution:
response = await this.clients.sfn.getStateMachineDetailsForExecution(params)
break
case ApiAction.SFNDescribeExecution:
response = await this.clients.sfn.getExecutionDetails(params)
break
case ApiAction.SFNDescribeMapRun:
response = await this.clients.sfn.getMapRunDetails(params)
break
case ApiAction.SFNGetExecutionHistory:
response = await this.clients.sfn.getExecutionHistory(params)
break
case ApiAction.SFNRedriveExecution:
response = await this.clients.sfn.reDriveExecution(params)
break
case ApiAction.SFNStartExecution:
response = await this.clients.sfn.executeStateMachine(params)
break
case ApiAction.SFNStopExecution:
response = await this.clients.sfn.stopExecution(params)
break
default:
throw new Error(`Unknown API: ${apiName}`)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ export type WebviewContext = {
fileId: string
}

export type ExecutionDetailsContext = {
stateMachineName: string
panel: vscode.WebviewPanel
fileId: string
loaderNotification: undefined | LoaderNotification
executionArn: string
}

export type LoaderNotification = {
progress: vscode.Progress<{
message?: string | undefined
Expand Down Expand Up @@ -96,11 +104,27 @@ export interface SyncFileRequestMessage extends SaveFileRequestMessage {
export enum ApiAction {
IAMListRoles = 'iam:ListRoles',
SFNTestState = 'sfn:TestState',
SFNDescribeStateMachine = 'sfn:describeStateMachine',
SFNDescribeStateMachineForExecution = 'sfn:describeStateMachineForExecution',
SFNDescribeExecution = 'sfn:describeExecution',
SFNDescribeMapRun = 'sfn:describeMapRun',
SFNGetExecutionHistory = 'sfn:getExecutionHistory',
SFNRedriveExecution = 'sfn:redriveExecution',
SFNStartExecution = 'sfn:startExecution',
SFNStopExecution = 'sfn:stopExecution',
}

type ApiCallRequestMapping = {
[ApiAction.IAMListRoles]: IAM.ListRolesRequest
[ApiAction.SFNTestState]: StepFunctions.TestStateInput
[ApiAction.SFNDescribeStateMachine]: StepFunctions.DescribeStateMachineInput
[ApiAction.SFNDescribeStateMachineForExecution]: StepFunctions.DescribeStateMachineForExecutionInput
[ApiAction.SFNDescribeExecution]: StepFunctions.DescribeExecutionInput
[ApiAction.SFNDescribeMapRun]: StepFunctions.DescribeMapRunInput
[ApiAction.SFNGetExecutionHistory]: StepFunctions.GetExecutionHistoryInput
[ApiAction.SFNRedriveExecution]: StepFunctions.RedriveExecutionInput
[ApiAction.SFNStartExecution]: StepFunctions.StartExecutionInput
[ApiAction.SFNStopExecution]: StepFunctions.StopExecutionInput
}

interface ApiCallRequestMessageBase<ApiName extends ApiAction> extends Message {
Expand All @@ -115,3 +139,11 @@ interface ApiCallRequestMessageBase<ApiName extends ApiAction> extends Message {
export type ApiCallRequestMessage =
| ApiCallRequestMessageBase<ApiAction.IAMListRoles>
| ApiCallRequestMessageBase<ApiAction.SFNTestState>
| ApiCallRequestMessageBase<ApiAction.SFNDescribeStateMachine>
| ApiCallRequestMessageBase<ApiAction.SFNDescribeStateMachineForExecution>
| ApiCallRequestMessageBase<ApiAction.SFNDescribeExecution>
| ApiCallRequestMessageBase<ApiAction.SFNDescribeMapRun>
| ApiCallRequestMessageBase<ApiAction.SFNGetExecutionHistory>
| ApiCallRequestMessageBase<ApiAction.SFNRedriveExecution>
| ApiCallRequestMessageBase<ApiAction.SFNStartExecution>
| ApiCallRequestMessageBase<ApiAction.SFNStopExecution>
Loading
Loading