Commit 7d0e80e4 authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Merge branch 'v1-misc-fixes' into 'v1.0.0'

Minor upload gui fixes. #709

See merge request !541
parents 6faa1770 09f53cc9
Pipeline #120574 passed with stages
in 28 minutes and 31 seconds
......@@ -52,8 +52,15 @@ export class ApiError extends Error {
}
}
export class ApiRequestError extends Error {
constructor(msg) {
super(msg)
this.name = 'BadRequest'
}
}
function handleApiError(e) {
if (e.name === 'CannotReachApi' || e.name === 'NotAuthorized' || e.name === 'DoesNotExist') {
if (e.name === 'CannotReachApi' || e.name === 'NotAuthorized' || e.name === 'DoesNotExist' || e.name === 'BadRequest') {
throw e
}
......@@ -66,6 +73,8 @@ function handleApiError(e) {
error = new DoesNotExist(errorMessage)
} else if (e.response.status === 401) {
error = new NotAuthorized(errorMessage)
} else if (e.response.status === 400) {
error = new ApiRequestError(errorMessage)
} else if (e.response.status === 502) {
error = new ApiError(errorMessage)
} else {
......
......@@ -17,7 +17,7 @@
*/
import React, { useCallback, useContext, useEffect, useState, useMemo } from 'react'
import PropTypes from 'prop-types'
import {Paper, IconButton, Tooltip, DialogContent, Button, Dialog} from '@material-ui/core'
import {Paper, IconButton, Tooltip, DialogContent, Button, Dialog, DialogTitle} from '@material-ui/core'
import { useApi, withLoginRequired } from '../api'
import Page from '../Page'
import { useErrors } from '../errors'
......@@ -67,33 +67,33 @@ const DatasetActions = React.memo(function VisitDatasetAction({data}) {
const {api} = useApi()
const {raiseError} = useErrors()
const {refresh} = useContext(PageContext)
const [openConfirmDialog, setOpenConfirmDialog] = useState(false)
const [openConfirmDeleteDialog, setOpenConfirmDeleteDialog] = useState(false)
const [openConfirmDoiDialog, setOpenConfirmDoiDialog] = useState(false)
const handleDelete = useCallback(() => {
setOpenConfirmDeleteDialog(false)
api.delete(`/datasets/${data.dataset_id}`)
.then(refresh).catch(raiseError)
}, [api, raiseError, data.dataset_id, refresh])
}, [api, raiseError, data.dataset_id, refresh, setOpenConfirmDeleteDialog])
const handleAssignDoi = useCallback(() => {
setOpenConfirmDoiDialog(false)
api.post(`/datasets/${data.dataset_id}/action/doi`)
.then(refresh).catch(raiseError)
}, [api, raiseError, data.dataset_id, refresh])
const onConfirm = () => {
setOpenConfirmDialog(true)
}
.then(refresh)
.catch(raiseError)
}, [api, raiseError, data.dataset_id, refresh, setOpenConfirmDoiDialog])
return <React.Fragment>
<Tooltip title="Assign a DOI">
<span>
<IconButton onClick={handleAssignDoi} disabled={!!data.doi}>
<IconButton onClick={() => setOpenConfirmDoiDialog(true)} disabled={!!data.doi}>
<DOIIcon />
</IconButton>
</span>
</Tooltip>
<Tooltip title={(data.doi ? 'The dataset cannot be deleted. A DOI has been assigned to the dataset.' : 'Delete the dataset')}>
<span>
<IconButton onClick={onConfirm} disabled={!!data.doi} style={{pointerEvents: 'auto'}}>
<IconButton onClick={() => setOpenConfirmDeleteDialog(true)} disabled={!!data.doi} style={{pointerEvents: 'auto'}}>
<DeleteIcon />
</IconButton>
</span>
......@@ -104,7 +104,7 @@ const DatasetActions = React.memo(function VisitDatasetAction({data}) {
</DatasetButton>
</Tooltip>
<Dialog
open={openConfirmDialog}
open={openConfirmDeleteDialog}
aria-describedby="alert-dialog-description"
>
<DialogContent>
......@@ -113,10 +113,26 @@ const DatasetActions = React.memo(function VisitDatasetAction({data}) {
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => setOpenConfirmDialog(false)} autoFocus>Cancel</Button>
<Button onClick={() => setOpenConfirmDeleteDialog(false)} autoFocus>Cancel</Button>
<Button onClick={handleDelete}>Delete</Button>
</DialogActions>
</Dialog>
<Dialog
open={openConfirmDoiDialog}
onClose={() => setOpenConfirmDoiDialog(false)}
>
<DialogTitle>Confirm that you want to assign a DOI</DialogTitle>
<DialogContent>
<DialogContentText>
Assigning a DOI is permanent. You will not be able to remove entries from
datasets with a DOI. You cannot delete datasets with a DOI.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => setOpenConfirmDoiDialog(false)} autoFocus>Cancel</Button>
<Button onClick={handleAssignDoi}>Assign DOI</Button>
</DialogActions>
</Dialog>
</React.Fragment>
})
DatasetActions.propTypes = {
......
......@@ -56,7 +56,9 @@ class ErrorSnacksUnstyled extends React.Component {
onError(error) {
let errorStr = 'Unexpected error. Please try again and let us know, if this error keeps happening.'
if (error instanceof Error) {
if (error.name === 'CannotReachApi') {
if (error.name === 'BadRequest') {
errorStr = error.message
} else if (error.name === 'CannotReachApi') {
errorStr = 'Cannot reach NOMAD, please try again later.'
} else if (error.name === 'NotAuthorized') {
errorStr = error.message
......
......@@ -224,7 +224,7 @@ function PublishUpload({upload, onPublish}) {
<Button
style={{height: 32, minWith: 100}}
size="small" variant="contained"
onClick={() => handlePublish()} color="primary" autoFocus
onClick={() => handlePublish()} color="primary"
disabled={upload.process_running}
>
{embargo > 0 ? 'Publish with embargo' : 'Publish'}
......@@ -499,7 +499,7 @@ function UploadPage() {
</Dialog>
</Grid>
</Grid>
<Stepper classes={{root: classes.stepper}} orientation="vertical" nonLinear>
<Stepper classes={{root: classes.stepper}} orientation="vertical" >
<Step expanded active={false}>
<StepLabel>Prepare and upload your files</StepLabel>
<StepContent>
......@@ -529,7 +529,7 @@ function UploadPage() {
<FilesBrower className={classes.stepContent} uploadId={uploadId} disabled={isProcessing || deleteClicked} />
</StepContent>
</Step>
<Step expanded={!isEmpty}>
<Step expanded={!isEmpty} active={false}>
<StepLabel>Process data</StepLabel>
<StepContent>
<ProcessingStatus data={data} />
......@@ -540,7 +540,7 @@ function UploadPage() {
onPaginationChanged={setPagination}/>
</StepContent>
</Step>
{(isAuthenticated && isWriter) && <Step expanded={!isEmpty}>
{(isAuthenticated && isWriter) && <Step expanded={!isEmpty} active={false}>
<StepLabel>Edit author metadata</StepLabel>
<StepContent>
<Typography className={classes.stepContent}>
......@@ -556,7 +556,7 @@ function UploadPage() {
{!isEmpty && <EditMetaDataDialog selectedEntries={{'upload_id': upload.upload_id}}/>}
</StepContent>
</Step>}
{(isAuthenticated && isWriter) && <Step expanded={!isEmpty}>
{(isAuthenticated && isWriter) && <Step expanded={!isEmpty} active={false}>
<StepLabel>Publish</StepLabel>
<StepContent>
{isPublished && <Typography className={classes.stepContent}>
......
......@@ -253,7 +253,7 @@ function UploadsPage() {
page_size: 10,
page: 1,
order_by: 'upload_create_time',
order: 'asc'
order: 'desc'
})
const fetchData = useCallback(() => {
......
......@@ -24,19 +24,19 @@ from pydantic import BaseModel, Field, validator
from datetime import datetime
import enum
from nomad import utils, datamodel, processing
from nomad import utils, datamodel, processing, config
from nomad.metainfo.elasticsearch_extension import entry_type
from nomad.utils import strip, create_uuid
from nomad.datamodel import Dataset as DatasetDefinitionCls
from nomad.doi import DOI
from nomad.search import update_by_query
from nomad.search import search, update_by_query
from .auth import create_user_dependency
from .entries import _do_exaustive_search
from ..utils import create_responses, parameter_dependency_from_model
from ..models import (
Pagination, PaginationResponse, Query, HTTPExceptionModel, User,
Direction, Owner, Any_)
Pagination, PaginationResponse, MetadataPagination, Query, HTTPExceptionModel,
User, Direction, Owner, Any_)
router = APIRouter()
......@@ -76,6 +76,20 @@ _dataset_is_fixed_response = status.HTTP_400_BAD_REQUEST, {
The dataset already has a DOI and cannot be changed anymore.
''')}
_dataset_has_unpublished_contents = status.HTTP_400_BAD_REQUEST, {
'model': HTTPExceptionModel,
'description': strip('''
The dataset has unpublished contents. No DOI can be assigned at the moment.
Publish the dataset contents first.
''')}
_dataset_is_empty = status.HTTP_400_BAD_REQUEST, {
'model': HTTPExceptionModel,
'description': strip('''
The dataset is empty. No DOI can be assigned at this moment. Add some published
contents to the dataset first.
''')}
Dataset = datamodel.Dataset.m_def.a_pydantic.model
......@@ -336,7 +350,9 @@ async def delete_dataset(
'/{dataset_id}/action/doi', tags=[default_tag],
summary='Assign a DOI to a dataset',
response_model=DatasetResponse,
responses=create_responses(_bad_id_response, _dataset_is_fixed_response, _bad_user_response),
responses=create_responses(
_bad_id_response, _dataset_is_fixed_response, _dataset_has_unpublished_contents,
_bad_user_response, _dataset_is_empty),
response_model_exclude_unset=True,
response_model_exclude_none=True)
async def assign_doi(
......@@ -362,6 +378,27 @@ async def assign_doi(
status_code=_bad_user_response[0],
detail=_bad_user_response[1]['description'])
response = search(
owner='admin',
query={'datasets.dataset_id': dataset_id},
pagination=MetadataPagination(page_size=0),
user_id=config.services.admin_user_id)
if response.pagination.total == 0:
raise HTTPException(
status_code=_dataset_is_empty[0],
detail=_dataset_is_empty[1]['description'])
response = search(
owner='admin',
query={'datasets.dataset_id': dataset_id, 'published': False},
pagination=MetadataPagination(page_size=0),
user_id=config.services.admin_user_id)
if response.pagination.total > 0:
raise HTTPException(
status_code=_dataset_has_unpublished_contents[0],
detail=_dataset_has_unpublished_contents[1]['description'])
doi = DOI.create(title='NOMAD dataset: %s' % dataset.dataset_name, user=user)
doi.create_draft()
doi.make_findable()
......
......@@ -43,13 +43,14 @@ to assert for certain aspects in the responses.
'''
def create_dataset(**kwargs):
dataset = Dataset(dataset_create_time=datetime.now(), dataset_modified_time=datetime.now(), **kwargs)
dataset.m_get_annotations('mongo').save()
return dataset
@pytest.fixture(scope='function')
def data(elastic, raw_files, mongo, test_user, other_test_user):
def create_dataset(**kwargs):
dataset = Dataset(dataset_create_time=datetime.now(), dataset_modified_time=datetime.now(), **kwargs)
dataset.m_get_annotations('mongo').save()
return dataset
data = ExampleData(main_author=test_user)
data.create_upload(upload_id='upload_1', published=True)
data.create_entry(
......@@ -276,9 +277,28 @@ def test_delete_dataset(client, data, test_user_auth, other_test_user_auth, data
pytest.param('dataset_1', 'test_user', 200, id='plain'),
pytest.param('dataset_1', None, 401, id='no-user'),
pytest.param('dataset_1', 'other_test_user', 401, id='wrong-user'),
pytest.param('dataset_doi', 'test_user', 400, id='with-doi')
pytest.param('dataset_doi', 'test_user', 400, id='with-doi'),
pytest.param('unpublished', 'test_user', 400, id='unpublished'),
pytest.param('empty', 'test_user', 400, id='empty')
])
def test_assign_doi_dataset(client, data, test_user, test_user_auth, other_test_user_auth, dataset_id, user, status_code):
more_data = ExampleData(main_author=test_user)
more_data.create_upload(upload_id='unpublished', published=False)
more_data.create_entry(
upload_id='unpublished', entry_id='unpublished',
mainfile='test_content/1/mainfile.json',
datasets=[create_dataset(
dataset_id='unpublished', user_id=test_user.user_id,
dataset_name='test unpublished entries',
dataset_type='owned')])
create_dataset(
dataset_id='empty', user_id=test_user.user_id,
dataset_name='test empty dataset',
dataset_type='owned')
more_data.save(with_files=False)
auth = None
if user == 'test_user':
auth = test_user_auth
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment