Skip to content

Commit

Permalink
Merge pull request #81 from ChangePlusPlusVandy/event-application-api
Browse files Browse the repository at this point in the history
Event application api
  • Loading branch information
JiashuHarryHuang authored Feb 12, 2024
2 parents fc1dbc1 + c394236 commit 74b84d8
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 2 deletions.
9 changes: 7 additions & 2 deletions middleware.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { testingAPI } from './utils/api-testing';

/**
* THIS FILE HAS TO STAY HERE!
* Use the default next-auth middleware pattern from https://next-auth.js.org/configuration/nextjs
Expand All @@ -16,5 +18,8 @@ export const config = {
matcher: ['/api/((?!users/create).*)'],
};

// Comment everything and uncomment this to test
// export function middleware() {}
function middleware() {}
// when in development, export the empty middleware function
if (testingAPI) {
module.exports = { ...module.exports, middleware };
}
56 changes: 56 additions & 0 deletions pages/api/applications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import dbConnect from '@/lib/dbConnect';
import { ObjectId } from 'mongodb';
import type { NextApiRequest, NextApiResponse } from 'next';
import mongoose from 'mongoose';
import VolunteerApplications from 'bookem-shared/src/models/VolunteerApplications';
import { ApplicationResponseData } from 'bookem-shared/src/types/database';
import ApplicationResponse from 'bookem-shared/src/models/ApplicationResponse';
import VolunteerEvents from 'bookem-shared/src/models/VolunteerEvents';
import { authOptions } from '@/pages/api/auth/[...nextauth]';
import { getServerSession } from 'next-auth';
import { makeSessionForAPITest } from '@/utils/api-testing';

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Get session user
const session =
(await getServerSession(req, res, authOptions)) || makeSessionForAPITest();

// Get request parameter
const {
query: { id },
method,
} = req;

switch (method) {
/**
* @route GET /api/applications
* @desc Get all applications to all applied events for a user
* @req event id, user in session
* @res list of applied events
*/
case 'GET':
try {
await dbConnect();
await VolunteerEvents.find({});

// query volunteerApplication by event id attributes
const applicationResponses = await ApplicationResponse.find({
userId: session.user.id,
}).populate('eventId');

// get all the events that the user has applied to
const events = applicationResponses.map(response => {
return response.eventId;
});

return res.status(200).json({ message: events });
} catch (error: any) {
console.error(error);
res.status(500).json({ message: error.message });
}
break;
}
}
108 changes: 108 additions & 0 deletions pages/api/event/[id]/apply.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import dbConnect from '@/lib/dbConnect';
import { ObjectId } from 'mongodb';
import type { NextApiRequest, NextApiResponse } from 'next';
import mongoose from 'mongoose';
import VolunteerApplications from 'bookem-shared/src/models/VolunteerApplications';
import { ApplicationResponseData } from 'bookem-shared/src/types/database';
import ApplicationResponse from 'bookem-shared/src/models/ApplicationResponse';
import { authOptions } from '@/pages/api/auth/[...nextauth]';
import { getServerSession } from 'next-auth';
import { makeSessionForAPITest } from '@/utils/api-testing';

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Get session user
let session =
(await getServerSession(req, res, authOptions)) || makeSessionForAPITest();

// Get request parameter
const {
query: { id },
method,
} = req;

switch (method) {
/**
* @route GET /api/event/[id]/apply
* @desc Return a list of application questions that this event needs
* @req event id, user in session
* @res list of application questions
*/
case 'GET':
try {
await dbConnect();

if (!id) return res.status(400).json({ message: 'Missing id' });

// check if id is a valid mongoose id
if (!ObjectId.isValid(id as string))
return res.status(400).json({ message: 'Invalid id' });

// query volunteerApplication by event id attributes
const volunteerApplication = await VolunteerApplications.findOne({
eventId: id,
});

if (!volunteerApplication) {
return res
.status(404)
.json({ message: 'No application for the event found' });
}

const questions = volunteerApplication.questions;

return res.status(200).json({ message: questions });
} catch (error) {
console.error(error);
res.status(500).json({ message: error });
}
break;

/**
* @route POST /api/event/[id]/apply
* @desc Submit the answers to the questions this event needs
* @req event id, user in session
* @res Success message
*/
case 'POST':
try {
await dbConnect();

const response = req.body as ApplicationResponseData;
const { answers } = response;

// Declare the following ops to be an atomic transaction
const mongoSession = await mongoose.startSession();
await mongoSession.withTransaction(async () => {
// insert the response to applicationResponses data
const newResponse = new ApplicationResponse({
userId: session.user.id,
status: 'pending',
eventId: id,
answers,
});

await newResponse.save();

// use the id of the saved response to update the volunteerApplications data
await VolunteerApplications.updateOne(
{ eventId: id },
{
$push: {
responses: newResponse._id,
},
}
);
});

return res.status(200).json('Application Submitted');
} catch (error: any) {
res.status(500).json({ message: error.message });
console.error(error);
}

break;
}
}
61 changes: 61 additions & 0 deletions pages/api/event/[id]/submitted-application.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import dbConnect from '@/lib/dbConnect';
import { ObjectId } from 'mongodb';
import type { NextApiRequest, NextApiResponse } from 'next';
import ApplicationResponse from 'bookem-shared/src/models/ApplicationResponse';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/pages/api/auth/[...nextauth]';
import { makeSessionForAPITest } from '@/utils/api-testing';

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Get session user
let session =
(await getServerSession(req, res, authOptions)) || makeSessionForAPITest();

// Get request parameter
const {
query: { id },
method,
} = req;

switch (method) {
/**
* @route GET /api/event/[id]/submitted-application
* @desc Get submitted application
* @req event id, user in session
* @res list of application questions
*/
case 'GET':
try {
await dbConnect();

if (!id) return res.status(400).json({ message: 'Missing id' });

// check if id is a valid mongoose id
if (!ObjectId.isValid(id as string))
return res.status(400).json({ message: 'Invalid id' });

// query volunteerApplication by event id attributes
// using findOne - assuming there is only one application per event per user
const volunteerApplication = await ApplicationResponse.findOne({
userId: session.user.id,
eventId: id,
});

// TODO - refactor a common util for when something is not found
if (!volunteerApplication) {
return res
.status(404)
.json({ message: 'No application for the event found' });
}

return res.status(200).json({ message: volunteerApplication });
} catch (error: any) {
console.error(error);
res.status(500).json({ message: error });
}
break;
}
}
15 changes: 15 additions & 0 deletions utils/api-testing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// common utils for simple API testing in development phase

// change this var to true when you are testing the API endpoints without logging in
export const testingAPI = false;

const fakeUserSession = {
user: {
id: '65ae7bbd24dc37492f2581c0',
},
};

// returns a fake user session when testing the API
export const makeSessionForAPITest = () => {
return testingAPI ? fakeUserSession : null;
};

0 comments on commit 74b84d8

Please sign in to comment.