stoobly-agent 0.34.5__py3-none-any.whl → 0.34.7__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 CHANGED
@@ -1,2 +1,2 @@
1
1
  COMMAND = 'stoobly-agent'
2
- VERSION = '0.34.5'
2
+ VERSION = '0.34.7'
@@ -88,7 +88,7 @@ def list(**kwargs):
88
88
  def replay(**kwargs):
89
89
  replay_handler(kwargs)
90
90
 
91
- if not is_remote:
91
+ if is_local:
92
92
  @request.command(
93
93
  help="Snapshot a request"
94
94
  )
@@ -74,6 +74,14 @@ def _list(**kwargs):
74
74
  if len(formatted_events):
75
75
  print_snapshots(formatted_events, **print_options)
76
76
 
77
+ @snapshot.command(
78
+ help="Prune deleted snapshots."
79
+ )
80
+ @click.option('--dry-run', is_flag=True, default=False)
81
+ def prune(**kwargs):
82
+ log = Log()
83
+ log.prune(kwargs['dry_run'])
84
+
77
85
  @snapshot.command(
78
86
  help="Update snapshot",
79
87
  )
@@ -5,8 +5,10 @@ import time
5
5
  from typing import List
6
6
 
7
7
  from stoobly_agent.config.data_dir import DataDir
8
+ from stoobly_agent.lib.logger import bcolors, Logger
8
9
 
9
- from .log_event import LogEvent, Resource
10
+ from .log_event import LogEvent
11
+ from .snapshot_types import DELETE_ACTION, PUT_ACTION, Resource
10
12
 
11
13
  EVENT_DELIMITTER = "\n"
12
14
 
@@ -28,7 +30,7 @@ class Log():
28
30
  if events_count == 0:
29
31
  return []
30
32
 
31
- return list(map(lambda raw_event: LogEvent(raw_event), self.raw_events))
33
+ return list(map(lambda raw_event: LogEvent(raw_event), events))
32
34
 
33
35
  @property
34
36
  def history_dir_path(self):
@@ -56,15 +58,12 @@ class Log():
56
58
  @property
57
59
  def target_events(self):
58
60
  events = self.events
59
- return self.prune(events)
61
+ return self.collapse(events)
60
62
 
61
63
  @property
62
64
  def raw_events(self):
63
65
  contents = self.read()
64
- if not contents:
65
- return []
66
- events = contents.strip().split(EVENT_DELIMITTER)
67
- return list(filter(lambda e: not not e, events))
66
+ return self.build_raw_events(contents)
68
67
 
69
68
  @property
70
69
  def unprocessed_events(self) -> List[LogEvent]:
@@ -75,14 +74,11 @@ class Log():
75
74
  return []
76
75
 
77
76
  version = self.version.strip()
78
- if not version:
79
- return self.prune(events)
80
-
81
- # Find diverge point
82
- version_uuids = version.split(EVENT_DELIMITTER)
83
- unprocessed_events: List[LogEvent] = self.remove_processed_events(events, version_uuids)
77
+ version_uuids = []
78
+ if version:
79
+ version_uuids = version.split(EVENT_DELIMITTER)
84
80
 
85
- return self.prune(unprocessed_events)
81
+ return self.remove_processed_events(events, version_uuids)
86
82
 
87
83
  @property
88
84
  def version(self):
@@ -126,6 +122,9 @@ class Log():
126
122
  with open(self.log_file_path, 'w') as fp:
127
123
  fp.write(_events + EVENT_DELIMITTER)
128
124
 
125
+ def build_log_events(self, raw_events) -> List[LogEvent]:
126
+ return list(map(lambda raw_event: LogEvent(raw_event), raw_events))
127
+
129
128
  def next_version(self, last_processed_uuid: str = None):
130
129
  uuids = self.uuids()
131
130
 
@@ -138,7 +137,7 @@ class Log():
138
137
 
139
138
  return self.generate_version(uuids)
140
139
 
141
- def prune(self, events: List[LogEvent]) -> List[LogEvent]:
140
+ def collapse(self, events: List[LogEvent]) -> List[LogEvent]:
142
141
  events_count = {}
143
142
 
144
143
  # More recent events take precedence over earlier ones, keep only the most recent event
@@ -164,6 +163,62 @@ class Log():
164
163
  serialized_event = LogEvent.serialize_put(resource)
165
164
  self.append(serialized_event)
166
165
 
