das-cli 1.0.6__py3-none-any.whl → 1.2.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
das/common/api.py CHANGED
@@ -27,6 +27,26 @@ def get_data(url, headers=None, params=None):
27
27
  print(f"Error fetching API data: {e}")
28
28
  return {"error": str(e)}
29
29
 
30
+ def get_binary_response(url, headers=None, params=None, stream=True):
31
+ """
32
+ Perform a GET request expected to return binary content.
33
+
34
+ Returns the raw requests.Response so callers can inspect headers
35
+ and stream content to disk.
36
+ """
37
+ try:
38
+ response = requests.get(
39
+ url,
40
+ headers=headers,
41
+ params=params,
42
+ verify=load_verify_ssl(),
43
+ stream=stream,
44
+ )
45
+ response.raise_for_status()
46
+ return response
47
+ except requests.RequestException as e:
48
+ return {"error": str(e)}
49
+
30
50
  def post_data(url, headers=None, data=None):
31
51
  """
32
52
  Send data to a REST API endpoint.
das/common/config.py CHANGED
@@ -81,12 +81,18 @@ def load_verify_ssl() -> bool:
81
81
 
82
82
  if os.getenv("VERIFY_SSL") is not None:
83
83
  VERIFY_SSL = os.getenv("VERIFY_SSL") == "True"
84
+ if not VERIFY_SSL:
85
+ print("SSL certificate verification is disabled")
84
86
  return VERIFY_SSL
85
87
 
86
88
  verify = config.get("verify_ssl")
87
89
  if verify is not None:
88
90
  VERIFY_SSL = verify
91
+ if not VERIFY_SSL:
92
+ print("SSL certificate verification is disabled")
89
93
  return verify
94
+ else:
95
+ raise ValueError("SSL certificate verification is not set")
90
96
  except Exception:
91
97
  pass
92
98
  return VERIFY_SSL
@@ -0,0 +1,84 @@
1
+ import os
2
+ import sys
3
+ from das.common.config import load_api_url
4
+ from das.services.search import SearchService
5
+ from das.services.entries import EntriesService
6
+ from das.services.digital_objects import DigitalObjectsService
7
+
8
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
9
+
10
+ class DigitalObjectsManager:
11
+ """Manager for digital objects."""
12
+
13
+ def __init__(self):
14
+ base_url = load_api_url()
15
+ if base_url is None or base_url == "":
16
+ raise ValueError(f"Base URL is required - {self.__class__.__name__}")
17
+
18
+ self.__attribute_id_digital_object_type = 5;
19
+ self.digital_objects_service = DigitalObjectsService(base_url)
20
+ self.entry_service = EntriesService(base_url)
21
+ self.search_service = SearchService(base_url)
22
+
23
+ def link_existing_digital_objects(
24
+ self, entry_code: str, digital_object_code_list: list[str], is_unlink: bool = False
25
+ ) -> bool:
26
+ """Attach or detach (unlink) digital objects to an entry using codes."""
27
+ entry_response = self.entry_service.get_entry(entry_code)
28
+
29
+ if entry_response is None:
30
+ raise ValueError(f"Entry with code '{entry_code}' not found")
31
+
32
+ entry_payload = entry_response.get("entry")
33
+ if entry_payload is None:
34
+ raise ValueError(f"Entry with code '{entry_code}' not found")
35
+
36
+ digital_object_id_list: list[str] = []
37
+
38
+ for code in digital_object_code_list:
39
+ do_response = self.entry_service.get_entry(code)
40
+ do_entry = do_response.get("entry") if do_response else None
41
+ if do_entry is None:
42
+ raise ValueError(f"Digital object with code '{code}' not found")
43
+ digital_object_id_list.append(do_entry.get("id"))
44
+
45
+ result = self.digital_objects_service.link_existing_digital_objects(
46
+ attribute_id=entry_response.get("attributeId"),
47
+ entry_id=entry_payload.get("id"),
48
+ digital_object_id_list=digital_object_id_list,
49
+ is_unlink=is_unlink,
50
+ )
51
+
52
+ return result
53
+
54
+ def upload_digital_object(self, entry_code: str, file_description: str, digital_object_type: str, file_path: str):
55
+ """Upload a digital object to the digital object service."""
56
+ response = self.search_service.search_entries(
57
+ queryString=f"displayname({digital_object_type})",
58
+ attributeId=self.__attribute_id_digital_object_type,
59
+ maxResultCount=1,
60
+ skipCount=0
61
+ )
62
+
63
+ entry_response = self.entry_service.get_entry(entry_code)
64
+ if entry_response is None:
65
+ raise ValueError(f"Entry with code '{entry_code}' not found")
66
+
67
+ if response.get('totalCount', 0) == 0:
68
+ raise ValueError(f"Digital object type '{digital_object_type}' not found")
69
+
70
+ digital_object_type_id = response.get('items', [])[0].get('entry').get('id')
71
+ digital_object_id = self.digital_objects_service.upload_digital_object(file_description, digital_object_type_id, file_path)
72
+
73
+ self.digital_objects_service.link_existing_digital_objects(
74
+ attribute_id=entry_response.get('attributeId'),
75
+ entry_id=entry_response.get('entry').get('id'),
76
+ digital_object_id_list=[digital_object_id]
77
+ )
78
+
79
+ return digital_object_id
80
+
81
+
82
+ if __name__ == "__main__":
83
+ digital_objects_manager = DigitalObjectsManager()
84
+ digital_objects_manager.upload_digital_object(entry_code="zb.b.f7", file_description="test", digital_object_type="Dataset", file_path="my_new_file.txt")
@@ -90,4 +90,46 @@ class DownloadManager:
90
90
 
91
91
  def get_my_requests(self):
92
92
  """Get all download requests for the current user."""
93
- return self.download_request_service.get_my_requests()
93
+ return self.download_request_service.get_my_requests()
94
+
95
+ def download_files(self, request_id: str):
96
+ """Return streaming response for files of a download request."""
97
+ return self.download_request_service.download_files(request_id)
98
+
99
+ def save_download(self, request_id: str, output_path: str, overwrite: bool = False) -> str:
100
+ """
101
+ Download and save the request bundle to disk.
102
+
103
+ Returns the path to the saved file.
104
+ """
105
+ import os
106
+
107
+ resp = self.download_files(request_id)
108
+ # If an error structure was returned from lower layer
109
+ if isinstance(resp, dict) and resp.get('error'):
110
+ raise ValueError(resp['error'])
111
+
112
+ # Determine filename from headers if available
113
+ filename = f"download_{request_id}.zip"
114
+ try:
115
+ cd = resp.headers.get('Content-Disposition') if hasattr(resp, 'headers') else None
116
+ if cd and 'filename=' in cd:
117
+ filename = cd.split('filename=')[-1].strip('"')
118
+ except Exception:
119
+ pass
120
+
121
+ # If output_path is a directory, join with filename
122
+ target_path = output_path
123
+ if os.path.isdir(output_path) or output_path.endswith(os.path.sep):
124
+ target_path = os.path.join(output_path, filename)
125
+
126
+ if os.path.exists(target_path) and not overwrite:
127
+ raise FileExistsError(f"File already exists: {target_path}. Use overwrite to replace.")
128
+
129
+ # Stream to disk
130
+ with open(target_path, 'wb') as f:
131
+ for chunk in resp.iter_content(chunk_size=8192):
132
+ if chunk:
133
+ f.write(chunk)
134
+
135
+ return target_path
@@ -23,6 +23,23 @@ class EntryManager:
23
23
  self.attribute_service = AttributesService(base_url)
24
24
  self.user_service = UsersService(base_url)
25
25
 
26
+ def delete(self, id: str = None, code: str = None) -> bool:
27
+ """Delete an entry by its id or code."""
28
+ if not id and not code:
29
+ raise ValueError("Entry ID or code is required")
30
+ if id:
31
+ if self.entry_service.delete_by_id(id=id) is True:
32
+ return True
33
+ else:
34
+ return False
35
+ elif code:
36
+ if self.entry_service.delete(code=code) is True:
37
+ return True
38
+ else:
39
+ return False
40
+ else:
41
+ raise ValueError("Entry ID or code is required")
42
+
26
43
  def get_entry(self, entry_id: str):
27
44
  """Get entry details by ID"""
28
45
  if not entry_id:
@@ -222,6 +239,9 @@ class EntryManager:
222
239
  raise ValueError(f"Invalid existing entry response: {existing_entry_response}")
223
240
 
224
241
  attribute_id = existing_entry_response.get("attributeId")
242
+
243
+ # make all keys lowercase
244
+ entry = {k.lower(): v for k, v in entry.items()}
225
245
 
226
246
  if not attribute_id:
227
247
  raise ValueError("Attribute ID is missing in the existing entry")
@@ -244,6 +264,10 @@ class EntryManager:
244
264
 
245
265
  if field_name in entry:
246
266
  updated_entry[column_name] = self.__get_value(field, entry[field_name])
267
+ elif column_name in entry:
268
+ updated_entry[column_name] = self.__get_value(field, entry[column_name])
269
+ else:
270
+ updated_entry[column_name] = None
247
271
 
248
272
  return self.entry_service.update(attribute_id=attribute_id, entry=updated_entry)
249
273
 
@@ -276,7 +300,7 @@ class EntryManager:
276
300
  if (datasource is not None and isinstance(datasource, dict) and "attributeid" in datasource):
277
301
  attribute_id = datasource.get("attributeid")
278
302
  except json.JSONDecodeError:
279
- raise ValueError(f"Invalid customdata JSON: {field.get('customdata')}")
303
+ raise ValueError(f"Invalid customdata JSON: {field.get('customdata')}")
280
304
 
281
305
  search_params = {
282
306
  "attributeId": attribute_id,
@@ -285,6 +309,14 @@ class EntryManager:
285
309
  "skipCount": 0
286
310
  }
287
311
 
312
+ # checks if source is GUID, if so, than we search by id
313
+ if self.is_guid(source):
314
+ search_params['queryString'] = f"id({source});"
315
+ search_response = self.search_service.search_entries(**search_params)
316
+ else:
317
+ search_params['queryString'] = f"displayname({source});"
318
+ search_response = self.search_service.search_entries(**search_params)
319
+
288
320
  search_response = self.search_service.search_entries(**search_params)
289
321
 
290
322
  if search_response.get('totalCount', 0) == 0:
@@ -303,6 +335,10 @@ class EntryManager:
303
335
  return json.dumps([result])
304
336
  else:
305
337
  return source
338
+
339
+ def is_guid(self, source: str) -> bool:
340
+ """Helper method to check if a string is a GUID."""
341
+ return len(source) == 36 and source.count('-') == 4
306
342
 
307
343
  def __get_field_value(self, entry_raw, field):
308
344
  """Helper method to safely get field value from entry_raw."""
@@ -3,7 +3,6 @@ from das.services.attributes import AttributesService
3
3
  from das.services.entry_fields import EntryFieldsService
4
4
  from das.services.search import SearchService
5
5
 
6
-
7
6
  class SearchManager:
8
7
  def __init__(self):
9
8
  base_url = load_api_url()
@@ -59,6 +58,22 @@ class SearchManager:
59
58
  "sorting": self.__convert_sorting(entry_fields, sort_by, sort_order)
60
59
  }
61
60
  results = self.search_service.search_entries(**search_params)
62
- return results
61
+
62
+ # Build user-friendly items list while preserving totalCount
63
+ friendly_items = []
64
+ for result in results.get('items', []):
65
+ entry = result.get('entry', {}) if isinstance(result, dict) else {}
66
+ friendly_item = {}
67
+ for field in entry_fields:
68
+ display_name = field.get('displayName')
69
+ column_name = field.get('column')
70
+ if display_name and column_name:
71
+ friendly_item[display_name] = entry.get(column_name)
72
+ friendly_items.append(friendly_item)
73
+
74
+ return {
75
+ 'items': friendly_items,
76
+ 'totalCount': results.get('totalCount', len(friendly_items))
77
+ }
63
78
  except Exception as e:
64
79
  raise ValueError(f"Search failed: {str(e)}")
@@ -0,0 +1,142 @@
1
+ import os
2
+ import sys
3
+ from math import ceil
4
+ from os.path import exists
5
+ import json
6
+ from base64 import b64encode
7
+ from das.common.api import post_data
8
+ from das.common.config import load_token, load_verify_ssl
9
+ from pathlib import Path
10
+ import math
11
+ import uuid
12
+ import requests
13
+
14
+ CHUNK_SIZE = 1000000 # 1MB
15
+ class DigitalObjectsService:
16
+ def __init__(self, base_url):
17
+ self.base_url = f"{base_url}/api/services/app/DigitalObject"
18
+ # Common possible upload endpoints observed across deployments
19
+ self.upload_digital_object_url = f"{base_url}/File/UploadDigitalObject"
20
+
21
+ def link_existing_digital_objects(self, attribute_id: int, entry_id: str, digital_object_id_list: list[str], is_unlink: bool = False):
22
+ """Link existing digital objects to an entry."""
23
+ token = load_token()
24
+
25
+ if token is None or token == "":
26
+ raise ValueError("Authorization token is required")
27
+
28
+ headers = {
29
+ "Authorization": f"Bearer {token}",
30
+ "Content-Type": "application/json",
31
+ }
32
+
33
+ payload = {
34
+ "attributeId": attribute_id,
35
+ "attributeValueId": entry_id,
36
+ "digitalObjects": [],
37
+ }
38
+
39
+ for digital_object_id in digital_object_id_list:
40
+ payload["digitalObjects"].append(
41
+ {
42
+ "attributeId": attribute_id,
43
+ "attributeValueId": entry_id,
44
+ "digitalObjectId": digital_object_id,
45
+ "isDeleted": is_unlink,
46
+ }
47
+ )
48
+
49
+ response = post_data(
50
+ f"{self.base_url}/LinkExistingDigitalObject", data=payload, headers=headers
51
+ )
52
+
53
+ return response.get("success")
54
+
55
+ # This is our chunk reader. This is what gets the next chunk of data ready to send.
56
+ def __read_in_chunks(self, file_object, chunk_size):
57
+ while True:
58
+ data = file_object.read(chunk_size)
59
+ if not data:
60
+ break
61
+ yield data
62
+
63
+
64
+ def upload_digital_object(self, file_description: str, digital_object_type_id: str, file_path: str):
65
+
66
+ if not exists(file_path):
67
+ raise ValueError(f"File '{file_path}' does not exist")
68
+
69
+ head, tail = os.path.split(file_path)
70
+
71
+ metadata = {
72
+ "fileName": tail,
73
+ "fileSize": os.path.getsize(file_path),
74
+ "description": file_description,
75
+ "digitalObjectTypeId": digital_object_type_id,
76
+ "id": str(uuid.uuid4()).lower(),
77
+ "description": file_description,
78
+ "totalCount": ceil(os.path.getsize(file_path) / CHUNK_SIZE),
79
+ "index": 0,
80
+ }
81
+
82
+ binary_file = open(file_path, "rb")
83
+ index = 0
84
+ offset = 0
85
+ digital_object_id = None
86
+ headers = {}
87
+
88
+ try:
89
+ for chunk in self.__read_in_chunks(binary_file, CHUNK_SIZE):
90
+ offset = index + len(chunk)
91
+ headers['Content-Range'] = 'bytes %s-%s/%s' % (index, offset - 1, metadata.get('fileSize'))
92
+ index = offset
93
+ json_metadata = json.dumps(metadata)
94
+ base654_bytes = b64encode(json_metadata.encode('utf-8')).decode('ascii')
95
+ headers['metadata'] = base654_bytes
96
+
97
+ r = self.upload_file(chunk, metadata, headers)
98
+
99
+ if r.get('result', None) is None:
100
+ continue
101
+
102
+ digital_object_id = r.get('result').get('id')
103
+ metadata['index'] = index + 1
104
+
105
+ binary_file.close()
106
+
107
+ except Exception as e:
108
+ raise ValueError(f"Error uploading file '{file_path}': {str(e)}")
109
+ finally:
110
+ binary_file.close()
111
+
112
+ return digital_object_id
113
+
114
+
115
+
116
+ def upload_file(self, file, body, headers):
117
+ """Upload a file to the digital object service."""
118
+ token = load_token()
119
+ headers.update({
120
+ "Accept": "application/json",
121
+ "Authorization": f"Bearer {token}",
122
+ # Do NOT set Content-Type here when sending files; requests will set proper multipart boundary
123
+ })
124
+
125
+ files = {
126
+ "file": ("chunk", file, "application/octet-stream"),
127
+ }
128
+
129
+ try:
130
+ response = requests.post(self.upload_digital_object_url, headers=headers, files=files, verify=load_verify_ssl())
131
+ response.raise_for_status()
132
+ if response.status_code == 200:
133
+ return response.json()
134
+ else:
135
+ raise ValueError(f"Error uploading file: {response.status_code} - {response.text}")
136
+ except requests.RequestException as e:
137
+ raise ValueError(f"Error uploading file: {str(e)}")
138
+
139
+
140
+
141
+
142
+
das/services/downloads.py CHANGED
@@ -1,84 +1,101 @@
1
- from das.common.api import post_data, get_data
2
- from das.common.config import load_token
3
-
4
-
5
- class DownloadRequestService:
6
- def __init__(self, base_url):
7
- self.base_url = f"{base_url}/api/services/app/DownloadRequest"
8
-
9
- def create(self, request_data: list[dict]):
10
- """Create a new download request."""
11
- token = load_token()
12
-
13
- if (token is None or token == ""):
14
- raise ValueError("Authorization token is required")
15
-
16
- headers = {
17
- "Authorization": f"Bearer {token}",
18
- "Content-Type": "application/json"
19
- }
20
-
21
- url = f"{self.base_url}/Create"
22
-
23
- response = post_data(url, data=request_data, headers=headers)
24
-
25
- if response.get('success') == True:
26
- return response.get('result')
27
- else:
28
- raise ValueError(response.get('error'))
29
-
30
- def delete(self, request_id: str):
31
- """Delete a download request by ID."""
32
-
33
- #check if request_id is valid uuid
34
- if not isinstance(request_id, str) or len(request_id) != 36:
35
- raise ValueError("Invalid request ID")
36
-
37
- token = load_token()
38
-
39
- if (token is None or token == ""):
40
- raise ValueError("Authorization token is required")
41
-
42
- headers = {
43
- "Authorization": f"Bearer {token}",
44
- "Content-Type": "application/json"
45
- }
46
-
47
- url = f"{self.base_url}/Delete?downloadRequestId={request_id}"
48
-
49
- response = post_data(url, data={}, headers=headers)
50
-
51
- if response.get('success') == True:
52
- return response.get('result')
53
- else:
54
- raise ValueError(response.get('error'))
55
-
56
- def get_my_requests(self):
57
- """Get all download requests for the current user."""
58
- token = load_token()
59
-
60
- if (token is None or token == ""):
61
- raise ValueError("Authorization token is required")
62
-
63
- headers = {
64
- "Authorization": f"Bearer {token}"
65
- }
66
-
67
- url = f"{self.base_url}/GetMyRequests"
68
- response = get_data(url, headers=headers)
69
-
70
- # Expected API response shape:
71
- # { success: true, result: { totalCount: number, items: [...] }, ... }
72
- if isinstance(response, dict) and response.get('success') is True:
73
- return response.get('result')
74
- # Some backends might already return the result without 'success'
75
- if isinstance(response, dict) and 'result' in response and 'success' not in response:
76
- return response.get('result')
77
- # If the API directly returns the payload (result), pass it through
78
- if isinstance(response, dict) and 'items' in response and 'totalCount' in response:
79
- return response
80
- # Otherwise raise a meaningful error
81
- error_msg = None
82
- if isinstance(response, dict):
83
- error_msg = response.get('error') or response.get('message')
84
- raise ValueError(error_msg or 'Failed to fetch download requests')
1
+ from das.common.api import post_data, get_data, get_binary_response
2
+ from das.common.config import load_token
3
+
4
+
5
+ class DownloadRequestService:
6
+ def __init__(self, base_url):
7
+ self.base_url = f"{base_url}/api/services/app/DownloadRequest"
8
+ self.download_files_url = f"{base_url}/File/DownloadRequestSet"
9
+
10
+ def create(self, request_data: list[dict]):
11
+ """Create a new download request."""
12
+ token = load_token()
13
+
14
+ if (token is None or token == ""):
15
+ raise ValueError("Authorization token is required")
16
+
17
+ headers = {
18
+ "Authorization": f"Bearer {token}",
19
+ "Content-Type": "application/json"
20
+ }
21
+
22
+ url = f"{self.base_url}/Create"
23
+
24
+ response = post_data(url, data=request_data, headers=headers)
25
+
26
+ if response.get('success') == True:
27
+ return response.get('result')
28
+ else:
29
+ raise ValueError(response.get('error'))
30
+
31
+ def delete(self, request_id: str):
32
+ """Delete a download request by ID."""
33
+
34
+ #check if request_id is valid uuid
35
+ if not isinstance(request_id, str) or len(request_id) != 36:
36
+ raise ValueError("Invalid request ID")
37
+
38
+ token = load_token()
39
+
40
+ if (token is None or token == ""):
41
+ raise ValueError("Authorization token is required")
42
+
43
+ headers = {
44
+ "Authorization": f"Bearer {token}",
45
+ "Content-Type": "application/json"
46
+ }
47
+
48
+ url = f"{self.base_url}/Delete?downloadRequestId={request_id}"
49
+
50
+ response = post_data(url, data={}, headers=headers)
51
+
52
+ if response.get('success') == True:
53
+ return response.get('result')
54
+ else:
55
+ raise ValueError(response.get('error'))
56
+
57
+ def get_my_requests(self):
58
+ """Get all download requests for the current user."""
59
+ token = load_token()
60
+
61
+ if (token is None or token == ""):
62
+ raise ValueError("Authorization token is required")
63
+
64
+ headers = {
65
+ "Authorization": f"Bearer {token}"
66
+ }
67
+
68
+ url = f"{self.base_url}/GetMyRequests"
69
+ response = get_data(url, headers=headers)
70
+
71
+ # Expected API response shape:
72
+ # { success: true, result: { totalCount: number, items: [...] }, ... }
73
+ if isinstance(response, dict) and response.get('success') is True:
74
+ return response.get('result')
75
+ # Some backends might already return the result without 'success'
76
+ if isinstance(response, dict) and 'result' in response and 'success' not in response:
77
+ return response.get('result')
78
+ # If the API directly returns the payload (result), pass it through
79
+ if isinstance(response, dict) and 'items' in response and 'totalCount' in response:
80
+ return response
81
+ # Otherwise raise a meaningful error
82
+ error_msg = None
83
+ if isinstance(response, dict):
84
+ error_msg = response.get('error') or response.get('message')
85
+ raise ValueError(error_msg or 'Failed to fetch download requests')
86
+
87
+ def download_files(self, request_id: str):
88
+ """Return a streaming HTTP response for the download bundle of a request."""
89
+ token = load_token()
90
+
91
+ if (token is None or token == ""):
92
+ raise ValueError("Authorization token is required")
93
+
94
+ headers = {
95
+ "Authorization": f"Bearer {token}"
96
+ }
97
+
98
+ url = f"{self.download_files_url}?requestId={request_id}"
99
+
100
+ response = get_binary_response(url, headers=headers, params=None, stream=True)
101
+ return response
das/services/entries.py CHANGED
@@ -52,8 +52,34 @@ class EntriesService():
52
52
  raise ValueError(f"API returned invalid JSON: {response.get('error')}\nResponse content: {raw_content}")
53
53
  else:
54
54
  raise ValueError(response.get('error') or "Unknown error occurred")
55
-
56
- def delete(self, code: str):
55
+
56
+ def delete_by_id(self, id: str) -> bool:
57
+ """Delete an entry by its id."""
58
+
59
+ token = load_token()
60
+
61
+ if (token is None or token == ""):
62
+ raise ValueError("Authorization token is required")
63
+
64
+ headers = {
65
+ "Authorization": f"Bearer {token}"
66
+ }
67
+
68
+ response_entry = self.get(id=id)
69
+
70
+ if response_entry is None:
71
+ raise ValueError(f"Entry with id {id} not found")
72
+
73
+ url = f"{self.base_url}/Delete?Id={response_entry.get('entry',{}).get('id')}&AttributeId={response_entry.get('attributeId')}"
74
+
75
+ response = delete_data(url, headers=headers)
76
+
77
+ if response.get('success') == True:
78
+ return True
79
+ else:
80
+ raise ValueError(response.get('error'))
81
+
82
+ def delete(self, code: str) -> bool:
57
83
  """Delete an entry by its code."""
58
84
  token = load_token()
59
85
 
@@ -74,7 +100,7 @@ class EntriesService():
74
100
  response = delete_data(url, headers=headers)
75
101
 
76
102
  if response.get('success') == True:
77
- return response
103
+ return True
78
104
  else:
79
105
  raise ValueError(response.get('error'))
80
106