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:
Koen Vlaswinkel
2023-04-12 15:43:08 +02:00
parent c7bb22c312
commit f0fbaabd69
5 changed files with 203 additions and 129 deletions

View File

@@ -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 {

View 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: "",
},
}),
},
};

View File

@@ -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,

View File

@@ -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>
);
};

View File

@@ -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>
);