Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(): support typeorm ^0.3.0 version #1233

Merged
merged 9 commits into from
Jun 1, 2022
24 changes: 12 additions & 12 deletions lib/common/typeorm.decorators.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { Inject } from '@nestjs/common';
import { Connection, ConnectionOptions } from 'typeorm';
import { DataSource, DataSourceOptions } from 'typeorm';
import { EntityClassOrSchema } from '../interfaces/entity-class-or-schema.type';
import { DEFAULT_CONNECTION_NAME } from '../typeorm.constants';
import { DEFAULT_DATA_SOURCE_NAME } from '../typeorm.constants';
import {
getConnectionToken,
getDataSourceToken,
getEntityManagerToken,
getRepositoryToken,
} from './typeorm.utils';

export const InjectRepository = (
entity: EntityClassOrSchema,
connection: string = DEFAULT_CONNECTION_NAME,
): ReturnType<typeof Inject> => Inject(getRepositoryToken(entity, connection));
dataSource: string = DEFAULT_DATA_SOURCE_NAME,
): ReturnType<typeof Inject> => Inject(getRepositoryToken(entity, dataSource));

export const InjectConnection: (
connection?: Connection | ConnectionOptions | string,
export const InjectdataSource: (
kamilmysliwiec marked this conversation as resolved.
Show resolved Hide resolved
kamilmysliwiec marked this conversation as resolved.
Show resolved Hide resolved
dataSource?: DataSource | DataSourceOptions | string,
) => ReturnType<typeof Inject> = (
connection?: Connection | ConnectionOptions | string,
) => Inject(getConnectionToken(connection));
dataSource?: DataSource | DataSourceOptions | string,
) => Inject(getDataSourceToken(dataSource));

export const InjectEntityManager: (
connection?: Connection | ConnectionOptions | string,
dataSource?: DataSource | DataSourceOptions | string,
) => ReturnType<typeof Inject> = (
connection?: Connection | ConnectionOptions | string,
) => Inject(getEntityManagerToken(connection));
dataSource?: DataSource | DataSourceOptions | string,
) => Inject(getEntityManagerToken(dataSource));
119 changes: 66 additions & 53 deletions lib/common/typeorm.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,53 @@ import { Observable } from 'rxjs';
import { delay, retryWhen, scan } from 'rxjs/operators';
import {
AbstractRepository,
Connection,
ConnectionOptions,
DataSource,
DataSourceOptions,
EntityManager,
EntitySchema,
Repository
Repository,
} from 'typeorm';
import { v4 as uuid } from 'uuid';
import { CircularDependencyException } from '../exceptions/circular-dependency.exception';
import { EntityClassOrSchema } from '../interfaces/entity-class-or-schema.type';
import { DEFAULT_CONNECTION_NAME } from '../typeorm.constants';
import { DEFAULT_DATA_SOURCE_NAME } from '../typeorm.constants';

const logger = new Logger('TypeOrmModule');

