stoobly-agent 0.21.2__py3-none-any.whl → 0.22.3__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 +2 -1
- stoobly_agent/app/api/bodies_controller.py +2 -2
- stoobly_agent/app/api/requests_controller.py +28 -10
- stoobly_agent/app/api/simple_http_request_handler.py +1 -1
- stoobly_agent/app/cli/ca_cert_installer.py +16 -5
- stoobly_agent/app/cli/config_cli.py +32 -20
- stoobly_agent/app/cli/helpers/context.py +7 -0
- stoobly_agent/app/cli/helpers/handle_mock_service.py +14 -0
- stoobly_agent/app/cli/helpers/print_service.py +1 -1
- stoobly_agent/app/cli/intercept.py +46 -2
- stoobly_agent/app/cli/main_group.py +3 -3
- stoobly_agent/app/cli/project_cli.py +3 -1
- stoobly_agent/app/cli/request_cli.py +6 -10
- stoobly_agent/app/models/adapters/mitmproxy/__init__.py +2 -0
- stoobly_agent/app/models/adapters/mitmproxy/request/__init__.py +12 -0
- stoobly_agent/app/models/adapters/mitmproxy/request/python_adapter.py +16 -0
- stoobly_agent/app/models/adapters/mitmproxy/response/__init__.py +12 -0
- stoobly_agent/app/models/adapters/mitmproxy/response/python_adapter.py +24 -0
- stoobly_agent/app/models/adapters/python/__init__.py +2 -0
- stoobly_agent/app/models/adapters/python/request/__init__.py +18 -0
- stoobly_agent/app/models/adapters/python/request/mitmproxy_adapter.py +53 -0
- stoobly_agent/app/models/adapters/python/request/stoobly_adapter.py +49 -0
- stoobly_agent/app/models/adapters/python/response/__init__.py +13 -0
- stoobly_agent/app/models/adapters/{mitmproxy_response_adapter.py → python/response/mitmproxy_adapter.py} +15 -7
- stoobly_agent/app/models/adapters/raw_http_response_adapter.py +10 -3
- stoobly_agent/app/models/adapters/stoobly/request/__init__.py +11 -0
- stoobly_agent/app/models/adapters/stoobly/request/mitmproxy_adapter.py +32 -0
- stoobly_agent/app/models/body_model.py +2 -2
- stoobly_agent/app/models/factories/__init__.py +0 -0
- stoobly_agent/app/models/factories/resource/__init__.py +0 -0
- stoobly_agent/app/models/{adapters/body_adapter_factory.py → factories/resource/body.py} +1 -1
- stoobly_agent/app/models/{adapters/header_adapter_factory.py → factories/resource/header.py} +1 -1
- stoobly_agent/app/models/factories/resource/local_db/__init__.py +0 -0
- stoobly_agent/app/models/{adapters → factories/resource}/local_db/replayed_response_adapter.py +1 -2
- stoobly_agent/app/models/{adapters → factories/resource}/local_db/request_adapter.py +15 -8
- stoobly_agent/app/models/{adapters → factories/resource}/local_db/scenario_adapter.py +13 -7
- stoobly_agent/app/models/{adapters/query_param_adapter_factory.py → factories/resource/query_param.py} +1 -1
- stoobly_agent/app/models/{adapters/replayed_response_adapter_factory.py → factories/resource/replayed_response.py} +1 -1
- stoobly_agent/app/models/{adapters/request_adapter_factory.py → factories/resource/request.py} +2 -2
- stoobly_agent/app/models/{adapters/response_adapter_factory.py → factories/resource/response.py} +1 -1
- stoobly_agent/app/models/{adapters/response_header_adapter_factory.py → factories/resource/response_header.py} +1 -1
- stoobly_agent/app/models/{adapters/scenario_adapter_factory.py → factories/resource/scenario.py} +2 -2
- stoobly_agent/app/models/factories/resource/stoobly/__init__.py +0 -0
- stoobly_agent/app/models/{adapters/stoobly_request_adapter.py → factories/resource/stoobly/request_adapter.py} +1 -2
- stoobly_agent/app/models/{adapters/stoobly_scenario_adapter.py → factories/resource/stoobly/scenario_adapter.py} +1 -2
- stoobly_agent/app/models/header_model.py +2 -2
- stoobly_agent/app/models/query_param_model.py +2 -2
- stoobly_agent/app/models/replayed_response_model.py +2 -2
- stoobly_agent/app/models/request_model.py +4 -5
- stoobly_agent/app/models/response_header_model.py +2 -2
- stoobly_agent/app/models/response_model.py +2 -2
- stoobly_agent/app/models/scenario_model.py +4 -4
- stoobly_agent/app/models/schemas/request.py +8 -1
- stoobly_agent/app/models/types/__init__.py +6 -0
- stoobly_agent/app/proxy/__init__.py +0 -5
- stoobly_agent/app/proxy/handle_mock_service.py +0 -3
- stoobly_agent/app/proxy/handle_test_service.py +1 -1
- stoobly_agent/app/proxy/intercept_handler.py +0 -1
- stoobly_agent/app/proxy/mitmproxy/flow_mock.py +35 -0
- stoobly_agent/app/proxy/mitmproxy/request_facade.py +11 -2
- stoobly_agent/app/proxy/replay/replay_request_service.py +57 -40
- stoobly_agent/app/proxy/simulate_intercept_service.py +30 -0
- stoobly_agent/app/proxy/test/__init__.py +1 -0
- stoobly_agent/app/proxy/test/context.py +2 -1
- stoobly_agent/app/proxy/test/context_abc.py +155 -0
- stoobly_agent/app/proxy/test/matchers/context.py +1 -1
- stoobly_agent/app/proxy/test/matchers/contract.py +1 -1
- stoobly_agent/app/proxy/test/matchers/custom.py +1 -1
- stoobly_agent/app/proxy/test/matchers/diff.py +1 -1
- stoobly_agent/app/proxy/test/matchers/fuzzy.py +1 -1
- stoobly_agent/app/proxy/test/test_service.py +1 -1
- stoobly_agent/app/proxy/upload/joined_request.py +0 -1
- stoobly_agent/app/proxy/upload/proxy_request.py +4 -1
- stoobly_agent/app/proxy/upload/request_string.py +1 -1
- stoobly_agent/app/settings/constants/mode.py +3 -0
- stoobly_agent/cli.py +76 -7
- stoobly_agent/config/constants/env_vars.py +2 -1
- stoobly_agent/config/data_dir.py +11 -8
- stoobly_agent/lib/api/api.py +4 -4
- stoobly_agent/lib/api/keys/organization_key.py +2 -0
- stoobly_agent/lib/api/keys/project_key.py +9 -0
- stoobly_agent/lib/api/scenarios_resource.py +1 -1
- stoobly_agent/lib/api/stoobly_api.py +9 -5
- stoobly_agent/lib/orm/__init__.py +1 -1
- stoobly_agent/lib/orm/replayed_response.py +3 -3
- stoobly_agent/lib/orm/request.py +25 -5
- stoobly_agent/lib/orm/transformers/orm_to_stoobly_request_transformer.py +1 -1
- stoobly_agent/mock.py +94 -0
- stoobly_agent/public/{2-es2015.3d54569af612a07a2e06.js → 1-es2015.37917aa26708d8f35d36.js} +1 -1
- stoobly_agent/public/{2-es5.3d54569af612a07a2e06.js → 1-es5.37917aa26708d8f35d36.js} +1 -1
- stoobly_agent/public/10-es2015.e9556b0d0f0e92fb548b.js +1 -0
- stoobly_agent/public/10-es5.e9556b0d0f0e92fb548b.js +1 -0
- stoobly_agent/public/11-es2015.bc6212fccbe72a623f81.js +1 -0
- stoobly_agent/public/11-es5.bc6212fccbe72a623f81.js +1 -0
- stoobly_agent/public/{13-es2015.76b6c147b0c46f995cd7.js → 12-es2015.d0768894ddffd6efa5e5.js} +1 -1
- stoobly_agent/public/{13-es5.76b6c147b0c46f995cd7.js → 12-es5.d0768894ddffd6efa5e5.js} +1 -1
- stoobly_agent/public/13-es2015.8a044490a76fd298162d.js +1 -0
- stoobly_agent/public/13-es5.8a044490a76fd298162d.js +1 -0
- stoobly_agent/public/{15-es2015.60c3b41c385f5bdedb7b.js → 14-es2015.1cd1a021e51ca0e62e1c.js} +1 -1
- stoobly_agent/public/{15-es5.60c3b41c385f5bdedb7b.js → 14-es5.1cd1a021e51ca0e62e1c.js} +1 -1
- stoobly_agent/public/{16-es2015.5d395009a77978db4405.js → 15-es2015.587781d19864ff0eb4f5.js} +1 -1
- stoobly_agent/public/{16-es5.5d395009a77978db4405.js → 15-es5.587781d19864ff0eb4f5.js} +1 -1
- stoobly_agent/public/16-es2015.ec6a175b1f9578203cd8.js +1 -0
- stoobly_agent/public/16-es5.ec6a175b1f9578203cd8.js +1 -0
- stoobly_agent/public/17-es2015.ad9c4756c96a15bd29d7.js +1 -0
- stoobly_agent/public/17-es5.ad9c4756c96a15bd29d7.js +1 -0
- stoobly_agent/public/{19-es2015.517f68e08f4c582dae66.js → 18-es2015.8583df0f8eccb3e47c0b.js} +1 -1
- stoobly_agent/public/{19-es5.517f68e08f4c582dae66.js → 18-es5.8583df0f8eccb3e47c0b.js} +1 -1
- stoobly_agent/public/{20-es2015.473486aabfa4d4a6431b.js → 19-es2015.d0225852a844dc03a09f.js} +1 -1
- stoobly_agent/public/{20-es5.473486aabfa4d4a6431b.js → 19-es5.d0225852a844dc03a09f.js} +1 -1
- stoobly_agent/public/{3-es5.1dad290844ea619e4c16.js → 2-es2015.8f184ac63348ba447b1f.js} +1 -1
- stoobly_agent/public/{3-es2015.1dad290844ea619e4c16.js → 2-es5.8f184ac63348ba447b1f.js} +1 -1
- stoobly_agent/public/{21-es2015.56aa10803cc1348a55a3.js → 20-es2015.f7c107847935264d58aa.js} +1 -1
- stoobly_agent/public/{21-es5.56aa10803cc1348a55a3.js → 20-es5.f7c107847935264d58aa.js} +1 -1
- stoobly_agent/public/{22-es2015.46d81010003b2a50eeab.js → 21-es2015.dd358e1edaf3d32dd2c0.js} +1 -1
- stoobly_agent/public/{22-es5.46d81010003b2a50eeab.js → 21-es5.dd358e1edaf3d32dd2c0.js} +1 -1
- stoobly_agent/public/26-es2015.6332c32f1b7c8c288f2f.js +1 -0
- stoobly_agent/public/26-es5.6332c32f1b7c8c288f2f.js +1 -0
- stoobly_agent/public/27-es2015.af505e744b0c869a93d1.js +1 -0
- stoobly_agent/public/27-es5.af505e744b0c869a93d1.js +1 -0
- stoobly_agent/public/28-es2015.7c7c0f64e4af29d2e4d4.js +1 -0
- stoobly_agent/public/28-es5.7c7c0f64e4af29d2e4d4.js +1 -0
- stoobly_agent/public/32-es2015.2ab8267be7275dee9059.js +1 -0
- stoobly_agent/public/32-es5.2ab8267be7275dee9059.js +1 -0
- stoobly_agent/public/{34-es2015.ef24f6f7630620a38b19.js → 33-es2015.5b575f3a87c6c2c6b93b.js} +1 -1
- stoobly_agent/public/{34-es5.ef24f6f7630620a38b19.js → 33-es5.5b575f3a87c6c2c6b93b.js} +1 -1
- stoobly_agent/public/{35-es2015.0667e742725cc828f59f.js → 34-es2015.6a1160649c718cdb9338.js} +1 -1
- stoobly_agent/public/{35-es5.0667e742725cc828f59f.js → 34-es5.6a1160649c718cdb9338.js} +1 -1
- stoobly_agent/public/35-es2015.1b9dc7a46a6d5296c3ae.js +1 -0
- stoobly_agent/public/35-es5.1b9dc7a46a6d5296c3ae.js +1 -0
- stoobly_agent/public/{37-es2015.50d0c2d3fe4d0a74fc8f.js → 36-es2015.2fc9151fe6a5ff2bff31.js} +1 -1
- stoobly_agent/public/{37-es5.50d0c2d3fe4d0a74fc8f.js → 36-es5.2fc9151fe6a5ff2bff31.js} +1 -1
- stoobly_agent/public/{38-es2015.c14bde0b8d0cc14e915e.js → 37-es2015.ac7108c3625fd6e1d981.js} +1 -1
- stoobly_agent/public/{38-es5.c14bde0b8d0cc14e915e.js → 37-es5.ac7108c3625fd6e1d981.js} +1 -1
- stoobly_agent/public/38-es2015.9c183b14373c0e449932.js +1 -0
- stoobly_agent/public/38-es5.9c183b14373c0e449932.js +1 -0
- stoobly_agent/public/39-es2015.4624cdeb29fe9850b216.js +1 -0
- stoobly_agent/public/39-es5.4624cdeb29fe9850b216.js +1 -0
- stoobly_agent/public/40-es2015.24d981230c0c8f369cde.js +1 -0
- stoobly_agent/public/40-es5.24d981230c0c8f369cde.js +1 -0
- stoobly_agent/public/{46-es2015.abc7e4fd207d54277fcb.js → 45-es2015.c76937ed45d460bcd36f.js} +1 -1
- stoobly_agent/public/{46-es5.abc7e4fd207d54277fcb.js → 45-es5.c76937ed45d460bcd36f.js} +1 -1
- stoobly_agent/public/6-es2015.53acc5d2ca7f48ef857f.js +1 -0
- stoobly_agent/public/6-es5.53acc5d2ca7f48ef857f.js +1 -0
- stoobly_agent/public/7-es2015.1c6b3d315d50ccd228cb.js +1 -0
- stoobly_agent/public/7-es5.1c6b3d315d50ccd228cb.js +1 -0
- stoobly_agent/public/{9-es2015.ef0f7cb32f5fadb085d0.js → 8-es2015.0fe7492f7b61eb4699b6.js} +1 -1
- stoobly_agent/public/{9-es5.ef0f7cb32f5fadb085d0.js → 8-es5.0fe7492f7b61eb4699b6.js} +1 -1
- stoobly_agent/public/common-es2015.86f70de6df2c705a87f6.js +1 -0
- stoobly_agent/public/common-es5.86f70de6df2c705a87f6.js +1 -0
- stoobly_agent/public/dashboard.agent-alpha-0.22.3.tar.gz +0 -0
- stoobly_agent/public/index.html +1 -1
- stoobly_agent/public/main-es2015.aceb967cb4cccc0bc521.js +1 -0
- stoobly_agent/public/main-es5.aceb967cb4cccc0bc521.js +1 -0
- stoobly_agent/public/{polyfills-es2015.2b40b2ecdf98a9210572.js → polyfills-es2015.580f7a6e775c2c348c9d.js} +1 -1
- stoobly_agent/public/{polyfills-es5.d9fb2eee68607c3f7f64.js → polyfills-es5.4c3461a071d35be3dd81.js} +1 -1
- stoobly_agent/public/runtime-es2015.49eaebd2913fa7fe2b97.js +1 -0
- stoobly_agent/public/runtime-es5.49eaebd2913fa7fe2b97.js +1 -0
- stoobly_agent/test/test_helper.py +4 -5
- {stoobly_agent-0.21.2.dist-info → stoobly_agent-0.22.3.dist-info}/METADATA +1 -1
- {stoobly_agent-0.21.2.dist-info → stoobly_agent-0.22.3.dist-info}/RECORD +175 -157
- {stoobly_agent-0.21.2.dist-info → stoobly_agent-0.22.3.dist-info}/WHEEL +1 -1
- stoobly_agent/app/models/adapters/mitmproxy_request_adapter.py +0 -60
- stoobly_agent/app/models/adapters/types/__init__.py +0 -3
- stoobly_agent/public/11-es2015.b85bdc528bab0ee542fe.js +0 -1
- stoobly_agent/public/11-es5.b85bdc528bab0ee542fe.js +0 -1
- stoobly_agent/public/12-es2015.72399d40488de533bb97.js +0 -1
- stoobly_agent/public/12-es5.72399d40488de533bb97.js +0 -1
- stoobly_agent/public/14-es2015.7cedfd0829bd7a64677a.js +0 -1
- stoobly_agent/public/14-es5.7cedfd0829bd7a64677a.js +0 -1
- stoobly_agent/public/17-es2015.f6f28ba9f681063632a8.js +0 -1
- stoobly_agent/public/17-es5.f6f28ba9f681063632a8.js +0 -1
- stoobly_agent/public/18-es2015.b0cd6822ebd1090b0d60.js +0 -1
- stoobly_agent/public/18-es5.b0cd6822ebd1090b0d60.js +0 -1
- stoobly_agent/public/27-es2015.4635450ff709f7868796.js +0 -1
- stoobly_agent/public/27-es5.4635450ff709f7868796.js +0 -1
- stoobly_agent/public/28-es2015.1b29b35529772ab108f1.js +0 -1
- stoobly_agent/public/28-es5.1b29b35529772ab108f1.js +0 -1
- stoobly_agent/public/32-es2015.862b67803e6242451976.js +0 -1
- stoobly_agent/public/32-es5.862b67803e6242451976.js +0 -1
- stoobly_agent/public/33-es2015.4ff4325d1aec37e1b43c.js +0 -1
- stoobly_agent/public/33-es5.4ff4325d1aec37e1b43c.js +0 -1
- stoobly_agent/public/36-es2015.cd370fdf8990019d0c8e.js +0 -1
- stoobly_agent/public/36-es5.cd370fdf8990019d0c8e.js +0 -1
- stoobly_agent/public/39-es2015.4ec5fc16202c4759eac4.js +0 -1
- stoobly_agent/public/39-es5.4ec5fc16202c4759eac4.js +0 -1
- stoobly_agent/public/40-es2015.25287ce77b40050f3471.js +0 -1
- stoobly_agent/public/40-es5.25287ce77b40050f3471.js +0 -1
- stoobly_agent/public/45-es2015.fd110741ac0fbaada177.js +0 -1
- stoobly_agent/public/45-es5.fd110741ac0fbaada177.js +0 -1
- stoobly_agent/public/6-es2015.7219d596e3545ebaed3a.js +0 -1
- stoobly_agent/public/6-es5.7219d596e3545ebaed3a.js +0 -1
- stoobly_agent/public/7-es2015.98b085349ebd9d246060.js +0 -1
- stoobly_agent/public/7-es5.98b085349ebd9d246060.js +0 -1
- stoobly_agent/public/8-es2015.335fd5c122ad083aec65.js +0 -1
- stoobly_agent/public/8-es5.335fd5c122ad083aec65.js +0 -1
- stoobly_agent/public/common-es2015.81f870bf87411a04446d.js +0 -1
- stoobly_agent/public/common-es5.81f870bf87411a04446d.js +0 -1
- stoobly_agent/public/dashboard.agent-alpha-0.21.0.tar.gz +0 -0
- stoobly_agent/public/main-es2015.575c1d17fc866d3a6649.js +0 -1
- stoobly_agent/public/main-es5.575c1d17fc866d3a6649.js +0 -1
- stoobly_agent/public/runtime-es2015.8915d042dc9b55368999.js +0 -1
- stoobly_agent/public/runtime-es5.8915d042dc9b55368999.js +0 -1
- /stoobly_agent/app/models/adapters/{local_db → stoobly}/__init__.py +0 -0
- /stoobly_agent/app/models/{adapters → factories/resource}/local_db/body_adapter.py +0 -0
- /stoobly_agent/app/models/{adapters → factories/resource}/local_db/header_adapter.py +0 -0
- /stoobly_agent/app/models/{adapters → factories/resource}/local_db/query_param_adapter.py +0 -0
- /stoobly_agent/app/models/{adapters → factories/resource}/local_db/response_adapter.py +0 -0
- /stoobly_agent/app/models/{adapters → factories/resource}/local_db/response_header_adapter.py +0 -0
- /stoobly_agent/app/models/{adapters/types → types}/replayed_response.py +0 -0
- /stoobly_agent/app/models/{adapters/types → types}/request_create_params.py +0 -0
- /stoobly_agent/app/models/{adapters/types → types}/request_show_params.py +0 -0
- /stoobly_agent/app/models/{adapters/types → types}/scenario_create_params.py +0 -0
- {stoobly_agent-0.21.2.dist-info → stoobly_agent-0.22.3.dist-info}/LICENSE +0 -0
- {stoobly_agent-0.21.2.dist-info → stoobly_agent-0.22.3.dist-info}/entry_points.txt +0 -0
- {stoobly_agent-0.21.2.dist-info → stoobly_agent-0.22.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
import requests
|
2
|
+
|
3
|
+
from mitmproxy.http import Request as MitmproxyRequest, Response as MitmproxyResponse
|
4
|
+
from typing import Union
|
5
|
+
|
6
|
+
from stoobly_agent.app.models.adapters.python import PythonRequestAdapterFactory, PythonResponseAdapterFactory
|
7
|
+
from stoobly_agent.app.models.adapters.raw_http_request_adapter import DEFAULT_HTTP_VERSION
|
8
|
+
|
9
|
+
class MitmproxyFlowMock():
|
10
|
+
|
11
|
+
def __init__(self):
|
12
|
+
self.__request: MitmproxyRequest = None
|
13
|
+
self.__response: MitmproxyResponse = None
|
14
|
+
|
15
|
+
@property
|
16
|
+
def request(self):
|
17
|
+
return self.__request
|
18
|
+
|
19
|
+
@property
|
20
|
+
def response(self):
|
21
|
+
return self.__response
|
22
|
+
|
23
|
+
@request.setter
|
24
|
+
def request(self, _request: Union[MitmproxyRequest, requests.Request]):
|
25
|
+
if isinstance(_request, requests.Request):
|
26
|
+
self.__request = PythonRequestAdapterFactory(_request).mitmproxy_request(DEFAULT_HTTP_VERSION)
|
27
|
+
else:
|
28
|
+
self.__request = _request
|
29
|
+
|
30
|
+
@response.setter
|
31
|
+
def response(self, _response: Union[MitmproxyResponse, requests.Response]):
|
32
|
+
if isinstance(_response, requests.Response):
|
33
|
+
self.__response = PythonResponseAdapterFactory(_response).mitmproxy_response()
|
34
|
+
else:
|
35
|
+
self.__response = _response
|
@@ -5,6 +5,7 @@ from mitmproxy.http import Headers, Request as MitmproxyRequest
|
|
5
5
|
from mitmproxy.coretypes import multidict
|
6
6
|
from typing import Callable, List, Union
|
7
7
|
from urllib.parse import urlparse
|
8
|
+
import requests
|
8
9
|
|
9
10
|
from stoobly_agent.app.settings.constants import request_component
|
10
11
|
from stoobly_agent.app.settings.rewrite_rule import RewriteRule, ParameterRule
|
@@ -53,7 +54,7 @@ class MitmproxyRequestFacade(Request):
|
|
53
54
|
|
54
55
|
@property
|
55
56
|
def body(self):
|
56
|
-
content = self.request.raw_content
|
57
|
+
content = self.request.raw_content or ''
|
57
58
|
|
58
59
|
try:
|
59
60
|
if isinstance(content, bytes):
|
@@ -96,6 +97,14 @@ class MitmproxyRequestFacade(Request):
|
|
96
97
|
def rewrite_rules(self) -> List[ParameterRule]:
|
97
98
|
return self.__rewrite_rules
|
98
99
|
|
100
|
+
def to_python_request(self):
|
101
|
+
return requests.Request(
|
102
|
+
method=self.request.method,
|
103
|
+
url=self.request.url,
|
104
|
+
headers=self.request.headers,
|
105
|
+
data=self.request.content
|
106
|
+
)
|
107
|
+
|
99
108
|
def with_rewrite_rules(self, rules: List[RewriteRule]):
|
100
109
|
if type(rules) == list:
|
101
110
|
self.__rewrite_rules = self.select_parameter_rules(rules)
|
@@ -154,7 +163,7 @@ class MitmproxyRequestFacade(Request):
|
|
154
163
|
})
|
155
164
|
|
156
165
|
def __rewrite_handler(self, rewrite: ParameterRule) -> str:
|
157
|
-
Logger.instance().
|
166
|
+
Logger.instance().info(f"{bcolors.OKCYAN}Rewriting {rewrite.type.lower()}{bcolors.ENDC} {rewrite.name} => {rewrite.value}")
|
158
167
|
return rewrite.value
|
159
168
|
|
160
169
|
def __rewrite_headers(self, rewrites: List[ParameterRule]):
|
@@ -5,16 +5,17 @@ import requests
|
|
5
5
|
from http.cookies import SimpleCookie
|
6
6
|
from typing import Callable, TypedDict, Union
|
7
7
|
|
8
|
+
from stoobly_agent.app.cli.ca_cert_installer import CACertInstaller
|
8
9
|
from stoobly_agent.app.cli.helpers.context import ReplayContext
|
9
10
|
from stoobly_agent.app.models.schemas.request import Request
|
10
11
|
from stoobly_agent.app.proxy.intercept_settings import InterceptSettings
|
11
12
|
from stoobly_agent.app.proxy.mitmproxy.request_facade import MitmproxyRequestFacade
|
12
13
|
from stoobly_agent.app.proxy.replay.trace_context import TraceContext
|
14
|
+
from stoobly_agent.app.proxy.simulate_intercept_service import simulate_intercept
|
13
15
|
from stoobly_agent.app.settings import Settings
|
14
16
|
from stoobly_agent.config.constants import alias_resolve_strategy, custom_headers, request_origin, test_filter, test_strategy
|
15
17
|
from stoobly_agent.config.mitmproxy import MitmproxyConfig
|
16
|
-
from stoobly_agent.config.constants import mode
|
17
|
-
from stoobly_agent.lib.api.api import Api
|
18
|
+
from stoobly_agent.config.constants import mock_policy, mode
|
18
19
|
|
19
20
|
class ReplayRequestOptions(TypedDict):
|
20
21
|
alias_resolve_strategy: alias_resolve_strategy.AliasResolveStrategy
|
@@ -25,6 +26,7 @@ class ReplayRequestOptions(TypedDict):
|
|
25
26
|
before_replay: Union[Callable[[ReplayContext], None], None]
|
26
27
|
after_replay: Union[Callable[[ReplayContext], Union[requests.Response, None]], None]
|
27
28
|
project_key: Union[str, None]
|
29
|
+
proxies: dict
|
28
30
|
report_key: Union[str, None]
|
29
31
|
request_origin: Union[request_origin.CLI, None]
|
30
32
|
scenario_key: Union[str, None]
|
@@ -37,17 +39,14 @@ def replay_with_trace(context: ReplayContext, trace_context: TraceContext, optio
|
|
37
39
|
trace_context.alias_resolve_strategy = options.get('alias_resolve_strategy')
|
38
40
|
return trace_context.with_replay_context(context, lambda context: replay(context, options))
|
39
41
|
|
40
|
-
def replay_with_rewrite(context: ReplayContext) -> requests.Response:
|
41
|
-
return replay(context, {
|
42
|
-
'before_replay': __handle_before_replay
|
43
|
-
})
|
44
|
-
|
45
42
|
def replay(context: ReplayContext, options: ReplayRequestOptions) -> requests.Response:
|
46
43
|
if 'before_replay' in options and callable(options['before_replay']):
|
47
44
|
options['before_replay'](context)
|
48
45
|
|
49
46
|
request = context.request
|
50
47
|
headers = request.headers
|
48
|
+
method = request.method
|
49
|
+
cookies = __get_cookies(headers)
|
51
50
|
|
52
51
|
if options.get('host'):
|
53
52
|
request.host = options['host']
|
@@ -56,48 +55,70 @@ def replay(context: ReplayContext, options: ReplayRequestOptions) -> requests.Re
|
|
56
55
|
if options.get('scheme'):
|
57
56
|
request.scheme = options['scheme']
|
58
57
|
|
59
|
-
if 'alias_resolve_strategy'
|
58
|
+
if options.get('alias_resolve_strategy'):
|
60
59
|
headers[custom_headers.ALIAS_RESOLVE_STRATEGY] = options['alias_resolve_strategy']
|
61
60
|
|
62
|
-
if 'lifecycle_hooks_script_path'
|
61
|
+
if options.get('lifecycle_hooks_script_path'):
|
63
62
|
__handle_lifecycle_hooks_script_path(options['lifecycle_hooks_script_path'], headers)
|
64
63
|
|
65
|
-
if 'mode'
|
64
|
+
if options.get('mode'):
|
66
65
|
__handle_mode_option(options['mode'], request, headers)
|
67
66
|
|
68
|
-
if 'project_key'
|
67
|
+
if options.get('project_key'):
|
69
68
|
headers[custom_headers.PROJECT_KEY] = options['project_key']
|
70
69
|
|
71
|
-
if 'report_key'
|
70
|
+
if options.get('report_key'):
|
72
71
|
headers[custom_headers.REPORT_KEY] = options['report_key']
|
73
72
|
|
74
|
-
if 'request_origin'
|
73
|
+
if options.get('request_origin'):
|
75
74
|
headers[custom_headers.REQUEST_ORIGIN] = options['request_origin']
|
76
75
|
|
77
|
-
if 'scenario_key'
|
76
|
+
if options.get('scenario_key'):
|
78
77
|
headers[custom_headers.SCENARIO_KEY] = options['scenario_key']
|
79
78
|
|
80
|
-
if 'test_filter'
|
79
|
+
if options.get('test_filter'):
|
81
80
|
headers[custom_headers.TEST_FILTER] = options['test_filter']
|
82
81
|
|
83
|
-
if 'test_strategy'
|
82
|
+
if options.get('test_strategy'):
|
84
83
|
headers[custom_headers.TEST_STRATEGY] = options['test_strategy']
|
85
84
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
85
|
+
request_config = {
|
86
|
+
'allow_redirects': True,
|
87
|
+
'stream': True,
|
88
|
+
'verify': not MitmproxyConfig.instance().get('ssl_insecure')
|
89
|
+
}
|
90
|
+
request_dict = {
|
91
|
+
'cookies': cookies,
|
92
|
+
'data': request.body,
|
93
|
+
'headers': headers,
|
94
|
+
#'params': request.query_params, # Do not send query params, they should be a part of the URL
|
95
|
+
}
|
96
|
+
|
97
|
+
res: requests.Response = None
|
98
|
+
if not options.get('proxy'):
|
99
|
+
request = requests.Request(**{
|
100
|
+
**request_dict,
|
101
|
+
'method': method,
|
102
|
+
'url': request.url,
|
103
|
+
})
|
104
|
+
|
105
|
+
res = simulate_intercept(request, **request_config)
|
106
|
+
else:
|
107
|
+
settings = Settings.instance()
|
108
|
+
handler = getattr(requests, method.lower())
|
109
|
+
|
110
|
+
res = handler(
|
111
|
+
request.url,
|
112
|
+
**{
|
113
|
+
**request_config,
|
114
|
+
**request_dict,
|
115
|
+
'proxies': {
|
116
|
+
'http': settings.proxy.url,
|
117
|
+
'https': settings.proxy.url,
|
118
|
+
},
|
119
|
+
'verify': CACertInstaller().mitm_crt_absolute_path if request_config['verify'] else False,
|
120
|
+
}
|
121
|
+
)
|
101
122
|
|
102
123
|
if 'after_replay' in options and callable(options['after_replay']):
|
103
124
|
context.with_response(res)
|
@@ -119,7 +140,10 @@ def __handle_mode_option(_mode, request: Request, headers):
|
|
119
140
|
|
120
141
|
if _mode == mode.MOCK or _mode == mode.TEST:
|
121
142
|
# If mocking or testing, we already know which request to get response from
|
122
|
-
|
143
|
+
if request.id:
|
144
|
+
headers[custom_headers.MOCK_REQUEST_ID] = str(request.id)
|
145
|
+
|
146
|
+
headers[custom_headers.MOCK_POLICY] = mock_policy.ALL
|
123
147
|
elif _mode == mode.RECORD:
|
124
148
|
# If recording, then it's actually a replay and record
|
125
149
|
headers[custom_headers.PROXY_MODE] = mode.REPLAY
|
@@ -127,11 +151,4 @@ def __handle_mode_option(_mode, request: Request, headers):
|
|
127
151
|
|
128
152
|
def __get_cookies(headers: Request.headers):
|
129
153
|
cookies = SimpleCookie(headers.get('Cookie')).items()
|
130
|
-
return {k: v.value for k, v in cookies}
|
131
|
-
|
132
|
-
def __handle_before_replay(context: ReplayContext):
|
133
|
-
request = context.request
|
134
|
-
request_facade = MitmproxyRequestFacade(request)
|
135
|
-
intercept_settings = InterceptSettings(Settings.instance())
|
136
|
-
rewrite_rules = intercept_settings.replay_rewrite_rules
|
137
|
-
request_facade.with_rewrite_rules(rewrite_rules).rewrite()
|
154
|
+
return {k: v.value for k, v in cookies}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import pdb
|
2
|
+
import requests
|
3
|
+
|
4
|
+
from mitmproxy.http import Request as MitmproxyRequest
|
5
|
+
|
6
|
+
from stoobly_agent.app.proxy.mitmproxy.flow_mock import MitmproxyFlowMock
|
7
|
+
from stoobly_agent.app.models.adapters.mitmproxy import MitmproxyRequestAdapterFactory, MitmproxyResponseAdapterFactory
|
8
|
+
|
9
|
+
from .intercept_handler import request as handle_request, response as handle_response
|
10
|
+
|
11
|
+
def simulate_intercept(request: requests.Request, **config):
|
12
|
+
flow = MitmproxyFlowMock()
|
13
|
+
|
14
|
+
flow.request = request
|
15
|
+
|
16
|
+
handle_request(flow)
|
17
|
+
|
18
|
+
if not flow.response:
|
19
|
+
session = requests.Session()
|
20
|
+
prepared_request = __prepare_mitmproxy_request(flow.request)
|
21
|
+
res = session.send(prepared_request, **config)
|
22
|
+
flow.response = res
|
23
|
+
|
24
|
+
handle_response(flow)
|
25
|
+
|
26
|
+
return MitmproxyResponseAdapterFactory(flow.response).python_response()
|
27
|
+
|
28
|
+
def __prepare_mitmproxy_request(request: MitmproxyRequest) -> requests.Request:
|
29
|
+
_request = MitmproxyRequestAdapterFactory(request).python_request()
|
30
|
+
return _request.prepare()
|
@@ -20,10 +20,11 @@ from stoobly_agent.lib.api.interfaces.endpoints import EndpointShowResponse
|
|
20
20
|
from stoobly_agent.lib.orm.trace import Trace
|
21
21
|
|
22
22
|
from .context_response import TestContextResponse
|
23
|
+
from .context_abc import TestContextABC
|
23
24
|
|
24
25
|
FuzzyContent = Union[dict, list, str]
|
25
26
|
|
26
|
-
class TestContext():
|
27
|
+
class TestContext(TestContextABC):
|
27
28
|
def __init__(self, replay_context: ReplayContext, mock_context: MockContext):
|
28
29
|
self.__flow = mock_context.flow
|
29
30
|
self.__intercept_settings = mock_context.intercept_settings
|
@@ -0,0 +1,155 @@
|
|
1
|
+
import abc
|
2
|
+
|
3
|
+
from mitmproxy.http import Request
|
4
|
+
from typing import Union
|
5
|
+
|
6
|
+
from stoobly_agent.app.proxy.test.helpers.endpoint_facade import EndpointFacade
|
7
|
+
from stoobly_agent.app.proxy.test.helpers.request_component_names_facade import RequestComponentNamesFacade
|
8
|
+
|
9
|
+
from stoobly_agent.lib.api.interfaces.endpoints import EndpointShowResponse
|
10
|
+
from stoobly_agent.lib.orm.trace import Trace
|
11
|
+
|
12
|
+
from .context_response import TestContextResponse
|
13
|
+
|
14
|
+
FuzzyContent = Union[dict, list, str]
|
15
|
+
|
16
|
+
class TestContextABC(abc.ABC):
|
17
|
+
|
18
|
+
@abc.abstractmethod
|
19
|
+
def with_endpoints_resource(self, resource: EndpointShowResponse):
|
20
|
+
pass
|
21
|
+
|
22
|
+
@property
|
23
|
+
@abc.abstractmethod
|
24
|
+
def cached_expected_response_content(self) -> Union[bytes, None, str]:
|
25
|
+
pass
|
26
|
+
|
27
|
+
@property
|
28
|
+
@abc.abstractmethod
|
29
|
+
def endpoint(self) -> EndpointFacade:
|
30
|
+
pass
|
31
|
+
|
32
|
+
@property
|
33
|
+
@abc.abstractmethod
|
34
|
+
def lifecycle_hooks(self):
|
35
|
+
pass
|
36
|
+
|
37
|
+
@property
|
38
|
+
@abc.abstractmethod
|
39
|
+
def lifecycle_hooks_script_path(self):
|
40
|
+
pass
|
41
|
+
|
42
|
+
@property
|
43
|
+
@abc.abstractmethod
|
44
|
+
def end_time(self):
|
45
|
+
pass
|
46
|
+
|
47
|
+
@property
|
48
|
+
@abc.abstractmethod
|
49
|
+
def expected_response(self) -> TestContextResponse:
|
50
|
+
pass
|
51
|
+
|
52
|
+
@property
|
53
|
+
@abc.abstractmethod
|
54
|
+
def decoded_response_content(self) -> FuzzyContent:
|
55
|
+
pass
|
56
|
+
|
57
|
+
@property
|
58
|
+
@abc.abstractmethod
|
59
|
+
def decoded_expected_response_content(self) -> FuzzyContent:
|
60
|
+
pass
|
61
|
+
|
62
|
+
@property
|
63
|
+
@abc.abstractmethod
|
64
|
+
def filter(self):
|
65
|
+
pass
|
66
|
+
|
67
|
+
@property
|
68
|
+
@abc.abstractmethod
|
69
|
+
def flow(self):
|
70
|
+
pass
|
71
|
+
|
72
|
+
@property
|
73
|
+
@abc.abstractmethod
|
74
|
+
def intercept_settings(self):
|
75
|
+
pass
|
76
|
+
|
77
|
+
@property
|
78
|
+
@abc.abstractmethod
|
79
|
+
def log(self):
|
80
|
+
pass
|
81
|
+
|
82
|
+
@log.setter
|
83
|
+
@abc.abstractmethod
|
84
|
+
def log(self, v: str):
|
85
|
+
pass
|
86
|
+
|
87
|
+
@property
|
88
|
+
@abc.abstractmethod
|
89
|
+
def mock_request_id(self) -> Union[str, None]:
|
90
|
+
pass
|
91
|
+
|
92
|
+
@property
|
93
|
+
@abc.abstractmethod
|
94
|
+
def mock_request_endpoint_id(self) -> Union[str, None]:
|
95
|
+
pass
|
96
|
+
|
97
|
+
@property
|
98
|
+
@abc.abstractmethod
|
99
|
+
def passed(self):
|
100
|
+
pass
|
101
|
+
|
102
|
+
@passed.setter
|
103
|
+
@abc.abstractmethod
|
104
|
+
def passed(self, v):
|
105
|
+
pass
|
106
|
+
|
107
|
+
@property
|
108
|
+
@abc.abstractmethod
|
109
|
+
def request(self) -> Request:
|
110
|
+
pass
|
111
|
+
|
112
|
+
@property
|
113
|
+
@abc.abstractmethod
|
114
|
+
def request_headers(self):
|
115
|
+
pass
|
116
|
+
|
117
|
+
@property
|
118
|
+
@abc.abstractmethod
|
119
|
+
def response(self) -> TestContextResponse:
|
120
|
+
pass
|
121
|
+
|
122
|
+
@property
|
123
|
+
@abc.abstractmethod
|
124
|
+
def response_param_names(self) -> RequestComponentNamesFacade:
|
125
|
+
pass
|
126
|
+
|
127
|
+
@property
|
128
|
+
@abc.abstractmethod
|
129
|
+
def rewritten_expected_response_content(self):
|
130
|
+
pass
|
131
|
+
|
132
|
+
@property
|
133
|
+
@abc.abstractmethod
|
134
|
+
def skipped(self):
|
135
|
+
pass
|
136
|
+
|
137
|
+
@skipped.setter
|
138
|
+
@abc.abstractmethod
|
139
|
+
def skipped(self, v: bool):
|
140
|
+
pass
|
141
|
+
|
142
|
+
@property
|
143
|
+
@abc.abstractmethod
|
144
|
+
def start_time(self):
|
145
|
+
pass
|
146
|
+
|
147
|
+
@property
|
148
|
+
@abc.abstractmethod
|
149
|
+
def strategy(self):
|
150
|
+
pass
|
151
|
+
|
152
|
+
@property
|
153
|
+
@abc.abstractmethod
|
154
|
+
def trace(self) -> Union[Trace, None]:
|
155
|
+
pass
|
@@ -2,7 +2,7 @@ import pdb
|
|
2
2
|
|
3
3
|
from typing import Iterable, List, TypedDict, Union
|
4
4
|
|
5
|
-
from stoobly_agent.app.proxy.test.
|
5
|
+
from stoobly_agent.app.proxy.test.context_abc import TestContextABC as TestContext
|
6
6
|
from stoobly_agent.app.proxy.test.helpers.request_component_names_facade import RequestComponentNamesFacade
|
7
7
|
from stoobly_agent.lib.api.interfaces.endpoints import RequestComponentName, ResponseParamName
|
8
8
|
from stoobly_agent.lib.utils.python_to_ruby_type import convert
|
@@ -5,7 +5,7 @@ from typing import List, Tuple, TypedDict, Union
|
|
5
5
|
from stoobly_agent.app.proxy.test.helpers.request_component_names_facade import RequestComponentNamesFacade
|
6
6
|
from stoobly_agent.lib.api.interfaces.endpoints import RequestComponentName
|
7
7
|
|
8
|
-
from ..
|
8
|
+
from ..context_abc import TestContextABC as TestContext
|
9
9
|
from .context import MatchContext, build_match_context
|
10
10
|
from .errors import length_match_error, param_name_exists_error, param_name_missing_error, type_match_error, valid_type_error
|
11
11
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import pdb
|
2
2
|
|
3
3
|
from typing import Tuple
|
4
|
-
from stoobly_agent.app.proxy.test.
|
4
|
+
from stoobly_agent.app.proxy.test.context_abc import TestContextABC as TestContext
|
5
5
|
|
6
6
|
from stoobly_agent.app.proxy.test.helpers.request_component_names_facade import RequestComponentNamesFacade
|
7
7
|
from stoobly_agent.app.proxy.test.matchers.errors import length_match_error, param_name_missing_error, type_match_error, value_match_error
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import pdb
|
2
2
|
|
3
3
|
from typing import Iterable, Tuple
|
4
|
-
from stoobly_agent.app.proxy.test.
|
4
|
+
from stoobly_agent.app.proxy.test.context_abc import TestContextABC as TestContext
|
5
5
|
from stoobly_agent.app.proxy.test.helpers.request_component_names_facade import RequestComponentNamesFacade
|
6
6
|
from stoobly_agent.app.proxy.test.matchers.errors import param_name_missing_error, type_match_error, valid_type_error
|
7
7
|
|
@@ -4,7 +4,7 @@ from stoobly_agent.app.proxy.replay.body_parser_service import decode_response,
|
|
4
4
|
|
5
5
|
from stoobly_agent.config.constants import test_strategy
|
6
6
|
|
7
|
-
from .
|
7
|
+
from .context_abc import TestContextABC as TestContext
|
8
8
|
from .matchers.custom import matches as custom_matches
|
9
9
|
from .matchers.contract import matches as contract_matches
|
10
10
|
from .matchers.diff import matches as diff_matches
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from ..mitmproxy.request_facade import MitmproxyRequestFacade
|
2
2
|
|
3
3
|
class ProxyRequest:
|
4
|
-
def __init__(self, request: MitmproxyRequestFacade, upstream_url: str):
|
4
|
+
def __init__(self, request: MitmproxyRequestFacade, upstream_url: str = None):
|
5
5
|
self.request = request
|
6
6
|
|
7
7
|
self.upstream_url = upstream_url
|
@@ -9,4 +9,7 @@ class ProxyRequest:
|
|
9
9
|
def url(self):
|
10
10
|
url = self.request.url
|
11
11
|
|
12
|
+
if not self.upstream_url:
|
13
|
+
return url
|
14
|
+
|
12
15
|
return url.replace(self.request.base_url, self.upstream_url)
|
stoobly_agent/cli.py
CHANGED
@@ -1,16 +1,21 @@
|
|
1
1
|
import click
|
2
2
|
import os
|
3
3
|
import pdb
|
4
|
+
import requests
|
4
5
|
import sys
|
5
6
|
|
6
7
|
from stoobly_agent import VERSION
|
7
|
-
from stoobly_agent.
|
8
|
+
from stoobly_agent.app.cli.helpers.context import ReplayContext
|
9
|
+
from stoobly_agent.app.cli.helpers.handle_mock_service import print_raw_response, RAW_FORMAT
|
10
|
+
from stoobly_agent.app.proxy.constants import custom_response_codes
|
11
|
+
from stoobly_agent.app.proxy.replay.replay_request_service import replay as replay_request
|
12
|
+
from stoobly_agent.config.constants import env_vars, mode
|
8
13
|
from stoobly_agent.config.data_dir import DataDir
|
9
14
|
from stoobly_agent.lib.utils.conditional_decorator import ConditionalDecorator
|
10
15
|
|
11
16
|
from .app.api import run as run_api
|
12
17
|
from .app.cli import ca_cert, config, feature, intercept, MainGroup, request, scenario, trace
|
13
|
-
from .app.proxy import CONNECTION_STRATEGIES,
|
18
|
+
from .app.proxy import CONNECTION_STRATEGIES, run as run_proxy
|
14
19
|
from .app.settings import Settings
|
15
20
|
from .lib import logger
|
16
21
|
from .lib.orm.migrate_service import migrate as migrate_database
|
@@ -67,7 +72,6 @@ def init(**kwargs):
|
|
67
72
|
@ConditionalDecorator(lambda f: click.option('--api-url', help='API URL.')(f), is_remote)
|
68
73
|
@click.option('--headless', is_flag=True, default=False, help='Disable starting UI.')
|
69
74
|
@click.option('--connection-strategy', help=', '.join(CONNECTION_STRATEGIES), type=click.Choice(CONNECTION_STRATEGIES))
|
70
|
-
@click.option('--intercept-mode', help=', '.join(INTERCEPT_MODES), type=click.Choice(INTERCEPT_MODES))
|
71
75
|
@click.option('--log-level', default=logger.INFO, type=click.Choice([logger.DEBUG, logger.INFO, logger.WARNING, logger.ERROR]), help='''
|
72
76
|
Log levels can be "debug", "info", "warning", or "error"
|
73
77
|
''')
|
@@ -94,8 +98,73 @@ def run(**kwargs):
|
|
94
98
|
if 'headless' in kwargs and not kwargs['headless']:
|
95
99
|
run_api(**kwargs)
|
96
100
|
|
97
|
-
|
98
|
-
print(f"Error: Invalid value for --intercept-mode, values: {', '.join(INTERCEPT_MODES)}", file=sys.stderr)
|
99
|
-
sys.exit(1)
|
101
|
+
run_proxy(**kwargs)
|
100
102
|
|
101
|
-
|
103
|
+
@main.command(
|
104
|
+
help="Mock request"
|
105
|
+
)
|
106
|
+
@click.option('-d', '--data', default='', help='HTTP POST data')
|
107
|
+
@click.option('--format', type=click.Choice([RAW_FORMAT]), help='Format response')
|
108
|
+
@click.option('-H', '--header', multiple=True, help='Pass custom header(s) to server')
|
109
|
+
@ConditionalDecorator(lambda f: click.option('--project-key')(f), is_remote)
|
110
|
+
@click.option('-X', '--request', default='GET', help='Specify request command to use')
|
111
|
+
@click.option('--scenario-key')
|
112
|
+
@click.argument('url')
|
113
|
+
def mock(**kwargs):
|
114
|
+
request = __build_request_from_curl(**kwargs)
|
115
|
+
|
116
|
+
context = ReplayContext.from_python_request(request)
|
117
|
+
response: requests.Response = replay_request(context, {
|
118
|
+
**kwargs,
|
119
|
+
'mode': mode.MOCK,
|
120
|
+
})
|
121
|
+
|
122
|
+
if response.status_code == custom_response_codes.NOT_FOUND:
|
123
|
+
print(f"Error: {response.content.decode()}")
|
124
|
+
sys.exit(1)
|
125
|
+
|
126
|
+
if kwargs['format'] == RAW_FORMAT:
|
127
|
+
print_raw_response(response)
|
128
|
+
else:
|
129
|
+
print(response.content.decode())
|
130
|
+
|
131
|
+
@main.command(
|
132
|
+
help="Record request"
|
133
|
+
)
|
134
|
+
@click.option('-d', '--data', default='', help='HTTP POST data')
|
135
|
+
@click.option('--format', type=click.Choice([RAW_FORMAT]), help='Format response')
|
136
|
+
@click.option('-H', '--header', multiple=True, help='Pass custom header(s) to server')
|
137
|
+
@ConditionalDecorator(lambda f: click.option('--project-key')(f), is_remote)
|
138
|
+
@click.option('-X', '--request', default='GET', help='Specify request command to use')
|
139
|
+
@click.option('--scenario-key')
|
140
|
+
@click.argument('url')
|
141
|
+
def record(**kwargs):
|
142
|
+
request = __build_request_from_curl(**kwargs)
|
143
|
+
|
144
|
+
context = ReplayContext.from_python_request(request)
|
145
|
+
response: requests.Response = replay_request(context, {
|
146
|
+
**kwargs,
|
147
|
+
'mode': mode.RECORD,
|
148
|
+
})
|
149
|
+
|
150
|
+
if kwargs['format'] == RAW_FORMAT:
|
151
|
+
print_raw_response(response)
|
152
|
+
else:
|
153
|
+
print(response.content.decode())
|
154
|
+
|
155
|
+
def __build_request_from_curl(**kwargs):
|
156
|
+
headers = {}
|
157
|
+
for header in kwargs['header']:
|
158
|
+
toks = header.split(':')
|
159
|
+
|
160
|
+
if len(toks) != 2:
|
161
|
+
continue
|
162
|
+
|
163
|
+
headers[toks[0]] = toks[1]
|
164
|
+
|
165
|
+
return requests.Request(
|
166
|
+
data=kwargs['data'],
|
167
|
+
headers=headers,
|
168
|
+
method=kwargs['request'],
|
169
|
+
url=kwargs['url']
|
170
|
+
)
|
@@ -1,8 +1,9 @@
|
|
1
1
|
AGENT_ACTIVE_MODE = 'STOOBLY_AGENT_ACTIVE_MODE'
|
2
2
|
AGENT_CONFIG_PATH = 'STOOBLY_AGENT_CONFIG_PATH'
|
3
3
|
AGENT_ENABLED = 'STOOBLY_AGENT_ENABLED'
|
4
|
-
|
4
|
+
AGENT_SELF_INTERCEPT_ENABLED = 'STOOBLY_AGENT_SELF_INTERCEPT_ENABLED'
|
5
5
|
AGENT_INCLUDE_PATTERNS = 'STOOBLY_AGENT_INCLUDE_PATTERNS'
|
6
|
+
AGENT_IS_HEADLESS = 'STOOBLY_AGENT_IS_HEADLESS'
|
6
7
|
AGENT_EXCLUDE_PATTERNS = 'STOOBLY_AGENT_EXCLUDE_PATTERNS'
|
7
8
|
AGENT_POLICY = 'STOOBLY_AGENT_POLICY'
|
8
9
|
AGENT_PROJECT_KEY = 'STOOBLY_AGENT_PROJECT_KEY'
|