Skip to content

Commit eb71672

Browse files
bjbennetCopilot
andauthored
fix: Codeful connection loading (#9601)
* codeful connection loading fix * fix: address connection loading review feedback Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent f32ee0b commit eb71672

11 files changed

Lines changed: 382 additions & 52 deletions

File tree

‎Localize/lang/strings.json‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@
172172
"1LSKq8": "Basics",
173173
"1NBvKu": "Convert the parameter argument to a floating-point number",
174174
"1NVeRR": "This assistant can help you learn about your workflows, answer questions about Azure Logic Apps, and make changes to your workflow.",
175+
"1OIW6v": "Set up now",
175176
"1Orv4i": "Details",
176177
"1REu5/": "See less",
177178
"1Xke9D": "open functions drawer",
@@ -1058,6 +1059,7 @@
10581059
"IjoW0x": "Dynamic Parameters",
10591060
"IjvmvR": "Dismiss trigger info message",
10601061
"IlyNs0": "{overflowItemsLength} more item",
1062+
"ImBIsD": "The connector could not be found.",
10611063
"Iov0/J": "MCP server name",
10621064
"IpD27y": "Logic app instance",
10631065
"IpUfon": "Location",
@@ -1528,6 +1530,7 @@
15281530
"S2KtbJ": "Select date and time",
15291531
"S4Bx4M": "Review your export configuration",
15301532
"S5kFNK": "Paste your sample data to test the mapping",
1533+
"S6TcFA": "Error loading connector",
15311534
"SC5XB0": "Create Parameter",
15321535
"SCCE6s": "Password",
15331536
"SGnTpt": "Heading 2",
@@ -2134,6 +2137,7 @@
21342137
"_1LSKq8.comment": "Accessibility label for the basics section",
21352138
"_1NBvKu.comment": "Label for description of custom float Function",
21362139
"_1NVeRR.comment": "Chatbot introduction message when workflow editing is enabled",
2140+
"_1OIW6v.comment": "Button text to start Azure connector setup",
21372141
"_1Orv4i.comment": "Title for the details section",
21382142
"_1REu5/.comment": "Select to view fewer token options.",
21392143
"_1Xke9D.comment": "aria label to open functions drawer",
@@ -3020,6 +3024,7 @@
30203024
"_IjoW0x.comment": "Title for dynamic inputs error message",
30213025
"_IjvmvR.comment": "Dismiss button label for trigger info",
30223026
"_IlyNs0.comment": "Message to show when exactly 1 item is present in the overflow menu",
3027+
"_ImBIsD.comment": "Error shown when no connector metadata is returned for the connection view",
30233028
"_Iov0/J.comment": "Label for the MCP server name field",
30243029
"_IpD27y.comment": "Label field for logic app instance",
30253030
"_IpUfon.comment": "Location label",
@@ -3490,6 +3495,7 @@
34903495
"_S2KtbJ.comment": "Label for custom date time picker",
34913496
"_S4Bx4M.comment": "Review description",
34923497
"_S5kFNK.comment": "Sample test data placeholder",
3498+
"_S6TcFA.comment": "Title shown when connector metadata cannot be loaded in the connection view",
34933499
"_SC5XB0.comment": "label to add a parameter",
34943500
"_SCCE6s.comment": "Basic Password Label Display Name",
34953501
"_SGnTpt.comment": "Heading 2 text",
@@ -3943,6 +3949,7 @@
39433949
"_aKf/r+.comment": "Run identifier not found error message",
39443950
"_aP1wk9.comment": "Label for the description of a custom 'indexOf' function",
39453951
"_aPxsVd.comment": "Button text for adding a new connection",
3952+
"_aRrWLB.comment": "Description shown when Azure connector setup is required to manage a connection",
39463953
"_aSnCCB.comment": "Header for resource lcoation",
39473954
"_aV/nLS.comment": "Frequency value ",
39483955
"_aWcxdZ.comment": "Label text custom templates tab",
@@ -4099,6 +4106,7 @@
40994106
"_dMAC4C.comment": "Chatbot prompt to replace an action",
41004107
"_dMklCt.comment": "Description for creating a simple logic app",
41014108
"_dOpdsP.comment": "Update Expression",
4109+
"_dQMghX.comment": "Button text to close the connection view without setting up Azure connectors",
41024110
"_dQmi91.comment": "Label for Frequency",
41034111
"_dU0RgS.comment": "Add a multi-select list of options to the text input dynamic parameter",
41044112
"_dU7f7n.comment": "The type for application insights resource",
@@ -4166,6 +4174,7 @@
41664174
"_elD6+N.comment": "Command for italic text for Mac users",
41674175
"_emnH/J.comment": "Prerequisites label",
41684176
"_en/5A3.comment": "Button text to reassign actions",
4177+
"_ensbBh.comment": "Button text to retry loading connector metadata",
41694178
"_eo1YKX.comment": "description of the split on setting",
41704179
"_eofB85.comment": "The status message to show timed out in monitoring view.",
41714180
"_epOMnV.comment": "Shared category name text",
@@ -4260,6 +4269,7 @@
42604269
"_ginGIZ.comment": "Time zone value ",
42614270
"_gkUDy6.comment": "Content for the toaster for adding workflows",
42624271
"_gkY5ya.comment": "Delete dynamic parameter corresponding to this row",
4272+
"_gkckoj.comment": "Title shown when a managed connection cannot be opened because Azure connector setup was skipped",
42634273
"_gkwo0O.comment": "Error message shown when required fields are not filled in new logic app details",
42644274
"_gl+tO3.comment": "Allowed values label",
42654275
"_gl0im8.comment": "Error message for selecting past time",
@@ -4336,6 +4346,7 @@
43364346
"_hzJ613.comment": "Label for the subscription field",
43374347
"_hzJxEr.comment": "Warning body for when unable to parse schema",
43384348
"_hza/cT.comment": "Time zone value ",
4349+
"_i+FZxg.comment": "Message shown while loading connector metadata for the connection view",
43394350
"_i/R+Q7.comment": "Text for local time",
43404351
"_i/SguY.comment": "Text for dropdown of system-assigned managed identity",
43414352
"_i0XjL5.comment": "Placeholder for search box that searches operations",
@@ -5317,6 +5328,7 @@
53175328
"aKf/r+": "Specified run identifier not found",
53185329
"aP1wk9": "Returns the first index of a value within a string (case-insensitive, invariant culture)",
53195330
"aPxsVd": "Adding new connection...",
5331+
"aRrWLB": "Set up Azure connectors to manage this connection.",
53205332
"aSnCCB": "Location",
53215333
"aV/nLS": "Second",
53225334
"aWcxdZ": "My Templates",
@@ -5473,6 +5485,7 @@
54735485
"dMAC4C": "Replace action",
54745486
"dMklCt": "Provide the additional requested details to create a logic app with the default values.",
54755487
"dOpdsP": "Update",
5488+
"dQMghX": "Cancel",
54765489
"dQmi91": "Frequency",
54775490
"dU0RgS": "Add a multi-select list of options",
54785491
"dU7f7n": "Application Insights",
@@ -5540,6 +5553,7 @@
55405553
"elD6+N": "Italic (⌘I)",
55415554
"emnH/J": "Prerequisites",
55425555
"en/5A3": "Reassign",
5556+
"ensbBh": "Retry",
55435557
"eo1YKX": "Enable split-on to start an instance of the workflow per item in the selected array. Each instance can also have a distinct tracking id",
55445558
"eofB85": "Timed out",
55455559
"epOMnV": "Shared",
@@ -5634,6 +5648,7 @@
56345648
"ginGIZ": "(UTC-06:00) Guadalajara, Mexico City, Monterrey",
56355649
"gkUDy6": "This template contains more than one workflow, therefore it is classified as an Accelerator.",
56365650
"gkY5ya": "Delete",
5651+
"gkckoj": "Azure connector setup has not been completed",
56375652
"gkwo0O": "Please fill all required fields and then continue.",
56385653
"gl+tO3": "Allowed values",
56395654
"gl0im8": "Please select a future time.",
@@ -5710,6 +5725,7 @@
57105725
"hzJ613": "Subscription",
57115726
"hzJxEr": "This could mean that the instruction schema is set up incorrectly.",
57125727
"hza/cT": "(UTC+00:00) Casablanca",
5728+
"i+FZxg": "Loading connector data...",
57135729
"i/R+Q7": "Local time",
57145730
"i/SguY": "System-assigned managed identity",
57155731
"i0XjL5": "Search for operation",

‎apps/vs-code-designer/src/app/commands/workflows/connectionView/panels/__test__/connectionPanelRace.test.ts‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ vi.mock('../../../../azureConnectors/azureConnectorDetails', () => ({
2727
getAzureConnectorDetailsForLocalProject: vi.fn(),
2828
}));
2929

30+
vi.mock('../../../../azureConnectors/enableAzureConnectors', () => ({
31+
enableAzureConnectors: vi.fn(),
32+
}));
33+
3034
vi.mock('../../../../../utils/codeless/startDesignTimeApi', () => ({
3135
startDesignTimeApi: vi.fn(),
3236
}));
@@ -248,3 +252,57 @@ describe('ConnectionPanel – getConnectionPanelMetadata reads localSettings aft
248252
expect(getLocalSettingsJson).toHaveBeenCalled();
249253
});
250254
});
255+
256+
describe('ConnectionPanel – configure Azure connectors recovery', () => {
257+
beforeEach(() => {
258+
vi.clearAllMocks();
259+
});
260+
261+
it('reloads the connection webview after Azure connector setup succeeds', async () => {
262+
const { enableAzureConnectors } = await import('../../../../azureConnectors/enableAzureConnectors');
263+
const instance = createInstance() as any;
264+
const panelMetadata = {
265+
artifacts: { maps: {}, schemas: [] },
266+
azureDetails: { enabled: true, subscriptionId: 'sub-1' },
267+
connectionsData: '{}',
268+
localSettings: { WORKFLOWS_SUBSCRIPTION_ID: 'sub-1' },
269+
parametersData: {},
270+
workflowDetails: {},
271+
};
272+
instance.getConnectionPanelMetadata = vi.fn().mockResolvedValue(panelMetadata);
273+
instance.getWebviewContent = vi.fn().mockResolvedValue('<html>configured</html>');
274+
275+
await handleMsg(instance, { command: 'configure-azure-connectors' });
276+
277+
expect(enableAzureConnectors).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ fsPath: '/test/workflow.cs' }));
278+
expect(instance.getWebviewContent).toHaveBeenCalledWith(expect.objectContaining({ azureDetails: panelMetadata.azureDetails }));
279+
expect(instance.panel.webview.html).toBe('<html>configured</html>');
280+
281+
instance.connectionData = { managedApiConnections: {} };
282+
instance.apiHubServiceDetails = { subscriptionId: 'sub-1' };
283+
await handleMsg(instance, { command: 'initialize' });
284+
285+
expect(instance.panel.webview.postMessage).toHaveBeenCalledWith(
286+
expect.objectContaining({
287+
command: 'initialize-frame',
288+
data: expect.objectContaining({
289+
apiHubServiceDetails: { subscriptionId: 'sub-1' },
290+
connectionData: { managedApiConnections: {} },
291+
panelMetadata,
292+
}),
293+
})
294+
);
295+
});
296+
297+
it('keeps the terminal recovery state when Azure connector setup is skipped again', async () => {
298+
const instance = createInstance() as any;
299+
instance.panel.webview.html = '<html>not configured</html>';
300+
instance.getConnectionPanelMetadata = vi.fn().mockResolvedValue({ azureDetails: { enabled: false } });
301+
instance.getWebviewContent = vi.fn();
302+
303+
await handleMsg(instance, { command: 'configure-azure-connectors' });
304+
305+
expect(instance.getWebviewContent).not.toHaveBeenCalled();
306+
expect(instance.panel.webview.html).toBe('<html>not configured</html>');
307+
});
308+
});

