Skip to content

Commit a77329a

Browse files
aasimkhan30Aasim Khan
andauthored
Fixing schema designer and table designer profiles before init (#20753)
* Adding telemetry and prepare conn profile to table designer and schema designer. * remove dup confirmTokenValidity call. * Fixing ordering * Fixing tests * Add error handling and user feedback for Schema Designer initialization and connection preparation - Introduced error messages for loading Schema Designer and retry options. - Enhanced error handling in SchemaDesignerStateProvider and TableDesignerWebviewController. - Updated locConstants for new error messages. * Fixed * Fixing rpc registration. * Fixing lint issues. * fixed name * Adding more error handling and fixng message. * Added loc * Fixing error telemetry * Made dialogs conditional. --------- Co-authored-by: Aasim Khan <[email protected]>
1 parent 20d9776 commit a77329a

File tree

15 files changed

+266
-71
lines changed

15 files changed

+266
-71
lines changed

extensions/mssql/l10n/bundle.l10n.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@
159159
"Generate Script": "Generate Script",
160160
"Publish": "Publish",
161161
"Preview Database Updates": "Preview Database Updates",
162-
"Error loading designer": "Error loading designer",
162+
"Error loading Table Designer": "Error loading Table Designer",
163163
"Severity": "Severity",
164164
"Script as Create": "Script as Create",
165165
"I have read the summary and understand the potential risks.": "I have read the summary and understand the potential risks.",
@@ -612,6 +612,7 @@
612612
"comment": ["{0} is the max length"]
613613
},
614614
"Loading Schema Designer": "Loading Schema Designer",
615+
"Error loading Schema Designer": "Error loading Schema Designer",
615616
"Generating report, this might take a while...": "Generating report, this might take a while...",
616617
"{0} warnings/{0} is the number of warnings": {
617618
"message": "{0} warnings",
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import {
7+
Button,
8+
Dialog,
9+
DialogActions,
10+
DialogBody,
11+
DialogContent,
12+
DialogSurface,
13+
DialogTitle,
14+
makeStyles,
15+
shorthands,
16+
} from "@fluentui/react-components";
17+
import { ErrorCircleRegular } from "@fluentui/react-icons";
18+
import React from "react";
19+
20+
const useStyles = makeStyles({
21+
dialogSurface: {
22+
minWidth: "320px",
23+
maxWidth: "420px",
24+
},
25+
title: {
26+
display: "flex",
27+
alignItems: "center",
28+
columnGap: "8px",
29+
},
30+
icon: {
31+
fontSize: "32px",
32+
},
33+
content: {
34+
...shorthands.marginBlock("16px", "0"),
35+
},
36+
});
37+
38+
export interface ErrorDialogProps {
39+
open: boolean;
40+
title: string;
41+
message: string;
42+
retryLabel: string;
43+
onRetry: () => void;
44+
}
45+
46+
export const ErrorDialog: React.FC<ErrorDialogProps> = ({
47+
open,
48+
title,
49+
message,
50+
retryLabel,
51+
onRetry,
52+
}) => {
53+
const classes = useStyles();
54+
return (
55+
<Dialog open={open} modalType="modal" inertTrapFocus>
56+
<DialogSurface className={classes.dialogSurface}>
57+
<DialogBody>
58+
<DialogTitle className={classes.title}>
59+
<ErrorCircleRegular className={classes.icon} />
60+
{title}
61+
</DialogTitle>
62+
<DialogContent className={classes.content}>{message}</DialogContent>
63+
<DialogActions>
64+
<Button appearance="primary" onClick={onRetry}>
65+
{retryLabel}
66+
</Button>
67+
</DialogActions>
68+
</DialogBody>
69+
</DialogSurface>
70+
</Dialog>
71+
);
72+
};

extensions/mssql/src/reactviews/common/locConstants.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export class LocConstants {
139139
generateScript: l10n.t("Generate Script"),
140140
publish: l10n.t("Publish"),
141141
previewDatabaseUpdates: l10n.t("Preview Database Updates"),
142-
errorLoadingDesigner: l10n.t("Error loading designer"),
142+
errorLoadingDesigner: l10n.t("Error loading Table Designer"),
143143
severity: l10n.t("Severity"),
144144
description: l10n.t("Description"),
145145
scriptAsCreate: l10n.t("Script as Create"),
@@ -876,6 +876,8 @@ export class LocConstants {
876876
comment: ["{0} is the max length"],
877877
}),
878878
loadingSchemaDesigner: l10n.t("Loading Schema Designer"),
879+
errorLoadingSchemaDesigner: l10n.t("Error loading Schema Designer"),
880+
retry: l10n.t("Retry"),
879881
generatingReport: l10n.t("Generating report, this might take a while..."),
880882
nWarnings: (warningCount: number) =>
881883
l10n.t({

extensions/mssql/src/reactviews/common/utils.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,25 @@ export function formatString(str: string, ...args: any[]): string {
3232
return result;
3333
}
3434

35+
/**
36+
* Get the error message from an unknown error object
37+
* @param error The error object
38+
* @returns The error message
39+
*/
40+
export function getErrorMessage(error: unknown): string {
41+
if (error instanceof Error) {
42+
return error.message;
43+
}
44+
if (typeof error === "string") {
45+
return error;
46+
}
47+
try {
48+
return JSON.stringify(error);
49+
} catch {
50+
return "Unknown error";
51+
}
52+
}
53+
3554
/**
3655
* Get the css string representation of a ColorThemeKind
3756
* @param themeKind The ColorThemeKind to convert

extensions/mssql/src/reactviews/pages/SchemaDesigner/graph/SchemaDiagramFlow.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,18 @@ export const SchemaDesignerFlow = () => {
8282

8383
useEffect(() => {
8484
const intialize = async () => {
85-
const { nodes, edges } = await context.initializeSchemaDesigner();
86-
setSchemaNodes(nodes);
87-
setRelationshipEdges(edges);
85+
try {
86+
const { nodes, edges } = await context.initializeSchemaDesigner();
87+
setSchemaNodes(nodes);
88+
setRelationshipEdges(edges);
89+
} catch (error) {
90+
context.log?.(`Failed to initialize schema designer: ${String(error)}`);
91+
setSchemaNodes([]);
92+
setRelationshipEdges([]);
93+
}
8894
};
8995
void intialize();
90-
}, []);
96+
}, [context.initializationRequestId]);
9197

9298
/**
9399
* Displays an error toast notification

extensions/mssql/src/reactviews/pages/SchemaDesigner/schemaDesignerPage.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { SchemaDesignerFindTableWidget } from "./schemaDesignerFindTables";
1414
import { makeStyles, Spinner } from "@fluentui/react-components";
1515
import { locConstants } from "../../common/locConstants";
1616
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
17+
import { ErrorDialog } from "../../common/errorDialog";
1718

1819
const useStyles = makeStyles({
1920
resizeHandle: {
@@ -43,7 +44,16 @@ export const SchemaDesignerPage = () => {
4344
<PanelResizeHandle className={classes.resizeHandle} />
4445
<SchemaDesignerDefinitionsPanel />
4546
</PanelGroup>
46-
{!context.isInitialized && <LoadingOverlay />}
47+
{!context.isInitialized && !context.initializationError && <LoadingOverlay />}
48+
{context?.initializationError && (
49+
<ErrorDialog
50+
open={!!context?.initializationError}
51+
title={locConstants.schemaDesigner.errorLoadingSchemaDesigner}
52+
message={context?.initializationError ?? ""}
53+
retryLabel={locConstants.schemaDesigner.retry}
54+
onRetry={context?.triggerInitialization}
55+
/>
56+
)}
4757
</MainLayout>
4858
</>
4959
);

extensions/mssql/src/reactviews/pages/SchemaDesigner/schemaDesignerStateProvider.tsx

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { createContext, useEffect, useState } from "react";
77
import { SchemaDesigner } from "../../../sharedInterfaces/schemaDesigner";
88
import { useVscodeWebview } from "../../common/vscodeWebviewProvider";
9-
import { getCoreRPCs } from "../../common/utils";
9+
import { getCoreRPCs, getErrorMessage } from "../../common/utils";
1010
import { WebviewRpc } from "../../common/rpc";
1111

1212
import { Edge, MarkerType, Node, ReactFlowJsonObject, useReactFlow } from "@xyflow/react";
@@ -48,6 +48,9 @@ export interface SchemaDesignerContextProps
4848
resetUndoRedoState: () => void;
4949
resetView: () => void;
5050
isInitialized: boolean;
51+
initializationError?: string;
52+
initializationRequestId: number;
53+
triggerInitialization: () => void;
5154
renderOnlyVisibleTables: boolean;
5255
setRenderOnlyVisibleTables: (value: boolean) => void;
5356
isExporting: boolean;
@@ -79,6 +82,8 @@ const SchemaDesignerStateProvider: React.FC<SchemaDesignerProviderProps> = ({ ch
7982
const [schemaNames, setSchemaNames] = useState<string[]>([]);
8083
const reactFlow = useReactFlow();
8184
const [isInitialized, setIsInitialized] = useState(false);
85+
const [initializationError, setInitializationError] = useState<string | undefined>(undefined);
86+
const [initializationRequestId, setInitializationRequestId] = useState(0);
8287
const [findTableText, setFindTableText] = useState<string>("");
8388
const [renderOnlyVisibleTables, setRenderOnlyVisibleTables] = useState<boolean>(true);
8489
const [isExporting, setIsExporting] = useState<boolean>(false);
@@ -132,29 +137,44 @@ const SchemaDesignerStateProvider: React.FC<SchemaDesignerProviderProps> = ({ ch
132137
}, []);
133138

134139
const initializeSchemaDesigner = async () => {
135-
const model = await extensionRpc.sendRequest(
136-
SchemaDesigner.InitializeSchemaDesignerRequest.type,
137-
);
140+
try {
141+
setIsInitialized(false);
142+
setInitializationError(undefined);
143+
const model = await extensionRpc.sendRequest(
144+
SchemaDesigner.InitializeSchemaDesignerRequest.type,
145+
);
138146

139-
const { nodes, edges } = flowUtils.generateSchemaDesignerFlowComponents(model.schema);
147+
const { nodes, edges } = flowUtils.generateSchemaDesignerFlowComponents(model.schema);
140148

141-
setDatatypes(model.dataTypes);
142-
setSchemaNames(model.schemaNames);
143-
setIsInitialized(true);
149+
setDatatypes(model.dataTypes);
150+
setSchemaNames(model.schemaNames);
151+
setIsInitialized(true);
144152

145-
setTimeout(() => {
146-
stateStack.setInitialState(
147-
reactFlow.toObject() as ReactFlowJsonObject<
148-
Node<SchemaDesigner.Table>,
149-
Edge<SchemaDesigner.ForeignKey>
150-
>,
151-
);
152-
});
153+
setTimeout(() => {
154+
stateStack.setInitialState(
155+
reactFlow.toObject() as ReactFlowJsonObject<
156+
Node<SchemaDesigner.Table>,
157+
Edge<SchemaDesigner.ForeignKey>
158+
>,
159+
);
160+
});
153161

154-
return {
155-
nodes,
156-
edges,
157-
};
162+
return {
163+
nodes,
164+
edges,
165+
};
166+
} catch (error) {
167+
const errorMessage = getErrorMessage(error);
168+
setInitializationError(errorMessage);
169+
setIsInitialized(false);
170+
throw error;
171+
}
172+
};
173+
174+
const triggerInitialization = () => {
175+
setInitializationError(undefined);
176+
setIsInitialized(false);
177+
setInitializationRequestId((id) => id + 1);
158178
};
159179

160180
// Get the script from the server
@@ -460,6 +480,9 @@ const SchemaDesignerStateProvider: React.FC<SchemaDesignerProviderProps> = ({ ch
460480
setFindTableText,
461481
getDefinition,
462482
initializeSchemaDesigner,
483+
initializationError,
484+
initializationRequestId,
485+
triggerInitialization,
463486
saveAsFile,
464487
getReport,
465488
openInEditor,

extensions/mssql/src/reactviews/pages/TableDesigner/tableDesignerPage.tsx

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { Button, Spinner, makeStyles, shorthands } from "@fluentui/react-components";
6+
import { Spinner, makeStyles, shorthands } from "@fluentui/react-components";
77
import { useContext, useEffect, useRef } from "react";
88
import * as designer from "../../../sharedInterfaces/tableDesigner";
99
import { TableDesignerContext } from "./tableDesignerStateProvider";
10-
import { ErrorCircleRegular } from "@fluentui/react-icons";
1110
import { DesignerPageRibbon } from "./designerPageRibbon";
1211
import { DesignerMainPane } from "./designerMainPane";
1312
import { DesignerPropertiesPane } from "./designerPropertiesPane";
1413
import { DesignerResultPane } from "./designerResultPane";
1514
import { locConstants } from "../../common/locConstants";
15+
import { ErrorDialog } from "../../common/errorDialog";
1616
import {
1717
ImperativePanelHandle,
1818
Panel,
@@ -37,13 +37,6 @@ const useStyles = makeStyles({
3737
width: "100%",
3838
flexDirection: "column",
3939
},
40-
errorIcon: {
41-
fontSize: "100px",
42-
opacity: 0.5,
43-
},
44-
retryButton: {
45-
marginTop: "10px",
46-
},
4740
resultPaneHandle: {
4841
position: "absolute",
4942
top: "0",
@@ -140,6 +133,8 @@ export const TableDesigner = () => {
140133
}
141134
}, [context?.state.propertiesPaneData, context?.propertiesPaneResizeInfo.isMaximized]);
142135

136+
const isErrorState = tableDesignerState.apiState?.initializeState === designer.LoadState.Error;
137+
143138
return (
144139
<div className={classes.root}>
145140
{tableDesignerState.apiState?.initializeState === designer.LoadState.Loading && (
@@ -150,14 +145,14 @@ export const TableDesigner = () => {
150145
/>
151146
</div>
152147
)}
153-
{tableDesignerState.apiState?.initializeState === designer.LoadState.Error && (
154-
<div className={classes.pageContext}>
155-
<ErrorCircleRegular className={classes.errorIcon} />
156-
<div>{locConstants.tableDesigner.errorLoadingDesigner}</div>
157-
<Button className={classes.retryButton}>
158-
{locConstants.tableDesigner.retry}
159-
</Button>
160-
</div>
148+
{isErrorState && (
149+
<ErrorDialog
150+
open={isErrorState}
151+
title={locConstants.tableDesigner.errorLoadingDesigner}
152+
message={tableDesignerState?.initializationError ?? ""}
153+
retryLabel={locConstants.tableDesigner.retry}
154+
onRetry={() => context.initializeTableDesigner()}
155+
/>
161156
)}
162157
{tableDesignerState.apiState?.initializeState === designer.LoadState.Loaded && (
163158
<div className={classes.mainContent}>

extensions/mssql/src/schemaDesigner/schemaDesignerWebviewController.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ export class SchemaDesignerWebviewController extends ReactWebviewPanelController
8787
TelemetryActions.Initialize,
8888
undefined,
8989
undefined,
90+
undefined,
91+
true, // include callstack in telemetry
9092
);
9193
try {
9294
let sessionResponse: SchemaDesigner.CreateSessionResponse;

extensions/mssql/src/schemaDesigner/schemaDesignerWebviewManager.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import MainController from "../controllers/mainController";
1212
import * as LocConstants from "../constants/locConstants";
1313
import { TelemetryViews, TelemetryActions } from "../sharedInterfaces/telemetry";
1414
import { sendActionEvent } from "../telemetry/telemetry";
15+
import { IConnectionProfile } from "../models/interfaces";
1516

1617
export class SchemaDesignerWebviewManager {
1718
private static instance: SchemaDesignerWebviewManager;
@@ -57,13 +58,15 @@ export class SchemaDesignerWebviewManager {
5758
let connectionString: string | undefined;
5859
let azureAccountToken: string | undefined;
5960
if (treeNode) {
60-
const connectionInfo = treeNode.connectionProfile;
61+
let connectionInfo = treeNode.connectionProfile;
62+
connectionInfo = (await mainController.connectionManager.prepareConnectionInfo(
63+
connectionInfo,
64+
)) as IConnectionProfile;
6165
connectionInfo.database = databaseName;
6266

6367
const connectionDetails =
6468
await mainController.connectionManager.createConnectionDetails(connectionInfo);
6569

66-
await mainController.connectionManager.confirmEntraTokenValidity(connectionInfo);
6770
treeNode.updateConnectionProfile(connectionInfo);
6871

6972
connectionString = await mainController.connectionManager.getConnectionString(

0 commit comments

Comments
 (0)