Skip to content

Commit 8489db9

Browse files
committed
make bootstrapping-lambda accept direct invocations (from octopus-api)
1 parent 9ce1c2e commit 8489db9

File tree

15 files changed

+333
-105
lines changed

15 files changed

+333
-105
lines changed
Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,5 @@
1-
import { AppSync } from "@aws-sdk/client-appsync";
2-
import {
3-
pinboardConfigPromiseGetter,
4-
STAGE,
5-
standardAwsConfig,
6-
} from "../../shared/awsIntegration";
7-
import { APP } from "../../shared/constants";
8-
import { ENVIRONMENT_VARIABLE_KEYS } from "../../shared/environmentVariables";
1+
import { initEnvVars } from "./initLocalEnvVars";
92

10-
pinboardConfigPromiseGetter("sentryDSN").then(
11-
(sentryDSN) => (process.env[ENVIRONMENT_VARIABLE_KEYS.sentryDSN] = sentryDSN)
12-
);
13-
14-
new AppSync(standardAwsConfig)
15-
.listGraphqlApis({
16-
maxResults: 25, // TODO consider implementing paging (for absolute future proofing)
17-
})
18-
.then((_) =>
19-
_.graphqlApis?.find(
20-
(api) => api.tags?.["Stage"] === STAGE && api.tags?.["App"] === APP
21-
)
22-
)
23-
.then((appSyncAPI) => {
24-
if (!appSyncAPI) {
25-
throw Error("could not find AppSync API");
26-
}
27-
process.env[ENVIRONMENT_VARIABLE_KEYS.graphqlEndpoint] =
28-
appSyncAPI.uris?.["GRAPHQL"];
29-
30-
import("../src/server"); // actually start the server, once the environment variable is set
31-
});
3+
initEnvVars().then(() => {
4+
import("../src/server"); // actually start the server, once the environment variables are set
5+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { AppSync } from "@aws-sdk/client-appsync";
2+
import {
3+
pinboardConfigPromiseGetter,
4+
STAGE,
5+
standardAwsConfig,
6+
} from "shared/awsIntegration";
7+
import { APP } from "shared/constants";
8+
import { ENVIRONMENT_VARIABLE_KEYS } from "shared/environmentVariables";
9+
10+
export const initEnvVars = (): Promise<void> => {
11+
pinboardConfigPromiseGetter("sentryDSN").then(
12+
(sentryDSN) =>
13+
(process.env[ENVIRONMENT_VARIABLE_KEYS.sentryDSN] = sentryDSN)
14+
);
15+
16+
return new AppSync(standardAwsConfig)
17+
.listGraphqlApis({
18+
maxResults: 25, // TODO consider implementing paging (for absolute future proofing)
19+
})
20+
.then((_) =>
21+
_.graphqlApis?.find(
22+
(api) => api.tags?.["Stage"] === STAGE && api.tags?.["App"] === APP
23+
)
24+
)
25+
.then((appSyncAPI) => {
26+
if (!appSyncAPI) {
27+
throw Error("could not find AppSync API");
28+
}
29+
process.env[ENVIRONMENT_VARIABLE_KEYS.graphqlEndpoint] =
30+
appSyncAPI.uris?.["GRAPHQL"];
31+
});
32+
};
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import prompts from "prompts";
2+
import { handleImagingCallFromOctopus } from "../src/octopusImagingHandler";
3+
import { S3 } from "@aws-sdk/client-s3";
4+
import { standardAwsConfig } from "shared/awsIntegration";
5+
import { IMAGINE_REQUEST_TYPES } from "shared/octopusImaging";
6+
import { getYourEmail } from "shared/local/yourEmail";
7+
import { initEnvVars } from "./initLocalEnvVars";
8+
9+
initEnvVars().then(async () => {
10+
const s3 = new S3(standardAwsConfig);
11+
12+
const newGridId = "TBC"; //TODO get media id of modified image
13+
14+
// noinspection InfiniteLoopJS
15+
while (
16+
// eslint-disable-next-line no-constant-condition
17+
true
18+
) {
19+
const { args } = await prompts(
20+
{
21+
type: "select",
22+
name: "args",
23+
message: "Operation?",
24+
choices: [
25+
{
26+
title: "ImagingOrderPickedUp",
27+
value: {},
28+
},
29+
{
30+
title: "GeneratePreSignedGridUploadUrl",
31+
value: {
32+
originalGridId: "223636f8d305a77e60fb2aa4525effbd66a7560d",
33+
filename: "Historic_yachts_22.JPG",
34+
newGridId,
35+
requestType: IMAGINE_REQUEST_TYPES[0],
36+
},
37+
},
38+
{
39+
title: "ImagingOrderCompleted",
40+
value: {
41+
newGridId,
42+
},
43+
},
44+
],
45+
},
46+
{ onCancel: () => process.exit() }
47+
);
48+
49+
console.log(
50+
(await handleImagingCallFromOctopus(s3, {
51+
userEmail: await getYourEmail(),
52+
workflowId: "65518",
53+
pinboardItemId: "3458",
54+
...args,
55+
})) || "DONE"
56+
);
57+
}
58+
});

bootstrapping-lambda/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"test": "jest --coverage --forceExit",
1010
"bundle": "esbuild src/server.ts --bundle --minify --outfile=dist/index.js --platform=node --external:aws-sdk --external:@aws-sdk",
1111
"watch": "ts-node-dev --respawn local/index.ts",
12+
"simulate-octopus-call": "ts-node-dev --respawn local/simulateOctopus.ts",
1213
"build": "run-p --print-label type-check bundle"
1314
},
1415
"devDependencies": {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { CreateItemInput } from "shared/graphql/graphql";
2+
import { AppSyncConfig } from "shared/appSyncConfig";
3+
import { itemReturnFields } from "shared/itemReturnFields";
4+
import fetch from "node-fetch";
5+
6+
export const appSyncCreateItem = (
7+
config: AppSyncConfig,
8+
input: CreateItemInput
9+
) =>
10+
fetch(config.graphqlEndpoint, {
11+
method: "POST",
12+
headers: {
13+
authorization: config.authToken,
14+
"content-type": "application/json",
15+
},
16+
body: JSON.stringify({
17+
operationName: "SendMessage",
18+
variables: {
19+
input,
20+
},
21+
// the client listening to the subscription requires various fields to be returned, hence reuse of itemReturnFields
22+
query: `mutation SendMessage($input: CreateItemInput!) { createItem(input: $input) { ${itemReturnFields} } }`,
23+
}),
24+
});
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { S3 } from "@aws-sdk/client-s3";
2+
import { generateAppSyncConfig } from "./generateAppSyncConfig";
3+
import { generatePreSignedGridUploadUrl } from "shared/grid";
4+
import { CreateItemInput } from "shared/graphql/graphql";
5+
import { appSyncCreateItem } from "./appSyncRequest";
6+
import {
7+
IMAGING_COMPLETED_ITEM_TYPE,
8+
IMAGING_PICKED_UP_ITEM_TYPE,
9+
} from "shared/octopusImaging";
10+
11+
interface CommonArgs {
12+
/* the email of the user who did the imaging work */
13+
userEmail: string; //TODO probably receive the desktop auth token instead (to verify on pinboard side)
14+
/* the id of the pinboard itself */
15+
workflowId: string;
16+
/* the itemId of the original request item in pinboard */
17+
pinboardItemId: string;
18+
}
19+
20+
type ImagingOrderPickedUp = CommonArgs;
21+
interface GeneratePreSignedGridUploadUrl extends CommonArgs {
22+
originalGridId: string;
23+
filename: string;
24+
/* SHA1 hash of the file content */
25+
newGridId: string;
26+
/* e.g. cutout, composite, etc */
27+
requestType: string;
28+
}
29+
interface ImagingOrderCompleted extends CommonArgs {
30+
/* SHA1 hash of the file content */
31+
newGridId: string;
32+
}
33+
34+
export type ImagingCallFromOctopus =
35+
| ImagingOrderPickedUp
36+
| GeneratePreSignedGridUploadUrl
37+
| ImagingOrderCompleted;
38+
39+
export const isImagingCallFromOctopus = (
40+
detail: any
41+
): detail is ImagingCallFromOctopus => !!detail && "pinboardItemId" in detail;
42+
43+
export const handleImagingCallFromOctopus = async (
44+
s3: S3,
45+
detail: ImagingCallFromOctopus
46+
): Promise<string | void> => {
47+
console.log("Handling imaging call from Octopus", detail);
48+
if ("originalGridId" in detail) {
49+
return await generatePreSignedGridUploadUrl(detail);
50+
}
51+
const appSyncConfig = await generateAppSyncConfig(detail.userEmail, s3);
52+
const appSyncCreateItemInput: CreateItemInput = {
53+
pinboardId: detail.workflowId,
54+
relatedItemId: detail.pinboardItemId,
55+
claimable: false,
56+
mentions: null, //TODO consider @ing the original requester for these updates
57+
message: null,
58+
groupMentions: null,
59+
...("newGridId" in detail
60+
? {
61+
type: IMAGING_COMPLETED_ITEM_TYPE,
62+
payload: null, //TODO make use of the newGridId (perform lookup to grid)
63+
}
64+
: {
65+
type: IMAGING_PICKED_UP_ITEM_TYPE,
66+
payload: null,
67+
}),
68+
};
69+
return appSyncCreateItem(appSyncConfig, appSyncCreateItemInput).then(
70+
async (response) => {
71+
console.log(await response.text());
72+
if (!response.ok) {
73+
throw new Error(
74+
`Failed to create item: ${response.status} ${response.statusText}`
75+
);
76+
}
77+
}
78+
);
79+
};

bootstrapping-lambda/src/server.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ import {
2525
} from "./middleware/auth-middleware";
2626

2727
import { getMetrics } from "./reporting/reportingServiceClient";
28+
import {
29+
handleImagingCallFromOctopus,
30+
ImagingCallFromOctopus,
31+
isImagingCallFromOctopus,
32+
} from "./octopusImagingHandler";
2833

2934
const s3 = new S3(standardAwsConfig);
3035

@@ -163,7 +168,17 @@ if (IS_RUNNING_LOCALLY) {
163168
server.listen(PORT, () => console.log(`Listening on port ${PORT}`));
164169
} else {
165170
exports.handler = (
166-
event: lambda.APIGatewayProxyEvent,
171+
payload: lambda.APIGatewayProxyEvent | ImagingCallFromOctopus | undefined,
167172
context: lambda.Context
168-
) => proxy(createServer(server), event, context);
173+
) => {
174+
console.log(payload);
175+
if (payload && "queryStringParameters" in payload) {
176+
// from API Gateway
177+
return proxy(createServer(server), payload, context);
178+
} else if (isImagingCallFromOctopus(payload)) {
179+
return handleImagingCallFromOctopus(s3, payload);
180+
}
181+
console.error("unexpected payload", payload);
182+
throw new Error("Not implemented");
183+
};
169184
}

0 commit comments

Comments
 (0)