Skip to content

Commit 23a8bf4

Browse files
authored
Merge pull request #139 from Avallone-io/feat/typeorm-v0.3
feat: upgrade to typeorm v0.3.0
2 parents bc69be1 + b23b881 commit 23a8bf4

17 files changed

+2157
-2119
lines changed

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v16.13.1

README.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ For example, assuming an express application:
3737

3838
```typescript
3939
app.use((req, res, next) => {
40-
const connection = getConnection(); // get default typeorm connection
40+
const dataSource = await new DataSource({...}).initialize(); // create a datasource and initialize it
4141

4242
// get tenantId and actorId from somewhere (headers/token etc)
43-
const rlsConnection = new RLSConnection(connection, {
43+
const rlsConnection = new RLSConnection(dataSource, {
4444
actorId,
4545
tenantId,
4646
});
@@ -54,24 +54,26 @@ const userRepo = res.locals.connection.getRepository(User);
5454
await userRepo.find(); // will return only the results where the db rls policy applies
5555
```
5656

57-
In the above example, you'll have to work with the supplied connection. Calling TypeORM function directly will work with the original connection which is not RLS aware.
57+
In the above example, you'll have to work with the supplied connection. Calling TypeORM function directly will work with the original DataSource object which is not RLS aware.
5858

5959
## NestJS integration
6060

6161
If you are using NestJS, this library provides helpers for making your connections and queries tenant aware.
6262

63-
Create your TypeORM config and load the TypeORM module using `.forRoot`. Then you'll need to load the `RLSModule` with `.forRoot` where you'll define where to take the `tenantId` and `actorId` from. The second part is that you now need to replace the `TypeOrmModule.forFeature` with `RLSModule.forFeature`.
63+
Create your TypeORM config and load the TypeORM module using `.forRoot`. Then you'll need to load the `RLSModule` with `.forRoot` where you'll define where to take the `tenantId` and `actorId` from. The second part is that you now need to replace the `TypeOrmModule.forFeature` with `RLSModule.forFeature`. This should be a 1-to-1 replacement.
6464
You can inject non-entity dependent Modules and Providers. First array imports modules, second array injects providers.
6565

