Skip to content

Commit

Permalink
feat(tr-odin): initial setup (#705)
Browse files Browse the repository at this point in the history
Co-authored-by: Can Sirin <[email protected]>
  • Loading branch information
cansirin and cansirin authored Dec 4, 2023
1 parent 53e0bed commit 0bdafec
Show file tree
Hide file tree
Showing 68 changed files with 6,815 additions and 55 deletions.
2 changes: 2 additions & 0 deletions apps/gql/loaders/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type Clients } from "~/clients";
import { createOdinLoaders } from "~/loaders/odin";
import { createPanoLoaders } from "./pano";
import { createSozlukLoaders } from "./sozluk";
import { createUserLoaders } from "./user";
Expand All @@ -10,5 +11,6 @@ export const createLoaders = (clients: Clients) => {
user: createUserLoaders(clients),
sozluk: createSozlukLoaders(),
pano: createPanoLoaders(clients),
odin: createOdinLoaders(),
};
};
90 changes: 90 additions & 0 deletions apps/gql/loaders/odin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import DataLoader from "dataloader";
import hash from "object-hash";

import { type Connection, type ConnectionArguments } from "@kampus/gql-utils/connection";
import { allLessons, type Lesson } from "@kampus/tr-odin-project-content";

import { applyPagination, generatePageInfo } from "~/features/relay/pagination";

export const createOdinLoaders = () => {
return {
lesson: createLessonLoader(),
lessons: createLessonsLoader(),
};
};

export type OdinLessonLoader = ReturnType<typeof createLessonLoader>;
export type OdinLessonsLoader = ReturnType<typeof createLessonsLoader>;

export const transformOdinLesson = (lesson: Lesson) => {
return {
__typename: "OdinLesson" as const,
id: lesson.id,
title: lesson.title,
body: {
raw: lesson.body.raw,
html: lesson.html,
},
};
};

export const transformOdinLessonsConnection = (connection: Connection<Lesson>) => ({
...connection,
nodes: connection.nodes.map(transformOdinLesson),
edges: connection.edges.map((edge) => ({ ...edge, node: transformOdinLesson(edge.node) })),
pageInfo: {
...connection.pageInfo,
endCursor: connection.pageInfo.endCursor ?? null,
startCursor: connection.pageInfo.startCursor ?? null,
},
totalCount: connection.totalCount,
});

const loadLesson = (id: string) => {
const lesson = allLessons.find((lesson) => {
return lesson.id === id;
});
if (!lesson) {
return null;
}
return Promise.resolve(lesson);
};

const createLessonLoader = () =>
new DataLoader<string, Lesson>(async (keys) => {
return await Promise.all(
keys.map(async (key) => {
const lesson = await loadLesson(key);
if (!lesson) {
return new Error(`Lesson not found for: ${key}`);
}
return lesson;
})
);
});

const createLessonsLoader = () =>
new DataLoader<ConnectionArguments, Connection<Lesson>, string>(
// eslint-disable-next-line @typescript-eslint/require-await
async (keys) => {
const results: Connection<Lesson>[] = [];

for (const key of keys) {
const nodes = applyPagination({ data: allLessons, ...key });
const edges = nodes.map((lesson) => ({ cursor: lesson.id, node: lesson }));

const result = {
nodes,
edges,
pageInfo: generatePageInfo({ data: allLessons, ...key }),
totalCount: allLessons.length,
};

results.push(result);
}
return results;
},
{
cacheKeyFn: (key) => hash(key),
}
);
1 change: 1 addition & 0 deletions apps/gql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@kampus/next-auth": "*",
"@kampus/prisma": "*",
"@kampus/sozluk-content": "*",
"@kampus/tr-odin-project-content": "*",
"@kampus/std": "*",
"dataloader": "2.2.2",
"graphql": "16.6.0",
Expand Down
33 changes: 33 additions & 0 deletions apps/gql/schema/resolvers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { assertNever } from "@kampus/std";
import { type Dictionary } from "@kampus/std/dictionary";

