Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions types/lib/model.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import Op = require('./operators');
import { Promise } from './promise';
import { QueryOptions, IndexesOptions } from './query-interface';
import { Config, Options, Sequelize, SyncOptions } from './sequelize';
import { Transaction } from './transaction';
import { Transaction, LOCK } from './transaction';
import { Col, Fn, Literal, Where } from './utils';
import { IndexHints } from '..';

Expand Down Expand Up @@ -549,8 +549,10 @@ export interface FindOptions extends QueryOptions, Filterable, Projectable, Para
* Postgres also supports transaction.LOCK.KEY_SHARE, transaction.LOCK.NO_KEY_UPDATE and specific model
* locks with joins. See [transaction.LOCK for an example](transaction#lock)
*/
lock?: Transaction.LOCK | { level: Transaction.LOCK; of: typeof Model };

lock?:
| LOCK
| { level: LOCK; of: typeof Model }
| boolean;
/**
* Skip locked rows. Only supported in Postgres.
*/
Expand Down
109 changes: 64 additions & 45 deletions types/lib/transaction.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ export class Transaction {
* Adds hook that is run after a transaction is committed
*/
public afterCommit(fn: (transaction: this) => void | Promise<void>): void;

/**
* Returns possible options for row locking
*/
static get LOCK(): LOCK;

/**
* Same as its static version, but can also be called on instances of
* transactions to get possible options for row locking directly from the
* instance.
*/
get LOCK(): LOCK;
}

// tslint:disable-next-line no-namespace
Expand Down Expand Up @@ -71,54 +83,61 @@ export namespace Transaction {
IMMEDIATE = 'IMMEDIATE',
EXCLUSIVE = 'EXCLUSIVE',
}
}

/**
* Possible options for row locking. Used in conjunction with `find` calls:
*
* ```js
* t1 // is a transaction
* t1.LOCK.UPDATE,
* t1.LOCK.SHARE,
* t1.LOCK.KEY_SHARE, // Postgres 9.3+ only
* t1.LOCK.NO_KEY_UPDATE // Postgres 9.3+ only
* ```
*
* Usage:
* ```js
* t1 // is a transaction
* Model.findAll({
* where: ...,
* transaction: t1,
* lock: t1.LOCK...
* });
* ```
*
* Postgres also supports specific locks while eager loading by using OF:
* ```js
* UserModel.findAll({
* where: ...,
* include: [TaskModel, ...],
* transaction: t1,
* lock: {
* level: t1.LOCK...,
* of: UserModel
* }
* });
* ```
* UserModel will be locked but TaskModel won't!
*/
export enum LOCK {
UPDATE = 'UPDATE',
SHARE = 'SHARE',
/**
* Possible options for row locking. Used in conjunction with `find` calls:
*
* ```js
* t1 // is a transaction
* t1.LOCK.UPDATE,
* t1.LOCK.SHARE,
* t1.LOCK.KEY_SHARE, // Postgres 9.3+ only
* t1.LOCK.NO_KEY_UPDATE // Postgres 9.3+ only
* ```
*
* Usage:
* ```js
* t1 // is a transaction
* Model.findAll({
* where: ...,
* transaction: t1,
* lock: t1.LOCK...
* });
* ```
*
* Postgres also supports specific locks while eager loading by using OF:
* ```js
* UserModel.findAll({
* where: ...,
* include: [TaskModel, ...],
* transaction: t1,
* lock: {
* level: t1.LOCK...,
* of: UserModel
* }
* });
* ```
* UserModel will be locked but TaskModel won't!
* Postgres 9.3+ only
*/
enum LOCK {
UPDATE = 'UPDATE',
SHARE = 'SHARE',
/**
* Postgres 9.3+ only
*/
KEY_SHARE = 'KEY SHARE',
/**
* Postgres 9.3+ only
*/
NO_KEY_UPDATE = 'NO KEY UPDATE',
}
KEY_SHARE = 'KEY SHARE',
/**
* Postgres 9.3+ only
*/
NO_KEY_UPDATE = 'NO KEY UPDATE',
}

interface LOCK {
UPDATE: LOCK.UPDATE;
SHARE: LOCK.SHARE;
KEY_SHARE: LOCK.KEY_SHARE;
NO_KEY_UPDATE: LOCK.NO_KEY_UPDATE;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, I have a question, I don't use TypeScript much, why is both an enum and an interface necessary? Also why is only the enum being exported?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also why did you move the enum outside the export namespace Transaction block?

Copy link
Copy Markdown
Contributor Author

@aecorredor aecorredor Oct 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, if sequelize was entirely written in TS (and not only types were provided), we could just use an enum directly in the code and that would get compiled to an actual object (This is actually discouraged by some people and they advise to do this if you want to support runtime enums: https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680, look at point 3).

But, since here we are just providing types and not touching the code, we can't just do that. Here, an enum is just a TS type that basically says "look, whatever you say has this type, can only be one of these values". In our case, LOCK is an enum with 4 possible values which we're saying are strings. Now, we might first think that the static get LOCK and the get LOCK would return a type of LOCK (which is an enum). But that would not be correct by looking at the code. The actual code returns an object with four properties, UPDATE, SHARE, KEY_SHARE and NO_KEY_UPDATE. So, for our types to be correct, we need to tell static get LOCK and get LOCK that they return an object with those four properties, therefore the need for the interface LOCK, which models exactly that.

We only export the enum because we use it as one of the possible types for lock in the models file. The interface is just used in this file to indicate what the getter functions return.

I moved the enum outside the namespace because then it would conflict with the type declaration of the actual class getters also called LOCK.

}

/**
Expand Down
42 changes: 42 additions & 0 deletions types/test/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,48 @@ async function trans() {
});
}

async function trans2() {
return await sequelize.transaction(async transaction => {
transaction.afterCommit(() => console.log('transaction complete'));
User.findAll(
{
transaction,
lock: transaction.LOCK.UPDATE,
}
);
return 1;
});
}

async function trans3() {
return await sequelize.transaction(async transaction => {
transaction.afterCommit(() => console.log('transaction complete'));
User.findAll(
{
transaction,
lock: true,
}
);
return 1;
});
}

async function trans4() {
return await sequelize.transaction(async transaction => {
transaction.afterCommit(() => console.log('transaction complete'));
User.findAll(
{
transaction,
lock: {
level: transaction.LOCK.UPDATE,
of: User,
},
}
);
return 1;
});
}

async function transact() {
const t = await sequelize.transaction({
deferrable: Deferrable.SET_DEFERRED(['test']),
Expand Down