Rename ExternalApiUsage to Method (#2776)

This commit is contained in:
Charis Kyriakou
2023-09-04 13:10:10 +01:00
committed by GitHub
parent 2d033bff57
commit 09077a0e24
43 changed files with 353 additions and 427 deletions

View File

@@ -12,7 +12,7 @@ import type {
} from "../variant-analysis/shared/variant-analysis"; } from "../variant-analysis/shared/variant-analysis";
import type { QLDebugConfiguration } from "../debugger/debug-configuration"; import type { QLDebugConfiguration } from "../debugger/debug-configuration";
import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item"; import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item";
import type { Usage } from "../model-editor/external-api-usage"; import type { Usage } from "../model-editor/method";
// A command function matching the signature that VS Code calls when // A command function matching the signature that VS Code calls when
// a command is invoked from a context menu on a TreeView with // a command is invoked from a context menu on a TreeView with

View File

@@ -17,7 +17,7 @@ import {
} from "../variant-analysis/shared/variant-analysis-filter-sort"; } from "../variant-analysis/shared/variant-analysis-filter-sort";
import { ErrorLike } from "../common/errors"; import { ErrorLike } from "../common/errors";
import { DataFlowPaths } from "../variant-analysis/shared/data-flow-paths"; import { DataFlowPaths } from "../variant-analysis/shared/data-flow-paths";
import { ExternalApiUsage, Usage } from "../model-editor/external-api-usage"; import { Method, Usage } from "../model-editor/method";
import { ModeledMethod } from "../model-editor/modeled-method"; import { ModeledMethod } from "../model-editor/modeled-method";
import { ModelEditorViewState } from "../model-editor/shared/view-state"; import { ModelEditorViewState } from "../model-editor/shared/view-state";
import { Mode } from "../model-editor/shared/mode"; import { Mode } from "../model-editor/shared/mode";
@@ -495,9 +495,9 @@ interface SetExtensionPackStateMessage {
viewState: ModelEditorViewState; viewState: ModelEditorViewState;
} }
interface SetExternalApiUsagesMessage { interface SetMethodsMessage {
t: "setExternalApiUsages"; t: "setMethods";
externalApiUsages: ExternalApiUsage[]; methods: Method[];
} }
interface LoadModeledMethodsMessage { interface LoadModeledMethodsMessage {
@@ -523,7 +523,7 @@ interface SwitchModeMessage {
interface JumpToUsageMessage { interface JumpToUsageMessage {
t: "jumpToUsage"; t: "jumpToUsage";
method: ExternalApiUsage; method: Method;
usage: Usage; usage: Usage;
} }
@@ -535,29 +535,29 @@ interface OpenExtensionPackMessage {
t: "openExtensionPack"; t: "openExtensionPack";
} }
interface RefreshExternalApiUsages { interface RefreshMethods {
t: "refreshExternalApiUsages"; t: "refreshMethods";
} }
interface SaveModeledMethods { interface SaveModeledMethods {
t: "saveModeledMethods"; t: "saveModeledMethods";
externalApiUsages: ExternalApiUsage[]; methods: Method[];
modeledMethods: Record<string, ModeledMethod>; modeledMethods: Record<string, ModeledMethod>;
} }
interface GenerateExternalApiMessage { interface GenerateMethodMessage {
t: "generateExternalApi"; t: "generateMethod";
} }
interface GenerateExternalApiFromLlmMessage { interface GenerateMethodsFromLlmMessage {
t: "generateExternalApiFromLlm"; t: "generateMethodsFromLlm";
packageName: string; packageName: string;
externalApiUsages: ExternalApiUsage[]; methods: Method[];
modeledMethods: Record<string, ModeledMethod>; modeledMethods: Record<string, ModeledMethod>;
} }
interface StopGeneratingExternalApiFromLlmMessage { interface StopGeneratingMethodsFromLlmMessage {
t: "stopGeneratingExternalApiFromLlm"; t: "stopGeneratingMethodsFromLlm";
packageName: string; packageName: string;
} }
@@ -572,7 +572,7 @@ interface HideModeledApisMessage {
export type ToModelEditorMessage = export type ToModelEditorMessage =
| SetExtensionPackStateMessage | SetExtensionPackStateMessage
| SetExternalApiUsagesMessage | SetMethodsMessage
| LoadModeledMethodsMessage | LoadModeledMethodsMessage
| AddModeledMethodsMessage | AddModeledMethodsMessage
| SetInProgressMethodsMessage; | SetInProgressMethodsMessage;
@@ -580,14 +580,14 @@ export type ToModelEditorMessage =
export type FromModelEditorMessage = export type FromModelEditorMessage =
| ViewLoadedMsg | ViewLoadedMsg
| SwitchModeMessage | SwitchModeMessage
| RefreshExternalApiUsages | RefreshMethods
| OpenDatabaseMessage | OpenDatabaseMessage
| OpenExtensionPackMessage | OpenExtensionPackMessage
| JumpToUsageMessage | JumpToUsageMessage
| SaveModeledMethods | SaveModeledMethods
| GenerateExternalApiMessage | GenerateMethodMessage
| GenerateExternalApiFromLlmMessage | GenerateMethodsFromLlmMessage
| StopGeneratingExternalApiFromLlmMessage | StopGeneratingMethodsFromLlmMessage
| ModelDependencyMessage | ModelDependencyMessage
| HideModeledApisMessage; | HideModeledApisMessage;
@@ -597,7 +597,7 @@ export type FromMethodModelingMessage =
interface SetMethodMessage { interface SetMethodMessage {
t: "setMethod"; t: "setMethod";
method: ExternalApiUsage; method: Method;
} }
export type ToMethodModelingMessage = SetMethodMessage; export type ToMethodModelingMessage = SetMethodMessage;

View File

@@ -11,7 +11,7 @@ import { assertNever } from "../common/helpers-pure";
import { dir } from "tmp-promise"; import { dir } from "tmp-promise";
import { writeFile, outputFile } from "fs-extra"; import { writeFile, outputFile } from "fs-extra";
import { dump as dumpYaml } from "js-yaml"; import { dump as dumpYaml } from "js-yaml";
import { MethodSignature } from "./external-api-usage"; import { MethodSignature } from "./method";
import { runQuery } from "../local-queries/run-query"; import { runQuery } from "../local-queries/run-query";
import { QueryMetadata } from "../common/interface-types"; import { QueryMetadata } from "../common/interface-types";
import { CancellationTokenSource } from "vscode"; import { CancellationTokenSource } from "vscode";

View File

@@ -4,7 +4,7 @@ import { AutoModelQueriesResult } from "./auto-model-codeml-queries";
import { assertNever } from "../common/helpers-pure"; import { assertNever } from "../common/helpers-pure";
import * as Sarif from "sarif"; import * as Sarif from "sarif";
import { gzipEncode } from "../common/zlib"; import { gzipEncode } from "../common/zlib";
import { ExternalApiUsage, MethodSignature } from "./external-api-usage"; import { Method, MethodSignature } from "./method";
import { ModeledMethod } from "./modeled-method"; import { ModeledMethod } from "./modeled-method";
import { groupMethods, sortGroupNames, sortMethods } from "./shared/sorting"; import { groupMethods, sortGroupNames, sortMethods } from "./shared/sorting";
@@ -13,28 +13,26 @@ import { groupMethods, sortGroupNames, sortMethods } from "./shared/sorting";
* candidates to the candidate limit and filtering out anything that is already modeled and respecting * candidates to the candidate limit and filtering out anything that is already modeled and respecting
* the order in the UI. * the order in the UI.
* @param mode Whether it is application or framework mode. * @param mode Whether it is application or framework mode.
* @param externalApiUsages all external API usages. * @param methods all methods.
* @param modeledMethods the currently modeled methods. * @param modeledMethods the currently modeled methods.
* @returns list of modeled methods that are candidates for modeling. * @returns list of modeled methods that are candidates for modeling.
*/ */
export function getCandidates( export function getCandidates(
mode: Mode, mode: Mode,
externalApiUsages: ExternalApiUsage[], methods: Method[],
modeledMethods: Record<string, ModeledMethod>, modeledMethods: Record<string, ModeledMethod>,
): MethodSignature[] { ): MethodSignature[] {
// Sort the same way as the UI so we send the first ones listed in the UI first // Sort the same way as the UI so we send the first ones listed in the UI first
const grouped = groupMethods(externalApiUsages, mode); const grouped = groupMethods(methods, mode);
const sortedGroupNames = sortGroupNames(grouped); const sortedGroupNames = sortGroupNames(grouped);
const sortedExternalApiUsages = sortedGroupNames.flatMap((name) => const sortedMethods = sortedGroupNames.flatMap((name) =>
sortMethods(grouped[name]), sortMethods(grouped[name]),
); );
const candidates: MethodSignature[] = []; const candidates: MethodSignature[] = [];
for (const externalApiUsage of sortedExternalApiUsages) { for (const method of sortedMethods) {
const modeledMethod: ModeledMethod = modeledMethods[ const modeledMethod: ModeledMethod = modeledMethods[method.signature] ?? {
externalApiUsage.signature
] ?? {
type: "none", type: "none",
}; };
@@ -44,12 +42,12 @@ export function getCandidates(
} }
// A method that is supported is modeled outside of the model file, so it is not a candidate. // A method that is supported is modeled outside of the model file, so it is not a candidate.
if (externalApiUsage.supported) { if (method.supported) {
continue; continue;
} }
// The rest are candidates // The rest are candidates
candidates.push(externalApiUsage); candidates.push(method);
} }
return candidates; return candidates;
} }

View File

@@ -1,4 +1,4 @@
import { ExternalApiUsage, MethodSignature } from "./external-api-usage"; import { Method, MethodSignature } from "./method";
import { ModeledMethod } from "./modeled-method"; import { ModeledMethod } from "./modeled-method";
import { extLogger } from "../common/logging/vscode"; import { extLogger } from "../common/logging/vscode";
import { load as loadYaml } from "js-yaml"; import { load as loadYaml } from "js-yaml";
@@ -52,13 +52,13 @@ export class AutoModeler {
* Models the given package's external API usages, except * Models the given package's external API usages, except
* the ones that are already modeled. * the ones that are already modeled.
* @param packageName The name of the package to model. * @param packageName The name of the package to model.
* @param externalApiUsages The external API usages. * @param methods The methods.
* @param modeledMethods The currently modeled methods. * @param modeledMethods The currently modeled methods.
* @param mode The mode we are modeling in. * @param mode The mode we are modeling in.
*/ */
public async startModeling( public async startModeling(
packageName: string, packageName: string,
externalApiUsages: ExternalApiUsage[], methods: Method[],
modeledMethods: Record<string, ModeledMethod>, modeledMethods: Record<string, ModeledMethod>,
mode: Mode, mode: Mode,
): Promise<void> { ): Promise<void> {
@@ -72,7 +72,7 @@ export class AutoModeler {
try { try {
await this.modelPackage( await this.modelPackage(
packageName, packageName,
externalApiUsages, methods,
modeledMethods, modeledMethods,
mode, mode,
cancellationTokenSource, cancellationTokenSource,
@@ -105,7 +105,7 @@ export class AutoModeler {
private async modelPackage( private async modelPackage(
packageName: string, packageName: string,
externalApiUsages: ExternalApiUsage[], methods: Method[],
modeledMethods: Record<string, ModeledMethod>, modeledMethods: Record<string, ModeledMethod>,
mode: Mode, mode: Mode,
cancellationTokenSource: CancellationTokenSource, cancellationTokenSource: CancellationTokenSource,
@@ -113,11 +113,7 @@ export class AutoModeler {
void extLogger.log(`Modeling package ${packageName}`); void extLogger.log(`Modeling package ${packageName}`);
await withProgress(async (progress) => { await withProgress(async (progress) => {
// Fetch the candidates to send to the model // Fetch the candidates to send to the model
const allCandidateMethods = getCandidates( const allCandidateMethods = getCandidates(mode, methods, modeledMethods);
mode,
externalApiUsages,
modeledMethods,
);
// If there are no candidates, there is nothing to model and we just return // If there are no candidates, there is nothing to model and we just return
if (allCandidateMethods.length === 0) { if (allCandidateMethods.length === 0) {

View File

@@ -1,16 +1,12 @@
import { DecodedBqrsChunk } from "../common/bqrs-cli-types"; import { DecodedBqrsChunk } from "../common/bqrs-cli-types";
import { import { Call, CallClassification, Method } from "./method";
Call,
CallClassification,
ExternalApiUsage,
} from "./external-api-usage";
import { ModeledMethodType } from "./modeled-method"; import { ModeledMethodType } from "./modeled-method";
import { parseLibraryFilename } from "./library"; import { parseLibraryFilename } from "./library";
export function decodeBqrsToExternalApiUsages( export function decodeBqrsToExternalApiUsages(
chunk: DecodedBqrsChunk, chunk: DecodedBqrsChunk,
): ExternalApiUsage[] { ): Method[] {
const methodsByApiName = new Map<string, ExternalApiUsage>(); const methodsByApiName = new Map<string, Method>();
chunk?.tuples.forEach((tuple) => { chunk?.tuples.forEach((tuple) => {
const usage = tuple[0] as Call; const usage = tuple[0] as Call;

View File

@@ -13,7 +13,7 @@ import { Mode } from "./shared/mode";
import { writeFile } from "fs-extra"; import { writeFile } from "fs-extra";
import { QueryLanguage } from "../common/query-language"; import { QueryLanguage } from "../common/query-language";
import { fetchExternalApiQueries } from "./queries"; import { fetchExternalApiQueries } from "./queries";
import { ExternalApiUsage } from "./external-api-usage"; import { Method } from "./method";
import { runQuery } from "../local-queries/run-query"; import { runQuery } from "../local-queries/run-query";
import { decodeBqrsToExternalApiUsages } from "./bqrs"; import { decodeBqrsToExternalApiUsages } from "./bqrs";
@@ -69,7 +69,7 @@ export async function runExternalApiQueries(
progress, progress,
token, token,
}: RunQueryOptions, }: RunQueryOptions,
): Promise<ExternalApiUsage[] | undefined> { ): Promise<Method[] | undefined> {
// The below code is temporary to allow for rapid prototyping of the queries. Once the queries are stabilized, we will // The below code is temporary to allow for rapid prototyping of the queries. Once the queries are stabilized, we will
// move these queries into the `github/codeql` repository and use them like any other contextual (e.g. AST) queries. // move these queries into the `github/codeql` repository and use them like any other contextual (e.g. AST) queries.
// This is intentionally not pretty code, as it will be removed soon. // This is intentionally not pretty code, as it will be removed soon.

View File

@@ -1,7 +1,7 @@
import { ExtensionContext, window } from "vscode"; import { ExtensionContext, window } from "vscode";
import { DisposableObject } from "../../common/disposable-object"; import { DisposableObject } from "../../common/disposable-object";
import { MethodModelingViewProvider } from "./method-modeling-view-provider"; import { MethodModelingViewProvider } from "./method-modeling-view-provider";
import { ExternalApiUsage } from "../external-api-usage"; import { Method } from "../method";
export class MethodModelingPanel extends DisposableObject { export class MethodModelingPanel extends DisposableObject {
private readonly provider: MethodModelingViewProvider; private readonly provider: MethodModelingViewProvider;
@@ -18,7 +18,7 @@ export class MethodModelingPanel extends DisposableObject {
); );
} }
public async setMethod(method: ExternalApiUsage): Promise<void> { public async setMethod(method: Method): Promise<void> {
await this.provider.setMethod(method); await this.provider.setMethod(method);
} }
} }

View File

@@ -6,7 +6,7 @@ import { telemetryListener } from "../../common/vscode/telemetry";
import { showAndLogExceptionWithTelemetry } from "../../common/logging/notifications"; import { showAndLogExceptionWithTelemetry } from "../../common/logging/notifications";
import { extLogger } from "../../common/logging/vscode/loggers"; import { extLogger } from "../../common/logging/vscode/loggers";
import { redactableError } from "../../common/errors"; import { redactableError } from "../../common/errors";
import { ExternalApiUsage } from "../external-api-usage"; import { Method } from "../method";
export class MethodModelingViewProvider implements WebviewViewProvider { export class MethodModelingViewProvider implements WebviewViewProvider {
public static readonly viewType = "codeQLMethodModeling"; public static readonly viewType = "codeQLMethodModeling";
@@ -46,7 +46,7 @@ export class MethodModelingViewProvider implements WebviewViewProvider {
this.webviewView = webviewView; this.webviewView = webviewView;
} }
public async setMethod(method: ExternalApiUsage): Promise<void> { public async setMethod(method: Method): Promise<void> {
if (this.webviewView) { if (this.webviewView) {
await this.webviewView.webview.postMessage({ await this.webviewView.webview.postMessage({
t: "setMethod", t: "setMethod",

View File

@@ -39,7 +39,7 @@ export interface MethodSignature {
methodParameters: string; methodParameters: string;
} }
export interface ExternalApiUsage extends MethodSignature { export interface Method extends MethodSignature {
/** /**
* Contains the name of the library containing the method declaration, e.g. `sql2o-1.6.0.jar` or `System.Runtime.dll` * Contains the name of the library containing the method declaration, e.g. `sql2o-1.6.0.jar` or `System.Runtime.dll`
*/ */

View File

@@ -9,7 +9,7 @@ import {
Uri, Uri,
} from "vscode"; } from "vscode";
import { DisposableObject } from "../../common/disposable-object"; import { DisposableObject } from "../../common/disposable-object";
import { ExternalApiUsage, Usage } from "../external-api-usage"; import { Method, Usage } from "../method";
import { DatabaseItem } from "../../databases/local-databases"; import { DatabaseItem } from "../../databases/local-databases";
import { relative } from "path"; import { relative } from "path";
import { CodeQLCliServer } from "../../codeql-cli/cli"; import { CodeQLCliServer } from "../../codeql-cli/cli";
@@ -19,7 +19,7 @@ export class MethodsUsageDataProvider
extends DisposableObject extends DisposableObject
implements TreeDataProvider<MethodsUsageTreeViewItem> implements TreeDataProvider<MethodsUsageTreeViewItem>
{ {
private externalApiUsages: ExternalApiUsage[] = []; private methods: Method[] = [];
private databaseItem: DatabaseItem | undefined = undefined; private databaseItem: DatabaseItem | undefined = undefined;
private sourceLocationPrefix: string | undefined = undefined; private sourceLocationPrefix: string | undefined = undefined;
private hideModeledApis: boolean = INITIAL_HIDE_MODELED_APIS_VALUE; private hideModeledApis: boolean = INITIAL_HIDE_MODELED_APIS_VALUE;
@@ -44,16 +44,16 @@ export class MethodsUsageDataProvider
* method and instead always pass new objects/arrays. * method and instead always pass new objects/arrays.
*/ */
public async setState( public async setState(
externalApiUsages: ExternalApiUsage[], methods: Method[],
databaseItem: DatabaseItem, databaseItem: DatabaseItem,
hideModeledApis: boolean, hideModeledApis: boolean,
): Promise<void> { ): Promise<void> {
if ( if (
this.externalApiUsages !== externalApiUsages || this.methods !== methods ||
this.databaseItem !== databaseItem || this.databaseItem !== databaseItem ||
this.hideModeledApis !== hideModeledApis this.hideModeledApis !== hideModeledApis
) { ) {
this.externalApiUsages = externalApiUsages; this.methods = methods;
this.databaseItem = databaseItem; this.databaseItem = databaseItem;
this.sourceLocationPrefix = this.sourceLocationPrefix =
await this.databaseItem.getSourceLocationPrefix(this.cliServer); await this.databaseItem.getSourceLocationPrefix(this.cliServer);
@@ -100,9 +100,9 @@ export class MethodsUsageDataProvider
getChildren(item?: MethodsUsageTreeViewItem): MethodsUsageTreeViewItem[] { getChildren(item?: MethodsUsageTreeViewItem): MethodsUsageTreeViewItem[] {
if (item === undefined) { if (item === undefined) {
if (this.hideModeledApis) { if (this.hideModeledApis) {
return this.externalApiUsages.filter((api) => !api.supported); return this.methods.filter((api) => !api.supported);
} else { } else {
return this.externalApiUsages; return this.methods;
} }
} else if (isExternalApiUsage(item)) { } else if (isExternalApiUsage(item)) {
return item.usages; return item.usages;
@@ -117,13 +117,13 @@ export class MethodsUsageDataProvider
if (isExternalApiUsage(item)) { if (isExternalApiUsage(item)) {
return undefined; return undefined;
} else { } else {
return this.externalApiUsages.find((e) => e.usages.includes(item)); return this.methods.find((e) => e.usages.includes(item));
} }
} }
public resolveCanonicalUsage(usage: Usage): Usage | undefined { public resolveCanonicalUsage(usage: Usage): Usage | undefined {
for (const externalApiUsage of this.externalApiUsages) { for (const method of this.methods) {
for (const u of externalApiUsage.usages) { for (const u of method.usages) {
if (usagesAreEqual(u, usage)) { if (usagesAreEqual(u, usage)) {
return u; return u;
} }
@@ -133,11 +133,9 @@ export class MethodsUsageDataProvider
} }
} }
export type MethodsUsageTreeViewItem = ExternalApiUsage | Usage; export type MethodsUsageTreeViewItem = Method | Usage;
function isExternalApiUsage( function isExternalApiUsage(item: MethodsUsageTreeViewItem): item is Method {
item: MethodsUsageTreeViewItem,
): item is ExternalApiUsage {
return (item as any).usages !== undefined; return (item as any).usages !== undefined;
} }

View File

@@ -4,7 +4,7 @@ import {
MethodsUsageDataProvider, MethodsUsageDataProvider,
MethodsUsageTreeViewItem, MethodsUsageTreeViewItem,
} from "./methods-usage-data-provider"; } from "./methods-usage-data-provider";
import { ExternalApiUsage, Usage } from "../external-api-usage"; import { Method, Usage } from "../method";
import { DatabaseItem } from "../../databases/local-databases"; import { DatabaseItem } from "../../databases/local-databases";
import { CodeQLCliServer } from "../../codeql-cli/cli"; import { CodeQLCliServer } from "../../codeql-cli/cli";
@@ -24,18 +24,14 @@ export class MethodsUsagePanel extends DisposableObject {
} }
public async setState( public async setState(
externalApiUsages: ExternalApiUsage[], methods: Method[],
databaseItem: DatabaseItem, databaseItem: DatabaseItem,
hideModeledApis: boolean, hideModeledApis: boolean,
): Promise<void> { ): Promise<void> {
await this.dataProvider.setState( await this.dataProvider.setState(methods, databaseItem, hideModeledApis);
externalApiUsages,
databaseItem,
hideModeledApis,
);
const numOfApis = hideModeledApis const numOfApis = hideModeledApis
? externalApiUsages.filter((api) => !api.supported).length ? methods.filter((api) => !api.supported).length
: externalApiUsages.length; : methods.length;
this.treeView.badge = { this.treeView.badge = {
value: numOfApis, value: numOfApis,
tooltip: "Number of external APIs", tooltip: "Number of external APIs",

View File

@@ -17,7 +17,7 @@ import { DisposableObject } from "../common/disposable-object";
import { MethodsUsagePanel } from "./methods-usage/methods-usage-panel"; import { MethodsUsagePanel } from "./methods-usage/methods-usage-panel";
import { Mode } from "./shared/mode"; import { Mode } from "./shared/mode";
import { showResolvableLocation } from "../databases/local-databases/locations"; import { showResolvableLocation } from "../databases/local-databases/locations";
import { ExternalApiUsage, Usage } from "./external-api-usage"; import { Method, Usage } from "./method";
import { setUpPack } from "./model-editor-queries"; import { setUpPack } from "./model-editor-queries";
import { MethodModelingPanel } from "./method-modeling/method-modeling-panel"; import { MethodModelingPanel } from "./method-modeling/method-modeling-panel";
@@ -175,10 +175,7 @@ export class ModelEditorModule extends DisposableObject {
await ensureDir(this.queryStorageDir); await ensureDir(this.queryStorageDir);
} }
private async showMethod( private async showMethod(method: Method, usage: Usage): Promise<void> {
method: ExternalApiUsage,
usage: Usage,
): Promise<void> {
await this.methodsUsagePanel.revealItem(usage); await this.methodsUsagePanel.revealItem(usage);
await this.methodModelingPanel.setMethod(method); await this.methodModelingPanel.setMethod(method);
} }

View File

@@ -28,7 +28,7 @@ import { App } from "../common/app";
import { showResolvableLocation } from "../databases/local-databases/locations"; import { showResolvableLocation } from "../databases/local-databases/locations";
import { redactableError } from "../common/errors"; import { redactableError } from "../common/errors";
import { runExternalApiQueries } from "./external-api-usage-queries"; import { runExternalApiQueries } from "./external-api-usage-queries";
import { ExternalApiUsage, Usage } from "./external-api-usage"; import { Method, Usage } from "./method";
import { ModeledMethod } from "./modeled-method"; import { ModeledMethod } from "./modeled-method";
import { ExtensionPack } from "./shared/extension-pack"; import { ExtensionPack } from "./shared/extension-pack";
import { showLlmGeneration } from "../config"; import { showLlmGeneration } from "../config";
@@ -46,7 +46,7 @@ export class ModelEditorView extends AbstractWebview<
> { > {
private readonly autoModeler: AutoModeler; private readonly autoModeler: AutoModeler;
private externalApiUsages: ExternalApiUsage[]; private methods: Method[];
private hideModeledApis: boolean; private hideModeledApis: boolean;
public constructor( public constructor(
@@ -61,12 +61,12 @@ export class ModelEditorView extends AbstractWebview<
private readonly extensionPack: ExtensionPack, private readonly extensionPack: ExtensionPack,
private mode: Mode, private mode: Mode,
private readonly updateMethodsUsagePanelState: ( private readonly updateMethodsUsagePanelState: (
externalApiUsages: ExternalApiUsage[], methods: Method[],
databaseItem: DatabaseItem, databaseItem: DatabaseItem,
hideModeledApis: boolean, hideModeledApis: boolean,
) => Promise<void>, ) => Promise<void>,
private readonly showMethod: ( private readonly showMethod: (
method: ExternalApiUsage, method: Method,
usage: Usage, usage: Usage,
) => Promise<void>, ) => Promise<void>,
private readonly handleViewBecameActive: (view: ModelEditorView) => void, private readonly handleViewBecameActive: (view: ModelEditorView) => void,
@@ -94,7 +94,7 @@ export class ModelEditorView extends AbstractWebview<
await this.postMessage({ t: "addModeledMethods", modeledMethods }); await this.postMessage({ t: "addModeledMethods", modeledMethods });
}, },
); );
this.externalApiUsages = []; this.methods = [];
this.hideModeledApis = INITIAL_HIDE_MODELED_APIS_VALUE; this.hideModeledApis = INITIAL_HIDE_MODELED_APIS_VALUE;
} }
@@ -106,7 +106,7 @@ export class ModelEditorView extends AbstractWebview<
if (panel.active) { if (panel.active) {
this.handleViewBecameActive(this); this.handleViewBecameActive(this);
await this.updateMethodsUsagePanelState( await this.updateMethodsUsagePanelState(
this.externalApiUsages, this.methods,
this.databaseItem, this.databaseItem,
this.hideModeledApis, this.hideModeledApis,
); );
@@ -188,7 +188,7 @@ export class ModelEditorView extends AbstractWebview<
); );
break; break;
case "refreshExternalApiUsages": case "refreshMethods":
await this.loadExternalApiUsages(); await this.loadExternalApiUsages();
break; break;
@@ -201,7 +201,7 @@ export class ModelEditorView extends AbstractWebview<
this.extensionPack, this.extensionPack,
this.databaseItem.name, this.databaseItem.name,
this.databaseItem.language, this.databaseItem.language,
msg.externalApiUsages, msg.methods,
msg.modeledMethods, msg.modeledMethods,
this.mode, this.mode,
this.cliServer, this.cliServer,
@@ -210,18 +210,18 @@ export class ModelEditorView extends AbstractWebview<
await Promise.all([this.setViewState(), this.loadExternalApiUsages()]); await Promise.all([this.setViewState(), this.loadExternalApiUsages()]);
break; break;
case "generateExternalApi": case "generateMethod":
await this.generateModeledMethods(); await this.generateModeledMethods();
break; break;
case "generateExternalApiFromLlm": case "generateMethodsFromLlm":
await this.generateModeledMethodsFromLlm( await this.generateModeledMethodsFromLlm(
msg.packageName, msg.packageName,
msg.externalApiUsages, msg.methods,
msg.modeledMethods, msg.modeledMethods,
); );
break; break;
case "stopGeneratingExternalApiFromLlm": case "stopGeneratingMethodsFromLlm":
await this.autoModeler.stopModeling(msg.packageName); await this.autoModeler.stopModeling(msg.packageName);
break; break;
case "modelDependency": case "modelDependency":
@@ -236,7 +236,7 @@ export class ModelEditorView extends AbstractWebview<
case "hideModeledApis": case "hideModeledApis":
this.hideModeledApis = msg.hideModeledApis; this.hideModeledApis = msg.hideModeledApis;
await this.updateMethodsUsagePanelState( await this.updateMethodsUsagePanelState(
this.externalApiUsages, this.methods,
this.databaseItem, this.databaseItem,
this.hideModeledApis, this.hideModeledApis,
); );
@@ -270,7 +270,7 @@ export class ModelEditorView extends AbstractWebview<
}); });
} }
protected async handleJumpToUsage(method: ExternalApiUsage, usage: Usage) { protected async handleJumpToUsage(method: Method, usage: Usage) {
await this.showMethod(method, usage); await this.showMethod(method, usage);
await showResolvableLocation(usage.url, this.databaseItem, this.app.logger); await showResolvableLocation(usage.url, this.databaseItem, this.app.logger);
} }
@@ -311,15 +311,15 @@ export class ModelEditorView extends AbstractWebview<
if (!queryResult) { if (!queryResult) {
return; return;
} }
this.externalApiUsages = queryResult; this.methods = queryResult;
await this.postMessage({ await this.postMessage({
t: "setExternalApiUsages", t: "setMethods",
externalApiUsages: this.externalApiUsages, methods: this.methods,
}); });
if (this.isMostRecentlyActiveView(this)) { if (this.isMostRecentlyActiveView(this)) {
await this.updateMethodsUsagePanelState( await this.updateMethodsUsagePanelState(
this.externalApiUsages, this.methods,
this.databaseItem, this.databaseItem,
this.hideModeledApis, this.hideModeledApis,
); );
@@ -399,12 +399,12 @@ export class ModelEditorView extends AbstractWebview<
private async generateModeledMethodsFromLlm( private async generateModeledMethodsFromLlm(
packageName: string, packageName: string,
externalApiUsages: ExternalApiUsage[], methods: Method[],
modeledMethods: Record<string, ModeledMethod>, modeledMethods: Record<string, ModeledMethod>,
): Promise<void> { ): Promise<void> {
await this.autoModeler.startModeling( await this.autoModeler.startModeling(
packageName, packageName,
externalApiUsages, methods,
modeledMethods, modeledMethods,
this.mode, this.mode,
); );

View File

@@ -1,5 +1,5 @@
import { outputFile, readFile } from "fs-extra"; import { outputFile, readFile } from "fs-extra";
import { ExternalApiUsage } from "./external-api-usage"; import { Method } from "./method";
import { ModeledMethod } from "./modeled-method"; import { ModeledMethod } from "./modeled-method";
import { Mode } from "./shared/mode"; import { Mode } from "./shared/mode";
import { createDataExtensionYamls, loadDataExtensionYaml } from "./yaml"; import { createDataExtensionYamls, loadDataExtensionYaml } from "./yaml";
@@ -15,7 +15,7 @@ export async function saveModeledMethods(
extensionPack: ExtensionPack, extensionPack: ExtensionPack,
databaseName: string, databaseName: string,
language: string, language: string,
externalApiUsages: ExternalApiUsage[], methods: Method[],
modeledMethods: Record<string, ModeledMethod>, modeledMethods: Record<string, ModeledMethod>,
mode: Mode, mode: Mode,
cliServer: CodeQLCliServer, cliServer: CodeQLCliServer,
@@ -30,7 +30,7 @@ export async function saveModeledMethods(
const yamls = createDataExtensionYamls( const yamls = createDataExtensionYamls(
databaseName, databaseName,
language, language,
externalApiUsages, methods,
modeledMethods, modeledMethods,
existingModeledMethods, existingModeledMethods,
mode, mode,

View File

@@ -1,4 +1,4 @@
import { MethodSignature } from "./external-api-usage"; import { MethodSignature } from "./method";
export type ModeledMethodType = export type ModeledMethodType =
| "none" | "none"

View File

@@ -1,15 +1,14 @@
import { ExternalApiUsage } from "../external-api-usage"; import { Method } from "../method";
export function calculateModeledPercentage( export function calculateModeledPercentage(
externalApiUsages: Array<Pick<ExternalApiUsage, "supported">>, methods: Array<Pick<Method, "supported">>,
): number { ): number {
if (externalApiUsages.length === 0) { if (methods.length === 0) {
return 0; return 0;
} }
const modeledExternalApiUsages = externalApiUsages.filter((m) => m.supported); const modeledMethods = methods.filter((m) => m.supported);
const modeledRatio = const modeledRatio = modeledMethods.length / methods.length;
modeledExternalApiUsages.length / externalApiUsages.length;
return modeledRatio * 100; return modeledRatio * 100;
} }

View File

@@ -1,47 +1,40 @@
import { ExternalApiUsage } from "../external-api-usage"; import { Method } from "../method";
import { Mode } from "./mode"; import { Mode } from "./mode";
import { calculateModeledPercentage } from "./modeled-percentage"; import { calculateModeledPercentage } from "./modeled-percentage";
export function groupMethods( export function groupMethods(
externalApiUsages: ExternalApiUsage[], methods: Method[],
mode: Mode, mode: Mode,
): Record<string, ExternalApiUsage[]> { ): Record<string, Method[]> {
const groupedByLibrary: Record<string, ExternalApiUsage[]> = {}; const groupedByLibrary: Record<string, Method[]> = {};
for (const externalApiUsage of externalApiUsages) { for (const method of methods) {
// Group by package if using framework mode // Group by package if using framework mode
const key = const key = mode === Mode.Framework ? method.packageName : method.library;
mode === Mode.Framework
? externalApiUsage.packageName
: externalApiUsage.library;
groupedByLibrary[key] ??= []; groupedByLibrary[key] ??= [];
groupedByLibrary[key].push(externalApiUsage); groupedByLibrary[key].push(method);
} }
return groupedByLibrary; return groupedByLibrary;
} }
export function sortGroupNames( export function sortGroupNames(methods: Record<string, Method[]>): string[] {
methods: Record<string, ExternalApiUsage[]>,
): string[] {
return Object.keys(methods).sort((a, b) => return Object.keys(methods).sort((a, b) =>
compareGroups(methods[a], a, methods[b], b), compareGroups(methods[a], a, methods[b], b),
); );
} }
export function sortMethods( export function sortMethods(methods: Method[]): Method[] {
externalApiUsages: ExternalApiUsage[], const sortedMethods = [...methods];
): ExternalApiUsage[] { sortedMethods.sort((a, b) => compareMethod(a, b));
const sortedExternalApiUsages = [...externalApiUsages]; return sortedMethods;
sortedExternalApiUsages.sort((a, b) => compareMethod(a, b));
return sortedExternalApiUsages;
} }
function compareGroups( function compareGroups(
a: ExternalApiUsage[], a: Method[],
aName: string, aName: string,
b: ExternalApiUsage[], b: Method[],
bName: string, bName: string,
): number { ): number {
const supportedPercentageA = calculateModeledPercentage(a); const supportedPercentageA = calculateModeledPercentage(a);
@@ -75,7 +68,7 @@ function compareGroups(
return numberOfUsagesB - numberOfUsagesA; return numberOfUsagesB - numberOfUsagesA;
} }
function compareMethod(a: ExternalApiUsage, b: ExternalApiUsage): number { function compareMethod(a: Method, b: Method): number {
// Sort first by supported, putting unmodeled methods first. // Sort first by supported, putting unmodeled methods first.
if (a.supported && !b.supported) { if (a.supported && !b.supported) {
return 1; return 1;

View File

@@ -1,6 +1,6 @@
import Ajv from "ajv"; import Ajv from "ajv";
import { ExternalApiUsage } from "./external-api-usage"; import { Method } from "./method";
import { ModeledMethod, ModeledMethodType } from "./modeled-method"; import { ModeledMethod, ModeledMethodType } from "./modeled-method";
import { import {
ExtensiblePredicateDefinition, ExtensiblePredicateDefinition,
@@ -71,7 +71,7 @@ ${extensions.join("\n")}`;
export function createDataExtensionYamls( export function createDataExtensionYamls(
databaseName: string, databaseName: string,
language: string, language: string,
externalApiUsages: ExternalApiUsage[], methods: Method[],
newModeledMethods: Record<string, ModeledMethod>, newModeledMethods: Record<string, ModeledMethod>,
existingModeledMethods: Record<string, Record<string, ModeledMethod>>, existingModeledMethods: Record<string, Record<string, ModeledMethod>>,
mode: Mode, mode: Mode,
@@ -80,7 +80,7 @@ export function createDataExtensionYamls(
case Mode.Application: case Mode.Application:
return createDataExtensionYamlsForApplicationMode( return createDataExtensionYamlsForApplicationMode(
language, language,
externalApiUsages, methods,
newModeledMethods, newModeledMethods,
existingModeledMethods, existingModeledMethods,
); );
@@ -88,7 +88,7 @@ export function createDataExtensionYamls(
return createDataExtensionYamlsForFrameworkMode( return createDataExtensionYamlsForFrameworkMode(
databaseName, databaseName,
language, language,
externalApiUsages, methods,
newModeledMethods, newModeledMethods,
existingModeledMethods, existingModeledMethods,
); );
@@ -99,7 +99,7 @@ export function createDataExtensionYamls(
export function createDataExtensionYamlsForApplicationMode( export function createDataExtensionYamlsForApplicationMode(
language: string, language: string,
externalApiUsages: ExternalApiUsage[], methods: Method[],
newModeledMethods: Record<string, ModeledMethod>, newModeledMethods: Record<string, ModeledMethod>,
existingModeledMethods: Record<string, Record<string, ModeledMethod>>, existingModeledMethods: Record<string, Record<string, ModeledMethod>>,
): Record<string, string> { ): Record<string, string> {
@@ -111,11 +111,9 @@ export function createDataExtensionYamlsForApplicationMode(
// We only want to generate a yaml file when it's a known external API usage // We only want to generate a yaml file when it's a known external API usage
// and there are new modeled methods for it. This avoids us overwriting other // and there are new modeled methods for it. This avoids us overwriting other
// files that may contain data we don't know about. // files that may contain data we don't know about.
for (const externalApiUsage of externalApiUsages) { for (const method of methods) {
if (externalApiUsage.signature in newModeledMethods) { if (method.signature in newModeledMethods) {
methodsByLibraryFilename[ methodsByLibraryFilename[createFilenameForLibrary(method.library)] = {};
createFilenameForLibrary(externalApiUsage.library)
] = {};
} }
} }
@@ -130,11 +128,11 @@ export function createDataExtensionYamlsForApplicationMode(
// Add the new modeled methods, potentially overwriting existing modeled methods // Add the new modeled methods, potentially overwriting existing modeled methods
// but not removing existing modeled methods that are not in the new set. // but not removing existing modeled methods that are not in the new set.
for (const externalApiUsage of externalApiUsages) { for (const method of methods) {
const method = newModeledMethods[externalApiUsage.signature]; const newMethod = newModeledMethods[method.signature];
if (method) { if (newMethod) {
const filename = createFilenameForLibrary(externalApiUsage.library); const filename = createFilenameForLibrary(method.library);
methodsByLibraryFilename[filename][method.signature] = method; methodsByLibraryFilename[filename][newMethod.signature] = newMethod;
} }
} }
@@ -153,7 +151,7 @@ export function createDataExtensionYamlsForApplicationMode(
export function createDataExtensionYamlsForFrameworkMode( export function createDataExtensionYamlsForFrameworkMode(
databaseName: string, databaseName: string,
language: string, language: string,
externalApiUsages: ExternalApiUsage[], unmodeledMethods: Method[],
newModeledMethods: Record<string, ModeledMethod>, newModeledMethods: Record<string, ModeledMethod>,
existingModeledMethods: Record<string, Record<string, ModeledMethod>>, existingModeledMethods: Record<string, Record<string, ModeledMethod>>,
prefix = "models/", prefix = "models/",
@@ -177,8 +175,8 @@ export function createDataExtensionYamlsForFrameworkMode(
// Add the new modeled methods, potentially overwriting existing modeled methods // Add the new modeled methods, potentially overwriting existing modeled methods
// but not removing existing modeled methods that are not in the new set. // but not removing existing modeled methods that are not in the new set.
for (const externalApiUsage of externalApiUsages) { for (const method of unmodeledMethods) {
const modeledMethod = newModeledMethods[externalApiUsage.signature]; const modeledMethod = newModeledMethods[method.signature];
if (modeledMethod) { if (modeledMethod) {
methods[modeledMethod.signature] = modeledMethod; methods[modeledMethod.signature] = modeledMethod;
} }

View File

@@ -1,18 +0,0 @@
import * as React from "react";
import { Meta, StoryFn } from "@storybook/react";
import { ExternalApiUsageName as ExternalApiUsageNameComponent } from "../../view/model-editor/ExternalApiUsageName";
import { createExternalApiUsage } from "../../../test/factories/data-extension/external-api-factories";
export default {
title: "CodeQL Model Editor/External API Usage Name",
component: ExternalApiUsageNameComponent,
} as Meta<typeof ExternalApiUsageNameComponent>;
const Template: StoryFn<typeof ExternalApiUsageNameComponent> = (args) => (
<ExternalApiUsageNameComponent {...args} />
);
export const ExternalApiUsageName = Template.bind({});
ExternalApiUsageName.args = createExternalApiUsage();

View File

@@ -0,0 +1,18 @@
import * as React from "react";
import { Meta, StoryFn } from "@storybook/react";
import { MethodName as MethodNameComponent } from "../../view/model-editor/MethodName";
import { createMethod } from "../../../test/factories/data-extension/method-factories";
export default {
title: "CodeQL Model Editor/Method Name",
component: MethodNameComponent,
} as Meta<typeof MethodNameComponent>;
const Template: StoryFn<typeof MethodNameComponent> = (args) => (
<MethodNameComponent {...args} />
);
export const MethodName = Template.bind({});
MethodName.args = createMethod();

View File

@@ -3,7 +3,7 @@ import * as React from "react";
import { Meta, StoryFn } from "@storybook/react"; import { Meta, StoryFn } from "@storybook/react";
import { MethodRow as MethodRowComponent } from "../../view/model-editor/MethodRow"; import { MethodRow as MethodRowComponent } from "../../view/model-editor/MethodRow";
import { CallClassification } from "../../model-editor/external-api-usage"; import { CallClassification } from "../../model-editor/method";
export default { export default {
title: "CodeQL Model Editor/Method Row", title: "CodeQL Model Editor/Method Row",
@@ -16,7 +16,7 @@ const Template: StoryFn<typeof MethodRowComponent> = (args) => (
export const MethodRow = Template.bind({}); export const MethodRow = Template.bind({});
MethodRow.args = { MethodRow.args = {
externalApiUsage: { method: {
library: "sql2o-1.6.0.jar", library: "sql2o-1.6.0.jar",
signature: "org.sql2o.Sql2o#open()", signature: "org.sql2o.Sql2o#open()",
packageName: "org.sql2o", packageName: "org.sql2o",

View File

@@ -4,7 +4,7 @@ import { Meta, StoryFn } from "@storybook/react";
import { Mode } from "../../model-editor/shared/mode"; import { Mode } from "../../model-editor/shared/mode";
import { ModelEditor as ModelEditorComponent } from "../../view/model-editor/ModelEditor"; import { ModelEditor as ModelEditorComponent } from "../../view/model-editor/ModelEditor";
import { CallClassification } from "../../model-editor/external-api-usage"; import { CallClassification } from "../../model-editor/method";
export default { export default {
title: "CodeQL Model Editor/CodeQL Model Editor", title: "CodeQL Model Editor/CodeQL Model Editor",
@@ -31,7 +31,7 @@ ModelEditor.args = {
showLlmButton: true, showLlmButton: true,
mode: Mode.Application, mode: Mode.Application,
}, },
initialExternalApiUsages: [ initialMethods: [
{ {
library: "sql2o", library: "sql2o",
libraryVersion: "1.6.0", libraryVersion: "1.6.0",

View File

@@ -4,8 +4,8 @@ import {
ModelingStatus, ModelingStatus,
ModelingStatusIndicator, ModelingStatusIndicator,
} from "../model-editor/ModelingStatusIndicator"; } from "../model-editor/ModelingStatusIndicator";
import { ExternalApiUsage } from "../../model-editor/external-api-usage"; import { Method } from "../../model-editor/method";
import { ExternalApiUsageName } from "../model-editor/ExternalApiUsageName"; import { MethodName } from "../model-editor/MethodName";
const Container = styled.div` const Container = styled.div`
background-color: var(--vscode-peekViewResult-background); background-color: var(--vscode-peekViewResult-background);
@@ -25,18 +25,18 @@ const DependencyContainer = styled.div`
export type MethodModelingProps = { export type MethodModelingProps = {
modelingStatus: ModelingStatus; modelingStatus: ModelingStatus;
externalApiUsage: ExternalApiUsage; method: Method;
}; };
export const MethodModeling = ({ export const MethodModeling = ({
modelingStatus, modelingStatus,
externalApiUsage, method,
}: MethodModelingProps): JSX.Element => { }: MethodModelingProps): JSX.Element => {
return ( return (
<Container> <Container>
<Title>API or Method</Title> <Title>API or Method</Title>
<DependencyContainer> <DependencyContainer>
<ExternalApiUsageName {...externalApiUsage} /> <MethodName {...method} />
<ModelingStatusIndicator status={modelingStatus} /> <ModelingStatusIndicator status={modelingStatus} />
</DependencyContainer> </DependencyContainer>
</Container> </Container>

View File

@@ -2,12 +2,12 @@ import * as React from "react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { MethodModeling } from "./MethodModeling"; import { MethodModeling } from "./MethodModeling";
import { ModelingStatus } from "../model-editor/ModelingStatusIndicator"; import { ModelingStatus } from "../model-editor/ModelingStatusIndicator";
import { ExternalApiUsage } from "../../model-editor/external-api-usage"; import { Method } from "../../model-editor/method";
import { ToMethodModelingMessage } from "../../common/interface-types"; import { ToMethodModelingMessage } from "../../common/interface-types";
import { assertNever } from "../../common/helpers-pure"; import { assertNever } from "../../common/helpers-pure";
export function MethodModelingView(): JSX.Element { export function MethodModelingView(): JSX.Element {
const [method, setMethod] = useState<ExternalApiUsage | undefined>(undefined); const [method, setMethod] = useState<Method | undefined>(undefined);
useEffect(() => { useEffect(() => {
const listener = (evt: MessageEvent) => { const listener = (evt: MessageEvent) => {
@@ -36,7 +36,5 @@ export function MethodModelingView(): JSX.Element {
} }
const modelingStatus: ModelingStatus = "saved"; const modelingStatus: ModelingStatus = "saved";
return ( return <MethodModeling modelingStatus={modelingStatus} method={method} />;
<MethodModeling modelingStatus={modelingStatus} externalApiUsage={method} />
);
} }

View File

@@ -1,7 +1,7 @@
import * as React from "react"; import * as React from "react";
import { render as reactRender, screen } from "@testing-library/react"; import { render as reactRender, screen } from "@testing-library/react";
import { MethodModeling, MethodModelingProps } from "../MethodModeling"; import { MethodModeling, MethodModelingProps } from "../MethodModeling";
import { createExternalApiUsage } from "../../../../test/factories/data-extension/external-api-factories"; import { createMethod } from "../../../../test/factories/data-extension/method-factories";
describe(MethodModeling.name, () => { describe(MethodModeling.name, () => {
const render = (props: MethodModelingProps) => const render = (props: MethodModelingProps) =>
@@ -10,7 +10,7 @@ describe(MethodModeling.name, () => {
it("renders method modeling panel", () => { it("renders method modeling panel", () => {
render({ render({
modelingStatus: "saved", modelingStatus: "saved",
externalApiUsage: createExternalApiUsage(), method: createMethod(),
}); });
expect(screen.getByText("API or Method")).toBeInTheDocument(); expect(screen.getByText("API or Method")).toBeInTheDocument();

View File

@@ -1,19 +0,0 @@
import * as React from "react";
import { styled } from "styled-components";
import { ExternalApiUsage } from "../../model-editor/external-api-usage";
const Name = styled.span`
font-family: var(--vscode-editor-font-family);
`;
export const ExternalApiUsageName = (
externalApiUsage: ExternalApiUsage,
): JSX.Element => {
return (
<Name>
{externalApiUsage.packageName && <>{externalApiUsage.packageName}.</>}
{externalApiUsage.typeName}.{externalApiUsage.methodName}
{externalApiUsage.methodParameters}
</Name>
);
};

View File

@@ -1,7 +1,7 @@
import * as React from "react"; import * as React from "react";
import { useCallback, useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
import { styled } from "styled-components"; import { styled } from "styled-components";
import { ExternalApiUsage } from "../../model-editor/external-api-usage"; import { Method } from "../../model-editor/method";
import { ModeledMethod } from "../../model-editor/modeled-method"; import { ModeledMethod } from "../../model-editor/modeled-method";
import { ModeledMethodDataGrid } from "./ModeledMethodDataGrid"; import { ModeledMethodDataGrid } from "./ModeledMethodDataGrid";
import { calculateModeledPercentage } from "../../model-editor/shared/modeled-percentage"; import { calculateModeledPercentage } from "../../model-editor/shared/modeled-percentage";
@@ -70,7 +70,7 @@ const ButtonsContainer = styled.div`
type Props = { type Props = {
title: string; title: string;
libraryVersion?: string; libraryVersion?: string;
externalApiUsages: ExternalApiUsage[]; methods: Method[];
modeledMethods: Record<string, ModeledMethod>; modeledMethods: Record<string, ModeledMethod>;
modifiedSignatures: Set<string>; modifiedSignatures: Set<string>;
inProgressMethods: InProgressMethods; inProgressMethods: InProgressMethods;
@@ -78,16 +78,16 @@ type Props = {
hideModeledApis: boolean; hideModeledApis: boolean;
onChange: ( onChange: (
modelName: string, modelName: string,
externalApiUsage: ExternalApiUsage, method: Method,
modeledMethod: ModeledMethod, modeledMethod: ModeledMethod,
) => void; ) => void;
onSaveModelClick: ( onSaveModelClick: (
externalApiUsages: ExternalApiUsage[], methods: Method[],
modeledMethods: Record<string, ModeledMethod>, modeledMethods: Record<string, ModeledMethod>,
) => void; ) => void;
onGenerateFromLlmClick: ( onGenerateFromLlmClick: (
dependencyName: string, dependencyName: string,
externalApiUsages: ExternalApiUsage[], methods: Method[],
modeledMethods: Record<string, ModeledMethod>, modeledMethods: Record<string, ModeledMethod>,
) => void; ) => void;
onStopGenerateFromLlmClick: (dependencyName: string) => void; onStopGenerateFromLlmClick: (dependencyName: string) => void;
@@ -98,7 +98,7 @@ type Props = {
export const LibraryRow = ({ export const LibraryRow = ({
title, title,
libraryVersion, libraryVersion,
externalApiUsages, methods,
modeledMethods, modeledMethods,
modifiedSignatures, modifiedSignatures,
inProgressMethods, inProgressMethods,
@@ -112,8 +112,8 @@ export const LibraryRow = ({
onModelDependencyClick, onModelDependencyClick,
}: Props) => { }: Props) => {
const modeledPercentage = useMemo(() => { const modeledPercentage = useMemo(() => {
return calculateModeledPercentage(externalApiUsages); return calculateModeledPercentage(methods);
}, [externalApiUsages]); }, [methods]);
const [isExpanded, setExpanded] = useState(false); const [isExpanded, setExpanded] = useState(false);
@@ -123,11 +123,11 @@ export const LibraryRow = ({
const handleModelWithAI = useCallback( const handleModelWithAI = useCallback(
async (e: React.MouseEvent) => { async (e: React.MouseEvent) => {
onGenerateFromLlmClick(title, externalApiUsages, modeledMethods); onGenerateFromLlmClick(title, methods, modeledMethods);
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
}, },
[title, externalApiUsages, modeledMethods, onGenerateFromLlmClick], [title, methods, modeledMethods, onGenerateFromLlmClick],
); );
const handleStopModelWithAI = useCallback( const handleStopModelWithAI = useCallback(
@@ -159,31 +159,29 @@ export const LibraryRow = ({
const handleSave = useCallback( const handleSave = useCallback(
async (e: React.MouseEvent) => { async (e: React.MouseEvent) => {
onSaveModelClick(externalApiUsages, modeledMethods); onSaveModelClick(methods, modeledMethods);
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
}, },
[externalApiUsages, modeledMethods, onSaveModelClick], [methods, modeledMethods, onSaveModelClick],
); );
const onChangeWithModelName = useCallback( const onChangeWithModelName = useCallback(
(externalApiUsage: ExternalApiUsage, modeledMethod: ModeledMethod) => { (method: Method, modeledMethod: ModeledMethod) => {
onChange(title, externalApiUsage, modeledMethod); onChange(title, method, modeledMethod);
}, },
[onChange, title], [onChange, title],
); );
const hasUnsavedChanges = useMemo(() => { const hasUnsavedChanges = useMemo(() => {
return externalApiUsages.some((externalApiUsage) => return methods.some((method) => modifiedSignatures.has(method.signature));
modifiedSignatures.has(externalApiUsage.signature), }, [methods, modifiedSignatures]);
);
}, [externalApiUsages, modifiedSignatures]);
const canStopAutoModeling = useMemo(() => { const canStopAutoModeling = useMemo(() => {
return externalApiUsages.some((externalApiUsage) => return methods.some((method) =>
inProgressMethods.hasMethod(title, externalApiUsage.signature), inProgressMethods.hasMethod(title, method.signature),
); );
}, [externalApiUsages, title, inProgressMethods]); }, [methods, title, inProgressMethods]);
return ( return (
<LibraryContainer> <LibraryContainer>
@@ -233,7 +231,7 @@ export const LibraryRow = ({
<SectionDivider /> <SectionDivider />
<ModeledMethodDataGrid <ModeledMethodDataGrid
packageName={title} packageName={title}
externalApiUsages={externalApiUsages} methods={methods}
modeledMethods={modeledMethods} modeledMethods={modeledMethods}
modifiedSignatures={modifiedSignatures} modifiedSignatures={modifiedSignatures}
inProgressMethods={inProgressMethods} inProgressMethods={inProgressMethods}

View File

@@ -1,9 +1,6 @@
import * as React from "react"; import * as React from "react";
import { useMemo } from "react"; import { useMemo } from "react";
import { import { CallClassification, Method } from "../../model-editor/method";
CallClassification,
ExternalApiUsage,
} from "../../model-editor/external-api-usage";
import { VSCodeTag } from "@vscode/webview-ui-toolkit/react"; import { VSCodeTag } from "@vscode/webview-ui-toolkit/react";
import { styled } from "styled-components"; import { styled } from "styled-components";
@@ -19,18 +16,18 @@ const ClassificationTag = styled(VSCodeTag)`
`; `;
type Props = { type Props = {
externalApiUsage: ExternalApiUsage; method: Method;
}; };
export const MethodClassifications = ({ externalApiUsage }: Props) => { export const MethodClassifications = ({ method }: Props) => {
const allUsageClassifications = useMemo( const allUsageClassifications = useMemo(
() => () =>
new Set( new Set(
externalApiUsage.usages.map((usage) => { method.usages.map((usage) => {
return usage.classification; return usage.classification;
}), }),
), ),
[externalApiUsage.usages], [method.usages],
); );
const inSource = allUsageClassifications.has(CallClassification.Source); const inSource = allUsageClassifications.has(CallClassification.Source);

View File

@@ -0,0 +1,17 @@
import * as React from "react";
import { styled } from "styled-components";
import { Method } from "../../model-editor/method";
const Name = styled.span`
font-family: var(--vscode-editor-font-family);
`;
export const MethodName = (method: Method): JSX.Element => {
return (
<Name>
{method.packageName && <>{method.packageName}.</>}
{method.typeName}.{method.methodName}
{method.methodParameters}
</Name>
);
};

View File

@@ -9,7 +9,7 @@ import { ChangeEvent, useCallback, useMemo } from "react";
import { styled } from "styled-components"; import { styled } from "styled-components";
import { vscode } from "../vscode-api"; import { vscode } from "../vscode-api";
import { ExternalApiUsage } from "../../model-editor/external-api-usage"; import { Method } from "../../model-editor/method";
import { import {
ModeledMethod, ModeledMethod,
ModeledMethodType, ModeledMethodType,
@@ -25,7 +25,7 @@ import {
ModelingStatusIndicator, ModelingStatusIndicator,
} from "./ModelingStatusIndicator"; } from "./ModelingStatusIndicator";
import { InProgressDropdown } from "./InProgressDropdown"; import { InProgressDropdown } from "./InProgressDropdown";
import { ExternalApiUsageName } from "./ExternalApiUsageName"; import { MethodName } from "./MethodName";
const ApiOrMethodCell = styled(VSCodeDataGridCell)` const ApiOrMethodCell = styled(VSCodeDataGridCell)`
display: flex; display: flex;
@@ -61,24 +61,20 @@ const modelTypeOptions: Array<{ value: ModeledMethodType; label: string }> = [
]; ];
type Props = { type Props = {
externalApiUsage: ExternalApiUsage; method: Method;
modeledMethod: ModeledMethod | undefined; modeledMethod: ModeledMethod | undefined;
methodIsUnsaved: boolean; methodIsUnsaved: boolean;
modelingInProgress: boolean; modelingInProgress: boolean;
mode: Mode; mode: Mode;
hideModeledApis: boolean; hideModeledApis: boolean;
onChange: ( onChange: (method: Method, modeledMethod: ModeledMethod) => void;
externalApiUsage: ExternalApiUsage,
modeledMethod: ModeledMethod,
) => void;
}; };
export const MethodRow = (props: Props) => { export const MethodRow = (props: Props) => {
const { externalApiUsage, modeledMethod, methodIsUnsaved, hideModeledApis } = const { method, modeledMethod, methodIsUnsaved, hideModeledApis } = props;
props;
const methodCanBeModeled = const methodCanBeModeled =
!externalApiUsage.supported || !method.supported ||
(modeledMethod && modeledMethod?.type !== "none") || (modeledMethod && modeledMethod?.type !== "none") ||
methodIsUnsaved; methodIsUnsaved;
@@ -92,17 +88,16 @@ export const MethodRow = (props: Props) => {
}; };
function ModelableMethodRow(props: Props) { function ModelableMethodRow(props: Props) {
const { externalApiUsage, modeledMethod, methodIsUnsaved, mode, onChange } = const { method, modeledMethod, methodIsUnsaved, mode, onChange } = props;
props;
const argumentsList = useMemo(() => { const argumentsList = useMemo(() => {
if (externalApiUsage.methodParameters === "()") { if (method.methodParameters === "()") {
return []; return [];
} }
return externalApiUsage.methodParameters return method.methodParameters
.substring(1, externalApiUsage.methodParameters.length - 1) .substring(1, method.methodParameters.length - 1)
.split(","); .split(",");
}, [externalApiUsage.methodParameters]); }, [method.methodParameters]);
const handleTypeInput = useCallback( const handleTypeInput = useCallback(
(e: ChangeEvent<HTMLSelectElement>) => { (e: ChangeEvent<HTMLSelectElement>) => {
@@ -113,7 +108,7 @@ function ModelableMethodRow(props: Props) {
newProvenance = "ai-manual"; newProvenance = "ai-manual";
} }
onChange(externalApiUsage, { onChange(method, {
// If there are no arguments, we will default to "Argument[this]" // If there are no arguments, we will default to "Argument[this]"
input: argumentsList.length === 0 ? "Argument[this]" : "Argument[0]", input: argumentsList.length === 0 ? "Argument[this]" : "Argument[0]",
output: "ReturnType", output: "ReturnType",
@@ -121,14 +116,14 @@ function ModelableMethodRow(props: Props) {
...modeledMethod, ...modeledMethod,
type: e.target.value as ModeledMethodType, type: e.target.value as ModeledMethodType,
provenance: newProvenance, provenance: newProvenance,
signature: externalApiUsage.signature, signature: method.signature,
packageName: externalApiUsage.packageName, packageName: method.packageName,
typeName: externalApiUsage.typeName, typeName: method.typeName,
methodName: externalApiUsage.methodName, methodName: method.methodName,
methodParameters: externalApiUsage.methodParameters, methodParameters: method.methodParameters,
}); });
}, },
[onChange, externalApiUsage, modeledMethod, argumentsList], [onChange, method, modeledMethod, argumentsList],
); );
const handleInputInput = useCallback( const handleInputInput = useCallback(
(e: ChangeEvent<HTMLSelectElement>) => { (e: ChangeEvent<HTMLSelectElement>) => {
@@ -138,12 +133,12 @@ function ModelableMethodRow(props: Props) {
const target = e.target as HTMLSelectElement; const target = e.target as HTMLSelectElement;
onChange(externalApiUsage, { onChange(method, {
...modeledMethod, ...modeledMethod,
input: target.value as ModeledMethod["input"], input: target.value as ModeledMethod["input"],
}); });
}, },
[onChange, externalApiUsage, modeledMethod], [onChange, method, modeledMethod],
); );
const handleOutputInput = useCallback( const handleOutputInput = useCallback(
(e: ChangeEvent<HTMLSelectElement>) => { (e: ChangeEvent<HTMLSelectElement>) => {
@@ -153,12 +148,12 @@ function ModelableMethodRow(props: Props) {
const target = e.target as HTMLSelectElement; const target = e.target as HTMLSelectElement;
onChange(externalApiUsage, { onChange(method, {
...modeledMethod, ...modeledMethod,
output: target.value as ModeledMethod["output"], output: target.value as ModeledMethod["output"],
}); });
}, },
[onChange, externalApiUsage, modeledMethod], [onChange, method, modeledMethod],
); );
const handleKindChange = useCallback( const handleKindChange = useCallback(
(kind: string) => { (kind: string) => {
@@ -166,17 +161,17 @@ function ModelableMethodRow(props: Props) {
return; return;
} }
onChange(externalApiUsage, { onChange(method, {
...modeledMethod, ...modeledMethod,
kind, kind,
}); });
}, },
[onChange, externalApiUsage, modeledMethod], [onChange, method, modeledMethod],
); );
const jumpToUsage = useCallback( const jumpToUsage = useCallback(
() => sendJumpToUsageMessage(externalApiUsage), () => sendJumpToUsageMessage(method),
[externalApiUsage], [method],
); );
const inputOptions = useMemo( const inputOptions = useMemo(
@@ -218,11 +213,11 @@ function ModelableMethodRow(props: Props) {
<VSCodeDataGridRow> <VSCodeDataGridRow>
<ApiOrMethodCell gridColumn={1}> <ApiOrMethodCell gridColumn={1}>
<ModelingStatusIndicator status={modelingStatus} /> <ModelingStatusIndicator status={modelingStatus} />
<MethodClassifications externalApiUsage={externalApiUsage} /> <MethodClassifications method={method} />
<ExternalApiUsageName {...props.externalApiUsage} /> <MethodName {...props.method} />
{mode === Mode.Application && ( {mode === Mode.Application && (
<UsagesButton onClick={jumpToUsage}> <UsagesButton onClick={jumpToUsage}>
{externalApiUsage.usages.length} {method.usages.length}
</UsagesButton> </UsagesButton>
)} )}
<ViewLink onClick={jumpToUsage}>View</ViewLink> <ViewLink onClick={jumpToUsage}>View</ViewLink>
@@ -284,25 +279,25 @@ function ModelableMethodRow(props: Props) {
} }
function UnmodelableMethodRow(props: Props) { function UnmodelableMethodRow(props: Props) {
const { externalApiUsage, mode } = props; const { method, mode } = props;
const jumpToUsage = useCallback( const jumpToUsage = useCallback(
() => sendJumpToUsageMessage(externalApiUsage), () => sendJumpToUsageMessage(method),
[externalApiUsage], [method],
); );
return ( return (
<VSCodeDataGridRow> <VSCodeDataGridRow>
<ApiOrMethodCell gridColumn={1}> <ApiOrMethodCell gridColumn={1}>
<ModelingStatusIndicator status="saved" /> <ModelingStatusIndicator status="saved" />
<ExternalApiUsageName {...props.externalApiUsage} /> <MethodName {...props.method} />
{mode === Mode.Application && ( {mode === Mode.Application && (
<UsagesButton onClick={jumpToUsage}> <UsagesButton onClick={jumpToUsage}>
{externalApiUsage.usages.length} {method.usages.length}
</UsagesButton> </UsagesButton>
)} )}
<ViewLink onClick={jumpToUsage}>View</ViewLink> <ViewLink onClick={jumpToUsage}>View</ViewLink>
<MethodClassifications externalApiUsage={externalApiUsage} /> <MethodClassifications method={method} />
</ApiOrMethodCell> </ApiOrMethodCell>
<VSCodeDataGridCell gridColumn="span 4"> <VSCodeDataGridCell gridColumn="span 4">
Method already modeled Method already modeled
@@ -311,12 +306,12 @@ function UnmodelableMethodRow(props: Props) {
); );
} }
function sendJumpToUsageMessage(externalApiUsage: ExternalApiUsage) { function sendJumpToUsageMessage(method: Method) {
vscode.postMessage({ vscode.postMessage({
t: "jumpToUsage", t: "jumpToUsage",
method: externalApiUsage, method,
// In framework mode, the first and only usage is the definition of the method // In framework mode, the first and only usage is the definition of the method
usage: externalApiUsage.usages[0], usage: method.usages[0],
}); });
} }

View File

@@ -7,7 +7,7 @@ import {
VSCodeTag, VSCodeTag,
} from "@vscode/webview-ui-toolkit/react"; } from "@vscode/webview-ui-toolkit/react";
import { styled } from "styled-components"; import { styled } from "styled-components";
import { ExternalApiUsage } from "../../model-editor/external-api-usage"; import { Method } from "../../model-editor/method";
import { ModeledMethod } from "../../model-editor/modeled-method"; import { ModeledMethod } from "../../model-editor/modeled-method";
import { assertNever } from "../../common/helpers-pure"; import { assertNever } from "../../common/helpers-pure";
import { vscode } from "../vscode-api"; import { vscode } from "../vscode-api";
@@ -73,14 +73,14 @@ const ButtonsContainer = styled.div`
type Props = { type Props = {
initialViewState?: ModelEditorViewState; initialViewState?: ModelEditorViewState;
initialExternalApiUsages?: ExternalApiUsage[]; initialMethods?: Method[];
initialModeledMethods?: Record<string, ModeledMethod>; initialModeledMethods?: Record<string, ModeledMethod>;
initialHideModeledApis?: boolean; initialHideModeledApis?: boolean;
}; };
export function ModelEditor({ export function ModelEditor({
initialViewState, initialViewState,
initialExternalApiUsages = [], initialMethods = [],
initialModeledMethods = {}, initialModeledMethods = {},
initialHideModeledApis = INITIAL_HIDE_MODELED_APIS_VALUE, initialHideModeledApis = INITIAL_HIDE_MODELED_APIS_VALUE,
}: Props): JSX.Element { }: Props): JSX.Element {
@@ -88,9 +88,7 @@ export function ModelEditor({
initialViewState, initialViewState,
); );
const [externalApiUsages, setExternalApiUsages] = useState< const [methods, setMethods] = useState<Method[]>(initialMethods);
ExternalApiUsage[]
>(initialExternalApiUsages);
const [modifiedSignatures, setModifiedSignatures] = useState<Set<string>>( const [modifiedSignatures, setModifiedSignatures] = useState<Set<string>>(
new Set(), new Set(),
); );
@@ -122,8 +120,8 @@ export function ModelEditor({
case "setModelEditorViewState": case "setModelEditorViewState":
setViewState(msg.viewState); setViewState(msg.viewState);
break; break;
case "setExternalApiUsages": case "setMethods":
setExternalApiUsages(msg.externalApiUsages); setMethods(msg.methods);
break; break;
case "loadModeledMethods": case "loadModeledMethods":
setModeledMethods((oldModeledMethods) => { setModeledMethods((oldModeledMethods) => {
@@ -177,12 +175,12 @@ export function ModelEditor({
}, []); }, []);
const modeledPercentage = useMemo( const modeledPercentage = useMemo(
() => calculateModeledPercentage(externalApiUsages), () => calculateModeledPercentage(methods),
[externalApiUsages], [methods],
); );
const onChange = useCallback( const onChange = useCallback(
(modelName: string, method: ExternalApiUsage, model: ModeledMethod) => { (modelName: string, method: Method, model: ModeledMethod) => {
setModeledMethods((oldModeledMethods) => ({ setModeledMethods((oldModeledMethods) => ({
...oldModeledMethods, ...oldModeledMethods,
[method.signature]: model, [method.signature]: model,
@@ -197,33 +195,30 @@ export function ModelEditor({
const onRefreshClick = useCallback(() => { const onRefreshClick = useCallback(() => {
vscode.postMessage({ vscode.postMessage({
t: "refreshExternalApiUsages", t: "refreshMethods",
}); });
}, []); }, []);
const onSaveAllClick = useCallback(() => { const onSaveAllClick = useCallback(() => {
vscode.postMessage({ vscode.postMessage({
t: "saveModeledMethods", t: "saveModeledMethods",
externalApiUsages, methods,
modeledMethods, modeledMethods,
}); });
setModifiedSignatures(new Set()); setModifiedSignatures(new Set());
}, [externalApiUsages, modeledMethods]); }, [methods, modeledMethods]);
const onSaveModelClick = useCallback( const onSaveModelClick = useCallback(
( (methods: Method[], modeledMethods: Record<string, ModeledMethod>) => {
externalApiUsages: ExternalApiUsage[],
modeledMethods: Record<string, ModeledMethod>,
) => {
vscode.postMessage({ vscode.postMessage({
t: "saveModeledMethods", t: "saveModeledMethods",
externalApiUsages, methods,
modeledMethods, modeledMethods,
}); });
setModifiedSignatures((oldModifiedSignatures) => { setModifiedSignatures((oldModifiedSignatures) => {
const newModifiedSignatures = new Set([...oldModifiedSignatures]); const newModifiedSignatures = new Set([...oldModifiedSignatures]);
for (const externalApiUsage of externalApiUsages) { for (const method of methods) {
newModifiedSignatures.delete(externalApiUsage.signature); newModifiedSignatures.delete(method.signature);
} }
return newModifiedSignatures; return newModifiedSignatures;
}); });
@@ -233,7 +228,7 @@ export function ModelEditor({
const onGenerateFromSourceClick = useCallback(() => { const onGenerateFromSourceClick = useCallback(() => {
vscode.postMessage({ vscode.postMessage({
t: "generateExternalApi", t: "generateMethod",
}); });
}, []); }, []);
@@ -246,13 +241,13 @@ export function ModelEditor({
const onGenerateFromLlmClick = useCallback( const onGenerateFromLlmClick = useCallback(
( (
packageName: string, packageName: string,
externalApiUsages: ExternalApiUsage[], methods: Method[],
modeledMethods: Record<string, ModeledMethod>, modeledMethods: Record<string, ModeledMethod>,
) => { ) => {
vscode.postMessage({ vscode.postMessage({
t: "generateExternalApiFromLlm", t: "generateMethodsFromLlm",
packageName, packageName,
externalApiUsages, methods,
modeledMethods, modeledMethods,
}); });
}, },
@@ -261,7 +256,7 @@ export function ModelEditor({
const onStopGenerateFromLlmClick = useCallback((packageName: string) => { const onStopGenerateFromLlmClick = useCallback((packageName: string) => {
vscode.postMessage({ vscode.postMessage({
t: "stopGeneratingExternalApiFromLlm", t: "stopGeneratingMethodsFromLlm",
packageName, packageName,
}); });
}, []); }, []);
@@ -292,7 +287,7 @@ export function ModelEditor({
setHideModeledApis((oldHideModeledApis) => !oldHideModeledApis); setHideModeledApis((oldHideModeledApis) => !oldHideModeledApis);
}, []); }, []);
if (viewState === undefined || externalApiUsages.length === 0) { if (viewState === undefined || methods.length === 0) {
return <LoadingContainer>Loading...</LoadingContainer>; return <LoadingContainer>Loading...</LoadingContainer>;
} }
@@ -357,7 +352,7 @@ export function ModelEditor({
)} )}
</ButtonsContainer> </ButtonsContainer>
<ModeledMethodsList <ModeledMethodsList
externalApiUsages={externalApiUsages} methods={methods}
modeledMethods={modeledMethods} modeledMethods={modeledMethods}
modifiedSignatures={modifiedSignatures} modifiedSignatures={modifiedSignatures}
inProgressMethods={inProgressMethods} inProgressMethods={inProgressMethods}

View File

@@ -5,7 +5,7 @@ import {
VSCodeDataGridRow, VSCodeDataGridRow,
} from "@vscode/webview-ui-toolkit/react"; } from "@vscode/webview-ui-toolkit/react";
import { MethodRow } from "./MethodRow"; import { MethodRow } from "./MethodRow";
import { ExternalApiUsage } from "../../model-editor/external-api-usage"; import { Method } from "../../model-editor/method";
import { ModeledMethod } from "../../model-editor/modeled-method"; import { ModeledMethod } from "../../model-editor/modeled-method";
import { useMemo } from "react"; import { useMemo } from "react";
import { Mode } from "../../model-editor/shared/mode"; import { Mode } from "../../model-editor/shared/mode";
@@ -14,21 +14,18 @@ import { InProgressMethods } from "../../model-editor/shared/in-progress-methods
type Props = { type Props = {
packageName: string; packageName: string;
externalApiUsages: ExternalApiUsage[]; methods: Method[];
modeledMethods: Record<string, ModeledMethod>; modeledMethods: Record<string, ModeledMethod>;
modifiedSignatures: Set<string>; modifiedSignatures: Set<string>;
inProgressMethods: InProgressMethods; inProgressMethods: InProgressMethods;
mode: Mode; mode: Mode;
hideModeledApis: boolean; hideModeledApis: boolean;
onChange: ( onChange: (method: Method, modeledMethod: ModeledMethod) => void;
externalApiUsage: ExternalApiUsage,
modeledMethod: ModeledMethod,
) => void;
}; };
export const ModeledMethodDataGrid = ({ export const ModeledMethodDataGrid = ({
packageName, packageName,
externalApiUsages, methods,
modeledMethods, modeledMethods,
modifiedSignatures, modifiedSignatures,
inProgressMethods, inProgressMethods,
@@ -36,10 +33,7 @@ export const ModeledMethodDataGrid = ({
hideModeledApis, hideModeledApis,
onChange, onChange,
}: Props) => { }: Props) => {
const sortedExternalApiUsages = useMemo( const sortedMethods = useMemo(() => sortMethods(methods), [methods]);
() => sortMethods(externalApiUsages),
[externalApiUsages],
);
return ( return (
<VSCodeDataGrid gridTemplateColumns="0.5fr 0.125fr 0.125fr 0.125fr 0.125fr"> <VSCodeDataGrid gridTemplateColumns="0.5fr 0.125fr 0.125fr 0.125fr 0.125fr">
@@ -60,15 +54,15 @@ export const ModeledMethodDataGrid = ({
Kind Kind
</VSCodeDataGridCell> </VSCodeDataGridCell>
</VSCodeDataGridRow> </VSCodeDataGridRow>
{sortedExternalApiUsages.map((externalApiUsage) => ( {sortedMethods.map((method) => (
<MethodRow <MethodRow
key={externalApiUsage.signature} key={method.signature}
externalApiUsage={externalApiUsage} method={method}
modeledMethod={modeledMethods[externalApiUsage.signature]} modeledMethod={modeledMethods[method.signature]}
methodIsUnsaved={modifiedSignatures.has(externalApiUsage.signature)} methodIsUnsaved={modifiedSignatures.has(method.signature)}
modelingInProgress={inProgressMethods.hasMethod( modelingInProgress={inProgressMethods.hasMethod(
packageName, packageName,
externalApiUsage.signature, method.signature,
)} )}
mode={mode} mode={mode}
hideModeledApis={hideModeledApis} hideModeledApis={hideModeledApis}

View File

@@ -1,6 +1,6 @@
import * as React from "react"; import * as React from "react";
import { useMemo } from "react"; import { useMemo } from "react";
import { ExternalApiUsage } from "../../model-editor/external-api-usage"; import { Method } from "../../model-editor/method";
import { ModeledMethod } from "../../model-editor/modeled-method"; import { ModeledMethod } from "../../model-editor/modeled-method";
import { LibraryRow } from "./LibraryRow"; import { LibraryRow } from "./LibraryRow";
import { Mode } from "../../model-editor/shared/mode"; import { Mode } from "../../model-editor/shared/mode";
@@ -12,7 +12,7 @@ import { ModelEditorViewState } from "../../model-editor/shared/view-state";
import { InProgressMethods } from "../../model-editor/shared/in-progress-methods"; import { InProgressMethods } from "../../model-editor/shared/in-progress-methods";
type Props = { type Props = {
externalApiUsages: ExternalApiUsage[]; methods: Method[];
modeledMethods: Record<string, ModeledMethod>; modeledMethods: Record<string, ModeledMethod>;
modifiedSignatures: Set<string>; modifiedSignatures: Set<string>;
inProgressMethods: InProgressMethods; inProgressMethods: InProgressMethods;
@@ -20,16 +20,16 @@ type Props = {
hideModeledApis: boolean; hideModeledApis: boolean;
onChange: ( onChange: (
modelName: string, modelName: string,
externalApiUsage: ExternalApiUsage, method: Method,
modeledMethod: ModeledMethod, modeledMethod: ModeledMethod,
) => void; ) => void;
onSaveModelClick: ( onSaveModelClick: (
externalApiUsages: ExternalApiUsage[], methods: Method[],
modeledMethods: Record<string, ModeledMethod>, modeledMethods: Record<string, ModeledMethod>,
) => void; ) => void;
onGenerateFromLlmClick: ( onGenerateFromLlmClick: (
packageName: string, packageName: string,
externalApiUsages: ExternalApiUsage[], methods: Method[],
modeledMethods: Record<string, ModeledMethod>, modeledMethods: Record<string, ModeledMethod>,
) => void; ) => void;
onStopGenerateFromLlmClick: (packageName: string) => void; onStopGenerateFromLlmClick: (packageName: string) => void;
@@ -42,7 +42,7 @@ const libraryNameOverrides: Record<string, string> = {
}; };
export const ModeledMethodsList = ({ export const ModeledMethodsList = ({
externalApiUsages, methods,
modeledMethods, modeledMethods,
modifiedSignatures, modifiedSignatures,
inProgressMethods, inProgressMethods,
@@ -56,8 +56,8 @@ export const ModeledMethodsList = ({
onModelDependencyClick, onModelDependencyClick,
}: Props) => { }: Props) => {
const grouped = useMemo( const grouped = useMemo(
() => groupMethods(externalApiUsages, viewState.mode), () => groupMethods(methods, viewState.mode),
[externalApiUsages, viewState.mode], [methods, viewState.mode],
); );
const libraryVersions = useMemo(() => { const libraryVersions = useMemo(() => {
@@ -67,8 +67,8 @@ export const ModeledMethodsList = ({
const libraryVersions: Record<string, string> = {}; const libraryVersions: Record<string, string> = {};
for (const externalApiUsage of externalApiUsages) { for (const method of methods) {
const { library, libraryVersion } = externalApiUsage; const { library, libraryVersion } = method;
if (library && libraryVersion) { if (library && libraryVersion) {
libraryVersions[library] = libraryVersion; libraryVersions[library] = libraryVersion;
@@ -76,7 +76,7 @@ export const ModeledMethodsList = ({
} }
return libraryVersions; return libraryVersions;
}, [externalApiUsages, viewState.mode]); }, [methods, viewState.mode]);
const sortedGroupNames = useMemo(() => sortGroupNames(grouped), [grouped]); const sortedGroupNames = useMemo(() => sortGroupNames(grouped), [grouped]);
@@ -87,7 +87,7 @@ export const ModeledMethodsList = ({
key={libraryName} key={libraryName}
title={libraryNameOverrides[libraryName] ?? libraryName} title={libraryNameOverrides[libraryName] ?? libraryName}
libraryVersion={libraryVersions[libraryName]} libraryVersion={libraryVersions[libraryName]}
externalApiUsages={grouped[libraryName]} methods={grouped[libraryName]}
modeledMethods={modeledMethods} modeledMethods={modeledMethods}
modifiedSignatures={modifiedSignatures} modifiedSignatures={modifiedSignatures}
inProgressMethods={inProgressMethods} inProgressMethods={inProgressMethods}

View File

@@ -1,18 +0,0 @@
import * as React from "react";
import { render as reactRender, screen } from "@testing-library/react";
import { ExternalApiUsageName } from "../ExternalApiUsageName";
import { ExternalApiUsage } from "../../../model-editor/external-api-usage";
import { createExternalApiUsage } from "../../../../test/factories/data-extension/external-api-factories";
describe(ExternalApiUsageName.name, () => {
const render = (props: ExternalApiUsage) =>
reactRender(<ExternalApiUsageName {...props} />);
it("renders method name", () => {
const apiUsage = createExternalApiUsage();
render(apiUsage);
const name = `${apiUsage.packageName}.${apiUsage.typeName}.${apiUsage.methodName}${apiUsage.methodParameters}`;
expect(screen.getByText(name)).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,17 @@
import * as React from "react";
import { render as reactRender, screen } from "@testing-library/react";
import { MethodName } from "../MethodName";
import { Method } from "../../../model-editor/method";
import { createMethod } from "../../../../test/factories/data-extension/method-factories";
describe(MethodName.name, () => {
const render = (props: Method) => reactRender(<MethodName {...props} />);
it("renders method name", () => {
const method = createMethod();
render(method);
const name = `${method.packageName}.${method.typeName}.${method.methodName}${method.methodParameters}`;
expect(screen.getByText(name)).toBeInTheDocument();
});
});

View File

@@ -1,12 +1,12 @@
import { import {
Usage, Usage,
ExternalApiUsage, Method,
CallClassification, CallClassification,
} from "../../../src/model-editor/external-api-usage"; } from "../../../src/model-editor/method";
import { ModeledMethodType } from "../../../src/model-editor/modeled-method"; import { ModeledMethodType } from "../../../src/model-editor/modeled-method";
import { ResolvableLocationValue } from "../../../src/common/bqrs-cli-types"; import { ResolvableLocationValue } from "../../../src/common/bqrs-cli-types";
export function createExternalApiUsage({ export function createMethod({
library = "sql2o-1.6.0.jar", library = "sql2o-1.6.0.jar",
supported = true, supported = true,
supportedType = "summary" as ModeledMethodType, supportedType = "summary" as ModeledMethodType,
@@ -26,7 +26,7 @@ export function createExternalApiUsage({
typeName?: string; typeName?: string;
methodName?: string; methodName?: string;
methodParameters?: string; methodParameters?: string;
} = {}): ExternalApiUsage { } = {}): Method {
return { return {
library, library,
supported, supported,

View File

@@ -8,7 +8,7 @@ import { AutomodelMode } from "../../../src/model-editor/auto-model-api";
import { AutoModelQueriesResult } from "../../../src/model-editor/auto-model-codeml-queries"; import { AutoModelQueriesResult } from "../../../src/model-editor/auto-model-codeml-queries";
import * as sarif from "sarif"; import * as sarif from "sarif";
import { gzipDecode } from "../../../src/common/zlib"; import { gzipDecode } from "../../../src/common/zlib";
import { ExternalApiUsage } from "../../../src/model-editor/external-api-usage"; import { Method } from "../../../src/model-editor/method";
import { ModeledMethod } from "../../../src/model-editor/modeled-method"; import { ModeledMethod } from "../../../src/model-editor/modeled-method";
describe("createAutoModelRequest", () => { describe("createAutoModelRequest", () => {
@@ -86,7 +86,7 @@ describe("createAutoModelRequest", () => {
describe("getCandidates", () => { describe("getCandidates", () => {
it("doesn't return methods that are already modelled", () => { it("doesn't return methods that are already modelled", () => {
const externalApiUsages: ExternalApiUsage[] = [ const methods: Method[] = [
{ {
library: "my.jar", library: "my.jar",
signature: "org.my.A#x()", signature: "org.my.A#x()",
@@ -113,16 +113,12 @@ describe("getCandidates", () => {
methodParameters: "()", methodParameters: "()",
}, },
}; };
const candidates = getCandidates( const candidates = getCandidates(Mode.Application, methods, modeledMethods);
Mode.Application,
externalApiUsages,
modeledMethods,
);
expect(candidates.length).toEqual(0); expect(candidates.length).toEqual(0);
}); });
it("doesn't return methods that are supported from other sources", () => { it("doesn't return methods that are supported from other sources", () => {
const externalApiUsages: ExternalApiUsage[] = [ const methods: Method[] = [
{ {
library: "my.jar", library: "my.jar",
signature: "org.my.A#x()", signature: "org.my.A#x()",
@@ -136,17 +132,13 @@ describe("getCandidates", () => {
}, },
]; ];
const modeledMethods = {}; const modeledMethods = {};
const candidates = getCandidates( const candidates = getCandidates(Mode.Application, methods, modeledMethods);
Mode.Application,
externalApiUsages,
modeledMethods,
);
expect(candidates.length).toEqual(0); expect(candidates.length).toEqual(0);
}); });
it("returns methods that are neither modeled nor supported from other sources", () => { it("returns methods that are neither modeled nor supported from other sources", () => {
const externalApiUsages: ExternalApiUsage[] = []; const methods: Method[] = [];
externalApiUsages.push({ methods.push({
library: "my.jar", library: "my.jar",
signature: "org.my.A#x()", signature: "org.my.A#x()",
packageName: "org.my", packageName: "org.my",
@@ -158,11 +150,7 @@ describe("getCandidates", () => {
usages: [], usages: [],
}); });
const modeledMethods = {}; const modeledMethods = {};
const candidates = getCandidates( const candidates = getCandidates(Mode.Application, methods, modeledMethods);
Mode.Application,
externalApiUsages,
modeledMethods,
);
expect(candidates.length).toEqual(1); expect(candidates.length).toEqual(1);
}); });
}); });

View File

@@ -1,6 +1,6 @@
import { decodeBqrsToExternalApiUsages } from "../../../src/model-editor/bqrs"; import { decodeBqrsToExternalApiUsages } from "../../../src/model-editor/bqrs";
import { DecodedBqrsChunk } from "../../../src/common/bqrs-cli-types"; import { DecodedBqrsChunk } from "../../../src/common/bqrs-cli-types";
import { CallClassification } from "../../../src/model-editor/external-api-usage"; import { CallClassification } from "../../../src/model-editor/method";
describe("decodeBqrsToExternalApiUsages", () => { describe("decodeBqrsToExternalApiUsages", () => {
const chunk: DecodedBqrsChunk = { const chunk: DecodedBqrsChunk = {

View File

@@ -5,7 +5,7 @@ import {
createFilenameForLibrary, createFilenameForLibrary,
loadDataExtensionYaml, loadDataExtensionYaml,
} from "../../../src/model-editor/yaml"; } from "../../../src/model-editor/yaml";
import { CallClassification } from "../../../src/model-editor/external-api-usage"; import { CallClassification } from "../../../src/model-editor/method";
describe("createDataExtensionYaml", () => { describe("createDataExtensionYaml", () => {
it("creates the correct YAML file", () => { it("creates the correct YAML file", () => {

View File

@@ -14,7 +14,7 @@ import { mockedObject, mockedUri } from "../../utils/mocking.helpers";
import { CodeQLCliServer } from "../../../../src/codeql-cli/cli"; import { CodeQLCliServer } from "../../../../src/codeql-cli/cli";
import { QueryRunner } from "../../../../src/query-server"; import { QueryRunner } from "../../../../src/query-server";
import * as queryResolver from "../../../../src/local-queries/query-resolver"; import * as queryResolver from "../../../../src/local-queries/query-resolver";
import { MethodSignature } from "../../../../src/model-editor/external-api-usage"; import { MethodSignature } from "../../../../src/model-editor/method";
import { join } from "path"; import { join } from "path";
import { exists, readFile } from "fs-extra"; import { exists, readFile } from "fs-extra";
import { load as loadYaml } from "js-yaml"; import { load as loadYaml } from "js-yaml";

View File

@@ -1,11 +1,11 @@
import { CodeQLCliServer } from "../../../../../src/codeql-cli/cli"; import { CodeQLCliServer } from "../../../../../src/codeql-cli/cli";
import { ExternalApiUsage } from "../../../../../src/model-editor/external-api-usage"; import { Method } from "../../../../../src/model-editor/method";
import { MethodsUsageDataProvider } from "../../../../../src/model-editor/methods-usage/methods-usage-data-provider"; import { MethodsUsageDataProvider } from "../../../../../src/model-editor/methods-usage/methods-usage-data-provider";
import { DatabaseItem } from "../../../../../src/databases/local-databases"; import { DatabaseItem } from "../../../../../src/databases/local-databases";
import { import {
createExternalApiUsage, createMethod,
createUsage, createUsage,
} from "../../../../factories/data-extension/external-api-factories"; } from "../../../../factories/data-extension/method-factories";
import { mockedObject } from "../../../utils/mocking.helpers"; import { mockedObject } from "../../../utils/mocking.helpers";
describe("MethodsUsageDataProvider", () => { describe("MethodsUsageDataProvider", () => {
@@ -18,31 +18,31 @@ describe("MethodsUsageDataProvider", () => {
describe("setState", () => { describe("setState", () => {
const hideModeledApis = false; const hideModeledApis = false;
const externalApiUsages: ExternalApiUsage[] = []; const methods: Method[] = [];
const dbItem = mockedObject<DatabaseItem>({ const dbItem = mockedObject<DatabaseItem>({
getSourceLocationPrefix: () => "test", getSourceLocationPrefix: () => "test",
}); });
it("should not emit onDidChangeTreeData event when state has not changed", async () => { it("should not emit onDidChangeTreeData event when state has not changed", async () => {
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis); await dataProvider.setState(methods, dbItem, hideModeledApis);
const onDidChangeTreeDataListener = jest.fn(); const onDidChangeTreeDataListener = jest.fn();
dataProvider.onDidChangeTreeData(onDidChangeTreeDataListener); dataProvider.onDidChangeTreeData(onDidChangeTreeDataListener);
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis); await dataProvider.setState(methods, dbItem, hideModeledApis);
expect(onDidChangeTreeDataListener).not.toHaveBeenCalled(); expect(onDidChangeTreeDataListener).not.toHaveBeenCalled();
}); });
it("should emit onDidChangeTreeData event when externalApiUsages has changed", async () => { it("should emit onDidChangeTreeData event when methods has changed", async () => {
const externalApiUsages2: ExternalApiUsage[] = []; const methods2: Method[] = [];
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis); await dataProvider.setState(methods, dbItem, hideModeledApis);
const onDidChangeTreeDataListener = jest.fn(); const onDidChangeTreeDataListener = jest.fn();
dataProvider.onDidChangeTreeData(onDidChangeTreeDataListener); dataProvider.onDidChangeTreeData(onDidChangeTreeDataListener);
await dataProvider.setState(externalApiUsages2, dbItem, hideModeledApis); await dataProvider.setState(methods2, dbItem, hideModeledApis);
expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1);
}); });
@@ -52,23 +52,23 @@ describe("MethodsUsageDataProvider", () => {
getSourceLocationPrefix: () => "test", getSourceLocationPrefix: () => "test",
}); });
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis); await dataProvider.setState(methods, dbItem, hideModeledApis);
const onDidChangeTreeDataListener = jest.fn(); const onDidChangeTreeDataListener = jest.fn();
dataProvider.onDidChangeTreeData(onDidChangeTreeDataListener); dataProvider.onDidChangeTreeData(onDidChangeTreeDataListener);
await dataProvider.setState(externalApiUsages, dbItem2, hideModeledApis); await dataProvider.setState(methods, dbItem2, hideModeledApis);
expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1);
}); });
it("should emit onDidChangeTreeData event when hideModeledApis has changed", async () => { it("should emit onDidChangeTreeData event when hideModeledApis has changed", async () => {
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis); await dataProvider.setState(methods, dbItem, hideModeledApis);
const onDidChangeTreeDataListener = jest.fn(); const onDidChangeTreeDataListener = jest.fn();
dataProvider.onDidChangeTreeData(onDidChangeTreeDataListener); dataProvider.onDidChangeTreeData(onDidChangeTreeDataListener);
await dataProvider.setState(externalApiUsages, dbItem, !hideModeledApis); await dataProvider.setState(methods, dbItem, !hideModeledApis);
expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1);
}); });
@@ -77,36 +77,29 @@ describe("MethodsUsageDataProvider", () => {
const dbItem2 = mockedObject<DatabaseItem>({ const dbItem2 = mockedObject<DatabaseItem>({
getSourceLocationPrefix: () => "test", getSourceLocationPrefix: () => "test",
}); });
const externalApiUsages2: ExternalApiUsage[] = []; const methods2: Method[] = [];
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis); await dataProvider.setState(methods, dbItem, hideModeledApis);
const onDidChangeTreeDataListener = jest.fn(); const onDidChangeTreeDataListener = jest.fn();
dataProvider.onDidChangeTreeData(onDidChangeTreeDataListener); dataProvider.onDidChangeTreeData(onDidChangeTreeDataListener);
await dataProvider.setState( await dataProvider.setState(methods2, dbItem2, !hideModeledApis);
externalApiUsages2,
dbItem2,
!hideModeledApis,
);
expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1);
}); });
}); });
describe("getChildren", () => { describe("getChildren", () => {
const supportedExternalApiUsage = createExternalApiUsage({ const supportedMethod = createMethod({
supported: true, supported: true,
}); });
const unsupportedExternalApiUsage = createExternalApiUsage({ const unsupportedMethod = createMethod({
supported: false, supported: false,
}); });
const externalApiUsages: ExternalApiUsage[] = [ const methods: Method[] = [supportedMethod, unsupportedMethod];
supportedExternalApiUsage,
unsupportedExternalApiUsage,
];
const dbItem = mockedObject<DatabaseItem>({ const dbItem = mockedObject<DatabaseItem>({
getSourceLocationPrefix: () => "test", getSourceLocationPrefix: () => "test",
}); });
@@ -118,19 +111,19 @@ describe("MethodsUsageDataProvider", () => {
}); });
it("should return usages if item is external api usage", async () => { it("should return usages if item is external api usage", async () => {
const externalApiUsage = createExternalApiUsage({ usages: [usage] }); const method = createMethod({ usages: [usage] });
expect(dataProvider.getChildren(externalApiUsage)).toEqual([usage]); expect(dataProvider.getChildren(method)).toEqual([usage]);
}); });
it("should show all externalApiUsages if hideModeledApis is false and looking at the root", async () => { it("should show all methods if hideModeledApis is false and looking at the root", async () => {
const hideModeledApis = false; const hideModeledApis = false;
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis); await dataProvider.setState(methods, dbItem, hideModeledApis);
expect(dataProvider.getChildren().length).toEqual(2); expect(dataProvider.getChildren().length).toEqual(2);
}); });
it("should filter externalApiUsages if hideModeledApis is true and looking at the root", async () => { it("should filter methods if hideModeledApis is true and looking at the root", async () => {
const hideModeledApis = true; const hideModeledApis = true;
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis); await dataProvider.setState(methods, dbItem, hideModeledApis);
expect(dataProvider.getChildren().length).toEqual(1); expect(dataProvider.getChildren().length).toEqual(1);
}); });
}); });

View File

@@ -1,13 +1,13 @@
import { window, TreeView } from "vscode"; import { window, TreeView } from "vscode";
import { CodeQLCliServer } from "../../../../../src/codeql-cli/cli"; import { CodeQLCliServer } from "../../../../../src/codeql-cli/cli";
import { ExternalApiUsage } from "../../../../../src/model-editor/external-api-usage"; import { Method } from "../../../../../src/model-editor/method";
import { MethodsUsagePanel } from "../../../../../src/model-editor/methods-usage/methods-usage-panel"; import { MethodsUsagePanel } from "../../../../../src/model-editor/methods-usage/methods-usage-panel";
import { DatabaseItem } from "../../../../../src/databases/local-databases"; import { DatabaseItem } from "../../../../../src/databases/local-databases";
import { mockedObject } from "../../../utils/mocking.helpers"; import { mockedObject } from "../../../utils/mocking.helpers";
import { import {
createExternalApiUsage, createMethod,
createUsage, createUsage,
} from "../../../../factories/data-extension/external-api-factories"; } from "../../../../factories/data-extension/method-factories";
describe("MethodsUsagePanel", () => { describe("MethodsUsagePanel", () => {
const mockCliServer = mockedObject<CodeQLCliServer>({}); const mockCliServer = mockedObject<CodeQLCliServer>({});
@@ -17,7 +17,7 @@ describe("MethodsUsagePanel", () => {
describe("setState", () => { describe("setState", () => {
const hideModeledApis = false; const hideModeledApis = false;
const externalApiUsages: ExternalApiUsage[] = [createExternalApiUsage()]; const methods: Method[] = [createMethod()];
it("should update the tree view with the correct batch number", async () => { it("should update the tree view with the correct batch number", async () => {
const mockTreeView = { const mockTreeView = {
@@ -26,7 +26,7 @@ describe("MethodsUsagePanel", () => {
jest.spyOn(window, "createTreeView").mockReturnValue(mockTreeView); jest.spyOn(window, "createTreeView").mockReturnValue(mockTreeView);
const panel = new MethodsUsagePanel(mockCliServer); const panel = new MethodsUsagePanel(mockCliServer);
await panel.setState(externalApiUsages, dbItem, hideModeledApis); await panel.setState(methods, dbItem, hideModeledApis);
expect(mockTreeView.badge?.value).toBe(1); expect(mockTreeView.badge?.value).toBe(1);
}); });
@@ -46,14 +46,14 @@ describe("MethodsUsagePanel", () => {
}); });
it("should reveal the correct item in the tree view", async () => { it("should reveal the correct item in the tree view", async () => {
const externalApiUsages = [ const methods = [
createExternalApiUsage({ createMethod({
usages: [usage], usages: [usage],
}), }),
]; ];
const panel = new MethodsUsagePanel(mockCliServer); const panel = new MethodsUsagePanel(mockCliServer);
await panel.setState(externalApiUsages, dbItem, hideModeledApis); await panel.setState(methods, dbItem, hideModeledApis);
await panel.revealItem(usage); await panel.revealItem(usage);
@@ -61,9 +61,9 @@ describe("MethodsUsagePanel", () => {
}); });
it("should do nothing if usage cannot be found", async () => { it("should do nothing if usage cannot be found", async () => {
const externalApiUsages = [createExternalApiUsage({})]; const methods = [createMethod({})];
const panel = new MethodsUsagePanel(mockCliServer); const panel = new MethodsUsagePanel(mockCliServer);
await panel.setState(externalApiUsages, dbItem, hideModeledApis); await panel.setState(methods, dbItem, hideModeledApis);
await panel.revealItem(usage); await panel.revealItem(usage);