wirejs-deploy-amplify-basic 0.0.121 → 0.0.122-async-api
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/amplify-backend-assets/backend.ts +12 -0
- package/amplify-backend-assets/functions/api/handler.ts +70 -23
- package/amplify-hosting-assets/compute/default/package.json +1 -1
- package/dist/resources/background-job.d.ts +36 -0
- package/dist/resources/background-job.js +59 -0
- package/package.json +3 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
1
2
|
import {
|
|
2
3
|
defineBackend,
|
|
3
4
|
} from '@aws-amplify/backend';
|
|
@@ -6,6 +7,7 @@ import { FunctionUrlAuthType } from 'aws-cdk-lib/aws-lambda';
|
|
|
6
7
|
import { Bucket, BlockPublicAccess } from 'aws-cdk-lib/aws-s3';
|
|
7
8
|
import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb';
|
|
8
9
|
import { PolicyStatement } from 'aws-cdk-lib/aws-iam';
|
|
10
|
+
|
|
9
11
|
import { TableDefinition, Resource, indexName } from 'wirejs-resources';
|
|
10
12
|
import { TableIndexes } from './constructs/table-indexes';
|
|
11
13
|
import { RealtimeService } from './constructs/realtime-service';
|
|
@@ -14,12 +16,19 @@ import { auth } from './auth/resource';
|
|
|
14
16
|
|
|
15
17
|
// @ts-ignore
|
|
16
18
|
import generatedResources from './generated-resources';
|
|
19
|
+
|
|
17
20
|
const generated: any[] = generatedResources;
|
|
18
21
|
|
|
19
22
|
const APP_ID = process.env.AWS_APP_ID ?? process.env.PWD?.replace(/[^a-zA-Z0-9-_]/g, '_');
|
|
20
23
|
const BRANCH_ID = process.env.AWS_BRANCH ?? process.env.USER ?? 'anonymous';
|
|
21
24
|
const TABLE_NAME_PREFIX = `${APP_ID}-${BRANCH_ID}-`;
|
|
22
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Simple secret ID used for backend jobs to authenticate themselves with
|
|
28
|
+
* each other. I.e., for API to trigger background jobs.
|
|
29
|
+
*/
|
|
30
|
+
const SELF_INVOCATION_ID = randomUUID();
|
|
31
|
+
|
|
23
32
|
/**
|
|
24
33
|
* Amplify resources
|
|
25
34
|
*/
|
|
@@ -177,6 +186,9 @@ backend.api.addEnvironment(
|
|
|
177
186
|
backend.api.addEnvironment(
|
|
178
187
|
'TABLE_NAME_PREFIX', TABLE_NAME_PREFIX
|
|
179
188
|
);
|
|
189
|
+
backend.api.addEnvironment(
|
|
190
|
+
'SELF_INVOCATION_ID', SELF_INVOCATION_ID
|
|
191
|
+
);
|
|
180
192
|
|
|
181
193
|
|
|
182
194
|
/**
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { env } from 'process';
|
|
2
2
|
import { LambdaFunctionURLHandler, APIGatewayProxyEventV2 } from 'aws-lambda';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
BackgroundJob,
|
|
5
|
+
CookieJar,
|
|
6
|
+
Context,
|
|
7
|
+
requiresContext,
|
|
8
|
+
} from 'wirejs-resources';
|
|
4
9
|
|
|
5
10
|
// @ts-ignore
|
|
6
11
|
import * as api from '../../../api/index';
|
|
@@ -44,33 +49,75 @@ async function callApiMethod(api: any, call: any, context: any) {
|
|
|
44
49
|
}
|
|
45
50
|
}
|
|
46
51
|
|
|
52
|
+
function isBackgroundJob(o: any): o is BackgroundJob<any> {
|
|
53
|
+
return typeof o === 'object' && typeof o.start === 'function';
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
export const handler: LambdaFunctionURLHandler = async (event, context) => {
|
|
48
57
|
const calls = JSON.parse(event.body!);
|
|
49
|
-
const responses = [];
|
|
50
|
-
const wjsContext = createContext(event);
|
|
51
58
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
if (Array.isArray(calls)) {
|
|
60
|
+
const responses = [];
|
|
61
|
+
const wjsContext = createContext(event);
|
|
62
|
+
for (const call of calls) {
|
|
63
|
+
console.log('handling API call', call);
|
|
64
|
+
responses.push(await callApiMethod(api, call, wjsContext));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log('setting cookies', wjsContext.cookies.getSetCookies());
|
|
68
|
+
|
|
69
|
+
const cookies: string[] = [];
|
|
70
|
+
for (const cookie of wjsContext.cookies.getSetCookies()) {
|
|
71
|
+
const cookieOptions = [];
|
|
72
|
+
if (cookie.maxAge) cookieOptions.push(`Max-Age=${cookie.maxAge}`);
|
|
73
|
+
if (cookie.httpOnly) cookieOptions.push('HttpOnly');
|
|
74
|
+
if (cookie.secure) cookieOptions.push('Secure');
|
|
75
|
+
cookies.push(`${cookie.name}=${cookie.value}; ${cookieOptions.join('; ')}`);
|
|
76
|
+
}
|
|
56
77
|
|
|
57
|
-
|
|
78
|
+
return {
|
|
79
|
+
statusCode: 200,
|
|
80
|
+
cookies,
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
83
|
+
},
|
|
84
|
+
body: JSON.stringify(responses)
|
|
85
|
+
}
|
|
86
|
+
} else if (typeof calls === 'object' && calls.async) {
|
|
87
|
+
console.log('handling async execution', calls);
|
|
58
88
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
89
|
+
if (!calls.absoluteId) {
|
|
90
|
+
return {
|
|
91
|
+
statusCode: 400,
|
|
92
|
+
body: JSON.stringify({ error: 'Missing absoluteId' })
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (calls.selfInvocationId !== env.SELF_INVOCATION_ID) {
|
|
97
|
+
return {
|
|
98
|
+
statusCode: 403,
|
|
99
|
+
body: JSON.stringify({ error: 'Forbidden' })
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const job = (BackgroundJob as any).registeredJobs[calls.absoluteId];
|
|
104
|
+
if (!job || !isBackgroundJob(job)) {
|
|
105
|
+
return {
|
|
106
|
+
statusCode: 404,
|
|
107
|
+
body: JSON.stringify({ error: 'Background job not found' })
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
await job.start(...calls.args)
|
|
67
112
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
113
|
+
return {
|
|
114
|
+
statusCode: 200,
|
|
115
|
+
body: JSON.stringify({ message: 'Background job complete' })
|
|
116
|
+
};
|
|
117
|
+
} else {
|
|
118
|
+
return {
|
|
119
|
+
statusCode: 400,
|
|
120
|
+
body: JSON.stringify({ error: 'Invalid request format' })
|
|
121
|
+
};
|
|
75
122
|
}
|
|
76
123
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Resource, BackgroundJob as BaseBackgroundJob } from 'wirejs-resources';
|
|
2
|
+
export type BackgroundJobDefinition = {
|
|
3
|
+
absoluteId: string;
|
|
4
|
+
timeoutSeconds: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* A function that will be executed in the background.
|
|
8
|
+
*
|
|
9
|
+
* Intended for longer-running work that will continue after an initial API response
|
|
10
|
+
* to the client.
|
|
11
|
+
*
|
|
12
|
+
* ## WARNINGS
|
|
13
|
+
*
|
|
14
|
+
* 1. It may execute in a completely different environment that the one that invoked it.
|
|
15
|
+
* DO NOT depend on global variables, closures, or other process state that is not lazily
|
|
16
|
+
* created or constructed in-place by the script.
|
|
17
|
+
* 2. Fire-and-forget behavior is undefined. As soon as the `Promise` returned by your
|
|
18
|
+
* function resolves, the execution environment is free to terminate the process.
|
|
19
|
+
* 3. In local development, **timeouts are not enforced.** (Yet.)
|
|
20
|
+
*/
|
|
21
|
+
export declare class BackgroundJob<T extends (...args: any) => Promise<void>> extends Resource implements Omit<BaseBackgroundJob<T>, '#private'> {
|
|
22
|
+
#private;
|
|
23
|
+
static registeredJobs: Record<string, BackgroundJob<any>>;
|
|
24
|
+
constructor(scope: Resource | string, id: string, options: {
|
|
25
|
+
handler: T;
|
|
26
|
+
timeoutSeconds?: number;
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Serializes parameter data and schedules background execution.
|
|
30
|
+
*
|
|
31
|
+
* **NOTE:** This resolves once the job is scheduled; *not when it completes!*
|
|
32
|
+
*
|
|
33
|
+
* @param data
|
|
34
|
+
*/
|
|
35
|
+
start(...args: Parameters<T>): Promise<void>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { env } from 'process';
|
|
2
|
+
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
|
|
3
|
+
import { Resource, } from 'wirejs-resources';
|
|
4
|
+
import { addResource } from '../resource-collector.js';
|
|
5
|
+
/**
|
|
6
|
+
* A function that will be executed in the background.
|
|
7
|
+
*
|
|
8
|
+
* Intended for longer-running work that will continue after an initial API response
|
|
9
|
+
* to the client.
|
|
10
|
+
*
|
|
11
|
+
* ## WARNINGS
|
|
12
|
+
*
|
|
13
|
+
* 1. It may execute in a completely different environment that the one that invoked it.
|
|
14
|
+
* DO NOT depend on global variables, closures, or other process state that is not lazily
|
|
15
|
+
* created or constructed in-place by the script.
|
|
16
|
+
* 2. Fire-and-forget behavior is undefined. As soon as the `Promise` returned by your
|
|
17
|
+
* function resolves, the execution environment is free to terminate the process.
|
|
18
|
+
* 3. In local development, **timeouts are not enforced.** (Yet.)
|
|
19
|
+
*/
|
|
20
|
+
export class BackgroundJob extends Resource {
|
|
21
|
+
#handler;
|
|
22
|
+
#timeoutSeconds;
|
|
23
|
+
static registeredJobs = {};
|
|
24
|
+
constructor(scope, id, options) {
|
|
25
|
+
super(scope, id);
|
|
26
|
+
this.#handler = options.handler;
|
|
27
|
+
if (options.timeoutSeconds && (options.timeoutSeconds < 1 || options.timeoutSeconds > 900)) {
|
|
28
|
+
throw new Error('timeoutSeconds must be between 1 and 900 seconds.');
|
|
29
|
+
}
|
|
30
|
+
this.#timeoutSeconds = options.timeoutSeconds ?? 900;
|
|
31
|
+
addResource('BackgroundJob', {
|
|
32
|
+
absoluteId: this.absoluteId,
|
|
33
|
+
timeoutSeconds: this.#timeoutSeconds,
|
|
34
|
+
});
|
|
35
|
+
BackgroundJob.registeredJobs[this.absoluteId] = this;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Serializes parameter data and schedules background execution.
|
|
39
|
+
*
|
|
40
|
+
* **NOTE:** This resolves once the job is scheduled; *not when it completes!*
|
|
41
|
+
*
|
|
42
|
+
* @param data
|
|
43
|
+
*/
|
|
44
|
+
async start(...args) {
|
|
45
|
+
const client = new LambdaClient();
|
|
46
|
+
const command = new InvokeCommand({
|
|
47
|
+
FunctionName: env.AWS_LAMBDA_FUNCTION_NAME,
|
|
48
|
+
InvocationType: 'Event',
|
|
49
|
+
Payload: JSON.stringify({
|
|
50
|
+
body: {
|
|
51
|
+
absoluteId: this.absoluteId,
|
|
52
|
+
selfInvocationId: env.SELF_INVOCATION_ID,
|
|
53
|
+
args,
|
|
54
|
+
},
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
57
|
+
await client.send(command);
|
|
58
|
+
}
|
|
59
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wirejs-deploy-amplify-basic",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.122-async-api",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"@aws-sdk/client-dynamodb": "^3.774.0",
|
|
33
33
|
"@aws-sdk/client-s3": "^3.738.0",
|
|
34
34
|
"@aws-sdk/credential-provider-node": "^3.806.0",
|
|
35
|
+
"@aws-sdk/client-lambda": "3.821.0",
|
|
35
36
|
"@aws-sdk/lib-dynamodb": "^3.778.0",
|
|
36
37
|
"@aws-sdk/protocol-http": "^3.370.0",
|
|
37
38
|
"@aws-sdk/signature-v4": "^3.370.0",
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
"recursive-copy": "^2.0.14",
|
|
42
43
|
"rimraf": "^6.0.1",
|
|
43
44
|
"wirejs-dom": "^1.0.41",
|
|
44
|
-
"wirejs-resources": "^0.1.
|
|
45
|
+
"wirejs-resources": "^0.1.90-async-api"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
48
|
"@aws-amplify/backend": "^1.14.0",
|