0.1.4 • Published 1 year ago

typeorm-sm v0.1.4

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

TypeORM SM

An elegant and efficient solution for integrating state machines with your TypeORM entities. This package provides an easy and robust approach to managing complex states in your applications, ensuring consistent state transitions and well-defined business rules. Perfect for projects that require advanced state control in a TypeORM environment.

Installation

  • Install using NPM:

    npm install typeorm typeorm-sm --save
  • Install using Yarn:

    yarn add typeorm typeorm-sm
  • Install using Pnpm:

    pnpm add typeorm typeorm-sm

Usage

In your DataSource settings, add StateMachineSubscriber to subscribers:

import { DataSource } from 'typeorm'
import { StateMachineSubscriber } from 'typeorm-sm'

export default new DataSource({
 subscribers: [StateMachineSubscriber],
 // ...
})

Now to have the state machine in your entity, simply extend your class with StateMachine:

The StateMachine abstract class by default is ActiveRecord

import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
import { StateMachine } from 'typeorm-sm'

enum UserState {
  VERIFYING_EMAIL = 'VERIFYING_EMAIL',
  VERIFIED_EMAIL = 'VERIFIED_EMAIL',
  RETRY = 'RETRY',
}

@Entity()
export class User extends StateMachine<UserState> {
  @PrimaryGeneratedColumn()
  id: number

  @Column({ unique: true })
  email: string

 static readonly initialState = UserState.PENDING
  static readonly transitions = Object.freeze<StateTransitions<UserState>>({
    [UserState.PENDING]: { from: [], to: UserState.VERIFYING_EMAIL }, // Initial state
    [UserState.VERIFYING_EMAIL]: { from: [UserState.VERIFIED_EMAIL, UserState.RETRY] },
    [UserState.VERIFIED_EMAIL]: { from: [UserState.VERIFYING_EMAIL] }, // Final state
    [UserState.RETRY]: { from: [UserState.VERIFYING_EMAIL] },
  })
}

Note that in the example above we did not define any columns to store the state. This is not necessary because the StateMachine model already internally adds this column for you.

By default the state column is indexed, not null of type varchar with a length of 255, but you can change these settings by rewriting this column, example:

@Entity()
export class User extends StateMachine<UserState> {
 // ...

 @Index()
 @Column({ type: 'integer' })
 state: UserState

 // ...
}

Validation

If you use the class-validator library to validate the value of some columns, you may only want to validate if the column has a value or if the record is in a certain state. With that in mind, we have two decorators to help us:

  • ValidateIfStateIs

    // ...
    import { ValidateIfStateIs} from 'typeorm-sm'
    
    @Entity()
    export class User extends StateMachine<UserState> {
     // ...
    
     @ValidateIfStateIs([UserState.VERIFIED])
     name?: string
    
     // ...
    }

    The column will only be validated if it has a value or if the current state of the record is the same as that entered

  • ValidateIfStateIsNot

    // ...
    import { ValidateIfStateIsNot } from 'typeorm-sm'
    
    @Entity()
    export class User extends StateMachine<UserState> {
     // ...
    
     @ValidateIfStateIsNot([UserState.PENDING])
     name?: string
    
     // ...
    }

    The column will only be validated if it has a value or if the current state of the record is different from that informed