Skip to content

Commit

Permalink
Merge 524f2c4 into bf23587
Browse files Browse the repository at this point in the history
  • Loading branch information
B4nan committed Mar 10, 2019
2 parents bf23587 + 524f2c4 commit 1467edc
Show file tree
Hide file tree
Showing 68 changed files with 559 additions and 412 deletions.
75 changes: 31 additions & 44 deletions lib/EntityManager.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
import { EntityRepository } from './entity/EntityRepository';
import { EntityFactory } from './entity/EntityFactory';
import { EntityValidator } from './entity/EntityValidator';
import { EntityAssigner } from './entity/EntityAssigner';
import { EntityLoader } from './entity/EntityLoader';
import { UnitOfWork } from './unit-of-work/UnitOfWork';
import { Utils } from './utils/Utils';
import { MikroORMOptions } from './MikroORM';
import { RequestContext } from './utils/RequestContext';
import { FilterQuery } from './drivers/DatabaseDriver';
import { IDatabaseDriver } from './drivers/IDatabaseDriver';
import { IPrimaryKey } from './decorators/PrimaryKey';
import { QueryBuilder, QueryOrder } from './query/QueryBuilder';
import { EntityClass, EntityData, IEntity, IEntityType } from './decorators/Entity';
import { MetadataStorage } from './metadata/MetadataStorage';
import { Configuration, RequestContext, Utils } from './utils';
import { EntityRepository, EntityAssigner, EntityFactory, EntityLoader, EntityValidator, ReferenceType } from './entity';
import { UnitOfWork } from './unit-of-work';
import { FilterQuery, IDatabaseDriver } from './drivers/IDatabaseDriver';
import { EntityData, EntityName, IEntity, IEntityType, IPrimaryKey } from './decorators';
import { QueryBuilder } from './query';
import { MetadataStorage } from './metadata';
import { Connection } from './connections/Connection';
import { ReferenceType } from './entity/enums';
import { QueryOrder } from './query';

