From 73aaf8ca2c73eb5fd074a942ae93c2034891393a Mon Sep 17 00:00:00 2001 From: Jason Gao Date: Tue, 10 Dec 2024 23:11:44 -0500 Subject: [PATCH] return a function from auth hooks to refresh the session/user --- packages/react/spec/auth/useSession.spec.ts | 51 ++++++++++++--------- packages/react/spec/auth/useUser.spec.ts | 21 +++++---- packages/react/src/auth/useAuth.ts | 4 +- packages/react/src/auth/useSession.ts | 35 ++++++++------ packages/react/src/auth/useSignOut.ts | 3 +- packages/react/src/auth/useUser.ts | 29 ++++++------ 6 files changed, 81 insertions(+), 62 deletions(-) diff --git a/packages/react/spec/auth/useSession.spec.ts b/packages/react/spec/auth/useSession.spec.ts index 4ea49f9c7..e620a23e6 100644 --- a/packages/react/spec/auth/useSession.spec.ts +++ b/packages/react/spec/auth/useSession.spec.ts @@ -62,10 +62,11 @@ describe("useSession", () => { } }" `); - expect(result.current.id).toEqual("123"); - expect(result.current.user!.id).toEqual("321"); - expect(result.current.user!.firstName).toEqual("Jane"); - expect(result.current.user!.lastName).toEqual("Doe"); + const [session] = result.current; + expect(session.id).toEqual("123"); + expect(session.user!.id).toEqual("321"); + expect(session.user!.firstName).toEqual("Jane"); + expect(session.user!.lastName).toEqual("Doe"); }); test("it returns the current session when the user is logged in and api client is passed", async () => { @@ -110,10 +111,11 @@ describe("useSession", () => { } }" `); - expect(result.current.id).toEqual("123"); - expect(result.current.user!.id).toEqual("321"); - expect(result.current.user!.firstName).toEqual("Jane"); - expect(result.current.user!.lastName).toEqual("Doe"); + const [session] = result.current; + expect(session.id).toEqual("123"); + expect(session.user!.id).toEqual("321"); + expect(session.user!.firstName).toEqual("Jane"); + expect(session.user!.lastName).toEqual("Doe"); }); test("it returns the current session when the user is logged in and api client with options is passed", async () => { @@ -140,9 +142,10 @@ describe("useSession", () => { } }" `); - expect(result.current.id).toEqual("123"); - expect(result.current.user?.id).toEqual("321"); - expect(result.current.user?.firstName).toEqual("Jane"); + const [session] = result.current; + expect(session.id).toEqual("123"); + expect(session.user?.id).toEqual("321"); + expect(session.user?.firstName).toEqual("Jane"); const { result: noUserResult, rerender: _noUserRerender } = renderHook( () => useSession(fullAuthApi, { filter: { user: { firstName: { equals: "Bob" } } } }), @@ -158,9 +161,10 @@ describe("useSession", () => { expectMockSignedOutUser(); rerender(); - expect(result.current).toBeDefined(); - expect(result.current.id).toEqual("123"); - expect(result.current.user).toBe(null); + const [session] = result.current; + expect(session).toBeDefined(); + expect(session.id).toEqual("123"); + expect(session.user).toBe(null); }); test("it returns the current session when the user is logged out and no options are passed", async () => { @@ -169,9 +173,10 @@ describe("useSession", () => { expectMockSignedOutUser(); rerender(); - expect(result.current).toBeDefined(); - expect(result.current.id).toEqual("123"); - expect(result.current.user).toBe(null); + const [session] = result.current; + expect(session).toBeDefined(); + expect(session.id).toEqual("123"); + expect(session.user).toBe(null); }); test("it returns the current session when the user is logged out and an api client with options is passed", async () => { @@ -180,9 +185,10 @@ describe("useSession", () => { expectMockSignedOutUser(); rerender(); - expect(result.current).toBeDefined(); - expect(result.current.id).toEqual("123"); - expect(result.current.user).toBe(null); + const [session] = result.current; + expect(session).toBeDefined(); + expect(session.id).toEqual("123"); + expect(session.user).toBe(null); }); test("it throws when the server responds with an error", async () => { @@ -214,7 +220,8 @@ describe("useSession", () => { expectMockSignedOutUser(); rerender(); - expect(result.current.id).toEqual("123"); - expect(result.current.user).toBeNull(); + const [session] = result.current; + expect(session.id).toEqual("123"); + expect(session.user).toBeNull(); }); }); diff --git a/packages/react/spec/auth/useUser.spec.ts b/packages/react/spec/auth/useUser.spec.ts index 4eb6a1882..cc3a121ea 100644 --- a/packages/react/spec/auth/useUser.spec.ts +++ b/packages/react/spec/auth/useUser.spec.ts @@ -61,9 +61,10 @@ describe("useUser", () => { } }" `); - expect(result.current.id).toEqual("321"); - expect(result.current.firstName).toEqual("Jane"); - expect(result.current.lastName).toEqual("Doe"); + const [user] = result.current; + expect(user.id).toEqual("321"); + expect(user.firstName).toEqual("Jane"); + expect(user.lastName).toEqual("Doe"); }); test("it returns the current user when the user is logged in with an api client passed", async () => { @@ -109,9 +110,10 @@ describe("useUser", () => { } }" `); - expect(result.current.id).toEqual("321"); - expect(result.current.firstName).toEqual("Jane"); - expect(result.current.lastName).toEqual("Doe"); + const [user] = result.current; + expect(user.id).toEqual("321"); + expect(user.firstName).toEqual("Jane"); + expect(user.lastName).toEqual("Doe"); }); test("it returns the current user when the user is logged in with an api client and options passed", async () => { @@ -141,8 +143,9 @@ describe("useUser", () => { } }" `); - expect(result.current.firstName).toEqual("Jane"); - expect(result.current).toMatchInlineSnapshot(` + const [user] = result.current; + expect(user.firstName).toEqual("Jane"); + expect(user).toMatchInlineSnapshot(` { "firstName": "Jane", "id": "321", @@ -157,7 +160,7 @@ describe("useUser", () => { expectMockSignedOutUser(); rerender(); - expect(result.current).toBe(null); + expect(result.current[0]).toBe(null); }); test("it throws when the server responds with an error", async () => { diff --git a/packages/react/src/auth/useAuth.ts b/packages/react/src/auth/useAuth.ts index 135020dad..0565e1b9a 100644 --- a/packages/react/src/auth/useAuth.ts +++ b/packages/react/src/auth/useAuth.ts @@ -52,8 +52,8 @@ export const useAuth = < isSignedIn: boolean; configuration: GadgetAuthConfiguration; } => { - const session = useSession(client); - const user = useUser(client); + const [session] = useSession(client); + const [user] = useUser(client); const context = useContext(GadgetConfigurationContext); if (!context) { diff --git a/packages/react/src/auth/useSession.ts b/packages/react/src/auth/useSession.ts index fc7355536..85bb3f832 100644 --- a/packages/react/src/auth/useSession.ts +++ b/packages/react/src/auth/useSession.ts @@ -7,6 +7,7 @@ import type { LimitToKnownKeys, Select, } from "@gadgetinc/api-client-core"; +import { useCallback } from "react"; import { useApi } from "../GadgetProvider.js"; import { useGet } from "../useGet.js"; import type { OptionsType, ReadOperationOptions } from "../utils.js"; @@ -35,20 +36,23 @@ export function useSession< >( client?: ClientType, options?: LimitToKnownKeys -): undefined extends ClientType - ? GadgetSession - : GadgetRecord< - Select< - Exclude["currentSession"]["get"]["schemaType"], null | undefined>, - DefaultSelection< - Exclude["currentSession"]["get"]["selectionType"], - Options, - Exclude["currentSession"]["get"]["defaultSelection"] & { - user: Exclude["user"]["findMany"]["defaultSelection"]; - } +): [ + undefined extends ClientType + ? GadgetSession + : GadgetRecord< + Select< + Exclude["currentSession"]["get"]["schemaType"], null | undefined>, + DefaultSelection< + Exclude["currentSession"]["get"]["selectionType"], + Options, + Exclude["currentSession"]["get"]["defaultSelection"] & { + user: Exclude["user"]["findMany"]["defaultSelection"]; + } + > > - > - > { + >, + () => void +] { const fallbackApi = useApi(); const api = client ?? (fallbackApi as ClientType); @@ -67,12 +71,13 @@ export function useSession< ...(restOptions ?? {}), }; - const [{ data: session, error }] = useGet(api.currentSession, opts); + const [{ data: session, error }, refresh] = useGet(api.currentSession, opts); + const refreshFunction = useCallback(() => refresh(opts), [refresh, opts]); if (error) throw error; if (!session) throw new Error("currentSession not found but should be present"); // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - return typeof client == "undefined" ? session : (session as any); + return [typeof client == "undefined" ? session : (session as any), refreshFunction]; } else { throw new Error("api client does not have a Session model"); } diff --git a/packages/react/src/auth/useSignOut.ts b/packages/react/src/auth/useSignOut.ts index b9a85bd32..93ee3f0a6 100644 --- a/packages/react/src/auth/useSignOut.ts +++ b/packages/react/src/auth/useSignOut.ts @@ -13,7 +13,7 @@ export const useSignOut = (opts?: { redirectOnSuccess?: boolean; redirectToPath? const redirectOnSuccess = opts?.redirectOnSuccess ?? true; const redirectToPath = opts?.redirectToPath; const api = useApi(); - const user = useUser(); + const [user, refreshUser] = useUser(); const context = useContext(GadgetConfigurationContext); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const { signOutActionApiIdentifier, signInPath } = context!.auth; @@ -38,6 +38,7 @@ export const useSignOut = (opts?: { redirectOnSuccess?: boolean; redirectToPath? if (!user) throw new Error("attempting to sign out when the user is not signed in"); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await signOutAction({ id: user.id }); + refreshUser(); }, [user, signOutAction]); } else { throw new Error(`missing configured signOutActionApiIdentifier '${signOutActionApiIdentifier}' on the \`api.user\` model manager.`); diff --git a/packages/react/src/auth/useUser.ts b/packages/react/src/auth/useUser.ts index 61689181d..c73d54e02 100644 --- a/packages/react/src/auth/useUser.ts +++ b/packages/react/src/auth/useUser.ts @@ -19,18 +19,21 @@ export function useUser< >( client?: ClientType, options?: LimitToKnownKeys -): undefined extends ClientType - ? GadgetRecord> - : GadgetRecord< - Select< - Exclude["user"]["findMany"]["schemaType"], null | undefined>, - DefaultSelection< - Exclude["user"]["findMany"]["selectionType"], - Options, - Exclude["user"]["findMany"]["defaultSelection"] +): [ + undefined extends ClientType + ? GadgetRecord> + : GadgetRecord< + Select< + Exclude["user"]["findMany"]["schemaType"], null | undefined>, + DefaultSelection< + Exclude["user"]["findMany"]["selectionType"], + Options, + Exclude["user"]["findMany"]["defaultSelection"] + > > - > - > { + >, + () => void +] { const fallbackApi = useApi() as any; const api = client ?? fallbackApi; @@ -48,6 +51,6 @@ export function useUser< user: select, }; } - const session = useSession(api, opts); - return session && session.getField("user"); + const [session, refreshSession] = useSession(api, opts); + return [session && session.getField("user"), refreshSession]; }