Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Octopus 'Imaging Orders' #226

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
34 changes: 4 additions & 30 deletions bootstrapping-lambda/local/index.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,5 @@
import { AppSync } from "@aws-sdk/client-appsync";
import {
pinboardConfigPromiseGetter,
STAGE,
standardAwsConfig,
} from "../../shared/awsIntegration";
import { APP } from "../../shared/constants";
import { ENVIRONMENT_VARIABLE_KEYS } from "../../shared/environmentVariables";
import { initEnvVars } from "./initLocalEnvVars";

pinboardConfigPromiseGetter("sentryDSN").then(
(sentryDSN) => (process.env[ENVIRONMENT_VARIABLE_KEYS.sentryDSN] = sentryDSN)
);

new AppSync(standardAwsConfig)
.listGraphqlApis({
maxResults: 25, // TODO consider implementing paging (for absolute future proofing)
})
.then((_) =>
_.graphqlApis?.find(
(api) => api.tags?.["Stage"] === STAGE && api.tags?.["App"] === APP
)
)
.then((appSyncAPI) => {
if (!appSyncAPI) {
throw Error("could not find AppSync API");
}
process.env[ENVIRONMENT_VARIABLE_KEYS.graphqlEndpoint] =
appSyncAPI.uris?.["GRAPHQL"];

import("../src/server"); // actually start the server, once the environment variable is set
});
initEnvVars().then(() => {
import("../src/server"); // actually start the server, once the environment variables are set
});
32 changes: 32 additions & 0 deletions bootstrapping-lambda/local/initLocalEnvVars.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { AppSync } from "@aws-sdk/client-appsync";
import {
pinboardConfigPromiseGetter,
STAGE,
standardAwsConfig,
} from "shared/awsIntegration";
import { APP } from "shared/constants";
import { ENVIRONMENT_VARIABLE_KEYS } from "shared/environmentVariables";

export const initEnvVars = (): Promise<void> => {
pinboardConfigPromiseGetter("sentryDSN").then(
(sentryDSN) =>
(process.env[ENVIRONMENT_VARIABLE_KEYS.sentryDSN] = sentryDSN)
);

return new AppSync(standardAwsConfig)
.listGraphqlApis({
maxResults: 25, // TODO consider implementing paging (for absolute future proofing)
})
.then((_) =>
_.graphqlApis?.find(
(api) => api.tags?.["Stage"] === STAGE && api.tags?.["App"] === APP
)
)
.then((appSyncAPI) => {
if (!appSyncAPI) {
throw Error("could not find AppSync API");
}
process.env[ENVIRONMENT_VARIABLE_KEYS.graphqlEndpoint] =
appSyncAPI.uris?.["GRAPHQL"];
});
};
58 changes: 58 additions & 0 deletions bootstrapping-lambda/local/simulateOctopus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import prompts from "prompts";
import { handleImagingCallFromOctopus } from "../src/octopusImagingHandler";
import { S3 } from "@aws-sdk/client-s3";
import { standardAwsConfig } from "shared/awsIntegration";
import { IMAGINE_REQUEST_TYPES } from "shared/octopusImaging";
import { getYourEmail } from "shared/local/yourEmail";
import { initEnvVars } from "./initLocalEnvVars";

