From 91e4031f1a3634108eb7f8cae024ab8400118eef Mon Sep 17 00:00:00 2001 From: Douglas DUTEIL Date: Sun, 17 Nov 2024 12:26:55 +0100 Subject: [PATCH] refactor(www): use new trpc root provider --- apps/www/app/(auth)/signup/student/page.tsx | 5 +- .../(public)/following/_client/List.tsx | 2 +- .../(main)/exchanges/@categories/Filter.tsx | 5 +- apps/www/app/(main)/exchanges/layout.tsx | 17 +- apps/www/app/(main)/exchanges/page.tsx | 6 +- apps/www/app/(main)/navbar.tsx | 4 +- .../navbar/notification_indicator.client.tsx | 18 +-- .../components/shell/AuthSessionProvider.tsx | 3 + apps/www/components/shell/RootProviders.tsx | 4 +- .../exchanges/filter}/Exchanges_Filter.tsx | 0 apps/www/widgets/exchanges/filter/index.ts | 3 + .../exchanges/list}/List.tsx | 8 +- .../exchanges/list}/SearchForm.tsx | 0 apps/www/widgets/exchanges/list/index.ts | 4 + .../infra/database/seeding/inbox/greetings.ts | 35 +++++ packages/@1/infra/database/seeding/index.ts | 1 + packages/@1/infra/database/testing/index.ts | 16 ++ packages/@1/modules/exchange/api/src/index.ts | 27 ++-- .../exchange/api/src/public/by_id.e2e.test.ts | 35 +---- .../modules/exchange/api/src/public/by_id.ts | 98 ++++++------ .../exchange/api/src/public/find.e2e.test.ts | 40 ++--- .../modules/exchange/api/src/public/find.ts | 148 +++++++++--------- .../notification/api/src/count_unread.ts | 76 --------- .../count_unread.e2e.test.ts.snap | 7 + .../src/count_unread/count_unread.e2e.test.ts | 131 ++++++++++++++++ .../api/src/count_unread/count_unread.ts | 78 +++++++++ .../api/src/count_unread/index.ts | 3 + .../@1/modules/notification/api/src/find.ts | 93 ----------- .../find/__snapshots__/find.e2e.test.ts.snap | 29 ++++ .../api/src/find/find.e2e.test.ts | 57 +++++++ .../modules/notification/api/src/find/find.ts | 81 ++++++++++ .../notification/api/src/find/index.ts | 3 + .../@1/modules/notification/api/src/index.ts | 14 +- packages/@1/modules/trpc/src/testing/index.ts | 13 ++ serkels.code-workspace | 11 ++ 35 files changed, 677 insertions(+), 398 deletions(-) rename apps/www/{app/(main)/exchanges/_client => widgets/exchanges/filter}/Exchanges_Filter.tsx (100%) create mode 100644 apps/www/widgets/exchanges/filter/index.ts rename apps/www/{app/(main)/exchanges/_client => widgets/exchanges/list}/List.tsx (93%) rename apps/www/{app/(main)/exchanges/_client => widgets/exchanges/list}/SearchForm.tsx (100%) create mode 100644 apps/www/widgets/exchanges/list/index.ts create mode 100644 packages/@1/infra/database/seeding/inbox/greetings.ts delete mode 100644 packages/@1/modules/notification/api/src/count_unread.ts create mode 100644 packages/@1/modules/notification/api/src/count_unread/__snapshots__/count_unread.e2e.test.ts.snap create mode 100644 packages/@1/modules/notification/api/src/count_unread/count_unread.e2e.test.ts create mode 100644 packages/@1/modules/notification/api/src/count_unread/count_unread.ts create mode 100644 packages/@1/modules/notification/api/src/count_unread/index.ts delete mode 100644 packages/@1/modules/notification/api/src/find.ts create mode 100644 packages/@1/modules/notification/api/src/find/__snapshots__/find.e2e.test.ts.snap create mode 100644 packages/@1/modules/notification/api/src/find/find.e2e.test.ts create mode 100644 packages/@1/modules/notification/api/src/find/find.ts create mode 100644 packages/@1/modules/notification/api/src/find/index.ts diff --git a/apps/www/app/(auth)/signup/student/page.tsx b/apps/www/app/(auth)/signup/student/page.tsx index 5bbafed62..dd43dbaa5 100644 --- a/apps/www/app/(auth)/signup/student/page.tsx +++ b/apps/www/app/(auth)/signup/student/page.tsx @@ -1,6 +1,5 @@ // -import { TrpcRootProvider } from ":trpc/root"; import { TRPC_SSR } from ":trpc/server"; import Form from ":widgets/auth/SignUpForm"; import { auth } from "@1.modules/auth.next"; @@ -38,9 +37,7 @@ async function Page() { return (
- -
- +
); } diff --git a/apps/www/app/(main)/door/[code]/(public)/following/_client/List.tsx b/apps/www/app/(main)/door/[code]/(public)/following/_client/List.tsx index 087df98ce..ef49a6dec 100644 --- a/apps/www/app/(main)/door/[code]/(public)/following/_client/List.tsx +++ b/apps/www/app/(main)/door/[code]/(public)/following/_client/List.tsx @@ -2,6 +2,7 @@ import { ProfileAvatarMedia } from ":components/avatar"; import type { inferInfiniteQueryObserverSuccessResult } from ":components/inferQueryResult"; +import { Loading_Placeholder } from ":components/placeholder/Loading_Placeholder"; import { TRPC_React } from ":trpc/client"; import type { AvatarProfile } from "@1.modules/profile.domain"; import { EmptyList, LoadMoreButton } from "@1.ui/react/async"; @@ -9,7 +10,6 @@ import { button_item } from "@1.ui/react/button/atom"; import { Spinner } from "@1.ui/react/spinner"; import Link from "next/link"; import { P, match } from "ts-pattern"; -import Loading_Placeholder from "../loading"; // diff --git a/apps/www/app/(main)/exchanges/@categories/Filter.tsx b/apps/www/app/(main)/exchanges/@categories/Filter.tsx index aaf1f4207..c8a82216f 100644 --- a/apps/www/app/(main)/exchanges/@categories/Filter.tsx +++ b/apps/www/app/(main)/exchanges/@categories/Filter.tsx @@ -2,7 +2,7 @@ import { useSyncSearchQuery } from ":components/hooks/useSyncSearchQuery"; import { useAutoClose } from ":components/shell/AsideFilter.client"; -import { TRPC_React } from ":trpc/client"; +import { trpc_client } from "@1.infra/trpc/react-query/client"; import { CATEGORY_ALL } from "@1.modules/category.domain"; import { FilterRadioList } from "@1.ui/react/form/FilterRadioList"; import { usePathname, useRouter } from "next/navigation"; @@ -13,7 +13,8 @@ import { useMemo } from "react"; export function Filter() { const { query, setQuery } = useSyncSearchQuery("category"); const { query: filter } = useSyncSearchQuery("f"); - const { data: categories_, status } = TRPC_React.category.exchange.useQuery(); + const { data: categories_, status } = + trpc_client.category.exchange.useQuery(); const { close } = useAutoClose(); const pathname = usePathname() ?? ""; const router = useRouter(); diff --git a/apps/www/app/(main)/exchanges/layout.tsx b/apps/www/app/(main)/exchanges/layout.tsx index d6f0fb8c4..20f1affc5 100644 --- a/apps/www/app/(main)/exchanges/layout.tsx +++ b/apps/www/app/(main)/exchanges/layout.tsx @@ -1,19 +1,10 @@ /// import { AsideFilter } from ":components/shell/AsideFilter"; +import { Exchanges_Filter } from ":widgets/exchanges/filter"; +import { SearchForm } from ":widgets/exchanges/list"; import { Grid } from "@1.ui/react/grid"; -import InputSearch from "@1.ui/react/input/InputSearch"; -import dynamic from "next/dynamic"; import type { PropsWithChildren, ReactNode } from "react"; -import { Exchanges_Filter } from "./_client/Exchanges_Filter"; - -// - -const SearchForm = dynamic(() => import("./_client/SearchForm"), { - loading() { - return ; - }, -}); // @@ -37,9 +28,9 @@ export default async function Layout({ {categories}
{children}
- */} ); } diff --git a/apps/www/app/(main)/exchanges/page.tsx b/apps/www/app/(main)/exchanges/page.tsx index 8ff7d0d6a..25ec083db 100644 --- a/apps/www/app/(main)/exchanges/page.tsx +++ b/apps/www/app/(main)/exchanges/page.tsx @@ -1,8 +1,9 @@ // import { TRPC_Hydrate } from ":trpc/server"; +import { List } from ":widgets/exchanges/list"; import { trpc_server } from "@1.infra/trpc/react-query/server"; -import { getServerSession } from "@1.modules/auth.next"; +import { auth } from "@1.modules/auth.next"; import { Exchange_Filter, type ExchangeSearchParams, @@ -12,7 +13,6 @@ import { PlusBox } from "@1.ui/react/icons"; import { link } from "@1.ui/react/link/atom"; import type { Metadata, ResolvingMetadata } from "next"; import Link from "next/link"; -import List from "./_client/List"; // @@ -61,7 +61,7 @@ export default async function Page({ } async function NewExchangeSection() { - const session = await getServerSession(); + const session = await auth(); if (!session) return null; if (session.profile.role !== "STUDENT") return null; diff --git a/apps/www/app/(main)/navbar.tsx b/apps/www/app/(main)/navbar.tsx index a17982c30..e0fd2f7da 100644 --- a/apps/www/app/(main)/navbar.tsx +++ b/apps/www/app/(main)/navbar.tsx @@ -7,7 +7,7 @@ import { } from ":components/navbar/notification_indicator.client"; import { MobileNavBar } from ":components/shell/MobileNavBar"; import { TRPC_SSR } from ":trpc/server"; -import { getServerSession } from "@1.modules/auth.next"; +import { auth } from "@1.modules/auth.next"; import { PROFILE_ROLES, type AuthProfile } from "@1.modules/profile.domain"; import { Avatar } from "@1.modules/profile.ui"; import { Grid } from "@1.ui/react/grid"; @@ -40,7 +40,7 @@ export default function UserBar() { } async function UserNavGroup({ className }: ComponentPropsWithoutRef<"nav">) { - const session = await getServerSession(); + const session = await auth(); const { base, icon } = user_nav_group_variants({ size: { initial: "xsmall", diff --git a/apps/www/components/navbar/notification_indicator.client.tsx b/apps/www/components/navbar/notification_indicator.client.tsx index 6fa2f0722..654fab426 100644 --- a/apps/www/components/navbar/notification_indicator.client.tsx +++ b/apps/www/components/navbar/notification_indicator.client.tsx @@ -1,6 +1,6 @@ "use client"; -import { TRPC_React } from ":trpc/client"; +import { trpc_client } from "@1.infra/trpc/react-query/client"; import { useSession } from "@1.modules/auth.next/react"; import { NotificationGroup } from "@1.modules/notification.domain"; import { DotIndicator } from "@1.modules/notification.ui/DotIndicator"; @@ -11,8 +11,8 @@ import { useAsync, useUpdateEffect } from "@react-hookz/web"; export function Notification_DotIndicator() { const { status } = useSession(); - const utils = TRPC_React.useUtils(); - const { data: count_unread } = TRPC_React.notification.count_unread.useQuery( + const utils = trpc_client.useUtils(); + const { data: count_unread } = trpc_client.notification.count_unread.useQuery( {}, { enabled: status === "authenticated", @@ -43,9 +43,9 @@ export function Notification_DotIndicator() { export function MessageNews_DotIndicator() { const { status } = useSession(); - const utils = TRPC_React.useUtils(); + const utils = trpc_client.useUtils(); const { data: count_unread, dataUpdatedAt } = - TRPC_React.notification.count_unread.useQuery( + trpc_client.notification.count_unread.useQuery( { type: NotificationGroup.Enum.INBOX, }, @@ -64,9 +64,9 @@ export function MessageNews_DotIndicator() { export function ExchangeNews_DotIndicator() { const { status } = useSession(); - const utils = TRPC_React.useUtils(); + const utils = trpc_client.useUtils(); const { data: count_unread, dataUpdatedAt } = - TRPC_React.notification.count_unread.useQuery( + trpc_client.notification.count_unread.useQuery( { type: NotificationGroup.Enum.EXCHANGE, }, @@ -85,7 +85,7 @@ export function ExchangeNews_DotIndicator() { export function NewsInMessage_Indicator() { const { status } = useSession(); - const { data: count_unread } = TRPC_React.notification.count_unread.useQuery( + const { data: count_unread } = trpc_client.notification.count_unread.useQuery( { type: NotificationGroup.Enum.INBOX, }, @@ -96,7 +96,7 @@ export function NewsInMessage_Indicator() { export function NewsInExchange_Indicator() { const { status } = useSession(); - const { data: count_unread } = TRPC_React.notification.count_unread.useQuery( + const { data: count_unread } = trpc_client.notification.count_unread.useQuery( { type: NotificationGroup.Enum.EXCHANGE, }, diff --git a/apps/www/components/shell/AuthSessionProvider.tsx b/apps/www/components/shell/AuthSessionProvider.tsx index f06fb80b2..f73c178fa 100644 --- a/apps/www/components/shell/AuthSessionProvider.tsx +++ b/apps/www/components/shell/AuthSessionProvider.tsx @@ -1,9 +1,12 @@ "use client"; + import type { Session } from "@1.modules/auth.next"; import { SessionProvider } from "@1.modules/auth.next/react"; import { sendGAEvent } from "@next/third-parties/google"; import { useEffect, type PropsWithChildren } from "react"; +// + export function AuthSessionProvider({ children, session, diff --git a/apps/www/components/shell/RootProviders.tsx b/apps/www/components/shell/RootProviders.tsx index eda311e4f..e674decde 100644 --- a/apps/www/components/shell/RootProviders.tsx +++ b/apps/www/components/shell/RootProviders.tsx @@ -1,7 +1,7 @@ "use client"; import { LegalProvider } from ":components/terms/context"; -import { TrpcProvider } from ":trpc/client"; +import { TrpcRootProvider } from ":trpc/root"; import { SessionProvider } from "@1.modules/auth.next/react"; import { useMediaQuery } from "@react-hookz/web"; import { @@ -27,7 +27,7 @@ export function RootProviders({ children }: PropsWithChildren) { - {children} + {children} diff --git a/apps/www/app/(main)/exchanges/_client/Exchanges_Filter.tsx b/apps/www/widgets/exchanges/filter/Exchanges_Filter.tsx similarity index 100% rename from apps/www/app/(main)/exchanges/_client/Exchanges_Filter.tsx rename to apps/www/widgets/exchanges/filter/Exchanges_Filter.tsx diff --git a/apps/www/widgets/exchanges/filter/index.ts b/apps/www/widgets/exchanges/filter/index.ts new file mode 100644 index 000000000..f36d6ef5d --- /dev/null +++ b/apps/www/widgets/exchanges/filter/index.ts @@ -0,0 +1,3 @@ +// + +export { Exchanges_Filter } from "./Exchanges_Filter"; diff --git a/apps/www/app/(main)/exchanges/_client/List.tsx b/apps/www/widgets/exchanges/list/List.tsx similarity index 93% rename from apps/www/app/(main)/exchanges/_client/List.tsx rename to apps/www/widgets/exchanges/list/List.tsx index d73f0d4a7..1f5c6e86c 100644 --- a/apps/www/app/(main)/exchanges/_client/List.tsx +++ b/apps/www/widgets/exchanges/list/List.tsx @@ -1,8 +1,9 @@ "use client"; import type { inferInfiniteQueryObserverSuccessResult } from ":components/inferQueryResult"; -import { TRPC_React } from ":trpc/client"; +import { Loading_Placeholder } from ":components/placeholder/Loading_Placeholder"; import { Exchange_Card } from ":widgets/exchanges/card"; +import { trpc_client } from "@1.infra/trpc/react-query/client"; import { useSession } from "@1.modules/auth.next/react"; import type { Entity } from "@1.modules/core/domain"; import { Exchange_Filter, type Exchange } from "@1.modules/exchange.domain"; @@ -13,7 +14,6 @@ import { useSearchParams } from "next/navigation"; import { useEffect, type ComponentProps, type ReactNode } from "react"; import { match, P } from "ts-pattern"; import type { z } from "zod"; -import Loading_Placeholder from "../loading"; // @@ -22,7 +22,7 @@ function useQueryExchanges(input: { filter: z.infer | undefined; search: string | undefined; }) { - return TRPC_React.exchanges.find.useInfiniteQuery(input, { + return trpc_client.exchanges.find.useInfiniteQuery(input, { getNextPageParam: ({ next_cursor }) => next_cursor, keepPreviousData: true, }); @@ -116,7 +116,7 @@ function Item({ id }: Entity) { // function useQueryExchangeById(id: string) { - return TRPC_React.exchanges.by_id.useQuery(id); + return trpc_client.exchanges.by_id.useQuery(id); } function Exchange_byId({ diff --git a/apps/www/app/(main)/exchanges/_client/SearchForm.tsx b/apps/www/widgets/exchanges/list/SearchForm.tsx similarity index 100% rename from apps/www/app/(main)/exchanges/_client/SearchForm.tsx rename to apps/www/widgets/exchanges/list/SearchForm.tsx diff --git a/apps/www/widgets/exchanges/list/index.ts b/apps/www/widgets/exchanges/list/index.ts new file mode 100644 index 000000000..23d98aa2c --- /dev/null +++ b/apps/www/widgets/exchanges/list/index.ts @@ -0,0 +1,4 @@ +// + +export { default as List } from "./List"; +export { default as SearchForm } from "./SearchForm"; diff --git a/packages/@1/infra/database/seeding/inbox/greetings.ts b/packages/@1/infra/database/seeding/inbox/greetings.ts new file mode 100644 index 000000000..175a5cb82 --- /dev/null +++ b/packages/@1/infra/database/seeding/inbox/greetings.ts @@ -0,0 +1,35 @@ +// + +import { PrismaClient } from "#prisma/client"; + +// + +export async function create_greatings_message( + prisma: PrismaClient, + author_profile_id: string, + recipient_profile_id: string, + message = "Bonjour", +) { + await prisma.notification.create({ + data: { + created_at: new Date(), + id: "greatings_notification_id", + inbox_message: { + create: { + message: { + create: { + author: { connect: { id: recipient_profile_id } }, + content: message, + created_at: new Date(), + thread: { create: { id: "greatings_thread_id" } }, + updated_at: new Date(), + }, + }, + }, + }, + owner: { connect: { id: author_profile_id } }, + read_at: null, + type: "INBOX_NEW_MESSAGE", + }, + }); +} diff --git a/packages/@1/infra/database/seeding/index.ts b/packages/@1/infra/database/seeding/index.ts index 7f4dc9a6b..1d083d6ca 100644 --- a/packages/@1/infra/database/seeding/index.ts +++ b/packages/@1/infra/database/seeding/index.ts @@ -4,6 +4,7 @@ export * from "./category/film"; export * from "./category/masterclass"; export * from "./exchange/slap"; export * from "./exchange/the_creator"; +export * from "./inbox/greetings"; export * from "./opportunity/concert_20240915"; export * from "./partner/vulfpeck"; export * from "./studient/douglas"; diff --git a/packages/@1/infra/database/testing/index.ts b/packages/@1/infra/database/testing/index.ts index d74e55079..b7c636ff4 100644 --- a/packages/@1/infra/database/testing/index.ts +++ b/packages/@1/infra/database/testing/index.ts @@ -38,3 +38,19 @@ export async function empty_database() { await client.exec(`DROP SCHEMA public CASCADE;`); await client.exec(`CREATE SCHEMA public;`); } + +export async function database_status() { + console.log("๐Ÿ•ต๏ธ DATABASE STATUS"); + console.log({ + exchange_message_notification: + await prisma.exchangeMessageNotification.count(), + inbox_message_notification: await prisma.inboxMessageNotification.count(), + inbox_thread: await prisma.inboxThread.count(), + notification: await prisma.notification.count(), + profile_added_notification: await prisma.profileAddedNotification.count(), + profile: await prisma.profile.count(), + student: await prisma.student.count(), + thread: await prisma.thread.count(), + user: await prisma.user.count(), + }); +} diff --git a/packages/@1/modules/exchange/api/src/index.ts b/packages/@1/modules/exchange/api/src/index.ts index 29cbf03c6..cada983a7 100644 --- a/packages/@1/modules/exchange/api/src/index.ts +++ b/packages/@1/modules/exchange/api/src/index.ts @@ -5,11 +5,18 @@ import { Deal_Status_Schema, Exchange_Create_Schema, } from "@1.modules/exchange.domain"; -import { next_auth_procedure, procedure, router } from "@1.modules/trpc"; +import { + mergeRouters, + next_auth_procedure, + procedure, + router, +} from "@1.modules/trpc"; import { z } from "zod"; import { me } from "./me"; -import by_id from "./public/by_id"; -import find_router from "./public/find"; +import by_id_api_router from "./public/by_id"; +import find_api_router from "./public/find"; + +// const exchange_api_router = router({ // @@ -41,10 +48,6 @@ const exchange_api_router = router({ // - by_id, - - // - by_particitpant: next_auth_procedure .input( z.object({ @@ -133,11 +136,11 @@ const exchange_api_router = router({ }), // - - find: find_router, - - // }); -export default exchange_api_router; +export default mergeRouters( + by_id_api_router, + exchange_api_router, + find_api_router, +); export type ExchangeApiRouter = typeof exchange_api_router; diff --git a/packages/@1/modules/exchange/api/src/public/by_id.e2e.test.ts b/packages/@1/modules/exchange/api/src/public/by_id.e2e.test.ts index 8449f49da..01adab76a 100644 --- a/packages/@1/modules/exchange/api/src/public/by_id.e2e.test.ts +++ b/packages/@1/modules/exchange/api/src/public/by_id.e2e.test.ts @@ -8,45 +8,30 @@ import { create_slap_exchange, } from "@1.infra/database/seeding"; import prisma, { empty_database, migrate } from "@1.infra/database/testing"; -import { - create_nextauth_header, - createCallerFactory, - router, -} from "@1.modules/trpc"; -import { douglas_golden_nextauth_header } from "@1.modules/trpc/testing"; +import { createCallerFactory } from "@1.modules/trpc"; +import { douglas_student_session } from "@1.modules/trpc/testing"; import { afterEach, beforeAll, beforeEach, describe, expect, - setSystemTime, test, } from "bun:test"; -import exchange_by_id from "./by_id"; - -// - -const NEXTAUTH_SECRET = "๐Ÿ”‘"; - -beforeAll(() => { - process.env["NEXTAUTH_SECRET"] = NEXTAUTH_SECRET; -}); +import by_id_api_router from "./by_id"; // beforeAll(empty_database); beforeAll(migrate); -beforeAll(() => { - setSystemTime(new Date("2011-11-11")); -}); // describe("visitor", () => { test("should return a sanitize exchange", async () => { - const caller = createCallerFactory(router({ by_id: exchange_by_id })); + const caller = createCallerFactory(by_id_api_router); const trpc = caller({ + auth: () => null, prisma, } as any); const exchange = await trpc.by_id("slap_exchange_id"); @@ -55,16 +40,10 @@ describe("visitor", () => { }); describe("connected studient", () => { - let nextauth_header: Awaited>; - - beforeAll(async () => { - nextauth_header = douglas_golden_nextauth_header; - }); - test("should return a sanitize exchange", async () => { - const caller = createCallerFactory(router({ by_id: exchange_by_id })); + const caller = createCallerFactory(by_id_api_router); const trpc = caller({ - headers: { ...nextauth_header }, + auth: () => douglas_student_session, prisma, } as any); const exchange = await trpc.by_id("slap_exchange_id"); diff --git a/packages/@1/modules/exchange/api/src/public/by_id.ts b/packages/@1/modules/exchange/api/src/public/by_id.ts index f57388042..354b24648 100644 --- a/packages/@1/modules/exchange/api/src/public/by_id.ts +++ b/packages/@1/modules/exchange/api/src/public/by_id.ts @@ -1,63 +1,65 @@ // import { Deal_Status_Schema } from "@1.modules/exchange.domain"; -import { maybe_next_auth_procedure } from "@1.modules/trpc"; +import { maybe_session_procedure, router } from "@1.modules/trpc"; import { z } from "zod"; // -export default maybe_next_auth_procedure - .input(z.string()) - .use(async ({ ctx: { payload, prisma }, input: id, next }) => { - const profile_id = payload.profile?.id; - const get_exchange_by_id = prisma.exchange.findUniqueOrThrow({ - include: { - category: true, - return: true, - owner: { - select: { - profile: { - select: { id: true, name: true, image: true }, +export default router({ + by_id: maybe_session_procedure + .input(z.string()) + .use(async ({ ctx: { prisma, session }, input: id, next }) => { + const profile_id = session?.profile.id; + const get_exchange_by_id = prisma.exchange.findUniqueOrThrow({ + include: { + category: true, + return: true, + owner: { + select: { + profile: { + select: { id: true, name: true, image: true }, + }, + university: true, }, - university: true, }, - }, - deals: { - select: { - id: true, - status: true, - participant: { select: { profile: { select: { id: true } } } }, + deals: { + select: { + id: true, + status: true, + participant: { select: { profile: { select: { id: true } } } }, + }, + where: { status: Deal_Status_Schema.Enum.APPROVED }, }, - where: { status: Deal_Status_Schema.Enum.APPROVED }, }, - }, - where: { id }, - }); + where: { id }, + }); - /** - * Ensure to remove participant profile information for - */ - const sanitize = async (exchange: Awaited) => { - const { deals } = exchange; - return { - ...exchange, - deals: deals.map((deal) => ({ - ...deal, - participant: - deal.participant.profile.id === profile_id - ? deal.participant - : undefined, - })), + /** + * Ensure to remove participant profile information for + */ + const sanitize = async (exchange: Awaited) => { + const { deals } = exchange; + return { + ...exchange, + deals: deals.map((deal) => ({ + ...deal, + participant: + deal.participant.profile.id === profile_id + ? deal.participant + : undefined, + })), + }; }; - }; - // + // - return next({ - ctx: { get_exchange_by_id, sanitize }, - }); - }) - .query(async ({ ctx: { get_exchange_by_id, sanitize } }) => { - const exchange = await get_exchange_by_id; - return sanitize(exchange); - }); + return next({ + ctx: { get_exchange_by_id, sanitize }, + }); + }) + .query(async ({ ctx: { get_exchange_by_id, sanitize } }) => { + const exchange = await get_exchange_by_id; + return sanitize(exchange); + }), +}); diff --git a/packages/@1/modules/exchange/api/src/public/find.e2e.test.ts b/packages/@1/modules/exchange/api/src/public/find.e2e.test.ts index 16b7124ae..1baf1d5d1 100644 --- a/packages/@1/modules/exchange/api/src/public/find.e2e.test.ts +++ b/packages/@1/modules/exchange/api/src/public/find.e2e.test.ts @@ -7,13 +7,9 @@ import { create_slap_exchange, } from "@1.infra/database/seeding"; import prisma, { empty_database, migrate } from "@1.infra/database/testing"; +import { createCallerFactory } from "@1.modules/trpc"; import { - create_nextauth_header, - createCallerFactory, - router, -} from "@1.modules/trpc"; -import { - douglas_golden_nextauth_header, + douglas_student_session, NEXTAUTH_SECRET, } from "@1.modules/trpc/testing"; import { @@ -25,7 +21,7 @@ import { setSystemTime, test, } from "bun:test"; -import find from "./find"; +import find_api_router from "./find"; // @@ -45,8 +41,8 @@ beforeAll(() => { describe("visitor", () => { test("should return latest exchanges", async () => { - const caller = createCallerFactory(router({ find })); - const trpc = caller({ prisma } as any); + const caller = createCallerFactory(find_api_router); + const trpc = caller({ auth: () => null, prisma } as any); const exchange = await trpc.find({}); expect(exchange).toMatchSnapshot(); }); @@ -56,8 +52,8 @@ describe("visitor", () => { data: { is_active: false }, where: { id: "slap_exchange_id" }, }); - const caller = createCallerFactory(router({ find })); - const trpc = caller({ prisma } as any); + const caller = createCallerFactory(find_api_router); + const trpc = caller({ auth: () => null, prisma } as any); const exchange = await trpc.find({}); expect(exchange).toMatchSnapshot(); }); @@ -67,24 +63,18 @@ describe("visitor", () => { data: { expiry_date: new Date("2011-11-10") }, where: { id: "slap_exchange_id" }, }); - const caller = createCallerFactory(router({ find })); - const trpc = caller({ prisma } as any); + const caller = createCallerFactory(find_api_router); + const trpc = caller({ auth: () => null, prisma } as any); const exchange = await trpc.find({}); expect(exchange).toMatchSnapshot(); }); }); describe("connected studient", () => { - let nextauth_header: Awaited>; - - beforeAll(async () => { - nextauth_header = douglas_golden_nextauth_header; - }); - test("should return latest exchanges", async () => { - const caller = createCallerFactory(router({ find })); + const caller = createCallerFactory(find_api_router); const trpc = caller({ - headers: { ...nextauth_header }, + auth: () => douglas_student_session, prisma, } as any); const exchange = await trpc.find({}); @@ -96,9 +86,9 @@ describe("connected studient", () => { data: { blacklist: { create: { profile_id: "joedart_profile_id" } } }, where: { id: "douglas_profile_id" }, }); - const caller = createCallerFactory(router({ find })); + const caller = createCallerFactory(find_api_router); const trpc = caller({ - headers: { ...nextauth_header }, + auth: () => douglas_student_session, prisma, } as any); const exchange = await trpc.find({}); @@ -110,9 +100,9 @@ describe("connected studient", () => { data: { blacklisted_by: { create: { owner_id: "joedart_profile_id" } } }, where: { id: "douglas_profile_id" }, }); - const caller = createCallerFactory(router({ find })); + const caller = createCallerFactory(find_api_router); const trpc = caller({ - headers: { ...nextauth_header }, + auth: () => douglas_student_session, prisma, } as any); const exchange = await trpc.find({}); diff --git a/packages/@1/modules/exchange/api/src/public/find.ts b/packages/@1/modules/exchange/api/src/public/find.ts index c5c44028a..dab65fef4 100644 --- a/packages/@1/modules/exchange/api/src/public/find.ts +++ b/packages/@1/modules/exchange/api/src/public/find.ts @@ -2,7 +2,11 @@ import type { Prisma } from "@1.infra/database"; import { Exchange_Filter } from "@1.modules/exchange.domain"; -import { maybe_next_auth_procedure, with_next_cursor } from "@1.modules/trpc"; +import { + maybe_session_procedure, + router, + with_next_cursor, +} from "@1.modules/trpc"; import { endOfYesterday } from "date-fns"; import { match, P } from "ts-pattern"; import { z } from "zod"; @@ -19,84 +23,88 @@ const input_schema = z.object({ // -export default maybe_next_auth_procedure - .input(input_schema) - .query(async ({ ctx: { payload, prisma }, input }) => { - const { limit, category, cursor, search } = input; - const nerrow = match({ payload, input }) - .with( - { payload: { profile: { role: "STUDENT" } } }, - ({ - payload: { - profile: { id: profile_id }, - }, - input: { filter }, - }) => profile_filter(profile_id, filter), - ) - .otherwise(() => ({})); +export default router({ + find: maybe_session_procedure + .input(input_schema) + .query(async ({ ctx: { session, prisma }, input }) => { + const { limit, category, cursor, search } = input; + const nerrow = match({ session, input }) + .with( + { session: { profile: { role: "STUDENT" } } }, + ({ + session: { + profile: { id: profile_id }, + }, + input: { filter }, + }) => profile_filter(profile_id, filter), + ) + .otherwise(() => ({})); - const search_where: Prisma.ExchangeWhereInput = { - OR: [ - { title: { contains: search ?? "", mode: "insensitive" } }, - { - description: { contains: search ?? "", mode: "insensitive" }, - }, - { location: { contains: search ?? "", mode: "insensitive" } }, - { - owner: { - profile: { - name: { contains: search ?? "", mode: "insensitive" }, + const search_where: Prisma.ExchangeWhereInput = { + OR: [ + { title: { contains: search ?? "", mode: "insensitive" } }, + { + description: { contains: search ?? "", mode: "insensitive" }, + }, + { location: { contains: search ?? "", mode: "insensitive" } }, + { + owner: { + profile: { + name: { contains: search ?? "", mode: "insensitive" }, + }, }, }, - }, - ], - }; + ], + }; - const active_exchanges_where: Prisma.ExchangeWhereInput = { - is_active: true, - OR: [{ expiry_date: null }, { expiry_date: { gte: endOfYesterday() } }], - }; - const category_where: Prisma.ExchangeWhereInput = category - ? { category: { slug: category } } - : {}; + const active_exchanges_where: Prisma.ExchangeWhereInput = { + is_active: true, + OR: [{ expiry_date: null }, { expiry_date: { gte: endOfYesterday() } }], + }; + const category_where: Prisma.ExchangeWhereInput = category + ? { category: { slug: category } } + : {}; - const where_not_in_profile_blacklist = match(payload.profile) - .with( - { id: P.select() }, - (profile_id): Prisma.ExchangeWhereInput => ({ - AND: [ - { owner: { profile: { blacklist: { none: { profile_id } } } } }, - { - owner: { - profile: { blacklisted_by: { none: { owner_id: profile_id } } }, + const where_not_in_profile_blacklist = match(session?.profile) + .with( + { id: P.select() }, + (profile_id): Prisma.ExchangeWhereInput => ({ + AND: [ + { owner: { profile: { blacklist: { none: { profile_id } } } } }, + { + owner: { + profile: { + blacklisted_by: { none: { owner_id: profile_id } }, + }, + }, }, - }, - ], - }), - ) - .otherwise(() => ({})); + ], + }), + ) + .otherwise(() => ({})); - const items = await prisma.exchange.findMany({ - ...(cursor ? { cursor: { id: cursor } } : {}), - orderBy: [ - { expiry_date: { sort: "asc", nulls: "last" } }, - { created_at: "desc" }, - { id: "desc" }, - ], - take: limit + 1, - where: { - AND: [ - where_not_in_profile_blacklist, - search_where, - active_exchanges_where, - category_where, - nerrow, + const items = await prisma.exchange.findMany({ + ...(cursor ? { cursor: { id: cursor } } : {}), + orderBy: [ + { expiry_date: { sort: "asc", nulls: "last" } }, + { created_at: "desc" }, + { id: "desc" }, ], - }, - }); + take: limit + 1, + where: { + AND: [ + where_not_in_profile_blacklist, + search_where, + active_exchanges_where, + category_where, + nerrow, + ], + }, + }); - return with_next_cursor(limit, items)((item) => item.id); - }); + return with_next_cursor(limit, items)((item) => item.id); + }), +}); function profile_filter( profile_id: string, diff --git a/packages/@1/modules/notification/api/src/count_unread.ts b/packages/@1/modules/notification/api/src/count_unread.ts deleted file mode 100644 index 29157cc0f..000000000 --- a/packages/@1/modules/notification/api/src/count_unread.ts +++ /dev/null @@ -1,76 +0,0 @@ -// - -import { NotificationType } from "@1.infra/database"; -import { NotificationGroup } from "@1.modules/notification.domain"; -import { next_auth_procedure } from "@1.modules/trpc"; -import { P, match } from "ts-pattern"; -import { z } from "zod"; - -// - -const EXCHANGE_NOTIFICATIONS = [ - NotificationType.EXCHANGE_COMPLETED, - NotificationType.EXCHANGE_NEW_MESSAGE, - NotificationType.EXCHANGE_NEW_PARTICIPANT, -]; - -// - -export default next_auth_procedure - .input( - z.object({ - type: NotificationGroup.optional(), - }), - ) - .query(async ({ input: { type }, ctx: { prisma, payload } }) => { - const { id: owner_id } = payload.profile; - - const sub_count = await match(type) - .with(NotificationGroup.Enum.INBOX, async () => { - const data = await prisma.inboxMessageNotification.findMany({ - select: { message: { select: { thread_id: true } } }, - where: { - notification: { - owner_id, - read_at: null, - type: NotificationType.INBOX_NEW_MESSAGE, - }, - }, - }); - return new Set(data.map(({ message }) => message?.thread_id)); - }) - .with(NotificationGroup.Enum.EXCHANGE, async () => { - const data = await prisma.exchangeMessageNotification.findMany({ - select: { message: { select: { thread_id: true } } }, - where: { - notification: { - owner_id, - read_at: null, - type: { - in: EXCHANGE_NOTIFICATIONS, - }, - }, - }, - }); - return new Set(data.map(({ message }) => message?.thread_id)); - }) - .otherwise(() => Promise.resolve(null)); - - if (sub_count) return sub_count.size; - - const notification_type = match(type) - .with(NotificationGroup.Enum.EXCHANGE, () => ({ - type: { - in: EXCHANGE_NOTIFICATIONS, - }, - })) - .with(NotificationGroup.Enum.INBOX, () => ({ - type: NotificationType.INBOX_NEW_MESSAGE, - })) - .with(P.nullish, () => ({})) - .exhaustive(); - - return prisma.notification.count({ - where: { owner_id, read_at: null, ...notification_type }, - }); - }); diff --git a/packages/@1/modules/notification/api/src/count_unread/__snapshots__/count_unread.e2e.test.ts.snap b/packages/@1/modules/notification/api/src/count_unread/__snapshots__/count_unread.e2e.test.ts.snap new file mode 100644 index 000000000..850fb2b5d --- /dev/null +++ b/packages/@1/modules/notification/api/src/count_unread/__snapshots__/count_unread.e2e.test.ts.snap @@ -0,0 +1,7 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`return count of all unread notifications 1`] = `6`; + +exports[`return count of exchange notifications 1`] = `1`; + +exports[`return count of inbox notifications 1`] = `1`; diff --git a/packages/@1/modules/notification/api/src/count_unread/count_unread.e2e.test.ts b/packages/@1/modules/notification/api/src/count_unread/count_unread.e2e.test.ts new file mode 100644 index 000000000..3ee5933e7 --- /dev/null +++ b/packages/@1/modules/notification/api/src/count_unread/count_unread.e2e.test.ts @@ -0,0 +1,131 @@ +// + +import { + create_douglas_student, + create_greatings_message, + create_joedart_student, +} from "@1.infra/database/seeding"; +import prisma, { empty_database, migrate } from "@1.infra/database/testing"; +import { createCallerFactory } from "@1.modules/trpc"; +import { douglas_student_session } from "@1.modules/trpc/testing"; +import { afterEach, beforeAll, beforeEach, expect, test } from "bun:test"; +import api_router from "./count_unread"; + +// + +beforeAll(empty_database); +beforeAll(migrate); + +// + +test("return count of all unread notifications", async () => { + const caller = createCallerFactory(api_router); + const trpc = caller({ + auth: () => douglas_student_session, + prisma, + } as any); + const data = await trpc.count_unread({}); + expect(data).toMatchSnapshot(); +}); + +test("return count of exchange notifications", async () => { + const caller = createCallerFactory(api_router); + const trpc = caller({ + auth: () => douglas_student_session, + prisma, + } as any); + const data = await trpc.count_unread({ type: "EXCHANGE" }); + expect(data).toMatchSnapshot(); +}); + +test("return count of inbox notifications", async () => { + const caller = createCallerFactory(api_router); + const trpc = caller({ + auth: () => douglas_student_session, + prisma, + } as any); + const data = await trpc.count_unread({ type: "INBOX" }); + expect(data).toMatchSnapshot(); +}); + +// + +beforeEach(async function seed() { + await create_douglas_student(prisma); + await create_joedart_student(prisma); + await create_greatings_message( + prisma, + "douglas_profile_id", + "joedart_profile_id", + ); + + const { id: message_id } = await prisma.message.create({ + data: { + thread: { create: {} }, + author: { connect: { id: "douglas_profile_id" } }, + content: "World", + created_at: new Date(), + updated_at: new Date(), + }, + select: { id: true }, + }); + + await prisma.exchangeMessageNotification.create({ + data: { + message: { connect: { id: message_id } }, + notification: { + create: { + owner_id: "douglas_profile_id", + read_at: null, + type: "EXCHANGE_COMPLETED", + }, + }, + }, + }); + + await prisma.exchangeMessageNotification.create({ + data: { + message: { connect: { id: message_id } }, + notification: { + create: { + owner_id: "douglas_profile_id", + read_at: null, + type: "EXCHANGE_NEW_MESSAGE", + }, + }, + }, + }); + + await prisma.exchangeMessageNotification.create({ + data: { + message: { connect: { id: message_id } }, + notification: { + create: { + owner_id: "douglas_profile_id", + read_at: null, + type: "EXCHANGE_NEW_PARTICIPANT", + }, + }, + }, + }); + + await prisma.notification.createMany({ + data: [ + { + owner_id: "douglas_profile_id", + read_at: null, + type: "FORUM_NEW_AWNSER", + }, + { + owner_id: "douglas_profile_id", + read_at: null, + type: "PROFILE_ADDED", + }, + ], + }); +}); + +afterEach(async function empty_delete_entries() { + await prisma.thread.deleteMany(); + await prisma.user.deleteMany(); +}); diff --git a/packages/@1/modules/notification/api/src/count_unread/count_unread.ts b/packages/@1/modules/notification/api/src/count_unread/count_unread.ts new file mode 100644 index 000000000..1878ccd94 --- /dev/null +++ b/packages/@1/modules/notification/api/src/count_unread/count_unread.ts @@ -0,0 +1,78 @@ +// + +import { NotificationType } from "@1.infra/database"; +import { NotificationGroup } from "@1.modules/notification.domain"; +import { router, session_procedure } from "@1.modules/trpc"; +import { P, match } from "ts-pattern"; +import { z } from "zod"; + +// + +const EXCHANGE_NOTIFICATIONS = [ + NotificationType.EXCHANGE_COMPLETED, + NotificationType.EXCHANGE_NEW_MESSAGE, + NotificationType.EXCHANGE_NEW_PARTICIPANT, +]; + +// + +export default router({ + count_unread: session_procedure + .input( + z.object({ + type: NotificationGroup.optional(), + }), + ) + .query(async ({ input: { type }, ctx: { prisma, session } }) => { + const { id: owner_id } = session.profile; + + const sub_count = await match(type) + .with(NotificationGroup.Enum.INBOX, async () => { + const data = await prisma.inboxMessageNotification.findMany({ + select: { message: { select: { thread_id: true } } }, + where: { + notification: { + owner_id, + read_at: null, + type: NotificationType.INBOX_NEW_MESSAGE, + }, + }, + }); + return new Set(data.map(({ message }) => message?.thread_id)); + }) + .with(NotificationGroup.Enum.EXCHANGE, async () => { + const data = await prisma.exchangeMessageNotification.findMany({ + select: { message: { select: { thread_id: true } } }, + where: { + notification: { + owner_id, + read_at: null, + type: { + in: EXCHANGE_NOTIFICATIONS, + }, + }, + }, + }); + return new Set(data.map(({ message }) => message?.thread_id)); + }) + .otherwise(() => Promise.resolve(null)); + + if (sub_count) return sub_count.size; + + const notification_type = match(type) + .with(NotificationGroup.Enum.EXCHANGE, () => ({ + type: { + in: EXCHANGE_NOTIFICATIONS, + }, + })) + .with(NotificationGroup.Enum.INBOX, () => ({ + type: NotificationType.INBOX_NEW_MESSAGE, + })) + .with(P.nullish, () => ({})) + .exhaustive(); + + return prisma.notification.count({ + where: { owner_id, read_at: null, ...notification_type }, + }); + }), +}); diff --git a/packages/@1/modules/notification/api/src/count_unread/index.ts b/packages/@1/modules/notification/api/src/count_unread/index.ts new file mode 100644 index 000000000..436aa930e --- /dev/null +++ b/packages/@1/modules/notification/api/src/count_unread/index.ts @@ -0,0 +1,3 @@ +// + +export { default as count_unread_api_router } from "./count_unread"; diff --git a/packages/@1/modules/notification/api/src/find.ts b/packages/@1/modules/notification/api/src/find.ts deleted file mode 100644 index 525b362d2..000000000 --- a/packages/@1/modules/notification/api/src/find.ts +++ /dev/null @@ -1,93 +0,0 @@ -// - -import type { Prisma } from "@1.infra/database"; -import { next_auth_procedure } from "@1.modules/trpc"; -import { z } from "zod"; - -// - -export default next_auth_procedure - .input( - z.object({ - cursor: z.string().optional(), - limit: z.number().min(1).max(10).default(10), - }), - ) - .query(async ({ input, ctx: { prisma, payload } }) => { - const { cursor, limit } = input; - const { id: owner_id } = payload.profile; - const message_select: Prisma.MessageSelect = { - author: { select: { name: true, id: true, image: true } }, - thread_id: true, - }; - - const items = await prisma.notification.findMany({ - ...(cursor ? { cursor: { id: cursor } } : {}), - orderBy: { created_at: "desc" }, - take: limit + 1, - where: { owner_id }, - include: { - inbox_message: { - select: { - message: { - select: message_select, - }, - }, - }, - exchange_message: { - select: { - exchange: { - select: { - id: true, - owner: { - select: { - profile: { select: { id: true, name: true, image: true } }, - }, - }, - title: true, - }, - }, - message: { - select: message_select, - }, - }, - }, - forum_message: { - select: { - answer: { - select: { - owner: { - select: { - profile_id: true, - profile: { select: { name: true, image: true } }, - }, - }, - parent_id: true, - parent: { select: { id: true, title: true } }, - }, - }, - answer_id: true, - }, - }, - profile_added: { - select: { - profile: { - select: { - name: true, - id: true, - image: true, - }, - }, - }, - }, - }, - }); - - let prevCursor: typeof cursor | undefined = undefined; - if (items.length > limit) { - const nextItem = items.pop()!; - prevCursor = nextItem.id; - } - - return { data: items, prevCursor }; - }); diff --git a/packages/@1/modules/notification/api/src/find/__snapshots__/find.e2e.test.ts.snap b/packages/@1/modules/notification/api/src/find/__snapshots__/find.e2e.test.ts.snap new file mode 100644 index 000000000..d09c0a53c --- /dev/null +++ b/packages/@1/modules/notification/api/src/find/__snapshots__/find.e2e.test.ts.snap @@ -0,0 +1,29 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`should return latest notifications 1`] = ` +{ + "data": [ + { + "created_at": 2011-11-11T00:00:00.000Z, + "exchange_message": null, + "forum_message": null, + "id": "greatings_notification_id", + "inbox_message": { + "message": { + "author": { + "id": "joedart_profile_id", + "image": "https://picsum.photos/200/300", + "name": "Joe Dart", + }, + "thread_id": "greatings_thread_id", + }, + }, + "owner_id": "douglas_profile_id", + "profile_added": null, + "read_at": null, + "type": "INBOX_NEW_MESSAGE", + }, + ], + "prevCursor": undefined, +} +`; diff --git a/packages/@1/modules/notification/api/src/find/find.e2e.test.ts b/packages/@1/modules/notification/api/src/find/find.e2e.test.ts new file mode 100644 index 000000000..4e1a465d4 --- /dev/null +++ b/packages/@1/modules/notification/api/src/find/find.e2e.test.ts @@ -0,0 +1,57 @@ +// + +import { + create_douglas_student, + create_greatings_message, + create_joedart_student, +} from "@1.infra/database/seeding"; +import prisma, { empty_database, migrate } from "@1.infra/database/testing"; +import { createCallerFactory } from "@1.modules/trpc"; +import { douglas_student_session } from "@1.modules/trpc/testing"; +import { + afterEach, + beforeAll, + beforeEach, + expect, + setSystemTime, + test, +} from "bun:test"; +import find_api_router from "./find"; + +// + +beforeAll(empty_database); +beforeAll(migrate); +beforeAll(() => { + setSystemTime(new Date("2011-11-11")); +}); + +// + +test("should return latest notifications", async () => { + const caller = createCallerFactory(find_api_router); + const trpc = caller({ + auth: () => douglas_student_session, + prisma, + } as any); + const exchange = await trpc.find({}); + expect(exchange).toMatchSnapshot(); +}); + +// + +beforeEach(async function seed() { + await create_douglas_student(prisma); + await create_joedart_student(prisma); + await create_greatings_message( + prisma, + "douglas_profile_id", + "joedart_profile_id", + ); +}); + +afterEach(async function empty_delete_entries() { + await prisma.category.deleteMany({}); + await prisma.notification.deleteMany({}); + await prisma.user.deleteMany({}); +}); diff --git a/packages/@1/modules/notification/api/src/find/find.ts b/packages/@1/modules/notification/api/src/find/find.ts new file mode 100644 index 000000000..4ed1dc09e --- /dev/null +++ b/packages/@1/modules/notification/api/src/find/find.ts @@ -0,0 +1,81 @@ +// + +import type { Prisma } from "@1.infra/database"; +import { + router, + session_procedure, + with_previous_cursor, +} from "@1.modules/trpc"; +import { z } from "zod"; + +// + +export default router({ + find: session_procedure + .input( + z.object({ + cursor: z.string().optional(), + limit: z.number().min(1).max(10).default(10), + }), + ) + .query(async ({ input, ctx: { prisma, session } }) => { + const { cursor, limit } = input; + const { id: owner_id } = session.profile; + const message_select: Prisma.MessageSelect = { + author: { select: { name: true, id: true, image: true } }, + thread_id: true, + }; + + const items = await prisma.notification.findMany({ + ...(cursor ? { cursor: { id: cursor } } : {}), + orderBy: { created_at: "desc" }, + take: limit + 1, + where: { owner_id }, + include: { + inbox_message: { select: { message: { select: message_select } } }, + exchange_message: { + select: { + exchange: { + select: { + id: true, + owner: { + select: { + profile: { + select: { id: true, name: true, image: true }, + }, + }, + }, + title: true, + }, + }, + message: { select: message_select }, + }, + }, + forum_message: { + select: { + answer: { + select: { + owner: { + select: { + profile_id: true, + profile: { select: { name: true, image: true } }, + }, + }, + parent_id: true, + parent: { select: { id: true, title: true } }, + }, + }, + answer_id: true, + }, + }, + profile_added: { + select: { + profile: { select: { name: true, id: true, image: true } }, + }, + }, + }, + }); + + return with_previous_cursor(limit, items)((item) => item.id); + }), +}); diff --git a/packages/@1/modules/notification/api/src/find/index.ts b/packages/@1/modules/notification/api/src/find/index.ts new file mode 100644 index 000000000..554151cdf --- /dev/null +++ b/packages/@1/modules/notification/api/src/find/index.ts @@ -0,0 +1,3 @@ +// + +export { default as find_api_router } from "./find"; diff --git a/packages/@1/modules/notification/api/src/index.ts b/packages/@1/modules/notification/api/src/index.ts index bb9ab9769..8421cfe50 100644 --- a/packages/@1/modules/notification/api/src/index.ts +++ b/packages/@1/modules/notification/api/src/index.ts @@ -1,16 +1,18 @@ // -import { router } from "@1.modules/trpc"; -import count_unread from "./count_unread"; -import find from "./find"; +import { mergeRouters, router } from "@1.modules/trpc"; +import { count_unread_api_router } from "./count_unread"; +import { find_api_router } from "./find"; import mark_as_read from "./mark_as_read"; // const notification_api_router = router({ - count_unread: count_unread, - find: find, mark_as_read: mark_as_read, }); -export default notification_api_router; +export default mergeRouters( + count_unread_api_router, + find_api_router, + notification_api_router, +); diff --git a/packages/@1/modules/trpc/src/testing/index.ts b/packages/@1/modules/trpc/src/testing/index.ts index 39e770a09..467a0d58c 100644 --- a/packages/@1/modules/trpc/src/testing/index.ts +++ b/packages/@1/modules/trpc/src/testing/index.ts @@ -1,11 +1,24 @@ // +import type { Session } from "@1.modules/auth.next"; import { create_nextauth_header } from "@1.modules/auth.next/jwt"; // export const NEXTAUTH_SECRET = "๐Ÿ”‘"; +export const douglas_student_session: Session = { + expires: "2022-12-31T23:59:59.999Z", + user: {}, + header: { NEXTAUTH_TOKEN: "๐Ÿ”‘" }, + profile: { + id: "douglas_profile_id", + image: "https://picsum.photos/222/333", + name: "Douglas", + role: "STUDENT", + }, +}; + export const douglas_golden_nextauth_header = await create_nextauth_header({ secret: "๐Ÿ”‘", token: { diff --git a/serkels.code-workspace b/serkels.code-workspace index 3a392e585..216e3ee0e 100644 --- a/serkels.code-workspace +++ b/serkels.code-workspace @@ -297,6 +297,17 @@ "problemMatcher": ["$tsc-watch"], "runOptions": { "instanceLimit": 1 }, }, + { + "label": "๐Ÿงช Update Test Snapshots : Current file", + "command": "bun test --update-snapshots ${fileDirname}/${fileBasename}", + "type": "shell", + "group": "build", + "options": { + "cwd": "${workspaceFolder:root}", + }, + "problemMatcher": ["$tsc-watch"], + "runOptions": { "instanceLimit": 1 }, + }, { "label": "๐Ÿงช Watch Test", "command": "bun test --watch",