Skip to content

Commit bf4be27

Browse files
authored
feat(stepfunctions): Workflow Studio integration #6544
## Problem 1. Currently the indentation setting used in the integration defaults to 4 spaces. If it's different in user's VSCode setting, the spacing for the string sent from the integration will not match 2. Currently context menu lacks the command to launch WFS. It [has been added before](https://github.com/aws/aws-toolkit-vscode/pull/5834/files), but it looks it was accidentally added to auto-generated file and was thus removed later 3. WFS editor is opened by default in all modes, including conflict resolution and some other views 4. Bug: when opening split panel while having WFS integration open, it opens a panel with custom editor, which stays empty since we already have an instance of WFS editor launched for that file 5. Bug: If the webview is unavailable (e.g. CDN is down or the user is offline), integration throws an error modal and the file can't be opened easily, even in the default editor 6. Bug: when YAML in the local file is invalid, WFS integration is opened to the file, but since it fails to transform YAML to valid JSON, it opens with empty definition ## Solution 1. Passing a user setting for tab spacing to be used on the webview side to format JSON/YAML with the right indentation 2. Adding an option to launch WFS from context menu, as it was added before (but this time in the right package.json) 3. Add editorAssociation to not open WFS in specific environments that do not require it 4. When opening split panel panel while having WFS integration open, open that file in default editor 5. If the webview is unavailable, open file in default editor 6. When YAML in the local file is invalid, open default editor instead of WFS and show warning message (similar to what we do for invalid JSON)
1 parent dbfb0b2 commit bf4be27

File tree

4 files changed

+81
-25
lines changed

4 files changed

+81
-25
lines changed

packages/core/package.nls.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
"AWS.stepFunctions.asl.maxItemsComputed.desc": "The maximum number of outline symbols and folding regions computed (limited for performance reasons).",
2727
"AWS.stepFunctions.workflowStudio.actions.progressMessage": "Opening asl file in Workflow Studio",
2828
"AWS.stepFunctions.workflowStudio.actions.saveSuccessMessage": "{0} has been saved",
29-
"AWS.stepFunctions.workflowStudio.actions.invalidJson": "The Workflow Studio editor was not opened because the JSON in the file is invalid. To access Workflow Studio, please fix the JSON and manually reopen the integration.",
29+
"AWS.stepFunctions.workflowStudio.actions.InvalidJSONContent": "The Workflow Studio editor was not opened because the JSON in the file is invalid. To access Workflow Studio, please fix the JSON and manually reopen the integration.",
30+
"AWS.stepFunctions.workflowStudio.actions.InvalidYAMLContent": "The Workflow Studio editor was not opened because the YAML in the file is invalid. To access Workflow Studio, please fix the YAML and manually reopen the integration.",
31+
"AWS.stepFunctions.workflowStudio.actions.webviewFetchFailed": "Failed to load Workflow Studio editor. Please check your network connection and try again.",
3032
"AWS.configuration.description.awssam.debug.api": "API Gateway configuration",
3133
"AWS.configuration.description.awssam.debug.api.clientCertId": "The API Gateway client certificate ID",
3234
"AWS.configuration.description.awssam.debug.api.headers": "Additional HTTP headers",

packages/core/src/stepFunctions/utils.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as nls from 'vscode-nls'
66
const localize = nls.loadMessageBundle()
77

88
import { IAM, StepFunctions } from 'aws-sdk'
9+
import * as yaml from 'js-yaml'
910
import * as vscode from 'vscode'
1011
import { StepFunctionsClient } from '../shared/clients/stepFunctionsClient'
1112
import { fileExists } from '../shared/filesystemUtilities'
@@ -232,7 +233,7 @@ export async function isDocumentValid(text: string, textDocument?: vscode.TextDo
232233
}
233234

