Skip to content

Commit

Permalink
Merge branch '3.2.0' into CRDCDH-1991
Browse files Browse the repository at this point in the history
  • Loading branch information
amattu2 authored Dec 17, 2024
2 parents f3a82f8 + ef123ee commit 656a2c7
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 72 deletions.
173 changes: 122 additions & 51 deletions src/components/DataSubmissions/CreateDataSubmissionDialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@ import {
ContextState as AuthCtxState,
Status as AuthStatus,
} from "../Contexts/AuthContext";
import {
CREATE_SUBMISSION,
CreateSubmissionResp,
GetMyUserResp,
LIST_ORGS,
ListOrgsResp,
} from "../../graphql";
import { CREATE_SUBMISSION, CreateSubmissionResp, GetMyUserResp } from "../../graphql";

const baseStudies: GetMyUserResp["getMyUser"]["studies"] = [
{
Expand Down Expand Up @@ -66,42 +60,6 @@ const createSubmissionMocks: MockedResponse<CreateSubmissionResp>[] = [
},
];

const listOrgsMocks: MockedResponse<ListOrgsResp>[] = [
{
request: {
query: LIST_ORGS,
},
result: {
data: {
listOrganizations: [
{
_id: "some-org-1",
name: "org1",
status: "Active",
conciergeName: "",
abbreviation: "",
description: "",
studies: [
{
studyName: "study1",
studyAbbreviation: "SN",
},
{
studyName: "study2",
studyAbbreviation: "CS",
},
],
createdAt: "2023-10-06T19:19:04.183Z",
updateAt: "2024-07-03T19:09:29.513Z",
},
],
},
},
},
];

const baseMocks = [...createSubmissionMocks, ...listOrgsMocks];

const baseUser: Omit<User, "role"> = {
_id: "",
firstName: "",
Expand Down Expand Up @@ -129,7 +87,7 @@ type ParentProps = {

const TestParent: FC<ParentProps> = ({
authCtxState = baseAuthCtx,
mocks = baseMocks,
mocks = [...createSubmissionMocks],
children,
}) => (
<AuthCtx.Provider value={authCtxState}>
Expand Down Expand Up @@ -344,7 +302,6 @@ describe("Basic Functionality", () => {

it("should show an error message when submission could not be created (network)", async () => {
const mocks: MockedResponse[] = [
...listOrgsMocks,
{
request: {
query: CREATE_SUBMISSION,
Expand Down Expand Up @@ -425,7 +382,6 @@ describe("Basic Functionality", () => {

it("should show an error message when submission could not be created (GraphQL)", async () => {
const mocks: MockedResponse[] = [
...listOrgsMocks,
{
request: {
query: CREATE_SUBMISSION,
Expand Down Expand Up @@ -705,7 +661,6 @@ describe("Basic Functionality", () => {
{
wrapper: (p) => (
<TestParent
mocks={baseMocks}
authCtxState={{
...baseAuthCtx,
user: { ...baseUser, role: "Submitter", studies: baseStudies },
Expand Down Expand Up @@ -776,7 +731,7 @@ describe("Implementation Requirements", () => {
{
wrapper: (p) => (
<TestParent
mocks={[...listOrgsMocks]}
mocks={[]}
authCtxState={{
...baseAuthCtx,
user: { ...baseUser, role: "Submitter", studies: ApprovedStudyNoDbGaPID },
Expand Down Expand Up @@ -831,7 +786,7 @@ describe("Implementation Requirements", () => {
{
wrapper: (p) => (
<TestParent
mocks={[...listOrgsMocks]}
mocks={[]}
authCtxState={{
...baseAuthCtx,
user: { ...baseUser, role: "Submitter", studies: ApprovedStudyNoDbGaPID },
Expand Down Expand Up @@ -904,7 +859,7 @@ describe("Implementation Requirements", () => {
{
wrapper: (p) => (
<TestParent
mocks={[...listOrgsMocks]}
mocks={[]}
authCtxState={{
...baseAuthCtx,
user: { ...baseUser, role: "Submitter", studies: ApprovedStudyNoDbGaPID },
Expand Down Expand Up @@ -972,7 +927,7 @@ describe("Implementation Requirements", () => {
{
wrapper: (p) => (
<TestParent
mocks={[...listOrgsMocks]}
mocks={[]}
authCtxState={{
...baseAuthCtx,
user: { ...baseUser, role: "Submitter", studies: ApprovedStudyNoDbGaPID },
Expand Down Expand Up @@ -1017,4 +972,120 @@ describe("Implementation Requirements", () => {
{ normalizeWhitespace: true }
);
});

// NOTE: We're just random-testing against the opposite of the RequiresStudiesAssigned variable
// it.each<UserRole>(["Data Curator", "Data Commons POC"])(
// "should fetch all of the studies if the user's role is %s",
// async (role) => {
// const mockMatcher = jest.fn().mockImplementation(() => true);
// const listApprovedStudiesMock: MockedResponse<
// ListApprovedStudiesResp,
// ListApprovedStudiesInput
// > = {
// request: {
// query: LIST_APPROVED_STUDIES,
// },
// variableMatcher: mockMatcher,
// result: {
// data: {
// listApprovedStudies: {
// total: 1,
// studies: [
// {
// _id: "study1",
// studyName: "study-1-from-api",
// studyAbbreviation: "study-1-from-api-abbr",
// dbGaPID: "",
// controlledAccess: false,
// },
// {
// _id: "study2",
// studyName: "study-2-from-api",
// studyAbbreviation: "study-2-from-api-abbr",
// dbGaPID: "",
// controlledAccess: false,
// },
// ] as ApprovedStudy[],
// },
// },
// },
// };

// const { getByRole } = render(<CreateDataSubmissionDialog onCreate={jest.fn()} />, {
// wrapper: (p) => (
// <TestParent
// mocks={[listApprovedStudiesMock]}
// authCtxState={{ ...baseAuthCtx, user: { ...baseUser, role } }}
// {...p}
// />
// ),
// });

// userEvent.click(getByRole("button", { name: "Create a Data Submission" }));

// await waitFor(() => {
// expect(mockMatcher).toHaveBeenCalledTimes(1); // Ensure the listApprovedStudies query was called
// });
// }
// );

// it("should fetch all of the studies if the user's assigned studies contains the 'All' study", async () => {
// const mockMatcher = jest.fn().mockImplementation(() => true);
// const listApprovedStudiesMock: MockedResponse<
// ListApprovedStudiesResp,
// ListApprovedStudiesInput
// > = {
// request: {
// query: LIST_APPROVED_STUDIES,
// },
// variableMatcher: mockMatcher,
// result: {
// data: {
// listApprovedStudies: {
// total: 1,
// studies: [
// {
// _id: "study1",
// studyName: "study-1-from-api",
// studyAbbreviation: "study-1-from-api-abbr",
// dbGaPID: "",
// controlledAccess: false,
// },
// ] as ApprovedStudy[],
// },
// },
// },
// };

// const { getByRole } = render(<CreateDataSubmissionDialog onCreate={jest.fn()} />, {
// wrapper: (p) => (
// <TestParent
// mocks={[listApprovedStudiesMock]}
// authCtxState={{
// ...baseAuthCtx,
// user: {
// ...baseUser,
// role: "Federal Lead",
// studies: [
// {
// _id: "All", // This is the important part
// studyAbbreviation: "",
// studyName: "",
// dbGaPID: "",
// controlledAccess: false,
// },
// ],
// },
// }}
// {...p}
// />
// ),
// });

// userEvent.click(getByRole("button", { name: "Create a Data Submission" }));

// await waitFor(() => {
// expect(mockMatcher).toHaveBeenCalledTimes(1); // Ensure the listApprovedStudies query was called
// });
// });
});
52 changes: 41 additions & 11 deletions src/components/DataSubmissions/CreateDataSubmissionDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@ import {
Typography,
styled,
} from "@mui/material";
import { useMutation } from "@apollo/client";
import { useMutation, useQuery } from "@apollo/client";
import { Controller, SubmitHandler, useForm } from "react-hook-form";
import { CREATE_SUBMISSION, CreateSubmissionResp, CreateSubmissionInput } from "../../graphql";
import {
CREATE_SUBMISSION,
CreateSubmissionResp,
CreateSubmissionInput,
ListApprovedStudiesResp,
ListApprovedStudiesInput,
LIST_APPROVED_STUDIES,
} from "../../graphql";
import RadioInput, { RadioOption } from "./RadioInput";
import { DataCommons } from "../../config/DataCommons";
import { ReactComponent as CloseIconSvg } from "../../assets/icons/close_icon.svg";
Expand All @@ -26,6 +33,7 @@ import StyledLabel from "../StyledFormComponents/StyledLabel";
import BaseStyledHelperText from "../StyledFormComponents/StyledHelperText";
import Tooltip from "../Tooltip";
import { Logger, validateEmoji } from "../../utils";
import { RequiresStudiesAssigned } from "../../config/AuthRoles";

const CreateSubmissionDialog = styled(Dialog)({
"& .MuiDialog-paper": {
Expand Down Expand Up @@ -215,6 +223,14 @@ const CreateDataSubmissionDialog: FC<Props> = ({ onCreate }) => {
const [isDbGapRequired, setIsDbGapRequired] = useState<boolean>(false);
const [dbGaPID, setDbGaPID] = useState<string>("");

const shouldFetchAllStudies = useMemo<boolean>(
() =>
creatingSubmission &&
(!RequiresStudiesAssigned.includes(user?.role) ||
(user?.studies || [])?.findIndex((s) => s?._id === "All") !== -1),
[creatingSubmission, user?.role, user?.studies]
);

const [createDataSubmission] = useMutation<CreateSubmissionResp, CreateSubmissionInput>(
CREATE_SUBMISSION,
{
Expand All @@ -223,6 +239,26 @@ const CreateDataSubmissionDialog: FC<Props> = ({ onCreate }) => {
}
);

const { data: allStudies } = useQuery<ListApprovedStudiesResp, ListApprovedStudiesInput>(
LIST_APPROVED_STUDIES,
{
variables: { first: -1, orderBy: "studyAbbreviation", sortDirection: "asc" },
context: { clientName: "backend" },
fetchPolicy: "cache-first",
skip: !shouldFetchAllStudies,
}
);

const studies = useMemo<User["studies"]>(() => {
if (shouldFetchAllStudies) {
return allStudies?.listApprovedStudies?.studies || [];
}

return (
user?.studies?.sort((a, b) => a?.studyAbbreviation?.localeCompare(b?.studyAbbreviation)) || []
);
}, [shouldFetchAllStudies, allStudies, user?.studies]);

const orgOwnerOrSubmitter = user?.role === "Organization Owner" || user?.role === "Submitter";
const intention = watch("intention");
const submissionTypeOptions: RadioOption[] = [
Expand Down Expand Up @@ -265,12 +301,6 @@ const CreateDataSubmissionDialog: FC<Props> = ({ onCreate }) => {
},
];

const sortedStudies = useMemo<User["studies"]>(
() =>
user?.studies?.sort((a, b) => a.studyAbbreviation.localeCompare(b.studyAbbreviation)) || [],
[user?.studies]
);

const handleOpenDialog = () => {
reset();
setCreatingSubmission(true);
Expand Down Expand Up @@ -310,7 +340,7 @@ const CreateDataSubmissionDialog: FC<Props> = ({ onCreate }) => {

useEffect(() => {
const studyID = watch("studyID");
const mappedStudy = user?.studies?.find((s) => s?._id === studyID);
const mappedStudy = studies?.find((s) => s?._id === studyID);

if (!studyID || !mappedStudy) {
setDbGaPID("");
Expand All @@ -320,7 +350,7 @@ const CreateDataSubmissionDialog: FC<Props> = ({ onCreate }) => {

setDbGaPID(mappedStudy.dbGaPID || "");
setIsDbGapRequired(mappedStudy.controlledAccess);
}, [watch("studyID")]);
}, [watch("studyID"), studies]);

return (
<>
Expand Down Expand Up @@ -445,7 +475,7 @@ const CreateDataSubmissionDialog: FC<Props> = ({ onCreate }) => {
inputProps={{ "aria-labelledby": "study" }}
data-testid="create-data-submission-dialog-study-id-input"
>
{sortedStudies.map((study) => (
{studies.map((study) => (
<MenuItem key={study._id} value={study._id}>
{study.studyAbbreviation}
</MenuItem>
Expand Down
5 changes: 5 additions & 0 deletions src/config/AuthRoles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,8 @@ export const CanCreateSubmissionRequest: UserRole[] = ["User", "Submitter", "Org
* A set of user roles that are allowed to Submit a Submission Request.
*/
export const CanSubmitSubmissionRequestRoles: UserRole[] = ["User", "Submitter", "Federal Lead"];

/**
* A set of roles that are constrained to a set of studies.
*/
export const RequiresStudiesAssigned: UserRole[] = ["Submitter", "Federal Monitor", "Federal Lead"];
10 changes: 6 additions & 4 deletions src/hooks/useProfileFields.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ describe("Users View", () => {
it.each<[FieldState, UserRole]>([
["HIDDEN", "User"],
["HIDDEN", "Organization Owner"],
["HIDDEN", "Federal Lead"],
["HIDDEN", "Data Curator"],
["HIDDEN", "Data Commons POC"],
["HIDDEN", "Admin"],
["HIDDEN", "fake role" as UserRole],
["UNLOCKED", "Submitter"], // NOTE: This role accepts studies
["UNLOCKED", "Federal Monitor"], // NOTE: This role accepts studies
// NOTE: All of the following are assigned to studies
["UNLOCKED", "Federal Lead"],
["UNLOCKED", "Submitter"],
["UNLOCKED", "Federal Monitor"],
])("should return %s for the studies field on the users page for role %s", (state, role) => {
const user = { _id: "User-A", role: "Admin" } as User;
const profileOf: Pick<User, "_id" | "role"> = { _id: "I-Am-User-B", role };
Expand Down Expand Up @@ -120,7 +121,8 @@ describe("Profile View", () => {
it.each<UserRole>([
"User",
"Submitter",
"Federal Monitor", // NOTE: Only this role accepts studies
"Federal Monitor",
"Federal Lead",
"Data Commons POC",
"fake role" as UserRole,
])("should return HIDDEN for the studies field on the profile page for role %s", (role) => {
Expand Down
Loading

0 comments on commit 656a2c7

Please sign in to comment.