Extract external API usage from BQRS in the extension

This commit is contained in:
Koen Vlaswinkel
2023-04-05 11:03:31 +02:00
parent 8741ba9379
commit d51ff42ab3
4 changed files with 89 additions and 78 deletions

View File

@@ -0,0 +1,54 @@
import { DecodedBqrsChunk } from "../pure/bqrs-cli-types";
import { Call, ExternalApiUsage } from "./external-api-usage";
export function decodeBqrsToExternalApiUsages(
chunk: DecodedBqrsChunk,
): ExternalApiUsage[] {
const methodsByApiName = new Map<string, ExternalApiUsage>();
chunk?.tuples.forEach((tuple) => {
const signature = tuple[0] as string;
const supported = tuple[1] as boolean;
const usage = tuple[2] as Call;
const [packageWithType, methodDeclaration] = signature.split("#");
const packageName = packageWithType.substring(
0,
packageWithType.lastIndexOf("."),
);
const typeName = packageWithType.substring(
packageWithType.lastIndexOf(".") + 1,
);
const methodName = methodDeclaration.substring(
0,
methodDeclaration.indexOf("("),
);
const methodParameters = methodDeclaration.substring(
methodDeclaration.indexOf("("),
);
if (!methodsByApiName.has(signature)) {
methodsByApiName.set(signature, {
signature,
packageName,
typeName,
methodName,
methodParameters,
supported,
usages: [],
});
}
const method = methodsByApiName.get(signature)!;
method.usages.push(usage);
});
const externalApiUsages = Array.from(methodsByApiName.values());
externalApiUsages.sort((a, b) => {
// Sort by number of usages descending
return b.usages.length - a.usages.length;
});
return externalApiUsages;
}

View File

@@ -14,6 +14,7 @@ import { dump } from "js-yaml";
import { getOnDiskWorkspaceFolders } from "../helpers";
import { DatabaseItem } from "../local-databases";
import { CodeQLCliServer } from "../cli";
import { decodeBqrsToExternalApiUsages } from "./bqrs";
export class DataExtensionsEditorView extends AbstractWebview<
ToDataExtensionsEditorMessage,
@@ -84,8 +85,8 @@ export class DataExtensionsEditorView extends AbstractWebview<
const bqrsPath = queryResult.outputDir.bqrsPath;
const results = await this.getResults(bqrsPath);
if (!results) {
const bqrsChunk = await this.getResults(bqrsPath);
if (!bqrsChunk) {
await this.clearProgress();
return;
}
@@ -96,9 +97,11 @@ export class DataExtensionsEditorView extends AbstractWebview<
maxStep: 1500,
});
const externalApiUsages = decodeBqrsToExternalApiUsages(bqrsChunk);
await this.postMessage({
t: "setExternalApiRepoResults",
results,
t: "setExternalApiUsages",
externalApiUsages,
});
await this.clearProgress();

View File