export class EntityManager {

readonly validator = new EntityValidator(this.options.strict);
readonly validator = new EntityValidator(this.config.get('strict'));

private readonly repositoryMap: Record<string, EntityRepository<IEntity>> = {};
private readonly entityLoader = new EntityLoader(this);
private readonly metadata = MetadataStorage.getMetadata();
private readonly unitOfWork = new UnitOfWork(this);
private readonly entityFactory = new EntityFactory(this.unitOfWork, this.driver, this.options);
private readonly entityFactory = new EntityFactory(this.unitOfWork, this.driver, this.config);

constructor(readonly options: MikroORMOptions,
constructor(readonly config: Configuration,
private readonly driver: IDatabaseDriver<Connection>) { }

getDriver<D extends IDatabaseDriver<Connection> = IDatabaseDriver<Connection>>(): D {
Expand All @@ -37,31 +29,26 @@ export class EntityManager {
return this.driver.getConnection() as C;
}

getRepository<T extends IEntityType<T>>(entityName: string | EntityClass<T>): EntityRepository<T> {
getRepository<T extends IEntityType<T>>(entityName: EntityName<T>): EntityRepository<T> {
entityName = Utils.className(entityName);

if (!this.repositoryMap[entityName]) {
const meta = this.metadata[entityName];

if (meta.customRepository) {
const CustomRepository = meta.customRepository();
this.repositoryMap[entityName] = new CustomRepository(this, entityName);
} else {
this.repositoryMap[entityName] = new this.options.entityRepository(this, entityName);
}
const RepositoryClass = this.config.getRepositoryClass(meta.customRepository);
this.repositoryMap[entityName] = new RepositoryClass(this, entityName);
}

return this.repositoryMap[entityName] as EntityRepository<T>;
}

createQueryBuilder(entityName: string | EntityClass<IEntity>): QueryBuilder {
createQueryBuilder(entityName: EntityName<IEntity>): QueryBuilder {
entityName = Utils.className(entityName);
return new QueryBuilder(entityName, this.metadata, this.getConnection());
}

async find<T extends IEntityType<T>>(entityName: string | EntityClass<T>, where?: FilterQuery<T>, options?: FindOptions): Promise<T[]>;
async find<T extends IEntityType<T>>(entityName: string | EntityClass<T>, where?: FilterQuery<T>, populate?: string[], orderBy?: Record<string, QueryOrder>, limit?: number, offset?: number): Promise<T[]>;
async find<T extends IEntityType<T>>(entityName: string | EntityClass<T>, where = {} as FilterQuery<T>, populate?: string[] | FindOptions, orderBy?: Record<string, QueryOrder>, limit?: number, offset?: number): Promise<T[]> {
async find<T extends IEntityType<T>>(entityName: EntityName<T>, where?: FilterQuery<T>, options?: FindOptions): Promise<T[]>;
async find<T extends IEntityType<T>>(entityName: EntityName<T>, where?: FilterQuery<T>, populate?: string[], orderBy?: Record<string, QueryOrder>, limit?: number, offset?: number): Promise<T[]>;
async find<T extends IEntityType<T>>(entityName: EntityName<T>, where = {} as FilterQuery<T>, populate?: string[] | FindOptions, orderBy?: Record<string, QueryOrder>, limit?: number, offset?: number): Promise<T[]> {
entityName = Utils.className(entityName);
this.validator.validateParams(where);
const options = Utils.isObject<FindOptions>(populate) ? populate : { populate, orderBy, limit, offset };
Expand All @@ -83,7 +70,7 @@ export class EntityManager {
return ret;
}

async findOne<T extends IEntityType<T>>(entityName: string | EntityClass<T>, where: FilterQuery<T> | IPrimaryKey, populate: string[] = []): Promise<T | null> {
async findOne<T extends IEntityType<T>>(entityName: EntityName<T>, where: FilterQuery<T> | IPrimaryKey, populate: string[] = []): Promise<T | null> {
entityName = Utils.className(entityName);

if (!where || (typeof where === 'object' && Object.keys(where).length === 0)) {
Expand Down Expand Up @@ -132,20 +119,20 @@ export class EntityManager {
});
}

async nativeInsert<T extends IEntityType<T>>(entityName: string | EntityClass<T>, data: EntityData<T>): Promise<IPrimaryKey> {
async nativeInsert<T extends IEntityType<T>>(entityName: EntityName<T>, data: EntityData<T>): Promise<IPrimaryKey> {
entityName = Utils.className(entityName);
this.validator.validateParams(data, 'insert data');
return this.driver.nativeInsert(entityName, data);
}

async nativeUpdate<T extends IEntityType<T>>(entityName: string | EntityClass<T>, where: FilterQuery<T>, data: EntityData<T>): Promise<number> {
async nativeUpdate<T extends IEntityType<T>>(entityName: EntityName<T>, where: FilterQuery<T>, data: EntityData<T>): Promise<number> {
entityName = Utils.className(entityName);
this.validator.validateParams(data, 'update data');
this.validator.validateParams(where, 'update condition');
return this.driver.nativeUpdate(entityName, where, data);
}

async nativeDelete<T extends IEntityType<T>>(entityName: string | EntityClass<T>, where: FilterQuery<T> | string | any): Promise<number> {
async nativeDelete<T extends IEntityType<T>>(entityName: EntityName<T>, where: FilterQuery<T> | string | any): Promise<number> {
entityName = Utils.className(entityName);
this.validator.validateParams(where, 'delete condition');
return this.driver.nativeDelete(entityName, where);
Expand All @@ -154,12 +141,12 @@ export class EntityManager {
/**
* Shortcut to driver's aggregate method. Available in MongoDriver only.
*/
async aggregate(entityName: string | EntityClass<IEntity>, pipeline: any[]): Promise<any[]> {
async aggregate(entityName: EntityName<IEntity>, pipeline: any[]): Promise<any[]> {
entityName = Utils.className(entityName);
return this.driver.aggregate(entityName, pipeline);
}

merge<T extends IEntityType<T>>(entityName: string | EntityClass<T>, data: EntityData<T>): T {
merge<T extends IEntityType<T>>(entityName: EntityName<T>, data: EntityData<T>): T {
entityName = Utils.className(entityName);

if (!data || (!data.id && !data._id)) {
Expand All @@ -176,15 +163,15 @@ export class EntityManager {
/**
* Creates new instance of given entity and populates it with given data
*/
create<T extends IEntityType<T>>(entityName: string | EntityClass<T>, data: EntityData<T>): T {
create<T extends IEntityType<T>>(entityName: EntityName<T>, data: EntityData<T>): T {
entityName = Utils.className(entityName);
return this.entityFactory.create<T>(entityName, data, false);
}

/**
* Gets a reference to the entity identified by the given type and identifier without actually loading it, if the entity is not yet loaded
*/
getReference<T extends IEntityType<T>>(entityName: string | EntityClass<T>, id: IPrimaryKey): T {
getReference<T extends IEntityType<T>>(entityName: EntityName<T>, id: IPrimaryKey): T {
entityName = Utils.className(entityName);

if (this.getUnitOfWork().getById(entityName, id)) {
Expand All @@ -194,13 +181,13 @@ export class EntityManager {
return this.entityFactory.createReference<T>(entityName, id);
}

async count<T extends IEntityType<T>>(entityName: string | EntityClass<T>, where: FilterQuery<T>): Promise<number> {
async count<T extends IEntityType<T>>(entityName: EntityName<T>, where: FilterQuery<T>): Promise<number> {
entityName = Utils.className(entityName);
this.validator.validateParams(where);
return this.driver.count(entityName, where);
}

async persist(entity: IEntity | IEntity[], flush = this.options.autoFlush): Promise<void> {
async persist(entity: IEntity | IEntity[], flush = this.config.get('autoFlush')): Promise<void> {
if (flush) {
await this.persistAndFlush(entity);
} else {
Expand All @@ -226,7 +213,7 @@ export class EntityManager {
}
}

async remove<T extends IEntityType<T>>(entityName: string | EntityClass<T>, where: T | any, flush = this.options.autoFlush): Promise<number> {
async remove<T extends IEntityType<T>>(entityName: EntityName<T>, where: T | any, flush = this.config.get('autoFlush')): Promise<number> {
entityName = Utils.className(entityName);

if (Utils.isEntity(where)) {
Expand All @@ -237,7 +224,7 @@ export class EntityManager {
return this.nativeDelete(entityName, where);
}

async removeEntity(entity: IEntity, flush = this.options.autoFlush): Promise<void> {
async removeEntity(entity: IEntity, flush = this.config.get('autoFlush')): Promise<void> {
if (flush) {
await this.removeAndFlush(entity);
} else {
Expand Down Expand Up @@ -286,7 +273,7 @@ export class EntityManager {
}

fork(): EntityManager {
return new EntityManager(this.options, this.driver);
return new EntityManager(this.config, this.driver);
}

getUnitOfWork(): UnitOfWork {
Expand Down
114 changes: 15 additions & 99 deletions lib/MikroORM.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,22 @@
import { EntityManager } from './EntityManager';
import { IDatabaseDriver } from './drivers/IDatabaseDriver';
import { NamingStrategy } from './naming-strategy/NamingStrategy';
import { FileCacheAdapter } from './cache/FileCacheAdapter';
import { CacheAdapter } from './cache/CacheAdapter';
import { Logger } from './utils/Logger';
import { Utils } from './utils/Utils';
import { TypeScriptMetadataProvider } from './metadata/TypeScriptMetadataProvider';
import { MetadataProvider } from './metadata/MetadataProvider';
import { EntityRepository } from './entity/EntityRepository';
import { EntityClass, IEntity } from './decorators/Entity';
import { NullCacheAdapter } from './cache/NullCacheAdapter';
import { Hydrator } from './hydration/Hydrator';
import { ObjectHydrator } from './hydration/ObjectHydrator';
import { EntityFactory } from './entity/EntityFactory';
import { MetadataDiscovery } from './metadata/MetadataDiscovery';

const defaultOptions = {
entities: [],
entitiesDirs: [],
entitiesDirsTs: [],
tsConfigPath: process.cwd() + '/tsconfig.json',
autoFlush: true,
strict: false,
logger: () => undefined,
baseDir: process.cwd(),
entityRepository: EntityRepository,
hydrator: ObjectHydrator,
debug: false,
cache: {
enabled: true,
adapter: FileCacheAdapter,
options: { cacheDir: process.cwd() + '/temp' },
},
metadataProvider: TypeScriptMetadataProvider,
};
import { MetadataDiscovery } from './metadata';
import { Configuration, Logger, Options } from './utils';

export class MikroORM {

em: EntityManager;
readonly options: MikroORMOptions;
readonly config: Configuration;
private readonly driver: IDatabaseDriver;
private readonly logger: Logger;

static async init(options: Options): Promise<MikroORM> {
const orm = new MikroORM(options);
const driver = await orm.connect();
orm.em = new EntityManager(orm.options, driver);
orm.em = new EntityManager(orm.config, driver);

try {
const storage = new MetadataDiscovery(orm.em, orm.options, orm.logger);
const storage = new MetadataDiscovery(orm.em, orm.config, orm.logger);
await storage.discover();

return orm;
Expand All @@ -58,25 +26,22 @@ export class MikroORM {
}
}

constructor(options: Options) {
this.options = Utils.merge({}, defaultOptions, options);
this.validateOptions();
this.logger = new Logger(this.options);
this.driver = this.initDriver();

if (!this.options.cache.enabled) {
this.options.cache.adapter = NullCacheAdapter;
constructor(options: Options | Configuration) {
if (options instanceof Configuration) {
this.config = options;
} else {
this.config = new Configuration(options);
}

if (!this.options.clientUrl) {
this.options.clientUrl = this.driver.getConnection().getDefaultClientUrl();
}
this.driver = this.config.getDriver();
this.logger = this.config.getLogger();
}

async connect(): Promise<IDatabaseDriver> {
await this.driver.getConnection().connect();
const clientUrl = this.options.clientUrl!.replace(/\/\/([^:]+):(\w+)@/, '//$1:*****@');
this.logger.info(`MikroORM: successfully connected to database ${this.options.dbName}${clientUrl ? ' on ' + clientUrl : ''}`);
const clientUrl = this.config.getClientUrl(true);
const dbName = this.config.get('dbName');
this.logger.info(`MikroORM: successfully connected to database ${dbName}${clientUrl ? ' on ' + clientUrl : ''}`);

return this.driver;
}
Expand All @@ -89,53 +54,4 @@ export class MikroORM {
return this.driver.getConnection().close(force);
}

private validateOptions(): void {
if (!this.options.dbName) {
throw new Error('No database specified, please fill in `dbName` option');
}

if (this.options.entities.length === 0 && this.options.entitiesDirs.length === 0) {
throw new Error('No entities found, please use `entities` or `entitiesDirs` option');
}
}

private initDriver(): IDatabaseDriver {
if (!this.options.driver) {
this.options.driver = require('./drivers/MongoDriver').MongoDriver;
}

return new this.options.driver!(this.options, this.logger);
}

}

export interface MikroORMOptions {
dbName: string;
entities: EntityClass<IEntity>[];
entitiesDirs: string[];
entitiesDirsTs: string[];
tsConfigPath: string;
autoFlush: boolean;
driver?: { new (options: MikroORMOptions, logger: Logger): IDatabaseDriver };
namingStrategy?: { new (): NamingStrategy };
hydrator: { new (factory: EntityFactory, driver: IDatabaseDriver): Hydrator };
entityRepository: { new (em: EntityManager, entityName: string | EntityClass<IEntity>): EntityRepository<IEntity> };
clientUrl?: string;
host?: string;
port?: number;
user?: string;
password?: string;
multipleStatements?: boolean; // for mysql driver
strict: boolean;
logger: (message: string) => void;
debug: boolean;
baseDir: string;
cache: {
enabled: boolean,
adapter: { new (...params: any[]): CacheAdapter },
options: Record<string, any>,
},
metadataProvider: { new (options: MikroORMOptions): MetadataProvider },
}

export type Options = Pick<MikroORMOptions, Exclude<keyof MikroORMOptions, keyof typeof defaultOptions>> | MikroORMOptions;
3 changes: 3 additions & 0 deletions lib/cache/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './CacheAdapter';
export * from './NullCacheAdapter';
export * from './FileCacheAdapter';
8 changes: 4 additions & 4 deletions lib/connections/Connection.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { MikroORMOptions } from '../MikroORM';
import { Logger } from '../utils/Logger';
import { Configuration } from '../utils';

export abstract class Connection {

constructor(protected readonly options: MikroORMOptions,
protected readonly logger: Logger) { }
protected readonly logger = this.config.getLogger();

constructor(protected readonly config: Configuration) { }

/**
* Establishes connection to database
Expand Down
12 changes: 6 additions & 6 deletions lib/connections/MongoConnection.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import {
Collection, Db, DeleteWriteOpResultObject, InsertOneWriteOpResult, MongoClient, ObjectID, UpdateWriteOpResult,
} from 'mongodb';
import { Collection, Db, DeleteWriteOpResultObject, InsertOneWriteOpResult, MongoClient, ObjectID, UpdateWriteOpResult } from 'mongodb';
import { Connection } from './Connection';
import { FilterQuery, QueryOrder, Utils } from '..';
import { Utils } from '../utils';
import { QueryOrder } from '../query';
import { FilterQuery } from '..';

export class MongoConnection extends Connection {

protected client: MongoClient;
protected db: Db;

async connect(): Promise<void> {
this.client = await MongoClient.connect(this.options.clientUrl as string, { useNewUrlParser: true });
this.db = this.client.db(this.options.dbName);
this.client = await MongoClient.connect(this.config.getClientUrl(), { useNewUrlParser: true });
this.db = this.client.db(this.config.get('dbName'));
}

async close(force?: boolean): Promise<void> {
Expand Down

0 comments on commit 1467edc

Please sign in to comment.