1 | import {setTimeout} from "node:timers/promises"; |
2 | import {SignatureV4} from "@aws-sdk/signature-v4"; |
3 | import {HttpRequest} from "@aws-sdk/protocol-http"; |
4 | import {defaultProvider} from "@aws-sdk/credential-provider-node"; |
5 | import {URL} from "url"; |
6 | import {Hash} from "@aws-sdk/hash-node"; |
7 | |
8 | const {APIURL, APIREGION} = process.env; |
9 | |
10 | export const getIAMAuthRequest = async (APIURL, region, data) => { |
11 | const url = new URL(APIURL); |
12 | const httpRequest = new HttpRequest({ |
13 | body: JSON.stringify(data), |
14 | headers: { |
15 | "content-type": "application/json; charset=UTF-8", |
16 | "content-encoding": "amz-1.0", |
17 | "accept": "application/json, text/javascript", |
18 | host: url.hostname, |
19 | }, |
20 | hostname: url.hostname, |
21 | method: "POST", |
22 | path: url.pathname, |
23 | protocol: url.protocol, |
24 | query: {}, |
25 | }); |
26 | |
27 | const signer = new SignatureV4({ |
28 | credentials: defaultProvider(), |
29 | service: "appsync", |
30 | region: region, |
31 | sha256: Hash.bind(null, "sha256"), |
32 | }); |
33 | const req = await signer.sign(httpRequest); |
34 | return req; |
35 | }; |
36 | |
37 | const sendQuery = async (query, operationName, variables) => { |
38 | const req = await getIAMAuthRequest(APIURL, APIREGION, {query, operationName, variables}); |
39 | const res = await fetch(`${req.protocol}//${req.hostname}${req.path}`, { |
40 | method: req.method, |
41 | body: req.body, |
42 | headers: req.headers, |
43 | }); |
44 | |
45 | if (!res.ok) { |
46 | throw new Error("Failed"); |
47 | } |
48 | const resJson = await res.json(); |
49 | if (resJson.errors) { |
50 | throw new Error(resJson); |
51 | } |
52 | return resJson; |
53 | }; |
54 | |
55 | const getPaginatedResults = async (fn) => { |
56 | const EMPTY = Symbol("empty"); |
57 | const res = []; |
58 | for await (const lf of (async function*() { |
59 | let NextMarker = EMPTY; |
60 | while (NextMarker || NextMarker === EMPTY) { |
61 | const {marker, results} = await fn(NextMarker !== EMPTY ? NextMarker : undefined); |
62 | |
63 | yield* results; |
64 | NextMarker = marker; |
65 | } |
66 | })()) { |
67 | res.push(lf); |
68 | } |
69 | |
70 | return res; |
71 | }; |
72 | |
73 | const randomComment = () => { |
74 | const possibilities = [ |
75 | "Comment Ipsum!", |
76 | "Hell yeah!", |
77 | "Great!", |
78 | "Good!", |
79 | "Yeah!", |
80 | "Commenting!", |
81 | "Hmmm....", |
82 | "Interesting!", |
83 | ]; |
84 | return possibilities[Math.floor(Math.random() * possibilities.length)]; |
85 | }; |
86 | |
87 | const randomPost = () => { |
88 | const possibilities = [ |
89 | "Lorem Ipsum", |
90 | "Dolor set ameth", |
91 | "Hello World!", |
92 | "Something", |
93 | "Something interesting", |
94 | "What's up?", |
95 | "All good!", |
96 | ]; |
97 | return possibilities[Math.floor(Math.random() * possibilities.length)]; |
98 | }; |
99 | |
100 | const updateData = async () => { |
101 | const userIds = JSON.parse(process.env.USERIDS); |
102 | await Promise.all(userIds.map(async (userId) => { |
103 | const myPostsQuery = await sendQuery(` |
104 | query MyQuery($userId: ID!) { |
105 | user(id: $userId) { |
106 | name |
107 | posts(limit: 20) { |
108 | posts { |
109 | id |
110 | } |
111 | } |
112 | } |
113 | } |
114 | `, "MyQuery", {userId}); |
115 | |
116 | const posts = myPostsQuery.data.user.posts.posts; |
117 | await Promise.all(posts.map(async ({id}, index) => { |
118 | if (Math.random() < (0.05 * (1 / 2 ** index))) { |
119 | console.log(`${myPostsQuery.data.user.name}: Adding a new comment to a user's own post`); |
120 | await sendQuery(` |
121 | mutation MyMutation($userId: ID!, $postId: ID!, $text: String!) { |
122 | addComment(userId: $userId, postId: $postId, text: $text) { |
123 | id |
124 | } |
125 | } |
126 | `, "MyMutation", {userId, postId: id, text: randomComment()}); |
127 | } |
128 | })); |
129 | await setTimeout(2000); |
130 | |
131 | if (Math.random() < 0.1) { |
132 | console.log(`${myPostsQuery.data.user.name}: Adding a new post`); |
133 | await sendQuery(` |
134 | mutation MyMutation($userId: ID!, $text: String!) { |
135 | createPost(userId: $userId, text: $text) { |
136 | id |
137 | } |
138 | } |
139 | `, "MyMutation", {userId, text: randomPost()}); |
140 | } |
141 | await setTimeout(2000); |
142 | |
143 | const friendsPostsQuery = await sendQuery(` |
144 | query MyQuery($userId: ID!) { |
145 | user(id: $userId) { |
146 | friends { |
147 | users { |
148 | name |
149 | posts(limit: 10) { |
150 | posts { |
151 | id |
152 | } |
153 | } |
154 | } |
155 | } |
156 | } |
157 | } |
158 | `, "MyQuery", {userId}); |
159 | await Promise.all(friendsPostsQuery.data.user.friends.users.map(async (friend) => { |
160 | await Promise.all(friend.posts.posts.map(async ({id}, index) => { |
161 | if (Math.random() < (0.05 * (1 / 2 ** index))) { |
162 | await setTimeout(100); |
163 | console.log(`${myPostsQuery.data.user.name} => ${friend.name}: Adding a comment to another user's post`); |
164 | await sendQuery(` |
165 | mutation MyMutation($userId: ID!, $postId: ID!, $text: String!) { |
166 | addComment(userId: $userId, postId: $postId, text: $text) { |
167 | id |
168 | } |
169 | } |
170 | `, "MyMutation", {userId, postId: id, text: randomComment()}); |
171 | } |
172 | })); |
173 | })); |
174 | })); |
175 | }; |
176 | |
177 | export const handler = async (event, context) => { |
178 | await updateData(); |
179 | while(context.getRemainingTimeInMillis() > 15000) { |
180 | console.log(context.getRemainingTimeInMillis()); |
181 | await setTimeout(10000); |
182 | await updateData(); |
183 | } |
184 | }; |
185 | |
186 | |
187 | |