166
+ def prune(self, dry_run = False):
167
+ # event uuid => history path
168
+ events: List[LogEvent] = []
169
+ path_index = {}
170
+
171
+ history_files = self.history_files
172
+ for file_path in history_files:
173
+ with open(file_path, 'r') as fp:
174
+ contents = fp.read().strip()
175
+ raw_events = self.build_raw_events(contents)
176
+ history_events = self.build_log_events(raw_events)
177
+
178
+ for event in history_events:
179
+ path_index[event.uuid] = file_path
180
+
181
+ events += history_events
182
+
183
+ # resource_uuid => event
184
+ resource_index = {}
185
+ for event in events:
186
+ if event.resource_uuid not in resource_index:
187
+ resource_index[event.resource_uuid] = []
188
+
189
+ resource_index[event.resource_uuid].append(event)
190
+
191
+ pruned_events = self.collapse(events)
192
+ for event in pruned_events:
193
+ snapshot = event.snapshot()
194
+ snapshot_exists = snapshot.exists
195
+
196
+ if event.action == DELETE_ACTION or not snapshot_exists:
197
+ Logger.instance().info(f"{bcolors.OKBLUE}Removing {event.resource} {event.resource_uuid}{bcolors.ENDC}")
198
+
199
+ resource_events: List[LogEvent] = resource_index[event.resource_uuid]
200
+ removed_events = {}
201
+
202
+ for event in resource_events:
203
+ history_path = path_index[event.uuid]
204
+ if event.uuid in removed_events:
205
+ continue
206
+
207
+ Logger.instance().info(f"Removing event {event.uuid}")
208
+ self.remove_event_history(event, history_path, dry_run)
209
+ removed_events[event.uuid] = True
210
+
211
+ if event.action == DELETE_ACTION and snapshot_exists:
212
+ if not dry_run:
213
+ snapshot.remove()
214
+ Logger.instance().info(f"Removing {event.resource} snapshot")
215
+
216
+ def build_raw_events(self, contents: str) -> List[str]:
217
+ if not contents:
218
+ return []
219
+ events = contents.strip().split(EVENT_DELIMITTER)
220
+ return list(filter(lambda e: not not e, events))
221
+
167
222
  def read(self):
168
223
  history_files = self.history_files
169
224
 
@@ -173,18 +228,56 @@ class Log():
173
228
  log.append(fp.read().strip())
174
229
 
175
230
  return EVENT_DELIMITTER.join(log)
176
-
177
- def remove_processed_events(self, events: List[LogEvent], version_uuids: List[str]):
178
- iterations = min(len(events), len(version_uuids))
179
- for i in range(0, iterations):
180
- event = events[i]
181
-
182
- if event.uuid != version_uuids[i]:
183
- i -= 1
184
- break
185
-
186
- remaining_version_uuids = version_uuids[i + 1:]
187
- remaining_events = events[i + 1:]
231
+
232
+ def remove_dangling_events(self, processed_events: List[LogEvent], unprocessed_events: List[LogEvent]):
233
+ '''
234
+ Remove DELETE events where the last processed event was a PUT
235
+ '''
236
+ index = {}
237
+ for event in processed_events:
238
+ if event.action == PUT_ACTION:
239
+ index[event.resource_uuid] = event
240
+ elif event.action == DELETE_ACTION:
241
+ if event.resource_uuid in index:
242
+ del index[event.resource_uuid]
243
+
244
+ return list(
245
+ filter(
246
+ lambda e: e.action != DELETE_ACTION or (e.action == DELETE_ACTION and e.resource_uuid in index),
247
+ unprocessed_events
248
+ )
249
+ )
250
+
251
+ def remove_event_history(self, event: LogEvent, history_path: str, dry_run = False):
252
+ events = []
253
+ raw_events = []
254
+
255
+ with open(history_path, 'r') as fp:
256
+ contents = fp.read().strip()
257
+ raw_events = self.build_raw_events(contents)
258
+ events = self.build_log_events(raw_events)
259
+ events = list(filter(lambda log_event: log_event.uuid != event.uuid, events))
260
+
261
+ if len(events) == 0:
262
+ Logger.instance().info(f"Removing {history_path}")
263
+
264
+ if not dry_run:
265
+ os.remove(history_path)
266
+ else:
267
+ new_raw_events = list(map(lambda event: str(event), events))
268
+ Logger.instance().info(f"Updating {history_path}, Events: {len(raw_events)} -> {len(new_raw_events)}")
269
+
270
+ if not dry_run:
271
+ with open(history_path, 'w') as fp:
272
+ fp.write(EVENT_DELIMITTER.join(new_raw_events))
273
+
274
+ def remove_processed_events(self, events, version_uuids):
275
+ i = self.__diverge_point(events, version_uuids)
276
+ processed_events = self.collapse(events[0:i])
277
+ unprocessed_events = self.collapse(events[i:])
278
+ remaining_version_uuids = version_uuids[i:]
279
+
280
+ remaining_events = self.remove_dangling_events(processed_events, unprocessed_events)
188
281
  return list(filter(lambda e: e.uuid not in remaining_version_uuids, remaining_events))
189
282
 
190
283
  # Rotate log to history
@@ -201,6 +294,15 @@ class Log():
201
294
  _events = events or self.events
202
295
  return list(map(lambda e: e.uuid, _events))
203
296
 
