stoobly-agent 1.9.1__py3-none-any.whl → 1.9.2__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 = '1.9.1'
2
+ VERSION = '1.9.2'
@@ -70,22 +70,7 @@ class Log():
70
70
 
71
71
  @property
72
72
  def scenario_inverted_index(self):
73
- index = {}
74
-
75
- def handle_snapshot(snapshot: RequestSnapshot):
76
- request_uuid = snapshot.uuid
77
- if not request_uuid in index:
78
- index[request_uuid] = []
79
-
80
- index[request_uuid].append(event.resource_uuid)
81
-
82
- for event in self.target_events:
83
- if not event.is_scenario():
84
- continue
85
-
86
- event.snapshot().iter_request_snapshots(handle_snapshot)
87
-
88
- return index
73
+ return self.build_scenario_inverted_index(self.target_events)
89
74
 
90
75
  @property
91
76
  def unprocessed_events(self) -> List[LogEvent]:
@@ -153,6 +138,22 @@ class Log():
153
138
 
154
139
  def build_log_events(self, raw_events) -> List[LogEvent]:
155
140
  return list(map(lambda raw_event: LogEvent(raw_event), raw_events))
141
+
142
+ def build_scenario_inverted_index(self, events: List[LogEvent], index = {}):
143
+ def handle_snapshot(snapshot: RequestSnapshot):
144
+ request_uuid = snapshot.uuid
145
+ if not request_uuid in index:
146
+ index[request_uuid] = []
147
+
148
+ index[request_uuid].append(event.resource_uuid)
149
+
150
+ for event in events:
151
+ if not event.is_scenario():
152
+ continue
153
+
154
+ event.snapshot().iter_request_snapshots(handle_snapshot)
155
+
156
+ return index
156
157
 
157
158
  def next_version(self, last_processed_uuid: str = None):
158
159
  uuids = self.uuids()
@@ -169,7 +170,7 @@ class Log():
169
170
  def collapse(self, events: List[LogEvent]) -> List[LogEvent]:
170
171
  events_count = {}
171
172
 
172
- # More recent events take precedence over earlier ones, keep only the most recent event
173
+ # More recent events take precedence over earlier ones, only the most recent event
173
174
  for event in events:
174
175
  event_key = event.key
175
176
 
@@ -274,10 +275,10 @@ class Log():
274
275
 
275
276
  def remove_dangling_events(self, processed_events: List[LogEvent], unprocessed_events: List[LogEvent]):
276
277
  '''
277
- Remove DELETE events where the last processed event was a PUT
278
+ Remove DELETE events unless the last processed event was a PUT
278
279
  '''
279
280
 
280
- # Build an index such that if the last event is DELETE_ACTION, then it will NOT exist in the index
281
+ # Build an index to keep track of the last action that occurred for a resource
281
282
  index = {}
282
283
  for event in processed_events:
283
284
  if event.action == PUT_ACTION:
@@ -286,13 +287,27 @@ class Log():
286
287
  if event.resource_uuid in index:
287
288
  del index[event.resource_uuid]
288
289
 
289
- scenario_inverted_index = self.scenario_inverted_index
290
-
290
+ scenario_inverted_index = self.build_scenario_inverted_index(processed_events)
291
+
291
292
  def keep(e: LogEvent):
292
- if e.action != DELETE_ACTION:
293
+ # Keep the event if it's a PUT, it may have been updated
294
+ if e.action == PUT_ACTION:
293
295
  return True
294
-
295
- return e.action == DELETE_ACTION and e.is_request() and e.resource_uuid in scenario_inverted_index
296
+
297
+ if e.action == DELETE_ACTION:
298
+ if e.is_request():
299
+ referenced = e.resource_uuid in scenario_inverted_index
300
+ # Keep the DELETE event if the requests exists in a scenario
301
+ # or if it doesn't exist in a scenario, there was a previous delete event
302
+ return referenced or (e.resource_uuid in index and not referenced)
303
+ elif e.is_scenario():
304
+ # Update scenario_inverted_index with unprocessed event
305
+ self.build_scenario_inverted_index([e], scenario_inverted_index)
306
+
307
+ # Keep DELETE scenario event only if previous event for the resource was PUT
308
+ return e.resource_uuid in index
309
+ else:
310
+ return True
296
311
 