@@ -5,7 +5,6 @@ import {
ResultSetSchema,
Column,
ResolvableLocationValue,
DecodedBqrsChunk,
} from "./bqrs-cli-types";
import {
VariantAnalysis,
@@ -15,6 +14,7 @@ import {
import { RepositoriesFilterSortStateWithIds } from "./variant-analysis-filter-sort";
import { ErrorLike } from "./errors";
import { DataFlowPaths } from "../variant-analysis/shared/data-flow-paths";
import { ExternalApiUsage } from "../data-extensions-editor/external-api-usage";
/**
* This module contains types and code that are shared between
@@ -480,9 +480,9 @@ export type ToDataFlowPathsMessage = SetDataFlowPathsMessage;
export type FromDataFlowPathsMessage = CommonFromViewMessages;
export interface SetExternalApiResultsMessage {
t: "setExternalApiRepoResults";
results: DecodedBqrsChunk;
export interface SetExternalApiUsagesMessage {
t: "setExternalApiUsages";
externalApiUsages: ExternalApiUsage[];
}
export interface ShowProgressMessage {
@@ -493,7 +493,7 @@ export interface ShowProgressMessage {
}
export type ToDataExtensionsEditorMessage =
| SetExternalApiResultsMessage
| SetExternalApiUsagesMessage
| ShowProgressMessage;
export type FromDataExtensionsEditorMessage = ViewLoadedMsg;

View File

@@ -1,6 +1,5 @@
import * as React from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { DecodedBqrsChunk } from "../../pure/bqrs-cli-types";
import {
ShowProgressMessage,
ToDataExtensionsEditorMessage,
@@ -11,10 +10,7 @@ import {
VSCodeDataGridRow,
} from "@vscode/webview-ui-toolkit/react";
import styled from "styled-components";
import {
Call,
ExternalApiUsage,
} from "../../data-extensions-editor/external-api-usage";
import { ExternalApiUsage } from "../../data-extensions-editor/external-api-usage";
import { ModeledMethod } from "../../data-extensions-editor/modeled-method";
import { MethodRow } from "./MethodRow";
import { assertNever } from "../../pure/helpers-pure";
@@ -35,9 +31,9 @@ const ProgressBar = styled.div<ProgressBarProps>`
`;
export function DataExtensionsEditor(): JSX.Element {
const [results, setResults] = useState<DecodedBqrsChunk | undefined>(
undefined,
);
const [externalApiUsages, setExternalApiUsages] = useState<
ExternalApiUsage[]
>([]);
const [modeledMethods, setModeledMethods] = useState<
Record<string, ModeledMethod>
>({});
@@ -52,8 +48,8 @@ export function DataExtensionsEditor(): JSX.Element {
if (evt.origin === window.origin) {
const msg: ToDataExtensionsEditorMessage = evt.data;
switch (msg.t) {
case "setExternalApiRepoResults":
setResults(msg.results);
case "setExternalApiUsages":
setExternalApiUsages(msg.externalApiUsages);
break;
case "showProgress":
setProgress(msg);
@@ -74,63 +70,21 @@ export function DataExtensionsEditor(): JSX.Element {
};
}, []);
const methods = useMemo(() => {
const methodsByApiName = new Map<string, ExternalApiUsage>();
results?.tuples.forEach((tuple) => {
const signature = tuple[0] as string;
const supported = tuple[1] as boolean;
const usage = tuple[2] as Call;
const [packageWithType, methodDeclaration] = signature.split("#");
const packageName = packageWithType.substring(
0,
packageWithType.lastIndexOf("."),
);
const typeName = packageWithType.substring(
packageWithType.lastIndexOf(".") + 1,
);
const methodName = methodDeclaration.substring(
0,
methodDeclaration.indexOf("("),
);
const methodParameters = methodDeclaration.substring(
methodDeclaration.indexOf("("),
);
if (!methodsByApiName.has(signature)) {
methodsByApiName.set(signature, {
signature,
packageName,
typeName,
methodName,
methodParameters,
supported,
usages: [],
});
}
const method = methodsByApiName.get(signature)!;
method.usages.push(usage);
});
const externalApiUsages = Array.from(methodsByApiName.values());
externalApiUsages.sort((a, b) => {
// Sort by number of usages descending
return b.usages.length - a.usages.length;
});
return externalApiUsages;
}, [results]);
const supportedPercentage = useMemo(() => {
return (methods.filter((m) => m.supported).length / methods.length) * 100;
}, [methods]);
return (
(externalApiUsages.filter((m) => m.supported).length /
externalApiUsages.length) *
100
);
}, [externalApiUsages]);
const unsupportedPercentage = useMemo(() => {
return (methods.filter((m) => !m.supported).length / methods.length) * 100;
}, [methods]);
return (
(externalApiUsages.filter((m) => !m.supported).length /
externalApiUsages.length) *
100
);
}, [externalApiUsages]);
const onChange = useCallback(
(method: ExternalApiUsage, model: ModeledMethod) => {
@@ -151,7 +105,7 @@ export function DataExtensionsEditor(): JSX.Element {
</p>
)}
{methods.length > 0 && (
{externalApiUsages.length > 0 && (
<>
<div>
<h3>External API support stats</h3>
@@ -186,11 +140,11 @@ export function DataExtensionsEditor(): JSX.Element {
Kind
</VSCodeDataGridCell>
</VSCodeDataGridRow>
{methods.map((method) => (
{externalApiUsages.map((externalApiUsage) => (
<MethodRow
key={method.signature}
method={method}
model={modeledMethods[method.signature]}
key={externalApiUsage.signature}
method={externalApiUsage}
model={modeledMethods[externalApiUsage.signature]}
onChange={onChange}
/>
))}