Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
46 changes: 46 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,35 @@ 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,
StopExecutionCommandOutput,
TestStateCommand,
TestStateCommandInput,
TestStateCommandOutput,
Expand Down Expand Up @@ -50,10 +68,38 @@ export class StepFunctionsClient extends ClientWrapper<SFNClient> {
return this.makeRequest(DescribeStateMachineCommand, request)
}

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

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

public async describeMapRun(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<StopExecutionCommandOutput> {
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,16 @@ export class ExecutionDetailProvider {

// Set up the content
panel.webview.html = await this.getWebviewContent()
const context: ExecutionDetailsContext = {
panel,
loaderNotification: undefined,
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,47 @@
/*
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import { Command, Message, MessageType, BaseContext, 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: BaseContext) {
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: BaseContext) {
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: BaseContext, 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,14 @@
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, BaseContext } 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: BaseContext,
private readonly clients = {
sfn: new StepFunctionsClient(region),
iam: new IamClient(region),
Expand All @@ -33,6 +33,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.describeStateMachineForExecution(params)
break
case ApiAction.SFNDescribeExecution:
response = await this.clients.sfn.describeExecution(params)
break
case ApiAction.SFNDescribeMapRun:
response = await this.clients.sfn.describeMapRun(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 @@ -16,20 +16,27 @@ export enum WorkflowMode {
Readonly = 'readonly',
}

export type WebviewContext = {
export interface BaseContext {
panel: vscode.WebviewPanel
loaderNotification: undefined | LoaderNotification
}

export interface WebviewContext extends BaseContext {
stateMachineName: string
mode: WorkflowMode
panel: vscode.WebviewPanel
textDocument: vscode.TextDocument
disposables: vscode.Disposable[]
workSpacePath: string
defaultTemplatePath: string
defaultTemplateName: string
fileStates: Record<string, FileWatchInfo>
loaderNotification: undefined | LoaderNotification
fileId: string
}

export interface ExecutionDetailsContext extends BaseContext {
executionArn: string
}

export type LoaderNotification = {
progress: vscode.Progress<{
message?: string | undefined
Expand Down Expand Up @@ -96,11 +103,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 +138,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