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.
Files changed (227) hide show
  1. stoobly_agent/__init__.py +2 -1
  2. stoobly_agent/app/api/bodies_controller.py +2 -2
  3. stoobly_agent/app/api/requests_controller.py +51 -29
  4. stoobly_agent/app/api/simple_http_request_handler.py +1 -1
  5. stoobly_agent/app/cli/ca_cert_installer.py +16 -5
  6. stoobly_agent/app/cli/config_cli.py +225 -135
  7. stoobly_agent/app/cli/dev_tools_cli.py +3 -1
  8. stoobly_agent/app/cli/helpers/__init__.py +2 -1
  9. stoobly_agent/app/cli/helpers/context.py +7 -0
  10. stoobly_agent/app/cli/helpers/handle_mock_service.py +14 -0
  11. stoobly_agent/app/cli/helpers/print_service.py +8 -0
  12. stoobly_agent/app/cli/helpers/scenario_facade.py +10 -28
  13. stoobly_agent/app/cli/intercept.py +46 -2
  14. stoobly_agent/app/cli/main_group.py +8 -9
  15. stoobly_agent/app/cli/project_cli.py +10 -27
  16. stoobly_agent/app/cli/request_cli.py +14 -18
  17. stoobly_agent/app/cli/scenario_cli.py +94 -87
  18. stoobly_agent/app/models/adapters/mitmproxy/__init__.py +2 -0
  19. stoobly_agent/app/models/adapters/mitmproxy/request/__init__.py +12 -0
  20. stoobly_agent/app/models/adapters/mitmproxy/request/python_adapter.py +16 -0
  21. stoobly_agent/app/models/adapters/mitmproxy/response/__init__.py +12 -0
  22. stoobly_agent/app/models/adapters/mitmproxy/response/python_adapter.py +24 -0
  23. stoobly_agent/app/models/adapters/python/__init__.py +2 -0
  24. stoobly_agent/app/models/adapters/python/request/__init__.py +18 -0
  25. stoobly_agent/app/models/adapters/python/request/mitmproxy_adapter.py +53 -0
  26. stoobly_agent/app/models/adapters/python/request/stoobly_adapter.py +49 -0
  27. stoobly_agent/app/models/adapters/python/response/__init__.py +13 -0
  28. stoobly_agent/app/models/adapters/{mitmproxy_response_adapter.py → python/response/mitmproxy_adapter.py} +15 -7
  29. stoobly_agent/app/models/adapters/raw_http_request_adapter.py +19 -12
  30. stoobly_agent/app/models/adapters/raw_http_response_adapter.py +22 -12
  31. stoobly_agent/app/models/adapters/stoobly/request/__init__.py +11 -0
  32. stoobly_agent/app/models/adapters/stoobly/request/mitmproxy_adapter.py +32 -0
  33. stoobly_agent/app/models/body_model.py +2 -2
  34. stoobly_agent/app/models/factories/__init__.py +0 -0
  35. stoobly_agent/app/models/factories/resource/__init__.py +0 -0
  36. stoobly_agent/app/models/{adapters/body_adapter_factory.py → factories/resource/body.py} +1 -1
  37. stoobly_agent/app/models/{adapters/header_adapter_factory.py → factories/resource/header.py} +1 -1
  38. stoobly_agent/app/models/factories/resource/local_db/__init__.py +0 -0
  39. stoobly_agent/app/models/{adapters → factories/resource}/local_db/header_adapter.py +2 -2
  40. stoobly_agent/app/models/{adapters → factories/resource}/local_db/replayed_response_adapter.py +1 -2
  41. stoobly_agent/app/models/{adapters → factories/resource}/local_db/request_adapter.py +15 -8
  42. stoobly_agent/app/models/{adapters → factories/resource}/local_db/scenario_adapter.py +13 -7
  43. stoobly_agent/app/models/{adapters/query_param_adapter_factory.py → factories/resource/query_param.py} +1 -1
  44. stoobly_agent/app/models/{adapters/replayed_response_adapter_factory.py → factories/resource/replayed_response.py} +1 -1
  45. stoobly_agent/app/models/{adapters/request_adapter_factory.py → factories/resource/request.py} +2 -2
  46. stoobly_agent/app/models/{adapters/response_adapter_factory.py → factories/resource/response.py} +1 -1
  47. stoobly_agent/app/models/{adapters/response_header_adapter_factory.py → factories/resource/response_header.py} +1 -1
  48. stoobly_agent/app/models/{adapters/scenario_adapter_factory.py → factories/resource/scenario.py} +2 -2
  49. stoobly_agent/app/models/factories/resource/stoobly/__init__.py +0 -0
  50. stoobly_agent/app/models/{adapters/stoobly_request_adapter.py → factories/resource/stoobly/request_adapter.py} +1 -2
  51. stoobly_agent/app/models/{adapters/stoobly_scenario_adapter.py → factories/resource/stoobly/scenario_adapter.py} +1 -2
  52. stoobly_agent/app/models/header_model.py +2 -2
  53. stoobly_agent/app/models/query_param_model.py +2 -2
  54. stoobly_agent/app/models/replayed_response_model.py +2 -2
  55. stoobly_agent/app/models/request_model.py +4 -5
  56. stoobly_agent/app/models/response_header_model.py +2 -2
  57. stoobly_agent/app/models/response_model.py +2 -2
  58. stoobly_agent/app/models/scenario_model.py +4 -4
  59. stoobly_agent/app/models/schemas/request.py +8 -1
  60. stoobly_agent/app/models/types/__init__.py +6 -0
  61. stoobly_agent/app/proxy/__init__.py +0 -5
  62. stoobly_agent/app/proxy/handle_mock_service.py +0 -3
  63. stoobly_agent/app/proxy/handle_test_service.py +1 -1
  64. stoobly_agent/app/proxy/intercept_handler.py +0 -1
  65. stoobly_agent/app/proxy/mitmproxy/flow_mock.py +35 -0
  66. stoobly_agent/app/proxy/mitmproxy/request_facade.py +11 -2
  67. stoobly_agent/app/proxy/mock/hashed_request_decorator.py +2 -2
  68. stoobly_agent/app/proxy/replay/replay_request_service.py +58 -40
  69. stoobly_agent/app/proxy/simulate_intercept_service.py +30 -0
  70. stoobly_agent/app/proxy/test/__init__.py +1 -0
  71. stoobly_agent/app/proxy/test/context.py +2 -1
  72. stoobly_agent/app/proxy/test/context_abc.py +155 -0
  73. stoobly_agent/app/proxy/test/matchers/context.py +1 -1
  74. stoobly_agent/app/proxy/test/matchers/contract.py +1 -1
  75. stoobly_agent/app/proxy/test/matchers/custom.py +1 -1
  76. stoobly_agent/app/proxy/test/matchers/diff.py +1 -1
  77. stoobly_agent/app/proxy/test/matchers/fuzzy.py +1 -1
  78. stoobly_agent/app/proxy/test/test_service.py +1 -1
  79. stoobly_agent/app/proxy/upload/joined_request.py +0 -1
  80. stoobly_agent/app/proxy/upload/proxy_request.py +4 -1
  81. stoobly_agent/app/proxy/upload/request_string.py +1 -1
  82. stoobly_agent/app/settings/constants/mode.py +3 -0
  83. stoobly_agent/app/settings/data_settings.py +3 -1
  84. stoobly_agent/cli.py +89 -11
  85. stoobly_agent/config/constants/env_vars.py +2 -1
  86. stoobly_agent/config/data_dir.py +29 -8
  87. stoobly_agent/config/settings.yml.sample +1 -1
  88. stoobly_agent/db/migrations/2022_12_12_092437_align_requests.py +4 -4
  89. stoobly_agent/lib/api/api.py +4 -4
  90. stoobly_agent/lib/api/keys/organization_key.py +2 -0
  91. stoobly_agent/lib/api/keys/project_key.py +13 -0
  92. stoobly_agent/lib/api/keys/resource_key.py +5 -3
  93. stoobly_agent/lib/api/scenarios_resource.py +4 -12
  94. stoobly_agent/lib/api/stoobly_api.py +9 -5
  95. stoobly_agent/lib/orm/__init__.py +1 -1
  96. stoobly_agent/lib/orm/migrate_service.py +28 -13
  97. stoobly_agent/lib/orm/replayed_response.py +3 -3
  98. stoobly_agent/lib/orm/request.py +25 -5
  99. stoobly_agent/lib/orm/transformers/orm_to_stoobly_request_transformer.py +15 -24
  100. stoobly_agent/mock.py +94 -0
  101. stoobly_agent/public/{2-es2015.3d54569af612a07a2e06.js → 1-es2015.37917aa26708d8f35d36.js} +1 -1
  102. stoobly_agent/public/{2-es5.3d54569af612a07a2e06.js → 1-es5.37917aa26708d8f35d36.js} +1 -1
  103. stoobly_agent/public/10-es2015.e9556b0d0f0e92fb548b.js +1 -0
  104. stoobly_agent/public/10-es5.e9556b0d0f0e92fb548b.js +1 -0
  105. stoobly_agent/public/11-es2015.bc6212fccbe72a623f81.js +1 -0
  106. stoobly_agent/public/11-es5.bc6212fccbe72a623f81.js +1 -0
  107. stoobly_agent/public/{13-es2015.76b6c147b0c46f995cd7.js → 12-es2015.d0768894ddffd6efa5e5.js} +1 -1
  108. stoobly_agent/public/{13-es5.76b6c147b0c46f995cd7.js → 12-es5.d0768894ddffd6efa5e5.js} +1 -1
  109. stoobly_agent/public/13-es2015.8a044490a76fd298162d.js +1 -0
  110. stoobly_agent/public/13-es5.8a044490a76fd298162d.js +1 -0
  111. stoobly_agent/public/{15-es2015.60c3b41c385f5bdedb7b.js → 14-es2015.1cd1a021e51ca0e62e1c.js} +1 -1
  112. stoobly_agent/public/{15-es5.60c3b41c385f5bdedb7b.js → 14-es5.1cd1a021e51ca0e62e1c.js} +1 -1
  113. stoobly_agent/public/{16-es2015.5d395009a77978db4405.js → 15-es2015.587781d19864ff0eb4f5.js} +1 -1
  114. stoobly_agent/public/{16-es5.5d395009a77978db4405.js → 15-es5.587781d19864ff0eb4f5.js} +1 -1
  115. stoobly_agent/public/16-es2015.ec6a175b1f9578203cd8.js +1 -0
  116. stoobly_agent/public/16-es5.ec6a175b1f9578203cd8.js +1 -0
  117. stoobly_agent/public/17-es2015.ad9c4756c96a15bd29d7.js +1 -0
  118. stoobly_agent/public/17-es5.ad9c4756c96a15bd29d7.js +1 -0
  119. stoobly_agent/public/{19-es2015.517f68e08f4c582dae66.js → 18-es2015.8583df0f8eccb3e47c0b.js} +1 -1
  120. stoobly_agent/public/{19-es5.517f68e08f4c582dae66.js → 18-es5.8583df0f8eccb3e47c0b.js} +1 -1
  121. stoobly_agent/public/{20-es2015.473486aabfa4d4a6431b.js → 19-es2015.d0225852a844dc03a09f.js} +1 -1
  122. stoobly_agent/public/{20-es5.473486aabfa4d4a6431b.js → 19-es5.d0225852a844dc03a09f.js} +1 -1
  123. stoobly_agent/public/{3-es5.1dad290844ea619e4c16.js → 2-es2015.8f184ac63348ba447b1f.js} +1 -1
  124. stoobly_agent/public/{3-es2015.1dad290844ea619e4c16.js → 2-es5.8f184ac63348ba447b1f.js} +1 -1
  125. stoobly_agent/public/{21-es2015.56aa10803cc1348a55a3.js → 20-es2015.f7c107847935264d58aa.js} +1 -1
  126. stoobly_agent/public/{21-es5.56aa10803cc1348a55a3.js → 20-es5.f7c107847935264d58aa.js} +1 -1
  127. stoobly_agent/public/{22-es2015.46d81010003b2a50eeab.js → 21-es2015.dd358e1edaf3d32dd2c0.js} +1 -1
  128. stoobly_agent/public/{22-es5.46d81010003b2a50eeab.js → 21-es5.dd358e1edaf3d32dd2c0.js} +1 -1
  129. stoobly_agent/public/26-es2015.6332c32f1b7c8c288f2f.js +1 -0
  130. stoobly_agent/public/26-es5.6332c32f1b7c8c288f2f.js +1 -0
  131. stoobly_agent/public/27-es2015.af505e744b0c869a93d1.js +1 -0
  132. stoobly_agent/public/27-es5.af505e744b0c869a93d1.js +1 -0
  133. stoobly_agent/public/28-es2015.7c7c0f64e4af29d2e4d4.js +1 -0
  134. stoobly_agent/public/28-es5.7c7c0f64e4af29d2e4d4.js +1 -0
  135. stoobly_agent/public/32-es2015.2ab8267be7275dee9059.js +1 -0
  136. stoobly_agent/public/32-es5.2ab8267be7275dee9059.js +1 -0
  137. stoobly_agent/public/{34-es2015.ef24f6f7630620a38b19.js → 33-es2015.5b575f3a87c6c2c6b93b.js} +1 -1
  138. stoobly_agent/public/{34-es5.ef24f6f7630620a38b19.js → 33-es5.5b575f3a87c6c2c6b93b.js} +1 -1
  139. stoobly_agent/public/{35-es2015.0667e742725cc828f59f.js → 34-es2015.6a1160649c718cdb9338.js} +1 -1
  140. stoobly_agent/public/{35-es5.0667e742725cc828f59f.js → 34-es5.6a1160649c718cdb9338.js} +1 -1
  141. stoobly_agent/public/35-es2015.1b9dc7a46a6d5296c3ae.js +1 -0
  142. stoobly_agent/public/35-es5.1b9dc7a46a6d5296c3ae.js +1 -0
  143. stoobly_agent/public/{37-es2015.50d0c2d3fe4d0a74fc8f.js → 36-es2015.2fc9151fe6a5ff2bff31.js} +1 -1
  144. stoobly_agent/public/{37-es5.50d0c2d3fe4d0a74fc8f.js → 36-es5.2fc9151fe6a5ff2bff31.js} +1 -1
  145. stoobly_agent/public/{38-es2015.c14bde0b8d0cc14e915e.js → 37-es2015.ac7108c3625fd6e1d981.js} +1 -1
  146. stoobly_agent/public/{38-es5.c14bde0b8d0cc14e915e.js → 37-es5.ac7108c3625fd6e1d981.js} +1 -1
  147. stoobly_agent/public/38-es2015.9c183b14373c0e449932.js +1 -0
  148. stoobly_agent/public/38-es5.9c183b14373c0e449932.js +1 -0
  149. stoobly_agent/public/39-es2015.4624cdeb29fe9850b216.js +1 -0
  150. stoobly_agent/public/39-es5.4624cdeb29fe9850b216.js +1 -0
  151. stoobly_agent/public/40-es2015.24d981230c0c8f369cde.js +1 -0
  152. stoobly_agent/public/40-es5.24d981230c0c8f369cde.js +1 -0
  153. stoobly_agent/public/{46-es2015.abc7e4fd207d54277fcb.js → 45-es2015.c76937ed45d460bcd36f.js} +1 -1
  154. stoobly_agent/public/{46-es5.abc7e4fd207d54277fcb.js → 45-es5.c76937ed45d460bcd36f.js} +1 -1
  155. stoobly_agent/public/6-es2015.53acc5d2ca7f48ef857f.js +1 -0
  156. stoobly_agent/public/6-es5.53acc5d2ca7f48ef857f.js +1 -0
  157. stoobly_agent/public/7-es2015.1c6b3d315d50ccd228cb.js +1 -0
  158. stoobly_agent/public/7-es5.1c6b3d315d50ccd228cb.js +1 -0
  159. stoobly_agent/public/{9-es2015.ef0f7cb32f5fadb085d0.js → 8-es2015.0fe7492f7b61eb4699b6.js} +1 -1
  160. stoobly_agent/public/{9-es5.ef0f7cb32f5fadb085d0.js → 8-es5.0fe7492f7b61eb4699b6.js} +1 -1
  161. stoobly_agent/public/common-es2015.86f70de6df2c705a87f6.js +1 -0
  162. stoobly_agent/public/common-es5.86f70de6df2c705a87f6.js +1 -0
  163. stoobly_agent/public/dashboard.agent-alpha-0.22.3.tar.gz +0 -0
  164. stoobly_agent/public/index.html +1 -1
  165. stoobly_agent/public/main-es2015.aceb967cb4cccc0bc521.js +1 -0
  166. stoobly_agent/public/main-es5.aceb967cb4cccc0bc521.js +1 -0
  167. stoobly_agent/public/{polyfills-es2015.2b40b2ecdf98a9210572.js → polyfills-es2015.580f7a6e775c2c348c9d.js} +1 -1
  168. stoobly_agent/public/{polyfills-es5.d9fb2eee68607c3f7f64.js → polyfills-es5.4c3461a071d35be3dd81.js} +1 -1
  169. stoobly_agent/public/runtime-es2015.49eaebd2913fa7fe2b97.js +1 -0
  170. stoobly_agent/public/runtime-es5.49eaebd2913fa7fe2b97.js +1 -0
  171. stoobly_agent/test/test_helper.py +4 -5
  172. {stoobly_agent-0.21.0.dist-info → stoobly_agent-0.22.3.dist-info}/METADATA +1 -1
  173. {stoobly_agent-0.21.0.dist-info → stoobly_agent-0.22.3.dist-info}/RECORD +186 -168
  174. {stoobly_agent-0.21.0.dist-info → stoobly_agent-0.22.3.dist-info}/WHEEL +1 -1
  175. stoobly_agent/app/models/adapters/mitmproxy_request_adapter.py +0 -60
  176. stoobly_agent/app/models/adapters/types/__init__.py +0 -3
  177. stoobly_agent/public/11-es2015.b85bdc528bab0ee542fe.js +0 -1
  178. stoobly_agent/public/11-es5.b85bdc528bab0ee542fe.js +0 -1
  179. stoobly_agent/public/12-es2015.72399d40488de533bb97.js +0 -1
  180. stoobly_agent/public/12-es5.72399d40488de533bb97.js +0 -1
  181. stoobly_agent/public/14-es2015.7cedfd0829bd7a64677a.js +0 -1
  182. stoobly_agent/public/14-es5.7cedfd0829bd7a64677a.js +0 -1
  183. stoobly_agent/public/17-es2015.f6f28ba9f681063632a8.js +0 -1
  184. stoobly_agent/public/17-es5.f6f28ba9f681063632a8.js +0 -1
  185. stoobly_agent/public/18-es2015.b0cd6822ebd1090b0d60.js +0 -1
  186. stoobly_agent/public/18-es5.b0cd6822ebd1090b0d60.js +0 -1
  187. stoobly_agent/public/27-es2015.4635450ff709f7868796.js +0 -1
  188. stoobly_agent/public/27-es5.4635450ff709f7868796.js +0 -1
  189. stoobly_agent/public/28-es2015.1b29b35529772ab108f1.js +0 -1
  190. stoobly_agent/public/28-es5.1b29b35529772ab108f1.js +0 -1
  191. stoobly_agent/public/32-es2015.862b67803e6242451976.js +0 -1
  192. stoobly_agent/public/32-es5.862b67803e6242451976.js +0 -1
  193. stoobly_agent/public/33-es2015.4ff4325d1aec37e1b43c.js +0 -1
  194. stoobly_agent/public/33-es5.4ff4325d1aec37e1b43c.js +0 -1
  195. stoobly_agent/public/36-es2015.cd370fdf8990019d0c8e.js +0 -1
  196. stoobly_agent/public/36-es5.cd370fdf8990019d0c8e.js +0 -1
  197. stoobly_agent/public/39-es2015.4ec5fc16202c4759eac4.js +0 -1
  198. stoobly_agent/public/39-es5.4ec5fc16202c4759eac4.js +0 -1
  199. stoobly_agent/public/40-es2015.25287ce77b40050f3471.js +0 -1
  200. stoobly_agent/public/40-es5.25287ce77b40050f3471.js +0 -1
  201. stoobly_agent/public/45-es2015.fd110741ac0fbaada177.js +0 -1
  202. stoobly_agent/public/45-es5.fd110741ac0fbaada177.js +0 -1
  203. stoobly_agent/public/6-es2015.7219d596e3545ebaed3a.js +0 -1
  204. stoobly_agent/public/6-es5.7219d596e3545ebaed3a.js +0 -1
  205. stoobly_agent/public/7-es2015.98b085349ebd9d246060.js +0 -1
  206. stoobly_agent/public/7-es5.98b085349ebd9d246060.js +0 -1
  207. stoobly_agent/public/8-es2015.335fd5c122ad083aec65.js +0 -1
  208. stoobly_agent/public/8-es5.335fd5c122ad083aec65.js +0 -1
  209. stoobly_agent/public/common-es2015.81f870bf87411a04446d.js +0 -1
  210. stoobly_agent/public/common-es5.81f870bf87411a04446d.js +0 -1
  211. stoobly_agent/public/dashboard.agent-alpha-0.21.0.tar.gz +0 -0
  212. stoobly_agent/public/main-es2015.575c1d17fc866d3a6649.js +0 -1
  213. stoobly_agent/public/main-es5.575c1d17fc866d3a6649.js +0 -1
  214. stoobly_agent/public/runtime-es2015.8915d042dc9b55368999.js +0 -1
  215. stoobly_agent/public/runtime-es5.8915d042dc9b55368999.js +0 -1
  216. /stoobly_agent/app/models/adapters/{local_db → stoobly}/__init__.py +0 -0
  217. /stoobly_agent/app/models/{adapters → factories/resource}/local_db/body_adapter.py +0 -0
  218. /stoobly_agent/app/models/{adapters → factories/resource}/local_db/query_param_adapter.py +0 -0
  219. /stoobly_agent/app/models/{adapters → factories/resource}/local_db/response_adapter.py +0 -0
  220. /stoobly_agent/app/models/{adapters → factories/resource}/local_db/response_header_adapter.py +0 -0
  221. /stoobly_agent/app/models/{adapters/types → types}/replayed_response.py +0 -0
  222. /stoobly_agent/app/models/{adapters/types → types}/request_create_params.py +0 -0
  223. /stoobly_agent/app/models/{adapters/types → types}/request_show_params.py +0 -0
  224. /stoobly_agent/app/models/{adapters/types → types}/scenario_create_params.py +0 -0
  225. {stoobly_agent-0.21.0.dist-info → stoobly_agent-0.22.3.dist-info}/LICENSE +0 -0
  226. {stoobly_agent-0.21.0.dist-info → stoobly_agent-0.22.3.dist-info}/entry_points.txt +0 -0
  227. {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
- res: requests.Response = self.__api.from_project_key(
22
- project_key,
23
- lambda project_id: self.__api.create(
24
- project_id, {
25
- 'description': description,
26
- 'name': name,
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
- res = self.__api.index(**{ 'project_id': key.id, **cli_options})
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
- res = self.__api.show(key.id)
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="Toggle request intercept"
16
+ help="Manage request intercept"
12
17
  )
13
18
  @click.pass_context
14
19
  def intercept(ctx):
15
20
  pass
16
21
 
17
- ConfigDecorator(intercept, settings, 'proxy.intercept.active').decorate()
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', 'scenario', 'request', 'trace'],
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.tabulate_print_service import tabulate_print
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 = __select_print_options(kwargs)
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
- __print([project_response], **print_options)
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 = __select_print_options(kwargs)
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
- organization_key = OrganizationKey.encode(project_key.organization_id)
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
- __print(projects_response['list'], **print_options)
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 = __select_print_options(kwargs)
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
- __print([project_response], **print_options)
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
- @ConditionalDecorator(lambda f: click.option('--scenario-key')(f), is_remote)
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
- @ConditionalDecorator(lambda f: click.option('--scenario-key', help='Record to scenario.')(f), is_remote)
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('request-key')
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 list scenarios from.')
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
- del kwargs['project_key']
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,2 @@
1
+ from .request import MitmproxyRequestAdapterFactory
2
+ from .response import MitmproxyResponseAdapterFactory
@@ -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()