Skip to content
This repository has been archived by the owner on Jan 23, 2025. It is now read-only.

Commit

Permalink
feat: split upload csv endpoint (#1537)
Browse files Browse the repository at this point in the history
* feat: split upload csv endpoint

* refactor: fix flacky tests

* refactor: apply changes

* refactor: fix test
  • Loading branch information
pyphilia authored Oct 24, 2024
1 parent c415d66 commit 944102c
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 94 deletions.
2 changes: 1 addition & 1 deletion cypress/e2e/item/share/shareItemFromCsv.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ describe('Share Item From CSV', () => {
);
cy.get(`#${SHARE_ITEM_FROM_CSV_CONFIRM_BUTTON_ID}`).should('be.enabled');
cy.get(`#${SHARE_ITEM_FROM_CSV_CONFIRM_BUTTON_ID}`).click();
cy.wait('@uploadCSV').then(({ request }) => {
cy.wait('@uploadCSVWithTemplate').then(({ request }) => {
expect(request.query.templateId).equal(templateItemId);
});
cy.get(`#${SHARE_CSV_TEMPLATE_SUMMARY_CONTAINER_ID}`)
Expand Down
9 changes: 3 additions & 6 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,9 @@ import {
mockGetItemLoginSchema,
mockGetItemLoginSchemaType,
mockGetItemMembershipsForItem,
mockGetItemTags,
mockGetItemThumbnailUrl,
mockGetItemValidationGroups,
mockGetItems,
mockGetItemsTags,
mockGetLatestValidationGroup,
mockGetLinkMetadata,
mockGetManyPublishItemInformations,
Expand Down Expand Up @@ -99,6 +97,7 @@ import {
mockUnpublishItem,
mockUpdatePassword,
mockUploadInvitationCSV,
mockUploadInvitationCSVWithTemplate,
mockUploadItem,
} from './server';

Expand Down Expand Up @@ -237,10 +236,6 @@ Cypress.Commands.add(

mockGetItemMembershipsForItem(items, currentMember);

mockGetItemTags(items);

mockGetItemsTags(items);

mockPostItemTag(cachedItems, currentMember, postItemTagError);

mockDeleteItemTag(deleteItemTagError);
Expand Down Expand Up @@ -318,6 +313,8 @@ Cypress.Commands.add(

mockUploadInvitationCSV(items, false);

mockUploadInvitationCSVWithTemplate(items, false);

mockGetPublicationStatus(itemPublicationStatus);
mockPublishItem(items);
mockUnpublishItem(items);
Expand Down
5 changes: 4 additions & 1 deletion cypress/support/commands/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ Cypress.Commands.add('clickTreeMenuItem', (value: string) => {
Cypress.Commands.add(
'handleTreeMenu',
(toItemPath, treeRootId = HOME_MODAL_ITEM_ID) => {
const ids = getParentsIdsFromPath(toItemPath);
const ids =
toItemPath === MY_GRAASP_ITEM_PATH
? []
: getParentsIdsFromPath(toItemPath);

[MY_GRAASP_ITEM_PATH, ...ids].forEach((value, idx, array) => {
cy.get(`#${treeRootId}`).then(($tree) => {
Expand Down
58 changes: 20 additions & 38 deletions cypress/support/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ const {
buildPostItemLoginSignInRoute,
buildGetItemLoginSchemaRoute,
buildGetItemMembershipsForItemsRoute,
buildGetItemTagsRoute,
buildPostItemTagRoute,
buildPatchCurrentMemberRoute,
buildEditItemMembershipRoute,
Expand All @@ -90,6 +89,7 @@ const {
buildGetItemInvitationsForItemRoute,
buildDeleteInvitationRoute,
buildPatchInvitationRoute,
buildPostUserCSVUploadWithTemplateRoute,
buildResendInvitationRoute,
buildPostUserCSVUploadRoute,
buildGetPublishedItemsForMemberRoute,
Expand Down Expand Up @@ -1100,39 +1100,6 @@ export const mockDeleteItemMembershipForItem = (): void => {
).as('deleteItemMembership');
};

export const mockGetItemTags = (items: ItemForTest[]): void => {
cy.intercept(
{
method: HttpMethod.Get,
url: new RegExp(`${API_HOST}/${buildGetItemTagsRoute(ID_FORMAT)}$`),
},
({ reply, url }) => {
const itemId = url.slice(API_HOST.length).split('/')[2];
const result = items.find(({ id }) => id === itemId)?.tags || [];
reply(result);
},
).as('getItemTags');
};

export const mockGetItemsTags = (items: ItemForTest[]): void => {
cy.intercept(
{
method: HttpMethod.Get,
url: `${API_HOST}/items/tags?id=*`,
},
({ reply, url }) => {
const ids = new URL(url).searchParams.getAll('id');
const result = ids.map(
(itemId) =>
items.find(({ id }) => id === itemId)?.tags || [
{ statusCode: StatusCodes.NOT_FOUND },
],
);
reply(result);
},
).as('getItemsTags');
};

export const mockPostItemTag = (
items: ItemForTest[],
currentMember: Member,
Expand Down Expand Up @@ -1834,16 +1801,31 @@ export const mockUploadInvitationCSV = (
if (shouldThrowError) {
return reply({ statusCode: StatusCodes.BAD_REQUEST });
}
const query = new URL(url).searchParams;
if (query.get('templateId')) {
return reply([{ groupName: 'A', memberships: [], invitations: [] }]);
}
const itemId = url.split('/').at(-3);
const item = items.find(({ id }) => id === itemId);
return reply({ memberships: item.memberships });
},
).as('uploadCSV');
};
export const mockUploadInvitationCSVWithTemplate = (
items: ItemForTest[],
shouldThrowError: boolean,
): void => {
cy.intercept(
{
method: HttpMethod.Post,
url: new RegExp(
`${API_HOST}/${buildPostUserCSVUploadWithTemplateRoute(ID_FORMAT)}`,
),
},
({ reply }) => {
if (shouldThrowError) {
return reply({ statusCode: StatusCodes.BAD_REQUEST });
}
return reply([{ groupName: 'A', memberships: [], invitations: [] }]);
},
).as('uploadCSVWithTemplate');
};

export const mockGetPublicationStatus = (status: PublicationStatus): void => {
const interceptingPathFormat = buildGetPublicationStatusRoute(ID_FORMAT);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@emotion/styled": "11.13.0",
"@graasp/chatbox": "3.3.0",
"@graasp/map": "1.19.0",
"@graasp/query-client": "4.2.0",
"@graasp/query-client": "5.0.0",
"@graasp/sdk": "4.32.1",
"@graasp/stylis-plugin-rtl": "2.2.0",
"@graasp/translations": "1.39.0",
Expand Down
43 changes: 16 additions & 27 deletions src/components/item/sharing/csvImport/DisplayInvitationSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,15 @@ const LineDisplay = ({
);

type Props = {
userCsvData?:
| { memberships: ItemMembership[]; invitations: Invitation[] }
| {
groupName: string;
memberships: ItemMembership[];
invitations: Invitation[];
}[];
userCsvDataWithTemplate?: {
groupName: string;
memberships: ItemMembership[];
invitations: Invitation[];
}[];
error: Error | null | AxiosError;
};
const DisplayInvitationSummary = ({
userCsvData,
userCsvDataWithTemplate,
error,
}: Props): JSX.Element | null => {
const { t } = useBuilderTranslation();
Expand All @@ -68,16 +66,14 @@ const DisplayInvitationSummary = ({
</Alert>
);
}
if (userCsvData) {
if (userCsvDataWithTemplate) {
// display group creation
if (Array.isArray(userCsvData)) {
return (
<Alert severity="info" id={SHARE_CSV_TEMPLATE_SUMMARY_CONTAINER_ID}>
<AlertTitle>
{t(BUILDER.SHARE_ITEM_CSV_SUMMARY_GROUP_TITLE)}
</AlertTitle>
<Stack direction="column" gap={2}>
{userCsvData.map(({ groupName, memberships, invitations }) => (
return (
<Alert severity="info" id={SHARE_CSV_TEMPLATE_SUMMARY_CONTAINER_ID}>
<AlertTitle>{t(BUILDER.SHARE_ITEM_CSV_SUMMARY_GROUP_TITLE)}</AlertTitle>
<Stack direction="column" gap={2}>
{userCsvDataWithTemplate.map(
({ groupName, memberships, invitations }) => (
<Stack>
<Typography fontWeight="bold">
{t(BUILDER.INVITATION_SUMMARY_GROUP_TITLE, { groupName })}
Expand All @@ -100,16 +96,9 @@ const DisplayInvitationSummary = ({
))}
</Stack>
</Stack>
))}
</Stack>
</Alert>
);
}

return (
<Alert severity="success">
<AlertTitle>{t(BUILDER.IMPORT_CSV_SUCCESS_TITLE)}</AlertTitle>
<Typography>{t(BUILDER.IMPORT_CSV_SUCCESS_TEXT)}</Typography>
),
)}
</Stack>
</Alert>
);
}
Expand Down
49 changes: 34 additions & 15 deletions src/components/item/sharing/csvImport/ImportUsersDialogContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { ChangeEvent, ChangeEventHandler, useState } from 'react';

import {
Alert,
AlertTitle,
Button,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Stack,
Typography,
} from '@mui/material';

import { DiscriminatedItem } from '@graasp/sdk';
Expand Down Expand Up @@ -97,12 +99,16 @@ const ImportUsersDialogContent = ({
const [selectedTemplateId, setSelectedTemplateId] = useState<string>();
const [isConfirmButtonEnabled, setIsConfirmButtonEnabled] = useState(false);
const { t } = useBuilderTranslation();
const { mutate: postUserCsv, isSuccess: isSuccessPostingCSV } =
mutations.useCSVUserImport();
const {
mutate: postUserCsv,
data: userCsvData,
error: userCSVError,
isSuccess: isSuccessPostingCSV,
} = mutations.useCSVUserImport();
mutate: postUserCsvWithTemplate,
data: userCsvDataWithTemplate,
error: userCSVErrorWithTemplate,
isSuccess: isSuccessPostingCSVWithTemplate,
} = mutations.useCSVUserImportWithTemplate();

const isSuccess = isSuccessPostingCSV || isSuccessPostingCSVWithTemplate;

const handleFileChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
if (target.files?.length) {
Expand Down Expand Up @@ -154,11 +160,18 @@ const ImportUsersDialogContent = ({

const handlePostUserCSV = () => {
if (csvFile) {
postUserCsv({
file: csvFile,
itemId: item.id,
templateItemId: selectedTemplateId,
});
if (selectedTemplateId) {
postUserCsvWithTemplate({
file: csvFile,
itemId: item.id,
templateItemId: selectedTemplateId,
});
} else {
postUserCsv({
file: csvFile,
itemId: item.id,
});
}
} else {
console.error('no file set');
}
Expand Down Expand Up @@ -196,29 +209,35 @@ const ImportUsersDialogContent = ({
/>
)}
<DisplayInvitationSummary
userCsvData={userCsvData}
error={userCSVError}
userCsvDataWithTemplate={userCsvDataWithTemplate}
error={userCSVErrorWithTemplate}
/>
{isSuccess && (
<Alert severity="success">
<AlertTitle>{t(BUILDER.IMPORT_CSV_SUCCESS_TITLE)}</AlertTitle>
<Typography>{t(BUILDER.IMPORT_CSV_SUCCESS_TEXT)}</Typography>
</Alert>
)}
</Stack>
</DialogContent>
<DialogActions>
<Button
id={SHARE_ITEM_FROM_CSV_CANCEL_BUTTON_ID}
variant="text"
onClick={handleClose}
disabled={isSuccessPostingCSV}
disabled={isSuccess}
>
{translateCommon(COMMON.CANCEL_BUTTON)}
</Button>

<Button
id={SHARE_ITEM_FROM_CSV_CONFIRM_BUTTON_ID}
variant="contained"
onClick={isSuccessPostingCSV ? handleClose : handlePostUserCSV}
onClick={isSuccess ? handleClose : handlePostUserCSV}
color="primary"
disabled={!isConfirmButtonEnabled}
>
{isSuccessPostingCSV
{isSuccess
? translateCommon(COMMON.CLOSE_BUTTON)
: translateCommon(COMMON.CONFIRM_BUTTON)}
</Button>
Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1637,9 +1637,9 @@ __metadata:
languageName: node
linkType: hard

"@graasp/query-client@npm:4.2.0":
version: 4.2.0
resolution: "@graasp/query-client@npm:4.2.0"
"@graasp/query-client@npm:5.0.0":
version: 5.0.0
resolution: "@graasp/query-client@npm:5.0.0"
dependencies:
"@tanstack/react-query": "npm:5.59.8"
"@tanstack/react-query-devtools": "npm:5.59.8"
Expand All @@ -1649,7 +1649,7 @@ __metadata:
"@graasp/sdk": ^4.0.0
"@graasp/translations": "*"
react: ^18.0.0
checksum: 10/fdf06272486d4c9979459c00f7cb0d9f98c027deef0c948ff734679b46435658076aeede49d54730922f0d12e572cf9d3bac045e346ab38629a15fab6b30d00d
checksum: 10/463dd556081d76fb2cb4bcf69b595560eb40ff528de5b1c64f189af106209bf394135d400374c80c031a57b0e3a13fdedd12d4d86988a4533b49d37c405a662a
languageName: node
linkType: hard

Expand Down Expand Up @@ -6472,7 +6472,7 @@ __metadata:
"@emotion/styled": "npm:11.13.0"
"@graasp/chatbox": "npm:3.3.0"
"@graasp/map": "npm:1.19.0"
"@graasp/query-client": "npm:4.2.0"
"@graasp/query-client": "npm:5.0.0"
"@graasp/sdk": "npm:4.32.1"
"@graasp/stylis-plugin-rtl": "npm:2.2.0"
"@graasp/translations": "npm:1.39.0"
Expand Down

0 comments on commit 944102c

Please sign in to comment.