From 0f081ebaa5f29b3a3f7355d2c0e1e5c178636083 Mon Sep 17 00:00:00 2001 From: Will Ruggiano Date: Tue, 3 Dec 2024 10:48:04 -0800 Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?= =?UTF-8?q?l=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.4 --- .../resolvers/ChecklistAggregate.test.graphql | 10 ++ .../resolvers/ChecklistAggregate.test.ts | 52 ++++++ .../resolvers/ChecklistAggregate.ts | 162 ++++++++++++------ .../ChecklistAggregate.test.ts.snap | 25 +++ 4 files changed, 196 insertions(+), 53 deletions(-) create mode 100644 schema/application/resolvers/ChecklistAggregate.test.graphql create mode 100644 schema/application/resolvers/ChecklistAggregate.test.ts create mode 100644 schema/application/resolvers/__snapshots__/ChecklistAggregate.test.ts.snap diff --git a/schema/application/resolvers/ChecklistAggregate.test.graphql b/schema/application/resolvers/ChecklistAggregate.test.graphql new file mode 100644 index 0000000..f5d5a9d --- /dev/null +++ b/schema/application/resolvers/ChecklistAggregate.test.graphql @@ -0,0 +1,10 @@ +query TestChecklistAgg( + $parent: ID! + $assignedTo: [ID!]! + $dueOnInput: TemporalRangeInput! +) { + checklistAgg { + assignedTo(parent: $parent, assignees: $assignedTo) + dueOn(parent: $parent, input: $dueOnInput) + } +} diff --git a/schema/application/resolvers/ChecklistAggregate.test.ts b/schema/application/resolvers/ChecklistAggregate.test.ts new file mode 100644 index 0000000..20919e0 --- /dev/null +++ b/schema/application/resolvers/ChecklistAggregate.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, test } from "bun:test"; +import { resolvers, typeDefs } from "@/schema"; +import { encodeGlobalId } from "@/schema/system"; +import { execute } from "@/test/prelude"; +import { makeExecutableSchema } from "@graphql-tools/schema"; +import { TestChecklistAggDocument } from "./ChecklistAggregate.test.generated"; + +const schema = makeExecutableSchema({ resolvers, typeDefs }); + +const ASSIGNEE = encodeGlobalId({ + type: "worker", + id: "worker-instance_cbcb5607-373a-45df-aae0-01bdddc744e4", +}); +const CUSTOMER = encodeGlobalId({ + type: "organization", + id: "customer_1b2d6c60-8678-45ad-b30d-a10323c2c441", +}); +const TEMPLATE = encodeGlobalId({ + type: "worktemplate", + id: "a2b2004d-49a6-4fa2-b8e5-d86df0041fdd", +}); +const DUE_ON_BEFORE = "1733251472196"; // Date.now() circa Tue Dec 03 2024 10:45 + +describe.skipIf(!!process.env.CI)("ChecklistAggregate", () => { + test("when parent is customer", async () => { + const result = await execute(schema, TestChecklistAggDocument, { + parent: CUSTOMER, + assignedTo: [ASSIGNEE], + dueOnInput: { + before: { + instant: DUE_ON_BEFORE, + }, + }, + }); + expect(result.errors).toBeFalsy(); + expect(result).toMatchSnapshot(); + }); + + test("when parent is template", async () => { + const result = await execute(schema, TestChecklistAggDocument, { + parent: TEMPLATE, + assignedTo: [ASSIGNEE], + dueOnInput: { + before: { + instant: DUE_ON_BEFORE, + }, + }, + }); + expect(result.errors).toBeFalsy(); + expect(result).toMatchSnapshot(); + }); +}); diff --git a/schema/application/resolvers/ChecklistAggregate.ts b/schema/application/resolvers/ChecklistAggregate.ts index cee2f3c..574c2eb 100644 --- a/schema/application/resolvers/ChecklistAggregate.ts +++ b/schema/application/resolvers/ChecklistAggregate.ts @@ -3,40 +3,70 @@ import type { ChecklistAggregateResolvers } from "@/schema"; import { decodeGlobalId } from "@/schema/system"; import { map } from "@/util"; import { Temporal } from "@js-temporal/polyfill"; +import { match } from "ts-pattern"; export const ChecklistAggregate: ChecklistAggregateResolvers = { async assignedTo(_, args) { const { type, id } = decodeGlobalId(args.parent); - // TODO: switch on parent type const assignees = args.assignees.map(e => decodeGlobalId(e).id); - const [{ count }] = await sql<[{ count: number }]>` - SELECT count(*) - FROM public.workresultinstance - INNER JOIN public.workresult - ON - workresultinstanceworkresultid = workresultid - AND workresultentitytypeid = 850 - AND workresultisprimary - WHERE - workresultinstancecustomerid IN ( - SELECT customerid - FROM public.customer - WHERE customeruuid = ${id} - ) - AND workresultinstancevalue IN ( - SELECT workerinstanceid::text - FROM public.workerinstance - WHERE id IN ${sql(assignees)} - ) - `; + + const [{ count }] = await match(type) + .with( + "organization", + () => sql<[{ count: number }]>` + SELECT count(*) + FROM public.workresultinstance AS wri + INNER JOIN public.workresult AS wr + ON + wri.workresultinstanceworkresultid = wr.workresultid + AND wr.workresultentitytypeid = 850 + AND wr.workresultisprimary + INNER JOIN public.worktemplatetype AS wtt + ON wr.workresultworktemplateid = wtt.worktemplatetypeworktemplateid + INNER JOIN public.systag AS s + ON wtt.worktemplatetypesystaguuid = s.systaguuid + WHERE + s.systagtype = 'Checklist' + AND workresultinstancecustomerid IN ( + SELECT c.customerid + FROM public.customer AS c + WHERE c.customeruuid = ${id} + ) + AND workresultinstancevalue IN ( + SELECT w.workerinstanceid::text + FROM public.workerinstance AS w + WHERE w.workerinstanceuuid IN ${sql(assignees)} + ); + `, + ) + .with( + "worktemplate", + () => sql<[{ count: number }]>` + SELECT count(*) + FROM public.workresultinstance AS wri + INNER JOIN public.workresult AS wr + ON + wri.workresultinstanceworkresultid = wr.workresultid + AND wr.workresultentitytypeid = 850 + AND wr.workresultisprimary + INNER JOIN public.worktemplate AS wt + ON wr.workresultworktemplateid = wt.worktemplateid + WHERE + wt.id = ${id} + AND workresultinstancevalue IN ( + SELECT w.workerinstanceid::text + FROM public.workerinstance AS w + WHERE w.workerinstanceuuid IN ${sql(assignees)} + ); + `, + ) + .otherwise(() => [{ count: 0 }]); return count; }, async dueOn(_, args) { const { type, id } = decodeGlobalId(args.parent); - console.log("parent", { type, id }); - // TODO: switch on parent type const before = map(args.input.before, input => { switch (true) { @@ -48,10 +78,10 @@ export const ChecklistAggregate: ChecklistAggregateResolvers = { ).toZonedDateTimeISO(input.zdt.timeZone); default: { const _: never = input; - throw "invariant violated"; + return null; } } - }); + })?.toString({ calendarName: "never", timeZoneName: "never" }); const after = map(args.input.after, input => { switch (true) { @@ -63,39 +93,65 @@ export const ChecklistAggregate: ChecklistAggregateResolvers = { ).toZonedDateTimeISO(input.zdt.timeZone); default: { const _: never = input; - throw "invariant violated"; + return null; } } - }); + })?.toString({ calendarName: "never", timeZoneName: "never" }); if (!after && !before) return 0; - const [{ count }] = await sql<[{ count: number }]>` - SELECT count(*) - FROM public.workinstance - WHERE - workinstancecustomerid IN ( - SELECT customerid - FROM public.customer - WHERE customeruuid = ${id} - ) - AND ${join( - [ - sql`workinstancetargetstartdate IS NOT null`, - ...(after - ? [ - sql`workinstancetargetstartdate > ${after.toString({ calendarName: "never", timeZoneName: "never" })}`, - ] - : []), - ...(before - ? [ - sql`workinstancetargetstartdate < ${before.toString({ calendarName: "never", timeZoneName: "never" })}`, - ] - : []), - ], - sql`AND`, - )} - `; + const [{ count }] = await match(type) + .with( + "organization", + () => sql<[{ count: number }]>` + SELECT count(*) + FROM public.workinstance + WHERE + workinstancecustomerid IN ( + SELECT customerid + FROM public.customer + WHERE customeruuid = ${id} + ) + AND ${join( + [ + sql`workinstancetargetstartdate IS NOT null`, + ...(after + ? [sql`workinstancetargetstartdate > ${after}`] + : []), + ...(before + ? [sql`workinstancetargetstartdate < ${before}`] + : []), + ], + sql`AND`, + )} + `, + ) + .with( + "worktemplate", + () => sql<[{ count: number }]>` + SELECT count(*) + FROM public.workinstance + WHERE + workinstanceworktemplateid IN ( + SELECT wt.worktemplateid + FROM public.worktemplate AS wt + WHERE wt.id = ${id} + ) + AND ${join( + [ + sql`workinstancetargetstartdate IS NOT null`, + ...(after + ? [sql`workinstancetargetstartdate > ${after}`] + : []), + ...(before + ? [sql`workinstancetargetstartdate < ${before}`] + : []), + ], + sql`AND`, + )} + `, + ) + .otherwise(() => [{ count: 0 }]); return count; }, diff --git a/schema/application/resolvers/__snapshots__/ChecklistAggregate.test.ts.snap b/schema/application/resolvers/__snapshots__/ChecklistAggregate.test.ts.snap new file mode 100644 index 0000000..5775e02 --- /dev/null +++ b/schema/application/resolvers/__snapshots__/ChecklistAggregate.test.ts.snap @@ -0,0 +1,25 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`ChecklistAggregate when parent is customer 1`] = ` +{ + "data": { + "checklistAgg": { + "__typename": "ChecklistAggregate", + "assignedTo": 3, + "dueOn": 25, + }, + }, +} +`; + +exports[`ChecklistAggregate when parent is template 1`] = ` +{ + "data": { + "checklistAgg": { + "__typename": "ChecklistAggregate", + "assignedTo": 1, + "dueOn": 2, + }, + }, +} +`;