Skip to content

Commit b53d2d5

Browse files
committed
more commands
1 parent bde0e6b commit b53d2d5

28 files changed

+1010
-277
lines changed

.vscode/launch.json

+18
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,24 @@
1010
"request": "attach",
1111
"skipFiles": ["<node_internals>/**"],
1212
"type": "node"
13+
},
14+
{
15+
"name": "Debug Jest Tests",
16+
"type": "node",
17+
"request": "launch",
18+
"env": {
19+
// "DEBUG": "trace:*"
20+
},
21+
"runtimeArgs": [
22+
"--inspect-brk",
23+
"${workspaceRoot}/rank/node_modules/.bin/jest",
24+
"--runInBand"
25+
// "--testPathPattern=src/ranker/ranker.test.ts"
26+
],
27+
"cwd": "${workspaceFolder}/rank",
28+
"console": "integratedTerminal",
29+
"internalConsoleOptions": "neverOpen",
30+
"port": 9229
1331
}
1432
]
1533
}

deploy/src/app.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ const app = new cdk.App();
88

99
new DiscordStack(app, "discord", {
1010
// Yes these are not really a "secret" but it's a string that I don't want to store in the repo
11-
domain: discordSecretsJson.domain,
11+
discordDomain: discordSecretsJson.discordDomain,
12+
leaderboardDomain: discordSecretsJson.leaderboardDomain,
1213
discordAppId: discordSecretsJson["discord-app-id"],
1314
publicKey: discordSecretsJson["discord-public-key"],
14-
leaderboardApi: discordSecretsJson["leaderboard-api"],
1515
env: {
1616
region: process.env.CDK_DEFAULT_REGION,
1717
account: process.env.CDK_DEFAULT_ACCOUNT,

deploy/src/stack/discord.ts

+182-34
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import * as targets from "aws-cdk-lib/aws-route53-targets";
1515
import * as route53 from "aws-cdk-lib/aws-route53";
1616

1717
export interface DiscordProps extends cdk.StackProps {
18-
readonly domain: [string, string] | string;
19-
readonly leaderboardApi: string;
18+
readonly discordDomain: [string, string] | string;
19+
readonly leaderboardDomain: [string, string] | string;
2020
readonly discordAppId: string;
2121
readonly publicKey: string;
2222
}
@@ -25,7 +25,13 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
2525

2626
export class DiscordStack extends cdk.Stack {
2727
constructor(scope: cdk.App, id: string, props: DiscordProps) {
28-
const { domain, publicKey, discordAppId, leaderboardApi, ...rest } = props;
28+
const {
29+
discordDomain,
30+
leaderboardDomain,
31+
publicKey,
32+
discordAppId,
33+
...rest
34+
} = props;
2935
super(scope, id, rest);
3036

3137
// DynamoDB tables
@@ -36,6 +42,13 @@ export class DiscordStack extends cdk.Stack {
3642
},
3743
tableClass: dynamodb.TableClass.STANDARD,
3844
});
45+
const currentBoardTable = new dynamodb.Table(this, "CurrentBoard", {
46+
partitionKey: {
47+
name: "context",
48+
type: dynamodb.AttributeType.STRING,
49+
},
50+
tableClass: dynamodb.TableClass.STANDARD,
51+
});
3952
const rankBoardTable = new dynamodb.Table(this, "boards", {
4053
partitionKey: { name: "Name", type: dynamodb.AttributeType.STRING },
4154
tableClass: dynamodb.TableClass.STANDARD,
@@ -102,55 +115,97 @@ export class DiscordStack extends cdk.Stack {
102115
);
103116

104117
const discordHandler = new lambda.Function(this, "discordLambda", {
105-
runtime: lambda.Runtime.PROVIDED,
118+
runtime: lambda.Runtime.NODEJS_14_X,
106119
code: lambda.Code.fromAsset(
107120
path.join(__dirname, "../../../.layers/discord")
108121
),
109122
handler: "index.handler",
110123
timeout: cdk.Duration.seconds(10),
111124
memorySize: 256,
112-
layers: [node16Layer],
113125
environment: {
114126
PUBLIC_KEY: publicKey,
115127
STATIC_IMAGE_URL: `https://${staticAssetBucket.bucketName}.s3.amazonaws.com`,
116128
MINIMUM_LOG_LEVEL: "INFO",
117129
TABLE_NAME_MINECRAFT_PLAYER: minecraftPlayerTable.tableName,
130+
TABLE_NAME_CURRENT_BOARD: currentBoardTable.tableName,
118131
TABLE_NAME_RANKER_BOARDS: rankBoardTable.tableName,
119132
TABLE_NAME_RANKER_SCORES: rankScoresTable.tableName,
120133
TABLE_NAME_RANKER_NODES: rankNodesTable.tableName,
121134
TABLE_NAME_RANKER_LEADERBOARDS: rankLeaderboardsTable.tableName,
122-
CURRENT_LEADERBOARD: "potato",
123-
LEADERBOARD_BASE: leaderboardApi,
124135
DISCORD_DEFERRED_MESSAGE_TOPIC_ARN: deferredMessageTopic.topicArn,
125136
},
126137
});
127138

128139
minecraftPlayerTable.grantReadWriteData(discordHandler);
140+
currentBoardTable.grantReadWriteData(discordHandler);
129141
rankBoardTable.grantReadWriteData(discordHandler);
130142
rankScoresTable.grantReadWriteData(discordHandler);
131143
rankNodesTable.grantReadWriteData(discordHandler);
132144
rankLeaderboardsTable.grantReadWriteData(discordHandler);
133145
deferredMessageTopic.grantPublish(discordHandler);
134146

147+
const playersHandler = new lambda.Function(this, "playerHandler", {
148+
runtime: lambda.Runtime.NODEJS_14_X,
149+
code: lambda.Code.fromAsset(
150+
path.join(__dirname, "../../../.layers/players")
151+
),
152+
handler: "index.handler",
153+
timeout: cdk.Duration.seconds(10),
154+
memorySize: 256,
155+
environment: {
156+
MINIMUM_LOG_LEVEL: "INFO",
157+
TABLE_NAME_RANKER_BOARDS: rankBoardTable.tableName,
158+
TABLE_NAME_RANKER_SCORES: rankScoresTable.tableName,
159+
TABLE_NAME_RANKER_NODES: rankNodesTable.tableName,
160+
TABLE_NAME_RANKER_LEADERBOARDS: rankLeaderboardsTable.tableName,
161+
},
162+
});
163+
164+
rankBoardTable.grantReadData(playersHandler);
165+
rankScoresTable.grantReadData(playersHandler);
166+
rankNodesTable.grantReadData(playersHandler);
167+
rankLeaderboardsTable.grantReadData(playersHandler);
168+
169+
const leaderboardHandler = new lambda.Function(this, "leaderboardHandler", {
170+
runtime: lambda.Runtime.NODEJS_14_X,
171+
code: lambda.Code.fromAsset(
172+
path.join(__dirname, "../../../.layers/leaderboard")
173+
),
174+
handler: "index.handler",
175+
timeout: cdk.Duration.seconds(10),
176+
memorySize: 256,
177+
environment: {
178+
MINIMUM_LOG_LEVEL: "INFO",
179+
TABLE_NAME_RANKER_BOARDS: rankBoardTable.tableName,
180+
TABLE_NAME_RANKER_SCORES: rankScoresTable.tableName,
181+
TABLE_NAME_RANKER_NODES: rankNodesTable.tableName,
182+
TABLE_NAME_RANKER_LEADERBOARDS: rankLeaderboardsTable.tableName,
183+
},
184+
});
185+
186+
rankBoardTable.grantReadData(leaderboardHandler);
187+
rankScoresTable.grantReadData(leaderboardHandler);
188+
rankNodesTable.grantReadData(leaderboardHandler);
189+
rankLeaderboardsTable.grantReadData(leaderboardHandler);
190+
135191
const deferredMessageHandler = new lambda.Function(
136192
this,
137193
"deferredMessageHandler",
138194
{
139-
runtime: lambda.Runtime.PROVIDED,
195+
runtime: lambda.Runtime.NODEJS_14_X,
140196
code: lambda.Code.fromAsset(
141197
path.join(__dirname, "../../../.layers/deferred")
142198
),
143199
handler: "index.handler",
144200
timeout: cdk.Duration.seconds(30),
145201
memorySize: 256,
146-
layers: [node16Layer],
147202
environment: {
148203
DISCORD_APPLICATION_ID: discordAppId,
149204
STATIC_IMAGE_URL: `https://${staticAssetBucket.bucketName}.s3.amazonaws.com`,
150205
MINIMUM_LOG_LEVEL: "INFO",
151206
CURRENT_LEADERBOARD: "potato",
152-
LEADERBOARD_BASE: leaderboardApi,
153207
TABLE_NAME_MINECRAFT_PLAYER: minecraftPlayerTable.tableName,
208+
TABLE_NAME_CURRENT_BOARD: currentBoardTable.tableName,
154209
TABLE_NAME_RANKER_BOARDS: rankBoardTable.tableName,
155210
TABLE_NAME_RANKER_SCORES: rankScoresTable.tableName,
156211
TABLE_NAME_RANKER_NODES: rankNodesTable.tableName,
@@ -163,21 +218,22 @@ export class DiscordStack extends cdk.Stack {
163218
],
164219
}
165220
);
221+
166222
minecraftPlayerTable.grantReadWriteData(deferredMessageHandler);
167-
rankBoardTable.grantReadData(deferredMessageHandler);
223+
currentBoardTable.grantReadWriteData(deferredMessageHandler);
224+
rankBoardTable.grantReadWriteData(deferredMessageHandler);
168225
rankScoresTable.grantReadData(deferredMessageHandler);
169226
rankNodesTable.grantReadData(deferredMessageHandler);
170227
rankLeaderboardsTable.grantReadData(deferredMessageHandler);
171228

172229
const scoreHandler = new lambda.Function(this, "scoreHandler", {
173-
runtime: lambda.Runtime.PROVIDED,
230+
runtime: lambda.Runtime.NODEJS_14_X,
174231
code: lambda.Code.fromAsset(
175232
path.join(__dirname, "../../../.layers/ingest")
176233
),
177234
handler: "index.handler",
178235
timeout: cdk.Duration.seconds(10),
179236
memorySize: 256,
180-
layers: [node16Layer],
181237
environment: {
182238
TABLE_NAME_RANKER_BOARDS: rankBoardTable.tableName,
183239
TABLE_NAME_RANKER_SCORES: rankScoresTable.tableName,
@@ -193,40 +249,132 @@ export class DiscordStack extends cdk.Stack {
193249
rankLeaderboardsTable.grantReadWriteData(scoreHandler);
194250

195251
// Domain
196-
const domains = domain instanceof Array ? domain : [domain];
197-
const domainName = domains.join(".");
198-
const hostedZone = route53.HostedZone.fromLookup(this, "HostedZone", {
199-
domainName: domain.length === 2 ? domains[1] : domains[0],
200-
});
252+
const discordDomains =
253+
discordDomain instanceof Array ? discordDomain : [discordDomain];
254+
const discordDomainName = discordDomains.join(".");
255+
const discordHostedZone = route53.HostedZone.fromLookup(
256+
this,
257+
"HostedZone",
258+
{
259+
domainName:
260+
discordDomains.length === 2 ? discordDomains[1] : discordDomains[0],
261+
}
262+
);
201263

202-
const certificate = new acm.DnsValidatedCertificate(this, "certificate", {
203-
domainName,
204-
hostedZone: hostedZone,
205-
region: props.env?.region,
206-
});
264+
const leaderboardDomains =
265+
leaderboardDomain instanceof Array
266+
? leaderboardDomain
267+
: [leaderboardDomain];
268+
const leaderboardDomainName = leaderboardDomains.join(".");
269+
const leaderboardHostedZone = route53.HostedZone.fromLookup(
270+
this,
271+
"LeaderboardHostedZone",
272+
{
273+
domainName:
274+
leaderboardDomains.length === 2
275+
? leaderboardDomains[1]
276+
: leaderboardDomains[0],
277+
}
278+
);
207279

208-
const api = new apigateway.RestApi(this, "discordApi", {
280+
const discordCertificate = new acm.DnsValidatedCertificate(
281+
this,
282+
"certificate",
283+
{
284+
domainName: discordDomainName,
285+
hostedZone: discordHostedZone,
286+
region: props.env?.region,
287+
}
288+
);
289+
290+
const discordApi = new apigateway.RestApi(this, "discordApi", {
209291
restApiName: "Discord Service",
210292
description: "Discord callback",
211293
domainName: {
212-
domainName,
213-
certificate,
294+
domainName: discordDomainName,
295+
certificate: discordCertificate,
296+
},
297+
});
298+
299+
const leaderboardCertificate = new acm.DnsValidatedCertificate(
300+
this,
301+
"leaderboardCertificate",
302+
{
303+
domainName: leaderboardDomainName,
304+
hostedZone: leaderboardHostedZone,
305+
region: props.env?.region,
306+
}
307+
);
308+
309+
const leaderboardApi = new apigateway.RestApi(this, "leaderboardApi", {
310+
restApiName: "Leaderboard Service",
311+
description: "Leaderboard callback",
312+
domainName: {
313+
domainName: leaderboardDomainName,
314+
certificate: leaderboardCertificate,
214315
},
215316
});
317+
const defaultUsagePlan = leaderboardApi.addUsagePlan("defaultUsagePlan", {
318+
name: "Default Usage Plan",
319+
description: "Default Usage Plan",
320+
});
321+
const defaultApiKey = leaderboardApi.addApiKey("defaultKey", {
322+
description: "Default leaderboard API key",
323+
});
324+
defaultUsagePlan.addApiKey(defaultApiKey);
325+
defaultUsagePlan.addApiStage({
326+
stage: leaderboardApi.deploymentStage,
327+
});
216328

217329
const discordIntegration = new apigateway.LambdaIntegration(discordHandler);
218-
const resource = api.root.addResource("discord");
219-
resource.addMethod("POST", discordIntegration);
330+
const discordResource = discordApi.root.addResource("discord");
331+
discordResource.addMethod("POST", discordIntegration);
332+
const experienceResource = leaderboardApi.root.addResource("{experience}");
333+
const leaderboardResource = experienceResource.addResource("leaderboard");
334+
leaderboardResource.addMethod(
335+
"GET",
336+
new apigateway.LambdaIntegration(leaderboardHandler),
337+
{
338+
apiKeyRequired: true,
339+
}
340+
);
341+
342+
const playersResource = experienceResource.addResource("players");
343+
playersResource.addMethod(
344+
"GET",
345+
new apigateway.LambdaIntegration(playersHandler),
346+
{
347+
apiKeyRequired: true,
348+
}
349+
);
220350

221351
new route53.ARecord(this, "ipv4-record", {
222-
zone: hostedZone,
223-
recordName: domainName,
224-
target: route53.RecordTarget.fromAlias(new targets.ApiGateway(api)),
352+
zone: discordHostedZone,
353+
recordName: discordDomainName,
354+
target: route53.RecordTarget.fromAlias(
355+
new targets.ApiGateway(discordApi)
356+
),
225357
});
226358
new route53.AaaaRecord(this, "ipv6-record", {
227-
zone: hostedZone,
228-
recordName: domainName,
229-
target: route53.RecordTarget.fromAlias(new targets.ApiGateway(api)),
359+
zone: discordHostedZone,
360+
recordName: discordDomainName,
361+
target: route53.RecordTarget.fromAlias(
362+
new targets.ApiGateway(discordApi)
363+
),
364+
});
365+
new route53.ARecord(this, "ipv4-record-leaderboard", {
366+
zone: leaderboardHostedZone,
367+
recordName: leaderboardDomainName,
368+
target: route53.RecordTarget.fromAlias(
369+
new targets.ApiGateway(leaderboardApi)
370+
),
371+
});
372+
new route53.AaaaRecord(this, "ipv6-record-leaderboard", {
373+
zone: leaderboardHostedZone,
374+
recordName: leaderboardDomainName,
375+
target: route53.RecordTarget.fromAlias(
376+
new targets.ApiGateway(leaderboardApi)
377+
),
230378
});
231379

232380
new cdk.CfnOutput(this, "snsScoreTopicArn", {

fakeScores.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/bin/sh
22
aws sns publish \
33
--message-group-id 'potato' \
4-
--message '{"boardName":"potato","scores":[{"playerId":"dd3cb41b-6428-45b8-81e6-82d57f5a5508","score":[146]},{"playerId":"b5ad4c7d-f43d-4c96-ae98-637d43f8f88d","score":[31]}]}' \
4+
--message '{"boardName":"potato","scoreDeltas":[{"playerId":"dd3cb41b-6428-45b8-81e6-82d57f5a5508","score":[-3]},{"playerId":"7b53dd5f-33b3-4815-be6b-d40d831b365c","score":[31]}]}' \
55
--topic-arn "arn:aws:sns:us-west-2:163871723185:score-fifo-topic-2.fifo"

0 commit comments

Comments
 (0)