Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/functions/attend-event/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { middyfy } from '@libs/lambda';
import schema from './schema';

import { MongoDB, validateToken, ensureRoles } from '../../util';
import type { UserDocument } from 'src/types';
import type { UserDocument } from '../../types';
import { RegistrationStatus } from '../../types';
import * as path from 'path';
import * as dotenv from 'dotenv';
dotenv.config({ path: path.resolve(process.cwd(), '.env') });
Expand Down Expand Up @@ -63,7 +64,7 @@ const attendEvent: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (ev
};
}

if (attendEvent.registration_status != 'checked_in') {
if (attendEvent.registration_status != RegistrationStatus.CHECKED_IN) {
return {
statusCode: 409,
body: JSON.stringify({
Expand Down
3 changes: 2 additions & 1 deletion src/functions/discord/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { middyfy } from '@libs/lambda';
import schema from './schema';

import { MongoDB, validateToken } from '../../util';
import { RegistrationStatus } from '../../types';
import * as discordAPI from '@libs/discord';

import * as path from 'path';
Expand Down Expand Up @@ -41,7 +42,7 @@ const discord: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (event)
const discordUser = await discordAPI.getDiscordUser(tokens.accessToken);
await discordAPI.updateDiscordMetadata(tokens.accessToken, user.first_name + ' ' + user.last_name, {
verified: new Date().toISOString(),
checked_in: user.registration_status == 'checked_in' ? 1 : 0,
checked_in: user.registration_status == RegistrationStatus.CHECKED_IN ? 1 : 0,
});

await users.updateOne(
Expand Down
14 changes: 9 additions & 5 deletions src/functions/teams/create/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ValidatedEventAPIGatewayProxyEvent } from '@libs/api-gateway';
import { middyfy } from '@libs/lambda';
import schema from './schema';
import { MongoDB, validateToken, teamInviteLogic } from '../../../util';
import { UserDocument, TeamDocument } from 'src/types';
import { UserDocument, TeamDocument, RegistrationStatus, TeamStatus, TeamRole } from '../../../types';
import { v4 as uuidv4 } from 'uuid';
import * as path from 'path';
import * as dotenv from 'dotenv';
Expand Down Expand Up @@ -75,7 +75,11 @@ const teamsCreate: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (ev
}

// Check if auth user has valid registration status for team creation
const validStatesForTeamCreation = ['registered', 'confirmation', 'coming'];
const validStatesForTeamCreation = [
RegistrationStatus.REGISTERED,
RegistrationStatus.CONFIRMATION,
RegistrationStatus.COMING,
];
if (!validStatesForTeamCreation.includes(authUser.registration_status)) {
return {
statusCode: 400,
Expand All @@ -87,7 +91,7 @@ const teamsCreate: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (ev
}

// Check if user already leads a team
if (authUser.team_info?.role === 'leader') {
if (authUser.team_info?.role === TeamRole.LEADER) {
return {
statusCode: 400,
body: JSON.stringify({
Expand Down Expand Up @@ -188,7 +192,7 @@ const teamsCreate: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (ev
team_id: teamId,
leader_email: event.body.auth_email.toLowerCase(),
members: [],
status: 'Active' as const,
status: TeamStatus.ACTIVE,
team_name: teamName,
created: new Date(),
updated: new Date(),
Expand All @@ -210,7 +214,7 @@ const teamsCreate: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (ev
confirmed_team: true,
team_info: {
team_id: teamId,
role: 'leader' as const,
role: TeamRole.LEADER,
pending_invites: [],
},
},
Expand Down
6 changes: 3 additions & 3 deletions src/functions/teams/join/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ValidatedEventAPIGatewayProxyEvent } from '@libs/api-gateway';
import { middyfy } from '@libs/lambda';
import schema from './schema';
import { MongoDB, validateToken } from '../../../util';
import { UserDocument, TeamDocument } from '../../../types';
import { UserDocument, TeamDocument, TeamStatus, TeamRole } from '../../../types';
import * as path from 'path';
import * as dotenv from 'dotenv';
dotenv.config({ path: path.resolve(process.cwd(), '.env') });
Expand Down Expand Up @@ -76,7 +76,7 @@ const teamsJoin: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (even
};
}

if (team.status !== 'Active') {
if (team.status !== TeamStatus.ACTIVE) {
return {
statusCode: 400,
body: JSON.stringify({
Expand Down Expand Up @@ -117,7 +117,7 @@ const teamsJoin: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (even
confirmed_team: true,
team_info: {
team_id: event.body.team_id,
role: 'member',
role: TeamRole.MEMBER,
pending_invites: remainingInvites,
},
},
Expand Down
4 changes: 2 additions & 2 deletions src/functions/teams/member-removal/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import schema from './schema';
import { MongoDB, validateToken } from '../../../util';
import * as path from 'path';
import * as dotenv from 'dotenv';
import { UserDocument, TeamDocument } from '../../../types';
import { UserDocument, TeamDocument, TeamStatus } from '../../../types';
dotenv.config({ path: path.resolve(process.cwd(), '.env') });

const teamsMemberRemoval: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (event) => {
Expand Down Expand Up @@ -52,7 +52,7 @@ const teamsMemberRemoval: ValidatedEventAPIGatewayProxyEvent<typeof schema> = as
}

// Check if team is active
if (team.status !== 'Active') {
if (team.status !== TeamStatus.ACTIVE) {
return {
statusCode: 400,
body: JSON.stringify({
Expand Down
3 changes: 2 additions & 1 deletion src/functions/teams/read/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import schema from './schema';

import { ensureRoles, MongoDB, validateToken } from '../../../util';
import type { UserDocument, TeamDocument } from '../../../types';
import { TeamStatus } from '../../../types';

import * as path from 'path';
import * as dotenv from 'dotenv';
Expand Down Expand Up @@ -98,7 +99,7 @@ const teamsRead: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (even
};
}

if (team.status !== 'Active') {
if (team.status !== TeamStatus.ACTIVE) {
return {
statusCode: 400,
body: JSON.stringify({
Expand Down
60 changes: 40 additions & 20 deletions src/functions/update/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import schema from './schema';
import { validateEmail } from '../../helper';

import { MongoDB, validateToken, ensureRoles } from '../../util';
import { RegistrationStatus } from '../../types';
import * as path from 'path';
import * as dotenv from 'dotenv';
import { Document, WithId } from 'mongodb';
Expand Down Expand Up @@ -94,7 +95,7 @@ const update: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (event)
}

// add registered_at time if status is updated
if (event.body.updates?.$set?.registration_status == 'registered')
if (event.body.updates?.$set?.registration_status == RegistrationStatus.REGISTERED)
event.body.updates.$set['registered_at'] = new Date().toISOString();

// call updates
Expand Down Expand Up @@ -150,49 +151,68 @@ interface Updates {
// 2. no alteration to fields such as: _id, password,

const registrationStatusGraph = {
unregistered: ['registered'],
registered: ['rejected', 'confirmation', 'waitlist'],
confirmation: ['coming', 'not_coming'],
rejected: ['checked_in'],
coming: ['not_coming', 'confirmed'],
not_coming: ['coming', 'waitlist'],
confirmed: ['checked_in'],
waitlist: ['checked_in'],
checked_in: [],
[RegistrationStatus.UNREGISTERED]: [RegistrationStatus.REGISTERED],
[RegistrationStatus.REGISTERED]: [
RegistrationStatus.REJECTED,
RegistrationStatus.CONFIRMATION,
RegistrationStatus.WAITLIST,
],
[RegistrationStatus.CONFIRMATION]: [RegistrationStatus.COMING, RegistrationStatus.NOT_COMING],
[RegistrationStatus.REJECTED]: [RegistrationStatus.CHECKED_IN],
[RegistrationStatus.COMING]: [RegistrationStatus.NOT_COMING, RegistrationStatus.CONFIRMED],
[RegistrationStatus.NOT_COMING]: [RegistrationStatus.COMING, RegistrationStatus.WAITLIST],
[RegistrationStatus.CONFIRMED]: [RegistrationStatus.CHECKED_IN],
[RegistrationStatus.WAITLIST]: [RegistrationStatus.CHECKED_IN],
[RegistrationStatus.CHECKED_IN]: [],
};

function isValidRegistrationStatusUpdate(current: string, goal: string): boolean {
function isValidRegistrationStatusUpdate(current: RegistrationStatus, goal: RegistrationStatus): boolean {
if (current in registrationStatusGraph) return registrationStatusGraph[current].includes(goal);
return false;
}

// return true or false whether the proposed update is valid or not

function validateUpdates(updates: Updates, registrationStatus?: string, user?: WithId<Document>): boolean | string {
function validateUpdates(
updates: Updates,
registrationStatus?: RegistrationStatus,
user?: WithId<Document>
): boolean | string {
const setUpdates = updates.$set;
if (setUpdates) {
if ('registration_status' in setUpdates) {
const currentDate = new Date();
const goalStatus = setUpdates.registration_status as string;

if (goalStatus === 'checked_in') {
if (goalStatus === RegistrationStatus.CHECKED_IN) {
if (currentDate > CHECK_IN_CUT_OFF)
return `Registration is closed. The cutoff date was ${CHECK_IN_CUT_OFF.toLocaleString()}.`;
}

const atleastRegistered = ['confirmed', 'waitlist', 'registered', 'coming'].includes(
registrationStatus || 'unregistered'
);
if (goalStatus === 'checked_in' && atleastRegistered) {
if (currentDate >= CHECK_IN_START_DATE || registrationStatus === 'confirmed') return true;
const atleastRegistered = [
RegistrationStatus.CONFIRMED,
RegistrationStatus.WAITLIST,
RegistrationStatus.REGISTERED,
RegistrationStatus.COMING,
].includes(registrationStatus || RegistrationStatus.UNREGISTERED);
if (goalStatus === RegistrationStatus.CHECKED_IN && atleastRegistered) {
if (currentDate >= CHECK_IN_START_DATE || registrationStatus === RegistrationStatus.CONFIRMED) return true;
else
return `Current status of this user is ${registrationStatus}. Check-in will be available after ${CHECK_IN_START_DATE.toLocaleString()}.`;
}

if (!isValidRegistrationStatusUpdate(registrationStatus || 'unregistered', goalStatus))
if (
!isValidRegistrationStatusUpdate(
registrationStatus || RegistrationStatus.UNREGISTERED,
goalStatus as RegistrationStatus
)
)
return `Invalid registration status update from ${registrationStatus} to ${goalStatus}`;

if ((registrationStatus === undefined || registrationStatus == 'unregistered') && goalStatus === 'registered') {
if (
(registrationStatus === undefined || registrationStatus == RegistrationStatus.UNREGISTERED) &&
goalStatus === RegistrationStatus.REGISTERED
) {
if (
[
'email',
Expand Down
36 changes: 24 additions & 12 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/naming-convention */
export interface TeamInvite {
team_id: string;
invited_by: string;
Expand All @@ -12,15 +13,15 @@ export interface Failure {

export interface UserTeamInfo {
team_id: string | null;
role: 'leader' | 'member' | null;
role: TeamRole | null;
pending_invites: TeamInvite[];
}

export interface TeamDocument {
team_id: string;
leader_email: string;
members: string[];
status: 'Active' | 'Disbanded';
status: TeamStatus;
team_name: string;
created: Date;
updated: Date;
Expand Down Expand Up @@ -78,13 +79,24 @@ export interface UserDocument {
registered_at: string;
}

type RegistrationStatus =
| 'unregistered'
| 'registered'
| 'rejected'
| 'confirmation'
| 'waitlist'
| 'coming'
| 'not_coming'
| 'confirmed'
| 'checked_in';
export enum RegistrationStatus {
UNREGISTERED = 'unregistered',
REGISTERED = 'registered',
REJECTED = 'rejected',
CONFIRMATION = 'confirmation',
WAITLIST = 'waitlist',
COMING = 'coming',
NOT_COMING = 'not_coming',
CONFIRMED = 'confirmed',
CHECKED_IN = 'checked_in',
}

export enum TeamRole {
LEADER = 'leader',
MEMBER = 'member',
}

export enum TeamStatus {
ACTIVE = 'Active',
DISBANDED = 'Disbanded',
}
Loading