Skip to content
Open
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
21 changes: 16 additions & 5 deletions specifyweb/backend/context/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import json
import os
import re
import logging
from typing import List
from xml.etree import ElementTree

Expand All @@ -14,7 +15,7 @@
from django.db import connection, transaction
from django.db.models import Q
from django.http import Http404, HttpResponse, HttpResponseBadRequest, \
HttpResponseForbidden, JsonResponse
HttpResponseForbidden, HttpResponseServerError, JsonResponse
from django.urls import URLPattern
from django.utils.translation import get_language_info
from django.utils.translation import gettext as _
Expand Down Expand Up @@ -46,6 +47,8 @@
is_collection_available,
is_guided_setup_complete,
)

logger = logging.getLogger(__name__)

def set_collection_cookie(response, collection_id): # pragma: no cover
response.set_cookie('collection', str(collection_id), max_age=365*24*60*60)
Expand Down Expand Up @@ -655,13 +658,13 @@ def schema_localization_import(request):
if not isinstance(language, str) or not re.fullmatch(
r'[A-Za-z]{2}(?:-[A-Za-z]{2})?', language
):
raise ValueError
raise ValueError(f"Invalid language string: {language}")
if isinstance(schema, dict) and {'language', 'schema'} <= schema.keys():
source_language = schema['language']
if not isinstance(source_language, str) or (
source_language.lower() != language.lower()
):
raise ValueError
raise ValueError(f"Invalid Localization (expected {source_language}, got {language}")
schema = schema['schema']
references = {
'format': _schema_import_resource_names(
Expand Down Expand Up @@ -704,8 +707,16 @@ def schema_localization_import(request):
request.specify_collection, request.specify_user_agent,
model.__name__, data
)
except (AttributeError, KeyError, TypeError, ValueError, json.JSONDecodeError):
return HttpResponseBadRequest()
except (AttributeError, ValueError, json.JSONDecodeError) as err:
logger.warning("Schema import validation failed", exc_info=True)
return JsonResponse(
{"error": "Invalid schema localization import payload."},
status=400
)
except (KeyError, TypeError) as err:
logger.warning(f"Schema Import failed: {err}")
return HttpResponseServerError("An internal error has occurred.", content_type="text/plain")

return JsonResponse({'updated': len(operations)})

view_parameters_schema = [
Expand Down
41 changes: 15 additions & 26 deletions specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
useSchemaConfig,
} from './Store';
import { dialogIcons } from '../Atoms/Icons';
import { raise } from '../Errors/Crash';

export function SchemaConfigLayout(): JSX.Element {
const schemaData = useOutletContext<SchemaData>();
Expand Down Expand Up @@ -55,8 +56,7 @@ function SchemaConfigLayoutContent(): JSX.Element {
const setSingleResource = React.useContext(SetSingleResourceContext);
const loading = React.useContext(LoadingContext);
const [importFile, setImportFile] = React.useState<File | undefined>();
const [importError, setImportError] = React.useState(false);
const [importSuccessful, setImportSuccessful] = React.useState(false);
const [importStatus, setImportStatus] = React.useState(0);
const [importRefreshError, setImportRefreshError] = React.useState(false);

React.useEffect(() => {
Expand All @@ -80,7 +80,6 @@ function SchemaConfigLayoutContent(): JSX.Element {
);
};
const handleImport = (file: File): void => {
setImportError(false);
setImportFile(file);
};
const confirmImport = (): void => {
Expand All @@ -95,17 +94,25 @@ function SchemaConfigLayoutContent(): JSX.Element {
method: 'POST',
headers: { Accept: 'application/json' },
body: { schema, language: rawLanguage },
errorMode: 'silent',
expectedErrors: [400, 500, 504]
})
)
.then(
() => setImportSuccessful(true),
() => setImportError(true)
(response) => {
setImportStatus(response.status);
if (response.status !== 200)
raise({name: schemaText.importSchemaError({schemaConfig: schemaText.schemaConfig()}), message:
response.status === 504 ? schemaText.importSchemaErrorTimeout({schemaConfig: schemaText.schemaConfig()}) :
response.status === 400 ? schemaText.importSchemaErrorBadRequest({schemaConfig: schemaText.schemaConfig()}) :
schemaText.importSchemaErrorInternalError()
})
},
(err) => raise(err)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)
);
};
const closeImportSuccess = (): void => {
setImportSuccessful(false);
setImportStatus(0);
loading(
handleSchemaSaved(rawLanguage, tableName).catch(() =>
setImportRefreshError(true)
Expand Down Expand Up @@ -170,25 +177,7 @@ function SchemaConfigLayoutContent(): JSX.Element {
</p>
</Dialog>
)}
{importError && (
<Dialog
buttons={
<Button.DialogClose>{commonText.close()}</Button.DialogClose>
}
icon={dialogIcons.error}
header={schemaText.importSchema({
schemaConfig: schemaText.schemaConfig(),
})}
onClose={(): void => setImportError(false)}
>
<p>
{schemaText.importSchemaError({
schemaConfig: schemaText.schemaConfig(),
})}
</p>
</Dialog>
)}
{importSuccessful && (
{importStatus == 200 && (
<Dialog
buttons={
<Button.DialogClose>{commonText.close()}</Button.DialogClose>
Expand Down
35 changes: 35 additions & 0 deletions specifyweb/frontend/js_src/lib/localization/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ export const schemaText = createDictionary({
'uk-ua': '',
},
importSchemaError: {
'en-us': '{schemaConfig:string} import failed:',
'de-ch': '',
'es-es': '',
'fr-fr': '',
'hr-hr': '',
nb: '',
'pt-br': '',
'ru-ru': '',
'uk-ua': '',
},
importSchemaErrorBadRequest: {
'en-us':
'The {schemaConfig:string} export is invalid or was exported for a different language and cannot be imported.',
'de-ch': '',
Expand All @@ -102,6 +113,30 @@ export const schemaText = createDictionary({
'ru-ru': '',
'uk-ua': '',
},
importSchemaErrorTimeout: {
'en-us':
'Network request timed out, Specify may or may not still import the {schemaConfig:string} export in the background. Please check back in later to see if your changes have been applied.',
'de-ch': '',
'es-es': '',
'fr-fr': '',
'hr-hr': '',
nb: '',
'pt-br': '',
'ru-ru': '',
'uk-ua': '',
},
importSchemaErrorInternalError: {
'en-us':
'Specify experienced an error, please clear your cache and try again.',
'de-ch': '',
'es-es': '',
'fr-fr': '',
'hr-hr': '',
nb: '',
'pt-br': '',
'ru-ru': '',
'uk-ua': '',
},
importSchemaSuccess: {
'en-us': '{schemaConfig:string} imported successfully',
'de-ch': '',
Expand Down
Loading