@@ -2,42 +2,136 @@ import { IStep, IStepConfig } from '@plumber/types'
22
33import get from 'lodash.get'
44
5- export function validateApprovalConfig ( config : IStepConfig , prevStep : IStep ) {
5+ import { parsedMrfWorkflowStepSchema } from '@/apps/formsg/common/types'
6+ import Step from '@/models/step'
7+
8+ /**
9+ * TODO: write unit test for this function
10+ */
11+ export async function validateApprovalConfig (
12+ config : IStepConfig ,
13+ prevStep : IStep ,
14+ ) : Promise <
15+ | {
16+ isApprovalConfigValid : true
17+ newStepPosition : number
18+ }
19+ | {
20+ isApprovalConfigValid : false
21+ }
22+ > {
623 /**
7- * if approval config is not present, or is a trigger, return true
24+ * Case 1: new step is a trigger step , return early
825 */
9- if ( ! config ?. approval || ! prevStep ) {
10- return true
26+ if ( ! prevStep ) {
27+ return {
28+ isApprovalConfigValid : true ,
29+ newStepPosition : 1 ,
30+ }
1131 }
1232
33+ const isPreviousStepMrfApprovalStep =
34+ prevStep . appKey === 'formsg' &&
35+ prevStep . key === 'mrfSubmission' &&
36+ // if no approval field, it's just a normal mrf step
37+ ! ! get ( prevStep . parameters , 'mrf.approvalField' )
38+
1339 /**
14- * Both approval branch and approval step id must come together
40+ * Case 2: Prev step has no approval config and is not an mrf approval step,
41+ * current step should have no approval config either
1542 */
16- if ( ! ( config ?. approval ?. branch && config ?. approval ?. stepId ) ) {
17- return false
43+ if ( ! prevStep . config . approval && ! isPreviousStepMrfApprovalStep ) {
44+ if ( ! config ?. approval ) {
45+ return {
46+ isApprovalConfigValid : true ,
47+ newStepPosition : prevStep . position + 1 ,
48+ }
49+ }
50+ return {
51+ isApprovalConfigValid : false ,
52+ }
1853 }
1954
2055 /**
21- * If previous step an approval step return true
56+ * Case 3: Prev step is part of an approval branch, check that
57+ * the new step has the same approval config
2258 */
23- if (
24- prevStep . id === config . approval ?. stepId &&
25- prevStep . appKey === 'formsg' &&
26- prevStep . key === 'mrfSubmission' &&
27- ! ! get ( prevStep . parameters , 'mrf.approvalField' )
28- ) {
29- return true
59+ if ( prevStep . config . approval ) {
60+ const isSameApprovalConfig =
61+ prevStep . config . approval ?. branch === config ?. approval ?. branch &&
62+ prevStep . config . approval ?. stepId === config ?. approval ?. stepId
63+ if ( isSameApprovalConfig ) {
64+ return {
65+ isApprovalConfigValid : true ,
66+ newStepPosition : prevStep . position + 1 ,
67+ }
68+ }
69+ return {
70+ isApprovalConfigValid : false ,
71+ }
3072 }
3173
3274 /**
33- * If previous step has the same approval branch and step id, return true
75+ * Case 4: Previous step is an approval step
3476 */
35- if (
36- prevStep . config . approval ?. branch === config . approval ?. branch &&
37- prevStep . config . approval ?. stepId === config . approval ?. stepId
38- ) {
39- return true
77+ if ( isPreviousStepMrfApprovalStep ) {
78+ // validate approval config
79+ const { success } = parsedMrfWorkflowStepSchema . safeParse ( config ?. approval )
80+ // validate approval config step id is the same as prev step id
81+ if ( ! success || config . approval . stepId !== prevStep . id ) {
82+ return {
83+ isApprovalConfigValid : false ,
84+ }
85+ }
86+ /**
87+ * If approval branch, simply add 1 to the prev step position
88+ */
89+ if ( config . approval . branch === 'approve' ) {
90+ return {
91+ isApprovalConfigValid : true ,
92+ newStepPosition : prevStep . position + 1 ,
93+ }
94+ }
95+ // If reject branch, find the end of the approval branch and add one to that step
96+ // first find the next mrf step (if any)
97+ const nextMrfStep = await Step . query ( )
98+ . where ( 'flow_id' , prevStep . flowId )
99+ . andWhere ( 'type' , 'action' )
100+ . andWhere ( 'key' , 'mrfSubmission' )
101+ . andWhere ( 'position' , '>' , prevStep . position )
102+ . orderBy ( 'position' , 'asc' )
103+ . first ( )
104+ // then find the last approval step in the curr branch (if any)
105+ const lastApprovalStepQuery = Step . query ( )
106+ . where ( 'flow_id' , prevStep . flowId )
107+ . andWhere ( 'position' , '>' , prevStep . position )
108+ . andWhere ( 'config.approval.branch' , 'approve' )
109+ . orderBy ( 'position' , 'desc' )
110+ . first ( )
111+
112+ if ( nextMrfStep ) {
113+ lastApprovalStepQuery . andWhere ( 'position' , '<' , nextMrfStep . position )
114+ }
115+ const lastApprovalStep = await lastApprovalStepQuery . first ( )
116+ // If last approval step exists, return the position after that
117+ if ( lastApprovalStep ) {
118+ return {
119+ isApprovalConfigValid : true ,
120+ newStepPosition : lastApprovalStep . position + 1 ,
121+ }
122+ } else {
123+ // If no last approval step, return the position after the previous step
124+ return {
125+ isApprovalConfigValid : true ,
126+ newStepPosition : prevStep . position + 1 ,
127+ }
128+ }
40129 }
41130
42- return false
131+ /**
132+ * Catch all branch, something seems fishy
133+ */
134+ return {
135+ isApprovalConfigValid : false ,
136+ }
43137}
0 commit comments