mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-10 17:53:36 +02:00
approval flow poc
This commit is contained in:
parent
95779754fb
commit
a6959e39d0
101
src/lib/types/models/state-machine.ts
Normal file
101
src/lib/types/models/state-machine.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* StateMachine.ts
|
||||||
|
* TypeScript finite state machine class with async transformations using promises.
|
||||||
|
* https://github.com/eram/ts-fsm/blob/master/src/stateMachine.ts
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface ITransition<STATE, EVENT> {
|
||||||
|
fromState: STATE;
|
||||||
|
event: EVENT;
|
||||||
|
toState: STATE;
|
||||||
|
cb?: (...args: unknown[]) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transition<STATE, EVENT>(
|
||||||
|
fromState: STATE,
|
||||||
|
event: EVENT,
|
||||||
|
toState: STATE,
|
||||||
|
cb?: (...args: unknown[]) => Promise<void>,
|
||||||
|
): ITransition<STATE, EVENT> {
|
||||||
|
return { fromState, event, toState, cb };
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StateMachine<STATE, EVENT> {
|
||||||
|
protected current: STATE;
|
||||||
|
|
||||||
|
// initalize the state-machine
|
||||||
|
constructor(
|
||||||
|
initState: STATE,
|
||||||
|
protected transitions: ITransition<STATE, EVENT>[] = [],
|
||||||
|
) {
|
||||||
|
this.current = initState;
|
||||||
|
}
|
||||||
|
|
||||||
|
addTransitions(transitions: ITransition<STATE, EVENT>[]): void {
|
||||||
|
transitions.forEach((tran) => this.transitions.push(tran));
|
||||||
|
}
|
||||||
|
|
||||||
|
getState(): STATE {
|
||||||
|
return this.current;
|
||||||
|
}
|
||||||
|
|
||||||
|
can(event: EVENT): boolean {
|
||||||
|
return this.transitions.some(
|
||||||
|
(trans) =>
|
||||||
|
trans.fromState === this.current && trans.event === event,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
isFinal(): boolean {
|
||||||
|
// search for a transition that starts from current state.
|
||||||
|
// if none is found it's a terminal state.
|
||||||
|
return this.transitions.every(
|
||||||
|
(trans) => trans.fromState !== this.current,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// post event asynch
|
||||||
|
async dispatch(event: EVENT, ...args: unknown[]): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
// delay execution to make it async
|
||||||
|
setTimeout(
|
||||||
|
(me: this) => {
|
||||||
|
// find transition
|
||||||
|
const found = this.transitions.some((tran) => {
|
||||||
|
if (
|
||||||
|
tran.fromState === me.current &&
|
||||||
|
tran.event === event
|
||||||
|
) {
|
||||||
|
me.current = tran.toState;
|
||||||
|
if (tran.cb) {
|
||||||
|
try {
|
||||||
|
tran.cb(args).then(resolve).catch(reject);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
'Exception caught in callback',
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// no such transition
|
||||||
|
if (!found) {
|
||||||
|
console.error(
|
||||||
|
`no transition: from ${me.current.toString()} event ${event.toString()}`,
|
||||||
|
);
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
84
src/lib/types/models/suggest-changes-approval.ts
Normal file
84
src/lib/types/models/suggest-changes-approval.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { StateMachine, transition } from './state-machine';
|
||||||
|
|
||||||
|
export enum Events {
|
||||||
|
SUBMIT,
|
||||||
|
REJECT,
|
||||||
|
APPROVE,
|
||||||
|
REQUEST_CHANGES,
|
||||||
|
APPLY,
|
||||||
|
CANCEL,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum States {
|
||||||
|
DRAFT,
|
||||||
|
IN_REViEW,
|
||||||
|
APPROVED,
|
||||||
|
CHANGES_REQUESTED,
|
||||||
|
APPLIED,
|
||||||
|
REJECTED,
|
||||||
|
CANCELLED,
|
||||||
|
}
|
||||||
|
/* eslint-disable */
|
||||||
|
class ApprovalFlow extends StateMachine<States, Events> {
|
||||||
|
|
||||||
|
constructor(init: States = States.DRAFT) {
|
||||||
|
|
||||||
|
super(init);
|
||||||
|
|
||||||
|
const s = States;
|
||||||
|
const e = Events;
|
||||||
|
|
||||||
|
|
||||||
|
this.addTransitions([
|
||||||
|
// fromState event toState callback
|
||||||
|
transition(s.DRAFT, e.SUBMIT, s.IN_REViEW, this.onSubmitted.bind(this)),
|
||||||
|
transition(s.DRAFT, e.CANCEL, s.CANCELLED, this.onCancelled.bind(this)),
|
||||||
|
transition(s.IN_REViEW, e.APPROVE, s.APPROVED, this.onApproved.bind(this)),
|
||||||
|
transition(s.APPROVED, e.APPLY, s.APPLIED, this.onApplied.bind(this)),
|
||||||
|
transition(s.APPROVED, e.CANCEL, s.CANCELLED, this.onCancelled.bind(this)),
|
||||||
|
transition(s.IN_REViEW, e.CANCEL, s.CANCELLED, this.onCancelled.bind(this)),
|
||||||
|
transition(s.IN_REViEW, e.REQUEST_CHANGES,s.CHANGES_REQUESTED, this.onChangesRequested.bind(this)),
|
||||||
|
transition(s.IN_REViEW, e.REJECT, s.REJECTED, this.onRejected.bind(this)),
|
||||||
|
transition(s.CHANGES_REQUESTED,e.SUBMIT, s.DRAFT, this.onSubmitted.bind(this)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// public methods
|
||||||
|
async submitDraft() { return this.dispatch(Events.SUBMIT); }
|
||||||
|
|
||||||
|
async cancel() { return this.dispatch(Events.CANCEL); }
|
||||||
|
|
||||||
|
async approve() { return this.dispatch(Events.APPROVE); }
|
||||||
|
async apply() { return this.dispatch(Events.APPLY); }
|
||||||
|
async reject() { return this.dispatch(Events.REJECT); }
|
||||||
|
async requestChanges() { return this.dispatch(Events.REQUEST_CHANGES); }
|
||||||
|
|
||||||
|
|
||||||
|
// transition callbacks
|
||||||
|
private async onSubmitted() {
|
||||||
|
console.log(`onSubmitted...`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async onRejected() {
|
||||||
|
console.log(`onRejected...`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async onChangesRequested() {
|
||||||
|
console.log(`onApproved...`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async onCancelled() {
|
||||||
|
console.log(`onCancelled...`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async onApproved() {
|
||||||
|
console.log(`onApproved...`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async onApplied() {
|
||||||
|
console.log(`onApplied...`);
|
||||||
|
}
|
||||||
|
/* eslint-enable */
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user