Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/reference/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6663,8 +6663,52 @@ paths:
summary: Get Human Resources for a campaign
tags: []
parameters:
- schema:
type: string
name: campaign
in: path
required: true
- $ref: '#/components/parameters/campaign'
description: Updates tokens_usage in campaign and updates the link between cp_id and agreementId
put:
summary: Your PUT endpoint
tags: []
responses:
'200':
description: OK
'403':
$ref: '#/components/responses/NotAuthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
description: Internal Server Error
operationId: put-dossiers-campaign-humanResources
x-stoplight:
id: 9b1ouv9rd766s
security:
- JWT: []
requestBody:
description: Overwrites the data for the given campaign in the campaign_human_resources table
content:
application/json:
schema:
type: array
items:
type: object
required:
- assignee
- days
- rate
properties:
assignee:
type: integer
minimum: 1
days:
type: number
minimum: 0
rate:
type: integer
minimum: 1
'/dossiers/{campaign}/manual':
parameters:
- $ref: '#/components/parameters/campaign'
Expand Down
151 changes: 151 additions & 0 deletions src/routes/dossiers/campaignId/humanResources/_put/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import app from "@src/app";
import { tryber } from "@src/features/database";
import request from "supertest";

describe("Route PUT/dossiers/:campaignId/humanResources", () => {
beforeAll(async () => {
const campaign = {
title: "Test Campaign",
customer_title: "Test Campaign",
start_date: "2023-01-01",
end_date: "2023-12-31",
pm_id: 1,
platform_id: 1,
page_preview_id: 1,
page_manual_id: 1,
customer_id: 1,
project_id: 1,
};
await tryber.tables.WpAppqEvdCampaign.do().insert([
{ ...campaign, id: 1 },
{ ...campaign, id: 2 },
]);
const tester = {
education_id: 1,
email: "",
employment_id: 1,
};
await tryber.tables.WpAppqEvdProfile.do().insert([
{ ...tester, id: 1, wp_user_id: 10, name: "Tester One" },
{ ...tester, id: 2, wp_user_id: 20, name: "Tester Two" },
{ ...tester, id: 3, wp_user_id: 30, name: "Tester Three" },
]);
await tryber.tables.WorkRates.do().insert([
{ id: 1, name: "Researcher", daily_rate: 1.5 },
{ id: 2, name: "PM", daily_rate: 2.0 },
]);
});
afterAll(async () => {
await tryber.tables.WpAppqEvdCampaign.do().delete();
await tryber.tables.WpAppqEvdProfile.do().delete();
await tryber.tables.WorkRates.do().delete();
});

it("Should answer 403 if not logged in", async () => {
const response = await request(app)
.put("/dossiers/1/humanResources")
.send([{ assignee: 3, days: 5, rate: 1 }]);
expect(response.status).toBe(403);
});

it("Should answer 403 if no access to campaign", async () => {
const response = await request(app)
.put("/dossiers/2/humanResources")
.send([{ assignee: 3, days: 5, rate: 1 }])
.set("Authorization", 'Bearer tester olp {"appq_campaign":[1]}');
expect(response.status).toBe(403);
});

it("Should answer 400 if invalid body", async () => {
const response = await request(app)
.put("/dossiers/1/humanResources")
.send([{ assignee: -3, days: 5, rate: 1 }])
.set("Authorization", 'Bearer tester olp {"appq_campaign":[1]}');
expect(response.status).toBe(400);
});

it("Should answer 400 if profile does not exist", async () => {
const response = await request(app)
.put("/dossiers/1/humanResources")
.send([{ assignee: 999, days: 5, rate: 1 }])
.set("Authorization", 'Bearer tester olp {"appq_campaign":[1]}');
expect(response.status).toBe(400);
});

it("Should answer 400 if work rate does not exist", async () => {
const response = await request(app)
.put("/dossiers/1/humanResources")
.send([{ assignee: 3, days: 5, rate: 999 }])
.set("Authorization", 'Bearer tester olp {"appq_campaign":[1]}');
expect(response.status).toBe(400);
});

it("Should answer 200 if admin", async () => {
const response = await request(app)
.put("/dossiers/1/humanResources")
.send([{ assignee: 3, days: 5, rate: 1 }])
.set("authorization", "Bearer admin");
expect(response.status).toBe(200);
});
it("Should answer 400 if tester", async () => {
const response = await request(app)
.put("/dossiers/1/humanResources")
.send([{ assignee: 3, days: 5, rate: 1 }])
.set("authorization", "Bearer tester");
expect(response.status).toBe(403);
});
it("Should answer 200 if has access to campaign", async () => {
const response = await request(app)
.put("/dossiers/1/humanResources")
.send([{ assignee: 3, days: 5, rate: 1 }])
.set("Authorization", 'Bearer tester olp {"appq_campaign":[1]}');
expect(response.status).toBe(200);
});

describe("With basic data", () => {
beforeAll(async () => {
await tryber.tables.CampaignHumanResources.do().insert([
{
id: 1,
campaign_id: 1,
profile_id: 1,
days: 10,
work_rate_id: 1,
},
]);
});
afterAll(async () => {
await tryber.tables.CampaignHumanResources.do().delete();
});

it("Should remove the old records and update the human resources", async () => {
const response = await request(app)
.put("/dossiers/1/humanResources")
.send([
{ assignee: 3, days: 5, rate: 1 },
{ assignee: 2, days: 8, rate: 2 },
])
.set("Authorization", 'Bearer tester olp {"appq_campaign":[1]}');
const humanResources =
await tryber.tables.CampaignHumanResources.do().select();
expect(humanResources).toHaveLength(2);
expect(humanResources).toEqual(
expect.arrayContaining([
expect.objectContaining({
campaign_id: 1,
profile_id: 3,
days: 5,
work_rate_id: 1,
}),
expect.objectContaining({
campaign_id: 1,
profile_id: 2,
days: 8,
work_rate_id: 2,
}),
])
);
expect(response.status).toBe(200);
});
});
});
93 changes: 93 additions & 0 deletions src/routes/dossiers/campaignId/humanResources/_put/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/** OPENAPI-CLASS: put-dossiers-campaign-humanResources */
import { tryber } from "@src/features/database";
import OpenapiError from "@src/features/OpenapiError";
import CampaignRoute from "@src/features/routes/CampaignRoute";

