pyxecm 1.5__py3-none-any.whl → 1.6__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.

Potentially problematic release.


This version of pyxecm might be problematic. Click here for more details.

pyxecm/customizer/pht.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """
2
- PHT is an OpenText internal application aiming at creating a common naming reference for Engineering Products and
2
+ PHT stands for Product Hierarchy Tracker and is an OpenText internal application aiming at creating a common naming reference for Engineering Products and
3
3
  track all product-related data. It also provides an approved reporting hierarchy.
4
4
  See: https://pht.opentext.com
5
5
 
@@ -10,6 +10,7 @@ __init__ : class initializer
10
10
  config : Returns config data set
11
11
  get_data: Get the Data object that holds all processed PHT products
12
12
  request_header: Returns the request header for ServiceNow API calls
13
+ do_request: Call an PHT REST API in a safe way
13
14
  parse_request_response: Parse the REST API responses and convert
14
15
  them to Python dict in a safe way
15
16
 
@@ -18,9 +19,24 @@ authenticate : Authenticates at ServiceNow API
18
19
  get_attributes: Get a list of all product attributes (schema) of PHT
19
20
  get_business_units: Get the list of PHT Business Units
20
21
  get_product_families: Get the list of PHT product families
22
+
21
23
  get_products: Get the list of PHT products
24
+ get_products_filtered: Get a list of filtered PHT products
25
+ get_product: Get a specific product in PHT
26
+ search_products: Search products in PHT
27
+
22
28
  get_master_products: Get the list of PHT master products
23
- filter_products: Get a list of filtered PHT products
29
+ get_master_products_filtered: Get a list of filtered PHT master products
30
+ get_master_product: Get a specific product in PHT
31
+
32
+ get_teams: Get a list of all teams in PHT.
33
+ get_team: Get a specific team in PHT.
34
+
35
+ get_componets: Get a list of all components in PHT.
36
+ get_components_filtered: Get a list of filtered PHT components.
37
+ get_component: Get a specific component in PHT.
38
+ search_components: Search components in PHT
39
+
24
40
  load_products: Load products into a data frame.
25
41
 
26
42
  """
@@ -33,6 +49,7 @@ __email__ = "mdiefenb@opentext.com"
33
49
 
34
50
  import json
35
51
  import logging
52
+ import time
36
53
 
37
54
  import requests
38
55
  from requests.auth import HTTPBasicAuth
@@ -43,7 +60,8 @@ logger = logging.getLogger("pyxecm.customizer.pht")
43
60
  REQUEST_HEADERS = {"Accept": "application/json", "Content-Type": "application/json"}
44
61
 
45
62
  REQUEST_TIMEOUT = 60
46
-
63
+ REQUEST_RETRY_DELAY = 20
64
+ REQUEST_MAX_RETRIES = 2
47
65
 
48
66
  class PHT(object):
49
67
  """Used to retrieve data from OpenText PHT."""
@@ -77,10 +95,18 @@ class PHT(object):
77
95
  pht_config["businessUnitUrl"] = pht_config["restUrl"] + "/business-unit"
78
96
  pht_config["productFamilyUrl"] = pht_config["restUrl"] + "/product-family"
79
97
  pht_config["productUrl"] = pht_config["restUrl"] + "/product"
80
- pht_config["searchUrl"] = pht_config["productUrl"] + "/product/search"
98
+ pht_config["productFilteredUrl"] = pht_config["productUrl"] + "/filtered"
99
+ pht_config["productSearchUrl"] = pht_config["productUrl"] + "/search"
100
+ pht_config["productUsersUrl"] = pht_config["productUrl"] + "/users"
81
101
  pht_config["teamUrl"] = pht_config["restUrl"] + "/team"
82
- pht_config["componentUrl"] = pht_config["restUrl"] + "/component"
83
102
  pht_config["masterProductUrl"] = pht_config["restUrl"] + "/master-product"
