4
4
# license information.
5
5
# --------------------------------------------------------------------------
6
6
from typing import TYPE_CHECKING
7
+ from urllib .error import HTTPError
7
8
import requests
8
9
9
10
from .config import PROXY_URL
10
11
from .helpers import get_recording_id , is_live , is_live_and_not_recording
11
12
12
13
if TYPE_CHECKING :
13
- from typing import Any , Optional
14
+ from typing import Any , Iterable , Optional
15
+
16
+
17
+ # This file contains methods for adjusting many aspects of test proxy behavior:
18
+ #
19
+ # - Sanitizers: record stand-in values to hide secrets and/or enable playback when behavior is inconsistent
20
+ # - Transforms: extend test proxy functionality by changing how recordings are processed in playback mode
21
+ # - Matchers: modify the conditions that are used to match request and response content with recorded values
22
+ # - Recording options: further customization for advanced scenarios, such as providing certificates to the transport
23
+ #
24
+ # Methods for a given category are grouped together under a header containing more details.
14
25
15
26
16
27
def set_default_function_settings () -> None :
@@ -34,7 +45,7 @@ def set_default_session_settings() -> None:
34
45
"""Resets sanitizers, matchers, and transforms for the test proxy to their default settings, for all tests.
35
46
36
47
This will reset any setting customizations for an entire test session. To reset setting customizations for a single
37
- test -- which is recommended -- use `set_default_session_settings ` instead.
48
+ test -- which is recommended -- use `set_default_function_settings ` instead.
38
49
"""
39
50
40
51
_send_reset_request ({})
@@ -393,21 +404,110 @@ def add_storage_request_id_transform() -> None:
393
404
_send_transform_request ("StorageRequestIdTransform" , {})
394
405
395
406
407
+ # ----------RECORDING OPTIONS----------
408
+ #
409
+ # Recording options enable customization beyond what is offered by sanitizers, matchers, and transforms. These are
410
+ # intended for advanced scenarios and are generally not applicable.
411
+ #
412
+ # -------------------------------------
413
+
414
+
415
+ def set_function_recording_options (** kwargs : "Any" ) -> None :
416
+ """Sets custom recording options for the current test only.
417
+
418
+ This must be called during test case execution, rather than at a session, module, or class level. To set recording
419
+ options for all tests, use `set_session_recording_options` instead.
420
+
421
+ :keyword bool handle_redirects: The test proxy does not perform transparent follow directs by default. That means
422
+ that if the initial request sent through the test proxy results in a 3XX redirect status, the test proxy will
423
+ not follow. It will return that redirect response to the client and allow it to handle the redirect. Setting
424
+ `handle_redirects` to True will set the proxy to instead handle redirects itself.
425
+ :keyword str context_directory: This changes the "root" path that the test proxy uses when loading a recording.
426
+ :keyword certificates: A list of `PemCertificate`s. Any number of certificates is allowed.
427
+ :type certificates: Iterable[PemCertificate]
428
+ :keyword str tls_certificate: The public key portion of a TLS certificate, as a string. This is used specifically so
429
+ that an SSL connection presenting a non-standard certificate can still be validated.
430
+ """
431
+
432
+ x_recording_id = get_recording_id ()
433
+ request_args = _get_recording_option_args (** kwargs )
434
+ _send_recording_options_request (request_args , {"x-recording-id" : x_recording_id })
435
+
436
+
437
+ def set_session_recording_options (** kwargs : "Any" ) -> None :
438
+ """Sets custom recording options for all tests.
439
+
440
+ This will set the specified recording options for an entire test session. To set recording options for a single test
441
+ -- which is recommended -- use `set_function_recording_options` instead.
442
+
443
+ :keyword bool handle_redirects: The test proxy does not perform transparent follow directs by default. That means
444
+ that if the initial request sent through the test proxy results in a 3XX redirect status, the test proxy will
445
+ not follow. It will return that redirect response to the client and allow it to handle the redirect. Setting
446
+ `handle_redirects` to True will set the proxy to instead handle redirects itself.
447
+ :keyword str context_directory: This changes the "root" path that the test proxy uses when loading a recording.
448
+ :keyword certificates: A list of `PemCertificate`s. Any number of certificates is allowed.
449
+ :type certificates: Iterable[PemCertificate]
450
+ :keyword str tls_certificate: The public key portion of a TLS certificate, as a string. This is used specifically so
451
+ that an SSL connection presenting a non-standard certificate can still be validated.
452
+ """
453
+
454
+ request_args = _get_recording_option_args (** kwargs )
455
+ _send_recording_options_request (request_args )
456
+
457
+
458
+ class PemCertificate :
459
+ """Represents a PEM certificate that can be sent to and used by the test proxy.
460
+
461
+ :param str data: The content of the certificate, as a string.
462
+ :param str key: The certificate key, as a string.
463
+ """
464
+
465
+ def __init__ (self , data : str , key : str ) -> None :
466
+ self .data = data
467
+ self .key = key
468
+
469
+
396
470
# ----------HELPERS----------
397
471
398
472
473
+ def _get_recording_option_args (** kwargs : "Any" ) -> dict :
474
+ """Returns a dictionary of recording option request arguments, formatted for test proxy consumption."""
475
+
476
+ certificates = kwargs .pop ("certificates" , None )
477
+ tls_certificate = kwargs .pop ("tls_certificate" , None )
478
+ request_args = _get_request_args (** kwargs )
479
+
480
+ if certificates or tls_certificate :
481
+ transport = {}
482
+
483
+ if certificates :
484
+ cert_pairs = [{"PemValue" : cert .data , "PemKey" : cert .key } for cert in certificates ]
485
+ transport ["Certificates" ] = cert_pairs
486
+
487
+ if tls_certificate :
488
+ transport ["TLSValidationCert" ] = tls_certificate
489
+
490
+ request_args ["Transport" ] = transport
491
+
492
+ return request_args
493
+
494
+
399
495
def _get_request_args (** kwargs : "Any" ) -> dict :
400
- """Returns a dictionary of sanitizer constructor headers """
496
+ """Returns a dictionary of request arguments, formatted for test proxy consumption. """
401
497
402
498
request_args = {}
403
499
if "compare_bodies" in kwargs :
404
500
request_args ["compareBodies" ] = kwargs .get ("compare_bodies" )
405
501
if "condition" in kwargs :
406
502
request_args ["condition" ] = kwargs .get ("condition" )
503
+ if "context_directory" in kwargs :
504
+ request_args ["ContextDirectory" ] = kwargs .get ("context_directory" )
407
505
if "excluded_headers" in kwargs :
408
506
request_args ["excludedHeaders" ] = kwargs .get ("excluded_headers" )
409
507
if "group_for_replace" in kwargs :
410
508
request_args ["groupForReplace" ] = kwargs .get ("group_for_replace" )
509
+ if "handle_redirects" in kwargs :
510
+ request_args ["HandleRedirects" ] = kwargs .get ("handle_redirects" )
411
511
if "headers" in kwargs :
412
512
request_args ["headersForRemoval" ] = kwargs .get ("headers" )
413
513
if "ignored_headers" in kwargs :
@@ -440,6 +540,8 @@ def _send_matcher_request(matcher: str, headers: dict, parameters: "Optional[dic
440
540
441
541
:param str matcher: The name of the matcher to set.
442
542
:param dict headers: Any matcher headers, as a dictionary.
543
+ :param parameters: Any matcher constructor parameters, as a dictionary. Defaults to None.
544
+ :type parameters: Optional[dict]
443
545
"""
444
546
445
547
if is_live ():
@@ -455,6 +557,28 @@ def _send_matcher_request(matcher: str, headers: dict, parameters: "Optional[dic
455
557
response .raise_for_status ()
456
558
457
559
560
+ def _send_recording_options_request (parameters : dict , headers : "Optional[dict]" = None ) -> None :
561
+ """Sends a POST request to the test proxy endpoint to set the specified recording options.
562
+
563
+ If live tests are being run with recording turned off via the AZURE_SKIP_LIVE_RECORDING environment variable, no
564
+ request will be sent.
565
+
566
+ :param dict parameters: The recording options, as a dictionary.
567
+ :param headers: Any recording option request headers, as a dictionary. Defaults to None.
568
+ :type headers: Optional[dict]
569
+ """
570
+
571
+ if is_live_and_not_recording ():
572
+ return
573
+
574
+ response = requests .post (
575
+ f"{ PROXY_URL } /Admin/SetRecordingOptions" ,
576
+ headers = headers ,
577
+ json = parameters
578
+ )
579
+ response .raise_for_status ()
580
+
581
+
458
582
def _send_reset_request (headers : dict ) -> None :
459
583
"""Sends a POST request to the test proxy endpoint to reset setting customizations.
460
584
0 commit comments