Skip to content

Commit 3ac8a81

Browse files
authored
Update provenance when inputs change (#3064)
1 parent ab4717c commit 3ac8a81

File tree

6 files changed

+70
-13
lines changed

6 files changed

+70
-13
lines changed

extensions/ql-vscode/src/model-editor/modeled-method.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,38 @@ export function isModelAccepted(
126126
modeledMethod.provenance !== "ai-generated"
127127
);
128128
}
129+
130+
/**
131+
* Calculates the new provenance for a modeled method based on the current provenance.
132+
* @param modeledMethod The modeled method if there is one.
133+
* @returns The new provenance.
134+
*/
135+
export function calculateNewProvenance(
136+
modeledMethod: ModeledMethod | undefined,
137+
) {
138+
if (!modeledMethod || !modeledMethodSupportsProvenance(modeledMethod)) {
139+
// If nothing has been modeled or the modeled method does not support
140+
// provenance, we assume that the user has entered it manually.
141+
return "manual";
142+
}
143+
144+
switch (modeledMethod.provenance) {
145+
case "df-generated":
146+
// If the method has been generated and there has been a change, we assume
147+
// that the user has manually edited it.
148+
return "df-manual";
149+
case "df-manual":
150+
// If the method has had manual edits, we want the provenance to stay the same.
151+
return "df-manual";
152+
case "ai-generated":
153+
// If the method has been generated and there has been a change, we assume
154+
// that the user has manually edited it.
155+
return "ai-manual";
156+
case "ai-manual":
157+
// If the method has had manual edits, we want the provenance to stay the same.
158+
return "ai-manual";
159+
default:
160+
// The method has been modeled manually.
161+
return "manual";
162+
}
163+
}

extensions/ql-vscode/src/view/model-editor/ModelInputDropdown.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as React from "react";
22
import { ChangeEvent, useCallback, useMemo } from "react";
33
import {
44
ModeledMethod,
5+
calculateNewProvenance,
56
isModelAccepted,
67
modeledMethodSupportsInput,
78
} from "../../model-editor/modeled-method";
@@ -53,6 +54,7 @@ export const ModelInputDropdown = ({
5354

5455
onChange({
5556
...modeledMethod,
57+
provenance: calculateNewProvenance(modeledMethod),
5658
input: target.value,
5759
});
5860
},

extensions/ql-vscode/src/view/model-editor/ModelKindDropdown.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
ModeledMethodKind,
66
modeledMethodSupportsKind,
77
isModelAccepted,
8+
calculateNewProvenance,
89
} from "../../model-editor/modeled-method";
910
import { getModelsAsDataLanguage } from "../../model-editor/languages";
1011
import { QueryLanguage } from "../../common/query-language";
@@ -52,6 +53,7 @@ export const ModelKindDropdown = ({
5253

5354
onChange({
5455
...modeledMethod,
56+
provenance: calculateNewProvenance(modeledMethod),
5557
kind,
5658
});
5759
},

extensions/ql-vscode/src/view/model-editor/ModelOutputDropdown.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as React from "react";
22
import { ChangeEvent, useCallback, useMemo } from "react";
33
import {
44
ModeledMethod,
5+
calculateNewProvenance,
56
isModelAccepted,
67
modeledMethodSupportsOutput,
78
} from "../../model-editor/modeled-method";
@@ -54,6 +55,7 @@ export const ModelOutputDropdown = ({
5455

5556
onChange({
5657
...modeledMethod,
58+
provenance: calculateNewProvenance(modeledMethod),
5759
output: target.value,
5860
});
5961
},

extensions/ql-vscode/src/view/model-editor/ModelTypeDropdown.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import * as React from "react";
22
import { ChangeEvent, useCallback } from "react";
33
import {
4+
calculateNewProvenance,
45
isModelAccepted,
56
ModeledMethod,
6-
modeledMethodSupportsProvenance,
77
ModeledMethodType,
8-
Provenance,
98
} from "../../model-editor/modeled-method";
109
import { Method } from "../../model-editor/method";
1110
import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty";
@@ -43,15 +42,6 @@ export const ModelTypeDropdown = ({
4342
(e: ChangeEvent<HTMLSelectElement>) => {
4443
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
4544

46-
let newProvenance: Provenance = "manual";
47-
if (modeledMethod && modeledMethodSupportsProvenance(modeledMethod)) {
48-
if (modeledMethod.provenance === "df-generated") {
49-
newProvenance = "df-manual";
50-
} else if (modeledMethod.provenance === "ai-generated") {
51-
newProvenance = "ai-manual";
52-
}
53-
}
54-
5545
const emptyModeledMethod = createEmptyModeledMethod(
5646
e.target.value as ModeledMethodType,
5747
method,
@@ -67,7 +57,7 @@ export const ModelTypeDropdown = ({
6757
updatedModeledMethod.output = "ReturnValue";
6858
}
6959
if ("provenance" in updatedModeledMethod) {
70-
updatedModeledMethod.provenance = newProvenance;
60+
updatedModeledMethod.provenance = calculateNewProvenance(modeledMethod);
7161
}
7262
if ("kind" in updatedModeledMethod) {
7363
updatedModeledMethod.kind = "value";

extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe(MethodRow.name, () => {
2727
input: "Argument[0]",
2828
output: "ReturnValue",
2929
kind: "taint",
30-
provenance: "df-generated",
30+
provenance: "manual",
3131
};
3232
const onChange = jest.fn();
3333

@@ -111,6 +111,32 @@ describe(MethodRow.name, () => {
111111
]);
112112
});
113113

114+
it("changes the provenance when the kind is changed", async () => {
115+
const modeledMethodWithGeneratedProvenance: ModeledMethod = {
116+
...modeledMethod,
117+
provenance: "df-generated",
118+
};
119+
render({ modeledMethods: [modeledMethodWithGeneratedProvenance] });
120+
121+
onChange.mockReset();
122+
123+
expect(screen.getByRole("combobox", { name: "Kind" })).toHaveValue("taint");
124+
125+
await userEvent.selectOptions(
126+
screen.getByRole("combobox", { name: "Kind" }),
127+
"value",
128+
);
129+
130+
expect(onChange).toHaveBeenCalledTimes(1);
131+
expect(onChange).toHaveBeenCalledWith(method.signature, [
132+
{
133+
...modeledMethod,
134+
kind: "value",
135+
provenance: "df-manual",
136+
},
137+
]);
138+
});
139+
114140
it("has the correct input options", () => {
115141
render();
116142

0 commit comments

Comments
 (0)