6666
When using `RLSModule.forRoot` it will set your `scope` to `REQUEST`! Be sure you understand the implications of this and especially read about the request-scoped authentication strategy on [Nestjs docs](https://docs.nestjs.com/security/authentication#request-scoped-strategies).
6767

68+
The `RLSModule.forRoot` accepts the factory funtion as async or non-async function.
69+
6870
```typescript
6971
app.controller.ts
7072

7173
@Module({
7274
imports: [
7375
TypeOrmModule.forRoot(...),
74-
RLSModule.forRoot([/*Module*/], [/*Service*/], (req: Request, /*serviceInstance*/) => {
76+
RLSModule.forRoot([/*Module*/], [/*Service*/], async (req: Request, /*serviceInstance*/) => {
7577
// You can take the tenantId and actorId from headers/tokens etc
7678
const tenantId = req.headers['tenant_id'];
7779
const actorId = req.headers['actor_id'];
@@ -107,6 +109,9 @@ export class AppService {
107109
}
108110
```
109111

110-
Same as before, do not use the TypeORM functions directly (eg: `getConnection()`) as that will give you the default connection to the database, not the wrapped instance.
112+
Same as before, do not use the TypeORM functions directly from the `dataSource` as that will give you the default connection to the database, not the wrapped instance.
111113

112114
For more specific examples, check the `test/nestjs/src`.
115+
116+
# Typeorm >v0.3.0
117+
Since typeorm v0.3.0, the Connection class has been replaced by DataSource. This module still uses Connection as its language which is also helpful now to differenciate between the actual database connection (typeorm DataSource) and RLS wrapper (RLSConnection). However, if you want to be on par with typeorm terminalogy, there is an alias for `RLSConnection` called `RLSDataSource`.

lib/common/RLSConnection.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Connection, EntityMetadata, ReplicationMode } from 'typeorm';
1+
import { EntityMetadata, ReplicationMode, DataSource } from 'typeorm';
22
import { RLSPostgresDriver } from '../common/RLSPostgresDriver';
33
import {
44
ActorId,
@@ -7,18 +7,18 @@ import {
77
} from '../interfaces/tenant-options.interface';
88
import { RLSPostgresQueryRunner } from './RLSPostgresQueryRunner';
99

10-
export class RLSConnection extends Connection {
10+
export class RLSConnection extends DataSource {
1111
readonly driver: RLSPostgresDriver;
1212

1313
tenantId: TenantId = null;
1414
actorId: ActorId = null;
1515

1616
constructor(
17-
connection: Connection,
17+
dataSource: DataSource,
1818
tenancyModelOptions: TenancyModelOptions,
1919
) {
20-
super(connection.options);
21-
Object.assign(this, connection);
20+
super(dataSource.options);
21+
Object.assign(this, dataSource);
2222
Object.assign(this.relationLoader, { connection: this });
2323

2424
this.tenantId = tenancyModelOptions.tenantId;
@@ -97,9 +97,21 @@ export class RLSConnection extends Connection {
9797
return queryRunner;
9898
}
9999

100+
/**
101+
* @deprecated use .destroy method instead
102+
*/
100103
close(): Promise<void> {
101104
throw new Error(
102-
'Cannot close connection on a virtual connection. Use the original connection object to close the connection',
105+
'Cannot close virtual connection. Use the original DataSource object to close it',
106+
);
107+
}
108+
109+
destroy(): Promise<void> {
110+
throw new Error(
111+
'Cannot destroy virtual connection. Use the original DataSource object to destoy it',
103112
);
104113
}
105114
}
115+
116+
// eslint-disable-next-line @typescript-eslint/naming-convention
117+
export const RLSDataSource = RLSConnection;

lib/rls.module.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,22 @@ import { RLSConnection } from './common';
1515
import { TenancyModelOptions } from './interfaces/tenant-options.interface';
1616
import { TENANT_CONNECTION } from './rls.constants';
1717
import { createTypeormRLSProviders } from './rls.provider';
18-
import { Connection, ConnectionOptions } from 'typeorm';
19-
import { getCustomRepositoryEntity } from '@nestjs/typeorm/dist/helpers/get-custom-repository-entity';
18+
import { DataSource, DataSourceOptions } from 'typeorm';
2019
import { EntitiesMetadataStorage } from '@nestjs/typeorm/dist/entities-metadata.storage';
21-
import { DEFAULT_CONNECTION_NAME } from '@nestjs/typeorm/dist/typeorm.constants';
20+
import { DEFAULT_DATA_SOURCE_NAME } from '@nestjs/typeorm/dist/typeorm.constants';
2221

2322
@Global()
2423
@Module({})
2524
export class RLSModule {
2625
static forFeature(
2726
entities: EntityClassOrSchema[] = [],
2827
connection:
29-
| Connection
30-
| ConnectionOptions
31-
| string = DEFAULT_CONNECTION_NAME,
28+
| DataSource
29+
| DataSourceOptions
30+
| string = DEFAULT_DATA_SOURCE_NAME,
3231
): DynamicModule {
3332
const providers = createTypeormRLSProviders(entities, connection);
34-
const customRepositoryEntities = getCustomRepositoryEntity(entities);
35-
EntitiesMetadataStorage.addEntitiesByConnection(connection, [
36-
...entities,
37-
...customRepositoryEntities,
38-
]);
33+
EntitiesMetadataStorage.addEntitiesByDataSource(connection, [...entities]);
3934
return {
4035
module: RLSModule,
4136
providers: providers,
@@ -60,9 +55,9 @@ export class RLSModule {
6055
): DynamicModule {
6156
const rlsProvider: Provider = {
6257
provide: TENANT_CONNECTION,
63-
inject: [REQUEST, Connection, ...injectServices],
58+
inject: [REQUEST, DataSource, ...injectServices],
6459
scope: Scope.REQUEST,
65-
useFactory: async (request: Request, connection: Connection, ...args) => {
60+
useFactory: async (request: Request, connection: DataSource, ...args) => {
6661
const tenantModelOptions: TenancyModelOptions = await extractTenant(
6762
request,
6863
...args,

0 commit comments

Comments
 (0)