Skip to content

Commit 6f602d0

Browse files
committed
Lazily reference workflow in service construction
To prevent circular dependencies on initialization
1 parent 93d4b3d commit 6f602d0

File tree

7 files changed

+31
-17
lines changed

7 files changed

+31
-17
lines changed

src/components/project/workflow/events/project-transitioned.event.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { UnsecuredDto } from '~/common';
22
import type { Project, ProjectStep } from '../../dto';
33
import type { ProjectWorkflowEvent as WorkflowEvent } from '../dto';
4-
import { ProjectWorkflow } from '../project-workflow';
4+
import type { ProjectWorkflow } from '../project-workflow';
55

66
export class ProjectTransitionedEvent {
77
constructor(

src/components/project/workflow/project-workflow.flowchart.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ import { ProjectWorkflow } from './project-workflow';
44

55
@Injectable()
66
export class ProjectWorkflowFlowchart extends WorkflowFlowchart(
7-
ProjectWorkflow,
7+
() => ProjectWorkflow,
88
) {}

src/components/project/workflow/project-workflow.granter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ProjectWorkflow } from './project-workflow';
55

66
@Granter(Event)
77
export class ProjectWorkflowEventGranter extends WorkflowEventGranter(
8-
ProjectWorkflow,
8+
() => ProjectWorkflow,
99
) {}
1010

1111
declare module '../../authorization/policy/granters' {

src/components/project/workflow/project-workflow.service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import { ProjectWorkflow } from './project-workflow';
1616
import { ProjectWorkflowRepository } from './project-workflow.repository';
1717

1818
@Injectable()
19-
export class ProjectWorkflowService extends WorkflowService(ProjectWorkflow) {
19+
export class ProjectWorkflowService extends WorkflowService(
20+
() => ProjectWorkflow,
21+
) {
2022
constructor(
2123
private readonly resources: ResourceLoader,
2224
private readonly repo: ProjectWorkflowRepository,

src/components/workflow/workflow.flowchart.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ import { deflateSync as deflate } from 'zlib';
1212
import { Workflow } from './define-workflow';
1313
import { DynamicState } from './transitions/dynamic-state';
1414

15-
export const WorkflowFlowchart = <W extends Workflow>(workflow: W) => {
15+
export const WorkflowFlowchart = <W extends Workflow>(workflow: () => W) => {
1616
abstract class WorkflowFlowchartClass {
17+
protected workflow: W;
18+
constructor() {
19+
this.workflow = workflow();
20+
}
21+
1722
/** Generate a flowchart in mermaid markup. */
1823
generateMarkup() {
1924
const rgbHexAddAlpha = (rgb: string, alpha: number) =>
@@ -46,12 +51,12 @@ export const WorkflowFlowchart = <W extends Workflow>(workflow: W) => {
4651
uuid.v1().replaceAll(/-/g, ''),
4752
);
4853

49-
const transitions = workflow.transitions
54+
const transitions = this.workflow.transitions
5055
.toSorted(
5156
cmpBy((t) => {
5257
const endState =
5358
typeof t.to === 'string' ? t.to : t.to.relatedStates?.[0];
54-
return workflow.states.entries.findIndex(
59+
return this.workflow.states.entries.findIndex(
5560
(e) => e.value === endState,
5661
);
5762
}),
@@ -95,8 +100,8 @@ export const WorkflowFlowchart = <W extends Workflow>(workflow: W) => {
95100
.join('\n'),
96101
);
97102

98-
const stateLines = [...workflow.states].map((state) => {
99-
const { label } = workflow.states.entry(state);
103+
const stateLines = [...this.workflow.states].map((state) => {
104+
const { label } = this.workflow.states.entry(state);
100105
const className = `${usedStates.has(state) ? '' : 'Unused'}State`;
101106
return `${state}(${label}):::${className}`;
102107
});

src/components/workflow/workflow.granter.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { Workflow } from './define-workflow';
1414
export function WorkflowEventGranter<
1515
W extends Workflow,
1616
EventClass extends W['eventResource'],
17-
>(workflow: W) {
17+
>(workflow: () => W) {
1818
type State = Workflow['state'];
1919
type Names = Workflow['transition']['name'];
2020

@@ -37,7 +37,8 @@ export function WorkflowEventGranter<
3737
* Can read & execute all transitions.
3838
*/
3939
get executeAll(): this {
40-
return this.transitions(workflow.transitions.map((t) => t.name)).execute;
40+
return this.transitions(workflow().transitions.map((t) => t.name))
41+
.execute;
4142
}
4243

4344
/**
@@ -48,15 +49,15 @@ export function WorkflowEventGranter<
4849
}
4950

5051
isTransitions(...transitions: Array<Many<Names>>) {
51-
return TransitionCondition.fromName(workflow, transitions.flat());
52+
return TransitionCondition.fromName(workflow(), transitions.flat());
5253
}
5354

5455
transitions(...transitions: Array<Many<Names>>) {
5556
return this.when(this.isTransitions(...transitions));
5657
}
5758

5859
isState(...states: Array<Many<State>>) {
59-
return TransitionCondition.fromEndState(workflow, states.flat());
60+
return TransitionCondition.fromEndState(workflow(), states.flat());
6061
}
6162

6263
state(...states: Array<Many<State>>) {

src/components/workflow/workflow.service.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@ type ExecuteTransitionInput = ReturnType<
1414
typeof ExecuteTransitionInputFn
1515
>['prototype'];
1616

17-
export const WorkflowService = <W extends Workflow>(workflow: W) => {
17+
export const WorkflowService = <W extends Workflow>(workflow: () => W) => {
1818
@Injectable()
1919
abstract class WorkflowServiceClass {
2020
@Inject() protected readonly privileges: Privileges;
21-
protected readonly workflow = workflow;
21+
protected readonly workflow: W;
22+
23+
constructor() {
24+
this.workflow = workflow();
25+
}
2226

2327
protected transitionByKey(key: ID | Nil, to: W['state']) {
2428
if (!key) {
@@ -45,7 +49,7 @@ export const WorkflowService = <W extends Workflow>(workflow: W) => {
4549
);
4650

4751
// Filter out transitions without authorization to execute
48-
const p = this.privileges.for(session, workflow.eventResource);
52+
const p = this.privileges.for(session, this.workflow.eventResource);
4953
available = available.filter((t) =>
5054
// I don't have a good way to type this right now.
5155
// Context usage is still fuzzy when conditions need different shapes.
@@ -101,7 +105,9 @@ export const WorkflowService = <W extends Workflow>(workflow: W) => {
101105
}
102106

103107
canBypass(session: Session) {
104-
return this.privileges.for(session, workflow.eventResource).can('create');
108+
return this.privileges
109+
.for(session, this.workflow.eventResource)
110+
.can('create');
105111
}
106112

107113
protected getBypassIfValid(

0 commit comments

Comments
 (0)