103
+ pht_config["masterProductFilteredUrl"] = (
104
+ pht_config["masterProductUrl"] + "/filtered"
105
+ )
106
+ pht_config["componentUrl"] = pht_config["restUrl"] + "/component"
107
+ pht_config["componentFilteredUrl"] = pht_config["componentUrl"] + "/filtered"
108
+ pht_config["componentSearchUrl"] = pht_config["componentUrl"] + "/search"
109
+ pht_config["componentUsersUrl"] = pht_config["componentUrl"] + "/users"
84
110
 
85
111
  self._config = pht_config
86
112
 
@@ -132,6 +158,135 @@ class PHT(object):
132
158
 
133
159
  # end method definition
134
160
 
161
+ def do_request(
162
+ self,
163
+ url: str,
164
+ method: str = "GET",
165
+ headers: dict | None = None,
166
+ data: dict | None = None,
167
+ files: dict | None = None,
168
+ timeout: int | None = REQUEST_TIMEOUT,
169
+ show_error: bool = True,
170
+ failure_message: str = "",
171
+ success_message: str = "",
172
+ max_retries: int = REQUEST_MAX_RETRIES,
173
+ retry_forever: bool = False,
174
+ ) -> dict | None:
175
+ """Call an PHT REST API in a safe way
176
+
177
+ Args:
178
+ url (str): URL to send the request to.
179
+ method (str, optional): HTTP method (GET, POST, etc.). Defaults to "GET".
180
+ headers (dict | None, optional): Request Headers. Defaults to None.
181
+ json (dict | None, optional): Request payload. Defaults to None.
182
+ files (dict | None, optional): Dictionary of {"name": file-tuple} for multipart encoding upload.
183
+ file-tuple can be a 2-tuple ("filename", fileobj) or a 3-tuple ("filename", fileobj, "content_type")
184
+ timeout (int | None, optional): Timeout for the request in seconds. Defaults to REQUEST_TIMEOUT.
185
+ show_error (bool, optional): Whether or not an error should be logged in case of a failed REST call.
186
+ If False, then only a warning is logged. Defaults to True.
187
+ failure_message (str, optional): Specific error message. Defaults to "".
188
+ max_retries (int, optional): How many retries on Connection errors? Default is REQUEST_MAX_RETRIES.
189
+ retry_forever (bool, optional): Eventually wait forever - without timeout. Defaults to False.
190
+
191
+ Returns:
192
+ dict | None: Response of PHT REST API or None in case of an error.
193
+ """
194
+
195
+ retries = 0
196
+ while True:
197
+ try:
198
+ response = self._session.request(
199
+ method=method,
200
+ url=url,
201
+ json=data,
202
+ files=files,
203
+ headers=headers,
204
+ timeout=timeout,
205
+ )
206
+
207
+ if response.ok:
208
+ if success_message:
209
+ logger.debug(success_message)
210
+ return self.parse_request_response(response)
211
+ # Check if Session has expired - then re-authenticate and try once more
212
+ elif response.status_code == 401 and retries == 0:
213
+ logger.debug("Session has expired - try to re-authenticate...")
214
+ self.authenticate()
215
+ retries += 1
216
+ else:
217
+ # Handle plain HTML responses to not pollute the logs
218
+ content_type = response.headers.get("content-type", None)
219
+ if content_type == "text/html":
220
+ response_text = "HTML content (see debug log)"
221
+ else:
222
+ response_text = response.text
223
+
224
+ if show_error:
225
+ logger.error(
226
+ "%s; status -> %s; error -> %s",
227
+ failure_message,
228
+ response.status_code,
229
+ response_text,
230
+ )
231
+ else:
232
+ logger.warning(
233
+ "%s; status -> %s; warning -> %s",
234
+ failure_message,
235
+ response.status_code,
236
+ response_text,
237
+ )
238
+
239
+ if content_type == "text/html":
240
+ logger.debug(
241
+ "%s; status -> %s; warning -> %s",
242
+ failure_message,
243
+ response.status_code,
244
+ response.text,
245
+ )
246
+
247
+ return None
248
+ except requests.exceptions.Timeout:
249
+ if retries <= max_retries:
250
+ logger.warning(
251
+ "Request timed out. Retrying in %s seconds...",
252
+ str(REQUEST_RETRY_DELAY),
253
+ )
254
+ retries += 1
255
+ time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
256
+ else:
257
+ logger.error(
258
+ "%s; timeout error",
259
+ failure_message,
260
+ )
261
+ if retry_forever:
262
+ # If it fails after REQUEST_MAX_RETRIES retries we let it wait forever
263
+ logger.warning("Turn timeouts off and wait forever...")
264
+ timeout = None
265
+ else:
266
+ return None
267
+ except requests.exceptions.ConnectionError:
268
+ if retries <= max_retries:
269
+ logger.warning(
270
+ "Connection error. Retrying in %s seconds...",
271
+ str(REQUEST_RETRY_DELAY),
272
+ )
273
+ retries += 1
274
+ time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
275
+ else:
276
+ logger.error(
277
+ "%s; connection error",
278
+ failure_message,
279
+ )
280
+ if retry_forever:
281
+ # If it fails after REQUEST_MAX_RETRIES retries we let it wait forever
282
+ logger.warning("Turn timeouts off and wait forever...")
283
+ timeout = None
284
+ time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
285
+ else:
286
+ return None
287
+
288
+ # end method definition
289
+
135
290
  def parse_request_response(
136
291
  self,
137
292
  response_object: requests.Response,
@@ -229,24 +384,13 @@ class PHT(object):
229
384
  request_header = self.request_header()
230
385
  request_url = self.config()["attributeUrl"]
231
386
 
232
- retries = 0
233
-
234
- while True:
235
- response = self._session.get(url=request_url, headers=request_header)
236
- if response.ok:
237
- return self.parse_request_response(response)
238
- # Check if Session has expired - then re-authenticate and try once more
239
- elif response.status_code == 401 and retries == 0:
240
- logger.debug("Session has expired - try to re-authenticate...")
241
- self.authenticate()
242
- retries += 1
243
- else:
244
- logger.error(
245
- "Failed to get PHT attributes; error -> %s (%s)",
246
- response.text,
247
- response.status_code,
248
- )
249
- return None
387
+ return self.do_request(
388
+ url=request_url,
389
+ method="GET",
390
+ headers=request_header,
391
+ timeout=None,
392
+ failure_message="Failed to get PHT attributes!",
393
+ )
250
394
 
251
395
  # end method definition
252
396
 
@@ -310,24 +454,13 @@ class PHT(object):
310
454
  request_header = self.request_header()
311
455
  request_url = self.config()["businessUnitUrl"]
312
456
 
313
- retries = 0
314
-
315
- while True:
316
- response = self._session.get(url=request_url, headers=request_header)
317
- if response.ok:
318
- return self.parse_request_response(response)
319
- # Check if Session has expired - then re-authenticate and try once more
320
- elif response.status_code == 401 and retries == 0:
321
- logger.debug("Session has expired - try to re-authenticate...")
322
- self.authenticate()
323
- retries += 1
324
- else:
325
- logger.error(
326
- "Failed to get PHT business units; error -> %s (%s)",
327
- response.text,
328
- response.status_code,
329
- )
330
- return None
457
+ return self.do_request(
458
+ url=request_url,
459
+ method="GET",
460
+ headers=request_header,
461
+ timeout=None,
462
+ failure_message="Failed to get PHT business units!",
463
+ )
331
464
 
332
465
  # end method definition
333
466
 
@@ -341,24 +474,13 @@ class PHT(object):
341
474
  request_header = self.request_header()
342
475
  request_url = self.config()["productFamilyUrl"]
343
476
 
344
- retries = 0
345
-
346
- while True:
347
- response = self._session.get(url=request_url, headers=request_header)
348
- if response.ok:
349
- return self.parse_request_response(response)
350
- # Check if Session has expired - then re-authenticate and try once more
351
- elif response.status_code == 401 and retries == 0:
352
- logger.debug("Session has expired - try to re-authenticate...")
353
- self.authenticate()
354
- retries += 1
355
- else:
356
- logger.error(
357
- "Failed to get PHT product families; error -> %s (%s)",
358
- response.text,
359
- response.status_code,
360
- )
361
- return None
477
+ return self.do_request(
478
+ url=request_url,
479
+ method="GET",
480
+ headers=request_header,
481
+ timeout=None,
482
+ failure_message="Failed to get PHT product families!",
483
+ )
362
484
 
363
485
  # end method definition
364
486
 
@@ -372,24 +494,111 @@ class PHT(object):
372
494
  request_header = self.request_header()
373
495
  request_url = self.config()["productUrl"]
374
496
 
375
- retries = 0
497
+ return self.do_request(
498
+ url=request_url,
499
+ method="GET",
500
+ headers=request_header,
501
+ timeout=None,
502
+ failure_message="Failed to get PHT products!",
503
+ )
376
504
 
377
- while True:
378
- response = self._session.get(url=request_url, headers=request_header)
379
- if response.ok:
380
- return self.parse_request_response(response)
381
- # Check if Session has expired - then re-authenticate and try once more
382
- elif response.status_code == 401 and retries == 0:
383
- logger.debug("Session has expired - try to re-authenticate...")
384
- self.authenticate()
385
- retries += 1
386
- else:
387
- logger.error(
388
- "Failed to get PHT products; error -> %s (%s)",
389
- response.text,
390
- response.status_code,
391
- )
392
- return None
505
+ # end method definition
506
+
507
+ def get_products_filtered(
508
+ self, filter_definition: dict | None = None
509
+ ) -> list | None:
510
+ """Get a list of filtered PHT products
511
+
512
+ Args:
513
+ filter_definition (dict): a dictionary of filter conditions.
514
+ Example filters:
515
+ {
516
+ businessUnitName: <String>,
517
+ productFamilyName: <String>,
518
+ productName: <String>,
519
+ productSyncId: <String>,
520
+ productStatus: ACTIVE | INACTIVE | MAINTENANCE,
521
+ productManager: <String>,
522
+ developmentManager: <String>,
523
+ attributeOperator: AND | OR,
524
+ attributes: {
525
+ "<AttributeName>": {
526
+ "compare": CONTAINS | EXISTS | DOES_NOT_EXISTS,
527
+ "values": List<String>
528
+ },
529
+ ...
530
+ },
531
+ includeAttributes: true | false
532
+ }
533
+ Returns:
534
+ list | None: list of matching products.
535
+ """
536
+
537
+ if not filter_definition:
538
+ return self.get_products()
539
+
540
+ request_header = self.request_header()
541
+ request_url = self.config()["productFilteredUrl"]
542
+ request_data = filter_definition
543
+
544
+ return self.do_request(
545
+ url=request_url,
546
+ method="POST",
547
+ headers=request_header,
548
+ data=request_data,
549
+ timeout=None,
550
+ failure_message="Failed to get filtered PHT products!",
551
+ )
552
+
553
+ # end method definition
554
+
555
+ def get_product(self, sync_id: int) -> dict | None:
556
+ """Get a specific product in PHT.
557
+
558
+ Returns:
559
+ dict | None: product data matching the sync ID
560
+ """
561
+
562
+ request_header = self.request_header()
563
+ request_url = self.config()["productUrl"] + "/" + str(sync_id)
564
+
565
+ return self.do_request(
566
+ url=request_url,
567
+ method="GET",
568
+ headers=request_header,
569
+ timeout=None,
570
+ failure_message="Failed to retrieve PHT product with sync ID -> {}!".format(
571
+ sync_id
572
+ ),
573
+ )
574
+
575
+ # end method definition
576
+
577
+ def search_products(
578
+ self, query: str, business_unit: str | None = None, family: str | None = None
579
+ ) -> list | None:
580
+ """Search for specific product in PHT by the product name.
581
+
582
+ Returns:
583
+ str: search term matches any part of the component name
584
+ """
585
+
586
+ request_header = self.request_header()
587
+ request_url = self.config()["componentSearchUrl"] + "?q=" + query
588
+ if business_unit:
589
+ request_url += "&businessUnit=" + business_unit
590
+ if family:
591
+ request_url += "&family=" + family
592
+
593
+ return self.do_request(
594
+ url=request_url,
595
+ method="GET",
596
+ headers=request_header,
597
+ timeout=None,
598
+ failure_message="Failed to retrieve PHT components matching -> {}!".format(
599
+ query
600
+ ),
601
+ )
393
602
 
394
603
  # end method definition
395
604
 
@@ -403,41 +612,32 @@ class PHT(object):
403
612
  request_header = self.request_header()
404
613
  request_url = self.config()["masterProductUrl"]
405
614
 
406
- retries = 0
407
-
408
- while True:
409
- response = self._session.get(url=request_url, headers=request_header)
410
- if response.ok:
411
- return self.parse_request_response(response)
412
- # Check if Session has expired - then re-authenticate and try once more
413
- elif response.status_code == 401 and retries == 0:
414
- logger.debug("Session has expired - try to re-authenticate...")
415
- self.authenticate()
416
- retries += 1
417
- else:
418
- logger.error(
419
- "Failed to get PHT master products; error -> %s (%s)",
420
- response.text,
421
- response.status_code,
422
- )
423
- return None
615
+ return self.do_request(
616
+ url=request_url,
617
+ method="GET",
618
+ headers=request_header,
619
+ timeout=None,
620
+ failure_message="Failed to get PHT master products!",
621
+ )
424
622
 
425
623
  # end method definition
426
624
 
427
- def filter_products(self, filter_definition: dict | None = None) -> list | None:
428
- """Get a list of filtered PHT products
625
+ def get_master_products_filtered(
626
+ self, filter_definition: dict | None = None
627
+ ) -> list | None:
628
+ """Get a list of filtered PHT master products
429
629
 
430
630
  Args:
431
631
  filter_definition (dict): a dictionary of filter conditions.
432
632
  Example filters:
433
- businessUnitName: <String>
434
- productFamilyName: <String>
435
- productName: <String>
436
- productSyncId: <String>
437
- productStatus: ACTIVE | INACTIVE | MAINTENANCE
438
- productManager: <String>
439
- developmentManager: <String>
440
- attributeOperator: AND | OR
633
+ {
634
+ businessUnitName: <String>,
635
+ productFamilyName: <String>,
636
+ masterproductName: <String>,
637
+ masterproductSyncId: <String>,
638
+ masterproductStatus: ACTIVE | INACTIVE | MAINTENANCE,
639
+ productManagerDomain: <String>,
640
+ attributeOperator: AND | OR,
441
641
  attributes: {
442
642
  "<AttributeName>": {
443
643
  "compare": CONTAINS | EXISTS | DOES_NOT_EXISTS,
@@ -446,6 +646,8 @@ class PHT(object):
446
646
  ...
447
647
  },
448
648
  includeAttributes: true | false
649
+ includeLinkedProducts: true | false
650
+ }
449
651
  Returns:
450
652
  list | None: list of matching products.
451
653
  """
@@ -454,29 +656,250 @@ class PHT(object):
454
656
  return self.get_products()
455
657
 
456
658
  request_header = self.request_header()
457
- request_url = self.config()["productUrl"] + "/filtered"
659
+ request_url = self.config()["masterProductFilteredUrl"]
458
660
  request_data = filter_definition
459
661
 
460
- retries = 0
662
+ return self.do_request(
663
+ url=request_url,
664
+ method="POST",
665
+ headers=request_header,
666
+ data=request_data,
667
+ timeout=None,
668
+ failure_message="Failed to get filtered PHT master products!",
669
+ )
461
670
 
462
- while True:
463
- response = self._session.post(
464
- url=request_url, headers=request_header, json=request_data
465
- )
466
- if response.ok:
467
- return self.parse_request_response(response)
468
- # Check if Session has expired - then re-authenticate and try once more
469
- elif response.status_code == 401 and retries == 0:
470
- logger.debug("Session has expired - try to re-authenticate...")
471
- self.authenticate()
472
- retries += 1
473
- else:
474
- logger.error(
475
- "Failed to get PHT master products; error -> %s (%s)",
476
- response.text,
477
- response.status_code,
478
- )
479
- return None
671
+ # end method definition
672
+
673
+ def get_master_product(self, sync_id: int) -> dict | None:
674
+ """Get a specific product in PHT.
675
+
676
+ Returns:
677
+ dict | None: product data matching the sync ID
678
+ """
679
+
680
+ request_header = self.request_header()
681
+ request_url = self.config()["productUrl"] + "/" + str(sync_id)
682
+
683
+ return self.do_request(
684
+ url=request_url,
685
+ method="GET",
686
+ headers=request_header,
687
+ timeout=None,
688
+ failure_message="Failed to retrieve PHT product with sync ID -> {}!".format(
689
+ sync_id
690
+ ),
691
+ )
692
+
693
+ # end method definition
694
+
695
+ def get_teams(self) -> list:
696
+ """Get a list of all teams in PHT.
697
+
698
+ Returns:
699
+ list: list of PHT teams
700
+ """
701
+
702
+ request_header = self.request_header()
703
+ request_url = self.config()["teamUrl"]
704
+
705
+ return self.do_request(
706
+ url=request_url,
707
+ method="GET",
708
+ headers=request_header,
709
+ timeout=None,
710
+ failure_message="Failed to retrieve PHT teams!",
711
+ )
712
+
713
+ # end method definition
714
+
715
+ def get_team(self, team_id: int) -> dict | None:
716
+ """Get a specific team in PHT.
717
+
718
+ Returns:
719
+ dict | None: dict of the PHT team
720
+ """
721
+
722
+ request_header = self.request_header()
723
+ request_url = self.config()["teamUrl"] + "/" + str(team_id)
724
+
725
+ return self.do_request(
726
+ url=request_url,
727
+ method="GET",
728
+ headers=request_header,
729
+ timeout=None,
730
+ failure_message="Failed to retrieve PHT teams!",
731
+ )
732
+
733
+ # end method definition
734
+
735
+ def get_components(self) -> list:
736
+ """Get a list of all components in PHT.
737
+
738
+ Returns:
739
+ list: list of PHT components
740
+
741
+ Example result:
742
+ [
743
+ {
744
+ 'id': 468,
745
+ 'syncId': '6380c3da-8ded-40cd-8071-61f6721956f5',
746
+ 'name': 'XOTE',
747
+ 'developmentManager': {
748
+ 'id': 237,
749
+ 'domain': 'kurt',
750
+ 'email': 'kurt.junker@opentext.com',
751
+ 'name': 'Kurt Junker',
752
+ 'role': None,
753
+ 'status': 'ACTIVE',
754
+ 'location': 'Grasbrunn, DEU',
755
+ 'title': 'Sr. Manager, Software Engineering',
756
+ 'type': 'OTHERS'
757
+ },
758
+ 'componentCategory': {
759
+ 'id': 2,
760
+ 'name': 'Testing scripts',
761
+ 'shortName': 'Testing scripts'
762
+ },
763
+ 'comment': 'Test Framework maintained and used by Core Archive Team',
764
+ 'status': 'MAINTENANCE',
765
+ 'attributes': [
766
+ {
767
+ 'id': 409,
768
+ 'attribute': {
769
+ 'id': 4,
770
+ 'uuid': '03e228b5-9eae-11ea-96ab-00505682bce9',
771
+ 'name': 'Build Advocate',
772
+ 'description': 'Primary contact for build items.',
773
+ 'type': 'USER'
774
+ },
775
+ 'value': 'burkhard',
776
+ 'textAttributeValue': None,
777
+ 'userAttributeValue': {
778
+ 'id': 414,
779
+ 'domain': 'burkhard',
780
+ 'email': 'burkhard.meier@opentext.com',
781
+ 'name': 'Burkhard Meier',
782
+ 'role': None,
783
+ 'status': 'ACTIVE',
784
+ 'location': 'Virtual, DEU',
785
+ 'title': 'Principal Software Engineer',
786
+ 'type': 'DEV'
787
+ },
788
+ 'listAttributeValue': None
789
+ },
790
+ ...
791
+ ],
792
+ 'sourceRepos': [],
793
+ 'artifacts': [],
794
+ 'products': [],
795
+ 'teams': [],
796
+ 'users': [],
797
+ 'guestTeams': [],
798
+ 'guestUsers': [],
799
+ 'relatedLOBS': []
800
+ }
801
+ ]
802
+ """
803
+
804
+ request_header = self.request_header()
805
+ request_url = self.config()["componentUrl"]
806
+
807
+ return self.do_request(
808
+ url=request_url,
809
+ method="GET",
810
+ headers=request_header,
811
+ timeout=None,
812
+ failure_message="Failed to retrieve PHT components!",
813
+ )
814
+
815
+ # end method definition
816
+
817
+ def get_components_filtered(
818
+ self, filter_definition: dict | None = None
819
+ ) -> list | None:
820
+ """Get a list of filtered PHT components
821
+
822
+ Args:
823
+ filter_definition (dict): a dictionary of filter conditions.
824
+ Example filters:
825
+ {
826
+ componentName: <String>,
827
+ componentSyncId: <String>,
828
+ componentStatus: ACTIVE | INACTIVE | MAINTENANCE,
829
+ developmentManager: <String>,
830
+ attributeOperator: AND | OR,
831
+ attributes: {
832
+ "<AttributeName>": {
833
+ "compare": CONTAINS | EXISTS | DOES_NOT_EXISTS,
834
+ "values": List<String>
835
+ },
836
+ ...
837
+ },
838
+ includeAttributes: true | false
839
+ }
840
+ Returns:
841
+ list | None: list of matching components.
842
+ """
843
+
844
+ if not filter_definition:
845
+ return self.get_products()
846
+
847
+ request_header = self.request_header()
848
+ request_url = self.config()["masterProductFilteredUrl"]
849
+ request_data = filter_definition
850
+
851
+ return self.do_request(
852
+ url=request_url,
853
+ method="POST",
854
+ headers=request_header,
855
+ data=request_data,
856
+ timeout=None,
857
+ failure_message="Failed to get filtered PHT master products!",
858
+ )
859
+
860
+ # end method definition
861
+
862
+ def get_component(self, sync_id: int) -> dict | None:
863
+ """Get a specific component in PHT.
864
+
865
+ Returns:
866
+ dict | None: PHT component
867
+ """
868
+
869
+ request_header = self.request_header()
870
+ request_url = self.config()["componentUrl"] + "/" + str(sync_id)
871
+
872
+ return self.do_request(
873
+ url=request_url,
874
+ method="GET",
875
+ headers=request_header,
876
+ timeout=None,
877
+ failure_message="Failed to retrieve PHT component with sync ID -> {}!".format(
878
+ sync_id
879
+ ),
880
+ )
881
+
882
+ # end method definition
883
+
884
+ def search_components(self, query: str) -> list | None:
885
+ """Search for specific components in PHT by the component name.
886
+
887
+ Returns:
888
+ str: search term matches any part of the component name
889
+ """
890
+
891
+ request_header = self.request_header()
892
+ request_url = self.config()["componentSearchUrl"] + "?q=" + query
893
+
894
+ return self.do_request(
895
+ url=request_url,
896
+ method="GET",
897
+ headers=request_header,
898
+ timeout=None,
899
+ failure_message="Failed to retrieve PHT components matching -> {}!".format(
900
+ query
901
+ ),
902
+ )
480
903
 
481
904
  # end method definition
482
905