Skip to content

Commit 39924fc

Browse files
Merge branch 'IRCraziestTaxi-move-keepconnectionalive-into-retry'
2 parents 5da668f + eaefeb3 commit 39924fc

4 files changed

+143
-48
lines changed

lib/interfaces/typeorm-options.interface.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Type } from '@nestjs/common';
22
import { ModuleMetadata } from '@nestjs/common/interfaces';
3-
import { ConnectionOptions } from 'typeorm';
3+
import { Connection, ConnectionOptions } from 'typeorm';
44

55
export type TypeOrmModuleOptions = {
66
/**
@@ -41,6 +41,10 @@ export interface TypeOrmOptionsFactory {
4141
): Promise<TypeOrmModuleOptions> | TypeOrmModuleOptions;
4242
}
4343

44+
export type TypeOrmConnectionFactory = (
45+
options?: ConnectionOptions,
46+
) => Promise<Connection>;
47+
4448
export interface TypeOrmModuleAsyncOptions
4549
extends Pick<ModuleMetadata, 'imports'> {
4650
name?: string;
@@ -49,5 +53,6 @@ export interface TypeOrmModuleAsyncOptions
4953
useFactory?: (
5054
...args: any[]
5155
) => Promise<TypeOrmModuleOptions> | TypeOrmModuleOptions;
56+
connectionFactory?: TypeOrmConnectionFactory;
5257
inject?: any[];
5358
}

lib/typeorm-core.module.ts

+60-47
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,34 @@
11
import {
22
DynamicModule,
33
Global,
4-
Inject, Logger, Module,
4+
Inject,
5+
Logger,
6+
Module,
57
OnApplicationShutdown,
68
Provider,
7-
Type
9+
Type,
810
} from '@nestjs/common';
911
import { ModuleRef } from '@nestjs/core';
10-
import { defer, lastValueFrom } from 'rxjs';
12+
import { defer, lastValueFrom, of } from 'rxjs';
1113
import {
1214
Connection,
1315
ConnectionOptions,
1416
createConnection,
15-
getConnectionManager
17+
getConnectionManager,
1618
} from 'typeorm';
1719
import {
1820
generateString,
1921
getConnectionName,
2022
getConnectionToken,
2123
getEntityManagerToken,
22-
handleRetry
24+
handleRetry,
2325
} from './common/typeorm.utils';
2426
import { EntitiesMetadataStorage } from './entities-metadata.storage';
2527
import {
28+
TypeOrmConnectionFactory,
2629
TypeOrmModuleAsyncOptions,
2730
TypeOrmModuleOptions,
28-
TypeOrmOptionsFactory
31+
TypeOrmOptionsFactory,
2932
} from './interfaces/typeorm-options.interface';
3033
import { TYPEORM_MODULE_ID, TYPEORM_MODULE_OPTIONS } from './typeorm.constants';
3134

@@ -68,12 +71,18 @@ export class TypeOrmCoreModule implements OnApplicationShutdown {
6871
provide: getConnectionToken(options as ConnectionOptions) as string,
6972
useFactory: async (typeOrmOptions: TypeOrmModuleOptions) => {
7073
if (options.name) {
71-
return await this.createConnectionFactory({
72-
...typeOrmOptions,
73-
name: options.name,
74-
});
74+
return await this.createConnectionFactory(
75+
{
76+
...typeOrmOptions,
77+
name: options.name,
78+
},
79+
options.connectionFactory,
80+
);
7581
}
76-
return await this.createConnectionFactory(typeOrmOptions);
82+
return await this.createConnectionFactory(
83+
typeOrmOptions,
84+
options.connectionFactory,
85+
);
7786
},
7887
inject: [TYPEORM_MODULE_OPTIONS],
7988
};
@@ -164,52 +173,56 @@ export class TypeOrmCoreModule implements OnApplicationShutdown {
164173

165174
private static async createConnectionFactory(
166175
options: TypeOrmModuleOptions,
176+
connectionFactory?: TypeOrmConnectionFactory,
167177
): Promise<Connection> {
168-
try {
169-
if (options.keepConnectionAlive) {
170-
const connectionName = getConnectionName(options as ConnectionOptions);
171-
const manager = getConnectionManager();
172-
if (manager.has(connectionName)) {
173-
const connection = manager.get(connectionName);
174-
if (connection.isConnected) {
175-
return connection;
178+
const connectionToken = getConnectionName(options as ConnectionOptions);
179+
const createTypeormConnection = connectionFactory ?? createConnection;
180+
return await lastValueFrom(
181+
defer(() => {
182+
try {
183+
if (options.keepConnectionAlive) {
184+
const connectionName = getConnectionName(
185+
options as ConnectionOptions,
186+
);
187+
const manager = getConnectionManager();
188+
if (manager.has(connectionName)) {
189+
const connection = manager.get(connectionName);
190+
if (connection.isConnected) {
191+
return of(connection);
192+
}
193+
}
176194
}
177-
}
178-
}
179-
} catch {}
195+
} catch {}
180196

181-
const connectionToken = getConnectionName(options as ConnectionOptions);
182-
return lastValueFrom(defer(() => {
183-
if (!options.type) {
184-
return createConnection();
185-
}
186-
if (!options.autoLoadEntities) {
187-
return createConnection(options as ConnectionOptions);
188-
}
197+
if (!options.type) {
198+
return createTypeormConnection();
199+
}
200+
if (!options.autoLoadEntities) {
201+
return createTypeormConnection(options as ConnectionOptions);
202+
}
189203

190-
let entities = options.entities;
191-
if (entities) {
192-
entities = entities.concat(
193-
EntitiesMetadataStorage.getEntitiesByConnection(connectionToken),
194-
);
195-
} else {
196-
entities = EntitiesMetadataStorage.getEntitiesByConnection(
197-
connectionToken,
198-
);
199-
}
200-
return createConnection({
201-
...options,
202-
entities,
203-
} as ConnectionOptions);
204-
})
205-
.pipe(
204+
let entities = options.entities;
205+
if (entities) {
206+
entities = entities.concat(
207+
EntitiesMetadataStorage.getEntitiesByConnection(connectionToken),
208+
);
209+
} else {
210+
entities =
211+
EntitiesMetadataStorage.getEntitiesByConnection(connectionToken);
212+
}
213+
return createTypeormConnection({
214+
...options,
215+
entities,
216+
} as ConnectionOptions);
217+
}).pipe(
206218
handleRetry(
207219
options.retryAttempts,
208220
options.retryDelay,
209221
connectionToken,
210222
options.verboseRetryLog,
211223
options.toRetry,
212224
),
213-
))
225+
),
226+
);
214227
}
215228
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { INestApplication } from '@nestjs/common';
2+
import { Test } from '@nestjs/testing';
3+
import { Server } from 'http';
4+
import * as request from 'supertest';
5+
import { AsyncConnectionFactoryOptionsFactoryModule } from '../src/async-connection-factory-options.module';
6+
7+
describe('TypeOrm (async configuration with connectionFactory)', () => {
8+
let server: Server;
9+
let app: INestApplication;
10+
11+
beforeEach(async () => {
12+
const module = await Test.createTestingModule({
13+
imports: [AsyncConnectionFactoryOptionsFactoryModule],
14+
}).compile();
15+
16+
app = module.createNestApplication();
17+
server = app.getHttpServer();
18+
await app.init();
19+
});
20+
21+
it(`should return created entity`, () => {
22+
return request(server)
23+
.post('/photo')
24+
.expect(201, { name: 'Nest', description: 'Is great!', views: 6000 });
25+
});
26+
27+
afterEach(async () => {
28+
await app.close();
29+
});
30+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Module } from '@nestjs/common';
2+
import { createConnection } from 'typeorm';
3+
import { TypeOrmModule } from '../../lib';
4+
import { Photo } from './photo/photo.entity';
5+
import { PhotoModule } from './photo/photo.module';
6+
7+
@Module({
8+
imports: [
9+
TypeOrmModule.forRootAsync({
10+
useFactory: () => ({
11+
type: 'postgres',
12+
host: '0.0.0.0',
13+
port: 3306,
14+
username: 'root',
15+
password: 'root',
16+
database: 'test',
17+
entities: [Photo],
18+
synchronize: true,
19+
retryAttempts: 2,
20+
retryDelay: 1000,
21+
}),
22+
connectionFactory: async (options) => {
23+
// Realistically, this function would be used for more than simply creating a connection,
24+
// i.e. checking for an existing and active connection prior to creating a new one.
25+
// However, including that logic here causes runtime test errors about variables being used before assignment.
26+
// Therefore, given the simple nature of this test case, simply create and return a connection.
27+
const connection = await createConnection(options!);
28+
return connection;
29+
},
30+
}),
31+
TypeOrmModule.forRoot({
32+
name: 'connection_2',
33+
type: 'postgres',
34+
host: '0.0.0.0',
35+
port: 3306,
36+
username: 'root',
37+
password: 'root',
38+
database: 'test',
39+
entities: [Photo],
40+
synchronize: true,
41+
retryAttempts: 2,
42+
retryDelay: 1000,
43+
}),
44+
PhotoModule,
45+
],
46+
})
47+
export class AsyncConnectionFactoryOptionsFactoryModule {}

0 commit comments

Comments
 (0)