Skip to content

Commit

Permalink
ASAP-470 Add teams reference and display manuscript title in the fron…
Browse files Browse the repository at this point in the history
…tend (#4279)

* ASAP-470 Add teams reference and display manuscript title in the frontend

* validation tweaks
  • Loading branch information
gabiayako authored May 21, 2024
1 parent 4104b65 commit b40ec43
Show file tree
Hide file tree
Showing 28 changed files with 504 additions and 44 deletions.
13 changes: 10 additions & 3 deletions apps/crn-frontend/src/network/teams/TeamManuscript.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import {
} from '@asap-hub/react-components';
import { network } from '@asap-hub/routing';
import { FormProvider, useForm } from 'react-hook-form';
import { usePostManuscript } from './state';
import { useSetRecoilState } from 'recoil';
import { refreshTeamState, usePostManuscript } from './state';
import { useManuscriptToast } from './useManuscriptToast';

type TeamManuscriptProps = {
teamId: string;
};
const TeamManuscript: React.FC<TeamManuscriptProps> = ({ teamId }) => {
const setRefreshTeamState = useSetRecoilState(refreshTeamState(teamId));

const { setShowSuccessBanner } = useManuscriptToast();
const form = useForm();
const createManuscript = usePostManuscript();
Expand All @@ -21,16 +24,20 @@ const TeamManuscript: React.FC<TeamManuscriptProps> = ({ teamId }) => {

const onSuccess = () => {
const path = network({}).teams({}).team({ teamId }).workspace({}).$;

setShowSuccessBanner(true);
setRefreshTeamState((value) => value + 1);
pushFromHere(path);
};

return (
<FormProvider {...form}>
<Frame title="Create Manuscript">
<ManuscriptHeader />
<ManuscriptForm onSuccess={onSuccess} onSave={createManuscript} />
<ManuscriptForm
onSuccess={onSuccess}
onSave={createManuscript}
teamId={teamId}
/>
</Frame>
</FormProvider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,10 @@ it('can publish a form when the data is valid and navigates to team workspace',
userEvent.click(submitButton);

await waitFor(() => {
expect(createManuscript).toHaveBeenCalledWith({ title }, expect.anything());
expect(createManuscript).toHaveBeenCalledWith(
{ title, teamId },
expect.anything(),
);
expect(history.location.pathname).toBe(
`/network/teams/${teamId}/workspace`,
);
Expand Down
1 change: 1 addition & 0 deletions apps/crn-frontend/src/network/teams/__tests__/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ describe('Manuscript', () => {
describe('POST', () => {
const payload: ManuscriptPostRequest = {
title: 'The Manuscript',
teamId: '42',
};
it('makes an authorized POST request to create a manuscript', async () => {
nock(API_BASE_URL, { reqheaders: { authorization: 'Bearer x' } })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
FetchManuscriptByIdQuery,
FetchManuscriptByIdQueryVariables,
FETCH_MANUSCRIPT_BY_ID,
getLinkEntities,
GraphQLClient,
} from '@asap-hub/contentful';
import {
Expand Down Expand Up @@ -44,8 +45,13 @@ export class ManuscriptContentfulDataProvider
async create(input: ManuscriptCreateDataObject): Promise<string> {
const environment = await this.getRestClient();

const { teamId, ...plainFields } = input;

const manuscriptEntry = await environment.createEntry('manuscripts', {
fields: addLocaleToFields(input),
fields: addLocaleToFields({
...plainFields,
teams: getLinkEntities([teamId]),
}),
});

await manuscriptEntry.publish();
Expand All @@ -59,4 +65,5 @@ const parseGraphQLManuscript = (
): ManuscriptDataObject => ({
id: manuscripts.sys.id,
title: manuscripts.title || '',
teamId: manuscripts.teamsCollection?.items[0]?.sys.id || '',
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TeamListItemDataObject,
ListTeamDataObject,
TeamRole,
TeamManuscript,
} from '@asap-hub/model';

import {
Expand All @@ -22,7 +23,7 @@ import {
Environment,
addLocaleToFields,
} from '@asap-hub/contentful';
import { parseUserDisplayName } from '@asap-hub/server-common';
import { cleanArray, parseUserDisplayName } from '@asap-hub/server-common';

import { sortMembers } from '../transformers';
import {
Expand Down Expand Up @@ -300,6 +301,10 @@ export const parseContentfulGraphQlTeam = (
lastModifiedDate: new Date(item.sys.publishedAt).toISOString(),
tags: parseResearchTags(item.researchTagsCollection?.items || []),
tools,
manuscripts: cleanArray(item.linkedFrom?.manuscriptsCollection?.items).map(
(manuscript) =>
({ id: manuscript.sys.id, title: manuscript.title }) as TeamManuscript,
),
projectSummary: item.projectSummary ?? undefined,
members: members.sort(sortMembers),
labCount,
Expand Down
6 changes: 5 additions & 1 deletion apps/crn-server/src/routes/manuscript.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ export const manuscriptRouteFactory = (
const { body, loggedInUser } = req;
const createRequest = validateManuscriptPostRequestParameters(body);

if (!loggedInUser) throw Boom.forbidden();
const userBelongsToTeam = loggedInUser?.teams.some(
(team) => team.id === createRequest.teamId,
);

if (!loggedInUser || !userBelongsToTeam) throw Boom.forbidden();

const manuscript = await manuscriptController.create(createRequest);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ describe('Manuscripts Contentful Data Provider', () => {
title: {
'en-US': 'Manuscript Title',
},
teams: {
'en-US': [
{
sys: {
id: 'team-1',
linkType: 'Entry',
type: 'Link',
},
},
],
},
},
});
expect(publish).toHaveBeenCalled();
Expand Down
9 changes: 6 additions & 3 deletions apps/crn-server/test/fixtures/manuscript.fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const getManuscriptDataObject = (
): ManuscriptDataObject => ({
id: 'manuscript-id-1',
title: 'Manuscript Title',
teamId: 'team-1',
...data,
});

Expand All @@ -26,12 +27,14 @@ export const getContentfulGraphqlManuscripts = (
id: 'manuscript-id-1',
},
title: 'Manuscript Title',

teamsCollection: {
items: [{ sys: { id: 'team-1' } }],
},
...props,
});

export const getManuscriptCreateDataObject = (): ManuscriptCreateDataObject => {
const { title } = getManuscriptDataObject();
const { title, teamId } = getManuscriptDataObject();

return { title };
return { title, teamId };
};
15 changes: 15 additions & 0 deletions apps/crn-server/test/fixtures/teams.fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const getContentfulGraphql = (teamById = false) => ({
TeamMembershipCollection: () => getContentfulGraphqlTeamMemberships(),
Users: () => getContentfulGraphqlTeamMembers(),
UsersLabsCollection: () => getContentfulGraphqlTeamMemberLabs(),
ManuscriptsCollection: () => getContentfulGraphqlManuscripts(),
});

export const getContentfulGraphqlTeamById = (): NonNullable<
Expand All @@ -50,6 +51,7 @@ export const getContentfulGraphqlTeamById = (): NonNullable<
},
},
linkedFrom: {
manuscriptsCollection: getContentfulGraphqlManuscripts(),
teamMembershipCollection: {
items: [
{
Expand Down Expand Up @@ -135,6 +137,15 @@ export const getContentfulGraphqlTeamMemberLabs = () => ({
],
});

export const getContentfulGraphqlManuscripts = (): NonNullable<
NonNullable<FetchTeamByIdQuery['teams']>['linkedFrom']
>['manuscriptsCollection'] => ({
items: [
{ sys: { id: '1' }, title: 'Manuscript 1' },
{ sys: { id: '2' }, title: 'Manuscript 2' },
],
});

export const getContentfulTeamsGraphqlResponse =
(): ContentfulFetchTeamsQuery => ({
teamsCollection: {
Expand Down Expand Up @@ -175,6 +186,10 @@ export const getTeamDataObject = (): TeamDataObject => ({
],
},
],
manuscripts: [
{ id: '1', title: 'Manuscript 1' },
{ id: '2', title: 'Manuscript 2' },
],
projectTitle:
'The genome-microbiome axis in the cause of Parkinson disease: Mechanistic insights and therapeutic implications from experimental models and a genetically stratified patient population.',
proposalURL: '4cfb1b7b-bafe-4fca-b2ab-197e84d98996',
Expand Down
52 changes: 49 additions & 3 deletions apps/crn-server/test/routes/manuscript.route.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createUserResponse } from '@asap-hub/fixtures';
import { UserResponse } from '@asap-hub/model';
import { ManuscriptPostRequest, UserResponse } from '@asap-hub/model';
import { AuthHandler } from '@asap-hub/server-common';
import Boom from '@hapi/boom';
import supertest from 'supertest';
Expand Down Expand Up @@ -79,7 +79,7 @@ describe('/manuscripts/ route', () => {
describe('POST /manuscripts/', () => {
const manuscriptResponse = getManuscriptResponse();

test('Should return 403 when not allowed to create a manuscript', async () => {
test('Should return 403 when not allowed to create a manuscript because user is not onboarded', async () => {
const createManuscriptRequest = getManuscriptCreateDataObject();

userMockFactory.mockReturnValueOnce({
Expand All @@ -95,8 +95,54 @@ describe('/manuscripts/ route', () => {
expect(response.status).toEqual(403);
});

test('Should return 403 when not allowed to create a manuscript because user does not belong to the team', async () => {
const createManuscriptRequest: ManuscriptPostRequest = {
...getManuscriptCreateDataObject(),
teamId: 'team-3',
};

userMockFactory.mockReturnValueOnce({
...createUserResponse(),
teams: [
{
role: 'Key Personnel',
displayName: 'Test 1',
id: 'test-1',
},
{
role: 'Collaborating PI',
displayName: 'Test 2',
id: 'test-2',
},
],
});

const response = await supertest(app)
.post('/manuscripts')
.send(createManuscriptRequest)
.set('Accept', 'application/json');

expect(response.status).toEqual(403);
});

test('Should return a 201 when is hit', async () => {
const createManuscriptRequest = getManuscriptCreateDataObject();
const teamId = 'team-1';

const createManuscriptRequest: ManuscriptPostRequest = {
...getManuscriptCreateDataObject(),
teamId,
};

userMockFactory.mockReturnValueOnce({
...createUserResponse(),
teams: [
{
role: 'Key Personnel',
displayName: 'Test 1',
id: teamId,
},
],
});

manuscriptControllerMock.create.mockResolvedValueOnce(manuscriptResponse);

Expand Down
1 change: 1 addition & 0 deletions apps/storybook/src/TeamProfilePage.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const props = (): Omit<ComponentProps<typeof TeamProfilePage>, 'children'> => ({
lastModifiedDate: formatISO(subDays(new Date(), 2)),
labCount: number('Lab count', 15),
tags: [],
manuscripts: [],
teamListElementId: 'uuid',
upcomingEventsCount: 7,
pointOfContact: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module.exports.description = 'Add field teams';

module.exports.up = (migration) => {
const manuscripts = migration.editContentType('manuscripts');

manuscripts
.createField('teams')
.name('Teams')
.type('Array')
.localized(false)
.required(false)
.validations([])
.disabled(false)
.omitted(false)
.items({
type: 'Link',

validations: [
{
linkContentType: ['teams'],
},
],

linkType: 'Entry',
});

manuscripts.changeFieldControl('teams', 'builtin', 'entryLinksEditor', {
bulkEditing: false,
showLinkEntityAction: true,
showCreateEntityAction: false,
});
};

module.exports.down = (migration) => {
const manuscripts = migration.editContentType('manuscripts');
manuscripts.deleteField('announcements');
};
Loading

0 comments on commit b40ec43

Please sign in to comment.