Merge branch 'koesie10/selected-copy' into koesie10/export-results-sorting-filtering
This commit is contained in:
@@ -1,8 +1,17 @@
|
||||
import { Disposable } from '../pure/disposable-object';
|
||||
import { AppEventEmitter } from './events';
|
||||
|
||||
export interface App {
|
||||
createEventEmitter<T>(): AppEventEmitter<T>;
|
||||
mode: AppMode;
|
||||
subscriptions: Disposable[];
|
||||
extensionPath: string;
|
||||
globalStoragePath: string;
|
||||
workspaceStoragePath?: string;
|
||||
}
|
||||
|
||||
export enum AppMode {
|
||||
Production = 1,
|
||||
Development = 2,
|
||||
Test = 3,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { App } from '../app';
|
||||
import { Disposable } from '../../pure/disposable-object';
|
||||
import { App, AppMode } from '../app';
|
||||
import { AppEventEmitter } from '../events';
|
||||
import { VSCodeAppEventEmitter } from './events';
|
||||
|
||||
@@ -21,6 +22,21 @@ export class ExtensionApp implements App {
|
||||
return this.extensionContext.storageUri?.fsPath;
|
||||
}
|
||||
|
||||
public get subscriptions(): Disposable[] {
|
||||
return this.extensionContext.subscriptions;
|
||||
}
|
||||
|
||||
public get mode(): AppMode {
|
||||
switch (this.extensionContext.extensionMode) {
|
||||
case vscode.ExtensionMode.Development:
|
||||
return AppMode.Development;
|
||||
case vscode.ExtensionMode.Test:
|
||||
return AppMode.Test;
|
||||
default:
|
||||
return AppMode.Production;
|
||||
}
|
||||
}
|
||||
|
||||
public createEventEmitter<T>(): AppEventEmitter<T> {
|
||||
return new VSCodeAppEventEmitter<T>();
|
||||
}
|
||||
|
||||
@@ -6,8 +6,12 @@ import { DisposableObject } from '../pure/disposable-object';
|
||||
import { DbConfigValidator } from './db-config-validator';
|
||||
import { ValueResult } from '../common/value-result';
|
||||
import { App } from '../common/app';
|
||||
import { AppEvent, AppEventEmitter } from '../common/events';
|
||||
|
||||
export class DbConfigStore extends DisposableObject {
|
||||
public readonly onDidChangeConfig: AppEvent<void>;
|
||||
private readonly onDidChangeConfigEventEmitter: AppEventEmitter<void>;
|
||||
|
||||
private readonly configPath: string;
|
||||
private readonly configValidator: DbConfigValidator;
|
||||
|
||||
@@ -25,6 +29,8 @@ export class DbConfigStore extends DisposableObject {
|
||||
this.configErrors = [];
|
||||
this.configWatcher = undefined;
|
||||
this.configValidator = new DbConfigValidator(app.extensionPath);
|
||||
this.onDidChangeConfigEventEmitter = app.createEventEmitter<void>();
|
||||
this.onDidChangeConfig = this.onDidChangeConfigEventEmitter.event;
|
||||
}
|
||||
|
||||
public async initialize(): Promise<void> {
|
||||
@@ -85,6 +91,8 @@ export class DbConfigStore extends DisposableObject {
|
||||
}
|
||||
|
||||
this.config = this.configErrors.length === 0 ? newConfig : undefined;
|
||||
|
||||
this.onDidChangeConfigEventEmitter.fire();
|
||||
}
|
||||
|
||||
private watchConfig(): void {
|
||||
@@ -95,15 +103,17 @@ export class DbConfigStore extends DisposableObject {
|
||||
|
||||
private createEmptyConfig(): DbConfig {
|
||||
return {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [],
|
||||
repositories: [],
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: [],
|
||||
},
|
||||
databases: {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [],
|
||||
repositories: [],
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: [],
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
// Contains models for the data we want to store in the database config
|
||||
|
||||
export interface DbConfig {
|
||||
databases: DbConfigDatabases;
|
||||
selected?: SelectedDbItem;
|
||||
}
|
||||
|
||||
export interface DbConfigDatabases {
|
||||
remote: RemoteDbConfig;
|
||||
local: LocalDbConfig;
|
||||
}
|
||||
|
||||
export interface SelectedDbItem {
|
||||
kind: SelectedDbItemKind;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export enum SelectedDbItemKind {
|
||||
ConfigDefined = 'configDefined',
|
||||
RemoteSystemDefinedList = 'remoteSystemDefinedList',
|
||||
}
|
||||
|
||||
export interface RemoteDbConfig {
|
||||
repositoryLists: RemoteRepositoryList[];
|
||||
owners: string[];
|
||||
@@ -35,20 +50,26 @@ export interface LocalDatabase {
|
||||
|
||||
export function cloneDbConfig(config: DbConfig): DbConfig {
|
||||
return {
|
||||
remote: {
|
||||
repositoryLists: config.remote.repositoryLists.map((list) => ({
|
||||
name: list.name,
|
||||
repositories: [...list.repositories],
|
||||
})),
|
||||
owners: [...config.remote.owners],
|
||||
repositories: [...config.remote.repositories],
|
||||
},
|
||||
local: {
|
||||
lists: config.local.lists.map((list) => ({
|
||||
name: list.name,
|
||||
databases: list.databases.map((db) => ({ ...db })),
|
||||
})),
|
||||
databases: config.local.databases.map((db) => ({ ...db })),
|
||||
databases: {
|
||||
remote: {
|
||||
repositoryLists: config.databases.remote.repositoryLists.map((list) => ({
|
||||
name: list.name,
|
||||
repositories: [...list.repositories],
|
||||
})),
|
||||
owners: [...config.databases.remote.owners],
|
||||
repositories: [...config.databases.remote.repositories],
|
||||
},
|
||||
local: {
|
||||
lists: config.databases.local.lists.map((list) => ({
|
||||
name: list.name,
|
||||
databases: list.databases.map((db) => ({ ...db })),
|
||||
})),
|
||||
databases: config.databases.local.databases.map((db) => ({ ...db })),
|
||||
},
|
||||
},
|
||||
selected: config.selected ? {
|
||||
kind: config.selected.kind,
|
||||
value: config.selected.value,
|
||||
} : undefined
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
import { App } from '../common/app';
|
||||
import { AppEvent, AppEventEmitter } from '../common/events';
|
||||
import { ValueResult } from '../common/value-result';
|
||||
import { DbConfigStore } from './db-config-store';
|
||||
import { DbItem } from './db-item';
|
||||
import { createLocalTree, createRemoteTree } from './db-tree-creator';
|
||||
|
||||
export class DbManager {
|
||||
public readonly onDbItemsChanged: AppEvent<void>;
|
||||
private readonly onDbItemsChangesEventEmitter: AppEventEmitter<void>;
|
||||
|
||||
constructor(
|
||||
app: App,
|
||||
private readonly dbConfigStore: DbConfigStore
|
||||
) {
|
||||
this.onDbItemsChangesEventEmitter = app.createEventEmitter<void>();
|
||||
this.onDbItemsChanged = this.onDbItemsChangesEventEmitter.event;
|
||||
|
||||
this.dbConfigStore.onDidChangeConfig(() => {
|
||||
this.onDbItemsChangesEventEmitter.fire();
|
||||
});
|
||||
}
|
||||
|
||||
public getDbItems(): ValueResult<DbItem[]> {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { ExtensionApp } from '../common/vscode/vscode-app';
|
||||
import { App, AppMode } from '../common/app';
|
||||
import { isCanary, isNewQueryRunExperienceEnabled } from '../config';
|
||||
import { logger } from '../logging';
|
||||
import { DisposableObject } from '../pure/disposable-object';
|
||||
@@ -8,10 +7,8 @@ import { DbManager } from './db-manager';
|
||||
import { DbPanel } from './ui/db-panel';
|
||||
|
||||
export class DbModule extends DisposableObject {
|
||||
public async initialize(
|
||||
extensionContext: vscode.ExtensionContext
|
||||
): Promise<void> {
|
||||
if (extensionContext.extensionMode !== vscode.ExtensionMode.Development ||
|
||||
public async initialize(app: App): Promise<void> {
|
||||
if (app.mode !== AppMode.Development ||
|
||||
!isCanary() ||
|
||||
!isNewQueryRunExperienceEnabled()) {
|
||||
// Currently, we only want to expose the new database panel when we
|
||||
@@ -22,25 +19,20 @@ export class DbModule extends DisposableObject {
|
||||
|
||||
void logger.log('Initializing database module');
|
||||
|
||||
const app = new ExtensionApp(extensionContext);
|
||||
|
||||
const dbConfigStore = new DbConfigStore(app);
|
||||
await dbConfigStore.initialize();
|
||||
|
||||
const dbManager = new DbManager(dbConfigStore);
|
||||
const dbManager = new DbManager(app, dbConfigStore);
|
||||
const dbPanel = new DbPanel(dbManager);
|
||||
await dbPanel.initialize();
|
||||
extensionContext.subscriptions.push(dbPanel);
|
||||
|
||||
this.push(dbPanel);
|
||||
this.push(dbConfigStore);
|
||||
}
|
||||
}
|
||||
|
||||
export async function initializeDbModule(
|
||||
extensionContext: vscode.ExtensionContext
|
||||
): Promise<DbModule> {
|
||||
export async function initializeDbModule(app: App): Promise<DbModule> {
|
||||
const dbModule = new DbModule();
|
||||
await dbModule.initialize(extensionContext);
|
||||
await dbModule.initialize(app);
|
||||
return dbModule;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ export function createRemoteTree(dbConfig: DbConfig): RootRemoteDbItem {
|
||||
createSystemDefinedList(1000)
|
||||
];
|
||||
|
||||
const userDefinedRepoLists = dbConfig.remote.repositoryLists.map(createUserDefinedList);
|
||||
const owners = dbConfig.remote.owners.map(createOwnerItem);
|
||||
const repos = dbConfig.remote.repositories.map(createRepoItem);
|
||||
const userDefinedRepoLists = dbConfig.databases.remote.repositoryLists.map(createUserDefinedList);
|
||||
const owners = dbConfig.databases.remote.owners.map(createOwnerItem);
|
||||
const repos = dbConfig.databases.remote.repositories.map(createRepoItem);
|
||||
|
||||
return {
|
||||
kind: DbItemKind.RootRemote,
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
import { ProviderResult, TreeDataProvider, TreeItem } from 'vscode';
|
||||
import { Event, EventEmitter, ProviderResult, TreeDataProvider, TreeItem } from 'vscode';
|
||||
import { createDbTreeViewItemError, DbTreeViewItem } from './db-tree-view-item';
|
||||
import { DbManager } from '../db-manager';
|
||||
import { mapDbItemToTreeViewItem } from './db-item-mapper';
|
||||
import { DisposableObject } from '../../pure/disposable-object';
|
||||
|
||||
export class DbTreeDataProvider implements TreeDataProvider<DbTreeViewItem> {
|
||||
export class DbTreeDataProvider extends DisposableObject implements TreeDataProvider<DbTreeViewItem> {
|
||||
|
||||
// This is an event to signal that there's been a change in the tree which
|
||||
// will case the view to refresh. It is part of the TreeDataProvider interface.
|
||||
public readonly onDidChangeTreeData: Event<DbTreeViewItem | undefined>;
|
||||
|
||||
private _onDidChangeTreeData = this.push(new EventEmitter<DbTreeViewItem | undefined>());
|
||||
private dbTreeItems: DbTreeViewItem[];
|
||||
|
||||
public constructor(
|
||||
private readonly dbManager: DbManager
|
||||
) {
|
||||
super();
|
||||
this.dbTreeItems = this.createTree();
|
||||
this.onDidChangeTreeData = this._onDidChangeTreeData.event;
|
||||
|
||||
dbManager.onDbItemsChanged(() => {
|
||||
this.dbTreeItems = this.createTree();
|
||||
this._onDidChangeTreeData.fire(undefined);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -120,6 +120,7 @@ import { createVariantAnalysisContentProvider } from './remote-queries/variant-a
|
||||
import { VSCodeMockGitHubApiServer } from './mocks/vscode-mock-gh-api-server';
|
||||
import { VariantAnalysisResultsManager } from './remote-queries/variant-analysis-results-manager';
|
||||
import { initializeDbModule } from './databases/db-module';
|
||||
import { ExtensionApp } from './common/vscode/vscode-app';
|
||||
import { RepositoriesFilterSortStateWithIds } from './pure/variant-analysis-filter-sort';
|
||||
|
||||
/**
|
||||
@@ -1267,7 +1268,8 @@ async function activateWithInstalledDistribution(
|
||||
void logger.log('Reading query history');
|
||||
await qhm.readQueryHistory();
|
||||
|
||||
const dbModule = await initializeDbModule(ctx);
|
||||
const app = new ExtensionApp(ctx);
|
||||
const dbModule = await initializeDbModule(app);
|
||||
ctx.subscriptions.push(dbModule);
|
||||
|
||||
void logger.log('Successfully finished extension initialization.');
|
||||
|
||||
@@ -30,7 +30,7 @@ export class MockGitHubApiServer extends DisposableObject {
|
||||
return;
|
||||
}
|
||||
|
||||
this.server.listen();
|
||||
this.server.listen({ onUnhandledRequest: 'bypass' });
|
||||
this._isListening = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -134,13 +134,9 @@ export async function exportVariantAnalysisResults(
|
||||
continue;
|
||||
}
|
||||
|
||||
let result: VariantAnalysisScannedRepositoryResult;
|
||||
|
||||
if (!variantAnalysisManager.areResultsLoaded(variantAnalysis.id, repo.repository.fullName)) {
|
||||
result = await variantAnalysisManager.loadResultsFromStorage(variantAnalysis.id, repo.repository.fullName);
|
||||
} else {
|
||||
result = await variantAnalysisManager.loadResults(variantAnalysis.id, repo.repository.fullName);
|
||||
}
|
||||
const result = await variantAnalysisManager.loadResults(variantAnalysis.id, repo.repository.fullName, {
|
||||
skipCacheStore: true,
|
||||
});
|
||||
|
||||
yield [repo, result];
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import { getErrorMessage } from '../pure/helpers-pure';
|
||||
import { VariantAnalysisView } from './variant-analysis-view';
|
||||
import { VariantAnalysisViewManager } from './variant-analysis-view-manager';
|
||||
import { VariantAnalysisResultsManager } from './variant-analysis-results-manager';
|
||||
import { LoadResultsOptions, VariantAnalysisResultsManager } from './variant-analysis-results-manager';
|
||||
import { getControllerRepo, getQueryName, prepareRemoteQueryRun } from './run-remote-query';
|
||||
import {
|
||||
processUpdatedVariantAnalysis,
|
||||
@@ -214,34 +214,13 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
|
||||
return this.variantAnalyses.size;
|
||||
}
|
||||
|
||||
public async loadResults(variantAnalysisId: number, repositoryFullName: string): Promise<VariantAnalysisScannedRepositoryResult> {
|
||||
public async loadResults(variantAnalysisId: number, repositoryFullName: string, options?: LoadResultsOptions): Promise<VariantAnalysisScannedRepositoryResult> {
|
||||
const variantAnalysis = this.variantAnalyses.get(variantAnalysisId);
|
||||
if (!variantAnalysis) {
|
||||
throw new Error(`No variant analysis with id: ${variantAnalysisId}`);
|
||||
}
|
||||
|
||||
return this.variantAnalysisResultsManager.loadResults(variantAnalysisId, this.getVariantAnalysisStorageLocation(variantAnalysisId), repositoryFullName);
|
||||
}
|
||||
|
||||
public areResultsLoaded(variantAnalysisId: number, repositoryFullName: string): boolean {
|
||||
if (!this.variantAnalyses.has(variantAnalysisId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.variantAnalysisResultsManager.areResultsLoaded(variantAnalysisId, repositoryFullName);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should only be used to temporarily get the results for a variant analysis. In general, loadResults should
|
||||
* be preferred.
|
||||
*/
|
||||
public async loadResultsFromStorage(variantAnalysisId: number, repositoryFullName: string): Promise<VariantAnalysisScannedRepositoryResult> {
|
||||
const variantAnalysis = this.variantAnalyses.get(variantAnalysisId);
|
||||
if (!variantAnalysis) {
|
||||
throw new Error(`No variant analysis with id: ${variantAnalysisId}`);
|
||||
}
|
||||
|
||||
return this.variantAnalysisResultsManager.loadResultsFromStorage(variantAnalysisId, this.getVariantAnalysisStorageLocation(variantAnalysisId), repositoryFullName);
|
||||
return this.variantAnalysisResultsManager.loadResults(variantAnalysisId, this.getVariantAnalysisStorageLocation(variantAnalysisId), repositoryFullName, options);
|
||||
}
|
||||
|
||||
private async variantAnalysisRecordExists(variantAnalysisId: number): Promise<boolean> {
|
||||
|
||||
@@ -28,6 +28,12 @@ export type ResultDownloadedEvent = {
|
||||
repoTask: VariantAnalysisRepositoryTask;
|
||||
}
|
||||
|
||||
export type LoadResultsOptions = {
|
||||
// If true, when results are loaded from storage, they will not be stored in the cache. This reduces memory usage if
|
||||
// results are only needed temporarily (e.g. for exporting results to a different format).
|
||||
skipCacheStore?: boolean;
|
||||
}
|
||||
|
||||
export class VariantAnalysisResultsManager extends DisposableObject {
|
||||
private static readonly REPO_TASK_FILENAME = 'repo_task.json';
|
||||
private static readonly RESULTS_DIRECTORY = 'results';
|
||||
@@ -86,18 +92,19 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
||||
public async loadResults(
|
||||
variantAnalysisId: number,
|
||||
variantAnalysisStoragePath: string,
|
||||
repositoryFullName: string
|
||||
repositoryFullName: string,
|
||||
options?: LoadResultsOptions,
|
||||
): Promise<VariantAnalysisScannedRepositoryResult> {
|
||||
const result = this.cachedResults.get(createCacheKey(variantAnalysisId, repositoryFullName));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return result ?? await this.loadResultsIntoMemory(variantAnalysisId, variantAnalysisStoragePath, repositoryFullName);
|
||||
}
|
||||
if (options?.skipCacheStore) {
|
||||
return this.loadResultsFromStorage(variantAnalysisId, variantAnalysisStoragePath, repositoryFullName);
|
||||
}
|
||||
|
||||
public areResultsLoaded(
|
||||
variantAnalysisId: number,
|
||||
repositoryFullName: string
|
||||
): boolean {
|
||||
return this.cachedResults.has(createCacheKey(variantAnalysisId, repositoryFullName));
|
||||
return this.loadResultsIntoMemory(variantAnalysisId, variantAnalysisStoragePath, repositoryFullName);
|
||||
}
|
||||
|
||||
private async loadResultsIntoMemory(
|
||||
@@ -111,7 +118,7 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
||||
return result;
|
||||
}
|
||||
|
||||
public async loadResultsFromStorage(
|
||||
private async loadResultsFromStorage(
|
||||
variantAnalysisId: number,
|
||||
variantAnalysisStoragePath: string,
|
||||
repositoryFullName: string,
|
||||
|
||||
@@ -31,10 +31,12 @@ describe('db panel', async () => {
|
||||
globalStoragePath,
|
||||
workspaceStoragePath
|
||||
});
|
||||
await fs.ensureDir(workspaceStoragePath);
|
||||
|
||||
const app = new ExtensionApp(extensionContext);
|
||||
|
||||
dbConfigStore = new DbConfigStore(app);
|
||||
dbManager = new DbManager(dbConfigStore);
|
||||
dbManager = new DbManager(app, dbConfigStore);
|
||||
|
||||
// Create a modified version of the DbPanel module that allows
|
||||
// us to override the creation of the DbTreeDataProvider
|
||||
@@ -63,14 +65,16 @@ describe('db panel', async () => {
|
||||
|
||||
it('should render default local and remote nodes when the config is empty', async () => {
|
||||
const dbConfig: DbConfig = {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [],
|
||||
repositories: []
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
databases: {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [],
|
||||
repositories: []
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -109,30 +113,32 @@ describe('db panel', async () => {
|
||||
|
||||
it('should render remote repository list nodes', async () => {
|
||||
const dbConfig: DbConfig = {
|
||||
remote: {
|
||||
repositoryLists: [
|
||||
{
|
||||
name: 'my-list-1',
|
||||
repositories: [
|
||||
'owner1/repo1',
|
||||
'owner1/repo2'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'my-list-2',
|
||||
repositories: [
|
||||
'owner1/repo1',
|
||||
'owner2/repo1',
|
||||
'owner2/repo2'
|
||||
]
|
||||
},
|
||||
],
|
||||
owners: [],
|
||||
repositories: []
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
databases: {
|
||||
remote: {
|
||||
repositoryLists: [
|
||||
{
|
||||
name: 'my-list-1',
|
||||
repositories: [
|
||||
'owner1/repo1',
|
||||
'owner1/repo2'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'my-list-2',
|
||||
repositories: [
|
||||
'owner1/repo1',
|
||||
'owner2/repo1',
|
||||
'owner2/repo2'
|
||||
]
|
||||
},
|
||||
],
|
||||
owners: [],
|
||||
repositories: []
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -161,14 +167,16 @@ describe('db panel', async () => {
|
||||
|
||||
it('should render owner list nodes', async () => {
|
||||
const dbConfig: DbConfig = {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: ['owner1', 'owner2'],
|
||||
repositories: []
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
databases: {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: ['owner1', 'owner2'],
|
||||
repositories: []
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -194,14 +202,16 @@ describe('db panel', async () => {
|
||||
|
||||
it('should render repository nodes', async () => {
|
||||
const dbConfig: DbConfig = {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [],
|
||||
repositories: ['owner1/repo1', 'owner1/repo2']
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
databases: {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [],
|
||||
repositories: ['owner1/repo1', 'owner1/repo2']
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -228,8 +238,10 @@ describe('db panel', async () => {
|
||||
async function saveDbConfig(dbConfig: DbConfig): Promise<void> {
|
||||
await fs.writeJson(dbConfigFilePath, dbConfig);
|
||||
|
||||
// Once we have watching of the db config, this can happen
|
||||
// at the start of the test.
|
||||
// Ideally we would just initialise the db config store at the start
|
||||
// of each test and then rely on the file watcher to update the config.
|
||||
// However, this requires adding sleep to the tests to allow for the
|
||||
// file watcher to catch up, so we instead initialise the config store here.
|
||||
await dbConfigStore.initialize();
|
||||
dbTreeDataProvider = new DbTreeDataProvider(dbManager);
|
||||
}
|
||||
|
||||
@@ -495,6 +495,96 @@ describe('query-history', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleCopyRepoList', () => {
|
||||
it('should not call a command for a local query', async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(localQueryHistory);
|
||||
|
||||
const item = localQueryHistory[4];
|
||||
await queryHistoryManager.handleCopyRepoList(item, [item]);
|
||||
|
||||
expect(executeCommandSpy).to.not.have.been.called;
|
||||
});
|
||||
|
||||
it('should copy repo list for a single remote query', async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
|
||||
const item = remoteQueryHistory[1];
|
||||
await queryHistoryManager.handleCopyRepoList(item, [item]);
|
||||
expect(executeCommandSpy).to.have.been.calledWith('codeQL.copyRepoList', item.queryId);
|
||||
});
|
||||
|
||||
it('should not copy repo list for multiple remote queries', async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
|
||||
const item1 = remoteQueryHistory[1];
|
||||
const item2 = remoteQueryHistory[3];
|
||||
await queryHistoryManager.handleCopyRepoList(item1, [item1, item2]);
|
||||
expect(executeCommandSpy).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should copy repo list for a single variant analysis', async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
|
||||
const item = variantAnalysisHistory[1];
|
||||
await queryHistoryManager.handleCopyRepoList(item, [item]);
|
||||
expect(executeCommandSpy).to.have.been.calledWith('codeQL.copyVariantAnalysisRepoList', item.variantAnalysis.id);
|
||||
});
|
||||
|
||||
it('should not copy repo list for multiple variant analyses', async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
|
||||
const item1 = variantAnalysisHistory[1];
|
||||
const item2 = variantAnalysisHistory[3];
|
||||
await queryHistoryManager.handleCopyRepoList(item1, [item1, item2]);
|
||||
expect(executeCommandSpy).not.to.have.been.called;
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleExportResults', () => {
|
||||
it('should not call a command for a local query', async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(localQueryHistory);
|
||||
|
||||
const item = localQueryHistory[4];
|
||||
await queryHistoryManager.handleExportResults(item, [item]);
|
||||
|
||||
expect(executeCommandSpy).to.not.have.been.called;
|
||||
});
|
||||
|
||||
it('should export results for a single remote query', async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
|
||||
const item = remoteQueryHistory[1];
|
||||
await queryHistoryManager.handleExportResults(item, [item]);
|
||||
expect(executeCommandSpy).to.have.been.calledWith('codeQL.exportRemoteQueryResults', item.queryId);
|
||||
});
|
||||
|
||||
it('should not export results for multiple remote queries', async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
|
||||
const item1 = remoteQueryHistory[1];
|
||||
const item2 = remoteQueryHistory[3];
|
||||
await queryHistoryManager.handleExportResults(item1, [item1, item2]);
|
||||
expect(executeCommandSpy).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should export results for a single variant analysis', async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
|
||||
const item = variantAnalysisHistory[1];
|
||||
await queryHistoryManager.handleExportResults(item, [item]);
|
||||
expect(executeCommandSpy).to.have.been.calledWith('codeQL.exportVariantAnalysisResults', item.variantAnalysis.id);
|
||||
});
|
||||
|
||||
it('should not export results for multiple variant analyses', async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
|
||||
const item1 = variantAnalysisHistory[1];
|
||||
const item2 = variantAnalysisHistory[3];
|
||||
await queryHistoryManager.handleExportResults(item1, [item1, item2]);
|
||||
expect(executeCommandSpy).not.to.have.been.called;
|
||||
});
|
||||
});
|
||||
|
||||
describe('determineSelection', () => {
|
||||
const singleItem = 'a';
|
||||
const multipleItems = ['b', 'c', 'd'];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { App } from '../../src/common/app';
|
||||
import { App, AppMode } from '../../src/common/app';
|
||||
import { AppEvent, AppEventEmitter } from '../../src/common/events';
|
||||
import { Disposable } from '../../src/pure/disposable-object';
|
||||
|
||||
@@ -6,7 +6,7 @@ export function createMockApp({
|
||||
extensionPath = '/mock/extension/path',
|
||||
workspaceStoragePath = '/mock/workspace/storage/path',
|
||||
globalStoragePath = '/mock/global/storage/path',
|
||||
createEventEmitter = <T>() => new MockAppEventEmitter<T>()
|
||||
createEventEmitter = <T>() => new MockAppEventEmitter<T>(),
|
||||
}: {
|
||||
extensionPath?: string,
|
||||
workspaceStoragePath?: string,
|
||||
@@ -14,10 +14,12 @@ export function createMockApp({
|
||||
createEventEmitter?: <T>() => AppEventEmitter<T>
|
||||
}): App {
|
||||
return {
|
||||
createEventEmitter,
|
||||
mode: AppMode.Test,
|
||||
subscriptions: [],
|
||||
extensionPath,
|
||||
workspaceStoragePath,
|
||||
globalStoragePath
|
||||
globalStoragePath,
|
||||
createEventEmitter,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"databases": {
|
||||
"remote": {
|
||||
"repositoryLists": [],
|
||||
"owners": [],
|
||||
"repositories": []
|
||||
},
|
||||
"local": {
|
||||
"lists": [],
|
||||
"databases": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,52 @@
|
||||
{
|
||||
"remote": {
|
||||
"repositoryLists": [
|
||||
{
|
||||
"name": "repoList1",
|
||||
"repositories": ["foo/bar", "foo/baz"]
|
||||
}
|
||||
],
|
||||
"owners": [],
|
||||
"repositories": ["owner/repo1", "owner/repo2", "owner/repo3"]
|
||||
"databases": {
|
||||
"remote": {
|
||||
"repositoryLists": [
|
||||
{
|
||||
"name": "repoList1",
|
||||
"repositories": ["foo/bar", "foo/baz"]
|
||||
}
|
||||
],
|
||||
"owners": [],
|
||||
"repositories": ["owner/repo1", "owner/repo2", "owner/repo3"]
|
||||
},
|
||||
"local": {
|
||||
"lists": [
|
||||
{
|
||||
"name": "localList1",
|
||||
"databases": [
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"dateAdded": 1668096745193,
|
||||
"language": "go",
|
||||
"storagePath": "/path/to/database/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "localList2",
|
||||
"databases": [
|
||||
{
|
||||
"name": "foo/baz",
|
||||
"dateAdded": 1668096760848,
|
||||
"language": "javascript",
|
||||
"storagePath": "/path/to/database/"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"databases": [
|
||||
{
|
||||
"name": "example-db",
|
||||
"dateAdded": 1668096927267,
|
||||
"language": "ruby",
|
||||
"storagePath": "/path/to/database/"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"local": {
|
||||
"lists": [
|
||||
{
|
||||
"name": "localList1",
|
||||
"databases": [
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"dateAdded": 1668096745193,
|
||||
"language": "go",
|
||||
"storagePath": "/path/to/database/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "localList2",
|
||||
"databases": [
|
||||
{
|
||||
"name": "foo/baz",
|
||||
"dateAdded": 1668096760848,
|
||||
"language": "javascript",
|
||||
"storagePath": "/path/to/database/"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"databases": [
|
||||
{
|
||||
"name": "example-db",
|
||||
"dateAdded": 1668096927267,
|
||||
"language": "ruby",
|
||||
"storagePath": "/path/to/database/"
|
||||
}
|
||||
]
|
||||
"selected": {
|
||||
"kind": "configDefined",
|
||||
"value": "path.to.database"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,14 @@ describe('db config store', async () => {
|
||||
await configStore.initialize();
|
||||
|
||||
expect(await fs.pathExists(configPath)).to.be.true;
|
||||
|
||||
const config = configStore.getConfig().value;
|
||||
expect(config.remote.repositoryLists).to.be.empty;
|
||||
expect(config.remote.owners).to.be.empty;
|
||||
expect(config.remote.repositories).to.be.empty;
|
||||
expect(config.local.lists).to.be.empty;
|
||||
expect(config.local.databases).to.be.empty;
|
||||
expect(config.databases.remote.repositoryLists).to.be.empty;
|
||||
expect(config.databases.remote.owners).to.be.empty;
|
||||
expect(config.databases.remote.repositories).to.be.empty;
|
||||
expect(config.databases.local.lists).to.be.empty;
|
||||
expect(config.databases.local.databases).to.be.empty;
|
||||
expect(config.selected).to.be.undefined;
|
||||
});
|
||||
|
||||
it('should load an existing config', async () => {
|
||||
@@ -46,20 +48,20 @@ describe('db config store', async () => {
|
||||
await configStore.initialize();
|
||||
|
||||
const config = configStore.getConfig().value;
|
||||
expect(config.remote.repositoryLists).to.have.length(1);
|
||||
expect(config.remote.repositoryLists[0]).to.deep.equal({
|
||||
expect(config.databases.remote.repositoryLists).to.have.length(1);
|
||||
expect(config.databases.remote.repositoryLists[0]).to.deep.equal({
|
||||
name: 'repoList1',
|
||||
repositories: ['foo/bar', 'foo/baz']
|
||||
});
|
||||
expect(config.remote.owners).to.be.empty;
|
||||
expect(config.remote.repositories).to.have.length(3);
|
||||
expect(config.remote.repositories).to.deep.equal([
|
||||
expect(config.databases.remote.owners).to.be.empty;
|
||||
expect(config.databases.remote.repositories).to.have.length(3);
|
||||
expect(config.databases.remote.repositories).to.deep.equal([
|
||||
'owner/repo1',
|
||||
'owner/repo2',
|
||||
'owner/repo3',
|
||||
]);
|
||||
expect(config.local.lists).to.have.length(2);
|
||||
expect(config.local.lists[0]).to.deep.equal({
|
||||
expect(config.databases.local.lists).to.have.length(2);
|
||||
expect(config.databases.local.lists[0]).to.deep.equal({
|
||||
name: 'localList1',
|
||||
databases: [
|
||||
{
|
||||
@@ -70,13 +72,32 @@ describe('db config store', async () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(config.local.databases).to.have.length(1);
|
||||
expect(config.local.databases[0]).to.deep.equal({
|
||||
expect(config.databases.local.databases).to.have.length(1);
|
||||
expect(config.databases.local.databases[0]).to.deep.equal({
|
||||
name: 'example-db',
|
||||
dateAdded: 1668096927267,
|
||||
language: 'ruby',
|
||||
storagePath: '/path/to/database/',
|
||||
});
|
||||
expect(config.selected).to.deep.equal({
|
||||
kind: 'configDefined',
|
||||
value: 'path.to.database',
|
||||
});
|
||||
});
|
||||
|
||||
it('should load an existing config without selected db', async () => {
|
||||
const testDataStoragePathWithout = path.join(__dirname, 'data', 'without-selected');
|
||||
|
||||
const app = createMockApp({
|
||||
extensionPath,
|
||||
workspaceStoragePath: testDataStoragePathWithout
|
||||
});
|
||||
|
||||
const configStore = new DbConfigStore(app);
|
||||
await configStore.initialize();
|
||||
|
||||
const config = configStore.getConfig().value;
|
||||
expect(config.selected).to.be.undefined;
|
||||
});
|
||||
|
||||
it('should not allow modification of the config', async () => {
|
||||
@@ -88,9 +109,9 @@ describe('db config store', async () => {
|
||||
await configStore.initialize();
|
||||
|
||||
const config = configStore.getConfig().value;
|
||||
config.remote.repositoryLists = [];
|
||||
config.databases.remote.repositoryLists = [];
|
||||
|
||||
const reRetrievedConfig = configStore.getConfig().value;
|
||||
expect(reRetrievedConfig.remote.repositoryLists).to.have.length(1);
|
||||
expect(reRetrievedConfig.databases.remote.repositoryLists).to.have.length(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,15 +11,17 @@ describe('db config validation', async () => {
|
||||
// We're intentionally bypassing the type check because we'd
|
||||
// like to make sure validation errors are highlighted.
|
||||
const dbConfig = {
|
||||
'remote': {
|
||||
'repositoryLists': [
|
||||
{
|
||||
'name': 'repoList1',
|
||||
'repositories': ['foo/bar', 'foo/baz']
|
||||
}
|
||||
],
|
||||
'repositories': ['owner/repo1', 'owner/repo2', 'owner/repo3'],
|
||||
'somethingElse': 'bar'
|
||||
'databases': {
|
||||
'remote': {
|
||||
'repositoryLists': [
|
||||
{
|
||||
'name': 'repoList1',
|
||||
'repositories': ['foo/bar', 'foo/baz']
|
||||
}
|
||||
],
|
||||
'repositories': ['owner/repo1', 'owner/repo2', 'owner/repo3'],
|
||||
'somethingElse': 'bar'
|
||||
}
|
||||
}
|
||||
} as any as DbConfig;
|
||||
|
||||
@@ -27,8 +29,8 @@ describe('db config validation', async () => {
|
||||
|
||||
expect(validationOutput).to.have.length(3);
|
||||
|
||||
expect(validationOutput[0]).to.deep.equal(' must have required property \'local\'');
|
||||
expect(validationOutput[1]).to.deep.equal('/remote must have required property \'owners\'');
|
||||
expect(validationOutput[2]).to.deep.equal('/remote must NOT have additional properties');
|
||||
expect(validationOutput[0]).to.deep.equal('/databases must have required property \'local\'');
|
||||
expect(validationOutput[1]).to.deep.equal('/databases/remote must have required property \'owners\'');
|
||||
expect(validationOutput[2]).to.deep.equal('/databases/remote must NOT have additional properties');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,15 +8,17 @@ describe('db tree creator', () => {
|
||||
describe('createRemoteTree', () => {
|
||||
it('should build root node and system defined lists', () => {
|
||||
const dbConfig: DbConfig = {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [],
|
||||
repositories: []
|
||||
databases: {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [],
|
||||
repositories: []
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
}
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
}
|
||||
};
|
||||
|
||||
const dbTreeRoot = createRemoteTree(dbConfig);
|
||||
@@ -46,32 +48,34 @@ describe('db tree creator', () => {
|
||||
|
||||
it('should create remote user defined list nodes', () => {
|
||||
const dbConfig: DbConfig = {
|
||||
remote: {
|
||||
repositoryLists: [
|
||||
{
|
||||
name: 'my-list-1',
|
||||
repositories: [
|
||||
'owner1/repo1',
|
||||
'owner1/repo2',
|
||||
'owner2/repo1'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'my-list-2',
|
||||
repositories: [
|
||||
'owner3/repo1',
|
||||
'owner3/repo2',
|
||||
'owner4/repo1'
|
||||
]
|
||||
}
|
||||
],
|
||||
owners: [],
|
||||
repositories: []
|
||||
databases: {
|
||||
remote: {
|
||||
repositoryLists: [
|
||||
{
|
||||
name: 'my-list-1',
|
||||
repositories: [
|
||||
'owner1/repo1',
|
||||
'owner1/repo2',
|
||||
'owner2/repo1'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'my-list-2',
|
||||
repositories: [
|
||||
'owner3/repo1',
|
||||
'owner3/repo2',
|
||||
'owner4/repo1'
|
||||
]
|
||||
}
|
||||
],
|
||||
owners: [],
|
||||
repositories: []
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
},
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
}
|
||||
};
|
||||
|
||||
const dbTreeRoot = createRemoteTree(dbConfig);
|
||||
@@ -85,16 +89,16 @@ describe('db tree creator', () => {
|
||||
expect(repositoryListNodes.length).to.equal(2);
|
||||
expect(repositoryListNodes[0]).to.deep.equal({
|
||||
kind: DbItemKind.RemoteUserDefinedList,
|
||||
listName: dbConfig.remote.repositoryLists[0].name,
|
||||
repos: dbConfig.remote.repositoryLists[0].repositories.map((repo) => ({
|
||||
listName: dbConfig.databases.remote.repositoryLists[0].name,
|
||||
repos: dbConfig.databases.remote.repositoryLists[0].repositories.map((repo) => ({
|
||||
kind: DbItemKind.RemoteRepo,
|
||||
repoFullName: repo
|
||||
}))
|
||||
});
|
||||
expect(repositoryListNodes[1]).to.deep.equal({
|
||||
kind: DbItemKind.RemoteUserDefinedList,
|
||||
listName: dbConfig.remote.repositoryLists[1].name,
|
||||
repos: dbConfig.remote.repositoryLists[1].repositories.map((repo) => ({
|
||||
listName: dbConfig.databases.remote.repositoryLists[1].name,
|
||||
repos: dbConfig.databases.remote.repositoryLists[1].repositories.map((repo) => ({
|
||||
kind: DbItemKind.RemoteRepo,
|
||||
repoFullName: repo
|
||||
}))
|
||||
@@ -103,17 +107,19 @@ describe('db tree creator', () => {
|
||||
|
||||
it('should create remote owner nodes', () => {
|
||||
const dbConfig: DbConfig = {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [
|
||||
'owner1',
|
||||
'owner2'
|
||||
],
|
||||
repositories: []
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
databases: {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [
|
||||
'owner1',
|
||||
'owner2'
|
||||
],
|
||||
repositories: []
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -128,28 +134,30 @@ describe('db tree creator', () => {
|
||||
expect(ownerNodes.length).to.equal(2);
|
||||
expect(ownerNodes[0]).to.deep.equal({
|
||||
kind: DbItemKind.RemoteOwner,
|
||||
ownerName: dbConfig.remote.owners[0]
|
||||
ownerName: dbConfig.databases.remote.owners[0]
|
||||
});
|
||||
expect(ownerNodes[1]).to.deep.equal({
|
||||
kind: DbItemKind.RemoteOwner,
|
||||
ownerName: dbConfig.remote.owners[1]
|
||||
ownerName: dbConfig.databases.remote.owners[1]
|
||||
});
|
||||
});
|
||||
|
||||
it('should create remote repo nodes', () => {
|
||||
const dbConfig: DbConfig = {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [],
|
||||
repositories: [
|
||||
'owner1/repo1',
|
||||
'owner1/repo2',
|
||||
'owner2/repo1'
|
||||
]
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
databases: {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [],
|
||||
repositories: [
|
||||
'owner1/repo1',
|
||||
'owner1/repo2',
|
||||
'owner2/repo1'
|
||||
]
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: []
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -164,15 +172,15 @@ describe('db tree creator', () => {
|
||||
expect(repoNodes.length).to.equal(3);
|
||||
expect(repoNodes[0]).to.deep.equal({
|
||||
kind: DbItemKind.RemoteRepo,
|
||||
repoFullName: dbConfig.remote.repositories[0]
|
||||
repoFullName: dbConfig.databases.remote.repositories[0]
|
||||
});
|
||||
expect(repoNodes[1]).to.deep.equal({
|
||||
kind: DbItemKind.RemoteRepo,
|
||||
repoFullName: dbConfig.remote.repositories[1]
|
||||
repoFullName: dbConfig.databases.remote.repositories[1]
|
||||
});
|
||||
expect(repoNodes[2]).to.deep.equal({
|
||||
kind: DbItemKind.RemoteRepo,
|
||||
repoFullName: dbConfig.remote.repositories[2]
|
||||
repoFullName: dbConfig.databases.remote.repositories[2]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,112 +4,138 @@
|
||||
"$schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"remote": {
|
||||
"databases": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"repositoryLists": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"repositories": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9-_\\.]+/[a-zA-Z0-9-_\\.]+$"
|
||||
}
|
||||
"remote": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"repositoryLists": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"repositories": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9-_\\.]+/[a-zA-Z0-9-_\\.]+$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["name", "repositories"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["name", "repositories"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
"owners": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9-_\\.]+$"
|
||||
}
|
||||
},
|
||||
"repositories": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9-_\\.]+/[a-zA-Z0-9-_\\.]+$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["repositoryLists", "owners", "repositories"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"owners": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9-_\\.]+$"
|
||||
}
|
||||
},
|
||||
"repositories": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9-_\\.]+/[a-zA-Z0-9-_\\.]+$"
|
||||
}
|
||||
"local": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"lists": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"databases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"dateAdded": {
|
||||
"type": "number"
|
||||
},
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"storagePath": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"dateAdded",
|
||||
"language",
|
||||
"storagePath"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["name", "databases"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"databases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"dateAdded": {
|
||||
"type": "number"
|
||||
},
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"storagePath": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name", "dateAdded", "language", "storagePath"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["lists", "databases"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["repositoryLists", "owners", "repositories"],
|
||||
"required": ["remote", "local"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"local": {
|
||||
"selected": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"lists": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"databases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"dateAdded": {
|
||||
"type": "number"
|
||||
},
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"storagePath": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name", "dateAdded", "language", "storagePath"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["name", "databases"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["configDefined", "remoteSystemDefinedList"]
|
||||
},
|
||||
"databases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"dateAdded": {
|
||||
"type": "number"
|
||||
},
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"storagePath": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name", "dateAdded", "language", "storagePath"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["lists", "databases"],
|
||||
"required": ["kind", "value"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["remote", "local"],
|
||||
"required": ["databases"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user