1.0.1 • Published 9 months ago
nestjs-state-machine v1.0.1
StateMachineModule
A simple module to manage a state-machine in a more nestjsish way
Requirements
Installation
$ npm i --save bruno-de-queiroz/nestjs-state-machine
Usage
Considering this example:
class TestEntity {
status:
| 'pending'
| 'processed'
| 'synchronized'
| 'fulfilled'
| 'failed'
| 'cancelled';
}
Initialization
StateMachineModule.forFeature({
entity: TestEntity,
field: 'status',
graph: {
root: {
state: 'pending',
next: [
{
state: 'processed',
next: [
{
state: 'synchronized',
next: [
'fulfilled',
{
state: 'failed',
next: 'fulfilled',
},
'cancelled',
],
},
'cancelled',
],
},
'cancelled',
],
},
manual: ['cancelled', 'failed'],
strict: true,
},
guards: [
TestTransitionWithInjected,
],
})
Transition guard declaration
@TransitionGuard<TestEntity, 'status'>('processed', 'synchronized')
class TestTransitionWithInjected implements CanTransition<TestEntity> {
constructor(private readonly service: Service) {}
canTransition(input: TestEntity): Observable<TestEntity> {
return of(input)
.pipe(filter(() => this.service.validate()))
.pipe(map(() => input));
}
}
Service injection
import { Injectable } from '@nestjs/common';
@Injectable()
class MyService {
constructor(private readonly stateMachine: StateMachineService<TestEntity, 'status'>) {}
}
Service methods
transition(input: T, to: T[U]): Observable<T>
This method will make all the transitions till the targeted state calling the registered guards for each transition, and returns the entity as Observable<T>
.
this.stateMachie.transition(new TestEntity(), 'fulfilled')
.subscribe(data => expect(data.status).toBe('fulfilled'));
next(input: T): Observable<T>
This method will call the next transition that is not manual and returns the entity as Observable<T>
. The order in the next
field of the graph declaration is preserved.
StateMachineModule.forFeature({
entity: TestEntity,
field: 'status',
graph: {
root: {
state: 'pending',
next: [
{ state: 'processed', next: 'fulfilled' },
'synchronized',
'cancelled',
],
},
manual: ['cancelled'],
strict: true,
},
guards: [
TestTransitionWithInjected,
],
})
// ...
this.stateMachie.next(new TestEntity({ status: 'pending' }))
.subscribe(data => expect(data.status).toBe('processed'));
// ...
this.stateMachie.next(new TestEntity({ status: 'processed' }))
.subscribe(data => expect(data.status).toBe('fulfilled'));
Test
# tests
$ npm run test
# test coverage
$ npm run test:cov
Commit convention
See https://www.conventionalcommits.org/en/v1.0.0/#summary