2
0
mirror of https://github.com/frappe/books.git synced 2024-12-22 19:09:01 +00:00

refactor: check if migration will run

This commit is contained in:
18alantom 2023-07-11 12:41:22 +05:30
parent 05475fcacc
commit 952241b0bd
2 changed files with 139 additions and 39 deletions

View File

@ -16,10 +16,14 @@ import {
import { DatabaseBase, GetAllOptions, QueryFilter } from '../../utils/db/types'; import { DatabaseBase, GetAllOptions, QueryFilter } from '../../utils/db/types';
import { getDefaultMetaFieldValueMap, sqliteTypeMap, SYSTEM } from '../helpers'; import { getDefaultMetaFieldValueMap, sqliteTypeMap, SYSTEM } from '../helpers';
import { import {
AlterConfig,
ColumnDiff, ColumnDiff,
FieldValueMap, FieldValueMap,
GetQueryBuilderOptions, GetQueryBuilderOptions,
MigrationConfig,
NonExtantConfig,
SingleValue, SingleValue,
UpdateSinglesConfig,
} from './types'; } from './types';
/** /**
@ -97,21 +101,73 @@ export default class DatabaseCore extends DatabaseBase {
await this.knex!.destroy(); await this.knex!.destroy();
} }
async migrate() { async migrate(config: MigrationConfig = {}) {
for (const schemaName in this.schemaMap) { const { create, alter } = await this.#getCreateAlterList();
const schema = this.schemaMap[schemaName] as Schema; const hasSingleValueTable = !create.includes('SingleValue');
if (schema.isSingle) { let singlesConfig: UpdateSinglesConfig = {
update: [],
updateNonExtant: [],
};
if (hasSingleValueTable) {
singlesConfig = await this.#getSinglesUpdateList();
}
const shouldMigrate = !!(
create.length ||
alter.length ||
singlesConfig.update.length ||
singlesConfig.updateNonExtant.length
);
if (!shouldMigrate) {
return;
}
await config.pre?.();
for (const schemaName of create) {
await this.#createTable(schemaName);
}
for (const config of alter) {
await this.#alterTable(config);
}
if (!hasSingleValueTable) {
singlesConfig = await this.#getSinglesUpdateList();
}
await this.#initializeSingles(singlesConfig);
await config.post?.();
}
async #getCreateAlterList() {
const create: string[] = [];
const alter: AlterConfig[] = [];
for (const [schemaName, schema] of Object.entries(this.schemaMap)) {
if (!schema || schema.isSingle) {
continue; continue;
} }
if (await this.#tableExists(schemaName)) { const exists = await this.#tableExists(schemaName);
await this.#alterTable(schemaName); if (!exists) {
} else { create.push(schemaName);
await this.#createTable(schemaName); continue;
}
const diff: ColumnDiff = await this.#getColumnDiff(schemaName);
const newForeignKeys: Field[] = await this.#getNewForeignKeys(schemaName);
if (diff.added.length || diff.removed.length || newForeignKeys.length) {
alter.push({
schemaName,
diff,
newForeignKeys,
});
} }
} }
await this.#initializeSingles(); return { create, alter };
} }
async exists(schemaName: string, name?: string): Promise<boolean> { async exists(schemaName: string, name?: string): Promise<boolean> {
@ -584,11 +640,7 @@ export default class DatabaseCore extends DatabaseBase {
} }
} }
async #alterTable(schemaName: string) { async #alterTable({ schemaName, diff, newForeignKeys }: AlterConfig) {
// get columns
const diff: ColumnDiff = await this.#getColumnDiff(schemaName);
const newForeignKeys: Field[] = await this.#getNewForeignKeys(schemaName);
await this.knex!.schema.table(schemaName, (table) => { await this.knex!.schema.table(schemaName, (table) => {
if (!diff.added.length) { if (!diff.added.length) {
return; return;
@ -631,15 +683,17 @@ export default class DatabaseCore extends DatabaseBase {
.select('fieldname')) as { fieldname: string }[] .select('fieldname')) as { fieldname: string }[]
).map(({ fieldname }) => fieldname); ).map(({ fieldname }) => fieldname);
return this.schemaMap[singleSchemaName]!.fields.map( const nonExtant: NonExtantConfig['nonExtant'] = [];
({ fieldname, default: value }) => ({ const fields = this.schemaMap[singleSchemaName]?.fields ?? [];
fieldname, for (const { fieldname, default: value } of fields) {
value: value, if (existingFields.includes(fieldname) || value === undefined) {
}) continue;
).filter( }
({ fieldname, value }) =>
!existingFields.includes(fieldname) && value !== undefined nonExtant.push({ fieldname, value });
); }
return nonExtant;
} }
async #deleteOne(schemaName: string, name: string) { async #deleteOne(schemaName: string, name: string) {
@ -798,22 +852,42 @@ export default class DatabaseCore extends DatabaseBase {
return await this.knex!('SingleValue').insert(fieldValueMap); return await this.knex!('SingleValue').insert(fieldValueMap);
} }
async #initializeSingles() { async #getSinglesUpdateList() {
const singleSchemaNames = Object.keys(this.schemaMap).filter( const update: string[] = [];
(n) => this.schemaMap[n]!.isSingle const updateNonExtant: NonExtantConfig[] = [];
); for (const [schemaName, schema] of Object.entries(this.schemaMap)) {
if (!schema || !schema.isSingle) {
for (const schemaName of singleSchemaNames) {
if (await this.#singleExists(schemaName)) {
await this.#updateNonExtantSingleValues(schemaName);
continue; continue;
} }
const exists = await this.#singleExists(schemaName);
if (!exists && schema.fields.some((f) => f.default !== undefined)) {
update.push(schemaName);
}
if (!exists) {
continue;
}
const nonExtant = await this.#getNonExtantSingleValues(schemaName);
if (nonExtant.length) {
updateNonExtant.push({
schemaName,
nonExtant,
});
}
}
return { update, updateNonExtant };
}
async #initializeSingles({ update, updateNonExtant }: UpdateSinglesConfig) {
for (const config of updateNonExtant) {
await this.#updateNonExtantSingleValues(config);
}
for (const schemaName of update) {
const fields = this.schemaMap[schemaName]!.fields; const fields = this.schemaMap[schemaName]!.fields;
if (fields.every((f) => f.default === undefined)) {
continue;
}
const defaultValues: FieldValueMap = fields.reduce((acc, f) => { const defaultValues: FieldValueMap = fields.reduce((acc, f) => {
if (f.default !== undefined) { if (f.default !== undefined) {
acc[f.fieldname] = f.default; acc[f.fieldname] = f.default;
@ -826,10 +900,12 @@ export default class DatabaseCore extends DatabaseBase {
} }
} }
async #updateNonExtantSingleValues(schemaName: string) { async #updateNonExtantSingleValues({
const singleValues = await this.#getNonExtantSingleValues(schemaName); schemaName,
for (const sv of singleValues) { nonExtant,
await this.#updateSingleValue(schemaName, sv.fieldname, sv.value!); }: NonExtantConfig) {
for (const { fieldname, value } of nonExtant) {
await this.#updateSingleValue(schemaName, fieldname, value);
} }
} }

View File

@ -16,6 +16,30 @@ export type FieldValueMap = Record<
RawValue | undefined | FieldValueMap[] RawValue | undefined | FieldValueMap[]
>; >;
export type AlterConfig = {
schemaName: string;
diff: ColumnDiff;
newForeignKeys: Field[];
};
export type NonExtantConfig = {
schemaName: string;
nonExtant: {
fieldname: string;
value: RawValue;
}[];
};
export type UpdateSinglesConfig = {
update: string[];
updateNonExtant: NonExtantConfig[];
};
export type MigrationConfig = {
pre?: () => Promise<void> | void;
post?: () => Promise<void> | void;
};
export interface Patch { export interface Patch {
name: string; name: string;
version: string; version: string;