airbyte-cdk 0.62.0__py3-none-any.whl → 0.62.2__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- airbyte_cdk/sources/connector_state_manager.py +5 -3
- airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py +15 -3
- airbyte_cdk/test/mock_http/mocker.py +3 -1
- airbyte_cdk/test/mock_http/response.py +9 -1
- airbyte_cdk/utils/airbyte_secrets_utils.py +8 -2
- {airbyte_cdk-0.62.0.dist-info → airbyte_cdk-0.62.2.dist-info}/METADATA +1 -1
- {airbyte_cdk-0.62.0.dist-info → airbyte_cdk-0.62.2.dist-info}/RECORD +13 -13
- unit_tests/sources/declarative/auth/test_oauth.py +27 -0
- unit_tests/test/mock_http/test_mocker.py +3 -1
- unit_tests/utils/test_secret_utils.py +13 -1
- {airbyte_cdk-0.62.0.dist-info → airbyte_cdk-0.62.2.dist-info}/LICENSE.txt +0 -0
- {airbyte_cdk-0.62.0.dist-info → airbyte_cdk-0.62.2.dist-info}/WHEEL +0 -0
- {airbyte_cdk-0.62.0.dist-info → airbyte_cdk-0.62.2.dist-info}/top_level.txt +0 -0
@@ -38,7 +38,7 @@ class ConnectorStateManager:
|
|
38
38
|
|
39
39
|
def __init__(
|
40
40
|
self,
|
41
|
-
stream_instance_map: Mapping[str, AirbyteStream],
|
41
|
+
stream_instance_map: Mapping[str, Union[Stream, AirbyteStream]],
|
42
42
|
state: Optional[Union[List[AirbyteStateMessage], MutableMapping[str, Any]]] = None,
|
43
43
|
):
|
44
44
|
shared_state, per_stream_states = self._extract_from_state_message(state, stream_instance_map)
|
@@ -107,7 +107,9 @@ class ConnectorStateManager:
|
|
107
107
|
|
108
108
|
@classmethod
|
109
109
|
def _extract_from_state_message(
|
110
|
-
cls,
|
110
|
+
cls,
|
111
|
+
state: Optional[Union[List[AirbyteStateMessage], MutableMapping[str, Any]]],
|
112
|
+
stream_instance_map: Mapping[str, Union[Stream, AirbyteStream]],
|
111
113
|
) -> Tuple[Optional[AirbyteStateBlob], MutableMapping[HashableStreamDescriptor, Optional[AirbyteStateBlob]]]:
|
112
114
|
"""
|
113
115
|
Takes an incoming list of state messages or the legacy state format and extracts state attributes according to type
|
@@ -159,7 +161,7 @@ class ConnectorStateManager:
|
|
159
161
|
|
160
162
|
@staticmethod
|
161
163
|
def _create_descriptor_to_stream_state_mapping(
|
162
|
-
state: MutableMapping[str, Any], stream_to_instance_map: Mapping[str, Stream]
|
164
|
+
state: MutableMapping[str, Any], stream_to_instance_map: Mapping[str, Union[Stream, AirbyteStream]]
|
163
165
|
) -> MutableMapping[HashableStreamDescriptor, Optional[AirbyteStateBlob]]:
|
164
166
|
"""
|
165
167
|
Takes incoming state received in the legacy format and transforms it into a mapping of StreamDescriptor to AirbyteStreamState
|
@@ -14,6 +14,7 @@ from airbyte_cdk.models import FailureType, Level
|
|
14
14
|
from airbyte_cdk.sources.http_logger import format_http_message
|
15
15
|
from airbyte_cdk.sources.message import MessageRepository, NoopMessageRepository
|
16
16
|
from airbyte_cdk.utils import AirbyteTracedException
|
17
|
+
from airbyte_cdk.utils.airbyte_secrets_utils import add_to_secrets
|
17
18
|
from requests.auth import AuthBase
|
18
19
|
|
19
20
|
from ..exceptions import DefaultBackoffException
|
@@ -115,9 +116,20 @@ class AbstractOauth2Authenticator(AuthBase):
|
|
115
116
|
def _get_refresh_access_token_response(self) -> Any:
|
116
117
|
try:
|
117
118
|
response = requests.request(method="POST", url=self.get_token_refresh_endpoint(), data=self.build_refresh_request_body())
|
118
|
-
|
119
|
-
|
120
|
-
|
119
|
+
if response.ok:
|
120
|
+
response_json = response.json()
|
121
|
+
# Add the access token to the list of secrets so it is replaced before logging the response
|
122
|
+
# An argument could be made to remove the prevous access key from the list of secrets, but unmasking values seems like a security incident waiting to happen...
|
123
|
+
access_key = response_json.get(self.get_access_token_name())
|
124
|
+
if not access_key:
|
125
|
+
raise Exception("Token refresh API response was missing access token {self.get_access_token_name()}")
|
126
|
+
add_to_secrets(access_key)
|
127
|
+
self._log_response(response)
|
128
|
+
return response_json
|
129
|
+
else:
|
130
|
+
# log the response even if the request failed for troubleshooting purposes
|
131
|
+
self._log_response(response)
|
132
|
+
response.raise_for_status()
|
121
133
|
except requests.exceptions.RequestException as e:
|
122
134
|
if e.response is not None:
|
123
135
|
if e.response.status_code == 429 or e.response.status_code >= 500:
|
@@ -60,7 +60,9 @@ class HttpMocker(contextlib.ContextDecorator):
|
|
60
60
|
getattr(self._mocker, method)(
|
61
61
|
requests_mock.ANY,
|
62
62
|
additional_matcher=self._matches_wrapper(matcher),
|
63
|
-
response_list=[
|
63
|
+
response_list=[
|
64
|
+
{"text": response.body, "status_code": response.status_code, "headers": response.headers} for response in responses
|
65
|
+
],
|
64
66
|
)
|
65
67
|
|
66
68
|
def get(self, request: HttpRequest, responses: Union[HttpResponse, List[HttpResponse]]) -> None:
|
@@ -1,10 +1,14 @@
|
|
1
1
|
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
2
2
|
|
3
|
+
from types import MappingProxyType
|
4
|
+
from typing import Mapping
|
5
|
+
|
3
6
|
|
4
7
|
class HttpResponse:
|
5
|
-
def __init__(self, body: str, status_code: int = 200):
|
8
|
+
def __init__(self, body: str, status_code: int = 200, headers: Mapping[str, str] = MappingProxyType({})):
|
6
9
|
self._body = body
|
7
10
|
self._status_code = status_code
|
11
|
+
self._headers = headers
|
8
12
|
|
9
13
|
@property
|
10
14
|
def body(self) -> str:
|
@@ -13,3 +17,7 @@ class HttpResponse:
|
|
13
17
|
@property
|
14
18
|
def status_code(self) -> int:
|
15
19
|
return self._status_code
|
20
|
+
|
21
|
+
@property
|
22
|
+
def headers(self) -> Mapping[str, str]:
|
23
|
+
return self._headers
|
@@ -10,7 +10,7 @@ import dpath.util
|
|
10
10
|
def get_secret_paths(spec: Mapping[str, Any]) -> List[List[str]]:
|
11
11
|
paths = []
|
12
12
|
|
13
|
-
def traverse_schema(schema_item: Any, path: List[str]):
|
13
|
+
def traverse_schema(schema_item: Any, path: List[str]) -> None:
|
14
14
|
"""
|
15
15
|
schema_item can be any property or value in the originally input jsonschema, depending on how far down the recursion stack we go
|
16
16
|
path is the path to that schema item in the original input
|
@@ -56,12 +56,18 @@ def get_secrets(connection_specification: Mapping[str, Any], config: Mapping[str
|
|
56
56
|
__SECRETS_FROM_CONFIG: List[str] = []
|
57
57
|
|
58
58
|
|
59
|
-
def update_secrets(secrets: List[str]):
|
59
|
+
def update_secrets(secrets: List[str]) -> None:
|
60
60
|
"""Update the list of secrets to be replaced"""
|
61
61
|
global __SECRETS_FROM_CONFIG
|
62
62
|
__SECRETS_FROM_CONFIG = secrets
|
63
63
|
|
64
64
|
|
65
|
+
def add_to_secrets(secret: str) -> None:
|
66
|
+
"""Add to the list of secrets to be replaced"""
|
67
|
+
global __SECRETS_FROM_CONFIG
|
68
|
+
__SECRETS_FROM_CONFIG.append(secret)
|
69
|
+
|
70
|
+
|
65
71
|
def filter_secrets(string: str) -> str:
|
66
72
|
"""Filter secrets from a string by replacing them with ****"""
|
67
73
|
# TODO this should perform a maximal match for each secret. if "x" and "xk" are both secret values, and this method is called twice on
|
@@ -26,7 +26,7 @@ airbyte_cdk/models/well_known_types.py,sha256=KKfNbow2gdLoC1Z4hcXy_JR8m_acsB2ol7
|
|
26
26
|
airbyte_cdk/sources/__init__.py,sha256=Ov7Uf03KPSZUmMZqZfUAK3tQwsdKjDQUDvTb-H0JyfA,1141
|
27
27
|
airbyte_cdk/sources/abstract_source.py,sha256=GSpNwbwJ0v-KvxWa0u_nWeC0r6G2fZNkpKUhXzf6YlI,14399
|
28
28
|
airbyte_cdk/sources/config.py,sha256=PYsY7y2u3EUwxLiEb96JnuKwH_E8CuxKggsRO2ZPSRc,856
|
29
|
-
airbyte_cdk/sources/connector_state_manager.py,sha256=
|
29
|
+
airbyte_cdk/sources/connector_state_manager.py,sha256=p9iwWbb5uqRbsrHsdZBMXKmyHgLVbsOcV3QQexBFnPE,11052
|
30
30
|
airbyte_cdk/sources/http_config.py,sha256=OBZeuyFilm6NlDlBhFQvHhTWabEvZww6OHDIlZujIS0,730
|
31
31
|
airbyte_cdk/sources/http_logger.py,sha256=v0kkpDtA0GUOgj6_3AayrYaBrSHBqG4t3MGbrtxaNmU,1437
|
32
32
|
airbyte_cdk/sources/source.py,sha256=dk50z8Roc28MJ8FxWe652B-GwItO__bTZqFm7WOtHnw,4412
|
@@ -232,7 +232,7 @@ airbyte_cdk/sources/streams/http/auth/core.py,sha256=_s9wewvvIcOgYjhHGDj_YHApnF5
|
|
232
232
|
airbyte_cdk/sources/streams/http/auth/oauth.py,sha256=zchPWN1utNg02F93f5b4UFI5OXYo8-QhocbsXhLdG4U,4135
|
233
233
|
airbyte_cdk/sources/streams/http/auth/token.py,sha256=oU1ul0LsGsPGN_vOJOKw1xX2y_XWULRxjqXu7Rivcr8,1940
|
234
234
|
airbyte_cdk/sources/streams/http/requests_native_auth/__init__.py,sha256=RN0D3nOX1xLgwEwKWu6pkGy3XqBFzKSNZ8Lf6umU2eY,413
|
235
|
-
airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py,sha256=
|
235
|
+
airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py,sha256=VfI81zpWYMrzhU0nzN7J1lYz-aZCyHzvv-cLqzm8in0,10125
|
236
236
|
airbyte_cdk/sources/streams/http/requests_native_auth/abstract_token.py,sha256=T0hVF2cBXGgIfrCslvTC1uNm9rNbYjENNl2Cb3mXuSY,961
|
237
237
|
airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py,sha256=HrnA76k4i-YOp7ygDwyMg0Hf80gjJaNSU3GbXiTzxMc,13381
|
238
238
|
airbyte_cdk/sources/streams/http/requests_native_auth/token.py,sha256=hDti8DlF_R5YYX95hg9BPogYtG-KUYtOifrFDv_L3Hk,2456
|
@@ -253,12 +253,12 @@ airbyte_cdk/test/entrypoint_wrapper.py,sha256=uTOEYoWkYnbkooPJ4a4gZ-NEll5j1tTCAz
|
|
253
253
|
airbyte_cdk/test/state_builder.py,sha256=SlKadhKVi38ZSKMeceVAxjowxsDDT9vJoG6gU4zDrQE,705
|
254
254
|
airbyte_cdk/test/mock_http/__init__.py,sha256=uil6k-0NbUyDFZXtWw88HaS7r13i43VzA9H7hOHzZx8,322
|
255
255
|
airbyte_cdk/test/mock_http/matcher.py,sha256=J4C8g8PkdKo4OwHWMJGYJIyrLnQpXI5gXWUtyxsxHpM,1240
|
256
|
-
airbyte_cdk/test/mock_http/mocker.py,sha256=
|
256
|
+
airbyte_cdk/test/mock_http/mocker.py,sha256=Sb1Nnf3bVEJfiy5_IliRcyIiIPQL8esSWmm5j9u0E_E,6202
|
257
257
|
airbyte_cdk/test/mock_http/request.py,sha256=dd_i47FOGD5iRlU23daotv2gEn5NOVqTBAqykxdG6-0,3687
|
258
|
-
airbyte_cdk/test/mock_http/response.py,sha256=
|
258
|
+
airbyte_cdk/test/mock_http/response.py,sha256=F09QGG8N3Z8fL_b0rmSKTYoKgku5yZJQCpj0Fwwxu3s,588
|
259
259
|
airbyte_cdk/test/mock_http/response_builder.py,sha256=sc0lU_LN3wjBc4mFFV-3Y5IhYeapRdtB_-EDdHfyArA,7804
|
260
260
|
airbyte_cdk/utils/__init__.py,sha256=qZoNqzEKhIXdN_ZfvXlIGnmiDDjCFy6BVCzzWjUZcuU,294
|
261
|
-
airbyte_cdk/utils/airbyte_secrets_utils.py,sha256=
|
261
|
+
airbyte_cdk/utils/airbyte_secrets_utils.py,sha256=UIu8jzVGstjrlT8iKxvWieiO57rmxuOtx8QphHEzs9Y,3079
|
262
262
|
airbyte_cdk/utils/analytics_message.py,sha256=om0y9U_Y1RNHREi_K8D3mkZgBNfwiOyG71ixOBKZbVs,598
|
263
263
|
airbyte_cdk/utils/constants.py,sha256=QzCi7j5SqpI5I06uRvQ8FC73JVJi7rXaRnR3E_gro5c,108
|
264
264
|
airbyte_cdk/utils/datetime_format_inferrer.py,sha256=gGKDQ3OdY18R5CVFhq4c7zB_E4Cxe6J6SLA29cz3cJM,3954
|
@@ -299,7 +299,7 @@ unit_tests/sources/declarative/test_declarative_stream.py,sha256=Tt3PBIAo7DeQgvX
|
|
299
299
|
unit_tests/sources/declarative/test_manifest_declarative_source.py,sha256=HsDeDgtipkciNOnOeaM1H7eUh1Noq_OVDoFEMugm124,63391
|
300
300
|
unit_tests/sources/declarative/test_yaml_declarative_source.py,sha256=6HhsUFgB7ueN0yOUHWb4gpPYLng5jasxN_plvz3x37g,5097
|
301
301
|
unit_tests/sources/declarative/auth/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
302
|
-
unit_tests/sources/declarative/auth/test_oauth.py,sha256=
|
302
|
+
unit_tests/sources/declarative/auth/test_oauth.py,sha256=NLa7zUhN2pmFsEZFykxpzKVYuw_9luHMkQtFfxP72U4,14039
|
303
303
|
unit_tests/sources/declarative/auth/test_selective_authenticator.py,sha256=RAolWBLCLtibul5wlteQzLAdnUF8vh893qAr9fhoYOk,1315
|
304
304
|
unit_tests/sources/declarative/auth/test_session_token_auth.py,sha256=nKNBx7yGrrvFW9BUwG2xI472Q2sNXl2j1LhWZuUaWmY,6183
|
305
305
|
unit_tests/sources/declarative/auth/test_token_auth.py,sha256=Kg90S04_4WjTUCzwcj62OxnF_TPQcjL_r7-BaeDhxPI,7384
|
@@ -445,7 +445,7 @@ unit_tests/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
445
445
|
unit_tests/test/test_entrypoint_wrapper.py,sha256=m4csYvjO2PzvZZma7K322SBBiL5D33xuv8eUMjitDXE,10839
|
446
446
|
unit_tests/test/mock_http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
447
447
|
unit_tests/test/mock_http/test_matcher.py,sha256=dBndYzqvo3AdHRilLrqruXdPviwi91gWt-ubDsGb-yg,2327
|
448
|
-
unit_tests/test/mock_http/test_mocker.py,sha256=
|
448
|
+
unit_tests/test/mock_http/test_mocker.py,sha256=sOoWutnrPDKB99Y3bkEyr3HFELGivwuklVJ_ii8C-ew,8523
|
449
449
|
unit_tests/test/mock_http/test_request.py,sha256=O9ihefGNiZKpHqsGtis6BjF8VoaOULNR8zOblVqmsL4,7602
|
450
450
|
unit_tests/test/mock_http/test_response_builder.py,sha256=IxAww4gaOxG-9MW8kEZkRzYL2mO6xe4jIsxhi40i2ow,7878
|
451
451
|
unit_tests/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -453,11 +453,11 @@ unit_tests/utils/test_datetime_format_inferrer.py,sha256=1EUW1_afccMDrZM6YZyyPqr
|
|
453
453
|
unit_tests/utils/test_mapping_helpers.py,sha256=hqRppuban9hGKviiNFqp2fNdAz77d1_gjvgg8L7-jy8,1408
|
454
454
|
unit_tests/utils/test_rate_limiting.py,sha256=ESrPBH61EZSeAf-hYWnd49igCgkUWnT21rUHPQaOLQM,873
|
455
455
|
unit_tests/utils/test_schema_inferrer.py,sha256=Z2jHBZ540wnYkylIdV_2xr75Vtwlxuyg4MNPAG-xhpk,7817
|
456
|
-
unit_tests/utils/test_secret_utils.py,sha256=
|
456
|
+
unit_tests/utils/test_secret_utils.py,sha256=CdKK8A2-5XVxbXVtX22FK9dwwMeP5KNqDH6luWRXSNw,5256
|
457
457
|
unit_tests/utils/test_stream_status_utils.py,sha256=Xr8MZ2HWgTVIyMbywDvuYkRaUF4RZLQOT8-JjvcfR24,2970
|
458
458
|
unit_tests/utils/test_traced_exception.py,sha256=bDFP5zMBizFenz6V2WvEZTRCKGB5ijh3DBezjbfoYIs,4198
|
459
|
-
airbyte_cdk-0.62.
|
460
|
-
airbyte_cdk-0.62.
|
461
|
-
airbyte_cdk-0.62.
|
462
|
-
airbyte_cdk-0.62.
|
463
|
-
airbyte_cdk-0.62.
|
459
|
+
airbyte_cdk-0.62.2.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
|
460
|
+
airbyte_cdk-0.62.2.dist-info/METADATA,sha256=mg5FUvzFvSF_W3YQZY6V6fUcSoUy4C03oFt2hF6w0FI,11073
|
461
|
+
airbyte_cdk-0.62.2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
462
|
+
airbyte_cdk-0.62.2.dist-info/top_level.txt,sha256=edvsDKTnE6sD2wfCUaeTfKf5gQIL6CPVMwVL2sWZzqo,51
|
463
|
+
airbyte_cdk-0.62.2.dist-info/RECORD,,
|
@@ -10,6 +10,7 @@ import pendulum
|
|
10
10
|
import pytest
|
11
11
|
import requests
|
12
12
|
from airbyte_cdk.sources.declarative.auth import DeclarativeOauth2Authenticator
|
13
|
+
from airbyte_cdk.utils.airbyte_secrets_utils import filter_secrets
|
13
14
|
from requests import Response
|
14
15
|
|
15
16
|
LOGGER = logging.getLogger(__name__)
|
@@ -165,6 +166,32 @@ class TestOauth2Authenticator:
|
|
165
166
|
|
166
167
|
assert ("access_token", 1000) == token
|
167
168
|
|
169
|
+
filtered = filter_secrets("access_token")
|
170
|
+
assert filtered == "****"
|
171
|
+
|
172
|
+
def test_refresh_access_token_missing_access_token(self, mocker):
|
173
|
+
oauth = DeclarativeOauth2Authenticator(
|
174
|
+
token_refresh_endpoint="{{ config['refresh_endpoint'] }}",
|
175
|
+
client_id="{{ config['client_id'] }}",
|
176
|
+
client_secret="{{ config['client_secret'] }}",
|
177
|
+
refresh_token="{{ config['refresh_token'] }}",
|
178
|
+
config=config,
|
179
|
+
scopes=["scope1", "scope2"],
|
180
|
+
token_expiry_date="{{ config['token_expiry_date'] }}",
|
181
|
+
refresh_request_body={
|
182
|
+
"custom_field": "{{ config['custom_field'] }}",
|
183
|
+
"another_field": "{{ config['another_field'] }}",
|
184
|
+
"scopes": ["no_override"],
|
185
|
+
},
|
186
|
+
parameters={},
|
187
|
+
)
|
188
|
+
|
189
|
+
resp.status_code = 200
|
190
|
+
mocker.patch.object(resp, "json", return_value={"expires_in": 1000})
|
191
|
+
mocker.patch.object(requests, "request", side_effect=mock_request, autospec=True)
|
192
|
+
with pytest.raises(Exception):
|
193
|
+
oauth.refresh_access_token()
|
194
|
+
|
168
195
|
@pytest.mark.parametrize(
|
169
196
|
"timestamp, expected_date",
|
170
197
|
[
|
@@ -15,6 +15,7 @@ _ANOTHER_RESPONSE_BODY = "another body"
|
|
15
15
|
_A_RESPONSE = HttpResponse("any response")
|
16
16
|
_SOME_QUERY_PARAMS = {"q1": "query value"}
|
17
17
|
_SOME_HEADERS = {"h1": "header value"}
|
18
|
+
_OTHER_HEADERS = {"h2": "another header value"}
|
18
19
|
_SOME_REQUEST_BODY_MAPPING = {"first_field": "first_value", "second_field": 2}
|
19
20
|
_SOME_REQUEST_BODY_STR = "some_request_body"
|
20
21
|
|
@@ -24,13 +25,14 @@ class HttpMockerTest(TestCase):
|
|
24
25
|
def test_given_get_request_match_when_decorate_then_return_response(self, http_mocker):
|
25
26
|
http_mocker.get(
|
26
27
|
HttpRequest(_A_URL, _SOME_QUERY_PARAMS, _SOME_HEADERS),
|
27
|
-
HttpResponse(_A_RESPONSE_BODY, 474),
|
28
|
+
HttpResponse(_A_RESPONSE_BODY, 474, _OTHER_HEADERS),
|
28
29
|
)
|
29
30
|
|
30
31
|
response = requests.get(_A_URL, params=_SOME_QUERY_PARAMS, headers=_SOME_HEADERS)
|
31
32
|
|
32
33
|
assert response.text == _A_RESPONSE_BODY
|
33
34
|
assert response.status_code == 474
|
35
|
+
assert response.headers == _OTHER_HEADERS
|
34
36
|
|
35
37
|
@HttpMocker()
|
36
38
|
def test_given_loose_headers_matching_when_decorate_then_match(self, http_mocker):
|
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
|
5
5
|
import pytest
|
6
|
-
from airbyte_cdk.utils.airbyte_secrets_utils import filter_secrets, get_secret_paths, get_secrets, update_secrets
|
6
|
+
from airbyte_cdk.utils.airbyte_secrets_utils import add_to_secrets, filter_secrets, get_secret_paths, get_secrets, update_secrets
|
7
7
|
|
8
8
|
SECRET_STRING_KEY = "secret_key1"
|
9
9
|
SECRET_STRING_VALUE = "secret_value"
|
@@ -121,3 +121,15 @@ def test_secret_filtering():
|
|
121
121
|
update_secrets([SECRET_STRING_VALUE, SECRET_STRING_2_VALUE])
|
122
122
|
filtered = filter_secrets(sensitive_str)
|
123
123
|
assert filtered == f"**** {NOT_SECRET_VALUE} **** ****"
|
124
|
+
|
125
|
+
|
126
|
+
def test_secrets_added_are_filtered():
|
127
|
+
ADDED_SECRET = "only_a_secret_if_added"
|
128
|
+
sensitive_str = f"{ADDED_SECRET} {NOT_SECRET_VALUE}"
|
129
|
+
|
130
|
+
filtered = filter_secrets(sensitive_str)
|
131
|
+
assert filtered == sensitive_str
|
132
|
+
|
133
|
+
add_to_secrets(ADDED_SECRET)
|
134
|
+
filtered = filter_secrets(sensitive_str)
|
135
|
+
assert filtered == f"**** {NOT_SECRET_VALUE}"
|
File without changes
|
File without changes
|
File without changes
|