Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tmi2 364/add redirection logic for new mandatory question journey #89

12 changes: 10 additions & 2 deletions packages/admin/.snyk
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ ignore:
reason: >-
dependency only used during dev and build, no upgrade currently
available
expires: 2023-09-30T14:47:46.319Z
expires: 2023-11-30T14:47:46.319Z
created: 2023-06-21T14:47:46.321Z

SNYK-JS-WORDWRAP-3149973:
- '*':
- '*':
reason: >-
Dev dependency - no upgrade currently available
expires: 2023-09-30T14:10:46.319Z
Expand All @@ -23,4 +23,12 @@ ignore:
Dev dependency - no upgrade currently available
expires: 2023-09-30T14:10:46.319Z
created: 2023-09-05T14:10:46.321Z

SNYK-JS-POSTCSS-5926692:
- '*':
reason: >-
Dev dependency - upgrade requires migration to next 13
expires: 2023-12-09T14:10:46.319Z
created: 2023-10-09T14:10:46.321Z

patch: {}
12 changes: 10 additions & 2 deletions packages/applicant/.snyk
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ ignore:
reason: >-
dependency only used during dev and build, no upgrade currently
available
expires: 2023-09-30T14:47:46.319Z
expires: 2023-11-30T14:47:46.319Z
created: 2023-06-21T14:47:46.321Z

SNYK-JS-WORDWRAP-3149973:
- '*':
- '*':
reason: >-
Dev dependency - no upgrade currently available
expires: 2023-09-30T14:10:46.319Z
Expand All @@ -23,4 +23,12 @@ ignore:
Dev dependency - no upgrade currently available
expires: 2023-09-30T14:10:46.319Z
created: 2023-09-05T14:10:46.321Z

SNYK-JS-POSTCSS-5926692:
- '*':
reason: >-
Dev dependency - upgrade requires migration to next 13
expires: 2023-12-09T14:10:46.319Z
created: 2023-10-09T14:10:46.321Z

patch: {}
125 changes: 125 additions & 0 deletions packages/applicant/src/pages/api/redirect-from-find.page.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { merge } from 'lodash';
import { AdvertDto, getAdvertBySlug } from '../../services/GrantAdvertService';
import { getJwtFromCookies } from '../../utils/jwt';
import handler from './redirect-from-find.page';

// Mock the getAdvertBySlug function (you may need to adjust this based on your actual implementation)
jest.mock('../../services/GrantAdvertService');
jest.mock('../../utils/jwt');
const mockedRedirect = jest.fn();
const mockedSetHeader = jest.fn();
const mockedSend = jest.fn();

const req = (overrides: any = {}) =>
merge({
query: {
slug: 'slug',
},
});

const res = (overrides: any = {}) =>
merge(
{
redirect: mockedRedirect,
setHeader: mockedSetHeader,
send: mockedSend,
},
overrides
);

describe('API Handler Tests', () => {
beforeEach(() => {
process.env.HOST = 'http://localhost';
jest.resetAllMocks();
});
it('should redirect to /applications/<applicationId> when advert is version 1 and have internal application', async () => {
const advertDTO: AdvertDto = {
id: '123',
version: 1,
grantApplicationId: 123,
isInternal: true,
grantSchemeId: 456,
externalSubmissionUrl: 'http://example.com',
};

(getAdvertBySlug as jest.Mock).mockResolvedValue(advertDTO);
(getJwtFromCookies as jest.Mock).mockReturnValue('testJwt');
await handler(req(), res());

expect(mockedRedirect).toHaveBeenCalledWith(
'http://localhost/applications/123'
);
});
it('should redirect to the external Submission Url when advert is version 1 and have internal application', async () => {
const advertDTO: AdvertDto = {
id: '123',
version: 1,
grantApplicationId: 123,
isInternal: false,
grantSchemeId: 456,
externalSubmissionUrl: 'http://example.com',
};

(getAdvertBySlug as jest.Mock).mockResolvedValue(advertDTO);
(getJwtFromCookies as jest.Mock).mockReturnValue('testJwt');
await handler(req(), res());

expect(mockedRedirect).toHaveBeenCalledWith('http://example.com');
});

it('should redirect to the new Mandatory Question journey start page when advert is version 2 and have internal application', async () => {
const advertDTO: AdvertDto = {
id: '123',
version: 2,
grantApplicationId: 123,
isInternal: true,
grantSchemeId: 456,
externalSubmissionUrl: 'http://example.com',
};

(getAdvertBySlug as jest.Mock).mockResolvedValue(advertDTO);
(getJwtFromCookies as jest.Mock).mockReturnValue('testJwt');
await handler(req(), res());

expect(mockedRedirect).toHaveBeenCalledWith(
'http://localhost/mandatory-questions/start?schemeId=456'
);
});

it('should redirect to the new Mandatory Question journey start page when advert is version 2 and have external application', async () => {
const advertDTO: AdvertDto = {
id: '123',
version: 2,
grantApplicationId: 123,
isInternal: false,
grantSchemeId: 456,
externalSubmissionUrl: 'http://example.com',
};

(getAdvertBySlug as jest.Mock).mockResolvedValue(advertDTO);
(getJwtFromCookies as jest.Mock).mockReturnValue('testJwt');
await handler(req(), res());

expect(mockedRedirect).toHaveBeenCalledWith(
'http://localhost/mandatory-questions/start?schemeId=456'
);
});
it('should redirect to the service Error when there is an error in the call to the backend', async () => {
(getAdvertBySlug as jest.Mock).mockRejectedValue(new Error('error'));
(getJwtFromCookies as jest.Mock).mockReturnValue('testJwt');
await handler(req(), res());
const serviceErrorProps = {
errorInformation: 'There was an error in the service',
linkAttributes: {
href: '/dashboard',
linkText: 'Go back to your dashboard',
linkInformation: '',
},
};
expect(mockedRedirect).toHaveBeenCalledWith(
`http://localhost/service-error?serviceErrorProps=${JSON.stringify(
serviceErrorProps
)}`
);
});
});
47 changes: 47 additions & 0 deletions packages/applicant/src/pages/api/redirect-from-find.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { getAdvertBySlug } from '../../services/GrantAdvertService';
import { getJwtFromCookies } from '../../utils/jwt';
import { routes } from '../../utils/routes';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const slug = req.query.slug as string;
try {
const {
externalSubmissionUrl,
version,
grantApplicationId,
isInternal,
grantSchemeId,
} = await getAdvertBySlug(getJwtFromCookies(req), slug);

if (version === 1) {
const redirectUrl = isInternal
? `${process.env.HOST}${routes.applications}/${grantApplicationId}`
: externalSubmissionUrl;
res.redirect(redirectUrl);
}

if (version === 2) {
res.redirect(
`${process.env.HOST}${routes.mandatoryQuestions.startPage(
grantSchemeId.toString()
)}`
);
}
} catch (e) {
const serviceErrorProps = {
errorInformation: 'There was an error in the service',
linkAttributes: {
href: routes.dashboard,
linkText: 'Go back to your dashboard',
linkInformation: '',
},
};
console.log(e);
res.redirect(
`${process.env.HOST}${routes.serviceError(serviceErrorProps)}`
);
}
}
17 changes: 17 additions & 0 deletions packages/applicant/src/pages/mandatory-questions/start.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Layout from '../../components/partials/Layout';
import Meta from '../../components/partials/Meta';
import { routes } from '../../utils/routes';

