Add database configuration store (#1691)
This "config store" creates a `dbconfig.json` file (if it doesn't yet exist), and reads the file to load the database panel state. Only the database config store should be able to modify the config — the config cannot be modified externally.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
**/* @github/codeql-vscode-reviewers
|
||||
**/remote-queries/ @github/code-scanning-secexp-reviewers
|
||||
**/variant-analysis/ @github/code-scanning-secexp-reviewers
|
||||
**/databases/ @github/code-scanning-secexp-reviewers
|
||||
|
||||
3
extensions/ql-vscode/src/databases/README.md
Normal file
3
extensions/ql-vscode/src/databases/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Databases
|
||||
|
||||
This folder contains code for the new experimental databases panel and new query run experience.
|
||||
46
extensions/ql-vscode/src/databases/db-config-store.ts
Normal file
46
extensions/ql-vscode/src/databases/db-config-store.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { cloneDbConfig, DbConfig } from './db-config';
|
||||
|
||||
export class DbConfigStore {
|
||||
private readonly configPath: string;
|
||||
|
||||
private config: DbConfig;
|
||||
|
||||
public constructor(workspaceStoragePath: string) {
|
||||
this.configPath = path.join(workspaceStoragePath, 'dbconfig.json');
|
||||
|
||||
this.config = this.createEmptyConfig();
|
||||
}
|
||||
|
||||
public async initialize(): Promise<void> {
|
||||
await this.loadConfig();
|
||||
}
|
||||
|
||||
public getConfig(): DbConfig {
|
||||
// Clone the config so that it's not modified outside of this class.
|
||||
return cloneDbConfig(this.config);
|
||||
}
|
||||
|
||||
private async loadConfig(): Promise<void> {
|
||||
if (!await fs.pathExists(this.configPath)) {
|
||||
await fs.writeJSON(this.configPath, this.createEmptyConfig(), { spaces: 2 });
|
||||
}
|
||||
|
||||
await this.readConfig();
|
||||
}
|
||||
|
||||
private async readConfig(): Promise<void> {
|
||||
this.config = await fs.readJSON(this.configPath);
|
||||
}
|
||||
|
||||
private createEmptyConfig(): DbConfig {
|
||||
return {
|
||||
remote: {
|
||||
repositoryLists: [],
|
||||
owners: [],
|
||||
repositories: [],
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
29
extensions/ql-vscode/src/databases/db-config.ts
Normal file
29
extensions/ql-vscode/src/databases/db-config.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
// Contains models for the data we want to store in the database config
|
||||
|
||||
export interface DbConfig {
|
||||
remote: RemoteDbConfig;
|
||||
}
|
||||
|
||||
export interface RemoteDbConfig {
|
||||
repositoryLists: RemoteRepositoryList[];
|
||||
owners: string[];
|
||||
repositories: string[];
|
||||
}
|
||||
|
||||
export interface RemoteRepositoryList {
|
||||
name: string;
|
||||
repositories: string[];
|
||||
}
|
||||
|
||||
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],
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"remote": {
|
||||
"repositoryLists": [
|
||||
{
|
||||
"name": "repoList1",
|
||||
"repositories": ["foo/bar", "foo/baz"]
|
||||
}
|
||||
],
|
||||
"owners": [],
|
||||
"repositories": ["owner/repo1", "owner/repo2", "owner/repo3"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { DbConfigStore } from '../../../src/databases/db-config-store';
|
||||
import { expect } from 'chai';
|
||||
|
||||
|
||||
describe('db config store', async () => {
|
||||
const workspaceStoragePath = path.join(__dirname, 'test-workspace');
|
||||
const testStoragePath = path.join(__dirname, 'data');
|
||||
|
||||
beforeEach(async () => {
|
||||
await fs.ensureDir(workspaceStoragePath);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.remove(workspaceStoragePath);
|
||||
});
|
||||
|
||||
it('should create a new config if one does not exist', async () => {
|
||||
const configPath = path.join(workspaceStoragePath, 'dbconfig.json');
|
||||
|
||||
const configStore = new DbConfigStore(workspaceStoragePath);
|
||||
await configStore.initialize();
|
||||
|
||||
expect(await fs.pathExists(configPath)).to.be.true;
|
||||
const config = configStore.getConfig();
|
||||
expect(config.remote.repositoryLists).to.be.empty;
|
||||
expect(config.remote.owners).to.be.empty;
|
||||
expect(config.remote.repositories).to.be.empty;
|
||||
});
|
||||
|
||||
it('should load an existing config', async () => {
|
||||
const configStore = new DbConfigStore(testStoragePath);
|
||||
await configStore.initialize();
|
||||
|
||||
const config = configStore.getConfig();
|
||||
expect(config.remote.repositoryLists).to.have.length(1);
|
||||
expect(config.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(['owner/repo1', 'owner/repo2', 'owner/repo3']);
|
||||
});
|
||||
|
||||
it('should not allow modification of the config', async () => {
|
||||
const configStore = new DbConfigStore(testStoragePath);
|
||||
await configStore.initialize();
|
||||
|
||||
const config = configStore.getConfig();
|
||||
config.remote.repositoryLists = [];
|
||||
|
||||
const reRetrievedConfig = configStore.getConfig();
|
||||
expect(reRetrievedConfig.remote.repositoryLists).to.have.length(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user