297
312
  return list(
298
313
  filter(
@@ -147,15 +147,8 @@ def handle_request_mock(context: MockContext):
147
147
  # 2. AFTER_MOCK gets triggered (if mock found)
148
148
  #
149
149
  def handle_response_mock(context: MockContext):
150
- response = context.flow.response
151
- request_key = response.headers.get(custom_headers.MOCK_REQUEST_KEY)
152
-
153
- if request_key:
154
- request = context.flow.request
155
- Logger.instance(LOG_ID).info(f"{bcolors.OKBLUE}Mocked{bcolors.ENDC} {request.url} -> {request_key}")
156
-
157
- __rewrite_response(context)
158
- __mock_hook(lifecycle_hooks.AFTER_MOCK, context)
150
+ __rewrite_response(context)
151
+ __mock_hook(lifecycle_hooks.AFTER_MOCK, context)
159
152
 
160
153
  def __handle_mock_failure(context: MockContext) -> None:
161
154
  flow = context.flow
@@ -186,6 +179,19 @@ def __handle_found_policy(context: MockContext) -> None:
186
179
  reverse_proxy(req, upstream_url, {})
187
180
 
188
181
  def __handle_mock_success(context: MockContext) -> None:
182
+ response = context.response
183
+
184
+ if response:
185
+ request = context.flow.request
186
+
187
+ request_key = response.headers.get(custom_headers.MOCK_REQUEST_KEY)
188
+ if request_key:
189
+ Logger.instance(LOG_ID).info(f"{bcolors.OKBLUE}Mocked{bcolors.ENDC} {request.url} -> {request_key}")
190
+
191
+ fixture_path = response.headers.get(custom_headers.MOCK_FIXTURE_PATH)
192
+ if fixture_path:
193
+ Logger.instance(LOG_ID).info(f"{bcolors.OKBLUE}Mocked{bcolors.ENDC} {request.url} -> {fixture_path}")
194
+
189
195
  if os.environ.get(env_vars.AGENT_SIMULATE_LATENCY):
190
196
  response = context.response
191
197
  start_time = context.start_time
@@ -10,6 +10,7 @@ from requests.structures import CaseInsensitiveDict
10
10
  from typing import Union
11
11
 
12
12
  from stoobly_agent.lib.logger import bcolors, Logger
13
+ from stoobly_agent.config.constants.custom_headers import MOCK_FIXTURE_PATH
13
14
 
14
15
  from .types import Fixtures
15
16
 
@@ -20,45 +21,50 @@ class Options():
20
21
  response_fixtures: Fixtures
21
22
 
22
23
  def eval_fixtures(request: MitmproxyRequest, **options: Options) -> Union[Response, None]:
23
- fixture_path = None
24
+ fixture_path = request.headers.get(MOCK_FIXTURE_PATH)
24
25
  headers = CaseInsensitiveDict()
25
26
  status_code = 200
26
27
 
27
- response_fixtures = options.get('response_fixtures')
28
- fixture: dict = __eval_response_fixtures(request, response_fixtures)
29
-
30
- if not fixture:
31
- public_directory_path = options.get('public_directory_path')
32
-
33
- if not public_directory_path:
28
+ if fixture_path:
29
+ if not os.path.exists(fixture_path):
34
30
  return
31
+ else:
32
+ response_fixtures = options.get('response_fixtures')
33
+ fixture: dict = __eval_response_fixtures(request, response_fixtures)
35
34
 
36
- request_path = 'index' if request.path == '/' else request.path
37
- _fixture_path = os.path.join(public_directory_path, request_path.lstrip('/'))
38
- if request.headers.get('accept'):
39
- fixture_path = __guess_file_path(_fixture_path, request.headers['accept'])
35
+ if not fixture:
36
+ public_directory_path = options.get('public_directory_path')
40
37
 
41
- if not fixture_path:
42
- fixture_path = _fixture_path
38
+ if not public_directory_path:
39
+ return
43
40
 
44
- if not os.path.isfile(fixture_path):
45
- return
46
- else:
47
- fixture_path = fixture.get('path')
48
- if not fixture_path or not os.path.isfile(fixture_path):
49
- return
41
+ request_path = 'index' if request.path == '/' else request.path
42
+ _fixture_path = os.path.join(public_directory_path, request_path.lstrip('/'))
43
+ if request.headers.get('accept'):
44
+ fixture_path = __guess_file_path(_fixture_path, request.headers['accept'])
45
+
46
+ if not fixture_path:
47
+ fixture_path = _fixture_path
50
48
 
51
- _headers = fixture.get('headers')
52
- headers = CaseInsensitiveDict(_headers if isinstance(_headers, dict) else {})
49
+ if not os.path.isfile(fixture_path):
50
+ return
51
+ else:
52
+ fixture_path = fixture.get('path')
53
+ if not fixture_path or not os.path.isfile(fixture_path):
54
+ return
53
55
 
54
- if fixture.get('status_code'):
55
- status_code = fixture.get('status_code')
56
+ _headers = fixture.get('headers')
57
+ headers = CaseInsensitiveDict(_headers if isinstance(_headers, dict) else {})
56
58
 
59
+ if fixture.get('status_code'):
60
+ status_code = fixture.get('status_code')
61
+
57
62
  with open(fixture_path, 'rb') as fp:
58
63
  response = Response()
59
64
 
60
65
  response.status_code = status_code
61
66
  response.raw = BytesIO(fp.read())
67
+ headers[MOCK_FIXTURE_PATH] = fixture_path
62
68
  response.headers = headers
63
69
 
64
70
  if not response.headers.get('content-type'):
@@ -2,6 +2,7 @@ ALIAS_RESOLVE_STRATEGY = 'X-Stoobly-Alias-Resolve-Strategy'
2
2
  CONTENT_TYPE = 'X-Stoobly-Content-Type'
3
3
  CONTENT_TYPE_TEST_RESULTS = 'test/results'
4
4
  REMOTE_PROJECT_KEY = 'X-Stoobly-Endpoints-Project-Id'
5
+ MOCK_FIXTURE_PATH = 'X-Stoobly-Fixture-Path'
5
6
  MOCK_POLICY = 'X-Stoobly-Mock-Policy'
6
7
  MOCK_REQUEST_ID = 'X-Stoobly-Request-Id'
7
8
  MOCK_REQUEST_ENDPOINT_ID = 'X-Stoobly-Request-Endpoint-Id'
@@ -28,6 +28,10 @@ class UuidKey(ResourceKey):
28
28
  toks = key.split(DELIMITTER)
29
29
 
30
30
  d = {}
31
+
32
+ if len(toks) == 0:
33
+ return d
34
+
31
35
  for tok in toks:
32
36
  d[tok[0]] = tok[1:]
33
37
 
@@ -1,16 +1,18 @@
1
1
  import pdb
2
2
  import pytest
3
+ import time
3
4
 
4
5
  from click.testing import CliRunner
5
-
6
- from stoobly_agent.test.test_helper import DETERMINISTIC_GET_REQUEST_URL, reset
6
+ from typing import List
7
7
 
8
8
  from stoobly_agent.app.models.adapters.orm import JoinedRequestStringAdapter
9
9
  from stoobly_agent.app.models.factories.resource.local_db.helpers.log import Log
10
- from stoobly_agent.app.models.factories.resource.local_db.helpers.log_event import DELETE_ACTION
10
+ from stoobly_agent.app.models.factories.resource.local_db.helpers.log_event import DELETE_ACTION, PUT_ACTION
11
11
  from stoobly_agent.app.models.factories.resource.local_db.helpers.request_snapshot import RequestSnapshot
12
- from stoobly_agent.cli import record, request
12
+ from stoobly_agent.cli import record, request, scenario, snapshot
13
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, NON_DETERMINISTIC_GET_REQUEST_URL, reset
14
16
 
15
17
  @pytest.fixture(scope='module')
16
18
  def runner():
@@ -122,3 +124,51 @@ class TestRequestSnapshot():
122
124
 
123
125
  event = unprocessed_events[0]
124
126
  assert event.resource_uuid == recorded_request_two.uuid
127
+ assert event.action == PUT_ACTION
128
+
129
+ class TestWhenDeleteFirst():
130
+
131
+ @pytest.fixture(scope='class')
132
+ def recorded_request(self, runner: CliRunner):
133
+ record_result = runner.invoke(record, [DETERMINISTIC_GET_REQUEST_URL])
134
+ assert record_result.exit_code == 0
135
+ return Request.last()
136
+
137
+ def test_initial_delete(self, runner: CliRunner, recorded_request: Request):
138
+ snapshot_result = runner.invoke(request, ['snapshot', '--action', DELETE_ACTION, recorded_request.key()])
139
+ assert snapshot_result.exit_code == 0
140
+
141
+ log = Log()
142
+
143
+ events = log.raw_events
144
+ assert len(events) == 1
145
+
146
+ unprocessed_events = log.unprocessed_events
147
+ assert len(unprocessed_events) == 0
148
+
149
+ def test_puts(self, runner: CliRunner, recorded_request: Request):
150
+ snapshot_result = runner.invoke(request, ['snapshot', '--action', PUT_ACTION, recorded_request.key()])
151
+ assert snapshot_result.exit_code == 0
152
+
153
+ log = Log()
154
+
155
+ events = log.raw_events
156
+ assert len(events) == 2
157
+
158
+ unprocessed_events = log.unprocessed_events
159
+ assert len(unprocessed_events) == 1
160
+ assert unprocessed_events[0].action == PUT_ACTION
161
+
162
+ def test_final_delete(self, runner: CliRunner, recorded_request: Request):
163
+ snapshot_result = runner.invoke(request, ['snapshot', '--action', DELETE_ACTION, recorded_request.key()])
164
+ assert snapshot_result.exit_code == 0
165
+
166
+ log = Log()
167
+
168
+ events = log.events
169
+ assert len(events) == 3
170
+
171
+ collapsed_events = log.collapse(events)
172
+ assert len(collapsed_events) == 1
173
+ unprocessed_events = log.unprocessed_events
174
+ assert len(unprocessed_events) == 0
@@ -556,7 +556,7 @@ class TestApply():
556
556
  1. Create scenario
557
557
  2. Add 2 requests to it
558
558
  3. Snapshot scenario
559
- 4. Snapshot requests with action DELETE_ACTION
559
+ 4. Snapshot second request with action DELETE_ACTION
560
560
  5. Apply
561
561
  6. Expect scenario to have 1 request
562
562
  '''
@@ -689,4 +689,124 @@ class TestApply():
689
689
 
690
690
  requests = created_scenario_two.requests
691
691
 
692
- assert_orm_request_equivalent(requests[0], created_scenario_request)
692
+ assert_orm_request_equivalent(requests[0], created_scenario_request)
693
+
694
+ class TestApply():
695
+
696
+ class TestWhenNoApply():
697
+ '''
698
+ 1. Create scenario
699
+ 2. Add 2 requests to it
700
+ 3. Snapshot scenario
701
+ 4. Apply scenario
702
+ 5. Snapshot second request with action DELETE_ACTION
703
+ 6. Prune
704
+ 7. Apply
705
+ 8. Expect scenario to have 1 request, because scenario depends on the request, should not be able to prune
706
+ '''
707
+
708
+ @pytest.fixture(scope='class')
709
+ def created_scenario(self, runner: CliRunner):
710
+ create_result = runner.invoke(scenario, ['create', 'test'])
711
+ assert create_result.exit_code == 0
712
+ return Scenario.last()
713
+
714
+ @pytest.fixture(scope='class', autouse=True)
715
+ def created_scenario_requests(self, runner: CliRunner, created_scenario: Scenario):
716
+ record_result = runner.invoke(record, ['--scenario-key', created_scenario.key(), DETERMINISTIC_GET_REQUEST_URL])
717
+ assert record_result.exit_code == 0
718
+
719
+ record_result = runner.invoke(record, ['--scenario-key', created_scenario.key(), NON_DETERMINISTIC_GET_REQUEST_URL])
720
+ assert record_result.exit_code == 0
721
+
722
+ return created_scenario.requests
723
+
724
+ @pytest.fixture(scope='class', autouse=True)
725
+ def snapshots(self, runner: CliRunner, created_scenario: Scenario, created_scenario_requests: List[Request]):
726
+ snapshot_result = runner.invoke(scenario, ['snapshot', created_scenario.key()])
727
+ assert snapshot_result.exit_code == 0
728
+
729
+ created_request = created_scenario_requests[1]
730
+ snapshot_result = runner.invoke(request, ['snapshot', created_request.key(), '--action', DELETE_ACTION])
731
+ assert snapshot_result.exit_code == 0
732
+
733
+ def test_events(self):
734
+ log = Log()
735
+
736
+ events = log.events
737
+ assert len(events) == 2
738
+
739
+ def test_collapsed_events(self):
740
+ log = Log()
741
+
742
+ collapsed_events = log.collapse(log.events)
743
+ assert len(collapsed_events) == 2
744
+
745
+ def test_unprocessed_events(self):
746
+ log = Log()
747
+
748
+ unprocessed_events = log.unprocessed_events
749
+ assert len(unprocessed_events) == 2
750
+
751
+ class TestWhenRemoveScenarioRequest():
752
+ '''
753
+ 1. Create scenario
754
+ 2. Add 2 requests to it
755
+ 3. Snapshot scenario
756
+ 4. Apply scenario
757
+ 5. Snapshot second request with action DELETE_ACTION
758
+ 6. Prune
759
+ 7. Apply
760
+ 8. Expect scenario to have 1 request, because scenario depends on the request, should not be able to prune
761
+ '''
762
+
763
+ @pytest.fixture(scope='class')
764
+ def created_scenario(self, runner: CliRunner):
765
+ create_result = runner.invoke(scenario, ['create', 'test'])
766
+ assert create_result.exit_code == 0
767
+ return Scenario.last()
768
+
769
+ @pytest.fixture(scope='class', autouse=True)
770
+ def created_scenario_requests(self, runner: CliRunner, created_scenario: Scenario):
771
+ record_result = runner.invoke(record, ['--scenario-key', created_scenario.key(), DETERMINISTIC_GET_REQUEST_URL])
772
+ assert record_result.exit_code == 0
773
+
774
+ record_result = runner.invoke(record, ['--scenario-key', created_scenario.key(), NON_DETERMINISTIC_GET_REQUEST_URL])
775
+ assert record_result.exit_code == 0
776
+
777
+ return created_scenario.requests
778
+
779
+ @pytest.fixture(scope='class', autouse=True)
780
+ def apply_result(self, runner: CliRunner, created_scenario: Scenario, created_scenario_requests: List[Request]):
781
+ snapshot_result = runner.invoke(scenario, ['snapshot', created_scenario.key()])
782
+ assert snapshot_result.exit_code == 0
783
+
784
+ created_scenario = Scenario.find(created_scenario.id)
785
+ assert created_scenario.requests_count == 2
786
+ apply_result = runner.invoke(snapshot, ['apply'])
787
+ assert apply_result.exit_code == 0
788
+
789
+ created_request = created_scenario_requests[1]
790
+ snapshot_result = runner.invoke(request, ['snapshot', created_request.key(), '--action', DELETE_ACTION])
791
+ assert snapshot_result.exit_code == 0
792
+
793
+ return apply_result
794
+
795
+ def test_events(self):
796
+ log = Log()
797
+
798
+ events = log.events
799
+ assert len(events) == 2
800
+
801
+ def test_collapsed_events(self):
802
+ log = Log()
803
+
804
+ collapsed_events = log.collapse(log.events)
805
+ assert len(collapsed_events) == 2
806
+
807
+ def test_unprocessed_events(self):
808
+ log = Log()
809
+
810
+ unprocessed_events = log.unprocessed_events
811
+ assert len(unprocessed_events) == 1
812
+ assert unprocessed_events[0].action == DELETE_ACTION
@@ -116,10 +116,10 @@ class TestPrune():
116
116
  1. Create scenario
117
117
  2. Add 2 requests to it
118
118
  3. Snapshot scenario
119
- 4. Snapshot request with action DELETE_ACTION
120
- 5. Prune, but because scenario depends on the request, should not be able to prune
119
+ 4. Snapshot second request with action DELETE_ACTION
120
+ 5. Prune
121
121
  6. Apply
122
- 7. Expect scenario to have 1 request
122
+ 7. Expect scenario to have 1 request, because scenario depends on the request, should not be able to prune
123
123
  '''
124
124
 
125
125
  @pytest.fixture(scope='class')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: stoobly-agent
3
- Version: 1.9.1
3
+ Version: 1.9.2
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=ehByLp1z2dzieXsaNQLADxDog0UyQfS9wSZ3n9PqZNs,44
1
+ stoobly_agent/__init__.py,sha256=epZElDe2ihHM7JdgjopRIH49aMQpYH4SYIjmxXXMnY0,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
@@ -258,7 +258,7 @@ stoobly_agent/app/models/factories/resource/local_db/body_adapter.py,sha256=lrnI
258
258
  stoobly_agent/app/models/factories/resource/local_db/header_adapter.py,sha256=NQdCErFtJL7sBaLpKLYfJSEA3AiaaVuU7LUcGJ-dHOI,3104
259
259
  stoobly_agent/app/models/factories/resource/local_db/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
260
260
  stoobly_agent/app/models/factories/resource/local_db/helpers/create_request_columns_service.py,sha256=HABMelW-cvhm2WXaywbQcd4PQhzSrz4vAbN7uOdetXM,1541
261
- stoobly_agent/app/models/factories/resource/local_db/helpers/log.py,sha256=T0PCrMX2s7PMwZSMdr436PzNa0QQTCBDBshosgZ24BI,10899
261
+ stoobly_agent/app/models/factories/resource/local_db/helpers/log.py,sha256=COKyrRFZgxQgLVH6cs_Mvc616BScoldamg8QqG8JV3c,11670
262
262
  stoobly_agent/app/models/factories/resource/local_db/helpers/log_event.py,sha256=30wWhqibTWgnpibosclaB9OCWzArL7R4krGS8WW2bGY,3584
263
263
  stoobly_agent/app/models/factories/resource/local_db/helpers/request_builder.py,sha256=PyVvsYmi5bBQ6PsUPxQ4nJM9rhhjGVOTd7ipuc2tMMM,3227
264
264
  stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py,sha256=Tpuu7sZ4A2Vc5e5OAyU9pSKzbOpLpZoI-4E2Ty7n4Ac,2672
@@ -309,7 +309,7 @@ stoobly_agent/app/proxy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
309
309
  stoobly_agent/app/proxy/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
310
310
  stoobly_agent/app/proxy/constants/custom_response_codes.py,sha256=1CaApt_6W7GrxvN8_Ozbf_SEodVEQaNZRR2sMYpI0U8,40
311
311
  stoobly_agent/app/proxy/context.py,sha256=m6iu6QSZsim8meZS8H8DsZeXyjfoC-MRMHhQD1MFKM0,532
312
- stoobly_agent/app/proxy/handle_mock_service.py,sha256=HszD77W3X1UIz0DvBtbRUfuWSgc8-GS6xvWq4VDL-4w,9216
312
+ stoobly_agent/app/proxy/handle_mock_service.py,sha256=qz7rxAg6R8U9aiXbYjuncDkx5eliPYX3x-uVLelwSr0,9451
313
313
  stoobly_agent/app/proxy/handle_record_service.py,sha256=Fe8RAcMVvHhl3-XgjZ41242p4JXooYHQ-MhjcQhJn2E,4223
314
314
  stoobly_agent/app/proxy/handle_replay_service.py,sha256=kBUYd8kj8UPrsYHoBXmq06DPLjCnHo8K-QsKzhDIKcw,2526
315
315
  stoobly_agent/app/proxy/handle_test_service.py,sha256=WkMPbM4argVtl-TQB7VdQIvB8cOwURAahxFX5Vkqwws,8405
@@ -327,7 +327,7 @@ stoobly_agent/app/proxy/mitmproxy/response_facade.py,sha256=0wCSzUULUhDDV93QXUgz
327
327
  stoobly_agent/app/proxy/mock/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
328
328
  stoobly_agent/app/proxy/mock/context.py,sha256=vDo5_3WBL73mVFnsmQWvcxvPg5nWtRJbigSrE3zGc-o,794
329
329
  stoobly_agent/app/proxy/mock/custom_not_found_response_builder.py,sha256=0KWB3KFxVrnJOKDaYxm5eoJEccw7IpJZRyUvBX61-8k,697
330
- stoobly_agent/app/proxy/mock/eval_fixtures_service.py,sha256=hc4VLnN50HBaWvFnrhQUJqdH-r3jBu9DHrpt8gbvkHY,3847
330
+ stoobly_agent/app/proxy/mock/eval_fixtures_service.py,sha256=sVw8l916ouEBj2ViDtHFjgmEA4tLmZGvBfw8wD1Ab-c,4132
331
331
  stoobly_agent/app/proxy/mock/eval_request_service.py,sha256=A1tcE3wmrC1HwLpz0aRuRw-Nucn0dyHD_yHw5BeQEJU,8146
332
332
  stoobly_agent/app/proxy/mock/hashed_request_decorator.py,sha256=h1ma90fdaYI9LBWpMWMqWBz-RjNwI628O4VuS_uUBX4,5061
333
333
  stoobly_agent/app/proxy/mock/ignored_components_response_builder.py,sha256=E32_E1eSdmPn2SeM_e1jWnqu4xh5w_SnmOs32Shx99E,501
@@ -419,7 +419,7 @@ stoobly_agent/cli.py,sha256=sw8Ke5mCvzQ50X-zsb2Ld_zW4T6S58P0fN5GyKNOrcQ,10255
419
419
  stoobly_agent/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
420
420
  stoobly_agent/config/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
421
421
  stoobly_agent/config/constants/alias_resolve_strategy.py,sha256=_R1tVqFnyGxCraVS5-dhSskaDj_X8-NthsY7i_bEt9M,119
422
- stoobly_agent/config/constants/custom_headers.py,sha256=9PhSGOsvoyi-w3sxfYJGl9mdTqtOGaog7fMuHrdu-20,1353
422
+ stoobly_agent/config/constants/custom_headers.py,sha256=8YiKDjrpe0qH8tr4qM_CgonBy6bf_HHTqQ7B_hYv2L0,1398
423
423
  stoobly_agent/config/constants/env_vars.py,sha256=HAR_ZIdXXbpWQgCDaRR5RtpVyGXCsMLr_Fh8n6S12K0,1344
424
424
  stoobly_agent/config/constants/headers.py,sha256=Hfv7R8_NPXAGaMiZPqywGZDnr0qcVUyfenPb4g465rE,169
425
425
  stoobly_agent/config/constants/intercept_policy.py,sha256=5hIgOft8PQmCRdOHb5OEvEj10tU66DIQF3GYltlWyM8,25
@@ -488,7 +488,7 @@ stoobly_agent/lib/api/keys/request_key.py,sha256=68hN0KLdf0FwxSCSjeJZ1uUs526PCbN
488
488
  stoobly_agent/lib/api/keys/resource_key.py,sha256=9qrkp3t8iJsScZvKVcBDvVbcfwmP0yEBWy3b1tHi8CM,689
489
489
  stoobly_agent/lib/api/keys/scenario_key.py,sha256=VAc6gayvJS7shWgDL3SAqVET3fmgBefcygXTsKot07U,629
490
490
  stoobly_agent/lib/api/keys/test_key.py,sha256=-MCWp1oYLkJ3S_Pqs62j8KkkssXpT9quKb4YqMSq1Ks,438
491
- stoobly_agent/lib/api/keys/uuid_key.py,sha256=7_aL5wVTKF68bESHvqeQ2RUeC-Fw9-zpSrl8EWuFTJw,603
491
+ stoobly_agent/lib/api/keys/uuid_key.py,sha256=30ZJIEAQHsR1KCsmeALxsKwCaUQtm7pKvFUhLmk0J4I,643
492
492
  stoobly_agent/lib/api/param_builder.py,sha256=50eq0zkqRz5MVezPZMIu69fYSB6z4CD5FG26_IU3eq0,996
493
493
  stoobly_agent/lib/api/projects_resource.py,sha256=7leBuQnlpgUwbYUX445lUC3jy-dzLMpTumeNPrqRc5c,1166
494
494
  stoobly_agent/lib/api/query_param_names_resource.py,sha256=k-3nlyfCOa_Vwj-TMI7v8_4g9qAs50baP073gkStAKE,1710
@@ -675,7 +675,7 @@ stoobly_agent/test/app/cli/request/request_list_test.py,sha256=tqMjkODU2i2eDvYyk
675
675
  stoobly_agent/test/app/cli/request/request_replay_test.py,sha256=w13NzkXhmBKvoogpQ8CdmBbCzoyqxEQvBZsEkGDCPx0,6237
676
676
  stoobly_agent/test/app/cli/request/request_reset_test.py,sha256=5My6Z452eideAOUun77tWUkuu3_yGhDVxCG1baUF8Zo,1290
677
677
  stoobly_agent/test/app/cli/request/request_response_test.py,sha256=Fu-A8tIn016DKme4WIaPzo3YeFY-CPtTOpaSFigUVVM,1263
678
- stoobly_agent/test/app/cli/request/request_snapshot_test.py,sha256=0013aoiMZin-2YEtHzEmQspAPA3SUd_6XiItbX0U7Ok,4425
678
+ stoobly_agent/test/app/cli/request/request_snapshot_test.py,sha256=3kMmv0CuvnMXLgDQA-_u9S1DIiNOdL63L-IptVuOpf8,6308
679
679
  stoobly_agent/test/app/cli/request/request_test_test.py,sha256=-cJNXKjgryVVfVt-7IN5fIhBwe3NjFoPmeavDH8lAjU,5527
680
680
  stoobly_agent/test/app/cli/scaffold/cli_invoker.py,sha256=_nGDLUsYxqkeqs5DdhvAeXy3IuotpgqKHXKVzu6GDF4,3700
681
681
  stoobly_agent/test/app/cli/scaffold/cli_test.py,sha256=sMNvO845MIu5DVGa1HmwXQDmKDcwrfNTdEb3fK5886w,4557
@@ -688,10 +688,10 @@ stoobly_agent/test/app/cli/scenario/scenario_reset_test.py,sha256=QOyytOoFu1FALn
688
688
  stoobly_agent/test/app/cli/scenario/scenario_snapshot_test.py,sha256=IAe1l69Smd8a9E6I0CVs8lgqnC4mI4M1EFfUjC4ZHs8,3396
689
689
  stoobly_agent/test/app/cli/scenario/scenario_test_integration_test.py,sha256=BdXGe1xfb79tCCTKtp4sqI6CkZL_KamvQKLK8Wwb4u0,5543
690
690
  stoobly_agent/test/app/cli/snapshot/lifecycle_hooks_migrate.py,sha256=x3x5vHfZ1xmoOpZ0yRSB3kbWbPjfbLk2PJFSa1xOLoU,316
691
- stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py,sha256=YIcaCxJV8CcTnCX08vq9HQyuyYf6YIwVVA3Hhx_RG2A,27752
691
+ stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py,sha256=mpkTZx8eaFFZU_RKHPcphaNl6zKCIOTJ2i4kY9BZRgQ,32395
692
692
  stoobly_agent/test/app/cli/snapshot/snapshot_copy_test.py,sha256=Yg78-FhSiG_r6Jpm-sN8sn0LjVXTwTOXt6hg8ni2GIY,1953
693
693
  stoobly_agent/test/app/cli/snapshot/snapshot_migrate_test.py,sha256=voEvblK6CMGCrSJDTHVmkUkLXj0auNb78jxlGiiBBQQ,7370
694
- stoobly_agent/test/app/cli/snapshot/snapshot_prune_test.py,sha256=G3vrXQRMoDedNpAuKgDivNMqLK-5xsCAAl80xsgeVaU,6690
694
+ stoobly_agent/test/app/cli/snapshot/snapshot_prune_test.py,sha256=bn4yUU7Eb4-6GnwnRaPZPi5Cn7XEaIsrJ_mB7jydgWw,6693
695
695
  stoobly_agent/test/app/cli/snapshot/snapshot_update_test.py,sha256=fILsX2M5j4wuLRP6LJTHe4CPB8gvaEbsSoYmFCHmKVk,4514
696
696
  stoobly_agent/test/app/models/adapters/orm/joined_request_string_adapter_test.py,sha256=a2IHTk3l7aiLyYF7vtqissrk0MFTF2wlUBiaKWyJKfU,2667
697
697
  stoobly_agent/test/app/models/adapters/orm/request/orm_mitmproxy_request_adapter_test.py,sha256=PbJsAaxPUEbF9vM7DX4z858biWf4qlGnvE8KBuy8SgY,2763
@@ -748,8 +748,8 @@ stoobly_agent/test/mock_data/scaffold/docker-compose-local-service.yml,sha256=1W
748
748
  stoobly_agent/test/mock_data/scaffold/index.html,sha256=qJwuYajKZ4ihWZrJQ3BNObV5kf1VGnnm_vqlPJzdqLE,258
749
749
  stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
750
750
  stoobly_agent/test/test_helper.py,sha256=m_oAI7tmRYCNZdKfNqISWhMv3e44tjeYViQ3nTUfnos,1007
751
- stoobly_agent-1.9.1.dist-info/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
752
- stoobly_agent-1.9.1.dist-info/METADATA,sha256=wlI-HxrR6hqRTUREg2Oo4NJNTsLXe7NPKrnO4mHThs0,3087
753
- stoobly_agent-1.9.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
754
- stoobly_agent-1.9.1.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
755
- stoobly_agent-1.9.1.dist-info/RECORD,,
751
+ stoobly_agent-1.9.2.dist-info/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
752
+ stoobly_agent-1.9.2.dist-info/METADATA,sha256=qMOKulRLao6AQJC0ctx4h0IyYP6Fd-ACe5NtoyuIY1U,3087
753
+ stoobly_agent-1.9.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
754
+ stoobly_agent-1.9.2.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
755
+ stoobly_agent-1.9.2.dist-info/RECORD,,