From c8c7bb90894d256ece3e6c393df23d37ddce4eaf Mon Sep 17 00:00:00 2001 From: simon-debruijn Date: Wed, 13 Apr 2022 17:33:27 +0200 Subject: [PATCH 01/29] Add useAuthenticatedQuery --- src/hooks/api/authenticated-query2.ts | 111 ++++++++++++++++++++++++++ src/pages/test.page.tsx | 52 ++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 src/hooks/api/authenticated-query2.ts create mode 100644 src/pages/test.page.tsx diff --git a/src/hooks/api/authenticated-query2.ts b/src/hooks/api/authenticated-query2.ts new file mode 100644 index 000000000..be21be997 --- /dev/null +++ b/src/hooks/api/authenticated-query2.ts @@ -0,0 +1,111 @@ +import { + QueryFunctionContext, + QueryKey, + useQuery, + UseQueryOptions, +} from 'react-query'; + +import { FetchError } from '@/utils/fetchFromApi'; +import { isTokenValid } from '@/utils/isTokenValid'; + +import { useCookiesWithOptions } from '../useCookiesWithOptions'; +import { Headers } from './types/Headers'; +import { useHeaders } from './useHeaders'; + +type QueryArguments = Record; + +type GenerateUniqueQueryKeyArguments = { + queryKey: QueryKey; + queryArguments: QueryArguments; +}; + +type GeneratedQueryKey = readonly [QueryKey, QueryArguments]; + +type AuthenticatedQueryFunctionContext = QueryFunctionContext & { + headers: Headers; +}; + +const generateQueryKey = ({ + queryKey, + queryArguments, +}: GenerateUniqueQueryKeyArguments): GeneratedQueryKey => { + if (Object.keys(queryArguments ?? {}).length > 0) { + return [queryKey, queryArguments]; + } + + return [queryKey, {}]; +}; + +type GetPreparedOptionsArguments = { + options: UseAuthenticatedQueryOptions; + isTokenPresent: boolean; + headers: Headers; +}; + +const getPreparedOptions = ({ + options, + isTokenPresent, + headers, +}: GetPreparedOptionsArguments): UseQueryOptions< + TQueryFnData, + TError, + TQueryFnData, + GeneratedQueryKey +> => { + const { + queryKey, + queryArguments, + queryFn, + enabled, + ...restOptions + } = options; + const generatedQueryKey = generateQueryKey({ + queryKey, + queryArguments, + }); + + const queryFunctionWithHeaders = async ( + context: QueryFunctionContext, + ) => { + return await queryFn( + context, + // @ts-expect-error + headers, + ); + }; + return { + ...restOptions, + queryKey: generatedQueryKey, + queryFn: queryFunctionWithHeaders, + enabled: isTokenPresent && !!enabled, + }; +}; + +type UseAuthenticatedQueryOptions = UseQueryOptions< + TQueryFnData, + TError, + TQueryFnData, + QueryKey +> & { + queryArguments?: QueryArguments; +}; + +const useAuthenticatedQuery = ( + options: UseAuthenticatedQueryOptions, +) => { + const headers = useHeaders(); + const { cookies } = useCookiesWithOptions(['token']); + + const preparedOptions = getPreparedOptions({ + options, + isTokenPresent: isTokenValid(cookies.token), + headers, + }); + + return useQuery( + preparedOptions, + ); +}; + +export { useAuthenticatedQuery }; +export type { AuthenticatedQueryFunctionContext }; diff --git a/src/pages/test.page.tsx b/src/pages/test.page.tsx new file mode 100644 index 000000000..42f238908 --- /dev/null +++ b/src/pages/test.page.tsx @@ -0,0 +1,52 @@ +import { useEffect } from 'react'; + +import { + AuthenticatedQueryFunctionContext, + useAuthenticatedQuery, +} from '@/hooks/api/authenticated-query2'; +import { Headers } from '@/hooks/api/types/Headers'; +import { Event } from '@/types/Event'; +import { fetchFromApi, isErrorObject } from '@/utils/fetchFromApi'; + +type GetEventByIdArguments = { + headers: Headers; + id: string; +}; + +const Test = () => { + const id = 'a9e9cdce-2e3c-4b17-9c6b-a7eb445252d1'; + + const getEventById = async ({ + queryKey, + headers, + }: AuthenticatedQueryFunctionContext) => { + const [key, { id }] = queryKey; + const res = await fetchFromApi({ + path: `/event/${id.toString()}`, + options: { + headers, + }, + }); + if (isErrorObject(res)) { + // eslint-disable-next-line no-console + return console.error(res); + } + return await res.json(); + }; + + const query = useAuthenticatedQuery({ + queryKey: ['events'], + queryFn: getEventById, + queryArguments: { id }, + enabled: !!id, + }); + + useEffect(() => { + query.data; + console.log(query.data); + }, [query.data]); + + return null; +}; + +export default Test; From ad56f497721cd30054f65f94d357ef50f046692c Mon Sep 17 00:00:00 2001 From: simon-debruijn Date: Wed, 13 Apr 2022 17:40:26 +0200 Subject: [PATCH 02/29] Add redirect when unauthorized --- src/hooks/api/authenticated-query2.ts | 31 +++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/hooks/api/authenticated-query2.ts b/src/hooks/api/authenticated-query2.ts index be21be997..e1fa63874 100644 --- a/src/hooks/api/authenticated-query2.ts +++ b/src/hooks/api/authenticated-query2.ts @@ -11,6 +11,7 @@ import { isTokenValid } from '@/utils/isTokenValid'; import { useCookiesWithOptions } from '../useCookiesWithOptions'; import { Headers } from './types/Headers'; import { useHeaders } from './useHeaders'; +import { useRouter } from 'next/router'; type QueryArguments = Record; @@ -25,6 +26,8 @@ type AuthenticatedQueryFunctionContext = QueryFunctionContext headers: Headers; }; +const isUnAuthorized = (status: number) => [401, 403].includes(status); + const generateQueryKey = ({ queryKey, queryArguments, @@ -94,7 +97,10 @@ const useAuthenticatedQuery = ( options: UseAuthenticatedQueryOptions, ) => { const headers = useHeaders(); - const { cookies } = useCookiesWithOptions(['token']); + const { cookies, removeAuthenticationCookies } = useCookiesWithOptions([ + 'token', + ]); + const router = useRouter(); const preparedOptions = getPreparedOptions({ options, @@ -102,9 +108,26 @@ const useAuthenticatedQuery = ( headers, }); - return useQuery( - preparedOptions, - ); + const result = useQuery< + TQueryFnData, + FetchError, + TQueryFnData, + GeneratedQueryKey + >(preparedOptions); + + if ( + isUnAuthorized(result?.error?.status) && + !router.asPath.startsWith('/login') && + router.asPath !== '/[...params]' + ) { + removeAuthenticationCookies(); + + router.push('/login'); + + return; + } + + return result; }; export { useAuthenticatedQuery }; From 2cac59492a1369f8313fbd6399257d45ab4af5bb Mon Sep 17 00:00:00 2001 From: simon-debruijn Date: Wed, 13 Apr 2022 17:42:29 +0200 Subject: [PATCH 03/29] Remove unnecessary typing --- src/hooks/api/authenticated-query2.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/hooks/api/authenticated-query2.ts b/src/hooks/api/authenticated-query2.ts index e1fa63874..bf80691b3 100644 --- a/src/hooks/api/authenticated-query2.ts +++ b/src/hooks/api/authenticated-query2.ts @@ -108,12 +108,7 @@ const useAuthenticatedQuery = ( headers, }); - const result = useQuery< - TQueryFnData, - FetchError, - TQueryFnData, - GeneratedQueryKey - >(preparedOptions); + const result = useQuery(preparedOptions); if ( isUnAuthorized(result?.error?.status) && From f550c18c47d85be49408b3d41c461a787fda63e8 Mon Sep 17 00:00:00 2001 From: simon-debruijn Date: Thu, 14 Apr 2022 10:44:18 +0200 Subject: [PATCH 04/29] Sort imports --- src/hooks/api/authenticated-query2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/api/authenticated-query2.ts b/src/hooks/api/authenticated-query2.ts index bf80691b3..f1cd235eb 100644 --- a/src/hooks/api/authenticated-query2.ts +++ b/src/hooks/api/authenticated-query2.ts @@ -1,3 +1,4 @@ +import { useRouter } from 'next/router'; import { QueryFunctionContext, QueryKey, @@ -11,7 +12,6 @@ import { isTokenValid } from '@/utils/isTokenValid'; import { useCookiesWithOptions } from '../useCookiesWithOptions'; import { Headers } from './types/Headers'; import { useHeaders } from './useHeaders'; -import { useRouter } from 'next/router'; type QueryArguments = Record; From 0b55d57d216dfdb1b4d2ff1ebeff10676e074b38 Mon Sep 17 00:00:00 2001 From: simon-debruijn Date: Thu, 14 Apr 2022 10:44:32 +0200 Subject: [PATCH 05/29] Add server side api call to test example --- src/pages/test.page.tsx | 66 ++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/src/pages/test.page.tsx b/src/pages/test.page.tsx index 42f238908..fc5226866 100644 --- a/src/pages/test.page.tsx +++ b/src/pages/test.page.tsx @@ -1,4 +1,6 @@ +import { NextApiRequest } from 'next'; import { useEffect } from 'react'; +import { dehydrate } from 'react-query/types/hydration'; import { AuthenticatedQueryFunctionContext, @@ -7,33 +9,34 @@ import { import { Headers } from '@/hooks/api/types/Headers'; import { Event } from '@/types/Event'; import { fetchFromApi, isErrorObject } from '@/utils/fetchFromApi'; +import { getApplicationServerSideProps } from '@/utils/getApplicationServerSideProps'; type GetEventByIdArguments = { headers: Headers; id: string; }; +const getEventById = async ({ + queryKey, + headers, +}: AuthenticatedQueryFunctionContext) => { + const [key, { id }] = queryKey; + const res = await fetchFromApi({ + path: `/event/${id.toString()}`, + options: { + headers, + }, + }); + if (isErrorObject(res)) { + // eslint-disable-next-line no-console + return console.error(res); + } + return await res.json(); +}; + const Test = () => { const id = 'a9e9cdce-2e3c-4b17-9c6b-a7eb445252d1'; - const getEventById = async ({ - queryKey, - headers, - }: AuthenticatedQueryFunctionContext) => { - const [key, { id }] = queryKey; - const res = await fetchFromApi({ - path: `/event/${id.toString()}`, - options: { - headers, - }, - }); - if (isErrorObject(res)) { - // eslint-disable-next-line no-console - return console.error(res); - } - return await res.json(); - }; - const query = useAuthenticatedQuery({ queryKey: ['events'], queryFn: getEventById, @@ -49,4 +52,31 @@ const Test = () => { return null; }; +export const getServerSideProps = getApplicationServerSideProps( + async ({ + req, + cookies, + queryClient, + }: { + req: NextApiRequest; + cookies: any; + queryClient: any; + }) => { + useAuthenticatedQuery({ + req, + queryClient, + queryKey: ['events'], + queryFn: getEventById, + queryArguments: { id: 'a9e9cdce-2e3c-4b17-9c6b-a7eb445252d1' }, + }); + + return { + props: { + dehydratedState: dehydrate(queryClient), + cookies, + }, + }; + }, +); + export default Test; From 637eecf9379e0536034633859135ece13be3cd0b Mon Sep 17 00:00:00 2001 From: simon-debruijn Date: Thu, 14 Apr 2022 11:54:32 +0200 Subject: [PATCH 06/29] Use separate prefetchAuthenticatedQuery function --- src/hooks/api/authenticated-query2.ts | 79 +++++++++++++++++++-------- src/pages/test.page.tsx | 37 ++++--------- 2 files changed, 68 insertions(+), 48 deletions(-) diff --git a/src/hooks/api/authenticated-query2.ts b/src/hooks/api/authenticated-query2.ts index f1cd235eb..d07411df1 100644 --- a/src/hooks/api/authenticated-query2.ts +++ b/src/hooks/api/authenticated-query2.ts @@ -1,5 +1,9 @@ +import { NextApiRequest } from 'next'; import { useRouter } from 'next/router'; +import { Cookies } from 'react-cookie'; import { + FetchQueryOptions, + QueryClient, QueryFunctionContext, QueryKey, useQuery, @@ -11,7 +15,7 @@ import { isTokenValid } from '@/utils/isTokenValid'; import { useCookiesWithOptions } from '../useCookiesWithOptions'; import { Headers } from './types/Headers'; -import { useHeaders } from './useHeaders'; +import { createHeaders, useHeaders } from './useHeaders'; type QueryArguments = Record; @@ -26,6 +30,20 @@ type AuthenticatedQueryFunctionContext = QueryFunctionContext headers: Headers; }; +type ServerSideOptions = { + req: NextApiRequest; + queryClient: QueryClient; +}; + +type PrefetchAuthenticatedQueryOptions = { + queryArguments?: QueryArguments; +} & ServerSideOptions & + FetchQueryOptions; + +type UseAuthenticatedQueryOptions = { + queryArguments?: QueryArguments; +} & UseQueryOptions; + const isUnAuthorized = (status: number) => [401, 403].includes(status); const generateQueryKey = ({ @@ -39,29 +57,23 @@ const generateQueryKey = ({ return [queryKey, {}]; }; -type GetPreparedOptionsArguments = { - options: UseAuthenticatedQueryOptions; +type GetPreparedOptionsArguments = { + options: UseAuthenticatedQueryOptions; isTokenPresent: boolean; headers: Headers; }; -const getPreparedOptions = ({ +const getPreparedOptions = ({ options, isTokenPresent, headers, -}: GetPreparedOptionsArguments): UseQueryOptions< +}: GetPreparedOptionsArguments): UseQueryOptions< TQueryFnData, - TError, + FetchError, TQueryFnData, GeneratedQueryKey > => { - const { - queryKey, - queryArguments, - queryFn, - enabled, - ...restOptions - } = options; + const { queryKey, queryArguments, queryFn, ...restOptions } = options; const generatedQueryKey = generateQueryKey({ queryKey, queryArguments, @@ -80,21 +92,42 @@ const getPreparedOptions = ({ ...restOptions, queryKey: generatedQueryKey, queryFn: queryFunctionWithHeaders, - enabled: isTokenPresent && !!enabled, + ...('enabled' in restOptions && { + enabled: isTokenPresent && !!restOptions.enabled, + }), }; }; -type UseAuthenticatedQueryOptions = UseQueryOptions< - TQueryFnData, - TError, - TQueryFnData, - QueryKey -> & { - queryArguments?: QueryArguments; +const prefetchAuthenticatedQuery = async ({ + req, + queryClient, + ...options +}: PrefetchAuthenticatedQueryOptions) => { + if (typeof window !== 'undefined') { + throw new Error('Only use prefetchAuthenticatedQuery in server-side code'); + } + + const cookies = new Cookies(req?.headers?.cookie); + const headers = createHeaders(cookies.get('token')); + + const { queryKey, queryFn } = getPreparedOptions({ + options, + isTokenPresent: isTokenValid(cookies.get('token')), + headers, + }); + + try { + await queryClient.prefetchQuery( + queryKey, + queryFn, + ); + } catch {} + + return await queryClient.getQueryData(queryKey); }; const useAuthenticatedQuery = ( - options: UseAuthenticatedQueryOptions, + options: UseAuthenticatedQueryOptions, ) => { const headers = useHeaders(); const { cookies, removeAuthenticationCookies } = useCookiesWithOptions([ @@ -125,5 +158,5 @@ const useAuthenticatedQuery = ( return result; }; -export { useAuthenticatedQuery }; +export { prefetchAuthenticatedQuery, useAuthenticatedQuery }; export type { AuthenticatedQueryFunctionContext }; diff --git a/src/pages/test.page.tsx b/src/pages/test.page.tsx index fc5226866..23e0a3f34 100644 --- a/src/pages/test.page.tsx +++ b/src/pages/test.page.tsx @@ -1,20 +1,16 @@ -import { NextApiRequest } from 'next'; import { useEffect } from 'react'; -import { dehydrate } from 'react-query/types/hydration'; +import { dehydrate } from 'react-query/hydration'; import { AuthenticatedQueryFunctionContext, + prefetchAuthenticatedQuery, useAuthenticatedQuery, } from '@/hooks/api/authenticated-query2'; -import { Headers } from '@/hooks/api/types/Headers'; import { Event } from '@/types/Event'; import { fetchFromApi, isErrorObject } from '@/utils/fetchFromApi'; import { getApplicationServerSideProps } from '@/utils/getApplicationServerSideProps'; -type GetEventByIdArguments = { - headers: Headers; - id: string; -}; +const EVENT_ID = 'a9e9cdce-2e3c-4b17-9c6b-a7eb445252d1'; const getEventById = async ({ queryKey, @@ -35,41 +31,32 @@ const getEventById = async ({ }; const Test = () => { - const id = 'a9e9cdce-2e3c-4b17-9c6b-a7eb445252d1'; - - const query = useAuthenticatedQuery({ + const query = useAuthenticatedQuery({ queryKey: ['events'], queryFn: getEventById, - queryArguments: { id }, - enabled: !!id, + queryArguments: { id: EVENT_ID }, + enabled: !!EVENT_ID, }); useEffect(() => { - query.data; - console.log(query.data); + console.log({ eventOnClient: query.data }); }, [query.data]); return null; }; export const getServerSideProps = getApplicationServerSideProps( - async ({ - req, - cookies, - queryClient, - }: { - req: NextApiRequest; - cookies: any; - queryClient: any; - }) => { - useAuthenticatedQuery({ + async ({ req, cookies, queryClient }) => { + const eventOnServer = await prefetchAuthenticatedQuery({ req, queryClient, queryKey: ['events'], queryFn: getEventById, - queryArguments: { id: 'a9e9cdce-2e3c-4b17-9c6b-a7eb445252d1' }, + queryArguments: { id: EVENT_ID }, }); + console.log({ eventOnServer }); + return { props: { dehydratedState: dehydrate(queryClient), From 241f589ba940d747b1210a445ce250a71d0a04f2 Mon Sep 17 00:00:00 2001 From: simon-debruijn Date: Thu, 14 Apr 2022 11:55:19 +0200 Subject: [PATCH 07/29] Rename file to v2 --- .../api/{authenticated-query2.ts => authenticated-query-v2.ts} | 0 src/pages/test.page.tsx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/hooks/api/{authenticated-query2.ts => authenticated-query-v2.ts} (100%) diff --git a/src/hooks/api/authenticated-query2.ts b/src/hooks/api/authenticated-query-v2.ts similarity index 100% rename from src/hooks/api/authenticated-query2.ts rename to src/hooks/api/authenticated-query-v2.ts diff --git a/src/pages/test.page.tsx b/src/pages/test.page.tsx index 23e0a3f34..30a381d1b 100644 --- a/src/pages/test.page.tsx +++ b/src/pages/test.page.tsx @@ -5,7 +5,7 @@ import { AuthenticatedQueryFunctionContext, prefetchAuthenticatedQuery, useAuthenticatedQuery, -} from '@/hooks/api/authenticated-query2'; +} from '@/hooks/api/authenticated-query-v2'; import { Event } from '@/types/Event'; import { fetchFromApi, isErrorObject } from '@/utils/fetchFromApi'; import { getApplicationServerSideProps } from '@/utils/getApplicationServerSideProps'; From e8340e0149fe55e8583174a494f7a6bae9ae258a Mon Sep 17 00:00:00 2001 From: simon-debruijn Date: Thu, 14 Apr 2022 11:55:34 +0200 Subject: [PATCH 08/29] Change after next build --- next-env.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/next-env.d.ts b/next-env.d.ts index 9bc3dd46b..4f11a03dc 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,5 +1,4 @@ /// -/// /// // NOTE: This file should not be edited From 98e3c49fec823506826ba897d9c969565c601689 Mon Sep 17 00:00:00 2001 From: simon-debruijn Date: Thu, 14 Apr 2022 11:59:36 +0200 Subject: [PATCH 09/29] Rename type to GenerateQueryKeyArguments --- src/hooks/api/authenticated-query-v2.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/api/authenticated-query-v2.ts b/src/hooks/api/authenticated-query-v2.ts index d07411df1..e6413f992 100644 --- a/src/hooks/api/authenticated-query-v2.ts +++ b/src/hooks/api/authenticated-query-v2.ts @@ -19,7 +19,7 @@ import { createHeaders, useHeaders } from './useHeaders'; type QueryArguments = Record; -type GenerateUniqueQueryKeyArguments = { +type GenerateQueryKeyArguments = { queryKey: QueryKey; queryArguments: QueryArguments; }; @@ -49,7 +49,7 @@ const isUnAuthorized = (status: number) => [401, 403].includes(status); const generateQueryKey = ({ queryKey, queryArguments, -}: GenerateUniqueQueryKeyArguments): GeneratedQueryKey => { +}: GenerateQueryKeyArguments): GeneratedQueryKey => { if (Object.keys(queryArguments ?? {}).length > 0) { return [queryKey, queryArguments]; } From 0409ad5397e188ab0647c37192f532aa5cdc132e Mon Sep 17 00:00:00 2001 From: simon-debruijn Date: Thu, 14 Apr 2022 11:59:43 +0200 Subject: [PATCH 10/29] Remove toString --- src/pages/test.page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/test.page.tsx b/src/pages/test.page.tsx index 30a381d1b..6bb08aca3 100644 --- a/src/pages/test.page.tsx +++ b/src/pages/test.page.tsx @@ -18,7 +18,7 @@ const getEventById = async ({ }: AuthenticatedQueryFunctionContext) => { const [key, { id }] = queryKey; const res = await fetchFromApi({ - path: `/event/${id.toString()}`, + path: `/event/${id}`, options: { headers, }, From 90c2f9810f18393691820ee78b3261b4a6584649 Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Thu, 10 Aug 2023 16:43:37 +0200 Subject: [PATCH 11/29] Update useGetUserQuery --- src/hooks/api/user.ts | 3 ++- src/layouts/Sidebar.tsx | 3 +-- src/pages/dashboard/index.page.tsx | 7 +------ .../steps/AdditionalInformationStep/OrganizerPicker.tsx | 1 - src/pages/steps/LocationStep.tsx | 1 - 5 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/hooks/api/user.ts b/src/hooks/api/user.ts index 0713382cf..efeb10d9e 100644 --- a/src/hooks/api/user.ts +++ b/src/hooks/api/user.ts @@ -8,6 +8,7 @@ import { ServerSideQueryOptions, useAuthenticatedQuery, } from './authenticated-query'; +import { useAuthenticatedQuery as useAuthenticatedQueryV2 } from './authenticated-query-v2'; type User = { sub: string; @@ -58,7 +59,7 @@ const getUser = async (cookies: Cookies) => { const useGetUserQuery = () => { const { cookies } = useCookiesWithOptions(['idToken']); - return useAuthenticatedQuery({ + return useAuthenticatedQueryV2({ queryKey: ['user'], queryFn: () => getUser(cookies), }); diff --git a/src/layouts/Sidebar.tsx b/src/layouts/Sidebar.tsx index be9861aaf..9df9ce26d 100644 --- a/src/layouts/Sidebar.tsx +++ b/src/layouts/Sidebar.tsx @@ -187,8 +187,7 @@ const ProfileMenu = ({ defaultProfileImageUrl }: ProfileMenuProps) => { const queryClient = useQueryClient(); const getUserQuery = useGetUserQuery(); - // @ts-expect-error - const user = getUserQuery.data as User; + const user = getUserQuery.data; const loginMenu = [ { diff --git a/src/pages/dashboard/index.page.tsx b/src/pages/dashboard/index.page.tsx index bd20003df..ccb237b02 100644 --- a/src/pages/dashboard/index.page.tsx +++ b/src/pages/dashboard/index.page.tsx @@ -241,9 +241,7 @@ const OfferRow = ({ item: offer, onDelete, ...props }: OfferRowProps) => { const { t, i18n } = useTranslation(); const getUserQuery = useGetUserQuery(); - // @ts-expect-error const userId = getUserQuery.data?.sub; - // @ts-expect-error const userIdv1 = getUserQuery.data?.['https://publiq.be/uitidv1id']; const isExternalCreator = ![userId, userIdv1].includes(offer.creator); @@ -361,9 +359,7 @@ const OrganizerRow = ({ const { t, i18n } = useTranslation(); const getUserQuery = useGetUserQuery(); - // @ts-expect-error const userId = getUserQuery.data?.sub; - // @ts-expect-error const userIdv1 = getUserQuery.data?.['https://publiq.be/uitidv1id']; const isExternalCreator = ![userId, userIdv1].includes(organizer.creator); @@ -550,8 +546,7 @@ const Dashboard = (): any => { ); const getUserQuery = useGetUserQuery(); - // @ts-expect-error - const user = getUserQuery.data as User; + const user = getUserQuery.data; const handleSelectSorting = (event) => { const sortValue = event.target.value; diff --git a/src/pages/steps/AdditionalInformationStep/OrganizerPicker.tsx b/src/pages/steps/AdditionalInformationStep/OrganizerPicker.tsx index 1baed1b33..04ed50993 100644 --- a/src/pages/steps/AdditionalInformationStep/OrganizerPicker.tsx +++ b/src/pages/steps/AdditionalInformationStep/OrganizerPicker.tsx @@ -136,7 +136,6 @@ const OrganizerPicker = ({ const queryClient = useQueryClient(); const getUserQuery = useGetUserQuery(); - // @ts-expect-error const user = getUserQuery.data; const [addButtonHasBeenPressed, setAddButtonHasBeenPressed] = useState(false); diff --git a/src/pages/steps/LocationStep.tsx b/src/pages/steps/LocationStep.tsx index 541897a91..3680945fb 100644 --- a/src/pages/steps/LocationStep.tsx +++ b/src/pages/steps/LocationStep.tsx @@ -524,7 +524,6 @@ const RecentLocations = ({ onFieldChange, ...props }) => { const getOffersQuery = useGetOffersByCreatorQuery( { advancedQuery: '_exists_:location.id AND NOT (audienceType:"education")', - // @ts-expect-error creator: getUserQuery?.data, sortOptions: { field: 'modified', From 65329749f24042d999744565a4b72f69a72a770b Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 11 Aug 2023 12:54:35 +0200 Subject: [PATCH 12/29] Add types to useHeaders --- src/hooks/api/{useHeaders.js => useHeaders.ts} | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) rename src/hooks/api/{useHeaders.js => useHeaders.ts} (83%) diff --git a/src/hooks/api/useHeaders.js b/src/hooks/api/useHeaders.ts similarity index 83% rename from src/hooks/api/useHeaders.js rename to src/hooks/api/useHeaders.ts index f5a18ab0c..a268b5926 100644 --- a/src/hooks/api/useHeaders.js +++ b/src/hooks/api/useHeaders.ts @@ -2,7 +2,10 @@ import getConfig from 'next/config'; import { useCookiesWithOptions } from '../useCookiesWithOptions'; -const createHeaders = (token, extraHeaders) => { +const createHeaders = ( + token: string, + extraHeaders: HeadersInit = {}, +): HeadersInit => { const { publicRuntimeConfig } = getConfig(); return { From ca3892dc049d00aebb965e9068a4e87d99d72e2f Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 11 Aug 2023 12:56:48 +0200 Subject: [PATCH 13/29] Update useGetPermissionsQuery --- src/hooks/api/authenticated-query-v2.ts | 21 +++++++-------------- src/hooks/api/user.ts | 12 +++++++----- src/layouts/Sidebar.tsx | 3 --- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/hooks/api/authenticated-query-v2.ts b/src/hooks/api/authenticated-query-v2.ts index e6413f992..6049df91e 100644 --- a/src/hooks/api/authenticated-query-v2.ts +++ b/src/hooks/api/authenticated-query-v2.ts @@ -26,9 +26,10 @@ type GenerateQueryKeyArguments = { type GeneratedQueryKey = readonly [QueryKey, QueryArguments]; -type AuthenticatedQueryFunctionContext = QueryFunctionContext & { - headers: Headers; -}; +type AuthenticatedQueryFunctionContext = + QueryFunctionContext & { + headers: Headers; + }; type ServerSideOptions = { req: NextApiRequest; @@ -60,7 +61,7 @@ const generateQueryKey = ({ type GetPreparedOptionsArguments = { options: UseAuthenticatedQueryOptions; isTokenPresent: boolean; - headers: Headers; + headers: HeadersInit; }; const getPreparedOptions = ({ @@ -79,19 +80,11 @@ const getPreparedOptions = ({ queryArguments, }); - const queryFunctionWithHeaders = async ( - context: QueryFunctionContext, - ) => { - return await queryFn( - context, - // @ts-expect-error - headers, - ); - }; return { ...restOptions, queryKey: generatedQueryKey, - queryFn: queryFunctionWithHeaders, + meta: { headers }, + queryFn: queryFn, ...('enabled' in restOptions && { enabled: isTokenPresent && !!restOptions.enabled, }), diff --git a/src/hooks/api/user.ts b/src/hooks/api/user.ts index efeb10d9e..2fbbc234d 100644 --- a/src/hooks/api/user.ts +++ b/src/hooks/api/user.ts @@ -1,5 +1,4 @@ import jwt_decode from 'jwt-decode'; -import getConfig from 'next/config'; import { FetchError, fetchFromApi, isErrorObject } from '@/utils/fetchFromApi'; @@ -80,22 +79,25 @@ const useGetUserQueryServerSide = ( }); }; -const getPermissions = async ({ headers }) => { +const getPermissions = async ({ meta }): Promise => { const res = await fetchFromApi({ path: '/user/permissions/', options: { - headers, + headers: meta.headers as Headers, }, }); + if (isErrorObject(res)) { // eslint-disable-next-line no-console - return console.error(res); + console.error(res); + return []; } + return await res.json(); }; const useGetPermissionsQuery = (configuration = {}) => - useAuthenticatedQuery({ + useAuthenticatedQueryV2({ queryKey: ['user', 'permissions'], queryFn: getPermissions, ...configuration, diff --git a/src/layouts/Sidebar.tsx b/src/layouts/Sidebar.tsx index 9df9ce26d..2893506da 100644 --- a/src/layouts/Sidebar.tsx +++ b/src/layouts/Sidebar.tsx @@ -464,7 +464,6 @@ const Sidebar = () => { ]; const filteredManageMenu = useMemo(() => { - // @ts-expect-error if (!getPermissionsQuery.data) { return []; } @@ -519,10 +518,8 @@ const Sidebar = () => { ]; return manageMenu.filter((menuItem) => { - // @ts-expect-error return getPermissionsQuery.data.includes(menuItem.permission); }); - // @ts-expect-error }, [countEventsToModerate, getPermissionsQuery.data, i18n.language, t]); return [ From 62579f29839e45c138cd10c3db2f78290fb8bb9b Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 11 Aug 2023 12:59:03 +0200 Subject: [PATCH 14/29] Update useGetRolesQuery --- src/hooks/api/user.ts | 6 +++--- src/layouts/Sidebar.tsx | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/hooks/api/user.ts b/src/hooks/api/user.ts index 2fbbc234d..c88b0c8dc 100644 --- a/src/hooks/api/user.ts +++ b/src/hooks/api/user.ts @@ -103,11 +103,11 @@ const useGetPermissionsQuery = (configuration = {}) => ...configuration, }); -const getRoles = async ({ headers }) => { +const getRoles = async ({ meta }) => { const res = await fetchFromApi({ path: '/user/roles/', options: { - headers, + headers: meta.headers as HeadersInit, }, }); if (isErrorObject(res)) { @@ -118,7 +118,7 @@ const getRoles = async ({ headers }) => { }; const useGetRolesQuery = (configuration = {}) => - useAuthenticatedQuery({ + useAuthenticatedQueryV2({ queryKey: ['user', 'roles'], queryFn: getRoles, ...configuration, diff --git a/src/layouts/Sidebar.tsx b/src/layouts/Sidebar.tsx index 2893506da..cb2669976 100644 --- a/src/layouts/Sidebar.tsx +++ b/src/layouts/Sidebar.tsx @@ -406,19 +406,16 @@ const Sidebar = () => { }, [rawAnnouncements]); useEffect(() => { - // @ts-expect-error if (!getRolesQuery.data) { return; } - // @ts-expect-error const validationQuery = getRolesQuery.data .map((role) => (role.constraints?.v3 ? role.constraints.v3 : null)) .filter((constraint) => constraint !== null) .join(' OR '); setSearchQuery(validationQuery); - // @ts-expect-error }, [getRolesQuery.data]); const announcements = useMemo( From 960564de7be98899770e35b9c7a0e5a17d8c6016 Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 11 Aug 2023 14:23:19 +0200 Subject: [PATCH 15/29] Pass headers through context instead of meta --- src/hooks/api/authenticated-query-v2.ts | 15 +++++++++++---- src/hooks/api/user.ts | 21 +++++++++++---------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/hooks/api/authenticated-query-v2.ts b/src/hooks/api/authenticated-query-v2.ts index 6049df91e..6aa1f38ca 100644 --- a/src/hooks/api/authenticated-query-v2.ts +++ b/src/hooks/api/authenticated-query-v2.ts @@ -28,7 +28,7 @@ type GeneratedQueryKey = readonly [QueryKey, QueryArguments]; type AuthenticatedQueryFunctionContext = QueryFunctionContext & { - headers: Headers; + headers: HeadersInit; }; type ServerSideOptions = { @@ -43,7 +43,14 @@ type PrefetchAuthenticatedQueryOptions = { type UseAuthenticatedQueryOptions = { queryArguments?: QueryArguments; -} & UseQueryOptions; +} & Omit< + UseQueryOptions, + 'queryFn' +> & { + queryFn: ( + context: AuthenticatedQueryFunctionContext, + ) => TQueryFnData | Promise; + }; const isUnAuthorized = (status: number) => [401, 403].includes(status); @@ -83,8 +90,7 @@ const getPreparedOptions = ({ return { ...restOptions, queryKey: generatedQueryKey, - meta: { headers }, - queryFn: queryFn, + queryFn: (context) => queryFn({ ...context, headers }), ...('enabled' in restOptions && { enabled: isTokenPresent && !!restOptions.enabled, }), @@ -104,6 +110,7 @@ const prefetchAuthenticatedQuery = async ({ const headers = createHeaders(cookies.get('token')); const { queryKey, queryFn } = getPreparedOptions({ + // @ts-expect-error options, isTokenPresent: isTokenValid(cookies.get('token')), headers, diff --git a/src/hooks/api/user.ts b/src/hooks/api/user.ts index c88b0c8dc..2465476f0 100644 --- a/src/hooks/api/user.ts +++ b/src/hooks/api/user.ts @@ -79,21 +79,19 @@ const useGetUserQueryServerSide = ( }); }; -const getPermissions = async ({ meta }): Promise => { +const getPermissions = async ({ headers }) => { const res = await fetchFromApi({ path: '/user/permissions/', - options: { - headers: meta.headers as Headers, - }, + options: { headers }, }); if (isErrorObject(res)) { // eslint-disable-next-line no-console console.error(res); - return []; + return; } - return await res.json(); + return (await res.json()) as string[]; }; const useGetPermissionsQuery = (configuration = {}) => @@ -103,18 +101,21 @@ const useGetPermissionsQuery = (configuration = {}) => ...configuration, }); -const getRoles = async ({ meta }) => { +const getRoles = async ({ headers }) => { const res = await fetchFromApi({ path: '/user/roles/', options: { - headers: meta.headers as HeadersInit, + headers, }, }); + if (isErrorObject(res)) { // eslint-disable-next-line no-console - return console.error(res); + console.error(res); + return; } - return await res.json(); + + return (await res.json()) as any[]; }; const useGetRolesQuery = (configuration = {}) => From c01e417be64bb22820bafcd0fccf14c35a6b7ef9 Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 11 Aug 2023 15:39:34 +0200 Subject: [PATCH 16/29] Update useGetUserQueryServerSide --- src/hooks/api/user.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hooks/api/user.ts b/src/hooks/api/user.ts index 2465476f0..4935e3d0e 100644 --- a/src/hooks/api/user.ts +++ b/src/hooks/api/user.ts @@ -3,11 +3,11 @@ import jwt_decode from 'jwt-decode'; import { FetchError, fetchFromApi, isErrorObject } from '@/utils/fetchFromApi'; import { Cookies, useCookiesWithOptions } from '../useCookiesWithOptions'; +import { ServerSideQueryOptions } from './authenticated-query'; import { - ServerSideQueryOptions, - useAuthenticatedQuery, -} from './authenticated-query'; -import { useAuthenticatedQuery as useAuthenticatedQueryV2 } from './authenticated-query-v2'; + prefetchAuthenticatedQuery, + useAuthenticatedQuery as useAuthenticatedQueryV2, +} from './authenticated-query-v2'; type User = { sub: string; @@ -70,7 +70,7 @@ const useGetUserQueryServerSide = ( ) => { const cookies = req.cookies; - return useAuthenticatedQuery({ + return prefetchAuthenticatedQuery({ req, queryClient, queryKey: ['user'], From c08fcac2fe44ac55b3eb088befe7fad28c40e10f Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 11 Aug 2023 15:44:23 +0200 Subject: [PATCH 17/29] Pass headers through context instead of meta # Conflicts: # src/hooks/api/authenticated-query-v2.ts # src/hooks/api/user.ts --- src/hooks/api/authenticated-query-v2.ts | 19 ++++++++++++++----- src/hooks/api/user.ts | 16 +++++++++------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/hooks/api/authenticated-query-v2.ts b/src/hooks/api/authenticated-query-v2.ts index e6413f992..db29e4bfa 100644 --- a/src/hooks/api/authenticated-query-v2.ts +++ b/src/hooks/api/authenticated-query-v2.ts @@ -26,9 +26,10 @@ type GenerateQueryKeyArguments = { type GeneratedQueryKey = readonly [QueryKey, QueryArguments]; -type AuthenticatedQueryFunctionContext = QueryFunctionContext & { - headers: Headers; -}; +type AuthenticatedQueryFunctionContext = + QueryFunctionContext & { + headers: HeadersInit; + }; type ServerSideOptions = { req: NextApiRequest; @@ -42,7 +43,14 @@ type PrefetchAuthenticatedQueryOptions = { type UseAuthenticatedQueryOptions = { queryArguments?: QueryArguments; -} & UseQueryOptions; +} & Omit< + UseQueryOptions, + 'queryFn' +> & { + queryFn: ( + context: AuthenticatedQueryFunctionContext, + ) => TQueryFnData | Promise; + }; const isUnAuthorized = (status: number) => [401, 403].includes(status); @@ -91,7 +99,7 @@ const getPreparedOptions = ({ return { ...restOptions, queryKey: generatedQueryKey, - queryFn: queryFunctionWithHeaders, + queryFn: (context) => queryFn({ ...context, headers }), ...('enabled' in restOptions && { enabled: isTokenPresent && !!restOptions.enabled, }), @@ -111,6 +119,7 @@ const prefetchAuthenticatedQuery = async ({ const headers = createHeaders(cookies.get('token')); const { queryKey, queryFn } = getPreparedOptions({ + // @ts-expect-error options, isTokenPresent: isTokenValid(cookies.get('token')), headers, diff --git a/src/hooks/api/user.ts b/src/hooks/api/user.ts index 0713382cf..f94a109b3 100644 --- a/src/hooks/api/user.ts +++ b/src/hooks/api/user.ts @@ -82,15 +82,14 @@ const useGetUserQueryServerSide = ( const getPermissions = async ({ headers }) => { const res = await fetchFromApi({ path: '/user/permissions/', - options: { - headers, - }, + options: { headers }, }); if (isErrorObject(res)) { // eslint-disable-next-line no-console - return console.error(res); + console.error(res); + return; } - return await res.json(); + return (await res.json()) as string[]; }; const useGetPermissionsQuery = (configuration = {}) => @@ -107,11 +106,14 @@ const getRoles = async ({ headers }) => { headers, }, }); + if (isErrorObject(res)) { // eslint-disable-next-line no-console - return console.error(res); + console.error(res); + return; } - return await res.json(); + + return (await res.json()) as any[]; }; const useGetRolesQuery = (configuration = {}) => From d63ebf467a4e1b68ff52e342bc3735543a75bb46 Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 11 Aug 2023 16:54:41 +0200 Subject: [PATCH 18/29] Pass queryArguments as part of context --- src/hooks/api/authenticated-query-v2.ts | 29 +++++++++---------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/hooks/api/authenticated-query-v2.ts b/src/hooks/api/authenticated-query-v2.ts index db29e4bfa..2c2a136e7 100644 --- a/src/hooks/api/authenticated-query-v2.ts +++ b/src/hooks/api/authenticated-query-v2.ts @@ -26,9 +26,10 @@ type GenerateQueryKeyArguments = { type GeneratedQueryKey = readonly [QueryKey, QueryArguments]; -type AuthenticatedQueryFunctionContext = +type AuthenticatedQueryFunctionContext = QueryFunctionContext & { headers: HeadersInit; + queryArguments?: TQueryArguments; }; type ServerSideOptions = { @@ -41,8 +42,11 @@ type PrefetchAuthenticatedQueryOptions = { } & ServerSideOptions & FetchQueryOptions; -type UseAuthenticatedQueryOptions = { - queryArguments?: QueryArguments; +type UseAuthenticatedQueryOptions< + TQueryFnData, + TQueryArguments = QueryArguments, +> = { + queryArguments?: TQueryArguments; } & Omit< UseQueryOptions, 'queryFn' @@ -75,31 +79,18 @@ const getPreparedOptions = ({ options, isTokenPresent, headers, -}: GetPreparedOptionsArguments): UseQueryOptions< - TQueryFnData, - FetchError, - TQueryFnData, - GeneratedQueryKey -> => { +}: GetPreparedOptionsArguments) => { const { queryKey, queryArguments, queryFn, ...restOptions } = options; const generatedQueryKey = generateQueryKey({ queryKey, queryArguments, }); - const queryFunctionWithHeaders = async ( - context: QueryFunctionContext, - ) => { - return await queryFn( - context, - // @ts-expect-error - headers, - ); - }; return { ...restOptions, queryKey: generatedQueryKey, - queryFn: (context) => queryFn({ ...context, headers }), + queryArguments, + queryFn: (context) => queryFn({ ...context, queryArguments, headers }), ...('enabled' in restOptions && { enabled: isTokenPresent && !!restOptions.enabled, }), From b27f7bb4ba3670a6b67bdfaf710d917469328c88 Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Thu, 24 Aug 2023 14:35:35 +0200 Subject: [PATCH 19/29] Work on failed authentication --- src/hooks/api/authenticated-query-v2.ts | 3 +- src/hooks/api/authenticated-query.ts | 3 +- src/hooks/api/user.ts | 2 +- src/layouts/{index.js => index.tsx} | 4 +- src/pages/dashboard/index.page.tsx | 4 +- ...ps.js => getApplicationServerSideProps.ts} | 52 +++++++++++++------ 6 files changed, 46 insertions(+), 22 deletions(-) rename src/layouts/{index.js => index.tsx} (98%) rename src/utils/{getApplicationServerSideProps.js => getApplicationServerSideProps.ts} (75%) diff --git a/src/hooks/api/authenticated-query-v2.ts b/src/hooks/api/authenticated-query-v2.ts index 6aa1f38ca..414eb440d 100644 --- a/src/hooks/api/authenticated-query-v2.ts +++ b/src/hooks/api/authenticated-query-v2.ts @@ -16,6 +16,7 @@ import { isTokenValid } from '@/utils/isTokenValid'; import { useCookiesWithOptions } from '../useCookiesWithOptions'; import { Headers } from './types/Headers'; import { createHeaders, useHeaders } from './useHeaders'; +import { GetServerSidePropsContext } from 'next/types'; type QueryArguments = Record; @@ -32,7 +33,7 @@ type AuthenticatedQueryFunctionContext = }; type ServerSideOptions = { - req: NextApiRequest; + req: GetServerSidePropsContext['req']; queryClient: QueryClient; }; diff --git a/src/hooks/api/authenticated-query.ts b/src/hooks/api/authenticated-query.ts index 66aeb6791..62ce18575 100644 --- a/src/hooks/api/authenticated-query.ts +++ b/src/hooks/api/authenticated-query.ts @@ -19,9 +19,10 @@ import { isErrorObject } from '@/utils/fetchFromApi'; import { isTokenValid } from '@/utils/isTokenValid'; import { createHeaders, useHeaders } from './useHeaders'; +import { GetServerSidePropsContext } from 'next/types'; type ServerSideQueryOptions = { - req?: NextApiRequest; + req?: GetServerSidePropsContext['req']; queryClient?: QueryClient; }; diff --git a/src/hooks/api/user.ts b/src/hooks/api/user.ts index 4935e3d0e..f8b7f968c 100644 --- a/src/hooks/api/user.ts +++ b/src/hooks/api/user.ts @@ -45,7 +45,7 @@ const getUser = async (cookies: Cookies) => { throw new FetchError(401, 'Unauthorized'); } - const userInfo = jwt_decode(cookies.idToken) as User; + const userInfo = jwt_decode(cookies.idToken); const decodedAccessToken = jwt_decode(cookies.token) as decodedAccessToken; if (Date.now() >= decodedAccessToken.exp * 1000) { diff --git a/src/layouts/index.js b/src/layouts/index.tsx similarity index 98% rename from src/layouts/index.js rename to src/layouts/index.tsx index 785b7fb50..53bf05b31 100644 --- a/src/layouts/index.js +++ b/src/layouts/index.tsx @@ -33,10 +33,10 @@ const useHandleAuthentication = () => { const getUserQuery = useGetUserQuery(); useEffect(() => { - if (!getUserQuery.data) return; + if (!getUserQuery?.data) return; Sentry.setUser({ id: getUserQuery.data.id }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [getUserQuery.data]); + }, [getUserQuery?.data]); // redirect when there is no token or user cookie // manipulation from outside the application diff --git a/src/pages/dashboard/index.page.tsx b/src/pages/dashboard/index.page.tsx index ccb237b02..e09defce8 100644 --- a/src/pages/dashboard/index.page.tsx +++ b/src/pages/dashboard/index.page.tsx @@ -725,10 +725,10 @@ const Dashboard = (): any => { const getServerSideProps = getApplicationServerSideProps( async ({ req, query, cookies: rawCookies, queryClient }) => { - const user = (await useGetUserQueryServerSide({ + const user = await useGetUserQueryServerSide({ req, queryClient, - })) as User; + }); await Promise.all( Object.entries(UseGetItemsByCreatorMap).map(([key, hook]) => { diff --git a/src/utils/getApplicationServerSideProps.js b/src/utils/getApplicationServerSideProps.ts similarity index 75% rename from src/utils/getApplicationServerSideProps.js rename to src/utils/getApplicationServerSideProps.ts index 26e1be804..3de3b2cea 100644 --- a/src/utils/getApplicationServerSideProps.js +++ b/src/utils/getApplicationServerSideProps.ts @@ -1,17 +1,16 @@ import * as Sentry from '@sentry/nextjs'; import getConfig from 'next/config'; +import { GetServerSidePropsContext } from 'next/types'; import absoluteUrl from 'next-absolute-url'; import { QueryClient } from 'react-query'; import { generatePath, matchPath } from 'react-router'; import UniversalCookies from 'universal-cookie'; -import { useGetUserQuery, useGetUserQueryServerSide } from '@/hooks/api/user'; -import { defaultCookieOptions } from '@/hooks/useCookiesWithOptions'; +import { useGetUserQueryServerSide } from '@/hooks/api/user'; import { isFeatureFlagEnabledInCookies } from '@/hooks/useFeatureFlag'; import { getRedirects } from '../redirects'; import { FetchError } from './fetchFromApi'; -import { isTokenValid } from './isTokenValid'; class Cookies extends UniversalCookies { toString() { @@ -19,7 +18,7 @@ class Cookies extends UniversalCookies { return cookieEntries.reduce((previous, [key, value], currentIndex) => { const end = currentIndex !== cookieEntries.length - 1 ? '; ' : ''; - const pair = `${key}=${encodeURIComponent(value)}${end}`; + const pair = `${key}=${encodeURIComponent(value as string)}${end}`; return `${previous}${pair}`; }, ''); } @@ -61,7 +60,13 @@ const getRedirect = (originalPath, environment, cookies) => { .find((match) => !!match); }; -const redirectToLogin = (cookies, req, resolvedUrl) => { +const redirectToLogin = ({ + cookies, + req, + resolvedUrl, +}: Pick & { + cookies: Cookies; +}) => { Sentry.setUser(null); cookies.remove('token'); @@ -76,22 +81,31 @@ const redirectToLogin = (cookies, req, resolvedUrl) => { }; }; +type ExtendedGetServerSidePropsContext = GetServerSidePropsContext & { + cookies: string; + queryClient: QueryClient; +}; + const getApplicationServerSideProps = - (callbackFn) => - async ({ req, query, resolvedUrl }) => { + (callbackFn?: (context: ExtendedGetServerSidePropsContext) => any) => + async ({ + req, + query, + resolvedUrl, + ...context + }: GetServerSidePropsContext) => { const { publicRuntimeConfig } = getConfig(); if (publicRuntimeConfig.environment === 'development') { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; } - const rawCookies = req?.headers?.cookie ?? ''; - - const cookies = new Cookies(rawCookies, defaultCookieOptions); + const cookies = new Cookies(req.cookies); req.headers.cookie = cookies.toString(); - const isDynamicUrl = !!query.params; - const path = isDynamicUrl ? `/${query.params.join('/')}` : resolvedUrl; + const params = (query.params as string[]) ?? []; + const isDynamicUrl = Object.keys(params).length > 0; + const path = isDynamicUrl ? `/${params.join('/')}` : resolvedUrl; const redirect = getRedirect( path, @@ -102,7 +116,9 @@ const getApplicationServerSideProps = if (redirect) { // Don't include the `params` in the redirect URL's query. delete query.params; - const queryParameters = new URLSearchParams(query); + const queryParameters = new URLSearchParams( + query as Record, + ); // Return the redirect as-is if there are no additional query parameters // to append. @@ -124,7 +140,11 @@ const getApplicationServerSideProps = await useGetUserQueryServerSide({ req, queryClient }); } catch (error) { if (error instanceof FetchError) { - return redirectToLogin(cookies, req, resolvedUrl); + return redirectToLogin({ + cookies, + req, + resolvedUrl, + }); } } @@ -133,6 +153,8 @@ const getApplicationServerSideProps = if (!callbackFn) return { props: { cookies: cookies.toString() } }; return await callbackFn({ + ...context, + resolvedUrl, req, query, queryClient, From 5cbbdc5f26567fbfda67036b4757a1e7d110b5f7 Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 25 Aug 2023 12:02:46 +0200 Subject: [PATCH 20/29] Fix Sidebar not returning valid JSX --- src/layouts/Sidebar.tsx | 200 ++++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 99 deletions(-) diff --git a/src/layouts/Sidebar.tsx b/src/layouts/Sidebar.tsx index cb2669976..02aeef95e 100644 --- a/src/layouts/Sidebar.tsx +++ b/src/layouts/Sidebar.tsx @@ -519,119 +519,121 @@ const Sidebar = () => { }); }, [countEventsToModerate, getPermissionsQuery.data, i18n.language, t]); - return [ - { - if (!sidebarComponent?.current) return; - if (document.activeElement?.tagName?.toLowerCase() !== 'iframe') { - return; - } - // @ts-expect-error - document.activeElement.blur(); - }} - > - - - + return ( + <> :not(:first-child) { - border-top: 1px solid ${getValueForMenu('borderColor')}; - } + overflow: hidden; `} + width={{ default: '230px', s: '65px' }} + backgroundColor={getValueForSidebar('backgroundColor')} + color={getValueForSidebar('color')} + zIndex={getValueForSidebar('zIndex')} + padding={{ default: 2, s: 1 }} + spacing={3} + ref={sidebarComponent} + onMouseOver={() => { + if (!sidebarComponent?.current) return; + if (document.activeElement?.tagName?.toLowerCase() !== 'iframe') { + return; + } + // @ts-expect-error + document.activeElement.blur(); + }} > - + + + 0 ? 'space-between' : 'flex-end' - } + paddingTop={4} + spacing={4} flex={1} + css={` + > :not(:first-child) { + border-top: 1px solid ${getValueForMenu('borderColor')}; + } + `} > - {filteredManageMenu.length > 0 && ( - - )} - - + + 0 ? 'space-between' : 'flex-end' + } + flex={1} + > + {filteredManageMenu.length > 0 && ( + + )} + - - { - setIsNewCreateEnabled((prev) => !prev); - }} - /> - - {!isSmallView && ( - - - - )} - - - + + { + setIsNewCreateEnabled((prev) => !prev); + }} + /> + + {!isSmallView && ( + + + + )} + + + + - , - setIsJobLoggerVisible((oldState) => !oldState)} - onStatusChange={setJobLoggerState} - />, - , - ]; + setIsJobLoggerVisible((oldState) => !oldState)} + onStatusChange={setJobLoggerState} + /> + + + ); }; export { Sidebar }; From c04d9e83ad3b11760e568d5a0f3c7f282852f021 Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 25 Aug 2023 13:27:40 +0200 Subject: [PATCH 21/29] Fix some TS errors --- src/pages/dashboard/index.page.tsx | 7 ++++--- src/pages/events/[eventId]/duplicate/index.page.tsx | 2 +- src/pages/events/[eventId]/edit/index.page.tsx | 6 +++--- src/pages/manage/movies/[eventId]/edit.page.tsx | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/pages/dashboard/index.page.tsx b/src/pages/dashboard/index.page.tsx index e09defce8..438d0db55 100644 --- a/src/pages/dashboard/index.page.tsx +++ b/src/pages/dashboard/index.page.tsx @@ -733,10 +733,11 @@ const getServerSideProps = getApplicationServerSideProps( await Promise.all( Object.entries(UseGetItemsByCreatorMap).map(([key, hook]) => { const page = - query.tab === key ? (query.page ? parseInt(query.page) : 1) : 1; + query.tab === key ? parseInt((query.page as string) ?? '1') : 1; - const sortingField = query?.sort?.split('_')[0] ?? SortingField.CREATED; - const sortingOrder = query?.sort?.split('_')[1] ?? SortingOrder.DESC; + const sort = (query?.sort ?? '') as string; + const sortingField = sort.split('_')[0] ?? SortingField.CREATED; + const sortingOrder = sort.split('_')[1] ?? SortingOrder.DESC; return hook({ req, diff --git a/src/pages/events/[eventId]/duplicate/index.page.tsx b/src/pages/events/[eventId]/duplicate/index.page.tsx index 4b57e5d2e..27924e3e1 100644 --- a/src/pages/events/[eventId]/duplicate/index.page.tsx +++ b/src/pages/events/[eventId]/duplicate/index.page.tsx @@ -10,7 +10,7 @@ export const getServerSideProps = getApplicationServerSideProps( const { eventId } = query; await useGetEventByIdQuery({ - id: eventId, + id: eventId as string, req, queryClient, }); diff --git a/src/pages/events/[eventId]/edit/index.page.tsx b/src/pages/events/[eventId]/edit/index.page.tsx index fbd82928f..34df25bec 100644 --- a/src/pages/events/[eventId]/edit/index.page.tsx +++ b/src/pages/events/[eventId]/edit/index.page.tsx @@ -10,11 +10,11 @@ export const getServerSideProps = getApplicationServerSideProps( async ({ req, query, queryClient, cookies }) => { const { eventId } = query; - const event = (await useGetEventByIdQuery({ - id: eventId, + await useGetEventByIdQuery({ + id: eventId as string, req, queryClient, - })) as Event; + }); return { props: { diff --git a/src/pages/manage/movies/[eventId]/edit.page.tsx b/src/pages/manage/movies/[eventId]/edit.page.tsx index fe1276cba..e6a8054d7 100644 --- a/src/pages/manage/movies/[eventId]/edit.page.tsx +++ b/src/pages/manage/movies/[eventId]/edit.page.tsx @@ -12,7 +12,7 @@ export const getServerSideProps = getApplicationServerSideProps( await useGetEventByIdQuery({ req, queryClient, - id: eventId, + id: eventId as string, }); return { From b2f3187cf9f1ffe3a74fb139346cedf71cfa01cb Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 25 Aug 2023 14:23:16 +0200 Subject: [PATCH 22/29] Return query state --- src/hooks/api/authenticated-query-v2.ts | 6 ++---- src/hooks/api/authenticated-query.ts | 7 ++++--- src/pages/dashboard/index.page.tsx | 2 +- src/pages/test.page.tsx | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/hooks/api/authenticated-query-v2.ts b/src/hooks/api/authenticated-query-v2.ts index 414eb440d..5f01921de 100644 --- a/src/hooks/api/authenticated-query-v2.ts +++ b/src/hooks/api/authenticated-query-v2.ts @@ -1,5 +1,5 @@ -import { NextApiRequest } from 'next'; import { useRouter } from 'next/router'; +import { GetServerSidePropsContext } from 'next/types'; import { Cookies } from 'react-cookie'; import { FetchQueryOptions, @@ -14,9 +14,7 @@ import { FetchError } from '@/utils/fetchFromApi'; import { isTokenValid } from '@/utils/isTokenValid'; import { useCookiesWithOptions } from '../useCookiesWithOptions'; -import { Headers } from './types/Headers'; import { createHeaders, useHeaders } from './useHeaders'; -import { GetServerSidePropsContext } from 'next/types'; type QueryArguments = Record; @@ -124,7 +122,7 @@ const prefetchAuthenticatedQuery = async ({ ); } catch {} - return await queryClient.getQueryData(queryKey); + return queryClient.getQueryState(queryKey); }; const useAuthenticatedQuery = ( diff --git a/src/hooks/api/authenticated-query.ts b/src/hooks/api/authenticated-query.ts index 62ce18575..f13851465 100644 --- a/src/hooks/api/authenticated-query.ts +++ b/src/hooks/api/authenticated-query.ts @@ -1,16 +1,18 @@ import { isEqual } from 'lodash'; import flatten from 'lodash/flatten'; -import type { NextApiRequest } from 'next'; import { useRouter } from 'next/router'; +import { GetServerSidePropsContext } from 'next/types'; import { useCallback } from 'react'; import { Cookies } from 'react-cookie'; import { MutationFunction, QueryClient, + useMutation, + useQueries, + useQuery, useQueryClient, UseQueryResult, } from 'react-query'; -import { useMutation, useQueries, useQuery } from 'react-query'; import { useCookiesWithOptions } from '@/hooks/useCookiesWithOptions'; import type { CalendarSummaryFormat } from '@/utils/createEmbededCalendarSummaries'; @@ -19,7 +21,6 @@ import { isErrorObject } from '@/utils/fetchFromApi'; import { isTokenValid } from '@/utils/isTokenValid'; import { createHeaders, useHeaders } from './useHeaders'; -import { GetServerSidePropsContext } from 'next/types'; type ServerSideQueryOptions = { req?: GetServerSidePropsContext['req']; diff --git a/src/pages/dashboard/index.page.tsx b/src/pages/dashboard/index.page.tsx index 438d0db55..85012b67a 100644 --- a/src/pages/dashboard/index.page.tsx +++ b/src/pages/dashboard/index.page.tsx @@ -725,7 +725,7 @@ const Dashboard = (): any => { const getServerSideProps = getApplicationServerSideProps( async ({ req, query, cookies: rawCookies, queryClient }) => { - const user = await useGetUserQueryServerSide({ + const { data: user } = await useGetUserQueryServerSide({ req, queryClient, }); diff --git a/src/pages/test.page.tsx b/src/pages/test.page.tsx index 6bb08aca3..5bb9f7989 100644 --- a/src/pages/test.page.tsx +++ b/src/pages/test.page.tsx @@ -47,7 +47,7 @@ const Test = () => { export const getServerSideProps = getApplicationServerSideProps( async ({ req, cookies, queryClient }) => { - const eventOnServer = await prefetchAuthenticatedQuery({ + const { data: eventOnServer } = await prefetchAuthenticatedQuery({ req, queryClient, queryKey: ['events'], From 38f474ae73095b7747339dfa76e7aa3534f80f45 Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 25 Aug 2023 15:02:57 +0200 Subject: [PATCH 23/29] Try to revert some changes? --- src/pages/dashboard/index.page.tsx | 7 ++- src/utils/getApplicationServerSideProps.ts | 52 +++++++--------------- 2 files changed, 18 insertions(+), 41 deletions(-) diff --git a/src/pages/dashboard/index.page.tsx b/src/pages/dashboard/index.page.tsx index 85012b67a..f0cb60ad9 100644 --- a/src/pages/dashboard/index.page.tsx +++ b/src/pages/dashboard/index.page.tsx @@ -733,11 +733,10 @@ const getServerSideProps = getApplicationServerSideProps( await Promise.all( Object.entries(UseGetItemsByCreatorMap).map(([key, hook]) => { const page = - query.tab === key ? parseInt((query.page as string) ?? '1') : 1; + query.tab === key ? (query.page ? parseInt(query.page) : 1) : 1; - const sort = (query?.sort ?? '') as string; - const sortingField = sort.split('_')[0] ?? SortingField.CREATED; - const sortingOrder = sort.split('_')[1] ?? SortingOrder.DESC; + const sortingField = query?.sort?.split('_')[0] ?? SortingField.CREATED; + const sortingOrder = query?.sort?.split('_')[1] ?? SortingOrder.DESC; return hook({ req, diff --git a/src/utils/getApplicationServerSideProps.ts b/src/utils/getApplicationServerSideProps.ts index 3de3b2cea..da1bd212e 100644 --- a/src/utils/getApplicationServerSideProps.ts +++ b/src/utils/getApplicationServerSideProps.ts @@ -1,16 +1,17 @@ import * as Sentry from '@sentry/nextjs'; import getConfig from 'next/config'; -import { GetServerSidePropsContext } from 'next/types'; import absoluteUrl from 'next-absolute-url'; import { QueryClient } from 'react-query'; import { generatePath, matchPath } from 'react-router'; import UniversalCookies from 'universal-cookie'; -import { useGetUserQueryServerSide } from '@/hooks/api/user'; +import { useGetUserQuery, useGetUserQueryServerSide } from '@/hooks/api/user'; +import { defaultCookieOptions } from '@/hooks/useCookiesWithOptions'; import { isFeatureFlagEnabledInCookies } from '@/hooks/useFeatureFlag'; import { getRedirects } from '../redirects'; import { FetchError } from './fetchFromApi'; +import { isTokenValid } from './isTokenValid'; class Cookies extends UniversalCookies { toString() { @@ -18,7 +19,7 @@ class Cookies extends UniversalCookies { return cookieEntries.reduce((previous, [key, value], currentIndex) => { const end = currentIndex !== cookieEntries.length - 1 ? '; ' : ''; - const pair = `${key}=${encodeURIComponent(value as string)}${end}`; + const pair = `${key}=${encodeURIComponent(value)}${end}`; return `${previous}${pair}`; }, ''); } @@ -60,13 +61,7 @@ const getRedirect = (originalPath, environment, cookies) => { .find((match) => !!match); }; -const redirectToLogin = ({ - cookies, - req, - resolvedUrl, -}: Pick & { - cookies: Cookies; -}) => { +const redirectToLogin = (cookies, req, resolvedUrl) => { Sentry.setUser(null); cookies.remove('token'); @@ -81,31 +76,22 @@ const redirectToLogin = ({ }; }; -type ExtendedGetServerSidePropsContext = GetServerSidePropsContext & { - cookies: string; - queryClient: QueryClient; -}; - const getApplicationServerSideProps = - (callbackFn?: (context: ExtendedGetServerSidePropsContext) => any) => - async ({ - req, - query, - resolvedUrl, - ...context - }: GetServerSidePropsContext) => { + (callbackFn) => + async ({ req, query, resolvedUrl }) => { const { publicRuntimeConfig } = getConfig(); if (publicRuntimeConfig.environment === 'development') { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; } - const cookies = new Cookies(req.cookies); + const rawCookies = req?.headers?.cookie ?? req.cookies ?? ''; + + const cookies = new Cookies(rawCookies); req.headers.cookie = cookies.toString(); - const params = (query.params as string[]) ?? []; - const isDynamicUrl = Object.keys(params).length > 0; - const path = isDynamicUrl ? `/${params.join('/')}` : resolvedUrl; + const isDynamicUrl = !!query.params; + const path = isDynamicUrl ? `/${query.params.join('/')}` : resolvedUrl; const redirect = getRedirect( path, @@ -116,9 +102,7 @@ const getApplicationServerSideProps = if (redirect) { // Don't include the `params` in the redirect URL's query. delete query.params; - const queryParameters = new URLSearchParams( - query as Record, - ); + const queryParameters = new URLSearchParams(query); // Return the redirect as-is if there are no additional query parameters // to append. @@ -140,11 +124,7 @@ const getApplicationServerSideProps = await useGetUserQueryServerSide({ req, queryClient }); } catch (error) { if (error instanceof FetchError) { - return redirectToLogin({ - cookies, - req, - resolvedUrl, - }); + return redirectToLogin(cookies, req, resolvedUrl); } } @@ -153,8 +133,6 @@ const getApplicationServerSideProps = if (!callbackFn) return { props: { cookies: cookies.toString() } }; return await callbackFn({ - ...context, - resolvedUrl, req, query, queryClient, From e21e07f89954b31786d086a1b64746c4b0397dad Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 25 Aug 2023 15:21:33 +0200 Subject: [PATCH 24/29] Try to make authentication work properly --- src/hooks/api/authenticated-query-v2.ts | 9 +--- src/layouts/index.tsx | 2 +- src/pages/dashboard/index.page.tsx | 9 ++-- src/pages/test.page.tsx | 2 +- src/utils/getApplicationServerSideProps.ts | 52 +++++++++++++++------- 5 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/hooks/api/authenticated-query-v2.ts b/src/hooks/api/authenticated-query-v2.ts index 5f01921de..7df7c7b39 100644 --- a/src/hooks/api/authenticated-query-v2.ts +++ b/src/hooks/api/authenticated-query-v2.ts @@ -115,14 +115,7 @@ const prefetchAuthenticatedQuery = async ({ headers, }); - try { - await queryClient.prefetchQuery( - queryKey, - queryFn, - ); - } catch {} - - return queryClient.getQueryState(queryKey); + return queryClient.fetchQuery(queryKey, queryFn); }; const useAuthenticatedQuery = ( diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 53bf05b31..18a08336b 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -34,7 +34,7 @@ const useHandleAuthentication = () => { useEffect(() => { if (!getUserQuery?.data) return; - Sentry.setUser({ id: getUserQuery.data.id }); + Sentry.setUser({ id: getUserQuery.data.sub }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [getUserQuery?.data]); diff --git a/src/pages/dashboard/index.page.tsx b/src/pages/dashboard/index.page.tsx index f0cb60ad9..74ba34024 100644 --- a/src/pages/dashboard/index.page.tsx +++ b/src/pages/dashboard/index.page.tsx @@ -725,7 +725,7 @@ const Dashboard = (): any => { const getServerSideProps = getApplicationServerSideProps( async ({ req, query, cookies: rawCookies, queryClient }) => { - const { data: user } = await useGetUserQueryServerSide({ + const user = await useGetUserQueryServerSide({ req, queryClient, }); @@ -733,10 +733,11 @@ const getServerSideProps = getApplicationServerSideProps( await Promise.all( Object.entries(UseGetItemsByCreatorMap).map(([key, hook]) => { const page = - query.tab === key ? (query.page ? parseInt(query.page) : 1) : 1; + query.tab === key ? parseInt((query.page as string) ?? '1') : 1; - const sortingField = query?.sort?.split('_')[0] ?? SortingField.CREATED; - const sortingOrder = query?.sort?.split('_')[1] ?? SortingOrder.DESC; + const sort = query?.sort as string | undefined; + const sortingField = sort?.split('_')[0] ?? SortingField.CREATED; + const sortingOrder = sort?.split('_')[1] ?? SortingOrder.DESC; return hook({ req, diff --git a/src/pages/test.page.tsx b/src/pages/test.page.tsx index 5bb9f7989..6bb08aca3 100644 --- a/src/pages/test.page.tsx +++ b/src/pages/test.page.tsx @@ -47,7 +47,7 @@ const Test = () => { export const getServerSideProps = getApplicationServerSideProps( async ({ req, cookies, queryClient }) => { - const { data: eventOnServer } = await prefetchAuthenticatedQuery({ + const eventOnServer = await prefetchAuthenticatedQuery({ req, queryClient, queryKey: ['events'], diff --git a/src/utils/getApplicationServerSideProps.ts b/src/utils/getApplicationServerSideProps.ts index da1bd212e..3de3b2cea 100644 --- a/src/utils/getApplicationServerSideProps.ts +++ b/src/utils/getApplicationServerSideProps.ts @@ -1,17 +1,16 @@ import * as Sentry from '@sentry/nextjs'; import getConfig from 'next/config'; +import { GetServerSidePropsContext } from 'next/types'; import absoluteUrl from 'next-absolute-url'; import { QueryClient } from 'react-query'; import { generatePath, matchPath } from 'react-router'; import UniversalCookies from 'universal-cookie'; -import { useGetUserQuery, useGetUserQueryServerSide } from '@/hooks/api/user'; -import { defaultCookieOptions } from '@/hooks/useCookiesWithOptions'; +import { useGetUserQueryServerSide } from '@/hooks/api/user'; import { isFeatureFlagEnabledInCookies } from '@/hooks/useFeatureFlag'; import { getRedirects } from '../redirects'; import { FetchError } from './fetchFromApi'; -import { isTokenValid } from './isTokenValid'; class Cookies extends UniversalCookies { toString() { @@ -19,7 +18,7 @@ class Cookies extends UniversalCookies { return cookieEntries.reduce((previous, [key, value], currentIndex) => { const end = currentIndex !== cookieEntries.length - 1 ? '; ' : ''; - const pair = `${key}=${encodeURIComponent(value)}${end}`; + const pair = `${key}=${encodeURIComponent(value as string)}${end}`; return `${previous}${pair}`; }, ''); } @@ -61,7 +60,13 @@ const getRedirect = (originalPath, environment, cookies) => { .find((match) => !!match); }; -const redirectToLogin = (cookies, req, resolvedUrl) => { +const redirectToLogin = ({ + cookies, + req, + resolvedUrl, +}: Pick & { + cookies: Cookies; +}) => { Sentry.setUser(null); cookies.remove('token'); @@ -76,22 +81,31 @@ const redirectToLogin = (cookies, req, resolvedUrl) => { }; }; +type ExtendedGetServerSidePropsContext = GetServerSidePropsContext & { + cookies: string; + queryClient: QueryClient; +}; + const getApplicationServerSideProps = - (callbackFn) => - async ({ req, query, resolvedUrl }) => { + (callbackFn?: (context: ExtendedGetServerSidePropsContext) => any) => + async ({ + req, + query, + resolvedUrl, + ...context + }: GetServerSidePropsContext) => { const { publicRuntimeConfig } = getConfig(); if (publicRuntimeConfig.environment === 'development') { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; } - const rawCookies = req?.headers?.cookie ?? req.cookies ?? ''; - - const cookies = new Cookies(rawCookies); + const cookies = new Cookies(req.cookies); req.headers.cookie = cookies.toString(); - const isDynamicUrl = !!query.params; - const path = isDynamicUrl ? `/${query.params.join('/')}` : resolvedUrl; + const params = (query.params as string[]) ?? []; + const isDynamicUrl = Object.keys(params).length > 0; + const path = isDynamicUrl ? `/${params.join('/')}` : resolvedUrl; const redirect = getRedirect( path, @@ -102,7 +116,9 @@ const getApplicationServerSideProps = if (redirect) { // Don't include the `params` in the redirect URL's query. delete query.params; - const queryParameters = new URLSearchParams(query); + const queryParameters = new URLSearchParams( + query as Record, + ); // Return the redirect as-is if there are no additional query parameters // to append. @@ -124,7 +140,11 @@ const getApplicationServerSideProps = await useGetUserQueryServerSide({ req, queryClient }); } catch (error) { if (error instanceof FetchError) { - return redirectToLogin(cookies, req, resolvedUrl); + return redirectToLogin({ + cookies, + req, + resolvedUrl, + }); } } @@ -133,6 +153,8 @@ const getApplicationServerSideProps = if (!callbackFn) return { props: { cookies: cookies.toString() } }; return await callbackFn({ + ...context, + resolvedUrl, req, query, queryClient, From 8eac43b35a19aa165fe6e56c926045e6ca81e343 Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 25 Aug 2023 15:28:10 +0200 Subject: [PATCH 25/29] Fix optional feature flag property --- src/utils/getApplicationServerSideProps.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/getApplicationServerSideProps.ts b/src/utils/getApplicationServerSideProps.ts index 3de3b2cea..4fabcad37 100644 --- a/src/utils/getApplicationServerSideProps.ts +++ b/src/utils/getApplicationServerSideProps.ts @@ -26,7 +26,7 @@ class Cookies extends UniversalCookies { const getRedirect = (originalPath, environment, cookies) => { return getRedirects(environment, cookies['udb-language']) - .map(({ source, destination, permanent, featureFlag }) => { + .map(({ source, destination, permanent, featureFlag = null }) => { // Don't follow redirects that are behind a feature flag if (featureFlag && !isFeatureFlagEnabledInCookies(featureFlag, cookies)) { return false; From 32feb8f2d9ef9aa0f04e2692f1240c761db9ba29 Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 25 Aug 2023 15:34:45 +0200 Subject: [PATCH 26/29] Make return type explicit --- src/redirects.tsx | 7 ++++++- src/utils/getApplicationServerSideProps.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/redirects.tsx b/src/redirects.tsx index e6afc9d01..8dc975298 100644 --- a/src/redirects.tsx +++ b/src/redirects.tsx @@ -42,7 +42,12 @@ const createDashboardRedirects = (environment: Environment) => { const getRedirects = ( environment: Environment, language: Values = 'nl', -) => [ +): { + featureFlag?: 'react_create'; + permanent: boolean; + destination: string; + source: string; +}[] => [ // Only make the permanent redirects really permanent in environments other // than development, so we don't get permanent redirects on localhost which // may conflict with other projects. diff --git a/src/utils/getApplicationServerSideProps.ts b/src/utils/getApplicationServerSideProps.ts index 4fabcad37..3de3b2cea 100644 --- a/src/utils/getApplicationServerSideProps.ts +++ b/src/utils/getApplicationServerSideProps.ts @@ -26,7 +26,7 @@ class Cookies extends UniversalCookies { const getRedirect = (originalPath, environment, cookies) => { return getRedirects(environment, cookies['udb-language']) - .map(({ source, destination, permanent, featureFlag = null }) => { + .map(({ source, destination, permanent, featureFlag }) => { // Don't follow redirects that are behind a feature flag if (featureFlag && !isFeatureFlagEnabledInCookies(featureFlag, cookies)) { return false; From 343ecd129826eaf1cea6141288d3cbb5ed1d71d2 Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Fri, 25 Aug 2023 15:39:26 +0200 Subject: [PATCH 27/29] Linting --- src/pages/dashboard/index.page.tsx | 6 +----- src/redirects.tsx | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pages/dashboard/index.page.tsx b/src/pages/dashboard/index.page.tsx index 74ba34024..00abdc001 100644 --- a/src/pages/dashboard/index.page.tsx +++ b/src/pages/dashboard/index.page.tsx @@ -22,11 +22,7 @@ import { useDeletePlaceByIdMutation, useGetPlacesByCreatorQuery, } from '@/hooks/api/places'; -import { - useGetUserQuery, - useGetUserQueryServerSide, - User, -} from '@/hooks/api/user'; +import { useGetUserQuery, useGetUserQueryServerSide } from '@/hooks/api/user'; import { FeatureFlags, useFeatureFlag } from '@/hooks/useFeatureFlag'; import { Footer } from '@/pages/Footer'; import type { Event } from '@/types/Event'; diff --git a/src/redirects.tsx b/src/redirects.tsx index 8dc975298..7fc5ca4dc 100644 --- a/src/redirects.tsx +++ b/src/redirects.tsx @@ -43,7 +43,7 @@ const getRedirects = ( environment: Environment, language: Values = 'nl', ): { - featureFlag?: 'react_create'; + featureFlag?: string; permanent: boolean; destination: string; source: string; From d8a21e12c0deca3a150165ff6a11d95d6bfdea64 Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Thu, 7 Sep 2023 15:50:58 +0200 Subject: [PATCH 28/29] Better handling of undefined query page --- src/pages/dashboard/index.page.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/dashboard/index.page.tsx b/src/pages/dashboard/index.page.tsx index 00abdc001..0897e3d96 100644 --- a/src/pages/dashboard/index.page.tsx +++ b/src/pages/dashboard/index.page.tsx @@ -729,7 +729,9 @@ const getServerSideProps = getApplicationServerSideProps( await Promise.all( Object.entries(UseGetItemsByCreatorMap).map(([key, hook]) => { const page = - query.tab === key ? parseInt((query.page as string) ?? '1') : 1; + query.tab === key && query.page + ? parseInt((query.page as string) ?? '1') + : 1; const sort = query?.sort as string | undefined; const sortingField = sort?.split('_')[0] ?? SortingField.CREATED; From 73936c47a1eb0314bd048943efd39feae54df3fb Mon Sep 17 00:00:00 2001 From: Emma Fabre Date: Thu, 7 Sep 2023 15:57:12 +0200 Subject: [PATCH 29/29] Correct feature flag type --- src/hooks/useFeatureFlag.ts | 2 ++ src/redirects.tsx | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hooks/useFeatureFlag.ts b/src/hooks/useFeatureFlag.ts index e6721346e..55a1f0e5a 100644 --- a/src/hooks/useFeatureFlag.ts +++ b/src/hooks/useFeatureFlag.ts @@ -80,3 +80,5 @@ export { isFeatureFlagEnabledInCookies, useFeatureFlag, }; + +export type { FeatureFlagName }; diff --git a/src/redirects.tsx b/src/redirects.tsx index 7fc5ca4dc..b96a12a5a 100644 --- a/src/redirects.tsx +++ b/src/redirects.tsx @@ -1,4 +1,4 @@ -import { FeatureFlags } from './hooks/useFeatureFlag'; +import { FeatureFlagName, FeatureFlags } from './hooks/useFeatureFlag'; import type { SupportedLanguages } from './i18n'; import type { Values } from './types/Values'; @@ -43,7 +43,7 @@ const getRedirects = ( environment: Environment, language: Values = 'nl', ): { - featureFlag?: string; + featureFlag?: FeatureFlagName; permanent: boolean; destination: string; source: string;