2
0
mirror of https://github.com/frappe/books.git synced 2025-01-22 22:58:28 +00:00

incr: add DatabaseManager.call

This commit is contained in:
18alantom 2022-03-31 12:48:32 +05:30
parent 18dd3f8518
commit 9765707d21
10 changed files with 142 additions and 43 deletions

View File

@ -1,5 +1,6 @@
import { knex, Knex } from 'knex'; import { knex, Knex } from 'knex';
import { getRandomString, getValueMapFromList } from 'utils'; import { getRandomString, getValueMapFromList } from 'utils';
import { DatabaseBase, GetAllOptions, QueryFilter } from 'utils/db/types';
import { import {
CannotCommitError, CannotCommitError,
DatabaseError, DatabaseError,
@ -15,14 +16,8 @@ import {
SchemaMap, SchemaMap,
TargetField, TargetField,
} from '../../schemas/types'; } from '../../schemas/types';
import { getDefaultMetaFieldValueMap, sqliteTypeMap, SYSTEM } from '../common'; import { getDefaultMetaFieldValueMap, sqliteTypeMap, SYSTEM } from '../helpers';
import { import { ColumnDiff, FieldValueMap, GetQueryBuilderOptions } from './types';
ColumnDiff,
FieldValueMap,
GetAllOptions,
GetQueryBuilderOptions,
QueryFilter,
} from './types';
/** /**
* # DatabaseCore * # DatabaseCore
@ -43,7 +38,7 @@ import {
* the `fieldValueMap`. * the `fieldValueMap`.
*/ */
export default class DatabaseCore { export default class DatabaseCore extends DatabaseBase {
knex?: Knex; knex?: Knex;
typeMap = sqliteTypeMap; typeMap = sqliteTypeMap;
dbPath: string; dbPath: string;
@ -51,6 +46,7 @@ export default class DatabaseCore {
connectionParams: Knex.Config; connectionParams: Knex.Config;
constructor(dbPath?: string) { constructor(dbPath?: string) {
super();
this.dbPath = dbPath ?? ':memory:'; this.dbPath = dbPath ?? ':memory:';
this.connectionParams = { this.connectionParams = {
client: 'sqlite3', client: 'sqlite3',
@ -133,7 +129,10 @@ export default class DatabaseCore {
return row.length > 0; return row.length > 0;
} }
async insert(schemaName: string, fieldValueMap: FieldValueMap) { async insert(
schemaName: string,
fieldValueMap: FieldValueMap
): Promise<FieldValueMap> {
// insert parent // insert parent
if (this.schemaMap[schemaName].isSingle) { if (this.schemaMap[schemaName].isSingle) {
await this.#updateSingleValues(schemaName, fieldValueMap); await this.#updateSingleValues(schemaName, fieldValueMap);

View File

@ -1,4 +1,6 @@
import { databaseMethodSet } from 'backend/helpers';
import fs from 'fs/promises'; import fs from 'fs/promises';
import { DatabaseMethod } from 'utils/db/types';
import { getSchemas } from '../../schemas'; import { getSchemas } from '../../schemas';
import patches from '../patches'; import patches from '../patches';
import DatabaseCore from './core'; import DatabaseCore from './core';
@ -7,11 +9,14 @@ import { Patch } from './types';
export class DatabaseManager { export class DatabaseManager {
db?: DatabaseCore; db?: DatabaseCore;
constructor() {}
async createNewDatabase(dbPath: string, countryCode: string) { get #isInitialized(): boolean {
return this.db !== undefined && this.db.knex !== undefined;
}
async createNewDatabase(dbPath: string, countryCode?: string) {
await this.#unlinkIfExists(dbPath); await this.#unlinkIfExists(dbPath);
this.connectToDatabase(dbPath, countryCode); await this.connectToDatabase(dbPath, countryCode);
} }
async connectToDatabase(dbPath: string, countryCode?: string) { async connectToDatabase(dbPath: string, countryCode?: string) {
@ -22,14 +27,37 @@ export class DatabaseManager {
const schemaMap = getSchemas(countryCode); const schemaMap = getSchemas(countryCode);
this.db.setSchemaMap(schemaMap); this.db.setSchemaMap(schemaMap);
await this.migrate(); await this.#migrate();
} }
async migrate() { async call(method: DatabaseMethod, ...args: unknown[]) {
if (this.db === undefined) { if (!this.#isInitialized) {
return; return;
} }
if (!databaseMethodSet.has(method)) {
return;
}
// @ts-ignore
const response = await this.db[method](...args);
if (method === 'close') {
delete this.db;
}
return response;
}
async #migrate(): Promise<void> {
if (!this.#isInitialized) {
return;
}
const isFirstRun = await this.#getIsFirstRun();
if (isFirstRun) {
await this.db!.migrate();
}
const patchesToExecute = await this.#getPatchesToExecute(); const patchesToExecute = await this.#getPatchesToExecute();
const preMigrationPatches = patchesToExecute.filter( const preMigrationPatches = patchesToExecute.filter(
(p) => p.patch.beforeMigrate (p) => p.patch.beforeMigrate
@ -39,7 +67,7 @@ export class DatabaseManager {
); );
await runPatches(preMigrationPatches, this); await runPatches(preMigrationPatches, this);
await this.db.migrate(); await this.db!.migrate();
await runPatches(postMigrationPatches, this); await runPatches(postMigrationPatches, this);
} }
@ -60,10 +88,15 @@ export class DatabaseManager {
return undefined; return undefined;
} }
const query = await this.db.knex!('SingleValue').where({ let query: { countryCode: string }[] = [];
fieldname: 'countryCode', try {
parent: 'SystemSettings', query = await this.db.knex!('SingleValue').where({
}); fieldname: 'countryCode',
parent: 'SystemSettings',
});
} catch {
// Database not inialized and no countryCode passed
}
if (query.length > 0) { if (query.length > 0) {
return query[0].countryCode as string; return query[0].countryCode as string;
@ -83,6 +116,17 @@ export class DatabaseManager {
throw err; throw err;
} }
} }
async #getIsFirstRun(): Promise<boolean> {
if (!this.#isInitialized) {
return true;
}
const tableList: unknown[] = await this.db!.knex!.raw(
"SELECT name FROM sqlite_master WHERE type='table'"
);
return tableList.length === 0;
}
} }
export default new DatabaseManager(); export default new DatabaseManager();

View File

@ -1,4 +1,4 @@
import { getDefaultMetaFieldValueMap } from '../common'; import { getDefaultMetaFieldValueMap } from '../helpers';
import { DatabaseManager } from './manager'; import { DatabaseManager } from './manager';
import { FieldValueMap, Patch } from './types'; import { FieldValueMap, Patch } from './types';

View File

@ -3,7 +3,7 @@ import 'mocha';
import { getMapFromList } from 'schemas/helpers'; import { getMapFromList } from 'schemas/helpers';
import { FieldTypeEnum, RawValue } from 'schemas/types'; import { FieldTypeEnum, RawValue } from 'schemas/types';
import { getValueMapFromList, sleep } from 'utils'; import { getValueMapFromList, sleep } from 'utils';
import { getDefaultMetaFieldValueMap, sqliteTypeMap } from '../../common'; import { getDefaultMetaFieldValueMap, sqliteTypeMap } from '../../helpers';
import DatabaseCore from '../core'; import DatabaseCore from '../core';
import { FieldValueMap, SqliteTableInfo } from '../types'; import { FieldValueMap, SqliteTableInfo } from '../types';
import { import {

View File

@ -1,8 +1,6 @@
import { Field, RawValue } from '../../schemas/types'; import { Field, RawValue } from '../../schemas/types';
import { DatabaseManager } from './manager'; import { DatabaseManager } from './manager';
export type QueryFilter = Record<string, string | string[]>;
export interface GetQueryBuilderOptions { export interface GetQueryBuilderOptions {
offset?: number; offset?: number;
limit?: number; limit?: number;
@ -11,16 +9,6 @@ export interface GetQueryBuilderOptions {
order?: 'desc' | 'asc'; order?: 'desc' | 'asc';
} }
export interface GetAllOptions {
fields?: string[];
filters?: QueryFilter;
offset?: number;
limit?: number;
groupBy?: string;
orderBy?: string;
order?: 'asc' | 'desc';
}
export type ColumnDiff = { added: Field[]; removed: string[] }; export type ColumnDiff = { added: Field[]; removed: string[] };
export type FieldValueMap = Record< export type FieldValueMap = Record<
string, string,

View File

@ -1,3 +1,4 @@
import { DatabaseMethod } from 'utils/db/types';
import { KnexColumnType } from './database/types'; import { KnexColumnType } from './database/types';
export const sqliteTypeMap: Record<string, KnexColumnType> = { export const sqliteTypeMap: Record<string, KnexColumnType> = {
@ -34,3 +35,15 @@ export function getDefaultMetaFieldValueMap() {
modified: now, modified: now,
}; };
} }
export const databaseMethodSet: Set<DatabaseMethod> = new Set([
'insert',
'get',
'getAll',
'getSingleValues',
'rename',
'update',
'delete',
'close',
'exists',
]);

View File

@ -1,6 +0,0 @@
import models from '../models';
import coreModels from '../frappe/models';
export function getModel() {
console.log(models, coreModels);
}

View File

@ -18,7 +18,6 @@
"@/*": ["src/*"], "@/*": ["src/*"],
"schemas/*": ["schemas/*"], "schemas/*": ["schemas/*"],
"backend/*": ["backend/*"], "backend/*": ["backend/*"],
"common/*": ["common/*"],
"utils/*": ["utils/*"] "utils/*": ["utils/*"]
}, },
"lib": ["esnext", "dom", "dom.iterable", "scripthost"] "lib": ["esnext", "dom", "dom.iterable", "scripthost"]

63
utils/db/types.ts Normal file
View File

@ -0,0 +1,63 @@
/**
* The types in this file will be used by the main db class (core.ts) in the
* backend process and the the frontend db class (dbHandler.ts).
*
* DatabaseBase is an abstract class so that the function signatures
* match on both ends.
*/
type UnknownMap = Record<string, unknown>;
export abstract class DatabaseBase {
// Create
abstract insert(
schemaName: string,
fieldValueMap: UnknownMap
): Promise<UnknownMap>;
// Read
abstract get(
schemaName: string,
name: string,
fields?: string | string[]
): Promise<UnknownMap>;
abstract getAll(
schemaName: string,
options: GetAllOptions
): Promise<UnknownMap[]>;
abstract getSingleValues(
...fieldnames: ({ fieldname: string; parent?: string } | string)[]
): Promise<{ fieldname: string; parent: string; value: unknown }[]>;
// Update
abstract rename(
schemaName: string,
oldName: string,
newName: string
): Promise<void>;
abstract update(schemaName: string, fieldValueMap: UnknownMap): Promise<void>;
// Delete
abstract delete(schemaName: string, name: string): Promise<void>;
// Other
abstract close(): Promise<void>;
abstract exists(schemaName: string, name?: string): Promise<boolean>;
}
export type DatabaseMethod = keyof DatabaseBase;
export interface GetAllOptions {
fields?: string[];
filters?: QueryFilter;
offset?: number;
limit?: number;
groupBy?: string;
orderBy?: string;
order?: 'asc' | 'desc';
}
export type QueryFilter = Record<string, string | string[]>;

View File

@ -42,7 +42,6 @@ module.exports = {
'~': path.resolve('.'), '~': path.resolve('.'),
schemas: path.resolve(__dirname, './schemas'), schemas: path.resolve(__dirname, './schemas'),
backend: path.resolve(__dirname, './backend'), backend: path.resolve(__dirname, './backend'),
common: path.resolve(__dirname, './common'),
utils: path.resolve(__dirname, './utils'), utils: path.resolve(__dirname, './utils'),
}); });