diff --git a/packages/core/lib/helpers/index.ts b/packages/core/lib/helpers/index.ts new file mode 100644 index 00000000..23abf7bd --- /dev/null +++ b/packages/core/lib/helpers/index.ts @@ -0,0 +1 @@ +export * from './knexion-execution-context-host'; diff --git a/packages/core/lib/helpers/knexion-execution-context-host.ts b/packages/core/lib/helpers/knexion-execution-context-host.ts new file mode 100644 index 00000000..c7eabf48 --- /dev/null +++ b/packages/core/lib/helpers/knexion-execution-context-host.ts @@ -0,0 +1,95 @@ +import { Type } from '@nestjs/common/interfaces'; +import { + KnexArgumentsHost, + KnexionContextType, + KnexionExecutionContext, + KnexionMethodType, +} from '../interfaces'; +import { DefaultRepositoryOptions } from '../repository'; + +export class KnexionExecutionContextHost< + TRecord, + TResult, + IdType = DefaultRepositoryOptions['idType'], +> implements KnexionExecutionContext +{ + private contextType = 'knex'; + private methodType = null; + + constructor( + private readonly args: any[], + private readonly constructorRef: Type = null, + private readonly handler: Function = null, + ) {} + + setType(type: TContext): void { + type && (this.contextType = type); + } + + getType(): TContext { + return this.contextType as TContext; + } + + setMethod(type: TMethod): void { + type && (this.methodType = type); + } + + getMethod(): TMethod { + return this.methodType as TMethod; + } + + getClass(): Type { + return this.constructorRef; + } + + getHandler(): Function { + return this.handler; + } + + getArgs = any[]>(): T { + return this.args as T; + } + + getArgByIndex(index: number): T { + return this.args[index] as T; + } + + switchToKnex(): KnexArgumentsHost { + return Object.assign(this, { + getQueryBuilder: () => this.getArgByIndex(0), + getRawBuilder: () => this.getArgByIndex(1), + createQueryBuilder: () => this.getArgByIndex(2)(), + getOptions: () => this.getArgByIndex(3), + switchToList: () => + Object.assign(this, { + getQueryBuilder: () => this.getArgByIndex(0), + getOptions: () => this.getArgByIndex(3), + }), + switchToCreate: () => + Object.assign(this, { + getQueryBuilder: () => this.getArgByIndex(0), + getOptions: () => this.getArgByIndex(3), + getPayload: () => this.getArgByIndex(4), + }), + switchToRetrieve: () => + Object.assign(this, { + getQueryBuilder: () => this.getArgByIndex(0), + getOptions: () => this.getArgByIndex(3), + getId: () => this.getArgByIndex(4), + }), + switchToUpdate: () => + Object.assign(this, { + getQueryBuilder: () => this.getArgByIndex(0), + getOptions: () => this.getArgByIndex(3), + getId: () => this.getArgByIndex(4), + getPayload: () => this.getArgByIndex(5), + }), + switchToDelete: () => + Object.assign(this, { + getQueryBuilder: () => this.getArgByIndex(0), + getOptions: () => this.getArgByIndex(3), + getId: () => this.getArgByIndex(4), + }), + }); + } +} diff --git a/packages/core/lib/index.ts b/packages/core/lib/index.ts index 7f9ce9d3..e3bd780a 100644 --- a/packages/core/lib/index.ts +++ b/packages/core/lib/index.ts @@ -1,9 +1,9 @@ export * from './decorators'; +export * from './helpers'; export * from './indicators'; export * from './interfaces'; export * from './services'; export * from './utils'; export * from './interceptors-consumer'; export * from './knexion.module'; -export * from './knexion-context'; export * from './repository'; diff --git a/packages/core/lib/interceptors-consumer.ts b/packages/core/lib/interceptors-consumer.ts index 2119ac42..310cc4b3 100644 --- a/packages/core/lib/interceptors-consumer.ts +++ b/packages/core/lib/interceptors-consumer.ts @@ -1,14 +1,14 @@ import { defer, from, isObservable, lastValueFrom, Observable } from 'rxjs'; import { mergeAll, switchMap } from 'rxjs/operators'; -import { KnexionCallHandler } from './interfaces'; -import { KnexionContext } from './knexion-context'; +import { KnexionCallHandler, KnexionInterceptors } from './interfaces'; +import { KnexionExecutionContextHost } from './helpers'; export class InterceptorsConsumer { - public async intercept( - context: KnexionContext, + public async intercept( + interceptors: KnexionInterceptors, + context: KnexionExecutionContextHost, next: () => Promise, ): Promise { - const { intercept: interceptors = [] } = context.options; if (!interceptors.length) { return next(); } diff --git a/packages/core/lib/interfaces/database-options.interface.ts b/packages/core/lib/interfaces/database-options.interface.ts index 2bd42ff2..e67babf7 100644 --- a/packages/core/lib/interfaces/database-options.interface.ts +++ b/packages/core/lib/interfaces/database-options.interface.ts @@ -5,17 +5,13 @@ export interface AliasableDatabaseOptions { alias?: string; } -export interface DatabaseOptions { +export interface DatabaseOptions { transaction?: Knex.Transaction; - intercept?: KnexionInterceptors; + intercept?: KnexionInterceptors; } export interface SelectDatabaseOptions - extends DatabaseOptions< - TRecord, - TResult, - SelectDatabaseOptions - >, + extends DatabaseOptions, AliasableDatabaseOptions { [field: string]: unknown; } diff --git a/packages/core/lib/interfaces/index.ts b/packages/core/lib/interfaces/index.ts index 96350c83..2935d9e2 100644 --- a/packages/core/lib/interfaces/index.ts +++ b/packages/core/lib/interfaces/index.ts @@ -1,4 +1,6 @@ export * from './database-options.interface'; +export * from './knexion-arguments-host.interface'; +export * from './knexion-execution-contex.interface'; export * from './knexion-module-options.interface'; export * from './knexion-repository-options.interface'; export * from './knexion-transaction.interface'; diff --git a/packages/core/lib/interfaces/knexion-arguments-host.interface.ts b/packages/core/lib/interfaces/knexion-arguments-host.interface.ts new file mode 100644 index 00000000..935d68b6 --- /dev/null +++ b/packages/core/lib/interfaces/knexion-arguments-host.interface.ts @@ -0,0 +1,118 @@ +import { + DatabaseOptions, + SelectDatabaseOptions, +} from './database-options.interface'; +import { Knex } from 'knex'; + +export type KnexionContextType = 'knex'; + +export type KnexionMethodType = + | 'list' + | 'create' + | 'retrieve' + | 'update' + | 'delete'; + +export interface KnexMethodArgumentsHost { + getQueryBuilder(): Knex.QueryBuilder; + getOptions(): unknown; +} + +export interface ListKnexMethodArgumentsHost + extends KnexMethodArgumentsHost { + getOptions< + Options extends SelectDatabaseOptions< + TRecord, + TResult + > = SelectDatabaseOptions, + >(): Options; +} + +export interface CreateKnexMethodArgumentsHost + extends KnexMethodArgumentsHost { + getPayload(): T; + getOptions< + Options extends DatabaseOptions = DatabaseOptions< + TRecord, + TResult + >, + >(): Options; +} + +export interface RetrieveKnexMethodArgumentsHost + extends KnexMethodArgumentsHost { + getId(): IdType; + getOptions< + Options extends SelectDatabaseOptions< + TRecord, + TResult + > = SelectDatabaseOptions, + >(): Options; +} + +export interface UpdateKnexMethodArgumentsHost + extends KnexMethodArgumentsHost { + getId(): IdType; + getPayload(): T; + getOptions< + Options extends DatabaseOptions = DatabaseOptions< + TRecord, + TResult + >, + >(): Options; +} + +export interface DeleteKnexMethodArgumentsHost + extends KnexMethodArgumentsHost { + getId(): IdType; + getOptions< + Options extends DatabaseOptions = DatabaseOptions< + TRecord, + TResult + >, + >(): Options; +} + +export interface KnexArgumentsHost { + getQueryBuilder(): Knex.QueryBuilder; + getRawBuilder(): Knex.RawBuilder; + createQueryBuilder(): Knex.QueryBuilder; + getOptions< + Options extends DatabaseOptions = DatabaseOptions< + TRecord, + TResult + >, + >(): Options; + switchToList(): ListKnexMethodArgumentsHost; + switchToCreate(): CreateKnexMethodArgumentsHost; + switchToRetrieve(): RetrieveKnexMethodArgumentsHost; + switchToUpdate(): UpdateKnexMethodArgumentsHost; + switchToDelete(): DeleteKnexMethodArgumentsHost; +} + +/** + * Provides methods for retrieving the arguments being passed to a handler. + */ +export interface KnexionArgumentsHost { + /** + * Returns the array of arguments being passed to the handler. + */ + getArgs = any[]>(): T; + /** + * Returns a particular argument by index. + * @param index index of argument to retrieve + */ + getArgByIndex(index: number): T; + + /** + * Returns the current execution context type (string) + */ + getType(): TContext; + + /** + * Returns the current execution method type (string) + */ + getMethod(): TMethod; + + switchToKnex(): KnexArgumentsHost; +} diff --git a/packages/core/lib/interfaces/knexion-execution-contex.interface.ts b/packages/core/lib/interfaces/knexion-execution-contex.interface.ts new file mode 100644 index 00000000..d4f9ea0b --- /dev/null +++ b/packages/core/lib/interfaces/knexion-execution-contex.interface.ts @@ -0,0 +1,19 @@ +import { Type } from '@nestjs/common'; +import { KnexionArgumentsHost } from './knexion-arguments-host.interface'; +import { DefaultRepositoryOptions } from '../repository'; + +export interface KnexionExecutionContext< + TRecord, + TResult = unknown, + IdType = DefaultRepositoryOptions['idType'], +> extends KnexionArgumentsHost { + /** + * Returns the *type* of the controller class which the current handler belongs to. + */ + getClass(): Type; + /** + * Returns a reference to the handler (method) that will be invoked next in the + * request pipeline. + */ + getHandler(): Function; +} diff --git a/packages/core/lib/interfaces/repository-interceptor.interface.ts b/packages/core/lib/interfaces/repository-interceptor.interface.ts index 7a4d2f65..38484d1d 100644 --- a/packages/core/lib/interfaces/repository-interceptor.interface.ts +++ b/packages/core/lib/interfaces/repository-interceptor.interface.ts @@ -1,6 +1,5 @@ import { Observable } from 'rxjs'; -import { KnexionContext } from '../knexion-context'; -import { DatabaseOptions } from './database-options.interface'; +import { KnexionExecutionContext } from './knexion-execution-contex.interface'; export interface KnexionCallHandler { handle(): Observable; @@ -9,15 +8,11 @@ export interface KnexionCallHandler { export interface KnexionInterceptor< TRecord = any, TResult = any, - Options extends DatabaseOptions = DatabaseOptions< - TRecord, - TResult - >, TInput = unknown, TOutput = TInput, > { intercept( - context: KnexionContext, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable | Promise>; } @@ -25,8 +20,4 @@ export interface KnexionInterceptor< export type KnexionInterceptors< TRecord = any, TResult = any, - Options extends DatabaseOptions = DatabaseOptions< - TRecord, - TResult - >, -> = KnexionInterceptor[]; +> = KnexionInterceptor[]; diff --git a/packages/core/lib/knexion-context.ts b/packages/core/lib/knexion-context.ts deleted file mode 100644 index d914f033..00000000 --- a/packages/core/lib/knexion-context.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Knex } from 'knex'; -import { DatabaseOptions } from './interfaces'; - -export class KnexionContext< - TRecord, - TResult = unknown, - Options extends DatabaseOptions = DatabaseOptions< - TRecord, - TResult - >, -> { - get queryBuilder(): Knex.QueryBuilder { - return this._queryBuilder; - } - - get rawBuilder(): Knex.RawBuilder { - return this._rawBuilder; - } - - get options(): Options { - return this._options ?? ({} as Options); - } - - constructor( - public readonly buildQueryBuilder: ( - trx?: Knex.Transaction, - ) => Knex.QueryBuilder, - private readonly _queryBuilder: Knex.QueryBuilder, - private readonly _rawBuilder: Knex.RawBuilder, - private readonly _constructorRef: Function, - private readonly _handler?: Function, - private readonly _options?: Options, - ) {} - - public getClass(): Function { - return this._constructorRef; - } - - public getHandler(): Function | undefined { - return this._handler; - } -} diff --git a/packages/core/lib/repository.ts b/packages/core/lib/repository.ts index 0c225eb9..56866735 100644 --- a/packages/core/lib/repository.ts +++ b/packages/core/lib/repository.ts @@ -5,7 +5,6 @@ import { KNEXION_INTERCEPTORS, KNEXION_REPOSITORY_OPTIONS, } from './knexion.constants'; -import { KnexionContext } from './knexion-context'; import { addPrefixColumn } from './utils'; import { DatabaseOptions, @@ -18,8 +17,9 @@ import { import { InjectKnex } from './decorators'; import { InterceptorsConsumer } from './interceptors-consumer'; import { isFunction } from './utils/internal'; +import { KnexionExecutionContextHost } from './helpers'; -export interface RepositoryOptions { +export interface DefaultRepositoryOptions { idType: string | number; omitCreateFields?: string; omitUpdateFields?: string; @@ -27,7 +27,7 @@ export interface RepositoryOptions { export class Repository< TRecord, - Options extends RepositoryOptions = RepositoryOptions, + RepositoryOptions extends DefaultRepositoryOptions = DefaultRepositoryOptions, > implements OnModuleInit { private readonly options: KnexionRepositoryOptions; @@ -57,31 +57,44 @@ export class Repository< options?: SelectDatabaseOptions, ): Promise { options = this.addAliasToOptions(options); - return (await this.queryBuilder(options, this.list).select( - addPrefixColumn('*', options?.alias), - )) as Promise; + return await this.createQueryRunner( + this.queryBuilder().select(addPrefixColumn('*', options?.alias)), + options, + [], + this.list, + ); } public async create( - createPayload: Omit>, + createPayload: Omit< + TRecord, + TakeField + >, options?: DatabaseOptions, ): Promise { - const [record] = await this.queryBuilder( + const [record] = (await this.createQueryRunner( + this.queryBuilder().insert(createPayload as any, '*'), options, + [createPayload], this.create, - ).insert(createPayload as any, '*'); - return record as TRecord; + )) as TRecord[]; + return record; } public async retrieve( - id: Options['idType'], + id: RepositoryOptions['idType'], options?: SelectDatabaseOptions, ): Promise { options = this.addAliasToOptions(options); - const record = await this.queryBuilder(options, this.retrieve) - .select(addPrefixColumn('*', options?.alias)) - .where(addPrefixColumn('id', options?.alias), id) - .first(); + const record = await this.createQueryRunner( + this.queryBuilder() + .select(addPrefixColumn('*', options?.alias)) + .where(addPrefixColumn('id', options?.alias), id) + .first(), + options, + [id], + this.retrieve, + ); if (!record) { return null; } @@ -89,15 +102,20 @@ export class Repository< } public async update( - id: Options['idType'], + id: RepositoryOptions['idType'], updatePayload: Partial< - Omit> + Omit> >, options?: DatabaseOptions, ): Promise { - const [record] = await this.queryBuilder(options, this.update) - .where('id', id) - .update(updatePayload as any, '*'); + const [record] = (await this.createQueryRunner( + this.queryBuilder() + .where('id', id) + .update(updatePayload as any, '*'), + options, + [id, updatePayload], + this.update, + )) as TRecord[]; if (!record) { return null; } @@ -105,30 +123,57 @@ export class Repository< } public async delete( - id: Options['idType'], + id: RepositoryOptions['idType'], options?: DatabaseOptions, ): Promise { - const [record] = await this.queryBuilder(options, this.delete) - .where('id', id) - .del('*'); + const [record] = (await this.createQueryRunner( + this.queryBuilder().where('id', id).del('*'), + options, + [id], + this.delete, + )) as TRecord[]; if (!record) { return null; } return record as TRecord; } - public queryBuilder< - TResult, - Options extends DatabaseOptions = DatabaseOptions< - TRecord, - TResult - >, - >( - options?: Options, + public async createQueryRunner( + queryBuilder: Knex.QueryBuilder, + options?: DatabaseOptions, + args: any[] = [], handler?: Function, + ): Promise { + if (options?.transaction) { + queryBuilder.transacting(options.transaction); + } + const context = new KnexionExecutionContextHost< + TRecord, + TResult, + RepositoryOptions['idType'] + >( + [ + queryBuilder, + this.rawBuilder(options?.transaction), + () => this.queryBuilder(options?.transaction), + options, + ...args, + ], + this.constructor as Type, + handler, + ); + context.setMethod(handler.name); + return this.augmentQueryBuilder(queryBuilder, context) as Promise; + } + + public queryBuilder( + trx?: Knex.Transaction, ): Knex.QueryBuilder { - const queryBuilder = this.pureQueryBuilder(options?.transaction); - return this.augmentQueryBuilder(queryBuilder, options, handler); + const queryBuilder = this.knex(this.options.name); + if (trx) { + queryBuilder.transacting(trx); + } + return queryBuilder; } public rawBuilder( @@ -152,16 +197,6 @@ export class Repository< }; } - public pureQueryBuilder( - trx?: Knex.Transaction, - ): Knex.QueryBuilder { - const queryBuilder = this.knex(this.options.name); - if (trx) { - queryBuilder.transacting(trx); - } - return queryBuilder; - } - public addAliasToOptions( options?: SelectDatabaseOptions, ): SelectDatabaseOptions { @@ -175,10 +210,6 @@ export class Repository< } public async onModuleInit(): Promise { - await this.resolveInterceptors(); - } - - private async resolveInterceptors(): Promise { const interceptors = this.reflector.get< | KnexionInterceptors @@ -186,38 +217,26 @@ export class Repository< >(KNEXION_INTERCEPTORS, this.constructor) ?? []; this.interceptors = await Promise.all( - interceptors.map((inteceptor) => { - if (isFunction(inteceptor)) { - return this.createInterceptor(inteceptor); + interceptors.map((interceptor) => { + if (isFunction(interceptor)) { + return this.moduleRef.create(interceptor); } - return inteceptor; + return interceptor; }), ); } - private async createInterceptor( - interceptorType: Type>, - ): Promise> { - return this.moduleRef.create(interceptorType); - } - - private augmentQueryBuilder< - TResult, - Options extends DatabaseOptions, - >( + private augmentQueryBuilder( queryBuilder: Knex.QueryBuilder, - options?: Options, - handler?: Function, + context: KnexionExecutionContextHost< + TRecord, + TResult, + RepositoryOptions['idType'] + >, ): Knex.QueryBuilder { - const context = new KnexionContext( - (trx?: Knex.Transaction) => this.pureQueryBuilder(trx), - queryBuilder, - this.rawBuilder(options?.transaction), - this.constructor, - handler, - this.resolveOptions(options, handler), - ); - + const options: DatabaseOptions | undefined = context + .switchToKnex() + .getOptions(); // eslint-disable-next-line @typescript-eslint/no-this-alias const repositoryRef = this; const originalThen = queryBuilder.then; @@ -227,6 +246,10 @@ export class Repository< const [originalOnResolve, originalOnReject] = arguments; try { const result = await repositoryRef.interceptorsConsumer.intercept( + repositoryRef.resolveInterceptors( + options?.intercept, + context.getHandler(), + ), context, () => new Promise((resolve, reject) => { @@ -244,21 +267,15 @@ export class Repository< return queryBuilder; } - private resolveOptions< - TResult, - Options extends DatabaseOptions, - >(options?: Options, handler?: Function): Options { - if (!options) { - options = { - intercept: [] as KnexionInterceptors, - } as Options; - } - - if (!options.intercept) { - options.intercept = []; - } + private resolveInterceptors( + interceptors: KnexionInterceptors = [], + handler?: Function, + ): KnexionInterceptors { + const resolvedInterceptors: KnexionInterceptors = [ + ...interceptors, + ]; - options.intercept?.unshift(...this.interceptors); + resolvedInterceptors.unshift(...this.interceptors); if (handler) { const methodInterceptors = @@ -279,11 +296,11 @@ export class Repository< 'Passing type of interceptor to bootstrap on nestjs side currently is not supported. Please creare instance on interceptor and pass to interceptors', ); } - options.intercept?.push( + resolvedInterceptors.push( ...(methodInterceptors as KnexionInterceptors), ); } - return options; + return resolvedInterceptors; } } diff --git a/packages/core/tests/integration/interceptors.spec.ts b/packages/core/tests/integration/interceptors.spec.ts index 18d737f7..076c60ec 100644 --- a/packages/core/tests/integration/interceptors.spec.ts +++ b/packages/core/tests/integration/interceptors.spec.ts @@ -1,6 +1,6 @@ import { KnexionRepository, - KnexionContext, + KnexionExecutionContext, Repository, KnexionInterceptor, KnexionCallHandler, @@ -55,10 +55,12 @@ describe('Interceptors', () => { test('should intercept query with additional select', async () => { class TestInterceptor implements KnexionInterceptor { public intercept( - context: KnexionContext, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable { - context.queryBuilder.select(context.rawBuilder('1 as intercepted')); + const queryBuilder = context.switchToKnex().getQueryBuilder(); + const rawBuilder = context.switchToKnex().getRawBuilder(); + queryBuilder.select(rawBuilder('1 as intercepted')); return next.handle(); } } @@ -72,7 +74,7 @@ describe('Interceptors', () => { test('should intercept result with additional field', async () => { class TestInterceptor implements KnexionInterceptor { public intercept( - context: KnexionContext, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable { return next @@ -94,7 +96,7 @@ describe('Interceptors', () => { test('interceptor should have access to class and handler refs', async () => { class TestInterceptor implements KnexionInterceptor { public intercept( - context: KnexionContext, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable { expect(context.getClass()).toBe(TestRepository); @@ -110,7 +112,7 @@ describe('Interceptors', () => { describe('reflect interceptors', () => { class TestInterceptor implements KnexionInterceptor { public intercept( - context: KnexionContext, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable { return next.handle().pipe( diff --git a/packages/filter/lib/interceptors/filter-by-array.interceptor.ts b/packages/filter/lib/interceptors/filter-by-array.interceptor.ts index e4bcb300..f8e297ad 100644 --- a/packages/filter/lib/interceptors/filter-by-array.interceptor.ts +++ b/packages/filter/lib/interceptors/filter-by-array.interceptor.ts @@ -1,7 +1,7 @@ import { Observable } from 'rxjs'; import { addPrefixColumn, - KnexionContext, + KnexionExecutionContext, KnexionInterceptor, KnexionCallHandler, SelectDatabaseOptions, @@ -21,21 +21,21 @@ export class FilterByArrayInterceptor ) {} public intercept( - context: KnexionContext< - TRecord, - TResult, - SelectDatabaseOptions - >, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable { const { useAlias = true } = this.options; + const queryBuilder = context.switchToKnex().getQueryBuilder(); + const options = context + .switchToKnex() + .getOptions>(); const column = useAlias - ? addPrefixColumn(this.name as string, context.options.alias) + ? addPrefixColumn(this.name as string, options.alias) : (this.name as string); if (this.operator === 'in') { - context.queryBuilder.whereIn(column, this.value); + queryBuilder.whereIn(column, this.value); } else { - context.queryBuilder.where(column, this.operator, this.value); + queryBuilder.where(column, this.operator, this.value); } return next.handle(); } diff --git a/packages/filter/lib/interceptors/filter-by-number-range.interceptor.ts b/packages/filter/lib/interceptors/filter-by-number-range.interceptor.ts index 69f00da4..be4bc2f9 100644 --- a/packages/filter/lib/interceptors/filter-by-number-range.interceptor.ts +++ b/packages/filter/lib/interceptors/filter-by-number-range.interceptor.ts @@ -1,7 +1,7 @@ import { Observable } from 'rxjs'; import { addPrefixColumn, - KnexionContext, + KnexionExecutionContext, KnexionInterceptor, KnexionCallHandler, SelectDatabaseOptions, @@ -18,18 +18,18 @@ export class FilterByNumberRangeInterceptor private readonly options: FilterOptions = {}, ) {} public intercept( - context: KnexionContext< - TRecord, - TResult, - SelectDatabaseOptions - >, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable { const { useAlias = true } = this.options; + const options = context + .switchToKnex() + .getOptions>(); + const queryBuilder = context.switchToKnex().getQueryBuilder(); const column = useAlias - ? addPrefixColumn(this.name as string, context.options.alias) + ? addPrefixColumn(this.name as string, options.alias) : (this.name as string); - buildRangeNumberFilter(context.queryBuilder, column, this.value); + buildRangeNumberFilter(queryBuilder, column, this.value); return next.handle(); } } diff --git a/packages/filter/lib/interceptors/filter-timestamp.interceptor.ts b/packages/filter/lib/interceptors/filter-timestamp.interceptor.ts index 39825bfd..5b845923 100644 --- a/packages/filter/lib/interceptors/filter-timestamp.interceptor.ts +++ b/packages/filter/lib/interceptors/filter-timestamp.interceptor.ts @@ -2,7 +2,7 @@ import { Observable } from 'rxjs'; import { Knex } from 'knex'; import { addPrefixColumn, - KnexionContext, + KnexionExecutionContext, KnexionInterceptor, KnexionCallHandler, SelectDatabaseOptions, @@ -15,28 +15,25 @@ export class FilterTimestampInterceptor constructor(private readonly timestampField: keyof TRecord) {} public intercept( - context: KnexionContext< - TRecord, - TResult, - SelectDatabaseOptions - >, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable { - if (!context.options[this.timestampField as string]) { + const options = context + .switchToKnex() + .getOptions>(); + const queryBuilder = context.switchToKnex().getQueryBuilder(); + + if (!options[this.timestampField as string]) { return next.handle(); } - const timestampFilter = context.options[this.timestampField as string]; + const timestampFilter = options[this.timestampField as string]; if (this.isNumber(timestampFilter)) { - context.queryBuilder.where( - addPrefixColumn('created_at', context.options.alias), + queryBuilder.where( + addPrefixColumn('created_at', options.alias), timestampFilter, ); } else { - this.buildComplexDateFilterQuery( - context.queryBuilder, - timestampFilter, - context.options, - ); + this.buildComplexDateFilterQuery(queryBuilder, timestampFilter, options); } return next.handle(); } diff --git a/packages/filter/lib/interceptors/filter.interceptor.ts b/packages/filter/lib/interceptors/filter.interceptor.ts index 42bc2251..43871671 100644 --- a/packages/filter/lib/interceptors/filter.interceptor.ts +++ b/packages/filter/lib/interceptors/filter.interceptor.ts @@ -1,7 +1,7 @@ import { Observable } from 'rxjs'; import { addPrefixColumn, - KnexionContext, + KnexionExecutionContext, KnexionInterceptor, KnexionCallHandler, SelectDatabaseOptions, @@ -15,19 +15,19 @@ export class FilterInterceptor constructor(private readonly options: FilterObjectOptions = {}) {} public intercept( - context: KnexionContext< - TRecord, - TResult, - SelectDatabaseOptions - >, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable { - const filterObject = context.options[this.options.optionKey ?? 'filter']; + const queryBuilder = context.switchToKnex().getQueryBuilder(); + const options = context + .switchToKnex() + .getOptions>(); + const filterObject = options[this.options.optionKey ?? 'filter']; if (isPlainObject(filterObject)) { - context.queryBuilder.where( + queryBuilder.where( this.appendAliasToFilterColumns( filterObject as FilterObject, - context.options.alias, + options.alias, ), ); } diff --git a/packages/page-pagination/lib/interceptors/page-pagination.interceptor.ts b/packages/page-pagination/lib/interceptors/page-pagination.interceptor.ts index 1da26502..bcb7c4b6 100644 --- a/packages/page-pagination/lib/interceptors/page-pagination.interceptor.ts +++ b/packages/page-pagination/lib/interceptors/page-pagination.interceptor.ts @@ -1,5 +1,5 @@ import { - KnexionContext, + KnexionExecutionContext, KnexionInterceptor, KnexionCallHandler, } from '@knexion/core'; @@ -15,17 +15,17 @@ export class PagePaginationInterceptor constructor(private readonly options?: PagePaginationInterceptorOptions) {} public intercept( - context: KnexionContext< - TRecord, - TResult, - ListPaginationSelectDatabaseOptions - >, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable { const { page = 0, limit = this.options?.defaultLimit ?? 20 } = - context.options; + context + .switchToKnex() + .getOptions>() ?? + {}; + const queryBuilder = context.switchToKnex().getQueryBuilder(); const offset = page * limit; - context.queryBuilder.limit(limit).offset(offset); + queryBuilder.limit(limit).offset(offset); if (this.options?.transform ?? true) { return next.handle().pipe( diff --git a/packages/page-pagination/tests/paga-pagination-interceptor.spec.ts b/packages/page-pagination/tests/paga-pagination-interceptor.spec.ts index 521be79a..9c49496a 100644 --- a/packages/page-pagination/tests/paga-pagination-interceptor.spec.ts +++ b/packages/page-pagination/tests/paga-pagination-interceptor.spec.ts @@ -54,7 +54,7 @@ describe('PagePaginationInterceptor', () => { test('should apply offset 0 and limit 20', async () => { await generateTestRecords(21); - const result = await testRepository.list>({ + const result = await testRepository.list>({ intercept: [new PagePaginationInterceptor()], }); expect(result.data.length).toBe(20); @@ -62,7 +62,7 @@ describe('PagePaginationInterceptor', () => { test('should use custom default limit 2', async () => { await generateTestRecords(3); - const result = await testRepository.list>({ + const result = await testRepository.list>({ intercept: [new PagePaginationInterceptor({ defaultLimit: 2 })], }); expect(result.data.length).toBe(2); @@ -70,7 +70,7 @@ describe('PagePaginationInterceptor', () => { test('should use custom limit from options', async () => { await generateTestRecords(3); - const result = await testRepository.list>({ + const result = await testRepository.list>({ intercept: [new PagePaginationInterceptor()], limit: 2, }); @@ -79,7 +79,7 @@ describe('PagePaginationInterceptor', () => { test('should apply offset', async () => { await generateTestRecords(1); - const result = await testRepository.list>({ + const result = await testRepository.list>({ intercept: [new PagePaginationInterceptor()], limit: 1, page: 1, diff --git a/packages/soft-delete/lib/interceptors/filter-soft-deleted.interceptor.ts b/packages/soft-delete/lib/interceptors/filter-soft-deleted.interceptor.ts index bbe4776f..72b94e65 100644 --- a/packages/soft-delete/lib/interceptors/filter-soft-deleted.interceptor.ts +++ b/packages/soft-delete/lib/interceptors/filter-soft-deleted.interceptor.ts @@ -1,7 +1,7 @@ import { Observable } from 'rxjs'; import { addPrefixColumn, - KnexionContext, + KnexionExecutionContext, KnexionInterceptor, KnexionCallHandler, SelectDatabaseOptions, @@ -14,17 +14,18 @@ export class FilterSoftDeletedInterceptor constructor(private readonly options: SoftDeleteOptions = {}) {} public intercept( - context: KnexionContext< - TRecord, - TResult, - SelectDatabaseOptions - >, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable { - context.queryBuilder.whereNull( + const queryBuilder = context.switchToKnex().getQueryBuilder(); + const options = + context + .switchToKnex() + .getOptions>() ?? {}; + queryBuilder.whereNull( addPrefixColumn( (this.options.field as string) ?? 'deleted_at', - context.options.alias, + options.alias, ), ); return next.handle(); diff --git a/packages/sort/lib/interceptors/sort.interceptor.ts b/packages/sort/lib/interceptors/sort.interceptor.ts index 4feb3d44..c6671d0f 100644 --- a/packages/sort/lib/interceptors/sort.interceptor.ts +++ b/packages/sort/lib/interceptors/sort.interceptor.ts @@ -1,6 +1,6 @@ import { Observable } from 'rxjs'; import { - KnexionContext, + KnexionExecutionContext, KnexionCallHandler, KnexionInterceptor, addPrefixColumn, @@ -15,21 +15,22 @@ export class SortInterceptor constructor(private readonly options: SortOptions = {}) {} public intercept( - context: KnexionContext< - TRecord, - TResult, - SelectDatabaseOptions - >, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable { + const queryBuilder = context.switchToKnex().getQueryBuilder(); + const options = context + .switchToKnex() + .getOptions>(); + const sortOptionKey = this.options.optionKey ?? 'sort'; - const sort = context.options[sortOptionKey]; + const sort = options[sortOptionKey]; if (sort && Array.isArray(sort)) { sort.forEach((property) => { const [direction, column] = getSortDirection(property); - context.queryBuilder.orderBy( - addPrefixColumn(column, context.options.alias), + queryBuilder.orderBy( + addPrefixColumn(column, options.alias), direction, direction === 'desc' ? 'last' : 'first', ); diff --git a/packages/transform/lib/interceptors/field-transform.interceptor.ts b/packages/transform/lib/interceptors/field-transform.interceptor.ts index 2e891725..a25787f5 100644 --- a/packages/transform/lib/interceptors/field-transform.interceptor.ts +++ b/packages/transform/lib/interceptors/field-transform.interceptor.ts @@ -3,7 +3,7 @@ import { map } from 'rxjs/operators'; import { Reflector } from '@nestjs/core'; import { Injectable } from '@nestjs/common'; import { - KnexionContext, + KnexionExecutionContext, KnexionCallHandler, KnexionInterceptor, } from '@knexion/core'; @@ -19,7 +19,7 @@ export class FieldTransformInterceptor constructor(private readonly reflector: Reflector) {} public intercept( - context: KnexionContext, + context: KnexionExecutionContext, next: KnexionCallHandler, ): Observable { const { schema, transformer } = this.retrieveTransformSchema(context); @@ -66,7 +66,7 @@ export class FieldTransformInterceptor } private retrieveTransformSchema( - context: KnexionContext, + context: KnexionExecutionContext, ): FieldTransformOptions { const handler = context.getHandler(); if (handler) {