stoobly-agent 0.21.0__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 +51 -29
- 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 +225 -135
- stoobly_agent/app/cli/dev_tools_cli.py +3 -1
- stoobly_agent/app/cli/helpers/__init__.py +2 -1
- 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 +8 -0
- stoobly_agent/app/cli/helpers/scenario_facade.py +10 -28
- stoobly_agent/app/cli/intercept.py +46 -2
- stoobly_agent/app/cli/main_group.py +8 -9
- stoobly_agent/app/cli/project_cli.py +10 -27
- stoobly_agent/app/cli/request_cli.py +14 -18
- stoobly_agent/app/cli/scenario_cli.py +94 -87
- 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_request_adapter.py +19 -12
- stoobly_agent/app/models/adapters/raw_http_response_adapter.py +22 -12
- 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/header_adapter.py +2 -2
- 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/mock/hashed_request_decorator.py +2 -2
- stoobly_agent/app/proxy/replay/replay_request_service.py +58 -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/app/settings/data_settings.py +3 -1
- stoobly_agent/cli.py +89 -11
- stoobly_agent/config/constants/env_vars.py +2 -1
- stoobly_agent/config/data_dir.py +29 -8
- stoobly_agent/config/settings.yml.sample +1 -1
- stoobly_agent/db/migrations/2022_12_12_092437_align_requests.py +4 -4
- 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 +13 -0
- stoobly_agent/lib/api/keys/resource_key.py +5 -3
- stoobly_agent/lib/api/scenarios_resource.py +4 -12
- stoobly_agent/lib/api/stoobly_api.py +9 -5
- stoobly_agent/lib/orm/__init__.py +1 -1
- stoobly_agent/lib/orm/migrate_service.py +28 -13
- 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 +15 -24
- 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.0.dist-info → stoobly_agent-0.22.3.dist-info}/METADATA +1 -1
- {stoobly_agent-0.21.0.dist-info → stoobly_agent-0.22.3.dist-info}/RECORD +186 -168
- {stoobly_agent-0.21.0.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/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.0.dist-info → stoobly_agent-0.22.3.dist-info}/LICENSE +0 -0
- {stoobly_agent-0.21.0.dist-info → stoobly_agent-0.22.3.dist-info}/entry_points.txt +0 -0
- {stoobly_agent-0.21.0.dist-info → stoobly_agent-0.22.3.dist-info}/top_level.txt +0 -0
@@ -11,6 +11,14 @@ def select_print_options(kwargs):
|
|
11
11
|
|
12
12
|
return print_options
|
13
13
|
|
14
|
+
def print_projects(projects, **kwargs):
|
15
|
+
tabulate_print(
|
16
|
+
projects,
|
17
|
+
filter=['created_at', 'is_deleted', 'organization_id', 'project_id', 'starred', 'updated_at'],
|
18
|
+
headers=not kwargs.get('without_headers'),
|
19
|
+
select=kwargs.get('select') or []
|
20
|
+
)
|
21
|
+
|
14
22
|
def print_requests(requests, **kwargs):
|
15
23
|
tabulate_print(
|
16
24
|
requests,
|
@@ -3,53 +3,35 @@ import requests
|
|
3
3
|
|
4
4
|
from stoobly_agent.app.cli.helpers.iterate_group_by import iterate_group_by
|
5
5
|
from stoobly_agent.app.cli.helpers.replay_facade import ReplayCliOptions, ReplayFacade, TestCliOptions
|
6
|
+
from stoobly_agent.app.models.scenario_model import ScenarioModel
|
6
7
|
from stoobly_agent.app.proxy.replay.replay_scenario_service import inject_replay
|
7
8
|
from stoobly_agent.app.proxy.replay.trace_context import TraceContext
|
8
9
|
from stoobly_agent.app.settings import Settings
|
9
10
|
from stoobly_agent.config.constants import mode
|
10
11
|
from stoobly_agent.lib.api.interfaces.scenarios import ScenarioShowResponse, ScenariosIndexResponse
|
11
12
|
from stoobly_agent.lib.api.keys import ProjectKey, ScenarioKey
|
12
|
-
from stoobly_agent.lib.api.scenarios_resource import ScenariosResource
|
13
13
|
|
14
14
|
class ScenarioFacade(ReplayFacade):
|
15
15
|
|
16
16
|
def __init__(self, settings: Settings):
|
17
|
-
self.__api = ScenariosResource(settings.remote.api_url, settings.remote.api_key)
|
18
17
|
super().__init__(settings)
|
18
|
+
self.__model = ScenarioModel(settings)
|
19
19
|
|
20
20
|
def create(self, project_key: str, name: str, description: str = ''):
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
}
|
28
|
-
)
|
29
|
-
)
|
30
|
-
|
31
|
-
if not res.ok:
|
32
|
-
raise AssertionError(res.content)
|
33
|
-
|
34
|
-
return res.json()
|
21
|
+
key = ProjectKey(project_key)
|
22
|
+
return self.__model.create(**{
|
23
|
+
'project_id': key.id,
|
24
|
+
'description': description,
|
25
|
+
'name': name,
|
26
|
+
})
|
35
27
|
|
36
28
|
def index(self, project_key, cli_options: dict) -> ScenariosIndexResponse:
|
37
29
|
key = ProjectKey(project_key)
|
38
|
-
|
39
|
-
|
40
|
-
if not res.ok:
|
41
|
-
raise AssertionError(res.content)
|
42
|
-
|
43
|
-
return res.json()
|
30
|
+
return self.__model.index(**{ 'project_id': key.id, **cli_options})
|
44
31
|
|
45
32
|
def show(self, scenario_key: str) -> ScenarioShowResponse:
|
46
33
|
key = ScenarioKey(scenario_key)
|
47
|
-
|
48
|
-
|
49
|
-
if not res.ok:
|
50
|
-
raise AssertionError(res.content)
|
51
|
-
|
52
|
-
return res.json()
|
34
|
+
return self.__model.show(key.id)
|
53
35
|
|
54
36
|
def replay(self, source_key: str, cli_options: ReplayCliOptions):
|
55
37
|
return self.__replay(source_key, {
|
@@ -3,15 +3,59 @@ import pdb
|
|
3
3
|
|
4
4
|
from stoobly_agent.app.cli.decorators.config import ConfigDecorator
|
5
5
|
from stoobly_agent.app.settings import Settings
|
6
|
+
from stoobly_agent.config.constants import mode
|
6
7
|
|
7
8
|
settings = Settings.instance()
|
9
|
+
mode_options = [mode.MOCK, mode.RECORD, mode.REPLAY]
|
10
|
+
|
11
|
+
if settings.cli.features.remote:
|
12
|
+
mode_options.append(mode.TEST)
|
8
13
|
|
9
14
|
@click.group(
|
10
15
|
epilog="Run 'stoobly-agent intercept COMMAND --help' for more information on a command.",
|
11
|
-
help="
|
16
|
+
help="Manage request intercept"
|
12
17
|
)
|
13
18
|
@click.pass_context
|
14
19
|
def intercept(ctx):
|
15
20
|
pass
|
16
21
|
|
17
|
-
|
22
|
+
@intercept.command(
|
23
|
+
help="Enable intercept"
|
24
|
+
)
|
25
|
+
@click.option('--mode', type=click.Choice(mode_options))
|
26
|
+
def enable(**kwargs):
|
27
|
+
settings = Settings.instance()
|
28
|
+
|
29
|
+
if kwargs['mode']:
|
30
|
+
settings.proxy.intercept.mode = kwargs['mode']
|
31
|
+
|
32
|
+
settings.proxy.intercept.active = True
|
33
|
+
|
34
|
+
settings.commit()
|
35
|
+
|
36
|
+
print("Intercept enabled!")
|
37
|
+
|
38
|
+
@intercept.command(
|
39
|
+
help="Disable intercept"
|
40
|
+
)
|
41
|
+
def disable(**kwargs):
|
42
|
+
settings = Settings.instance()
|
43
|
+
|
44
|
+
settings.proxy.intercept.active = False
|
45
|
+
|
46
|
+
settings.commit()
|
47
|
+
|
48
|
+
print("Intercept disabled!")
|
49
|
+
|
50
|
+
@intercept.command(
|
51
|
+
help="Show intercept"
|
52
|
+
)
|
53
|
+
def show(**kwargs):
|
54
|
+
settings = Settings.instance()
|
55
|
+
|
56
|
+
mode = settings.proxy.intercept.mode
|
57
|
+
|
58
|
+
if not mode:
|
59
|
+
print('No intercept mode set')
|
60
|
+
else:
|
61
|
+
print(f"{mode.capitalize()} {'enabled' if settings.proxy.intercept.active else 'disabled'}")
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import click
|
2
2
|
import collections
|
3
|
-
import pdb
|
4
3
|
|
5
4
|
from functools import reduce
|
6
5
|
from typing import List, TypedDict
|
@@ -27,23 +26,23 @@ class MainGroup(click.Group):
|
|
27
26
|
command_groups: List[CommandGroup] = [
|
28
27
|
{
|
29
28
|
'name': 'Commands',
|
30
|
-
'commands': ['dev-tools', 'exec', 'feature'],
|
29
|
+
'commands': ['dev-tools', 'exec', 'feature', 'init', 'mock', 'record'],
|
31
30
|
},
|
32
31
|
{
|
33
32
|
'name': 'Proxy Commands',
|
34
33
|
'commands': ['ca-cert', 'config', 'intercept', 'run'],
|
35
|
-
}
|
34
|
+
}
|
36
35
|
]
|
37
36
|
|
38
37
|
if self.__settings.cli.features.remote:
|
39
38
|
command_groups.append({
|
40
|
-
'name': 'Remote Commands',
|
41
|
-
'commands': ['project', 'report', '
|
39
|
+
'name': 'Remote Resource Commands',
|
40
|
+
'commands': ['project', 'report', 'request', 'scenario', 'trace'],
|
42
41
|
})
|
43
42
|
else:
|
44
43
|
command_groups.append({
|
45
|
-
'name': 'Local Commands',
|
46
|
-
'commands': ['request'],
|
44
|
+
'name': 'Local Resource Commands',
|
45
|
+
'commands': ['request', 'scenario'],
|
47
46
|
})
|
48
47
|
|
49
48
|
self.__print(formatter, command_groups)
|
@@ -69,6 +68,6 @@ class MainGroup(click.Group):
|
|
69
68
|
|
70
69
|
def __get_commands(self, command_names: List[str]):
|
71
70
|
return list(filter(
|
72
|
-
lambda c: c != None,
|
71
|
+
lambda c: c != None,
|
73
72
|
map(lambda c: self.commands.get(c), command_names)
|
74
|
-
))
|
73
|
+
))
|
@@ -5,7 +5,7 @@ import sys
|
|
5
5
|
from stoobly_agent.app.settings import Settings
|
6
6
|
|
7
7
|
from .helpers import ProjectFacade
|
8
|
-
from .helpers.
|
8
|
+
from .helpers.print_service import print_projects, select_print_options
|
9
9
|
from .helpers.validations import *
|
10
10
|
|
11
11
|
@click.group(
|
@@ -25,7 +25,7 @@ def project(ctx):
|
|
25
25
|
@click.option('--without-headers', is_flag=True, default=False, help='Disable printing column headers.')
|
26
26
|
@click.argument('name')
|
27
27
|
def create(**kwargs):
|
28
|
-
print_options =
|
28
|
+
print_options = select_print_options(kwargs)
|
29
29
|
|
30
30
|
project = ProjectFacade(Settings.instance())
|
31
31
|
|
@@ -34,7 +34,7 @@ def create(**kwargs):
|
|
34
34
|
except AssertionError as e:
|
35
35
|
return print(e, file=sys.stderr)
|
36
36
|
|
37
|
-
|
37
|
+
print_projects([project_response], **print_options)
|
38
38
|
|
39
39
|
@project.command(
|
40
40
|
help="Show all projects"
|
@@ -47,7 +47,7 @@ def create(**kwargs):
|
|
47
47
|
@click.option('--without-headers', is_flag=True, default=False, help='Disable printing column headers.')
|
48
48
|
@click.argument('organization_key', required=False)
|
49
49
|
def list(**kwargs):
|
50
|
-
print_options =
|
50
|
+
print_options = select_print_options(kwargs)
|
51
51
|
settings = Settings.instance()
|
52
52
|
project = ProjectFacade(settings)
|
53
53
|
|
@@ -57,7 +57,9 @@ def list(**kwargs):
|
|
57
57
|
if not organization_key:
|
58
58
|
try:
|
59
59
|
project_key = ProjectKey(settings.proxy.intercept.project_key)
|
60
|
-
|
60
|
+
|
61
|
+
if not project_key.is_local:
|
62
|
+
organization_key = OrganizationKey.encode(project_key.organization_id)
|
61
63
|
except InvalidProjectKey as e:
|
62
64
|
pass
|
63
65
|
|
@@ -81,7 +83,7 @@ def list(**kwargs):
|
|
81
83
|
if len(projects_response['list']) == 0:
|
82
84
|
print('No projects found.', file=sys.stderr)
|
83
85
|
else:
|
84
|
-
|
86
|
+
print_projects(projects_response['list'], **print_options)
|
85
87
|
|
86
88
|
@project.command(
|
87
89
|
help="Describe project"
|
@@ -90,7 +92,7 @@ def list(**kwargs):
|
|
90
92
|
@click.option('--without-headers', is_flag=True, default=False, help='Disable printing column headers.')
|
91
93
|
@click.argument('project_key', required=False)
|
92
94
|
def show(**kwargs):
|
93
|
-
print_options =
|
95
|
+
print_options = select_print_options(kwargs)
|
94
96
|
|
95
97
|
settings = Settings.instance()
|
96
98
|
project_key = resolve_project_key_and_validate(kwargs, settings)
|
@@ -101,23 +103,4 @@ def show(**kwargs):
|
|
101
103
|
except AssertionError as e:
|
102
104
|
return print(e, file=sys.stderr)
|
103
105
|
|
104
|
-
|
105
|
-
|
106
|
-
def __print(projects, **kwargs):
|
107
|
-
tabulate_print(
|
108
|
-
projects,
|
109
|
-
filter=['created_at', 'organization_id', 'project_id', 'starred', 'updated_at'],
|
110
|
-
headers=not kwargs.get('without_headers'),
|
111
|
-
select=kwargs.get('select') or []
|
112
|
-
)
|
113
|
-
|
114
|
-
def __select_print_options(kwargs):
|
115
|
-
print_options = {
|
116
|
-
'select': kwargs['select'],
|
117
|
-
'without_headers': kwargs['without_headers']
|
118
|
-
}
|
119
|
-
|
120
|
-
del kwargs['without_headers']
|
121
|
-
del kwargs['select']
|
122
|
-
|
123
|
-
return print_options
|
106
|
+
print_projects([project_response], **print_options)
|
@@ -38,7 +38,7 @@ def request(ctx):
|
|
38
38
|
)
|
39
39
|
@click.option('--page', default=0)
|
40
40
|
@ConditionalDecorator(lambda f: click.option('--project-key')(f), is_remote)
|
41
|
-
@
|
41
|
+
@click.option('--scenario-key')
|
42
42
|
@click.option('--select', multiple=True, help='Select column(s) to display.')
|
43
43
|
@click.option('--sort-by', default='created_at', help='created_at|path')
|
44
44
|
@click.option('--sort-order', default='desc', help='asc | desc')
|
@@ -68,16 +68,16 @@ def list(**kwargs):
|
|
68
68
|
@request.command(
|
69
69
|
help="Replay a request"
|
70
70
|
)
|
71
|
-
@click.option(
|
71
|
+
@ConditionalDecorator(lambda f: click.option(
|
72
72
|
'--alias-resolve-strategy',
|
73
73
|
default=alias_resolve_strategy.NONE,
|
74
74
|
type=click.Choice([alias_resolve_strategy.NONE, alias_resolve_strategy.FIFO, alias_resolve_strategy.LIFO]),
|
75
75
|
help='Strategy for resolving dynamic values for aliases.'
|
76
|
-
)
|
77
|
-
@click.option('--assign', multiple=True, help='Assign alias values. Format: <NAME>=<VALUE>')
|
76
|
+
)(f), is_remote)
|
77
|
+
@ConditionalDecorator(lambda f: click.option('--assign', multiple=True, help='Assign alias values. Format: <NAME>=<VALUE>')(f), is_remote)
|
78
78
|
@click.option('--format', default=DEFAULT_FORMAT, type=click.Choice([DEFAULT_FORMAT, JSON_FORMAT]), help='Format replay response.')
|
79
79
|
@click.option('--host', help='Rewrite request host.')
|
80
|
-
@click.option('--group-by', help='Repeat for each alias name.')
|
80
|
+
@ConditionalDecorator(lambda f: click.option('--group-by', help='Repeat for each alias name.')(f), is_remote)
|
81
81
|
@click.option('--lifecycle-hooks-script-path', help='Path to lifecycle hooks script.')
|
82
82
|
@click.option(
|
83
83
|
'--log-level', default=logger.WARNING, type=click.Choice(log_levels),
|
@@ -86,10 +86,10 @@ def list(**kwargs):
|
|
86
86
|
'''
|
87
87
|
)
|
88
88
|
@click.option('--record', is_flag=True, default=False, help='Replay and record request.')
|
89
|
-
@
|
89
|
+
@click.option('--scenario-key', help='Record to scenario.')
|
90
90
|
@click.option('--scheme', help='Rewrite request scheme.')
|
91
|
-
@click.option('--trace-id', help='Use existing trace.')
|
92
|
-
@click.option('--validate', multiple=True, help='Validate one or more aliases. Format: <NAME>=?<TYPE>')
|
91
|
+
@ConditionalDecorator(lambda f: click.option('--trace-id', help='Use existing trace.')(f), is_remote)
|
92
|
+
@ConditionalDecorator(lambda f: click.option('--validate', multiple=True, help='Validate one or more aliases. Format: <NAME>=?<TYPE>')(f), is_remote)
|
93
93
|
@click.argument('request_key')
|
94
94
|
def replay(**kwargs):
|
95
95
|
os.environ[env_vars.LOG_LEVEL] = kwargs['log_level']
|
@@ -103,7 +103,7 @@ def replay(**kwargs):
|
|
103
103
|
|
104
104
|
validate_scenario_key(kwargs['scenario_key'])
|
105
105
|
|
106
|
-
if len(kwargs['validate']):
|
106
|
+
if 'validate' in kwargs and len(kwargs['validate']):
|
107
107
|
validate_aliases(kwargs['validate'], assign=kwargs['assign'], format=kwargs['format'], trace_id=kwargs['trace_id'])
|
108
108
|
|
109
109
|
__assign_default_alias_resolve_strategy(kwargs)
|
@@ -203,9 +203,11 @@ def get(**kwargs):
|
|
203
203
|
|
204
204
|
print(res.content)
|
205
205
|
|
206
|
-
@response.command(
|
206
|
+
@response.command(
|
207
|
+
help="Query properties in response"
|
208
|
+
)
|
207
209
|
@click.option('--query', required=True)
|
208
|
-
@click.argument('
|
210
|
+
@click.argument('request_key')
|
209
211
|
def query(**kwargs):
|
210
212
|
validate_request_key(kwargs['request_key'])
|
211
213
|
|
@@ -218,12 +220,6 @@ def query(**kwargs):
|
|
218
220
|
|
219
221
|
print(jmespath.search(kwargs['query'], decoded_response))
|
220
222
|
|
221
|
-
@response.command(
|
222
|
-
help="Set new mocked response"
|
223
|
-
)
|
224
|
-
def set(**kwargs):
|
225
|
-
print("Not yet implemented. Stay tuned!")
|
226
|
-
|
227
223
|
request.add_command(response)
|
228
224
|
|
229
225
|
def __replay(handler, kwargs) -> requests.Response:
|
@@ -238,5 +234,5 @@ def __replay(handler, kwargs) -> requests.Response:
|
|
238
234
|
|
239
235
|
def __assign_default_alias_resolve_strategy(kwargs):
|
240
236
|
# If we have assigned values to aliases, it's likely we want to also have them resolved
|
241
|
-
if len(kwargs['assign']) > 0 and kwargs['alias_resolve_strategy'] == alias_resolve_strategy.NONE:
|
237
|
+
if 'assign' in kwargs and len(kwargs['assign']) > 0 and kwargs['alias_resolve_strategy'] == alias_resolve_strategy.NONE:
|
242
238
|
kwargs['alias_resolve_strategy'] = alias_resolve_strategy.FIFO
|
@@ -11,10 +11,14 @@ from stoobly_agent.app.cli.helpers.context import ReplayContext
|
|
11
11
|
from stoobly_agent.app.settings import Settings
|
12
12
|
from stoobly_agent.config.constants import alias_resolve_strategy, env_vars, test_filter, test_strategy
|
13
13
|
from stoobly_agent.lib import logger
|
14
|
+
from stoobly_agent.lib.utils.conditional_decorator import ConditionalDecorator
|
14
15
|
|
15
16
|
from .helpers.scenario_facade import ScenarioFacade
|
16
17
|
from .helpers.validations import *
|
17
18
|
|
19
|
+
settings = Settings.instance()
|
20
|
+
is_remote = settings.cli.features.remote
|
21
|
+
|
18
22
|
@click.group(
|
19
23
|
epilog="Run 'stoobly-agent scenario COMMAND --help' for more information on a command.",
|
20
24
|
help="Manage request scenarios"
|
@@ -26,8 +30,8 @@ def scenario(ctx):
|
|
26
30
|
@scenario.command(
|
27
31
|
help="Create a scenario"
|
28
32
|
)
|
29
|
-
@click.option('--description', help='Scenario description.')
|
30
|
-
@click.option('--project-key', help='Project to create scenario in.')
|
33
|
+
@click.option('--description', default='', help='Scenario description.')
|
34
|
+
@ConditionalDecorator(lambda f: click.option('--project-key', help='Project to create scenario in.')(f), is_remote)
|
31
35
|
@click.option('--select', multiple=True, help='Select column(s) to display.')
|
32
36
|
@click.option('--without-headers', is_flag=True, default=False, help='Disable printing column headers.')
|
33
37
|
@click.argument('name')
|
@@ -49,15 +53,15 @@ def create(**kwargs):
|
|
49
53
|
@scenario.command(
|
50
54
|
help="Replay a scenario"
|
51
55
|
)
|
52
|
-
@click.option(
|
56
|
+
@ConditionalDecorator(lambda f: click.option(
|
53
57
|
'--alias-resolve-strategy',
|
54
58
|
default=alias_resolve_strategy.NONE,
|
55
59
|
type=click.Choice([alias_resolve_strategy.NONE, alias_resolve_strategy.FIFO, alias_resolve_strategy.LIFO]),
|
56
60
|
help='Strategy for resolving dynamic values for aliases.'
|
57
|
-
)
|
58
|
-
@click.option('--assign', multiple=True, help='Assign alias values. Format: <NAME>=<VALUE>')
|
61
|
+
)(f), is_remote)
|
62
|
+
@ConditionalDecorator(lambda f: click.option('--assign', multiple=True, help='Assign alias values. Format: <NAME>=<VALUE>')(f), is_remote)
|
59
63
|
@click.option('--format', default=DEFAULT_FORMAT, type=click.Choice([DEFAULT_FORMAT, JSON_FORMAT]), help='Format replay response.')
|
60
|
-
@click.option('--group-by', help='Repeat for each alias name.')
|
64
|
+
@ConditionalDecorator(lambda f: click.option('--group-by', help='Repeat for each alias name.')(f), is_remote)
|
61
65
|
@click.option('--host', help='Rewrite request host.')
|
62
66
|
@click.option('--lifecycle-hooks-script-path', help='Path to lifecycle hooks script.')
|
63
67
|
@click.option(
|
@@ -69,8 +73,8 @@ def create(**kwargs):
|
|
69
73
|
@click.option('--record', is_flag=True, default=False, help='Replay and record scenario.')
|
70
74
|
@click.option('--scenario-key', help='Record to scenario.')
|
71
75
|
@click.option('--scheme', help='Rewrite request scheme.')
|
72
|
-
@click.option('--trace-id', help='Use existing trace.')
|
73
|
-
@click.option('--validate', multiple=True, help='Validate one or more aliases. Format: <NAME>=?<TYPE>')
|
76
|
+
@ConditionalDecorator(lambda f: click.option('--trace-id', help='Use existing trace.')(f), is_remote)
|
77
|
+
@ConditionalDecorator(lambda f: click.option('--validate', multiple=True, help='Validate one or more aliases. Format: <NAME>=?<TYPE>')(f), is_remote)
|
74
78
|
@click.argument('key')
|
75
79
|
def replay(**kwargs):
|
76
80
|
os.environ[env_vars.LOG_LEVEL] = kwargs['log_level']
|
@@ -84,7 +88,7 @@ def replay(**kwargs):
|
|
84
88
|
|
85
89
|
validate_scenario_key(kwargs['scenario_key'])
|
86
90
|
|
87
|
-
if len(kwargs['validate']):
|
91
|
+
if 'validate' in kwargs and len(kwargs['validate']):
|
88
92
|
validate_aliases(kwargs['validate'], assign=kwargs['assign'], format=kwargs['format'], trace_id=kwargs['trace_id'])
|
89
93
|
|
90
94
|
__assign_default_alias_resolve_strategy(kwargs)
|
@@ -99,86 +103,11 @@ def replay(**kwargs):
|
|
99
103
|
scenario = ScenarioFacade(Settings.instance())
|
100
104
|
scenario.replay(kwargs.get('key'), kwargs)
|
101
105
|
|
102
|
-
@scenario.command(
|
103
|
-
help="Replay and test a scenario"
|
104
|
-
)
|
105
|
-
@click.option('--aggregate-failures', default=False, is_flag=True, help='Toggles whether to continue execution on failure.')
|
106
|
-
@click.option(
|
107
|
-
'--alias-resolve-strategy',
|
108
|
-
default=alias_resolve_strategy.NONE,
|
109
|
-
type=click.Choice([alias_resolve_strategy.NONE, alias_resolve_strategy.FIFO, alias_resolve_strategy.LIFO]),
|
110
|
-
help='Strategy for resolving dynamic values for aliases.'
|
111
|
-
)
|
112
|
-
@click.option('--assign', multiple=True, help='Assign alias values. Format: <NAME>=<VALUE>')
|
113
|
-
@click.option(
|
114
|
-
'--filter',
|
115
|
-
default=test_filter.ALL,
|
116
|
-
type=click.Choice([test_filter.ALL, test_filter.ALIAS, test_filter.LINK]),
|
117
|
-
help='For iterable responses, selectively test properties.'
|
118
|
-
)
|
119
|
-
@click.option('--format', default=DEFAULT_FORMAT, type=click.Choice([DEFAULT_FORMAT, JSON_FORMAT]), help='Format replay response.')
|
120
|
-
@click.option('--group-by', help='Repeat for each alias name.')
|
121
|
-
@click.option('--host', help='Rewrite request host.')
|
122
|
-
@click.option('--lifecycle-hooks-script-path', help='Path to lifecycle hooks script.')
|
123
|
-
@click.option(
|
124
|
-
'--log-level', default=logger.WARNING, type=click.Choice([logger.DEBUG, logger.INFO, logger.WARNING, logger.ERROR]),
|
125
|
-
help='''
|
126
|
-
Log levels can be "debug", "info", "warning", or "error"
|
127
|
-
'''
|
128
|
-
)
|
129
|
-
@click.option('--report-key', help='Save to report.')
|
130
|
-
@click.option('--scheme', help='Rewrite request scheme.')
|
131
|
-
@click.option(
|
132
|
-
'--strategy',
|
133
|
-
default=test_strategy.DIFF,
|
134
|
-
type=click.Choice([test_strategy.CUSTOM, test_strategy.DIFF, test_strategy.FUZZY]),
|
135
|
-
help='How to test responses.'
|
136
|
-
)
|
137
|
-
@click.option('--trace-id', help='Use existing trace.')
|
138
|
-
@click.option('--validate', multiple=True, help='Validate one or more aliases. Format: <NAME>=?<TYPE>')
|
139
|
-
@click.argument('key')
|
140
|
-
def test(**kwargs):
|
141
|
-
os.environ[env_vars.LOG_LEVEL] = kwargs['log_level']
|
142
|
-
logger.Logger.reload()
|
143
|
-
|
144
|
-
settings = Settings.instance()
|
145
|
-
scenario_key = validate_scenario_key(kwargs['key'])
|
146
|
-
|
147
|
-
if kwargs.get('report_key'):
|
148
|
-
validate_report_key(kwargs['report_key'])
|
149
|
-
|
150
|
-
if len(kwargs['validate']):
|
151
|
-
validate_aliases(kwargs['validate'], assign=kwargs['assign'], format=kwargs['format'], trace_id=kwargs['trace_id'])
|
152
|
-
|
153
|
-
__assign_default_alias_resolve_strategy(kwargs)
|
154
|
-
|
155
|
-
session_context: SessionContext = {
|
156
|
-
'aggregate_failures': kwargs['aggregate_failures'],
|
157
|
-
'passed': 0,
|
158
|
-
'project_id': scenario_key.project_id,
|
159
|
-
'test_facade': TestFacade(settings),
|
160
|
-
'total': 0
|
161
|
-
}
|
162
|
-
|
163
|
-
kwargs['before_replay'] = lambda context: handle_before_replay(
|
164
|
-
context, kwargs['format']
|
165
|
-
)
|
166
|
-
kwargs['after_replay'] = lambda context: __handle_on_test_response(
|
167
|
-
context, session_context, kwargs['format']
|
168
|
-
)
|
169
|
-
|
170
|
-
scenario = ScenarioFacade(settings)
|
171
|
-
scenario.test(kwargs['key'], kwargs)
|
172
|
-
|
173
|
-
handle_test_session_complete(session_context, kwargs['format'])
|
174
|
-
|
175
|
-
exit_on_failure(session_context, format=kwargs['format'])
|
176
|
-
|
177
106
|
@scenario.command(
|
178
107
|
help="Show all scenarios"
|
179
108
|
)
|
180
109
|
@click.option('--page', default=0)
|
181
|
-
@click.option('--project-key', help='Project to
|
110
|
+
@ConditionalDecorator(lambda f: click.option('--project-key', help='Project to create scenario in.')(f), is_remote)
|
182
111
|
@click.option('--select', multiple=True, help='Select column(s) to display.')
|
183
112
|
@click.option('--size', default=10)
|
184
113
|
@click.option('--sort-by', default='created_at', help='created_at|name')
|
@@ -188,8 +117,10 @@ def list(**kwargs):
|
|
188
117
|
print_options = select_print_options(kwargs)
|
189
118
|
|
190
119
|
settings = Settings.instance()
|
120
|
+
|
191
121
|
project_key = resolve_project_key_and_validate(kwargs, settings)
|
192
|
-
|
122
|
+
if 'project_key' in kwargs:
|
123
|
+
del kwargs['project_key']
|
193
124
|
|
194
125
|
scenario = ScenarioFacade(settings)
|
195
126
|
|
@@ -223,6 +154,82 @@ def show(**kwargs):
|
|
223
154
|
|
224
155
|
print_scenarios([scenario_response], **print_options)
|
225
156
|
|
157
|
+
if is_remote:
|
158
|
+
@scenario.command(
|
159
|
+
help="Replay and test a scenario"
|
160
|
+
)
|
161
|
+
@click.option('--aggregate-failures', default=False, is_flag=True, help='Toggles whether to continue execution on failure.')
|
162
|
+
@click.option(
|
163
|
+
'--alias-resolve-strategy',
|
164
|
+
default=alias_resolve_strategy.NONE,
|
165
|
+
type=click.Choice([alias_resolve_strategy.NONE, alias_resolve_strategy.FIFO, alias_resolve_strategy.LIFO]),
|
166
|
+
help='Strategy for resolving dynamic values for aliases.'
|
167
|
+
)
|
168
|
+
@click.option('--assign', multiple=True, help='Assign alias values. Format: <NAME>=<VALUE>')
|
169
|
+
@click.option(
|
170
|
+
'--filter',
|
171
|
+
default=test_filter.ALL,
|
172
|
+
type=click.Choice([test_filter.ALL, test_filter.ALIAS, test_filter.LINK]),
|
173
|
+
help='For iterable responses, selectively test properties.'
|
174
|
+
)
|
175
|
+
@click.option('--format', default=DEFAULT_FORMAT, type=click.Choice([DEFAULT_FORMAT, JSON_FORMAT]), help='Format replay response.')
|
176
|
+
@click.option('--group-by', help='Repeat for each alias name.')
|
177
|
+
@click.option('--host', help='Rewrite request host.')
|
178
|
+
@click.option('--lifecycle-hooks-script-path', help='Path to lifecycle hooks script.')
|
179
|
+
@click.option(
|
180
|
+
'--log-level', default=logger.WARNING, type=click.Choice([logger.DEBUG, logger.INFO, logger.WARNING, logger.ERROR]),
|
181
|
+
help='''
|
182
|
+
Log levels can be "debug", "info", "warning", or "error"
|
183
|
+
'''
|
184
|
+
)
|
185
|
+
@click.option('--report-key', help='Save to report.')
|
186
|
+
@click.option('--scheme', help='Rewrite request scheme.')
|
187
|
+
@click.option(
|
188
|
+
'--strategy',
|
189
|
+
default=test_strategy.DIFF,
|
190
|
+
type=click.Choice([test_strategy.CUSTOM, test_strategy.DIFF, test_strategy.FUZZY]),
|
191
|
+
help='How to test responses.'
|
192
|
+
)
|
193
|
+
@click.option('--trace-id', help='Use existing trace.')
|
194
|
+
@click.option('--validate', multiple=True, help='Validate one or more aliases. Format: <NAME>=?<TYPE>')
|
195
|
+
@click.argument('key')
|
196
|
+
def test(**kwargs):
|
197
|
+
os.environ[env_vars.LOG_LEVEL] = kwargs['log_level']
|
198
|
+
logger.Logger.reload()
|
199
|
+
|
200
|
+
settings = Settings.instance()
|
201
|
+
scenario_key = validate_scenario_key(kwargs['key'])
|
202
|
+
|
203
|
+
if kwargs.get('report_key'):
|
204
|
+
validate_report_key(kwargs['report_key'])
|
205
|
+
|
206
|
+
if len(kwargs['validate']):
|
207
|
+
validate_aliases(kwargs['validate'], assign=kwargs['assign'], format=kwargs['format'], trace_id=kwargs['trace_id'])
|
208
|
+
|
209
|
+
__assign_default_alias_resolve_strategy(kwargs)
|
210
|
+
|
211
|
+
session_context: SessionContext = {
|
212
|
+
'aggregate_failures': kwargs['aggregate_failures'],
|
213
|
+
'passed': 0,
|
214
|
+
'project_id': scenario_key.project_id,
|
215
|
+
'test_facade': TestFacade(settings),
|
216
|
+
'total': 0
|
217
|
+
}
|
218
|
+
|
219
|
+
kwargs['before_replay'] = lambda context: handle_before_replay(
|
220
|
+
context, kwargs['format']
|
221
|
+
)
|
222
|
+
kwargs['after_replay'] = lambda context: __handle_on_test_response(
|
223
|
+
context, session_context, kwargs['format']
|
224
|
+
)
|
225
|
+
|
226
|
+
scenario = ScenarioFacade(settings)
|
227
|
+
scenario.test(kwargs['key'], kwargs)
|
228
|
+
|
229
|
+
handle_test_session_complete(session_context, kwargs['format'])
|
230
|
+
|
231
|
+
exit_on_failure(session_context, format=kwargs['format'])
|
232
|
+
|
226
233
|
def __handle_on_test_response(replay_context: ReplayContext, session_context: SessionContext, format = None):
|
227
234
|
handle_test_complete(replay_context, session_context, format)
|
228
235
|
|
@@ -231,5 +238,5 @@ def __handle_on_test_response(replay_context: ReplayContext, session_context: Se
|
|
231
238
|
|
232
239
|
def __assign_default_alias_resolve_strategy(kwargs):
|
233
240
|
# If we have assigned values to aliases, it's likely we want to also have them resolved
|
234
|
-
if len(kwargs['assign']) > 0 and kwargs['alias_resolve_strategy'] == alias_resolve_strategy.NONE:
|
241
|
+
if 'assign' in kwargs and len(kwargs['assign']) > 0 and kwargs['alias_resolve_strategy'] == alias_resolve_strategy.NONE:
|
235
242
|
kwargs['alias_resolve_strategy'] = alias_resolve_strategy.FIFO
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import pdb
|
2
|
+
import requests
|
3
|
+
|
4
|
+
from .python_adapter import PythonRequestAdapter
|
5
|
+
|
6
|
+
class MitmproxyRequestAdapterFactory():
|
7
|
+
|
8
|
+
def __init__(self, request: requests.Request):
|
9
|
+
self.__request = request
|
10
|
+
|
11
|
+
def python_request(self):
|
12
|
+
return PythonRequestAdapter(self.__request).adapt()
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import requests
|
2
|
+
|
3
|
+
from mitmproxy.http import Request as MitmproxyRequest
|
4
|
+
|
5
|
+
class PythonRequestAdapter():
|
6
|
+
|
7
|
+
def __init__(self, request: MitmproxyRequest):
|
8
|
+
self.__request = request
|
9
|
+
|
10
|
+
def adapt(self):
|
11
|
+
return requests.Request(
|
12
|
+
method=self.__request.method,
|
13
|
+
url=self.__request.url,
|
14
|
+
headers=self.__request.headers,
|
15
|
+
data=self.__request.content
|
16
|
+
)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import pdb
|
2
|
+
import requests
|
3
|
+
|
4
|
+
from .python_adapter import PythonResponseAdapter
|
5
|
+
|
6
|
+
class MitmproxyResponseAdapterFactory():
|
7
|
+
|
8
|
+
def __init__(self, response: requests.Response):
|
9
|
+
self.__response = response
|
10
|
+
|
11
|
+
def python_response(self):
|
12
|
+
return PythonResponseAdapter(self.__response).adapt()
|