‎apps/vs-code-designer/src/app/commands/workflows/connectionView/panels/connectionPanel.ts‎

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import * as vscode from 'vscode';
3030
import type { Connection } from '@microsoft/logic-apps-shared';
3131
import { getBundleVersionNumber } from '../../../../utils/bundleFeed';
3232
import { saveWorkflowParameter } from '../../../../utils/codeless/parameter';
33+
import { enableAzureConnectors } from '../../../azureConnectors/enableAzureConnectors';
3334

3435
export default class ConnectionPanel extends DesignerPanel {
3536
private readonly workflowFilePath: string;
@@ -94,9 +95,12 @@ export default class ConnectionPanel extends DesignerPanel {
9495
this.panel.webview.html = this.getLoadingHtml();
9596

9697
// Start design time API and load metadata in parallel
97-
const startDesignTimePromise = callWithTelemetryAndErrorHandling('ConnectionPanel.create.startDesignTimeApi', async (actionContext: IActionContext) => {
98-
await startDesignTimeApi(actionContext, this.projectPath!);
99-
});
98+
const startDesignTimePromise = callWithTelemetryAndErrorHandling(
99+
'ConnectionPanel.create.startDesignTimeApi',
100+
async (actionContext: IActionContext) => {
101+
await startDesignTimeApi(actionContext, this.projectPath!);
102+
}
103+
);
100104
const [_, panelMetadata] = await Promise.all([startDesignTimePromise, this.getConnectionPanelMetadata()]);
101105

102106
if (!ext.designTimeInstances.has(this.projectPath)) {
@@ -161,11 +165,11 @@ export default class ConnectionPanel extends DesignerPanel {
161165
<html>
162166
<head>
163167
<style>
164-
body {
165-
display: flex;
166-
justify-content: center;
167-
align-items: center;
168-
height: 100vh;
168+
body {
169+
display: flex;
170+
justify-content: center;
171+
align-items: center;
172+
height: 100vh;
169173
margin: 0;
170174
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
171175
color: var(--vscode-foreground);
@@ -228,6 +232,27 @@ export default class ConnectionPanel extends DesignerPanel {
228232
this.panel?.dispose();
229233
break;
230234
}
235+
case ExtensionCommand.configureAzureConnectors: {
236+
await callWithTelemetryAndErrorHandling('ConnectionPanel.configureAzureConnectors', async (actionContext: IActionContext) => {
237+
await enableAzureConnectors(actionContext, Uri.file(this.workflowFilePath));
238+
239+
const panelMetadata = await this.getConnectionPanelMetadata();
240+
if (!panelMetadata.azureDetails.enabled || !this.panel) {
241+
return;
242+
}
243+
244+
this.panelMetadata = panelMetadata;
245+
this.panel.webview.html = await this.getWebviewContent({
246+
connectionsData: panelMetadata.connectionsData,
247+
parametersData: panelMetadata.parametersData || {},
248+
localSettings: panelMetadata.localSettings,
249+
artifacts: panelMetadata.artifacts,
250+
azureDetails: panelMetadata.azureDetails,
251+
workflowDetails: panelMetadata.workflowDetails,
252+
});
253+
});
254+
break;
255+
}
231256
case ExtensionCommand.insert_connection: {
232257
await callWithTelemetryAndErrorHandling('InsertConnectionView', async (activateContext: IActionContext) => {
233258
const { connection, connectionReferences, connectionAndSetting } = message;

‎apps/vs-code-react/src/app/languageServer/__test__/connectionView.test.tsx‎

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
88
import { designerSlice, updateFileSystemConnection } from '../../../state/DesignerSlice';
99
import { LanguageServerConnectionView } from '../connectionView';
1010
import { initializeLanguageServer, languageServerSlice } from '../../../state/LanguageServerSlice';
11+
import { IntlProvider } from 'react-intl';
1112

1213
const mocks = vi.hoisted(() => ({
1314
getDesignerServices: vi.fn(),
@@ -87,7 +88,7 @@ vi.mock('@microsoft/logic-apps-designer', () => ({
8788
useThemeObserver: vi.fn(),
8889
}));
8990

90-
function createStore() {
91+
function createStore({ connectorType = 'serviceProvider', azureConnectorsEnabled = true } = {}) {
9192
const store = configureStore({
9293
reducer: {
9394
designer: designerSlice.reducer,
@@ -104,11 +105,12 @@ function createStore() {
104105
connector: {
105106
currentConnectionId: 'current',
106107
name: 'filesystem',
107-
type: 'serviceProvider',
108+
type: connectorType,
108109
},
109110
hostVersion: '4.0',
110111
oauthRedirectUrl: 'https://redirect',
111112
panelMetadata: {
113+
azureDetails: { enabled: azureConnectorsEnabled },
112114
localSettings: {},
113115
parametersData: {},
114116
},
@@ -119,14 +121,16 @@ function createStore() {
119121
return store;
120122
}
121123

122-
function renderConnectionView() {
124+
function renderConnectionView(options = {}) {
123125
const postMessage = vi.fn();
124-
const store = createStore();
126+
const store = createStore(options);
125127

126128
render(
127129
<VSCodeContext.Provider value={{ postMessage }}>
128130
<Provider store={store}>
129-
<LanguageServerConnectionView />
131+
<IntlProvider locale="en">
132+
<LanguageServerConnectionView />
133+
</IntlProvider>
130134
</Provider>
131135
</VSCodeContext.Provider>
132136
);
@@ -193,4 +197,32 @@ describe('LanguageServerConnectionView', () => {
193197

194198
await expect(pendingConnection).resolves.toEqual({ id: 'share' });
195199
});
200+
201+
it.each([
202+
'ApiConnection',
203+
'ApiConnectionNotification',
204+
'ApiConnectionWebhook',
205+
'OpenApiConnection',
206+
'OpenApiConnectionNotification',
207+
'OpenApiConnectionWebhook',
208+
])('shows a terminal setup state for %s when Azure connector setup was skipped', (connectorType) => {
209+
const { postMessage } = renderConnectionView({ connectorType, azureConnectorsEnabled: false });
210+
211+
expect(screen.getByText('Azure connector setup has not been completed')).toBeInTheDocument();
212+
expect(screen.getByText('Set up Azure connectors to manage this connection.')).toBeInTheDocument();
213+
expect(mocks.getDesignerServices).not.toHaveBeenCalled();
214+
215+
fireEvent.click(screen.getByRole('button', { name: 'Set up now' }));
216+
expect(postMessage).toHaveBeenCalledWith({ command: ExtensionCommand.configureAzureConnectors });
217+
218+
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
219+
expect(postMessage).toHaveBeenCalledWith({ command: ExtensionCommand.close_panel });
220+
});
221+
222+
it('continues to load local connectors when Azure connector setup was skipped', () => {
223+
renderConnectionView({ connectorType: 'serviceProvider', azureConnectorsEnabled: false });
224+
225+
expect(screen.getByTestId('designer-provider')).toBeInTheDocument();
226+
expect(mocks.getDesignerServices).toHaveBeenCalledTimes(1);
227+
});
196228
});

0 commit comments

Comments
 (0)