Skip to content

Commit

Permalink
feat: update fsmoothy version
Browse files Browse the repository at this point in the history
add transaction example
  • Loading branch information
bondiano committed Sep 28, 2023
1 parent b884558 commit af1919b
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 56 deletions.
12 changes: 0 additions & 12 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ module.exports = {
extends: [
'plugin:node/recommended',
'plugin:prettier/recommended',
'plugin:sonarjs/recommended',
'plugin:unicorn/recommended',
'plugin:import/recommended',
],
Expand Down Expand Up @@ -122,17 +121,6 @@ module.exports = {
argsIgnorePattern: '^_',
},
],
'@typescript-eslint/naming-convention': [
ERROR,
{
selector: 'interface',
format: ['PascalCase'],
custom: {
regex: '^I[A-Z]',
match: true,
},
},
],
'@typescript-eslint/ban-ts-comment': [
'error',
{
Expand Down
31 changes: 9 additions & 22 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
"eslint-plugin-lodash": "^7.4.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-sonarjs": "^0.21.0",
"eslint-plugin-unicorn": "^48.0.1",
"prettier": "^3.0.2",
"semantic-release": "^21.1.1",
Expand All @@ -72,6 +71,6 @@
}
},
"dependencies": {
"fsmoothy": "1.5.0"
"fsmoothy": "1.7.1"
}
}
5 changes: 5 additions & 0 deletions src/__tests__/examples/file-upload.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ class File extends StateMachineEntity({
transitions: [
t(FileState.pending, FileEvent.start, FileState.uploading),
t(FileState.uploading, FileEvent.finish, FileState.completed, {
async guard(this: File, _context, url: string) {
const hasTheSameUrl = (this.url !== url) as boolean;

return hasTheSameUrl;
},
async onEnter(this: File, _context, url: string | null) {
this.url = url;
},
Expand Down
175 changes: 175 additions & 0 deletions src/__tests__/examples/task-status.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import {
Entity,
PrimaryGeneratedColumn,
BaseEntity,
Column,
DataSource,
EntityManager,
OneToMany,
JoinColumn,
ManyToOne,
} from 'typeorm';
import { describe, it, expect, afterAll, afterEach, beforeAll } from 'vitest';

import { StateMachineEntity, state, t } from '../..';

const fakeDate = new Date('2020-01-01');

const enum TaskState {
Inactive = 'inactive',
Active = 'active',
Completed = 'completed',
}

const enum TaskEvent {
Activate = 'activate',
Complete = 'complete',
}

interface ITask {
id: number;
title: string;
tags: Array<ITag>;
completedAt?: Date;
}

interface ITag {
id: number;
name: string;
}

const activate = t(TaskState.Inactive, TaskEvent.Activate, TaskState.Active, {
async onEnter(this: ITask, _context, tx: EntityManager, tags: Array<ITag>) {
const _tags = await Promise.all(
tags.map(async (tag) => {
const newTag = tx.create(Tag, tag);
return await tx.save(Tag, newTag);
}),
);

this.tags = _tags;
},
async onExit(this: ITask, _context, tx: EntityManager) {
await tx.save(Task, this);
},
});

const complete = t(TaskState.Active, TaskEvent.Complete, TaskState.Completed, {
onEnter(this: ITask) {
this.completedAt = fakeDate;
},
async onExit(this: ITask, _context, tx: EntityManager) {
for (const tag of this.tags) {
tag.name = tag.name.toUpperCase() + '-completed';
await tx.save(Tag, tag);
}

await tx.save(Task, this);
},
});

@Entity()
class Task
extends StateMachineEntity({
status: state<TaskState, TaskEvent>({
initial: TaskState.Inactive,
saveAfterTransition: false,
transitions: [activate, complete],
}),
})
implements ITask
{
@PrimaryGeneratedColumn()
id: number;

@Column()
title: string;

@OneToMany(() => Tag, (tag) => tag.task, {
eager: true,
})
@JoinColumn({ name: 'tag_id' })
tags: Array<Tag>;

@Column({ nullable: true })
completedAt?: Date;
}

@Entity()
class Tag extends BaseEntity implements ITag {
@PrimaryGeneratedColumn()
id: number;

@Column()
name: string;

@ManyToOne(() => Task, (task) => task.id)
task: Task;
}

describe('Task Status', () => {
let dataSource: DataSource;

beforeAll(async () => {
dataSource = new DataSource({
name: (Date.now() * Math.random()).toString(16),
database: ':memory:',
dropSchema: true,
entities: [Tag, Task],
logging: ['error', 'warn'],
synchronize: true,
type: 'better-sqlite3',
});

await dataSource.initialize();
await dataSource.synchronize();
});

afterAll(async () => {
await dataSource.dropDatabase();
await dataSource.destroy();
});

afterEach(async () => {
await dataSource.manager.clear(Tag);
await dataSource.manager.clear(Task);
});

it('should be able to pass user flow', async () => {
const task = new Task();
task.title = 'My Task';
await task.save();

await dataSource.manager.transaction(async (tx) => {
await task.fsm.status.activate(tx, [
{
name: 'Tag One',
},
{
name: 'Tag Two',
},
]);
});

expect(task.status).toBe(TaskState.Active);

await dataSource.manager.transaction(async (tx) => {
await task.fsm.status.complete(tx);
});

const taskFromDatabase = await dataSource.manager.findOneByOrFail(Task, {
id: task.id,
});
expect(taskFromDatabase.status).toBe(TaskState.Completed);
expect(taskFromDatabase.tags).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'TAG ONE-completed',
}),
expect.objectContaining({
name: 'TAG TWO-completed',
}),
]),
);
});
});
22 changes: 8 additions & 14 deletions src/fsm.entity.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
IStateMachineParameters,
StateMachineParameters,
StateMachine,
IStateMachine,
ITransition,
Transition,
} from 'fsmoothy';
import { StateMachineError } from 'fsmoothy/fsm.error';
import { AllowedNames } from 'fsmoothy/types';
Expand All @@ -13,12 +13,12 @@ export interface IStateMachineEntityColumnParameters<
Event extends AllowedNames,
Context extends object,
> extends Omit<
IStateMachineParameters<
StateMachineParameters<
State,
Event,
Context,
ITransition<State, Event, any>,
[ITransition<State, Event, any>, ...Array<ITransition<State, Event, any>>]
Transition<State, Event, any>,
[Transition<State, Event, any>, ...Array<Transition<State, Event, any>>]
>,
'states'
> {
Expand Down Expand Up @@ -104,14 +104,8 @@ function initializeStateMachine<
saveAfterTransition = true,
transitions,
ctx,
// @ts-expect-error we don't allow state
states,
} = parameters;

