stoobly-agent 1.8.4__py3-none-any.whl → 1.8.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stoobly_agent/__init__.py +1 -1
- stoobly_agent/app/api/headers_controller.py +1 -1
- stoobly_agent/app/api/query_params_controller.py +4 -4
- stoobly_agent/app/cli/snapshot_cli.py +57 -0
- stoobly_agent/app/cli/types/snapshot_migration.py +61 -0
- stoobly_agent/app/models/adapters/orm/request/mitmproxy_adapter.py +1 -1
- stoobly_agent/app/models/adapters/python/request/__init__.py +2 -2
- stoobly_agent/app/models/adapters/python/request/mitmproxy_adapter.py +1 -1
- stoobly_agent/app/models/adapters/python/request/raw_adapter.py +2 -2
- stoobly_agent/app/models/factories/resource/local_db/helpers/log.py +7 -0
- stoobly_agent/app/models/factories/resource/local_db/helpers/log_event.py +10 -0
- stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py +20 -0
- stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py +8 -1
- stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot.py +1 -1
- stoobly_agent/app/models/factories/resource/local_db/query_param_adapter.py +2 -2
- stoobly_agent/app/models/helpers/apply.py +5 -4
- stoobly_agent/app/models/helpers/create_request_params_service.py +4 -2
- stoobly_agent/app/proxy/record/join_request_service.py +22 -10
- stoobly_agent/app/proxy/record/proxy_request.py +9 -0
- stoobly_agent/app/proxy/record/request_string.py +4 -1
- stoobly_agent/app/proxy/record/upload_request_service.py +7 -5
- stoobly_agent/app/proxy/test/helpers/upload_test_service.py +1 -1
- stoobly_agent/config/constants/lifecycle_hooks.py +1 -0
- stoobly_agent/lib/orm/transformers/orm_to_request_transformer.py +1 -1
- stoobly_agent/test/app/cli/scenario/scenario_snapshot_test.py +1 -1
- stoobly_agent/test/app/cli/snapshot/lifecycle_hooks_migrate.py +10 -0
- stoobly_agent/test/app/cli/snapshot/snapshot_migrate_test.py +181 -0
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
- {stoobly_agent-1.8.4.dist-info → stoobly_agent-1.8.5.dist-info}/METADATA +1 -1
- {stoobly_agent-1.8.4.dist-info → stoobly_agent-1.8.5.dist-info}/RECORD +33 -30
- {stoobly_agent-1.8.4.dist-info → stoobly_agent-1.8.5.dist-info}/LICENSE +0 -0
- {stoobly_agent-1.8.4.dist-info → stoobly_agent-1.8.5.dist-info}/WHEEL +0 -0
- {stoobly_agent-1.8.4.dist-info → stoobly_agent-1.8.5.dist-info}/entry_points.txt +0 -0
stoobly_agent/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
COMMAND = 'stoobly-agent'
|
2
|
-
VERSION = '1.8.
|
2
|
+
VERSION = '1.8.5'
|
@@ -103,13 +103,13 @@ class QueryParamsController:
|
|
103
103
|
|
104
104
|
if context.filter_response(query_param, status):
|
105
105
|
return
|
106
|
-
|
106
|
+
|
107
107
|
context.render(
|
108
|
-
plain =
|
109
|
-
status =
|
108
|
+
plain = '',
|
109
|
+
status = 204
|
110
110
|
)
|
111
111
|
|
112
112
|
def __query_param_model(self, context: SimpleHTTPRequestHandler):
|
113
113
|
access_token = context.headers.get('access-token')
|
114
114
|
query_param_model = QueryParamModel(Settings.instance(), access_token=access_token)
|
115
|
-
return query_param_model
|
115
|
+
return query_param_model
|
@@ -20,6 +20,7 @@ from stoobly_agent.lib.api.keys import RequestKey, ScenarioKey
|
|
20
20
|
|
21
21
|
from .helpers.print_service import FORMATS, print_snapshots, select_print_options
|
22
22
|
from .helpers.verify_raw_request_service import verify_raw_request
|
23
|
+
from .types.snapshot_migration import SnapshotMigration
|
23
24
|
|
24
25
|
@click.group(
|
25
26
|
epilog="Run 'stoobly-agent project COMMAND --help' for more information on a command.",
|
@@ -91,6 +92,62 @@ def _list(**kwargs):
|
|
91
92
|
if len(formatted_events):
|
92
93
|
print_snapshots(formatted_events, **print_options)
|
93
94
|
|
95
|
+
@snapshot.command(
|
96
|
+
help="Migrate snapshots."
|
97
|
+
)
|
98
|
+
@click.option('--scenario-key', help='Apply migration to specific scenario.')
|
99
|
+
@click.argument('lifecycle_hooks_path')
|
100
|
+
def migrate(**kwargs):
|
101
|
+
from runpy import run_path
|
102
|
+
from stoobly_agent.config.constants.lifecycle_hooks import BEFORE_MIGRATE
|
103
|
+
|
104
|
+
scenario_key = None
|
105
|
+
if kwargs['scenario_key']:
|
106
|
+
scenario_key = ScenarioKey(kwargs['scenario_key'])
|
107
|
+
|
108
|
+
try:
|
109
|
+
lifecycle_hooks = run_path(kwargs['lifecycle_hooks_path'])
|
110
|
+
except Exception as e:
|
111
|
+
lifecycle_hooks = {}
|
112
|
+
|
113
|
+
before_migrate_hook = None
|
114
|
+
if BEFORE_MIGRATE not in lifecycle_hooks:
|
115
|
+
sys.exit(1)
|
116
|
+
else:
|
117
|
+
before_migrate_hook = lifecycle_hooks[BEFORE_MIGRATE]
|
118
|
+
|
119
|
+
log = Log()
|
120
|
+
|
121
|
+
request_snapshots = []
|
122
|
+
for event in log.resource_events:
|
123
|
+
if event.is_scenario():
|
124
|
+
# If scenario_key is set, only consider that scenario
|
125
|
+
if scenario_key and event.resource_uuid != scenario_key.id:
|
126
|
+
continue
|
127
|
+
|
128
|
+
scenario_snapshot: ScenarioSnapshot = event.snapshot()
|
129
|
+
_request_snapshots = scenario_snapshot.request_snapshots
|
130
|
+
|
131
|
+
for request_snapshot in _request_snapshots:
|
132
|
+
# Scenario requests will have the same log event
|
133
|
+
request_snapshots.append((request_snapshot, event))
|
134
|
+
elif event.is_request():
|
135
|
+
# If scenario_key is set, only consider that scenario
|
136
|
+
if scenario_key:
|
137
|
+
continue
|
138
|
+
|
139
|
+
request_snapshot: RequestSnapshot = event.snapshot()
|
140
|
+
request_snapshots.append((request_snapshot, event))
|
141
|
+
|
142
|
+
snapshot_migrations = {}
|
143
|
+
for request_snapshot, event in request_snapshots:
|
144
|
+
if request_snapshot.uuid not in snapshot_migrations:
|
145
|
+
snapshot_migration = SnapshotMigration(request_snapshot, event)
|
146
|
+
snapshot_migrations[request_snapshot.uuid] = snapshot_migration
|
147
|
+
|
148
|
+
if before_migrate_hook(snapshot_migration):
|
149
|
+
break
|
150
|
+
|
94
151
|
@snapshot.command(
|
95
152
|
help="Prune deleted snapshots."
|
96
153
|
)
|
@@ -0,0 +1,61 @@
|
|
1
|
+
from mitmproxy.http import Request, Response
|
2
|
+
|
3
|
+
from stoobly_agent.app.models.factories.resource.local_db.helpers.log import Log
|
4
|
+
from stoobly_agent.app.models.factories.resource.local_db.helpers.log_event import LogEvent
|
5
|
+
from stoobly_agent.app.models.factories.resource.local_db.helpers.request_snapshot import RequestSnapshot
|
6
|
+
from stoobly_agent.app.proxy.record.join_request_service import join_request_from_request_response
|
7
|
+
|
8
|
+
class SnapshotMigration():
|
9
|
+
_request: Request
|
10
|
+
_response: Response
|
11
|
+
_snapshot: RequestSnapshot
|
12
|
+
|
13
|
+
def __init__(self, snapshot: RequestSnapshot, log_event: LogEvent, log: Log = None):
|
14
|
+
self._event = log_event
|
15
|
+
self._log = log or Log()
|
16
|
+
self._request = snapshot.mitmproxy_request
|
17
|
+
self._response = snapshot.mitmproxy_response
|
18
|
+
self._snapshot = snapshot
|
19
|
+
|
20
|
+
@property
|
21
|
+
def request(self):
|
22
|
+
return self._request
|
23
|
+
|
24
|
+
@request.setter
|
25
|
+
def request(self, v: Request):
|
26
|
+
if not isinstance(v, Request):
|
27
|
+
raise TypeError('Invalid type.')
|
28
|
+
self._request = v
|
29
|
+
|
30
|
+
@property
|
31
|
+
def response(self):
|
32
|
+
return self._response
|
33
|
+
|
34
|
+
@response.setter
|
35
|
+
def response(self, v: Response):
|
36
|
+
if not isinstance(v, Response):
|
37
|
+
raise TypeError('Invalid type.')
|
38
|
+
self._response = v
|
39
|
+
|
40
|
+
@property
|
41
|
+
def snapshot(self):
|
42
|
+
return self._snapshot
|
43
|
+
|
44
|
+
@property
|
45
|
+
def uuid(self):
|
46
|
+
return self.snapshot.uuid
|
47
|
+
|
48
|
+
def delete(self, log: Log = None):
|
49
|
+
log = log or self._log
|
50
|
+
new_event = self._event.duplicate_as_delete()
|
51
|
+
log.append(str(new_event))
|
52
|
+
|
53
|
+
def save(self, log: Log = None):
|
54
|
+
log = log or self._log
|
55
|
+
request_uuid = self.snapshot.uuid
|
56
|
+
joined_request = join_request_from_request_response(self.request, self.response, id=request_uuid)
|
57
|
+
raw_request = joined_request.build()
|
58
|
+
self.snapshot.write_raw(raw_request)
|
59
|
+
new_event = self._event.duplicate()
|
60
|
+
log.append(str(new_event))
|
61
|
+
return new_event
|
@@ -15,4 +15,4 @@ class MitmproxyRequestAdapter():
|
|
15
15
|
protocol = raw_http_request_adapter.protocol
|
16
16
|
http_version = protocol.split('/')[1]
|
17
17
|
|
18
|
-
return PythonRequestMitmproxyRequestAdapter(
|
18
|
+
return PythonRequestMitmproxyRequestAdapter(python_request, http_version).adapt()
|
@@ -13,10 +13,10 @@ class PythonRequestAdapterFactory():
|
|
13
13
|
self.__request = request
|
14
14
|
|
15
15
|
def mitmproxy_request(self, http_version: str = 'HTTP/1.1'):
|
16
|
-
return MitmproxyRequestAdapter(
|
16
|
+
return MitmproxyRequestAdapter(self.__request, http_version).adapt()
|
17
17
|
|
18
18
|
def raw_request(self, http_version: str = 'HTTP/1.1'):
|
19
|
-
return RawRequestAdapter(
|
19
|
+
return RawRequestAdapter(self.__request, http_version).adapt()
|
20
20
|
|
21
21
|
def stoobly_request(self):
|
22
22
|
return StooblyRequestAdapter(self.__request).adapt()
|
@@ -8,7 +8,7 @@ from stoobly_agent.lib.utils.decode import decode
|
|
8
8
|
|
9
9
|
class MitmproxyRequestAdapter():
|
10
10
|
|
11
|
-
def __init__(self,
|
11
|
+
def __init__(self, request: requests.Request, http_version: str = 'HTTP/1.1'):
|
12
12
|
self.__http_version = http_version
|
13
13
|
self.__request = request
|
14
14
|
|
@@ -9,12 +9,12 @@ from .mitmproxy_adapter import MitmproxyRequestAdapter
|
|
9
9
|
|
10
10
|
class RawRequestAdapter():
|
11
11
|
|
12
|
-
def __init__(self,
|
12
|
+
def __init__(self, request: requests.Request, http_version: str):
|
13
13
|
self.__http_version = http_version
|
14
14
|
self.__request = request
|
15
15
|
|
16
16
|
def adapt(self):
|
17
|
-
mitmproxy_request = MitmproxyRequestAdapter(self.
|
17
|
+
mitmproxy_request = MitmproxyRequestAdapter(self.__request, self.__http_version).adapt()
|
18
18
|
adapted_request = MitmproxyRequestFacade(mitmproxy_request)
|
19
19
|
proxy_request = ProxyRequest(adapted_request)
|
20
20
|
request_string = RequestString(proxy_request)
|
@@ -82,6 +82,13 @@ class Log():
|
|
82
82
|
|
83
83
|
return self.remove_processed_events(events, version_uuids)
|
84
84
|
|
85
|
+
@property
|
86
|
+
def resource_events(self) -> List[LogEvent]:
|
87
|
+
events = self.target_events
|
88
|
+
events = self.remove_dangling_events(events, events)
|
89
|
+
|
90
|
+
return list(filter(lambda e: e.action != DELETE_ACTION, events))
|
91
|
+
|
85
92
|
@property
|
86
93
|
def version(self):
|
87
94
|
version_file_path = DataDir.instance().snapshosts_version_path
|
@@ -84,6 +84,16 @@ class LogEvent():
|
|
84
84
|
toks[4] = str(int(time.time() * 1000))
|
85
85
|
return LogEvent(COLUMN_DELIMITTER.join(toks))
|
86
86
|
|
87
|
+
def duplicate_as_delete(self):
|
88
|
+
event = self.duplicate()
|
89
|
+
event.action = DELETE_ACTION
|
90
|
+
return event
|
91
|
+
|
92
|
+
def duplicate_as_put(self):
|
93
|
+
event = self.duplicate()
|
94
|
+
event.action = PUT_ACTION
|
95
|
+
return event
|
96
|
+
|
87
97
|
def is_request(self):
|
88
98
|
return self.resource == REQUEST_RESOURCE
|
89
99
|
|
@@ -34,6 +34,26 @@ class RequestSnapshot(Snapshot):
|
|
34
34
|
with open(request_file_path, 'rb') as fp:
|
35
35
|
return fp.read()
|
36
36
|
|
37
|
+
@property
|
38
|
+
def mitmproxy_request(self):
|
39
|
+
from stoobly_agent.app.models.adapters.python.request.mitmproxy_adapter import MitmproxyRequestAdapter
|
40
|
+
return MitmproxyRequestAdapter(self.python_request).adapt()
|
41
|
+
|
42
|
+
@property
|
43
|
+
def mitmproxy_response(self):
|
44
|
+
from stoobly_agent.app.models.adapters.python.response.mitmproxy_adapter import MitmproxyResponseAdapter
|
45
|
+
return MitmproxyResponseAdapter(self.python_response).adapt()
|
46
|
+
|
47
|
+
@property
|
48
|
+
def python_request(self):
|
49
|
+
from stoobly_agent.app.models.adapters.raw_joined.request.python_adapter import PythonRequestAdapter
|
50
|
+
return PythonRequestAdapter(self.request).adapt()
|
51
|
+
|
52
|
+
@property
|
53
|
+
def python_response(self):
|
54
|
+
from stoobly_agent.app.models.adapters.raw_joined.response.python_adapter import PythonResponseAdapter
|
55
|
+
return PythonResponseAdapter(self.request).adapt()
|
56
|
+
|
37
57
|
@property
|
38
58
|
def path(self):
|
39
59
|
dir_path = os.path.join(self.__requests_dir_path, self.uuid[0:2])
|
@@ -3,7 +3,7 @@ import os
|
|
3
3
|
import pdb
|
4
4
|
import shutil
|
5
5
|
|
6
|
-
from typing import Callable
|
6
|
+
from typing import Callable, List
|
7
7
|
|
8
8
|
from stoobly_agent.lib.orm.scenario import Scenario
|
9
9
|
|
@@ -46,6 +46,13 @@ class ScenarioSnapshot(Snapshot):
|
|
46
46
|
def metadata_path(self):
|
47
47
|
return os.path.join(self.__scenarios_dir_path, self.uuid)
|
48
48
|
|
49
|
+
@property
|
50
|
+
def request_snapshots(self) -> List[RequestSnapshot]:
|
51
|
+
snapshots = []
|
52
|
+
handler = lambda request_snapshot: snapshots.append(request_snapshot)
|
53
|
+
self.iter_request_snapshots(handler)
|
54
|
+
return snapshots
|
55
|
+
|
49
56
|
@property
|
50
57
|
def requests(self):
|
51
58
|
requests_file_path = self.requests_path
|
@@ -122,10 +122,10 @@ class LocalDBQueryParamAdapter(LocalDBAdapter):
|
|
122
122
|
parsed_url = parsed_url._replace(query=urlencode(_query_params, True))
|
123
123
|
request = LocalDBRequestAdapter(self.__request_orm).update(request_id, url=parsed_url.geturl())
|
124
124
|
|
125
|
-
return {
|
125
|
+
return self.success({
|
126
126
|
'name': name,
|
127
127
|
'value': value,
|
128
|
-
}
|
128
|
+
})
|
129
129
|
|
130
130
|
def __decode_id(self, id: str):
|
131
131
|
id = base64.b64decode(id)
|
@@ -218,15 +218,16 @@ class Apply():
|
|
218
218
|
|
219
219
|
snapshot_requests = {}
|
220
220
|
|
221
|
-
|
222
|
-
for
|
221
|
+
request_snapshots = snapshot.request_snapshots
|
222
|
+
for request_snapshot in request_snapshots:
|
223
|
+
raw_request = request_snapshot.request
|
224
|
+
|
223
225
|
toks = raw_request.split(REQUEST_STRING_CLRF.encode(), 1)
|
224
226
|
|
225
227
|
if len(toks) != 2:
|
226
228
|
return f"{snapshot.requests_path} contains an invalid request", 400
|
227
229
|
|
228
|
-
|
229
|
-
uuid = control.id
|
230
|
+
uuid = request_snapshot.uuid
|
230
231
|
res, status = self.__put_request(uuid, raw_request, scenario_id=scenario['id'])
|
231
232
|
|
232
233
|
if status != 200:
|
@@ -4,7 +4,7 @@ import requests
|
|
4
4
|
from stoobly_agent.app.models.adapters import JoinedRequestAdapter, RawHttpRequestAdapter, RawHttpResponseAdapter
|
5
5
|
from stoobly_agent.app.models.adapters.python import PythonRequestAdapterFactory, PythonResponseAdapterFactory
|
6
6
|
|
7
|
-
from stoobly_agent.app.proxy.record.join_request_service import InterceptSettings,
|
7
|
+
from stoobly_agent.app.proxy.record.join_request_service import InterceptSettings, join_request_from_request_response
|
8
8
|
from stoobly_agent.app.settings import Settings
|
9
9
|
from stoobly_agent.lib.logger import Logger
|
10
10
|
|
@@ -40,7 +40,9 @@ def build_params_from_python(request: requests.Request, response: requests.Respo
|
|
40
40
|
mitmproxy_response = PythonResponseAdapterFactory(response).mitmproxy_response()
|
41
41
|
|
42
42
|
intercept_settings = InterceptSettings(Settings.instance(), mitmproxy_request)
|
43
|
-
joined_request =
|
43
|
+
joined_request = join_request_from_request_response(
|
44
|
+
mitmproxy_request, mitmproxy_response, intercept_settings=intercept_settings
|
45
|
+
)
|
44
46
|
|
45
47
|
mitmproxy_flow_mock = MitmproxyFlowMock(mitmproxy_request, mitmproxy_response)
|
46
48
|
|
@@ -1,31 +1,43 @@
|
|
1
1
|
import pdb
|
2
2
|
|
3
|
-
from mitmproxy.http import HTTPFlow as MitmproxyHTTPFlow
|
4
|
-
from typing import
|
3
|
+
from mitmproxy.http import HTTPFlow as MitmproxyHTTPFlow, Request as MitmproxyRequest, Response as MitmproxyResponse
|
4
|
+
from typing import TypedDict
|
5
5
|
|
6
6
|
from stoobly_agent.app.proxy.intercept_settings import InterceptSettings
|
7
|
-
from stoobly_agent.app.settings.rewrite_rule import RewriteRule
|
8
7
|
|
9
8
|
from ..mitmproxy.request_facade import MitmproxyRequestFacade
|
10
9
|
from ..mitmproxy.response_facade import MitmproxyResponseFacade
|
11
|
-
from ..utils.rewrite import rewrite_request_response
|
12
10
|
from .joined_request import JoinedRequest
|
13
11
|
from .proxy_request import ProxyRequest
|
14
12
|
|
13
|
+
class JoinRequestOptions(TypedDict):
|
14
|
+
id: str
|
15
|
+
intercept_settings: InterceptSettings
|
16
|
+
|
15
17
|
def join_request(
|
16
|
-
adapted_request: MitmproxyRequestFacade, adapted_response: MitmproxyResponseFacade,
|
18
|
+
adapted_request: MitmproxyRequestFacade, adapted_response: MitmproxyResponseFacade, **options: JoinRequestOptions
|
17
19
|
) -> JoinedRequest:
|
20
|
+
intercept_settings: InterceptSettings = options.get('intercept_settings')
|
21
|
+
|
18
22
|
# Decorate request with service_url
|
19
|
-
upstream_url = intercept_settings.upstream_url
|
23
|
+
upstream_url = intercept_settings.upstream_url if intercept_settings else None
|
20
24
|
proxy_request = ProxyRequest(adapted_request, upstream_url)
|
21
25
|
|
26
|
+
if options.get('id'):
|
27
|
+
proxy_request.id = options['id']
|
28
|
+
|
22
29
|
# Create JoinedRequest
|
23
30
|
return JoinedRequest(proxy_request).with_response(adapted_response)
|
24
31
|
|
25
32
|
def join_request_from_flow(
|
26
|
-
flow: MitmproxyHTTPFlow,
|
33
|
+
flow: MitmproxyHTTPFlow, **options: JoinRequestOptions
|
27
34
|
) -> JoinedRequest:
|
28
|
-
|
29
|
-
|
35
|
+
return join_request_from_request_response(flow.request, flow.response, **options)
|
36
|
+
|
37
|
+
def join_request_from_request_response(
|
38
|
+
request: MitmproxyRequest, response: MitmproxyResponse, **options: JoinRequestOptions
|
39
|
+
):
|
40
|
+
request = MitmproxyRequestFacade(request)
|
41
|
+
response = MitmproxyResponseFacade(response)
|
30
42
|
|
31
|
-
return join_request(request, response,
|
43
|
+
return join_request(request, response, **options)
|
@@ -2,10 +2,19 @@ from ..mitmproxy.request_facade import MitmproxyRequestFacade
|
|
2
2
|
|
3
3
|
class ProxyRequest:
|
4
4
|
def __init__(self, request: MitmproxyRequestFacade, upstream_url: str = None):
|
5
|
+
self._id = None
|
5
6
|
self.request = request
|
6
7
|
|
7
8
|
self.upstream_url = upstream_url
|
8
9
|
|
10
|
+
@property
|
11
|
+
def id(self):
|
12
|
+
return self._id
|
13
|
+
|
14
|
+
@id.setter
|
15
|
+
def id(self, v):
|
16
|
+
self._id = v
|
17
|
+
|
9
18
|
def url(self):
|
10
19
|
url = self.request.url
|
11
20
|
|
@@ -15,6 +15,7 @@ class RequestString:
|
|
15
15
|
ENCODING = 'utf-8'
|
16
16
|
|
17
17
|
__current_time = None
|
18
|
+
request_id = None
|
18
19
|
|
19
20
|
def __init__(self, proxy_request: ProxyRequest):
|
20
21
|
self.__current_time = self.__get_current_time()
|
@@ -29,7 +30,9 @@ class RequestString:
|
|
29
30
|
self.__headers()
|
30
31
|
self.__body()
|
31
32
|
|
32
|
-
|
33
|
+
self.request_id = proxy_request.id
|
34
|
+
|
35
|
+
self.request_id = self.request_id or self.__generate_request_id()
|
33
36
|
|
34
37
|
def get(self, **kwargs):
|
35
38
|
if kwargs.get('control'):
|
@@ -4,6 +4,7 @@ import pdb
|
|
4
4
|
import time
|
5
5
|
import tempfile
|
6
6
|
|
7
|
+
from copy import deepcopy
|
7
8
|
from mitmproxy.http import HTTPFlow as MitmproxyHTTPFlow
|
8
9
|
from mitmproxy.http import Request as MitmproxyRequest
|
9
10
|
|
@@ -50,22 +51,23 @@ def upload_request(
|
|
50
51
|
):
|
51
52
|
Logger.instance(LOG_ID).info(f"{bcolors.OKCYAN}Recording{bcolors.ENDC} {flow.request.url}")
|
52
53
|
|
53
|
-
|
54
|
+
flow_copy = deepcopy(flow) # When applying modifications we don't want to persist them in the response
|
55
|
+
joined_request = join_request_from_flow(flow_copy, intercept_settings=intercept_settings)
|
54
56
|
|
55
57
|
project_key = intercept_settings.project_key
|
56
58
|
scenario_key = intercept_settings.scenario_key
|
57
59
|
body_params = __build_body_params(
|
58
60
|
project_key,
|
59
|
-
joined_request,
|
61
|
+
joined_request,
|
60
62
|
flow=flow,
|
61
63
|
scenario_key=scenario_key
|
62
64
|
)
|
63
65
|
|
64
66
|
# If request_origin is WEB, then we are in proxy
|
65
67
|
# This means that we have access to Cache singleton and do not need send a request to update the status
|
66
|
-
sync = intercept_settings.request_origin == request_origin.WEB
|
68
|
+
sync = intercept_settings.request_origin == request_origin.WEB
|
67
69
|
res = __upload_request_with_body_params(request_model, body_params, sync)
|
68
|
-
|
70
|
+
|
69
71
|
if intercept_settings.settings.is_debug():
|
70
72
|
file_path = __debug_request(flow.request, joined_request.build())
|
71
73
|
Logger.instance(LOG_ID).debug(f"Writing request to {file_path}")
|
@@ -142,4 +144,4 @@ def __debug_request(request: MitmproxyRequest, raw_requests: bytes):
|
|
142
144
|
with open(file_path, 'wb') as f:
|
143
145
|
f.write(raw_requests)
|
144
146
|
|
145
|
-
return file_path
|
147
|
+
return file_path
|
@@ -43,7 +43,7 @@ def upload_test(
|
|
43
43
|
flow: MitmproxyHTTPFlow,
|
44
44
|
**kwargs: UploadTestData
|
45
45
|
) -> Response:
|
46
|
-
joined_request = join_request_from_flow(flow, intercept_settings)
|
46
|
+
joined_request = join_request_from_flow(flow, intercept_settings=intercept_settings)
|
47
47
|
|
48
48
|
Logger.instance(LOG_ID).info(f"{bcolors.OKCYAN}Uploading{bcolors.ENDC} test results for {joined_request.proxy_request.url()}")
|
49
49
|
|
@@ -3,6 +3,7 @@ AFTER_RECORD = 'handle_after_record'
|
|
3
3
|
AFTER_REPLAY = 'handle_after_replay'
|
4
4
|
AFTER_TEST = 'handle_after_test'
|
5
5
|
BEFORE_MOCK = 'handle_before_mock'
|
6
|
+
BEFORE_MIGRATE = 'handle_before_migrate'
|
6
7
|
BEFORE_RECORD = 'handle_before_record'
|
7
8
|
BEFORE_REPLAY = 'handle_before_replay'
|
8
9
|
BEFORE_REQUEST = 'handle_before_request'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
from stoobly_agent.app.cli.types.snapshot_migration import SnapshotMigration
|
2
|
+
|
3
|
+
def handle_before_migrate(snapshot_migration: SnapshotMigration):
|
4
|
+
request = snapshot_migration.request
|
5
|
+
response = snapshot_migration.response
|
6
|
+
|
7
|
+
request.headers['X-Test'] = '1'
|
8
|
+
response.content = b'Test'
|
9
|
+
|
10
|
+
snapshot_migration.save()
|
@@ -0,0 +1,181 @@
|
|
1
|
+
import os
|
2
|
+
import pdb
|
3
|
+
import pytest
|
4
|
+
import time
|
5
|
+
|
6
|
+
from click.testing import CliRunner
|
7
|
+
|
8
|
+
from stoobly_agent.app.models.adapters.raw_http_request_adapter import RawHttpRequestAdapter
|
9
|
+
from stoobly_agent.app.models.adapters.raw_http_response_adapter import RawHttpResponseAdapter
|
10
|
+
from stoobly_agent.app.models.factories.resource.local_db.helpers.log import Log
|
11
|
+
from stoobly_agent.app.models.factories.resource.local_db.helpers.log_event import LogEvent
|
12
|
+
from stoobly_agent.cli import record, request, scenario, snapshot
|
13
|
+
from stoobly_agent.lib.orm.request import Request
|
14
|
+
from stoobly_agent.lib.orm.scenario import Scenario
|
15
|
+
from stoobly_agent.test.test_helper import DETERMINISTIC_GET_REQUEST_URL, reset
|
16
|
+
|
17
|
+
@pytest.fixture(scope='module')
|
18
|
+
def runner():
|
19
|
+
return CliRunner()
|
20
|
+
|
21
|
+
class TestMigrate():
|
22
|
+
@pytest.fixture(scope='class', autouse=True)
|
23
|
+
def settings(self):
|
24
|
+
return reset()
|
25
|
+
|
26
|
+
class TestWithoutScenarioKey():
|
27
|
+
|
28
|
+
@pytest.fixture(scope='class')
|
29
|
+
def recorded_request(self, runner: CliRunner):
|
30
|
+
record_result = runner.invoke(record, [DETERMINISTIC_GET_REQUEST_URL])
|
31
|
+
assert record_result.exit_code == 0
|
32
|
+
return Request.last()
|
33
|
+
|
34
|
+
@pytest.fixture(autouse=True, scope='class')
|
35
|
+
def put_event(self, runner: CliRunner, recorded_request: Request):
|
36
|
+
snapshot_result = runner.invoke(request, ['snapshot', recorded_request.key()])
|
37
|
+
assert snapshot_result.exit_code == 0
|
38
|
+
|
39
|
+
log = Log()
|
40
|
+
events = log.events
|
41
|
+
return events[len(events) - 1]
|
42
|
+
|
43
|
+
@pytest.fixture(autouse=True, scope='class')
|
44
|
+
def migrated_event(self, runner: CliRunner, put_event: LogEvent):
|
45
|
+
time.sleep(1) # Simulate update at different time
|
46
|
+
|
47
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
48
|
+
lifecycle_hooks_path = os.path.join(current_dir, 'lifecycle_hooks_migrate.py')
|
49
|
+
migrate_result = runner.invoke(snapshot, ['migrate', lifecycle_hooks_path], catch_exceptions=False)
|
50
|
+
assert migrate_result.exit_code == 0
|
51
|
+
|
52
|
+
log = Log()
|
53
|
+
events = log.events
|
54
|
+
assert len(events) == 2
|
55
|
+
assert events[0].uuid == put_event.uuid
|
56
|
+
return events[1]
|
57
|
+
|
58
|
+
@pytest.fixture(scope='class')
|
59
|
+
def migrated_request(self, runner: CliRunner, recorded_request: Request):
|
60
|
+
apply_result = runner.invoke(snapshot, ['apply'], catch_exceptions=False)
|
61
|
+
assert apply_result.exit_code == 0
|
62
|
+
|
63
|
+
request = Request.find(recorded_request.id)
|
64
|
+
assert request != None
|
65
|
+
|
66
|
+
return request
|
67
|
+
|
68
|
+
def test_it_has_different_uuid(self, migrated_event: LogEvent, put_event: LogEvent):
|
69
|
+
assert migrated_event.uuid != put_event.uuid
|
70
|
+
|
71
|
+
def test_it_has_same_action(self, migrated_event: LogEvent, put_event: LogEvent):
|
72
|
+
assert migrated_event.action == put_event.action
|
73
|
+
|
74
|
+
def test_it_has_same_resource(self, migrated_event: LogEvent, put_event: LogEvent):
|
75
|
+
assert migrated_event.resource == put_event.resource
|
76
|
+
|
77
|
+
def test_it_has_same_resource_uuid(self, migrated_event: LogEvent, put_event: LogEvent):
|
78
|
+
assert migrated_event.resource_uuid == put_event.resource_uuid
|
79
|
+
|
80
|
+
def test_it_has_different_created_at(self, migrated_event: LogEvent, put_event: LogEvent):
|
81
|
+
assert migrated_event.created_at != put_event.created_at
|
82
|
+
|
83
|
+
def test_it_migrates_request(self, migrated_request: Request):
|
84
|
+
python_request = RawHttpRequestAdapter(migrated_request.raw).to_request()
|
85
|
+
assert python_request.headers['X-Test'] == '1'
|
86
|
+
|
87
|
+
def test_it_migrates_response(self, migrated_request: Request):
|
88
|
+
python_response = RawHttpResponseAdapter(migrated_request.response.raw).to_response()
|
89
|
+
assert python_response.content == b'Test'
|
90
|
+
|
91
|
+
class TestWithScenarioKey():
|
92
|
+
|
93
|
+
@pytest.fixture(scope='class')
|
94
|
+
def created_scenario(self, runner: CliRunner):
|
95
|
+
create_result = runner.invoke(scenario, ['create', 'test'])
|
96
|
+
assert create_result.exit_code == 0
|
97
|
+
return Scenario.last()
|
98
|
+
|
99
|
+
@pytest.fixture(scope='class')
|
100
|
+
def recorded_request_one(self, runner: CliRunner, created_scenario: Scenario):
|
101
|
+
record_result = runner.invoke(record, ['--scenario-key', created_scenario.key(), DETERMINISTIC_GET_REQUEST_URL])
|
102
|
+
assert record_result.exit_code == 0
|
103
|
+
return Request.last()
|
104
|
+
|
105
|
+
@pytest.fixture(scope='class')
|
106
|
+
def recorded_request_two(self, runner: CliRunner):
|
107
|
+
record_result = runner.invoke(record, [DETERMINISTIC_GET_REQUEST_URL])
|
108
|
+
assert record_result.exit_code == 0
|
109
|
+
return Request.last()
|
110
|
+
|
111
|
+
@pytest.fixture(autouse=True, scope='class')
|
112
|
+
def put_event(self, runner: CliRunner, recorded_request_one: Request):
|
113
|
+
snapshot_result = runner.invoke(scenario, ['snapshot', recorded_request_one.scenario.key()])
|
114
|
+
assert snapshot_result.exit_code == 0
|
115
|
+
|
116
|
+
log = Log()
|
117
|
+
events = log.events
|
118
|
+
return events[len(events) - 1]
|
119
|
+
|
120
|
+
@pytest.fixture(autouse=True, scope='class')
|
121
|
+
def put_event_two(self, runner: CliRunner, recorded_request_two: Request):
|
122
|
+
snapshot_result = runner.invoke(request, ['snapshot', recorded_request_two.key()])
|
123
|
+
assert snapshot_result.exit_code == 0
|
124
|
+
|
125
|
+
log = Log()
|
126
|
+
events = log.events
|
127
|
+
return events[len(events) - 1]
|
128
|
+
|
129
|
+
@pytest.fixture(autouse=True, scope='class')
|
130
|
+
def migrated_event(
|
131
|
+
self, runner: CliRunner, put_event: LogEvent, put_event_two: LogEvent, created_scenario: Scenario
|
132
|
+
):
|
133
|
+
time.sleep(1) # Simulate update at different time
|
134
|
+
|
135
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
136
|
+
lifecycle_hooks_path = os.path.join(current_dir, 'lifecycle_hooks_migrate.py')
|
137
|
+
migrate_result = runner.invoke(snapshot,
|
138
|
+
['migrate', '--scenario-key', created_scenario.key(), lifecycle_hooks_path]
|
139
|
+
)
|
140
|
+
assert migrate_result.exit_code == 0
|
141
|
+
|
142
|
+
log = Log()
|
143
|
+
events = log.events
|
144
|
+
|
145
|
+
assert len(events) == 3
|
146
|
+
assert events[0].uuid == put_event.uuid
|
147
|
+
assert events[1].uuid == put_event_two.uuid
|
148
|
+
return events[2]
|
149
|
+
|
150
|
+
@pytest.fixture(scope='class')
|
151
|
+
def migrated_request(self, runner: CliRunner, recorded_request_one: Request):
|
152
|
+
apply_result = runner.invoke(snapshot, ['apply'], catch_exceptions=False)
|
153
|
+
assert apply_result.exit_code == 0
|
154
|
+
|
155
|
+
request = Request.find(recorded_request_one.id)
|
156
|
+
assert request != None
|
157
|
+
|
158
|
+
return request
|
159
|
+
|
160
|
+
def test_it_has_different_uuid(self, migrated_event: LogEvent, put_event: LogEvent):
|
161
|
+
assert migrated_event.uuid != put_event.uuid
|
162
|
+
|
163
|
+
def test_it_has_same_action(self, migrated_event: LogEvent, put_event: LogEvent):
|
164
|
+
assert migrated_event.action == put_event.action
|
165
|
+
|
166
|
+
def test_it_has_same_resource(self, migrated_event: LogEvent, put_event: LogEvent):
|
167
|
+
assert migrated_event.resource == put_event.resource
|
168
|
+
|
169
|
+
def test_it_has_same_resource_uuid(self, migrated_event: LogEvent, put_event: LogEvent):
|
170
|
+
assert migrated_event.resource_uuid == put_event.resource_uuid
|
171
|
+
|
172
|
+
def test_it_has_different_created_at(self, migrated_event: LogEvent, put_event: LogEvent):
|
173
|
+
assert migrated_event.created_at != put_event.created_at
|
174
|
+
|
175
|
+
def test_it_migrates_request(self, migrated_request: Request):
|
176
|
+
python_request = RawHttpRequestAdapter(migrated_request.raw).to_request()
|
177
|
+
assert python_request.headers['X-Test'] == '1'
|
178
|
+
|
179
|
+
def test_it_migrates_response(self, migrated_request: Request):
|
180
|
+
python_response = RawHttpResponseAdapter(migrated_request.response.raw).to_response()
|
181
|
+
assert python_response.content == b'Test'
|
@@ -1 +1 @@
|
|
1
|
-
1.8.
|
1
|
+
1.8.5
|
@@ -1,12 +1,12 @@
|
|
1
|
-
stoobly_agent/__init__.py,sha256=
|
1
|
+
stoobly_agent/__init__.py,sha256=GQNR-Vd2pipbgXV4dXfROzchWJkAabmUFXIF2vgyat8,44
|
2
2
|
stoobly_agent/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
stoobly_agent/app/api/__init__.py,sha256=ctkB8KR-eXO0SFhj602huHiyvQ3PslFWd8fkcufgrAI,1000
|
4
4
|
stoobly_agent/app/api/application_http_request_handler.py,sha256=Vvz53yB0bR7J-QqMAkLlhcZrA4P64ZEN7w8cMbgl6o0,5261
|
5
5
|
stoobly_agent/app/api/bodies_controller.py,sha256=tiuBIcLsRIUrVlStFwhGj_8AzsURSmW0E_MDQb09WXY,2267
|
6
6
|
stoobly_agent/app/api/configs_controller.py,sha256=wkO3_9cexH7BKwm_2KflKEEdxvBFwc9vju_wouUjjBI,4692
|
7
|
-
stoobly_agent/app/api/headers_controller.py,sha256=
|
7
|
+
stoobly_agent/app/api/headers_controller.py,sha256=qUM0U0WNn_87outx1y4r8Mfr6RqHO0UR91JP0-LNajc,2781
|
8
8
|
stoobly_agent/app/api/proxy_controller.py,sha256=XMGk8-5WAdJS1_n5Li0aA7-q_3vDG4Q0pqLq4YVzsDg,5056
|
9
|
-
stoobly_agent/app/api/query_params_controller.py,sha256=
|
9
|
+
stoobly_agent/app/api/query_params_controller.py,sha256=3bdu-Gr6hyLh5_wql2-s6kDRJlYoarmd7m0df2rdCMk,3621
|
10
10
|
stoobly_agent/app/api/replayed_response_headers_controller.py,sha256=0rdNlw74L5KsQxKJyKUf_lfoxoh82xKkJ7m1Nmz_3sM,1768
|
11
11
|
stoobly_agent/app/api/replayed_responses_controller.py,sha256=bPd8MmS1mT1uuWoJ6uhJQk_dafISDGhH8yIyEjCRCvw,3435
|
12
12
|
stoobly_agent/app/api/requests_controller.py,sha256=VzUhJwavUu7SJqP-Osg4S8IGv-6qDToG1iZJvdUNalM,9598
|
@@ -204,13 +204,14 @@ stoobly_agent/app/cli/scaffold/workflow_run_command.py,sha256=eF3aaK4OIZXYuSBEAe
|
|
204
204
|
stoobly_agent/app/cli/scaffold/workflow_validate_command.py,sha256=Uo_yo6rVR1ZR7xpvsQvlH48AyMBVLRupd4G-bRjzm_Q,5584
|
205
205
|
stoobly_agent/app/cli/scaffold_cli.py,sha256=zrXf2nUI5jRnf6x-j3kKmia8vH_AngjpweVnjdD_R3Q,30075
|
206
206
|
stoobly_agent/app/cli/scenario_cli.py,sha256=3J1EiJOvunkfWrEkOsanw-XrKkOk78ij_GjBlE9p7CE,8229
|
207
|
-
stoobly_agent/app/cli/snapshot_cli.py,sha256=
|
207
|
+
stoobly_agent/app/cli/snapshot_cli.py,sha256=Uf6g6ivsD0hUY8G99eU0fxzS3FymncAhI70PxV7Uaac,11919
|
208
208
|
stoobly_agent/app/cli/trace_cli.py,sha256=K7E-vx3JUcqEDSWOdIOi_AieKNQz7dBfmRrVvKDkzFI,4605
|
209
209
|
stoobly_agent/app/cli/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
210
210
|
stoobly_agent/app/cli/types/output.py,sha256=2wazv56g5IwLQeJCWfJXXAxTB9Y5WH1cKMHbpjbXNeo,439
|
211
211
|
stoobly_agent/app/cli/types/print_options.py,sha256=UQSf2aZu4rmamjgmt5g7xIZf8KV02uVHPCt6edZn_c0,219
|
212
212
|
stoobly_agent/app/cli/types/request.py,sha256=QthojE5sfx7OvKu-vVNnSUfGk8n4pLzuBQO0YgoO46Q,88
|
213
213
|
stoobly_agent/app/cli/types/scenario.py,sha256=28WxmOlbm2Bsek1uu7yc4hJGz-d5oHbYAro7LlFWRoc,81
|
214
|
+
stoobly_agent/app/cli/types/snapshot_migration.py,sha256=4_Re46FKjsflcTOO3qhNsbWWmdEU67SFsF-XE_FKG3M,1859
|
214
215
|
stoobly_agent/app/cli/types/test.py,sha256=1c458B7DFBWsEk5Q1CrZ2CUi84YzEzcs-W4qTcudwAk,714
|
215
216
|
stoobly_agent/app/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
216
217
|
stoobly_agent/app/models/adapters/__init__.py,sha256=cEEE--Bvrvk6DAsHx_uPgFhLnZJETP4zSBtWjMqyIKc,233
|
@@ -223,15 +224,15 @@ stoobly_agent/app/models/adapters/mitmproxy/response/python_adapter.py,sha256=cl
|
|
223
224
|
stoobly_agent/app/models/adapters/orm/__init__.py,sha256=0qVpOA89f42H87gVAQzu2uS_P-RnPbcTJWmbK8azMds,183
|
224
225
|
stoobly_agent/app/models/adapters/orm/joined_request_string_adapter.py,sha256=hn7QivdXfFOu8THLvB3ez2Tq39WsDE_Y7mwxUkMMnRI,1702
|
225
226
|
stoobly_agent/app/models/adapters/orm/request/__init__.py,sha256=blE8DoV4KNgU3ZKlcc4OW0OR9E6fKkP15zHAf7PtpSQ,313
|
226
|
-
stoobly_agent/app/models/adapters/orm/request/mitmproxy_adapter.py,sha256=
|
227
|
+
stoobly_agent/app/models/adapters/orm/request/mitmproxy_adapter.py,sha256=6uQI9PnLDr_0Gq5Dz8NORpQUQ6HVUAJVzvziCrMblbk,732
|
227
228
|
stoobly_agent/app/models/adapters/orm/request/python_adapter.py,sha256=LEjhyPAhNwXNdKyZc9BkYikd_CsXYc2yNoqsk5BRiJQ,397
|
228
229
|
stoobly_agent/app/models/adapters/orm/response/__init__.py,sha256=w_8krEc0nSGEbqfeNQfW27hi0WNM34S5bkn0SO_OtfA,455
|
229
230
|
stoobly_agent/app/models/adapters/orm/response/mitmproxy_adapter.py,sha256=IfY2_N6rcgCeVYAq5tgbvlKG5zN_BhPNZiy9xmNBcJA,571
|
230
231
|
stoobly_agent/app/models/adapters/orm/response/python_adapter.py,sha256=oHQAl4iPDHcVoS1gUkXKPtTsGAof4X-pw9ljHM-cnZ4,353
|
231
232
|
stoobly_agent/app/models/adapters/python/__init__.py,sha256=-myrvhs82ykPRtoTWVxTyicntSvFU1xp0_BoPhNLp20,99
|
232
|
-
stoobly_agent/app/models/adapters/python/request/__init__.py,sha256=
|
233
|
-
stoobly_agent/app/models/adapters/python/request/mitmproxy_adapter.py,sha256=
|
234
|
-
stoobly_agent/app/models/adapters/python/request/raw_adapter.py,sha256=
|
233
|
+
stoobly_agent/app/models/adapters/python/request/__init__.py,sha256=zgOz6a2DvYQ6JYJiL8KMnZv0PN5hP6oS63QfnH5FyYM,687
|
234
|
+
stoobly_agent/app/models/adapters/python/request/mitmproxy_adapter.py,sha256=4VmqJS32IIPBx8P4BZxYJgU11eoj-t8kagDuO3ohq5k,1317
|
235
|
+
stoobly_agent/app/models/adapters/python/request/raw_adapter.py,sha256=KI_UttTLzF5Rfd8kmexCbdkkUCQcNSVgrCm1JFLLi-o,784
|
235
236
|
stoobly_agent/app/models/adapters/python/request/stoobly_adapter.py,sha256=kSkTu6wQrZcTod6Alsd09kUU62hHtASRNC71IF4V7ec,1072
|
236
237
|
stoobly_agent/app/models/adapters/python/response/__init__.py,sha256=jDyYnJ59t_uPwj6N8fY849A4kPAbBHlaaqDCxXeSiWI,425
|
237
238
|
stoobly_agent/app/models/adapters/python/response/mitmproxy_adapter.py,sha256=Q_nXiGFgn8r236jBWVmEw4XDwhmyLhOycJFHepYMW6c,1602
|
@@ -256,19 +257,19 @@ stoobly_agent/app/models/factories/resource/local_db/body_adapter.py,sha256=lrnI
|
|
256
257
|
stoobly_agent/app/models/factories/resource/local_db/header_adapter.py,sha256=NQdCErFtJL7sBaLpKLYfJSEA3AiaaVuU7LUcGJ-dHOI,3104
|
257
258
|
stoobly_agent/app/models/factories/resource/local_db/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
258
259
|
stoobly_agent/app/models/factories/resource/local_db/helpers/create_request_columns_service.py,sha256=HABMelW-cvhm2WXaywbQcd4PQhzSrz4vAbN7uOdetXM,1541
|
259
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/log.py,sha256=
|
260
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/log_event.py,sha256=
|
260
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/log.py,sha256=0a3XRfrnbChCVrGpm5gqSlZ3zVK1S7nbWYwXTJawx58,9216
|
261
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/log_event.py,sha256=30wWhqibTWgnpibosclaB9OCWzArL7R4krGS8WW2bGY,3584
|
261
262
|
stoobly_agent/app/models/factories/resource/local_db/helpers/request_builder.py,sha256=PyVvsYmi5bBQ6PsUPxQ4nJM9rhhjGVOTd7ipuc2tMMM,3227
|
262
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py,sha256=
|
263
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py,sha256=
|
263
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py,sha256=XvUAqa9icAUPmktcAt30VnQspJVQZhA1K5n6ZVMRZIk,2686
|
264
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py,sha256=mv_79PgvG6Q7rwYXdV0wZMneb4eC_PPjBk612cQPLqo,4914
|
264
265
|
stoobly_agent/app/models/factories/resource/local_db/helpers/search.py,sha256=A7KVcmxj9c3CT2rh26YH6khiEPkB_4U1UHhiYelNaws,782
|
265
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot.py,sha256=
|
266
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot.py,sha256=_8WPNE2NnvLlDbqnCfgnQveppDWfFZnqCBcX6THb1AU,742
|
266
267
|
stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot_service.py,sha256=fbqh5J5sMj0n_YFQFei2xhXLrSGU9vNmSX_uifA3U3M,1671
|
267
268
|
stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot_types.py,sha256=1pX0jrg13632g_yv_sF1ABzPR7LY7r2HC9g1a5zR1qI,379
|
268
269
|
stoobly_agent/app/models/factories/resource/local_db/helpers/tiebreak_scenario_request.py,sha256=BajElzsTE115hWBT6v67eyNFnrHGz-sU6WBNCzWp7Fg,1237
|
269
270
|
stoobly_agent/app/models/factories/resource/local_db/local_db_adapter.py,sha256=Zeem6abkSHXRXTvE3XwzOkUY6fbNm9wocPP2qSX3zQ8,1439
|
270
271
|
stoobly_agent/app/models/factories/resource/local_db/orm_request_builder.py,sha256=4UjFY26ln2Drlg71R4AKY5vUZYbQ28HYQolJv3SBDqE,1581
|
271
|
-
stoobly_agent/app/models/factories/resource/local_db/query_param_adapter.py,sha256=
|
272
|
+
stoobly_agent/app/models/factories/resource/local_db/query_param_adapter.py,sha256=RSf7wunwOYyIQaJBih1Bd5G3ke65i89PN-WTaOa3ceM,4082
|
272
273
|
stoobly_agent/app/models/factories/resource/local_db/replayed_response_adapter.py,sha256=vUN9ARmRVrxKHpyzWB5w-XX-quOBMFpWOKkyBxTn6mM,3634
|
273
274
|
stoobly_agent/app/models/factories/resource/local_db/request_adapter.py,sha256=6lARtzqrN4m0qKp7PUT-PSjGYoCLwT9xsFuTmUnaqMM,13528
|
274
275
|
stoobly_agent/app/models/factories/resource/local_db/response_adapter.py,sha256=5mc5_EkPcN44TdrGrrC4egla-Dv6mkj7yrz-jVJ_PIw,3791
|
@@ -285,8 +286,8 @@ stoobly_agent/app/models/factories/resource/stoobly/request_adapter.py,sha256=Zr
|
|
285
286
|
stoobly_agent/app/models/factories/resource/stoobly/scenario_adapter.py,sha256=HnM4g5Qdv16QXj8u4JCiJm2Dbw9OhAxmn9e_R8oaHG4,1105
|
286
287
|
stoobly_agent/app/models/header_model.py,sha256=m91upRZr8GfE5um0d5dguUESKigBMWhSyu_X3HFk28Y,1406
|
287
288
|
stoobly_agent/app/models/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
288
|
-
stoobly_agent/app/models/helpers/apply.py,sha256=
|
289
|
-
stoobly_agent/app/models/helpers/create_request_params_service.py,sha256=
|
289
|
+
stoobly_agent/app/models/helpers/apply.py,sha256=aWy_OVdDebUDrz3O2qNDIFNPoN4JQJ8VgNzErvpW7TA,8324
|
290
|
+
stoobly_agent/app/models/helpers/create_request_params_service.py,sha256=o_VB2FsTGBX55UBBw1yQ8MtsJ4-0YXcxd4kNQ9l21nM,2070
|
290
291
|
stoobly_agent/app/models/model.py,sha256=77ZTByQmH5sWBcSrCF3kG_C4muHggcFyH1DWsOhIgvg,1180
|
291
292
|
stoobly_agent/app/models/query_param_model.py,sha256=EBj76phSJ9_45KgP0vIZGbkkG6-tSn_U1fNW_7qLy_4,1455
|
292
293
|
stoobly_agent/app/models/replayed_response_model.py,sha256=YSxDnUJb1XfB4eNK8ygax9ugBD-niZlK5V4C24tDeic,1819
|
@@ -334,15 +335,15 @@ stoobly_agent/app/proxy/mock/search_endpoint.py,sha256=HDN3tIkPnlgmkt-BbZDz7iwac
|
|
334
335
|
stoobly_agent/app/proxy/mock/types/__init__.py,sha256=r4Zwqx6V5HHZwrcYWEv6xSXG0nXaN5FqxqCm0d3U-GM,150
|
335
336
|
stoobly_agent/app/proxy/record/__init__.py,sha256=lbW-G6DZ8IrRpRzvgt-1tn2cRuyLT744u9ERIrZ2dFI,233
|
336
337
|
stoobly_agent/app/proxy/record/context.py,sha256=L9tykwoB35hOPHft3IHSPjuJ2v3TSj9SQAJ9E1q-U_4,343
|
337
|
-
stoobly_agent/app/proxy/record/join_request_service.py,sha256
|
338
|
+
stoobly_agent/app/proxy/record/join_request_service.py,sha256=XivD1Anb8P-FfAz5No1o2MqSz4g7w8wZs7QtlWcNMHU,1601
|
338
339
|
stoobly_agent/app/proxy/record/joined_request.py,sha256=va79Pkz5RWACJn07ipV_FtVmGUzjSlqhfUu_amw9bX8,1553
|
339
340
|
stoobly_agent/app/proxy/record/overwrite_scenario_service.py,sha256=3em2a2ry4MrE30XlAukat-l3L9cu4o13Cxhi6BBUgkc,940
|
340
|
-
stoobly_agent/app/proxy/record/proxy_request.py,sha256=
|
341
|
-
stoobly_agent/app/proxy/record/request_string.py,sha256=
|
341
|
+
stoobly_agent/app/proxy/record/proxy_request.py,sha256=CsvWx-dcnPLsuEGQ6IB4ofVstHy6esXVZ-R0-LroxRQ,557
|
342
|
+
stoobly_agent/app/proxy/record/request_string.py,sha256=IaBxlcKq0ZeGJBZud88DibkDaybJl7MIBRi1Yk3PXuA,2513
|
342
343
|
stoobly_agent/app/proxy/record/request_string_control.py,sha256=qnqfD8iD6qtACM9Bq_ZwVgT5tjr-Yg_JfdFYVvodF5Y,887
|
343
344
|
stoobly_agent/app/proxy/record/response_string.py,sha256=ZdWYAntE7Z2oPd8tDizo8fKqbgq_BeqZ1_-JJM55b44,2818
|
344
345
|
stoobly_agent/app/proxy/record/response_string_control.py,sha256=YXQGGxaldRFA3UEQKt8r6lZ0R-kYyp_zWOZErNwiLmQ,1105
|
345
|
-
stoobly_agent/app/proxy/record/upload_request_service.py,sha256=
|
346
|
+
stoobly_agent/app/proxy/record/upload_request_service.py,sha256=Yoatu_feC5Na0Tjv5r_ZmEKdNue1NJ58GRMoH3YWso0,5237
|
346
347
|
stoobly_agent/app/proxy/replay/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
347
348
|
stoobly_agent/app/proxy/replay/alias_context.py,sha256=u_CL64wrLcZiIYWvyOMFlpVcAsigC-8IqrE0iDeRMro,748
|
348
349
|
stoobly_agent/app/proxy/replay/alias_resolver.py,sha256=0t7dISfVoFIa_fOqhUZQr0IbTR3sN3nmGPK3OkM7GSw,3493
|
@@ -368,7 +369,7 @@ stoobly_agent/app/proxy/test/helpers/request_component_names_facade.py,sha256=-c
|
|
368
369
|
stoobly_agent/app/proxy/test/helpers/requests_response_adapter.py,sha256=WYDaTOhD_cKt3P8Y9uVZHeIS1lSIoT3HXO8pn8v2cMM,425
|
369
370
|
stoobly_agent/app/proxy/test/helpers/stoobly_response_adapter.py,sha256=wrvoxn-b7D4LylvBTPK5-Aqxq6oCcHSVeoIill0durk,634
|
370
371
|
stoobly_agent/app/proxy/test/helpers/test_results_builder.py,sha256=YGXgcFokekBiSMFlMGkdPsp91rmPWBJD93OM-eS_JOA,4412
|
371
|
-
stoobly_agent/app/proxy/test/helpers/upload_test_service.py,sha256=
|
372
|
+
stoobly_agent/app/proxy/test/helpers/upload_test_service.py,sha256=GEN2XG_Fkb9-gLm0lSKEhzTax4XDLsjR-tm6QqEieX0,2108
|
372
373
|
stoobly_agent/app/proxy/test/matchers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
373
374
|
stoobly_agent/app/proxy/test/matchers/context.py,sha256=8ZLKjBVAmJ_zlp6vb1SbHHgDdMB4XjYAQauz7Jf9I7k,7983
|
374
375
|
stoobly_agent/app/proxy/test/matchers/contract.py,sha256=eE5Q9uVHR8wjyXJU_GyUA_zoNOg1TCo4FCdWIr2sITU,5524
|
@@ -421,7 +422,7 @@ stoobly_agent/config/constants/custom_headers.py,sha256=cZZ3nae2fpnk_uUXFNYjrPp7
|
|
421
422
|
stoobly_agent/config/constants/env_vars.py,sha256=HAR_ZIdXXbpWQgCDaRR5RtpVyGXCsMLr_Fh8n6S12K0,1344
|
422
423
|
stoobly_agent/config/constants/headers.py,sha256=Hfv7R8_NPXAGaMiZPqywGZDnr0qcVUyfenPb4g465rE,169
|
423
424
|
stoobly_agent/config/constants/intercept_policy.py,sha256=5hIgOft8PQmCRdOHb5OEvEj10tU66DIQF3GYltlWyM8,25
|
424
|
-
stoobly_agent/config/constants/lifecycle_hooks.py,sha256=
|
425
|
+
stoobly_agent/config/constants/lifecycle_hooks.py,sha256=uEiINpFP_jOPghg_cprE2r6yn77_kP4-JPzycUTSwTg,887
|
425
426
|
stoobly_agent/config/constants/mock_policy.py,sha256=oS3eKL5Ezpnm_4zvztcLvvtKppxm7eKvOOBHYKyKpeU,133
|
426
427
|
stoobly_agent/config/constants/mode.py,sha256=J35vV1LPQRhD2ob6DaJ7mLGJrMhv-CVnC6IHWd7b_GM,160
|
427
428
|
stoobly_agent/config/constants/query_params.py,sha256=b-QQGKTqf-HePt0X4ltFksf5ETiIFPgBvh-kFrF7kUk,63
|
@@ -511,7 +512,7 @@ stoobly_agent/lib/orm/trace.py,sha256=gBDP5o5prdjHU47U1y1_NYz9RMpJt2OMj1prTvqXwn
|
|
511
512
|
stoobly_agent/lib/orm/trace_alias.py,sha256=RMlEY8k9mF81YmFNANkHOyFeTfyI4CSC6jNESlRBgSg,2381
|
512
513
|
stoobly_agent/lib/orm/trace_request.py,sha256=IQ4rC_MrTLqJ-aaUbM3ZRwMr7J1yN7i15cp6GFsN7YE,479
|
513
514
|
stoobly_agent/lib/orm/transformers/__init__.py,sha256=polEv91Do0puZAzea3HZzkA3T5zZ0RQFMHl0TZnTRuk,306
|
514
|
-
stoobly_agent/lib/orm/transformers/orm_to_request_transformer.py,sha256
|
515
|
+
stoobly_agent/lib/orm/transformers/orm_to_request_transformer.py,sha256=-j_7jrZBPGnWZLqt9yelsd_GsdaxaK8GkP6fGpI5lgU,3512
|
515
516
|
stoobly_agent/lib/orm/transformers/orm_to_requests_response_transformer.py,sha256=_dPweJxGpNGRVyQrgc0F1VGOSnrmVZSE1RaaCIjU454,1552
|
516
517
|
stoobly_agent/lib/orm/transformers/orm_to_stoobly_request_transformer.py,sha256=JcPZMWEFYJkAFwlwz0AbikEXM1t0Ot2i4YolVXAAUSg,4591
|
517
518
|
stoobly_agent/lib/orm/transformers/orm_to_stoobly_response_transformer.py,sha256=QowSh1F2p62AKOTQOTlwqUnWHtBX4sfXBxuNzuj3KH0,954
|
@@ -682,10 +683,12 @@ stoobly_agent/test/app/cli/scenario/scenario_create_test.py,sha256=fGqcjO1_1Ovdp
|
|
682
683
|
stoobly_agent/test/app/cli/scenario/scenario_replay_integration_test.py,sha256=NbGJzmvPsNLBR0ac65yt_cOTfpnsST1IG7i3F0euwAk,7031
|
683
684
|
stoobly_agent/test/app/cli/scenario/scenario_replay_test.py,sha256=XqR-GDrR8uUtxvukBuIFJ4GsOzBj9WWi4b0VKmfhFyE,6874
|
684
685
|
stoobly_agent/test/app/cli/scenario/scenario_reset_test.py,sha256=QOyytOoFu1FALny3-xwp9r4bfOE9fftgnhkDJWYxXh0,2021
|
685
|
-
stoobly_agent/test/app/cli/scenario/scenario_snapshot_test.py,sha256=
|
686
|
+
stoobly_agent/test/app/cli/scenario/scenario_snapshot_test.py,sha256=IAe1l69Smd8a9E6I0CVs8lgqnC4mI4M1EFfUjC4ZHs8,3396
|
686
687
|
stoobly_agent/test/app/cli/scenario/scenario_test_integration_test.py,sha256=BdXGe1xfb79tCCTKtp4sqI6CkZL_KamvQKLK8Wwb4u0,5543
|
688
|
+
stoobly_agent/test/app/cli/snapshot/lifecycle_hooks_migrate.py,sha256=x3x5vHfZ1xmoOpZ0yRSB3kbWbPjfbLk2PJFSa1xOLoU,316
|
687
689
|
stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py,sha256=p0Nyv66agEPOKY23lMNBZQNAXNL1zoYJz2N5ev4rvO0,21729
|
688
690
|
stoobly_agent/test/app/cli/snapshot/snapshot_copy_test.py,sha256=Yg78-FhSiG_r6Jpm-sN8sn0LjVXTwTOXt6hg8ni2GIY,1953
|
691
|
+
stoobly_agent/test/app/cli/snapshot/snapshot_migrate_test.py,sha256=voEvblK6CMGCrSJDTHVmkUkLXj0auNb78jxlGiiBBQQ,7370
|
689
692
|
stoobly_agent/test/app/cli/snapshot/snapshot_prune_test.py,sha256=Hi8-rpQ9ryy1Jdl_KB9ERXgbLHLKmuFQZaxn5EVFj9I,3752
|
690
693
|
stoobly_agent/test/app/cli/snapshot/snapshot_update_test.py,sha256=fILsX2M5j4wuLRP6LJTHe4CPB8gvaEbsSoYmFCHmKVk,4514
|
691
694
|
stoobly_agent/test/app/models/adapters/orm/joined_request_string_adapter_test.py,sha256=a2IHTk3l7aiLyYF7vtqissrk0MFTF2wlUBiaKWyJKfU,2667
|
@@ -702,7 +705,7 @@ stoobly_agent/test/app/models/factories/resource/local_db/helpers/log_test.py,sh
|
|
702
705
|
stoobly_agent/test/app/models/factories/resource/local_db/helpers/tiebreak_scenario_request_test.py,sha256=a1SFLyEyRRLuADvAw6ckQQKORFXvyK1lyrbkaLWx8oU,3399
|
703
706
|
stoobly_agent/test/app/models/factories/resource/local_db/request_adapter_test.py,sha256=Pzq1cBPnP9oSWG-p0c-VoymoHxgp483QmNwmV1b78RA,8453
|
704
707
|
stoobly_agent/test/app/models/factories/resource/local_db/response_adapter_test.py,sha256=9P95EKH5rZGOrmRkRIDlQZqtiLJHk9735og18Ffwpfw,2204
|
705
|
-
stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=
|
708
|
+
stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=vBPve_ulWPaMP-stw3TjfbPykk0t0aSluYQaVmn0ZJA,5
|
706
709
|
stoobly_agent/test/app/models/schemas/.stoobly/db/stoobly_agent.sqlite3,sha256=ch8gNx6zIelLKQx65gwFx_LRNqUD3EC5xcHZ0ukIQiU,188416
|
707
710
|
stoobly_agent/test/app/models/schemas/.stoobly/settings.yml,sha256=vLwMjweKOdod6tSLtIlyBefPQuNXq9wio4kBaODKtAU,726
|
708
711
|
stoobly_agent/test/app/models/schemas/.stoobly/tmp/options.json,sha256=OTRzarwus48CTrItedXCrgQttJHSEZonEYc7R_knvYg,2212
|
@@ -743,8 +746,8 @@ stoobly_agent/test/mock_data/scaffold/docker-compose-local-service.yml,sha256=1W
|
|
743
746
|
stoobly_agent/test/mock_data/scaffold/index.html,sha256=qJwuYajKZ4ihWZrJQ3BNObV5kf1VGnnm_vqlPJzdqLE,258
|
744
747
|
stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
|
745
748
|
stoobly_agent/test/test_helper.py,sha256=m_oAI7tmRYCNZdKfNqISWhMv3e44tjeYViQ3nTUfnos,1007
|
746
|
-
stoobly_agent-1.8.
|
747
|
-
stoobly_agent-1.8.
|
748
|
-
stoobly_agent-1.8.
|
749
|
-
stoobly_agent-1.8.
|
750
|
-
stoobly_agent-1.8.
|
749
|
+
stoobly_agent-1.8.5.dist-info/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
|
750
|
+
stoobly_agent-1.8.5.dist-info/METADATA,sha256=s6qQBEtGsqNOLm2JIOEX_M8O9Y0CEuzorBfg-umAM2U,3087
|
751
|
+
stoobly_agent-1.8.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
752
|
+
stoobly_agent-1.8.5.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
|
753
|
+
stoobly_agent-1.8.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|