297
+ def __diverge_point(self, events: List[LogEvent], version_uuids: List[str]):
298
+ iterations = min(len(events), len(version_uuids))
299
+ for i in range(0, iterations):
300
+ event = events[i]
301
+
302
+ if event.uuid != version_uuids[i]:
303
+ return i
304
+ return iterations
305
+
204
306
  def __history_file_path(self, bucket_interval: int):
205
307
  file_name = f"{int(time.time() / bucket_interval) * bucket_interval}"
206
308
  return os.path.join(self.history_dir_path, file_name)
@@ -2,7 +2,7 @@ import pdb
2
2
  import uuid
3
3
  import time
4
4
 
5
- from typing import Callable, Literal, TypedDict
5
+ from typing import Callable, TypedDict
6
6
 
7
7
  from stoobly_agent.lib.orm.request import Request
8
8
  from stoobly_agent.lib.orm.scenario import Scenario
@@ -19,6 +19,10 @@ class RequestSnapshot(Snapshot):
19
19
  def backup(self):
20
20
  return self.__backup
21
21
 
22
+ @property
23
+ def exists(self):
24
+ return os.path.exists(self.path)
25
+
22
26
  @property
23
27
  def request(self):
24
28
  request_file_path = self.path
@@ -20,6 +20,10 @@ class ScenarioSnapshot(Snapshot):
20
20
  self.__metadata_backup = None
21
21
  self.__requests_backup = None
22
22
 
23
+ @property
24
+ def exists(self):
25
+ return os.path.exists(self.requests_path) and os.path.exists(self.metadata_path)
26
+
23
27
  @property
24
28
  def metadata(self):
25
29
  if not os.path.exists(self.metadata_path):
@@ -84,6 +88,10 @@ class ScenarioSnapshot(Snapshot):
84
88
  request_snapshot = RequestSnapshot(uuid)
85
89
  handler(request_snapshot)
86
90
 
91
+ def remove(self):
92
+ self.remove_metadata()
93
+ self.remove_requests()
94
+
87
95
  def remove_metadata(self):
88
96
  metadata_file_path = self.metadata_path
89
97
 
@@ -124,6 +124,9 @@ def __filter_options(options):
124
124
  if 'lifecycle_hooks_path' in options:
125
125
  del options['lifecycle_hooks_path']
126
126
 
127
+ if 'proxyless' in options:
128
+ del options['proxyless']
129
+
127
130
  if 'public_directory_path' in options:
128
131
  del options['public_directory_path']
129
132
 
stoobly_agent/cli.py CHANGED
@@ -98,6 +98,7 @@ def init(**kwargs):
98
98
  An empty header-value removes existing header-name headers. May be passed multiple times.
99
99
  ''')
100
100
  @click.option('--proxy-host', default='0.0.0.0', help='Address to bind proxy to.')
101
+ @click.option('--proxyless', is_flag=True, default=False, help='Disable starting proxy.')
101
102
  @click.option('--proxy-mode', default="regular", help='''
102
103
  Proxy mode can be "regular", "transparent", "socks5",
103
104
  "reverse:SPEC", or "upstream:SPEC". For reverse and
@@ -136,7 +137,8 @@ def run(**kwargs):
136
137
  if 'headless' in kwargs and not kwargs['headless']:
137
138
  run_api(**kwargs)
138
139
 
139
- run_proxy(**kwargs)
140
+ if 'proxyless' in kwargs and not kwargs['proxyless']:
141
+ run_proxy(**kwargs)
140
142
 