if (states) {
throw new StateMachineError('Nested states are not allowed');
}

if (!Array.isArray(transitions) || transitions.length === 0) {
throw new StateMachineError('Transitions are not defined');
}
Expand All @@ -120,11 +114,11 @@ function initializeStateMachine<
parameters.transitions = transitions.map((transition) => {
return {
...transition,
async onExit(context, ...arguments_) {
await transition.onExit?.(context, ...arguments_);

async onExit(context: object, ...arguments_: Array<unknown>) {
entity[column] = transition.to as State;

await transition.onExit?.call?.(this, context, ...arguments_);

if (persistContext) {
entity[buildContextColumnName(column)] = JSON.stringify(context);
}
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export {
StateMachine,
t,
IStateMachine,
ITransition,
Transition,
isStateMachineError,
} from 'fsmoothy';
export { StateMachineEntity } from './fsm.entity';
Expand Down
7 changes: 2 additions & 5 deletions src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import { IStateMachineEntityColumnParameters } from './fsm.entity';
export const state = <
const State extends AllowedNames,
const Event extends AllowedNames,
const Context extends object,
const Context extends object = object,
>(
parameters: Omit<
IStateMachineEntityColumnParameters<State, Event, Context>,
'states'
>,
parameters: IStateMachineEntityColumnParameters<State, Event, Context>,
) => parameters;

0 comments on commit af1919b

Please sign in to comment.