Skip to content

Commit 8c93b31

Browse files
authored
Refactoring form controller logic to base class for reuse (#18842)
* checkpoint * checkpoint * compiling * Additional cleanup
1 parent 9559efd commit 8c93b31

File tree

11 files changed

+334
-206
lines changed

11 files changed

+334
-206
lines changed

src/connectionconfig/connectionDialogWebviewController.ts

Lines changed: 45 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
AddFirewallRuleDialogProps,
2121
IConnectionDialogProfile,
2222
TrustServerCertDialogProps,
23+
ConnectionDialogFormItemSpec,
2324
} from "../sharedInterfaces/connectionDialog";
2425
import { ConnectionCompleteParams } from "../models/contracts/connection";
2526
import {
@@ -51,7 +52,6 @@ import { AzureSubscription } from "@microsoft/vscode-azext-azureauth";
5152
import { IConnectionInfo } from "vscode-mssql";
5253
import MainController from "../controllers/mainController";
5354
import { ObjectExplorerProvider } from "../objectExplorer/objectExplorerProvider";
54-
import { ReactWebviewPanelController } from "../controllers/reactWebviewPanelController";
5555
import { UserSurvey } from "../nps/userSurvey";
5656
import VscodeWrapper from "../controllers/vscodeWrapper";
5757
import {
@@ -69,13 +69,14 @@ import {
6969
import { IAccount } from "../models/contracts/azure";
7070
import {
7171
generateConnectionComponents,
72-
getActiveFormComponents,
73-
getFormComponent,
7472
groupAdvancedOptions,
7573
} from "./formComponentHelpers";
74+
import { FormWebviewController } from "../forms/formWebviewController";
7675

77-
export class ConnectionDialogWebviewController extends ReactWebviewPanelController<
76+
export class ConnectionDialogWebviewController extends FormWebviewController<
77+
IConnectionDialogProfile,
7878
ConnectionDialogWebviewState,
79+
ConnectionDialogFormItemSpec,
7980
ConnectionDialogReducers
8081
> {
8182
//#region Properties
@@ -176,12 +177,13 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
176177
);
177178
}
178179

180+
this.state.formComponents = await generateConnectionComponents(
181+
this._mainController.connectionManager,
182+
getAccounts(this._mainController.azureAccountService),
183+
this.getAzureActionButtons(),
184+
);
185+
179186
this.state.connectionComponents = {
180-
components: await generateConnectionComponents(
181-
this._mainController.connectionManager,
182-
getAccounts(this._mainController.azureAccountService),
183-
this.getAzureActionButtons(),
184-
),
185187
mainOptions: [...ConnectionDialogWebviewController.mainOptions],
186188
topAdvancedOptions: [
187189
"port",
@@ -195,7 +197,10 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
195197
};
196198

197199
this.state.connectionComponents.groupedAdvancedOptions =
198-
groupAdvancedOptions(this.state.connectionComponents);
200+
groupAdvancedOptions(
201+
this.state.formComponents as any,
202+
this.state.connectionComponents,
203+
);
199204

200205
await this.updateItemVisibility();
201206
this.updateState();
@@ -220,36 +225,6 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
220225
},
221226
);
222227

223-
this.registerReducer("formAction", async (state, payload) => {
224-
if (payload.event.isAction) {
225-
const component = getFormComponent(
226-
this.state,
227-
payload.event.propertyName,
228-
);
229-
if (component && component.actionButtons) {
230-
const actionButton = component.actionButtons.find(
231-
(b) => b.id === payload.event.value,
232-
);
233-
if (actionButton?.callback) {
234-
await actionButton.callback();
235-
}
236-
}
237-
} else {
238-
(this.state.connectionProfile[
239-
payload.event.propertyName
240-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
241-
] as any) = payload.event.value;
242-
await this.validateConnectionProfile(
243-
this.state.connectionProfile,
244-
payload.event.propertyName,
245-
);
246-
await this.handleAzureMFAEdits(payload.event.propertyName);
247-
}
248-
await this.updateItemVisibility();
249-
250-
return state;
251-
});
252-
253228
this.registerReducer("loadConnection", async (state, payload) => {
254229
sendActionEvent(
255230
TelemetryViews.ConnectionDialog,
@@ -432,7 +407,13 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
432407

433408
//#region Connection helpers
434409

435-
private async updateItemVisibility() {
410+
override async afterSetFormProperty(
411+
propertyName: keyof IConnectionDialogProfile,
412+
): Promise<void> {
413+
return await this.handleAzureMFAEdits(propertyName);
414+
}
415+
416+
async updateItemVisibility() {
436417
let hiddenProperties: (keyof IConnectionDialogProfile)[] = [];
437418

438419
if (
@@ -466,56 +447,23 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
466447
}
467448
}
468449

469-
for (const component of Object.values(
470-
this.state.connectionComponents.components,
471-
)) {
450+
for (const component of Object.values(this.state.formComponents)) {
472451
component.hidden = hiddenProperties.includes(
473452
component.propertyName,
474453
);
475454
}
476455
}
477456

478-
private async validateConnectionProfile(
479-
connectionProfile: IConnectionDialogProfile,
480-
propertyName?: keyof IConnectionDialogProfile,
481-
): Promise<string[]> {
482-
const erroredInputs = [];
483-
if (propertyName) {
484-
const component = getFormComponent(this.state, propertyName);
485-
if (component && component.validate) {
486-
component.validation = component.validate(
487-
this.state,
488-
connectionProfile[propertyName],
489-
);
490-
if (!component.validation.isValid) {
491-
erroredInputs.push(component.propertyName);
492-
}
493-
}
494-
} else {
495-
getActiveFormComponents(this.state)
496-
.map((x) => this.state.connectionComponents.components[x])
497-
.forEach((c) => {
498-
if (c.hidden) {
499-
c.validation = {
500-
isValid: true,
501-
validationMessage: "",
502-
};
503-
return;
504-
} else {
505-
if (c.validate) {
506-
c.validation = c.validate(
507-
this.state,
508-
connectionProfile[c.propertyName],
509-
);
510-
if (!c.validation.isValid) {
511-
erroredInputs.push(c.propertyName);
512-
}
513-
}
514-
}
515-
});
457+
protected getActiveFormComponents(
458+
state: ConnectionDialogWebviewState,
459+
): (keyof IConnectionDialogProfile)[] {
460+
if (
461+
state.selectedInputMode === ConnectionInputMode.Parameters ||
462+
state.selectedInputMode === ConnectionInputMode.AzureBrowse
463+
) {
464+
return state.connectionComponents.mainOptions;
516465
}
517-
518-
return erroredInputs;
466+
return ["connectionString", "profileName"];
519467
}
520468

521469
/** Returns a copy of `connection` that's been cleaned up by clearing the properties that aren't being used
@@ -526,9 +474,7 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
526474
const cleanedConnection = structuredClone(connection);
527475

528476
// Clear values for inputs that are hidden due to form selections
529-
for (const option of Object.values(
530-
this.state.connectionComponents.components,
531-
)) {
477+
for (const option of Object.values(this.state.formComponents)) {
532478
if (option.hidden) {
533479
(cleanedConnection[
534480
option.propertyName as keyof IConnectionDialogProfile
@@ -663,7 +609,7 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
663609
// clean the connection by clearing the options that aren't being used
664610
const cleanedConnection = this.cleanConnection(connectionProfile);
665611

666-
return await this.validateConnectionProfile(cleanedConnection);
612+
return await this.validateForm(cleanedConnection);
667613
}
668614

669615
private async connectHelper(
@@ -963,7 +909,7 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
963909
callback: async () => {
964910
const account =
965911
await this._mainController.azureAccountService.addAccount();
966-
const accountsComponent = getFormComponent(
912+
const accountsComponent = this.getFormComponent(
967913
this.state,
968914
"accountId",
969915
);
@@ -1047,8 +993,14 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
1047993
) {
1048994
return;
1049995
}
1050-
const accountComponent = getFormComponent(this.state, "accountId");
1051-
const tenantComponent = getFormComponent(this.state, "tenantId");
996+
const accountComponent = this.getFormComponent(
997+
this.state,
998+
"accountId",
999+
);
1000+
const tenantComponent = this.getFormComponent(
1001+
this.state,
1002+
"tenantId",
1003+
);
10521004
let tenants: FormItemOptions[] = [];
10531005
switch (propertyName) {
10541006
case "accountId":
@@ -1321,8 +1273,8 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
13211273

13221274
private clearFormError() {
13231275
this.state.formError = "";
1324-
for (const component of getActiveFormComponents(this.state).map(
1325-
(x) => this.state.connectionComponents.components[x],
1276+
for (const component of this.getActiveFormComponents(this.state).map(
1277+
(x) => this.state.formComponents[x],
13261278
)) {
13271279
component.validation = undefined;
13281280
}

src/connectionconfig/formComponentHelpers.ts

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ export async function generateConnectionComponents(
100100
}
101101

102102
export function groupAdvancedOptions(
103+
components: Record<
104+
keyof IConnectionDialogProfile,
105+
ConnectionDialogFormItemSpec
106+
>,
103107
componentsInfo: ConnectionComponentsInfo,
104108
): ConnectionComponentGroup[] {
105109
const groupMap: Map<string, ConnectionComponentGroup> = new Map([
@@ -112,7 +116,7 @@ export function groupAdvancedOptions(
112116
["context", undefined],
113117
]);
114118

115-
const optionsToGroup = Object.values(componentsInfo.components).filter(
119+
const optionsToGroup = Object.values(components).filter(
116120
(c) =>
117121
c.isAdvancedOption &&
118122
!componentsInfo.mainOptions.includes(c.propertyName) &&
@@ -141,7 +145,11 @@ export function groupAdvancedOptions(
141145

142146
export function convertToFormComponent(
143147
connOption: ConnectionOption,
144-
): FormItemSpec<ConnectionDialogWebviewState, IConnectionDialogProfile> {
148+
): FormItemSpec<
149+
IConnectionDialogProfile,
150+
ConnectionDialogWebviewState,
151+
ConnectionDialogFormItemSpec
152+
> {
145153
switch (connOption.valueType) {
146154
case "boolean":
147155
return {
@@ -344,26 +352,3 @@ export async function completeFormComponents(
344352
};
345353
};
346354
}
347-
348-
export function getActiveFormComponents(
349-
state: ConnectionDialogWebviewState,
350-
): (keyof IConnectionDialogProfile)[] {
351-
if (
352-
state.selectedInputMode === ConnectionInputMode.Parameters ||
353-
state.selectedInputMode === ConnectionInputMode.AzureBrowse
354-
) {
355-
return state.connectionComponents.mainOptions;
356-
}
357-
return ["connectionString", "profileName"];
358-
}
359-
360-
export function getFormComponent(
361-
state: ConnectionDialogWebviewState,
362-
propertyName: keyof IConnectionDialogProfile,
363-
):
364-
| FormItemSpec<ConnectionDialogWebviewState, IConnectionDialogProfile>
365-
| undefined {
366-
return getActiveFormComponents(state).includes(propertyName)
367-
? state.connectionComponents.components[propertyName]
368-
: undefined;
369-
}

0 commit comments

Comments
 (0)