234235
/**
235-
* Checks if the JSON content in a text document is invalid.
236+
* Checks if the JSON content in an ASL text document is invalid.
236237
* Returns `true` for invalid JSON; `false` for valid JSON, empty content, or non-JSON files.
237238
*
238239
* @param textDocument - The text document to check.
@@ -241,9 +242,25 @@ export async function isDocumentValid(text: string, textDocument?: vscode.TextDo
241242
export const isInvalidJsonFile = (textDocument: vscode.TextDocument): boolean => {
242243
const documentContent = textDocument.getText().trim()
243244
// An empty file or whitespace-only text is considered valid JSON for our use case
244-
return textDocument.fileName.toLowerCase().endsWith('.json') && documentContent
245-
? isInvalidJson(documentContent)
246-
: false
245+
return textDocument.languageId === 'asl' && documentContent ? isInvalidJson(documentContent) : false
246+
}
247+
248+
/**
249+
* Checks if the YAML content in an ASL text document is invalid.
250+
* Returns `true` for invalid YAML; `false` for valid YAML, empty content, or non-YAML files.
251+
*
252+
* @param textDocument - The text document to check.
253+
* @returns `true` if invalid; `false` otherwise.
254+
*/
255+
export const isInvalidYamlFile = (textDocument: vscode.TextDocument): boolean => {
256+
try {
257+
if (textDocument.languageId === 'asl-yaml') {
258+
yaml.load(textDocument.getText())
259+
}
260+
return false
261+
} catch {
262+
return true
263+
}
247264
}
248265

