Skip to content

Commit 63ff892

Browse files
committed
Serialize workflow for GQL
1 parent d870f3f commit 63ff892

File tree

4 files changed

+156
-2
lines changed

4 files changed

+156
-2
lines changed

src/components/project/workflow/resolvers/project-transitions.resolver.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
1+
import { Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
22
import { Loader, LoaderOf } from '@seedcompany/data-loader';
33
import { stripIndent } from 'common-tags';
44
import {
@@ -7,6 +7,7 @@ import {
77
Session,
88
viewOfChangeset,
99
} from '~/common';
10+
import { SerializedWorkflow } from '../../../workflow/dto';
1011
import { SecuredProjectStep } from '../../dto';
1112
import { ProjectLoader } from '../../project.loader';
1213
import { ProjectWorkflowTransition } from '../dto';
@@ -16,6 +17,11 @@ import { ProjectWorkflowService } from '../project-workflow.service';
1617
export class ProjectTransitionsResolver {
1718
constructor(private readonly workflow: ProjectWorkflowService) {}
1819

20+
@Query(() => SerializedWorkflow)
21+
async projectWorkflow() {
22+
return this.workflow.serialize();
23+
}
24+
1925
@ResolveField(() => [ProjectWorkflowTransition], {
2026
description:
2127
'The transitions currently available to execute for this project',
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './execute-transition.input';
22
export * from './workflow-event.dto';
33
export * from './workflow-transition.dto';
4+
export { SerializedWorkflow } from './serialized-workflow.dto';
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { createUnionType, Field, ObjectType } from '@nestjs/graphql';
2+
import { cacheable } from '@seedcompany/common';
3+
import * as uuid from 'uuid';
4+
import { DataObject, ID, IdField } from '~/common';
5+
import { Workflow } from '../define-workflow';
6+
import { DynamicState } from '../transitions/dynamic-state';
7+
import { TransitionType } from './workflow-transition.dto';
8+
9+
@ObjectType('WorkflowState')
10+
export class SerializedWorkflowState extends DataObject {
11+
@Field()
12+
readonly value: string;
13+
14+
@Field()
15+
readonly label: string;
16+
}
17+
18+
@ObjectType('WorkflowTransitionStaticTo')
19+
export class SerializedWorkflowTransitionStaticTo extends DataObject {
20+
@Field()
21+
readonly state: SerializedWorkflowState;
22+
}
23+
24+
@ObjectType('WorkflowTransitionDynamicTo')
25+
export class SerializedWorkflowTransitionDynamicTo extends DataObject {
26+
@IdField()
27+
readonly id: string;
28+
29+
@Field()
30+
readonly label: string;
31+
32+
@Field(() => [SerializedWorkflowState])
33+
readonly relatedStates: readonly SerializedWorkflowState[];
34+
}
35+
36+
export const SerializedWorkflowTransitionTo = createUnionType({
37+
name: 'WorkflowTransitionTo',
38+
types: () => [
39+
SerializedWorkflowTransitionStaticTo,
40+
SerializedWorkflowTransitionDynamicTo,
41+
],
42+
resolveType: (
43+
value:
44+
| SerializedWorkflowTransitionStaticTo
45+
| SerializedWorkflowTransitionDynamicTo,
46+
) =>
47+
'state' in value
48+
? SerializedWorkflowTransitionStaticTo
49+
: SerializedWorkflowTransitionDynamicTo,
50+
});
51+
52+
@ObjectType('WorkflowCondition')
53+
export class SerializedWorkflowCondition extends DataObject {
54+
@Field()
55+
readonly label: string;
56+
}
57+
58+
@ObjectType('WorkflowNotifier')
59+
export class SerializedWorkflowNotifier extends DataObject {
60+
@Field()
61+
readonly label: string;
62+
}
63+
64+
@ObjectType('WorkflowTransition')
65+
export class SerializedWorkflowTransition extends DataObject {
66+
@IdField()
67+
readonly key: ID;
68+
69+
@Field()
70+
readonly devName: string;
71+
72+
@Field()
73+
readonly label: string;
74+
75+
@Field(() => TransitionType)
76+
readonly type: TransitionType;
77+
78+
@Field(() => [SerializedWorkflowState])
79+
readonly from: readonly SerializedWorkflowState[];
80+
81+
@Field(() => SerializedWorkflowTransitionTo)
82+
readonly to: typeof SerializedWorkflowTransitionTo;
83+
84+
@Field(() => [SerializedWorkflowCondition])
85+
readonly conditions: readonly SerializedWorkflowCondition[];
86+
87+
@Field(() => [SerializedWorkflowNotifier])
88+
readonly notifiers: readonly SerializedWorkflowNotifier[];
89+
}
90+
91+
@ObjectType('Workflow')
92+
export class SerializedWorkflow extends DataObject {
93+
@IdField()
94+
readonly id: ID;
95+
96+
@Field(() => [SerializedWorkflowState])
97+
readonly states: readonly SerializedWorkflowState[];
98+
99+
@Field(() => [SerializedWorkflowTransition])
100+
readonly transitions: readonly SerializedWorkflowTransition[];
101+
102+
static from<W extends Workflow>(workflow: W): SerializedWorkflow {
103+
const serializeState = (state: Workflow['state']) => {
104+
const { value, label } = workflow.states.entry(state);
105+
return { value, label };
106+
};
107+
const dynamicToId = cacheable(
108+
new Map<DynamicState<W['state'], W['context']>, string>(),
109+
() => uuid.v1(),
110+
);
111+
return {
112+
id: workflow.id,
113+
states: [...workflow.states].map(serializeState),
114+
transitions: workflow.transitions.map((transition) => ({
115+
key: transition.key,
116+
devName: transition.name,
117+
label: transition.label,
118+
type: transition.type,
119+
from: transition.from ? [...transition.from].map(serializeState) : [],
120+
to:
121+
typeof transition.to === 'string'
122+
? {
123+
state: serializeState(transition.to),
124+
}
125+
: {
126+
id: dynamicToId(transition.to),
127+
label: transition.to.description,
128+
relatedStates:
129+
transition.to.relatedStates?.map(serializeState) ?? [],
130+
},
131+
conditions: (transition.conditions ?? []).map((condition) => ({
132+
label: condition.description,
133+
})),
134+
notifiers: (transition.notifiers ?? []).map((notifier) => ({
135+
label: notifier.description,
136+
})),
137+
})),
138+
};
139+
}
140+
}

src/components/workflow/workflow.service.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { Nil } from '@seedcompany/common';
33
import { ID, Session, UnauthorizedException } from '~/common';
44
import { Privileges } from '../authorization';
55
import { Workflow } from './define-workflow';
6-
import { ExecuteTransitionInput as ExecuteTransitionInputFn } from './dto';
6+
import {
7+
ExecuteTransitionInput as ExecuteTransitionInputFn,
8+
SerializedWorkflow,
9+
} from './dto';
710
import { withTransitionKey } from './workflow.granter';
811

912
type ExecuteTransitionInput = ReturnType<
@@ -119,6 +122,10 @@ export const WorkflowService = <W extends Workflow>(workflow: W) => {
119122
}
120123
return input.bypassTo;
121124
}
125+
126+
serialize() {
127+
return SerializedWorkflow.from(this.workflow);
128+
}
122129
}
123130

124131
return WorkflowServiceClass;

0 commit comments

Comments
 (0)