diff --git a/client/src/app.tsx b/client/src/app.tsx index 74278775..e4f7ef0c 100644 --- a/client/src/app.tsx +++ b/client/src/app.tsx @@ -22,7 +22,7 @@ import { gqlSetWebPushSubscriptionForUser, } from "../gql"; import { Item, MyUser, User } from "shared/graphql/graphql"; -import { ItemWithParsedPayload } from "./types/ItemWithParsedPayload"; +import { ItemWithParsedPayload } from "shared/types/ItemWithParsedPayload"; import { HiddenIFrameForServiceWorker } from "./pushNotificationPreferences"; import { GlobalStateProvider } from "./globalState"; import { Floaty } from "./floaty"; diff --git a/client/src/push-notifications/serviceWorker.ts b/client/src/push-notifications/serviceWorker.ts index 7758f89f..062c000a 100644 --- a/client/src/push-notifications/serviceWorker.ts +++ b/client/src/push-notifications/serviceWorker.ts @@ -1,10 +1,10 @@ -import { ItemWithParsedPayload } from "../types/ItemWithParsedPayload"; +import { ItemWithParsedPayload } from "shared/types/ItemWithParsedPayload"; import { EXPAND_PINBOARD_QUERY_PARAM, OPEN_PINBOARD_QUERY_PARAM, PINBOARD_ITEM_ID_QUERY_PARAM, -} from "../../../shared/constants"; -import { extractNameFromEmail } from "../../../shared/util"; +} from "shared/constants"; +import { extractNameFromEmail } from "shared/util"; const toolsDomain = self.location.hostname.replace("pinboard.", ""); diff --git a/client/src/types/ItemWithParsedPayload.ts b/client/src/types/ItemWithParsedPayload.ts deleted file mode 100644 index 8829feca..00000000 --- a/client/src/types/ItemWithParsedPayload.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Item } from "../../../shared/graphql/graphql"; - -export type ItemWithParsedPayload = Item & { - payload: Record | null | undefined; -}; diff --git a/database-bridge-lambda/src/imagingRequestCallout.ts b/database-bridge-lambda/src/imagingRequestCallout.ts new file mode 100644 index 00000000..e5b1ddb2 --- /dev/null +++ b/database-bridge-lambda/src/imagingRequestCallout.ts @@ -0,0 +1,43 @@ +import fetch from "node-fetch"; +import { getEnvironmentVariableOrThrow } from "shared/environmentVariables"; +import { ItemWithParsedPayload } from "shared/types/ItemWithParsedPayload"; + +export const performImagingRequest = async (item: ItemWithParsedPayload) => { + const gridId = (item.payload?.embeddableUrl as string)?.split("/").pop(); + if (!gridId) { + throw new Error(`Couldn't extract grid ID from payload: ${item.payload}`); + } + const imagingRequestBody = { + workflowId: item.pinboardId, + pinboardItemId: item.id, + lastUser: item.userEmail, + notes: item.message, //TODO check for 256 max (probably limit in UI too) + requestType: item.payload?.requestType, // TODO tighten this up + gridId, + // composerId: TODO lookup somehow + // pubDate TODO scheduled launch vs some date field in workflow - what's worse wrong date or no date? + // section TODO lookup somehow + // story group name TODO (synced from InCopy most likely, if available) + }; + console.log("Performing imaging request", imagingRequestBody); + + process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; // self-signed cert on imaging server, which fails SSL check, so ignore + const response = await fetch( + `https://${getEnvironmentVariableOrThrow( + "octopusImagingApiVpcEndpoint" + )}/v1/rgbimageorder`, + { + // note this travels via vpc endpoint, via VPN to specific machine(s) on office network, no need to auth at this point + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(imagingRequestBody), + } + ); + if (!response.ok) { + throw new Error( + `Imaging request failed: ${response.status} ${response.statusText}` + ); + } +}; diff --git a/database-bridge-lambda/src/sql/Item.ts b/database-bridge-lambda/src/sql/Item.ts index efe977a7..89b66278 100644 --- a/database-bridge-lambda/src/sql/Item.ts +++ b/database-bridge-lambda/src/sql/Item.ts @@ -3,9 +3,12 @@ import { EditItemInput, Item, PinboardIdWithClaimCounts, -} from "../../../shared/graphql/graphql"; -import { Sql } from "../../../shared/database/types"; -import { Range } from "../../../shared/types/grafanaType"; +} from "shared/graphql/graphql"; +import { Sql } from "shared/database/types"; +import { Range } from "shared/types/grafanaType"; +import { performImagingRequest } from "../imagingRequestCallout"; +import { IMAGING_REQUEST_ITEM_TYPE } from "shared/octopusImaging"; +import { ItemWithParsedPayload } from "shared/types/ItemWithParsedPayload"; const fragmentIndividualMentionsToMentionHandles = ( sql: Sql, @@ -59,10 +62,20 @@ export const createItem = async ( args: { input: CreateItemInput }, userEmail: string ) => - sql` + sql.begin(async (sql) => { + const insertResult = (await sql` INSERT INTO "Item" ${sql({ userEmail, ...args.input })} RETURNING ${fragmentItemFields(sql, userEmail)} - `.then((rows) => rows[0]); + `.then((rows) => rows[0])) as ItemWithParsedPayload; + if ( + insertResult.type === IMAGING_REQUEST_ITEM_TYPE && + insertResult.payload + ) { + // if this throws, the SQL transaction should be rolled back + await performImagingRequest(insertResult); + } + return insertResult; + }); export const editItem = async ( sql: Sql, diff --git a/shared/types/ItemWithParsedPayload.ts b/shared/types/ItemWithParsedPayload.ts new file mode 100644 index 00000000..7c39b1d0 --- /dev/null +++ b/shared/types/ItemWithParsedPayload.ts @@ -0,0 +1,5 @@ +import { Item } from "../graphql/graphql"; + +export type ItemWithParsedPayload = Omit & { + payload: Record | null | undefined; +};