plugins/nfc/helpers.ts
import { ObjectID } from 'mongodb';
import { AuthenticationError, UserInputError } from 'apollo-server-express';
import { HackerDbObject, UserDbInterface } from '../../src/server/generated/graphql';
import { Models } from '../../src/server/models';
import logger from '../../src/server/logger';
// TODO: (kenli/timliang) Expand functions for Organizers, Mentors collections
export async function checkIfNFCUIDExisted(
nfcID: string,
models: Models
): Promise<HackerDbObject | null> {
const hacker = await models.Hackers.findOne({
secondaryIds: nfcID,
});
return hacker;
}
export async function userIsAttendingEvent(
userID: string,
eventID: string,
models: Models
): Promise<boolean> {
const userObjectID = new ObjectID(userID);
const user = await models.Hackers.findOne({ _id: userObjectID });
return user !== null && user.eventsAttended && user.eventsAttended.includes(eventID);
}
export async function shouldWarnRepeatedCheckIn(
userID: string,
eventID: string,
models: Models
): Promise<boolean> {
const event = await models.Events.findOne({ _id: new ObjectID(eventID) });
return (
event != null && event.warnRepeatedCheckins && userIsAttendingEvent(userID, eventID, models)
);
}
export async function getUser(nfcID: string, models: Models): Promise<HackerDbObject | null> {
const user = await models.Hackers.findOne({
'secondaryIds.0': nfcID,
});
return user;
}
export async function isNFCUIDAvailable(nfcID: string, models: Models): Promise<boolean> {
return (await getUser(nfcID, models)) == null;
}
export async function registerNFCUIDWithUser(
nfcID: string,
userID: string,
models: Models
): Promise<UserDbInterface> {
if (!(await isNFCUIDAvailable(nfcID, models)))
throw new AuthenticationError(`NFC ID ${nfcID} not available`);
const ret = await models.Hackers.findOneAndUpdate(
{ _id: new ObjectID(userID) },
{
$push: {
secondaryIds: {
$each: [nfcID],
$position: 0,
},
},
},
{ returnOriginal: false }
);
logger.info(`registered user ${userID} with NFC id ${nfcID}`);
if (ret.value) {
if (ret.value.secondaryIds.length > 1) {
throw new Error(`Associated new NFC ID with user overriding the old NFC ID`);
}
return ret.value;
}
throw new Error(`Unable to associate NFC ID with user: ${userID}. User not found.`);
}
export async function removeUserFromEvent(
userID: string,
eventID: string,
models: Models
): Promise<UserDbInterface> {
const userObjectID = new ObjectID(userID);
const user = await models.Hackers.findOne({ _id: userObjectID, eventsAttended: eventID });
if (!user) throw new UserInputError(`User has not attended this event`);
const ret = await Promise.all([
models.Events.updateOne(
{ _id: new ObjectID(eventID) },
{
$pull: {
attendees: userObjectID.toHexString(),
},
},
{}
),
models.Hackers.updateOne(
{ _id: userObjectID },
{
$pull: {
eventsAttended: eventID,
},
}
),
]);
if (ret[0].result.ok && ret[1].result.ok) {
logger.info(
`removed user ${userID} (${user.firstName} ${user.lastName}) from event ${eventID}`
);
return user;
}
throw new Error(`failed to remove ${user.firstName} ${user.lastName} from event ${eventID}`);
}
export async function checkInUserToEvent(
userID: string,
eventID: string,
models: Models
): Promise<UserDbInterface> {
const userObjectID = new ObjectID(userID);
const eventObjectID = new ObjectID(eventID);
const user = await models.Hackers.findOne({ _id: userObjectID });
if (!user) throw new UserInputError(`user ${userID} not found`);
// TODO(mattleon): This requires hitting the DB like 5 times rip
if (await shouldWarnRepeatedCheckIn(userID, eventID, models)) {
throw new Error(`${user.firstName} ${user.lastName} is already checked into event`);
}
const retEvent = await models.Events.findOneAndUpdate(
{ _id: eventObjectID },
{
$addToSet: { attendees: userObjectID.toHexString() },
},
{ returnOriginal: false }
);
const retUsr = await models.Hackers.findOneAndUpdate(
{ _id: userObjectID },
{
$addToSet: {
eventsAttended: eventObjectID.toHexString(),
},
},
{ returnOriginal: false }
);
if (retEvent.ok && retUsr.ok && retUsr.value) {
logger.info(`checked in user ${userID} to event ${eventID}`);
return retUsr.value;
}
throw new Error(`failed checking user <${userID}> into event <${eventID}>`);
}
export async function getEventsAttended(userID: string, models: Models): Promise<string[]> {
const user = await models.Hackers.findOne({ _id: new ObjectID(userID) });
return user != null && user.eventsAttended != null ? user.eventsAttended : [];
}
export async function getAttendees(eventID: string, models: Models): Promise<string[]> {
const event = await models.Events.findOne({ _id: new ObjectID(eventID) });
return event != null ? event.attendees : [];
}