import { InvalidInput, NotAuthorized } from "~/features/errors";
import { transformOdinLesson, transformOdinLessonsConnection } from "~/loaders/odin";
import {
transformPanoComment,
transformPanoCommentConnection,
Expand Down Expand Up @@ -51,6 +52,7 @@ export const resolvers = {
sozluk: () => ({ term: null, terms: null }),
// @see PanoQuery field resolvers
pano: () => ({ post: null, posts: [], postsBySite: null, allPosts: null }),
odin: () => ({ lesson: null, lessons: null }),

node: async (_, args, { loaders }) => {
const id = parse<NodeTypename>(args.id);
Expand All @@ -64,6 +66,8 @@ export const resolvers = {
return transformPanoPost(await loaders.pano.post.byID.load(id.value));
case "PanoComment":
return transformPanoComment(await loaders.pano.comment.byID.load(id.value));
case "OdinLesson":
return transformOdinLesson(await loaders.odin.lesson.load(id.value));
default:
return assertNever(id.type);
}
Expand Down Expand Up @@ -186,6 +190,35 @@ export const resolvers = {
node: (edge) => edge.node,
cursor: (edge) => stringify("SozlukTerm", edge.cursor),
},

OdinQuery: {
lesson: async (_, args, { loaders }) =>
transformOdinLesson(await loaders.odin.lesson.load(args.id)),
lessons: async (_, args, { loaders }) => {
return transformOdinLessonsConnection(
await loaders.odin.lessons.load(parseConnectionArgs(args))
);
},
},
OdinLesson: {
id: (lesson) => stringify("OdinLesson", lesson.id),
title: (lesson) => lesson.title,
body: (lesson) => lesson.body,
},
OdinLessonBody: {
raw: (body) => body.raw,
html: (body) => body.html,
},
OdinLessonConnection: {
edges: (connection) => connection.edges,
pageInfo: (connection) => transformPageInfo("OdinLesson", connection.pageInfo),
totalCount: (connection) => connection.totalCount,
},
OdinLessonEdge: {
node: (edge) => edge.node,
cursor: (edge) => stringify("OdinLesson", edge.cursor),
},

PanoQuery: {
post: async (_, args, { loaders }) =>
transformPanoPost(await loaders.pano.post.byID.load(parse(args.id).value)),
Expand Down
56 changes: 53 additions & 3 deletions apps/gql/schema/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Query {
user(id: ID, username: String): User
sozluk: SozlukQuery!
pano: PanoQuery!
odin: OdinQuery!
}

type PageInfo {
Expand Down Expand Up @@ -75,7 +76,12 @@ type User implements Node & Actor {

type SozlukQuery {
term(id: ID!): SozlukTerm
terms(after: String, before: String, first: Int, last: Int): SozlukTermConnection
terms(
after: String
before: String
first: Int
last: Int
): SozlukTermConnection
}

type SozlukTerm implements Node {
Expand Down Expand Up @@ -131,7 +137,12 @@ type PanoPost implements Node & Upvotable {
content: String
createdAt: DateTime!
owner: User
comments(after: String, before: String, first: Int, last: Int): PanoCommentConnection
comments(
after: String
before: String
first: Int
last: Int
): PanoCommentConnection
commentCount: Int
upvoteCount: Int
isUpvotedByViewer: Boolean!
Expand All @@ -155,7 +166,12 @@ type PanoComment implements Node & Upvotable {
owner: User
post: PanoPost
parent: PanoComment
comments(after: String, before: String, first: Int, last: Int): PanoCommentConnection
comments(
after: String
before: String
first: Int
last: Int
): PanoCommentConnection
commentCount: Int
upvoteCount: Int
isUpvotedByViewer: Boolean!
Expand Down Expand Up @@ -284,3 +300,37 @@ type RemovePanoUpvotePayload {
node: PanoUpvote
error: PanoUpvoteError
}

### TR-ODIN-PROJECT

type OdinQuery {
lesson(id: ID!): OdinLesson
lessons(
after: String
before: String
first: Int
last: Int
): OdinLessonConnection
}

type OdinLesson implements Node {
id: ID!
title: String!
body: OdinLessonBody!
}

type OdinLessonBody {
raw: String!
html: String!
}

type OdinLessonConnection {
edges: [OdinLessonEdge!]
pageInfo: PageInfo!
totalCount: Int!
}

type OdinLessonEdge {
cursor: String!
node: OdinLesson
}
Loading

0 comments on commit 0bdafec

Please sign in to comment.