stoobly-agent 1.10.0__py3-none-any.whl → 1.10.1__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/application_http_request_handler.py +5 -2
- stoobly_agent/app/cli/scaffold/constants.py +1 -3
- stoobly_agent/app/cli/scaffold/docker/service/build_decorator.py +2 -2
- stoobly_agent/app/cli/scaffold/docker/service/builder.py +17 -6
- stoobly_agent/app/cli/scaffold/docker/workflow/builder.py +0 -9
- stoobly_agent/app/cli/scaffold/docker/workflow/command_decorator.py +2 -1
- stoobly_agent/app/cli/scaffold/service_config.py +1 -25
- stoobly_agent/app/cli/scaffold/service_docker_compose.py +3 -3
- stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py +10 -7
- stoobly_agent/app/cli/scaffold/templates/app/build/.docker-compose.base.yml +2 -2
- stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/configure +1 -1
- stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/configure +26 -1
- stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/configure +1 -1
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.docker-compose.base.yml +2 -2
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/configure +1 -1
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/configure +1 -1
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/configure +1 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.configure +21 -1
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/configure +2 -10
- stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/configure +19 -45
- stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/configure +2 -10
- stoobly_agent/app/cli/scaffold_cli.py +21 -23
- stoobly_agent/app/proxy/intercept_settings.py +1 -1
- stoobly_agent/app/proxy/record/upload_request_service.py +3 -6
- stoobly_agent/app/proxy/utils/publish_change_service.py +20 -23
- stoobly_agent/app/settings/__init__.py +10 -7
- stoobly_agent/config/data_dir.py +1 -0
- stoobly_agent/public/index.html +1 -1
- stoobly_agent/public/main-es2015.5a9aa16433404c3f423a.js +1 -0
- stoobly_agent/public/main-es5.5a9aa16433404c3f423a.js +1 -0
- stoobly_agent/test/app/cli/scaffold/cli_invoker.py +2 -2
- stoobly_agent/test/app/cli/scaffold/cli_test.py +3 -3
- stoobly_agent/test/app/cli/scaffold/e2e_test.py +11 -11
- {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.1.dist-info}/METADATA +1 -1
- {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.1.dist-info}/RECORD +39 -39
- stoobly_agent/public/main-es2015.089b46f303768fbe864f.js +0 -1
- stoobly_agent/public/main-es5.089b46f303768fbe864f.js +0 -1
- {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.1.dist-info}/LICENSE +0 -0
- {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.1.dist-info}/WHEEL +0 -0
- {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.1.dist-info}/entry_points.txt +0 -0
@@ -12,7 +12,7 @@ from stoobly_agent.app.cli.helpers.shell import exec_stream
|
|
12
12
|
from stoobly_agent.app.cli.scaffold.app import App
|
13
13
|
from stoobly_agent.app.cli.scaffold.app_create_command import AppCreateCommand
|
14
14
|
from stoobly_agent.app.cli.scaffold.constants import (
|
15
|
-
|
15
|
+
SERVICES_NAMESPACE, WORKFLOW_CONTAINER_PROXY, WORKFLOW_MOCK_TYPE, WORKFLOW_RECORD_TYPE, WORKFLOW_TEST_TYPE
|
16
16
|
)
|
17
17
|
from stoobly_agent.app.cli.scaffold.constants import PLUGIN_CYPRESS, PLUGIN_PLAYWRIGHT
|
18
18
|
from stoobly_agent.app.cli.scaffold.containerized_app import ContainerizedApp
|
@@ -98,10 +98,13 @@ def hostname(ctx):
|
|
98
98
|
def create(**kwargs):
|
99
99
|
__validate_app_dir(kwargs['app_dir_path'])
|
100
100
|
|
101
|
-
app = App(kwargs['app_dir_path'],
|
101
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE)
|
102
102
|
|
103
|
-
if not kwargs['quiet']
|
104
|
-
|
103
|
+
if not kwargs['quiet']:
|
104
|
+
if os.path.exists(app.scaffold_namespace_path):
|
105
|
+
print(f"{kwargs['app_dir_path']} already exists, updating scaffold maintained files...")
|
106
|
+
else:
|
107
|
+
print(f"Creating scaffold in {kwargs['app_dir_path']}")
|
105
108
|
|
106
109
|
res = AppCreateCommand(app, **kwargs).build()
|
107
110
|
|
@@ -118,7 +121,7 @@ def create(**kwargs):
|
|
118
121
|
@click.option('--service', multiple=True, help='Select which services to run. Defaults to all.')
|
119
122
|
@click.option('--workflow', multiple=True, help='Specify services by workflow(s). Defaults to all.')
|
120
123
|
def mkcert(**kwargs):
|
121
|
-
app = App(kwargs['app_dir_path'],
|
124
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE, **kwargs)
|
122
125
|
__validate_app(app)
|
123
126
|
|
124
127
|
services = __get_services(
|
@@ -137,7 +140,6 @@ def mkcert(**kwargs):
|
|
137
140
|
@click.option('--local', is_flag=True, help='Specifies upstream service is local. Overrides `--upstream-hostname` option.')
|
138
141
|
@click.option('--port', type=click.IntRange(1, 65535), help='Service port.')
|
139
142
|
@click.option('--priority', default=5, type=click.FloatRange(1.0, 9.0), help='Determines the service run order. Lower values run first.')
|
140
|
-
@click.option('--proxy-mode', type=click.Choice(['regular', 'reverse']), help='Proxy mode can be regular or reverse.')
|
141
143
|
@click.option('--quiet', is_flag=True, help='Disable log output.')
|
142
144
|
@click.option('--scheme', type=click.Choice(['http', 'https']), help='Defaults to https if hostname is set.')
|
143
145
|
@click.option('--upstream-hostname', callback=validate_hostname, help='Upstream service hostname.')
|
@@ -148,7 +150,7 @@ def mkcert(**kwargs):
|
|
148
150
|
def create(**kwargs):
|
149
151
|
__validate_app_dir(kwargs['app_dir_path'])
|
150
152
|
|
151
|
-
app = App(kwargs['app_dir_path'],
|
153
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE)
|
152
154
|
service = Service(kwargs['service_name'], app)
|
153
155
|
|
154
156
|
if not kwargs['quiet'] and os.path.exists(service.dir_path):
|
@@ -168,7 +170,7 @@ def create(**kwargs):
|
|
168
170
|
@click.option('--all', is_flag=True, default=False, help='Display all services including core and user defined services')
|
169
171
|
@click.option('--workflow', multiple=True, help='Specify workflow(s) to filter the services by. Defaults to all.')
|
170
172
|
def _list(**kwargs):
|
171
|
-
app = App(kwargs['app_dir_path'],
|
173
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE)
|
172
174
|
__validate_app(app)
|
173
175
|
|
174
176
|
without_core = not kwargs['all']
|
@@ -209,7 +211,7 @@ def show(ctx, **kwargs):
|
|
209
211
|
@click.option('--app-dir-path', default=current_working_dir, help='Path to application directory.')
|
210
212
|
@click.argument('service_name')
|
211
213
|
def delete(**kwargs):
|
212
|
-
app = App(kwargs['app_dir_path'],
|
214
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE)
|
213
215
|
__validate_app(app)
|
214
216
|
|
215
217
|
service = Service(kwargs['service_name'], app)
|
@@ -230,14 +232,13 @@ def delete(**kwargs):
|
|
230
232
|
@click.option('--name', callback=validate_service_name, type=click.STRING, help='New name of the service to update to.')
|
231
233
|
@click.option('--port', type=click.IntRange(1, 65535), help='Service port.')
|
232
234
|
@click.option('--priority', type=click.FloatRange(1.0, 9.0), help='Determines the service run order. Lower values run first.')
|
233
|
-
@click.option('--proxy-mode', type=click.Choice(['regular', 'reverse']), help='Proxy mode can be regular or reverse.')
|
234
235
|
@click.option('--scheme', type=click.Choice(['http', 'https']), help='Defaults to https if hostname is set.')
|
235
236
|
@click.option('--upstream-hostname', callback=validate_hostname, help='Upstream service hostname.')
|
236
237
|
@click.option('--upstream-port', type=click.IntRange(1, 65535), help='Upstream service port.')
|
237
238
|
@click.option('--upstream-scheme', type=click.Choice(['http', 'https']), help='Upstream service scheme.')
|
238
239
|
@click.argument('service_name')
|
239
240
|
def update(**kwargs):
|
240
|
-
app = App(kwargs['app_dir_path'],
|
241
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE)
|
241
242
|
__validate_app(app)
|
242
243
|
|
243
244
|
service = Service(kwargs['service_name'], app)
|
@@ -257,9 +258,6 @@ def update(**kwargs):
|
|
257
258
|
if kwargs['priority']:
|
258
259
|
service_config.priority = kwargs['priority']
|
259
260
|
|
260
|
-
if kwargs['proxy_mode']:
|
261
|
-
service_config.proxy_mode = kwargs['proxy_mode']
|
262
|
-
|
263
261
|
if kwargs['scheme']:
|
264
262
|
service_config.scheme = kwargs['scheme']
|
265
263
|
|
@@ -299,7 +297,7 @@ def update(**kwargs):
|
|
299
297
|
def create(**kwargs):
|
300
298
|
__validate_app_dir(kwargs['app_dir_path'])
|
301
299
|
|
302
|
-
app = App(kwargs['app_dir_path'],
|
300
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE, **kwargs)
|
303
301
|
|
304
302
|
for service_name in kwargs['service']:
|
305
303
|
config = { **kwargs }
|
@@ -325,7 +323,7 @@ def create(**kwargs):
|
|
325
323
|
@click.argument('workflow_name')
|
326
324
|
@click.argument('destination_workflow_name')
|
327
325
|
def copy(**kwargs):
|
328
|
-
app = App(kwargs['app_dir_path'],
|
326
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE, **kwargs)
|
329
327
|
|
330
328
|
for service_name in kwargs['service']:
|
331
329
|
config = { **kwargs }
|
@@ -362,7 +360,7 @@ def down(**kwargs):
|
|
362
360
|
containerized = kwargs['containerized']
|
363
361
|
|
364
362
|
app_dir_path = current_working_dir if containerized else kwargs['app_dir_path']
|
365
|
-
app = App(app_dir_path,
|
363
|
+
app = App(app_dir_path, SERVICES_NAMESPACE, **kwargs)
|
366
364
|
__validate_app(app)
|
367
365
|
|
368
366
|
__with_namespace_defaults(kwargs)
|
@@ -441,7 +439,7 @@ def down(**kwargs):
|
|
441
439
|
def logs(**kwargs):
|
442
440
|
os.environ[env_vars.LOG_LEVEL] = kwargs['log_level']
|
443
441
|
|
444
|
-
app = App(kwargs['app_dir_path'],
|
442
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE)
|
445
443
|
__validate_app(app)
|
446
444
|
|
447
445
|
__with_namespace_defaults(kwargs)
|
@@ -517,7 +515,7 @@ def up(**kwargs):
|
|
517
515
|
# Because we are running a docker-compose command which depends on APP_DIR env var
|
518
516
|
# when we are running this command within a container, the host's app_dir_path will likely differ
|
519
517
|
app_dir_path = current_working_dir if containerized else kwargs['app_dir_path']
|
520
|
-
app = App(app_dir_path,
|
518
|
+
app = App(app_dir_path, SERVICES_NAMESPACE, **kwargs)
|
521
519
|
__validate_app(app)
|
522
520
|
|
523
521
|
__with_namespace_defaults(kwargs)
|
@@ -528,7 +526,7 @@ def up(**kwargs):
|
|
528
526
|
)
|
529
527
|
|
530
528
|
if kwargs['mkcert']:
|
531
|
-
_app = ContainerizedApp(app_dir_path,
|
529
|
+
_app = ContainerizedApp(app_dir_path, SERVICES_NAMESPACE) if containerized else app
|
532
530
|
__services_mkcert(_app, services)
|
533
531
|
|
534
532
|
# Gateway ports are dynamically set depending on the workflow run
|
@@ -598,7 +596,7 @@ def up(**kwargs):
|
|
598
596
|
@click.option('--app-dir-path', default=current_working_dir, help='Path to validate the app scaffold.')
|
599
597
|
@click.argument('workflow_name')
|
600
598
|
def validate(**kwargs):
|
601
|
-
app = App(kwargs['app_dir_path'],
|
599
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE)
|
602
600
|
__validate_app(app)
|
603
601
|
|
604
602
|
workflow = Workflow(kwargs['workflow_name'], app)
|
@@ -634,7 +632,7 @@ def validate(**kwargs):
|
|
634
632
|
@click.option('--service', multiple=True, help='Select specific services. Defaults to all.')
|
635
633
|
@click.option('--workflow', multiple=True, help='Specify services by workflow(s). Defaults to all.')
|
636
634
|
def install(**kwargs):
|
637
|
-
app = App(kwargs['app_dir_path'],
|
635
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE)
|
638
636
|
__validate_app(app)
|
639
637
|
|
640
638
|
services = __get_services(
|
@@ -666,7 +664,7 @@ def install(**kwargs):
|
|
666
664
|
@click.option('--service', multiple=True, help='Select specific services. Defaults to all.')
|
667
665
|
@click.option('--workflow', multiple=True, help='Specify services by workflow(s). Defaults to all.')
|
668
666
|
def uninstall(**kwargs):
|
669
|
-
app = App(kwargs['app_dir_path'],
|
667
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE)
|
670
668
|
__validate_app(app)
|
671
669
|
|
672
670
|
services = __get_services(
|
@@ -280,7 +280,7 @@ class InterceptSettings:
|
|
280
280
|
if self.__headers and custom_headers.REQUEST_ORIGIN in self.__headers:
|
281
281
|
return self.__headers[custom_headers.REQUEST_ORIGIN]
|
282
282
|
|
283
|
-
return request_origin.
|
283
|
+
return request_origin.PROXY
|
284
284
|
|
285
285
|
def for_response(self):
|
286
286
|
self.__for_response = True
|
@@ -63,10 +63,7 @@ def upload_request(
|
|
63
63
|
scenario_key=scenario_key
|
64
64
|
)
|
65
65
|
|
66
|
-
|
67
|
-
# This means that we have access to Cache singleton and do not need send a request to update the status
|
68
|
-
sync = intercept_settings.request_origin == request_origin.WEB
|
69
|
-
res = __upload_request_with_body_params(request_model, body_params, sync)
|
66
|
+
res = __upload_request_with_body_params(request_model, body_params)
|
70
67
|
|
71
68
|
if intercept_settings.settings.is_debug():
|
72
69
|
file_path = __debug_request(flow.request, joined_request.build())
|
@@ -107,11 +104,11 @@ def upload_staged_request(
|
|
107
104
|
|
108
105
|
return __upload_request_with_body_params(request_model, body_params)
|
109
106
|
|
110
|
-
def __upload_request_with_body_params(request_model: RequestModel, body_params: dict
|
107
|
+
def __upload_request_with_body_params(request_model: RequestModel, body_params: dict):
|
111
108
|
request, status = request_model.create(**body_params)
|
112
109
|
|
113
110
|
if status < 400:
|
114
|
-
publish_requests_modified(body_params['project_id']
|
111
|
+
publish_requests_modified(body_params['project_id'])
|
115
112
|
|
116
113
|
return request
|
117
114
|
|
@@ -1,41 +1,35 @@
|
|
1
|
-
import os
|
2
1
|
import pdb
|
2
|
+
import requests
|
3
3
|
import threading
|
4
4
|
|
5
|
-
from typing import TypedDict
|
6
|
-
|
7
5
|
from stoobly_agent.app.settings import Settings
|
8
|
-
from stoobly_agent.config.constants.statuses import REQUESTS_MODIFIED
|
6
|
+
from stoobly_agent.config.constants.statuses import REQUESTS_MODIFIED, SETTINGS_MODIFIED
|
9
7
|
from stoobly_agent.lib.api.agent_api import AgentApi
|
10
8
|
from stoobly_agent.lib.cache import Cache
|
11
9
|
from stoobly_agent.lib.logger import Logger
|
12
10
|
|
13
|
-
|
14
|
-
sync: bool
|
15
|
-
|
16
|
-
# Announce that a new request has been created
|
17
|
-
def publish_change(status: str, value, **options: Options):
|
18
|
-
if options.get('sync'):
|
19
|
-
return __publish_change_sync(status, value)
|
11
|
+
LOG_ID = 'PublishChange'
|
20
12
|
|
13
|
+
def publish_settings_modified(value):
|
14
|
+
return __publish_change_sync(SETTINGS_MODIFIED, value)
|
15
|
+
|
16
|
+
def publish_requests_modified(value):
|
21
17
|
settings: Settings = Settings.instance()
|
22
18
|
|
23
|
-
# If
|
24
|
-
if
|
25
|
-
return
|
19
|
+
# If not headless...
|
20
|
+
if settings.ui.active:
|
21
|
+
return __publish_change_sync(REQUESTS_MODIFIED, value)
|
26
22
|
|
27
23
|
ui_url = settings.ui.url
|
28
|
-
|
29
24
|
if not ui_url:
|
30
|
-
Logger.instance().warn('Settings.ui.url not configured')
|
31
25
|
return False
|
32
|
-
else:
|
33
|
-
thread = threading.Thread(target=__put_status, args=(ui_url, status, value))
|
34
|
-
thread.start()
|
35
|
-
return True
|
36
26
|
|
37
|
-
|
38
|
-
|
27
|
+
return __publish_change_async(REQUESTS_MODIFIED, value, ui_url)
|
28
|
+
|
29
|
+
def __publish_change_async(status, value, ui_url: str):
|
30
|
+
thread = threading.Thread(target=__put_status, args=(ui_url, status, value))
|
31
|
+
thread.start()
|
32
|
+
return True
|
39
33
|
|
40
34
|
def __publish_change_sync(status: str, value):
|
41
35
|
cache = Cache.instance()
|
@@ -44,4 +38,7 @@ def __publish_change_sync(status: str, value):
|
|
44
38
|
|
45
39
|
def __put_status(ui_url, status, value):
|
46
40
|
api: AgentApi = AgentApi(ui_url)
|
47
|
-
|
41
|
+
try:
|
42
|
+
api.update_status(status, value)
|
43
|
+
except requests.exceptions.ConnectionError:
|
44
|
+
Logger.instance(LOG_ID).error(f"could not connect to {ui_url}")
|
@@ -8,7 +8,7 @@ from watchdog.observers import Observer
|
|
8
8
|
from watchdog.events import PatternMatchingEventHandler
|
9
9
|
from yamale import *
|
10
10
|
|
11
|
-
from stoobly_agent.config.constants import env_vars
|
11
|
+
from stoobly_agent.config.constants import env_vars
|
12
12
|
from stoobly_agent.config.data_dir import DataDir
|
13
13
|
from stoobly_agent.config.source_dir import SourceDir
|
14
14
|
from stoobly_agent.lib.logger import Logger
|
@@ -19,6 +19,7 @@ from .remote_settings import RemoteSettings
|
|
19
19
|
from .ui_settings import UISettings
|
20
20
|
|
21
21
|
LOG_ID = 'Settings'
|
22
|
+
SETTINGS_YML = 'settings.yml'
|
22
23
|
|
23
24
|
class Settings:
|
24
25
|
_instances = None
|
@@ -102,7 +103,7 @@ class Settings:
|
|
102
103
|
if self.__watching:
|
103
104
|
return False
|
104
105
|
|
105
|
-
patterns = [
|
106
|
+
patterns = [SETTINGS_YML]
|
106
107
|
ignore_patterns = None
|
107
108
|
ignore_directories = False
|
108
109
|
case_sensitive = True
|
@@ -180,11 +181,12 @@ class Settings:
|
|
180
181
|
if not contents:
|
181
182
|
return
|
182
183
|
|
183
|
-
|
184
|
-
|
184
|
+
lock_file = f".{SETTINGS_YML}.lock"
|
185
|
+
lock_file_path = os.path.join(os.path.dirname(self.__settings_file_path), lock_file)
|
186
|
+
lock = FileLock(lock_file_path) # lock file alongside the target
|
185
187
|
|
186
188
|
with lock:
|
187
|
-
with open(
|
189
|
+
with open(self.__settings_file_path, 'w') as fp:
|
188
190
|
yaml.dump(contents, fp, allow_unicode=True)
|
189
191
|
|
190
192
|
### Helpers
|
@@ -215,13 +217,14 @@ class Settings:
|
|
215
217
|
|
216
218
|
def __reload_settings(self, event):
|
217
219
|
if not self.__load_lock:
|
218
|
-
from stoobly_agent.app.proxy.utils.publish_change_service import
|
220
|
+
from stoobly_agent.app.proxy.utils.publish_change_service import publish_settings_modified
|
219
221
|
|
220
222
|
self.__load_lock = True
|
221
223
|
|
222
224
|
Logger.instance(LOG_ID).debug('Reloading settings')
|
223
225
|
self.__load_settings()
|
224
226
|
|
225
|
-
|
227
|
+
if self.__ui_settings.active:
|
228
|
+
publish_settings_modified(self.__settings)
|
226
229
|
|
227
230
|
self.__load_lock = False
|
stoobly_agent/config/data_dir.py
CHANGED
stoobly_agent/public/index.html
CHANGED
@@ -113,6 +113,6 @@
|
|
113
113
|
</div>
|
114
114
|
|
115
115
|
<root></root>
|
116
|
-
<script src="runtime-es2015.f8c814b38b27708e91c1.js" type="module"></script><script src="runtime-es5.f8c814b38b27708e91c1.js" nomodule="" defer=""></script><script src="polyfills-es5.7530172ddcec11a10eb3.js" nomodule="" defer=""></script><script src="polyfills-es2015.8ce2adc69f283f6c4c5e.js" type="module"></script><script src="main-es2015.
|
116
|
+
<script src="runtime-es2015.f8c814b38b27708e91c1.js" type="module"></script><script src="runtime-es5.f8c814b38b27708e91c1.js" nomodule="" defer=""></script><script src="polyfills-es5.7530172ddcec11a10eb3.js" nomodule="" defer=""></script><script src="polyfills-es2015.8ce2adc69f283f6c4c5e.js" type="module"></script><script src="main-es2015.5a9aa16433404c3f423a.js" type="module"></script><script src="main-es5.5a9aa16433404c3f423a.js" nomodule="" defer=""></script></body>
|
117
117
|
|
118
118
|
</html>
|