Merge remote-tracking branch 'origin/main' into koesie10/export-progress
This commit is contained in:
@@ -10,14 +10,53 @@ export interface DbConfigDatabases {
|
|||||||
local: LocalDbConfig;
|
local: LocalDbConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SelectedDbItem {
|
export type SelectedDbItem =
|
||||||
kind: SelectedDbItemKind;
|
| SelectedLocalUserDefinedList
|
||||||
value: string;
|
| SelectedLocalDatabase
|
||||||
}
|
| SelectedRemoteSystemDefinedList
|
||||||
|
| SelectedRemoteUserDefinedList
|
||||||
|
| SelectedRemoteOwner
|
||||||
|
| SelectedRemoteRepository;
|
||||||
|
|
||||||
export enum SelectedDbItemKind {
|
export enum SelectedDbItemKind {
|
||||||
ConfigDefined = "configDefined",
|
LocalUserDefinedList = "localUserDefinedList",
|
||||||
|
LocalDatabase = "localDatabase",
|
||||||
RemoteSystemDefinedList = "remoteSystemDefinedList",
|
RemoteSystemDefinedList = "remoteSystemDefinedList",
|
||||||
|
RemoteUserDefinedList = "remoteUserDefinedList",
|
||||||
|
RemoteOwner = "remoteOwner",
|
||||||
|
RemoteRepository = "remoteRepository",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectedLocalUserDefinedList {
|
||||||
|
kind: SelectedDbItemKind.LocalUserDefinedList;
|
||||||
|
listName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectedLocalDatabase {
|
||||||
|
kind: SelectedDbItemKind.LocalDatabase;
|
||||||
|
databaseName: string;
|
||||||
|
listName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectedRemoteSystemDefinedList {
|
||||||
|
kind: SelectedDbItemKind.RemoteSystemDefinedList;
|
||||||
|
listName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectedRemoteUserDefinedList {
|
||||||
|
kind: SelectedDbItemKind.RemoteUserDefinedList;
|
||||||
|
listName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectedRemoteOwner {
|
||||||
|
kind: SelectedDbItemKind.RemoteOwner;
|
||||||
|
ownerName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectedRemoteRepository {
|
||||||
|
kind: SelectedDbItemKind.RemoteRepository;
|
||||||
|
repositoryName: string;
|
||||||
|
listName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RemoteDbConfig {
|
export interface RemoteDbConfig {
|
||||||
@@ -70,10 +109,44 @@ export function cloneDbConfig(config: DbConfig): DbConfig {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
selected: config.selected
|
selected: config.selected
|
||||||
? {
|
? cloneDbConfigSelectedItem(config.selected)
|
||||||
kind: config.selected.kind,
|
|
||||||
value: config.selected.value,
|
|
||||||
}
|
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cloneDbConfigSelectedItem(selected: SelectedDbItem): SelectedDbItem {
|
||||||
|
switch (selected.kind) {
|
||||||
|
case SelectedDbItemKind.LocalUserDefinedList:
|
||||||
|
return {
|
||||||
|
kind: SelectedDbItemKind.LocalUserDefinedList,
|
||||||
|
listName: selected.listName,
|
||||||
|
};
|
||||||
|
case SelectedDbItemKind.LocalDatabase:
|
||||||
|
return {
|
||||||
|
kind: SelectedDbItemKind.LocalDatabase,
|
||||||
|
databaseName: selected.databaseName,
|
||||||
|
listName: selected.listName,
|
||||||
|
};
|
||||||
|
case SelectedDbItemKind.RemoteSystemDefinedList:
|
||||||
|
return {
|
||||||
|
kind: SelectedDbItemKind.RemoteSystemDefinedList,
|
||||||
|
listName: selected.listName,
|
||||||
|
};
|
||||||
|
case SelectedDbItemKind.RemoteUserDefinedList:
|
||||||
|
return {
|
||||||
|
kind: SelectedDbItemKind.RemoteUserDefinedList,
|
||||||
|
listName: selected.listName,
|
||||||
|
};
|
||||||
|
case SelectedDbItemKind.RemoteOwner:
|
||||||
|
return {
|
||||||
|
kind: SelectedDbItemKind.RemoteOwner,
|
||||||
|
ownerName: selected.ownerName,
|
||||||
|
};
|
||||||
|
case SelectedDbItemKind.RemoteRepository:
|
||||||
|
return {
|
||||||
|
kind: SelectedDbItemKind.RemoteRepository,
|
||||||
|
repositoryName: selected.repositoryName,
|
||||||
|
listName: selected.listName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,12 +20,14 @@ export type LocalDbItem = LocalListDbItem | LocalDatabaseDbItem;
|
|||||||
|
|
||||||
export interface LocalListDbItem {
|
export interface LocalListDbItem {
|
||||||
kind: DbItemKind.LocalList;
|
kind: DbItemKind.LocalList;
|
||||||
|
selected: boolean;
|
||||||
listName: string;
|
listName: string;
|
||||||
databases: LocalDatabaseDbItem[];
|
databases: LocalDatabaseDbItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LocalDatabaseDbItem {
|
export interface LocalDatabaseDbItem {
|
||||||
kind: DbItemKind.LocalDatabase;
|
kind: DbItemKind.LocalDatabase;
|
||||||
|
selected: boolean;
|
||||||
databaseName: string;
|
databaseName: string;
|
||||||
dateAdded: number;
|
dateAdded: number;
|
||||||
language: string;
|
language: string;
|
||||||
@@ -51,6 +53,7 @@ export type RemoteDbItem =
|
|||||||
|
|
||||||
export interface RemoteSystemDefinedListDbItem {
|
export interface RemoteSystemDefinedListDbItem {
|
||||||
kind: DbItemKind.RemoteSystemDefinedList;
|
kind: DbItemKind.RemoteSystemDefinedList;
|
||||||
|
selected: boolean;
|
||||||
listName: string;
|
listName: string;
|
||||||
listDisplayName: string;
|
listDisplayName: string;
|
||||||
listDescription: string;
|
listDescription: string;
|
||||||
@@ -58,16 +61,66 @@ export interface RemoteSystemDefinedListDbItem {
|
|||||||
|
|
||||||
export interface RemoteUserDefinedListDbItem {
|
export interface RemoteUserDefinedListDbItem {
|
||||||
kind: DbItemKind.RemoteUserDefinedList;
|
kind: DbItemKind.RemoteUserDefinedList;
|
||||||
|
selected: boolean;
|
||||||
listName: string;
|
listName: string;
|
||||||
repos: RemoteRepoDbItem[];
|
repos: RemoteRepoDbItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RemoteOwnerDbItem {
|
export interface RemoteOwnerDbItem {
|
||||||
kind: DbItemKind.RemoteOwner;
|
kind: DbItemKind.RemoteOwner;
|
||||||
|
selected: boolean;
|
||||||
ownerName: string;
|
ownerName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RemoteRepoDbItem {
|
export interface RemoteRepoDbItem {
|
||||||
kind: DbItemKind.RemoteRepo;
|
kind: DbItemKind.RemoteRepo;
|
||||||
|
selected: boolean;
|
||||||
repoFullName: string;
|
repoFullName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isRemoteSystemDefinedListDbItem(
|
||||||
|
dbItem: DbItem,
|
||||||
|
): dbItem is RemoteSystemDefinedListDbItem {
|
||||||
|
return dbItem.kind === DbItemKind.RemoteSystemDefinedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRemoteUserDefinedListDbItem(
|
||||||
|
dbItem: DbItem,
|
||||||
|
): dbItem is RemoteUserDefinedListDbItem {
|
||||||
|
return dbItem.kind === DbItemKind.RemoteUserDefinedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRemoteOwnerDbItem(
|
||||||
|
dbItem: DbItem,
|
||||||
|
): dbItem is RemoteOwnerDbItem {
|
||||||
|
return dbItem.kind === DbItemKind.RemoteOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRemoteRepoDbItem(dbItem: DbItem): dbItem is RemoteRepoDbItem {
|
||||||
|
return dbItem.kind === DbItemKind.RemoteRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLocalListDbItem(dbItem: DbItem): dbItem is LocalListDbItem {
|
||||||
|
return dbItem.kind === DbItemKind.LocalList;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLocalDatabaseDbItem(
|
||||||
|
dbItem: DbItem,
|
||||||
|
): dbItem is LocalDatabaseDbItem {
|
||||||
|
return dbItem.kind === DbItemKind.LocalDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SelectableDbItem = RemoteDbItem | LocalDbItem;
|
||||||
|
|
||||||
|
export function isSelectableDbItem(dbItem: DbItem): dbItem is SelectableDbItem {
|
||||||
|
return SelectableDbItemKinds.includes(dbItem.kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelectableDbItemKinds = [
|
||||||
|
DbItemKind.LocalList,
|
||||||
|
DbItemKind.LocalDatabase,
|
||||||
|
DbItemKind.RemoteSystemDefinedList,
|
||||||
|
DbItemKind.RemoteUserDefinedList,
|
||||||
|
DbItemKind.RemoteOwner,
|
||||||
|
DbItemKind.RemoteRepo,
|
||||||
|
];
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { window } from "vscode";
|
||||||
import { App, AppMode } from "../common/app";
|
import { App, AppMode } from "../common/app";
|
||||||
import { isCanary, isNewQueryRunExperienceEnabled } from "../config";
|
import { isCanary, isNewQueryRunExperienceEnabled } from "../config";
|
||||||
import { extLogger } from "../common";
|
import { extLogger } from "../common";
|
||||||
@@ -5,6 +6,7 @@ import { DisposableObject } from "../pure/disposable-object";
|
|||||||
import { DbConfigStore } from "./config/db-config-store";
|
import { DbConfigStore } from "./config/db-config-store";
|
||||||
import { DbManager } from "./db-manager";
|
import { DbManager } from "./db-manager";
|
||||||
import { DbPanel } from "./ui/db-panel";
|
import { DbPanel } from "./ui/db-panel";
|
||||||
|
import { DbSelectionDecorationProvider } from "./ui/db-selection-decoration-provider";
|
||||||
|
|
||||||
export class DbModule extends DisposableObject {
|
export class DbModule extends DisposableObject {
|
||||||
public async initialize(app: App): Promise<void> {
|
public async initialize(app: App): Promise<void> {
|
||||||
@@ -30,6 +32,10 @@ export class DbModule extends DisposableObject {
|
|||||||
|
|
||||||
this.push(dbPanel);
|
this.push(dbPanel);
|
||||||
this.push(dbConfigStore);
|
this.push(dbConfigStore);
|
||||||
|
|
||||||
|
const dbSelectionDecorationProvider = new DbSelectionDecorationProvider();
|
||||||
|
|
||||||
|
window.registerFileDecorationProvider(dbSelectionDecorationProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
LocalDatabase,
|
LocalDatabase,
|
||||||
LocalList,
|
LocalList,
|
||||||
RemoteRepositoryList,
|
RemoteRepositoryList,
|
||||||
|
SelectedDbItemKind,
|
||||||
} from "./config/db-config";
|
} from "./config/db-config";
|
||||||
import {
|
import {
|
||||||
DbItemKind,
|
DbItemKind,
|
||||||
@@ -18,16 +19,20 @@ import {
|
|||||||
|
|
||||||
export function createRemoteTree(dbConfig: DbConfig): RootRemoteDbItem {
|
export function createRemoteTree(dbConfig: DbConfig): RootRemoteDbItem {
|
||||||
const systemDefinedLists = [
|
const systemDefinedLists = [
|
||||||
createSystemDefinedList(10),
|
createSystemDefinedList(10, dbConfig),
|
||||||
createSystemDefinedList(100),
|
createSystemDefinedList(100, dbConfig),
|
||||||
createSystemDefinedList(1000),
|
createSystemDefinedList(1000, dbConfig),
|
||||||
];
|
];
|
||||||
|
|
||||||
const userDefinedRepoLists = dbConfig.databases.remote.repositoryLists.map(
|
const userDefinedRepoLists = dbConfig.databases.remote.repositoryLists.map(
|
||||||
createUserDefinedList,
|
(r) => createRemoteUserDefinedList(r, dbConfig),
|
||||||
|
);
|
||||||
|
const owners = dbConfig.databases.remote.owners.map((o) =>
|
||||||
|
createOwnerItem(o, dbConfig),
|
||||||
|
);
|
||||||
|
const repos = dbConfig.databases.remote.repositories.map((r) =>
|
||||||
|
createRepoItem(r, dbConfig),
|
||||||
);
|
);
|
||||||
const owners = dbConfig.databases.remote.owners.map(createOwnerItem);
|
|
||||||
const repos = dbConfig.databases.remote.repositories.map(createRepoItem);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: DbItemKind.RootRemote,
|
kind: DbItemKind.RootRemote,
|
||||||
@@ -41,8 +46,12 @@ export function createRemoteTree(dbConfig: DbConfig): RootRemoteDbItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createLocalTree(dbConfig: DbConfig): RootLocalDbItem {
|
export function createLocalTree(dbConfig: DbConfig): RootLocalDbItem {
|
||||||
const localLists = dbConfig.databases.local.lists.map(createLocalList);
|
const localLists = dbConfig.databases.local.lists.map((l) =>
|
||||||
const localDbs = dbConfig.databases.local.databases.map(createLocalDb);
|
createLocalList(l, dbConfig),
|
||||||
|
);
|
||||||
|
const localDbs = dbConfig.databases.local.databases.map((l) =>
|
||||||
|
createLocalDb(l, dbConfig),
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: DbItemKind.RootLocal,
|
kind: DbItemKind.RootLocal,
|
||||||
@@ -50,53 +59,105 @@ export function createLocalTree(dbConfig: DbConfig): RootLocalDbItem {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSystemDefinedList(n: number): RemoteSystemDefinedListDbItem {
|
function createSystemDefinedList(
|
||||||
|
n: number,
|
||||||
|
dbConfig: DbConfig,
|
||||||
|
): RemoteSystemDefinedListDbItem {
|
||||||
|
const listName = `top_${n}`;
|
||||||
|
|
||||||
|
const selected =
|
||||||
|
dbConfig.selected &&
|
||||||
|
dbConfig.selected.kind === SelectedDbItemKind.RemoteSystemDefinedList &&
|
||||||
|
dbConfig.selected.listName === listName;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: DbItemKind.RemoteSystemDefinedList,
|
kind: DbItemKind.RemoteSystemDefinedList,
|
||||||
listName: `top_${n}`,
|
listName,
|
||||||
listDisplayName: `Top ${n} repositories`,
|
listDisplayName: `Top ${n} repositories`,
|
||||||
listDescription: `Top ${n} repositories of a language`,
|
listDescription: `Top ${n} repositories of a language`,
|
||||||
|
selected: !!selected,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createUserDefinedList(
|
function createRemoteUserDefinedList(
|
||||||
list: RemoteRepositoryList,
|
list: RemoteRepositoryList,
|
||||||
|
dbConfig: DbConfig,
|
||||||
): RemoteUserDefinedListDbItem {
|
): RemoteUserDefinedListDbItem {
|
||||||
|
const selected =
|
||||||
|
dbConfig.selected &&
|
||||||
|
dbConfig.selected.kind === SelectedDbItemKind.RemoteUserDefinedList &&
|
||||||
|
dbConfig.selected.listName === list.name;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: DbItemKind.RemoteUserDefinedList,
|
kind: DbItemKind.RemoteUserDefinedList,
|
||||||
listName: list.name,
|
listName: list.name,
|
||||||
repos: list.repositories.map((r) => createRepoItem(r)),
|
repos: list.repositories.map((r) => createRepoItem(r, dbConfig, list.name)),
|
||||||
|
selected: !!selected,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOwnerItem(owner: string): RemoteOwnerDbItem {
|
function createOwnerItem(owner: string, dbConfig: DbConfig): RemoteOwnerDbItem {
|
||||||
|
const selected =
|
||||||
|
dbConfig.selected &&
|
||||||
|
dbConfig.selected.kind === SelectedDbItemKind.RemoteOwner &&
|
||||||
|
dbConfig.selected.ownerName === owner;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: DbItemKind.RemoteOwner,
|
kind: DbItemKind.RemoteOwner,
|
||||||
ownerName: owner,
|
ownerName: owner,
|
||||||
|
selected: !!selected,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRepoItem(repo: string): RemoteRepoDbItem {
|
function createRepoItem(
|
||||||
|
repo: string,
|
||||||
|
dbConfig: DbConfig,
|
||||||
|
listName?: string,
|
||||||
|
): RemoteRepoDbItem {
|
||||||
|
const selected =
|
||||||
|
dbConfig.selected &&
|
||||||
|
dbConfig.selected.kind === SelectedDbItemKind.RemoteRepository &&
|
||||||
|
dbConfig.selected.repositoryName === repo &&
|
||||||
|
dbConfig.selected.listName === listName;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: DbItemKind.RemoteRepo,
|
kind: DbItemKind.RemoteRepo,
|
||||||
repoFullName: repo,
|
repoFullName: repo,
|
||||||
|
selected: !!selected,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createLocalList(list: LocalList): LocalListDbItem {
|
function createLocalList(list: LocalList, dbConfig: DbConfig): LocalListDbItem {
|
||||||
|
const selected =
|
||||||
|
dbConfig.selected &&
|
||||||
|
dbConfig.selected.kind === SelectedDbItemKind.LocalUserDefinedList &&
|
||||||
|
dbConfig.selected.listName === list.name;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: DbItemKind.LocalList,
|
kind: DbItemKind.LocalList,
|
||||||
listName: list.name,
|
listName: list.name,
|
||||||
databases: list.databases.map(createLocalDb),
|
databases: list.databases.map((d) => createLocalDb(d, dbConfig, list.name)),
|
||||||
|
selected: !!selected,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createLocalDb(db: LocalDatabase): LocalDatabaseDbItem {
|
function createLocalDb(
|
||||||
|
db: LocalDatabase,
|
||||||
|
dbConfig: DbConfig,
|
||||||
|
listName?: string,
|
||||||
|
): LocalDatabaseDbItem {
|
||||||
|
const selected =
|
||||||
|
dbConfig.selected &&
|
||||||
|
dbConfig.selected.kind === SelectedDbItemKind.LocalDatabase &&
|
||||||
|
dbConfig.selected.databaseName === db.name &&
|
||||||
|
dbConfig.selected.listName === listName;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: DbItemKind.LocalDatabase,
|
kind: DbItemKind.LocalDatabase,
|
||||||
databaseName: db.name,
|
databaseName: db.name,
|
||||||
dateAdded: db.dateAdded,
|
dateAdded: db.dateAdded,
|
||||||
language: db.language,
|
language: db.language,
|
||||||
storagePath: db.storagePath,
|
storagePath: db.storagePath,
|
||||||
|
selected: !!selected,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import {
|
||||||
|
CancellationToken,
|
||||||
|
FileDecoration,
|
||||||
|
FileDecorationProvider,
|
||||||
|
ProviderResult,
|
||||||
|
Uri,
|
||||||
|
} from "vscode";
|
||||||
|
|
||||||
|
export class DbSelectionDecorationProvider implements FileDecorationProvider {
|
||||||
|
provideFileDecoration(
|
||||||
|
uri: Uri,
|
||||||
|
_token: CancellationToken,
|
||||||
|
): ProviderResult<FileDecoration> {
|
||||||
|
if (uri?.query === "selected=true") {
|
||||||
|
return {
|
||||||
|
badge: "✔",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as vscode from "vscode";
|
import * as vscode from "vscode";
|
||||||
import {
|
import {
|
||||||
DbItem,
|
DbItem,
|
||||||
|
isSelectableDbItem,
|
||||||
LocalDatabaseDbItem,
|
LocalDatabaseDbItem,
|
||||||
LocalListDbItem,
|
LocalListDbItem,
|
||||||
RemoteOwnerDbItem,
|
RemoteOwnerDbItem,
|
||||||
@@ -28,6 +29,16 @@ export class DbTreeViewItem extends vscode.TreeItem {
|
|||||||
public readonly children: DbTreeViewItem[],
|
public readonly children: DbTreeViewItem[],
|
||||||
) {
|
) {
|
||||||
super(label, collapsibleState);
|
super(label, collapsibleState);
|
||||||
|
|
||||||
|
if (dbItem && isSelectableDbItem(dbItem)) {
|
||||||
|
if (dbItem.selected) {
|
||||||
|
// Define the resource id to drive the UI to render this item as selected.
|
||||||
|
this.resourceUri = vscode.Uri.parse("codeql://databases?selected=true");
|
||||||
|
} else {
|
||||||
|
// Define a context value to drive the UI to show an action to select the item.
|
||||||
|
this.contextValue = "selectableDbItem";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { join } from "path";
|
|||||||
import { ensureDir, writeFile } from "fs-extra";
|
import { ensureDir, writeFile } from "fs-extra";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
window,
|
|
||||||
commands,
|
commands,
|
||||||
Uri,
|
|
||||||
ExtensionContext,
|
|
||||||
workspace,
|
|
||||||
ViewColumn,
|
|
||||||
CancellationToken,
|
CancellationToken,
|
||||||
|
ExtensionContext,
|
||||||
|
Uri,
|
||||||
|
ViewColumn,
|
||||||
|
window,
|
||||||
|
workspace,
|
||||||
} from "vscode";
|
} from "vscode";
|
||||||
import { Credentials } from "../authentication";
|
import { Credentials } from "../authentication";
|
||||||
import { ProgressCallback, UserCancellationException } from "../commandRunner";
|
import { ProgressCallback, UserCancellationException } from "../commandRunner";
|
||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
generateMarkdown,
|
generateMarkdown,
|
||||||
generateVariantAnalysisMarkdown,
|
generateVariantAnalysisMarkdown,
|
||||||
MarkdownFile,
|
MarkdownFile,
|
||||||
|
RepositorySummary,
|
||||||
} from "./remote-queries-markdown-generation";
|
} from "./remote-queries-markdown-generation";
|
||||||
import { RemoteQuery } from "./remote-query";
|
import { RemoteQuery } from "./remote-query";
|
||||||
import { AnalysisResults, sumAnalysesResults } from "./shared/analysis-result";
|
import { AnalysisResults, sumAnalysesResults } from "./shared/analysis-result";
|
||||||
@@ -30,6 +31,7 @@ import { assertNever } from "../pure/helpers-pure";
|
|||||||
import {
|
import {
|
||||||
VariantAnalysis,
|
VariantAnalysis,
|
||||||
VariantAnalysisScannedRepository,
|
VariantAnalysisScannedRepository,
|
||||||
|
VariantAnalysisScannedRepositoryDownloadStatus,
|
||||||
VariantAnalysisScannedRepositoryResult,
|
VariantAnalysisScannedRepositoryResult,
|
||||||
} from "./shared/variant-analysis";
|
} from "./shared/variant-analysis";
|
||||||
import {
|
import {
|
||||||
@@ -162,6 +164,10 @@ export async function exportVariantAnalysisResults(
|
|||||||
throw new UserCancellationException("Cancelled");
|
throw new UserCancellationException("Cancelled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const repoStates = await variantAnalysisManager.getRepoStates(
|
||||||
|
variantAnalysisId,
|
||||||
|
);
|
||||||
|
|
||||||
void extLogger.log(
|
void extLogger.log(
|
||||||
`Exporting variant analysis results for variant analysis with id ${variantAnalysis.id}`,
|
`Exporting variant analysis results for variant analysis with id ${variantAnalysis.id}`,
|
||||||
);
|
);
|
||||||
@@ -197,6 +203,18 @@ export async function exportVariantAnalysisResults(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const repo of repositories) {
|
for (const repo of repositories) {
|
||||||
|
const repoState = repoStates.find(
|
||||||
|
(r) => r.repositoryId === repo.repository.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Do not export if it has not yet completed or the download has not yet succeeded.
|
||||||
|
if (
|
||||||
|
repoState?.downloadStatus !==
|
||||||
|
VariantAnalysisScannedRepositoryDownloadStatus.Succeeded
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (repo.resultCount == 0) {
|
if (repo.resultCount == 0) {
|
||||||
yield [
|
yield [
|
||||||
repo,
|
repo,
|
||||||
@@ -268,11 +286,14 @@ export async function exportVariantAnalysisAnalysisResults(
|
|||||||
message: "Generating Markdown files",
|
message: "Generating Markdown files",
|
||||||
});
|
});
|
||||||
|
|
||||||
const description = buildVariantAnalysisGistDescription(variantAnalysis);
|
const { markdownFiles, summaries } = await generateVariantAnalysisMarkdown(
|
||||||
const markdownFiles = await generateVariantAnalysisMarkdown(
|
|
||||||
variantAnalysis,
|
variantAnalysis,
|
||||||
analysesResults,
|
analysesResults,
|
||||||
"gist",
|
exportFormat,
|
||||||
|
);
|
||||||
|
const description = buildVariantAnalysisGistDescription(
|
||||||
|
variantAnalysis,
|
||||||
|
summaries,
|
||||||
);
|
);
|
||||||
|
|
||||||
await exportResults(
|
await exportResults(
|
||||||
@@ -407,20 +428,16 @@ const buildGistDescription = (
|
|||||||
*/
|
*/
|
||||||
const buildVariantAnalysisGistDescription = (
|
const buildVariantAnalysisGistDescription = (
|
||||||
variantAnalysis: VariantAnalysis,
|
variantAnalysis: VariantAnalysis,
|
||||||
|
summaries: RepositorySummary[],
|
||||||
) => {
|
) => {
|
||||||
const resultCount =
|
const resultCount = summaries.reduce(
|
||||||
variantAnalysis.scannedRepos?.reduce(
|
(acc, summary) => acc + (summary.resultCount ?? 0),
|
||||||
(acc, item) => acc + (item.resultCount ?? 0),
|
0,
|
||||||
0,
|
);
|
||||||
) ?? 0;
|
|
||||||
const resultLabel = pluralize(resultCount, "result", "results");
|
const resultLabel = pluralize(resultCount, "result", "results");
|
||||||
|
|
||||||
const repositoryLabel = variantAnalysis.scannedRepos?.length
|
const repositoryLabel = summaries.length
|
||||||
? `(${pluralize(
|
? `(${pluralize(summaries.length, "repository", "repositories")})`
|
||||||
variantAnalysis.scannedRepos.length,
|
|
||||||
"repository",
|
|
||||||
"repositories",
|
|
||||||
)})`
|
|
||||||
: "";
|
: "";
|
||||||
return `${variantAnalysis.query.name} (${variantAnalysis.query.language}) ${resultLabel} ${repositoryLabel}`;
|
return `${variantAnalysis.query.name} (${variantAnalysis.query.language}) ${resultLabel} ${repositoryLabel}`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
VariantAnalysisScannedRepository,
|
VariantAnalysisScannedRepository,
|
||||||
VariantAnalysisScannedRepositoryResult,
|
VariantAnalysisScannedRepositoryResult,
|
||||||
} from "./shared/variant-analysis";
|
} from "./shared/variant-analysis";
|
||||||
|
import { RepositoryWithMetadata } from "./shared/repository";
|
||||||
|
|
||||||
export type MarkdownLinkType = "local" | "gist";
|
export type MarkdownLinkType = "local" | "gist";
|
||||||
|
|
||||||
@@ -74,6 +75,17 @@ export function generateMarkdown(
|
|||||||
return [summaryFile, ...resultsFiles];
|
return [summaryFile, ...resultsFiles];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RepositorySummary {
|
||||||
|
fileName: string;
|
||||||
|
repository: RepositoryWithMetadata;
|
||||||
|
resultCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VariantAnalysisMarkdown {
|
||||||
|
markdownFiles: MarkdownFile[];
|
||||||
|
summaries: RepositorySummary[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates markdown files with variant analysis results.
|
* Generates markdown files with variant analysis results.
|
||||||
*/
|
*/
|
||||||
@@ -83,23 +95,22 @@ export async function generateVariantAnalysisMarkdown(
|
|||||||
[VariantAnalysisScannedRepository, VariantAnalysisScannedRepositoryResult]
|
[VariantAnalysisScannedRepository, VariantAnalysisScannedRepositoryResult]
|
||||||
>,
|
>,
|
||||||
linkType: MarkdownLinkType,
|
linkType: MarkdownLinkType,
|
||||||
): Promise<MarkdownFile[]> {
|
): Promise<VariantAnalysisMarkdown> {
|
||||||
const resultsFiles: MarkdownFile[] = [];
|
const resultsFiles: MarkdownFile[] = [];
|
||||||
// Generate summary file with links to individual files
|
const summaries: RepositorySummary[] = [];
|
||||||
const summaryFile: MarkdownFile =
|
|
||||||
generateVariantAnalysisMarkdownSummary(variantAnalysis);
|
|
||||||
for await (const [scannedRepo, result] of results) {
|
for await (const [scannedRepo, result] of results) {
|
||||||
if (scannedRepo.resultCount === 0) {
|
if (!scannedRepo.resultCount || scannedRepo.resultCount === 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append nwo and results count to the summary table
|
// Append nwo and results count to the summary table
|
||||||
const fullName = scannedRepo.repository.fullName;
|
const fullName = scannedRepo.repository.fullName;
|
||||||
const fileName = createFileName(fullName);
|
const fileName = createFileName(fullName);
|
||||||
const link = createRelativeLink(fileName, linkType);
|
summaries.push({
|
||||||
summaryFile.content.push(
|
fileName,
|
||||||
`| ${fullName} | [${scannedRepo.resultCount} result(s)](${link}) |`,
|
repository: scannedRepo.repository,
|
||||||
);
|
resultCount: scannedRepo.resultCount,
|
||||||
|
});
|
||||||
|
|
||||||
// Generate individual markdown file for each repository
|
// Generate individual markdown file for each repository
|
||||||
const resultsFileContent = [`### ${scannedRepo.repository.fullName}`, ""];
|
const resultsFileContent = [`### ${scannedRepo.repository.fullName}`, ""];
|
||||||
@@ -121,7 +132,18 @@ export async function generateVariantAnalysisMarkdown(
|
|||||||
content: resultsFileContent,
|
content: resultsFileContent,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return [summaryFile, ...resultsFiles];
|
|
||||||
|
// Generate summary file with links to individual files
|
||||||
|
const summaryFile: MarkdownFile = generateVariantAnalysisMarkdownSummary(
|
||||||
|
variantAnalysis,
|
||||||
|
summaries,
|
||||||
|
linkType,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
markdownFiles: [summaryFile, ...resultsFiles],
|
||||||
|
summaries,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateMarkdownSummary(query: RemoteQuery): MarkdownFile {
|
export function generateMarkdownSummary(query: RemoteQuery): MarkdownFile {
|
||||||
@@ -147,6 +169,8 @@ export function generateMarkdownSummary(query: RemoteQuery): MarkdownFile {
|
|||||||
|
|
||||||
export function generateVariantAnalysisMarkdownSummary(
|
export function generateVariantAnalysisMarkdownSummary(
|
||||||
variantAnalysis: VariantAnalysis,
|
variantAnalysis: VariantAnalysis,
|
||||||
|
summaries: RepositorySummary[],
|
||||||
|
linkType: MarkdownLinkType,
|
||||||
): MarkdownFile {
|
): MarkdownFile {
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
// Title
|
// Title
|
||||||
@@ -165,7 +189,14 @@ export function generateVariantAnalysisMarkdownSummary(
|
|||||||
|
|
||||||
// Summary table
|
// Summary table
|
||||||
lines.push("### Summary", "", "| Repository | Results |", "| --- | --- |");
|
lines.push("### Summary", "", "| Repository | Results |", "| --- | --- |");
|
||||||
// nwo and result count will be appended to this table
|
|
||||||
|
for (const summary of summaries) {
|
||||||
|
// Append nwo and results count to the summary table
|
||||||
|
const fullName = summary.repository.fullName;
|
||||||
|
const link = createRelativeLink(summary.fileName, linkType);
|
||||||
|
lines.push(`| ${fullName} | [${summary.resultCount} result(s)](${link}) |`);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fileName: "_summary",
|
fileName: "_summary",
|
||||||
content: lines,
|
content: lines,
|
||||||
|
|||||||
@@ -13,9 +13,12 @@ import {
|
|||||||
VariantAnalysis,
|
VariantAnalysis,
|
||||||
VariantAnalysisScannedRepository,
|
VariantAnalysisScannedRepository,
|
||||||
} from "./shared/variant-analysis";
|
} from "./shared/variant-analysis";
|
||||||
|
import { VariantAnalysis as ApiVariantAnalysis } from "./gh-api/variant-analysis";
|
||||||
import { processUpdatedVariantAnalysis } from "./variant-analysis-processor";
|
import { processUpdatedVariantAnalysis } from "./variant-analysis-processor";
|
||||||
import { DisposableObject } from "../pure/disposable-object";
|
import { DisposableObject } from "../pure/disposable-object";
|
||||||
import { sleep } from "../pure/time";
|
import { sleep } from "../pure/time";
|
||||||
|
import { getErrorMessage } from "../pure/helpers-pure";
|
||||||
|
import { showAndLogWarningMessage } from "../helpers";
|
||||||
|
|
||||||
export class VariantAnalysisMonitor extends DisposableObject {
|
export class VariantAnalysisMonitor extends DisposableObject {
|
||||||
// With a sleep of 5 seconds, the maximum number of attempts takes
|
// With a sleep of 5 seconds, the maximum number of attempts takes
|
||||||
@@ -60,11 +63,19 @@ export class VariantAnalysisMonitor extends DisposableObject {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const variantAnalysisSummary = await getVariantAnalysis(
|
let variantAnalysisSummary: ApiVariantAnalysis;
|
||||||
credentials,
|
try {
|
||||||
variantAnalysis.controllerRepo.id,
|
variantAnalysisSummary = await getVariantAnalysis(
|
||||||
variantAnalysis.id,
|
credentials,
|
||||||
);
|
variantAnalysis.controllerRepo.id,
|
||||||
|
variantAnalysis.id,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
void showAndLogWarningMessage(
|
||||||
|
`Error while monitoring variant analysis: ${getErrorMessage(e)}`,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
variantAnalysis = processUpdatedVariantAnalysis(
|
variantAnalysis = processUpdatedVariantAnalysis(
|
||||||
variantAnalysis,
|
variantAnalysis,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export interface VariantAnalysisViewManager<
|
|||||||
> {
|
> {
|
||||||
registerView(view: T): void;
|
registerView(view: T): void;
|
||||||
unregisterView(view: T): void;
|
unregisterView(view: T): void;
|
||||||
|
getView(variantAnalysisId: number): T | undefined;
|
||||||
|
|
||||||
getVariantAnalysis(
|
getVariantAnalysis(
|
||||||
variantAnalysisId: number,
|
variantAnalysisId: number,
|
||||||
|
|||||||
@@ -38,6 +38,15 @@ export class VariantAnalysisViewSerializer implements WebviewPanelSerializer {
|
|||||||
|
|
||||||
const manager = await this.waitForExtensionFullyLoaded();
|
const manager = await this.waitForExtensionFullyLoaded();
|
||||||
|
|
||||||
|
const existingView = manager.getView(
|
||||||
|
variantAnalysisState.variantAnalysisId,
|
||||||
|
);
|
||||||
|
if (existingView) {
|
||||||
|
await existingView.openView();
|
||||||
|
webviewPanel.dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const view = new VariantAnalysisView(
|
const view = new VariantAnalysisView(
|
||||||
this.ctx,
|
this.ctx,
|
||||||
variantAnalysisState.variantAnalysisId,
|
variantAnalysisState.variantAnalysisId,
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ const variantAnalysis: VariantAnalysisDomainModel = {
|
|||||||
private: false,
|
private: false,
|
||||||
},
|
},
|
||||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||||
|
resultCount: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
repository: {
|
repository: {
|
||||||
|
|||||||
@@ -47,10 +47,24 @@ InProgress.args = {
|
|||||||
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
|
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const InProgressWithResults = Template.bind({});
|
||||||
|
InProgressWithResults.args = {
|
||||||
|
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
|
||||||
|
showResultActions: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InProgressWithoutDownloadedRepos = Template.bind({});
|
||||||
|
InProgressWithoutDownloadedRepos.args = {
|
||||||
|
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
|
||||||
|
showResultActions: true,
|
||||||
|
exportResultsDisabled: true,
|
||||||
|
};
|
||||||
|
|
||||||
export const Succeeded = Template.bind({});
|
export const Succeeded = Template.bind({});
|
||||||
Succeeded.args = {
|
Succeeded.args = {
|
||||||
...InProgress.args,
|
...InProgress.args,
|
||||||
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
|
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
|
||||||
|
showResultActions: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Failed = Template.bind({});
|
export const Failed = Template.bind({});
|
||||||
|
|||||||
@@ -144,6 +144,9 @@ export function VariantAnalysis({
|
|||||||
<>
|
<>
|
||||||
<VariantAnalysisHeader
|
<VariantAnalysisHeader
|
||||||
variantAnalysis={variantAnalysis}
|
variantAnalysis={variantAnalysis}
|
||||||
|
repositoryStates={repoStates}
|
||||||
|
filterSortState={filterSortState}
|
||||||
|
selectedRepositoryIds={selectedRepositoryIds}
|
||||||
onOpenQueryFileClick={openQueryFile}
|
onOpenQueryFileClick={openQueryFile}
|
||||||
onViewQueryTextClick={openQueryText}
|
onViewQueryTextClick={openQueryText}
|
||||||
onStopQueryClick={stopQuery}
|
onStopQueryClick={stopQuery}
|
||||||
|
|||||||
@@ -3,14 +3,17 @@ import styled from "styled-components";
|
|||||||
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react";
|
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react";
|
||||||
import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis";
|
import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis";
|
||||||
|
|
||||||
type Props = {
|
export type VariantAnalysisActionsProps = {
|
||||||
variantAnalysisStatus: VariantAnalysisStatus;
|
variantAnalysisStatus: VariantAnalysisStatus;
|
||||||
|
|
||||||
onStopQueryClick: () => void;
|
onStopQueryClick: () => void;
|
||||||
stopQueryDisabled?: boolean;
|
stopQueryDisabled?: boolean;
|
||||||
|
|
||||||
|
showResultActions?: boolean;
|
||||||
onCopyRepositoryListClick: () => void;
|
onCopyRepositoryListClick: () => void;
|
||||||
onExportResultsClick: () => void;
|
onExportResultsClick: () => void;
|
||||||
|
copyRepositoryListDisabled?: boolean;
|
||||||
|
exportResultsDisabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
@@ -26,12 +29,33 @@ const Button = styled(VSCodeButton)`
|
|||||||
export const VariantAnalysisActions = ({
|
export const VariantAnalysisActions = ({
|
||||||
variantAnalysisStatus,
|
variantAnalysisStatus,
|
||||||
onStopQueryClick,
|
onStopQueryClick,
|
||||||
|
stopQueryDisabled,
|
||||||
|
showResultActions,
|
||||||
onCopyRepositoryListClick,
|
onCopyRepositoryListClick,
|
||||||
onExportResultsClick,
|
onExportResultsClick,
|
||||||
stopQueryDisabled,
|
copyRepositoryListDisabled,
|
||||||
}: Props) => {
|
exportResultsDisabled,
|
||||||
|
}: VariantAnalysisActionsProps) => {
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
{showResultActions && (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
appearance="secondary"
|
||||||
|
onClick={onCopyRepositoryListClick}
|
||||||
|
disabled={copyRepositoryListDisabled}
|
||||||
|
>
|
||||||
|
Copy repository list
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
appearance="primary"
|
||||||
|
onClick={onExportResultsClick}
|
||||||
|
disabled={exportResultsDisabled}
|
||||||
|
>
|
||||||
|
Export results
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{variantAnalysisStatus === VariantAnalysisStatus.InProgress && (
|
{variantAnalysisStatus === VariantAnalysisStatus.InProgress && (
|
||||||
<Button
|
<Button
|
||||||
appearance="secondary"
|
appearance="secondary"
|
||||||
@@ -41,16 +65,6 @@ export const VariantAnalysisActions = ({
|
|||||||
Stop query
|
Stop query
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{variantAnalysisStatus === VariantAnalysisStatus.Succeeded && (
|
|
||||||
<>
|
|
||||||
<Button appearance="secondary" onClick={onCopyRepositoryListClick}>
|
|
||||||
Copy repository list
|
|
||||||
</Button>
|
|
||||||
<Button appearance="primary" onClick={onExportResultsClick}>
|
|
||||||
Export results
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,15 +6,25 @@ import {
|
|||||||
getTotalResultCount,
|
getTotalResultCount,
|
||||||
hasRepoScanCompleted,
|
hasRepoScanCompleted,
|
||||||
VariantAnalysis,
|
VariantAnalysis,
|
||||||
|
VariantAnalysisScannedRepositoryDownloadStatus,
|
||||||
|
VariantAnalysisScannedRepositoryState,
|
||||||
} from "../../remote-queries/shared/variant-analysis";
|
} from "../../remote-queries/shared/variant-analysis";
|
||||||
import { QueryDetails } from "./QueryDetails";
|
import { QueryDetails } from "./QueryDetails";
|
||||||
import { VariantAnalysisActions } from "./VariantAnalysisActions";
|
import { VariantAnalysisActions } from "./VariantAnalysisActions";
|
||||||
import { VariantAnalysisStats } from "./VariantAnalysisStats";
|
import { VariantAnalysisStats } from "./VariantAnalysisStats";
|
||||||
import { parseDate } from "../../pure/date";
|
import { parseDate } from "../../pure/date";
|
||||||
import { basename } from "../common/path";
|
import { basename } from "../common/path";
|
||||||
|
import {
|
||||||
|
defaultFilterSortState,
|
||||||
|
filterAndSortRepositoriesWithResults,
|
||||||
|
RepositoriesFilterSortState,
|
||||||
|
} from "../../pure/variant-analysis-filter-sort";
|
||||||
|
|
||||||
export type VariantAnalysisHeaderProps = {
|
export type VariantAnalysisHeaderProps = {
|
||||||
variantAnalysis: VariantAnalysis;
|
variantAnalysis: VariantAnalysis;
|
||||||
|
repositoryStates?: VariantAnalysisScannedRepositoryState[];
|
||||||
|
filterSortState?: RepositoriesFilterSortState;
|
||||||
|
selectedRepositoryIds?: number[];
|
||||||
|
|
||||||
onOpenQueryFileClick: () => void;
|
onOpenQueryFileClick: () => void;
|
||||||
onViewQueryTextClick: () => void;
|
onViewQueryTextClick: () => void;
|
||||||
@@ -40,6 +50,9 @@ const Row = styled.div`
|
|||||||
|
|
||||||
export const VariantAnalysisHeader = ({
|
export const VariantAnalysisHeader = ({
|
||||||
variantAnalysis,
|
variantAnalysis,
|
||||||
|
repositoryStates,
|
||||||
|
filterSortState,
|
||||||
|
selectedRepositoryIds,
|
||||||
onOpenQueryFileClick,
|
onOpenQueryFileClick,
|
||||||
onViewQueryTextClick,
|
onViewQueryTextClick,
|
||||||
onStopQueryClick,
|
onStopQueryClick,
|
||||||
@@ -62,6 +75,36 @@ export const VariantAnalysisHeader = ({
|
|||||||
const hasSkippedRepos = useMemo(() => {
|
const hasSkippedRepos = useMemo(() => {
|
||||||
return getSkippedRepoCount(variantAnalysis.skippedRepos) > 0;
|
return getSkippedRepoCount(variantAnalysis.skippedRepos) > 0;
|
||||||
}, [variantAnalysis.skippedRepos]);
|
}, [variantAnalysis.skippedRepos]);
|
||||||
|
const filteredRepositories = useMemo(() => {
|
||||||
|
return filterAndSortRepositoriesWithResults(variantAnalysis.scannedRepos, {
|
||||||
|
...defaultFilterSortState,
|
||||||
|
...filterSortState,
|
||||||
|
repositoryIds: selectedRepositoryIds,
|
||||||
|
});
|
||||||
|
}, [filterSortState, selectedRepositoryIds, variantAnalysis.scannedRepos]);
|
||||||
|
const hasDownloadedRepos = useMemo(() => {
|
||||||
|
const repositoryStatesById = new Map<
|
||||||
|
number,
|
||||||
|
VariantAnalysisScannedRepositoryState
|
||||||
|
>();
|
||||||
|
if (repositoryStates) {
|
||||||
|
for (const repositoryState of repositoryStates) {
|
||||||
|
repositoryStatesById.set(repositoryState.repositoryId, repositoryState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredRepositories?.some((repo) => {
|
||||||
|
return (
|
||||||
|
repositoryStatesById.get(repo.repository.id)?.downloadStatus ===
|
||||||
|
VariantAnalysisScannedRepositoryDownloadStatus.Succeeded
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}, [repositoryStates, filteredRepositories]);
|
||||||
|
const hasReposWithResults = useMemo(() => {
|
||||||
|
return filteredRepositories?.some(
|
||||||
|
(repo) => repo.resultCount && repo.resultCount > 0,
|
||||||
|
);
|
||||||
|
}, [filteredRepositories]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
@@ -74,10 +117,13 @@ export const VariantAnalysisHeader = ({
|
|||||||
/>
|
/>
|
||||||
<VariantAnalysisActions
|
<VariantAnalysisActions
|
||||||
variantAnalysisStatus={variantAnalysis.status}
|
variantAnalysisStatus={variantAnalysis.status}
|
||||||
|
showResultActions={(resultCount ?? 0) > 0}
|
||||||
onStopQueryClick={onStopQueryClick}
|
onStopQueryClick={onStopQueryClick}
|
||||||
onCopyRepositoryListClick={onCopyRepositoryListClick}
|
onCopyRepositoryListClick={onCopyRepositoryListClick}
|
||||||
onExportResultsClick={onExportResultsClick}
|
onExportResultsClick={onExportResultsClick}
|
||||||
stopQueryDisabled={!variantAnalysis.actionsWorkflowRunId}
|
stopQueryDisabled={!variantAnalysis.actionsWorkflowRunId}
|
||||||
|
exportResultsDisabled={!hasDownloadedRepos}
|
||||||
|
copyRepositoryListDisabled={!hasReposWithResults}
|
||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
<VariantAnalysisStats
|
<VariantAnalysisStats
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ import * as React from "react";
|
|||||||
import { render as reactRender, screen } from "@testing-library/react";
|
import { render as reactRender, screen } from "@testing-library/react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { VariantAnalysisStatus } from "../../../remote-queries/shared/variant-analysis";
|
import { VariantAnalysisStatus } from "../../../remote-queries/shared/variant-analysis";
|
||||||
import { VariantAnalysisActions } from "../VariantAnalysisActions";
|
import {
|
||||||
|
VariantAnalysisActions,
|
||||||
|
VariantAnalysisActionsProps,
|
||||||
|
} from "../VariantAnalysisActions";
|
||||||
|
|
||||||
describe(VariantAnalysisActions.name, () => {
|
describe(VariantAnalysisActions.name, () => {
|
||||||
const onStopQueryClick = jest.fn();
|
const onStopQueryClick = jest.fn();
|
||||||
@@ -15,51 +18,78 @@ describe(VariantAnalysisActions.name, () => {
|
|||||||
onExportResultsClick.mockReset();
|
onExportResultsClick.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
const render = (variantAnalysisStatus: VariantAnalysisStatus) =>
|
const render = (
|
||||||
|
props: Pick<VariantAnalysisActionsProps, "variantAnalysisStatus"> &
|
||||||
|
Partial<VariantAnalysisActionsProps>,
|
||||||
|
) =>
|
||||||
reactRender(
|
reactRender(
|
||||||
<VariantAnalysisActions
|
<VariantAnalysisActions
|
||||||
variantAnalysisStatus={variantAnalysisStatus}
|
|
||||||
onStopQueryClick={onStopQueryClick}
|
onStopQueryClick={onStopQueryClick}
|
||||||
onCopyRepositoryListClick={onCopyRepositoryListClick}
|
onCopyRepositoryListClick={onCopyRepositoryListClick}
|
||||||
onExportResultsClick={onExportResultsClick}
|
onExportResultsClick={onExportResultsClick}
|
||||||
|
{...props}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
it("renders 1 button when in progress", async () => {
|
it("renders 1 button when in progress", async () => {
|
||||||
const { container } = render(VariantAnalysisStatus.InProgress);
|
const { container } = render({
|
||||||
|
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
|
||||||
|
});
|
||||||
|
|
||||||
expect(container.querySelectorAll("vscode-button").length).toEqual(1);
|
expect(container.querySelectorAll("vscode-button").length).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders the stop query button when in progress", async () => {
|
it("renders the stop query button when in progress", async () => {
|
||||||
render(VariantAnalysisStatus.InProgress);
|
render({
|
||||||
|
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
|
||||||
|
});
|
||||||
|
|
||||||
await userEvent.click(screen.getByText("Stop query"));
|
await userEvent.click(screen.getByText("Stop query"));
|
||||||
expect(onStopQueryClick).toHaveBeenCalledTimes(1);
|
expect(onStopQueryClick).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders 3 buttons when in progress with results", async () => {
|
||||||
|
const { container } = render({
|
||||||
|
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
|
||||||
|
showResultActions: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll("vscode-button").length).toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
it("renders 2 buttons when succeeded", async () => {
|
it("renders 2 buttons when succeeded", async () => {
|
||||||
const { container } = render(VariantAnalysisStatus.Succeeded);
|
const { container } = render({
|
||||||
|
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
|
||||||
|
showResultActions: true,
|
||||||
|
});
|
||||||
|
|
||||||
expect(container.querySelectorAll("vscode-button").length).toEqual(2);
|
expect(container.querySelectorAll("vscode-button").length).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders the copy repository list button when succeeded", async () => {
|
it("renders the copy repository list button when succeeded", async () => {
|
||||||
render(VariantAnalysisStatus.Succeeded);
|
render({
|
||||||
|
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
|
||||||
|
showResultActions: true,
|
||||||
|
});
|
||||||
|
|
||||||
await userEvent.click(screen.getByText("Copy repository list"));
|
await userEvent.click(screen.getByText("Copy repository list"));
|
||||||
expect(onCopyRepositoryListClick).toHaveBeenCalledTimes(1);
|
expect(onCopyRepositoryListClick).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders the export results button when succeeded", async () => {
|
it("renders the export results button when succeeded", async () => {
|
||||||
render(VariantAnalysisStatus.Succeeded);
|
render({
|
||||||
|
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
|
||||||
|
showResultActions: true,
|
||||||
|
});
|
||||||
|
|
||||||
await userEvent.click(screen.getByText("Export results"));
|
await userEvent.click(screen.getByText("Export results"));
|
||||||
expect(onExportResultsClick).toHaveBeenCalledTimes(1);
|
expect(onExportResultsClick).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not render any buttons when failed", () => {
|
it("does not render any buttons when failed", () => {
|
||||||
const { container } = render(VariantAnalysisStatus.Failed);
|
const { container } = render({
|
||||||
|
variantAnalysisStatus: VariantAnalysisStatus.Failed,
|
||||||
|
});
|
||||||
|
|
||||||
expect(container.querySelectorAll("vscode-button").length).toEqual(0);
|
expect(container.querySelectorAll("vscode-button").length).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { TreeItemCollapsibleState, ThemeIcon } from "vscode";
|
import { TreeItemCollapsibleState, ThemeIcon } from "vscode";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { ensureDir, remove, writeJson } from "fs-extra";
|
import { ensureDir, remove, writeJson } from "fs-extra";
|
||||||
import { DbConfig } from "../../../databases/config/db-config";
|
import {
|
||||||
|
DbConfig,
|
||||||
|
SelectedDbItemKind,
|
||||||
|
} from "../../../databases/config/db-config";
|
||||||
import { DbManager } from "../../../databases/db-manager";
|
import { DbManager } from "../../../databases/db-manager";
|
||||||
import { DbConfigStore } from "../../../databases/config/db-config-store";
|
import { DbConfigStore } from "../../../databases/config/db-config-store";
|
||||||
import { DbTreeDataProvider } from "../../../databases/ui/db-tree-data-provider";
|
import { DbTreeDataProvider } from "../../../databases/ui/db-tree-data-provider";
|
||||||
@@ -307,6 +310,7 @@ describe("db panel", () => {
|
|||||||
dateAdded: 1668428293677,
|
dateAdded: 1668428293677,
|
||||||
language: "cpp",
|
language: "cpp",
|
||||||
storagePath: "/path/to/db1/",
|
storagePath: "/path/to/db1/",
|
||||||
|
selected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
kind: DbItemKind.LocalDatabase,
|
kind: DbItemKind.LocalDatabase,
|
||||||
@@ -314,6 +318,7 @@ describe("db panel", () => {
|
|||||||
dateAdded: 1668428472731,
|
dateAdded: 1668428472731,
|
||||||
language: "cpp",
|
language: "cpp",
|
||||||
storagePath: "/path/to/db2/",
|
storagePath: "/path/to/db2/",
|
||||||
|
selected: false,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
checkLocalListItem(localListItems[1], "my-list-2", [
|
checkLocalListItem(localListItems[1], "my-list-2", [
|
||||||
@@ -323,6 +328,7 @@ describe("db panel", () => {
|
|||||||
dateAdded: 1668428472731,
|
dateAdded: 1668428472731,
|
||||||
language: "ruby",
|
language: "ruby",
|
||||||
storagePath: "/path/to/db3/",
|
storagePath: "/path/to/db3/",
|
||||||
|
selected: false,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -381,6 +387,7 @@ describe("db panel", () => {
|
|||||||
dateAdded: 1668428293677,
|
dateAdded: 1668428293677,
|
||||||
language: "csharp",
|
language: "csharp",
|
||||||
storagePath: "/path/to/db1/",
|
storagePath: "/path/to/db1/",
|
||||||
|
selected: false,
|
||||||
});
|
});
|
||||||
checkLocalDatabaseItem(localDatabaseItems[1], {
|
checkLocalDatabaseItem(localDatabaseItems[1], {
|
||||||
kind: DbItemKind.LocalDatabase,
|
kind: DbItemKind.LocalDatabase,
|
||||||
@@ -388,9 +395,134 @@ describe("db panel", () => {
|
|||||||
dateAdded: 1668428472731,
|
dateAdded: 1668428472731,
|
||||||
language: "go",
|
language: "go",
|
||||||
storagePath: "/path/to/db2/",
|
storagePath: "/path/to/db2/",
|
||||||
|
selected: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should mark selected remote db list as selected", async () => {
|
||||||
|
const dbConfig: DbConfig = {
|
||||||
|
databases: {
|
||||||
|
remote: {
|
||||||
|
repositoryLists: [
|
||||||
|
{
|
||||||
|
name: "my-list-1",
|
||||||
|
repositories: ["owner1/repo1", "owner1/repo2"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "my-list-2",
|
||||||
|
repositories: ["owner2/repo1", "owner2/repo2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
owners: [],
|
||||||
|
repositories: [],
|
||||||
|
},
|
||||||
|
local: {
|
||||||
|
lists: [],
|
||||||
|
databases: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
kind: SelectedDbItemKind.RemoteUserDefinedList,
|
||||||
|
listName: "my-list-2",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await saveDbConfig(dbConfig);
|
||||||
|
|
||||||
|
const dbTreeItems = await dbTreeDataProvider.getChildren();
|
||||||
|
|
||||||
|
expect(dbTreeItems).toBeTruthy();
|
||||||
|
const items = dbTreeItems!;
|
||||||
|
|
||||||
|
const remoteRootNode = items[0];
|
||||||
|
expect(remoteRootNode.dbItem).toBeTruthy();
|
||||||
|
expect(remoteRootNode.dbItem?.kind).toEqual(DbItemKind.RootRemote);
|
||||||
|
|
||||||
|
const list1 = remoteRootNode.children.find(
|
||||||
|
(c) =>
|
||||||
|
c.dbItem?.kind === DbItemKind.RemoteUserDefinedList &&
|
||||||
|
c.dbItem?.listName === "my-list-1",
|
||||||
|
);
|
||||||
|
const list2 = remoteRootNode.children.find(
|
||||||
|
(c) =>
|
||||||
|
c.dbItem?.kind === DbItemKind.RemoteUserDefinedList &&
|
||||||
|
c.dbItem?.listName === "my-list-2",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(list1).toBeTruthy();
|
||||||
|
expect(list2).toBeTruthy();
|
||||||
|
expect(isTreeViewItemSelectable(list1!)).toBeTruthy();
|
||||||
|
expect(isTreeViewItemSelected(list2!)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should mark selected remote db inside list as selected", async () => {
|
||||||
|
const dbConfig: DbConfig = {
|
||||||
|
databases: {
|
||||||
|
remote: {
|
||||||
|
repositoryLists: [
|
||||||
|
{
|
||||||
|
name: "my-list-1",
|
||||||
|
repositories: ["owner1/repo1", "owner1/repo2"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "my-list-2",
|
||||||
|
repositories: ["owner1/repo1", "owner2/repo2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
owners: [],
|
||||||
|
repositories: ["owner1/repo1"],
|
||||||
|
},
|
||||||
|
local: {
|
||||||
|
lists: [],
|
||||||
|
databases: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
kind: SelectedDbItemKind.RemoteRepository,
|
||||||
|
repositoryName: "owner1/repo1",
|
||||||
|
listName: "my-list-2",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await saveDbConfig(dbConfig);
|
||||||
|
|
||||||
|
const dbTreeItems = await dbTreeDataProvider.getChildren();
|
||||||
|
|
||||||
|
expect(dbTreeItems).toBeTruthy();
|
||||||
|
const items = dbTreeItems!;
|
||||||
|
|
||||||
|
const remoteRootNode = items[0];
|
||||||
|
expect(remoteRootNode.dbItem).toBeTruthy();
|
||||||
|
expect(remoteRootNode.dbItem?.kind).toEqual(DbItemKind.RootRemote);
|
||||||
|
|
||||||
|
const list2 = remoteRootNode.children.find(
|
||||||
|
(c) =>
|
||||||
|
c.dbItem?.kind === DbItemKind.RemoteUserDefinedList &&
|
||||||
|
c.dbItem?.listName === "my-list-2",
|
||||||
|
);
|
||||||
|
expect(list2).toBeTruthy();
|
||||||
|
|
||||||
|
const repo1Node = list2?.children.find(
|
||||||
|
(c) =>
|
||||||
|
c.dbItem?.kind === DbItemKind.RemoteRepo &&
|
||||||
|
c.dbItem?.repoFullName === "owner1/repo1",
|
||||||
|
);
|
||||||
|
expect(repo1Node).toBeTruthy();
|
||||||
|
expect(isTreeViewItemSelected(repo1Node!)).toBeTruthy();
|
||||||
|
|
||||||
|
const repo2Node = list2?.children.find(
|
||||||
|
(c) =>
|
||||||
|
c.dbItem?.kind === DbItemKind.RemoteRepo &&
|
||||||
|
c.dbItem?.repoFullName === "owner2/repo2",
|
||||||
|
);
|
||||||
|
expect(repo2Node).toBeTruthy();
|
||||||
|
expect(isTreeViewItemSelectable(repo2Node!)).toBeTruthy();
|
||||||
|
|
||||||
|
for (const item of remoteRootNode.children) {
|
||||||
|
expect(isTreeViewItemSelectable(item)).toBeTruthy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
async function saveDbConfig(dbConfig: DbConfig): Promise<void> {
|
async function saveDbConfig(dbConfig: DbConfig): Promise<void> {
|
||||||
await writeJson(dbConfigFilePath, dbConfig);
|
await writeJson(dbConfigFilePath, dbConfig);
|
||||||
|
|
||||||
@@ -471,4 +603,18 @@ describe("db panel", () => {
|
|||||||
expect(item.iconPath).toEqual(new ThemeIcon("database"));
|
expect(item.iconPath).toEqual(new ThemeIcon("database"));
|
||||||
expect(item.collapsibleState).toBe(TreeItemCollapsibleState.None);
|
expect(item.collapsibleState).toBe(TreeItemCollapsibleState.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isTreeViewItemSelectable(treeViewItem: DbTreeViewItem) {
|
||||||
|
return (
|
||||||
|
treeViewItem.resourceUri === undefined &&
|
||||||
|
treeViewItem.contextValue === "selectableDbItem"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTreeViewItemSelected(treeViewItem: DbTreeViewItem) {
|
||||||
|
return (
|
||||||
|
treeViewItem.resourceUri?.query === "selected=true" &&
|
||||||
|
treeViewItem.contextValue === undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"selected": {
|
"selected": {
|
||||||
"kind": "configDefined",
|
"kind": "remoteUserDefinedList",
|
||||||
"value": "path.to.database"
|
"listName": "repoList1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ describe("db config store", () => {
|
|||||||
storagePath: "/path/to/database/",
|
storagePath: "/path/to/database/",
|
||||||
});
|
});
|
||||||
expect(config.selected).toEqual({
|
expect(config.selected).toEqual({
|
||||||
kind: "configDefined",
|
kind: "remoteUserDefinedList",
|
||||||
value: "path.to.database",
|
listName: "repoList1",
|
||||||
});
|
});
|
||||||
|
|
||||||
configStore.dispose();
|
configStore.dispose();
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
import { DbConfig } from "../../../src/databases/config/db-config";
|
import {
|
||||||
import { DbItemKind } from "../../../src/databases/db-item";
|
DbConfig,
|
||||||
|
SelectedDbItemKind,
|
||||||
|
} from "../../../src/databases/config/db-config";
|
||||||
|
import {
|
||||||
|
DbItemKind,
|
||||||
|
isRemoteOwnerDbItem,
|
||||||
|
isRemoteRepoDbItem,
|
||||||
|
isRemoteUserDefinedListDbItem,
|
||||||
|
} from "../../../src/databases/db-item";
|
||||||
import {
|
import {
|
||||||
createLocalTree,
|
createLocalTree,
|
||||||
createRemoteTree,
|
createRemoteTree,
|
||||||
@@ -29,18 +37,21 @@ describe("db tree creator", () => {
|
|||||||
expect(dbTreeRoot.children.length).toBe(3);
|
expect(dbTreeRoot.children.length).toBe(3);
|
||||||
expect(dbTreeRoot.children[0]).toEqual({
|
expect(dbTreeRoot.children[0]).toEqual({
|
||||||
kind: DbItemKind.RemoteSystemDefinedList,
|
kind: DbItemKind.RemoteSystemDefinedList,
|
||||||
|
selected: false,
|
||||||
listName: "top_10",
|
listName: "top_10",
|
||||||
listDisplayName: "Top 10 repositories",
|
listDisplayName: "Top 10 repositories",
|
||||||
listDescription: "Top 10 repositories of a language",
|
listDescription: "Top 10 repositories of a language",
|
||||||
});
|
});
|
||||||
expect(dbTreeRoot.children[1]).toEqual({
|
expect(dbTreeRoot.children[1]).toEqual({
|
||||||
kind: DbItemKind.RemoteSystemDefinedList,
|
kind: DbItemKind.RemoteSystemDefinedList,
|
||||||
|
selected: false,
|
||||||
listName: "top_100",
|
listName: "top_100",
|
||||||
listDisplayName: "Top 100 repositories",
|
listDisplayName: "Top 100 repositories",
|
||||||
listDescription: "Top 100 repositories of a language",
|
listDescription: "Top 100 repositories of a language",
|
||||||
});
|
});
|
||||||
expect(dbTreeRoot.children[2]).toEqual({
|
expect(dbTreeRoot.children[2]).toEqual({
|
||||||
kind: DbItemKind.RemoteSystemDefinedList,
|
kind: DbItemKind.RemoteSystemDefinedList,
|
||||||
|
selected: false,
|
||||||
listName: "top_1000",
|
listName: "top_1000",
|
||||||
listDisplayName: "Top 1000 repositories",
|
listDisplayName: "Top 1000 repositories",
|
||||||
listDescription: "Top 1000 repositories of a language",
|
listDescription: "Top 1000 repositories of a language",
|
||||||
@@ -76,26 +87,30 @@ describe("db tree creator", () => {
|
|||||||
expect(dbTreeRoot).toBeTruthy();
|
expect(dbTreeRoot).toBeTruthy();
|
||||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||||
const repositoryListNodes = dbTreeRoot.children.filter(
|
const repositoryListNodes = dbTreeRoot.children.filter(
|
||||||
(child) => child.kind === DbItemKind.RemoteUserDefinedList,
|
isRemoteUserDefinedListDbItem,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(repositoryListNodes.length).toBe(2);
|
expect(repositoryListNodes.length).toBe(2);
|
||||||
expect(repositoryListNodes[0]).toEqual({
|
expect(repositoryListNodes[0]).toEqual({
|
||||||
kind: DbItemKind.RemoteUserDefinedList,
|
kind: DbItemKind.RemoteUserDefinedList,
|
||||||
|
selected: false,
|
||||||
listName: dbConfig.databases.remote.repositoryLists[0].name,
|
listName: dbConfig.databases.remote.repositoryLists[0].name,
|
||||||
repos: dbConfig.databases.remote.repositoryLists[0].repositories.map(
|
repos: dbConfig.databases.remote.repositoryLists[0].repositories.map(
|
||||||
(repo) => ({
|
(repo) => ({
|
||||||
kind: DbItemKind.RemoteRepo,
|
kind: DbItemKind.RemoteRepo,
|
||||||
|
selected: false,
|
||||||
repoFullName: repo,
|
repoFullName: repo,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
expect(repositoryListNodes[1]).toEqual({
|
expect(repositoryListNodes[1]).toEqual({
|
||||||
kind: DbItemKind.RemoteUserDefinedList,
|
kind: DbItemKind.RemoteUserDefinedList,
|
||||||
|
selected: false,
|
||||||
listName: dbConfig.databases.remote.repositoryLists[1].name,
|
listName: dbConfig.databases.remote.repositoryLists[1].name,
|
||||||
repos: dbConfig.databases.remote.repositoryLists[1].repositories.map(
|
repos: dbConfig.databases.remote.repositoryLists[1].repositories.map(
|
||||||
(repo) => ({
|
(repo) => ({
|
||||||
kind: DbItemKind.RemoteRepo,
|
kind: DbItemKind.RemoteRepo,
|
||||||
|
selected: false,
|
||||||
repoFullName: repo,
|
repoFullName: repo,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -121,17 +136,17 @@ describe("db tree creator", () => {
|
|||||||
|
|
||||||
expect(dbTreeRoot).toBeTruthy();
|
expect(dbTreeRoot).toBeTruthy();
|
||||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||||
const ownerNodes = dbTreeRoot.children.filter(
|
const ownerNodes = dbTreeRoot.children.filter(isRemoteOwnerDbItem);
|
||||||
(child) => child.kind === DbItemKind.RemoteOwner,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(ownerNodes.length).toBe(2);
|
expect(ownerNodes.length).toBe(2);
|
||||||
expect(ownerNodes[0]).toEqual({
|
expect(ownerNodes[0]).toEqual({
|
||||||
kind: DbItemKind.RemoteOwner,
|
kind: DbItemKind.RemoteOwner,
|
||||||
|
selected: false,
|
||||||
ownerName: dbConfig.databases.remote.owners[0],
|
ownerName: dbConfig.databases.remote.owners[0],
|
||||||
});
|
});
|
||||||
expect(ownerNodes[1]).toEqual({
|
expect(ownerNodes[1]).toEqual({
|
||||||
kind: DbItemKind.RemoteOwner,
|
kind: DbItemKind.RemoteOwner,
|
||||||
|
selected: false,
|
||||||
ownerName: dbConfig.databases.remote.owners[1],
|
ownerName: dbConfig.databases.remote.owners[1],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -155,25 +170,171 @@ describe("db tree creator", () => {
|
|||||||
|
|
||||||
expect(dbTreeRoot).toBeTruthy();
|
expect(dbTreeRoot).toBeTruthy();
|
||||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||||
const repoNodes = dbTreeRoot.children.filter(
|
const repoNodes = dbTreeRoot.children.filter(isRemoteRepoDbItem);
|
||||||
(child) => child.kind === DbItemKind.RemoteRepo,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(repoNodes.length).toBe(3);
|
expect(repoNodes.length).toBe(3);
|
||||||
expect(repoNodes[0]).toEqual({
|
expect(repoNodes[0]).toEqual({
|
||||||
kind: DbItemKind.RemoteRepo,
|
kind: DbItemKind.RemoteRepo,
|
||||||
|
selected: false,
|
||||||
repoFullName: dbConfig.databases.remote.repositories[0],
|
repoFullName: dbConfig.databases.remote.repositories[0],
|
||||||
});
|
});
|
||||||
expect(repoNodes[1]).toEqual({
|
expect(repoNodes[1]).toEqual({
|
||||||
kind: DbItemKind.RemoteRepo,
|
kind: DbItemKind.RemoteRepo,
|
||||||
|
selected: false,
|
||||||
repoFullName: dbConfig.databases.remote.repositories[1],
|
repoFullName: dbConfig.databases.remote.repositories[1],
|
||||||
});
|
});
|
||||||
expect(repoNodes[2]).toEqual({
|
expect(repoNodes[2]).toEqual({
|
||||||
kind: DbItemKind.RemoteRepo,
|
kind: DbItemKind.RemoteRepo,
|
||||||
|
selected: false,
|
||||||
repoFullName: dbConfig.databases.remote.repositories[2],
|
repoFullName: dbConfig.databases.remote.repositories[2],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("selected db item", () => {
|
||||||
|
it("should allow selecting a remote user defined list node", () => {
|
||||||
|
const dbConfig: DbConfig = {
|
||||||
|
databases: {
|
||||||
|
remote: {
|
||||||
|
repositoryLists: [
|
||||||
|
{
|
||||||
|
name: "my-list-1",
|
||||||
|
repositories: [
|
||||||
|
"owner1/repo1",
|
||||||
|
"owner1/repo2",
|
||||||
|
"owner2/repo1",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
owners: [],
|
||||||
|
repositories: [],
|
||||||
|
},
|
||||||
|
local: {
|
||||||
|
lists: [],
|
||||||
|
databases: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
kind: SelectedDbItemKind.RemoteUserDefinedList,
|
||||||
|
listName: "my-list-1",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const dbTreeRoot = createRemoteTree(dbConfig);
|
||||||
|
|
||||||
|
expect(dbTreeRoot).toBeTruthy();
|
||||||
|
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||||
|
const repositoryListNodes = dbTreeRoot.children.filter(
|
||||||
|
(child) => child.kind === DbItemKind.RemoteUserDefinedList,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(repositoryListNodes.length).toBe(1);
|
||||||
|
expect(repositoryListNodes[0].selected).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should allow selecting a remote owner node", () => {
|
||||||
|
const dbConfig: DbConfig = {
|
||||||
|
databases: {
|
||||||
|
remote: {
|
||||||
|
repositoryLists: [],
|
||||||
|
owners: ["owner1", "owner2"],
|
||||||
|
repositories: [],
|
||||||
|
},
|
||||||
|
local: {
|
||||||
|
lists: [],
|
||||||
|
databases: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
kind: SelectedDbItemKind.RemoteOwner,
|
||||||
|
ownerName: "owner1",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const dbTreeRoot = createRemoteTree(dbConfig);
|
||||||
|
|
||||||
|
expect(dbTreeRoot).toBeTruthy();
|
||||||
|
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||||
|
const ownerNodes = dbTreeRoot.children.filter(
|
||||||
|
(child) => child.kind === DbItemKind.RemoteOwner,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(ownerNodes.length).toBe(2);
|
||||||
|
expect(ownerNodes[0].selected).toEqual(true);
|
||||||
|
expect(ownerNodes[1].selected).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should allow selecting a remote repo node", () => {
|
||||||
|
const dbConfig: DbConfig = {
|
||||||
|
databases: {
|
||||||
|
remote: {
|
||||||
|
repositoryLists: [],
|
||||||
|
owners: [],
|
||||||
|
repositories: ["owner1/repo1", "owner1/repo2"],
|
||||||
|
},
|
||||||
|
local: {
|
||||||
|
lists: [],
|
||||||
|
databases: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
kind: SelectedDbItemKind.RemoteRepository,
|
||||||
|
repositoryName: "owner1/repo2",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const dbTreeRoot = createRemoteTree(dbConfig);
|
||||||
|
|
||||||
|
expect(dbTreeRoot).toBeTruthy();
|
||||||
|
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||||
|
const repoNodes = dbTreeRoot.children.filter(isRemoteRepoDbItem);
|
||||||
|
|
||||||
|
expect(repoNodes.length).toBe(2);
|
||||||
|
expect(repoNodes[0].selected).toEqual(false);
|
||||||
|
expect(repoNodes[1].selected).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should allow selecting a remote repo in a list", () => {
|
||||||
|
const dbConfig: DbConfig = {
|
||||||
|
databases: {
|
||||||
|
remote: {
|
||||||
|
repositoryLists: [
|
||||||
|
{
|
||||||
|
name: "my-list-1",
|
||||||
|
repositories: ["owner1/repo1"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
owners: [],
|
||||||
|
repositories: ["owner1/repo2"],
|
||||||
|
},
|
||||||
|
local: {
|
||||||
|
lists: [],
|
||||||
|
databases: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
kind: SelectedDbItemKind.RemoteRepository,
|
||||||
|
listName: "my-list-1",
|
||||||
|
repositoryName: "owner1/repo1",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const dbTreeRoot = createRemoteTree(dbConfig);
|
||||||
|
|
||||||
|
expect(dbTreeRoot).toBeTruthy();
|
||||||
|
|
||||||
|
const listNodes = dbTreeRoot.children.filter(
|
||||||
|
isRemoteUserDefinedListDbItem,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(listNodes.length).toBe(1);
|
||||||
|
expect(listNodes[0].selected).toEqual(false);
|
||||||
|
expect(listNodes[0].repos.length).toBe(1);
|
||||||
|
expect(listNodes[0].repos[0].repoFullName).toBe("owner1/repo1");
|
||||||
|
expect(listNodes[0].repos[0].selected).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("createLocalTree", () => {
|
describe("createLocalTree", () => {
|
||||||
it("should build root node", () => {
|
it("should build root node", () => {
|
||||||
const dbConfig: DbConfig = {
|
const dbConfig: DbConfig = {
|
||||||
@@ -252,9 +413,11 @@ describe("db tree creator", () => {
|
|||||||
expect(localListNodes.length).toBe(2);
|
expect(localListNodes.length).toBe(2);
|
||||||
expect(localListNodes[0]).toEqual({
|
expect(localListNodes[0]).toEqual({
|
||||||
kind: DbItemKind.LocalList,
|
kind: DbItemKind.LocalList,
|
||||||
|
selected: false,
|
||||||
listName: dbConfig.databases.local.lists[0].name,
|
listName: dbConfig.databases.local.lists[0].name,
|
||||||
databases: dbConfig.databases.local.lists[0].databases.map((db) => ({
|
databases: dbConfig.databases.local.lists[0].databases.map((db) => ({
|
||||||
kind: DbItemKind.LocalDatabase,
|
kind: DbItemKind.LocalDatabase,
|
||||||
|
selected: false,
|
||||||
databaseName: db.name,
|
databaseName: db.name,
|
||||||
dateAdded: db.dateAdded,
|
dateAdded: db.dateAdded,
|
||||||
language: db.language,
|
language: db.language,
|
||||||
@@ -263,9 +426,11 @@ describe("db tree creator", () => {
|
|||||||
});
|
});
|
||||||
expect(localListNodes[1]).toEqual({
|
expect(localListNodes[1]).toEqual({
|
||||||
kind: DbItemKind.LocalList,
|
kind: DbItemKind.LocalList,
|
||||||
|
selected: false,
|
||||||
listName: dbConfig.databases.local.lists[1].name,
|
listName: dbConfig.databases.local.lists[1].name,
|
||||||
databases: dbConfig.databases.local.lists[1].databases.map((db) => ({
|
databases: dbConfig.databases.local.lists[1].databases.map((db) => ({
|
||||||
kind: DbItemKind.LocalDatabase,
|
kind: DbItemKind.LocalDatabase,
|
||||||
|
selected: false,
|
||||||
databaseName: db.name,
|
databaseName: db.name,
|
||||||
dateAdded: db.dateAdded,
|
dateAdded: db.dateAdded,
|
||||||
language: db.language,
|
language: db.language,
|
||||||
@@ -313,6 +478,7 @@ describe("db tree creator", () => {
|
|||||||
expect(localDatabaseNodes.length).toBe(2);
|
expect(localDatabaseNodes.length).toBe(2);
|
||||||
expect(localDatabaseNodes[0]).toEqual({
|
expect(localDatabaseNodes[0]).toEqual({
|
||||||
kind: DbItemKind.LocalDatabase,
|
kind: DbItemKind.LocalDatabase,
|
||||||
|
selected: false,
|
||||||
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,
|
||||||
@@ -320,6 +486,7 @@ describe("db tree creator", () => {
|
|||||||
});
|
});
|
||||||
expect(localDatabaseNodes[1]).toEqual({
|
expect(localDatabaseNodes[1]).toEqual({
|
||||||
kind: DbItemKind.LocalDatabase,
|
kind: DbItemKind.LocalDatabase,
|
||||||
|
selected: false,
|
||||||
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,
|
||||||
|
|||||||
@@ -123,17 +123,92 @@
|
|||||||
},
|
},
|
||||||
"selected": {
|
"selected": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"oneOf": [
|
||||||
"kind": {
|
{
|
||||||
"type": "string",
|
"properties": {
|
||||||
"enum": ["configDefined", "remoteSystemDefinedList"]
|
"kind": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["localUserDefinedList"]
|
||||||
|
},
|
||||||
|
"listName": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["kind", "listName"],
|
||||||
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"value": {
|
{
|
||||||
"type": "string"
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["localDatabase"]
|
||||||
|
},
|
||||||
|
"databaseName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"listName": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["kind", "databaseName"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["remoteSystemDefinedList"]
|
||||||
|
},
|
||||||
|
"listName": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["kind", "listName"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["remoteUserDefinedList"]
|
||||||
|
},
|
||||||
|
"listName": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["kind", "listName"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["remoteOwner"]
|
||||||
|
},
|
||||||
|
"ownerName": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["kind", "ownerName"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["remoteRepository"]
|
||||||
|
},
|
||||||
|
"repositoryName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"listName": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["kind", "repositoryName"],
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
"required": ["kind", "value"],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["databases"],
|
"required": ["databases"],
|
||||||
|
|||||||
Reference in New Issue
Block a user