Read schema from file

This commit is contained in:
Charis Kyriakou
2022-11-07 16:07:10 +00:00
parent 916b840407
commit 03bc63c689
6 changed files with 47 additions and 72 deletions

View File

@@ -3,21 +3,25 @@ import * as path from 'path';
import { cloneDbConfig, DbConfig } from './db-config';
import * as chokidar from 'chokidar';
import { DisposableObject } from '../pure/disposable-object';
import { validateDbConfig } from './db-config-validation';
import { DbConfigValidator } from './db-config-validator';
export class DbConfigStore extends DisposableObject {
private readonly configPath: string;
private readonly configValidator: DbConfigValidator;
private config: DbConfig;
private configWatcher: chokidar.FSWatcher | undefined;
public constructor(workspaceStoragePath: string) {
public constructor(
workspaceStoragePath: string,
extensionPath: string) {
super();
this.configPath = path.join(workspaceStoragePath, 'workspace-databases.json');
this.config = this.createEmptyConfig();
this.configWatcher = undefined;
this.configValidator = new DbConfigValidator(extensionPath);
}
public async initialize(): Promise<void> {
@@ -34,8 +38,12 @@ export class DbConfigStore extends DisposableObject {
return cloneDbConfig(this.config);
}
public getConfigPath(): string {
return this.configPath;
}
public validateConfig(): string[] {
return validateDbConfig(this.config);
return this.configValidator.validate(this.config);
}
private async loadConfig(): Promise<void> {

View File

@@ -1,63 +0,0 @@
import { DbConfig } from './db-config';
import Ajv from 'ajv';
export function validateDbConfig(dbConfig: DbConfig): string[] {
const ajv = new Ajv({ allErrors: true });
const schema = {
type: 'object',
properties: {
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
}
},
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
}
},
required: ['remote'],
additionalProperties: false
};
ajv.validate(schema, dbConfig);
if (ajv.errors) {
return ajv.errors.map((error) => `${error.instancePath} ${error.message}`);
}
return [];
}

View File

@@ -0,0 +1,24 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import Ajv from 'ajv';
import { DbConfig } from './db-config';
export class DbConfigValidator {
private readonly schema: any;
constructor(extensionPath: string) {
const schemaPath = path.resolve(extensionPath, 'workspace-databases-schema.json');
this.schema = fs.readJsonSync(schemaPath);
}
public validate(dbConfig: DbConfig): string[] {
const ajv = new Ajv({ allErrors: true });
ajv.validate(this.schema, dbConfig);
if (ajv.errors) {
return ajv.errors.map((error) => `${error.instancePath} ${error.message}`);
}
return [];
}
}

View File

@@ -22,7 +22,8 @@ export class DbModule extends DisposableObject {
void logger.log('Initializing database module');
const storagePath = extensionContext.storageUri?.fsPath || extensionContext.globalStorageUri.fsPath;
const dbConfigStore = new DbConfigStore(storagePath);
const extensionPath = extensionContext.extensionPath;
const dbConfigStore = new DbConfigStore(storagePath, extensionPath);
await dbConfigStore.initialize();
const dbManager = new DbManager(dbConfigStore);

View File

@@ -4,6 +4,7 @@ import { DbConfigStore } from '../../../src/databases/db-config-store';
import { expect } from 'chai';
describe('db config store', async () => {
const extensionPath = path.join(__dirname, '../../..');
const tempWorkspaceStoragePath = path.join(__dirname, 'test-workspace');
const testDataStoragePath = path.join(__dirname, 'data');
@@ -18,7 +19,7 @@ describe('db config store', async () => {
it('should create a new config if one does not exist', async () => {
const configPath = path.join(tempWorkspaceStoragePath, 'workspace-databases.json');
const configStore = new DbConfigStore(tempWorkspaceStoragePath);
const configStore = new DbConfigStore(tempWorkspaceStoragePath, extensionPath);
await configStore.initialize();
expect(await fs.pathExists(configPath)).to.be.true;
@@ -29,7 +30,7 @@ describe('db config store', async () => {
});
it('should load an existing config', async () => {
const configStore = new DbConfigStore(testDataStoragePath);
const configStore = new DbConfigStore(testDataStoragePath, extensionPath);
await configStore.initialize();
const config = configStore.getConfig();
@@ -44,7 +45,7 @@ describe('db config store', async () => {
});
it('should not allow modification of the config', async () => {
const configStore = new DbConfigStore(testDataStoragePath);
const configStore = new DbConfigStore(testDataStoragePath, extensionPath);
await configStore.initialize();
const config = configStore.getConfig();

View File

@@ -1,8 +1,12 @@
import { expect } from 'chai';
import { validateDbConfig } from '../../../src/databases/db-config-validation';
import * as path from 'path';
import { DbConfig } from '../../../src/databases/db-config';
import { DbConfigValidator } from '../../../src/databases/db-config-validator';
describe('db config validation', async () => {
const extensionPath = path.join(__dirname, '../../..');
const configValidator = new DbConfigValidator(extensionPath);
it('should return error when file is not valid', async () => {
// We're intentionally bypassing the type check because we'd
// like to make sure validation errors are highlighted.
@@ -19,7 +23,7 @@ describe('db config validation', async () => {
}
} as any as DbConfig;
const validationOutput = validateDbConfig(dbConfig);
const validationOutput = configValidator.validate(dbConfig);
expect(validationOutput).to.have.length(2);