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

Feature/project-id #696

Merged
merged 7 commits into from
Mar 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/e2etests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
if [[ $BRANCH == master ]]; then
echo "VERSION=master" >> $GITHUB_ENV
else
echo "VERSION=develop" >> $GITHUB_ENV
echo "VERSION=feature/project-ownership" >> $GITHUB_ENV
fi
- name: Clone backend
uses: actions/checkout@v2
Expand Down
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Folder's Id is needed when creating a new object or draft object
- Patching folder is no longer needed when patching object or draft object
- Updated projectId in GET and POST requests for folders and templates #696

### Added

- ProjectId is a mandatory query parameter in GET requests for folders and templates
- ProjectId is a mandatory propperty in POST requests for folders and templates
- Templates has a separate API endpoint and is no longer a propety under User
- Template's index is a mandatory property in PATCH request for a template

- Folder's Id is needed when creating a new object or draft object #672

### Added

- Added folderId as query parameter for object's and draft object's Post request
- Patching folder is no longer needed when patching object or draft object

- Updated Node.js version in GitHub workflows and Dockerfile #655
- Disallow use of any-type #624
Expand Down
2 changes: 0 additions & 2 deletions cypress/integration/doiForm.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
describe("DOI form", function () {
beforeEach(() => {
cy.task("resetDb")

cy.login()

cy.get("button", { timeout: 10000 }).contains("Create submission").click()

// Add folder name & description, navigate to submissions
Expand Down
4 changes: 4 additions & 0 deletions cypress/integration/home.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ describe("Home e2e", function () {

it("show draft submission data table in Home page, be able to edit and delete a draft submission inside the table", () => {
cy.intercept("/folders*").as("fetchFolders")

// Check that there is projectId in home page
cy.get("[data-testid='project-id-selection']").should("be.visible")
cy.get("[data-testid='project-id-selection'] > div > input").invoke("val").should("not.be.empty")
// Create a new Unpublished folder
cy.get("button").contains("Create submission").click()

Expand Down
7 changes: 2 additions & 5 deletions cypress/integration/objectLinksAttributes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,8 @@ describe("render objects' links and attributes ", function () {

cy.get("div[data-testid='studyLinks'] > div", { timeout: 10000 }).should("have.length", 3)
// Remove URL Link and check that the rest of the Study Links render correctly
cy.get("[data-testid='studyLinks[1]']").should("be.visible")

cy.get("[data-testid='studyLinks[1]'] > button", { timeout: 30000 })
.should("be.visible")
.then($el => $el.click())
cy.wait(0)
cy.get("[data-testid='studyLinks[1]'] > button", { timeout: 10000 }).should("be.visible").click()

cy.get("div[data-testid='studyLinks'] > div", { timeout: 30000 }).should("have.length", 2)

Expand Down
646 changes: 363 additions & 283 deletions cypress/integration/pagination.spec.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ Cypress.on("uncaught:exception", () => {

Cypress.Commands.add("login", () => {
cy.visit(baseUrl)
cy.intercept("/folders*").as("getFolders")
cy.get('a[data-testid="login-button"]').click()
cy.wait("@getFolders")
})

Cypress.Commands.add("newSubmission", folderName => {
Expand Down
8 changes: 7 additions & 1 deletion src/__tests__/Home.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ jest.mock("react-i18next", () => ({

describe("HomePage", () => {
const store = mockStore({
user: { name: "Test User" },
user: {
name: "Test User",
projects: [
{ projectId: "PROJECT1", projectName: "Project 1" },
{ projectId: "PROJECT2", projectName: "Project 2" },
],
},
})
beforeEach(() => {
render(
Expand Down
44 changes: 5 additions & 39 deletions src/__tests__/Page403.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,45 +36,11 @@ describe("Page403", () => {
test("redirects to Home Page after 10s", () => {
jest.useFakeTimers()
const store = mockStore({
user: { name: "test" },
unpublishedFolders: [
{ descriptioni: "d", drafts: [], folderId: "UNPUB1", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB2", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB3", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB4", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB5", metadataObjects: [], name: "Unpub", published: false },
],
publishedFolders: [
{ descriptioni: "d", drafts: [], folderId: "PUB1", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB2", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB3", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB4", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB5", metadataObjects: [], name: "Pub", published: false },
],
totalFolders: {
totalUnpublishedFolders: [
{ descriptioni: "d", drafts: [], folderId: "UNPUB1", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB2", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB3", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB4", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB5", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB6", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB7", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB8", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB9", metadataObjects: [], name: "Unpub", published: false },
{ descriptioni: "d", drafts: [], folderId: "UNPUB10", metadataObjects: [], name: "Unpub", published: false },
],
totalPublishedFolders: [
{ descriptioni: "d", drafts: [], folderId: "PUB1", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB2", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB3", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB4", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB5", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB6", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB7", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB8", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB9", metadataObjects: [], name: "Pub", published: false },
{ descriptioni: "d", drafts: [], folderId: "PUB10", metadataObjects: [], name: "Pub", published: false },
user: {
name: "test",
projects: [
{ projectId: "PROJECT1", projectName: "Project 1" },
{ projectId: "PROJECT2", projectName: "Project 2" },
],
},
})
Expand Down
38 changes: 16 additions & 22 deletions src/components/Home/SubmissionDataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,18 @@ const DataTable = styled(DataGrid)(({ theme }) => ({
color: theme.palette.secondary.main,
"&.MuiDataGrid-root .MuiDataGrid-columnHeader:focus, &.MuiDataGrid-root .MuiDataGrid-cell:focus": {
outline: "none",
},
"& .MuiDataGrid-columnHeaders": {
fontSize: "1.4rem",
position: "relative",
},
"& .MuiDataGrid-columnSeparator": {
display: "none",
},
"& .MuiDataGrid-cell:last-of-type:not(.MuiDataGrid-cell--withRenderer)": {
display: "none",
},
"& .MuiDataGrid-columnHeadersInner, .MuiDataGrid-columnHeader, .MuiDataGrid-virtualScrollerRenderZone, .MuiDataGrid-cell, .MuiDataGrid-cell--withRenderer":
{
width: "100% !important",
},
"& .MuiDataGrid-columnHeaderTitleContainer": {
padding: 0,
"& > *": { fontWeight: 700 },
Expand All @@ -56,19 +61,17 @@ const DataTable = styled(DataGrid)(({ theme }) => ({
},
"& .MuiDataGrid-row": {
border: `1px solid ${theme.palette.secondary.light}`,
width: "100% !important",
},
"& .MuiDataGrid-virtualScrollerContent": {
fontSize: "1.6rem",
},
"& .MuiDataGrid-cell--withRenderer": {
width: "100%",
maxWidth: "none !important",
},

"& .MuiDataGrid-actionsCell": {
color: theme.palette.primary.main,
marginRight: "1rem",
"& .MuiMenuItem-root": {
fontSize: "1.6rem",
alignItems: "flex-start",
[theme.breakpoints.down("sm")]: {
display: "flex",
flexDirection: "column",
gridGap: 0,
"& .MuiMenuItem-root.MuiMenuItem-gutters.MuiButtonBase-root": { minHeight: "0 !important" },
},
},
}))
Expand All @@ -95,12 +98,10 @@ const SubmissionDataTable: React.FC<SubmissionDataTableProps> = props => {
field: "name",
headerName: "Name",
sortable: false,
width: 310,
},
{
field: "dateCreated",
headerName: "Date created",
width: 250,
type: "date",
valueFormatter: (params: GridValueFormatterParams): GridCellValue => {
const { convertedDate } = params.value as Record<string, string>
Expand All @@ -115,13 +116,6 @@ const SubmissionDataTable: React.FC<SubmissionDataTableProps> = props => {
{
field: "lastModifiedBy",
headerName: "Last modified by",
width: 250,
sortable: false,
},
{
field: "cscProject",
headerName: "CSC Project",
width: 250,
sortable: false,
},
{
Expand Down
2 changes: 1 addition & 1 deletion src/components/Home/UserDraftTemplateActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import WizardFillObjectDetailsForm from "components/NewDraftWizard/WizardForms/W
import { ResponseStatus } from "constants/responseStatus"
import { ObjectStatus } from "constants/wizardObject"
import { updateStatus } from "features/statusMessageSlice"
import { deleteTemplateByAccessionId } from "features/userSlice"
import { deleteTemplateByAccessionId } from "features/templateSlice"
import { setCurrentObject, resetCurrentObject } from "features/wizardCurrentObjectSlice"
import { setObjectType, resetObjectType } from "features/wizardObjectTypeSlice"
import { setFolder } from "features/wizardSubmissionFolderSlice"
Expand Down
22 changes: 14 additions & 8 deletions src/components/Home/UserDraftTemplates.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react"
import React, { useEffect, useState } from "react"

import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"
Expand All @@ -17,10 +17,11 @@ import { makeStyles } from "@mui/styles"

import UserDraftTemplateActions from "./UserDraftTemplateActions"

import { setTemplateAccessionIds } from "features/templatesSlice"
import { setTemplateAccessionIds } from "features/templateAccessionIdsSlice"
import { getTemplates } from "features/templateSlice"
import { useAppSelector, useAppDispatch } from "hooks"
import { ObjectInsideFolderWithTags } from "types"
import { formatDisplayObjectType, getUserTemplates, getItemPrimaryText } from "utils"
import { formatDisplayObjectType, getItemPrimaryText, getUserTemplates } from "utils"

const useStyles = makeStyles(theme => ({
card: {
Expand Down Expand Up @@ -65,19 +66,25 @@ const useStyles = makeStyles(theme => ({

const UserDraftTemplates: React.FC = () => {
const classes = useStyles()
const user = useAppSelector(state => state.user)
const dispatch = useAppDispatch()

const projectId = useAppSelector(state => state.projectId)
const templates: Array<ObjectInsideFolderWithTags> | [] = useAppSelector(state => state.templates)
const objectTypesArray = useAppSelector(state => state.objectTypesArray)
const templateAccessionIds = useAppSelector(state => state.templateAccessionIds)

const templates = user.templates ? getUserTemplates(user.templates, objectTypesArray) : []

const displayTemplates = getUserTemplates(templates, objectTypesArray)
type TemplateGroupProps = { draft: { [schema: string]: ObjectInsideFolderWithTags[] } }

useEffect(() => {
dispatch(getTemplates(projectId))
}, [dispatch])

// Render when there is user's draft template(s)
const DraftList = () => {
return (
<React.Fragment>
{templates.map((draft: { [key: string]: ObjectInsideFolderWithTags[] }, index: number) => (
{displayTemplates.map((draft: { [key: string]: ObjectInsideFolderWithTags[] }, index: number) => (
<TemplateGroup key={index} draft={draft} />
))}
</React.Fragment>
Expand All @@ -87,7 +94,6 @@ const UserDraftTemplates: React.FC = () => {
const TemplateGroup = (props: TemplateGroupProps) => {
const { draft } = props

const dispatch = useAppDispatch()
const [checkedItems, setCheckedItems] = useState<string[]>(templateAccessionIds)

const handleChange = (accessionId: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const useStyles = makeStyles(theme => ({
const WizardFooter: React.FC = () => {
const classes = useStyles()
const dispatch = useAppDispatch()
const projectId = useAppSelector(state => state.projectId)
const folder = useAppSelector(state => state.submissionFolder)

const [dialogOpen, setDialogOpen] = useState(false)
Expand Down Expand Up @@ -81,7 +82,7 @@ const WizardFooter: React.FC = () => {
resetDispatch()
} else if (alertWizard && alertType === "publish") {
if (formData && formData?.length > 0) {
await saveDraftsAsTemplates(formData, dispatch)
await saveDraftsAsTemplates(projectId, formData, dispatch)
}
// Publish the folder
dispatch(publishFolderContent(folder))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { setDraftStatus, resetDraftStatus } from "features/draftStatusSlice"
import { setFileTypes } from "features/fileTypesSlice"
import { resetFocus } from "features/focusSlice"
import { updateStatus } from "features/statusMessageSlice"
import { replaceTemplate } from "features/userSlice"
import { updateTemplateDisplayTitle } from "features/templateSlice"
import { setCurrentObject, resetCurrentObject } from "features/wizardCurrentObjectSlice"
import { deleteObjectFromFolder, replaceObjectInFolder } from "features/wizardSubmissionFolderSlice"
import { useAppSelector, useAppDispatch } from "hooks"
Expand Down Expand Up @@ -246,8 +246,8 @@ const FormContent = ({
const draftStatus = useAppSelector(state => state.draftStatus)
const alert = useAppSelector(state => state.alert)
const clearForm = useAppSelector(state => state.clearForm)
const user = useAppSelector(state => state.user)

const templates = useAppSelector(state => state.templates)
const methods = useForm({ mode: "onBlur", resolver })

const [currentObjectId, setCurrentObjectId] = useState<string | null>(currentObject?.accessionId)
Expand Down Expand Up @@ -406,35 +406,28 @@ const FormContent = ({
const cleanedValues = getCleanedValues()

if (checkFormCleanedValuesEmpty(cleanedValues)) {
const response = await templateAPI.patchTemplateFromJSON(objectType, currentObject.accessionId, cleanedValues)

const templates = user.templates
const index = templates.findIndex(
(item: { accessionId: string }) => item.accessionId === currentObject.accessionId
const index =
templates?.findIndex((item: { accessionId: string }) => item.accessionId === currentObject.accessionId) || 0
const response = await templateAPI.patchTemplateFromJSON(
objectType,
currentObject.accessionId,
cleanedValues,
index
)

const displayTitle = getObjectDisplayTitle(objectType, cleanedValues as unknown as ObjectDisplayValues)

if (response.ok) {
closeDialog()
dispatch(replaceTemplate(index, displayTitle, currentObject.accessionId))
.then(() =>
dispatch(
updateStatus({
status: ResponseStatus.success,
response: response,
helperText: "",
})
)
)
.catch(error => {
dispatch(
updateStatus({
status: ResponseStatus.error,
response: error,
helperText: "Cannot replace template",
})
)
dispatch(updateTemplateDisplayTitle({ accessionId: currentObject.accessionId, displayTitle: displayTitle }))

dispatch(
updateStatus({
status: ResponseStatus.success,
response: response,
helperText: "",
})
)
} else {
dispatch(
updateStatus({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import templateAPIService from "services/templateAPI"
import { ObjectInsideFolderWithTags } from "types"
import { getOrigObjectType, getObjectDisplayTitle } from "utils"

const saveDraftsAsTemplates = async (formData: ObjectInsideFolderWithTags[], dispatch: (reducer: unknown) => void) => {
const saveDraftsAsTemplates = async (
projectId: string,
formData: ObjectInsideFolderWithTags[],
dispatch: (reducer: unknown) => void
) => {
// Filter unique draft-schemas existing in formData
const draftSchemas = formData
.map((item: { schema: string }) => item.schema)
Expand Down Expand Up @@ -40,7 +44,7 @@ const saveDraftsAsTemplates = async (formData: ObjectInsideFolderWithTags[], dis

// Remove unnecessary values such as "date"
// Add the object in the form of {template: draft's values, tags: {displayTitle}} to the array
draftsArr.push({ template: { ...omit(draftResponse.data, OmitObjectValues) }, ...draftTags })
draftsArr.push({ projectId, template: { ...omit(draftResponse.data, OmitObjectValues) }, ...draftTags })
} catch (err) {
dispatch(
updateStatus({
Expand Down
Loading