Skip to content

Commit 3950495

Browse files
authored
feat: add mutation to reject opportunity match (#3251)
1 parent 4f8112c commit 3950495

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed

__tests__/schema/opportunity.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,90 @@ describe('mutation acceptOpportunityMatch', () => {
11801180
});
11811181
});
11821182

1183+
describe('mutation rejectOpportunityMatch', () => {
1184+
const MUTATION = /* GraphQL */ `
1185+
mutation RejectOpportunityMatch($id: ID!) {
1186+
rejectOpportunityMatch(id: $id) {
1187+
_
1188+
}
1189+
}
1190+
`;
1191+
1192+
it('should require authentication', async () => {
1193+
await testMutationErrorCode(
1194+
client,
1195+
{
1196+
mutation: MUTATION,
1197+
variables: {
1198+
id: '550e8400-e29b-41d4-a716-446655440001',
1199+
},
1200+
},
1201+
'UNAUTHENTICATED',
1202+
);
1203+
});
1204+
1205+
it('should accept opportunity match for authenticated user', async () => {
1206+
loggedUser = '1';
1207+
1208+
expect(
1209+
await con.getRepository(OpportunityMatch).countBy({
1210+
opportunityId: '550e8400-e29b-41d4-a716-446655440001',
1211+
userId: '1',
1212+
status: OpportunityMatchStatus.Pending,
1213+
}),
1214+
).toEqual(1);
1215+
1216+
const res = await client.mutate(MUTATION, {
1217+
variables: {
1218+
id: '550e8400-e29b-41d4-a716-446655440001',
1219+
},
1220+
});
1221+
1222+
expect(res.errors).toBeFalsy();
1223+
expect(res.data.rejectOpportunityMatch).toEqual({ _: true });
1224+
1225+
expect(
1226+
await con.getRepository(OpportunityMatch).countBy({
1227+
opportunityId: '550e8400-e29b-41d4-a716-446655440001',
1228+
userId: '1',
1229+
status: OpportunityMatchStatus.CandidateRejected,
1230+
}),
1231+
).toEqual(1);
1232+
});
1233+
1234+
it('should return error when the match is not pending', async () => {
1235+
loggedUser = '2';
1236+
1237+
await testMutationErrorCode(
1238+
client,
1239+
{
1240+
mutation: MUTATION,
1241+
variables: {
1242+
id: '550e8400-e29b-41d4-a716-446655440001',
1243+
},
1244+
},
1245+
'FORBIDDEN',
1246+
'Access denied! Match is not pending',
1247+
);
1248+
});
1249+
1250+
it('should return error when the opportunity is not live', async () => {
1251+
loggedUser = '1';
1252+
1253+
await testMutationErrorCode(
1254+
client,
1255+
{
1256+
mutation: MUTATION,
1257+
variables: {
1258+
id: '550e8400-e29b-41d4-a716-446655440003',
1259+
},
1260+
},
1261+
'FORBIDDEN',
1262+
'Access denied! Opportunity is not live',
1263+
);
1264+
});
1265+
});
1266+
11831267
describe('mutation candidateAddKeywords', () => {
11841268
const MUTATION = /* GraphQL */ `
11851269
mutation CandidateAddKeywords($keywords: [String!]!) {

src/schema/opportunity.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,13 @@ export const typeDefs = /* GraphQL */ `
302302
id: ID!
303303
): EmptyResponse @auth
304304
305+
rejectOpportunityMatch(
306+
"""
307+
Id of the Opportunity
308+
"""
309+
id: ID!
310+
): EmptyResponse @auth
311+
305312
candidateAddKeywords(
306313
"""
307314
Keywords to add to candidate profile
@@ -602,6 +609,58 @@ export const resolvers: IResolvers<unknown, BaseContext> = traceResolvers<
602609

603610
return { _: true };
604611
},
612+
rejectOpportunityMatch: async (
613+
_,
614+
{ id }: { id: string },
615+
{ userId, con, log }: AuthContext,
616+
): Promise<GQLEmptyResponse> => {
617+
const match = await con.getRepository(OpportunityMatch).findOne({
618+
where: {
619+
opportunityId: id,
620+
userId,
621+
},
622+
relations: {
623+
opportunity: true,
624+
},
625+
});
626+
627+
if (!match) {
628+
log.error(
629+
{ opportunityId: id, userId },
630+
'No match found for opportunity',
631+
);
632+
throw new ForbiddenError('Access denied! No match found');
633+
}
634+
635+
if (match.status !== OpportunityMatchStatus.Pending) {
636+
log.error(
637+
{ opportunityId: id, userId, status: match.status },
638+
'Match is not pending',
639+
);
640+
throw new ForbiddenError(`Access denied! Match is not pending`);
641+
}
642+
643+
const opportunity = await match.opportunity;
644+
if (opportunity.state !== OpportunityState.LIVE) {
645+
log.error(
646+
{ opportunityId: id, userId, state: opportunity.state },
647+
'Opportunity is not live',
648+
);
649+
throw new ForbiddenError(`Access denied! Opportunity is not live`);
650+
}
651+
652+
await con.getRepository(OpportunityMatch).update(
653+
{
654+
opportunityId: id,
655+
userId,
656+
},
657+
{
658+
status: OpportunityMatchStatus.CandidateRejected,
659+
},
660+
);
661+
662+
return { _: true };
663+
},
605664
candidateAddKeywords: async (
606665
_,
607666
payload: z.infer<typeof userCandidateToggleKeywordSchema>,

0 commit comments

Comments
 (0)