Merge remote-tracking branch 'origin/main' into koesie10/find-github-repository

This commit is contained in:
Koen Vlaswinkel
2023-11-16 16:49:35 +01:00
45 changed files with 479 additions and 191 deletions

View File

@@ -2,6 +2,15 @@
## [UNRELEASED] ## [UNRELEASED]
- Add new CodeQL views for managing databases and queries:
1. A queries panel, to create and run queries in one place.
2. A language selector, to quickly determine the language compatibility of databases and queries.
For more information, see the [documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/analyzing-your-projects/#filtering-databases-and-queries-by-language).
- When adding a CodeQL database, we no longer add the database source folder to the workspace by default (since this caused bugs in single-folder workspaces). [#3047](https://github.com/github/vscode-codeql/pull/3047)
- You can manually add individual database source folders to the workspace with the "Add Database Source to Workspace" right-click command in the databases view.
- To restore the old behavior of adding all database source folders by default, set the `codeQL.addingDatabases.addDatabaseSourceToWorkspace` setting to `true`.
- Rename the `codeQL.databaseDownload.allowHttp` setting to `codeQL.addingDatabases.allowHttp`, so that database-related settings are grouped together in the Settings UI. [#3047](https://github.com/github/vscode-codeql/pull/3047) & [#3069](https://github.com/github/vscode-codeql/pull/3069)
- The "Sort by Language" action in the databases view now sorts by name within each language. [#3055](https://github.com/github/vscode-codeql/pull/3055) - The "Sort by Language" action in the databases view now sorts by name within each language. [#3055](https://github.com/github/vscode-codeql/pull/3055)
## 1.9.4 - 6 November 2023 ## 1.9.4 - 6 November 2023

View File

@@ -4392,9 +4392,9 @@
} }
}, },
"node_modules/@octokit/openapi-types": { "node_modules/@octokit/openapi-types": {
"version": "19.0.0", "version": "19.0.2",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.2.tgz",
"integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw==" "integrity": "sha512-8li32fUDUeml/ACRp/njCWTsk5t17cfTM1jp9n08pBrqs5cDFJubtjsSnuz56r5Tad6jdEPJld7LxNp9dNcyjQ=="
}, },
"node_modules/@octokit/plugin-paginate-rest": { "node_modules/@octokit/plugin-paginate-rest": {
"version": "9.0.0", "version": "9.0.0",
@@ -35269,9 +35269,9 @@
} }
}, },
"@octokit/openapi-types": { "@octokit/openapi-types": {
"version": "19.0.0", "version": "19.0.2",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.2.tgz",
"integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw==" "integrity": "sha512-8li32fUDUeml/ACRp/njCWTsk5t17cfTM1jp9n08pBrqs5cDFJubtjsSnuz56r5Tad6jdEPJld7LxNp9dNcyjQ=="
}, },
"@octokit/plugin-paginate-rest": { "@octokit/plugin-paginate-rest": {
"version": "9.0.0", "version": "9.0.0",

View File

@@ -374,13 +374,23 @@
}, },
{ {
"type": "object", "type": "object",
"title": "Downloading databases", "title": "Adding databases",
"order": 6, "order": 6,
"properties": { "properties": {
"codeQL.databaseDownload.allowHttp": { "codeQL.addingDatabases.allowHttp": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "Allow database to be downloaded via HTTP. Warning: enabling this option will allow downloading from insecure servers." "description": "Allow databases to be downloaded via HTTP. Warning: enabling this option will allow downloading from insecure servers."
},
"codeQL.databaseDownload.allowHttp": {
"type": "boolean",
"markdownDeprecationMessage": "**Deprecated**: Please use `#codeQL.addingDatabases.allowHttp#` instead.",
"deprecationMessage": "Deprecated: Please use codeQL.addingDatabases.allowHttp instead."
},
"codeQL.addingDatabases.addDatabaseSourceToWorkspace": {
"type": "boolean",
"default": false,
"markdownDescription": "When adding a CodeQL database, automatically add the database's source folder as a workspace folder. Warning: enabling this option in a single-folder workspace will cause the workspace to reload as a [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces). This may cause query history and database lists to be reset."
} }
} }
}, },
@@ -1678,10 +1688,6 @@
"command": "codeQL.mockGitHubApiServer.unloadScenario", "command": "codeQL.mockGitHubApiServer.unloadScenario",
"when": "config.codeQL.mockGitHubApiServer.enabled && codeQL.mockGitHubApiServer.scenarioLoaded" "when": "config.codeQL.mockGitHubApiServer.enabled && codeQL.mockGitHubApiServer.scenarioLoaded"
}, },
{
"command": "codeQL.createQuery",
"when": "config.codeQL.codespacesTemplate || config.codeQL.canary && config.codeQL.queriesPanel"
},
{ {
"command": "codeQLTests.acceptOutputContextTestItem", "command": "codeQLTests.acceptOutputContextTestItem",
"when": "false" "when": "false"
@@ -1766,8 +1772,7 @@
"ql-container": [ "ql-container": [
{ {
"id": "codeQLLanguageSelection", "id": "codeQLLanguageSelection",
"name": "Language", "name": "Language"
"when": "config.codeQL.canary && config.codeQL.showLanguageFilter"
}, },
{ {
"id": "codeQLDatabases", "id": "codeQLDatabases",
@@ -1775,8 +1780,7 @@
}, },
{ {
"id": "codeQLQueries", "id": "codeQLQueries",
"name": "Queries", "name": "Queries"
"when": "config.codeQL.canary && config.codeQL.queriesPanel"
}, },
{ {
"id": "codeQLVariantAnalysisRepositories", "id": "codeQLVariantAnalysisRepositories",

View File

@@ -641,12 +641,32 @@ export function isCodespacesTemplate() {
return !!CODESPACES_TEMPLATE.getValue<boolean>(); return !!CODESPACES_TEMPLATE.getValue<boolean>();
} }
// Deprecated after v1.9.4. Can be removed in a few versions.
const DATABASE_DOWNLOAD_SETTING = new Setting("databaseDownload", ROOT_SETTING); const DATABASE_DOWNLOAD_SETTING = new Setting("databaseDownload", ROOT_SETTING);
const DEPRECATED_ALLOW_HTTP_SETTING = new Setting(
"allowHttp",
DATABASE_DOWNLOAD_SETTING,
);
const ALLOW_HTTP_SETTING = new Setting("allowHttp", DATABASE_DOWNLOAD_SETTING); const ADDING_DATABASES_SETTING = new Setting("addingDatabases", ROOT_SETTING);
const ALLOW_HTTP_SETTING = new Setting("allowHttp", ADDING_DATABASES_SETTING);
export function allowHttp(): boolean { export function allowHttp(): boolean {
return ALLOW_HTTP_SETTING.getValue<boolean>() || false; return (
ALLOW_HTTP_SETTING.getValue<boolean>() ||
DEPRECATED_ALLOW_HTTP_SETTING.getValue<boolean>() ||
false
);
}
const ADD_DATABASE_SOURCE_TO_WORKSPACE_SETTING = new Setting(
"addDatabaseSourceToWorkspace",
ADDING_DATABASES_SETTING,
);
export function addDatabaseSourceToWorkspace(): boolean {
return ADD_DATABASE_SOURCE_TO_WORKSPACE_SETTING.getValue<boolean>() || false;
} }
/** /**
@@ -690,15 +710,6 @@ export async function setAutogenerateQlPacks(choice: AutogenerateQLPacks) {
); );
} }
/**
* A flag indicating whether to show the queries panel in the QL view container.
*/
const QUERIES_PANEL = new Setting("queriesPanel", ROOT_SETTING);
export function showQueriesPanel(): boolean {
return !!QUERIES_PANEL.getValue<boolean>();
}
const MODEL_SETTING = new Setting("model", ROOT_SETTING); const MODEL_SETTING = new Setting("model", ROOT_SETTING);
const FLOW_GENERATION = new Setting("flowGeneration", MODEL_SETTING); const FLOW_GENERATION = new Setting("flowGeneration", MODEL_SETTING);
const LLM_GENERATION = new Setting("llmGeneration", MODEL_SETTING); const LLM_GENERATION = new Setting("llmGeneration", MODEL_SETTING);

View File

@@ -1,6 +1,8 @@
// Contains models and consts for the data we want to store in the database config. // Contains models and consts for the data we want to store in the database config.
// Changes to these models should be done carefully and account for backwards compatibility of data. // Changes to these models should be done carefully and account for backwards compatibility of data.
import { DatabaseOrigin } from "../local-databases/database-origin";
export const DB_CONFIG_VERSION = 1; export const DB_CONFIG_VERSION = 1;
export interface DbConfig { export interface DbConfig {
@@ -88,6 +90,7 @@ export interface LocalDatabase {
name: string; name: string;
dateAdded: number; dateAdded: number;
language: string; language: string;
origin: DatabaseOrigin;
storagePath: string; storagePath: string;
} }

View File

@@ -29,10 +29,11 @@ import {
} from "../common/github-url-identifier-helper"; } from "../common/github-url-identifier-helper";
import { Credentials } from "../common/authentication"; import { Credentials } from "../common/authentication";
import { AppCommandManager } from "../common/commands"; import { AppCommandManager } from "../common/commands";
import { allowHttp } from "../config"; import { addDatabaseSourceToWorkspace, allowHttp } from "../config";
import { showAndLogInformationMessage } from "../common/logging"; import { showAndLogInformationMessage } from "../common/logging";
import { AppOctokit } from "../common/octokit"; import { AppOctokit } from "../common/octokit";
import { getLanguageDisplayName } from "../common/query-language"; import { getLanguageDisplayName } from "../common/query-language";
import { DatabaseOrigin } from "./local-databases/database-origin";
/** /**
* Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file. * Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file.
@@ -62,6 +63,10 @@ export async function promptImportInternetDatabase(
databaseManager, databaseManager,
storagePath, storagePath,
undefined, undefined,
{
type: "url",
url: databaseUrl,
},
progress, progress,
cli, cli,
); );
@@ -99,7 +104,7 @@ export async function promptImportGithubDatabase(
cli?: CodeQLCliServer, cli?: CodeQLCliServer,
language?: string, language?: string,
makeSelected = true, makeSelected = true,
addSourceArchiveFolder = true, addSourceArchiveFolder = addDatabaseSourceToWorkspace(),
): Promise<DatabaseItem | undefined> { ): Promise<DatabaseItem | undefined> {
const githubRepo = await askForGitHubRepo(progress); const githubRepo = await askForGitHubRepo(progress);
if (!githubRepo) { if (!githubRepo) {
@@ -178,7 +183,7 @@ export async function downloadGitHubDatabase(
cli?: CodeQLCliServer, cli?: CodeQLCliServer,
language?: string, language?: string,
makeSelected = true, makeSelected = true,
addSourceArchiveFolder = true, addSourceArchiveFolder = addDatabaseSourceToWorkspace(),
): Promise<DatabaseItem | undefined> { ): Promise<DatabaseItem | undefined> {
const nwo = getNwoFromGitHubUrl(githubRepo) || githubRepo; const nwo = getNwoFromGitHubUrl(githubRepo) || githubRepo;
if (!isValidGitHubNwo(nwo)) { if (!isValidGitHubNwo(nwo)) {
@@ -199,7 +204,8 @@ export async function downloadGitHubDatabase(
return; return;
} }
const { databaseUrl, name, owner } = result; const { databaseUrl, name, owner, databaseId, databaseCreatedAt, commitOid } =
result;
/** /**
* The 'token' property of the token object returned by `octokit.auth()`. * The 'token' property of the token object returned by `octokit.auth()`.
@@ -221,6 +227,13 @@ export async function downloadGitHubDatabase(
databaseManager, databaseManager,
storagePath, storagePath,
`${owner}/${name}`, `${owner}/${name}`,
{
type: "github",
repository: nwo,
databaseId,
databaseCreatedAt,
commitOid,
},
progress, progress,
cli, cli,
makeSelected, makeSelected,
@@ -250,6 +263,10 @@ export async function importArchiveDatabase(
databaseManager, databaseManager,
storagePath, storagePath,
undefined, undefined,
{
type: "archive",
path: databaseUrl,
},
progress, progress,
cli, cli,
); );
@@ -282,6 +299,7 @@ export async function importArchiveDatabase(
* @param databaseManager the DatabaseManager * @param databaseManager the DatabaseManager
* @param storagePath where to store the unzipped database. * @param storagePath where to store the unzipped database.
* @param nameOverride a name for the database that overrides the default * @param nameOverride a name for the database that overrides the default
* @param origin the origin of the database
* @param progress callback to send progress messages to * @param progress callback to send progress messages to
* @param makeSelected make the new database selected in the databases panel (default: true) * @param makeSelected make the new database selected in the databases panel (default: true)
* @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace
@@ -292,10 +310,11 @@ async function databaseArchiveFetcher(
databaseManager: DatabaseManager, databaseManager: DatabaseManager,
storagePath: string, storagePath: string,
nameOverride: string | undefined, nameOverride: string | undefined,
origin: DatabaseOrigin,
progress: ProgressCallback, progress: ProgressCallback,
cli?: CodeQLCliServer, cli?: CodeQLCliServer,
makeSelected = true, makeSelected = true,
addSourceArchiveFolder = true, addSourceArchiveFolder = addDatabaseSourceToWorkspace(),
): Promise<DatabaseItem> { ): Promise<DatabaseItem> {
progress({ progress({
message: "Getting database", message: "Getting database",
@@ -336,6 +355,7 @@ async function databaseArchiveFetcher(
const item = await databaseManager.openDatabase( const item = await databaseManager.openDatabase(
Uri.file(dbPath), Uri.file(dbPath),
origin,
makeSelected, makeSelected,
nameOverride, nameOverride,
{ {
@@ -476,7 +496,7 @@ async function checkForFailingResponse(
return response; return response;
} }
// An error downloading the database. Attempt to extract the resaon behind it. // An error downloading the database. Attempt to extract the reason behind it.
const text = await response.text(); const text = await response.text();
let msg: string; let msg: string;
try { try {
@@ -533,16 +553,19 @@ export async function convertGithubNwoToDatabaseUrl(
databaseUrl: string; databaseUrl: string;
owner: string; owner: string;
name: string; name: string;
databaseId: number;
databaseCreatedAt: string;
commitOid: string | null;
} }
| undefined | undefined
> { > {
try { try {
const [owner, repo] = nwo.split("/"); const [owner, repo] = nwo.split("/");
const response = await octokit.request( const response = await octokit.rest.codeScanning.listCodeqlDatabases({
"GET /repos/:owner/:repo/code-scanning/codeql/databases", owner,
{ owner, repo }, repo,
); });
const languages = response.data.map((db: any) => db.language); const languages = response.data.map((db: any) => db.language);
@@ -553,10 +576,20 @@ export async function convertGithubNwoToDatabaseUrl(
} }
} }
const databaseForLanguage = response.data.find(
(db: any) => db.language === language,
);
if (!databaseForLanguage) {
throw new Error(`No database found for language '${language}'`);
}
return { return {
databaseUrl: `https://api.github.com/repos/${owner}/${repo}/code-scanning/codeql/databases/${language}`, databaseUrl: databaseForLanguage.url,
owner, owner,
name: repo, name: repo,
databaseId: databaseForLanguage.id,
databaseCreatedAt: databaseForLanguage.created_at,
commitOid: databaseForLanguage.commit_oid ?? null,
}; };
} catch (e) { } catch (e) {
void extLogger.log(`Error: ${getErrorMessage(e)}`); void extLogger.log(`Error: ${getErrorMessage(e)}`);

View File

@@ -1,5 +1,7 @@
// This file contains models that are used to represent the databases. // This file contains models that are used to represent the databases.
import { DatabaseOrigin } from "./local-databases/database-origin";
export enum DbItemKind { export enum DbItemKind {
RootLocal = "RootLocal", RootLocal = "RootLocal",
LocalList = "LocalList", LocalList = "LocalList",
@@ -38,6 +40,7 @@ export interface LocalDatabaseDbItem {
databaseName: string; databaseName: string;
dateAdded: number; dateAdded: number;
language: string; language: string;
origin: DatabaseOrigin;
storagePath: string; storagePath: string;
parentListName?: string; parentListName?: string;
} }

View File

@@ -197,6 +197,7 @@ function createLocalDb(
databaseName: db.name, databaseName: db.name,
dateAdded: db.dateAdded, dateAdded: db.dateAdded,
language: db.language, language: db.language,
origin: db.origin,
storagePath: db.storagePath, storagePath: db.storagePath,
selected: !!selected, selected: !!selected,
parentListName: listName, parentListName: listName,

View File

@@ -367,6 +367,9 @@ export class DatabaseUI extends DisposableObject {
await this.databaseManager.openDatabase( await this.databaseManager.openDatabase(
uri, uri,
{
type: "folder",
},
makeSelected, makeSelected,
nameOverride, nameOverride,
{ {
@@ -704,7 +707,9 @@ export class DatabaseUI extends DisposableObject {
this.queryServer?.cliServer, this.queryServer?.cliServer,
); );
} else { } else {
await this.databaseManager.openDatabase(uri); await this.databaseManager.openDatabase(uri, {
type: "folder",
});
} }
} catch (e) { } catch (e) {
// rethrow and let this be handled by default error handling. // rethrow and let this be handled by default error handling.
@@ -819,7 +824,9 @@ export class DatabaseUI extends DisposableObject {
if (byFolder) { if (byFolder) {
const fixedUri = await this.fixDbUri(uri); const fixedUri = await this.fixDbUri(uri);
// we are selecting a database folder // we are selecting a database folder
return await this.databaseManager.openDatabase(fixedUri); return await this.databaseManager.openDatabase(fixedUri, {
type: "folder",
});
} else { } else {
// we are selecting a database archive. Must unzip into a workspace-controlled area // we are selecting a database archive. Must unzip into a workspace-controlled area
// before importing. // before importing.

View File

@@ -14,6 +14,7 @@ import { isLikelyDatabaseRoot } from "./db-contents-heuristics";
import { stat } from "fs-extra"; import { stat } from "fs-extra";
import { containsPath, pathsEqual } from "../../common/files"; import { containsPath, pathsEqual } from "../../common/files";
import { DatabaseContents } from "./database-contents"; import { DatabaseContents } from "./database-contents";
import { DatabaseOrigin } from "./database-origin";
export class DatabaseItemImpl implements DatabaseItem { export class DatabaseItemImpl implements DatabaseItem {
// These are only public in the implementation, they are readonly in the interface // These are only public in the implementation, they are readonly in the interface
@@ -61,6 +62,10 @@ export class DatabaseItemImpl implements DatabaseItem {
return this.options.dateAdded; return this.options.dateAdded;
} }
public get origin(): DatabaseOrigin | undefined {
return this.options.origin;
}
public resolveSourceFile(uriStr: string | undefined): vscode.Uri { public resolveSourceFile(uriStr: string | undefined): vscode.Uri {
const sourceArchive = this.sourceArchive; const sourceArchive = this.sourceArchive;
const uri = uriStr ? vscode.Uri.parse(uriStr, true) : undefined; const uri = uriStr ? vscode.Uri.parse(uriStr, true) : undefined;

View File

@@ -2,6 +2,7 @@ import vscode from "vscode";
import * as cli from "../../codeql-cli/cli"; import * as cli from "../../codeql-cli/cli";
import { DatabaseContents } from "./database-contents"; import { DatabaseContents } from "./database-contents";
import { DatabaseOptions } from "./database-options"; import { DatabaseOptions } from "./database-options";
import { DatabaseOrigin } from "./database-origin";
/** An item in the list of available databases */ /** An item in the list of available databases */
export interface DatabaseItem { export interface DatabaseItem {
@@ -25,6 +26,11 @@ export interface DatabaseItem {
*/ */
readonly dateAdded: number | undefined; readonly dateAdded: number | undefined;
/**
* The origin this database item was retrieved from or undefined if unknown.
*/
readonly origin: DatabaseOrigin | undefined;
/** If the database is invalid, describes why. */ /** If the database is invalid, describes why. */
readonly error: Error | undefined; readonly error: Error | undefined;

View File

@@ -7,6 +7,7 @@ import { QueryRunner } from "../../query-server";
import * as cli from "../../codeql-cli/cli"; import * as cli from "../../codeql-cli/cli";
import { ProgressCallback, withProgress } from "../../common/vscode/progress"; import { ProgressCallback, withProgress } from "../../common/vscode/progress";
import { import {
addDatabaseSourceToWorkspace,
getAutogenerateQlPacks, getAutogenerateQlPacks,
isCodespacesTemplate, isCodespacesTemplate,
setAutogenerateQlPacks, setAutogenerateQlPacks,
@@ -34,6 +35,7 @@ import { DatabaseChangedEvent, DatabaseEventKind } from "./database-events";
import { DatabaseResolver } from "./database-resolver"; import { DatabaseResolver } from "./database-resolver";
import { telemetryListener } from "../../common/vscode/telemetry"; import { telemetryListener } from "../../common/vscode/telemetry";
import { LanguageContextStore } from "../../language-context-store"; import { LanguageContextStore } from "../../language-context-store";
import { DatabaseOrigin } from "./database-origin";
/** /**
* The name of the key in the workspaceState dictionary in which we * The name of the key in the workspaceState dictionary in which we
@@ -115,7 +117,7 @@ export class DatabaseManager extends DisposableObject {
this.languageContext.onLanguageContextChanged(async () => { this.languageContext.onLanguageContextChanged(async () => {
if ( if (
this.currentDatabaseItem !== undefined && this.currentDatabaseItem !== undefined &&
!this.languageContext.isSelectedLanguage( !this.languageContext.shouldInclude(
tryGetQueryLanguage(this.currentDatabaseItem.language), tryGetQueryLanguage(this.currentDatabaseItem.language),
) )
) { ) {
@@ -131,14 +133,19 @@ export class DatabaseManager extends DisposableObject {
*/ */
public async openDatabase( public async openDatabase(
uri: vscode.Uri, uri: vscode.Uri,
origin: DatabaseOrigin | undefined,
makeSelected = true, makeSelected = true,
displayName?: string, displayName?: string,
{ {
isTutorialDatabase = false, isTutorialDatabase = false,
addSourceArchiveFolder = true, addSourceArchiveFolder = addDatabaseSourceToWorkspace(),
}: OpenDatabaseOptions = {}, }: OpenDatabaseOptions = {},
): Promise<DatabaseItem> { ): Promise<DatabaseItem> {
const databaseItem = await this.createDatabaseItem(uri, displayName); const databaseItem = await this.createDatabaseItem(
uri,
origin,
displayName,
);
return await this.addExistingDatabaseItem( return await this.addExistingDatabaseItem(
databaseItem, databaseItem,
@@ -158,7 +165,7 @@ export class DatabaseManager extends DisposableObject {
databaseItem: DatabaseItemImpl, databaseItem: DatabaseItemImpl,
makeSelected: boolean, makeSelected: boolean,
isTutorialDatabase?: boolean, isTutorialDatabase?: boolean,
addSourceArchiveFolder = true, addSourceArchiveFolder = addDatabaseSourceToWorkspace(),
): Promise<DatabaseItem> { ): Promise<DatabaseItem> {
const existingItem = this.findDatabaseItem(databaseItem.databaseUri); const existingItem = this.findDatabaseItem(databaseItem.databaseUri);
if (existingItem !== undefined) { if (existingItem !== undefined) {
@@ -189,6 +196,7 @@ export class DatabaseManager extends DisposableObject {
*/ */
private async createDatabaseItem( private async createDatabaseItem(
uri: vscode.Uri, uri: vscode.Uri,
origin: DatabaseOrigin | undefined,
displayName: string | undefined, displayName: string | undefined,
): Promise<DatabaseItemImpl> { ): Promise<DatabaseItemImpl> {
const contents = await DatabaseResolver.resolveDatabaseContents(uri); const contents = await DatabaseResolver.resolveDatabaseContents(uri);
@@ -197,6 +205,7 @@ export class DatabaseManager extends DisposableObject {
displayName, displayName,
dateAdded: Date.now(), dateAdded: Date.now(),
language: await this.getPrimaryLanguage(uri.fsPath), language: await this.getPrimaryLanguage(uri.fsPath),
origin,
}; };
const databaseItem = new DatabaseItemImpl(uri, contents, fullOptions); const databaseItem = new DatabaseItemImpl(uri, contents, fullOptions);
@@ -212,6 +221,7 @@ export class DatabaseManager extends DisposableObject {
*/ */
public async createOrOpenDatabaseItem( public async createOrOpenDatabaseItem(
uri: vscode.Uri, uri: vscode.Uri,
origin: DatabaseOrigin | undefined,
): Promise<DatabaseItem> { ): Promise<DatabaseItem> {
const existingItem = this.findDatabaseItem(uri); const existingItem = this.findDatabaseItem(uri);
if (existingItem !== undefined) { if (existingItem !== undefined) {
@@ -220,7 +230,7 @@ export class DatabaseManager extends DisposableObject {
} }
// We don't add this to the list automatically, but the user can add it later. // We don't add this to the list automatically, but the user can add it later.
return this.createDatabaseItem(uri, undefined); return this.createDatabaseItem(uri, origin, undefined);
} }
public async createSkeletonPacks(databaseItem: DatabaseItem) { public async createSkeletonPacks(databaseItem: DatabaseItem) {
@@ -355,6 +365,7 @@ export class DatabaseManager extends DisposableObject {
let displayName: string | undefined = undefined; let displayName: string | undefined = undefined;
let dateAdded = undefined; let dateAdded = undefined;
let language = undefined; let language = undefined;
let origin = undefined;
if (state.options) { if (state.options) {
if (typeof state.options.displayName === "string") { if (typeof state.options.displayName === "string") {
displayName = state.options.displayName; displayName = state.options.displayName;
@@ -363,6 +374,7 @@ export class DatabaseManager extends DisposableObject {
dateAdded = state.options.dateAdded; dateAdded = state.options.dateAdded;
} }
language = state.options.language; language = state.options.language;
origin = state.options.origin;
} }
const dbBaseUri = vscode.Uri.parse(state.uri, true); const dbBaseUri = vscode.Uri.parse(state.uri, true);
@@ -375,6 +387,7 @@ export class DatabaseManager extends DisposableObject {
displayName, displayName,
dateAdded, dateAdded,
language, language,
origin,
}; };
const item = new DatabaseItemImpl(dbBaseUri, undefined, fullOptions); const item = new DatabaseItemImpl(dbBaseUri, undefined, fullOptions);

View File

@@ -1,10 +1,14 @@
import { DatabaseOrigin } from "./database-origin";
export interface DatabaseOptions { export interface DatabaseOptions {
displayName?: string; displayName?: string;
dateAdded?: number | undefined; dateAdded?: number | undefined;
language?: string; language?: string;
origin?: DatabaseOrigin;
} }
export interface FullDatabaseOptions extends DatabaseOptions { export interface FullDatabaseOptions extends DatabaseOptions {
dateAdded: number | undefined; dateAdded: number | undefined;
language: string | undefined; language: string | undefined;
origin: DatabaseOrigin | undefined;
} }

View File

@@ -0,0 +1,32 @@
interface DatabaseOriginFolder {
type: "folder";
}
interface DatabaseOriginArchive {
type: "archive";
path: string;
}
interface DatabaseOriginGitHub {
type: "github";
repository: string;
databaseId: number;
databaseCreatedAt: string;
commitOid: string | null;
}
interface DatabaseOriginInternet {
type: "url";
url: string;
}
interface DatabaseOriginDebugger {
type: "debugger";
}
export type DatabaseOrigin =
| DatabaseOriginFolder
| DatabaseOriginArchive
| DatabaseOriginGitHub
| DatabaseOriginInternet
| DatabaseOriginDebugger;

View File

@@ -105,7 +105,9 @@ class QLDebugAdapterTracker
body: CodeQLProtocol.EvaluationStartedEvent["body"], body: CodeQLProtocol.EvaluationStartedEvent["body"],
): Promise<void> { ): Promise<void> {
const dbUri = Uri.file(this.configuration.database); const dbUri = Uri.file(this.configuration.database);
const dbItem = await this.dbm.createOrOpenDatabaseItem(dbUri); const dbItem = await this.dbm.createOrOpenDatabaseItem(dbUri, {
type: "debugger",
});
// When cancellation is requested from the query history view, we just stop the debug session. // When cancellation is requested from the query history view, we just stop the debug session.
const tokenSource = new CancellationTokenSource(); const tokenSource = new CancellationTokenSource();

View File

@@ -1,4 +1,4 @@
import { mkdir, writeFile } from "fs-extra"; import { ensureDir, writeFile } from "fs-extra";
import { dump } from "js-yaml"; import { dump } from "js-yaml";
import { dirname, join } from "path"; import { dirname, join } from "path";
import { Uri } from "vscode"; import { Uri } from "vscode";
@@ -76,7 +76,7 @@ export class QlPackGenerator {
} }
private async createWorkspaceFolder() { private async createWorkspaceFolder() {
await mkdir(this.folderUri.fsPath); await ensureDir(this.folderUri.fsPath);
} }
private async createQlPackYaml() { private async createQlPackYaml() {

View File

@@ -4,7 +4,6 @@ import { DisposableObject } from "../../common/disposable-object";
import { MethodModelingViewProvider } from "./method-modeling-view-provider"; import { MethodModelingViewProvider } from "./method-modeling-view-provider";
import { Method } from "../method"; import { Method } from "../method";
import { ModelingStore } from "../modeling-store"; import { ModelingStore } from "../modeling-store";
import { ModelEditorViewTracker } from "../model-editor-view-tracker";
import { ModelConfigListener } from "../../config"; import { ModelConfigListener } from "../../config";
import { DatabaseItem } from "../../databases/local-databases"; import { DatabaseItem } from "../../databases/local-databases";
import { ModelingEvents } from "../modeling-events"; import { ModelingEvents } from "../modeling-events";
@@ -16,7 +15,6 @@ export class MethodModelingPanel extends DisposableObject {
app: App, app: App,
modelingStore: ModelingStore, modelingStore: ModelingStore,
modelingEvents: ModelingEvents, modelingEvents: ModelingEvents,
editorViewTracker: ModelEditorViewTracker,
) { ) {
super(); super();
@@ -29,7 +27,6 @@ export class MethodModelingPanel extends DisposableObject {
app, app,
modelingStore, modelingStore,
modelingEvents, modelingEvents,
editorViewTracker,
modelConfig, modelConfig,
); );
this.push( this.push(

View File

@@ -10,7 +10,6 @@ import { Method } from "../method";
import { ModelingStore } from "../modeling-store"; import { ModelingStore } from "../modeling-store";
import { AbstractWebviewViewProvider } from "../../common/vscode/abstract-webview-view-provider"; import { AbstractWebviewViewProvider } from "../../common/vscode/abstract-webview-view-provider";
import { assertNever } from "../../common/helpers-pure"; import { assertNever } from "../../common/helpers-pure";
import { ModelEditorViewTracker } from "../model-editor-view-tracker";
import { ModelConfigListener } from "../../config"; import { ModelConfigListener } from "../../config";
import { DatabaseItem } from "../../databases/local-databases"; import { DatabaseItem } from "../../databases/local-databases";
import { ModelingEvents } from "../modeling-events"; import { ModelingEvents } from "../modeling-events";
@@ -33,7 +32,6 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
app: App, app: App,
private readonly modelingStore: ModelingStore, private readonly modelingStore: ModelingStore,
private readonly modelingEvents: ModelingEvents, private readonly modelingEvents: ModelingEvents,
private readonly editorViewTracker: ModelEditorViewTracker,
private readonly modelConfig: ModelConfigListener, private readonly modelConfig: ModelConfigListener,
) { ) {
super(app, "method-modeling"); super(app, "method-modeling");
@@ -158,10 +156,10 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
return; return;
} }
const view = this.editorViewTracker.getView( this.modelingEvents.fireRevealInModelEditorEvent(
this.databaseItem.databaseUri.toString(), this.databaseItem.databaseUri.toString(),
method,
); );
await view?.revealMethod(method);
} }
private registerToModelingEvents(): void { private registerToModelingEvents(): void {

View File

@@ -19,7 +19,6 @@ import { setUpPack } from "./model-editor-queries-setup";
import { MethodModelingPanel } from "./method-modeling/method-modeling-panel"; import { MethodModelingPanel } from "./method-modeling/method-modeling-panel";
import { ModelingStore } from "./modeling-store"; import { ModelingStore } from "./modeling-store";
import { showResolvableLocation } from "../databases/local-databases/locations"; import { showResolvableLocation } from "../databases/local-databases/locations";
import { ModelEditorViewTracker } from "./model-editor-view-tracker";
import { ModelConfigListener } from "../config"; import { ModelConfigListener } from "../config";
import { ModelingEvents } from "./modeling-events"; import { ModelingEvents } from "./modeling-events";
import { getModelsAsDataLanguage } from "./languages"; import { getModelsAsDataLanguage } from "./languages";
@@ -30,7 +29,6 @@ export class ModelEditorModule extends DisposableObject {
private readonly queryStorageDir: string; private readonly queryStorageDir: string;
private readonly modelingStore: ModelingStore; private readonly modelingStore: ModelingStore;
private readonly modelingEvents: ModelingEvents; private readonly modelingEvents: ModelingEvents;
private readonly editorViewTracker: ModelEditorViewTracker<ModelEditorView>;
private readonly methodsUsagePanel: MethodsUsagePanel; private readonly methodsUsagePanel: MethodsUsagePanel;
private readonly methodModelingPanel: MethodModelingPanel; private readonly methodModelingPanel: MethodModelingPanel;
private readonly modelConfig: ModelConfigListener; private readonly modelConfig: ModelConfigListener;
@@ -46,17 +44,11 @@ export class ModelEditorModule extends DisposableObject {
this.queryStorageDir = join(baseQueryStorageDir, "model-editor-results"); this.queryStorageDir = join(baseQueryStorageDir, "model-editor-results");
this.modelingEvents = new ModelingEvents(app); this.modelingEvents = new ModelingEvents(app);
this.modelingStore = new ModelingStore(this.modelingEvents); this.modelingStore = new ModelingStore(this.modelingEvents);
this.editorViewTracker = new ModelEditorViewTracker();
this.methodsUsagePanel = this.push( this.methodsUsagePanel = this.push(
new MethodsUsagePanel(this.modelingStore, this.modelingEvents, cliServer), new MethodsUsagePanel(this.modelingStore, this.modelingEvents, cliServer),
); );
this.methodModelingPanel = this.push( this.methodModelingPanel = this.push(
new MethodModelingPanel( new MethodModelingPanel(app, this.modelingStore, this.modelingEvents),
app,
this.modelingStore,
this.modelingEvents,
this.editorViewTracker,
),
); );
this.modelConfig = this.push(new ModelConfigListener()); this.modelConfig = this.push(new ModelConfigListener());
@@ -144,12 +136,10 @@ export class ModelEditorModule extends DisposableObject {
const initialMode = definition.availableModes?.[0] ?? INITIAL_MODE; const initialMode = definition.availableModes?.[0] ?? INITIAL_MODE;
const existingView = this.editorViewTracker.getView( if (this.modelingStore.isDbOpen(db.databaseUri.toString())) {
db.databaseUri.toString(), this.modelingEvents.fireFocusModelEditorEvent(
); db.databaseUri.toString(),
if (existingView) { );
await existingView.focusView();
return; return;
} }
@@ -218,12 +208,10 @@ export class ModelEditorModule extends DisposableObject {
// Check again just before opening the editor to ensure no model editor has been opened between // Check again just before opening the editor to ensure no model editor has been opened between
// our first check and now. // our first check and now.
const existingView = this.editorViewTracker.getView( if (this.modelingStore.isDbOpen(db.databaseUri.toString())) {
db.databaseUri.toString(), this.modelingEvents.fireFocusModelEditorEvent(
); db.databaseUri.toString(),
if (existingView) { );
await existingView.focusView();
return; return;
} }
@@ -231,7 +219,6 @@ export class ModelEditorModule extends DisposableObject {
this.app, this.app,
this.modelingStore, this.modelingStore,
this.modelingEvents, this.modelingEvents,
this.editorViewTracker,
this.modelConfig, this.modelConfig,
this.databaseManager, this.databaseManager,
this.cliServer, this.cliServer,

View File

@@ -1,33 +0,0 @@
import { Method } from "./method";
interface ModelEditorViewInterface {
databaseUri: string;
revealMethod(method: Method): Promise<void>;
}
export class ModelEditorViewTracker<
T extends ModelEditorViewInterface = ModelEditorViewInterface,
> {
private readonly views = new Map<string, T>();
constructor() {}
public registerView(view: T): void {
const databaseUri = view.databaseUri;
if (this.views.has(databaseUri)) {
throw new Error(`View for database ${databaseUri} already registered`);
}
this.views.set(databaseUri, view);
}
public unregisterView(view: T): void {
this.views.delete(view.databaseUri);
}
public getView(databaseUri: string): T | undefined {
return this.views.get(databaseUri);
}
}

View File

@@ -44,7 +44,6 @@ import {
import { AutoModeler } from "./auto-modeler"; import { AutoModeler } from "./auto-modeler";
import { telemetryListener } from "../common/vscode/telemetry"; import { telemetryListener } from "../common/vscode/telemetry";
import { ModelingStore } from "./modeling-store"; import { ModelingStore } from "./modeling-store";
import { ModelEditorViewTracker } from "./model-editor-view-tracker";
import { ModelingEvents } from "./modeling-events"; import { ModelingEvents } from "./modeling-events";
import { getModelsAsDataLanguage, ModelsAsDataLanguage } from "./languages"; import { getModelsAsDataLanguage, ModelsAsDataLanguage } from "./languages";
import { runGenerateQueries } from "./generate"; import { runGenerateQueries } from "./generate";
@@ -60,7 +59,6 @@ export class ModelEditorView extends AbstractWebview<
protected readonly app: App, protected readonly app: App,
private readonly modelingStore: ModelingStore, private readonly modelingStore: ModelingStore,
private readonly modelingEvents: ModelingEvents, private readonly modelingEvents: ModelingEvents,
private readonly viewTracker: ModelEditorViewTracker<ModelEditorView>,
private readonly modelConfig: ModelConfigListener, private readonly modelConfig: ModelConfigListener,
private readonly databaseManager: DatabaseManager, private readonly databaseManager: DatabaseManager,
private readonly cliServer: CodeQLCliServer, private readonly cliServer: CodeQLCliServer,
@@ -79,8 +77,6 @@ export class ModelEditorView extends AbstractWebview<
this.registerToModelingEvents(); this.registerToModelingEvents();
this.registerToModelConfigEvents(); this.registerToModelConfigEvents();
this.viewTracker.registerView(this);
this.autoModeler = new AutoModeler( this.autoModeler = new AutoModeler(
app, app,
cliServer, cliServer,
@@ -166,7 +162,7 @@ export class ModelEditorView extends AbstractWebview<
} }
protected onPanelDispose(): void { protected onPanelDispose(): void {
this.viewTracker.unregisterView(this); // Nothing to do
} }
protected async onMessage(msg: FromModelEditorMessage): Promise<void> { protected async onMessage(msg: FromModelEditorMessage): Promise<void> {
@@ -573,12 +569,9 @@ export class ModelEditorView extends AbstractWebview<
return; return;
} }
let existingView = this.viewTracker.getView( const addedDbUri = addedDatabase.databaseUri.toString();
addedDatabase.databaseUri.toString(), if (this.modelingStore.isDbOpen(addedDbUri)) {
); this.modelingEvents.fireFocusModelEditorEvent(addedDbUri);
if (existingView) {
await existingView.focusView();
return; return;
} }
@@ -596,12 +589,8 @@ export class ModelEditorView extends AbstractWebview<
// Check again just before opening the editor to ensure no model editor has been opened between // Check again just before opening the editor to ensure no model editor has been opened between
// our first check and now. // our first check and now.
existingView = this.viewTracker.getView( if (this.modelingStore.isDbOpen(addedDbUri)) {
addedDatabase.databaseUri.toString(), this.modelingEvents.fireFocusModelEditorEvent(addedDbUri);
);
if (existingView) {
await existingView.focusView();
return; return;
} }
@@ -609,7 +598,6 @@ export class ModelEditorView extends AbstractWebview<
this.app, this.app,
this.modelingStore, this.modelingStore,
this.modelingEvents, this.modelingEvents,
this.viewTracker,
this.modelConfig, this.modelConfig,
this.databaseManager, this.databaseManager,
this.cliServer, this.cliServer,
@@ -742,6 +730,22 @@ export class ModelEditorView extends AbstractWebview<
} }
}), }),
); );
this.push(
this.modelingEvents.onRevealInModelEditor(async (event) => {
if (event.dbUri === this.databaseItem.databaseUri.toString()) {
await this.revealMethod(event.method);
}
}),
);
this.push(
this.modelingEvents.onFocusModelEditor(async (event) => {
if (event.dbUri === this.databaseItem.databaseUri.toString()) {
await this.focusView();
}
}),
);
} }
private registerToModelConfigEvents() { private registerToModelConfigEvents() {

View File

@@ -126,3 +126,38 @@ export function isModelAccepted(
modeledMethod.provenance !== "ai-generated" modeledMethod.provenance !== "ai-generated"
); );
} }
/**
* Calculates the new provenance for a modeled method based on the current provenance.
* @param modeledMethod The modeled method if there is one.
* @returns The new provenance.
*/
export function calculateNewProvenance(
modeledMethod: ModeledMethod | undefined,
) {
if (!modeledMethod || !modeledMethodSupportsProvenance(modeledMethod)) {
// If nothing has been modeled or the modeled method does not support
// provenance, we assume that the user has entered it manually.
return "manual";
}
switch (modeledMethod.provenance) {
case "df-generated":
// If the method has been generated and there has been a change, we assume
// that the user has manually edited it.
return "df-manual";
case "df-manual":
// If the method has had manual edits, we want the provenance to stay the same.
return "df-manual";
case "ai-generated":
// If the method has been generated and there has been a change, we assume
// that the user has manually edited it.
return "ai-manual";
case "ai-manual":
// If the method has had manual edits, we want the provenance to stay the same.
return "ai-manual";
default:
// The method has been modeled manually.
return "manual";
}
}

View File

@@ -48,6 +48,15 @@ interface InProgressMethodsChangedEvent {
readonly methods: ReadonlySet<string>; readonly methods: ReadonlySet<string>;
} }
interface RevealInModelEditorEvent {
dbUri: string;
method: Method;
}
interface FocusModelEditorEvent {
dbUri: string;
}
export class ModelingEvents extends DisposableObject { export class ModelingEvents extends DisposableObject {
public readonly onActiveDbChanged: AppEvent<void>; public readonly onActiveDbChanged: AppEvent<void>;
public readonly onDbOpened: AppEvent<DatabaseItem>; public readonly onDbOpened: AppEvent<DatabaseItem>;
@@ -59,6 +68,8 @@ export class ModelingEvents extends DisposableObject {
public readonly onModifiedMethodsChanged: AppEvent<ModifiedMethodsChangedEvent>; public readonly onModifiedMethodsChanged: AppEvent<ModifiedMethodsChangedEvent>;
public readonly onSelectedMethodChanged: AppEvent<SelectedMethodChangedEvent>; public readonly onSelectedMethodChanged: AppEvent<SelectedMethodChangedEvent>;
public readonly onInProgressMethodsChanged: AppEvent<InProgressMethodsChangedEvent>; public readonly onInProgressMethodsChanged: AppEvent<InProgressMethodsChangedEvent>;
public readonly onRevealInModelEditor: AppEvent<RevealInModelEditorEvent>;
public readonly onFocusModelEditor: AppEvent<FocusModelEditorEvent>;
private readonly onActiveDbChangedEventEmitter: AppEventEmitter<void>; private readonly onActiveDbChangedEventEmitter: AppEventEmitter<void>;
private readonly onDbOpenedEventEmitter: AppEventEmitter<DatabaseItem>; private readonly onDbOpenedEventEmitter: AppEventEmitter<DatabaseItem>;
@@ -70,6 +81,8 @@ export class ModelingEvents extends DisposableObject {
private readonly onModifiedMethodsChangedEventEmitter: AppEventEmitter<ModifiedMethodsChangedEvent>; private readonly onModifiedMethodsChangedEventEmitter: AppEventEmitter<ModifiedMethodsChangedEvent>;
private readonly onSelectedMethodChangedEventEmitter: AppEventEmitter<SelectedMethodChangedEvent>; private readonly onSelectedMethodChangedEventEmitter: AppEventEmitter<SelectedMethodChangedEvent>;
private readonly onInProgressMethodsChangedEventEmitter: AppEventEmitter<InProgressMethodsChangedEvent>; private readonly onInProgressMethodsChangedEventEmitter: AppEventEmitter<InProgressMethodsChangedEvent>;
private readonly onRevealInModelEditorEventEmitter: AppEventEmitter<RevealInModelEditorEvent>;
private readonly onFocusModelEditorEventEmitter: AppEventEmitter<FocusModelEditorEvent>;
constructor(app: App) { constructor(app: App) {
super(); super();
@@ -126,6 +139,16 @@ export class ModelingEvents extends DisposableObject {
); );
this.onInProgressMethodsChanged = this.onInProgressMethodsChanged =
this.onInProgressMethodsChangedEventEmitter.event; this.onInProgressMethodsChangedEventEmitter.event;
this.onRevealInModelEditorEventEmitter = this.push(
app.createEventEmitter<RevealInModelEditorEvent>(),
);
this.onRevealInModelEditor = this.onRevealInModelEditorEventEmitter.event;
this.onFocusModelEditorEventEmitter = this.push(
app.createEventEmitter<FocusModelEditorEvent>(),
);
this.onFocusModelEditor = this.onFocusModelEditorEventEmitter.event;
} }
public fireActiveDbChangedEvent() { public fireActiveDbChangedEvent() {
@@ -220,4 +243,17 @@ export class ModelingEvents extends DisposableObject {
methods, methods,
}); });
} }
public fireRevealInModelEditorEvent(dbUri: string, method: Method) {
this.onRevealInModelEditorEventEmitter.fire({
dbUri,
method,
});
}
public fireFocusModelEditorEvent(dbUri: string) {
this.onFocusModelEditorEventEmitter.fire({
dbUri,
});
}
} }

View File

@@ -112,6 +112,10 @@ export class ModelingStore extends DisposableObject {
return this.state.size > 0; return this.state.size > 0;
} }
public isDbOpen(dbUri: string): boolean {
return this.state.has(dbUri);
}
/** /**
* Returns the method for the given database item and method signature. * Returns the method for the given database item and method signature.
* Returns undefined if no method exists with that signature. * Returns undefined if no method exists with that signature.

View File

@@ -1,7 +1,6 @@
import { CodeQLCliServer } from "../codeql-cli/cli"; import { CodeQLCliServer } from "../codeql-cli/cli";
import { extLogger } from "../common/logging/vscode"; import { extLogger } from "../common/logging/vscode";
import { App } from "../common/app"; import { App } from "../common/app";
import { isCanary, showQueriesPanel } from "../config";
import { DisposableObject } from "../common/disposable-object"; import { DisposableObject } from "../common/disposable-object";
import { QueriesPanel } from "./queries-panel"; import { QueriesPanel } from "./queries-panel";
import { QueryDiscovery } from "./query-discovery"; import { QueryDiscovery } from "./query-discovery";
@@ -41,11 +40,6 @@ export class QueriesModule extends DisposableObject {
langauageContext: LanguageContextStore, langauageContext: LanguageContextStore,
cliServer: CodeQLCliServer, cliServer: CodeQLCliServer,
): void { ): void {
// Currently, we only want to expose the new panel when we are in canary mode
// and the user has enabled the "Show queries panel" flag.
if (!isCanary() || !showQueriesPanel()) {
return;
}
void extLogger.log("Initializing queries panel."); void extLogger.log("Initializing queries panel.");
const queryPackDiscovery = new QueryPackDiscovery(cliServer); const queryPackDiscovery = new QueryPackDiscovery(cliServer);

View File

@@ -103,6 +103,7 @@ export class TestRunner extends DisposableObject {
try { try {
const reopenedDatabase = await this.databaseManager.openDatabase( const reopenedDatabase = await this.databaseManager.openDatabase(
uri, uri,
closedDatabase.origin,
false, false,
); );
await this.databaseManager.renameDatabaseItem( await this.databaseManager.renameDatabaseItem(

View File

@@ -2,6 +2,7 @@ import * as React from "react";
import { ChangeEvent, useCallback, useMemo } from "react"; import { ChangeEvent, useCallback, useMemo } from "react";
import { import {
ModeledMethod, ModeledMethod,
calculateNewProvenance,
isModelAccepted, isModelAccepted,
modeledMethodSupportsInput, modeledMethodSupportsInput,
} from "../../model-editor/modeled-method"; } from "../../model-editor/modeled-method";
@@ -53,6 +54,7 @@ export const ModelInputDropdown = ({
onChange({ onChange({
...modeledMethod, ...modeledMethod,
provenance: calculateNewProvenance(modeledMethod),
input: target.value, input: target.value,
}); });
}, },

View File

@@ -5,6 +5,7 @@ import {
ModeledMethodKind, ModeledMethodKind,
modeledMethodSupportsKind, modeledMethodSupportsKind,
isModelAccepted, isModelAccepted,
calculateNewProvenance,
} from "../../model-editor/modeled-method"; } from "../../model-editor/modeled-method";
import { getModelsAsDataLanguage } from "../../model-editor/languages"; import { getModelsAsDataLanguage } from "../../model-editor/languages";
import { QueryLanguage } from "../../common/query-language"; import { QueryLanguage } from "../../common/query-language";
@@ -52,6 +53,7 @@ export const ModelKindDropdown = ({
onChange({ onChange({
...modeledMethod, ...modeledMethod,
provenance: calculateNewProvenance(modeledMethod),
kind, kind,
}); });
}, },

View File

@@ -2,6 +2,7 @@ import * as React from "react";
import { ChangeEvent, useCallback, useMemo } from "react"; import { ChangeEvent, useCallback, useMemo } from "react";
import { import {
ModeledMethod, ModeledMethod,
calculateNewProvenance,
isModelAccepted, isModelAccepted,
modeledMethodSupportsOutput, modeledMethodSupportsOutput,
} from "../../model-editor/modeled-method"; } from "../../model-editor/modeled-method";
@@ -54,6 +55,7 @@ export const ModelOutputDropdown = ({
onChange({ onChange({
...modeledMethod, ...modeledMethod,
provenance: calculateNewProvenance(modeledMethod),
output: target.value, output: target.value,
}); });
}, },

View File

@@ -1,11 +1,10 @@
import * as React from "react"; import * as React from "react";
import { ChangeEvent, useCallback } from "react"; import { ChangeEvent, useCallback } from "react";
import { import {
calculateNewProvenance,
isModelAccepted, isModelAccepted,
ModeledMethod, ModeledMethod,
modeledMethodSupportsProvenance,
ModeledMethodType, ModeledMethodType,
Provenance,
} from "../../model-editor/modeled-method"; } from "../../model-editor/modeled-method";
import { Method } from "../../model-editor/method"; import { Method } from "../../model-editor/method";
import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty"; import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty";
@@ -43,15 +42,6 @@ export const ModelTypeDropdown = ({
(e: ChangeEvent<HTMLSelectElement>) => { (e: ChangeEvent<HTMLSelectElement>) => {
const modelsAsDataLanguage = getModelsAsDataLanguage(language); const modelsAsDataLanguage = getModelsAsDataLanguage(language);
let newProvenance: Provenance = "manual";
if (modeledMethod && modeledMethodSupportsProvenance(modeledMethod)) {
if (modeledMethod.provenance === "df-generated") {
newProvenance = "df-manual";
} else if (modeledMethod.provenance === "ai-generated") {
newProvenance = "ai-manual";
}
}
const emptyModeledMethod = createEmptyModeledMethod( const emptyModeledMethod = createEmptyModeledMethod(
e.target.value as ModeledMethodType, e.target.value as ModeledMethodType,
method, method,
@@ -67,7 +57,7 @@ export const ModelTypeDropdown = ({
updatedModeledMethod.output = "ReturnValue"; updatedModeledMethod.output = "ReturnValue";
} }
if ("provenance" in updatedModeledMethod) { if ("provenance" in updatedModeledMethod) {
updatedModeledMethod.provenance = newProvenance; updatedModeledMethod.provenance = calculateNewProvenance(modeledMethod);
} }
if ("kind" in updatedModeledMethod) { if ("kind" in updatedModeledMethod) {
updatedModeledMethod.kind = "value"; updatedModeledMethod.kind = "value";

View File

@@ -27,7 +27,7 @@ describe(MethodRow.name, () => {
input: "Argument[0]", input: "Argument[0]",
output: "ReturnValue", output: "ReturnValue",
kind: "taint", kind: "taint",
provenance: "df-generated", provenance: "manual",
}; };
const onChange = jest.fn(); const onChange = jest.fn();
@@ -111,6 +111,32 @@ describe(MethodRow.name, () => {
]); ]);
}); });
it("changes the provenance when the kind is changed", async () => {
const modeledMethodWithGeneratedProvenance: ModeledMethod = {
...modeledMethod,
provenance: "df-generated",
};
render({ modeledMethods: [modeledMethodWithGeneratedProvenance] });
onChange.mockReset();
expect(screen.getByRole("combobox", { name: "Kind" })).toHaveValue("taint");
await userEvent.selectOptions(
screen.getByRole("combobox", { name: "Kind" }),
"value",
);
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith(method.signature, [
{
...modeledMethod,
kind: "value",
provenance: "df-manual",
},
]);
});
it("has the correct input options", () => { it("has the correct input options", () => {
render(); render();

View File

@@ -1,5 +1,5 @@
[ [
"v2.15.1", "v2.15.2",
"v2.14.6", "v2.14.6",
"v2.13.5", "v2.13.5",
"v2.12.7", "v2.12.7",

View File

@@ -1,19 +0,0 @@
import { mockedObject } from "../../vscode-tests/utils/mocking.helpers";
import { ModelEditorViewTracker } from "../../../src/model-editor/model-editor-view-tracker";
import { ModelEditorView } from "../../../src/model-editor/model-editor-view";
export function createMockModelEditorViewTracker({
registerView = jest.fn(),
unregisterView = jest.fn(),
getView = jest.fn(),
}: {
registerView?: ModelEditorViewTracker["registerView"];
unregisterView?: ModelEditorViewTracker["unregisterView"];
getView?: ModelEditorViewTracker["getView"];
} = {}): ModelEditorViewTracker<ModelEditorView> {
return mockedObject<ModelEditorViewTracker<ModelEditorView>>({
registerView,
unregisterView,
getView,
});
}

View File

@@ -10,6 +10,8 @@ export function createMockModelingEvents({
onModeledMethodsChanged = jest.fn(), onModeledMethodsChanged = jest.fn(),
onModifiedMethodsChanged = jest.fn(), onModifiedMethodsChanged = jest.fn(),
onInProgressMethodsChanged = jest.fn(), onInProgressMethodsChanged = jest.fn(),
onRevealInModelEditor = jest.fn(),
onFocusModelEditor = jest.fn(),
}: { }: {
onActiveDbChanged?: ModelingEvents["onActiveDbChanged"]; onActiveDbChanged?: ModelingEvents["onActiveDbChanged"];
onDbClosed?: ModelingEvents["onDbClosed"]; onDbClosed?: ModelingEvents["onDbClosed"];
@@ -19,6 +21,8 @@ export function createMockModelingEvents({
onModeledMethodsChanged?: ModelingEvents["onModeledMethodsChanged"]; onModeledMethodsChanged?: ModelingEvents["onModeledMethodsChanged"];
onModifiedMethodsChanged?: ModelingEvents["onModifiedMethodsChanged"]; onModifiedMethodsChanged?: ModelingEvents["onModifiedMethodsChanged"];
onInProgressMethodsChanged?: ModelingEvents["onInProgressMethodsChanged"]; onInProgressMethodsChanged?: ModelingEvents["onInProgressMethodsChanged"];
onRevealInModelEditor?: ModelingEvents["onRevealInModelEditor"];
onFocusModelEditor?: ModelingEvents["onFocusModelEditor"];
} = {}): ModelingEvents { } = {}): ModelingEvents {
return mockedObject<ModelingEvents>({ return mockedObject<ModelingEvents>({
onActiveDbChanged, onActiveDbChanged,
@@ -29,5 +33,7 @@ export function createMockModelingEvents({
onModeledMethodsChanged, onModeledMethodsChanged,
onModifiedMethodsChanged, onModifiedMethodsChanged,
onInProgressMethodsChanged, onInProgressMethodsChanged,
onRevealInModelEditor,
onFocusModelEditor,
}); });
} }

View File

@@ -11,6 +11,9 @@ export function mockDbOptions(): FullDatabaseOptions {
return { return {
dateAdded: 123, dateAdded: 123,
language: "", language: "",
origin: {
type: "folder",
},
}; };
} }

View File

@@ -7,6 +7,7 @@ import {
SelectedDbItem, SelectedDbItem,
DB_CONFIG_VERSION, DB_CONFIG_VERSION,
} from "../../src/databases/config/db-config"; } from "../../src/databases/config/db-config";
import { DatabaseOrigin } from "../../src/databases/local-databases/database-origin";
export function createDbConfig({ export function createDbConfig({
remoteLists = [], remoteLists = [],
@@ -45,16 +46,21 @@ export function createLocalDbConfigItem({
dateAdded = faker.date.past().getTime(), dateAdded = faker.date.past().getTime(),
language = `language${faker.number.int()}`, language = `language${faker.number.int()}`,
storagePath = `storagePath${faker.number.int()}`, storagePath = `storagePath${faker.number.int()}`,
origin = {
type: "folder",
},
}: { }: {
name?: string; name?: string;
dateAdded?: number; dateAdded?: number;
language?: string; language?: string;
storagePath?: string; storagePath?: string;
origin?: DatabaseOrigin;
} = {}): LocalDatabase { } = {}): LocalDatabase {
return { return {
name, name,
dateAdded, dateAdded,
language, language,
storagePath, storagePath,
origin,
}; };
} }

View File

@@ -12,6 +12,7 @@ import {
RootLocalDbItem, RootLocalDbItem,
RootRemoteDbItem, RootRemoteDbItem,
} from "../../src/databases/db-item"; } from "../../src/databases/db-item";
import { DatabaseOrigin } from "../../src/databases/local-databases/database-origin";
// Root Remote Db Items // Root Remote Db Items
export function createRootRemoteDbItem({ export function createRootRemoteDbItem({
@@ -124,12 +125,16 @@ export function createLocalDatabaseDbItem({
language = `language${faker.number.int()}`, language = `language${faker.number.int()}`,
storagePath = `storagePath${faker.number.int()}`, storagePath = `storagePath${faker.number.int()}`,
selected = false, selected = false,
origin = {
type: "folder",
},
}: { }: {
databaseName?: string; databaseName?: string;
dateAdded?: number; dateAdded?: number;
language?: string; language?: string;
storagePath?: string; storagePath?: string;
selected?: boolean; selected?: boolean;
origin?: DatabaseOrigin;
} = {}): LocalDatabaseDbItem { } = {}): LocalDatabaseDbItem {
return { return {
kind: DbItemKind.LocalDatabase, kind: DbItemKind.LocalDatabase,
@@ -138,6 +143,7 @@ export function createLocalDatabaseDbItem({
dateAdded, dateAdded,
language, language,
storagePath, storagePath,
origin,
}; };
} }

View File

@@ -57,6 +57,9 @@ describe("db item selection", () => {
dateAdded: 1234, dateAdded: 1234,
language: "javascript", language: "javascript",
storagePath: "/foo/bar", storagePath: "/foo/bar",
origin: {
type: "folder",
},
selected: true, selected: true,
}); });
}); });

View File

@@ -337,12 +337,18 @@ describe("db tree creator", () => {
dateAdded: 1668428293677, dateAdded: 1668428293677,
language: QueryLanguage.Cpp, language: QueryLanguage.Cpp,
storagePath: "/path/to/db1/", storagePath: "/path/to/db1/",
origin: {
type: "folder",
},
}, },
{ {
name: "db2", name: "db2",
dateAdded: 1668428472731, dateAdded: 1668428472731,
language: "cpp", language: "cpp",
storagePath: "/path/to/db2/", storagePath: "/path/to/db2/",
origin: {
type: "folder",
},
}, },
], ],
}, },
@@ -354,6 +360,9 @@ describe("db tree creator", () => {
dateAdded: 1668428472731, dateAdded: 1668428472731,
language: "ruby", language: "ruby",
storagePath: "/path/to/db3/", storagePath: "/path/to/db3/",
origin: {
type: "folder",
},
}, },
], ],
}, },
@@ -380,6 +389,7 @@ describe("db tree creator", () => {
databaseName: db.name, databaseName: db.name,
dateAdded: db.dateAdded, dateAdded: db.dateAdded,
language: db.language, language: db.language,
origin: db.origin,
storagePath: db.storagePath, storagePath: db.storagePath,
parentListName: dbConfig.databases.local.lists[0].name, parentListName: dbConfig.databases.local.lists[0].name,
})), })),
@@ -395,6 +405,7 @@ describe("db tree creator", () => {
databaseName: db.name, databaseName: db.name,
dateAdded: db.dateAdded, dateAdded: db.dateAdded,
language: db.language, language: db.language,
origin: db.origin,
storagePath: db.storagePath, storagePath: db.storagePath,
parentListName: dbConfig.databases.local.lists[1].name, parentListName: dbConfig.databases.local.lists[1].name,
})), })),
@@ -409,12 +420,18 @@ describe("db tree creator", () => {
dateAdded: 1668428293677, dateAdded: 1668428293677,
language: "csharp", language: "csharp",
storagePath: "/path/to/db1/", storagePath: "/path/to/db1/",
origin: {
type: "folder",
},
}, },
{ {
name: "db2", name: "db2",
dateAdded: 1668428472731, dateAdded: 1668428472731,
language: "go", language: "go",
storagePath: "/path/to/db2/", storagePath: "/path/to/db2/",
origin: {
type: "folder",
},
}, },
], ],
}); });
@@ -434,6 +451,7 @@ describe("db tree creator", () => {
databaseName: dbConfig.databases.local.databases[0].name, databaseName: dbConfig.databases.local.databases[0].name,
dateAdded: dbConfig.databases.local.databases[0].dateAdded, dateAdded: dbConfig.databases.local.databases[0].dateAdded,
language: dbConfig.databases.local.databases[0].language, language: dbConfig.databases.local.databases[0].language,
origin: dbConfig.databases.local.databases[0].origin,
storagePath: dbConfig.databases.local.databases[0].storagePath, storagePath: dbConfig.databases.local.databases[0].storagePath,
}); });
expect(localDatabaseNodes[1]).toEqual({ expect(localDatabaseNodes[1]).toEqual({
@@ -442,6 +460,7 @@ describe("db tree creator", () => {
databaseName: dbConfig.databases.local.databases[1].name, databaseName: dbConfig.databases.local.databases[1].name,
dateAdded: dbConfig.databases.local.databases[1].dateAdded, dateAdded: dbConfig.databases.local.databases[1].dateAdded,
language: dbConfig.databases.local.databases[1].language, language: dbConfig.databases.local.databases[1].language,
origin: dbConfig.databases.local.databases[1].origin,
storagePath: dbConfig.databases.local.databases[1].storagePath, storagePath: dbConfig.databases.local.databases[1].storagePath,
}); });
}); });

View File

@@ -186,12 +186,18 @@ describe("db panel rendering nodes", () => {
dateAdded: 1668428293677, dateAdded: 1668428293677,
language: QueryLanguage.Cpp, language: QueryLanguage.Cpp,
storagePath: "/path/to/db1/", storagePath: "/path/to/db1/",
origin: {
type: "folder",
},
}, },
{ {
name: "db2", name: "db2",
dateAdded: 1668428472731, dateAdded: 1668428472731,
language: QueryLanguage.Cpp, language: QueryLanguage.Cpp,
storagePath: "/path/to/db2/", storagePath: "/path/to/db2/",
origin: {
type: "folder",
},
}, },
], ],
}, },
@@ -203,6 +209,9 @@ describe("db panel rendering nodes", () => {
dateAdded: 1668428472731, dateAdded: 1668428472731,
language: "ruby", language: "ruby",
storagePath: "/path/to/db3/", storagePath: "/path/to/db3/",
origin: {
type: "folder",
},
}, },
], ],
}, },
@@ -238,6 +247,9 @@ describe("db panel rendering nodes", () => {
language: QueryLanguage.Cpp, language: QueryLanguage.Cpp,
storagePath: "/path/to/db1/", storagePath: "/path/to/db1/",
selected: false, selected: false,
origin: {
type: "folder",
},
}, },
{ {
kind: DbItemKind.LocalDatabase, kind: DbItemKind.LocalDatabase,
@@ -246,6 +258,9 @@ describe("db panel rendering nodes", () => {
language: QueryLanguage.Cpp, language: QueryLanguage.Cpp,
storagePath: "/path/to/db2/", storagePath: "/path/to/db2/",
selected: false, selected: false,
origin: {
type: "folder",
},
}, },
]); ]);
checkLocalListItem(localListItems[1], "my-list-2", [ checkLocalListItem(localListItems[1], "my-list-2", [
@@ -256,6 +271,9 @@ describe("db panel rendering nodes", () => {
language: "ruby", language: "ruby",
storagePath: "/path/to/db3/", storagePath: "/path/to/db3/",
selected: false, selected: false,
origin: {
type: "folder",
},
}, },
]); ]);
}); });
@@ -268,12 +286,18 @@ describe("db panel rendering nodes", () => {
dateAdded: 1668428293677, dateAdded: 1668428293677,
language: "csharp", language: "csharp",
storagePath: "/path/to/db1/", storagePath: "/path/to/db1/",
origin: {
type: "folder",
},
}, },
{ {
name: "db2", name: "db2",
dateAdded: 1668428472731, dateAdded: 1668428472731,
language: "go", language: "go",
storagePath: "/path/to/db2/", storagePath: "/path/to/db2/",
origin: {
type: "folder",
},
}, },
], ],
}); });
@@ -306,6 +330,9 @@ describe("db panel rendering nodes", () => {
language: "csharp", language: "csharp",
storagePath: "/path/to/db1/", storagePath: "/path/to/db1/",
selected: false, selected: false,
origin: {
type: "folder",
},
}); });
checkLocalDatabaseItem(localDatabaseItems[1], { checkLocalDatabaseItem(localDatabaseItems[1], {
kind: DbItemKind.LocalDatabase, kind: DbItemKind.LocalDatabase,
@@ -314,6 +341,9 @@ describe("db panel rendering nodes", () => {
language: "go", language: "go",
storagePath: "/path/to/db2/", storagePath: "/path/to/db2/",
selected: false, selected: false,
origin: {
type: "folder",
},
}); });
}); });
}); });

View File

@@ -597,6 +597,9 @@ describe("local databases", () => {
const options: FullDatabaseOptions = { const options: FullDatabaseOptions = {
dateAdded: 123, dateAdded: 123,
language, language,
origin: {
type: "folder",
},
}; };
mockDbItem = createMockDB(dir, options); mockDbItem = createMockDB(dir, options);
@@ -728,19 +731,41 @@ describe("local databases", () => {
}); });
it("should resolve the database contents", async () => { it("should resolve the database contents", async () => {
await databaseManager.openDatabase(mockDbItem.databaseUri); await databaseManager.openDatabase(
mockDbItem.databaseUri,
mockDbItem.origin,
);
expect(resolveDatabaseContentsSpy).toBeCalledTimes(2); expect(resolveDatabaseContentsSpy).toBeCalledTimes(2);
}); });
it("should set the database as the currently selected one", async () => { it("should set the database as the currently selected one", async () => {
await databaseManager.openDatabase(mockDbItem.databaseUri); await databaseManager.openDatabase(
mockDbItem.databaseUri,
mockDbItem.origin,
);
expect(setCurrentDatabaseItemSpy).toBeCalledTimes(1); expect(setCurrentDatabaseItemSpy).toBeCalledTimes(1);
}); });
it("should add database source archive folder", async () => { it("should not add database source archive folder when `codeQL.addingDatabases.addDatabaseSourceToWorkspace` is `false`", async () => {
await databaseManager.openDatabase(mockDbItem.databaseUri); jest.spyOn(config, "addDatabaseSourceToWorkspace").mockReturnValue(false);
await databaseManager.openDatabase(
mockDbItem.databaseUri,
mockDbItem.origin,
);
expect(addDatabaseSourceArchiveFolderSpy).toBeCalledTimes(0);
});
it("should add database source archive folder when `codeQL.addingDatabases.addDatabaseSourceToWorkspace` is `true`", async () => {
jest.spyOn(config, "addDatabaseSourceToWorkspace").mockReturnValue(true);
await databaseManager.openDatabase(
mockDbItem.databaseUri,
mockDbItem.origin,
);
expect(addDatabaseSourceArchiveFolderSpy).toBeCalledTimes(1); expect(addDatabaseSourceArchiveFolderSpy).toBeCalledTimes(1);
}); });
@@ -756,6 +781,7 @@ describe("local databases", () => {
await databaseManager.openDatabase( await databaseManager.openDatabase(
mockDbItem.databaseUri, mockDbItem.databaseUri,
mockDbItem.origin,
makeSelected, makeSelected,
nameOverride, nameOverride,
{ isTutorialDatabase }, { isTutorialDatabase },
@@ -769,7 +795,10 @@ describe("local databases", () => {
it("should create a skeleton QL pack", async () => { it("should create a skeleton QL pack", async () => {
jest.spyOn(config, "isCodespacesTemplate").mockReturnValue(true); jest.spyOn(config, "isCodespacesTemplate").mockReturnValue(true);
await databaseManager.openDatabase(mockDbItem.databaseUri); await databaseManager.openDatabase(
mockDbItem.databaseUri,
mockDbItem.origin,
);
expect(createSkeletonPacksSpy).toBeCalledTimes(1); expect(createSkeletonPacksSpy).toBeCalledTimes(1);
}); });
@@ -780,7 +809,10 @@ describe("local databases", () => {
it("should not create a skeleton QL pack", async () => { it("should not create a skeleton QL pack", async () => {
jest.spyOn(config, "isCodespacesTemplate").mockReturnValue(false); jest.spyOn(config, "isCodespacesTemplate").mockReturnValue(false);
await databaseManager.openDatabase(mockDbItem.databaseUri); await databaseManager.openDatabase(
mockDbItem.databaseUri,
mockDbItem.origin,
);
expect(createSkeletonPacksSpy).toBeCalledTimes(0); expect(createSkeletonPacksSpy).toBeCalledTimes(0);
}); });
}); });

View File

@@ -8,7 +8,11 @@ import {
findDirWithFile, findDirWithFile,
} from "../../../../src/databases/database-fetcher"; } from "../../../../src/databases/database-fetcher";
import * as Octokit from "@octokit/rest"; import * as Octokit from "@octokit/rest";
import { mockedQuickPickItem } from "../../utils/mocking.helpers"; import {
mockedObject,
mockedOctokitFunction,
mockedQuickPickItem,
} from "../../utils/mocking.helpers";
// These tests make API calls and may need extra time to complete. // These tests make API calls and may need extra time to complete.
jest.setTimeout(10000); jest.setTimeout(10000);
@@ -18,10 +22,17 @@ describe("database-fetcher", () => {
let quickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>; let quickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
const progressSpy = jest.fn(); const progressSpy = jest.fn();
const mockRequest = jest.fn(); const mockListCodeqlDatabases = mockedOctokitFunction<
const octokit: Octokit.Octokit = { "codeScanning",
request: mockRequest, "listCodeqlDatabases"
} as unknown as Octokit.Octokit; >();
const octokit = mockedObject<Octokit.Octokit>({
rest: {
codeScanning: {
listCodeqlDatabases: mockListCodeqlDatabases,
},
},
});
// We can't make the real octokit request (since we need credentials), so we mock the response. // We can't make the real octokit request (since we need credentials), so we mock the response.
const successfullMockApiResponse = { const successfullMockApiResponse = {
@@ -72,7 +83,7 @@ describe("database-fetcher", () => {
}); });
it("should convert a GitHub nwo to a database url", async () => { it("should convert a GitHub nwo to a database url", async () => {
mockRequest.mockResolvedValue(successfullMockApiResponse); mockListCodeqlDatabases.mockResolvedValue(successfullMockApiResponse);
quickPickSpy.mockResolvedValue( quickPickSpy.mockResolvedValue(
mockedQuickPickItem({ mockedQuickPickItem({
label: "JavaScript", label: "JavaScript",
@@ -93,7 +104,7 @@ describe("database-fetcher", () => {
const { databaseUrl, name, owner } = result; const { databaseUrl, name, owner } = result;
expect(databaseUrl).toBe( expect(databaseUrl).toBe(
"https://api.github.com/repos/github/codeql/code-scanning/codeql/databases/javascript", "https://api.github.com/repositories/143040428/code-scanning/codeql/databases/javascript",
); );
expect(name).toBe("codeql"); expect(name).toBe("codeql");
expect(owner).toBe("github"); expect(owner).toBe("github");
@@ -128,7 +139,7 @@ describe("database-fetcher", () => {
}, },
status: 404, status: 404,
}; };
mockRequest.mockResolvedValue(mockApiResponse); mockListCodeqlDatabases.mockResolvedValue(mockApiResponse);
const githubRepo = "foo/bar-not-real"; const githubRepo = "foo/bar-not-real";
await expect( await expect(
convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy), convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy),
@@ -142,7 +153,7 @@ describe("database-fetcher", () => {
data: [], data: [],
}; };
mockRequest.mockResolvedValue(mockApiResponse); mockListCodeqlDatabases.mockResolvedValue(mockApiResponse);
const githubRepo = "foo/bar-with-no-dbs"; const githubRepo = "foo/bar-with-no-dbs";
await expect( await expect(
convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy), convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy),
@@ -153,7 +164,7 @@ describe("database-fetcher", () => {
describe("when language is already provided", () => { describe("when language is already provided", () => {
describe("when language is valid", () => { describe("when language is valid", () => {
it("should not prompt the user", async () => { it("should not prompt the user", async () => {
mockRequest.mockResolvedValue(successfullMockApiResponse); mockListCodeqlDatabases.mockResolvedValue(successfullMockApiResponse);
const githubRepo = "github/codeql"; const githubRepo = "github/codeql";
await convertGithubNwoToDatabaseUrl( await convertGithubNwoToDatabaseUrl(
githubRepo, githubRepo,
@@ -167,7 +178,7 @@ describe("database-fetcher", () => {
describe("when language is invalid", () => { describe("when language is invalid", () => {
it("should prompt for language", async () => { it("should prompt for language", async () => {
mockRequest.mockResolvedValue(successfullMockApiResponse); mockListCodeqlDatabases.mockResolvedValue(successfullMockApiResponse);
const githubRepo = "github/codeql"; const githubRepo = "github/codeql";
await convertGithubNwoToDatabaseUrl( await convertGithubNwoToDatabaseUrl(
githubRepo, githubRepo,
@@ -182,7 +193,7 @@ describe("database-fetcher", () => {
describe("when language is not provided", () => { describe("when language is not provided", () => {
it("should prompt for language", async () => { it("should prompt for language", async () => {
mockRequest.mockResolvedValue(successfullMockApiResponse); mockListCodeqlDatabases.mockResolvedValue(successfullMockApiResponse);
const githubRepo = "github/codeql"; const githubRepo = "github/codeql";
await convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy); await convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy);
expect(quickPickSpy).toHaveBeenCalled(); expect(quickPickSpy).toHaveBeenCalled();

View File

@@ -9,7 +9,6 @@ import { mockEmptyDatabaseManager } from "../query-testing/test-runner-helpers";
import { QueryRunner } from "../../../../src/query-server"; import { QueryRunner } from "../../../../src/query-server";
import { ExtensionPack } from "../../../../src/model-editor/shared/extension-pack"; import { ExtensionPack } from "../../../../src/model-editor/shared/extension-pack";
import { createMockModelingStore } from "../../../__mocks__/model-editor/modelingStoreMock"; import { createMockModelingStore } from "../../../__mocks__/model-editor/modelingStoreMock";
import { createMockModelEditorViewTracker } from "../../../__mocks__/model-editor/modelEditorViewTrackerMock";
import { ModelConfigListener } from "../../../../src/config"; import { ModelConfigListener } from "../../../../src/config";
import { createMockModelingEvents } from "../../../__mocks__/model-editor/modelingEventsMock"; import { createMockModelingEvents } from "../../../__mocks__/model-editor/modelingEventsMock";
import { QueryLanguage } from "../../../../src/common/query-language"; import { QueryLanguage } from "../../../../src/common/query-language";
@@ -18,7 +17,6 @@ describe("ModelEditorView", () => {
const app = createMockApp({}); const app = createMockApp({});
const modelingStore = createMockModelingStore(); const modelingStore = createMockModelingStore();
const modelingEvents = createMockModelingEvents(); const modelingEvents = createMockModelingEvents();
const viewTracker = createMockModelEditorViewTracker();
const modelConfig = mockedObject<ModelConfigListener>({ const modelConfig = mockedObject<ModelConfigListener>({
onDidChangeConfiguration: jest.fn(), onDidChangeConfiguration: jest.fn(),
}); });
@@ -48,7 +46,6 @@ describe("ModelEditorView", () => {
app, app,
modelingStore, modelingStore,
modelingEvents, modelingEvents,
viewTracker,
modelConfig, modelConfig,
databaseManager, databaseManager,
cliServer, cliServer,

View File

@@ -39,12 +39,18 @@ describe("test-runner", () => {
const preTestDatabaseItem = new DatabaseItemImpl( const preTestDatabaseItem = new DatabaseItemImpl(
Uri.file("/path/to/test/dir/dir.testproj"), Uri.file("/path/to/test/dir/dir.testproj"),
undefined, undefined,
mockedObject<FullDatabaseOptions>({ displayName: "custom display name" }), mockedObject<FullDatabaseOptions>({
displayName: "custom display name",
origin: { type: "folder" },
}),
); );
const postTestDatabaseItem = new DatabaseItemImpl( const postTestDatabaseItem = new DatabaseItemImpl(
Uri.file("/path/to/test/dir/dir.testproj"), Uri.file("/path/to/test/dir/dir.testproj"),
undefined, undefined,
mockedObject<FullDatabaseOptions>({ displayName: "default name" }), mockedObject<FullDatabaseOptions>({
displayName: "default name",
origin: { type: "folder" },
}),
); );
beforeEach(() => { beforeEach(() => {
@@ -160,6 +166,7 @@ describe("test-runner", () => {
expect(openDatabaseSpy).toBeCalledTimes(1); expect(openDatabaseSpy).toBeCalledTimes(1);
expect(openDatabaseSpy).toBeCalledWith( expect(openDatabaseSpy).toBeCalledWith(
preTestDatabaseItem.databaseUri, preTestDatabaseItem.databaseUri,
preTestDatabaseItem.origin,
false, false,
); );

View File

@@ -1,5 +1,6 @@
import type { QuickPickItem, window, Uri } from "vscode"; import type { QuickPickItem, window, Uri } from "vscode";
import { DatabaseItem } from "../../../src/databases/local-databases"; import { DatabaseItem } from "../../../src/databases/local-databases";
import * as Octokit from "@octokit/rest";
export type DeepPartial<T> = T extends object export type DeepPartial<T> = T extends object
? { ? {
@@ -57,6 +58,14 @@ export function mockedObject<T extends object>(
}); });
} }
export function mockedOctokitFunction<
Namespace extends keyof Octokit.Octokit["rest"],
Name extends keyof Octokit.Octokit["rest"][Namespace],
>(): Octokit.Octokit["rest"][Namespace][Name] & jest.Mock {
const fn = jest.fn();
return fn as unknown as Octokit.Octokit["rest"][Namespace][Name] & jest.Mock;
}
export function mockDatabaseItem( export function mockDatabaseItem(
props: DeepPartial<DatabaseItem> = {}, props: DeepPartial<DatabaseItem> = {},
): DatabaseItem { ): DatabaseItem {