Skip to content

Commit 013c52f

Browse files
committed
call out to new octopus digital imaging ordering API during createItem mutation, if the item type is imaging-request
1 parent d31d85b commit 013c52f

File tree

6 files changed

+140
-19
lines changed

6 files changed

+140
-19
lines changed

client/src/grid/octopusImagingOrderDisplay.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ export const OctopusImagingOrderDisplay = ({
6868
draggable={false}
6969
// TODO: hover for larger thumbnail
7070
/>
71+
{payload.octopusOrderId && (
72+
<div
73+
css={css`
74+
${agateSans.xxsmall()}
75+
`}
76+
>
77+
<strong>Order Ref:</strong> {payload.octopusOrderId}
78+
</div>
79+
)}
7180
</div>
7281
);
7382
};
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Item } from "../../../shared/graphql/graphql";
1+
import { Item } from "shared/graphql/graphql";
22

3-
export type ItemWithParsedPayload = Item & {
3+
export type ItemWithParsedPayload = Omit<Item, "payload"> & {
44
payload: Record<string, unknown> | null | undefined;
55
};

client/src/types/PayloadAndType.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export interface PayloadWithApiUrl extends PayloadCommon {
4747

4848
export interface PayloadWithRequestType extends PayloadWithThumbnail {
4949
requestType: ImagingRequestType;
50+
octopusOrderId?: string;
5051
}
5152

5253
export type Payload =

database-bridge-lambda/run.ts

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { handler } from "./src";
2-
import { AppSyncResolverEvent } from "aws-lambda/trigger/appsync-resolver";
3-
import { createDatabaseTunnel } from "../shared/database/local/databaseTunnel";
2+
import { createDatabaseTunnel } from "shared/database/local/databaseTunnel";
43
import prompts from "prompts";
5-
import { DatabaseOperation } from "../shared/graphql/operations";
6-
import { getYourEmail } from "../shared/local/yourEmail";
7-
import { CreateItemInput, EditItemInput } from "../shared/graphql/graphql";
4+
import { DatabaseOperation } from "shared/graphql/operations";
5+
import { getYourEmail } from "shared/local/yourEmail";
6+
import { CreateItemInput, EditItemInput } from "shared/graphql/graphql";
7+
import { IMAGING_REQUEST_ITEM_TYPE } from "shared/octopusImaging";
88

99
(async () => {
1010
const baseInput = {
1111
identity: { resolverContext: { userEmail: await getYourEmail() } },
12+
request: { headers: { referrer: "LOCAL-TESTING" }, domainName: null },
1213
};
1314

14-
const sampleInputs: Partial<Record<DatabaseOperation, unknown>> = {
15+
const sampleInputs = {
1516
listItems: { pinboardId: "63206" },
1617
searchMentionableUsers: { prefix: "a" },
1718
claimItem: { itemId: "1667" },
@@ -35,7 +36,7 @@ import { CreateItemInput, EditItemInput } from "../shared/graphql/graphql";
3536
deleteItem: { itemId: "2352" },
3637
getMyUser: {},
3738
visitTourStep: { tourStepId: "testing" },
38-
};
39+
} satisfies Partial<Record<DatabaseOperation, unknown>>;
3940

4041
await createDatabaseTunnel();
4142

@@ -48,14 +49,34 @@ import { CreateItemInput, EditItemInput } from "../shared/graphql/graphql";
4849
type: "select",
4950
name: "inputPayload",
5051
message: "Operation?",
51-
choices: Object.entries(sampleInputs).map(([operation, sampleInput]) => ({
52-
title: operation,
53-
value: {
54-
...baseInput,
55-
arguments: sampleInput,
56-
info: { fieldName: operation },
57-
} as AppSyncResolverEvent<unknown, unknown>,
58-
})),
52+
choices: [
53+
...Object.entries(sampleInputs).map(([operation, sampleInput]) => ({
54+
title: operation,
55+
value: {
56+
...baseInput,
57+
arguments: sampleInput,
58+
info: { fieldName: operation },
59+
},
60+
})),
61+
{
62+
title: "Digital Imaging Order (via octopus)",
63+
value: {
64+
...baseInput,
65+
info: { fieldName: "createItem" },
66+
arguments: {
67+
input: {
68+
...sampleInputs.createItem.input,
69+
type: IMAGING_REQUEST_ITEM_TYPE,
70+
payload: {
71+
requestType: "Cut out",
72+
embeddableUrl:
73+
"https://media.test.dev-gutools.co.uk/images/24733ea386c7fcb37496c55cc86e8f1468b9dfcf",
74+
},
75+
},
76+
},
77+
},
78+
},
79+
],
5980
});
6081

6182
console.log(JSON.stringify(await handler(inputPayload), null, 2));
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import {
2+
pinboardConfigPromiseGetter,
3+
pinboardSecretPromiseGetter,
4+
STAGE,
5+
} from "shared/awsIntegration";
6+
import fetch from "node-fetch";
7+
import { ItemWithParsedPayload } from "client/src/types/ItemWithParsedPayload";
8+
9+
const paramStorePrefix = `octopus-imaging/${STAGE}`;
10+
const octopusImagingBaseUrl = pinboardConfigPromiseGetter(
11+
`${paramStorePrefix}/base-url`
12+
);
13+
const octopusImagingApiKey = pinboardSecretPromiseGetter(
14+
`${paramStorePrefix}/api-key`
15+
);
16+
const octopusImagingApiSecret = pinboardSecretPromiseGetter(
17+
`${paramStorePrefix}/api-secret`
18+
);
19+
20+
export const performImagingRequest = async (
21+
item: ItemWithParsedPayload,
22+
{ embeddableUrl, requestType }: Record<string, unknown>
23+
): Promise<string> => {
24+
const gridId = (embeddableUrl as string).split("/").pop();
25+
if (!gridId) {
26+
throw new Error(`Couldn't extract grid ID from payload: ${item.payload}`);
27+
}
28+
const imagingRequestBody = {
29+
workflowId: item.pinboardId,
30+
pinboardItemId: item.id,
31+
gridId,
32+
lastUser: item.userEmail,
33+
notes: item.message, //TODO check for 256 max (probably limit in UI too)
34+
requestType,
35+
sectionId: 0, // FIXME this shouldn't be a requirement of the API surely
36+
};
37+
console.log("Performing imaging request", imagingRequestBody);
38+
39+
const response = await fetch(await octopusImagingBaseUrl, {
40+
method: "PUT",
41+
headers: {
42+
"Content-Type": "application/json",
43+
"x-api-key": await octopusImagingApiKey,
44+
"x-api-secret": await octopusImagingApiSecret,
45+
},
46+
body: JSON.stringify(imagingRequestBody),
47+
});
48+
49+
if (!response.ok) {
50+
const errorMessage = `Octopus Imaging request failed: ${response.status} ${response.statusText}`;
51+
console.error(errorMessage, await response.text());
52+
throw Error(errorMessage);
53+
}
54+
55+
const body = await response.json();
56+
57+
console.log("Imaging request successful", body);
58+
59+
return body.id;
60+
};

database-bridge-lambda/src/sql/Item.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import {
88
} from "shared/graphql/graphql";
99
import { Sql } from "shared/database/types";
1010
import { Range } from "shared/types/grafanaType";
11+
import { performImagingRequest } from "../imagingRequestCallout";
12+
import { IMAGING_REQUEST_ITEM_TYPE } from "shared/octopusImaging";
13+
import { ItemWithParsedPayload } from "client/src/types/ItemWithParsedPayload";
1114

1215
const fragmentIndividualMentionsToMentionHandles = (
1316
sql: Sql,
@@ -61,10 +64,37 @@ export const createItem = async (
6164
args: { input: CreateItemInput },
6265
userEmail: string
6366
) =>
64-
sql`
67+
sql.begin(async (sql) => {
68+
const insertResult = (await sql`
6569
INSERT INTO "Item" ${sql({ userEmail, ...args.input })}
6670
RETURNING ${fragmentItemFields(sql, userEmail)}
67-
`.then((rows) => rows[0]);
71+
`.then((rows) => rows[0])) as ItemWithParsedPayload;
72+
if (
73+
insertResult.type === IMAGING_REQUEST_ITEM_TYPE &&
74+
insertResult.payload
75+
) {
76+
// if this throws, the SQL transaction should be rolled back
77+
const octopusOrderId = await performImagingRequest(
78+
insertResult,
79+
insertResult.payload
80+
);
81+
82+
const augmentedPayload = {
83+
...insertResult.payload,
84+
octopusOrderId,
85+
};
86+
87+
await sql`UPDATE "Item" SET payload = ${JSON.stringify(
88+
augmentedPayload
89+
)}`;
90+
91+
return {
92+
...insertResult,
93+
payload: augmentedPayload,
94+
};
95+
}
96+
return insertResult;
97+
});
6898

6999
export const editItem = async (
70100
sql: Sql,

0 commit comments

Comments
 (0)