Skip to content

Commit a5e332b

Browse files
authored
Adding schedule API :) (#246)
Con esta query podemos mostrar el programa para los 3 dias ```GQL query e { event(id: "f4e59c2c-9385-4592-acfc-3e46fd1a167f") { id name description schedules { id title startTimestamp endTimestamp sessions { id title startTimestamp endTimestamp speakers { id avatar name } } } } } ``` ![image](https://github.com/user-attachments/assets/92fcd449-d981-40a5-9bb5-04095001eae9)
1 parent 0ecde29 commit a5e332b

6 files changed

Lines changed: 220 additions & 11 deletions

File tree

src/schema/events/types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { SQL, eq, inArray } from "drizzle-orm";
33
import { authHelpers } from "~/authz/helpers";
44
import { builder } from "~/builder";
55
import {
6+
ScheduleStatus,
67
selectCommunitySchema,
8+
selectScheduleSchema,
79
selectSpeakerSchema,
810
selectTagsSchema,
911
selectTeamsSchema,
@@ -17,6 +19,8 @@ import {
1719
} from "~/datasources/db/schema";
1820
import { getImagesBySanityEventId } from "~/datasources/sanity/images";
1921
import { eventsFetcher } from "~/schema/events/eventsFetcher";
22+
import { schedulesFetcher } from "~/schema/schedules/schedulesFetcher";
23+
import { ScheduleLoadable, ScheduleRef } from "~/schema/schedules/types";
2024
import {
2125
CommunityRef,
2226
EventRef,
@@ -300,6 +304,20 @@ export const EventLoadable = builder.loadableObject(EventRef, {
300304
return tickets.map((t) => selectTicketSchema.parse(t));
301305
},
302306
}),
307+
schedules: t.field({
308+
type: [ScheduleRef],
309+
resolve: async (root, args, ctx) => {
310+
const schedules = await schedulesFetcher.searchSchedules({
311+
DB: ctx.DB,
312+
search: {
313+
eventIds: [root.id],
314+
satus: ScheduleStatus.active,
315+
},
316+
});
317+
318+
return schedules.map((s) => selectScheduleSchema.parse(s));
319+
},
320+
}),
303321
usersTickets: t.field({
304322
description: "List of tickets that a user owns for this event.",
305323
type: [UserTicketRef],

src/schema/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import "./purchaseOrder/types";
2929
import "./salary/mutations";
3030
import "./salary/queries";
3131
import "./salary/types";
32+
import "./schedules/types";
3233
import "./sessions/types";
3334
import "./speakers/types";
3435
import "./status/types";
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { SQL, and, asc, desc, eq, ilike, inArray } from "drizzle-orm";
2+
3+
import { ORM_TYPE } from "~/datasources/db";
4+
import { scheduleSchema, ScheduleStatus } from "~/datasources/db/schedule";
5+
import {
6+
PaginationOptionsType,
7+
paginationDBHelper,
8+
} from "~/datasources/helpers/paginationQuery";
9+
import { SortableSchemaFields } from "~/datasources/helpers/sorting";
10+
import { sanitizeForLikeSearch } from "~/schema/shared/helpers";
11+
12+
export type SchedulesSearch = {
13+
scheduleIds?: string[];
14+
eventIds?: string[];
15+
title?: string;
16+
description?: string;
17+
satus?: ScheduleStatus;
18+
};
19+
20+
type SortableFields =
21+
| "description"
22+
| "title"
23+
| "startTimestamp"
24+
| "endTimestamp";
25+
type EventFetcherSort = SortableSchemaFields<SortableFields>;
26+
27+
const getSearchSchedulesQuery = (
28+
DB: ORM_TYPE,
29+
search: SchedulesSearch = {},
30+
sort: EventFetcherSort,
31+
) => {
32+
const { scheduleIds, title, description, eventIds, satus } = search;
33+
34+
const wheres: SQL[] = [];
35+
const query = DB.select().from(scheduleSchema);
36+
37+
if (scheduleIds && scheduleIds.length > 0) {
38+
wheres.push(inArray(scheduleSchema.id, scheduleIds));
39+
}
40+
41+
if (eventIds && eventIds.length > 0) {
42+
wheres.push(inArray(scheduleSchema.eventId, eventIds));
43+
}
44+
45+
if (title) {
46+
wheres.push(ilike(scheduleSchema.title, sanitizeForLikeSearch(title)));
47+
}
48+
49+
if (description) {
50+
wheres.push(
51+
ilike(scheduleSchema.description, sanitizeForLikeSearch(description)),
52+
);
53+
}
54+
55+
if (satus) {
56+
wheres.push(eq(scheduleSchema.status, satus));
57+
}
58+
59+
const orderBy: SQL<unknown>[] = [];
60+
61+
if (sort) {
62+
const sorts = sort.map(([field, direction]) => {
63+
const sortDirection = direction === "asc" ? asc : desc;
64+
65+
return sortDirection(scheduleSchema[field]);
66+
});
67+
68+
orderBy.push(...sorts);
69+
}
70+
71+
return query.where(and(...wheres)).orderBy(...orderBy);
72+
};
73+
74+
const searchSchedules = async ({
75+
DB,
76+
search = {},
77+
sort,
78+
}: {
79+
DB: ORM_TYPE;
80+
search: SchedulesSearch;
81+
sort?: EventFetcherSort;
82+
}) => {
83+
const schedules = await getSearchSchedulesQuery(DB, search, sort).execute();
84+
85+
return schedules;
86+
};
87+
88+
const searchPaginatedSchedules = async ({
89+
DB,
90+
pagination,
91+
search = {},
92+
sort,
93+
}: {
94+
DB: ORM_TYPE;
95+
search: SchedulesSearch;
96+
pagination: PaginationOptionsType;
97+
sort?: EventFetcherSort;
98+
}) => {
99+
const query = getSearchSchedulesQuery(DB, search, sort);
100+
101+
const results = await paginationDBHelper(DB, query, pagination);
102+
103+
return results;
104+
};
105+
106+
export const schedulesFetcher = {
107+
searchSchedules,
108+
searchPaginatedSchedules,
109+
};

src/schema/schedules/types.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { z } from "zod";
2+
3+
import { builder } from "~/builder";
4+
import {
5+
selectEventsSchema,
6+
selectScheduleSchema,
7+
selectSessionSchema,
8+
} from "~/datasources/db/schema";
9+
import { applicationError, ServiceErrors } from "~/errors";
10+
import { EventLoadable } from "~/schema/events/types";
11+
import { schedulesFetcher } from "~/schema/schedules/schedulesFetcher";
12+
import { sessionsFetcher } from "~/schema/sessions/sessionsFetcher";
13+
import { SessionRef } from "~/schema/sessions/types";
14+
import { EventRef } from "~/schema/shared/refs";
15+
16+
type ScheduleraphqlSchema = z.infer<typeof selectScheduleSchema>;
17+
18+
export const ScheduleRef = builder.objectRef<ScheduleraphqlSchema>("Schedule");
19+
20+
export const ScheduleLoadable = builder.loadableObject(ScheduleRef, {
21+
description: "Representation of a Schedule",
22+
load: (ids: string[], context) =>
23+
schedulesFetcher.searchSchedules({
24+
DB: context.DB,
25+
search: { scheduleIds: ids },
26+
}),
27+
fields: (t) => ({
28+
id: t.exposeID("id", { nullable: false }),
29+
title: t.exposeString("title", { nullable: false }),
30+
description: t.exposeString("description", { nullable: true }),
31+
startTimestamp: t.field({
32+
type: "DateTime",
33+
resolve: (root) => new Date(root.startTimestamp),
34+
}),
35+
endTimestamp: t.field({
36+
type: "DateTime",
37+
resolve: (root) => new Date(root.endTimestamp),
38+
}),
39+
event: t.field({
40+
type: EventRef,
41+
resolve: async (root, args, ctx) => {
42+
const event = await EventLoadable.getDataloader(ctx).load(root.eventId);
43+
44+
if (!event) {
45+
throw applicationError(
46+
"Event not found",
47+
ServiceErrors.NOT_FOUND,
48+
ctx.logger,
49+
);
50+
}
51+
52+
return selectEventsSchema.parse(event);
53+
},
54+
}),
55+
sessions: t.field({
56+
type: [SessionRef],
57+
resolve: async (root, args, ctx) => {
58+
const sessions = await sessionsFetcher.searchSessions({
59+
DB: ctx.DB,
60+
search: {
61+
scheduleIds: [root.id],
62+
},
63+
sort: [["startTimestamp", "asc"]],
64+
});
65+
66+
return sessions.map((s) => selectSessionSchema.parse(s));
67+
},
68+
}),
69+
}),
70+
});

src/schema/sessions/sessionsFetcher.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SQL, and, asc, ilike, inArray } from "drizzle-orm";
1+
import { SQL, and, asc, desc, ilike, inArray } from "drizzle-orm";
22

33
import { ORM_TYPE } from "~/datasources/db";
44
import { sessionSchema } from "~/datasources/db/sessions";
@@ -7,6 +7,7 @@ import {
77
PaginationOptionsType,
88
paginationDBHelper,
99
} from "~/datasources/helpers/paginationQuery";
10+
import { SortableSchemaFields } from "~/datasources/helpers/sorting";
1011
import { sanitizeForLikeSearch } from "~/schema/shared/helpers";
1112

1213
export type SessionSearch = {
@@ -17,12 +18,16 @@ export type SessionSearch = {
1718
speakerIds?: string[];
1819
};
1920

20-
type SortableType = null | undefined;
21-
21+
type SortableFields =
22+
| "description"
23+
| "title"
24+
| "startTimestamp"
25+
| "endTimestamp";
26+
type EventFetcherSort = SortableSchemaFields<SortableFields>;
2227
const getSearchSessionsQuery = (
2328
DB: ORM_TYPE,
2429
search: SessionSearch = {},
25-
sort: SortableType,
30+
sort: EventFetcherSort,
2631
) => {
2732
const { sessionIds, scheduleIds, title, description, speakerIds } = search;
2833

@@ -59,8 +64,14 @@ const getSearchSessionsQuery = (
5964

6065
const orderBy: SQL<unknown>[] = [];
6166

62-
if (sort !== null) {
63-
orderBy.push(asc(sessionSchema.startTimestamp));
67+
if (sort) {
68+
const sorts = sort.map(([field, direction]) => {
69+
const sortDirection = direction === "asc" ? asc : desc;
70+
71+
return sortDirection(sessionSchema[field]);
72+
});
73+
74+
orderBy.push(...sorts);
6475
}
6576

6677
return query.where(and(...wheres)).orderBy(...orderBy);
@@ -73,7 +84,7 @@ const searchSessions = async ({
7384
}: {
7485
DB: ORM_TYPE;
7586
search: SessionSearch;
76-
sort?: SortableType;
87+
sort?: EventFetcherSort;
7788
}) => {
7889
const sessions = await getSearchSessionsQuery(DB, search, sort).execute();
7990

@@ -89,7 +100,7 @@ const searchPaginatedSessions = async ({
89100
DB: ORM_TYPE;
90101
search: SessionSearch;
91102
pagination: PaginationOptionsType;
92-
sort?: SortableType;
103+
sort?: EventFetcherSort;
93104
}) => {
94105
const query = getSearchSessionsQuery(DB, search, sort);
95106

src/schema/sessions/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { SpeakerRef } from "~/schema/speakers/types";
1111

1212
type SessionGraphqlSchema = z.infer<typeof selectSessionSchema>;
1313

14-
export const SessionRef = builder.objectRef<SessionGraphqlSchema>("SessionRef");
14+
export const SessionRef = builder.objectRef<SessionGraphqlSchema>("Session");
1515

1616
export const SessionLoadable = builder.loadableObject(SessionRef, {
1717
description: "Representation of a Session",
@@ -25,11 +25,11 @@ export const SessionLoadable = builder.loadableObject(SessionRef, {
2525
title: t.exposeString("title", { nullable: false }),
2626
description: t.exposeString("description", { nullable: true }),
2727
startTimestamp: t.field({
28-
type: "Date",
28+
type: "DateTime",
2929
resolve: (root) => new Date(root.startTimestamp),
3030
}),
3131
endTimestamp: t.field({
32-
type: "Date",
32+
type: "DateTime",
3333
resolve: (root) => new Date(root.endTimestamp),
3434
}),
3535
speakers: t.field({

0 commit comments

Comments
 (0)