Skip to content

Commit 7296c64

Browse files
authored
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.
1 parent 0965448 commit 7296c64

File tree

6 files changed

+148
-0
lines changed

6 files changed

+148
-0
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
**/* @github/codeql-vscode-reviewers
22
**/remote-queries/ @github/code-scanning-secexp-reviewers
33
**/variant-analysis/ @github/code-scanning-secexp-reviewers
4+
**/databases/ @github/code-scanning-secexp-reviewers
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Databases
2+
3+
This folder contains code for the new experimental databases panel and new query run experience.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import * as fs from 'fs-extra';
2+
import * as path from 'path';
3+
import { cloneDbConfig, DbConfig } from './db-config';
4+
5+
export class DbConfigStore {
6+
private readonly configPath: string;
7+
8+
private config: DbConfig;
9+
10+
public constructor(workspaceStoragePath: string) {
11+
this.configPath = path.join(workspaceStoragePath, 'dbconfig.json');
12+
13+
this.config = this.createEmptyConfig();
14+
}
15+
16+
public async initialize(): Promise<void> {
17+
await this.loadConfig();
18+
}
19+
20+
public getConfig(): DbConfig {
21+
// Clone the config so that it's not modified outside of this class.
22+
return cloneDbConfig(this.config);
23+
}
24+
25+
private async loadConfig(): Promise<void> {
26+
if (!await fs.pathExists(this.configPath)) {
27+
await fs.writeJSON(this.configPath, this.createEmptyConfig(), { spaces: 2 });
28+
}
29+
30+
await this.readConfig();
31+
}
32+
33+
private async readConfig(): Promise<void> {
34+
this.config = await fs.readJSON(this.configPath);
35+
}
36+
37+
private createEmptyConfig(): DbConfig {
38+
return {
39+
remote: {
40+
repositoryLists: [],
41+
owners: [],
42+
repositories: [],
43+
}
44+
};
45+
}
46+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Contains models for the data we want to store in the database config
2+
3+
export interface DbConfig {
4+
remote: RemoteDbConfig;
5+
}
6+
7+
export interface RemoteDbConfig {
8+
repositoryLists: RemoteRepositoryList[];
9+
owners: string[];
10+
repositories: string[];
11+
}
12+
13+
export interface RemoteRepositoryList {
14+
name: string;
15+
repositories: string[];
16+
}
17+
18+
export function cloneDbConfig(config: DbConfig): DbConfig {
19+
return {
20+
remote: {
21+
repositoryLists: config.remote.repositoryLists.map((list) => ({
22+
name: list.name,
23+
repositories: [...list.repositories],
24+
})),
25+
owners: [...config.remote.owners],
26+
repositories: [...config.remote.repositories],
27+
}
28+
};
29+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"remote": {
3+
"repositoryLists": [
4+
{
5+
"name": "repoList1",
6+
"repositories": ["foo/bar", "foo/baz"]
7+
}
8+
],
9+
"owners": [],
10+
"repositories": ["owner/repo1", "owner/repo2", "owner/repo3"]
11+
}
12+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import * as fs from 'fs-extra';
2+
import * as path from 'path';
3+
import { DbConfigStore } from '../../../src/databases/db-config-store';
4+
import { expect } from 'chai';
5+
6+
7+
describe('db config store', async () => {
8+
const workspaceStoragePath = path.join(__dirname, 'test-workspace');
9+
const testStoragePath = path.join(__dirname, 'data');
10+
11+
beforeEach(async () => {
12+
await fs.ensureDir(workspaceStoragePath);
13+
});
14+
15+
afterEach(async () => {
16+
await fs.remove(workspaceStoragePath);
17+
});
18+
19+
it('should create a new config if one does not exist', async () => {
20+
const configPath = path.join(workspaceStoragePath, 'dbconfig.json');
21+
22+
const configStore = new DbConfigStore(workspaceStoragePath);
23+
await configStore.initialize();
24+
25+
expect(await fs.pathExists(configPath)).to.be.true;
26+
const config = configStore.getConfig();
27+
expect(config.remote.repositoryLists).to.be.empty;
28+
expect(config.remote.owners).to.be.empty;
29+
expect(config.remote.repositories).to.be.empty;
30+
});
31+
32+
it('should load an existing config', async () => {
33+
const configStore = new DbConfigStore(testStoragePath);
34+
await configStore.initialize();
35+
36+
const config = configStore.getConfig();
37+
expect(config.remote.repositoryLists).to.have.length(1);
38+
expect(config.remote.repositoryLists[0]).to.deep.equal({
39+
'name': 'repoList1',
40+
'repositories': ['foo/bar', 'foo/baz']
41+
});
42+
expect(config.remote.owners).to.be.empty;
43+
expect(config.remote.repositories).to.have.length(3);
44+
expect(config.remote.repositories).to.deep.equal(['owner/repo1', 'owner/repo2', 'owner/repo3']);
45+
});
46+
47+
it('should not allow modification of the config', async () => {
48+
const configStore = new DbConfigStore(testStoragePath);
49+
await configStore.initialize();
50+
51+
const config = configStore.getConfig();
52+
config.remote.repositoryLists = [];
53+
54+
const reRetrievedConfig = configStore.getConfig();
55+
expect(reRetrievedConfig.remote.repositoryLists).to.have.length(1);
56+
});
57+
});

0 commit comments

Comments
 (0)