export default class RouteItem extends CampaignRoute<{
response: StoplightOperations["put-dossiers-campaign-humanResources"]["responses"]["200"];
parameters: StoplightOperations["put-dossiers-campaign-humanResources"]["parameters"]["path"];
body: StoplightOperations["put-dossiers-campaign-humanResources"]["requestBody"]["content"]["application/json"];
}> {
protected async filter() {
if (!(await super.filter())) return false;

if (!this.hasAccessToCampaign(this.cp_id)) {
this.setError(403, new OpenapiError("You are not authorized to do this"));
return false;
}
if (!(await this.validateHumanResources())) {
return false;
}
return true;
}

private async isProfileValid(profileId: number) {
const profile = await tryber.tables.WpAppqEvdProfile.do()
.select("id")
.where("id", profileId)
.first();
return !!profile;
}

private async isWorkRateValid(rateId: number) {
const workRate = await tryber.tables.WorkRates.do()
.select("id")
.where("id", rateId)
.first();
return !!workRate;
}

private async validateHumanResources() {
const body = this.getBody();
for (const item of body) {
if (!(await this.isProfileValid(item.assignee))) {
this.setError(
400,
new OpenapiError(`Profile with id ${item.assignee} does not exist`)
);
return false;
}
if (!(await this.isWorkRateValid(item.rate))) {
this.setError(
400,
new OpenapiError(`Work rate with id ${item.rate} does not exist`)
);
return false;
}
}
return true;
}

private async updateHumanResources() {
const body = this.getBody();

try {
await tryber.tables.CampaignHumanResources.do()
.delete()
.where("campaign_id", this.cp_id);

await tryber.tables.CampaignHumanResources.do().insert([
...body.map((item) => ({
campaign_id: this.cp_id,
profile_id: item.assignee,
days: item.days,
work_rate_id: item.rate,
})),
]);
} catch (error) {
throw new OpenapiError("Failed to update human resources");
}
}

protected async prepare() {
try {
await this.updateHumanResources();
} catch (error: OpenapiError | any) {
this.setError(500, {
message: error.message,
} as OpenapiError);
return;
}
this.setSuccess(200, {});
}
}
27 changes: 27 additions & 0 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ export interface paths {
};
"/dossiers/{campaign}/humanResources": {
get: operations["get-dossiers-campaign-humanResources"];
put: operations["put-dossiers-campaign-humanResources"];
parameters: {
path: {
/** A campaign id */
Expand Down Expand Up @@ -3245,6 +3246,32 @@ export interface operations {
};
};
};
"put-dossiers-campaign-humanResources": {
parameters: {
path: {
/** A campaign id */
campaign: components["parameters"]["campaign"];
};
};
responses: {
/** OK */
200: unknown;
403: components["responses"]["NotAuthorized"];
404: components["responses"]["NotFound"];
/** Internal Server Error */
500: unknown;
};
/** Overwrites the data for the given campaign in the campaign_human_resources table */
requestBody: {
content: {
"application/json": {
assignee: number;
days: number;
rate: number;
}[];
};
};
};
"post-dossiers-campaign-manual": {
parameters: {
path: {
Expand Down
Loading