const FirstMandatoryQuestion = () => {
return (
<>
<Meta title="View my applications - Apply for a grant" />

<Layout backBtnUrl={routes.dashboard}>
<>NEW PAGE</>
</Layout>
</>
);
};

export default FirstMandatoryQuestion;
48 changes: 48 additions & 0 deletions packages/applicant/src/services/GrantAdvertService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import axios from 'axios';
import { AdvertDto, getAdvertBySlug } from './GrantAdvertService';

jest.mock('axios');

const BACKEND_HOST = process.env.BACKEND_HOST + '/grant-adverts';
const advertDTO: AdvertDto = {
id: '123',
version: 2,
grantApplicationId: 123,
isInternal: true,
grantSchemeId: 456,
externalSubmissionUrl: 'http://example.com',
};
describe('GrantAdvert Service', () => {
beforeEach(() => {
jest.resetAllMocks();
});

describe('getGrantBeneficiary', () => {
it('Should call axios', async () => {
(axios.get as jest.Mock).mockResolvedValue({});

await getAdvertBySlug('testJwt', 'slug');

expect(axios.get as jest.Mock).toHaveBeenNthCalledWith(
1,
`${BACKEND_HOST}?contentfulSlug=slug`,
{
headers: {
Accept: 'application/json',
Authorization: 'Bearer testJwt',
},
}
);
});

it('Should return a GrantBeneficiary', async () => {
(axios.get as jest.Mock).mockResolvedValue({
data: advertDTO,
});

const response = await getAdvertBySlug('testJwt', 'slug');

expect(response).toStrictEqual(advertDTO);
});
});
});
26 changes: 26 additions & 0 deletions packages/applicant/src/services/GrantAdvertService.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import axios from 'axios';
import getConfig from 'next/config';
import { axiosConfig } from '../utils/jwt';

const { serverRuntimeConfig } = getConfig();
const BACKEND_HOST = serverRuntimeConfig.backendHost;

export async function getAdvertBySlug(
jwt: string,
slug: string
): Promise<AdvertDto> {
const { data } = await axios.get<AdvertDto>(
`${BACKEND_HOST}/grant-adverts?contentfulSlug=${slug}`,
axiosConfig(jwt)
);
return data;
}

export interface AdvertDto {
id: string;
externalSubmissionUrl: string;
version: number;
grantApplicationId: number | null;
isInternal: boolean;
grantSchemeId: number;
}
6 changes: 6 additions & 0 deletions packages/applicant/src/utils/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export const routes = {
name: '/organisation/name',
type: '/organisation/type',
},
mandatoryQuestions: {
startPage: (schemeId: string) =>
`/mandatory-questions/start?schemeId=${schemeId}`,
},
applications: '/applications',
submissions: {
index: '/submissions',
Expand All @@ -33,6 +37,8 @@ export const routes = {
`/submissions/${grantSubmissionId}/submission-confirmation`,
},
findAGrant: publicRuntimeConfig.FIND_A_GRANT_URL,
serviceError: (serviceErrorProps) =>
`/service-error?serviceErrorProps=${JSON.stringify(serviceErrorProps)}`,
api: {
submissions: {
section: (grantSubmissionId: string, sectionId: string) =>
Expand Down
Loading