Change kind input to a dropdown
This changes the kind input from a text field to a dropdown in the data extensions editor. The supported values for each extensible predicate are based on what is currently in-scope for the documentation. Other kinds are not supported. The supported kinds are now stored on the `extensiblePredicateDefinitions` to make it easier to add new kinds in the future.
This commit is contained in:
@@ -4,7 +4,7 @@ import { join } from "path";
|
||||
import { QueryRunner } from "../queryRunner";
|
||||
import { CodeQLCliServer } from "../cli";
|
||||
import { TeeLogger } from "../common";
|
||||
import { extensiblePredicateDefinitions } from "./yaml";
|
||||
import { extensiblePredicateDefinitions } from "./predicates";
|
||||
import { ProgressCallback } from "../progress";
|
||||
import { getOnDiskWorkspaceFolders } from "../helpers";
|
||||
import {
|
||||
|
||||
136
extensions/ql-vscode/src/data-extensions-editor/predicates.ts
Normal file
136
extensions/ql-vscode/src/data-extensions-editor/predicates.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { ExternalApiUsage } from "./external-api-usage";
|
||||
import {
|
||||
ModeledMethod,
|
||||
ModeledMethodType,
|
||||
ModeledMethodWithSignature,
|
||||
} from "./modeled-method";
|
||||
|
||||
export type ExternalApiUsageByType = {
|
||||
externalApiUsage: ExternalApiUsage;
|
||||
modeledMethod: ModeledMethod;
|
||||
};
|
||||
|
||||
export type ExtensiblePredicateDefinition = {
|
||||
extensiblePredicate: string;
|
||||
generateMethodDefinition: (method: ExternalApiUsageByType) => any[];
|
||||
readModeledMethod: (row: any[]) => ModeledMethodWithSignature;
|
||||
|
||||
supportedKinds?: string[];
|
||||
};
|
||||
|
||||
function readRowToMethod(row: any[]): string {
|
||||
return `${row[0]}.${row[1]}#${row[3]}${row[4]}`;
|
||||
}
|
||||
|
||||
export const extensiblePredicateDefinitions: Record<
|
||||
Exclude<ModeledMethodType, "none">,
|
||||
ExtensiblePredicateDefinition
|
||||
> = {
|
||||
source: {
|
||||
extensiblePredicate: "sourceModel",
|
||||
// extensible predicate sourceModel(
|
||||
// string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
// string output, string kind, string provenance
|
||||
// );
|
||||
generateMethodDefinition: (method) => [
|
||||
method.externalApiUsage.packageName,
|
||||
method.externalApiUsage.typeName,
|
||||
true,
|
||||
method.externalApiUsage.methodName,
|
||||
method.externalApiUsage.methodParameters,
|
||||
"",
|
||||
method.modeledMethod.output,
|
||||
method.modeledMethod.kind,
|
||||
"manual",
|
||||
],
|
||||
readModeledMethod: (row) => ({
|
||||
signature: readRowToMethod(row),
|
||||
modeledMethod: {
|
||||
type: "source",
|
||||
input: "",
|
||||
output: row[6],
|
||||
kind: row[7],
|
||||
},
|
||||
}),
|
||||
supportedKinds: ["remote"],
|
||||
},
|
||||
sink: {
|
||||
extensiblePredicate: "sinkModel",
|
||||
// extensible predicate sinkModel(
|
||||
// string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
// string input, string kind, string provenance
|
||||
// );
|
||||
generateMethodDefinition: (method) => [
|
||||
method.externalApiUsage.packageName,
|
||||
method.externalApiUsage.typeName,
|
||||
true,
|
||||
method.externalApiUsage.methodName,
|
||||
method.externalApiUsage.methodParameters,
|
||||
"",
|
||||
method.modeledMethod.input,
|
||||
method.modeledMethod.kind,
|
||||
"manual",
|
||||
],
|
||||
readModeledMethod: (row) => ({
|
||||
signature: readRowToMethod(row),
|
||||
modeledMethod: {
|
||||
type: "sink",
|
||||
input: row[6],
|
||||
output: "",
|
||||
kind: row[7],
|
||||
},
|
||||
}),
|
||||
supportedKinds: ["sql", "xss", "logging"],
|
||||
},
|
||||
summary: {
|
||||
extensiblePredicate: "summaryModel",
|
||||
// extensible predicate summaryModel(
|
||||
// string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
// string input, string output, string kind, string provenance
|
||||
// );
|
||||
generateMethodDefinition: (method) => [
|
||||
method.externalApiUsage.packageName,
|
||||
method.externalApiUsage.typeName,
|
||||
true,
|
||||
method.externalApiUsage.methodName,
|
||||
method.externalApiUsage.methodParameters,
|
||||
"",
|
||||
method.modeledMethod.input,
|
||||
method.modeledMethod.output,
|
||||
method.modeledMethod.kind,
|
||||
"manual",
|
||||
],
|
||||
readModeledMethod: (row) => ({
|
||||
signature: readRowToMethod(row),
|
||||
modeledMethod: {
|
||||
type: "summary",
|
||||
input: row[6],
|
||||
output: row[7],
|
||||
kind: row[8],
|
||||
},
|
||||
}),
|
||||
supportedKinds: ["taint", "value"],
|
||||
},
|
||||
neutral: {
|
||||
extensiblePredicate: "neutralModel",
|
||||
// extensible predicate neutralModel(
|
||||
// string package, string type, string name, string signature, string provenance
|
||||
// );
|
||||
generateMethodDefinition: (method) => [
|
||||
method.externalApiUsage.packageName,
|
||||
method.externalApiUsage.typeName,
|
||||
method.externalApiUsage.methodName,
|
||||
method.externalApiUsage.methodParameters,
|
||||
"manual",
|
||||
],
|
||||
readModeledMethod: (row) => ({
|
||||
signature: `${row[0]}.${row[1]}#${row[2]}${row[3]}`,
|
||||
modeledMethod: {
|
||||
type: "neutral",
|
||||
input: "",
|
||||
output: "",
|
||||
kind: "",
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
ModeledMethodType,
|
||||
ModeledMethodWithSignature,
|
||||
} from "./modeled-method";
|
||||
import { extensiblePredicateDefinitions } from "./predicates";
|
||||
|
||||
type ExternalApiUsageByType = {
|
||||
externalApiUsage: ExternalApiUsage;
|
||||
@@ -16,120 +17,6 @@ type ExtensiblePredicateDefinition = {
|
||||
readModeledMethod: (row: any[]) => ModeledMethodWithSignature;
|
||||
};
|
||||
|
||||
function readRowToMethod(row: any[]): string {
|
||||
return `${row[0]}.${row[1]}#${row[3]}${row[4]}`;
|
||||
}
|
||||
|
||||
export const extensiblePredicateDefinitions: Record<
|
||||
Exclude<ModeledMethodType, "none">,
|
||||
ExtensiblePredicateDefinition
|
||||
> = {
|
||||
source: {
|
||||
extensiblePredicate: "sourceModel",
|
||||
// extensible predicate sourceModel(
|
||||
// string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
// string output, string kind, string provenance
|
||||
// );
|
||||
generateMethodDefinition: (method) => [
|
||||
method.externalApiUsage.packageName,
|
||||
method.externalApiUsage.typeName,
|
||||
true,
|
||||
method.externalApiUsage.methodName,
|
||||
method.externalApiUsage.methodParameters,
|
||||
"",
|
||||
method.modeledMethod.output,
|
||||
method.modeledMethod.kind,
|
||||
"manual",
|
||||
],
|
||||
readModeledMethod: (row) => ({
|
||||
signature: readRowToMethod(row),
|
||||
modeledMethod: {
|
||||
type: "source",
|
||||
input: "",
|
||||
output: row[6],
|
||||
kind: row[7],
|
||||
},
|
||||
}),
|
||||
},
|
||||
sink: {
|
||||
extensiblePredicate: "sinkModel",
|
||||
// extensible predicate sinkModel(
|
||||
// string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
// string input, string kind, string provenance
|
||||
// );
|
||||
generateMethodDefinition: (method) => [
|
||||
method.externalApiUsage.packageName,
|
||||
method.externalApiUsage.typeName,
|
||||
true,
|
||||
method.externalApiUsage.methodName,
|
||||
method.externalApiUsage.methodParameters,
|
||||
"",
|
||||
method.modeledMethod.input,
|
||||
method.modeledMethod.kind,
|
||||
"manual",
|
||||
],
|
||||
readModeledMethod: (row) => ({
|
||||
signature: readRowToMethod(row),
|
||||
modeledMethod: {
|
||||
type: "sink",
|
||||
input: row[6],
|
||||
output: "",
|
||||
kind: row[7],
|
||||
},
|
||||
}),
|
||||
},
|
||||
summary: {
|
||||
extensiblePredicate: "summaryModel",
|
||||
// extensible predicate summaryModel(
|
||||
// string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
// string input, string output, string kind, string provenance
|
||||
// );
|
||||
generateMethodDefinition: (method) => [
|
||||
method.externalApiUsage.packageName,
|
||||
method.externalApiUsage.typeName,
|
||||
true,
|
||||
method.externalApiUsage.methodName,
|
||||
method.externalApiUsage.methodParameters,
|
||||
"",
|
||||
method.modeledMethod.input,
|
||||
method.modeledMethod.output,
|
||||
method.modeledMethod.kind,
|
||||
"manual",
|
||||
],
|
||||
readModeledMethod: (row) => ({
|
||||
signature: readRowToMethod(row),
|
||||
modeledMethod: {
|
||||
type: "summary",
|
||||
input: row[6],
|
||||
output: row[7],
|
||||
kind: row[8],
|
||||
},
|
||||
}),
|
||||
},
|
||||
neutral: {
|
||||
extensiblePredicate: "neutralModel",
|
||||
// extensible predicate neutralModel(
|
||||
// string package, string type, string name, string signature, string provenance
|
||||
// );
|
||||
generateMethodDefinition: (method) => [
|
||||
method.externalApiUsage.packageName,
|
||||
method.externalApiUsage.typeName,
|
||||
method.externalApiUsage.methodName,
|
||||
method.externalApiUsage.methodParameters,
|
||||
"manual",
|
||||
],
|
||||
readModeledMethod: (row) => ({
|
||||
signature: `${row[0]}.${row[1]}#${row[2]}${row[3]}`,
|
||||
modeledMethod: {
|
||||
type: "neutral",
|
||||
input: "",
|
||||
output: "",
|
||||
kind: "",
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
function createDataProperty(
|
||||
methods: ExternalApiUsageByType[],
|
||||
definition: ExtensiblePredicateDefinition,
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import * as React from "react";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react";
|
||||
|
||||
import type { ModeledMethod } from "../../data-extensions-editor/modeled-method";
|
||||
|
||||
const Dropdown = styled(VSCodeDropdown)`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
kinds: Array<ModeledMethod["kind"]>;
|
||||
|
||||
value: ModeledMethod["kind"] | undefined;
|
||||
onChange: (value: ModeledMethod["kind"]) => void;
|
||||
};
|
||||
|
||||
export const KindInput = ({ kinds, value, onChange }: Props) => {
|
||||
const handleInput = useCallback(
|
||||
(e: InputEvent) => {
|
||||
const target = e.target as HTMLSelectElement;
|
||||
|
||||
onChange(target.value as ModeledMethod["kind"]);
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (value === undefined && kinds.length > 0) {
|
||||
onChange(kinds[0]);
|
||||
}
|
||||
|
||||
if (value !== undefined && !kinds.includes(value)) {
|
||||
onChange(kinds[0]);
|
||||
}
|
||||
}, [value, kinds, onChange]);
|
||||
|
||||
return (
|
||||
<Dropdown value={value} onInput={handleInput}>
|
||||
{kinds.map((kind) => (
|
||||
<VSCodeOption key={kind} value={kind}>
|
||||
{kind}
|
||||
</VSCodeOption>
|
||||
))}
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
VSCodeDataGridRow,
|
||||
VSCodeDropdown,
|
||||
VSCodeOption,
|
||||
VSCodeTextField,
|
||||
} from "@vscode/webview-ui-toolkit/react";
|
||||
import * as React from "react";
|
||||
import { useCallback, useMemo } from "react";
|
||||
@@ -15,15 +14,13 @@ import {
|
||||
ModeledMethod,
|
||||
ModeledMethodType,
|
||||
} from "../../data-extensions-editor/modeled-method";
|
||||
import { KindInput } from "./KindInput";
|
||||
import { extensiblePredicateDefinitions } from "../../data-extensions-editor/predicates";
|
||||
|
||||
const Dropdown = styled(VSCodeDropdown)`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const TextField = styled(VSCodeTextField)`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
type SupportedUnsupportedSpanProps = {
|
||||
supported: boolean;
|
||||
};
|
||||
@@ -107,17 +104,15 @@ export const MethodRow = ({
|
||||
},
|
||||
[onChange, externalApiUsage, modeledMethod],
|
||||
);
|
||||
const handleKindInput = useCallback(
|
||||
(e: InputEvent) => {
|
||||
const handleKindChange = useCallback(
|
||||
(kind: string) => {
|
||||
if (!modeledMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = e.target as HTMLSelectElement;
|
||||
|
||||
onChange(externalApiUsage, {
|
||||
...modeledMethod,
|
||||
kind: target.value as ModeledMethod["kind"],
|
||||
kind,
|
||||
});
|
||||
},
|
||||
[onChange, externalApiUsage, modeledMethod],
|
||||
@@ -130,6 +125,11 @@ export const MethodRow = ({
|
||||
});
|
||||
}, [externalApiUsage]);
|
||||
|
||||
const predicate =
|
||||
modeledMethod?.type && modeledMethod.type !== "none"
|
||||
? extensiblePredicateDefinitions[modeledMethod.type]
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<VSCodeDataGridRow>
|
||||
<VSCodeDataGridCell gridColumn={1}>
|
||||
@@ -195,10 +195,13 @@ export const MethodRow = ({
|
||||
)}
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={7}>
|
||||
{modeledMethod?.type &&
|
||||
["source", "sink", "summary"].includes(modeledMethod?.type) && (
|
||||
<TextField value={modeledMethod?.kind} onInput={handleKindInput} />
|
||||
)}
|
||||
{predicate?.supportedKinds && (
|
||||
<KindInput
|
||||
kinds={predicate.supportedKinds}
|
||||
value={modeledMethod?.kind}
|
||||
onChange={handleKindChange}
|
||||
/>
|
||||
)}
|
||||
</VSCodeDataGridCell>
|
||||
</VSCodeDataGridRow>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user