Skip to content

Commit 50a5e26

Browse files
authored
Merge pull request #217 from gdcc/fix-edit-metadata-endpoint
Fix edit metadata endpoint
2 parents ab94997 + f4b5e53 commit 50a5e26

2 files changed

Lines changed: 182 additions & 4 deletions

File tree

pyDataverse/api.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,9 @@ def post_request(
275275
**request_params,
276276
)
277277

278-
def put_request(self, url, data=None, auth=DEPRECATION_GUARD, params=None):
278+
def put_request(
279+
self, url, data=None, auth=DEPRECATION_GUARD, params=None, files=None
280+
):
279281
"""Make a PUT request.
280282
281283
Parameters
@@ -326,7 +328,6 @@ def put_request(self, url, data=None, auth=DEPRECATION_GUARD, params=None):
326328
return self._sync_request(
327329
method=httpx.put,
328330
url=url,
329-
json=data,
330331
headers=headers,
331332
params=params,
332333
**request_params,
@@ -335,7 +336,6 @@ def put_request(self, url, data=None, auth=DEPRECATION_GUARD, params=None):
335336
return self._async_request(
336337
method=self.client.put,
337338
url=url,
338-
json=data,
339339
headers=headers,
340340
params=params,
341341
**request_params,
@@ -1545,7 +1545,12 @@ def edit_dataset_metadata(
15451545
self.base_url_api_native, identifier
15461546
)
15471547
params = {"replace": True} if replace else {}
1548-
resp = self.put_request(url, metadata, auth, params)
1548+
resp = self.put_request(
1549+
url=url,
1550+
data=metadata,
1551+
auth=auth,
1552+
params=params,
1553+
)
15491554

15501555
if resp.status_code == 401:
15511556
error_msg = resp.json()["message"]

tests/api/test_edit.py

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import json
2+
import os
3+
from typing import Any
4+
import httpx
5+
from pyDataverse.api import NativeApi
6+
7+
8+
class TestEditDatasetMetadata:
9+
def test_edit_dataset_metadata_replace(self):
10+
"""
11+
Test case for editing a dataset's metadata.
12+
13+
This test case performs the following steps:
14+
1. Creates a dataset using the provided metadata.
15+
2. Edits the dataset metadata and replaces the existing metadata.
16+
3. Asserts that the metadata was edited successfully.
17+
18+
Raises:
19+
AssertionError: If the metadata edit fails.
20+
21+
"""
22+
# Arrange
23+
BASE_URL = os.getenv("BASE_URL").rstrip("/")
24+
API_TOKEN = os.getenv("API_TOKEN")
25+
26+
# Create dataset
27+
metadata = json.load(open("tests/data/file_upload_ds_minimum.json"))
28+
pid = self._create_dataset(BASE_URL, API_TOKEN, metadata)
29+
api = NativeApi(BASE_URL, API_TOKEN)
30+
31+
# Prepare file upload
32+
edit_metadata = {
33+
"fields": [
34+
{
35+
"typeName": "title",
36+
"value": "New Title",
37+
},
38+
{
39+
"typeName": "datasetContact",
40+
"value": [
41+
{
42+
"datasetContactEmail": {
43+
"typeName": "datasetContactEmail",
44+
"value": "jane@doe.com",
45+
},
46+
"datasetContactName": {
47+
"typeName": "datasetContactName",
48+
"value": "Jane Doe",
49+
},
50+
}
51+
],
52+
},
53+
]
54+
}
55+
56+
# Act
57+
response = api.edit_dataset_metadata(
58+
identifier=pid,
59+
metadata=edit_metadata,
60+
replace=True,
61+
)
62+
63+
response.raise_for_status()
64+
65+
# Assert
66+
dataset = api.get_dataset(pid).json()
67+
new_title = self._get_field_value(dataset, "citation", "title")
68+
new_contact = self._get_field_value(dataset, "citation", "datasetContact")[0]
69+
70+
assert new_title == "New Title", "Metadata edit failed."
71+
assert (
72+
new_contact["datasetContactEmail"]["value"] == "jane@doe.com"
73+
), "Metadata edit failed."
74+
assert (
75+
new_contact["datasetContactName"]["value"] == "Jane Doe"
76+
), "Metadata edit failed."
77+
78+
def test_edit_dataset_metadata_add(self):
79+
"""
80+
Test case for editing a dataset's metadata.
81+
82+
This test case performs the following steps:
83+
1. Creates a dataset using the provided metadata.
84+
2. Edits the dataset metadata and replaces the existing metadata.
85+
3. Asserts that the metadata was edited successfully.
86+
87+
Raises:
88+
AssertionError: If the metadata edit fails.
89+
90+
"""
91+
# Arrange
92+
BASE_URL = os.getenv("BASE_URL").rstrip("/")
93+
API_TOKEN = os.getenv("API_TOKEN")
94+
95+
# Create dataset
96+
metadata = json.load(open("tests/data/file_upload_ds_minimum.json"))
97+
pid = self._create_dataset(BASE_URL, API_TOKEN, metadata)
98+
api = NativeApi(BASE_URL, API_TOKEN)
99+
100+
# Prepare file upload
101+
edit_metadata = {
102+
"fields": [
103+
{"typeName": "subject", "value": ["Astronomy and Astrophysics"]},
104+
{"typeName": "subtitle", "value": "Subtitle"},
105+
]
106+
}
107+
108+
# Act
109+
response = api.edit_dataset_metadata(
110+
identifier=pid,
111+
metadata=edit_metadata,
112+
)
113+
114+
response.raise_for_status()
115+
116+
# Assert
117+
dataset = api.get_dataset(pid).json()
118+
new_subject = self._get_field_value(dataset, "citation", "subject")
119+
new_subtitle = self._get_field_value(dataset, "citation", "subtitle")
120+
121+
assert "Astronomy and Astrophysics" in new_subject, "Metadata edit failed."
122+
assert new_subtitle == "Subtitle", "Metadata edit failed."
123+
124+
@staticmethod
125+
def _create_dataset(
126+
BASE_URL: str,
127+
API_TOKEN: str,
128+
metadata: dict,
129+
):
130+
"""
131+
Create a dataset in the Dataverse.
132+
133+
Args:
134+
BASE_URL (str): The base URL of the Dataverse instance.
135+
API_TOKEN (str): The API token for authentication.
136+
metadata (dict): The metadata for the dataset.
137+
138+
Returns:
139+
str: The persistent identifier (PID) of the created dataset.
140+
"""
141+
url = f"{BASE_URL}/api/dataverses/root/datasets"
142+
response = httpx.post(
143+
url=url,
144+
json=metadata,
145+
headers={
146+
"X-Dataverse-key": API_TOKEN,
147+
"Content-Type": "application/json",
148+
},
149+
)
150+
151+
response.raise_for_status()
152+
153+
return response.json()["data"]["persistentId"]
154+
155+
@staticmethod
156+
def _get_field_value(data: dict, block: str, field: str) -> Any:
157+
"""
158+
Get the value of a field in a metadata block.
159+
"""
160+
161+
blocks = data["data"]["latestVersion"]["metadataBlocks"]
162+
163+
assert block in blocks, f"Block {block} not found in metadata blocks"
164+
165+
metadata_block = blocks[block]
166+
167+
try:
168+
filtered = next(
169+
filter(lambda f: f["typeName"] == field, metadata_block["fields"])
170+
)
171+
return filtered["value"]
172+
except StopIteration:
173+
raise ValueError(f"Field {field} not found in block {block}")

0 commit comments

Comments
 (0)