stoobly-agent 1.3.0__py3-none-any.whl → 1.4.0__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/api/application_http_request_handler.py +3 -3
- stoobly_agent/app/api/proxy_controller.py +8 -7
- stoobly_agent/app/cli/config_cli.py +1 -1
- stoobly_agent/app/cli/helpers/certificate_authority.py +6 -1
- stoobly_agent/app/cli/helpers/print_service.py +17 -0
- stoobly_agent/app/cli/scaffold/app.py +2 -2
- stoobly_agent/app/cli/scaffold/constants.py +0 -2
- stoobly_agent/app/cli/scaffold/hosts_file_manager.py +112 -0
- stoobly_agent/app/cli/scaffold/service.py +0 -1
- stoobly_agent/app/cli/scaffold/service_config.py +10 -14
- stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py +3 -3
- stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context +1 -1
- stoobly_agent/app/cli/scaffold/templates/app/.Makefile +77 -53
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.services +9 -0
- stoobly_agent/app/cli/scaffold/workflow_create_command.py +0 -1
- stoobly_agent/app/cli/scaffold/workflow_run_command.py +6 -9
- stoobly_agent/app/cli/scaffold_cli.py +200 -69
- stoobly_agent/app/cli/snapshot_cli.py +1 -1
- stoobly_agent/app/proxy/handle_mock_service.py +2 -0
- stoobly_agent/app/proxy/handle_replay_service.py +2 -0
- stoobly_agent/app/proxy/mitmproxy/request_facade.py +1 -1
- stoobly_agent/app/proxy/mitmproxy/response_body_facade.py +19 -0
- stoobly_agent/app/proxy/mitmproxy/response_facade.py +90 -18
- stoobly_agent/app/proxy/record/join_request_service.py +1 -1
- stoobly_agent/app/settings/constants/request_component.py +2 -1
- stoobly_agent/config/constants/custom_headers.py +13 -13
- stoobly_agent/config/constants/headers.py +0 -2
- stoobly_agent/public/18-es2015.583f191cc7ad512ee262.js +1 -0
- stoobly_agent/public/18-es5.583f191cc7ad512ee262.js +1 -0
- stoobly_agent/public/35-es2015.8f79ff8748d4ff06ab03.js +1 -0
- stoobly_agent/public/35-es5.8f79ff8748d4ff06ab03.js +1 -0
- stoobly_agent/public/index.html +1 -1
- stoobly_agent/public/main-es2015.2cc16523aa3fcaba51e5.js +1 -0
- stoobly_agent/public/main-es5.2cc16523aa3fcaba51e5.js +1 -0
- stoobly_agent/public/{runtime-es2015.9addf49b79aca951b7e2.js → runtime-es2015.b914470164e4d6e75d96.js} +1 -1
- stoobly_agent/public/{runtime-es5.9addf49b79aca951b7e2.js → runtime-es5.b914470164e4d6e75d96.js} +1 -1
- stoobly_agent/test/app/cli/scaffold/{hosts_file_reader_test.py → hosts_file_manager_test.py} +20 -20
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
- {stoobly_agent-1.3.0.dist-info → stoobly_agent-1.4.0.dist-info}/METADATA +1 -1
- {stoobly_agent-1.3.0.dist-info → stoobly_agent-1.4.0.dist-info}/RECORD +44 -42
- stoobly_agent/app/cli/scaffold/hosts_file_reader.py +0 -65
- stoobly_agent/public/18-es2015.d3b430636a4d6f544d92.js +0 -1
- stoobly_agent/public/18-es5.d3b430636a4d6f544d92.js +0 -1
- stoobly_agent/public/35-es2015.f741ebce0bfc25f0ec99.js +0 -1
- stoobly_agent/public/35-es5.f741ebce0bfc25f0ec99.js +0 -1
- stoobly_agent/public/main-es2015.ccd46ac1b6638ddf2066.js +0 -1
- stoobly_agent/public/main-es5.ccd46ac1b6638ddf2066.js +0 -1
- {stoobly_agent-1.3.0.dist-info → stoobly_agent-1.4.0.dist-info}/LICENSE +0 -0
- {stoobly_agent-1.3.0.dist-info → stoobly_agent-1.4.0.dist-info}/WHEEL +0 -0
- {stoobly_agent-1.3.0.dist-info → stoobly_agent-1.4.0.dist-info}/entry_points.txt +0 -0
@@ -1,33 +1,45 @@
|
|
1
|
-
|
1
|
+
import pdb
|
2
2
|
|
3
|
-
from
|
3
|
+
from mitmproxy.http import Headers, Response as MitmproxyResponse
|
4
|
+
from mitmproxy.coretypes import multidict
|
5
|
+
from typing import Callable, List
|
4
6
|
|
7
|
+
from stoobly_agent.app.settings.constants import request_component
|
8
|
+
from stoobly_agent.app.settings.rewrite_rule import ParameterRule, RewriteRule
|
9
|
+
from stoobly_agent.config.constants import custom_headers
|
10
|
+
from stoobly_agent.lib.logger import Logger, bcolors
|
11
|
+
from stoobly_agent.lib.utils import jmespath
|
12
|
+
from stoobly_agent.lib.utils.decode import decode
|
13
|
+
|
14
|
+
from .request_facade import MitmproxyRequestFacade
|
15
|
+
from .response_body_facade import MitmproxyResponseBodyFacade
|
5
16
|
from .response import Response
|
6
17
|
|
18
|
+
LOG_ID = 'Response'
|
19
|
+
|
7
20
|
class MitmproxyResponseFacade(Response):
|
8
21
|
|
9
22
|
def __init__(self, response: MitmproxyResponse):
|
10
23
|
self.response = response
|
11
|
-
self.content = response.raw_content
|
12
24
|
|
13
|
-
self.
|
25
|
+
self.__body = MitmproxyResponseBodyFacade(response)
|
26
|
+
self.__parameter_rules: List[ParameterRule] = []
|
14
27
|
|
15
28
|
@property
|
16
29
|
def code(self):
|
17
30
|
return self.response.status_code
|
18
31
|
|
32
|
+
@property
|
33
|
+
def content_type(self):
|
34
|
+
return self.headers.get('content-type')
|
35
|
+
|
19
36
|
@property
|
20
37
|
def headers(self):
|
21
|
-
return self.response.headers
|
38
|
+
return self.__filter_custom_headers(self.response.headers)
|
22
39
|
|
23
40
|
@property
|
24
41
|
def body(self):
|
25
|
-
|
26
|
-
|
27
|
-
if not content:
|
28
|
-
return b''
|
29
|
-
|
30
|
-
return content
|
42
|
+
return self.response.raw_content or ''
|
31
43
|
|
32
44
|
@property
|
33
45
|
def http_verison(self):
|
@@ -40,13 +52,73 @@ class MitmproxyResponseFacade(Response):
|
|
40
52
|
# Update Content-Lenght header to decoded content length
|
41
53
|
self.response.headers['content-length'] = str(len(self.content))
|
42
54
|
|
43
|
-
|
44
|
-
def with_rewrite_rules(self, rules: RewriteRule):
|
55
|
+
def with_parameter_rules(self, rules: List[RewriteRule], request_facade: MitmproxyRequestFacade):
|
45
56
|
if type(rules) == list:
|
46
|
-
self.
|
57
|
+
self.__parameter_rules = request_facade.select_parameter_rules(rules)
|
58
|
+
return self
|
47
59
|
|
48
|
-
return self
|
49
|
-
|
50
|
-
# TODO
|
51
60
|
def rewrite(self):
|
52
|
-
|
61
|
+
rewrites = self.__parameter_rules
|
62
|
+
|
63
|
+
if len(rewrites) != 0:
|
64
|
+
self.__rewrite_headers(rewrites)
|
65
|
+
self.__rewrite_content(rewrites)
|
66
|
+
|
67
|
+
def __apply_rewrites(self, params: dict, rewrites: List[ParameterRule], handler: Callable):
|
68
|
+
if len(rewrites) == 0:
|
69
|
+
return
|
70
|
+
|
71
|
+
for rewrite in rewrites:
|
72
|
+
jmespath.search(rewrite.name, params, {
|
73
|
+
'replacements': [handler(rewrite) if handler else rewrite.value],
|
74
|
+
})
|
75
|
+
|
76
|
+
def __rewrite_handler(self, rewrite: ParameterRule) -> str:
|
77
|
+
Logger.instance(LOG_ID).info(f"{bcolors.OKCYAN}Rewriting{bcolors.ENDC} {rewrite.type.lower()} {rewrite.name} => {rewrite.value}")
|
78
|
+
return rewrite.value
|
79
|
+
|
80
|
+
def __rewrite_headers(self, rewrites: List[ParameterRule]):
|
81
|
+
self.__apply_headers(rewrites, self.__rewrite_handler)
|
82
|
+
|
83
|
+
def __rewrite_content(self, rewrites: List[ParameterRule]):
|
84
|
+
self.__apply_content(rewrites, self.__rewrite_handler)
|
85
|
+
|
86
|
+
def __apply_headers(self, rewrites: List[ParameterRule], handler: Callable):
|
87
|
+
rewrites = list(filter(lambda rewrite: rewrite.type == request_component.RESPONSE_HEADER, rewrites))
|
88
|
+
self.__apply_rewrites(self.response.headers, rewrites, handler)
|
89
|
+
|
90
|
+
def __apply_content(self, rewrites: List[ParameterRule], handler: Callable):
|
91
|
+
rewrites = list(filter(lambda rewrite: rewrite.type == request_component.RESPONSE_PARAM, rewrites))
|
92
|
+
if len(rewrites) == 0:
|
93
|
+
return
|
94
|
+
|
95
|
+
content_type = self.content_type
|
96
|
+
parsed_content = self.__body.get(content_type)
|
97
|
+
|
98
|
+
if not isinstance(parsed_content, dict) and not isinstance(parsed_content, multidict.MultiDictView):
|
99
|
+
content_type = 'application/json'
|
100
|
+
self.response.headers['content-type'] = content_type
|
101
|
+
parsed_content = {}
|
102
|
+
|
103
|
+
self.__apply_rewrites(parsed_content, rewrites, handler)
|
104
|
+
self.__body.set(parsed_content, content_type)
|
105
|
+
|
106
|
+
def __filter_custom_headers(self, response_headers: Headers):
|
107
|
+
'''
|
108
|
+
Remove custom headers
|
109
|
+
'''
|
110
|
+
_response_headers = Headers(**response_headers)
|
111
|
+
|
112
|
+
headers = custom_headers.__dict__
|
113
|
+
for key in headers:
|
114
|
+
if key[0:2] == '__' and key[-2:] == '__':
|
115
|
+
continue
|
116
|
+
|
117
|
+
name = headers[key]
|
118
|
+
|
119
|
+
if name not in response_headers:
|
120
|
+
continue
|
121
|
+
|
122
|
+
_response_headers.pop(name)
|
123
|
+
|
124
|
+
return _response_headers
|
@@ -27,6 +27,6 @@ def join_rewritten_request(flow: MitmproxyHTTPFlow, intercept_settings: Intercep
|
|
27
27
|
rewrite_rules = intercept_settings.record_rewrite_rules
|
28
28
|
|
29
29
|
request.with_parameter_rules(rewrite_rules).with_url_rules(rewrite_rules).rewrite()
|
30
|
-
response.
|
30
|
+
response.with_parameter_rules(rewrite_rules, request).rewrite()
|
31
31
|
|
32
32
|
return join_request(request, response, intercept_settings)
|
@@ -3,6 +3,7 @@ from typing import Literal
|
|
3
3
|
HEADER = 'Header'
|
4
4
|
BODY_PARAM = 'Body Param'
|
5
5
|
QUERY_PARAM = 'Query Param'
|
6
|
+
RESPONSE_HEADER = 'Response Header'
|
6
7
|
RESPONSE_PARAM = 'Response Param'
|
7
8
|
|
8
|
-
RequestComponent = Literal[BODY_PARAM, HEADER, QUERY_PARAM, RESPONSE_PARAM]
|
9
|
+
RequestComponent = Literal[BODY_PARAM, HEADER, QUERY_PARAM, RESPONSE_HEADER, RESPONSE_PARAM]
|
@@ -2,28 +2,28 @@ ALIAS_RESOLVE_STRATEGY = 'X-Stoobly-Alias-Resolve-Strategy'
|
|
2
2
|
CONTENT_TYPE = 'X-Stoobly-Content-Type'
|
3
3
|
CONTENT_TYPE_TEST_RESULTS = 'test/results'
|
4
4
|
REMOTE_PROJECT_KEY = 'X-Stoobly-Endpoints-Project-Id'
|
5
|
-
MOCK_POLICY = 'X-Mock-Policy'
|
5
|
+
MOCK_POLICY = 'X-Stoobly-Mock-Policy'
|
6
6
|
MOCK_REQUEST_ID = 'X-Stoobly-Request-Id'
|
7
7
|
MOCK_REQUEST_ENDPOINT_ID = 'X-Stoobly-Request-Endpoint-Id'
|
8
8
|
MOCK_REQUEST_KEY = 'X-Stoobly-Request-Key'
|
9
|
-
DO_PROXY = 'X-Do-Proxy'
|
9
|
+
DO_PROXY = 'X-Stoobly-Do-Proxy'
|
10
10
|
LIFECYCLE_HOOKS_PATH = 'X-Stoobly-Lifecycle-Hooks-Path'
|
11
|
-
PROJECT_KEY = 'X-Project-Key'
|
12
|
-
PROXY_MODE = 'X-Proxy-Mode'
|
11
|
+
PROJECT_KEY = 'X-Stoobly-Project-Key'
|
12
|
+
PROXY_MODE = 'X-Stoobly-Proxy-Mode'
|
13
13
|
PUBLIC_DIRECTORY_PATH = 'X-Stoobly-Public-Directory-Path'
|
14
|
-
RECORD_POLICY = 'X-Record-Policy'
|
15
|
-
REPORT_KEY = 'X-Report-Key'
|
16
|
-
REQUEST_ORIGIN = 'X-
|
14
|
+
RECORD_POLICY = 'X-Stoobly-Record-Policy'
|
15
|
+
REPORT_KEY = 'X-Stoobly-Report-Key'
|
16
|
+
REQUEST_ORIGIN = 'X-Stoobly-Request-Origin'
|
17
17
|
RESPONSE_FIXTURES_PATH = 'X-Stoobly-Response-Fixtures-Path'
|
18
|
-
RESPONSE_ID = 'X-Response-Id'
|
18
|
+
RESPONSE_ID = 'X-Stoobly-Response-Id'
|
19
19
|
RESPONSE_LATENCY = 'X-Stoobly-Request-Response-Latency'
|
20
|
-
RESPONSE_PROXY_MODE = 'X-Response-Proxy-Mode'
|
21
|
-
SCENARIO_KEY = 'X-Scenario-Key'
|
22
|
-
SERVICE_URL = 'X-Service-Url'
|
23
|
-
TEST_FILTER = 'X-Test-Filter'
|
20
|
+
RESPONSE_PROXY_MODE = 'X-Stoobly-Response-Proxy-Mode'
|
21
|
+
SCENARIO_KEY = 'X-Stoobly-Scenario-Key'
|
22
|
+
SERVICE_URL = 'X-Stoobly-Service-Url'
|
23
|
+
TEST_FILTER = 'X-Stoobly-Test-Filter'
|
24
24
|
TEST_ID = 'X-Stoobly-Test-Id'
|
25
25
|
TEST_SAVE_RESULTS = 'X-Stoobly-Test-Save-Results'
|
26
26
|
TEST_SKIP = 'X-Stoobly-Test-Skip'
|
27
|
-
TEST_STRATEGY = 'X-Test-Strategy'
|
27
|
+
TEST_STRATEGY = 'X-Stoobly-Test-Strategy'
|
28
28
|
TRACE_ID = 'X-Stoobly-Trace-Id'
|
29
29
|
TRACE_REQUEST_ID = 'X-Stoobly-Trace-Request-Id'
|