Skip to content

Commit

Permalink
Merge pull request #1023 from CruGlobal/8094-make-get-server-side-props
Browse files Browse the repository at this point in the history
[MPDX-8094] Add makeGetServerSideProps helper to reduce boilerplate in `getServerSideProps`
  • Loading branch information
canac authored Sep 4, 2024
2 parents af2818c + eab6af8 commit 20ce9ed
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 171 deletions.
24 changes: 3 additions & 21 deletions pages/accountLists.page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ThemeProvider } from '@mui/material/styles';
import { render } from '@testing-library/react';
import { getSession } from 'next-auth/react';
import { I18nextProvider } from 'react-i18next';
import { session } from '__tests__/fixtures/session';
import makeSsrClient from 'src/lib/apollo/ssrClient';
import i18n from 'src/lib/i18n';
import theme from 'src/theme';
Expand All @@ -27,12 +28,7 @@ describe('Account Lists page', () => {

describe('NextAuth unauthorized', () => {
it('should redirect to login', async () => {
(getSession as jest.Mock).mockResolvedValue({
user: {
apiToken: null,
userID: null,
},
});
(getSession as jest.Mock).mockResolvedValue(null);

const { props, redirect } = (await getServerSideProps(
context as GetServerSidePropsContext,
Expand All @@ -48,12 +44,7 @@ describe('Account Lists page', () => {

describe('NextAuth authorized', () => {
beforeEach(() => {
(getSession as jest.Mock).mockResolvedValue({
user: {
apiToken: 'apiToken',
userID: 'userID',
},
});
(getSession as jest.Mock).mockResolvedValue(session);
});

it('redirects user to their accountList page if only one accountList', async () => {
Expand All @@ -69,15 +60,6 @@ describe('Account Lists page', () => {
context as GetServerSidePropsContext,
)) as GetServerSidePropsReturn;

const { queryByText } = render(
<ThemeProvider theme={theme}>
<I18nextProvider i18n={i18n}>
<AccountListsPage {...props} />
</I18nextProvider>
</ThemeProvider>,
);

expect(queryByText('My Accounts')).not.toBeInTheDocument();
expect(props).toBeUndefined();
expect(redirect).toEqual({
destination: `/accountLists/${accountListId}`,
Expand Down
33 changes: 8 additions & 25 deletions pages/accountLists.page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { GetServerSideProps, GetServerSidePropsResult } from 'next';
import Head from 'next/head';
import React, { ReactElement } from 'react';
import { Session } from 'next-auth';
import { getSession } from 'next-auth/react';
import { useTranslation } from 'react-i18next';
import { logErrorOnRollbar } from 'pages/api/utils/rollBar';
import AccountLists from 'src/components/AccountLists';
Expand All @@ -14,11 +11,11 @@ import {
GetAccountListsQuery,
GetAccountListsQueryVariables,
} from './GetAccountLists.generated';
import { makeGetServerSideProps } from './api/utils/pagePropsHelpers';

export type AccountListsPageProps = {
data?: GetAccountListsQuery;
session: Session | null;
};
export interface AccountListsPageProps {
data: GetAccountListsQuery;
}

const AccountListsPage = ({ data }: AccountListsPageProps): ReactElement => {
const { t } = useTranslation();
Expand All @@ -31,29 +28,16 @@ const AccountListsPage = ({ data }: AccountListsPageProps): ReactElement => {
{appName} | {t('Account Lists')}
</title>
</Head>
{data && <AccountLists data={data} />}
<AccountLists data={data} />
</>
);
};

AccountListsPage.layout = BaseLayout;

export const getServerSideProps: GetServerSideProps = async (
context,
): Promise<GetServerSidePropsResult<AccountListsPageProps>> => {
export const getServerSideProps = makeGetServerSideProps(async (session) => {
try {
const session = await getSession(context);
const apiToken = session?.user?.apiToken;
if (!apiToken) {
return {
redirect: {
destination: '/login',
permanent: false,
},
};
}

const ssrClient = makeSsrClient(apiToken);
const ssrClient = makeSsrClient(session.user.apiToken);
const { data } = await ssrClient.query<
GetAccountListsQuery,
GetAccountListsQueryVariables
Expand All @@ -73,13 +57,12 @@ export const getServerSideProps: GetServerSideProps = async (
return {
props: {
data,
session,
},
};
} catch (error) {
logErrorOnRollbar(error, '/accountLists.page');
throw error;
}
};
});

export default AccountListsPage;
15 changes: 3 additions & 12 deletions pages/accountLists/[accountListId].page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ThemeProvider } from '@mui/material/styles';
import { render } from '@testing-library/react';
import { getSession } from 'next-auth/react';
import { I18nextProvider } from 'react-i18next';
import { session } from '__tests__/fixtures/session';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
import makeSsrClient from 'src/lib/apollo/ssrClient';
Expand Down Expand Up @@ -30,12 +31,7 @@ describe('AccountListsId page', () => {

describe('NextAuth unauthorized', () => {
it('should redirect to login', async () => {
(getSession as jest.Mock).mockResolvedValue({
user: {
apiToken: null,
userID: null,
},
});
(getSession as jest.Mock).mockResolvedValue(null);

const { props, redirect } = (await getServerSideProps(
context,
Expand All @@ -51,12 +47,7 @@ describe('AccountListsId page', () => {

describe('NextAuth authorized', () => {
beforeEach(() => {
(getSession as jest.Mock).mockResolvedValue({
user: {
apiToken: 'apiToken',
userID: 'userID',
},
});
(getSession as jest.Mock).mockResolvedValue(session);
});

it('redirects to the home page on GraphQL query error', async () => {
Expand Down
98 changes: 42 additions & 56 deletions pages/accountLists/[accountListId].page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { GetServerSideProps } from 'next';
import Head from 'next/head';
import React, { ReactElement, useEffect, useState } from 'react';
import { Session } from 'next-auth';
import { getSession } from 'next-auth/react';
import { makeGetServerSideProps } from 'pages/api/utils/pagePropsHelpers';
import { logErrorOnRollbar } from 'pages/api/utils/rollBar';
import Dashboard from 'src/components/Dashboard';
import {
Expand All @@ -23,7 +21,6 @@ import {
export interface AccountListIdPageProps {
data: GetDashboardQuery;
modal: string;
session: Session | null;
}

const AccountListIdPage = ({
Expand Down Expand Up @@ -80,59 +77,48 @@ const AccountListIdPage = ({
);
};

export const getServerSideProps: GetServerSideProps = async (context) => {
const session = await getSession(context);
const apiToken = session?.user.apiToken;
// If no token from session, redirect to login page
if (!apiToken) {
return {
redirect: {
destination: '/login',
permanent: false,
},
};
}
export const getServerSideProps = makeGetServerSideProps(
async (session, context) => {
try {
const { query } = context;
if (typeof query.accountListId !== 'string') {
throw new Error('Invalid accountListId');
}

try {
const { query } = context;
if (typeof query.accountListId !== 'string') {
throw new Error('Invalid accountListId');
}

const ssrClient = makeSsrClient(apiToken);
const { data } = await ssrClient.query<
GetDashboardQuery,
GetDashboardQueryVariables
>({
query: GetDashboardDocument,
variables: {
accountListId: query.accountListId,
// TODO: implement these variables in query
// endOfDay: DateTime.local().endOf('day').toISO(),
// today: DateTime.local().endOf('day').toISODate(),
// twoWeeksFromNow: DateTime.local()
// .endOf('day')
// .plus({ weeks: 2 })
// .toISODate(),
},
});
const ssrClient = makeSsrClient(session.user.apiToken);
const { data } = await ssrClient.query<
GetDashboardQuery,
GetDashboardQueryVariables
>({
query: GetDashboardDocument,
variables: {
accountListId: query.accountListId,
// TODO: implement these variables in query
// endOfDay: DateTime.local().endOf('day').toISO(),
// today: DateTime.local().endOf('day').toISODate(),
// twoWeeksFromNow: DateTime.local()
// .endOf('day')
// .plus({ weeks: 2 })
// .toISODate(),
},
});

return {
props: {
data,
modal: query?.modal?.toString() ?? '',
session,
},
};
} catch (error) {
logErrorOnRollbar(error, '/accountLists/[accountListId].page');
return {
redirect: {
destination: '/',
permanent: false,
},
};
}
};
return {
props: {
data,
modal: query?.modal?.toString() ?? '',
},
};
} catch (error) {
logErrorOnRollbar(error, '/accountLists/[accountListId].page');
return {
redirect: {
destination: '/',
permanent: false,
},
};
}
},
);

export default AccountListIdPage;
77 changes: 76 additions & 1 deletion pages/api/utils/pagePropsHelpers.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { GetServerSidePropsContext } from 'next';
import { getSession } from 'next-auth/react';
import { enforceAdmin, loadSession } from './pagePropsHelpers';
import { session } from '__tests__/fixtures/session';
import {
enforceAdmin,
loadSession,
makeGetServerSideProps,
} from './pagePropsHelpers';

jest.mock('next-auth/react');

Expand Down Expand Up @@ -50,3 +55,73 @@ describe('loadSession', () => {
});
});
});

describe('makeGetServerSideProps', () => {
it('redirects to the login page if the session is missing', async () => {
(getSession as jest.Mock).mockResolvedValue(null);

const getServerSidePropsFromSession = jest.fn();
const getServerSideProps = makeGetServerSideProps(
getServerSidePropsFromSession,
);

await expect(getServerSideProps(context)).resolves.toEqual({
redirect: {
destination: '/login',
permanent: false,
},
});
expect(getServerSidePropsFromSession).not.toHaveBeenCalled();
});

it('calls the custom function and adds the session to the returned props', async () => {
(getSession as jest.Mock).mockResolvedValue(session);

const getServerSidePropsFromSession = jest.fn().mockResolvedValue({
props: {
data1: 1,
dataA: 'A',
},
});
const getServerSideProps = makeGetServerSideProps(
getServerSidePropsFromSession,
);

await expect(getServerSideProps(context)).resolves.toEqual({
props: {
session,
data1: 1,
dataA: 'A',
},
});
expect(getServerSidePropsFromSession).toHaveBeenCalledWith(
session,
context,
);
});

it('calls the custom function and passes through redirects', async () => {
(getSession as jest.Mock).mockResolvedValue(session);

const getServerSidePropsFromSession = jest.fn().mockResolvedValue({
redirect: {
destination: '/new/url',
permanent: false,
},
});
const getServerSideProps = makeGetServerSideProps(
getServerSidePropsFromSession,
);

await expect(getServerSideProps(context)).resolves.toEqual({
redirect: {
destination: '/new/url',
permanent: false,
},
});
expect(getServerSidePropsFromSession).toHaveBeenCalledWith(
session,
context,
);
});
});
Loading

0 comments on commit 20ce9ed

Please sign in to comment.