Move DatabaseResolver to separate file

This commit is contained in:
Koen Vlaswinkel
2023-05-24 16:58:24 +02:00
parent 67983c64ca
commit 7888d210c4
3 changed files with 150 additions and 146 deletions

View File

@@ -1,19 +1,15 @@
import { pathExists, remove } from "fs-extra";
import { glob } from "glob";
import { join, basename, resolve, dirname, extname } from "path";
import { remove } from "fs-extra";
import { join, dirname, extname } from "path";
import * as vscode from "vscode";
import * as cli from "../codeql-cli/cli";
import { ExtensionContext } from "vscode";
import {
showAndLogWarningMessage,
showAndLogInformationMessage,
showAndLogExceptionWithTelemetry,
isFolderAlreadyInWorkspace,
getFirstWorkspaceFolder,
showNeverAskAgainDialog,
} from "../helpers";
import { ProgressCallback, withProgress } from "../common/vscode/progress";
import { encodeArchiveBasePath } from "../common/vscode/archive-filesystem-provider";
import { DisposableObject } from "../pure/disposable-object";
import { Logger, extLogger } from "../common";
import { asError, getErrorMessage } from "../pure/helpers-pure";
@@ -35,14 +31,11 @@ import {
PersistedDatabaseItem,
} from "./local-databases/database-item";
import { DatabaseItemImpl } from "./local-databases/database-item-impl";
import {
DatabaseContents,
DatabaseContentsWithDbScheme,
DatabaseKind,
} from "./local-databases/database-contents";
import { DatabaseResolver } from "./local-databases/database-resolver";
export { DatabaseContentsWithDbScheme } from "./local-databases/database-contents";
export { DatabaseItem } from "./local-databases/database-item";
export { DatabaseResolver } from "./local-databases/database-resolver";
/**
* databases.ts
@@ -66,136 +59,6 @@ const CURRENT_DB = "currentDatabase";
*/
const DB_LIST = "databaseList";
/**
* An error thrown when we cannot find a valid database in a putative
* database directory.
*/
class InvalidDatabaseError extends Error {}
async function findDataset(parentDirectory: string): Promise<vscode.Uri> {
/*
* Look directly in the root
*/
let dbRelativePaths = await glob("db-*/", {
cwd: parentDirectory,
});
if (dbRelativePaths.length === 0) {
/*
* Check If they are in the old location
*/
dbRelativePaths = await glob("working/db-*/", {
cwd: parentDirectory,
});
}
if (dbRelativePaths.length === 0) {
throw new InvalidDatabaseError(
`'${parentDirectory}' does not contain a dataset directory.`,
);
}
const dbAbsolutePath = join(parentDirectory, dbRelativePaths[0]);
if (dbRelativePaths.length > 1) {
void showAndLogWarningMessage(
`Found multiple dataset directories in database, using '${dbAbsolutePath}'.`,
);
}
return vscode.Uri.file(dbAbsolutePath);
}
// exported for testing
export async function findSourceArchive(
databasePath: string,
): Promise<vscode.Uri | undefined> {
const relativePaths = ["src", "output/src_archive"];
for (const relativePath of relativePaths) {
const basePath = join(databasePath, relativePath);
const zipPath = `${basePath}.zip`;
// Prefer using a zip archive over a directory.
if (await pathExists(zipPath)) {
return encodeArchiveBasePath(zipPath);
} else if (await pathExists(basePath)) {
return vscode.Uri.file(basePath);
}
}
void showAndLogInformationMessage(
`Could not find source archive for database '${databasePath}'. Assuming paths are absolute.`,
);
return undefined;
}
/** Gets the relative paths of all `.dbscheme` files in the given directory. */
async function getDbSchemeFiles(dbDirectory: string): Promise<string[]> {
return await glob("*.dbscheme", { cwd: dbDirectory });
}
export class DatabaseResolver {
public static async resolveDatabaseContents(
uri: vscode.Uri,
): Promise<DatabaseContentsWithDbScheme> {
if (uri.scheme !== "file") {
throw new Error(
`Database URI scheme '${uri.scheme}' not supported; only 'file' URIs are supported.`,
);
}
const databasePath = uri.fsPath;
if (!(await pathExists(databasePath))) {
throw new InvalidDatabaseError(
`Database '${databasePath}' does not exist.`,
);
}
const contents = await this.resolveDatabase(databasePath);
if (contents === undefined) {
throw new InvalidDatabaseError(
`'${databasePath}' is not a valid database.`,
);
}
// Look for a single dbscheme file within the database.
// This should be found in the dataset directory, regardless of the form of database.
const dbPath = contents.datasetUri.fsPath;
const dbSchemeFiles = await getDbSchemeFiles(dbPath);
if (dbSchemeFiles.length === 0) {
throw new InvalidDatabaseError(
`Database '${databasePath}' does not contain a CodeQL dbscheme under '${dbPath}'.`,
);
} else if (dbSchemeFiles.length > 1) {
throw new InvalidDatabaseError(
`Database '${databasePath}' contains multiple CodeQL dbschemes under '${dbPath}'.`,
);
} else {
const dbSchemeUri = vscode.Uri.file(resolve(dbPath, dbSchemeFiles[0]));
return {
...contents,
dbSchemeUri,
};
}
}
public static async resolveDatabase(
databasePath: string,
): Promise<DatabaseContents> {
const name = basename(databasePath);
// Look for dataset and source archive.
const datasetUri = await findDataset(databasePath);
const sourceArchiveUri = await findSourceArchive(databasePath);
return {
kind: DatabaseKind.Database,
name,
datasetUri,
sourceArchiveUri,
};
}
}
export enum DatabaseEventKind {
Add = "Add",
Remove = "Remove",

View File

@@ -14,12 +14,9 @@ import { DatabaseItem, PersistedDatabaseItem } from "./database-item";
import { isLikelyDatabaseRoot } from "../../helpers";
import { stat } from "fs-extra";
import { pathsEqual } from "../../pure/files";
import {
DatabaseChangedEvent,
DatabaseEventKind,
DatabaseResolver,
} from "../local-databases";
import { DatabaseChangedEvent, DatabaseEventKind } from "../local-databases";
import { DatabaseContents } from "./database-contents";
import { DatabaseResolver } from "./database-resolver";
export class DatabaseItemImpl implements DatabaseItem {
private _error: Error | undefined = undefined;

View File

@@ -0,0 +1,144 @@
import vscode from "vscode";
import { pathExists } from "fs-extra";
import { basename, join, resolve } from "path";
import {
DatabaseContents,
DatabaseContentsWithDbScheme,
DatabaseKind,
} from "./database-contents";
import { glob } from "glob";
import {
showAndLogInformationMessage,
showAndLogWarningMessage,
} from "../../helpers";
import { encodeArchiveBasePath } from "../../common/vscode/archive-filesystem-provider";
export class DatabaseResolver {
public static async resolveDatabaseContents(
uri: vscode.Uri,
): Promise<DatabaseContentsWithDbScheme> {
if (uri.scheme !== "file") {
throw new Error(
`Database URI scheme '${uri.scheme}' not supported; only 'file' URIs are supported.`,
);
}
const databasePath = uri.fsPath;
if (!(await pathExists(databasePath))) {
throw new InvalidDatabaseError(
`Database '${databasePath}' does not exist.`,
);
}
const contents = await this.resolveDatabase(databasePath);
if (contents === undefined) {
throw new InvalidDatabaseError(
`'${databasePath}' is not a valid database.`,
);
}
// Look for a single dbscheme file within the database.
// This should be found in the dataset directory, regardless of the form of database.
const dbPath = contents.datasetUri.fsPath;
const dbSchemeFiles = await getDbSchemeFiles(dbPath);
if (dbSchemeFiles.length === 0) {
throw new InvalidDatabaseError(
`Database '${databasePath}' does not contain a CodeQL dbscheme under '${dbPath}'.`,
);
} else if (dbSchemeFiles.length > 1) {
throw new InvalidDatabaseError(
`Database '${databasePath}' contains multiple CodeQL dbschemes under '${dbPath}'.`,
);
} else {
const dbSchemeUri = vscode.Uri.file(resolve(dbPath, dbSchemeFiles[0]));
return {
...contents,
dbSchemeUri,
};
}
}
public static async resolveDatabase(
databasePath: string,
): Promise<DatabaseContents> {
const name = basename(databasePath);
// Look for dataset and source archive.
const datasetUri = await findDataset(databasePath);
const sourceArchiveUri = await findSourceArchive(databasePath);
return {
kind: DatabaseKind.Database,
name,
datasetUri,
sourceArchiveUri,
};
}
}
/**
* An error thrown when we cannot find a valid database in a putative
* database directory.
*/
class InvalidDatabaseError extends Error {}
async function findDataset(parentDirectory: string): Promise<vscode.Uri> {
/*
* Look directly in the root
*/
let dbRelativePaths = await glob("db-*/", {
cwd: parentDirectory,
});
if (dbRelativePaths.length === 0) {
/*
* Check If they are in the old location
*/
dbRelativePaths = await glob("working/db-*/", {
cwd: parentDirectory,
});
}
if (dbRelativePaths.length === 0) {
throw new InvalidDatabaseError(
`'${parentDirectory}' does not contain a dataset directory.`,
);
}
const dbAbsolutePath = join(parentDirectory, dbRelativePaths[0]);
if (dbRelativePaths.length > 1) {
void showAndLogWarningMessage(
`Found multiple dataset directories in database, using '${dbAbsolutePath}'.`,
);
}
return vscode.Uri.file(dbAbsolutePath);
}
/** Gets the relative paths of all `.dbscheme` files in the given directory. */
async function getDbSchemeFiles(dbDirectory: string): Promise<string[]> {
return await glob("*.dbscheme", { cwd: dbDirectory });
}
// exported for testing
export async function findSourceArchive(
databasePath: string,
): Promise<vscode.Uri | undefined> {
const relativePaths = ["src", "output/src_archive"];
for (const relativePath of relativePaths) {
const basePath = join(databasePath, relativePath);
const zipPath = `${basePath}.zip`;
// Prefer using a zip archive over a directory.
if (await pathExists(zipPath)) {
return encodeArchiveBasePath(zipPath);
} else if (await pathExists(basePath)) {
return vscode.Uri.file(basePath);
}
}
void showAndLogInformationMessage(
`Could not find source archive for database '${databasePath}'. Assuming paths are absolute.`,
);
return undefined;
}