-
Notifications
You must be signed in to change notification settings - Fork 149
Improve Event Grid Local Development Experience #3984
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
Merged
Merged
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
efa7e82
first working poc
hossam-nasr f8f4558
prompt for fields enclosed by {}
hossam-nasr 9995b87
add button and code lens + clean up and refactor
hossam-nasr 6c48f01
fix rebase
hossam-nasr 9fa9774
more refactor and cleanup
hossam-nasr 8c646aa
fix change in tree
hossam-nasr 7c77ae7
add license comments
hossam-nasr 20cc9e6
import type
hossam-nasr 3a1425d
restructure
hossam-nasr 00089ec
switch to using a wizard
hossam-nasr 1ea43c6
keep the file open and remember the function associated with each file
hossam-nasr 2adbf39
add aka.ms link & add code lens at the bottom
hossam-nasr f043928
switch to using AzExtFsExtra
hossam-nasr e0a3cb0
wrap in try/finally
hossam-nasr 012a593
rename to save and execute
hossam-nasr 8ee991e
revert aka.ms link for now
hossam-nasr 203c151
fix codelens last line
hossam-nasr 9b2eeb7
remove commented out code
hossam-nasr acd4317
add source of event sources
hossam-nasr 79ea4f3
revert styling changes in unrelated file
hossam-nasr b2029b4
remove last line codelens
hossam-nasr 1ceca9e
add licenses
hossam-nasr 4eb5e40
PR nits
hossam-nasr b17f5e8
show info box only once per session
hossam-nasr 907ce44
update to use workspaceState
hossam-nasr 3a93fe6
PR feedback
hossam-nasr 3c055fd
remove weird defaulting
hossam-nasr bdf6a59
telemetry: entry point
hossam-nasr b24e300
telemetry: event source & type
hossam-nasr 774e414
telemetry: whether file was modified
hossam-nasr 4241096
check
hossam-nasr 99333f8
use constants
hossam-nasr e90ead8
Merge branch 'main' into hossamnasr/eventgrid-lde
hossam-nasr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
src/commands/executeFunction/eventGrid/EventGridCodeLensProvider.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
| import { CodeLens, Range, type CodeLensProvider } from 'vscode'; | ||
| import { localize } from '../../../localize'; | ||
|
|
||
| export class EventGridCodeLensProvider implements CodeLensProvider { | ||
| public provideCodeLenses(): CodeLens[] { | ||
| const firstLineLens = new CodeLens(new Range(0, 0, 0, 0)); | ||
|
|
||
| firstLineLens.command = { | ||
| title: localize('saveExecute', 'Save and execute'), | ||
| command: 'azureFunctions.eventGrid.sendMockRequest', | ||
| arguments: ['codeLens'] | ||
|
hossam-nasr marked this conversation as resolved.
Outdated
|
||
| }; | ||
|
|
||
| return [firstLineLens]; | ||
| } | ||
| } | ||
15 changes: 15 additions & 0 deletions
15
src/commands/executeFunction/eventGrid/EventGridExecuteFunctionContext.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| import { type IActionContext } from "@microsoft/vscode-azext-utils"; | ||
| import { type EventGridSource } from "./eventGridSources"; | ||
|
|
||
| export interface EventGridExecuteFunctionContext extends IActionContext { | ||
| eventSource?: EventGridSource; | ||
| selectedFileName?: string; | ||
| selectedFileUrl?: string; | ||
| fileOpened?: boolean; | ||
| } | ||
|
|
118 changes: 118 additions & 0 deletions
118
src/commands/executeFunction/eventGrid/EventGridFileOpenStep.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| import { AzExtFsExtra, AzureWizardExecuteStep, callWithTelemetryAndErrorHandling, nonNullProp, type IActionContext } from "@microsoft/vscode-azext-utils"; | ||
| import * as os from 'os'; | ||
| import * as path from "path"; | ||
| import * as vscode from 'vscode'; | ||
| import { type Progress } from "vscode"; | ||
| import { ext } from "../../../extensionVariables"; | ||
| import { localize } from "../../../localize"; | ||
| import { feedUtils } from "../../../utils/feedUtils"; | ||
| import { type EventGridExecuteFunctionContext } from "./EventGridExecuteFunctionContext"; | ||
|
|
||
| export class EventGridFileOpenStep extends AzureWizardExecuteStep<EventGridExecuteFunctionContext> { | ||
| public priority: number; | ||
|
|
||
| public async execute(context: EventGridExecuteFunctionContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined; }>): Promise<void> { | ||
| const eventSource = nonNullProp(context, 'eventSource'); | ||
| const selectedFileName = nonNullProp(context, 'selectedFileName'); | ||
| const selectedFileUrl = nonNullProp(context, 'selectedFileUrl'); | ||
|
|
||
| // Get selected contents of sample request | ||
| const downloadingMsg: string = localize('downloadingSample', 'Downloading sample request...'); | ||
| progress.report({ message: downloadingMsg }); | ||
| const selectedFileContent = await feedUtils.getJsonFeed(context, selectedFileUrl); | ||
|
|
||
| // Create a temp file with the sample request & open in new window | ||
| const openingFileMsg: string = localize('openingFile', 'Opening file...'); | ||
| progress.report({ message: openingFileMsg }); | ||
| const tempFilePath: string = await createTempSampleFile(eventSource, selectedFileName, selectedFileContent); | ||
| const document: vscode.TextDocument = await vscode.workspace.openTextDocument(tempFilePath); | ||
| await vscode.window.showTextDocument(document, { | ||
| preview: false, | ||
| }); | ||
| ext.fileToFunctionNodeMap.set(document.fileName, nonNullProp(ext, 'currentExecutingFunctionNode')); | ||
| context.fileOpened = true; | ||
|
|
||
| // Request will be sent when the user clicks on the button or on the codelens link | ||
| // Show the message only once per workspace | ||
| if (!ext.context.workspaceState.get('didShowEventGridFileOpenMsg')) { | ||
| const doneMsg = localize('modifyFile', "You can modify the file and then click the 'Save and execute' button to send the request."); | ||
| void vscode.window.showInformationMessage(doneMsg); | ||
| await ext.context.workspaceState.update('didShowEventGridFileOpenMsg', true); | ||
| } | ||
|
|
||
|
hossam-nasr marked this conversation as resolved.
|
||
| // Set a listener to track whether the file was modified before the request is sent | ||
| let modifiedListenerDisposable: vscode.Disposable; | ||
| void new Promise<void>((resolve, reject) => { | ||
| modifiedListenerDisposable = vscode.workspace.onDidChangeTextDocument(async (event) => { | ||
|
nturinski marked this conversation as resolved.
|
||
| if (event.contentChanges.length > 0 && event.document.fileName === document.fileName) { | ||
| try { | ||
| await callWithTelemetryAndErrorHandling('eventGridSampleModified', async (actionContext: IActionContext) => { | ||
| actionContext.telemetry.properties.eventGridSampleModified = 'true'; | ||
| }); | ||
| resolve(); | ||
| } catch (error) { | ||
| context.errorHandling.suppressDisplay = true; | ||
| reject(error); | ||
| } finally { | ||
| modifiedListenerDisposable.dispose(); | ||
| } | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| // Set a listener to delete the temp file after it's closed | ||
| void new Promise<void>((resolve, reject) => { | ||
| const closedListenerDisposable = vscode.workspace.onDidCloseTextDocument(async (closedDocument) => { | ||
| if (closedDocument.fileName === document.fileName) { | ||
| try { | ||
| ext.fileToFunctionNodeMap.delete(document.fileName); | ||
| await AzExtFsExtra.deleteResource(tempFilePath); | ||
| resolve(); | ||
| } catch (error) { | ||
| context.errorHandling.suppressDisplay = true; | ||
| reject(error); | ||
|
hossam-nasr marked this conversation as resolved.
|
||
| } finally { | ||
| closedListenerDisposable.dispose(); | ||
| if (modifiedListenerDisposable) { | ||
| modifiedListenerDisposable.dispose(); | ||
| } | ||
| } | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| } | ||
|
|
||
| public shouldExecute(context: EventGridExecuteFunctionContext): boolean { | ||
| return !context.fileOpened | ||
| } | ||
|
|
||
| } | ||
|
|
||
| async function createTempSampleFile(eventSource: string, fileName: string, contents: {}): Promise<string> { | ||
| const samplesDirPath = await getSamplesDirPath(eventSource); | ||
| const sampleFileName = fileName.replace(/\.json$/, '.eventgrid.json'); | ||
| const filePath: string = path.join(samplesDirPath, sampleFileName); | ||
|
|
||
| await AzExtFsExtra.writeJSON(filePath, contents); | ||
|
|
||
| return filePath; | ||
| } | ||
|
|
||
| async function getSamplesDirPath(eventSource: string): Promise<string> { | ||
| // Create the path to the directory | ||
| const baseDir: string = path.join(os.tmpdir(), 'vscode', 'azureFunctions', 'eventGridSamples'); | ||
| const dirPath = path.join(baseDir, eventSource); | ||
|
|
||
| // Create the directory if it doesn't already exist | ||
| await AzExtFsExtra.ensureDir(dirPath); | ||
|
|
||
| // Return the path to the directory | ||
| return dirPath; | ||
| } | ||
|
|
||
37 changes: 37 additions & 0 deletions
37
src/commands/executeFunction/eventGrid/EventGridSourceStep.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| import { AzureWizardPromptStep, type IAzureQuickPickItem } from "@microsoft/vscode-azext-utils"; | ||
| import { localize } from "../../../localize"; | ||
| import { type EventGridExecuteFunctionContext } from "./EventGridExecuteFunctionContext"; | ||
| import { supportedEventGridSourceLabels, supportedEventGridSources, type EventGridSource } from "./eventGridSources"; | ||
|
|
||
| export class EventGridSourceStep extends AzureWizardPromptStep<EventGridExecuteFunctionContext> { | ||
| public hideStepCount: boolean = false; | ||
|
|
||
| public async prompt(context: EventGridExecuteFunctionContext): Promise<void> { | ||
| // Prompt for event source | ||
| const eventGridSourcePicks: IAzureQuickPickItem<EventGridSource | undefined>[] = supportedEventGridSources.map((source: EventGridSource) => { | ||
| return { | ||
| label: supportedEventGridSourceLabels.get(source) || source, | ||
| data: source, | ||
| }; | ||
| }); | ||
| const eventSource = | ||
| ( | ||
| await context.ui.showQuickPick(eventGridSourcePicks, { | ||
| placeHolder: localize('selectEventSource', 'Select the event source'), | ||
| stepName: 'eventGridSource', | ||
| }) | ||
| ).data; | ||
|
|
||
| context.telemetry.properties.eventGridSource = eventSource; | ||
| context.eventSource = eventSource; | ||
| } | ||
|
|
||
| public shouldPrompt(context: EventGridExecuteFunctionContext): boolean { | ||
| return !context.eventSource; | ||
| } | ||
| } |
73 changes: 73 additions & 0 deletions
73
src/commands/executeFunction/eventGrid/EventGridTypeStep.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| import { AzureWizardPromptStep, nonNullProp, type IAzureQuickPickItem } from "@microsoft/vscode-azext-utils"; | ||
| import { localize } from "../../../localize"; | ||
| import { feedUtils } from "../../../utils/feedUtils"; | ||
| import { type EventGridExecuteFunctionContext } from "./EventGridExecuteFunctionContext"; | ||
|
|
||
| const sampleFilesUrl = | ||
| 'https://api.github.com/repos/Azure/azure-rest-api-specs/contents/specification/eventgrid/data-plane/' + | ||
| '{eventSource}' + | ||
| '/stable/2018-01-01/examples/cloud-events-schema/'; | ||
|
|
||
| type FileMetadata = { | ||
| name: string; | ||
| path: string; | ||
| sha: string; | ||
| size: number; | ||
| url: string; | ||
| html_url: string; | ||
| git_url: string; | ||
| download_url: string; | ||
| type: string; | ||
| _links: { | ||
| self: string; | ||
| git: string; | ||
| html: string; | ||
| }; | ||
| }; | ||
|
|
||
| export class EventGridTypeStep extends AzureWizardPromptStep<EventGridExecuteFunctionContext> { | ||
| public hideStepCount: boolean = false; | ||
|
|
||
| public async prompt(context: EventGridExecuteFunctionContext): Promise<void> { | ||
| const eventSource = nonNullProp(context, 'eventSource'); | ||
|
|
||
| // Get sample files for event source | ||
| const samplesUrl = sampleFilesUrl.replace('{eventSource}', eventSource); | ||
| const sampleFiles: FileMetadata[] = await feedUtils.getJsonFeed(context, samplesUrl); | ||
| const fileNames: string[] = sampleFiles.map((fileMetadata) => fileMetadata.name); | ||
|
|
||
| // Prompt for event type | ||
| const eventTypePicks: IAzureQuickPickItem<string | undefined>[] = fileNames.map((name: string) => ({ | ||
| data: name, | ||
| // give human-readable name for event type from file name | ||
| label: name | ||
| .replace(/\.json$/, '') | ||
| .split('_') | ||
| .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) | ||
| .join(' '), | ||
| })); | ||
|
|
||
| const selectedFileName = | ||
| ( | ||
| await context.ui.showQuickPick(eventTypePicks, { | ||
| placeHolder: localize('selectEventType', 'Select the event type'), | ||
| stepName: 'eventType', | ||
| }) | ||
| ).data; | ||
|
|
||
| context.telemetry.properties.eventGridSample = selectedFileName; | ||
| context.selectedFileName = selectedFileName; | ||
|
|
||
| context.selectedFileUrl = sampleFiles.find((fileMetadata) => fileMetadata.name === context.selectedFileName)?.download_url || sampleFiles[0].download_url; | ||
|
|
||
| } | ||
|
|
||
| public shouldPrompt(context: EventGridExecuteFunctionContext): boolean { | ||
| return !context.selectedFileName; | ||
| } | ||
| } |
70 changes: 70 additions & 0 deletions
70
src/commands/executeFunction/eventGrid/eventGridSources.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
|
hossam-nasr marked this conversation as resolved.
|
||
| /** | ||
| * Sources here were obtained as the names of sources found on | ||
| * the EventGrid samples in the azure-rest-api-specs repository: | ||
| * https://github.com/Azure/azure-rest-api-specs/tree/master/specification/eventgrid/data-plane | ||
| */ | ||
|
|
||
| export type EventGridSource = | ||
| | 'Microsoft.ApiManagement' | ||
| | 'Microsoft.AppConfiguration' | ||
| | 'Microsoft.AVS' | ||
| | 'Microsoft.Cache' | ||
| | 'Microsoft.Communication' | ||
| | 'Microsoft.ContainerRegistry' | ||
| | 'Microsoft.ContainerService' | ||
| | 'Microsoft.DataBox' | ||
| | 'Microsoft.Devices' | ||
| | 'Microsoft.EventHub' | ||
| | 'Microsoft.HealthcareApis' | ||
| | 'Microsoft.KeyVault' | ||
| | 'Microsoft.MachineLearningServices' | ||
| | 'Microsoft.Maps' | ||
| | 'Microsoft.Media' | ||
| | 'Microsoft.PolicyInsights' | ||
| | 'Microsoft.ResourceNotification' | ||
| | 'Microsoft.Resources' | ||
| | 'Microsoft.ServiceBus' | ||
| | 'Microsoft.SignalRService' | ||
| | 'Microsoft.Storage' | ||
| | 'Microsoft.Web' | ||
| | string; | ||
|
|
||
| export const supportedEventGridSources: EventGridSource[] = [ | ||
| 'Microsoft.ApiManagement', | ||
| 'Microsoft.AppConfiguration', | ||
| 'Microsoft.AVS', | ||
| 'Microsoft.Cache', | ||
| 'Microsoft.Communication', | ||
| 'Microsoft.ContainerRegistry', | ||
| 'Microsoft.ContainerService', | ||
| 'Microsoft.DataBox', | ||
| 'Microsoft.Devices', | ||
| 'Microsoft.EventHub', | ||
| 'Microsoft.HealthcareApis', | ||
| 'Microsoft.KeyVault', | ||
| 'Microsoft.MachineLearningServices', | ||
| 'Microsoft.Maps', | ||
| 'Microsoft.Media', | ||
| 'Microsoft.PolicyInsights', | ||
| 'Microsoft.ResourceNotification', | ||
| 'Microsoft.Resources', | ||
| 'Microsoft.ServiceBus', | ||
| 'Microsoft.SignalRService', | ||
| 'Microsoft.Storage', | ||
| 'Microsoft.Web', | ||
| ]; | ||
|
|
||
| export const supportedEventGridSourceLabels: Map<EventGridSource, string> = new Map([ | ||
| ['Microsoft.Storage', 'Blob Storage'], | ||
| ['Microsoft.EventHub', 'Event Hubs'], | ||
| ['Microsoft.ServiceBus', 'Service Bus'], | ||
| ['Microsoft.ContainerRegistry', 'Container Registry'], | ||
| ['Microsoft.ApiManagement', 'API Management'], | ||
| ['Microsoft.Resources', 'Resources'], | ||
| ['Microsoft.HealthcareApis', 'Health Data Services'], | ||
| ]); | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.