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:
parent
05475fcacc
commit
952241b0bd
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user