Skip to content

Commit

Permalink
feat(schema): add options to schema.ensureDatabase() method to crea…
Browse files Browse the repository at this point in the history
…te/clear
  • Loading branch information
B4nan committed Jan 5, 2024
1 parent 2cfdb59 commit 6a12fe1
Show file tree
Hide file tree
Showing 17 changed files with 148 additions and 81 deletions.
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Expand Up @@ -10,6 +10,7 @@ export {
IEntityGenerator, ISeedManager, EntityClassGroup, OptionalProps, EagerProps, HiddenProps, RequiredEntityData, CheckCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator,
UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset,
EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig,
ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions,
} from './typings';
export * from './enums';
export * from './errors';
Expand Down
53 changes: 44 additions & 9 deletions packages/core/src/typings.ts
Expand Up @@ -711,21 +711,56 @@ export interface EntityMetadata<T = any> {
readonly _id: number;
}

export interface CreateSchemaOptions {
wrap?: boolean;
schema?: string;
}

export interface ClearDatabaseOptions {
schema?: string;
truncate?: boolean;
}

export interface EnsureDatabaseOptions extends CreateSchemaOptions, ClearDatabaseOptions {
clear?: boolean;
create?: boolean;
}

export interface DropSchemaOptions {
wrap?: boolean;
dropMigrationsTable?: boolean;
dropDb?: boolean;
schema?: string;
}

export interface UpdateSchemaOptions<DatabaseSchema = unknown> {
wrap?: boolean;
safe?: boolean;
dropDb?: boolean;
dropTables?: boolean;
schema?: string;
fromSchema?: DatabaseSchema;
}

export interface RefreshDatabaseOptions extends CreateSchemaOptions {
ensureIndexes?: boolean;
}

export interface ISchemaGenerator {
createSchema(options?: { wrap?: boolean; schema?: string }): Promise<void>;
createSchema(options?: CreateSchemaOptions): Promise<void>;
ensureDatabase(): Promise<boolean>;
getCreateSchemaSQL(options?: { wrap?: boolean; schema?: string }): Promise<string>;
dropSchema(options?: { wrap?: boolean; dropMigrationsTable?: boolean; dropDb?: boolean; schema?: string }): Promise<void>;
getDropSchemaSQL(options?: { wrap?: boolean; dropMigrationsTable?: boolean; schema?: string }): Promise<string>;
updateSchema(options?: { wrap?: boolean; safe?: boolean; dropDb?: boolean; dropTables?: boolean; schema?: string }): Promise<void>;
getUpdateSchemaSQL(options?: { wrap?: boolean; safe?: boolean; dropDb?: boolean; dropTables?: boolean; schema?: string }): Promise<string>;
getUpdateSchemaMigrationSQL(options?: { wrap?: boolean; safe?: boolean; dropDb?: boolean; dropTables?: boolean }): Promise<{ up: string; down: string }>;
getCreateSchemaSQL(options?: CreateSchemaOptions): Promise<string>;
dropSchema(options?: DropSchemaOptions): Promise<void>;
getDropSchemaSQL(options?: Omit<DropSchemaOptions, 'dropDb'>): Promise<string>;
updateSchema(options?: UpdateSchemaOptions): Promise<void>;
getUpdateSchemaSQL(options?: UpdateSchemaOptions): Promise<string>;
getUpdateSchemaMigrationSQL(options?: UpdateSchemaOptions): Promise<{ up: string; down: string }>;
createDatabase(name: string): Promise<void>;
dropDatabase(name?: string): Promise<void>;
execute(sql: string, options?: { wrap?: boolean }): Promise<void>;
ensureIndexes(): Promise<void>;
refreshDatabase(options?: { ensureIndexes?: boolean }): Promise<void>;
clearDatabase(options?: { schema?: string }): Promise<void>;
refreshDatabase(options?: RefreshDatabaseOptions): Promise<void>;
clearDatabase(options?: ClearDatabaseOptions): Promise<void>;
}

export interface GenerateOptions {
Expand Down
33 changes: 21 additions & 12 deletions packages/core/src/utils/AbstractSchemaGenerator.ts
@@ -1,4 +1,13 @@
import type { EntityMetadata, ISchemaGenerator } from '../typings';
import type {
ClearDatabaseOptions,
DropSchemaOptions,
EntityMetadata,
ISchemaGenerator,
UpdateSchemaOptions,
CreateSchemaOptions,
RefreshDatabaseOptions,
EnsureDatabaseOptions,
} from '../typings';
import { CommitOrderCalculator } from '../unit-of-work/CommitOrderCalculator';
import type { IDatabaseDriver } from '../drivers/IDatabaseDriver';
import type { MetadataStorage } from '../metadata/MetadataStorage';
Expand All @@ -23,24 +32,24 @@ export abstract class AbstractSchemaGenerator<D extends IDatabaseDriver> impleme
this.connection = this.driver.getConnection() as ReturnType<D['getConnection']>;
}

