-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
added(server/api): availability slots "find" and "set" api handlers. #278
Conversation
d26c6df
to
949ebfc
Compare
222472c
to
1d5aa0a
Compare
1d5aa0a
to
e19fdab
Compare
@mmoehabb please rebase and resolve the conflicts. |
e19fdab
to
7ec0498
Compare
7ec0498
to
447a967
Compare
447a967
to
f23361f
Compare
f23361f
to
1560efe
Compare
@neuodev |
@mmoehabb please rebase and resolve the conflicts. |
You are missing the |
edee3ad
to
a4c9e4b
Compare
a4c9e4b
to
1861159
Compare
1861159
to
1906f49
Compare
packages/models/src/lessons.ts
Outdated
@@ -151,6 +157,24 @@ export class Lessons { | |||
.where(this.columns.lessons("id"), id); | |||
} | |||
|
|||
async cancelBatch({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case the old one should be updated to accept a list of id not just one @mmoehabb
import { dayjs } from "@/dayjs"; | ||
import { flatten, orderBy } from "lodash"; | ||
import { INTERVIEW_DURATION } from "./constants"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import { INTERVIEW_DURATION } from "./constants"; | |
import { INTERVIEW_DURATION } from "@/constants"; |
services/server/fixtures/db.ts
Outdated
return ( | ||
await availabilitySlots.create([ | ||
{ | ||
userId: payload?.userId || 1, | ||
start: start.toISOString(), | ||
end: end.toISOString(), | ||
}, | ||
]) | ||
)[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not clean and prone to errors. Please refactor it.
const canUseFullFlag = | ||
after && before && dayjs.utc(before).diff(after, "days") <= 30; | ||
|
||
const paginatedSlots = await availabilitySlots.find({ | ||
users: [userId], | ||
after, | ||
before, | ||
pagination: { | ||
page: pagination?.page || 1, | ||
size: pagination?.size || 10, | ||
full: pagination?.full || !!canUseFullFlag, | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const canUseFullFlag = | |
after && before && dayjs.utc(before).diff(after, "days") <= 30; | |
const paginatedSlots = await availabilitySlots.find({ | |
users: [userId], | |
after, | |
before, | |
pagination: { | |
page: pagination?.page || 1, | |
size: pagination?.size || 10, | |
full: pagination?.full || !!canUseFullFlag, | |
}, | |
}); | |
const canUseFullFlag = | |
after && before && dayjs.utc(before).diff(after, "days") <= 30; | |
if (!canUseFullFlag) reutrn next(bad); | |
const paginatedSlots = await availabilitySlots.find({ | |
users: [userId], | |
after, | |
before, | |
pagination, | |
}); |
const result: IAvailabilitySlot.FindAvailabilitySlotsApiResponse = { | ||
list: [ | ||
{ | ||
slots: paginatedSlots.list, | ||
subslots: asSubSlots([ | ||
...paginatedLessons.list, | ||
...userInterviews.list, | ||
]), | ||
}, | ||
], | ||
total: paginatedSlots.total, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The response sturcture is not a little bit misleading. The location of the paginated data is not right. I believe a better response sturcutre should look like this:
type Response = {
slots: Paginated<IAvailabilitySlot.Self[]>,
subslots: IAvailabilitySlot.SubSlot[],
}
The slots are pagianted that's why it has the Paginated
type. Subslot isn't.
if (slotsOnly && (isTutor(user.role) || isTutorManager(user.role))) { | ||
const result = { | ||
list: [{ slots: paginatedSlots.list, subslots: [] }], | ||
total: paginatedSlots.total, | ||
}; | ||
res.status(200).json(result); | ||
return; | ||
} | ||
|
||
const slotIds = paginatedSlots.list.map((slot) => slot.id); | ||
|
||
const paginatedLessons = await lessons.find({ | ||
users: [userId], | ||
slots: slotIds, | ||
after, | ||
before, | ||
}); | ||
|
||
const userInterviews = await interviews.find({ | ||
users: [userId], | ||
slots: slotIds, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can move this logic into a seperate function. It will be more cleaner. Here is how the final code should look like.
function getSubslot(payload: {slotIds: number[], userId: number}): Promise<IAvailabilitySlot.SubSlot[]>;
const subslots = isTutor(user) || isTutorManager(user) ? [] : getSubSlot(slotIds, user.id);
This way we will only have one res.json()
and the logic will relatively simple to grasb what this handler is doing from a first look.
}: { | ||
currentUserId: number; | ||
ids: number[]; | ||
tx?: Knex.Transaction; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make the tx required to force the consumer to pass it.
tx?: Knex.Transaction; | |
tx: Knex.Transaction; |
await lessons.cancelBatch({ | ||
ids: associatedLessons.list.map((l) => l.id), | ||
canceledBy: currentUserId, | ||
tx, | ||
}); | ||
await interviews.cancel({ | ||
ids: associatedInterviews.list.map((i) => i.ids.self), | ||
canceledBy: currentUserId, | ||
tx, | ||
}); | ||
await availabilitySlots.markAsDeleted({ ids: ids, tx }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Empty line for readability.
- Use
Promise.all
.
await lessons.cancelBatch({ | |
ids: associatedLessons.list.map((l) => l.id), | |
canceledBy: currentUserId, | |
tx, | |
}); | |
await interviews.cancel({ | |
ids: associatedInterviews.list.map((i) => i.ids.self), | |
canceledBy: currentUserId, | |
tx, | |
}); | |
await availabilitySlots.markAsDeleted({ ids: ids, tx }); | |
await lessons.cancelBatch({ | |
ids: associatedLessons.list.map((l) => l.id), | |
canceledBy: currentUserId, | |
tx, | |
}); | |
await interviews.cancel({ | |
ids: associatedInterviews.list.map((i) => i.ids.self), | |
canceledBy: currentUserId, | |
tx, | |
}); | |
await availabilitySlots.markAsDeleted({ ids: ids, tx }); |
const user = req.user; | ||
if (!isUser(user)) return next(forbidden()); | ||
|
||
const { userId, after, before, slotsOnly, pagination } = findPayload.parse( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As for the time being, it is not required to control the toggle of subslots on and off for outside. It might change in the future but not now. In this case we can remove this flag.
const { userId, after, before, slotsOnly, pagination } = findPayload.parse( | |
const { userId, after, before, pagination } = findPayload.parse( |
6987738
to
425de44
Compare
e607fc2
to
f7d8990
Compare
f7d8990
to
620c5fa
Compare
620c5fa
to
8d1ebd7
Compare
ClickUp Task: https://app.clickup.com/t/86978ub4n