Repository: SciCatProject/scitacean

Issue:
Validate dataset with backend before upload
As discussed in #55, when the new backend is out, we should ask the backend if a dataset is valid before uploading any files.

Discussion:

Patch:
diff --git a/src/scitacean/client.py b/src/scitacean/client.py
index 8c68c4b..9c461e1 100644
--- a/src/scitacean/client.py
+++ b/src/scitacean/client.py
@@ -249,7 +249,7 @@ class Client:
         dataset = dataset.replace(
             source_folder=self._expect_file_transfer().source_folder_for(dataset)
         )
-        dataset.validate()
+        self.scicat.validate_dataset_model(dataset.make_upload_model())
         # TODO skip if there are no files
         with self._connect_for_file_upload(dataset) as con:
             # TODO check if any remote file is out of date.
@@ -829,6 +829,30 @@ class ScicatClient:
             model.DownloadAttachment, _strict_validation=False, **uploaded
         )
 
+    def validate_dataset_model(
+        self, dset: Union[model.UploadDerivedDataset, model.UploadRawDataset]
+    ) -> None:
+        """Validate a dataset in SciCat.
+
+        Parameters
+        ----------
+        dset:
+            Model of the dataset to validate.
+
+        Raises
+        ------
+        ValueError
+            If the dataset does not pass validation.
+        """
+        response = self._call_endpoint(
+            cmd="post",
+            url="datasets/isValid",
+            data=dset,
+            operation="validate_dataset_model",
+        )
+        if not response["valid"]:
+            raise ValueError(f"Dataset {dset} did not pass validation in SciCat.")
+
     def _send_to_scicat(
         self, *, cmd: str, url: str, data: Optional[model.BaseModel] = None
     ) -> requests.Response:


Test patch:
diff --git a/src/scitacean/testing/client.py b/src/scitacean/testing/client.py
index 00d3863..4a67d2a 100644
--- a/src/scitacean/testing/client.py
+++ b/src/scitacean/testing/client.py
@@ -249,6 +249,14 @@ class FakeScicatClient(ScicatClient):
         self.main.attachments.setdefault(dataset_id, []).append(ingested)
         return ingested
 
+    @_conditionally_disabled
+    def validate_dataset_model(
+        self, dset: Union[model.UploadDerivedDataset, model.UploadRawDataset]
+    ):
+        """Validate model remotely in SciCat."""
+        # Models were locally validated on construction, assume they are valid.
+        pass
+
 
 def _model_dict(mod: model.BaseModel) -> Dict[str, Any]:
     return {
diff --git a/tests/client/dataset_client_test.py b/tests/client/dataset_client_test.py
index c2e864f..eaffecc 100644
--- a/tests/client/dataset_client_test.py
+++ b/tests/client/dataset_client_test.py
@@ -65,6 +65,13 @@ def test_create_dataset_model(scicat_client, derived_dataset):
             assert expected == dict(downloaded)[key], f"key = {key}"
 
 
+def test_validate_dataset_model(real_client, require_scicat_backend, derived_dataset):
+    real_client.scicat.validate_dataset_model(derived_dataset)
+    derived_dataset.contactEmail = "NotAnEmail"
+    with pytest.raises(ValueError):
+        real_client.scicat.validate_dataset_model(derived_dataset)
+
+
 def test_get_dataset(client):
     dset = INITIAL_DATASETS["raw"]
     dblock = INITIAL_ORIG_DATABLOCKS["raw"][0]
diff --git a/tests/upload_test.py b/tests/upload_test.py
index d949838..ab7a0e4 100644
--- a/tests/upload_test.py
+++ b/tests/upload_test.py
@@ -194,6 +194,20 @@ def test_upload_cleans_up_files_if_dataset_ingestion_fails(dataset_with_files, f
     assert not get_file_transfer(client).files
 
 
+def test_upload_does_not_create_dataset_if_validation_fails(dataset_with_files, fs):
+    client = FakeClient(
+        disable={"validate_dataset_model": ValueError},
+        file_transfer=FakeFileTransfer(fs=fs),
+    )
+    with pytest.raises(ValueError):
+        client.upload_new_dataset_now(dataset_with_files)
+
+    assert not client.datasets
+    assert not client.orig_datablocks
+    assert not client.attachments
+    assert not get_file_transfer(client).files
+
+
 def test_failed_datablock_upload_does_not_revert(dataset_with_files, fs):
     client = FakeClient(
         disable={"create_orig_datablock": ScicatCommError("Ingestion failed")},