stoobly-agent 0.34.0__py3-none-any.whl → 0.34.1__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.
- stoobly_agent/__init__.py +1 -1
- stoobly_agent/app/cli/dev_tools_cli.py +1 -0
- stoobly_agent/app/cli/endpoint_cli.py +2 -2
- stoobly_agent/app/cli/helpers/endpoints_import_service.py +14 -9
- stoobly_agent/app/cli/helpers/replay_facade.py +4 -0
- stoobly_agent/app/cli/request_cli.py +2 -0
- stoobly_agent/app/cli/scenario_cli.py +2 -0
- stoobly_agent/app/cli/types/test.py +2 -0
- stoobly_agent/app/models/factories/resource/local_db/helpers/request_builder.py +1 -1
- stoobly_agent/app/proxy/handle_mock_service.py +15 -3
- stoobly_agent/app/proxy/intercept_settings.py +39 -0
- stoobly_agent/app/proxy/mock/eval_fixtures_service.py +61 -0
- stoobly_agent/app/proxy/mock/types/__init__.py +10 -0
- stoobly_agent/app/proxy/replay/replay_request_service.py +11 -3
- stoobly_agent/app/proxy/run.py +6 -0
- stoobly_agent/app/proxy/test/context.py +12 -0
- stoobly_agent/app/proxy/test/context_abc.py +15 -0
- stoobly_agent/cli.py +13 -2
- stoobly_agent/config/constants/custom_headers.py +2 -0
- stoobly_agent/config/constants/env_vars.py +2 -0
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
- stoobly_agent/test/app/proxy/mock/eval_fixtures_service_test.py +95 -0
- {stoobly_agent-0.34.0.dist-info → stoobly_agent-0.34.1.dist-info}/METADATA +1 -1
- {stoobly_agent-0.34.0.dist-info → stoobly_agent-0.34.1.dist-info}/RECORD +27 -24
- {stoobly_agent-0.34.0.dist-info → stoobly_agent-0.34.1.dist-info}/LICENSE +0 -0
- {stoobly_agent-0.34.0.dist-info → stoobly_agent-0.34.1.dist-info}/WHEEL +0 -0
- {stoobly_agent-0.34.0.dist-info → stoobly_agent-0.34.1.dist-info}/entry_points.txt +0 -0
stoobly_agent/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
COMMAND = 'stoobly-agent'
|
2
|
-
VERSION = '0.34.
|
2
|
+
VERSION = '0.34.1'
|
@@ -5,6 +5,7 @@ import sys
|
|
5
5
|
from stoobly_agent import VERSION
|
6
6
|
from stoobly_agent.app.proxy.replay.body_parser_service import decode_response, is_traversable
|
7
7
|
from stoobly_agent.app.models.adapters.raw_http_response_adapter import RawHttpResponseAdapter
|
8
|
+
from stoobly_agent.lib.api.response_param_names_resource import ResponseParamNamesResource
|
8
9
|
from stoobly_agent.lib.orm.migrate_service import migrate as database_migrate, rollback as database_rollback
|
9
10
|
from stoobly_agent.lib.orm.replayed_response import ReplayedResponse
|
10
11
|
from stoobly_agent.lib.orm.request import Request
|
@@ -47,7 +47,7 @@ def apply(**kwargs):
|
|
47
47
|
context.with_lifecycle_hooks_path(kwargs.get('lifecycle_hooks_path'))
|
48
48
|
context.with_source(kwargs.get('source_path'), kwargs.get('source_format'))
|
49
49
|
|
50
|
-
endpoint_handler = lambda endpoint: print(f"{bcolors.OKBLUE}Applying Endpoint
|
50
|
+
endpoint_handler = lambda endpoint: print(f"{bcolors.OKBLUE}Applying Endpoint {endpoint['method']} {endpoint['path']}{bcolors.ENDC}")
|
51
51
|
context.with_endpoint_handler(endpoint_handler)
|
52
52
|
|
53
53
|
request_handler = lambda request, endpoint: print(request.url)
|
@@ -72,7 +72,7 @@ def import_endpoint(**kwargs):
|
|
72
72
|
|
73
73
|
context.with_source(kwargs.get('source_path'), kwargs.get('source_format'))
|
74
74
|
|
75
|
-
endpoint_handler = lambda endpoint: print(f"{bcolors.OKBLUE}Importing Endpoint
|
75
|
+
endpoint_handler = lambda endpoint: print(f"{bcolors.OKBLUE}Importing Endpoint {endpoint['method']} {endpoint['path']}{bcolors.ENDC}")
|
76
76
|
context.with_endpoint_handler(endpoint_handler)
|
77
77
|
|
78
78
|
facade = EndpointFacade(settings)
|
@@ -24,7 +24,7 @@ def import_endpoints(context: EndpointsImportContext):
|
|
24
24
|
try:
|
25
25
|
res = import_endpoint(context.project_id, context.resources['endpoint'], endpoint)
|
26
26
|
except requests.HTTPError as e:
|
27
|
-
error_handler(
|
27
|
+
error_handler(e, f"Failed to import endpoint: {endpoint['method']} {endpoint['path']}")
|
28
28
|
return
|
29
29
|
|
30
30
|
if res.status_code == 409: # Endpoint already created
|
@@ -34,11 +34,12 @@ def import_endpoints(context: EndpointsImportContext):
|
|
34
34
|
try:
|
35
35
|
res = import_component_names(context.project_id, endpoint_id, endpoint, context.resources)
|
36
36
|
except requests.HTTPError as e:
|
37
|
-
error_handler(
|
37
|
+
error_handler(e, f"Failed to import endpoint: {endpoint['method']} {endpoint['path']}")
|
38
|
+
|
38
39
|
try:
|
39
40
|
cleanup_endpoint(context.project_id, endpoint_id, context.resources['endpoint'])
|
40
41
|
except requests.HTTPError as e:
|
41
|
-
|
42
|
+
error_handler(e, f"Failed to delete endpoint: {endpoint['method']} {endpoint['path']}, id {endpoint_id}")
|
42
43
|
return
|
43
44
|
|
44
45
|
def import_endpoint(project_id: int, endpoint_resource: EndpointsResource, endpoint: EndpointShowResponse):
|
@@ -133,15 +134,19 @@ def import_component_names(project_id: int, endpoint_id: int, endpoint: Endpoint
|
|
133
134
|
process_import(component_name, project_id, endpoint_id, resource, component)
|
134
135
|
|
135
136
|
def cleanup_endpoint(project_id: int, endpoint_id: int, resource: EndpointsResource):
|
136
|
-
print(f"{bcolors.
|
137
|
+
print(f"{bcolors.OKBLUE}Cleaning up partial import...{bcolors.ENDC}")
|
137
138
|
res: requests.Response = resource.destroy(endpoint_id, **{
|
138
139
|
'project_id': project_id
|
139
140
|
})
|
140
141
|
|
141
142
|
res.raise_for_status()
|
142
143
|
|
143
|
-
def error_handler(
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
144
|
+
def error_handler(error: requests.HTTPError, message = ''):
|
145
|
+
if message:
|
146
|
+
print(
|
147
|
+
f"{bcolors.FAIL}{message}{bcolors.ENDC}",
|
148
|
+
file=sys.stderr
|
149
|
+
)
|
150
|
+
|
151
|
+
print(error.response.text, file=sys.stderr)
|
152
|
+
print(f"Error {error.response.status_code} {error.response.reason}", file=sys.stderr)
|
@@ -17,7 +17,9 @@ class ReplayCliOptions(TypedDict):
|
|
17
17
|
lifecycle_hooks_path: str
|
18
18
|
on_response: Callable
|
19
19
|
project_key: str
|
20
|
+
public_directory_path: str
|
20
21
|
record: bool
|
22
|
+
response_fixtures_path: str
|
21
23
|
scenario_key: str
|
22
24
|
scheme: str
|
23
25
|
trace: Trace
|
@@ -57,7 +59,9 @@ class ReplayFacade():
|
|
57
59
|
'host': cli_options.get('host'),
|
58
60
|
'lifecycle_hooks_path': cli_options.get('lifecycle_hooks_path'),
|
59
61
|
'overwrite': cli_options.get('overwrite'),
|
62
|
+
'public_directory_path': cli_options.get('public_directory_path'),
|
60
63
|
'request_origin': request_origin.CLI,
|
64
|
+
'response_fixtures_path': cli_options.get('response_fixtures_path'),
|
61
65
|
'save': cli_options.get('save'),
|
62
66
|
'scheme': cli_options.get('scheme'),
|
63
67
|
'trace_context': trace_context,
|
@@ -134,8 +134,10 @@ if not is_remote:
|
|
134
134
|
Configure which tests to print. Defaults to {test_output_level.PASSED}.
|
135
135
|
'''
|
136
136
|
)
|
137
|
+
@click.option('--public-directory-path', help='Path to public files. Used for mocking requests.')
|
137
138
|
@ConditionalDecorator(lambda f: click.option('--remote-project-key', help='Use remote project for endpoint definitions.')(f), is_remote and is_local)
|
138
139
|
@ConditionalDecorator(lambda f: click.option('--report-key', help='Save to report.')(f), is_remote)
|
140
|
+
@click.option('--response-fixtures-path', help='Path to response fixtures yaml. Used for mocking requests.')
|
139
141
|
@ConditionalDecorator(lambda f: click.option('--save', is_flag=True, default=False, help='Saves test results.')(f), is_remote)
|
140
142
|
@click.option('--scheme', type=click.Choice(['http', 'https']), help='Rewrite request scheme.')
|
141
143
|
@click.option('--strategy', default=test_strategy.DIFF, type=click.Choice([test_strategy.CONTRACT, test_strategy.CUSTOM, test_strategy.DIFF, test_strategy.FUZZY]), help='How to test responses.')
|
@@ -158,8 +158,10 @@ if is_local:
|
|
158
158
|
Configure which tests to print. Defaults to {test_output_level.PASSED}.
|
159
159
|
'''
|
160
160
|
)
|
161
|
+
@click.option('--public-directory-path', help='Path to public files. Used for mocking requests.')
|
161
162
|
@ConditionalDecorator(lambda f: click.option('--remote-project-key', help='Use remote project for endpoint definitions.')(f), is_remote)
|
162
163
|
@ConditionalDecorator(lambda f: click.option('--report-key', help='Save results to report.')(f), is_remote)
|
164
|
+
@click.option('--response-fixtures-path', help='Path to response fixtures yaml. Used for mocking requests.')
|
163
165
|
@ConditionalDecorator(lambda f: click.option('--save', is_flag=True, default=False, help='Save results.')(f), is_remote)
|
164
166
|
@click.option('--scheme', help='Rewrite request scheme.')
|
165
167
|
@click.option(
|
@@ -15,8 +15,10 @@ class TestOptions(TypedDict):
|
|
15
15
|
lifecycle_hooks_path: str
|
16
16
|
log_level: logger.LogLevel
|
17
17
|
output_level: test_output_level.TestOutputLevel
|
18
|
+
public_directory_path: str
|
18
19
|
remote_project_key: str
|
19
20
|
report_key: str
|
21
|
+
response_fixtures_path: str
|
20
22
|
save: str
|
21
23
|
scheme: str
|
22
24
|
strategy: test_strategy.TestStrategy
|
@@ -14,6 +14,7 @@ from stoobly_agent.lib.logger import Logger
|
|
14
14
|
|
15
15
|
from .constants import custom_response_codes
|
16
16
|
from .mock.context import MockContext
|
17
|
+
from .mock.eval_fixtures_service import eval_fixtures
|
17
18
|
from .mock.eval_request_service import inject_eval_request
|
18
19
|
from .utils.allowed_request_service import get_active_mode_policy
|
19
20
|
from .utils.request_handler import reverse_proxy
|
@@ -65,14 +66,14 @@ def handle_request_mock_generic(context: MockContext, **options: MockOptions):
|
|
65
66
|
if handle_failure:
|
66
67
|
res = handle_failure(context)
|
67
68
|
elif policy == mock_policy.ALL:
|
68
|
-
res = eval_request_with_retry(
|
69
|
+
res = eval_request_with_retry(context, eval_request, **options)
|
69
70
|
|
70
71
|
context.with_response(res)
|
71
72
|
|
72
73
|
if handle_success:
|
73
74
|
res = handle_success(context) or res
|
74
75
|
elif policy == mock_policy.FOUND:
|
75
|
-
res = eval_request_with_retry(
|
76
|
+
res = eval_request_with_retry(context, eval_request, **options)
|
76
77
|
|
77
78
|
context.with_response(res)
|
78
79
|
|
@@ -93,7 +94,8 @@ def handle_request_mock_generic(context: MockContext, **options: MockOptions):
|
|
93
94
|
|
94
95
|
return pass_on(context.flow, res)
|
95
96
|
|
96
|
-
def eval_request_with_retry(
|
97
|
+
def eval_request_with_retry(context: MockContext, eval_request, **options: MockOptions):
|
98
|
+
request = context.flow.request
|
97
99
|
infer = bool(options.get('infer'))
|
98
100
|
ignored_components = options['ignored_components'] if 'ignored_components' in options else []
|
99
101
|
|
@@ -103,6 +105,16 @@ def eval_request_with_retry(eval_request, request, **options: MockOptions):
|
|
103
105
|
ignored_components.append(res.content)
|
104
106
|
res = eval_request(request, ignored_components, infer=infer, retry=1)
|
105
107
|
|
108
|
+
if res.status_code == custom_response_codes.NOT_FOUND:
|
109
|
+
intercept_settings = context.intercept_settings
|
110
|
+
fixture = eval_fixtures(
|
111
|
+
request,
|
112
|
+
public_directory_path=intercept_settings.public_directory_path,
|
113
|
+
response_fixtures=intercept_settings.response_fixtures
|
114
|
+
)
|
115
|
+
if fixture:
|
116
|
+
res = fixture
|
117
|
+
|
106
118
|
return res
|
107
119
|
|
108
120
|
def handle_request_mock(context: MockContext):
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
import pdb
|
3
|
+
import yaml
|
3
4
|
|
4
5
|
from runpy import run_path
|
5
6
|
from typing import List, Union
|
@@ -37,6 +38,9 @@ class InterceptSettings:
|
|
37
38
|
self.__lifecycle_hooks = None
|
38
39
|
self.__initialize_lifecycle_hooks()
|
39
40
|
|
41
|
+
self.__response_fixtures = None
|
42
|
+
self.__initialize_response_fixtures()
|
43
|
+
|
40
44
|
@property
|
41
45
|
def settings(self):
|
42
46
|
return self.__settings
|
@@ -102,6 +106,14 @@ class InterceptSettings:
|
|
102
106
|
except InvalidProjectKey:
|
103
107
|
pass
|
104
108
|
|
109
|
+
@property
|
110
|
+
def public_directory_path(self):
|
111
|
+
if self.__headers and custom_headers.PUBLIC_DIRECTORY_PATH in self.__headers:
|
112
|
+
return self.__headers[custom_headers.PUBLIC_DIRECTORY_PATH]
|
113
|
+
|
114
|
+
if os.environ.get(env_vars.AGENT_PUBLIC_DIRECTORY_PATH):
|
115
|
+
return os.environ[env_vars.AGENT_PUBLIC_DIRECTORY_PATH]
|
116
|
+
|
105
117
|
@property
|
106
118
|
def remote_project_key(self):
|
107
119
|
# When not local project, don't return set remote project_key
|
@@ -114,6 +126,18 @@ class InterceptSettings:
|
|
114
126
|
|
115
127
|
return self.__settings.remote.project_key
|
116
128
|
|
129
|
+
@property
|
130
|
+
def response_fixtures_path(self):
|
131
|
+
if self.__headers and custom_headers.RESPONSE_FIXTURES_PATH in self.__headers:
|
132
|
+
return self.__headers[custom_headers.RESPONSE_FIXTURES_PATH]
|
133
|
+
|
134
|
+
if os.environ.get(env_vars.AGENT_RESPONSE_FIXTURES_PATH):
|
135
|
+
return os.environ[env_vars.AGENT_RESPONSE_FIXTURES_PATH]
|
136
|
+
|
137
|
+
@property
|
138
|
+
def response_fixtures(self):
|
139
|
+
return self.__response_fixtures or {}
|
140
|
+
|
117
141
|
@property
|
118
142
|
def parsed_remote_project_key(self):
|
119
143
|
try:
|
@@ -288,6 +312,21 @@ class InterceptSettings:
|
|
288
312
|
except Exception as e:
|
289
313
|
return Logger.instance().error(e)
|
290
314
|
|
315
|
+
def __initialize_response_fixtures(self):
|
316
|
+
fixtures_path = self.response_fixtures_path
|
317
|
+
|
318
|
+
if not fixtures_path:
|
319
|
+
return
|
320
|
+
|
321
|
+
if not os.path.exists(fixtures_path):
|
322
|
+
return Logger.instance().error(f"Response fixtures {fixtures_path} does not exist")
|
323
|
+
|
324
|
+
with open(fixtures_path, 'r') as stream:
|
325
|
+
try:
|
326
|
+
self.__response_fixtures = yaml.safe_load(stream)
|
327
|
+
except yaml.YAMLError as exc:
|
328
|
+
Logger.instance().error(exc)
|
329
|
+
|
291
330
|
def __policy(self, mode):
|
292
331
|
if mode == intercept_mode.MOCK:
|
293
332
|
if self.__headers and custom_headers.MOCK_POLICY in self.__headers:
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import os
|
2
|
+
import pdb
|
3
|
+
import re
|
4
|
+
|
5
|
+
from io import BytesIO
|
6
|
+
from mitmproxy.http import Request as MitmproxyRequest
|
7
|
+
from requests import Response
|
8
|
+
from typing import Union
|
9
|
+
|
10
|
+
from stoobly_agent.lib.logger import bcolors, Logger
|
11
|
+
|
12
|
+
from .types import Fixtures
|
13
|
+
|
14
|
+
class Options():
|
15
|
+
public_directory_path: str
|
16
|
+
response_fixtures: Fixtures
|
17
|
+
|
18
|
+
def eval_fixtures(request: MitmproxyRequest, **options: Options) -> Union[Response, None]:
|
19
|
+
response_fixtures = options.get('response_fixtures')
|
20
|
+
|
21
|
+
fixture_path = __eval_response_fixtures(request, response_fixtures)
|
22
|
+
if not fixture_path:
|
23
|
+
public_directory_path = options.get('public_directory_path')
|
24
|
+
|
25
|
+
if public_directory_path and os.path.exists(public_directory_path):
|
26
|
+
static_file_path = os.path.join(public_directory_path, request.path.lstrip('/'))
|
27
|
+
|
28
|
+
if os.path.exists(static_file_path):
|
29
|
+
fixture_path = static_file_path
|
30
|
+
|
31
|
+
if not fixture_path:
|
32
|
+
return
|
33
|
+
|
34
|
+
with open(fixture_path, 'rb') as fp:
|
35
|
+
response = Response()
|
36
|
+
|
37
|
+
response.status_code = 200
|
38
|
+
response.raw = BytesIO(fp.read())
|
39
|
+
|
40
|
+
Logger.instance().debug(f"{bcolors.OKBLUE}Resolved fixture {fixture_path}{bcolors.ENDC}")
|
41
|
+
|
42
|
+
return response
|
43
|
+
|
44
|
+
def __eval_response_fixtures(request: MitmproxyRequest, response_fixtures: Fixtures):
|
45
|
+
if not response_fixtures:
|
46
|
+
return
|
47
|
+
|
48
|
+
method = request.method
|
49
|
+
routes = response_fixtures.get(method)
|
50
|
+
|
51
|
+
if not routes:
|
52
|
+
return
|
53
|
+
|
54
|
+
for path_pattern in routes:
|
55
|
+
if not re.match(path_pattern, request.path):
|
56
|
+
continue
|
57
|
+
|
58
|
+
path = routes[path_pattern].get('path')
|
59
|
+
|
60
|
+
if path and os.path.exists(path):
|
61
|
+
return path
|
@@ -27,9 +27,11 @@ class ReplayRequestOptions(TypedDict):
|
|
27
27
|
after_replay: Union[Callable[[ReplayContext], Union[requests.Response, None]], None]
|
28
28
|
project_key: Union[str, None]
|
29
29
|
proxies: dict
|
30
|
+
public_directory_path: str
|
30
31
|
remote_project_key: str
|
31
32
|
report_key: Union[str, None]
|
32
33
|
request_origin: Union[request_origin.CLI, None]
|
34
|
+
response_fixtures_path: str
|
33
35
|
scenario_key: Union[str, None]
|
34
36
|
scheme: str
|
35
37
|
test_filter: test_filter.TestFilter
|
@@ -58,7 +60,7 @@ def replay(context: ReplayContext, options: ReplayRequestOptions) -> requests.Re
|
|
58
60
|
headers[custom_headers.ALIAS_RESOLVE_STRATEGY] = options['alias_resolve_strategy']
|
59
61
|
|
60
62
|
if options.get('lifecycle_hooks_path'):
|
61
|
-
|
63
|
+
__handle_path_header(custom_headers.LIFECYCLE_HOOKS_PATH, options['lifecycle_hooks_path'], headers)
|
62
64
|
|
63
65
|
if options.get('mode'):
|
64
66
|
__handle_mode_option(options['mode'], request, headers)
|
@@ -66,6 +68,9 @@ def replay(context: ReplayContext, options: ReplayRequestOptions) -> requests.Re
|
|
66
68
|
if options.get('project_key'):
|
67
69
|
headers[custom_headers.PROJECT_KEY] = options['project_key']
|
68
70
|
|
71
|
+
if options.get('public_directory_path'):
|
72
|
+
__handle_path_header(custom_headers.PUBLIC_DIRECTORY_PATH, options['public_directory_path'], headers)
|
73
|
+
|
69
74
|
if options.get('report_key'):
|
70
75
|
headers[custom_headers.REPORT_KEY] = options['report_key']
|
71
76
|
|
@@ -75,6 +80,9 @@ def replay(context: ReplayContext, options: ReplayRequestOptions) -> requests.Re
|
|
75
80
|
if custom_headers.REQUEST_ORIGIN not in headers:
|
76
81
|
headers[custom_headers.REQUEST_ORIGIN] = request_origin.CLI
|
77
82
|
|
83
|
+
if options.get('response_fixtures_path'):
|
84
|
+
__handle_path_header(custom_headers.RESPONSE_FIXTURES_PATH, options['response_fixtures_path'], headers)
|
85
|
+
|
78
86
|
if options.get('scenario_key'):
|
79
87
|
headers[custom_headers.SCENARIO_KEY] = options['scenario_key']
|
80
88
|
|
@@ -146,14 +154,14 @@ def replay(context: ReplayContext, options: ReplayRequestOptions) -> requests.Re
|
|
146
154
|
|
147
155
|
return res
|
148
156
|
|
149
|
-
def
|
157
|
+
def __handle_path_header(header_name: str, script_path: str, headers):
|
150
158
|
if not script_path:
|
151
159
|
return
|
152
160
|
|
153
161
|
if not os.path.isabs(script_path):
|
154
162
|
script_path = os.path.join(os.path.abspath('.'), script_path)
|
155
163
|
|
156
|
-
headers[
|
164
|
+
headers[header_name] = script_path
|
157
165
|
|
158
166
|
def __handle_mode_option(_mode, request: Request, headers):
|
159
167
|
headers[custom_headers.PROXY_MODE] = _mode
|
stoobly_agent/app/proxy/run.py
CHANGED
@@ -124,6 +124,12 @@ def __filter_options(options):
|
|
124
124
|
if 'lifecycle_hooks_path' in options:
|
125
125
|
del options['lifecycle_hooks_path']
|
126
126
|
|
127
|
+
if 'public_directory_path' in options:
|
128
|
+
del options['public_directory_path']
|
129
|
+
|
130
|
+
if 'response_fixtures_path' in options:
|
131
|
+
del options['response_fixtures_path']
|
132
|
+
|
127
133
|
if 'ui_host' in options:
|
128
134
|
del options['ui_host']
|
129
135
|
|
@@ -142,6 +142,10 @@ class TestContext(TestContextABC):
|
|
142
142
|
def passed(self, v):
|
143
143
|
self.__passed = v
|
144
144
|
|
145
|
+
@property
|
146
|
+
def public_directory_path(self):
|
147
|
+
return self.__intercept_settings.public_directory_path
|
148
|
+
|
145
149
|
@property
|
146
150
|
def replay_context(self) -> ReplayContext:
|
147
151
|
return self.__replay_context
|
@@ -162,6 +166,14 @@ class TestContext(TestContextABC):
|
|
162
166
|
def response(self) -> TestContextResponse:
|
163
167
|
return self.__response
|
164
168
|
|
169
|
+
@property
|
170
|
+
def response_fixtures(self):
|
171
|
+
return self.__intercept_settings.response_fixtures
|
172
|
+
|
173
|
+
@property
|
174
|
+
def response_fixtures_path(self):
|
175
|
+
return self.__intercept_settings.response_fixtures_path
|
176
|
+
|
165
177
|
@property
|
166
178
|
def response_param_names(self) -> RequestComponentNamesFacade:
|
167
179
|
if self.__cached_response_param_names:
|
@@ -119,6 +119,11 @@ class TestContextABC(abc.ABC):
|
|
119
119
|
def passed(self, v):
|
120
120
|
pass
|
121
121
|
|
122
|
+
@property
|
123
|
+
@abc.abstractmethod
|
124
|
+
def public_directory_path(self):
|
125
|
+
pass
|
126
|
+
|
122
127
|
@property
|
123
128
|
@abc.abstractmethod
|
124
129
|
def replay_context(self):
|
@@ -144,6 +149,16 @@ class TestContextABC(abc.ABC):
|
|
144
149
|
def response(self) -> TestContextResponse:
|
145
150
|
pass
|
146
151
|
|
152
|
+
@property
|
153
|
+
@abc.abstractmethod
|
154
|
+
def response_fixtures(self):
|
155
|
+
pass
|
156
|
+
|
157
|
+
@property
|
158
|
+
@abc.abstractmethod
|
159
|
+
def response_fixtures_path(self):
|
160
|
+
pass
|
161
|
+
|
147
162
|
@property
|
148
163
|
@abc.abstractmethod
|
149
164
|
def response_param_names(self) -> RequestComponentNamesFacade:
|
stoobly_agent/cli.py
CHANGED
@@ -105,6 +105,8 @@ def init(**kwargs):
|
|
105
105
|
the form of "http[s]://host[:port]".
|
106
106
|
''')
|
107
107
|
@click.option('--proxy-port', default=8080, help='Proxy service port.')
|
108
|
+
@click.option('--public-directory-path', help='Path to public files. Used for mocking requests.')
|
109
|
+
@click.option('--response-fixtures-path', help='Path to response fixtures yaml. Used for mocking requests.')
|
108
110
|
@click.option('--ssl-insecure', is_flag=True, default=False, help='Do not verify upstream server SSL/TLS certificates.')
|
109
111
|
@click.option('--ui-host', default='0.0.0.0', help='Address to bind UI to.')
|
110
112
|
@click.option('--ui-port', default=4200, help='UI service port.')
|
@@ -116,6 +118,12 @@ def run(**kwargs):
|
|
116
118
|
if kwargs.get('lifecycle_hooks_path'):
|
117
119
|
os.environ[env_vars.AGENT_LIFECYCLE_HOOKS_PATH] = kwargs['lifecycle_hooks_path']
|
118
120
|
|
121
|
+
if kwargs.get('public_directory_path'):
|
122
|
+
os.environ[env_vars.AGENT_PUBLIC_DIRECOTRY_PATH] = kwargs['public_directory_path']
|
123
|
+
|
124
|
+
if kwargs.get('response_fixtures_path'):
|
125
|
+
os.environ[env_vars.AGENT_RESPONSE_FIXTURES_PATH] = kwargs['response_fixtures_path']
|
126
|
+
|
119
127
|
# Observe config for changes
|
120
128
|
Settings.instance().watch()
|
121
129
|
|
@@ -137,7 +145,10 @@ def run(**kwargs):
|
|
137
145
|
@ConditionalDecorator(lambda f: click.option('--remote-project-key', help='Use remote project for endpoint definitions.')(f), is_remote and is_local)
|
138
146
|
@click.option('--format', type=click.Choice([RAW_FORMAT]), help='Format response')
|
139
147
|
@click.option('-H', '--header', multiple=True, help='Pass custom header(s) to server')
|
148
|
+
@click.option('--lifecycle-hooks-path', help='Path to lifecycle hooks script.')
|
140
149
|
@ConditionalDecorator(lambda f: click.option('--project-key')(f), is_remote)
|
150
|
+
@click.option('--public-directory-path', help='Path to public files. Used for mocking requests.')
|
151
|
+
@click.option('--response-fixtures-path', help='Path to response fixtures yaml. Used for mocking requests.')
|
141
152
|
@click.option('-X', '--request', default='GET', help='Specify request command to use')
|
142
153
|
@click.option('--scenario-key')
|
143
154
|
@click.argument('url')
|
@@ -165,7 +176,7 @@ def mock(**kwargs):
|
|
165
176
|
print_raw_response(response)
|
166
177
|
else:
|
167
178
|
content = response.content
|
168
|
-
print(decode(content))
|
179
|
+
print(decode(content), end='')
|
169
180
|
|
170
181
|
@main.command(
|
171
182
|
help="Record request"
|
@@ -194,7 +205,7 @@ def record(**kwargs):
|
|
194
205
|
else:
|
195
206
|
try:
|
196
207
|
content = response.raw.data
|
197
|
-
print(content.decode(json.detect_encoding(content)))
|
208
|
+
print(content.decode(json.detect_encoding(content)), end='')
|
198
209
|
except UnicodeDecodeError:
|
199
210
|
print('Warning: Binary output can mess up your terminal.')
|
200
211
|
|
@@ -9,9 +9,11 @@ DO_PROXY = 'X-Do-Proxy'
|
|
9
9
|
LIFECYCLE_HOOKS_PATH = 'X-Stoobly-Lifecycle-Hooks-Path'
|
10
10
|
PROJECT_KEY = 'X-Project-Key'
|
11
11
|
PROXY_MODE = 'X-Proxy-Mode'
|
12
|
+
PUBLIC_DIRECTORY_PATH = 'X-Stoobly-Public-Directory-Path'
|
12
13
|
RECORD_POLICY = 'X-Record-Policy'
|
13
14
|
REPORT_KEY = 'X-Report-Key'
|
14
15
|
REQUEST_ORIGIN = 'X-Proxy-Request-Origin'
|
16
|
+
RESPONSE_FIXTURES_PATH = 'X-Stoobly-Response-Fixtures-Path'
|
15
17
|
RESPONSE_ID = 'X-Response-Id'
|
16
18
|
RESPONSE_LATENCY = 'X-Stoobly-Request-Response-Latency'
|
17
19
|
RESPONSE_PROXY_MODE = 'X-Response-Proxy-Mode'
|
@@ -9,10 +9,12 @@ AGENT_LIFECYCLE_HOOKS_PATH = 'STOOBLY_AGENT_LIFECYCLE_HOOKS_PATH'
|
|
9
9
|
AGENT_POLICY = 'STOOBLY_AGENT_POLICY'
|
10
10
|
AGENT_PROJECT_KEY = 'STOOBLY_AGENT_PROJECT_KEY'
|
11
11
|
AGENT_PROXY_URL = 'STOOBLY_AGENT_PROXY_URL'
|
12
|
+
AGENT_PUBLIC_DIRECTORY_PATH = 'STOOBLY_AGENT_PUBLIC_DIRECTORY_PATH'
|
12
13
|
AGENT_REMOTE_ENABLED = 'STOOBLY_AGENT_REMOTE_ENABLED'
|
13
14
|
AGENT_REMOTE_PROJECT_KEY = 'STOOBLY_AGENT_REMOTE_PROJECT_KEY'
|
14
15
|
AGENT_REPLAY_HOST = 'STOOBLY_AGENT_REPLAY_HOST'
|
15
16
|
AGENT_REPLAY_SCHEME = 'STOOBLY_AGENT_REPLAY_SCHEME'
|
17
|
+
AGENT_RESPONSE_FIXTURES_PATH = 'STOOBLY_AGENT_RESPONSE_FIXTURES_PATH'
|
16
18
|
AGENT_SERVICE_URL = 'STOOBLY_AGENT_SERVICE_URL'
|
17
19
|
AGENT_SCENARIO_KEY = 'STOOBLY_AGENT_SCENARIO_KEY'
|
18
20
|
AGENT_SIMULATE_LATENCY = 'STOOBLY_AGENT_SIMULATE_LATENCY'
|
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.34.0
|
@@ -0,0 +1,95 @@
|
|
1
|
+
import os
|
2
|
+
import pdb
|
3
|
+
import pytest
|
4
|
+
import requests
|
5
|
+
|
6
|
+
from click.testing import CliRunner
|
7
|
+
from mitmproxy.http import Request as MitmproxyRequest
|
8
|
+
|
9
|
+
from stoobly_agent.test.test_helper import reset
|
10
|
+
|
11
|
+
from stoobly_agent.app.models.adapters.orm.request.mitmproxy_adapter import MitmproxyRequestAdapter
|
12
|
+
from stoobly_agent.app.models.factories.resource.local_db.helpers.request_builder import RequestBuilder
|
13
|
+
from stoobly_agent.app.proxy.mock.eval_fixtures_service import eval_fixtures
|
14
|
+
from stoobly_agent.app.proxy.mock.types import Fixtures
|
15
|
+
from stoobly_agent.app.settings import Settings
|
16
|
+
from stoobly_agent.config.data_dir import DataDir
|
17
|
+
from stoobly_agent.lib.orm.request import Request
|
18
|
+
|
19
|
+
@pytest.fixture(scope='module')
|
20
|
+
def runner():
|
21
|
+
return CliRunner()
|
22
|
+
|
23
|
+
@pytest.fixture(scope='module')
|
24
|
+
def settings():
|
25
|
+
return reset()
|
26
|
+
|
27
|
+
class TestEvalFixturesService():
|
28
|
+
@pytest.fixture(scope='class')
|
29
|
+
def request_method(self):
|
30
|
+
return 'POST'
|
31
|
+
|
32
|
+
@pytest.fixture(scope='class')
|
33
|
+
def request_url(self):
|
34
|
+
return 'https://petstore.swagger.io/index.html'
|
35
|
+
|
36
|
+
@pytest.fixture(scope='class')
|
37
|
+
def created_request(
|
38
|
+
self, settings: Settings, request_method: str, request_url: str
|
39
|
+
):
|
40
|
+
status = RequestBuilder(
|
41
|
+
method=request_method,
|
42
|
+
response_body='',
|
43
|
+
status_code=200,
|
44
|
+
url=request_url,
|
45
|
+
).with_settings(settings).build()[1]
|
46
|
+
assert status == 200
|
47
|
+
|
48
|
+
return Request.last()
|
49
|
+
|
50
|
+
@pytest.fixture(scope='class')
|
51
|
+
def public_directory(self):
|
52
|
+
tmp_dir_path = DataDir.instance().tmp_dir_path
|
53
|
+
public_dir_path = os.path.join(tmp_dir_path, 'public')
|
54
|
+
if not os.path.exists(public_dir_path):
|
55
|
+
os.mkdir(public_dir_path)
|
56
|
+
return public_dir_path
|
57
|
+
|
58
|
+
@pytest.fixture(scope='class')
|
59
|
+
def index_file_contents(self):
|
60
|
+
return b'Hello World!'
|
61
|
+
|
62
|
+
@pytest.fixture(autouse=True, scope='class')
|
63
|
+
def index_file_path(self, public_directory: str, index_file_contents: str):
|
64
|
+
path = os.path.join(public_directory, 'index.html')
|
65
|
+
with open(path, 'wb') as fp:
|
66
|
+
fp.write(index_file_contents)
|
67
|
+
return path
|
68
|
+
|
69
|
+
@pytest.fixture(scope='class')
|
70
|
+
def response_fixtures(self, request_method: str, index_file_path: str) -> Fixtures:
|
71
|
+
fixtures = {}
|
72
|
+
fixtures[request_method] = {
|
73
|
+
'/index.html': {
|
74
|
+
'path': index_file_path
|
75
|
+
}
|
76
|
+
}
|
77
|
+
return fixtures
|
78
|
+
|
79
|
+
@pytest.fixture()
|
80
|
+
def mitmproxy_request(self, created_request: Request) -> MitmproxyRequest:
|
81
|
+
return MitmproxyRequestAdapter(created_request).adapt()
|
82
|
+
|
83
|
+
def test_it_evaluates_response_fixture(
|
84
|
+
self, mitmproxy_request: MitmproxyRequest, response_fixtures: Fixtures, index_file_contents: str
|
85
|
+
):
|
86
|
+
res: requests.Response = eval_fixtures(mitmproxy_request, response_fixtures=response_fixtures)
|
87
|
+
assert res
|
88
|
+
assert res.raw.read() == index_file_contents
|
89
|
+
|
90
|
+
def test_it_evaluates_public_directory(
|
91
|
+
self, mitmproxy_request: MitmproxyRequest, public_directory: str, index_file_contents: str
|
92
|
+
):
|
93
|
+
res: requests.Response = eval_fixtures(mitmproxy_request, public_directory_path=public_directory)
|
94
|
+
assert res
|
95
|
+
assert res.raw.read() == index_file_contents
|
@@ -1,4 +1,4 @@
|
|
1
|
-
stoobly_agent/__init__.py,sha256=
|
1
|
+
stoobly_agent/__init__.py,sha256=o6llxNBqohA4qNGyvnHwKNOZSKfIk-O2AgU_xubMBnY,45
|
2
2
|
stoobly_agent/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
stoobly_agent/app/api/__init__.py,sha256=FFSlVoTgjPfUNlYPr_7u6-P5Y4WOKyaUfAHtUcB-Xio,810
|
4
4
|
stoobly_agent/app/api/application_http_request_handler.py,sha256=jf4fkqjOiCeI2IM5Ro7ie0v_C6y0-7-5TIE_IKMPOfg,5513
|
@@ -23,8 +23,8 @@ stoobly_agent/app/cli/config_cli.py,sha256=FhhB1PCAX4rLuNxBGd4_x-lBHwIlGYV3KZmj2
|
|
23
23
|
stoobly_agent/app/cli/decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
24
|
stoobly_agent/app/cli/decorators/config.py,sha256=AWrDGZm_gjCWFYlRwdla3iE6H7OSlM4FxkaXRNovBZA,2428
|
25
25
|
stoobly_agent/app/cli/decorators/exec.py,sha256=170tkugOHBb6Pe3AzgDSfoQ1n7ntcah7JVZz-G5cSYU,994
|
26
|
-
stoobly_agent/app/cli/dev_tools_cli.py,sha256=
|
27
|
-
stoobly_agent/app/cli/endpoint_cli.py,sha256=
|
26
|
+
stoobly_agent/app/cli/dev_tools_cli.py,sha256=gNiiB5hzQrhI--K9EObW8tI6iJXRPC7LRw43qPnsKMc,1890
|
27
|
+
stoobly_agent/app/cli/endpoint_cli.py,sha256=UfeWc_UTS-P_TChZ0oe5LVaewfyhqrQXa8MDxT6GDB4,3264
|
28
28
|
stoobly_agent/app/cli/feature_cli.py,sha256=d-MUTbK3gbZxZNJUjM43fioZyhPyzetQNKjI1rVeuhQ,1702
|
29
29
|
stoobly_agent/app/cli/handlers/request_cli_handler.py,sha256=IPToK9OfiDda-mkq8HpiSSqK2m-UR15-8f8LVoLAHTA,6965
|
30
30
|
stoobly_agent/app/cli/handlers/scenario_cli_handler.py,sha256=1KSsfhR1mmPUCmhjzWTjf_L9NcQglLvVK3KURrPt92A,7425
|
@@ -34,7 +34,7 @@ stoobly_agent/app/cli/helpers/endpoint_facade.py,sha256=_1TR9PcRnqpl9Q4njFHMM_Bt
|
|
34
34
|
stoobly_agent/app/cli/helpers/endpoints_apply_context.py,sha256=PvUzJLXNKl6wIQj2q8YZK1MAm1yqzDLLlpCYMoyWb9I,1831
|
35
35
|
stoobly_agent/app/cli/helpers/endpoints_apply_service.py,sha256=9GYuUlo-nNn6z7jmS1KaJ_gzJo896_Bl4dapGKL5ckY,2315
|
36
36
|
stoobly_agent/app/cli/helpers/endpoints_import_context.py,sha256=w-iRZpNh3tX8GfafEe_6QMWCBiGNNSqvJKsfAXpnrLM,2209
|
37
|
-
stoobly_agent/app/cli/helpers/endpoints_import_service.py,sha256=
|
37
|
+
stoobly_agent/app/cli/helpers/endpoints_import_service.py,sha256=PQBwp2k6DeYcgc6rX8FP3TtPOq8Y2F3z6FD4IDuXkIs,6991
|
38
38
|
stoobly_agent/app/cli/helpers/feature_flags.py,sha256=neIdXHhDMv8Zq1zrfoZ28NDXkamUgDiwnu-7AS3LshE,558
|
39
39
|
stoobly_agent/app/cli/helpers/handle_config_update_service.py,sha256=AA67-mxHHk6cWsE0DhkQtGwoicZSCgzk3W8EcA9FNZ8,5675
|
40
40
|
stoobly_agent/app/cli/helpers/handle_mock_service.py,sha256=bhqzI6XaFlSq39N_f1CUWnmgOhJ3MnViW31Rmjaiutw,568
|
@@ -45,7 +45,7 @@ stoobly_agent/app/cli/helpers/json_print.py,sha256=E7UOcxtzGCv8_GCsz0Wos1XP5RhhM
|
|
45
45
|
stoobly_agent/app/cli/helpers/openapi_endpoint_adapter.py,sha256=rUM6AeOtzqCPNxzjT6I7nDlfMEv0FR67qD1OTJlwHrc,24638
|
46
46
|
stoobly_agent/app/cli/helpers/print_service.py,sha256=Z3m-O2aj3oETTm06h_XScgxJAfulTHlEyhOCoE10VG4,4185
|
47
47
|
stoobly_agent/app/cli/helpers/project_facade.py,sha256=rThwVur2clCCBcHa-U4_vGynj0miKv78B3uF6ZRmBSQ,1905
|
48
|
-
stoobly_agent/app/cli/helpers/replay_facade.py,sha256=
|
48
|
+
stoobly_agent/app/cli/helpers/replay_facade.py,sha256=0Saxae9HN-cdwrelJXz89X139M9bz9hY8_9hERgxjB4,3730
|
49
49
|
stoobly_agent/app/cli/helpers/report_facade.py,sha256=LO2SURvjkzqhbGx_Jaei8I-o7eXF7QKq6lWewBkQ9Qc,1056
|
50
50
|
stoobly_agent/app/cli/helpers/request_facade.py,sha256=HnaB_mBP-o8wWzYTeWteq5dMn-dVkMk89fgabhl7i5A,4215
|
51
51
|
stoobly_agent/app/cli/helpers/request_synchronize_handler.py,sha256=wawAL8hoZLqOHGJtcTYeJ4H5FeBVSdB3EwgUAdGQnSE,3603
|
@@ -64,8 +64,8 @@ stoobly_agent/app/cli/intercept_cli.py,sha256=jYbmH9FUI6J9vTlC4ycwRRNVYk0GRC8ZI5
|
|
64
64
|
stoobly_agent/app/cli/main_group.py,sha256=kHR-0P9QL2No4XG1asgDfisIDdfxgLbj30y3vEobClQ,2175
|
65
65
|
stoobly_agent/app/cli/project_cli.py,sha256=EXjeLjbnq9PhfCjvyfZ0UnJ2tejeCS0SIAo3Nc4fKOc,3852
|
66
66
|
stoobly_agent/app/cli/report_cli.py,sha256=ZxJw0Xkx7KFZJn9e45BSKRKon8AD0Msrwy1fbPfbv0c,2543
|
67
|
-
stoobly_agent/app/cli/request_cli.py,sha256=
|
68
|
-
stoobly_agent/app/cli/scenario_cli.py,sha256=
|
67
|
+
stoobly_agent/app/cli/request_cli.py,sha256=hTN9qSq7lkKYLtAZHWMCYFvuoUoYZEhsb6CWMtn0ucI,7752
|
68
|
+
stoobly_agent/app/cli/scenario_cli.py,sha256=3J1EiJOvunkfWrEkOsanw-XrKkOk78ij_GjBlE9p7CE,8229
|
69
69
|
stoobly_agent/app/cli/snapshot_cli.py,sha256=SEbCHt0NxCSDR86p6doNpS_SbOIVhJ-H_JD8yDOOps0,7578
|
70
70
|
stoobly_agent/app/cli/trace_cli.py,sha256=K7E-vx3JUcqEDSWOdIOi_AieKNQz7dBfmRrVvKDkzFI,4605
|
71
71
|
stoobly_agent/app/cli/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -73,7 +73,7 @@ stoobly_agent/app/cli/types/output.py,sha256=2wazv56g5IwLQeJCWfJXXAxTB9Y5WH1cKMH
|
|
73
73
|
stoobly_agent/app/cli/types/print_options.py,sha256=UQSf2aZu4rmamjgmt5g7xIZf8KV02uVHPCt6edZn_c0,219
|
74
74
|
stoobly_agent/app/cli/types/request.py,sha256=QthojE5sfx7OvKu-vVNnSUfGk8n4pLzuBQO0YgoO46Q,88
|
75
75
|
stoobly_agent/app/cli/types/scenario.py,sha256=28WxmOlbm2Bsek1uu7yc4hJGz-d5oHbYAro7LlFWRoc,81
|
76
|
-
stoobly_agent/app/cli/types/test.py,sha256=
|
76
|
+
stoobly_agent/app/cli/types/test.py,sha256=1c458B7DFBWsEk5Q1CrZ2CUi84YzEzcs-W4qTcudwAk,714
|
77
77
|
stoobly_agent/app/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
78
78
|
stoobly_agent/app/models/adapters/__init__.py,sha256=cEEE--Bvrvk6DAsHx_uPgFhLnZJETP4zSBtWjMqyIKc,233
|
79
79
|
stoobly_agent/app/models/adapters/joined_request_adapter.py,sha256=iINSDAV2-WlK_Mwty-dO5DNR38-1B9DykyzhGCOFeyg,2308
|
@@ -120,7 +120,7 @@ stoobly_agent/app/models/factories/resource/local_db/helpers/__init__.py,sha256=
|
|
120
120
|
stoobly_agent/app/models/factories/resource/local_db/helpers/create_request_columns_service.py,sha256=HABMelW-cvhm2WXaywbQcd4PQhzSrz4vAbN7uOdetXM,1541
|
121
121
|
stoobly_agent/app/models/factories/resource/local_db/helpers/log.py,sha256=BKIWS_UoRyZxbJ7Z3zU8gp-5Tvb1n3BLk-jprQje7uc,5252
|
122
122
|
stoobly_agent/app/models/factories/resource/local_db/helpers/log_event.py,sha256=qRws34HK5fv3C_rEoIzpeIwZrSVhCG9sLz_jiuIbatY,3373
|
123
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/request_builder.py,sha256=
|
123
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/request_builder.py,sha256=PyVvsYmi5bBQ6PsUPxQ4nJM9rhhjGVOTd7ipuc2tMMM,3227
|
124
124
|
stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py,sha256=W0CszJaKD_-e5tr7JtfWfoOBXOl5dZBCrRXPhTF7hQk,1640
|
125
125
|
stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py,sha256=HayWx_-O7JtTYM-6k3fJbErdtoGan7M9C6l3q3KwrCI,3652
|
126
126
|
stoobly_agent/app/models/factories/resource/local_db/helpers/search.py,sha256=A7KVcmxj9c3CT2rh26YH6khiEPkB_4U1UHhiYelNaws,782
|
@@ -169,13 +169,13 @@ stoobly_agent/app/proxy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
|
|
169
169
|
stoobly_agent/app/proxy/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
170
170
|
stoobly_agent/app/proxy/constants/custom_response_codes.py,sha256=1CaApt_6W7GrxvN8_Ozbf_SEodVEQaNZRR2sMYpI0U8,40
|
171
171
|
stoobly_agent/app/proxy/context.py,sha256=XLteUEP8T8aT0hHI_jHDaL2382OaC7QyVwlni5_FS9U,456
|
172
|
-
stoobly_agent/app/proxy/handle_mock_service.py,sha256=
|
172
|
+
stoobly_agent/app/proxy/handle_mock_service.py,sha256=z6HOX8g7ltKlQt_WlPXCiU80R3REj8cI4l2uJ_uNGKQ,6608
|
173
173
|
stoobly_agent/app/proxy/handle_record_service.py,sha256=_6UT4vaLncPGp1XDlUVFTqyhlc6Tv70MDTSUwVw3y-U,3355
|
174
174
|
stoobly_agent/app/proxy/handle_replay_service.py,sha256=zBsDnmibMZtYUoi9y6co6Ou5-JYHVrKiWnsJKHSkKtk,1795
|
175
175
|
stoobly_agent/app/proxy/handle_test_service.py,sha256=nGoE2m3lfDYVs3_k-_wOXCmmf27vz99pwlsIhMxDu9I,5732
|
176
176
|
stoobly_agent/app/proxy/hot_reload.py,sha256=RM2xPMl9qHh5SgjnRE1mnz8GilreFSQf3wpiJ6ax0Mw,903
|
177
177
|
stoobly_agent/app/proxy/intercept_handler.py,sha256=dnKYi9lkJSoa0yLcxOwZ4nH9gljbPPExnnfAKxEXpLU,4190
|
178
|
-
stoobly_agent/app/proxy/intercept_settings.py,sha256=
|
178
|
+
stoobly_agent/app/proxy/intercept_settings.py,sha256=EqcJEYnoLsk0bqjdHwOL547jp_V5g6r_XDC74ufjG3U,11349
|
179
179
|
stoobly_agent/app/proxy/mitmproxy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
180
180
|
stoobly_agent/app/proxy/mitmproxy/flow_mock.py,sha256=YxQV3pXIzicvLOCFn7zmnSoAstAmR8fzONYOXAkYbT4,1153
|
181
181
|
stoobly_agent/app/proxy/mitmproxy/request.py,sha256=niy38718CAe4Li4k40GXnWDDmUTCPkXesDZTaC8BeLc,878
|
@@ -186,11 +186,13 @@ stoobly_agent/app/proxy/mitmproxy/response_facade.py,sha256=NsNH0aW-mMfLcUXB-Wt9
|
|
186
186
|
stoobly_agent/app/proxy/mock/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
187
187
|
stoobly_agent/app/proxy/mock/context.py,sha256=vDo5_3WBL73mVFnsmQWvcxvPg5nWtRJbigSrE3zGc-o,794
|
188
188
|
stoobly_agent/app/proxy/mock/custom_not_found_response_builder.py,sha256=Bwy7qsPzcIUw5FTaUGeaOnsTneuCCbGkTJU-wsmN3NE,415
|
189
|
+
stoobly_agent/app/proxy/mock/eval_fixtures_service.py,sha256=EVxYsa9QmdnRSkIuTU27yzhXE05PXKUdJQR0ujARoIk,1586
|
189
190
|
stoobly_agent/app/proxy/mock/eval_request_service.py,sha256=TIWRDDVTlvVc9cBkhIULtp2A89Q_PXTBGJpkCTEHkbg,6965
|
190
191
|
stoobly_agent/app/proxy/mock/hashed_request_decorator.py,sha256=8SnIplrK7VocpvAFcg8-io2Dbhot4i6Ez6H7k3avwWs,5094
|
191
192
|
stoobly_agent/app/proxy/mock/ignored_components_response_builder.py,sha256=E32_E1eSdmPn2SeM_e1jWnqu4xh5w_SnmOs32Shx99E,501
|
192
193
|
stoobly_agent/app/proxy/mock/request_hasher.py,sha256=91RLNk163digRaMd1Kg88RLzXrXkpvoNKDQxB_NeiNo,3723
|
193
194
|
stoobly_agent/app/proxy/mock/search_endpoint.py,sha256=HDN3tIkPnlgmkt-BbZDz7iwacMTmbZH77HNtk9g-ymc,841
|
195
|
+
stoobly_agent/app/proxy/mock/types/__init__.py,sha256=r4Zwqx6V5HHZwrcYWEv6xSXG0nXaN5FqxqCm0d3U-GM,150
|
194
196
|
stoobly_agent/app/proxy/record/__init__.py,sha256=lbW-G6DZ8IrRpRzvgt-1tn2cRuyLT744u9ERIrZ2dFI,233
|
195
197
|
stoobly_agent/app/proxy/record/context.py,sha256=L9tykwoB35hOPHft3IHSPjuJ2v3TSj9SQAJ9E1q-U_4,343
|
196
198
|
stoobly_agent/app/proxy/record/join_request_service.py,sha256=Cz34NWBHBIRhHGtfdgnuhTYLPfWPWx6R_mSqljPGr7k,1307
|
@@ -208,16 +210,16 @@ stoobly_agent/app/proxy/replay/alias_resolver.py,sha256=KzCShXKuWGG88op4AIMq5wcw
|
|
208
210
|
stoobly_agent/app/proxy/replay/body_parser_service.py,sha256=hPC65IxiYKdzhV1rnU7HZHor0u7KQQWukA1vYTJIDuM,3735
|
209
211
|
stoobly_agent/app/proxy/replay/context.py,sha256=_dtU2-OmTVY_zsXRU7gdL9E7zbx9a9jSfkm9_H6vfp0,796
|
210
212
|
stoobly_agent/app/proxy/replay/multipart.py,sha256=jEFyX5G2n5z5v9mDCEH5gAh-Nb2T2yqdmLOuqbES_2k,2828
|
211
|
-
stoobly_agent/app/proxy/replay/replay_request_service.py,sha256=
|
213
|
+
stoobly_agent/app/proxy/replay/replay_request_service.py,sha256=1OyrGb9Vfflhcfy4GBDIwa8iP5_7v3Cr7Q0zi6U9apc,7013
|
212
214
|
stoobly_agent/app/proxy/replay/replay_scenario_service.py,sha256=9jV-iO5EBg8geUblEtjjWRFIkom_Pqmo7P-lTc3S4Xw,2824
|
213
215
|
stoobly_agent/app/proxy/replay/rewrite_params_service.py,sha256=jEHlT6_OHq_VBa09Hd6QaRyErv7tZnziDvW7m3Q8CQg,2234
|
214
216
|
stoobly_agent/app/proxy/replay/trace_context.py,sha256=n-jqoVeZaUqzTdHI4ImaQ5vUqLfU4sMDDPW0Ydpvuic,10362
|
215
|
-
stoobly_agent/app/proxy/run.py,sha256=
|
217
|
+
stoobly_agent/app/proxy/run.py,sha256=VgA-IoSjvXm2wD-3j6lc3w7Aiz3QZs_s7ohx_JyVB3w,3970
|
216
218
|
stoobly_agent/app/proxy/settings.py,sha256=R0LkSa9HrkUXvCd-nur4syJePjbQZdlnAnOPpGnCx38,2172
|
217
219
|
stoobly_agent/app/proxy/simulate_intercept_service.py,sha256=R-L2dh2dfYFebttWXU0NwyxFI_jP6Ud36oKPC-T8HiI,1942
|
218
220
|
stoobly_agent/app/proxy/test/__init__.py,sha256=uW0Ab27oyH2odTeVRjcuUJF8A1FLbTT5sBMzhGZr1so,89
|
219
|
-
stoobly_agent/app/proxy/test/context.py,sha256=
|
220
|
-
stoobly_agent/app/proxy/test/context_abc.py,sha256=
|
221
|
+
stoobly_agent/app/proxy/test/context.py,sha256=xb8QhurwLkt7kyWr5w4gM2xS85v0Xj0xag8KwlCtdwE,7341
|
222
|
+
stoobly_agent/app/proxy/test/context_abc.py,sha256=Yt_9OBu3SDbb1ihzk2LL1sEfbVf2-ZNXVpIeGRYa2Pk,3570
|
221
223
|
stoobly_agent/app/proxy/test/context_response.py,sha256=UJNGpW-Ageri42YBmpQ6vrIbFzggGf1Xndjca8MB6w4,1156
|
222
224
|
stoobly_agent/app/proxy/test/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
223
225
|
stoobly_agent/app/proxy/test/helpers/diff_service.py,sha256=HuHADf7uAZCtmxhny1s1l4eXnLYZf4b_rkF254T0-CU,1541
|
@@ -271,12 +273,12 @@ stoobly_agent/app/settings/types/remote_settings.py,sha256=4PvEGKULXM0zv29XTDzV7
|
|
271
273
|
stoobly_agent/app/settings/types/ui_settings.py,sha256=BqPy2F32AbODqzi2mp2kRk28QVUydQIwVmvftn46pco,84
|
272
274
|
stoobly_agent/app/settings/ui_settings.py,sha256=YDEUMPuJFh0SLHaGz6O-Gpz8nwsunNzeuc-TzO9OUbM,1170
|
273
275
|
stoobly_agent/app/settings/url_rule.py,sha256=D9g5jvagGpX-4K9WuiB9lUhvy9XfNZqZ3ZtdscUTmWE,827
|
274
|
-
stoobly_agent/cli.py,sha256=
|
276
|
+
stoobly_agent/cli.py,sha256=gvIaa-OFviVIWnKEiHe7MMOZEX_hkB6LAjWaf-roZ6I,9052
|
275
277
|
stoobly_agent/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
276
278
|
stoobly_agent/config/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
277
279
|
stoobly_agent/config/constants/alias_resolve_strategy.py,sha256=_R1tVqFnyGxCraVS5-dhSskaDj_X8-NthsY7i_bEt9M,119
|
278
|
-
stoobly_agent/config/constants/custom_headers.py,sha256=
|
279
|
-
stoobly_agent/config/constants/env_vars.py,sha256=
|
280
|
+
stoobly_agent/config/constants/custom_headers.py,sha256=OTxRJ4f5tDEyoXvVQWBle87ERegdWlMkXgBGVI6_jlo,1138
|
281
|
+
stoobly_agent/config/constants/env_vars.py,sha256=HAR_ZIdXXbpWQgCDaRR5RtpVyGXCsMLr_Fh8n6S12K0,1344
|
280
282
|
stoobly_agent/config/constants/headers.py,sha256=fm5FxKroCpNHTaKMjrIO4npNmT-eAUplXt_ix0N5Ruo,223
|
281
283
|
stoobly_agent/config/constants/lifecycle_hooks.py,sha256=aobclZmcND_mUnFKkUgpxgwd5EU3pjgAvB-NFf2dCgE,846
|
282
284
|
stoobly_agent/config/constants/mock_policy.py,sha256=KrRVPJYRSb0hIH9fjAV9aQ8OsnhnYo9l7dylug62YVg,41
|
@@ -551,12 +553,13 @@ stoobly_agent/test/app/models/factories/resource/local_db/helpers/log_test.py,sh
|
|
551
553
|
stoobly_agent/test/app/models/factories/resource/local_db/helpers/tiebreak_scenario_request_test.py,sha256=Ft5POn1ZJqt3YOSdPOb0PwwCaIkBEpw3Jug9WAeAt-g,2863
|
552
554
|
stoobly_agent/test/app/models/factories/resource/local_db/request_adapter_test.py,sha256=Pzq1cBPnP9oSWG-p0c-VoymoHxgp483QmNwmV1b78RA,8453
|
553
555
|
stoobly_agent/test/app/models/factories/resource/local_db/response_adapter_test.py,sha256=9P95EKH5rZGOrmRkRIDlQZqtiLJHk9735og18Ffwpfw,2204
|
554
|
-
stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=
|
556
|
+
stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=NE80Jl1qURiXeGm2Dnq527KijGz5PJu06aN7bchrC4A,6
|
555
557
|
stoobly_agent/test/app/models/schemas/.stoobly/db/stoobly_agent.sqlite3,sha256=ch8gNx6zIelLKQx65gwFx_LRNqUD3EC5xcHZ0ukIQiU,188416
|
556
558
|
stoobly_agent/test/app/models/schemas/.stoobly/settings.yml,sha256=vLwMjweKOdod6tSLtIlyBefPQuNXq9wio4kBaODKtAU,726
|
557
559
|
stoobly_agent/test/app/models/schemas/.stoobly/tmp/options.json,sha256=OTRzarwus48CTrItedXCrgQttJHSEZonEYc7R_knvYg,2212
|
558
560
|
stoobly_agent/test/app/models/schemas/request_test.py,sha256=9SF43KXbjO-vMr2uObPJlyLeop_JQstl6Jrh0M1A70c,1116
|
559
561
|
stoobly_agent/test/app/proxy/mitmproxy/request_facade_test.py,sha256=YPeYK7hndct7DuHLergmTEKv3KWAK8ns-216bbh4Wy8,5075
|
562
|
+
stoobly_agent/test/app/proxy/mock/eval_fixtures_service_test.py,sha256=fLn8MYlxjpB7ZqZgROS9dksSctmeRex6MxSXMgbQ3T0,3067
|
560
563
|
stoobly_agent/test/app/proxy/replay/body_parser_service_test.py,sha256=MTC4a3QxrptHzroHAfgrCEzCZ3Ur0Ijyj9_3k-bG0jQ,1228
|
561
564
|
stoobly_agent/test/app/proxy/replay/rewrite_params_service_test.py,sha256=4KVaP48KjCeoZKqY3IdrFAP5Pnb3jO86k8L7ffvz2ZI,3770
|
562
565
|
stoobly_agent/test/app/proxy/replay/trace_context_test.py,sha256=hwugGI0RMpzuanyHsDzQeck9lW_AgqMczGZ5u2wHNkE,13150
|
@@ -584,8 +587,8 @@ stoobly_agent/test/mock_data/petstore.yaml,sha256=CCdliJky04Az4FIOkFA883uunwFDHL
|
|
584
587
|
stoobly_agent/test/mock_data/request_show_response.py,sha256=K_a0fP0QT58T8sX9PaM6hqtX1A1depZsqg_GsNPf--k,707
|
585
588
|
stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
|
586
589
|
stoobly_agent/test/test_helper.py,sha256=m_oAI7tmRYCNZdKfNqISWhMv3e44tjeYViQ3nTUfnos,1007
|
587
|
-
stoobly_agent-0.34.
|
588
|
-
stoobly_agent-0.34.
|
589
|
-
stoobly_agent-0.34.
|
590
|
-
stoobly_agent-0.34.
|
591
|
-
stoobly_agent-0.34.
|
590
|
+
stoobly_agent-0.34.1.dist-info/LICENSE,sha256=8QKGyy45eN76Zk52h8gu1DKX2B_gbWgZ3nzDLofEbaE,548
|
591
|
+
stoobly_agent-0.34.1.dist-info/METADATA,sha256=YPVORaRRT7xAaOu-Kg4gWXzoalEFeq6OuWWQdMZ6j1o,3304
|
592
|
+
stoobly_agent-0.34.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
593
|
+
stoobly_agent-0.34.1.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
|
594
|
+
stoobly_agent-0.34.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|