From a8375b99c2a6336ca956ca635561834f87db2a1d Mon Sep 17 00:00:00 2001 From: lnn0q Date: Sat, 17 May 2025 17:16:26 +0300 Subject: [PATCH] added mappers, updated models --- app/client/pages/example.tsx | 46 -------- app/client/pages/index.tsx | 40 ++++--- app/server/graphql/graphql.ts | 1 + .../graphql/resolvers/bookOneResolver.ts | 13 --- .../graphql/resolvers/booksManyResolver.ts | 25 ---- .../resolvers/mappers/bookChapterMapper.ts | 23 ++++ .../graphql/resolvers/mappers/bookMapper.ts | 22 ++++ .../graphql/resolvers/mappers/reviewMapper.ts | 24 ++++ .../resolvers/mappers/workingMapper.ts | 65 +++++++++++ app/server/graphql/schema.graphql | 59 ++++++---- app/server/prisma/schema.prisma | 108 ++++++++++++++++++ 11 files changed, 306 insertions(+), 120 deletions(-) delete mode 100644 app/client/pages/example.tsx delete mode 100644 app/server/graphql/resolvers/bookOneResolver.ts delete mode 100644 app/server/graphql/resolvers/booksManyResolver.ts create mode 100644 app/server/graphql/resolvers/mappers/bookChapterMapper.ts create mode 100644 app/server/graphql/resolvers/mappers/bookMapper.ts create mode 100644 app/server/graphql/resolvers/mappers/reviewMapper.ts create mode 100644 app/server/graphql/resolvers/mappers/workingMapper.ts diff --git a/app/client/pages/example.tsx b/app/client/pages/example.tsx deleted file mode 100644 index 4e1cb6a..0000000 --- a/app/client/pages/example.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { Box, Button, Stack, Text } from '@chakra-ui/react' -import { createFileRoute } from '@tanstack/react-router' - -import { gql, useQuery } from '@apollo/client' - -import { BooksManyQuery } from '@generated/types' - -export const Route = createFileRoute('/example')({ - component: Dashboard, -}) - -function Dashboard() { - const { data, refetch } = useQuery(getBooksQuery) - - return ( - - Hello world! - This is my template! - - {data && ( - <> - {data.booksMany.map((book: any) => ( - - {book.title} - {book.author} - - ))} - - )} - - - - - - ) -} - -const getBooksQuery = gql` - query BooksMany { - booksMany { - id - author - title - } - } -` diff --git a/app/client/pages/index.tsx b/app/client/pages/index.tsx index 075b289..3ac6e6a 100644 --- a/app/client/pages/index.tsx +++ b/app/client/pages/index.tsx @@ -32,16 +32,16 @@ function RouteComponent() { return ( - { - alert(value); - }} - onChange={value => setWorkingSearchValue(value)} - /> - {data != null ? ( <> + { + alert(value); + }} + onChange={value => setWorkingSearchValue(value)} + /> + ({ ...item, @@ -58,7 +58,15 @@ function RouteComponent() { /> ) : ( - + <> + + + + + + + + )} @@ -70,21 +78,21 @@ const query = gql` workingMany(input: $input) { items { id + type title description author { id username } - progressStatus updatedAt - chapters { - total - } - wordsTotal + progressStatus ratingTotal - reviews { - total + reviewsTotal + book { + id + chaptersTotal + wordsTotal } } total diff --git a/app/server/graphql/graphql.ts b/app/server/graphql/graphql.ts index d16f7a8..9acb166 100644 --- a/app/server/graphql/graphql.ts +++ b/app/server/graphql/graphql.ts @@ -17,6 +17,7 @@ export const Query: QueryResolvers = { bookOne: BookOneResolver, identity: IdentityResolver, userOne: UserOneResolver + // workingMany: }; export const Mutation: MutationResolvers = { diff --git a/app/server/graphql/resolvers/bookOneResolver.ts b/app/server/graphql/resolvers/bookOneResolver.ts deleted file mode 100644 index fe9011a..0000000 --- a/app/server/graphql/resolvers/bookOneResolver.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { IdentityContext } from '@app/server/identity'; -import { Book, QueryBookOneArgs } from '@generated/types'; -import { books } from './booksManyResolver'; - -export async function BookOneResolver( - _: any, - { id }: QueryBookOneArgs, - { identity }: IdentityContext -): Promise { - const book = books.filter(item => item.id === id)[0]; - - return book; -} diff --git a/app/server/graphql/resolvers/booksManyResolver.ts b/app/server/graphql/resolvers/booksManyResolver.ts deleted file mode 100644 index 5cf16dc..0000000 --- a/app/server/graphql/resolvers/booksManyResolver.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { IdentityContext } from '@app/server/identity'; -import { Book } from '@generated/types'; - -export const books = [ - { - id: '1', - title: 'some-title-1', - author: 'someone' - }, - { - id: '2', - title: 'some-title-1', - author: 'someone' - } -]; - -export async function BooksManyResolver( - _: any, - __: any, - { req, res, identity }: IdentityContext -): Promise { - if (identity == null) throw new Error('Unauthorized'); - - return books; -} diff --git a/app/server/graphql/resolvers/mappers/bookChapterMapper.ts b/app/server/graphql/resolvers/mappers/bookChapterMapper.ts new file mode 100644 index 0000000..b4713f2 --- /dev/null +++ b/app/server/graphql/resolvers/mappers/bookChapterMapper.ts @@ -0,0 +1,23 @@ +import { call } from '@app/common'; +import { prisma } from '@app/server/prisma/client'; +import { BookChapter as BookChapterSchema } from '@generated/types'; +import { BookChapter } from '@prisma/client'; + +export async function toBookChapterMapping(model: BookChapter): Promise { + return { + id: model.id, + title: model.title, + createdAt: model.createdAt.toISOString(), + updatedAt: model.createdAt.toISOString(), + foreword: model.foreword, + footnote: model.footnote, + reviewsTotal: await call(async () => { + const reviewsCount = await prisma.review.count({ + where: { + bookChapterId: model.id + } + }); + return reviewsCount; + }) + }; +} diff --git a/app/server/graphql/resolvers/mappers/bookMapper.ts b/app/server/graphql/resolvers/mappers/bookMapper.ts new file mode 100644 index 0000000..eb2904a --- /dev/null +++ b/app/server/graphql/resolvers/mappers/bookMapper.ts @@ -0,0 +1,22 @@ +import { call } from '@app/common'; +import { prisma } from '@app/server/prisma/client'; +import { Book as BookSchema } from '@generated/types'; +import { Book } from '@prisma/client'; + +export async function toBookSchema(model: Book): Promise { + return { + id: model.id, + workingId: model.workingId, + createdAt: model.createdAt.toISOString(), + updatedAt: model.updatedAt.toISOString(), + wordsTotal: model.wordsTotal, + chaptersTotal: await call(async () => { + const chaptersCount = await prisma.bookChapter.count({ + where: { + bookId: model.id + } + }); + return chaptersCount; + }) + }; +} diff --git a/app/server/graphql/resolvers/mappers/reviewMapper.ts b/app/server/graphql/resolvers/mappers/reviewMapper.ts new file mode 100644 index 0000000..b9beb55 --- /dev/null +++ b/app/server/graphql/resolvers/mappers/reviewMapper.ts @@ -0,0 +1,24 @@ +import { Review } from '@prisma/client'; +import { Review as ReviewSchema } from '@generated/types'; +import { toUserSchema } from './userMapper'; +import { call } from '@app/common'; +import { prisma } from '@app/server/prisma/client'; + +export async function toReviewSchema(model: Review): Promise { + return { + id: model.id, + author: await call(async () => { + const userModel = await prisma.user.findUniqueOrThrow({ + where: { + id: model.userId + } + }); + return toUserSchema(userModel); + }), + text: model.text, + //todo: ratings implementation + ratingTotal: await call(async () => { + return 0; + }) + }; +} diff --git a/app/server/graphql/resolvers/mappers/workingMapper.ts b/app/server/graphql/resolvers/mappers/workingMapper.ts new file mode 100644 index 0000000..ddc46bd --- /dev/null +++ b/app/server/graphql/resolvers/mappers/workingMapper.ts @@ -0,0 +1,65 @@ +import { assertUnreachable } from '@app/client/helpers'; +import { call } from '@app/common'; +import { prisma } from '@app/server/prisma/client'; +import { + WorkingProgressStatus as WorkingProgressStatusSchema, + Working as WorkingSchema, + WorkingType as WorkingTypeSchema +} from '@generated/types'; +import { Working, WorkingProgressStatus, WorkingType } from '@prisma/client'; +import { toUserSchema } from './userMapper'; + +export async function toWorkingSchema(model: Working): Promise { + return { + id: model.id, + title: model.title, + description: model.description, + author: await call(async () => { + const userModel = await prisma.user.findUniqueOrThrow({ where: { id: model.authorId } }); + return toUserSchema(userModel); + }), + createdAt: model.createdAt.toISOString(), + updatedAt: model.updatedAt.toISOString(), + type: call(() => { + ensureWorkingType(model.type); + return model.type; + }), + progressStatus: call(() => { + ensureWorkingProgressStatus(model.progressStatus); + return model.progressStatus; + }), + //todo: ratings implementation + //todo: book/notes implementation + ratingTotal: await call(async () => { + const ratingsCount = await prisma.rating.count({ + where: { + workingId: model.id + } + }); + return ratingsCount; + }), + reviewsTotal: await call(async () => { + const reviewsCount = await prisma.review.count({ + where: { + workingId: model.id + } + }); + return reviewsCount; + }) + }; +} + +function ensureWorkingType(value: WorkingType): asserts value is WorkingTypeSchema { + if (value == WorkingType.book) return; + if (value == WorkingType.notepad) return; + assertUnreachable(value); +} + +function ensureWorkingProgressStatus( + value: WorkingProgressStatus +): asserts value is WorkingProgressStatusSchema { + if (value == WorkingProgressStatus.inProgress) return; + if (value == WorkingProgressStatus.frozen) return; + if (value == WorkingProgressStatus.complete) return; + assertUnreachable(value); +} diff --git a/app/server/graphql/schema.graphql b/app/server/graphql/schema.graphql index 100412f..d307e16 100644 --- a/app/server/graphql/schema.graphql +++ b/app/server/graphql/schema.graphql @@ -1,8 +1,6 @@ scalar Void type Query { - booksMany: [Book!]! - bookOne(id: ID!): Book! identity: Identity! userOne(id: ID!): User! workingMany(input: WorkingManyInput!): WorkingManyOutput! @@ -44,14 +42,6 @@ type User { updatedAt: String! } -type Book { - id: ID! - title: String! - author: String! -} - -input Working - input PaginationFilterInput { terms: String offset: Int @@ -60,12 +50,18 @@ input PaginationFilterInput { input WorkingManyInput { filter: PaginationFilterInput! + type: WorkingType! +} + +enum WorkingType { + book + notepad } enum WorkingProgressStatus { inProgress frozen - finished + complete } type WorkingManyOutput { @@ -80,35 +76,58 @@ type Working { author: User! createdAt: String! updatedAt: String! + type: WorkingType! + book: Book + notepad: Notepad progressStatus: WorkingProgressStatus! - wordsTotal: Int! ratingTotal: Int! - chapters: ChapterManyOutput! - reviews: ReviewManyOutput! + reviewsTotal: Int! } -type ChapterManyOutput { - items: [Chapter!]! - total: Int! +type Book { + id: ID! + workingId: ID! + createdAt: String! + updatedAt: String! + wordsTotal: Int! + chaptersTotal: Int! } -type Chapter { +type BookChapter { id: ID! title: String! createdAt: String! updatedAt: String! foreword: String! footnote: String! + reviewsTotal: Int! } -type ReviewManyOutput { - items: [Review!]! +input BookChapterManyInput { + filter: PaginationFilterInput! +} + +type BookChapterManyOutput { + items: [BookChapter!]! total: Int! } +type Notepad { + id: ID! +} + type Review { id: ID! author: User! text: String! ratingTotal: Int! } + +input ReviewManyInput { + filter: PaginationFilterInput! +} + +type ReviewManyOutput { + items: [Review!]! + total: Int! +} diff --git a/app/server/prisma/schema.prisma b/app/server/prisma/schema.prisma index bb96aeb..5f235c4 100644 --- a/app/server/prisma/schema.prisma +++ b/app/server/prisma/schema.prisma @@ -20,4 +20,112 @@ model User { refreshToken String? @unique @db.VarChar(255) isDarkMode Boolean @default(true) + + workings Working[] + reviews Review[] + ratings Rating[] +} + +model Working { + id String @id + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + title String @db.VarChar(255) + description String @db.MediumText + progressStatus WorkingProgressStatus @default(inProgress) + + type WorkingType + + author User @relation(fields: [authorId], references: [id]) + authorId String + + book Book? + + reviews Review[] + ratings Rating[] +} + +enum WorkingType { + book + notepad +} + +enum WorkingProgressStatus { + inProgress + frozen + complete +} + +model Book { + id String @id + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + wordsTotal Int @default(0) + + working Working @relation(fields: [workingId], references: [id]) + workingId String @unique @db.VarChar(255) + + chapters BookChapter[] +} + +model BookChapter { + id String @id + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + title String @db.VarChar(255) + foreword String @db.MediumText + footnote String @db.MediumText + + book Book @relation(fields: [bookId], references: [id]) + bookId String @db.VarChar(255) + + reviews Review[] + ratings Rating[] +} + +model Review { + id String @id + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + text String @db.MediumText + + user User @relation(fields: [userId], references: [id]) + userId String @db.VarChar(255) + + working Working? @relation(fields: [workingId], references: [id]) + workingId String? @db.VarChar(255) + + bookChapter BookChapter? @relation(fields: [bookChapterId], references: [id]) + bookChapterId String? @db.VarChar(255) +} + +enum RatingType { + up + down +} + +model Rating { + id String @id + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + type RatingType + + user User @relation(fields: [userId], references: [id]) + userId String @db.VarChar(255) + + working Working? @relation(fields: [workingId], references: [id]) + workingId String? @db.VarChar(255) + + bookChapter BookChapter? @relation(fields: [bookChapterId], references: [id]) + bookChapterId String? @db.VarChar(255) }