-
Notifications
You must be signed in to change notification settings - Fork 6
ENG-1719 List groups I'm a member of #1011
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
Changes from 7 commits
a8c3953
d35722e
868bac6
c07b6d5
00b769d
1294a8d
b561143
fe1af4b
77d236d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { ListGroups } from "~/components/auth/ListGroups"; | ||
|
|
||
| const Page = () => ( | ||
| <main> | ||
| <div className="mx-auto max-w-6xl space-y-12 px-6 py-12"> | ||
| <ListGroups /> | ||
| </div> | ||
| </main> | ||
| ); | ||
|
|
||
| export default Page; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import { createClient } from "~/utils/supabase/server"; | ||
| import { getSessionUserData } from "~/utils/supabase/account"; | ||
| import Link from "next/link"; | ||
| import { Tables } from "@repo/database/dbTypes"; | ||
| import internalError from "~/utils/internalErrorSsr"; | ||
|
|
||
| type GroupData = Tables<"my_groups">; | ||
|
|
||
| export const ListGroups = async () => { | ||
| let groupData: GroupData[] | null = null; | ||
| let adminData: Record<string, boolean> = {}; | ||
| let userName: string | undefined; | ||
| let error: string | undefined; | ||
|
|
||
| try { | ||
| const client = await createClient(); | ||
| const userData = await getSessionUserData(client); | ||
| if (!userData) { | ||
| throw new Error("Not logged in.\nPlease log in from application."); | ||
| } | ||
| const { name, type, id } = userData; | ||
| if (type === "anonymous") userName = "Space " + name; | ||
| else if (type === "group") userName = "group " + name; | ||
| else if (type === "person") userName = name; | ||
| const groupResponse = await client.from("my_groups").select(); | ||
| if (groupResponse.error) { | ||
| internalError({ | ||
| error: groupResponse.error, | ||
| }); | ||
| throw new Error("Could not access Discourse Graphs"); | ||
| } | ||
| groupData = groupResponse.data; | ||
| const membershipReq = await client | ||
| .from("group_membership") | ||
| .select("group_id,admin") | ||
| .eq("member_id", id); | ||
| if (membershipReq.error) { | ||
| internalError({ | ||
| error: membershipReq.error, | ||
| }); | ||
| throw new Error("Could not access Discourse Graphs"); | ||
| } | ||
| adminData = Object.fromEntries( | ||
| // eslint-disable-next-line @typescript-eslint/naming-convention | ||
| membershipReq.data.map(({ group_id, admin }) => [ | ||
| group_id, | ||
| admin || false, | ||
| ]), | ||
| ); | ||
| } catch (e) { | ||
| error = e instanceof Error ? e.message : "An unknown error occured"; | ||
| } | ||
|
|
||
| return ( | ||
| <> | ||
| <div className="text-right text-sm"> | ||
| {userName ? <p>Logged in as {userName}</p> : ""} | ||
| </div> | ||
| <div> | ||
| {error ? ( | ||
| "Error: " + error | ||
| ) : groupData === null ? ( | ||
| "Error" // we should have had an error in that case | ||
| ) : groupData.length === 0 ? ( | ||
| <p>You are not part of any group.</p> | ||
| ) : ( | ||
| <> | ||
| <p>Your groups:</p> | ||
| <ul className="list-inside list-disc space-y-2"> | ||
| {groupData.map((d) => ( | ||
| <li key={d.id}> | ||
| {adminData[d.id || ""] ? ( | ||
| <Link href={"group/" + d.id!}>{d.name}</Link> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Use a root-relative URL here (for example Useful? React with 👍 / 👎. |
||
| ) : ( | ||
| d.name | ||
| )} | ||
| </li> | ||
| ))} | ||
| </ul> | ||
| </> | ||
| )} | ||
| </div> | ||
| </> | ||
| ); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| import assert from "assert"; | ||
| import { describe, it, beforeAll, afterAll } from "vitest"; | ||
| import { createClient } from "@supabase/supabase-js"; | ||
| import type { Database } from "@repo/database/dbTypes"; | ||
| import type { DGSupabaseClient } from "@repo/database/lib/client"; | ||
| import { | ||
| fetchOrCreateSpaceDirect, | ||
| spaceAnonUserEmail, | ||
| } from "@repo/database/lib/contextFunctions"; | ||
| import { createGroup } from "../../app/utils/supabase/account"; | ||
|
|
||
| const SUPABASE_URL = process.env.SUPABASE_URL!; | ||
| const ANON_KEY = process.env.SUPABASE_PUBLISHABLE_KEY!; | ||
| const SERVICE_KEY = process.env.SUPABASE_SECRET_KEY!; | ||
| const PASSWORD = "abcdefgh"; | ||
|
|
||
| const freshClient = (): DGSupabaseClient => | ||
| createClient<Database, "public">(SUPABASE_URL, ANON_KEY); | ||
|
|
||
| const serviceClient = () => | ||
| createClient<Database, "public">(SUPABASE_URL, SERVICE_KEY); | ||
|
|
||
| const signedInClient = async (spaceId: number): Promise<DGSupabaseClient> => { | ||
| const client = freshClient(); | ||
| const { error } = await client.auth.signInWithPassword({ | ||
| email: spaceAnonUserEmail("Roam", spaceId), | ||
| password: PASSWORD, | ||
| }); | ||
| if (error) throw new Error(`Sign-in failed: ${error.message}`); | ||
| return client; | ||
| }; | ||
|
|
||
| describe("list group members flow", { tags: ["database"] }, () => { | ||
| let spaceId1: number; | ||
| let spaceId2: number; | ||
| let spaceAccountUuid1: string; | ||
| let spaceAccountUuid2: string; | ||
| let client1: DGSupabaseClient; | ||
| let client2: DGSupabaseClient; | ||
| let createdGroupId: string | null = null; | ||
|
|
||
| beforeAll(async () => { | ||
| const s1 = await fetchOrCreateSpaceDirect({ | ||
| name: "vitest-s1", | ||
| url: "https://roamresearch.com/#/app/vitest-s1", | ||
| platform: "Roam", | ||
| password: PASSWORD, | ||
| }); | ||
| if (!s1.data) | ||
| throw new Error(`Failed to create space 1: ${s1.error?.message}`); | ||
| spaceId1 = s1.data.id; | ||
| client1 = await signedInClient(spaceId1); | ||
| assert(client1); | ||
| const accountReq1 = await client1 | ||
| .from("PlatformAccount") | ||
| .select("id,dg_account") | ||
| .eq( | ||
| "account_local_id", | ||
| `roam-${spaceId1}-anon@database.discoursegraphs.com`, | ||
| ) | ||
| .maybeSingle(); | ||
| assert(!accountReq1.error); | ||
| assert(accountReq1.data); | ||
| assert(accountReq1.data.dg_account); | ||
| spaceAccountUuid1 = accountReq1.data.dg_account; | ||
| const s2 = await fetchOrCreateSpaceDirect({ | ||
| name: "vitest-s2", | ||
| url: "https://roamresearch.com/#/app/vitest-s2", | ||
| platform: "Roam", | ||
| password: PASSWORD, | ||
| }); | ||
| if (!s2.data) | ||
| throw new Error(`Failed to create space 2: ${s2.error?.message}`); | ||
| spaceId2 = s2.data.id; | ||
| client2 = await signedInClient(spaceId2); | ||
| assert(client2); | ||
| const accountReq2 = await client2 | ||
| .from("PlatformAccount") | ||
| .select("id,dg_account") | ||
| .eq( | ||
| "account_local_id", | ||
| `roam-${spaceId2}-anon@database.discoursegraphs.com`, | ||
| ) | ||
| .maybeSingle(); | ||
| assert(!accountReq2.error); | ||
| assert(accountReq2.data); | ||
| assert(accountReq2.data.dg_account); | ||
| spaceAccountUuid2 = accountReq2.data.dg_account; | ||
| }); | ||
|
|
||
| afterAll(async () => { | ||
| if (createdGroupId) | ||
| await serviceClient().auth.admin.deleteUser(createdGroupId); | ||
| if (spaceAccountUuid1) | ||
| await serviceClient().auth.admin.deleteUser(spaceAccountUuid1); | ||
| if (spaceAccountUuid2) | ||
| await serviceClient().auth.admin.deleteUser(spaceAccountUuid2); | ||
| if (spaceId1) | ||
| await serviceClient().from("Space").delete().eq("id", spaceId1); | ||
| if (spaceId2) | ||
| await serviceClient().from("Space").delete().eq("id", spaceId2); | ||
| }); | ||
|
|
||
| it("lists group members", async () => { | ||
| // Step 1: user1 creates a group | ||
| const groupId = await createGroup(client1, "vitest-invite-group"); | ||
| assert(groupId !== null, "createGroup should return a group ID"); | ||
| createdGroupId = groupId; | ||
|
|
||
| // Step 2: Add another member | ||
| const { error: errorAddMember } = await client1 | ||
| .from("group_membership") | ||
| .insert({ | ||
| member_id: spaceAccountUuid2, // eslint-disable-line @typescript-eslint/naming-convention | ||
| group_id: groupId, // eslint-disable-line @typescript-eslint/naming-convention | ||
| admin: false, | ||
| }); | ||
| assert(!errorAddMember); | ||
|
|
||
| const groupResponse = await client2.from("my_groups").select(); | ||
| assert(!groupResponse.error); | ||
| assert(groupResponse.data !== null); | ||
| assert(groupResponse.data.length === 1); | ||
| assert(groupResponse.data[0]!.id === groupId); | ||
| }); | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.