async createSchema(): Promise<void> {
async createSchema(options?: CreateSchemaOptions): Promise<void> {
this.notImplemented();
}

/**
* Returns true if the database was created.
*/
async ensureDatabase(): Promise<boolean> {
async ensureDatabase(options?: EnsureDatabaseOptions): Promise<boolean> {
this.notImplemented();
}

async refreshDatabase(): Promise<void> {
async refreshDatabase(options?: RefreshDatabaseOptions): Promise<void> {
await this.ensureDatabase();
await this.dropSchema();
await this.createSchema();
await this.createSchema(options);
}

async clearDatabase(options?: { schema?: string }): Promise<void> {
async clearDatabase(options?: ClearDatabaseOptions): Promise<void> {
for (const meta of this.getOrderedMetadata(options?.schema).reverse()) {
await this.driver.nativeDelete(meta.className, {}, options);
}
Expand All @@ -53,27 +62,27 @@ export abstract class AbstractSchemaGenerator<D extends IDatabaseDriver> impleme
}
}

async getCreateSchemaSQL(): Promise<string> {
async getCreateSchemaSQL(options?: CreateSchemaOptions): Promise<string> {
this.notImplemented();
}

async dropSchema(): Promise<void> {
async dropSchema(options?: DropSchemaOptions): Promise<void> {
this.notImplemented();
}

async getDropSchemaSQL(): Promise<string> {
async getDropSchemaSQL(options?: Omit<DropSchemaOptions, 'dropDb'>): Promise<string> {
this.notImplemented();
}

async updateSchema(): Promise<void> {
async updateSchema(options?: UpdateSchemaOptions): Promise<void> {
this.notImplemented();
}

async getUpdateSchemaSQL(): Promise<string> {
async getUpdateSchemaSQL(options?: UpdateSchemaOptions): Promise<string> {
this.notImplemented();
}

async getUpdateSchemaMigrationSQL(): Promise<{ up: string; down: string }> {
async getUpdateSchemaMigrationSQL(options?: UpdateSchemaOptions): Promise<{ up: string; down: string }> {
this.notImplemented();
}

Expand Down
42 changes: 31 additions & 11 deletions packages/knex/src/schema/SqlSchemaGenerator.ts
@@ -1,5 +1,17 @@
import type { Knex } from 'knex';
import { AbstractSchemaGenerator, Utils, type Dictionary, type EntityMetadata, type MikroORM, type ISchemaGenerator } from '@mikro-orm/core';
import {
AbstractSchemaGenerator,
Utils,
type Dictionary,
type EntityMetadata,
type MikroORM,
type ISchemaGenerator,
type ClearDatabaseOptions,
type CreateSchemaOptions,
type EnsureDatabaseOptions,
type DropSchemaOptions,
type UpdateSchemaOptions,
} from '@mikro-orm/core';
import type { CheckDef, ForeignKey, IndexDef, SchemaDifference, TableDifference } from '../typings';
import { DatabaseSchema } from './DatabaseSchema';
import type { DatabaseTable } from './DatabaseTable';
Expand All @@ -16,7 +28,7 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDrive
orm.config.registerExtension('@mikro-orm/schema-generator', () => new SqlSchemaGenerator(orm.em));
}

override async createSchema(options?: { wrap?: boolean; schema?: string }): Promise<void> {
override async createSchema(options?: CreateSchemaOptions): Promise<void> {
await this.ensureDatabase();
const sql = await this.getCreateSchemaSQL(options);
await this.execute(sql);
Expand All @@ -25,7 +37,7 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDrive
/**
* Returns true if the database was created.
*/
override async ensureDatabase(): Promise<boolean> {
override async ensureDatabase(options?: EnsureDatabaseOptions): Promise<boolean> {
const dbName = this.config.get('dbName')!;

if (this.lastEnsuredDatabase === dbName) {
Expand All @@ -42,9 +54,17 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDrive
this.config.set('dbName', dbName);
await this.driver.reconnect();

if (options?.create) {
await this.createSchema(options);
}

return true;
}

if (options?.clear) {
await this.clearDatabase(options);
}

return false;
}

Expand All @@ -54,7 +74,7 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDrive
return DatabaseSchema.fromMetadata(metadata, this.platform, this.config, schemaName);
}

override async getCreateSchemaSQL(options: { wrap?: boolean; schema?: string } = {}): Promise<string> {
override async getCreateSchemaSQL(options: CreateSchemaOptions = {}): Promise<string> {
const wrap = options.wrap ?? this.options.disableForeignKeys;
const toSchema = this.getTargetSchema(options.schema);
let ret = '';
Expand All @@ -78,7 +98,7 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDrive
return this.wrapSchema(ret, { wrap });
}

override async dropSchema(options: { wrap?: boolean; dropMigrationsTable?: boolean; dropDb?: boolean; schema?: string } = {}): Promise<void> {
override async dropSchema(options: DropSchemaOptions = {}): Promise<void> {
if (options.dropDb) {
const name = this.config.get('dbName')!;
return this.dropDatabase(name);
Expand All @@ -88,7 +108,7 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDrive
await this.execute(sql);
}

override async clearDatabase(options?: { schema?: string; truncate?: boolean }): Promise<void> {
override async clearDatabase(options?: ClearDatabaseOptions): Promise<void> {
// truncate by default, so no value is considered as true
/* istanbul ignore if */
if (options?.truncate === false) {
Expand All @@ -113,7 +133,7 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDrive
}
}

override async getDropSchemaSQL(options: { wrap?: boolean; dropMigrationsTable?: boolean; schema?: string } = {}): Promise<string> {
override async getDropSchemaSQL(options: Omit<DropSchemaOptions, 'dropDb'> = {}): Promise<string> {
await this.ensureDatabase();
const wrap = options.wrap ?? this.options.disableForeignKeys;
const metadata = this.getOrderedMetadata(options.schema).reverse();
Expand Down Expand Up @@ -159,12 +179,12 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDrive
return meta.schema && meta.schema === '*' ? schemaName : (meta.schema ?? schemaName);
}

override async updateSchema(options: { wrap?: boolean; safe?: boolean; dropTables?: boolean; fromSchema?: DatabaseSchema; schema?: string } = {}): Promise<void> {
override async updateSchema(options: UpdateSchemaOptions<DatabaseSchema> = {}): Promise<void> {
const sql = await this.getUpdateSchemaSQL(options);
await this.execute(sql);
}

override async getUpdateSchemaSQL(options: { wrap?: boolean; safe?: boolean; dropTables?: boolean; fromSchema?: DatabaseSchema; schema?: string } = {}): Promise<string> {
override async getUpdateSchemaSQL(options: UpdateSchemaOptions<DatabaseSchema> = {}): Promise<string> {
await this.ensureDatabase();
const { fromSchema, toSchema } = await this.prepareSchemaForComparison(options);
const comparator = new SchemaComparator(this.platform);
Expand All @@ -173,7 +193,7 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDrive
return this.diffToSQL(diffUp, options);
}

override async getUpdateSchemaMigrationSQL(options: { wrap?: boolean; safe?: boolean; dropTables?: boolean; fromSchema?: DatabaseSchema; schema?: string } = {}): Promise<{ up: string; down: string }> {
override async getUpdateSchemaMigrationSQL(options: UpdateSchemaOptions<DatabaseSchema> = {}): Promise<{ up: string; down: string }> {
if (!options.fromSchema) {
await this.ensureDatabase();
}
Expand All @@ -189,7 +209,7 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDrive
};
}

private async prepareSchemaForComparison(options: { wrap?: boolean; safe?: boolean; dropTables?: boolean; fromSchema?: DatabaseSchema; schema?: string }) {
private async prepareSchemaForComparison(options: UpdateSchemaOptions<DatabaseSchema>) {
options.wrap ??= this.options.disableForeignKeys;
options.safe ??= false;
options.dropTables ??= true;
Expand Down
22 changes: 13 additions & 9 deletions packages/mongodb/src/MongoSchemaGenerator.ts
@@ -1,4 +1,12 @@
import { AbstractSchemaGenerator, Utils, type Dictionary, type EntityMetadata, type EntityProperty, type MikroORM } from '@mikro-orm/core';
import {
AbstractSchemaGenerator,
Utils,
type CreateSchemaOptions,
type Dictionary,
type EntityMetadata,
type EntityProperty,
type MikroORM,
} from '@mikro-orm/core';
import type { MongoDriver } from './MongoDriver';

export class MongoSchemaGenerator extends AbstractSchemaGenerator<MongoDriver> {
Expand All @@ -7,7 +15,7 @@ export class MongoSchemaGenerator extends AbstractSchemaGenerator<MongoDriver> {
orm.config.registerExtension('@mikro-orm/schema-generator', () => new MongoSchemaGenerator(orm.em));
}

override async createSchema(options: CreateSchemaOptions = {}): Promise<void> {
override async createSchema(options: MongoCreateSchemaOptions = {}): Promise<void> {
options.ensureIndexes ??= true;
const existing = await this.connection.listCollections();
const metadata = this.getOrderedMetadata();
Expand Down Expand Up @@ -49,15 +57,15 @@ export class MongoSchemaGenerator extends AbstractSchemaGenerator<MongoDriver> {
await Promise.all(promises);
}

override async updateSchema(options: CreateSchemaOptions = {}): Promise<void> {
override async updateSchema(options: MongoCreateSchemaOptions = {}): Promise<void> {
await this.createSchema(options);
}

override async ensureDatabase(): Promise<boolean> {
return false;
}

override async refreshDatabase(options: CreateSchemaOptions = {}): Promise<void> {
override async refreshDatabase(options: MongoCreateSchemaOptions = {}): Promise<void> {
await this.ensureDatabase();
await this.dropSchema();
await this.createSchema(options);
Expand Down Expand Up @@ -214,13 +222,9 @@ export class MongoSchemaGenerator extends AbstractSchemaGenerator<MongoDriver> {

}

export interface CreateSchemaOptions {
export interface MongoCreateSchemaOptions extends CreateSchemaOptions {
/** create indexes? defaults to true */
ensureIndexes?: boolean;
/** not valid for mongo driver */
wrap?: boolean;
/** not valid for mongo driver */
schema?: string;
}

export interface EnsureIndexesOptions {
Expand Down
7 changes: 4 additions & 3 deletions tests/features/entity-generator/AmbiguousFks.mysql.test.ts
Expand Up @@ -168,12 +168,13 @@ beforeAll(async () => {
discovery: { warnWhenNoEntities: false },
extensions: [EntityGenerator],
multipleStatements: true,
ensureDatabase: false,
});
const driver = orm.config.getDriver();
if (!await driver.getPlatform().getSchemaHelper()?.databaseExists(driver.getConnection(), schemaName)) {
await orm.schema.createSchema({ schema: schemaName });

if (await orm.schema.ensureDatabase({ create: true })) {
await orm.schema.execute(schema);
}

await orm.close(true);
});

Expand Down
Expand Up @@ -48,10 +48,10 @@ beforeAll(async () => {
discovery: { warnWhenNoEntities: false },
extensions: [EntityGenerator],
multipleStatements: true,
ensureDatabase: false,
});
const driver = orm.config.getDriver();
if (!await driver.getPlatform().getSchemaHelper()?.databaseExists(driver.getConnection(), schemaName)) {
await orm.schema.createSchema({ schema: schemaName });

if (await orm.schema.ensureDatabase({ create: true })) {
await orm.schema.execute(schema);
}
await orm.close(true);
Expand Down
Expand Up @@ -52,10 +52,10 @@ beforeAll(async () => {
discovery: { warnWhenNoEntities: false },
extensions: [EntityGenerator],
multipleStatements: true,
ensureDatabase: false,
});
const driver = orm.config.getDriver();
if (!await driver.getPlatform().getSchemaHelper()?.databaseExists(driver.getConnection(), schemaName)) {
await orm.schema.createSchema({ schema: schemaName });

if (await orm.schema.ensureDatabase({ create: true })) {
await orm.schema.execute(schema);
}
await orm.close(true);
Expand Down
Expand Up @@ -181,14 +181,14 @@ beforeAll(async () => {
dbName: schemaName,
port: 3308,
discovery: { warnWhenNoEntities: false },
extensions: [EntityGenerator],
multipleStatements: true,
ensureDatabase: false,
});
const driver = orm.config.getDriver();
if (!await driver.getPlatform().getSchemaHelper()?.databaseExists(driver.getConnection(), schemaName)) {
await orm.schema.createSchema({ schema: schemaName });

if (await orm.schema.ensureDatabase({ create: true })) {
await orm.schema.execute(schema);
}

await orm.close(true);
});

Expand Down

0 comments on commit 6a12fe1

Please sign in to comment.