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:
parent
18dd3f8518
commit
9765707d21
@ -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);
|
||||||
|
@ -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();
|
||||||
|
@ -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';
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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',
|
||||||
|
]);
|
@ -1,6 +0,0 @@
|
|||||||
import models from '../models';
|
|
||||||
import coreModels from '../frappe/models';
|
|
||||||
|
|
||||||
export function getModel() {
|
|
||||||
console.log(models, coreModels);
|
|
||||||
}
|
|
@ -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
63
utils/db/types.ts
Normal 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[]>;
|
@ -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'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user