initEnvVars().then(async () => {
const s3 = new S3(standardAwsConfig);

const newGridId = "TBC"; //TODO get media id of modified image

// noinspection InfiniteLoopJS
while (
// eslint-disable-next-line no-constant-condition
true
) {
const { args } = await prompts(
{
type: "select",
name: "args",
message: "Operation?",
choices: [
{
title: "ImagingOrderPickedUp",
value: {},
},
{
title: "GeneratePreSignedGridUploadUrl",
value: {
originalGridId: "223636f8d305a77e60fb2aa4525effbd66a7560d",
filename: "Historic_yachts_22.JPG",
newGridId,
requestType: IMAGINE_REQUEST_TYPES[0],
},
},
{
title: "ImagingOrderCompleted",
value: {
newGridId,
},
},
],
},
{ onCancel: () => process.exit() }
);

console.log(
(await handleImagingCallFromOctopus(s3, {
userEmail: await getYourEmail(),
workflowId: "65518",
pinboardItemId: "3458",
...args,
})) || "DONE"
);
}
});
1 change: 1 addition & 0 deletions bootstrapping-lambda/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"test": "jest --coverage --forceExit",
"bundle": "esbuild src/server.ts --bundle --minify --outfile=dist/index.js --platform=node --external:aws-sdk --external:@aws-sdk",
"watch": "ts-node-dev --respawn local/index.ts",
"simulate-octopus-call": "ts-node-dev --respawn local/simulateOctopus.ts",
"build": "run-p --print-label type-check bundle"
},
"devDependencies": {
Expand Down
24 changes: 24 additions & 0 deletions bootstrapping-lambda/src/appSyncRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { CreateItemInput } from "shared/graphql/graphql";
import { AppSyncConfig } from "shared/appSyncConfig";
import { itemReturnFields } from "shared/itemReturnFields";
import fetch from "node-fetch";

export const appSyncCreateItem = (
config: AppSyncConfig,
input: CreateItemInput
) =>
fetch(config.graphqlEndpoint, {
method: "POST",
headers: {
authorization: config.authToken,
"content-type": "application/json",
},
body: JSON.stringify({
operationName: "SendMessage",
variables: {
input,
},
// the client listening to the subscription requires various fields to be returned, hence reuse of itemReturnFields
query: `mutation SendMessage($input: CreateItemInput!) { createItem(input: $input) { ${itemReturnFields} } }`,
}),
});
79 changes: 79 additions & 0 deletions bootstrapping-lambda/src/octopusImagingHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { S3 } from "@aws-sdk/client-s3";
import { generateAppSyncConfig } from "./generateAppSyncConfig";
import { generatePreSignedGridUploadUrl } from "shared/grid";
import { CreateItemInput } from "shared/graphql/graphql";
import { appSyncCreateItem } from "./appSyncRequest";
import {
IMAGING_COMPLETED_ITEM_TYPE,
IMAGING_PICKED_UP_ITEM_TYPE,
} from "shared/octopusImaging";

interface CommonArgs {
/* the email of the user who did the imaging work */
userEmail: string; //TODO probably receive the desktop auth token instead (to verify on pinboard side)
/* the id of the pinboard itself */
workflowId: string;
/* the itemId of the original request item in pinboard */
pinboardItemId: string;
}

type ImagingOrderPickedUp = CommonArgs;
interface GeneratePreSignedGridUploadUrl extends CommonArgs {
originalGridId: string;
filename: string;
/* SHA1 hash of the file content */
newGridId: string;
/* e.g. cutout, composite, etc */
requestType: string;
}
interface ImagingOrderCompleted extends CommonArgs {
/* SHA1 hash of the file content */
newGridId: string;
}

export type ImagingCallFromOctopus =
| ImagingOrderPickedUp
| GeneratePreSignedGridUploadUrl
| ImagingOrderCompleted;

export const isImagingCallFromOctopus = (
detail: any

Check warning on line 40 in bootstrapping-lambda/src/octopusImagingHandler.ts

View workflow job for this annotation

GitHub Actions / CI

Unexpected any. Specify a different type
): detail is ImagingCallFromOctopus => !!detail && "pinboardItemId" in detail;

export const handleImagingCallFromOctopus = async (
s3: S3,
detail: ImagingCallFromOctopus
): Promise<string | void> => {
console.log("Handling imaging call from Octopus", detail);
if ("originalGridId" in detail) {
return await generatePreSignedGridUploadUrl(detail);
}
const appSyncConfig = await generateAppSyncConfig(detail.userEmail, s3);
const appSyncCreateItemInput: CreateItemInput = {
pinboardId: detail.workflowId,
relatedItemId: detail.pinboardItemId,
claimable: false,
mentions: null, //TODO consider @ing the original requester for these updates
message: null,
groupMentions: null,
...("newGridId" in detail
? {
type: IMAGING_COMPLETED_ITEM_TYPE,
payload: null, //TODO make use of the newGridId (perform lookup to grid)
}
: {
type: IMAGING_PICKED_UP_ITEM_TYPE,
payload: null,
}),
};
return appSyncCreateItem(appSyncConfig, appSyncCreateItemInput).then(
async (response) => {
console.log(await response.text());
if (!response.ok) {
throw new Error(
`Failed to create item: ${response.status} ${response.statusText}`
);
}
}
);
};
19 changes: 17 additions & 2 deletions bootstrapping-lambda/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ import {
} from "./middleware/auth-middleware";

import { getMetrics } from "./reporting/reportingServiceClient";
import {
handleImagingCallFromOctopus,
ImagingCallFromOctopus,
isImagingCallFromOctopus,
} from "./octopusImagingHandler";

const s3 = new S3(standardAwsConfig);

Expand Down Expand Up @@ -163,7 +168,17 @@ if (IS_RUNNING_LOCALLY) {
server.listen(PORT, () => console.log(`Listening on port ${PORT}`));
} else {
exports.handler = (
event: lambda.APIGatewayProxyEvent,
payload: lambda.APIGatewayProxyEvent | ImagingCallFromOctopus | undefined,
context: lambda.Context
) => proxy(createServer(server), event, context);
) => {
console.log(payload);
if (payload && "queryStringParameters" in payload) {
// from API Gateway
return proxy(createServer(server), payload, context);
} else if (isImagingCallFromOctopus(payload)) {
return handleImagingCallFromOctopus(s3, payload);
}
console.error("unexpected payload", payload);
throw new Error("Not implemented");
};
}
Loading