Skip to content

Commit df7f133

Browse files
committed
add query parameter for upcoming events only
1 parent a9556f4 commit df7f133

File tree

5 files changed

+63
-9
lines changed

5 files changed

+63
-9
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"fastify-plugin": "^4.5.1",
4646
"jsonwebtoken": "^9.0.2",
4747
"jwks-rsa": "^3.1.0",
48+
"moment-timezone": "^0.5.45",
4849
"zod": "^3.23.8",
4950
"zod-to-json-schema": "^3.23.2",
5051
"zod-validation-error": "^3.3.1"

src/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type ConfigType = {
1313
type GenericConfigType = {
1414
DynamoTableName: string;
1515
ConfigSecretName: string;
16+
UpcomingEventThresholdSeconds: number;
1617
};
1718

1819
type EnvironmentConfigType = {
@@ -22,6 +23,7 @@ type EnvironmentConfigType = {
2223
const genericConfig: GenericConfigType = {
2324
DynamoTableName: "infra-events-api-records",
2425
ConfigSecretName: "infra-events-api-config",
26+
UpcomingEventThresholdSeconds: 1800 // 30 mins
2527
} as const;
2628

2729
const environmentConfig: EnvironmentConfigType = {

src/plugins/errorHandler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ const errorHandlerPlugin = fp(async (fastify) => {
2828
);
2929
} else if (err instanceof Error) {
3030
request.log.error(
31-
{ errName: err.name },
31+
{ errName: err.name, errMessage: err.message },
3232
"Native unhandled error: response sent to client.",
3333
);
3434
} else {
35-
request.log.error("Native unhandled error: response sent to client.");
35+
request.log.error(`Native unhandled error: response sent to client`);
3636
}
3737
if (!finalErr) {
3838
finalErr = new InternalServerError();

src/routes/events.ts

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FastifyPluginAsync } from "fastify";
1+
import { FastifyPluginAsync, FastifyRequest } from "fastify";
22
import { AppRoles } from "../roles.js";
33
import { z } from "zod";
44
import { zodToJsonSchema } from "zod-to-json-schema";
@@ -8,10 +8,11 @@ import {
88
PutItemCommand,
99
ScanCommand,
1010
} from "@aws-sdk/client-dynamodb";
11+
import { genericConfig } from "../config.js";
1112
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
12-
import config from "../config.js";
1313
import { DatabaseFetchError, DatabaseInsertError } from "../errors/index.js";
1414
import { randomUUID } from "crypto";
15+
import moment from 'moment-timezone';
1516

1617
// POST
1718

@@ -37,8 +38,10 @@ const requestBodySchema = baseBodySchema
3738
message: "repeats is required when repeatEnds is defined",
3839
});
3940

41+
4042
type EventPostRequest = z.infer<typeof requestBodySchema>;
4143

44+
4245
const responseJsonSchema = zodToJsonSchema(
4346
z.object({
4447
id: z.string(),
@@ -50,6 +53,14 @@ const responseJsonSchema = zodToJsonSchema(
5053
const getResponseBodySchema = z.array(requestBodySchema);
5154
const getResponseJsonSchema = zodToJsonSchema(getResponseBodySchema);
5255

56+
57+
const getQueryParams = z.object({
58+
upcomingOnly: z.boolean().default(false),
59+
})
60+
type EventsGetQueryParams = z.infer<typeof getQueryParams>;
61+
const getQueryParamsJsonSchema = zodToJsonSchema(getResponseBodySchema);
62+
63+
5364
const dynamoClient = new DynamoDBClient({
5465
region: process.env.AWS_REGION || "us-east-1",
5566
});
@@ -75,7 +86,7 @@ const eventsPlugin: FastifyPluginAsync = async (fastify, _options) => {
7586
randomUUID().toString();
7687
await dynamoClient.send(
7788
new PutItemCommand({
78-
TableName: config.DYNAMO_TABLE_NAME,
89+
TableName: genericConfig.DynamoTableName,
7990
Item: marshall({
8091
...request.body,
8192
id: entryUUID,
@@ -97,20 +108,48 @@ const eventsPlugin: FastifyPluginAsync = async (fastify, _options) => {
97108
}
98109
},
99110
);
100-
fastify.get<{ Body: undefined }>(
111+
type EventsGetRequest = {Body: undefined, Querystring?: EventsGetQueryParams}
112+
fastify.get<EventsGetRequest>(
101113
"/",
102114
{
103115
schema: {
116+
querystring: {
117+
upcomingOnly: { type: 'boolean' },
118+
},
104119
response: { 200: getResponseJsonSchema },
105120
},
106121
},
107-
async (request, reply) => {
122+
async (request: FastifyRequest<EventsGetRequest>, reply) => {
123+
const upcomingOnly = request.query?.upcomingOnly || false;
108124
try {
109125
const response = await dynamoClient.send(
110-
new ScanCommand({ TableName: config.DYNAMO_TABLE_NAME }),
126+
new ScanCommand({ TableName: genericConfig.DynamoTableName }),
111127
);
112128
const items = response.Items?.map((item) => unmarshall(item));
113-
reply.send(getResponseBodySchema.parse(items));
129+
const currentTimeChicago = moment().tz("America/Chicago");
130+
let parsedItems = getResponseBodySchema.parse(items);
131+
if (upcomingOnly) {
132+
parsedItems = parsedItems.filter(item => {
133+
try {
134+
if (!item.repeatEnds || item.repeatEnds === 'never') {
135+
return true;
136+
}
137+
if (!item.repeats) {
138+
const end = item.end || item.start;
139+
const momentEnds = moment.tz(end, "America/Chicago")
140+
const diffTime = currentTimeChicago.diff(momentEnds);
141+
return Boolean(diffTime <= genericConfig.UpcomingEventThresholdSeconds)
142+
}
143+
const momentRepeatEnds = moment.tz(item.repeatEnds, "America/Chicago")
144+
const diffTime = currentTimeChicago.diff(momentRepeatEnds);
145+
return Boolean(diffTime <= genericConfig.UpcomingEventThresholdSeconds);
146+
} catch (e) {
147+
request.log.warn(`Could not compute upcoming event status for event ${item.title}!`)
148+
return false;
149+
}
150+
})
151+
}
152+
reply.send(parsedItems);
114153
} catch (e: unknown) {
115154
if (e instanceof Error) {
116155
request.log.error("Failed to get from DynamoDB: " + e.toString());

yarn.lock

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3653,6 +3653,18 @@ mnemonist@0.39.6:
36533653
dependencies:
36543654
obliterator "^2.0.1"
36553655

3656+
moment-timezone@^0.5.45:
3657+
version "0.5.45"
3658+
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.45.tgz#cb685acd56bac10e69d93c536366eb65aa6bcf5c"
3659+
integrity sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==
3660+
dependencies:
3661+
moment "^2.29.4"
3662+
3663+
moment@^2.29.4:
3664+
version "2.30.1"
3665+
resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"
3666+
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
3667+
36563668
ms@2.1.2:
36573669
version "2.1.2"
36583670
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"

0 commit comments

Comments
 (0)