diff --git a/packages/repository-tests/src/transaction/transactions.suite.ts b/packages/repository-tests/src/transaction/transactions.suite.ts index d55239f42e8e..f2476af5e21a 100644 --- a/packages/repository-tests/src/transaction/transactions.suite.ts +++ b/packages/repository-tests/src/transaction/transactions.suite.ts @@ -3,24 +3,23 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {Entity, model, property} from '@loopback/repository'; +import {Entity, juggler, model, property} from '@loopback/repository'; +import {TransactionRepository} from '@loopback/repository/src'; import {expect, skipIf, toJSON} from '@loopback/testlab'; -import {Transaction} from 'loopback-datasource-juggler'; import {Suite} from 'mocha'; -import {EntityCrudRepository} from '../../../repository/src'; import {withCrudCtx} from '../helpers.repository-tests'; import { CrudConnectorFeatures, - CrudRepositoryCtor, CrudTestContext, DataSourceOptions, + TransactionRepositoryCtor, } from '../types.repository-tests'; // Core scenarios around creating new model instances and retrieving them back // Please keep this file short, put any advanced scenarios to other files export function transactionSuite( dataSourceOptions: DataSourceOptions, - repositoryClass: CrudRepositoryCtor, + repositoryClass: TransactionRepositoryCtor, connectorFeatures: CrudConnectorFeatures, ) { skipIf<[(this: Suite) => void], void>( @@ -47,7 +46,7 @@ export function transactionSuite( } describe('create-retrieve within transaction', () => { - let repo: EntityCrudRepository; + let repo: TransactionRepository; before( withCrudCtx(async function setupRepository(ctx: CrudTestContext) { repo = new repositoryClass(Product, ctx.dataSource); @@ -56,7 +55,7 @@ export function transactionSuite( ); it('retrieves a newly created model in a transaction', async () => { - const tx: Transaction = await repo.beginTransaction!({ + const tx: juggler.Transaction = await repo.beginTransaction({ isolationLevel: 'READ COMMITTED', }); const created = await repo.create( diff --git a/packages/repository-tests/src/types.repository-tests.ts b/packages/repository-tests/src/types.repository-tests.ts index 28282d4c289f..b755a0675e2e 100644 --- a/packages/repository-tests/src/types.repository-tests.ts +++ b/packages/repository-tests/src/types.repository-tests.ts @@ -9,6 +9,7 @@ import { juggler, Options, } from '@loopback/repository'; +import {TransactionRepository} from '@loopback/repository/src'; /** * DataSource configuration (connector name, connection string, etc.). @@ -62,6 +63,19 @@ export type CrudRepositoryCtor = new < dataSource: juggler.DataSource, ) => EntityCrudRepository; +/** + * A constructor of a class implementing CrudRepository interface, + * accepting the Entity class (constructor) and a dataSource instance. + */ +export type TransactionRepositoryCtor = new < + T extends Entity, + ID, + Relations extends object +>( + entityClass: typeof Entity & {prototype: T}, + dataSource: juggler.DataSource, +) => TransactionRepository; + /** * Additional properties added to Mocha TestContext/SuiteContext. * @internal diff --git a/packages/repository/src/repositories/legacy-juggler-bridge.ts b/packages/repository/src/repositories/legacy-juggler-bridge.ts index 1c08879b2eb6..ae0dc61f31bb 100644 --- a/packages/repository/src/repositories/legacy-juggler-bridge.ts +++ b/packages/repository/src/repositories/legacy-juggler-bridge.ts @@ -6,7 +6,6 @@ import {Getter} from '@loopback/context'; import * as assert from 'assert'; import * as legacy from 'loopback-datasource-juggler'; -import {IsolationLevel, Transaction} from 'loopback-datasource-juggler'; import { AnyObject, Command, @@ -31,7 +30,7 @@ import { HasOneRepositoryFactory, } from '../relations'; import {isTypeResolver, resolveType} from '../type-resolver'; -import {EntityCrudRepository} from './repository'; +import {EntityCrudRepository, TransactionRepository} from './repository'; export namespace juggler { /* eslint-disable @typescript-eslint/no-unused-vars */ @@ -41,6 +40,8 @@ export namespace juggler { export import PersistedModel = legacy.PersistedModel; export import KeyValueModel = legacy.KeyValueModel; export import PersistedModelClass = legacy.PersistedModelClass; + export import Transaction = legacy.Transaction; + export import IsolationLevel = legacy.IsolationLevel; } function isModelClass( @@ -90,7 +91,7 @@ export class DefaultCrudRepository< T extends Entity, ID, Relations extends object = {} -> implements EntityCrudRepository { +> implements TransactionRepository { modelClass: juggler.PersistedModelClass; /** @@ -438,8 +439,8 @@ export class DefaultCrudRepository< } async beginTransaction( - options: IsolationLevel | Options, - ): Promise { + options?: juggler.IsolationLevel | Options, + ): Promise { return await this.dataSource.beginTransaction(options); } diff --git a/packages/repository/src/repositories/repository.ts b/packages/repository/src/repositories/repository.ts index be59dd6653d4..3b3c1ff1d7f6 100644 --- a/packages/repository/src/repositories/repository.ts +++ b/packages/repository/src/repositories/repository.ts @@ -3,7 +3,6 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {IsolationLevel, Transaction} from 'loopback-datasource-juggler'; import { AnyObject, Command, @@ -18,6 +17,7 @@ import {DataSource} from '../datasource'; import {EntityNotFoundError} from '../errors'; import {Entity, Model, ValueObject} from '../model'; import {Filter, Where} from '../query'; +import {IsolationLevel, Transaction} from '../transaction'; /* eslint-disable @typescript-eslint/no-unused-vars */ @@ -38,6 +38,20 @@ export interface ExecutableRepository extends Repository { ): Promise; } +export interface TransactionRepository< + T extends Entity, + ID, + Relations extends object = {} +> extends EntityCrudRepository { + /** + * Begin a new Transaction + * @param options - Options for the operations + * @returns Promise if an entity exists for the id, otherwise + * Promise + */ + beginTransaction(options?: IsolationLevel | Options): Promise; +} + /** * Basic CRUD operations for ValueObject and Entity. No ID is required. */ @@ -194,16 +208,6 @@ export interface EntityCrudRepository< * Promise */ exists(id: ID, options?: Options): Promise; - - /** - * Begin a new Transaction - * @param options - Options for the operations - * @returns Promise if an entity exists for the id, otherwise - * Promise - */ - beginTransaction?( - options?: IsolationLevel | Options, - ): Promise; } /** diff --git a/packages/repository/src/transaction.ts b/packages/repository/src/transaction.ts new file mode 100644 index 000000000000..831bb960ff06 --- /dev/null +++ b/packages/repository/src/transaction.ts @@ -0,0 +1,32 @@ +// Copyright IBM Corp. 2019. All Rights Reserved. +// Node module: @loopback/repository +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +import {Callback} from './common-types'; + +/** + * Local transaction + */ +export interface Transaction { + /** + * Commit the transaction + * @param callback + */ + commit(callback?: Callback): void; + /** + * Rollback the transaction + * @param callback + */ + rollback(callback?: Callback): void; +} + +/** + * Isolation level + */ +export enum IsolationLevel { + READ_COMMITTED = 'READ COMMITTED', // default + READ_UNCOMMITTED = 'READ UNCOMMITTED', + SERIALIZABLE = 'SERIALIZABLE', + REPEATABLE_READ = 'REPEATABLE READ', +}