/**
* This function generates an injection token for an Entity or Repository
* @param {EntityClassOrSchema} entity parameter can either be an Entity or Repository
* @param {string} [connection='default'] Connection name
* @param {string} [dataSource='default'] DataSource name
* @returns {string} The Entity | Repository injection token
*/
export function getRepositoryToken(
entity: EntityClassOrSchema,
connection: Connection | ConnectionOptions | string = DEFAULT_CONNECTION_NAME,
dataSource:
| DataSource
| DataSourceOptions
| string = DEFAULT_DATA_SOURCE_NAME,
): Function | string {
if (entity === null || entity === undefined) {
throw new CircularDependencyException('@InjectRepository()');
}
const connectionPrefix = getConnectionPrefix(connection);
const dataSourcePrefix = getDataSourcePrefix(dataSource);
if (
entity instanceof Function &&
(entity.prototype instanceof Repository ||
entity.prototype instanceof AbstractRepository)
) {
if (!connectionPrefix) {
if (!dataSourcePrefix) {
return entity;
}
return `${connectionPrefix}${getCustomRepositoryToken(entity)}`;
return `${dataSourcePrefix}${getCustomRepositoryToken(entity)}`;
}

if (entity instanceof EntitySchema) {
return `${connectionPrefix}${
return `${dataSourcePrefix}${
entity.options.target ? entity.options.target.name : entity.options.name
}Repository`;
}
return `${connectionPrefix}${entity.name}Repository`;
return `${dataSourcePrefix}${entity.name}Repository`;
}

/**
Expand All @@ -62,66 +65,75 @@ export function getCustomRepositoryToken(repository: Function): string {
}

/**
* This function returns a Connection injection token for the given Connection, ConnectionOptions or connection name.
* @param {Connection | ConnectionOptions | string} [connection='default'] This optional parameter is either
* a Connection, or a ConnectionOptions or a string.
* @returns {string | Function} The Connection injection token.
* This function returns a DataSource injection token for the given DataSource, DataSourceOptions or dataSource name.
* @param {DataSource | DataSourceOptions | string} [dataSource='default'] This optional parameter is either
* a DataSource, or a DataSourceOptions or a string.
* @returns {string | Function} The DataSource injection token.
*/
export function getConnectionToken(
connection: Connection | ConnectionOptions | string = DEFAULT_CONNECTION_NAME,
): string | Function | Type<Connection> {
return DEFAULT_CONNECTION_NAME === connection
? Connection
: 'string' === typeof connection
? `${connection}Connection`
: DEFAULT_CONNECTION_NAME === connection.name || !connection.name
? Connection
: `${connection.name}Connection`;
export function getDataSourceToken(
dataSource:
| DataSource
| DataSourceOptions
| string = DEFAULT_DATA_SOURCE_NAME,
): string | Function | Type<DataSource> {
return DEFAULT_DATA_SOURCE_NAME === dataSource
? DataSource
: 'string' === typeof dataSource
? `${dataSource}DataSource`
: DEFAULT_DATA_SOURCE_NAME === dataSource.name || !dataSource.name
? DataSource
: `${dataSource.name}DataSource`;
}

/**
* This function returns a Connection prefix based on the connection name
* @param {Connection | ConnectionOptions | string} [connection='default'] This optional parameter is either
* a Connection, or a ConnectionOptions or a string.
* @returns {string | Function} The Connection injection token.
* This function returns a DataSource prefix based on the dataSource name
* @param {DataSource | DataSourceOptions | string} [dataSource='default'] This optional parameter is either
* a DataSource, or a DataSourceOptions or a string.
* @returns {string | Function} The DataSource injection token.
*/
export function getConnectionPrefix(
connection: Connection | ConnectionOptions | string = DEFAULT_CONNECTION_NAME,
export function getDataSourcePrefix(
dataSource:
| DataSource
| DataSourceOptions
| string = DEFAULT_DATA_SOURCE_NAME,
): string {
if (connection === DEFAULT_CONNECTION_NAME) {
if (dataSource === DEFAULT_DATA_SOURCE_NAME) {
return '';
}
if (typeof connection === 'string') {
return connection + '_';
if (typeof dataSource === 'string') {
return dataSource + '_';
}
if (connection.name === DEFAULT_CONNECTION_NAME || !connection.name) {
if (dataSource.name === DEFAULT_DATA_SOURCE_NAME || !dataSource.name) {
return '';
}
return connection.name + '_';
return dataSource.name + '_';
}

/**
* This function returns an EntityManager injection token for the given Connection, ConnectionOptions or connection name.
* @param {Connection | ConnectionOptions | string} [connection='default'] This optional parameter is either
* a Connection, or a ConnectionOptions or a string.
* This function returns an EntityManager injection token for the given DataSource, DataSourceOptions or dataSource name.
* @param {DataSource | DataSourceOptions | string} [dataSource='default'] This optional parameter is either
* a DataSource, or a DataSourceOptions or a string.
* @returns {string | Function} The EntityManager injection token.
*/
export function getEntityManagerToken(
connection: Connection | ConnectionOptions | string = DEFAULT_CONNECTION_NAME,
dataSource:
| DataSource
| DataSourceOptions
| string = DEFAULT_DATA_SOURCE_NAME,
): string | Function {
return DEFAULT_CONNECTION_NAME === connection
return DEFAULT_DATA_SOURCE_NAME === dataSource
? EntityManager
: 'string' === typeof connection
? `${connection}EntityManager`
: DEFAULT_CONNECTION_NAME === connection.name || !connection.name
: 'string' === typeof dataSource
? `${dataSource}EntityManager`
: DEFAULT_DATA_SOURCE_NAME === dataSource.name || !dataSource.name
? EntityManager
: `${connection.name}EntityManager`;
: `${dataSource.name}EntityManager`;
}

export function handleRetry(
retryAttempts = 9,
retryDelay = 3000,
connectionName = DEFAULT_CONNECTION_NAME,
dataSourceName = DEFAULT_DATA_SOURCE_NAME,
verboseRetryLog = false,
toRetry?: (err: any) => boolean,
): <T>(source: Observable<T>) => Observable<T> {
Expand All @@ -133,17 +145,18 @@ export function handleRetry(
if (toRetry && !toRetry(error)) {
throw error;
}
const connectionInfo =
connectionName === DEFAULT_CONNECTION_NAME
const dataSourceInfo =
dataSourceName === DEFAULT_DATA_SOURCE_NAME
? ''
: ` (${connectionName})`;
: ` (${dataSourceName})`;
const verboseMessage = verboseRetryLog
? ` Message: ${error.message}.`
: '';

logger.error(
`Unable to connect to the database${connectionInfo}.${verboseMessage} Retrying (${errorCount +
1})...`,
`Unable to connect to the database${dataSourceInfo}.${verboseMessage} Retrying (${
errorCount + 1
})...`,
error.stack,
);
if (errorCount + 1 >= retryAttempts) {
Expand All @@ -157,8 +170,8 @@ export function handleRetry(
);
}

export function getConnectionName(options: ConnectionOptions): string {
return options && options.name ? options.name : DEFAULT_CONNECTION_NAME;
export function getDataSourceName(options: DataSourceOptions): string {
return options && options.name ? options.name : DEFAULT_DATA_SOURCE_NAME;
}

export const generateString = (): string => uuid();
30 changes: 15 additions & 15 deletions lib/entities-metadata.storage.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { Connection, ConnectionOptions } from 'typeorm';
import { DataSource, DataSourceOptions } from 'typeorm';
import { EntityClassOrSchema } from './interfaces/entity-class-or-schema.type';

type ConnectionToken = Connection | ConnectionOptions | string;
type DataSourceToken = DataSource | DataSourceOptions | string;

export class EntitiesMetadataStorage {
private static readonly storage = new Map<string, EntityClassOrSchema[]>();

static addEntitiesByConnection(
connection: ConnectionToken,
static addEntitiesByDataSource(
dataSource: DataSourceToken,
entities: EntityClassOrSchema[],
): void {
const connectionToken =
typeof connection === 'string' ? connection : connection.name;
if (!connectionToken) {
const dataSourceToken =
typeof dataSource === 'string' ? dataSource : dataSource.name;
if (!dataSourceToken) {
return;
}

let collection = this.storage.get(connectionToken);
let collection = this.storage.get(dataSourceToken);
if (!collection) {
collection = [];
this.storage.set(connectionToken, collection);
this.storage.set(dataSourceToken, collection);
}
entities.forEach((entity) => {
if (collection!.includes(entity)) {
Expand All @@ -29,15 +29,15 @@ export class EntitiesMetadataStorage {
});
}

static getEntitiesByConnection(
connection: ConnectionToken,
static getEntitiesByDataSource(
dataSource: DataSourceToken,
): EntityClassOrSchema[] {
const connectionToken =
typeof connection === 'string' ? connection : connection.name;
const dataSourceToken =
typeof dataSource === 'string' ? dataSource : dataSource.name;

if (!connectionToken) {
if (!dataSourceToken) {
return [];
}
return this.storage.get(connectionToken) || [];
return this.storage.get(dataSourceToken) || [];
}
}
39 changes: 0 additions & 39 deletions lib/helpers/get-custom-repository-entity.ts

This file was deleted.

1 change: 0 additions & 1 deletion lib/helpers/index.ts

This file was deleted.

16 changes: 6 additions & 10 deletions lib/interfaces/typeorm-options.interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ModuleMetadata, Type } from '@nestjs/common';
import { Connection, ConnectionOptions } from 'typeorm';
import { DataSource, DataSourceOptions } from 'typeorm';

export type TypeOrmModuleOptions = {
/**
Expand All @@ -24,25 +24,21 @@ export type TypeOrmModuleOptions = {
* If `true`, entities will be loaded automatically.
*/
autoLoadEntities?: boolean;
/**
* If `true`, connection will not be closed on application shutdown.
*/
keepConnectionAlive?: boolean;
/**
* If `true`, will show verbose error messages on each connection retry.
*/
verboseRetryLog?: boolean;
} & Partial<ConnectionOptions>;
} & Partial<DataSourceOptions>;

export interface TypeOrmOptionsFactory {
createTypeOrmOptions(
connectionName?: string,
): Promise<TypeOrmModuleOptions> | TypeOrmModuleOptions;
}

export type TypeOrmConnectionFactory = (
options?: ConnectionOptions,
) => Promise<Connection>;
export type TypeOrmDataSourceFactory = (
options?: DataSourceOptions,
) => Promise<DataSource>;

export interface TypeOrmModuleAsyncOptions
extends Pick<ModuleMetadata, 'imports'> {
Expand All @@ -52,6 +48,6 @@ export interface TypeOrmModuleAsyncOptions
useFactory?: (
...args: any[]
) => Promise<TypeOrmModuleOptions> | TypeOrmModuleOptions;
connectionFactory?: TypeOrmConnectionFactory;
dataSourceFactory?: TypeOrmDataSourceFactory;
inject?: any[];
}