@nestjs/typeorm
Version:
TypeORM for NestJS: connections and repositories through dependency injection, entities, transactions and multiple databases.
130 lines (129 loc) • 5.75 kB
JavaScript
import { Logger } from '@nestjs/common';
import { delay, retryWhen, scan } from 'rxjs/operators';
import { DataSource, EntityManager, EntitySchema, Repository } from 'typeorm';
import { CircularDependencyException } from '../exceptions/circular-dependency.exception.js';
import { DEFAULT_DATA_SOURCE_NAME } from '../typeorm.constants.js';
import { AbstractRepository } from './typeorm-compat.js';
const logger = new Logger('TypeOrmModule');
/**
* This function returns the DataSource name for the given DataSource, DataSourceOptions
* or dataSource name. TypeORM v1 removed `name` from `DataSource` and `DataSourceOptions`,
* so it is read defensively while remaining backward compatible with 0.3.x
* (where the user-supplied options still carry it).
* @param {DataSource | DataSourceOptions | string} [dataSource='default'] This optional
* parameter is either a DataSource, or a DataSourceOptions or a string.
* @returns {string} The DataSource name, or 'default' when no name is set.
*
* @publicApi
*/
export function getDataSourceName(dataSource = DEFAULT_DATA_SOURCE_NAME) {
if (typeof dataSource === 'string') {
return dataSource;
}
return (dataSource?.name || DEFAULT_DATA_SOURCE_NAME);
}
/**
* This function generates an injection token for an Entity or Repository
* @param {EntityClassOrSchema} entity parameter can either be an Entity or Repository
* @param {DataSource | DataSourceOptions | string} [dataSource='default'] This optional parameter is either
* a DataSource, or a DataSourceOptions or a string.
* @returns {string} The Entity | Repository injection token
*
* @publicApi
*/
export function getRepositoryToken(entity, dataSource = DEFAULT_DATA_SOURCE_NAME) {
if (entity === null || entity === undefined) {
throw new CircularDependencyException('@InjectRepository()');
}
const dataSourcePrefix = getDataSourcePrefix(dataSource);
if (entity instanceof Function &&
(entity.prototype instanceof Repository ||
(AbstractRepository && entity.prototype instanceof AbstractRepository))) {
if (!dataSourcePrefix) {
return entity;
}
return `${dataSourcePrefix}${getCustomRepositoryToken(entity)}`;
}
if (entity instanceof EntitySchema) {
const schemaName = entity.options.target
? entity.options.target.name
: entity.options.name;
if (!schemaName) {
throw new Error('EntitySchema must have either "target" or "name" defined');
}
return `${dataSourcePrefix}${schemaName}Repository`;
}
return `${dataSourcePrefix}${entity.name}Repository`;
}
/**
* This function generates an injection token for an Entity or Repository
* @param {Function} This parameter can either be an Entity or Repository
* @returns {string} The Repository injection token
*
* @publicApi
*/
export function getCustomRepositoryToken(repository) {
if (repository === null || repository === undefined) {
throw new CircularDependencyException('@InjectRepository()');
}
return repository.name;
}
/**
* 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.
*
* @publicApi
*/
export function getDataSourceToken(dataSource = DEFAULT_DATA_SOURCE_NAME) {
const name = getDataSourceName(dataSource);
return name === DEFAULT_DATA_SOURCE_NAME ? DataSource : `${name}DataSource`;
}
/**
* @deprecated
*
* @publicApi
*/
export const getConnectionToken = getDataSourceToken;
/**
* 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 getDataSourcePrefix(dataSource = DEFAULT_DATA_SOURCE_NAME) {
const name = getDataSourceName(dataSource);
return name === DEFAULT_DATA_SOURCE_NAME ? '' : `${name}_`;
}
/**
* 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(dataSource = DEFAULT_DATA_SOURCE_NAME) {
const name = getDataSourceName(dataSource);
return name === DEFAULT_DATA_SOURCE_NAME
? EntityManager
: `${name}EntityManager`;
}
export function handleRetry(retryAttempts = 10, retryDelay = 3000, dataSourceName = DEFAULT_DATA_SOURCE_NAME, verboseRetryLog = false, toRetry) {
return (source) => source.pipe(retryWhen((e) => e.pipe(scan((errorCount, error) => {
if (toRetry && !toRetry(error)) {
throw error;
}
const dataSourceInfo = dataSourceName === DEFAULT_DATA_SOURCE_NAME
? ''
: ` (${dataSourceName})`;
const verboseMessage = verboseRetryLog
? ` Message: ${error.message}.`
: '';
logger.error(`Unable to connect to the database${dataSourceInfo}.${verboseMessage} Retrying (${errorCount + 1})...`, error.stack);
if (errorCount + 1 >= retryAttempts) {
throw error;
}
return errorCount + 1;
}, 0), delay(retryDelay))));
}
export const generateString = () => crypto.randomUUID();