diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 74ce7f3b27..8133a22b57 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -27,7 +27,9 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released - Fix performance bottleneck when deleting a lot of trees at once. [#8176](https://github.com/scalableminds/webknossos/pull/8176) - Fix that listing datasets with the `api/datasets` route without compression failed due to missing permissions regarding public datasets. [#8249](https://github.com/scalableminds/webknossos/pull/8249) - Fix a bug where changing the color of a segment via the menu in the segments tab would update the segment color of the previous segment, on which the context menu was opened. [#8225](https://github.com/scalableminds/webknossos/pull/8225) +- Fix a bug where in the add remote dataset view the dataset name setting was not in sync with the datasource setting of the advanced tab making the form not submittable. [#8245](https://github.com/scalableminds/webknossos/pull/8245) - Fix a bug when importing an NML with groups when only groups but no trees exist in an annotation. [#8176](https://github.com/scalableminds/webknossos/pull/8176) +- Added missing legacy support for `isValidNewName` route. [#8252](https://github.com/scalableminds/webknossos/pull/8252) - Fix a bug where trying to delete a non-existing node (via the API, for example) would delete the whole active tree. [#8176](https://github.com/scalableminds/webknossos/pull/8176) - Fix a bug where dataset uploads would fail if the organization directory on disk is missing. [#8230](https://github.com/scalableminds/webknossos/pull/8230) diff --git a/app/controllers/LegacyApiController.scala b/app/controllers/LegacyApiController.scala index ef80f7eec9..e92e686bdc 100644 --- a/app/controllers/LegacyApiController.scala +++ b/app/controllers/LegacyApiController.scala @@ -59,6 +59,14 @@ class LegacyApiController @Inject()(annotationController: AnnotationController, /* provide v8 */ + def isValidNewNameV8(datasetName: String, organizationId: String): Action[AnyContent] = sil.SecuredAction.async { + implicit request => + for { + _ <- Fox.successful(logVersioned(request)) + result <- datasetController.isValidNewName(datasetName)(request) + } yield result + } + def readDatasetV8(organizationId: String, datasetName: String, sharingToken: Option[String]): Action[AnyContent] = sil.UserAwareAction.async { implicit request => for { diff --git a/conf/webknossos.versioned.routes b/conf/webknossos.versioned.routes index 7f03f6a9a9..3ee48062b8 100644 --- a/conf/webknossos.versioned.routes +++ b/conf/webknossos.versioned.routes @@ -20,6 +20,7 @@ PATCH /v8/datasets/:organizationId/:datasetName controllers.LegacyApiController.updateDatasetV8(organizationId: String, datasetName: String) GET /v8/datasets/:organizationId/:datasetName/sharingToken controllers.LegacyApiController.getDatasetSharingTokenV8(organizationId: String, datasetName: String) PATCH /v8/datasets/:organizationId/:datasetName/teams controllers.LegacyApiController.updateDatasetTeamsV8(organizationId: String, datasetName: String) +GET /v8/datasets/:organizationId/:datasetName/isValidNewName controllers.LegacyApiController.isValidNewNameV8(datasetName: String, organizationId: String) GET /v8/datasets/:organizationId/:datasetName controllers.LegacyApiController.readDatasetV8(organizationId: String, datasetName: String, sharingToken: Option[String]) GET /v8/tasks/:id controllers.LegacyApiController.readTaskV8(id: String) POST /v8/tasks controllers.LegacyApiController.createTaskV8() @@ -33,6 +34,7 @@ GET /v8/tasks/:id/annotations co PATCH /v7/datasets/:organizationId/:datasetName controllers.LegacyApiController.updateDatasetV8(organizationId: String, datasetName: String) GET /v7/datasets/:organizationId/:datasetName/sharingToken controllers.LegacyApiController.getDatasetSharingTokenV8(organizationId: String, datasetName: String) PATCH /v7/datasets/:organizationId/:datasetName/teams controllers.LegacyApiController.updateDatasetTeamsV8(organizationId: String, datasetName: String) +GET /v7/datasets/:organizationId/:datasetName/isValidNewName controllers.LegacyApiController.isValidNewNameV8(datasetName: String, organizationId: String) GET /v7/datasets/:organizationId/:datasetName controllers.LegacyApiController.readDatasetV8(organizationId: String, datasetName: String, sharingToken: Option[String]) GET /v7/tasks/:id controllers.LegacyApiController.readTaskV8(id: String) POST /v7/tasks controllers.LegacyApiController.createTaskV8() @@ -50,6 +52,7 @@ GET /v7/datasets co PATCH /v6/datasets/:organizationId/:datasetName controllers.LegacyApiController.updateDatasetV8(organizationId: String, datasetName: String) GET /v6/datasets/:organizationId/:datasetName/sharingToken controllers.LegacyApiController.getDatasetSharingTokenV8(organizationId: String, datasetName: String) PATCH /v6/datasets/:organizationId/:datasetName/teams controllers.LegacyApiController.updateDatasetTeamsV8(organizationId: String, datasetName: String) +GET /v6/datasets/:organizationId/:datasetName/isValidNewName controllers.LegacyApiController.isValidNewNameV8(datasetName: String, organizationId: String) GET /v6/tasks/:id controllers.LegacyApiController.readTaskV8(id: String) POST /v6/tasks controllers.LegacyApiController.createTaskV8() PUT /v6/tasks/:id controllers.LegacyApiController.updateTaskV8(id: String) @@ -69,6 +72,7 @@ GET /v6/datasets/:organizationName/:datasetName co PATCH /v5/datasets/:organizationId/:datasetName controllers.LegacyApiController.updateDatasetV8(organizationId: String, datasetName: String) GET /v5/datasets/:organizationId/:datasetName/sharingToken controllers.LegacyApiController.getDatasetSharingTokenV8(organizationId: String, datasetName: String) PATCH /v5/datasets/:organizationId/:datasetName/teams controllers.LegacyApiController.updateDatasetTeamsV8(organizationId: String, datasetName: String) +GET /v5/datasets/:organizationId/:datasetName/isValidNewName controllers.LegacyApiController.isValidNewNameV8(datasetName: String, organizationId: String) GET /v5/tasks/:id controllers.LegacyApiController.readTaskV8(id: String) POST /v5/tasks controllers.LegacyApiController.createTaskV8() PUT /v5/tasks/:id controllers.LegacyApiController.updateTaskV8(id: String) diff --git a/frontend/javascripts/admin/dataset/dataset_add_remote_view.tsx b/frontend/javascripts/admin/dataset/dataset_add_remote_view.tsx index d14bbc5bec..847e720282 100644 --- a/frontend/javascripts/admin/dataset/dataset_add_remote_view.tsx +++ b/frontend/javascripts/admin/dataset/dataset_add_remote_view.tsx @@ -252,13 +252,13 @@ function DatasetAddRemoteView(props: Props) { form.setFieldsValue({ dataSourceJson }); // Since this function sets the JSON string, we have to update the // data which is rendered by the "simple" page. - syncDataSourceFields(form, "simple"); + syncDataSourceFields(form, "simple", true); form.validateFields(); }; async function handleStoreDataset() { // Sync simple with advanced and get newest datasourceJson - syncDataSourceFields(form, dataSourceEditMode === "simple" ? "advanced" : "simple"); + syncDataSourceFields(form, dataSourceEditMode === "simple" ? "advanced" : "simple", true); try { await form.validateFields(); } catch (_e) { @@ -376,7 +376,7 @@ function DatasetAddRemoteView(props: Props) { form={form} activeDataSourceEditMode={dataSourceEditMode} onChange={(activeEditMode) => { - syncDataSourceFields(form, activeEditMode); + syncDataSourceFields(form, activeEditMode, true); form.validateFields(); setDataSourceEditMode(activeEditMode); }} @@ -515,7 +515,7 @@ function AddRemoteLayer({ } // Sync simple with advanced and get newest datasourceJson - syncDataSourceFields(form, dataSourceEditMode === "simple" ? "advanced" : "simple"); + syncDataSourceFields(form, dataSourceEditMode === "simple" ? "advanced" : "simple", true); const datasourceConfigStr = form.getFieldValue("dataSourceJson"); const datastoreToUse = uploadableDatastores.find( (datastore) => form.getFieldValue("datastoreUrl") === datastore.url, diff --git a/frontend/javascripts/dashboard/dataset/dataset_settings_data_tab.tsx b/frontend/javascripts/dashboard/dataset/dataset_settings_data_tab.tsx index 2e879a87e8..2650c4e136 100644 --- a/frontend/javascripts/dashboard/dataset/dataset_settings_data_tab.tsx +++ b/frontend/javascripts/dashboard/dataset/dataset_settings_data_tab.tsx @@ -33,12 +33,16 @@ import { type APIDataLayer, type APIDataset, APIJobType } from "types/api_flow_t import { useStartAndPollJob } from "admin/job/job_hooks"; import { AllUnits, LongUnitToShortUnitMap, type Vector3 } from "oxalis/constants"; import Toast from "libs/toast"; +import type { ArbitraryObject } from "types/globals"; const FormItem = Form.Item; export const syncDataSourceFields = ( form: FormInstance, syncTargetTabKey: "simple" | "advanced", + // Syncing the dataset name is optional as this is needed for the add remote view, but not for the edit view. + // In the edit view, the datasource.id fields should never be changed and the backend will automatically ignore all changes to the id field. + syncDatasetName = false, ): void => { if (!form) { return; @@ -47,12 +51,25 @@ export const syncDataSourceFields = ( if (syncTargetTabKey === "advanced") { // Copy from simple to advanced: update json const dataSourceFromSimpleTab = form.getFieldValue("dataSource"); + if (syncDatasetName && dataSourceFromSimpleTab) { + dataSourceFromSimpleTab.id ??= {}; + dataSourceFromSimpleTab.id.name = form.getFieldValue(["dataset", "name"]); + } form.setFieldsValue({ dataSourceJson: jsonStringify(dataSourceFromSimpleTab), }); } else { - const dataSourceFromAdvancedTab = parseMaybe(form.getFieldValue("dataSourceJson")); + const dataSourceFromAdvancedTab = parseMaybe( + form.getFieldValue("dataSourceJson"), + ) as ArbitraryObject | null; // Copy from advanced to simple: update form values + if (syncDatasetName && dataSourceFromAdvancedTab?.id?.name) { + form.setFieldsValue({ + dataset: { + name: dataSourceFromAdvancedTab.id.name, + }, + }); + } form.setFieldsValue({ dataSource: dataSourceFromAdvancedTab, });