Skip to content

Commit be77ce5

Browse files
committed
Stabilize DAB tool open and init behavior
1 parent d216faf commit be77ce5

5 files changed

Lines changed: 45 additions & 35 deletions

File tree

extensions/mssql/src/copilot/tools/dabTool.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,22 +208,16 @@ export class DabTool extends ToolBase<DabToolParams> {
208208
}
209209

210210
const designer = await this._showDab(connectionId, connCreds.database);
211-
const state = await designer.getDabToolState();
212211
sendToolTelemetry({
213212
operation,
214213
success: true,
215-
measurements: {
216-
stateOmitted: 1,
217-
},
218214
});
219215

220216
return json(
221217
withTarget(
222218
{
223219
success: true,
224220
message: loc.dabToolShowSuccessMessage,
225-
version: state.version,
226-
summary: state.summary,
227221
recommendedTool: this.toolName,
228222
recommendedNextCall: {
229223
operation: "get_state",

extensions/mssql/src/reactviews/pages/SchemaDesigner/dab/dabContext.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ interface DabProviderProps {
4646

4747
export const DabProvider: React.FC<DabProviderProps> = ({ children }) => {
4848
const schemaDesignerContext = useContext(SchemaDesignerContext);
49-
const { extensionRpc, extractSchema, isInitialized } = schemaDesignerContext;
49+
const { extensionRpc, extractSchema, isInitialized, isInitializedRef, waitForInitialization } =
50+
schemaDesignerContext;
5051

5152
const [dabConfig, setDabConfig] = useState<Dab.DabConfig | null>(null);
5253
const [dabTextFilter, setDabTextFilter] = useState<string>("");
@@ -56,17 +57,12 @@ export const DabProvider: React.FC<DabProviderProps> = ({ children }) => {
5657
);
5758

5859
const dabConfigRef = useRef<Dab.DabConfig | null>(dabConfig);
59-
const isInitializedRef = useRef<boolean>(isInitialized);
6060
const extractSchemaRef = useRef<() => ReturnType<typeof extractSchema>>(extractSchema);
6161

6262
useEffect(() => {
6363
dabConfigRef.current = dabConfig;
6464
}, [dabConfig]);
6565

66-
useEffect(() => {
67-
isInitializedRef.current = isInitialized;
68-
}, [isInitialized]);
69-
7066
useEffect(() => {
7167
extractSchemaRef.current = extractSchema;
7268
}, [extractSchema]);
@@ -75,13 +71,14 @@ export const DabProvider: React.FC<DabProviderProps> = ({ children }) => {
7571
registerSchemaDesignerDabToolHandlers({
7672
extensionRpc,
7773
isInitializedRef,
74+
waitForInitialization,
7875
getCurrentDabConfig: () => dabConfigRef.current,
7976
getCurrentSchemaTables: () => extractSchemaRef.current().tables,
8077
commitDabConfig: (config) => {
8178
setDabConfig(config);
8279
},
8380
});
84-
}, [extensionRpc]);
81+
}, [extensionRpc, waitForInitialization]);
8582

8683
const initializeDabConfig = useCallback(() => {
8784
const schema = extractSchema();

extensions/mssql/src/reactviews/pages/SchemaDesigner/schemaDesignerRpcHandlers.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1509,21 +1509,26 @@ function applyDabToolChange(
15091509
export function registerSchemaDesignerDabToolHandlers(params: {
15101510
extensionRpc: WebviewRpc<SchemaDesigner.SchemaDesignerReducers>;
15111511
isInitializedRef: { current: boolean };
1512+
waitForInitialization: () => Promise<boolean>;
15121513
getCurrentDabConfig: () => Dab.DabConfig | null;
15131514
getCurrentSchemaTables: () => SchemaDesigner.Table[];
15141515
commitDabConfig: (config: Dab.DabConfig) => void;
15151516
}) {
15161517
const {
15171518
extensionRpc,
15181519
isInitializedRef,
1520+
waitForInitialization,
15191521
getCurrentDabConfig,
15201522
getCurrentSchemaTables,
15211523
commitDabConfig,
15221524
} = params;
15231525

15241526
const handleGetState = async (): Promise<Dab.GetDabToolStateResponse> => {
15251527
if (!isInitializedRef.current) {
1526-
throw new Error(locConstants.schemaDesigner.schemaDesignerNotInitialized);
1528+
const initialized = await waitForInitialization();
1529+
if (!initialized || !isInitializedRef.current) {
1530+
throw new Error(locConstants.schemaDesigner.schemaDesignerNotInitialized);
1531+
}
15271532
}
15281533

15291534
const baseSnapshot = getCurrentDabConfig();

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import { stateStack } from "./schemaDesignerUndoState";
3030

3131
export interface SchemaDesignerContextProps extends CoreRPCs {
3232
extensionRpc: WebviewRpc<SchemaDesigner.SchemaDesignerReducers>;
33+
isInitializedRef: { current: boolean };
34+
waitForInitialization: () => Promise<boolean>;
3335
schemaNames: string[];
3436
datatypes: string[];
3537
findTableText: string;
@@ -535,6 +537,8 @@ const SchemaDesignerStateProvider: React.FC<SchemaDesignerProviderProps> = ({ ch
535537
value={{
536538
...getCoreRPCs(extensionRpc),
537539
extensionRpc,
540+
isInitializedRef,
541+
waitForInitialization,
538542
schemaNames,
539543
datatypes,
540544
findTableText,

extensions/mssql/test/unit/dabTool.test.ts

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ suite("DabTool Tests", () => {
5959
tables: SchemaDesigner.Table[];
6060
dabConfig?: Dab.DabConfig | null;
6161
initialized?: boolean;
62+
waitForInitialization?: sinon.SinonStub;
6263
}) => {
6364
let currentTables = params.tables;
6465
let currentDabConfig = params.dabConfig ?? null;
@@ -67,6 +68,8 @@ suite("DabTool Tests", () => {
6768
const commitSpy = sandbox.spy((config: Dab.DabConfig) => {
6869
currentDabConfig = config;
6970
});
71+
const waitForInitialization =
72+
params.waitForInitialization ?? sandbox.stub().resolves(params.initialized ?? true);
7073

7174
const extensionRpc = {
7275
onRequest: sandbox.stub().callsFake((type: any, handler: any) => {
@@ -77,6 +80,7 @@ suite("DabTool Tests", () => {
7780
registerSchemaDesignerDabToolHandlers({
7881
extensionRpc: extensionRpc as any,
7982
isInitializedRef,
83+
waitForInitialization,
8084
getCurrentDabConfig: () => currentDabConfig,
8185
getCurrentSchemaTables: () => currentTables,
8286
commitDabConfig: commitSpy,
@@ -92,6 +96,8 @@ suite("DabTool Tests", () => {
9296
},
9397
getConfig: () => currentDabConfig,
9498
commitSpy,
99+
waitForInitialization,
100+
isInitializedRef,
95101
};
96102
};
97103

@@ -155,19 +161,6 @@ suite("DabTool Tests", () => {
155161
const mockDesigner = sandbox.createStubInstance(SchemaDesignerWebviewController);
156162
sandbox.stub(mockDesigner as any, "server").get(() => sampleServer);
157163
sandbox.stub(mockDesigner as any, "database").get(() => sampleDatabase);
158-
mockDesigner.getDabToolState.resolves({
159-
returnState: "full",
160-
version: "dabcfg_state",
161-
summary: {
162-
entityCount: 2,
163-
enabledEntityCount: 1,
164-
apiTypes: [Dab.ApiType.Rest],
165-
},
166-
config: Dab.createDefaultConfig([
167-
createTable("t1", "dbo", "Users"),
168-
createTable("t2", "sales", "Orders"),
169-
]),
170-
});
171164
showDabStub.resolves(mockDesigner);
172165

173166
const options = {
@@ -180,21 +173,17 @@ suite("DabTool Tests", () => {
180173
const parsed = JSON.parse(await dabTool.call(options, mockToken));
181174
expect(parsed.success).to.equal(true);
182175
expect(parsed.message).to.equal(loc.dabToolShowSuccessMessage);
183-
expect(parsed.version).to.equal("dabcfg_state");
184-
expect(parsed.summary).to.deep.equal({
185-
entityCount: 2,
186-
enabledEntityCount: 1,
187-
apiTypes: [Dab.ApiType.Rest],
188-
});
189176
expect(parsed.recommendedTool).to.equal("mssql_dab");
190177
expect(parsed.recommendedNextCall).to.deep.equal({
191178
operation: "get_state",
192179
});
193180
expect(parsed.server).to.equal(sampleServer);
194181
expect(parsed.database).to.equal(sampleDatabase);
195182
expect(parsed).to.not.have.property("config");
183+
expect(parsed).to.not.have.property("version");
184+
expect(parsed).to.not.have.property("summary");
196185
expect(showDabStub.calledOnceWith(sampleConnectionId, sampleDatabase)).to.equal(true);
197-
expect(mockDesigner.getDabToolState.calledOnce).to.equal(true);
186+
expect(mockDesigner.getDabToolState.called).to.equal(false);
198187
});
199188

200189
test("returns no_active_designer when there is no active schema designer", async () => {
@@ -733,7 +722,7 @@ suite("DabTool Tests", () => {
733722
expect(state).to.not.have.property("config");
734723
});
735724

736-
test("get_state throws when handlers are not initialized", async () => {
725+
test("get_state throws when initialization does not complete", async () => {
737726
const harness = createDabHandlerHarness({
738727
tables: [createTable("t1", "dbo", "Users")],
739728
dabConfig: null,
@@ -748,11 +737,32 @@ suite("DabTool Tests", () => {
748737
}
749738

750739
expect(caughtError).to.be.instanceOf(Error);
740+
expect(harness.waitForInitialization.calledOnce).to.equal(true);
751741
expect((caughtError as Error).message).to.equal(
752742
locConstants.schemaDesigner.schemaDesignerNotInitialized,
753743
);
754744
});
755745

746+
test("get_state waits for initialization before returning state", async () => {
747+
let harness: ReturnType<typeof createDabHandlerHarness>;
748+
const waitForInitialization = sandbox.stub().callsFake(async () => {
749+
harness.isInitializedRef.current = true;
750+
return true;
751+
});
752+
harness = createDabHandlerHarness({
753+
tables: [createTable("t1", "dbo", "Users")],
754+
dabConfig: null,
755+
initialized: false,
756+
waitForInitialization,
757+
});
758+
759+
const state = await harness.getState();
760+
761+
expect(waitForInitialization.calledOnce).to.equal(true);
762+
expect(state.returnState).to.equal("full");
763+
expect(state.summary.entityCount).to.equal(1);
764+
});
765+
756766
test("get_state avoids commit when config is already synchronized with schema", async () => {
757767
const tables = [
758768
createTable("t1", "dbo", "Users"),

0 commit comments

Comments
 (0)