Added create order endpoint
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
11861b4fb5
commit
e841a9e2fe
@ -1,3 +1,3 @@
|
|||||||
import { Client } from 'pg';
|
import { Pool } from 'pg';
|
||||||
export const client = new Client();
|
export const client = new Pool();
|
||||||
client.connect();
|
client.connect();
|
||||||
@ -0,0 +1,156 @@
|
|||||||
|
import express, { Router, Request, Response, NextFunction } from "express";
|
||||||
|
import Joi from "joi"
|
||||||
|
import dayjs from "dayjs"
|
||||||
|
import isoWeek from "dayjs/plugin/isoWeek"
|
||||||
|
import utc from "dayjs/plugin/utc"
|
||||||
|
|
||||||
|
import { client, client as pool } from "../db";
|
||||||
|
import { DatabaseError } from "pg";
|
||||||
|
import Trainer from "../interfaces/trainer";
|
||||||
|
import { ReservedTimeslots, Timeslot, WeeklyTimeslot } from "../interfaces/timeslot";
|
||||||
|
import { idSchema, timeSchema } from "../schemas";
|
||||||
|
import { UserAuth } from "../middlewares/auth";
|
||||||
|
import { AuthedRequest } from "../interfaces/auth";
|
||||||
|
|
||||||
|
dayjs.extend(isoWeek)
|
||||||
|
dayjs.extend(utc)
|
||||||
|
|
||||||
|
const router: Router = express.Router();
|
||||||
|
|
||||||
|
const orderSchema = Joi.object({
|
||||||
|
trainer: idSchema.required(),
|
||||||
|
startDate: Joi.date().required(),
|
||||||
|
endDate: Joi.date().required()
|
||||||
|
});
|
||||||
|
|
||||||
|
interface DatabaseResult {
|
||||||
|
trainer: Trainer
|
||||||
|
timeslots: WeeklyTimeslot[]
|
||||||
|
reserved_timeslots: ReservedTimeslots[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OrderBody {
|
||||||
|
trainer: number
|
||||||
|
startDate: Date
|
||||||
|
endDate: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TimeslotValidQueryResult {
|
||||||
|
weekly_timeslot_available: boolean
|
||||||
|
time_already_reserved: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
router.post("/order", UserAuth, async (req: AuthedRequest, res: Response) => {
|
||||||
|
try {
|
||||||
|
const client = await pool.connect();
|
||||||
|
try {
|
||||||
|
const validation = orderSchema.validate(req.body, { abortEarly: false });
|
||||||
|
if (validation.error !== undefined) {
|
||||||
|
return res.status(400).send(validation.error.details);
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderBody: OrderBody = validation.value;
|
||||||
|
|
||||||
|
const startDate: dayjs.Dayjs = dayjs(orderBody.startDate).utcOffset(0);
|
||||||
|
const endDate: dayjs.Dayjs = dayjs(orderBody.endDate).utcOffset(0);
|
||||||
|
|
||||||
|
const startTime: string = `${startDate.hour()}:${startDate.minute()}`;
|
||||||
|
const endTime: string = `${endDate.hour()}:${endDate.minute()}`;
|
||||||
|
|
||||||
|
const weekday = startDate.isoWeekday();
|
||||||
|
|
||||||
|
await client.query("BEGIN");
|
||||||
|
|
||||||
|
const timeslotValidQuery = await client.query(`
|
||||||
|
select
|
||||||
|
EXISTS(
|
||||||
|
SELECT 1 FROM weekly_timeslots
|
||||||
|
WHERE
|
||||||
|
trainer_id = $1
|
||||||
|
AND day_of_week = $2
|
||||||
|
AND start_time = $3
|
||||||
|
AND end_time = $4
|
||||||
|
) as weekly_timeslot_available,
|
||||||
|
EXISTS(
|
||||||
|
SELECT 1 FROM public.reserved_timeslots
|
||||||
|
WHERE (
|
||||||
|
(start_time between $5 AND $6
|
||||||
|
OR end_time between $5 AND $6))
|
||||||
|
AND trainer_id = $1
|
||||||
|
) as time_already_reserved;
|
||||||
|
`, [
|
||||||
|
orderBody.trainer,
|
||||||
|
weekday,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
orderBody.startDate,
|
||||||
|
orderBody.endDate
|
||||||
|
]);
|
||||||
|
|
||||||
|
const timeslotValidQueryResult: TimeslotValidQueryResult = timeslotValidQuery.rows[0];
|
||||||
|
|
||||||
|
if (timeslotValidQueryResult.time_already_reserved || !timeslotValidQueryResult.weekly_timeslot_available)
|
||||||
|
return res.status(400).send([{
|
||||||
|
message: "timeslot was not found",
|
||||||
|
path: [
|
||||||
|
"startDate"
|
||||||
|
],
|
||||||
|
type: "startDate.invalid"
|
||||||
|
}]);
|
||||||
|
|
||||||
|
const priceQuery = await client.query(`
|
||||||
|
SELECT hourly_price FROM trainers WHERE id = $1;
|
||||||
|
`, [
|
||||||
|
orderBody.trainer
|
||||||
|
]);
|
||||||
|
|
||||||
|
const hourlyPrice = priceQuery.rows[0].hourly_price;
|
||||||
|
|
||||||
|
const hours = endDate.diff(startDate, "hours", true);
|
||||||
|
const price = Math.ceil(hours * hourlyPrice);
|
||||||
|
|
||||||
|
const insertQuery = await client.query(`
|
||||||
|
WITH inserted_reserved_timeslot AS (
|
||||||
|
INSERT INTO reserved_timeslots (trainer_id, start_time, end_time)
|
||||||
|
VALUES ($1, $2, $3)
|
||||||
|
RETURNING *
|
||||||
|
)
|
||||||
|
|
||||||
|
INSERT INTO orders (timeslot_id, user_id, order_status, price)
|
||||||
|
select id, $4, 'Processing', $5
|
||||||
|
FROM inserted_reserved_timeslot
|
||||||
|
RETURNING id;
|
||||||
|
`, [
|
||||||
|
orderBody.trainer,
|
||||||
|
orderBody.startDate,
|
||||||
|
orderBody.endDate,
|
||||||
|
req.user?.userId,
|
||||||
|
price
|
||||||
|
]);
|
||||||
|
|
||||||
|
const insertedData = insertQuery.rows[0];
|
||||||
|
|
||||||
|
await client.query("COMMIT");
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
id: insertedData.id,
|
||||||
|
trainerId: orderBody.trainer,
|
||||||
|
startDate: orderBody.startDate,
|
||||||
|
endDate: orderBody.endDate,
|
||||||
|
price
|
||||||
|
});
|
||||||
|
} catch (error: DatabaseError | Error | any) {
|
||||||
|
await client.query("ROLLBACK");
|
||||||
|
console.error(error);
|
||||||
|
return res.sendStatus(500);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
client.release();
|
||||||
|
}
|
||||||
|
} catch (error: DatabaseError | Error | any) {
|
||||||
|
console.error(error);
|
||||||
|
return res.sendStatus(500);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router;
|
||||||
Loading…
Reference in New Issue