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
stoobly_agent/__init__.py CHANGED
@@ -1 +1,2 @@
1
-
1
+ COMMAND = 'stoobly-agent'
2
+ VERSION = '0.22.3'
@@ -41,12 +41,12 @@ class BodiesController:
41
41
 
42
42
  accepted_headers = ['content-encoding', 'content-length', 'content-type']
43
43
  for header, val in request.headers.items():
44
- decoded_header = header.decode().lower()
44
+ decoded_header = header.lower()
45
45
 
46
46
  if decoded_header not in accepted_headers:
47
47
  continue
48
48
 
49
- headers[decoded_header] = val.decode()
49
+ headers[decoded_header] = val
50
50
 
51
51
  context.render(
52
52
  data = request.data,
@@ -1,4 +1,6 @@
1
+ import json
1
2
  import pdb
3
+ import requests
2
4
 
3
5
  from datetime import datetime
4
6
  from time import time
@@ -7,15 +9,15 @@ from urllib.parse import urlparse
7
9
  from stoobly_agent.app.api.simple_http_request_handler import SimpleHTTPRequestHandler
8
10
  from stoobly_agent.app.cli.helpers.context import ReplayContext
9
11
  from stoobly_agent.app.models.adapters.joined_request_adapter import JoinedRequestAdapter
10
- from stoobly_agent.app.models.adapters.mitmproxy_request_adapter import MitmproxyRequestAdapter
11
- from stoobly_agent.app.models.adapters.mitmproxy_response_adapter import MitmproxyResponseAdapter
12
+ from stoobly_agent.app.models.adapters.python import PythonRequestAdapterFactory, PythonResponseAdapterFactory
12
13
  from stoobly_agent.app.models.adapters.raw_http_request_adapter import RawHttpRequestAdapter
13
14
  from stoobly_agent.app.models.adapters.raw_http_response_adapter import RawHttpResponseAdapter
14
15
  from stoobly_agent.app.models.request_model import RequestModel
15
16
  from stoobly_agent.app.models.schemas.request import Request
16
- from stoobly_agent.app.proxy.replay.replay_request_service import replay_with_rewrite
17
+ from stoobly_agent.app.proxy.replay.replay_request_service import replay
17
18
  from stoobly_agent.app.proxy.upload.upload_request_service import upload_staged_request
18
19
  from stoobly_agent.app.settings import Settings
20
+ from stoobly_agent.config.constants import mode
19
21
  from stoobly_agent.lib.orm.replayed_response import ReplayedResponse
20
22
  from stoobly_agent.lib.orm.request import Request as OrmRequest
21
23
 
@@ -52,8 +54,8 @@ class RequestsController:
52
54
  request_adapter = RawHttpRequestAdapter(joined_request.request_string.get())
53
55
  response_adapter = RawHttpResponseAdapter(joined_request.response_string.get())
54
56
 
55
- mitmproxy_request = MitmproxyRequestAdapter(request_adapter.protocol, request_adapter.to_request()).adapt()
56
- mitmproxy_response = MitmproxyResponseAdapter(response_adapter.protocol, response_adapter.to_response()).adapt()
57
+ mitmproxy_request = PythonRequestAdapterFactory(request_adapter.to_request()).mitmproxy_request(request_adapter.protocol)
58
+ mitmproxy_response = PythonResponseAdapterFactory(response_adapter.to_response()).mitmproxy_response()
57
59
 
58
60
  class MitmproxyFlowMock():
59
61
  def __init__(self, request, response):
@@ -66,6 +68,7 @@ class RequestsController:
66
68
  request = request_model.create(**{
67
69
  'flow': mitmproxy_flow_mock,
68
70
  'joined_request': joined_request,
71
+ 'scenario_id': body_params.get('scenario_id'),
69
72
  })
70
73
 
71
74
  if not request:
@@ -201,29 +204,21 @@ class RequestsController:
201
204
  return context.bad_request(f"Could not find request {request_id}")
202
205
 
203
206
  replay_context = ReplayContext(Request(request_response))
204
-
205
- now = time()
206
- res = replay_with_rewrite(replay_context)
207
- received_at = time()
208
-
209
- replayed_response = ReplayedResponse()
210
- replayed_response.request_id = request_id
211
- replayed_response.with_python_response(res)
212
- replayed_response.latency = (received_at - now) * 1000 # ms
213
- replayed_response.save()
214
-
215
- context.render(
216
- data = res.raw.data,
217
- headers = res.headers,
218
- status = res.status_code
219
- )
207
+ self.__replay(context, replay_context)
220
208
 
221
209
  # PUT /requests/send
222
210
  def send(self, context: SimpleHTTPRequestHandler):
211
+ headers = []
212
+
213
+ try:
214
+ headers = json.load(context.params.get('headers'))
215
+ except Exception as e:
216
+ pass
217
+
223
218
  url = urlparse(context.params.get('url'))
224
219
  request_response = {
225
220
  'body': context.params.get('body'),
226
- 'headers': context.params.get('headers'),
221
+ 'headers': headers,
227
222
  'method': context.params.get('method'),
228
223
  'path': url.path,
229
224
  'password': url.password,
@@ -234,16 +229,43 @@ class RequestsController:
234
229
  }
235
230
 
236
231
  replay_context = ReplayContext(Request(request_response))
237
- res = replay_with_rewrite(replay_context)
232
+ self.__send(context, replay_context)
233
+
234
+ def export(self, context: SimpleHTTPRequestHandler):
235
+ context.parse_path_params({
236
+ 'id': 1
237
+ })
238
+ request_id = int(context.params.get('id'))
239
+
240
+ def __request_model(self, context: SimpleHTTPRequestHandler):
241
+ request_model = RequestModel(Settings.instance())
242
+ request_model.as_remote() if context.headers.get('access-token') else request_model.as_local()
243
+ return request_model
244
+
245
+ def __replay(self, context: SimpleHTTPRequestHandler, replay_context: ReplayContext):
246
+ save = bool(context.params.get('save'))
247
+ callback = self.__create_replayed_response if save else None
248
+
249
+ self.__send(context, replay_context, callback)
250
+
251
+ def __send(self, context: SimpleHTTPRequestHandler, replay_context: ReplayContext, callback = None):
252
+ now = time()
253
+ res = replay(replay_context, { 'mode': mode.REPLAY })
254
+ received_at = time()
255
+
256
+ if callback:
257
+ callback(context, res, int((received_at - now) * 1000))
238
258
 
239
259
  context.render(
240
- data = res.raw.data,
260
+ data = res.raw.data if hasattr(res, 'raw') else res.content,
241
261
  headers = res.headers,
242
262
  status = res.status_code
243
263
  )
244
264
 
245
-
246
- def __request_model(self, context: SimpleHTTPRequestHandler):
247
- request_model = RequestModel(Settings.instance())
248
- request_model.as_remote() if context.headers.get('access-token') else request_model.as_local()
249
- return request_model
265
+ def __create_replayed_response(self, context: SimpleHTTPRequestHandler, res: requests.Response, latency: int):
266
+ request_id = int(context.params.get('id'))
267
+ replayed_response = ReplayedResponse()
268
+ replayed_response.request_id = request_id
269
+ replayed_response.with_python_response(res)
270
+ replayed_response.latency = latency # ms
271
+ replayed_response.save()
@@ -62,7 +62,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
62
62
  headers = self.filter_headers(kwargs.get('headers'), {
63
63
  'TRANSFER-ENCODING': 'CHUNKED',
64
64
  })
65
- headers['Content-Length'] = len(body)
65
+ headers['Content-Length'] = str(len(body))
66
66
 
67
67
  self.enable_cors(headers)
68
68
  self.render_headers(headers)
@@ -7,19 +7,22 @@ class CACertInstaller():
7
7
  home_dir = os.path.expanduser('~')
8
8
  self.mitmproxy_certs_dir = os.path.join(home_dir, '.mitmproxy')
9
9
  self.pem_file_name = 'mitmproxy-ca-cert.pem'
10
+ self.cer_file_name = 'mitmproxy-ca-cert.cer'
10
11
  self.crt_file_name = 'mitmproxy-ca-cert.crt'
11
12
 
13
+ @property
14
+ def mitm_crt_absolute_path(self):
15
+ return os.path.join(self.mitmproxy_certs_dir, self.crt_file_name)
16
+
12
17
  # https://askubuntu.com/a/94861
13
18
  def handle_debian(self):
14
19
  extra_ca_certs_dir = '/usr/local/share/ca-certificates/extra'
15
- mitm_pem_absolute_path = os.path.join(self.mitmproxy_certs_dir, self.pem_file_name)
16
- mitm_crt_absolute_path = os.path.join(self.mitmproxy_certs_dir, self.crt_file_name)
20
+ mitm_cer_absolute_path = os.path.join(self.mitmproxy_certs_dir, self.cer_file_name)
17
21
  extra_crt_absolute_path = os.path.join(extra_ca_certs_dir, self.crt_file_name)
18
22
 
19
23
  subprocess.run(f"sudo mkdir -p {extra_ca_certs_dir}".split(), check=True)
20
- subprocess.run(f"openssl x509 -in {mitm_pem_absolute_path} -inform PEM -out {mitm_crt_absolute_path}".split(), check=True)
21
- subprocess.run(f"sudo cp {mitm_crt_absolute_path} {extra_crt_absolute_path}".split(), check=True)
22
- subprocess.run('sudo update-ca-certificates'.split(), check=True)
24
+ subprocess.run(f"sudo cp {mitm_cer_absolute_path} {extra_crt_absolute_path}".split(), check=True)
25
+ subprocess.run("sudo update-ca-certificates".split(), check=True)
23
26
 
24
27
  # https://www.dssw.co.uk/reference/security.html
25
28
  def handle_darwin(self):
@@ -27,3 +30,11 @@ class CACertInstaller():
27
30
  mitm_pem_absolute_path = os.path.join(self.mitmproxy_certs_dir, self.pem_file_name)
28
31
 
29
32
  subprocess.run(f"sudo security add-trusted-cert -d -p ssl -p basic -k {system_keychain_path} {mitm_pem_absolute_path}".split(), check=True)
33
+
34
+ def handle_rhel(self):
35
+ ca_trust_dir = '/etc/pki/ca-trust/source/anchors'
36
+ mitm_cer_absolute_path = os.path.join(self.mitmproxy_certs_dir, self.cer_file_name)
37
+ ca_trust_crt_absolute_path = os.path.join(ca_trust_dir, self.crt_file_name)
38
+
39
+ subprocess.run(f"sudo cp {mitm_cer_absolute_path} {ca_trust_crt_absolute_path}".split(), check=True)
40
+ subprocess.run("sudo update-ca-trust extract".split(), check=True)
@@ -12,6 +12,8 @@ from stoobly_agent.config.constants import mode
12
12
  from stoobly_agent.lib.api.keys import ProjectKey, ScenarioKey
13
13
  from stoobly_agent.lib.logger import Logger
14
14
 
15
+ from .helpers import ProjectFacade, ScenarioFacade
16
+ from .helpers.print_service import print_projects, print_scenarios, select_print_options
15
17
  from .helpers.validations import *
16
18
 
17
19
  @click.group(
@@ -42,170 +44,234 @@ def dump(**kwargs):
42
44
  else:
43
45
  print(output)
44
46
 
45
- is_remote = Settings.instance().cli.features.remote
46
- if is_remote:
47
- @click.group(
48
- help="Manage active scenario."
49
- )
50
- @click.pass_context
51
- def scenario(ctx):
52
- pass
47
+ ### Scenario
53
48
 
54
- @scenario.command(
55
- help="Set active scenario."
56
- )
57
- @click.argument('scenario_key')
58
- def set(**kwargs):
59
- settings = Settings.instance()
49
+ @click.group(
50
+ help="Manage active scenario"
51
+ )
52
+ @click.pass_context
53
+ def scenario(ctx):
54
+ pass
60
55
 
61
- scenario_key = ScenarioKey(kwargs['scenario_key'])
62
- validate_scenario_key(scenario_key)
56
+ @scenario.command(
57
+ help="Describe scenario"
58
+ )
59
+ @click.option('--select', multiple=True, help='Select column(s) to display.')
60
+ @click.option('--without-headers', is_flag=True, default=False, help='Disable printing column headers.')
61
+ def show(**kwargs):
62
+ settings = Settings.instance()
63
+ print_options = select_print_options(kwargs)
63
64
 
64
- project_key = settings.proxy.intercept.project_key
65
- validate_project_key(project_key)
66
- project_key = ProjectKey(project_key)
65
+ project_key = __project_key(settings)
66
+ data_rule = settings.proxy.data.data_rules(project_key.id)
67
67
 
68
- if scenario_key.project_id != project_key.id:
69
- return print("Please provide a scenario that belongs to the current project.\n")
68
+ if not data_rule.scenario_key or len(data_rule.scenario_key) == 0:
69
+ return
70
70
 
71
- data_rule = settings.proxy.data.data_rules(project_key.id)
72
- data_rule.scenario_key = kwargs['scenario_key']
73
- settings.commit()
71
+ kwargs['key'] = data_rule.scenario_key
74
72
 
75
- print("Scenario updated!")
76
73
 
77
- @click.group(
78
- help="Manage active project."
79
- )
80
- @click.pass_context
81
- def project(ctx):
82
- pass
74
+ scenario_key = resolve_scenario_key_and_validate(kwargs, settings)
75
+ scenario = ScenarioFacade(settings)
83
76
 
84
- @project.command(
85
- help="Set active project."
86
- )
87
- @click.argument('project_key')
88
- def set(**kwargs):
89
- settings = Settings.instance()
77
+ try:
78
+ scenario_response = scenario.show(scenario_key)
79
+ except AssertionError as e:
80
+ return print(e, file=sys.stderr)
90
81
 
91
- project_key = kwargs['project_key']
92
- validate_project_key(project_key)
93
- _project_key = ProjectKey(project_key)
82
+ print_scenarios([scenario_response], **print_options)
94
83
 
95
- data_rule = settings.proxy.data.data_rules(_project_key.id)
96
- scenario_key = data_rule.scenario_key
84
+ @scenario.command(
85
+ help="Set active scenario."
86
+ )
87
+ @click.argument('scenario_key')
88
+ def set(**kwargs):
89
+ validate_scenario_key(kwargs['scenario_key'])
90
+ scenario_key = ScenarioKey(kwargs['scenario_key'])
97
91
 
98
- if scenario_key:
99
- validate_scenario_key(scenario_key)
100
- scenario_key = ScenarioKey(scenario_key)
92
+ settings = Settings.instance()
93
+ project_key = __project_key(settings)
101
94
 
102
- if project_key.id != scenario_key.project_id:
103
- data_rule.scenario_key = None
104
- print("Current scenario does not belong to current project, unsetting current scenario.\n")
95
+ if scenario_key.project_id != project_key.id:
96
+ return print("Please provide a scenario that belongs to the current project.\n")
105
97
 
106
- settings.proxy.intercept.project_key = project_key
107
- settings.commit()
98
+ data_rule = settings.proxy.data.data_rules(project_key.id)
99
+ data_rule.scenario_key = kwargs['scenario_key']
100
+ settings.commit()
108
101
 
109
- print("Project updated!")
102
+ print("Scenario updated!")
110
103
 
111
- @click.group(
112
- help="Manage API key."
113
- )
114
- @click.pass_context
115
- def api_key(ctx):
116
- pass
104
+ @scenario.command(
105
+ help="Clear active scenario."
106
+ )
107
+ def clear(**kwargs):
108
+ settings = Settings.instance()
117
109
 
118
- @api_key.command(
119
- help="Set API Key"
120
- )
121
- @click.argument('api_key')
122
- def set(**kwargs):
123
- settings = Settings.instance()
110
+ project_key = __project_key(settings)
111
+
112
+ data_rule = settings.proxy.data.data_rules(project_key.id)
113
+ data_rule.scenario_key = ''
114
+ settings.commit()
115
+
116
+ print("Scenario cleared!")
117
+
118
+ ### Rewrite
119
+
120
+ @click.group(
121
+ help="Manage rewrite rules"
122
+ )
123
+ @click.pass_context
124
+ def rewrite(ctx):
125
+ pass
126
+
127
+ @rewrite.command(
128
+ help="Set rewrite rule."
129
+ )
130
+ @click.option(
131
+ '--method',
132
+ multiple=True,
133
+ required=True,
134
+ type=click.Choice(['GET', 'POST', 'DELETE', 'OPTIONS', 'PUT']),
135
+ help='HTTP methods.'
136
+ )
137
+ @click.option(
138
+ '--mode',
139
+ multiple=True,
140
+ required=True,
141
+ type=click.Choice([mode.MOCK, mode.RECORD, mode.REPLAY, mode.TEST])
142
+ )
143
+ @click.option('--name', required=True, help='Name of the request component.')
144
+ @click.option('--pattern', required=True, help='URLs pattern.')
145
+ @click.option('--project_key', help='Project to add rewrite rule to.')
146
+ @click.option(
147
+ '--type',
148
+ required=True,
149
+ type=click.Choice([request_component.BODY_PARAM, request_component.HEADER, request_component.QUERY_PARAM]),
150
+ help='Request component type.'
151
+ )
152
+ @click.option('--value', required=True, help='Rewrite value.')
153
+ def set(**kwargs):
154
+ settings = Settings.instance()
155
+ project_key_str = resolve_project_key_and_validate(kwargs, settings)
156
+ project_key = ProjectKey(project_key_str)
157
+
158
+ methods = list(kwargs['method'])
159
+ modes = list(kwargs['mode'])
160
+
161
+ rewrite_rules = settings.proxy.rewrite.rewrite_rules(project_key.id)
162
+
163
+ rewrite_rule_filter = lambda rule: rule.pattern == kwargs['pattern'] and rule.methods == methods
164
+ filtered_rewrite_rules: List[RewriteRule] = list(filter(rewrite_rule_filter, rewrite_rules))
165
+
166
+ if len(filtered_rewrite_rules) == 0:
167
+ rewrite_rule = RewriteRule({
168
+ 'methods': methods,
169
+ 'pattern': kwargs['pattern'],
170
+ 'parameter_rules': [__select_parameter_rule(kwargs)]
171
+ })
172
+ rewrite_rules.append(rewrite_rule)
173
+ settings.proxy.rewrite.set_rewrite_rules(project_key.id, rewrite_rules)
174
+ else:
175
+ parameter_rule_filter = lambda rule: rule.name == kwargs['name'] and rule.type == kwargs['type'] and rule.modes == modes
176
+ for rewrite_rule in filtered_rewrite_rules:
177
+ parameter_rules = rewrite_rule.parameter_rules
178
+ filtered_parameter_rules: List[ParameterRule] = list(filter(parameter_rule_filter, parameter_rules))
179
+ parameter_rule_dict = __select_parameter_rule(kwargs)
180
+
181
+ if len(filtered_parameter_rules) == 0:
182
+ parameter_rule = ParameterRule(parameter_rule_dict)
183
+ parameter_rules.append(parameter_rule)
184
+ rewrite_rule.parameter_rules = parameter_rules
185
+ else:
186
+ for parameter_rule in filtered_parameter_rules:
187
+ parameter_rule.update(parameter_rule_dict)
188
+
189
+ settings.commit()
190
+
191
+ Logger.instance().debug(f"Rewrite {kwargs['name']} -> {kwargs['value']} set!")
192
+
193
+ ### API Key
194
+
195
+ @click.group(
196
+ help="Manage API key"
197
+ )
198
+ @click.pass_context
199
+ def api_key(ctx):
200
+ pass
124
201
 
125
- api_key = kwargs['api_key']
126
- settings.remote.api_key = api_key
202
+ @api_key.command(
203
+ help="Set API Key"
204
+ )
205
+ @click.argument('api_key')
206
+ def set(**kwargs):
207
+ settings = Settings.instance()
208
+
209
+ api_key = kwargs['api_key']
210
+ settings.remote.api_key = api_key
127
211
 
128
- settings.commit()
212
+ settings.commit()
129
213
 
130
- print("API Key updated!")
214
+ print("API Key updated!")
131
215
 
216
+ is_remote = Settings.instance().cli.features.remote
217
+ if is_remote:
132
218
  @click.group(
133
- help="Manage rewrite rules."
219
+ help="Manage active project"
134
220
  )
135
221
  @click.pass_context
136
- def rewrite(ctx):
222
+ def project(ctx):
137
223
  pass
138
224
 
139
- @rewrite.command(
140
- help="Set rewrite rule."
141
- )
142
- @click.option(
143
- '--method',
144
- multiple=True,
145
- required=True,
146
- type=click.Choice(['GET', 'POST', 'DELETE', 'OPTIONS', 'PUT']),
147
- help='HTTP methods.'
225
+ @project.command(
226
+ help="Use local project."
148
227
  )
149
- @click.option(
150
- '--mode',
151
- multiple=True,
152
- required=True,
153
- type=click.Choice([mode.MOCK, mode.RECORD, mode.REPLAY, mode.TEST])
228
+ def local(**kwargs):
229
+ project_key = ProjectKey.local_key
230
+ __set_project_key(project_key)
231
+
232
+ print("Using local project!")
233
+
234
+ @project.command(
235
+ help="Describe project."
154
236
  )
155
- @click.option('--name', required=True, help='Name of the request component.')
156
- @click.option('--pattern', required=True, help='URLs pattern.')
157
- @click.option('--project_key', help='Project to add rewrite rule to.')
158
- @click.option(
159
- '--type',
160
- required=True,
161
- type=click.Choice([request_component.BODY_PARAM, request_component.HEADER, request_component.QUERY_PARAM]),
162
- help='Request component type.'
237
+ @click.option('--select', multiple=True, help='Select column(s) to display.')
238
+ @click.option('--without-headers', is_flag=True, default=False, help='Disable printing column headers.')
239
+ def show(**kwargs):
240
+ settings = Settings.instance()
241
+ print_options = select_print_options(kwargs)
242
+
243
+ project_key = __project_key(settings)
244
+ if project_key.is_local:
245
+ return print('Using local project')
246
+
247
+ kwargs['project_key'] = project_key.raw
248
+
249
+ project_key = resolve_project_key_and_validate(kwargs, settings)
250
+ project = ProjectFacade(settings)
251
+
252
+ try:
253
+ project_response = project.show(project_key)
254
+ except AssertionError as e:
255
+ return print(e, file=sys.stderr)
256
+
257
+ print_projects([project_response], **print_options)
258
+
259
+ @project.command(
260
+ help="Set active project."
163
261
  )
164
- @click.option('--value', required=True, help='Rewrite value.')
262
+ @click.argument('project_key')
165
263
  def set(**kwargs):
166
- settings = Settings.instance()
167
- project_key_str = resolve_project_key_and_validate(kwargs, settings)
168
- project_key = ProjectKey(project_key_str)
169
-
170
- methods = list(kwargs['method'])
171
- modes = list(kwargs['mode'])
172
-
173
- rewrite_rules = settings.proxy.rewrite.rewrite_rules(project_key.id)
174
-
175
- rewrite_rule_filter = lambda rule: rule.pattern == kwargs['pattern'] and rule.methods == methods
176
- filtered_rewrite_rules: List[RewriteRule] = list(filter(rewrite_rule_filter, rewrite_rules))
177
-
178
- if len(filtered_rewrite_rules) == 0:
179
- rewrite_rule = RewriteRule({
180
- 'methods': methods,
181
- 'pattern': kwargs['pattern'],
182
- 'parameter_rules': [__select_parameter_rule(kwargs)]
183
- })
184
- rewrite_rules.append(rewrite_rule)
185
- settings.proxy.rewrite.set_rewrite_rules(project_key.id, rewrite_rules)
186
- else:
187
- parameter_rule_filter = lambda rule: rule.name == kwargs['name'] and rule.type == kwargs['type'] and rule.modes == modes
188
- for rewrite_rule in filtered_rewrite_rules:
189
- parameter_rules = rewrite_rule.parameter_rules
190
- filtered_parameter_rules: List[ParameterRule] = list(filter(parameter_rule_filter, parameter_rules))
191
- parameter_rule_dict = __select_parameter_rule(kwargs)
192
-
193
- if len(filtered_parameter_rules) == 0:
194
- parameter_rule = ParameterRule(parameter_rule_dict)
195
- parameter_rules.append(parameter_rule)
196
- rewrite_rule.parameter_rules = parameter_rules
197
- else:
198
- for parameter_rule in filtered_parameter_rules:
199
- parameter_rule.update(parameter_rule_dict)
200
-
201
- settings.commit()
202
-
203
- Logger.instance().debug(f"Rewrite {kwargs['name']} -> {kwargs['value']} set!")
204
-
205
- config.add_command(api_key)
264
+ project_key = kwargs['project_key']
265
+
266
+ __set_project_key(project_key)
267
+
268
+ print("Project updated!")
269
+
206
270
  config.add_command(project)
207
- config.add_command(rewrite)
208
- config.add_command(scenario)
271
+
272
+ config.add_command(api_key)
273
+ config.add_command(rewrite)
274
+ config.add_command(scenario)
209
275
 
210
276
  def __select_parameter_rule(kwargs):
211
277
  return {
@@ -213,4 +279,28 @@ def __select_parameter_rule(kwargs):
213
279
  'name': kwargs['name'],
214
280
  'value': kwargs['value'],
215
281
  'type': kwargs['type'],
216
- }
282
+ }
283
+
284
+ def __project_key(settings):
285
+ project_key = settings.proxy.intercept.project_key
286
+ validate_project_key(project_key)
287
+ return ProjectKey(project_key)
288
+
289
+ def __set_project_key(project_key: str):
290
+ settings = Settings.instance()
291
+ validate_project_key(project_key)
292
+ _project_key = ProjectKey(project_key)
293
+
294
+ data_rule = settings.proxy.data.data_rules(_project_key.id)
295
+ scenario_key = data_rule.scenario_key
296
+
297
+ if scenario_key:
298
+ validate_scenario_key(scenario_key)
299
+ scenario_key = ScenarioKey(scenario_key)
300
+
301
+ if project_key.id != scenario_key.project_id:
302
+ data_rule.scenario_key = None
303
+ print("Current scenario does not belong to current project, unsetting current scenario.\n")
304
+
305
+ settings.proxy.intercept.project_key = project_key
306
+ settings.commit()
@@ -1,5 +1,7 @@
1
1
  import click
2
2
  import pdb
3
+
4
+ from stoobly_agent import VERSION
3
5
  from stoobly_agent.app.proxy.replay.body_parser_service import decode_response
4
6
  from stoobly_agent.app.models.adapters.raw_http_response_adapter import RawHttpResponseAdapter
5
7
  from stoobly_agent.lib.orm.migrate_service import migrate as database_migrate, rollback as database_rollback
@@ -26,7 +28,7 @@ def debug(**kwargs):
26
28
 
27
29
  @dev_tools.command()
28
30
  def migrate():
29
- database_migrate()
31
+ database_migrate(VERSION)
30
32
 
31
33
  @dev_tools.command()
32
34
  def rollback():
@@ -1,2 +1,3 @@
1
1
  from .project_facade import ProjectFacade
2
- from .run_command_service import run_command, run_command_with_proxy_export
2
+ from .run_command_service import run_command, run_command_with_proxy_export
3
+ from .scenario_facade import ScenarioFacade
@@ -1,6 +1,8 @@
1
+ import pdb
1
2
  import requests
2
3
  import time
3
4
 
5
+ from stoobly_agent.app.models.adapters.python import PythonRequestAdapterFactory
4
6
  from stoobly_agent.app.models.schemas.request import Request
5
7
 
6
8
  class ReplayContext():
@@ -14,6 +16,11 @@ class ReplayContext():
14
16
 
15
17
  self.__sequence = None
16
18
 
19
+ @classmethod
20
+ def from_python_request(cls, request: requests.Request):
21
+ stoobly_request = PythonRequestAdapterFactory(request).stoobly_request()
22
+ return cls(Request(stoobly_request))
23
+
17
24
  @property
18
25
  def end_time(self):
19
26
  return self.__end_time
@@ -0,0 +1,14 @@
1
+ import requests
2
+
3
+ from stoobly_agent.app.models.adapters.python import PythonResponseAdapterFactory
4
+ from stoobly_agent.app.proxy.mitmproxy.response_facade import MitmproxyResponseFacade
5
+ from stoobly_agent.app.proxy.upload.response_string import ResponseString
6
+
7
+ RAW_FORMAT = 'raw'
8
+
9
+ def print_raw_response(response: requests.Response):
10
+ mitmproxy_response = PythonResponseAdapterFactory(response).mitmproxy_response()
11
+ facade = MitmproxyResponseFacade(mitmproxy_response)
12
+ response_string = ResponseString(facade, None)
13
+
14
+ print(response_string.get().decode())