Skip to content

Commit 07c34b0

Browse files
committed
Break product action into function, handle cancel
1 parent c352d4d commit 07c34b0

File tree

5 files changed

+159
-129
lines changed

5 files changed

+159
-129
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { WorkflowType } from "sil.appbuilder.portal.common/prisma";
2+
3+
export enum ProductActionType {
4+
Rebuild = 'rebuild',
5+
Republish = 'republish',
6+
Cancel = 'cancel'
7+
}
8+
9+
export function getProductActions(
10+
product: {
11+
WorkflowInstance: {
12+
WorkflowDefinition: {
13+
Type: WorkflowType;
14+
};
15+
} | null;
16+
DatePublished: unknown;
17+
ProductDefinition: { RebuildWorkflowId: unknown; RepublishWorkflowId: unknown };
18+
},
19+
projectOwnerId: number,
20+
userId: number
21+
) {
22+
const ret: ProductActionType[] = [];
23+
if (!product.WorkflowInstance) {
24+
if (product.DatePublished) {
25+
if (product.ProductDefinition.RebuildWorkflowId !== null) {
26+
ret.push(ProductActionType.Rebuild);
27+
}
28+
if (product.ProductDefinition.RepublishWorkflowId !== null) {
29+
ret.push(ProductActionType.Republish);
30+
}
31+
}
32+
} else if (
33+
projectOwnerId === userId &&
34+
product.WorkflowInstance.WorkflowDefinition.Type !== WorkflowType.Startup
35+
) {
36+
ret.push(ProductActionType.Cancel);
37+
}
38+
39+
return ret;
40+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { BullMQ, DatabaseWrites, prisma, Queues, Workflow } from 'sil.appbuilder.portal.common';
2+
import { ProductTransitionType, WorkflowType } from 'sil.appbuilder.portal.common/prisma';
3+
import { ProductActionType } from '.';
4+
5+
export async function doProductAction(productId: string, action: ProductActionType) {
6+
const product = await prisma.products.findUnique({
7+
where: {
8+
Id: productId
9+
},
10+
select: {
11+
Id: true,
12+
ProjectId: true,
13+
ProductDefinition: {
14+
select: {
15+
RebuildWorkflow: {
16+
select: {
17+
Type: true,
18+
ProductType: true,
19+
WorkflowOptions: true
20+
}
21+
},
22+
RepublishWorkflow: {
23+
select: {
24+
Type: true,
25+
ProductType: true,
26+
WorkflowOptions: true
27+
}
28+
}
29+
}
30+
},
31+
WorkflowInstance: {
32+
select: {
33+
WorkflowDefinition: {
34+
select: { Type: true }
35+
}
36+
}
37+
}
38+
}
39+
});
40+
41+
if (product) {
42+
switch (action) {
43+
case ProductActionType.Rebuild:
44+
case ProductActionType.Republish: {
45+
const flowType = action === 'rebuild' ? 'RebuildWorkflow' : 'RepublishWorkflow';
46+
if (product.ProductDefinition[flowType] && !product.WorkflowInstance) {
47+
await Workflow.create(productId, {
48+
productType: product.ProductDefinition[flowType].ProductType,
49+
options: new Set(product.ProductDefinition[flowType].WorkflowOptions),
50+
workflowType: product.ProductDefinition[flowType].Type
51+
});
52+
}
53+
break;
54+
}
55+
case ProductActionType.Cancel:
56+
if (
57+
product.WorkflowInstance?.WorkflowDefinition &&
58+
product.WorkflowInstance.WorkflowDefinition.Type !== WorkflowType.Startup
59+
) {
60+
// TODO: this won't cancel a build that is in progress
61+
await Queues.UserTasks.add(
62+
`Delete UserTasks for canceled workflow (product #${productId})`,
63+
{
64+
type: BullMQ.JobType.UserTasks_Modify,
65+
scope: 'Product',
66+
productId,
67+
operation: {
68+
type: BullMQ.UserTasks.OpType.Delete
69+
}
70+
}
71+
);
72+
await DatabaseWrites.productTransitions.create({
73+
data: {
74+
ProductId: productId,
75+
// This is how S1 does it. May want to change later
76+
AllowedUserNames: '',
77+
DateTransition: new Date(),
78+
TransitionType: ProductTransitionType.CancelWorkflow,
79+
WorkflowType: product.WorkflowInstance.WorkflowDefinition.Type
80+
}
81+
});
82+
await DatabaseWrites.workflowInstances.delete(productId, product.ProjectId);
83+
}
84+
break;
85+
}
86+
}
87+
}

source/SIL.AppBuilder.Portal/src/routes/(authenticated)/projects/[filter=projectSelector]/[id]/+page.server.ts

Lines changed: 5 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ProductActionType } from '$lib/products';
2+
import { doProductAction } from '$lib/products/server';
13
import {
24
bulkProjectActionSchema,
35
canModifyProject,
@@ -7,15 +9,15 @@ import {
79
import { doProjectAction, projectFilter, userGroupsForOrg } from '$lib/projects/common.server';
810
import type { Prisma } from '@prisma/client';
911
import { error, redirect, type Actions } from '@sveltejs/kit';
10-
import { prisma, Workflow } from 'sil.appbuilder.portal.common';
12+
import { prisma } from 'sil.appbuilder.portal.common';
1113
import { fail, superValidate } from 'sveltekit-superforms';
1214
import { valibot } from 'sveltekit-superforms/adapters';
1315
import * as v from 'valibot';
1416
import type { PageServerLoad } from './$types';
1517

1618
const bulkProductActionSchema = v.object({
1719
products: v.array(v.pipe(v.string(), v.uuid())),
18-
operation: v.nullable(v.picklist(['rebuild', 'republish']))
20+
operation: v.nullable(v.pipe(v.enum(ProductActionType), v.excludes(ProductActionType.Cancel)))
1921
});
2022

2123
function whereStatements(
@@ -187,45 +189,7 @@ export const actions: Actions = {
187189
const form = await superValidate(event.request, valibot(bulkProductActionSchema));
188190
if (!form.valid || !form.data.operation) return fail(400, { form, ok: false });
189191

190-
const products = await prisma.products.findMany({
191-
where: {
192-
Id: { in: form.data.products },
193-
WorkflowInstance: null
194-
},
195-
select: {
196-
Id: true,
197-
ProductDefinition: {
198-
select: {
199-
RebuildWorkflow: {
200-
select: {
201-
Type: true,
202-
ProductType: true,
203-
WorkflowOptions: true
204-
}
205-
},
206-
RepublishWorkflow: {
207-
select: {
208-
Type: true,
209-
ProductType: true,
210-
WorkflowOptions: true
211-
}
212-
}
213-
}
214-
}
215-
}
216-
});
217-
218-
const flowType = form.data.operation === 'rebuild' ? 'RebuildWorkflow' : 'RepublishWorkflow';
219-
220-
await Promise.all(products.map(async (p) => {
221-
if (p.ProductDefinition[flowType]) {
222-
await Workflow.create(p.Id, {
223-
productType: p.ProductDefinition[flowType].ProductType,
224-
options: new Set(p.ProductDefinition[flowType].WorkflowOptions),
225-
workflowType: p.ProductDefinition[flowType].Type
226-
});
227-
}
228-
}));
192+
await Promise.all(form.data.products.map((p) => doProductAction(p, form.data.operation!)));
229193

230194
return { form, ok: true };
231195
}

source/SIL.AppBuilder.Portal/src/routes/(authenticated)/projects/[id=idNumber]/+page.server.ts

Lines changed: 18 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { getProductActions, ProductActionType } from '$lib/products';
2+
import { doProductAction } from '$lib/products/server';
13
import { canModifyProject, projectActionSchema } from '$lib/projects/common';
24
import {
35
doProjectAction,
@@ -6,7 +8,7 @@ import {
68
} from '$lib/projects/common.server';
79
import { idSchema } from '$lib/valibot';
810
import { error } from '@sveltejs/kit';
9-
import { BullMQ, DatabaseWrites, prisma, Queues, Workflow } from 'sil.appbuilder.portal.common';
11+
import { BullMQ, DatabaseWrites, prisma, Queues } from 'sil.appbuilder.portal.common';
1012
import { RoleId } from 'sil.appbuilder.portal.common/prisma';
1113
import { fail, superValidate } from 'sveltekit-superforms';
1214
import { valibot } from 'sveltekit-superforms/adapters';
@@ -35,8 +37,10 @@ const addProductSchema = v.object({
3537
productDefinitionId: idSchema,
3638
storeId: idSchema
3739
});
40+
3841
const productActionSchema = v.object({
39-
id: v.pipe(v.string(), v.uuid())
42+
id: v.pipe(v.string(), v.uuid()),
43+
productAction: v.enum(ProductActionType)
4044
});
4145

4246
// Are we sending too much data?
@@ -95,7 +99,12 @@ export const load = (async ({ locals, params }) => {
9599
},
96100
WorkflowInstance: {
97101
select: {
98-
Id: true
102+
Id: true,
103+
WorkflowDefinition: {
104+
select: {
105+
Type: true
106+
}
107+
}
99108
}
100109
}
101110
}
@@ -235,8 +244,7 @@ export const load = (async ({ locals, params }) => {
235244
ActiveTransition: strippedTransitions.find(
236245
(t) => (t[0] ?? t[1])?.ProductId === product.Id
237246
)?.[1],
238-
CanRebuild: !product.WorkflowInstance && product.ProductDefinition.RebuildWorkflowId !== null,
239-
CanRepublish: !product.WorkflowInstance && product.ProductDefinition.RepublishWorkflowId !== null
247+
actions: getProductActions(product, project.Owner.Id, session.user.userId)
240248
}))
241249
},
242250
possibleProjectOwners: await prisma.users.findMany({
@@ -338,7 +346,7 @@ export const actions = {
338346

339347
return { form, ok: !!productId };
340348
},
341-
async rebuildProduct(event) {
349+
async productAction(event) {
342350
if (!verifyCanViewAndEdit((await event.locals.auth())!, parseInt(event.params.id)))
343351
return fail(403);
344352
const form = await superValidate(event.request, valibot(productActionSchema));
@@ -348,77 +356,13 @@ export const actions = {
348356
Id: form.data.id
349357
},
350358
select: {
351-
ProjectId: true,
352-
ProductDefinition: {
353-
select: {
354-
RebuildWorkflow: {
355-
select: {
356-
Id: true,
357-
Type: true,
358-
ProductType: true,
359-
WorkflowOptions: true
360-
}
361-
}
362-
}
363-
},
364-
WorkflowInstance: {
365-
select: {
366-
Id: true
367-
}
368-
}
359+
ProjectId: true
369360
}
370361
});
371362
if (!product || product.ProjectId !== parseInt(event.params.id)) return fail(404);
372-
if (!product.WorkflowInstance && product.ProductDefinition.RebuildWorkflow) {
373-
await Workflow.create(form.data.id, {
374-
productType: product.ProductDefinition.RebuildWorkflow.ProductType,
375-
options: new Set(product.ProductDefinition.RebuildWorkflow.WorkflowOptions),
376-
workflowType: product.ProductDefinition.RebuildWorkflow.Type
377-
});
378-
} else {
379-
return fail(400, { form, ok: false });
380-
}
381-
},
382-
async republishProduct(event) {
383-
if (!verifyCanViewAndEdit((await event.locals.auth())!, parseInt(event.params.id)))
384-
return fail(403);
385-
const form = await superValidate(event.request, valibot(productActionSchema));
386-
if (!form.valid) return fail(400, { form, ok: false });
387-
const product = await prisma.products.findUnique({
388-
where: {
389-
Id: form.id
390-
},
391-
select: {
392-
ProjectId: true,
393-
ProductDefinition: {
394-
select: {
395-
RepublishWorkflow: {
396-
select: {
397-
Id: true,
398-
Type: true,
399-
ProductType: true,
400-
WorkflowOptions: true
401-
}
402-
}
403-
}
404-
},
405-
WorkflowInstance: {
406-
select: {
407-
Id: true
408-
}
409-
}
410-
}
411-
});
412-
if (!product || product.ProjectId !== parseInt(event.params.id)) return fail(404);
413-
if (!product.WorkflowInstance && product.ProductDefinition.RepublishWorkflow) {
414-
await Workflow.create(form.data.id, {
415-
productType: product.ProductDefinition.RepublishWorkflow.ProductType,
416-
options: new Set(product.ProductDefinition.RepublishWorkflow.WorkflowOptions),
417-
workflowType: product.ProductDefinition.RepublishWorkflow.Type
418-
});
419-
} else {
420-
return fail(400, { form, ok: false });
421-
}
363+
await doProductAction(form.data.id, form.data.productAction);
364+
365+
return { form, ok: true };
422366
},
423367
async addAuthor(event) {
424368
if (!verifyCanViewAndEdit((await event.locals.auth())!, parseInt(event.params.id)))

source/SIL.AppBuilder.Portal/src/routes/(authenticated)/projects/[id=idNumber]/+page.svelte

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -269,26 +269,21 @@
269269
class="dropdown-content bottom-12 right-0 p-1 bg-base-200 z-10 rounded-md min-w-36 w-auto shadow-lg"
270270
>
271271
<ul class="menu menu-compact overflow-hidden rounded-md">
272-
{#if product.CanRebuild}
272+
{#each product.actions as action}
273+
{@const message =
274+
//@ts-expect-error this is in fact correct
275+
m['products_actions_'+action]()
276+
}
273277
<li class="w-full rounded-none">
274-
<form action="?/rebuildProduct" method="post" use:enhance>
278+
<form action="?/productAction" method="post" use:enhance>
275279
<input type="hidden" name="id" value={product.Id} />
280+
<input type="hidden" name="productAction" value={action} />
276281
<button type="submit" class="text-nowrap">
277-
{m.products_actions_rebuild()}
282+
{message}
278283
</button>
279284
</form>
280285
</li>
281-
{/if}
282-
{#if product.CanRepublish}
283-
<li class="w-full rounded-none">
284-
<form action="?/republishProduct" method="post" use:enhance>
285-
<input type="hidden" name="id" value={product.Id} />
286-
<button type="submit" class="text-nowrap">
287-
{m.products_actions_republish()}
288-
</button>
289-
</form>
290-
</li>
291-
{/if}
286+
{/each}
292287
<li class="w-full rounded-none">
293288
<button class="text-nowrap" on:click={() => openModal(product.Id)}>
294289
{m.project_products_popup_details()}

0 commit comments

Comments
 (0)