-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Labels
internship-portalFor supporting the internship-portal. Probably uses MongoDBFor supporting the internship-portal. Probably uses MongoDBnew feature
Description
Description:
Create endpoints for retrieving applications. Support different views for applicants (own application) and officers (committee-filtered applications).
Acceptance Criteria:
- Create
GET /api/v1/internship/applications/me- Get current user's application- Returns full application with all fields
- 404 if no application exists
- Create
GET /api/v1/internship/applications- Get all applications (OFFICER only)- Query params:
?committee=hack- Filter by committee (any choice position)?status=pending- Filter by status?page=1&limit=20- Pagination (default 20, max 100)
- Returns paginated list with total count
- Query params:
- Create
GET /api/v1/internship/applications/:id- Get single application (OFFICER only)- Returns full application details
- Populate committee information
- Add authorization checks:
- Applicants can only see their own application
- Officers can see applications for their committee(s)
- Admins can see all applications
- Add rate limiting: 100 requests per 15 minutes
Example Request/Response:
# Applicant view
GET /api/v1/internship/applications/me
Response:
{
"error": null,
"application": {
"_id": "...",
"userId": "user-uuid",
"firstName": "Alice",
"lastName": "Smith",
"email": "alice@ucla.edu",
"firstChoice": {
"_id": "...",
"name": "hack",
"displayName": "Hack"
},
"firstChoiceStatus": "pending",
"firstChoiceResponses": [...],
"submittedAt": "2025-01-15T10:30:00.000Z"
}
}
# Officer view
GET /api/v1/internship/applications?committee=hack&status=pending&page=1&limit=20
Response:
{
"error": null,
"applications": [...],
"total": 45,
"page": 1,
"limit": 20,
"pages": 3
}Technical Notes:
exports.getMyApplication = async (req, res, next) => {
try {
const application = await Application.findOne({ userId: req.user.uuid })
.populate('firstChoice secondChoice thirdChoice');
if (!application) {
return res.status(404).json({ error: 'No application found' });
}
res.json({ error: null, application });
} catch (error) {
next(error);
}
};
exports.getAllApplications = async (req, res, next) => {
try {
// Only officers/admins
if (!req.user.isAdmin()) {
return res.status(403).json({ error: 'Forbidden' });
}
const { committee, status, page = 1, limit = 20 } = req.query;
const query = {};
// Filter by committee (check all three choice fields)
if (committee) {
const comm = await Committee.findOne({ name: committee });
if (comm) {
query.$or = [
{ firstChoice: comm._id },
{ secondChoice: comm._id },
{ thirdChoice: comm._id },
];
}
}
// Filter by status (for first choice only for now)
if (status) {
query.firstChoiceStatus = status;
}
const skip = (page - 1) * Math.min(limit, 100);
const limitNum = Math.min(limit, 100);
const [applications, total] = await Promise.all([
Application.find(query)
.populate('firstChoice secondChoice thirdChoice')
.sort({ submittedAt: -1 })
.skip(skip)
.limit(limitNum),
Application.countDocuments(query),
]);
res.json({
error: null,
applications,
total,
page: parseInt(page),
limit: limitNum,
pages: Math.ceil(total / limitNum),
});
} catch (error) {
next(error);
}
};Dependencies: Issue #82, Issue #84
Estimated Effort: 4-5 hours
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
internship-portalFor supporting the internship-portal. Probably uses MongoDBFor supporting the internship-portal. Probably uses MongoDBnew feature