Skip to content
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

feat(stepfunctions): Custom editor to view and edit ASL files - tracer bullet #5613

Merged
merged 1 commit into from
Sep 23, 2024
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
15 changes: 15 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4018,6 +4018,21 @@
"filenamePattern": "*.tc.json"
}
]
},
{
"viewType": "workflowStudio.asl",
"displayName": "Workflow Studio",
"selector": [
{
"filenamePattern": "*.asl.json"
},
{
"filenamePattern": "*.asl.yaml"
},
{
"filenamePattern": "*.asl.yml"
}
]
}
],
"configurationDefaults": {
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"AWS.configuration.description.experiments": "Try experimental features and give feedback. Note that experimental features may be removed at any time.\n * `jsonResourceModification` - Enables basic create, update, and delete support for cloud resources via the JSON Resources explorer component.\n * `samSyncCode` - Adds an additional code-only option when synchronizing SAM applications. Code-only synchronizations are faster but can cause drift in the CloudFormation stack. Does nothing when using the legacy SAM deploy feature.\n * `iamPolicyChecks` - Enables IAM Policy Checks feature, allowing users to validate IAM policies against IAM policy grammar, AWS best practices, and specified security standards.",
"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",
"AWS.configuration.description.awssam.debug.api": "API Gateway configuration",
"AWS.configuration.description.awssam.debug.api.clientCertId": "The API Gateway client certificate ID",
"AWS.configuration.description.awssam.debug.api.headers": "Additional HTTP headers",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/extensionNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { activate as activateS3 } from './awsService/s3/activation'
import * as filetypes from './shared/filetypes'
import { activate as activateApiGateway } from './awsService/apigateway/activation'
import { activate as activateStepFunctions } from './stepFunctions/activation'
import { activate as activateStepFunctionsWorkflowStudio } from './stepFunctions/workflowStudio/activation'
import { activate as activateSsmDocument } from './ssmDocument/activation'
import { activate as activateDynamicResources } from './dynamicResources/activation'
import { activate as activateEcs } from './awsService/ecs/activation'
Expand Down Expand Up @@ -203,6 +204,8 @@ export async function activate(context: vscode.ExtensionContext) {

await activateStepFunctions(context, globals.awsContext, globals.outputChannel)

await activateStepFunctionsWorkflowStudio(context)

await activateRedshift(extContext)

await activateIamPolicyChecks(extContext)
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/shared/telemetry/vscodeTelemetry.json
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,16 @@
"name": "stepfunctions_previewstatemachine",
"description": ""
},
{
"name": "stepfunctions_openWorkflowStudio",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please plan to upstream this to https://github.com/aws/aws-toolkit-common as you near launch.

"description": "Called after opening asl file in Step Functions Workflow Studio",
"metadata": [
{
"type": "id",
"required": true
}
]
},
{
"name": "vscode_activeRegions",
"description": "Record the number of active regions at startup and when regions are added/removed",
Expand Down
15 changes: 15 additions & 0 deletions packages/core/src/stepFunctions/workflowStudio/activation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import * as vscode from 'vscode'
import { WorkflowStudioEditorProvider } from './workflowStudioEditorProvider'

/**
* Activates the extension and registers all necessary components.
* @param extensionContext The extension context object.
*/
export async function activate(extensionContext: vscode.ExtensionContext): Promise<void> {
extensionContext.subscriptions.push(WorkflowStudioEditorProvider.register(extensionContext))
}
25 changes: 25 additions & 0 deletions packages/core/src/stepFunctions/workflowStudio/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import * as vscode from 'vscode'

export type WebviewContext = {
panel: vscode.WebviewPanel
textDocument: vscode.TextDocument
disposables: vscode.Disposable[]
workSpacePath: string
defaultTemplatePath: string
defaultTemplateName: string
loaderNotification: undefined | LoaderNotification
fileId: string
}

export type LoaderNotification = {
progress: vscode.Progress<{
message?: string | undefined
increment?: number | undefined
}>
cancellationToken: vscode.CancellationToken
resolve: () => void
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import * as path from 'path'
import * as vscode from 'vscode'
import { telemetry } from '../../shared/telemetry/telemetry'
import { getLogger } from '../../shared/logger'
import { i18n } from '../../shared/i18n-helper'
import { WebviewContext } from './types'
import { CancellationError } from '../../shared/utilities/timeoutUtils'

/**
* The main class for Workflow Studio Editor. This class handles the creation and management
* of the webview panel for integration. It also handles the communication
* between the webview and the extension context. This class stores the state of the
* local file that is being edited in the webview panel, in the property 'fileStates'. The
* 'autoSaveFileStates' property is used to store local changes that are being made in the
* webview panel.
*/
export class WorkflowStudioEditor {
public readonly documentUri: vscode.Uri
public webviewPanel: vscode.WebviewPanel
protected readonly disposables: vscode.Disposable[] = []
protected isPanelDisposed = false
private readonly onVisualizationDisposeEmitter = new vscode.EventEmitter<void>()
private fileId: string
public workSpacePath: string
public defaultTemplatePath: string
public defaultTemplateName: string
// TODO: add fileStates and autoSaveFileStates variables to store the state of the file and handle auto-save
private getWebviewContent: () => Promise<string>

public constructor(
textDocument: vscode.TextDocument,
webviewPanel: vscode.WebviewPanel,
context: vscode.ExtensionContext,
fileId: string,
getWebviewContent: () => Promise<string>
) {
this.getWebviewContent = getWebviewContent
this.documentUri = textDocument.uri
this.webviewPanel = webviewPanel
this.workSpacePath = path.dirname(textDocument.uri.fsPath)
this.defaultTemplatePath = textDocument.uri.fsPath
this.defaultTemplateName = path.basename(this.defaultTemplatePath)
this.fileId = fileId

telemetry.stepfunctions_openWorkflowStudio.record({
id: this.fileId,
})

this.setupWebviewPanel(textDocument, context)
}

public get onVisualizationDisposeEvent(): vscode.Event<void> {
return this.onVisualizationDisposeEmitter.event
}
public getPanel(): vscode.WebviewPanel | undefined {
if (!this.isPanelDisposed) {
return this.webviewPanel
}
}

public showPanel(): void {
this.getPanel()?.reveal()
}

public async refreshPanel(context: vscode.ExtensionContext) {
if (!this.isPanelDisposed) {
this.webviewPanel.dispose()
const document = await vscode.workspace.openTextDocument(this.documentUri)
this.setupWebviewPanel(document, context)
}
}

protected getText(textDocument: vscode.TextDocument): string {
return textDocument.getText()
}

/**
* Sets up the webview panel for Workflow Studio Editor. This includes creating the
* panel, setting up the webview content, and handling the communication between the webview
* and the extension context.
* @param textDocument The text document to be displayed in the webview panel.
* @param context The extension context.
* @private
*/
private setupWebviewPanel(textDocument: vscode.TextDocument, context: vscode.ExtensionContext) {
const documentUri = textDocument.uri

const contextObject: WebviewContext = {
panel: this.webviewPanel,
textDocument: textDocument,
disposables: this.disposables,
workSpacePath: this.workSpacePath,
defaultTemplatePath: this.defaultTemplatePath,
defaultTemplateName: this.defaultTemplateName,
loaderNotification: undefined,
fileId: this.fileId,
}

void vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: i18n('AWS.stepFunctions.workflowStudio.actions.progressMessage'),
cancellable: true,
},
(progress, token) => {
token.onCancellationRequested(async () => {
// Cancel opening in Worflow Studio and open regular code editor instead
getLogger().debug('WorkflowStudio: Canceled opening')
contextObject.panel.dispose()
await vscode.commands.executeCommand('vscode.openWith', documentUri, 'default')
throw new CancellationError('user')
})

progress.report({ increment: 0 })

return new Promise<void>(async (resolve) => {
contextObject.loaderNotification = {
progress: progress,
cancellationToken: token,
resolve,
}

// Initialise webview panel for Workflow Studio and set up initial content
this.webviewPanel.webview.options = {
enableScripts: true,
localResourceRoots: [context.extensionUri],
}

// Set the initial html for the webpage
this.webviewPanel.webview.html = await this.getWebviewContent()
progress.report({ increment: 15 })

// TODO: Hook up event handlers so that we can synchronize the webview with the text document.
// Note that a single text document can also be shared between multiple custom
// editors (e.g. this can happen when you split a custom editor)

// When the panel is closed, dispose of any disposables/remove subscriptions
this.disposables.push(
this.webviewPanel.onDidDispose(() => {
if (this.isPanelDisposed) {
return
}

this.isPanelDisposed = true
resolve()
this.onVisualizationDisposeEmitter.fire()
this.disposables.forEach((disposable) => {
disposable.dispose()
})
this.onVisualizationDisposeEmitter.dispose()
})
)
progress.report({ increment: 15 })
})
}
)
}
}
Loading
Loading