Skip to content

Commit fa6c505

Browse files
authored
feat(job-application): Applicant should be able to apply to job post (#142)
- create mutation and schema to handle job applications [Delivers #140]
1 parent f42f00c commit fa6c505

File tree

4 files changed

+282
-0
lines changed

4 files changed

+282
-0
lines changed

Diff for: src/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ import { commentReplySchema } from "./schema/commentReplySchema";
8484
import { commentLikeSchema } from "./schema/commentLikeSchema";
8585
import { commentLikeResolvers } from "./resolvers/commentLikeResolvers";
8686
import { commentReplyResolvers } from "./resolvers/commentReplyResolvers";
87+
import { jobApplicationTypeDefs } from "./schema/jobApplicationSchema";
88+
import { jobApplicationResolver } from "./resolvers/jobApplicationResolver";
8789

8890
const PORT = process.env.PORT || 3000;
8991

@@ -125,6 +127,7 @@ const resolvers = mergeResolvers([
125127
ticketResolver,
126128
filterTicketResolver,
127129
applicationStageResolvers,
130+
jobApplicationResolver,
128131
blogResolvers,
129132
likeResolvers,
130133
commentResolvers,
@@ -170,6 +173,7 @@ const typeDefs = mergeTypeDefs([
170173
commentSchema,
171174
commentReplySchema,
172175
commentLikeSchema,
176+
jobApplicationTypeDefs,
173177
]);
174178

175179
const server = new ApolloServer({

Diff for: src/models/JobApplication.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import mongoose from 'mongoose'
2+
3+
const jobApplicationSchema = new mongoose.Schema({
4+
userId: {
5+
type: mongoose.Schema.Types.ObjectId,
6+
required: true,
7+
ref: 'LoggedUserModel'
8+
},
9+
jobId: {
10+
type: mongoose.Schema.Types.ObjectId,
11+
required: true,
12+
ref: 'jobform'
13+
},
14+
essay:{
15+
type: String,
16+
required: true
17+
},
18+
resume:{
19+
type:String,
20+
required: true
21+
},
22+
status:{
23+
type:String,
24+
enum:['submitted','under-review','accepted','rejected'],
25+
default:'submitted'
26+
}
27+
},{timestamps:true})
28+
29+
const JobApplication = mongoose.model('JobApplication', jobApplicationSchema)
30+
31+
export default JobApplication

Diff for: src/resolvers/jobApplicationResolver.ts

+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import { AuthenticationError } from 'apollo-server';
2+
import JobApplication from '../models/JobApplication'
3+
import applicationCycleResolver from './applicationCycleResolver';
4+
import { CustomGraphQLError } from '../utils/customErrorHandler';
5+
import { LoggedUserModel } from '../models/AuthUser';
6+
import { jobModels } from '../models/jobModels';
7+
8+
interface formData {
9+
traineeId: string,
10+
jobId: string,
11+
essay: string,
12+
resume: string
13+
}
14+
15+
interface getOneFormData {
16+
applicationId: string
17+
}
18+
19+
interface statusData {
20+
applicationId: string,
21+
status: string
22+
}
23+
24+
const formatDate = (date:Date) => {
25+
const year = date.getFullYear();
26+
const month = (`0${date.getMonth() + 1}`).slice(-2);
27+
const day = (`0${date.getDate()}`).slice(-2);
28+
return `${year}-${month}-${day}`;
29+
};
30+
31+
export const jobApplicationResolver = {
32+
Query: {
33+
getOwnJobApplications: async (_:any, { input }:{input: formData}, cxt:any) => {
34+
if (!cxt.currentUser) {
35+
throw new AuthenticationError('You must be logged in');
36+
}
37+
38+
const userId = cxt.currentUser._id
39+
40+
const applications = await JobApplication.find({userId})
41+
.populate('jobId')
42+
43+
return applications.map(application => {
44+
return {
45+
_id: application._id,
46+
userId: application.userId,
47+
essay: application.essay,
48+
resume: application.resume,
49+
status: application.status,
50+
jobId: application.jobId,
51+
createdAt: formatDate(application.createdAt)
52+
}
53+
})
54+
},
55+
getAllJobApplications: async (_:any, { input }:{input: formData}, cxt:any) => {
56+
if (!cxt.currentUser) {
57+
throw new AuthenticationError('You must be logged in');
58+
}
59+
60+
const userWithRole = await LoggedUserModel.findById(
61+
cxt.currentUser?._id
62+
).populate("role");
63+
64+
if (
65+
!userWithRole ||
66+
((userWithRole.role as any)?.roleName !== "admin" &&
67+
(userWithRole.role as any)?.roleName !== "superAdmin")
68+
) {
69+
throw new CustomGraphQLError(
70+
"You do not have permission to perform this action"
71+
);
72+
}
73+
74+
const applications = await JobApplication.find().populate('jobId').populate('userId')
75+
return applications.map(application => {
76+
return {
77+
_id: application._id,
78+
userId: application.userId,
79+
essay: application.essay,
80+
resume: application.resume,
81+
status: application.status,
82+
jobId: application.jobId,
83+
createdAt: formatDate(application.createdAt)
84+
}
85+
})
86+
},
87+
getOneJobApplication: async (_:any, { input }:{input: getOneFormData}, cxt:any) => {
88+
if (!cxt.currentUser) {
89+
throw new AuthenticationError('You must be logged in');
90+
}
91+
92+
const userWithRole = await LoggedUserModel.findById(
93+
cxt.currentUser?._id
94+
).populate("role");
95+
96+
if (
97+
!userWithRole ||
98+
((userWithRole.role as any)?.roleName !== "admin" &&
99+
(userWithRole.role as any)?.roleName !== "superAdmin")
100+
) {
101+
throw new CustomGraphQLError(
102+
"You do not have permission to perform this action"
103+
);
104+
}
105+
106+
const application = await JobApplication.findOne({_id: input.applicationId}).populate('jobId').populate('userId')
107+
if(!application){
108+
throw new Error('Application not found');
109+
}
110+
111+
return {
112+
_id: application._id,
113+
userId: application.userId,
114+
essay: application.essay,
115+
resume: application.resume,
116+
status: application.status,
117+
jobId: application.jobId,
118+
createdAt: formatDate(application.createdAt)
119+
}
120+
},
121+
checkIfUserApplied: async (_: any, args: any, cxt: any) => {
122+
if (!cxt.currentUser) {
123+
throw new AuthenticationError('You must be logged in');
124+
}
125+
126+
const application = await JobApplication.findOne({userId: cxt.currentUser._id, jobId: args.input.jobId})
127+
return {
128+
status: application ? true : false
129+
}
130+
}
131+
},
132+
Mutation: {
133+
createNewJobApplication: async (_:any, { input }:{input: formData}, cxt:any) => {
134+
if (!cxt.currentUser) {
135+
throw new AuthenticationError('You must be logged in');
136+
}
137+
138+
const userId = cxt.currentUser._id
139+
140+
const { jobId, essay, resume } = input;
141+
142+
const existingApplication = await JobApplication.findOne({userId, jobId})
143+
if(existingApplication){
144+
throw new Error('Application already exists');
145+
}
146+
147+
const jobApplication = new JobApplication({
148+
userId,
149+
jobId,
150+
essay,
151+
resume,
152+
});
153+
154+
155+
const savedApplication = await jobApplication.save();
156+
157+
return savedApplication;
158+
},
159+
changeApplicationStatus: async (_:any, { input }:{input: statusData}, cxt:any) => {
160+
if (!cxt.currentUser) {
161+
throw new AuthenticationError('You must be logged in');
162+
}
163+
164+
const userWithRole = await LoggedUserModel.findById(
165+
cxt.currentUser?._id
166+
).populate("role");
167+
168+
if (
169+
!userWithRole ||
170+
((userWithRole.role as any)?.roleName !== "admin" &&
171+
(userWithRole.role as any)?.roleName !== "superAdmin")
172+
) {
173+
throw new CustomGraphQLError(
174+
"You do not have permission to perform this action"
175+
);
176+
}
177+
178+
const { status, applicationId } = input
179+
await JobApplication.findByIdAndUpdate(applicationId, {status})
180+
const application = await JobApplication.findOne({_id:applicationId}).populate('userId').populate('jobId')
181+
return application
182+
}
183+
},
184+
};

Diff for: src/schema/jobApplicationSchema.ts

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { gql } from "apollo-server";
2+
3+
export const jobApplicationTypeDefs = gql`
4+
type Job {
5+
_id: ID!
6+
title: String!
7+
}
8+
9+
type User {
10+
_id: ID!
11+
email: String!
12+
gender: String!
13+
firstname: String!
14+
lastname: String!
15+
country: String!
16+
telephone: String!
17+
}
18+
19+
type JobApplication {
20+
_id: ID!
21+
userId: User!
22+
jobId: Job!
23+
essay: String!
24+
resume: String!
25+
status: String!
26+
createdAt: String!
27+
}
28+
29+
input JobApplicationInput {
30+
jobId: ID!
31+
essay: String!
32+
resume: String!
33+
}
34+
35+
input SingleJobApplicationInput {
36+
applicationId: ID!
37+
}
38+
39+
input StatusInput {
40+
applicationId: ID!
41+
status: String!
42+
}
43+
44+
type checkIfUserAppliedOutput {
45+
status: Boolean!
46+
}
47+
48+
input CheckIfUserAppliedInput {
49+
jobId: ID!
50+
}
51+
52+
type Query {
53+
getOwnJobApplications: [JobApplication!]!
54+
getAllJobApplications: [JobApplication!]!
55+
getOneJobApplication(input: SingleJobApplicationInput): JobApplication!
56+
checkIfUserApplied(input: CheckIfUserAppliedInput): checkIfUserAppliedOutput!
57+
}
58+
59+
type Mutation {
60+
createNewJobApplication(input: JobApplicationInput!): JobApplication!
61+
changeApplicationStatus(input: StatusInput!): JobApplication!
62+
}
63+
`

0 commit comments

Comments
 (0)