pyPreservica 0.9.9__py3-none-any.whl → 3.3.4__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.
@@ -0,0 +1,877 @@
1
+ """
2
+ pyPreservica AdminAPI module definition
3
+
4
+ A client library for the Preservica Repository web Administration and Management API
5
+ https://us.preservica.com/api/admin/documentation.html
6
+
7
+ author: James Carr
8
+ licence: Apache License 2.0
9
+
10
+ """
11
+ import csv
12
+ import xml.etree.ElementTree
13
+ from typing import List, Any, Union
14
+
15
+ from pyPreservica.common import *
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class AdminAPI(AuthenticatedAPI):
21
+
22
+ def delete_system_role(self, role_name):
23
+ """
24
+ Delete a system role
25
+
26
+ :param role_name: The role to delete
27
+ :type role_name: str
28
+
29
+ """
30
+ if (self.major_version < 7) and (self.minor_version < 5):
31
+ raise RuntimeError(
32
+ "delete_system_role API call is only available with a Preservica v6.5.0 system or higher")
33
+
34
+ self._check_if_user_has_manager_role()
35
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
36
+ request = self.session.delete(f'{self.protocol}://{self.server}/api/admin/security/roles/{role_name}',
37
+ headers=headers)
38
+ if request.status_code == requests.codes.no_content:
39
+ return None
40
+ elif request.status_code == requests.codes.unauthorized:
41
+ self.token = self.__token__()
42
+ return self.delete_system_role(role_name)
43
+ else:
44
+ logger.error(request.content.decode('utf-8'))
45
+ raise RuntimeError(request.status_code, "delete_system_role failed")
46
+
47
+ def delete_security_tag(self, tag_name):
48
+ """
49
+ Delete a security tag
50
+
51
+ :param tag_name: The security tag to delete
52
+ :type tag_name: str
53
+
54
+ """
55
+ if (self.major_version < 7) and (self.minor_version < 4):
56
+ raise RuntimeError(
57
+ "delete_security_tag API call is only available with a Preservica v6.4.0 system or higher")
58
+
59
+ self._check_if_user_has_manager_role()
60
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
61
+ request = self.session.delete(f'{self.protocol}://{self.server}/api/admin/security/tags/{tag_name}',
62
+ headers=headers)
63
+ if request.status_code == requests.codes.no_content:
64
+ return None
65
+ elif request.status_code == requests.codes.unauthorized:
66
+ self.token = self.__token__()
67
+ return self.delete_security_tag(tag_name)
68
+ else:
69
+ logger.error(request.content.decode('utf-8'))
70
+ raise RuntimeError(request.status_code, "delete_security_tag failed")
71
+
72
+ def add_system_role(self, role_name) -> str:
73
+ """
74
+ Create a new user roles
75
+
76
+ :param role_name: The new role
77
+ :type role_name: str
78
+
79
+ :return: The new role
80
+ :rtype: str
81
+
82
+ """
83
+ if (self.major_version < 7) and (self.minor_version < 5):
84
+ raise RuntimeError("add_system_role API call is only available with a Preservica v6.5.0 system or higher")
85
+
86
+ self._check_if_user_has_manager_role()
87
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
88
+
89
+ xml_tag = xml.etree.ElementTree.Element('Role', {"xmlns": self.admin_ns})
90
+ xml_tag.text = str(role_name).strip()
91
+ xml_request = xml.etree.ElementTree.tostring(xml_tag, encoding='utf-8')
92
+ request = self.session.post(f'{self.protocol}://{self.server}/api/admin/security/roles', data=xml_request,
93
+ headers=headers)
94
+ if request.status_code == requests.codes.created:
95
+ xml_response = str(request.content.decode('utf-8'))
96
+ logger.debug(xml_response)
97
+ entity_response = xml.etree.ElementTree.fromstring(xml_response)
98
+ return entity_response.text
99
+ elif request.status_code == requests.codes.unauthorized:
100
+ self.token = self.__token__()
101
+ return self.add_system_role(role_name)
102
+ else:
103
+ logger.error(request.content.decode('utf-8'))
104
+ raise RuntimeError(request.status_code, "add_system_role failed")
105
+
106
+ def add_security_tag(self, tag_name) -> str:
107
+ """
108
+ Create a new security tag
109
+
110
+ :param tag_name: The new security tag
111
+ :type tag_name: str
112
+
113
+ :return: The new security tag
114
+ :rtype: str
115
+
116
+ """
117
+
118
+ if (self.major_version < 7) and (self.minor_version < 4):
119
+ raise RuntimeError("add_security_tag API call is only available with a Preservica v6.4.0 system or higher")
120
+
121
+ self._check_if_user_has_manager_role()
122
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
123
+
124
+ xml_tag = xml.etree.ElementTree.Element('Tag', {"xmlns": self.admin_ns})
125
+ xml_tag.text = str(tag_name).strip()
126
+ xml_request = xml.etree.ElementTree.tostring(xml_tag, encoding='utf-8')
127
+
128
+ request = self.session.post(f'{self.protocol}://{self.server}/api/admin/security/tags', data=xml_request,
129
+ headers=headers)
130
+ if request.status_code == requests.codes.created:
131
+ xml_response = str(request.content.decode('utf-8'))
132
+ logger.debug(xml_response)
133
+ entity_response = xml.etree.ElementTree.fromstring(xml_response)
134
+ return entity_response.text
135
+ elif request.status_code == requests.codes.unauthorized:
136
+ self.token = self.__token__()
137
+ return self.add_security_tag(tag_name)
138
+ else:
139
+ logger.error(request.content.decode('utf-8'))
140
+ raise RuntimeError(request.status_code, "add_security_tag failed")
141
+
142
+ def system_roles(self) -> list:
143
+ """
144
+ List all the user access roles in the system
145
+
146
+ :return: list of roles
147
+ :rtype: list
148
+
149
+ """
150
+ self._check_if_user_has_manager_role()
151
+
152
+ if (self.major_version < 7) and (self.minor_version < 5):
153
+ raise RuntimeError(
154
+ "system_roles API call is only available with a Preservica v6.5.0 system or higher")
155
+
156
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
157
+ request = self.session.get(f'{self.protocol}://{self.server}/api/admin/security/roles', headers=headers)
158
+ if request.status_code == requests.codes.ok:
159
+ xml_response = str(request.content.decode('utf-8'))
160
+ logger.debug(xml_response)
161
+ entity_response = xml.etree.ElementTree.fromstring(xml_response)
162
+ roles = entity_response.findall(f'.//{{{self.admin_ns}}}Role')
163
+ security_roles = []
164
+ for role in roles:
165
+ security_roles.append(role.text)
166
+ return security_roles
167
+ elif request.status_code == requests.codes.unauthorized:
168
+ self.token = self.__token__()
169
+ return self.roles()
170
+ else:
171
+ logger.error(request.content.decode('utf-8'))
172
+ raise RuntimeError(request.status_code, "roles failed")
173
+
174
+ def security_tags(self) -> list:
175
+ """
176
+ List all the security tags in the system
177
+
178
+ :return: list of security tags
179
+ :rtype: list
180
+
181
+ """
182
+ self._check_if_user_has_manager_role()
183
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
184
+ request = self.session.get(f'{self.protocol}://{self.server}/api/admin/security/tags', headers=headers)
185
+ if request.status_code == requests.codes.ok:
186
+ xml_response = str(request.content.decode('utf-8'))
187
+ logger.debug(xml_response)
188
+ entity_response = xml.etree.ElementTree.fromstring(xml_response)
189
+ tags = entity_response.findall(f'.//{{{self.admin_ns}}}Tag')
190
+ security_tags = []
191
+ for tag in tags:
192
+ security_tags.append(tag.text)
193
+ return security_tags
194
+ elif request.status_code == requests.codes.unauthorized:
195
+ self.token = self.__token__()
196
+ return self.security_tags()
197
+ else:
198
+ logger.error(request.content.decode('utf-8'))
199
+ raise RuntimeError(request.status_code, "security_tags failed")
200
+
201
+ def delete_user(self, username: str):
202
+ """
203
+ Delete a user
204
+
205
+ :param username: email address of the preservica user
206
+ :type username: str
207
+
208
+ """
209
+ self._check_if_user_has_manager_role()
210
+ self.disable_user(username)
211
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
212
+ request = self.session.delete(f'{self.protocol}://{self.server}/api/admin/users/{username}', headers=headers)
213
+ if request.status_code == requests.codes.no_content:
214
+ return None
215
+ elif request.status_code == requests.codes.unauthorized:
216
+ self.token = self.__token__()
217
+ return self.delete_user(username)
218
+ else:
219
+ logger.error(request.content.decode('utf-8'))
220
+ raise RuntimeError(request.status_code, "delete_user failed")
221
+
222
+ def add_user(self, username: str, full_name: str, roles: list, externally_authenticated: bool = False):
223
+ """
224
+ Add a new user
225
+
226
+ :param externally_authenticated:
227
+
228
+ :param username: email address of the preservica user
229
+ :type username: str
230
+
231
+ :param full_name: Users real name
232
+ :type full_name: str
233
+
234
+ :param roles: List of roles assigned to the user
235
+ :type roles: list
236
+
237
+ :return: dictionary of user attributes
238
+ :rtype: dict
239
+ """
240
+ self._check_if_user_has_manager_role()
241
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
242
+
243
+ xml_object = xml.etree.ElementTree.Element('User ', {"xmlns": self.admin_ns})
244
+ xml.etree.ElementTree.SubElement(xml_object, "FullName").text = full_name
245
+ xml.etree.ElementTree.SubElement(xml_object, "Email").text = username
246
+ if externally_authenticated:
247
+ xml.etree.ElementTree.SubElement(xml_object, "externallyAuthenticated").text = "true"
248
+ xml.etree.ElementTree.SubElement(xml_object, "userName").text = username
249
+ xml_roles = xml.etree.ElementTree.SubElement(xml_object, "Roles")
250
+ for role in roles:
251
+ xml.etree.ElementTree.SubElement(xml_roles, "Role").text = role
252
+ xml_request = xml.etree.ElementTree.tostring(xml_object, encoding='utf-8')
253
+ logger.debug(xml_request)
254
+ params = {"source": "UX2"}
255
+ request = self.session.post(f'{self.protocol}://{self.server}/api/admin/users', data=xml_request,
256
+ headers=headers, params=params)
257
+ if request.status_code == requests.codes.created:
258
+ return self.user_details(username)
259
+ elif request.status_code == requests.codes.unauthorized:
260
+ self.token = self.__token__()
261
+ return self.add_user(username, full_name, roles)
262
+ else:
263
+ logger.error(request.content.decode('utf-8'))
264
+ raise RuntimeError(request.status_code, "add_user failed")
265
+
266
+ def change_user_display_name(self, username: str, new_display_name: str) -> dict:
267
+ """
268
+ Change the user display name
269
+
270
+ :param username: email address of the preservica user
271
+ :type username: str
272
+
273
+ :param new_display_name: Users real name
274
+ :type new_display_name: str
275
+
276
+ :return: dictionary of user attributes
277
+ :rtype: dict
278
+ """
279
+ self._check_if_user_has_manager_role()
280
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
281
+ request = self.session.get(f"{self.protocol}://{self.server}/api/admin/users/{username}", headers=headers)
282
+ if request.status_code == requests.codes.ok:
283
+ xml_response = str(request.content.decode('utf-8'))
284
+ logger.debug(xml_response)
285
+ entity_response = xml.etree.ElementTree.fromstring(xml_response)
286
+ fullname = entity_response.find(f'.//{{{self.admin_ns}}}FullName')
287
+ fullname.text = new_display_name
288
+ xml_request = xml.etree.ElementTree.tostring(entity_response, encoding='utf-8')
289
+ logger.debug(xml_request)
290
+ update_request = self.session.put(f'{self.protocol}://{self.server}/api/admin/users/{username}',
291
+ data=xml_request,
292
+ headers=headers)
293
+ if update_request.status_code == requests.codes.ok:
294
+ return self.user_details(username)
295
+ elif update_request.status_code == requests.codes.unauthorized:
296
+ self.token = self.__token__()
297
+ return self.change_user_display_name(username, new_display_name)
298
+ else:
299
+ logger.error(request.content.decode('utf-8'))
300
+ raise RuntimeError(request.status_code, "change_user_display_name failed")
301
+ elif request.status_code == requests.codes.unauthorized:
302
+ self.token = self.__token__()
303
+ return self.change_user_display_name(username, new_display_name)
304
+ else:
305
+ logger.error(request.content.decode('utf-8'))
306
+ raise RuntimeError(request.status_code, "change_user_display_name failed")
307
+
308
+ def user_details(self, username: str) -> dict:
309
+ """
310
+ Get the details of a user by their username
311
+
312
+ :param username: email address of the preservica user
313
+ :type username: str
314
+
315
+ :return: dictionary of user attributes
316
+ :rtype: dict
317
+ """
318
+
319
+ self._check_if_user_has_manager_role()
320
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
321
+ request = self.session.get(f"{self.protocol}://{self.server}/api/admin/users/{username}", headers=headers)
322
+ return_dict = {}
323
+ if request.status_code == requests.codes.ok:
324
+ xml_response = str(request.content.decode('utf-8'))
325
+ logger.debug(xml_response)
326
+ entity_response = xml.etree.ElementTree.fromstring(xml_response)
327
+ username = entity_response.find(f'.//{{{self.admin_ns}}}UserName')
328
+ return_dict['UserName'] = username.text
329
+ fullname = entity_response.find(f'.//{{{self.admin_ns}}}FullName')
330
+ return_dict['FullName'] = fullname.text
331
+ email = entity_response.find(f'.//{{{self.admin_ns}}}Email')
332
+ return_dict['Email'] = email.text
333
+ tenant = entity_response.find(f'.//{{{self.admin_ns}}}Tenant')
334
+ return_dict['Tenant'] = tenant.text
335
+ enable = entity_response.find(f'.//{{{self.admin_ns}}}Enabled')
336
+ return_dict['Enabled'] = bool(enable.text == "true")
337
+
338
+ roles = entity_response.findall(f'.//{{{self.admin_ns}}}Role')
339
+ return_roles = []
340
+ for role in roles:
341
+ return_roles.append(role.text)
342
+ return_dict['Roles'] = return_roles
343
+ return return_dict
344
+ elif request.status_code == requests.codes.unauthorized:
345
+ self.token = self.__token__()
346
+ return self.user_details(username)
347
+ else:
348
+ logger.error(request.content.decode('utf-8'))
349
+ raise RuntimeError(request.status_code, "user_details failed")
350
+
351
+ def _account_status_(self, username: str, status: str, name: str):
352
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'text/plain;charset=UTF-8'}
353
+ data = {"userEnabledStatus": status}
354
+ request = self.session.put(f"{self.protocol}://{self.server}/api/admin/users/{username}/enabled",
355
+ headers=headers,
356
+ data=data)
357
+ if request.status_code == requests.codes.ok:
358
+ return request.content.decode("utf-8")
359
+ elif request.status_code == requests.codes.unauthorized:
360
+ self.token = self.__token__()
361
+ return self._account_status_(username, status, name)
362
+ else:
363
+ logger.error(request.content.decode('utf-8'))
364
+ raise RuntimeError(request.status_code, f"{name} failed")
365
+
366
+ def disable_user(self, username):
367
+ """
368
+ Disable a Preservica User to prevent them logging in
369
+
370
+ :param username: email address of the preservica user
371
+ :type username: str
372
+
373
+ """
374
+ self._check_if_user_has_manager_role()
375
+ return self._account_status_(username, "false", "disable_user")
376
+
377
+ def enable_user(self, username):
378
+ """
379
+ Enable a Preservica User
380
+
381
+ :param username: email address of the preservica user
382
+ :type username: str
383
+
384
+ """
385
+ self._check_if_user_has_manager_role()
386
+ return self._account_status_(username, "true", "enable_user")
387
+
388
+ def user_report(self, report_name="users.csv"):
389
+ """
390
+ Create a report on all tenancy users
391
+ :return:
392
+ """
393
+
394
+ self._check_if_user_has_manager_role()
395
+
396
+ fieldnames = ['UserName', 'FullName', 'Email', 'Tenant', 'Enabled', 'Roles']
397
+
398
+ with open(report_name, newline='', mode="wt", encoding="utf-8") as csv_file:
399
+ writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
400
+ writer.writeheader()
401
+ for username in self.all_users():
402
+ user_details = self.user_details(username)
403
+ writer.writerow(user_details)
404
+
405
+ def all_users(self) -> list:
406
+ """
407
+ Return a list of all users in the system
408
+
409
+ :return list of usernames:
410
+ :rtype: list
411
+ """
412
+
413
+ self._check_if_user_has_manager_role()
414
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
415
+ request = self.session.get(f"{self.protocol}://{self.server}/api/admin/users", headers=headers)
416
+ if request.status_code == requests.codes.ok:
417
+ xml_response = str(request.content.decode('utf-8'))
418
+ logger.debug(xml_response)
419
+ entity_response = xml.etree.ElementTree.fromstring(xml_response)
420
+ users = entity_response.findall(f'.//{{{self.admin_ns}}}User')
421
+ system_users = []
422
+ for user in users:
423
+ system_users.append(user.text)
424
+ return system_users
425
+ elif request.status_code == requests.codes.unauthorized:
426
+ self.token = self.__token__()
427
+ return self.all_users()
428
+ else:
429
+ logger.error(request.content.decode('utf-8'))
430
+ raise RuntimeError(request.status_code, "all_users failed")
431
+
432
+ def add_xml_schema(self, name: str, description: str, originalName: str, xml_data: Any):
433
+ """
434
+ Add a new XSD document to Preservica
435
+
436
+ :param name: Name for the XSD schema
437
+ :type name: str
438
+
439
+ :param description: Description for the XSD schema
440
+ :type description: str
441
+
442
+ :param originalName: The original file name for the schema on disk
443
+ :type originalName: str
444
+
445
+ :param xml_data: The xml schema as a UTF-8 string or a file like object
446
+ :type xml_data: Any
447
+
448
+ :return:
449
+ :rtype: None
450
+ """
451
+
452
+ self._check_if_user_has_manager_role()
453
+
454
+ params = {"name": name, "description": description, "originalName": originalName}
455
+
456
+ if isinstance(xml_data, str):
457
+ xml.etree.ElementTree.fromstring(xml_data)
458
+ xml_data = xml_data.encode("utf-8")
459
+ elif hasattr(xml_data, "read"):
460
+ pass
461
+
462
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
463
+ request = self.session.post(f"{self.protocol}://{self.server}/api/admin/schemas", headers=headers,
464
+ params=params,
465
+ data=xml_data)
466
+ if request.status_code == requests.codes.created:
467
+ return None
468
+ elif request.status_code == requests.codes.unauthorized:
469
+ self.token = self.__token__()
470
+ return self.add_xml_schema(name, description, originalName, xml_data)
471
+ else:
472
+ logger.error(request.content.decode('utf-8'))
473
+ raise RuntimeError(request.status_code, "add_xml_schema failed")
474
+
475
+ def add_xml_document(self, name: str, xml_data: Any, document_type: str = "MetadataTemplate"):
476
+ """
477
+ Add a new XML document to Preservica
478
+ The default type of XML document is a descriptive metadata template
479
+
480
+ Options are:
481
+
482
+ MetadataDropdownLists -> Authority Lists
483
+ CustomIndexDefinition -> Custom Search Indexes
484
+ MetadataTemplate -> Metadata Template
485
+ UploadWizardConfigurationFile -> Upload Wizard Config
486
+ ConfigurationFile -> Heritrix Config File
487
+
488
+ :param name: The name of the xml document
489
+ :type name: str
490
+
491
+ :param xml_data: The xml schema as a UTF-8 string or as a file like object
492
+ :type xml_data:
493
+
494
+ :param document_type: The type of the XML document, defaults to descriptive metadata templates
495
+ :type document_type: str
496
+
497
+ :return:
498
+ :rtype: None
499
+
500
+ """
501
+
502
+ self._check_if_user_has_manager_role()
503
+
504
+ params = {"name": name, "type": document_type}
505
+
506
+ if isinstance(xml_data, str):
507
+ xml.etree.ElementTree.fromstring(xml_data)
508
+ xml_data = xml_data.encode("utf-8")
509
+ elif hasattr(xml_data, "read"):
510
+ pass
511
+
512
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
513
+ request = self.session.post(f"{self.protocol}://{self.server}/api/admin/documents", headers=headers,
514
+ params=params,
515
+ data=xml_data)
516
+ if request.status_code == requests.codes.created:
517
+ return None
518
+ elif request.status_code == requests.codes.unauthorized:
519
+ self.token = self.__token__()
520
+ return self.add_xml_document(name, xml_data, document_type)
521
+ else:
522
+ logger.error(request.content.decode('utf-8'))
523
+ raise RuntimeError(request.status_code, "add_xml_document failed")
524
+
525
+ def delete_xml_document(self, uri: str):
526
+ """
527
+ Delete an XML document from Preservica's XML document store
528
+
529
+ :param uri: The URI of the xml document to delete
530
+ :type uri: str
531
+
532
+ :return:
533
+ :rtype: None
534
+
535
+ """
536
+
537
+ self._check_if_user_has_manager_role()
538
+
539
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
540
+
541
+ for document in self.xml_documents():
542
+ if document['SchemaUri'] == uri.strip():
543
+ request = self.session.delete(
544
+ f"{self.protocol}://{self.server}/api/admin/documents/{document['ApiId']}",
545
+ headers=headers)
546
+ if request.status_code == requests.codes.no_content:
547
+ return None
548
+ elif request.status_code == requests.codes.unauthorized:
549
+ self.token = self.__token__()
550
+ return self.delete_xml_document(uri)
551
+ else:
552
+ logger.error(request.content.decode('utf-8'))
553
+ raise RuntimeError(request.status_code, "delete_xml_document failed")
554
+ return None
555
+
556
+ def delete_xml_schema(self, uri: str):
557
+ """
558
+ Delete an XML schema from Preservica
559
+
560
+ :param uri: The URI of the xml schema to delete
561
+ :type uri: str
562
+
563
+ :return:
564
+ :rtype: None
565
+
566
+ """
567
+
568
+ self._check_if_user_has_manager_role()
569
+
570
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
571
+
572
+ for schema in self.xml_schemas():
573
+ if schema['SchemaUri'] == uri.strip():
574
+ request = self.session.delete(f"{self.protocol}://{self.server}/api/admin/schemas/{schema['ApiId']}",
575
+ headers=headers)
576
+ if request.status_code == requests.codes.no_content:
577
+ return None
578
+ elif request.status_code == requests.codes.unauthorized:
579
+ self.token = self.__token__()
580
+ return self.delete_xml_schema(uri)
581
+ else:
582
+ logger.error(request.content.decode('utf-8'))
583
+ raise RuntimeError(request.status_code, "delete_xml_schema failed")
584
+ return None
585
+
586
+ def xml_schema(self, uri: str) -> Union[str, None]:
587
+ """
588
+ Fetch the metadata schema XSD document as a string by its URI
589
+
590
+ :param uri: The URI of the xml schema
591
+ :type uri: str
592
+
593
+ :return: The XML schema as a string
594
+ :rtype: str
595
+
596
+ """
597
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
598
+
599
+ for schema in self.xml_schemas():
600
+ if schema['SchemaUri'] == uri.strip():
601
+ request = self.session.get(
602
+ f"{self.protocol}://{self.server}/api/admin/schemas/{schema['ApiId']}/content",
603
+ headers=headers)
604
+ if request.status_code == requests.codes.ok:
605
+ xml_response = str(request.content.decode('utf-8'))
606
+ return xml_response
607
+ elif request.status_code == requests.codes.unauthorized:
608
+ self.token = self.__token__()
609
+ return self.xml_schema(uri)
610
+ else:
611
+ logger.error(request.content.decode('utf-8'))
612
+ raise RuntimeError(request.status_code, "xml_schema failed")
613
+ return None
614
+
615
+ def xml_document(self, uri: str) -> Union[str, None]:
616
+ """
617
+ fetch the metadata XML document as a string by its URI
618
+
619
+ :param uri: The URI of the xml document
620
+ :type uri: str
621
+
622
+ :return: The XML document as a string
623
+ :rtype: str
624
+
625
+ """
626
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
627
+ for document in self.xml_documents():
628
+ if document['SchemaUri'] == uri.strip():
629
+ request = self.session.get(
630
+ f"{self.protocol}://{self.server}/api/admin/documents/{document['ApiId']}/content",
631
+ headers=headers)
632
+ if request.status_code == requests.codes.ok:
633
+ xml_response = str(request.content.decode('utf-8'))
634
+ return xml_response
635
+ elif request.status_code == requests.codes.unauthorized:
636
+ self.token = self.__token__()
637
+ return self.xml_document(uri)
638
+ else:
639
+ logger.error(request.content.decode('utf-8'))
640
+ raise RuntimeError(request.status_code, "xml_document failed")
641
+ return None
642
+
643
+ def xml_documents(self) -> List:
644
+ """
645
+ fetch the list of XML documents stored in Preservica
646
+
647
+ :return: List of XML documents stored in Preservica
648
+ :rtype: list
649
+
650
+ """
651
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
652
+ request = self.session.get(f'{self.protocol}://{self.server}/api/admin/documents', headers=headers)
653
+ if request.status_code == requests.codes.ok:
654
+ xml_response = str(request.content.decode('utf-8'))
655
+ logger.debug(xml_response)
656
+ entity_response = xml.etree.ElementTree.fromstring(xml_response)
657
+ documents = entity_response.findall(f'.//{{{self.admin_ns}}}Document')
658
+ results = list()
659
+ for document in documents:
660
+ document_dict = {}
661
+ api_id = document.find(f'.//{{{self.admin_ns}}}ApiId')
662
+ name = document.find(f'.//{{{self.admin_ns}}}Name')
663
+ document_type = document.find(f'.//{{{self.admin_ns}}}DocumentType')
664
+ schema_uri = document.find(f'.//{{{self.admin_ns}}}SchemaUri')
665
+ document_dict['SchemaUri'] = schema_uri.text
666
+ document_dict['Name'] = name.text
667
+ document_dict['DocumentType'] = document_type.text
668
+ document_dict['ApiId'] = api_id.text
669
+ results.append(document_dict)
670
+ return results
671
+ elif request.status_code == requests.codes.unauthorized:
672
+ self.token = self.__token__()
673
+ return self.xml_documents()
674
+ else:
675
+ logger.error(request.content.decode('utf-8'))
676
+ raise RuntimeError(request.status_code, "xml_documents failed")
677
+
678
+ def xml_schemas(self) -> List:
679
+ """
680
+ fetch the list of metadata schema XSD documents stored in Preservica
681
+
682
+ :return: List of XML schema's stored in Preservica
683
+ :rtype: list
684
+
685
+ """
686
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
687
+
688
+ request = self.session.get(f'{self.protocol}://{self.server}/api/admin/schemas', headers=headers)
689
+ if request.status_code == requests.codes.ok:
690
+ xml_response = str(request.content.decode('utf-8'))
691
+ logger.debug(xml_response)
692
+ entity_response = xml.etree.ElementTree.fromstring(xml_response)
693
+ schemas = entity_response.findall(f'.//{{{self.admin_ns}}}Schema')
694
+ results = []
695
+ for schema in schemas:
696
+ schema_dict = {}
697
+ schema_uri = schema.find(f'.//{{{self.admin_ns}}}SchemaUri')
698
+ name = schema.find(f'.//{{{self.admin_ns}}}Name')
699
+ description = schema.find(f'.//{{{self.admin_ns}}}Description')
700
+ aip_id = schema.find(f'.//{{{self.admin_ns}}}ApiId')
701
+ schema_dict['SchemaUri'] = schema_uri.text
702
+ schema_dict['Name'] = name.text
703
+ if description is not None:
704
+ schema_dict['Description'] = description.text
705
+ else:
706
+ schema_dict['Description'] = ""
707
+ schema_dict['ApiId'] = aip_id.text
708
+ results.append(schema_dict)
709
+ return results
710
+ elif request.status_code == requests.codes.unauthorized:
711
+ self.token = self.__token__()
712
+ return self.xml_schemas()
713
+ else:
714
+ logger.error(request.content.decode('utf-8'))
715
+ raise RuntimeError(request.status_code, "xml_schemas failed")
716
+
717
+ def xml_transforms(self) -> List:
718
+ """
719
+ fetch the list of xml transforms stored in Preservica
720
+
721
+ :return: List of XML transforms stored in Preservica
722
+ :rtype: list
723
+
724
+ """
725
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
726
+ request = self.session.get(f'{self.protocol}://{self.server}/api/admin/transforms', headers=headers)
727
+ if request.status_code == requests.codes.ok:
728
+ xml_response = str(request.content.decode('utf-8'))
729
+ logger.debug(xml_response)
730
+ entity_response = xml.etree.ElementTree.fromstring(xml_response)
731
+ transforms = entity_response.findall(f'.//{{{self.admin_ns}}}Transform')
732
+ results = []
733
+ for transform in transforms:
734
+ transform_dict = {}
735
+ to_schema_uri = transform.find(f'.//{{{self.admin_ns}}}ToSchemaUri')
736
+ from_schema_uri = transform.find(f'.//{{{self.admin_ns}}}FromSchemaUri')
737
+ name = transform.find(f'.//{{{self.admin_ns}}}Name')
738
+ purpose = transform.find(f'.//{{{self.admin_ns}}}Purpose')
739
+ aip_id = transform.find(f'.//{{{self.admin_ns}}}ApiId')
740
+ if to_schema_uri is not None:
741
+ transform_dict['ToSchemaUri'] = to_schema_uri.text
742
+ else:
743
+ transform_dict['ToSchemaUri'] = ""
744
+ if from_schema_uri is not None:
745
+ transform_dict['FromSchemaUri'] = from_schema_uri.text
746
+ else:
747
+ transform_dict['FromSchemaUri'] = ""
748
+
749
+ transform_dict['Name'] = name.text
750
+ transform_dict['Purpose'] = purpose.text
751
+ transform_dict['ApiId'] = aip_id.text
752
+ results.append(transform_dict)
753
+ return results
754
+
755
+ elif request.status_code == requests.codes.unauthorized:
756
+ self.token = self.__token__()
757
+ return self.xml_transforms()
758
+ else:
759
+ logger.error(request.content.decode('utf-8'))
760
+ raise RuntimeError(request.status_code, "xml_transforms failed")
761
+
762
+ def xml_transform(self, input_uri: str, output_uri: str) -> Union[str, None]:
763
+ """
764
+ fetch the XML transform as a string by its URIs
765
+
766
+ :param input_uri: The URI of the input XML document
767
+ :type input_uri: str
768
+
769
+ :param output_uri: The URI of the output XML document
770
+ :type output_uri: str
771
+
772
+ :return: The XML transform as a string
773
+ :rtype: str
774
+
775
+ """
776
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
777
+ for transform in self.xml_transforms():
778
+ if (transform['FromSchemaUri'] == input_uri.strip()) and (transform['ToSchemaUri'] == output_uri.strip()):
779
+ request = self.session.get(
780
+ f"{self.protocol}://{self.server}/api/admin/transforms/{transform['ApiId']}/content",
781
+ headers=headers)
782
+ if request.status_code == requests.codes.ok:
783
+ return str(request.content.decode('utf-8'))
784
+ elif request.status_code == requests.codes.unauthorized:
785
+ self.token = self.__token__()
786
+ return self.xml_transform(input_uri, output_uri)
787
+ else:
788
+ logger.error(request.content.decode('utf-8'))
789
+ raise RuntimeError(request.status_code, "xml_transform failed")
790
+ return None
791
+
792
+ def delete_xml_transform(self, input_uri: str, output_uri: str):
793
+ """
794
+ Delete a XSD document from Preservica
795
+
796
+ :param input_uri: The URI of the input XML document
797
+ :type input_uri: str
798
+
799
+ :param output_uri: The URI of the output XML document
800
+ :type output_uri: str
801
+
802
+ :return:
803
+ :rtype: None
804
+
805
+ """
806
+
807
+ self._check_if_user_has_manager_role()
808
+
809
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
810
+
811
+ for transform in self.xml_transforms():
812
+ if (transform['FromSchemaUri'] == input_uri.strip()) and (transform['ToSchemaUri'] == output_uri.strip()):
813
+ request = self.session.delete(
814
+ f"{self.protocol}://{self.server}/api/admin/transforms/{transform['ApiId']}",
815
+ headers=headers)
816
+ if request.status_code == requests.codes.no_content:
817
+ return None
818
+ elif request.status_code == requests.codes.unauthorized:
819
+ self.token = self.__token__()
820
+ return self.delete_xml_transform(input_uri, output_uri)
821
+ else:
822
+ logger.error(request.content.decode('utf-8'))
823
+ raise RuntimeError(request.status_code, "delete_xml_transform failed")
824
+ return None
825
+
826
+ def add_xml_transform(self, name: str, input_uri: str, output_uri: str, purpose: str, originalName: str,
827
+ xml_data: Any):
828
+ """
829
+ Add a new XML transform to Preservica
830
+
831
+ :param name: The name of the XML transform
832
+ :type name: str
833
+
834
+ :param input_uri: The URI of the input XML document
835
+ :type input_uri: str
836
+
837
+ :param output_uri: The URI of the output XML document
838
+ :type output_uri: str
839
+
840
+ :param purpose: The purpose of the transform, "transform" , "edit", "view"
841
+ :type purpose: str
842
+
843
+ :param originalName: The original file name of the transform
844
+ :type originalName: str
845
+
846
+ :param xml_data: The transform xml as a string or file like object
847
+ :type xml_data: Any
848
+
849
+ :return:
850
+ :rtype: None
851
+
852
+ """
853
+
854
+ self._check_if_user_has_manager_role()
855
+
856
+ params = {"name": name, "from": input_uri, "to": output_uri, "purpose": purpose.lower(),
857
+ "originalName": originalName}
858
+
859
+ if isinstance(xml_data, str):
860
+ xml.etree.ElementTree.fromstring(xml_data)
861
+ xml_data = xml_data.encode("utf-8")
862
+ elif hasattr(xml_data, "read"):
863
+ pass
864
+
865
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
866
+ request = self.session.post(f"{self.protocol}://{self.server}/api/admin/transforms", headers=headers,
867
+ params=params,
868
+ data=xml_data)
869
+ if request.status_code == requests.codes.created:
870
+ return None
871
+
872
+ if request.status_code == requests.codes.unauthorized:
873
+ self.token = self.__token__()
874
+ return self.add_xml_transform(name, input_uri, output_uri, purpose, originalName, xml_data)
875
+
876
+ logger.error(request.content.decode('utf-8'))
877
+ raise RuntimeError(request.status_code, "add_xml_transform failed")