stoobly-agent 0.33.7__py3-none-any.whl → 0.34.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.
Files changed (164) hide show
  1. stoobly_agent/__init__.py +1 -1
  2. stoobly_agent/app/cli/dev_tools_cli.py +1 -0
  3. stoobly_agent/app/cli/endpoint_cli.py +25 -2
  4. stoobly_agent/app/cli/helpers/endpoint_facade.py +22 -1
  5. stoobly_agent/app/cli/helpers/endpoints_import_context.py +57 -0
  6. stoobly_agent/app/cli/helpers/endpoints_import_service.py +152 -0
  7. stoobly_agent/app/cli/helpers/openapi_endpoint_adapter.py +111 -6
  8. stoobly_agent/app/cli/helpers/replay_facade.py +4 -0
  9. stoobly_agent/app/cli/request_cli.py +2 -0
  10. stoobly_agent/app/cli/scenario_cli.py +2 -0
  11. stoobly_agent/app/cli/types/test.py +2 -0
  12. stoobly_agent/app/models/factories/resource/local_db/helpers/request_builder.py +1 -1
  13. stoobly_agent/app/models/types/endpoint.py +28 -2
  14. stoobly_agent/app/proxy/handle_mock_service.py +15 -3
  15. stoobly_agent/app/proxy/intercept_settings.py +39 -0
  16. stoobly_agent/app/proxy/mock/eval_fixtures_service.py +61 -0
  17. stoobly_agent/app/proxy/mock/types/__init__.py +10 -0
  18. stoobly_agent/app/proxy/replay/replay_request_service.py +11 -3
  19. stoobly_agent/app/proxy/run.py +6 -0
  20. stoobly_agent/app/proxy/test/context.py +12 -0
  21. stoobly_agent/app/proxy/test/context_abc.py +15 -0
  22. stoobly_agent/app/settings/intercept_settings.py +1 -1
  23. stoobly_agent/cli.py +13 -2
  24. stoobly_agent/config/constants/custom_headers.py +2 -0
  25. stoobly_agent/config/constants/env_vars.py +2 -0
  26. stoobly_agent/lib/api/api.py +4 -0
  27. stoobly_agent/lib/api/body_param_names_resource.py +36 -0
  28. stoobly_agent/lib/api/endpoints_resource.py +9 -2
  29. stoobly_agent/lib/api/header_names_resource.py +36 -0
  30. stoobly_agent/lib/api/interfaces/endpoints.py +2 -0
  31. stoobly_agent/lib/api/query_param_names_resource.py +36 -0
  32. stoobly_agent/lib/api/response_header_names_resource.py +36 -0
  33. stoobly_agent/lib/api/response_param_names_resource.py +36 -0
  34. stoobly_agent/public/0-es2015.a7e60cafc0868f87a771.js +1 -0
  35. stoobly_agent/public/0-es5.a7e60cafc0868f87a771.js +1 -0
  36. stoobly_agent/public/1-es2015.cb071776436f10954db5.js +1 -0
  37. stoobly_agent/public/1-es5.cb071776436f10954db5.js +1 -0
  38. stoobly_agent/public/10-es2015.bb1702aaf3968a2cb521.js +1 -0
  39. stoobly_agent/public/10-es5.bb1702aaf3968a2cb521.js +1 -0
  40. stoobly_agent/public/12-es2015.591ec692afb8f8d13842.js +1 -0
  41. stoobly_agent/public/12-es5.591ec692afb8f8d13842.js +1 -0
  42. stoobly_agent/public/13-es2015.f381e7d6ff361b669e12.js +1 -0
  43. stoobly_agent/public/13-es5.f381e7d6ff361b669e12.js +1 -0
  44. stoobly_agent/public/14-es2015.1ffb225a16d6292dbddd.js +1 -0
  45. stoobly_agent/public/14-es5.1ffb225a16d6292dbddd.js +1 -0
  46. stoobly_agent/public/15-es2015.2cf39bcaeb0ea2c52297.js +1 -0
  47. stoobly_agent/public/15-es5.2cf39bcaeb0ea2c52297.js +1 -0
  48. stoobly_agent/public/16-es2015.0e9422b274e642f95e41.js +1 -0
  49. stoobly_agent/public/16-es5.0e9422b274e642f95e41.js +1 -0
  50. stoobly_agent/public/17-es2015.7fb08367a22d1e34aed7.js +1 -0
  51. stoobly_agent/public/17-es5.7fb08367a22d1e34aed7.js +1 -0
  52. stoobly_agent/public/18-es2015.5caa4789d1c335e628f2.js +1 -0
  53. stoobly_agent/public/18-es5.5caa4789d1c335e628f2.js +1 -0
  54. stoobly_agent/public/19-es2015.8049aef58c329c500f9b.js +1 -0
  55. stoobly_agent/public/19-es5.8049aef58c329c500f9b.js +1 -0
  56. stoobly_agent/public/2-es2015.8f184ac63348ba447b1f.js +1 -0
  57. stoobly_agent/public/2-es5.8f184ac63348ba447b1f.js +1 -0
  58. stoobly_agent/public/20-es2015.473486aabfa4d4a6431b.js +1 -0
  59. stoobly_agent/public/20-es5.473486aabfa4d4a6431b.js +1 -0
  60. stoobly_agent/public/21-es2015.63ed4e6b242fbc047bd6.js +1 -0
  61. stoobly_agent/public/21-es5.63ed4e6b242fbc047bd6.js +1 -0
  62. stoobly_agent/public/22-es2015.df183804415330639987.js +1 -0
  63. stoobly_agent/public/22-es5.df183804415330639987.js +1 -0
  64. stoobly_agent/public/23-es2015.0da63b056fabde91bb0b.js +1 -0
  65. stoobly_agent/public/23-es5.0da63b056fabde91bb0b.js +1 -0
  66. stoobly_agent/public/28-es2015.9fe030e9d3b0e52239aa.js +1 -0
  67. stoobly_agent/public/28-es5.9fe030e9d3b0e52239aa.js +1 -0
  68. stoobly_agent/public/29-es2015.9b440f22de725732e5ab.js +1 -0
  69. stoobly_agent/public/29-es5.9b440f22de725732e5ab.js +1 -0
  70. stoobly_agent/public/30-es2015.6ad2a5126b0a27c1e7c6.js +1 -0
  71. stoobly_agent/public/30-es5.6ad2a5126b0a27c1e7c6.js +1 -0
  72. stoobly_agent/public/31-es2015.f4291150f35d54ff19ca.js +1 -0
  73. stoobly_agent/public/31-es5.f4291150f35d54ff19ca.js +1 -0
  74. stoobly_agent/public/32-es2015.c8f6ccb43bdba0adf199.js +1 -0
  75. stoobly_agent/public/32-es5.c8f6ccb43bdba0adf199.js +1 -0
  76. stoobly_agent/public/33-es2015.f985e93402ebf86322ef.js +1 -0
  77. stoobly_agent/public/33-es5.f985e93402ebf86322ef.js +1 -0
  78. stoobly_agent/public/34-es2015.0e1961fb3cf649a52d49.js +1 -0
  79. stoobly_agent/public/34-es5.0e1961fb3cf649a52d49.js +1 -0
  80. stoobly_agent/public/35-es2015.a4ae56a89c0324397624.js +1 -0
  81. stoobly_agent/public/35-es5.a4ae56a89c0324397624.js +1 -0
  82. stoobly_agent/public/36-es2015.b8fdd25590a79c820119.js +1 -0
  83. stoobly_agent/public/36-es5.b8fdd25590a79c820119.js +1 -0
  84. stoobly_agent/public/37-es2015.6567a0ce4cf87ad7287b.js +1 -0
  85. stoobly_agent/public/37-es5.6567a0ce4cf87ad7287b.js +1 -0
  86. stoobly_agent/public/38-es2015.6f6d751bff41d84d727a.js +1 -0
  87. stoobly_agent/public/38-es5.6f6d751bff41d84d727a.js +1 -0
  88. stoobly_agent/public/39-es2015.47f63177e7d4509a22fa.js +1 -0
  89. stoobly_agent/public/39-es5.47f63177e7d4509a22fa.js +1 -0
  90. stoobly_agent/public/3rdpartylicenses.txt +2418 -0
  91. stoobly_agent/public/4-es2015.182e1ce1811ef67571fb.js +1 -0
  92. stoobly_agent/public/4-es5.182e1ce1811ef67571fb.js +1 -0
  93. stoobly_agent/public/40-es2015.5333067cdc4077c7495a.js +1 -0
  94. stoobly_agent/public/40-es5.5333067cdc4077c7495a.js +1 -0
  95. stoobly_agent/public/41-es2015.7a2434380c81c11ff2c5.js +1 -0
  96. stoobly_agent/public/41-es5.7a2434380c81c11ff2c5.js +1 -0
  97. stoobly_agent/public/42-es2015.5dde662fe1e3b4e4bdd1.js +1 -0
  98. stoobly_agent/public/42-es5.5dde662fe1e3b4e4bdd1.js +1 -0
  99. stoobly_agent/public/43-es2015.608e917d689b9bb762cb.js +1 -0
  100. stoobly_agent/public/43-es5.608e917d689b9bb762cb.js +1 -0
  101. stoobly_agent/public/44-es2015.2ae5fea15b5e8c2681d3.js +1 -0
  102. stoobly_agent/public/44-es5.2ae5fea15b5e8c2681d3.js +1 -0
  103. stoobly_agent/public/45-es2015.e46f228c795174428515.js +1 -0
  104. stoobly_agent/public/45-es5.e46f228c795174428515.js +1 -0
  105. stoobly_agent/public/46-es2015.22a0d8e0b4bbfb513741.js +1 -0
  106. stoobly_agent/public/46-es5.22a0d8e0b4bbfb513741.js +1 -0
  107. stoobly_agent/public/47-es2015.3878e5d1d692107748f3.js +1 -0
  108. stoobly_agent/public/47-es5.3878e5d1d692107748f3.js +1 -0
  109. stoobly_agent/public/5-es2015.aba7173be56fc19b4b6f.js +1 -0
  110. stoobly_agent/public/5-es5.aba7173be56fc19b4b6f.js +1 -0
  111. stoobly_agent/public/6-es2015.5fb726c0555664300974.js +1 -0
  112. stoobly_agent/public/6-es5.5fb726c0555664300974.js +1 -0
  113. stoobly_agent/public/7-es2015.9b9ab4adf24d13bdc2f8.js +1 -0
  114. stoobly_agent/public/7-es5.9b9ab4adf24d13bdc2f8.js +1 -0
  115. stoobly_agent/public/8-es2015.cdd7dce2a24aaf9d974f.js +1 -0
  116. stoobly_agent/public/8-es5.cdd7dce2a24aaf9d974f.js +1 -0
  117. stoobly_agent/public/9-es2015.cdde98f2537997afbf0f.js +1 -0
  118. stoobly_agent/public/9-es5.cdde98f2537997afbf0f.js +1 -0
  119. stoobly_agent/public/CHANGELOG.md +58 -0
  120. stoobly_agent/public/README.md +264 -0
  121. stoobly_agent/public/_redirects +1 -0
  122. stoobly_agent/public/assets/img/demo/1.jpg +0 -0
  123. stoobly_agent/public/assets/img/demo/2.jpg +0 -0
  124. stoobly_agent/public/assets/img/demo/3.jpg +0 -0
  125. stoobly_agent/public/assets/img/demo/4.jpg +0 -0
  126. stoobly_agent/public/assets/img/demo/5.jpg +0 -0
  127. stoobly_agent/public/assets/img/demo/6.jpg +0 -0
  128. stoobly_agent/public/assets/img/demo/7.jpg +0 -0
  129. stoobly_agent/public/assets/img/demo/8.jpg +0 -0
  130. stoobly_agent/public/assets/img/demo/landscape.jpg +0 -0
  131. stoobly_agent/public/assets/img/demo/mountain-cinematic.jpg +0 -0
  132. stoobly_agent/public/assets/img/illustrations/checklist.svg +164 -0
  133. stoobly_agent/public/assets/img/illustrations/data_center.svg +150 -0
  134. stoobly_agent/public/assets/img/illustrations/idea.svg +213 -0
  135. stoobly_agent/public/assets/img/illustrations/it_support.svg +168 -0
  136. stoobly_agent/public/assets/img/illustrations/peak_mountain_3.svg +262 -0
  137. stoobly_agent/public/assets/img/illustrations/under_constructions_1.svg +282 -0
  138. stoobly_agent/public/assets/img/logo/colored.png +0 -0
  139. stoobly_agent/public/assets/img/logo/colored.svg +9 -0
  140. stoobly_agent/public/assets/img/logo/white.png +0 -0
  141. stoobly_agent/public/assets/img/logo/white.svg +8 -0
  142. stoobly_agent/public/common-es2015.6f230354b12587f9be81.js +1 -0
  143. stoobly_agent/public/common-es5.6f230354b12587f9be81.js +1 -0
  144. stoobly_agent/public/favicon.ico +0 -0
  145. stoobly_agent/public/index.html +118 -0
  146. stoobly_agent/public/main-es2015.081bbd2709f22e95933e.js +1 -0
  147. stoobly_agent/public/main-es5.081bbd2709f22e95933e.js +1 -0
  148. stoobly_agent/public/polyfills-es2015.8ce2adc69f283f6c4c5e.js +1 -0
  149. stoobly_agent/public/polyfills-es5.7530172ddcec11a10eb3.js +1 -0
  150. stoobly_agent/public/runtime-es2015.362e49d383fb724f5414.js +1 -0
  151. stoobly_agent/public/runtime-es5.362e49d383fb724f5414.js +1 -0
  152. stoobly_agent/public/styles.ca104d947fbb2eebbeca.css +6 -0
  153. stoobly_agent/test/app/cli/helpers/openapi_endpoint_adapter_additional_props_test.py +34 -0
  154. stoobly_agent/test/app/cli/helpers/openapi_endpoint_adapter_missing_info_test.py +80 -3
  155. stoobly_agent/test/app/cli/helpers/openapi_endpoint_adapter_missing_oauth2_scopes_test.py +82 -1
  156. stoobly_agent/test/app/cli/helpers/openapi_endpoint_adapter_missing_servers_test.py +80 -3
  157. stoobly_agent/test/app/cli/helpers/openapi_endpoint_adapter_test.py +617 -4
  158. stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
  159. stoobly_agent/test/app/proxy/mock/eval_fixtures_service_test.py +95 -0
  160. {stoobly_agent-0.33.7.dist-info → stoobly_agent-0.34.1.dist-info}/METADATA +1 -1
  161. {stoobly_agent-0.33.7.dist-info → stoobly_agent-0.34.1.dist-info}/RECORD +164 -35
  162. {stoobly_agent-0.33.7.dist-info → stoobly_agent-0.34.1.dist-info}/LICENSE +0 -0
  163. {stoobly_agent-0.33.7.dist-info → stoobly_agent-0.34.1.dist-info}/WHEEL +0 -0
  164. {stoobly_agent-0.33.7.dist-info → stoobly_agent-0.34.1.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  import pdb
3
+ import yaml
3
4
 
4
5
  from runpy import run_path
5
6
  from typing import List, Union
@@ -37,6 +38,9 @@ class InterceptSettings:
37
38
  self.__lifecycle_hooks = None
38
39
  self.__initialize_lifecycle_hooks()
39
40
 
41
+ self.__response_fixtures = None
42
+ self.__initialize_response_fixtures()
43
+
40
44
  @property
41
45
  def settings(self):
42
46
  return self.__settings
@@ -102,6 +106,14 @@ class InterceptSettings:
102
106
  except InvalidProjectKey:
103
107
  pass
104
108
 
109
+ @property
110
+ def public_directory_path(self):
111
+ if self.__headers and custom_headers.PUBLIC_DIRECTORY_PATH in self.__headers:
112
+ return self.__headers[custom_headers.PUBLIC_DIRECTORY_PATH]
113
+
114
+ if os.environ.get(env_vars.AGENT_PUBLIC_DIRECTORY_PATH):
115
+ return os.environ[env_vars.AGENT_PUBLIC_DIRECTORY_PATH]
116
+
105
117
  @property
106
118
  def remote_project_key(self):
107
119
  # When not local project, don't return set remote project_key
@@ -114,6 +126,18 @@ class InterceptSettings:
114
126
 
115
127
  return self.__settings.remote.project_key
116
128
 
129
+ @property
130
+ def response_fixtures_path(self):
131
+ if self.__headers and custom_headers.RESPONSE_FIXTURES_PATH in self.__headers:
132
+ return self.__headers[custom_headers.RESPONSE_FIXTURES_PATH]
133
+
134
+ if os.environ.get(env_vars.AGENT_RESPONSE_FIXTURES_PATH):
135
+ return os.environ[env_vars.AGENT_RESPONSE_FIXTURES_PATH]
136
+
137
+ @property
138
+ def response_fixtures(self):
139
+ return self.__response_fixtures or {}
140
+
117
141
  @property
118
142
  def parsed_remote_project_key(self):
119
143
  try:
@@ -288,6 +312,21 @@ class InterceptSettings:
288
312
  except Exception as e:
289
313
  return Logger.instance().error(e)
290
314
 
315
+ def __initialize_response_fixtures(self):
316
+ fixtures_path = self.response_fixtures_path
317
+
318
+ if not fixtures_path:
319
+ return
320
+
321
+ if not os.path.exists(fixtures_path):
322
+ return Logger.instance().error(f"Response fixtures {fixtures_path} does not exist")
323
+
324
+ with open(fixtures_path, 'r') as stream:
325
+ try:
326
+ self.__response_fixtures = yaml.safe_load(stream)
327
+ except yaml.YAMLError as exc:
328
+ Logger.instance().error(exc)
329
+
291
330
  def __policy(self, mode):
292
331
  if mode == intercept_mode.MOCK:
293
332
  if self.__headers and custom_headers.MOCK_POLICY in self.__headers:
@@ -0,0 +1,61 @@
1
+ import os
2
+ import pdb
3
+ import re
4
+
5
+ from io import BytesIO
6
+ from mitmproxy.http import Request as MitmproxyRequest
7
+ from requests import Response
8
+ from typing import Union
9
+
10
+ from stoobly_agent.lib.logger import bcolors, Logger
11
+
12
+ from .types import Fixtures
13
+
14
+ class Options():
15
+ public_directory_path: str
16
+ response_fixtures: Fixtures
17
+
18
+ def eval_fixtures(request: MitmproxyRequest, **options: Options) -> Union[Response, None]:
19
+ response_fixtures = options.get('response_fixtures')
20
+
21
+ fixture_path = __eval_response_fixtures(request, response_fixtures)
22
+ if not fixture_path:
23
+ public_directory_path = options.get('public_directory_path')
24
+
25
+ if public_directory_path and os.path.exists(public_directory_path):
26
+ static_file_path = os.path.join(public_directory_path, request.path.lstrip('/'))
27
+
28
+ if os.path.exists(static_file_path):
29
+ fixture_path = static_file_path
30
+
31
+ if not fixture_path:
32
+ return
33
+
34
+ with open(fixture_path, 'rb') as fp:
35
+ response = Response()
36
+
37
+ response.status_code = 200
38
+ response.raw = BytesIO(fp.read())
39
+
40
+ Logger.instance().debug(f"{bcolors.OKBLUE}Resolved fixture {fixture_path}{bcolors.ENDC}")
41
+
42
+ return response
43
+
44
+ def __eval_response_fixtures(request: MitmproxyRequest, response_fixtures: Fixtures):
45
+ if not response_fixtures:
46
+ return
47
+
48
+ method = request.method
49
+ routes = response_fixtures.get(method)
50
+
51
+ if not routes:
52
+ return
53
+
54
+ for path_pattern in routes:
55
+ if not re.match(path_pattern, request.path):
56
+ continue
57
+
58
+ path = routes[path_pattern].get('path')
59
+
60
+ if path and os.path.exists(path):
61
+ return path
@@ -0,0 +1,10 @@
1
+ from typing import TypedDict
2
+
3
+ class Route(TypedDict):
4
+ path: str
5
+
6
+ class Fixtures(TypedDict):
7
+ DELETE: Route
8
+ GET: Route
9
+ POST: Route
10
+ PUT: Route
@@ -27,9 +27,11 @@ class ReplayRequestOptions(TypedDict):
27
27
  after_replay: Union[Callable[[ReplayContext], Union[requests.Response, None]], None]
28
28
  project_key: Union[str, None]
29
29
  proxies: dict
30
+ public_directory_path: str
30
31
  remote_project_key: str
31
32
  report_key: Union[str, None]
32
33
  request_origin: Union[request_origin.CLI, None]
34
+ response_fixtures_path: str
33
35
  scenario_key: Union[str, None]
34
36
  scheme: str
35
37
  test_filter: test_filter.TestFilter
@@ -58,7 +60,7 @@ def replay(context: ReplayContext, options: ReplayRequestOptions) -> requests.Re
58
60
  headers[custom_headers.ALIAS_RESOLVE_STRATEGY] = options['alias_resolve_strategy']
59
61
 
60
62
  if options.get('lifecycle_hooks_path'):
61
- __handle_lifecycle_hooks_path(options['lifecycle_hooks_path'], headers)
63
+ __handle_path_header(custom_headers.LIFECYCLE_HOOKS_PATH, options['lifecycle_hooks_path'], headers)
62
64
 
63
65
  if options.get('mode'):
64
66
  __handle_mode_option(options['mode'], request, headers)
@@ -66,6 +68,9 @@ def replay(context: ReplayContext, options: ReplayRequestOptions) -> requests.Re
66
68
  if options.get('project_key'):
67
69
  headers[custom_headers.PROJECT_KEY] = options['project_key']
68
70
 
71
+ if options.get('public_directory_path'):
72
+ __handle_path_header(custom_headers.PUBLIC_DIRECTORY_PATH, options['public_directory_path'], headers)
73
+
69
74
  if options.get('report_key'):
70
75
  headers[custom_headers.REPORT_KEY] = options['report_key']
71
76
 
@@ -75,6 +80,9 @@ def replay(context: ReplayContext, options: ReplayRequestOptions) -> requests.Re
75
80
  if custom_headers.REQUEST_ORIGIN not in headers:
76
81
  headers[custom_headers.REQUEST_ORIGIN] = request_origin.CLI
77
82
 
83
+ if options.get('response_fixtures_path'):
84
+ __handle_path_header(custom_headers.RESPONSE_FIXTURES_PATH, options['response_fixtures_path'], headers)
85
+
78
86
  if options.get('scenario_key'):
79
87
  headers[custom_headers.SCENARIO_KEY] = options['scenario_key']
80
88
 
@@ -146,14 +154,14 @@ def replay(context: ReplayContext, options: ReplayRequestOptions) -> requests.Re
146
154
 
147
155
  return res
148
156
 
149
- def __handle_lifecycle_hooks_path(script_path, headers):
157
+ def __handle_path_header(header_name: str, script_path: str, headers):
150
158
  if not script_path:
151
159
  return
152
160
 
153
161
  if not os.path.isabs(script_path):
154
162
  script_path = os.path.join(os.path.abspath('.'), script_path)
155
163
 
156
- headers[custom_headers.LIFECYCLE_HOOKS_PATH] = script_path
164
+ headers[header_name] = script_path
157
165
 
158
166
  def __handle_mode_option(_mode, request: Request, headers):
159
167
  headers[custom_headers.PROXY_MODE] = _mode
@@ -124,6 +124,12 @@ def __filter_options(options):
124
124
  if 'lifecycle_hooks_path' in options:
125
125
  del options['lifecycle_hooks_path']
126
126
 
127
+ if 'public_directory_path' in options:
128
+ del options['public_directory_path']
129
+
130
+ if 'response_fixtures_path' in options:
131
+ del options['response_fixtures_path']
132
+
127
133
  if 'ui_host' in options:
128
134
  del options['ui_host']
129
135
 
@@ -142,6 +142,10 @@ class TestContext(TestContextABC):
142
142
  def passed(self, v):
143
143
  self.__passed = v
144
144
 
145
+ @property
146
+ def public_directory_path(self):
147
+ return self.__intercept_settings.public_directory_path
148
+
145
149
  @property
146
150
  def replay_context(self) -> ReplayContext:
147
151
  return self.__replay_context
@@ -162,6 +166,14 @@ class TestContext(TestContextABC):
162
166
  def response(self) -> TestContextResponse:
163
167
  return self.__response
164
168
 
169
+ @property
170
+ def response_fixtures(self):
171
+ return self.__intercept_settings.response_fixtures
172
+
173
+ @property
174
+ def response_fixtures_path(self):
175
+ return self.__intercept_settings.response_fixtures_path
176
+
165
177
  @property
166
178
  def response_param_names(self) -> RequestComponentNamesFacade:
167
179
  if self.__cached_response_param_names:
@@ -119,6 +119,11 @@ class TestContextABC(abc.ABC):
119
119
  def passed(self, v):
120
120
  pass
121
121
 
122
+ @property
123
+ @abc.abstractmethod
124
+ def public_directory_path(self):
125
+ pass
126
+
122
127
  @property
123
128
  @abc.abstractmethod
124
129
  def replay_context(self):
@@ -144,6 +149,16 @@ class TestContextABC(abc.ABC):
144
149
  def response(self) -> TestContextResponse:
145
150
  pass
146
151
 
152
+ @property
153
+ @abc.abstractmethod
154
+ def response_fixtures(self):
155
+ pass
156
+
157
+ @property
158
+ @abc.abstractmethod
159
+ def response_fixtures_path(self):
160
+ pass
161
+
147
162
  @property
148
163
  @abc.abstractmethod
149
164
  def response_param_names(self) -> RequestComponentNamesFacade:
@@ -42,7 +42,7 @@ class InterceptSettings:
42
42
 
43
43
  @mode.setter
44
44
  def mode(self, v):
45
- if v in [mode.MOCK, mode.NONE, mode.RECORD, mode.TEST]:
45
+ if v in [mode.MOCK, mode.NONE, mode.RECORD, mode.REPLAY, mode.TEST]:
46
46
  self.__mode = v
47
47
  self.__intercept_settings['mode'] = v
48
48
 
stoobly_agent/cli.py CHANGED
@@ -105,6 +105,8 @@ def init(**kwargs):
105
105
  the form of "http[s]://host[:port]".
106
106
  ''')
107
107
  @click.option('--proxy-port', default=8080, help='Proxy service port.')
108
+ @click.option('--public-directory-path', help='Path to public files. Used for mocking requests.')
109
+ @click.option('--response-fixtures-path', help='Path to response fixtures yaml. Used for mocking requests.')
108
110
  @click.option('--ssl-insecure', is_flag=True, default=False, help='Do not verify upstream server SSL/TLS certificates.')
109
111
  @click.option('--ui-host', default='0.0.0.0', help='Address to bind UI to.')
110
112
  @click.option('--ui-port', default=4200, help='UI service port.')
@@ -116,6 +118,12 @@ def run(**kwargs):
116
118
  if kwargs.get('lifecycle_hooks_path'):
117
119
  os.environ[env_vars.AGENT_LIFECYCLE_HOOKS_PATH] = kwargs['lifecycle_hooks_path']
118
120
 
121
+ if kwargs.get('public_directory_path'):
122
+ os.environ[env_vars.AGENT_PUBLIC_DIRECOTRY_PATH] = kwargs['public_directory_path']
123
+
124
+ if kwargs.get('response_fixtures_path'):
125
+ os.environ[env_vars.AGENT_RESPONSE_FIXTURES_PATH] = kwargs['response_fixtures_path']
126
+
119
127
  # Observe config for changes
120
128
  Settings.instance().watch()
121
129
 
@@ -137,7 +145,10 @@ def run(**kwargs):
137
145
  @ConditionalDecorator(lambda f: click.option('--remote-project-key', help='Use remote project for endpoint definitions.')(f), is_remote and is_local)
138
146
  @click.option('--format', type=click.Choice([RAW_FORMAT]), help='Format response')
139
147
  @click.option('-H', '--header', multiple=True, help='Pass custom header(s) to server')
148
+ @click.option('--lifecycle-hooks-path', help='Path to lifecycle hooks script.')
140
149
  @ConditionalDecorator(lambda f: click.option('--project-key')(f), is_remote)
150
+ @click.option('--public-directory-path', help='Path to public files. Used for mocking requests.')
151
+ @click.option('--response-fixtures-path', help='Path to response fixtures yaml. Used for mocking requests.')
141
152
  @click.option('-X', '--request', default='GET', help='Specify request command to use')
142
153
  @click.option('--scenario-key')
143
154
  @click.argument('url')
@@ -165,7 +176,7 @@ def mock(**kwargs):
165
176
  print_raw_response(response)
166
177
  else:
167
178
  content = response.content
168
- print(decode(content))
179
+ print(decode(content), end='')
169
180
 
170
181
  @main.command(
171
182
  help="Record request"
@@ -194,7 +205,7 @@ def record(**kwargs):
194
205
  else:
195
206
  try:
196
207
  content = response.raw.data
197
- print(content.decode(json.detect_encoding(content)))
208
+ print(content.decode(json.detect_encoding(content)), end='')
198
209
  except UnicodeDecodeError:
199
210
  print('Warning: Binary output can mess up your terminal.')
200
211
 
@@ -9,9 +9,11 @@ DO_PROXY = 'X-Do-Proxy'
9
9
  LIFECYCLE_HOOKS_PATH = 'X-Stoobly-Lifecycle-Hooks-Path'
10
10
  PROJECT_KEY = 'X-Project-Key'
11
11
  PROXY_MODE = 'X-Proxy-Mode'
12
+ PUBLIC_DIRECTORY_PATH = 'X-Stoobly-Public-Directory-Path'
12
13
  RECORD_POLICY = 'X-Record-Policy'
13
14
  REPORT_KEY = 'X-Report-Key'
14
15
  REQUEST_ORIGIN = 'X-Proxy-Request-Origin'
16
+ RESPONSE_FIXTURES_PATH = 'X-Stoobly-Response-Fixtures-Path'
15
17
  RESPONSE_ID = 'X-Response-Id'
16
18
  RESPONSE_LATENCY = 'X-Stoobly-Request-Response-Latency'
17
19
  RESPONSE_PROXY_MODE = 'X-Response-Proxy-Mode'
@@ -9,10 +9,12 @@ AGENT_LIFECYCLE_HOOKS_PATH = 'STOOBLY_AGENT_LIFECYCLE_HOOKS_PATH'
9
9
  AGENT_POLICY = 'STOOBLY_AGENT_POLICY'
10
10
  AGENT_PROJECT_KEY = 'STOOBLY_AGENT_PROJECT_KEY'
11
11
  AGENT_PROXY_URL = 'STOOBLY_AGENT_PROXY_URL'
12
+ AGENT_PUBLIC_DIRECTORY_PATH = 'STOOBLY_AGENT_PUBLIC_DIRECTORY_PATH'
12
13
  AGENT_REMOTE_ENABLED = 'STOOBLY_AGENT_REMOTE_ENABLED'
13
14
  AGENT_REMOTE_PROJECT_KEY = 'STOOBLY_AGENT_REMOTE_PROJECT_KEY'
14
15
  AGENT_REPLAY_HOST = 'STOOBLY_AGENT_REPLAY_HOST'
15
16
  AGENT_REPLAY_SCHEME = 'STOOBLY_AGENT_REPLAY_SCHEME'
17
+ AGENT_RESPONSE_FIXTURES_PATH = 'STOOBLY_AGENT_RESPONSE_FIXTURES_PATH'
16
18
  AGENT_SERVICE_URL = 'STOOBLY_AGENT_SERVICE_URL'
17
19
  AGENT_SCENARIO_KEY = 'STOOBLY_AGENT_SCENARIO_KEY'
18
20
  AGENT_SIMULATE_LATENCY = 'STOOBLY_AGENT_SIMULATE_LATENCY'
@@ -69,3 +69,7 @@ class Api():
69
69
  def put(self, url, **kwargs):
70
70
  handler = lambda: requests.put(url, **kwargs)
71
71
  return self.without_proxy(handler)
72
+
73
+ def delete(self, url, **kwargs):
74
+ handler = lambda: requests.delete(url, **kwargs)
75
+ return self.without_proxy(handler)
@@ -0,0 +1,36 @@
1
+ import requests
2
+ import urllib
3
+ import pdb
4
+
5
+ from stoobly_agent.app.models.types import ParamNameCreateParams
6
+
7
+ from ..logger import Logger
8
+ from .endpoints_resource import EndpointsResource
9
+
10
+ class BodyParamNamesResource(EndpointsResource):
11
+ BODY_PARAM_NAMES_ENDPOINT = 'body_param_names'
12
+
13
+ def create(self, endpoint_id: int, params: ParamNameCreateParams = {}):
14
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.BODY_PARAM_NAMES_ENDPOINT}"
15
+ return self.post(url, headers=self.default_headers, json=params)
16
+
17
+ def index(self, endpoint_id: int, query_params = {}) -> requests.Response:
18
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.BODY_PARAM_NAMES_ENDPOINT}"
19
+
20
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
21
+
22
+ return self.get(url, headers=self.default_headers, params=query_params)
23
+
24
+ def show(self, endpoint_id: int, body_param_name_id: int, query_params = {}) -> requests.Response:
25
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.BODY_PARAM_NAMES_ENDPOINT}/{body_param_name_id}"
26
+
27
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
28
+
29
+ return self.get(url, headers=self.default_headers, params=query_params)
30
+
31
+ def destroy(self, endpoint_id, body_param_name_id: int, query_params = {}) -> requests.Response:
32
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.BODY_PARAM_NAMES_ENDPOINT}/{body_param_name_id}"
33
+
34
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
35
+
36
+ return self.delete(url, headers=self.default_headers, params=query_params)
@@ -11,7 +11,7 @@ class EndpointsResource(StooblyApi):
11
11
 
12
12
  def create(self, **params):
13
13
  url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}"
14
- return self.post(url, headers=self.default_headers, data=params)
14
+ return self.post(url, headers=self.default_headers, json=params)
15
15
 
16
16
  def index(self, **query_params: EndpointsIndexQueryParams) -> requests.Response:
17
17
  url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}"
@@ -25,4 +25,11 @@ class EndpointsResource(StooblyApi):
25
25
 
26
26
  Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
27
27
 
28
- return self.get(url, headers=self.default_headers, params=query_params)
28
+ return self.get(url, headers=self.default_headers, params=query_params)
29
+
30
+ def destroy(self, endpoint_id: int, **query_params) -> requests.Response:
31
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}"
32
+
33
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
34
+
35
+ return self.delete(url, headers=self.default_headers, params=query_params)
@@ -0,0 +1,36 @@
1
+ import requests
2
+ import urllib
3
+ import pdb
4
+
5
+ from stoobly_agent.app.models.types import HeaderNameCreateParams
6
+
7
+ from ..logger import Logger
8
+ from .endpoints_resource import EndpointsResource
9
+
10
+ class HeaderNamesResource(EndpointsResource):
11
+ HEADER_NAMES_ENDPOINT = 'header_names'
12
+
13
+ def create(self, endpoint_id: int, params: HeaderNameCreateParams = {}):
14
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.HEADER_NAMES_ENDPOINT}"
15
+ return self.post(url, headers=self.default_headers, json=params)
16
+
17
+ def index(self, endpoint_id: int, query_params = {}) -> requests.Response:
18
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.HEADER_NAMES_ENDPOINT}"
19
+
20
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
21
+
22
+ return self.get(url, headers=self.default_headers, params=query_params)
23
+
24
+ def show(self, endpoint_id: int, header_name_id: int, query_params = {}) -> requests.Response:
25
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.HEADER_NAMES_ENDPOINT}/{header_name_id}"
26
+
27
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
28
+
29
+ return self.get(url, headers=self.default_headers, params=query_params)
30
+
31
+ def destroy(self, endpoint_id: int, header_name_id: int, query_params = {}) -> requests.Response:
32
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.HEADER_NAMES_ENDPOINT}/{header_name_id}"
33
+
34
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
35
+
36
+ return self.delete(url, headers=self.default_headers, params=query_params)
@@ -43,8 +43,10 @@ class EndpointShowResponse(TypedDict):
43
43
  path_segment_names: List[RequestComponentName]
44
44
  query_param_names: List[RequestComponentName]
45
45
  response_param_names: List[ResponseParamName]
46
+ response_header_names: List[RequestComponentName]
46
47
  literal_query_params: Optional[dict]
47
48
  literal_body_params: Optional[dict]
49
+ literal_response_params: Optional[dict]
48
50
 
49
51
  ARRAY_TYPE = 'Array'
50
52
 
@@ -0,0 +1,36 @@
1
+ import requests
2
+ import urllib
3
+ import pdb
4
+
5
+ from stoobly_agent.app.models.types import ParamNameCreateParams
6
+
7
+ from ..logger import Logger
8
+ from .endpoints_resource import EndpointsResource
9
+
10
+ class QueryParamNamesResource(EndpointsResource):
11
+ QUERY_PARAM_NAMES_ENDPOINT = 'query_param_names'
12
+
13
+ def create(self, endpoint_id: int, params: ParamNameCreateParams = {}):
14
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.QUERY_PARAM_NAMES_ENDPOINT}"
15
+ return self.post(url, headers=self.default_headers, json=params)
16
+
17
+ def index(self, endpoint_id: int, query_params = {}) -> requests.Response:
18
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.QUERY_PARAM_NAMES_ENDPOINT}"
19
+
20
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
21
+
22
+ return self.get(url, headers=self.default_headers, params=query_params)
23
+
24
+ def show(self, endpoint_id: int, query_param_name_id: int, query_params = {}) -> requests.Response:
25
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.QUERY_PARAM_NAMES_ENDPOINT}/{query_param_name_id}"
26
+
27
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
28
+
29
+ return self.get(url, headers=self.default_headers, params=query_params)
30
+
31
+ def destroy(self, endpoint_id: int, query_param_name_id: int, query_params = {}) -> requests.Response:
32
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.QUERY_PARAM_NAMES_ENDPOINT}/{query_param_name_id}"
33
+
34
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
35
+
36
+ return self.delete(url, headers=self.default_headers, params=query_params)
@@ -0,0 +1,36 @@
1
+ import requests
2
+ import urllib
3
+ import pdb
4
+
5
+ from stoobly_agent.app.models.types import HeaderNameCreateParams
6
+
7
+ from ..logger import Logger
8
+ from .endpoints_resource import EndpointsResource
9
+
10
+ class ResponseHeaderNamesResource(EndpointsResource):
11
+ RESPONSE_HEADER_NAME_ENDPOINT = 'response_header_names'
12
+
13
+ def create(self, endpoint_id: int, params: HeaderNameCreateParams = {}):
14
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.RESPONSE_HEADER_NAME_ENDPOINT}"
15
+ return self.post(url, headers=self.default_headers, json=params)
16
+
17
+ def index(self, endpoint_id: int, query_params = {}) -> requests.Response:
18
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.RESPONSE_HEADER_NAME_ENDPOINT}"
19
+
20
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
21
+
22
+ return self.get(url, headers=self.default_headers, params=query_params)
23
+
24
+ def show(self, endpoint_id: int, response_header_name_id: int, query_params = {}) -> requests.Response:
25
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.RESPONSE_HEADER_NAME_ENDPOINT}/{response_header_name_id}"
26
+
27
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
28
+
29
+ return self.get(url, headers=self.default_headers, params=query_params)
30
+
31
+ def destroy(self, endpoint_id: int, response_header_name_id: int, query_params = {}) -> requests.Response:
32
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.RESPONSE_HEADER_NAME_ENDPOINT}/{response_header_name_id}"
33
+
34
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
35
+
36
+ return self.delete(url, headers=self.default_headers, params=query_params)
@@ -0,0 +1,36 @@
1
+ import requests
2
+ import urllib
3
+ import pdb
4
+
5
+ from stoobly_agent.app.models.types import ParamNameCreateParams
6
+
7
+ from ..logger import Logger
8
+ from .endpoints_resource import EndpointsResource
9
+
10
+ class ResponseParamNamesResource(EndpointsResource):
11
+ RESPONSE_PARAM_NAMES_ENDPOINT = 'response_param_names'
12
+
13
+ def create(self, endpoint_id: int, params: ParamNameCreateParams = {}):
14
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.RESPONSE_PARAM_NAMES_ENDPOINT}"
15
+ return self.post(url, headers=self.default_headers, json=params)
16
+
17
+ def index(self, endpoint_id: int, query_params = {}) -> requests.Response:
18
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.RESPONSE_PARAM_NAMES_ENDPOINT}"
19
+
20
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
21
+
22
+ return self.get(url, headers=self.default_headers, params=query_params)
23
+
24
+ def show(self, endpoint_id: int, response_param_name_id: int, query_params = {}) -> requests.Response:
25
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.RESPONSE_PARAM_NAMES_ENDPOINT}/{response_param_name_id}"
26
+
27
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
28
+
29
+ return self.get(url, headers=self.default_headers, params=query_params)
30
+
31
+ def destroy(self, endpoint_id: int, response_param_name_id: int, query_params = {}) -> requests.Response:
32
+ url = f"{self.service_url}/{self.ENDPOINTS_ENDPOINT}/{endpoint_id}/{self.RESPONSE_PARAM_NAMES_ENDPOINT}/{response_param_name_id}"
33
+
34
+ Logger.instance().debug(f"{self.LOG_ID}.request_response:{url}?{urllib.parse.urlencode(query_params)}")
35
+
36
+ return self.delete(url, headers=self.default_headers, params=query_params)
@@ -0,0 +1 @@
1
+ (window.webpackJsonp=window.webpackJsonp||[]).push([[0],{pMoy:function(e,t,n){"use strict";n.d(t,"a",function(){return o}),n.d(t,"b",function(){return k}),n.d(t,"c",function(){return b}),n.d(t,"d",function(){return u});var c=n("8LU1"),i=n("8Y7J"),a=(n("s7LF"),n("UhP/"));const o=new i.s("mat-checkbox-default-options",{providedIn:"root",factory:r});function r(){return{color:"accent",clickAction:"check-indeterminate"}}let s=0;const h=r();class m{}class l{constructor(e){this._elementRef=e}}const d=Object(a.H)(Object(a.C)(Object(a.D)(Object(a.E)(l))));class k extends d{constructor(e,t,n,c,a,o,r){super(e),this._changeDetectorRef=t,this._focusMonitor=n,this._ngZone=c,this._animationMode=o,this._options=r,this.ariaLabel="",this.ariaLabelledby=null,this._uniqueId="mat-checkbox-"+ ++s,this.id=this._uniqueId,this.labelPosition="after",this.name=null,this.change=new i.o,this.indeterminateChange=new i.o,this._onTouched=()=>{},this._currentAnimationClass="",this._currentCheckState=0,this._controlValueAccessorChangeFn=()=>{},this._checked=!1,this._disabled=!1,this._indeterminate=!1,this._options=this._options||h,this.color=this.defaultColor=this._options.color||h.color,this.tabIndex=parseInt(a)||0}get inputId(){return(this.id||this._uniqueId)+"-input"}get required(){return this._required}set required(e){this._required=Object(c.c)(e)}ngAfterViewInit(){this._focusMonitor.monitor(this._elementRef,!0).subscribe(e=>{e||Promise.resolve().then(()=>{this._onTouched(),this._changeDetectorRef.markForCheck()})}),this._syncIndeterminate(this._indeterminate)}ngAfterViewChecked(){}ngOnDestroy(){this._focusMonitor.stopMonitoring(this._elementRef)}get checked(){return this._checked}set checked(e){e!=this.checked&&(this._checked=e,this._changeDetectorRef.markForCheck())}get disabled(){return this._disabled}set disabled(e){const t=Object(c.c)(e);t!==this.disabled&&(this._disabled=t,this._changeDetectorRef.markForCheck())}get indeterminate(){return this._indeterminate}set indeterminate(e){const t=e!=this._indeterminate;this._indeterminate=Object(c.c)(e),t&&(this._transitionCheckState(this._indeterminate?3:this.checked?1:2),this.indeterminateChange.emit(this._indeterminate)),this._syncIndeterminate(this._indeterminate)}_isRippleDisabled(){return this.disableRipple||this.disabled}_onLabelTextChange(){this._changeDetectorRef.detectChanges()}writeValue(e){this.checked=!!e}registerOnChange(e){this._controlValueAccessorChangeFn=e}registerOnTouched(e){this._onTouched=e}setDisabledState(e){this.disabled=e}_getAriaChecked(){return this.checked?"true":this.indeterminate?"mixed":"false"}_transitionCheckState(e){let t=this._currentCheckState,n=this._elementRef.nativeElement;if(t!==e&&(this._currentAnimationClass.length>0&&n.classList.remove(this._currentAnimationClass),this._currentAnimationClass=this._getAnimationClassForCheckStateTransition(t,e),this._currentCheckState=e,this._currentAnimationClass.length>0)){n.classList.add(this._currentAnimationClass);const e=this._currentAnimationClass;this._ngZone.runOutsideAngular(()=>{setTimeout(()=>{n.classList.remove(e)},1e3)})}}_emitChangeEvent(){const e=new m;e.source=this,e.checked=this.checked,this._controlValueAccessorChangeFn(this.checked),this.change.emit(e)}toggle(){this.checked=!this.checked}_onInputClick(e){var t;const n=null===(t=this._options)||void 0===t?void 0:t.clickAction;e.stopPropagation(),this.disabled||"noop"===n?this.disabled||"noop"!==n||(this._inputElement.nativeElement.checked=this.checked,this._inputElement.nativeElement.indeterminate=this.indeterminate):(this.indeterminate&&"check"!==n&&Promise.resolve().then(()=>{this._indeterminate=!1,this.indeterminateChange.emit(this._indeterminate)}),this.toggle(),this._transitionCheckState(this._checked?1:2),this._emitChangeEvent())}focus(e="keyboard",t){this._focusMonitor.focusVia(this._inputElement,e,t)}_onInteractionEvent(e){e.stopPropagation()}_getAnimationClassForCheckStateTransition(e,t){if("NoopAnimations"===this._animationMode)return"";let n="";switch(e){case 0:if(1===t)n="unchecked-checked";else{if(3!=t)return"";n="unchecked-indeterminate"}break;case 2:n=1===t?"unchecked-checked":"unchecked-indeterminate";break;case 1:n=2===t?"checked-unchecked":"checked-indeterminate";break;case 3:n=1===t?"indeterminate-checked":"indeterminate-unchecked"}return"mat-checkbox-anim-"+n}_syncIndeterminate(e){const t=this._inputElement;t&&(t.nativeElement.indeterminate=e)}}class u{}class b{}},"y3B+":function(e,t,n){"use strict";n.d(t,"a",function(){return s}),n.d(t,"b",function(){return h});var c=n("8Y7J"),i=(n("pMoy"),n("9b/N")),a=(n("9gLZ"),n("UhP/")),o=(n("YEUz"),n("SVse"),n("SCoL")),r=n("omvX"),s=(n("s7LF"),c.yb({encapsulation:2,styles:["@keyframes mat-checkbox-fade-in-background{0%{opacity:0}50%{opacity:1}}@keyframes mat-checkbox-fade-out-background{0%,50%{opacity:1}100%{opacity:0}}@keyframes mat-checkbox-unchecked-checked-checkmark-path{0%,50%{stroke-dashoffset:22.910259}50%{animation-timing-function:cubic-bezier(0, 0, 0.2, 0.1)}100%{stroke-dashoffset:0}}@keyframes mat-checkbox-unchecked-indeterminate-mixedmark{0%,68.2%{transform:scaleX(0)}68.2%{animation-timing-function:cubic-bezier(0, 0, 0, 1)}100%{transform:scaleX(1)}}@keyframes mat-checkbox-checked-unchecked-checkmark-path{from{animation-timing-function:cubic-bezier(0.4, 0, 1, 1);stroke-dashoffset:0}to{stroke-dashoffset:-22.910259}}@keyframes mat-checkbox-checked-indeterminate-checkmark{from{animation-timing-function:cubic-bezier(0, 0, 0.2, 0.1);opacity:1;transform:rotate(0deg)}to{opacity:0;transform:rotate(45deg)}}@keyframes mat-checkbox-indeterminate-checked-checkmark{from{animation-timing-function:cubic-bezier(0.14, 0, 0, 1);opacity:0;transform:rotate(45deg)}to{opacity:1;transform:rotate(360deg)}}@keyframes mat-checkbox-checked-indeterminate-mixedmark{from{animation-timing-function:cubic-bezier(0, 0, 0.2, 0.1);opacity:0;transform:rotate(-45deg)}to{opacity:1;transform:rotate(0deg)}}@keyframes mat-checkbox-indeterminate-checked-mixedmark{from{animation-timing-function:cubic-bezier(0.14, 0, 0, 1);opacity:1;transform:rotate(0deg)}to{opacity:0;transform:rotate(315deg)}}@keyframes mat-checkbox-indeterminate-unchecked-mixedmark{0%{animation-timing-function:linear;opacity:1;transform:scaleX(1)}32.8%,100%{opacity:0;transform:scaleX(0)}}.mat-checkbox-background,.mat-checkbox-frame{top:0;left:0;right:0;bottom:0;position:absolute;border-radius:2px;box-sizing:border-box;pointer-events:none}.mat-checkbox{display:inline-block;transition:background 400ms cubic-bezier(0.25, 0.8, 0.25, 1),box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);cursor:pointer;-webkit-tap-highlight-color:transparent}._mat-animation-noopable.mat-checkbox{transition:none;animation:none}.mat-checkbox .mat-ripple-element:not(.mat-checkbox-persistent-ripple){opacity:.16}.mat-checkbox-layout{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:inherit;align-items:baseline;vertical-align:middle;display:inline-flex;white-space:nowrap}.mat-checkbox-label{-webkit-user-select:auto;-moz-user-select:auto;-ms-user-select:auto;user-select:auto}.mat-checkbox-inner-container{display:inline-block;height:16px;line-height:0;margin:auto;margin-right:8px;order:0;position:relative;vertical-align:middle;white-space:nowrap;width:16px;flex-shrink:0}[dir=rtl] .mat-checkbox-inner-container{margin-left:8px;margin-right:auto}.mat-checkbox-inner-container-no-side-margin{margin-left:0;margin-right:0}.mat-checkbox-frame{background-color:transparent;transition:border-color 90ms cubic-bezier(0, 0, 0.2, 0.1);border-width:2px;border-style:solid}._mat-animation-noopable .mat-checkbox-frame{transition:none}.cdk-high-contrast-active .mat-checkbox.cdk-keyboard-focused .mat-checkbox-frame{border-style:dotted}.mat-checkbox-background{align-items:center;display:inline-flex;justify-content:center;transition:background-color 90ms cubic-bezier(0, 0, 0.2, 0.1),opacity 90ms cubic-bezier(0, 0, 0.2, 0.1)}._mat-animation-noopable .mat-checkbox-background{transition:none}.cdk-high-contrast-active .mat-checkbox .mat-checkbox-background{background:none}.mat-checkbox-persistent-ripple{display:block;width:100%;height:100%;transform:none}.mat-checkbox-inner-container:hover .mat-checkbox-persistent-ripple{opacity:.04}.mat-checkbox.cdk-keyboard-focused .mat-checkbox-persistent-ripple{opacity:.12}.mat-checkbox-persistent-ripple,.mat-checkbox.mat-checkbox-disabled .mat-checkbox-inner-container:hover .mat-checkbox-persistent-ripple{opacity:0}@media(hover: none){.mat-checkbox-inner-container:hover .mat-checkbox-persistent-ripple{display:none}}.mat-checkbox-checkmark{top:0;left:0;right:0;bottom:0;position:absolute;width:100%}.mat-checkbox-checkmark-path{stroke-dashoffset:22.910259;stroke-dasharray:22.910259;stroke-width:2.1333333333px}.cdk-high-contrast-black-on-white .mat-checkbox-checkmark-path{stroke:#000 !important}.mat-checkbox-mixedmark{width:calc(100% - 6px);height:2px;opacity:0;transform:scaleX(0) rotate(0deg);border-radius:2px}.cdk-high-contrast-active .mat-checkbox-mixedmark{height:0;border-top:solid 2px;margin-top:2px}.mat-checkbox-label-before .mat-checkbox-inner-container{order:1;margin-left:8px;margin-right:auto}[dir=rtl] .mat-checkbox-label-before .mat-checkbox-inner-container{margin-left:auto;margin-right:8px}.mat-checkbox-checked .mat-checkbox-checkmark{opacity:1}.mat-checkbox-checked .mat-checkbox-checkmark-path{stroke-dashoffset:0}.mat-checkbox-checked .mat-checkbox-mixedmark{transform:scaleX(1) rotate(-45deg)}.mat-checkbox-indeterminate .mat-checkbox-checkmark{opacity:0;transform:rotate(45deg)}.mat-checkbox-indeterminate .mat-checkbox-checkmark-path{stroke-dashoffset:0}.mat-checkbox-indeterminate .mat-checkbox-mixedmark{opacity:1;transform:scaleX(1) rotate(0deg)}.mat-checkbox-unchecked .mat-checkbox-background{background-color:transparent}.mat-checkbox-disabled{cursor:default}.cdk-high-contrast-active .mat-checkbox-disabled{opacity:.5}.mat-checkbox-anim-unchecked-checked .mat-checkbox-background{animation:180ms linear 0ms mat-checkbox-fade-in-background}.mat-checkbox-anim-unchecked-checked .mat-checkbox-checkmark-path{animation:180ms linear 0ms mat-checkbox-unchecked-checked-checkmark-path}.mat-checkbox-anim-unchecked-indeterminate .mat-checkbox-background{animation:180ms linear 0ms mat-checkbox-fade-in-background}.mat-checkbox-anim-unchecked-indeterminate .mat-checkbox-mixedmark{animation:90ms linear 0ms mat-checkbox-unchecked-indeterminate-mixedmark}.mat-checkbox-anim-checked-unchecked .mat-checkbox-background{animation:180ms linear 0ms mat-checkbox-fade-out-background}.mat-checkbox-anim-checked-unchecked .mat-checkbox-checkmark-path{animation:90ms linear 0ms mat-checkbox-checked-unchecked-checkmark-path}.mat-checkbox-anim-checked-indeterminate .mat-checkbox-checkmark{animation:90ms linear 0ms mat-checkbox-checked-indeterminate-checkmark}.mat-checkbox-anim-checked-indeterminate .mat-checkbox-mixedmark{animation:90ms linear 0ms mat-checkbox-checked-indeterminate-mixedmark}.mat-checkbox-anim-indeterminate-checked .mat-checkbox-checkmark{animation:500ms linear 0ms mat-checkbox-indeterminate-checked-checkmark}.mat-checkbox-anim-indeterminate-checked .mat-checkbox-mixedmark{animation:500ms linear 0ms mat-checkbox-indeterminate-checked-mixedmark}.mat-checkbox-anim-indeterminate-unchecked .mat-checkbox-background{animation:180ms linear 0ms mat-checkbox-fade-out-background}.mat-checkbox-anim-indeterminate-unchecked .mat-checkbox-mixedmark{animation:300ms linear 0ms mat-checkbox-indeterminate-unchecked-mixedmark}.mat-checkbox-input{bottom:0;left:50%}.mat-checkbox .mat-checkbox-ripple{position:absolute;left:calc(50% - 20px);top:calc(50% - 20px);height:40px;width:40px;z-index:1;pointer-events:none}\n"],data:{}}));function h(e){return c.bc(2,[c.Ub(671088640,1,{_inputElement:0}),c.Ub(671088640,2,{ripple:0}),(e()(),c.Ab(2,0,[["label",1]],null,16,"label",[["class","mat-checkbox-layout"]],[[1,"for",0]],null,null,null,null)),(e()(),c.Ab(3,0,null,null,10,"span",[["class","mat-checkbox-inner-container"]],[[2,"mat-checkbox-inner-container-no-side-margin",null]],null,null,null,null)),(e()(),c.Ab(4,0,[[1,0],["input",1]],null,0,"input",[["class","mat-checkbox-input cdk-visually-hidden"],["type","checkbox"]],[[8,"id",0],[8,"required",0],[8,"checked",0],[1,"value",0],[8,"disabled",0],[1,"name",0],[8,"tabIndex",0],[1,"aria-label",0],[1,"aria-labelledby",0],[1,"aria-checked",0],[1,"aria-describedby",0]],[[null,"change"],[null,"click"]],function(e,t,n){var c=!0,i=e.component;return"change"===t&&(c=!1!==i._onInteractionEvent(n)&&c),"click"===t&&(c=!1!==i._onInputClick(n)&&c),c},null,null)),(e()(),c.Ab(5,0,null,null,3,"span",[["class","mat-checkbox-ripple mat-focus-indicator mat-ripple"],["matRipple",""]],[[2,"mat-ripple-unbounded",null]],null,null,null,null)),c.zb(6,212992,[[2,4]],0,a.v,[c.l,c.B,o.a,[2,a.k],[2,r.a]],{centered:[0,"centered"],radius:[1,"radius"],animation:[2,"animation"],disabled:[3,"disabled"],trigger:[4,"trigger"]},null),c.Rb(7,{enterDuration:0}),(e()(),c.Ab(8,0,null,null,0,"span",[["class","mat-ripple-element mat-checkbox-persistent-ripple"]],null,null,null,null,null)),(e()(),c.Ab(9,0,null,null,0,"span",[["class","mat-checkbox-frame"]],null,null,null,null,null)),(e()(),c.Ab(10,0,null,null,3,"span",[["class","mat-checkbox-background"]],null,null,null,null,null)),(e()(),c.Ab(11,0,null,null,1,":svg:svg",[[":xml:space","preserve"],["class","mat-checkbox-checkmark"],["focusable","false"],["version","1.1"],["viewBox","0 0 24 24"]],null,null,null,null,null)),(e()(),c.Ab(12,0,null,null,0,":svg:path",[["class","mat-checkbox-checkmark-path"],["d","M4.1,12.7 9,17.6 20.3,6.3"],["fill","none"],["stroke","white"]],null,null,null,null,null)),(e()(),c.Ab(13,0,null,null,0,"span",[["class","mat-checkbox-mixedmark"]],null,null,null,null,null)),(e()(),c.Ab(14,0,[["checkboxLabel",1]],null,4,"span",[["class","mat-checkbox-label"]],null,[[null,"cdkObserveContent"]],function(e,t,n){var c=!0;return"cdkObserveContent"===t&&(c=!1!==e.component._onLabelTextChange()&&c),c},null,null)),c.zb(15,1196032,null,0,i.a,[i.b,c.l,c.B],null,{event:"cdkObserveContent"}),(e()(),c.Ab(16,0,null,null,1,"span",[["style","display:none"]],null,null,null,null,null)),(e()(),c.Yb(-1,null,["\xa0"])),c.Nb(null,0)],function(e,t){var n=t.component,i=e(t,7,0,150);e(t,6,0,!0,20,i,n._isRippleDisabled(),c.Ob(t,2))},function(e,t){var n=t.component;e(t,2,0,n.inputId),e(t,3,0,!c.Ob(t,14).textContent||!c.Ob(t,14).textContent.trim()),e(t,4,1,[n.inputId,n.required,n.checked,n.value,n.disabled,n.name,n.tabIndex,n.ariaLabel||null,n.ariaLabelledby,n._getAriaChecked(),n.ariaDescribedby]),e(t,5,0,c.Ob(t,6).unbounded)})}}}]);