249266
const isInvalidJson = (content: string): boolean => {

packages/core/src/stepFunctions/workflowStudio/workflowStudioEditorProvider.ts

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import { telemetry } from '../../shared/telemetry/telemetry'
1212
import globals from '../../shared/extensionGlobals'
1313
import { getRandomString, getStringHash } from '../../shared/utilities/textUtilities'
1414
import { ToolkitError } from '../../shared/errors'
15+
import { getTabSizeSetting } from '../../shared/utilities/editorUtilities'
1516
import { WorkflowStudioEditor } from './workflowStudioEditor'
1617
import { i18n } from '../../shared/i18n-helper'
17-
import { isInvalidJsonFile } from '../utils'
18+
import { isInvalidJsonFile, isInvalidYamlFile } from '../utils'
1819

1920
const isLocalDev = false
2021
const localhost = 'http://127.0.0.1:3002'
@@ -72,9 +73,6 @@ export class WorkflowStudioEditorProvider implements vscode.CustomTextEditorProv
7273
* @private
7374
*/
7475
private getWebviewContent = async () => {
75-
if (!this.webviewHtml) {
76-
await this.fetchWebviewHtml()
77-
}
7876
let htmlFileSplit = this.webviewHtml.split('<head>')
7977

8078
// Set asset source to CDN
@@ -86,13 +84,15 @@ export class WorkflowStudioEditorProvider implements vscode.CustomTextEditorProv
8684
const localeTag = `<meta name='locale' content='${locale}'>`
8785
const theme = vscode.window.activeColorTheme.kind
8886
const isDarkMode = theme === vscode.ColorThemeKind.Dark || theme === vscode.ColorThemeKind.HighContrast
87+
const tabSizeTag = `<meta name='tab-size' content='${getTabSizeSetting()}'>`
8988
const darkModeTag = `<meta name='dark-mode' content='${isDarkMode}'>`
90-
let html = `${htmlFileSplit[0]} <head> ${baseTag} ${localeTag} ${darkModeTag} ${htmlFileSplit[1]}`
89+
let html = `${htmlFileSplit[0]} <head> ${baseTag} ${localeTag} ${darkModeTag} ${tabSizeTag} ${htmlFileSplit[1]}`
9190

9291
const nonce = getRandomString()
92+
const localDevURL = isLocalDev ? localhost : ''
9393
htmlFileSplit = html.split("script-src 'self'")
9494

95-
html = `${htmlFileSplit[0]} script-src 'self' 'nonce-${nonce}' ${isLocalDev && localhost} ${htmlFileSplit[1]}`
95+
html = `${htmlFileSplit[0]} script-src 'self' 'nonce-${nonce}' ${localDevURL} ${htmlFileSplit[1]}`
9696
htmlFileSplit = html.split('<body>')
9797

9898
const script = await fs.readFileText(
@@ -115,35 +115,56 @@ export class WorkflowStudioEditorProvider implements vscode.CustomTextEditorProv
115115
_token: vscode.CancellationToken
116116
): Promise<void> {
117117
await telemetry.stepfunctions_openWorkflowStudio.run(async () => {
118-
// For invalid JSON, open default editor and show warning message
119-
if (isInvalidJsonFile(document)) {
118+
const reopenWithDefaultEditor = async () => {
120119
await vscode.commands.executeCommand('vscode.openWith', document.uri, 'default')
121120
webviewPanel.dispose()
122-
void vscode.window.showWarningMessage(i18n('AWS.stepFunctions.workflowStudio.actions.invalidJson'))
121+
}
122+
123+
const isInvalidJson = isInvalidJsonFile(document)
124+
const isInvalidYaml = isInvalidYamlFile(document)
123125

126+
if (isInvalidJson || isInvalidYaml) {
127+
const language = isInvalidJson ? 'JSON' : 'YAML'
128+
const errorKey = isInvalidJson ? 'InvalidJSONContent' : 'InvalidYAMLContent'
129+
130+
await reopenWithDefaultEditor()
131+
void vscode.window.showWarningMessage(i18n(`AWS.stepFunctions.workflowStudio.actions.${errorKey}`))
124132
throw ToolkitError.chain(
125-
'Invalid JSON file',
126-
'The Workflow Studio editor was not opened because the JSON in the file is invalid',
127-
{
128-
code: 'InvalidJSONContent',
129-
}
133+
`Invalid ${language} file`,
134+
`The Workflow Studio editor was not opened because the ${language} in the file is invalid`,
135+
{ code: errorKey }
130136
)
131137
}
132138

133139
if (!this.webviewHtml) {
134-
await this.fetchWebviewHtml()
140+
try {
141+
await this.fetchWebviewHtml()
142+
} catch (e) {
143+
await reopenWithDefaultEditor()
144+
145+
void vscode.window.showWarningMessage(
146+
i18n('AWS.stepFunctions.workflowStudio.actions.webviewFetchFailed')
147+
)
148+
throw ToolkitError.chain(
149+
'Failed to fetch editor content',
150+
'Could not retrieve content for the Workflow Studio editor',
151+
{
152+
code: 'webviewFetchFailed',
153+
}
154+
)
155+
}
135156
}
136157

137158
if (clientId === '') {
138159
clientId = getClientId(globals.globalState)
139160
}
140-
// Attempt to retrieve existing visualization if it exists.
141-
const existingVisualization = this.managedVisualizations.get(document.uri.fsPath)
142161

162+
const existingVisualization = this.managedVisualizations.get(document.uri.fsPath)
143163
if (existingVisualization) {
144-
existingVisualization.showPanel()
164+
// Prevent opening multiple custom editors for a single file
165+
await reopenWithDefaultEditor()
145166
} else {
146-
// Existing visualization does not exist, construct new visualization
167+
// Construct new visualization
147168
try {
148169
const fileId = getStringHash(`${document.uri.fsPath}${clientId}`)
149170
const newVisualization = new WorkflowStudioEditor(

packages/toolkit/package.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1412,6 +1412,11 @@
14121412
"command": "aws.openInApplicationComposer",
14131413
"when": "isFileSystemResource && !(resourceFilename =~ /^.*\\.tc\\.json$/) && resourceFilename =~ /^.*\\.(json|yml|yaml|template)$/",
14141414
"group": "z_aws@1"
1415+
},
1416+
{
1417+
"command": "aws.stepfunctions.openWithWorkflowStudio",
1418+
"when": "isFileSystemResource && resourceFilename =~ /^.*\\.asl\\.(json|yml|yaml)$/",
1419+
"group": "z_aws@1"
14151420
}
14161421
],
14171422
"view/item/context": [
@@ -3917,6 +3922,16 @@
39173922
}
39183923
}
39193924
},
3925+
{
3926+
"command": "aws.stepfunctions.openWithWorkflowStudio",
3927+
"title": "%AWS.command.stepFunctions.openWithWorkflowStudio%",
3928+
"category": "%AWS.title%",
3929+
"cloud9": {
3930+
"cn": {
3931+
"category": "%AWS.title.cn%"
3932+
}
3933+
}
3934+
},
39203935
{
39213936
"command": "aws.createNewThreatComposer",
39223937
"title": "%AWS.command.threatComposer.createNew%",
@@ -4654,7 +4669,8 @@
46544669
],
46554670
"configurationDefaults": {
46564671
"workbench.editorAssociations": {
4657-
"{git,gitlens,conflictResolution,vscode-local-history}:/**/*.tc.json": "default"
4672+
"{git,gitlens,conflictResolution,vscode-local-history}:/**/*.tc.json": "default",
4673+
"{git,gitlens,conflictResolution,vscode-local-history}:/**/*.{asl.json,asl.yaml,asl.yml}": "default"
46584674
}
46594675
}
46604676
},

0 commit comments

Comments
 (0)