141
143
  @main.command(
142
144
  help="Mock request"
@@ -77,7 +77,7 @@ class TestRequestSnapshot():
77
77
  assert len(events) == 3
78
78
 
79
79
  unprocessed_events = log.unprocessed_events
80
- assert len(unprocessed_events) == 1
80
+ assert len(unprocessed_events) == 0
81
81
 
82
82
  class TestWhenAppending():
83
83
 
@@ -118,7 +118,7 @@ class TestRequestSnapshot():
118
118
  assert len(events) == 3
119
119
 
120
120
  unprocessed_events = log.unprocessed_events
121
- assert len(unprocessed_events) == 2
121
+ assert len(unprocessed_events) == 1
122
122
 
123
123
  event = unprocessed_events[0]
124
124
  assert event.resource_uuid == recorded_request_two.uuid
@@ -1,5 +1,6 @@
1
1
  import pdb
2
2
  import pytest
3
+ import time
3
4
 
4
5
  from click.testing import CliRunner
5
6
  from typing import List
@@ -23,6 +24,10 @@ class TestApply():
23
24
 
24
25
  class TestWhenDeletingRequest():
25
26
 
27
+ @pytest.fixture(scope='class')
28
+ def log(self):
29
+ return Log()
30
+
26
31
  @pytest.fixture(scope='class')
27
32
  def recorded_request(self, runner: CliRunner):
28
33
  record_result = runner.invoke(record, [DETERMINISTIC_GET_REQUEST_URL])
@@ -30,13 +35,24 @@ class TestApply():
30
35
  return Request.last()
31
36
 
32
37
  @pytest.fixture(autouse=True, scope='class')
33
- def delete_event(self, runner: CliRunner, recorded_request: Request):
34
- snapshot_result = runner.invoke(request, ['snapshot', '--action', DELETE_ACTION, recorded_request.key()])
38
+ def events(self, runner: CliRunner, recorded_request: Request, log: Log):
39
+ snapshot_result = runner.invoke(request, ['snapshot', recorded_request.key()])
35
40
  assert snapshot_result.exit_code == 0
36
41
 
37
- log = Log()
38
42
  events = log.events
39
- return events[len(events) - 1]
43
+ event: LogEvent = events[len(events) - 1]
44
+ log.version = event.uuid
45
+
46
+ time.sleep(1)
47
+
48
+ snapshot_result = runner.invoke(request, ['snapshot', '--action', DELETE_ACTION, recorded_request.key()])
49
+ assert snapshot_result.exit_code == 0
50
+
51
+ return log.events
52
+
53
+ @pytest.fixture(scope='class')
54
+ def delete_event(self, events: List[LogEvent]):
55
+ return events[1]
40
56
 
41
57
  def test_it_deletes(self, runner: CliRunner, recorded_request: Request, delete_event: LogEvent):
42
58
  apply_result = runner.invoke(snapshot, ['apply', '--force', delete_event.uuid])
@@ -82,6 +98,10 @@ class TestApply():
82
98
 
83
99
  class TestWhenDeletingScenario():
84
100
 
101
+ @pytest.fixture(scope='class')
102
+ def log(self):
103
+ return Log()
104
+
85
105
  @pytest.fixture(scope='class')
86
106
  def created_scenario(self, runner: CliRunner):
87
107
  create_result = runner.invoke(scenario, ['create', 'test'])
@@ -89,13 +109,24 @@ class TestApply():
89
109
  return Scenario.last()
90
110
 
91
111
  @pytest.fixture(autouse=True, scope='class')
92
- def delete_event(self, runner: CliRunner, created_scenario: Scenario):
93
- snapshot_result = runner.invoke(scenario, ['snapshot', '--action', DELETE_ACTION, created_scenario.key()])
112
+ def events(self, runner: CliRunner, created_scenario: Scenario, log: Log):
113
+ snapshot_result = runner.invoke(scenario, ['snapshot', created_scenario.key()])
94
114
  assert snapshot_result.exit_code == 0
95
115
 
96
- log = Log()
97
116
  events = log.events
98
- return events[len(events) - 1]
117
+ event: LogEvent = events[len(events) - 1]
118
+ log.version = event.uuid
119
+
120
+ time.sleep(1)
121
+
122
+ snapshot_result = runner.invoke(scenario, ['snapshot', '--action', DELETE_ACTION, created_scenario.key()])
123
+ assert snapshot_result.exit_code == 0
124
+
125
+ return log.events
126
+
127
+ @pytest.fixture(scope='class')
128
+ def delete_event(self, events):
129
+ return events[1]
99
130
 
100
131
  def test_it_deletes(self, runner: CliRunner, created_scenario: Scenario, delete_event: LogEvent):
101
132
  snapshot_result = runner.invoke(snapshot, ['apply', '--force', delete_event.uuid])
@@ -0,0 +1,113 @@
1
+ import pdb
2
+ import pytest
3
+ import time
4
+
5
+ from click.testing import CliRunner
6
+ from typing import List
7
+
8
+ from stoobly_agent.app.models.factories.resource.local_db.helpers.log import Log
9
+ from stoobly_agent.app.models.factories.resource.local_db.helpers.log_event import DELETE_ACTION, LogEvent
10
+ from stoobly_agent.app.models.factories.resource.local_db.helpers.scenario_snapshot import ScenarioSnapshot
11
+ from stoobly_agent.cli import record, request, scenario, snapshot
12
+ from stoobly_agent.lib.orm.request import Request
13
+ from stoobly_agent.lib.orm.scenario import Scenario
14
+ from stoobly_agent.test.test_helper import assert_orm_request_equivalent, DETERMINISTIC_GET_REQUEST_URL, NON_DETERMINISTIC_GET_REQUEST_URL, reset
15
+
16
+ @pytest.fixture(scope='module')
17
+ def runner():
18
+ return CliRunner()
19
+
20
+ class TestPrune():
21
+ @pytest.fixture(scope='class', autouse=True)
22
+ def settings(self):
23
+ return reset()
24
+
25
+ class TestWhenDeletingRequest():
26
+
27
+ @pytest.fixture(scope='class')
28
+ def log(self):
29
+ return Log()
30
+
31
+ @pytest.fixture(scope='class')
32
+ def recorded_request(self, runner: CliRunner):
33
+ record_result = runner.invoke(record, [DETERMINISTIC_GET_REQUEST_URL])
34
+ assert record_result.exit_code == 0
35
+ return Request.last()
36
+
37
+ @pytest.fixture(autouse=True, scope='class')
38
+ def events(self, runner: CliRunner, recorded_request: Request, log: Log):
39
+ snapshot_result = runner.invoke(request, ['snapshot', recorded_request.key()])
40
+ assert snapshot_result.exit_code == 0
41
+
42
+ events = log.events
43
+ event: LogEvent = events[len(events) - 1]
44
+ log.version = event.uuid
45
+
46
+ time.sleep(1)
47
+
48
+ snapshot_result = runner.invoke(request, ['snapshot', '--action', DELETE_ACTION, recorded_request.key()])
49
+ assert snapshot_result.exit_code == 0
50
+
51
+ return log.events
52
+
53
+ def test_it_prunes(self, runner: CliRunner, log: Log):
54
+ assert len(log.events) == 2
55
+ apply_result = runner.invoke(snapshot, ['prune'])
56
+ assert apply_result.exit_code == 0
57
+ assert len(log.events) == 0
58
+
59
+ class TestWhenCreatingRequest():
60
+
61
+ @pytest.fixture(scope='class')
62
+ def log(self):
63
+ return Log()
64
+
65
+ @pytest.fixture(scope='function')
66
+ def recorded_request(self, runner: CliRunner):
67
+ record_result = runner.invoke(record, [DETERMINISTIC_GET_REQUEST_URL])
68
+ assert record_result.exit_code == 0
69
+ return Request.last()
70
+
71
+ @pytest.fixture(autouse=True, scope='function')
72
+ def put_event(self, runner: CliRunner, recorded_request: Request):
73
+ snapshot_result = runner.invoke(request, ['snapshot', recorded_request.key()])
74
+ assert snapshot_result.exit_code == 0
75
+
76
+ log = Log()
77
+ events = log.events
78
+ return events[len(events) - 1]
79
+
80
+ def test_it_does_not_prune(self, runner: CliRunner, log: Log):
81
+ apply_result = runner.invoke(snapshot, ['prune'])
82
+ assert apply_result.exit_code == 0
83
+ assert len(log.events) == 1
84
+
85
+ class TestWhenMissingSnapshot():
86
+
87
+ @pytest.fixture(scope='class')
88
+ def log(self):
89
+ return Log()
90
+
91
+ @pytest.fixture(scope='function')
92
+ def recorded_request(self, runner: CliRunner):
93
+ record_result = runner.invoke(record, [DETERMINISTIC_GET_REQUEST_URL])
94
+ assert record_result.exit_code == 0
95
+ return Request.last()
96
+
97
+ @pytest.fixture(autouse=True, scope='function')
98
+ def put_event(self, runner: CliRunner, recorded_request: Request):
99
+ snapshot_result = runner.invoke(request, ['snapshot', recorded_request.key()])
100
+ assert snapshot_result.exit_code == 0
101
+
102
+ log = Log()
103
+ events = log.events
104
+ return events[len(events) - 1]
105
+
106
+ def test_it_prunes(self, runner: CliRunner, log: Log):
107
+ event = log.events[0]
108
+ event.snapshot().remove()
109
+ assert len(log.events) == 1
110
+
111
+ apply_result = runner.invoke(snapshot, ['prune'])
112
+ assert apply_result.exit_code == 0
113
+ assert len(log.events) == 0
@@ -1 +1 @@
1
- 0.34.3
1
+ 0.34.6
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stoobly-agent
3
- Version: 0.34.5
3
+ Version: 0.34.7
4
4
  Summary: Record, mock, and test HTTP(s) requests. CLI agent for Stoobly
5
5
  License: Apache-2.0
6
6
  Author: Matt Le
@@ -1,4 +1,4 @@
1
- stoobly_agent/__init__.py,sha256=etFwPFpegxWHv7qtGZNy9ZBIAKSsVmi6O_geZY3r85I,45
1
+ stoobly_agent/__init__.py,sha256=YRhtJ34t0WtULrjMrEGKhizGdQGPnijkWflQA-KjphE,45
2
2
  stoobly_agent/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  stoobly_agent/app/api/__init__.py,sha256=FFSlVoTgjPfUNlYPr_7u6-P5Y4WOKyaUfAHtUcB-Xio,810
4
4
  stoobly_agent/app/api/application_http_request_handler.py,sha256=jf4fkqjOiCeI2IM5Ro7ie0v_C6y0-7-5TIE_IKMPOfg,5513
@@ -64,9 +64,9 @@ stoobly_agent/app/cli/intercept_cli.py,sha256=jYbmH9FUI6J9vTlC4ycwRRNVYk0GRC8ZI5
64
64
  stoobly_agent/app/cli/main_group.py,sha256=kHR-0P9QL2No4XG1asgDfisIDdfxgLbj30y3vEobClQ,2175
65
65
  stoobly_agent/app/cli/project_cli.py,sha256=EXjeLjbnq9PhfCjvyfZ0UnJ2tejeCS0SIAo3Nc4fKOc,3852
66
66
  stoobly_agent/app/cli/report_cli.py,sha256=ZxJw0Xkx7KFZJn9e45BSKRKon8AD0Msrwy1fbPfbv0c,2543
67
- stoobly_agent/app/cli/request_cli.py,sha256=hTN9qSq7lkKYLtAZHWMCYFvuoUoYZEhsb6CWMtn0ucI,7752
67
+ stoobly_agent/app/cli/request_cli.py,sha256=THNloW111l9MLE0oPg4y7hVXL1U7OXoz760g0A1CtJ0,7747
68
68
  stoobly_agent/app/cli/scenario_cli.py,sha256=3J1EiJOvunkfWrEkOsanw-XrKkOk78ij_GjBlE9p7CE,8229
69
- stoobly_agent/app/cli/snapshot_cli.py,sha256=SEbCHt0NxCSDR86p6doNpS_SbOIVhJ-H_JD8yDOOps0,7578
69
+ stoobly_agent/app/cli/snapshot_cli.py,sha256=1AvCNiw9KyCejcftjdKozmxK3f2EE1caHuMeANdmuT4,7756
70
70
  stoobly_agent/app/cli/trace_cli.py,sha256=K7E-vx3JUcqEDSWOdIOi_AieKNQz7dBfmRrVvKDkzFI,4605
71
71
  stoobly_agent/app/cli/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
72
  stoobly_agent/app/cli/types/output.py,sha256=2wazv56g5IwLQeJCWfJXXAxTB9Y5WH1cKMHbpjbXNeo,439
@@ -118,11 +118,11 @@ stoobly_agent/app/models/factories/resource/local_db/body_adapter.py,sha256=lrnI
118
118
  stoobly_agent/app/models/factories/resource/local_db/header_adapter.py,sha256=gIhWeoSPCK7gjL6CQFi39zYqkd38Lb1fVKhzW5DJSeE,2640
119
119
  stoobly_agent/app/models/factories/resource/local_db/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
120
120
  stoobly_agent/app/models/factories/resource/local_db/helpers/create_request_columns_service.py,sha256=HABMelW-cvhm2WXaywbQcd4PQhzSrz4vAbN7uOdetXM,1541
121
- stoobly_agent/app/models/factories/resource/local_db/helpers/log.py,sha256=BKIWS_UoRyZxbJ7Z3zU8gp-5Tvb1n3BLk-jprQje7uc,5252
122
- stoobly_agent/app/models/factories/resource/local_db/helpers/log_event.py,sha256=qRws34HK5fv3C_rEoIzpeIwZrSVhCG9sLz_jiuIbatY,3373
121
+ stoobly_agent/app/models/factories/resource/local_db/helpers/log.py,sha256=quuL1YoElqqeKk1tpSaxYbMyOdrWQyHnrM_KiT27S6w,8909
122
+ stoobly_agent/app/models/factories/resource/local_db/helpers/log_event.py,sha256=vYZ-tgl5brQREj2eQ9LPsWfwDIXN45ip6VE1ZZBKc10,3364
123
123
  stoobly_agent/app/models/factories/resource/local_db/helpers/request_builder.py,sha256=PyVvsYmi5bBQ6PsUPxQ4nJM9rhhjGVOTd7ipuc2tMMM,3227
124
- stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py,sha256=W0CszJaKD_-e5tr7JtfWfoOBXOl5dZBCrRXPhTF7hQk,1640
125
- stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py,sha256=HayWx_-O7JtTYM-6k3fJbErdtoGan7M9C6l3q3KwrCI,3652
124
+ stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py,sha256=u2sgQrhLJNbrsi1FMXFRZn8Gj8zYnmn3WizyhR3bHeo,1710
125
+ stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py,sha256=gpn4F26cSJiry93dECDqlS8PLIkN9Gh0nLgGztlfgsU,3845
126
126
  stoobly_agent/app/models/factories/resource/local_db/helpers/search.py,sha256=A7KVcmxj9c3CT2rh26YH6khiEPkB_4U1UHhiYelNaws,782
127
127
  stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot.py,sha256=4w3sHiG5F6gUdMfNTm5EfPrHJ-ah00KkJshypAIbmm0,312
128
128
  stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot_service.py,sha256=fbqh5J5sMj0n_YFQFei2xhXLrSGU9vNmSX_uifA3U3M,1671
@@ -214,7 +214,7 @@ stoobly_agent/app/proxy/replay/replay_request_service.py,sha256=1OyrGb9Vfflhcfy4
214
214
  stoobly_agent/app/proxy/replay/replay_scenario_service.py,sha256=9jV-iO5EBg8geUblEtjjWRFIkom_Pqmo7P-lTc3S4Xw,2824
215
215
  stoobly_agent/app/proxy/replay/rewrite_params_service.py,sha256=jEHlT6_OHq_VBa09Hd6QaRyErv7tZnziDvW7m3Q8CQg,2234
216
216
  stoobly_agent/app/proxy/replay/trace_context.py,sha256=n-jqoVeZaUqzTdHI4ImaQ5vUqLfU4sMDDPW0Ydpvuic,10362
217
- stoobly_agent/app/proxy/run.py,sha256=VgA-IoSjvXm2wD-3j6lc3w7Aiz3QZs_s7ohx_JyVB3w,3970
217
+ stoobly_agent/app/proxy/run.py,sha256=YQEPhZylsKhqFdGzEnw1hp-OwbgckKSrwv9SmK1Ln3w,4035
218
218
  stoobly_agent/app/proxy/settings.py,sha256=R0LkSa9HrkUXvCd-nur4syJePjbQZdlnAnOPpGnCx38,2172
219
219
  stoobly_agent/app/proxy/simulate_intercept_service.py,sha256=R-L2dh2dfYFebttWXU0NwyxFI_jP6Ud36oKPC-T8HiI,1942
220
220
  stoobly_agent/app/proxy/test/__init__.py,sha256=uW0Ab27oyH2odTeVRjcuUJF8A1FLbTT5sBMzhGZr1so,89
@@ -273,7 +273,7 @@ stoobly_agent/app/settings/types/remote_settings.py,sha256=4PvEGKULXM0zv29XTDzV7
273
273
  stoobly_agent/app/settings/types/ui_settings.py,sha256=BqPy2F32AbODqzi2mp2kRk28QVUydQIwVmvftn46pco,84
274
274
  stoobly_agent/app/settings/ui_settings.py,sha256=YDEUMPuJFh0SLHaGz6O-Gpz8nwsunNzeuc-TzO9OUbM,1170
275
275
  stoobly_agent/app/settings/url_rule.py,sha256=Qx7YrIpVRSC-4LeNiCAfCtE50Jou4423hojMW-4qUYg,954
276
- stoobly_agent/cli.py,sha256=toxciGfZBFA5qJWZs9AScJhUpdQN12uohd0iFGXuSpE,9052
276
+ stoobly_agent/cli.py,sha256=AS1ZdVnW5S7pm5YAfzJ_w3_4ot9fKqfBcbQOB4FrYTI,9204
277
277
  stoobly_agent/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
278
278
  stoobly_agent/config/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
279
279
  stoobly_agent/config/constants/alias_resolve_strategy.py,sha256=_R1tVqFnyGxCraVS5-dhSskaDj_X8-NthsY7i_bEt9M,119
@@ -529,7 +529,7 @@ stoobly_agent/test/app/cli/request/request_list_test.py,sha256=tqMjkODU2i2eDvYyk
529
529
  stoobly_agent/test/app/cli/request/request_replay_test.py,sha256=w13NzkXhmBKvoogpQ8CdmBbCzoyqxEQvBZsEkGDCPx0,6237
530
530
  stoobly_agent/test/app/cli/request/request_reset_test.py,sha256=5My6Z452eideAOUun77tWUkuu3_yGhDVxCG1baUF8Zo,1290
531
531
  stoobly_agent/test/app/cli/request/request_response_test.py,sha256=Fu-A8tIn016DKme4WIaPzo3YeFY-CPtTOpaSFigUVVM,1263
532
- stoobly_agent/test/app/cli/request/request_snapshot_test.py,sha256=izc61Zh7YGdYzWFvFgnBOdlX6Mqwo55sBxIgHW9bmwc,4425
532
+ stoobly_agent/test/app/cli/request/request_snapshot_test.py,sha256=0013aoiMZin-2YEtHzEmQspAPA3SUd_6XiItbX0U7Ok,4425
533
533
  stoobly_agent/test/app/cli/request/request_test_test.py,sha256=-cJNXKjgryVVfVt-7IN5fIhBwe3NjFoPmeavDH8lAjU,5527
534
534
  stoobly_agent/test/app/cli/scenario/scenario_create_test.py,sha256=fGqcjO1_1OvdpUMQfGRVkSyFe61u8WIcp_ndLFrf33A,3962
535
535
  stoobly_agent/test/app/cli/scenario/scenario_replay_integration_test.py,sha256=NbGJzmvPsNLBR0ac65yt_cOTfpnsST1IG7i3F0euwAk,7031
@@ -537,7 +537,8 @@ stoobly_agent/test/app/cli/scenario/scenario_replay_test.py,sha256=XqR-GDrR8uUtx
537
537
  stoobly_agent/test/app/cli/scenario/scenario_reset_test.py,sha256=QOyytOoFu1FALny3-xwp9r4bfOE9fftgnhkDJWYxXh0,2021
538
538
  stoobly_agent/test/app/cli/scenario/scenario_snapshot_test.py,sha256=vk572Oxp1c8Y4utqq3L24nTkFj5Fttig0R2cWXUzg3E,3395
539
539
  stoobly_agent/test/app/cli/scenario/scenario_test_integration_test.py,sha256=BdXGe1xfb79tCCTKtp4sqI6CkZL_KamvQKLK8Wwb4u0,5543
540
- stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py,sha256=EYE3QdoGpLIoawd5tjtQHSnxsklXWN1RmTnFcLD9WEE,14713
540
+ stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py,sha256=gyNWX9b4PFkmxQxEth8xe1-xNryvjBuM0AyuJsEPOQA,15529
541
+ stoobly_agent/test/app/cli/snapshot/snapshot_prune_test.py,sha256=221I9q-1L4qDE-CnvPO2eRCFphrbjedx5-KhRpaPQig,4012
541
542
  stoobly_agent/test/app/cli/snapshot/snapshot_update_test.py,sha256=1SBwY4uijn3UxSUHNafbj0leX6kIeb9bd3f_AqKLZxs,4538
542
543
  stoobly_agent/test/app/models/adapters/orm/joined_request_string_adapter_test.py,sha256=a2IHTk3l7aiLyYF7vtqissrk0MFTF2wlUBiaKWyJKfU,2667
543
544
  stoobly_agent/test/app/models/adapters/orm/request/orm_mitmproxy_request_adapter_test.py,sha256=PbJsAaxPUEbF9vM7DX4z858biWf4qlGnvE8KBuy8SgY,2763
@@ -553,7 +554,7 @@ stoobly_agent/test/app/models/factories/resource/local_db/helpers/log_test.py,sh
553
554
  stoobly_agent/test/app/models/factories/resource/local_db/helpers/tiebreak_scenario_request_test.py,sha256=Ft5POn1ZJqt3YOSdPOb0PwwCaIkBEpw3Jug9WAeAt-g,2863
554
555
  stoobly_agent/test/app/models/factories/resource/local_db/request_adapter_test.py,sha256=Pzq1cBPnP9oSWG-p0c-VoymoHxgp483QmNwmV1b78RA,8453
555
556
  stoobly_agent/test/app/models/factories/resource/local_db/response_adapter_test.py,sha256=9P95EKH5rZGOrmRkRIDlQZqtiLJHk9735og18Ffwpfw,2204
556
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=2DWRDXuoJ1f5QmN2z7oi7X5wOD_gfSGZmUvWQOQ9zOw,7
557
+ stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=E9YELIgwnEFxYMI--zJNcBtpkH9G7fPbJrBsKEXnYRU,6
557
558
  stoobly_agent/test/app/models/schemas/.stoobly/db/stoobly_agent.sqlite3,sha256=ch8gNx6zIelLKQx65gwFx_LRNqUD3EC5xcHZ0ukIQiU,188416
558
559
  stoobly_agent/test/app/models/schemas/.stoobly/settings.yml,sha256=vLwMjweKOdod6tSLtIlyBefPQuNXq9wio4kBaODKtAU,726
559
560
  stoobly_agent/test/app/models/schemas/.stoobly/tmp/options.json,sha256=OTRzarwus48CTrItedXCrgQttJHSEZonEYc7R_knvYg,2212
@@ -587,8 +588,8 @@ stoobly_agent/test/mock_data/petstore.yaml,sha256=CCdliJky04Az4FIOkFA883uunwFDHL
587
588
  stoobly_agent/test/mock_data/request_show_response.py,sha256=K_a0fP0QT58T8sX9PaM6hqtX1A1depZsqg_GsNPf--k,707
588
589
  stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
589
590
  stoobly_agent/test/test_helper.py,sha256=m_oAI7tmRYCNZdKfNqISWhMv3e44tjeYViQ3nTUfnos,1007
590
- stoobly_agent-0.34.5.dist-info/LICENSE,sha256=8QKGyy45eN76Zk52h8gu1DKX2B_gbWgZ3nzDLofEbaE,548
591
- stoobly_agent-0.34.5.dist-info/METADATA,sha256=wfwZZRlFLj8tuouNOMtLfTLXKg6spsqvKx_SD3CY-Lg,3304
592
- stoobly_agent-0.34.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
593
- stoobly_agent-0.34.5.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
594
- stoobly_agent-0.34.5.dist-info/RECORD,,
591
+ stoobly_agent-0.34.7.dist-info/LICENSE,sha256=8QKGyy45eN76Zk52h8gu1DKX2B_gbWgZ3nzDLofEbaE,548
592
+ stoobly_agent-0.34.7.dist-info/METADATA,sha256=pWqiDODcIgUwr_58gRyWrBY3BIHa3nxGIHi4cDpiC8Y,3304
593
+ stoobly_agent-0.34.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
594
+ stoobly_agent-0.34.7.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
595
+ stoobly_agent-0.34.7.dist-info/RECORD,,