hardpy 0.11.0__tar.gz → 0.11.2__tar.gz
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.
- {hardpy-0.11.0 → hardpy-0.11.2}/PKG-INFO +1 -1
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/__init__.py +8 -2
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/cli/cli.py +91 -1
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/common/config.py +6 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/common/stand_cloud/__init__.py +2 -1
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/common/stand_cloud/connector.py +32 -38
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/api.py +24 -2
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/asset-manifest.json +3 -3
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/index.html +1 -1
- hardpy-0.11.2/hardpy/hardpy_panel/frontend/dist/static/js/main.fb8b84a3.js +3 -0
- hardpy-0.11.2/hardpy/hardpy_panel/frontend/dist/static/js/main.fb8b84a3.js.map +1 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/plugin.py +23 -8
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/pytest_wrapper.py +14 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/result/__init__.py +4 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/result/couchdb_config.py +6 -8
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/result/report_loader/stand_cloud_loader.py +6 -9
- hardpy-0.11.2/hardpy/pytest_hardpy/result/report_reader/stand_cloud_reader.py +84 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/utils/dialog_box.py +2 -2
- {hardpy-0.11.0 → hardpy-0.11.2}/pyproject.toml +1 -1
- hardpy-0.11.0/hardpy/hardpy_panel/frontend/dist/static/js/main.114c5914.js +0 -3
- hardpy-0.11.0/hardpy/hardpy_panel/frontend/dist/static/js/main.114c5914.js.map +0 -1
- {hardpy-0.11.0 → hardpy-0.11.2}/.gitignore +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/LICENSE +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/README.md +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/cli/__init__.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/cli/template.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/common/__init__.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/common/stand_cloud/exception.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/common/stand_cloud/oauth_callback.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/common/stand_cloud/registration.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/common/stand_cloud/token_storage.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/__init__.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/favicon.ico +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/logo192.png +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/logo512.png +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/manifest.json +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/css/main.e8a862f1.css +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/css/main.e8a862f1.css.map +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/js/808.ce070002.chunk.js +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/js/808.ce070002.chunk.js.map +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-16px-paths.d605910e.chunk.js +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-16px-paths.d605910e.chunk.js.map +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-20px-paths.7ee05cc8.chunk.js +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-20px-paths.7ee05cc8.chunk.js.map +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-all-paths-loader.0aa89747.chunk.js +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-all-paths-loader.0aa89747.chunk.js.map +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-all-paths.f63155c9.chunk.js +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-all-paths.f63155c9.chunk.js.map +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-split-paths-by-size-loader.52a072d3.chunk.js +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-split-paths-by-size-loader.52a072d3.chunk.js.map +0 -0
- /hardpy-0.11.0/hardpy/hardpy_panel/frontend/dist/static/js/main.114c5914.js.LICENSE.txt → /hardpy-0.11.2/hardpy/hardpy_panel/frontend/dist/static/js/main.fb8b84a3.js.LICENSE.txt +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.520846c6beb41df528c8.eot +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.5c52b39c697f2323ce8b.svg +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.84db1772f4bfb529f64f.woff +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.b67ee1736e20e37a3225.woff2 +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.e02ecf515378db143652.ttf +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.429cacb8accf72488451.ttf +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.6ae3791ee2d86fc228a6.svg +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.8cecf62de42997e4d82f.woff2 +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.afbadb627d43b7857223.eot +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.e857f5a5132b8bfa71a1.woff +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/hardpy_panel/frontend/dist/static/media/logo_smol.5b16f92447a4a9e80331.png +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/__init__.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/db/__init__.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/db/base_connector.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/db/base_server.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/db/base_store.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/db/const.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/db/runstore.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/db/schema/__init__.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/db/schema/v1.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/db/statestore.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/pytest_call.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/reporter/__init__.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/reporter/base.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/reporter/hook_reporter.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/reporter/runner_reporter.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/result/report_loader/__init__.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/result/report_loader/couchdb_loader.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/result/report_reader/__init__.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/result/report_reader/couchdb_reader.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/utils/__init__.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/utils/connection_data.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/utils/const.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/utils/exception.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/utils/machineid.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/utils/node_info.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/utils/progress_calculator.py +0 -0
- {hardpy-0.11.0 → hardpy-0.11.2}/hardpy/pytest_hardpy/utils/singleton.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Copyright (c) 2024 Everypin
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
3
|
|
|
4
|
-
from hardpy.common.stand_cloud import StandCloudError
|
|
4
|
+
from hardpy.common.stand_cloud import StandCloudConnector, StandCloudError
|
|
5
5
|
from hardpy.pytest_hardpy.pytest_call import (
|
|
6
6
|
clear_operator_message,
|
|
7
7
|
get_current_attempt,
|
|
@@ -20,7 +20,11 @@ from hardpy.pytest_hardpy.pytest_call import (
|
|
|
20
20
|
set_stand_location,
|
|
21
21
|
set_stand_name,
|
|
22
22
|
)
|
|
23
|
-
from hardpy.pytest_hardpy.result import
|
|
23
|
+
from hardpy.pytest_hardpy.result import (
|
|
24
|
+
CouchdbLoader,
|
|
25
|
+
StandCloudLoader,
|
|
26
|
+
StandCloudReader,
|
|
27
|
+
)
|
|
24
28
|
from hardpy.pytest_hardpy.result.couchdb_config import CouchdbConfig
|
|
25
29
|
from hardpy.pytest_hardpy.utils import (
|
|
26
30
|
BaseWidget,
|
|
@@ -54,8 +58,10 @@ __all__ = [
|
|
|
54
58
|
"MultistepWidget",
|
|
55
59
|
"NumericInputWidget",
|
|
56
60
|
"RadiobuttonWidget",
|
|
61
|
+
"StandCloudConnector",
|
|
57
62
|
"StandCloudError",
|
|
58
63
|
"StandCloudLoader",
|
|
64
|
+
"StandCloudReader",
|
|
59
65
|
"StepWidget",
|
|
60
66
|
"TextInputWidget",
|
|
61
67
|
"clear_operator_message",
|
|
@@ -6,11 +6,12 @@ import sys
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Annotated, Optional
|
|
8
8
|
|
|
9
|
+
import requests
|
|
9
10
|
import typer
|
|
10
11
|
from uvicorn import run as uvicorn_run
|
|
11
12
|
|
|
12
13
|
from hardpy.cli.template import TemplateGenerator
|
|
13
|
-
from hardpy.common.config import ConfigManager
|
|
14
|
+
from hardpy.common.config import ConfigManager, HardpyConfig
|
|
14
15
|
from hardpy.common.stand_cloud import (
|
|
15
16
|
StandCloudConnector,
|
|
16
17
|
StandCloudError,
|
|
@@ -31,6 +32,10 @@ default_config = ConfigManager().get_config()
|
|
|
31
32
|
@cli.command()
|
|
32
33
|
def init( # noqa: PLR0913
|
|
33
34
|
tests_dir: Annotated[Optional[str], typer.Argument()] = None,
|
|
35
|
+
tests_name: str = typer.Option(
|
|
36
|
+
default="",
|
|
37
|
+
help="Specify a tests suite name.",
|
|
38
|
+
),
|
|
34
39
|
create_database: bool = typer.Option(
|
|
35
40
|
default=True,
|
|
36
41
|
help="Create CouchDB database.",
|
|
@@ -72,6 +77,7 @@ def init( # noqa: PLR0913
|
|
|
72
77
|
|
|
73
78
|
Args:
|
|
74
79
|
tests_dir (str | None): Tests directory. Current directory + `tests` by default
|
|
80
|
+
tests_name (str): Tests suite name, "Tests" by default
|
|
75
81
|
create_database (bool): Flag to create database
|
|
76
82
|
database_user (str): Database user name
|
|
77
83
|
database_password (str): Database password
|
|
@@ -83,8 +89,10 @@ def init( # noqa: PLR0913
|
|
|
83
89
|
sc_connection_only (bool): Flag to check StandCloud service availability
|
|
84
90
|
"""
|
|
85
91
|
_tests_dir = tests_dir if tests_dir else default_config.tests_dir
|
|
92
|
+
_tests_name = tests_name if tests_name else default_config.tests_name
|
|
86
93
|
ConfigManager().init_config(
|
|
87
94
|
tests_dir=str(_tests_dir),
|
|
95
|
+
tests_name=_tests_name,
|
|
88
96
|
database_user=database_user,
|
|
89
97
|
database_password=database_password,
|
|
90
98
|
database_host=database_host,
|
|
@@ -152,6 +160,48 @@ def run(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
|
|
|
152
160
|
)
|
|
153
161
|
|
|
154
162
|
|
|
163
|
+
@cli.command()
|
|
164
|
+
def start(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
|
|
165
|
+
"""Start HardPy tests.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
tests_dir (Optional[str]): Test directory. Current directory by default
|
|
169
|
+
"""
|
|
170
|
+
config = _get_config(tests_dir)
|
|
171
|
+
_check_config(config)
|
|
172
|
+
|
|
173
|
+
url = f"http://{config.frontend.host}:{config.frontend.port}/api/start"
|
|
174
|
+
_request_hardpy(url)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@cli.command()
|
|
178
|
+
def stop(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
|
|
179
|
+
"""Stop HardPy tests.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
tests_dir (Optional[str]): Test directory. Current directory by default
|
|
183
|
+
"""
|
|
184
|
+
config = _get_config(tests_dir)
|
|
185
|
+
_check_config(config)
|
|
186
|
+
|
|
187
|
+
url = f"http://{config.frontend.host}:{config.frontend.port}/api/stop"
|
|
188
|
+
_request_hardpy(url)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@cli.command()
|
|
192
|
+
def status(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
|
|
193
|
+
"""Get HardPy test launch status.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
tests_dir (Optional[str]): Test directory. Current directory by default
|
|
197
|
+
"""
|
|
198
|
+
config = _get_config(tests_dir)
|
|
199
|
+
_check_config(config)
|
|
200
|
+
|
|
201
|
+
url = f"http://{config.frontend.host}:{config.frontend.port}/api/status"
|
|
202
|
+
_request_hardpy(url)
|
|
203
|
+
|
|
204
|
+
|
|
155
205
|
@cli.command()
|
|
156
206
|
def sc_login(
|
|
157
207
|
address: Annotated[str, typer.Argument()],
|
|
@@ -195,5 +245,45 @@ def sc_logout() -> None:
|
|
|
195
245
|
print("HardPy logout failed")
|
|
196
246
|
|
|
197
247
|
|
|
248
|
+
def _get_config(tests_dir: str | None = None) -> HardpyConfig:
|
|
249
|
+
dir_path = Path.cwd() / tests_dir if tests_dir else Path.cwd()
|
|
250
|
+
config = ConfigManager().read_config(dir_path)
|
|
251
|
+
|
|
252
|
+
if not config:
|
|
253
|
+
print(f"Config at path {dir_path} not found.")
|
|
254
|
+
sys.exit()
|
|
255
|
+
|
|
256
|
+
return config
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _check_config(config: HardpyConfig) -> None:
|
|
260
|
+
url = f"http://{config.frontend.host}:{config.frontend.port}/api/hardpy_config"
|
|
261
|
+
error_msg = f"HardPy in directory {config.tests_dir} does not run."
|
|
262
|
+
try:
|
|
263
|
+
response = requests.get(url, timeout=2)
|
|
264
|
+
except Exception:
|
|
265
|
+
print(error_msg)
|
|
266
|
+
sys.exit()
|
|
267
|
+
|
|
268
|
+
running_config: dict = response.json()
|
|
269
|
+
if config.model_dump() != running_config:
|
|
270
|
+
print(error_msg)
|
|
271
|
+
sys.exit()
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _request_hardpy(url: str) -> None:
|
|
275
|
+
try:
|
|
276
|
+
response = requests.get(url, timeout=2)
|
|
277
|
+
except Exception:
|
|
278
|
+
print("HardPy operator panel is not running.")
|
|
279
|
+
sys.exit()
|
|
280
|
+
try:
|
|
281
|
+
status: dict = response.json().get("status", "ERROR")
|
|
282
|
+
except ValueError:
|
|
283
|
+
print(f"Hardpy internal error: {response}.")
|
|
284
|
+
sys.exit()
|
|
285
|
+
print(f"HardPy status: {status}.")
|
|
286
|
+
|
|
287
|
+
|
|
198
288
|
if __name__ == "__main__":
|
|
199
289
|
cli()
|
|
@@ -57,6 +57,7 @@ class HardpyConfig(BaseModel, extra="allow"):
|
|
|
57
57
|
|
|
58
58
|
title: str = "HardPy TOML config"
|
|
59
59
|
tests_dir: str = "tests"
|
|
60
|
+
tests_name: str = ""
|
|
60
61
|
database: DatabaseConfig = DatabaseConfig()
|
|
61
62
|
frontend: FrontendConfig = FrontendConfig()
|
|
62
63
|
stand_cloud: StandCloudConfig = StandCloudConfig()
|
|
@@ -72,6 +73,7 @@ class ConfigManager:
|
|
|
72
73
|
def init_config( # noqa: PLR0913
|
|
73
74
|
cls,
|
|
74
75
|
tests_dir: str,
|
|
76
|
+
tests_name: str,
|
|
75
77
|
database_user: str,
|
|
76
78
|
database_password: str,
|
|
77
79
|
database_host: str,
|
|
@@ -85,6 +87,7 @@ class ConfigManager:
|
|
|
85
87
|
|
|
86
88
|
Args:
|
|
87
89
|
tests_dir (str): Tests directory.
|
|
90
|
+
tests_name (str): Tests suite name.
|
|
88
91
|
database_user (str): Database user name.
|
|
89
92
|
database_password (str): Database password.
|
|
90
93
|
database_host (str): Database host.
|
|
@@ -95,6 +98,7 @@ class ConfigManager:
|
|
|
95
98
|
sc_connection_only (bool): StandCloud check availability.
|
|
96
99
|
"""
|
|
97
100
|
cls.obj.tests_dir = str(tests_dir)
|
|
101
|
+
cls.obj.tests_name = tests_name
|
|
98
102
|
cls.obj.database.user = database_user
|
|
99
103
|
cls.obj.database.password = database_password
|
|
100
104
|
cls.obj.database.host = database_host
|
|
@@ -113,6 +117,8 @@ class ConfigManager:
|
|
|
113
117
|
"""
|
|
114
118
|
if not cls.obj.stand_cloud.address:
|
|
115
119
|
del cls.obj.stand_cloud
|
|
120
|
+
if not cls.obj.tests_name:
|
|
121
|
+
del cls.obj.tests_name
|
|
116
122
|
config_str = tomli_w.dumps(cls.obj.model_dump())
|
|
117
123
|
with Path.open(parent_dir / "hardpy.toml", "w") as file:
|
|
118
124
|
file.write(config_str)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# Copyright (c) 2024 Everypin
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
3
|
|
|
4
|
-
from hardpy.common.stand_cloud.connector import StandCloudConnector
|
|
4
|
+
from hardpy.common.stand_cloud.connector import StandCloudAPIMode, StandCloudConnector
|
|
5
5
|
from hardpy.common.stand_cloud.exception import StandCloudError
|
|
6
6
|
from hardpy.common.stand_cloud.registration import login, logout
|
|
7
7
|
|
|
8
8
|
__all__ = [
|
|
9
|
+
"StandCloudAPIMode",
|
|
9
10
|
"StandCloudConnector",
|
|
10
11
|
"StandCloudError",
|
|
11
12
|
"login",
|
|
@@ -4,19 +4,12 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
from datetime import datetime, timedelta, timezone
|
|
7
|
+
from enum import Enum
|
|
7
8
|
from logging import getLogger
|
|
8
9
|
from typing import TYPE_CHECKING, NamedTuple
|
|
9
10
|
|
|
10
|
-
from oauthlib.oauth2.rfc6749.errors import
|
|
11
|
-
|
|
12
|
-
MissingTokenError,
|
|
13
|
-
TokenExpiredError,
|
|
14
|
-
)
|
|
15
|
-
from requests.exceptions import (
|
|
16
|
-
ConnectionError as RequestConnectionError,
|
|
17
|
-
HTTPError,
|
|
18
|
-
InvalidURL,
|
|
19
|
-
)
|
|
11
|
+
from oauthlib.oauth2.rfc6749.errors import OAuth2Error
|
|
12
|
+
from requests.exceptions import RequestException
|
|
20
13
|
from requests_oauth2client import ApiClient, BearerToken
|
|
21
14
|
from requests_oauth2client.tokens import ExpiredAccessToken
|
|
22
15
|
from requests_oauthlib import OAuth2Session
|
|
@@ -42,24 +35,41 @@ class StandCloudURL(NamedTuple):
|
|
|
42
35
|
par: str
|
|
43
36
|
auth: str
|
|
44
37
|
|
|
38
|
+
|
|
39
|
+
class StandCloudAPIMode(str, Enum):
|
|
40
|
+
"""StandCloud API mode.
|
|
41
|
+
|
|
42
|
+
HARDPY for test stand, integration for third-party service.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
HARDPY = "hardpy"
|
|
46
|
+
INTEGRATION = "integration"
|
|
47
|
+
|
|
48
|
+
|
|
45
49
|
class StandCloudConnector:
|
|
46
50
|
"""StandCloud API connector."""
|
|
47
51
|
|
|
48
52
|
def __init__(
|
|
49
53
|
self,
|
|
50
54
|
addr: str,
|
|
55
|
+
api_mode: StandCloudAPIMode = StandCloudAPIMode.HARDPY,
|
|
56
|
+
api_version: int = 1,
|
|
51
57
|
) -> None:
|
|
52
58
|
"""Create StandCLoud loader.
|
|
53
59
|
|
|
54
60
|
Args:
|
|
55
|
-
addr (str
|
|
56
|
-
|
|
61
|
+
addr (str): StandCloud service name.
|
|
62
|
+
api_mode (StandCloudAPIMode): StandCloud API mode,
|
|
63
|
+
hardpy for test stand, integration for third-party service.
|
|
64
|
+
Default: StandCloudAPIMode.HARDPY.
|
|
65
|
+
api_version (int): StandCloud API version.
|
|
66
|
+
Default: 1.
|
|
57
67
|
"""
|
|
58
68
|
https_prefix = "https://"
|
|
59
69
|
auth_addr = addr + "/auth"
|
|
60
70
|
|
|
61
71
|
self._url: StandCloudURL = StandCloudURL(
|
|
62
|
-
api=https_prefix + addr +
|
|
72
|
+
api=https_prefix + addr + f"/{api_mode.value}/api/v{api_version}",
|
|
63
73
|
token=https_prefix + auth_addr + "/api/oidc/token",
|
|
64
74
|
par=https_prefix + auth_addr + "/api/oidc/pushed-authorization-request",
|
|
65
75
|
auth=https_prefix + auth_addr + "/api/oidc/authorization",
|
|
@@ -98,13 +108,11 @@ class StandCloudConnector:
|
|
|
98
108
|
try:
|
|
99
109
|
resp = api.get(verify=self._verify_ssl)
|
|
100
110
|
except ExpiredAccessToken as exc:
|
|
101
|
-
raise StandCloudError(str(exc))
|
|
102
|
-
except
|
|
103
|
-
raise StandCloudError(exc.description)
|
|
104
|
-
except
|
|
105
|
-
raise StandCloudError(exc.
|
|
106
|
-
except HTTPError as exc:
|
|
107
|
-
raise StandCloudError(exc.strerror) # type: ignore
|
|
111
|
+
raise StandCloudError(str(exc)) from exc
|
|
112
|
+
except OAuth2Error as exc:
|
|
113
|
+
raise StandCloudError(exc.description) from exc
|
|
114
|
+
except RequestException as exc:
|
|
115
|
+
raise StandCloudError(exc.strerror) from exc # type: ignore
|
|
108
116
|
|
|
109
117
|
return resp
|
|
110
118
|
|
|
@@ -192,15 +200,10 @@ class StandCloudConnector:
|
|
|
192
200
|
verify=False,
|
|
193
201
|
**extra,
|
|
194
202
|
)
|
|
195
|
-
except
|
|
196
|
-
raise StandCloudError(exc.description)
|
|
197
|
-
except
|
|
198
|
-
raise StandCloudError(exc.strerror) # type: ignore
|
|
199
|
-
except MissingTokenError as exc:
|
|
200
|
-
raise StandCloudError(exc.description)
|
|
201
|
-
except InvalidURL:
|
|
202
|
-
msg = "Authentication URL is not available"
|
|
203
|
-
raise StandCloudError(msg)
|
|
203
|
+
except OAuth2Error as exc:
|
|
204
|
+
raise StandCloudError(exc.description) from exc
|
|
205
|
+
except RequestException as exc:
|
|
206
|
+
raise StandCloudError(exc.strerror) from exc # type: ignore
|
|
204
207
|
self._token_update(ret) # type: ignore
|
|
205
208
|
|
|
206
209
|
return ApiClient(self._url.api + "/" + endpoint, session=session, timeout=10)
|
|
@@ -216,12 +219,3 @@ class StandCloudConnector:
|
|
|
216
219
|
expires_at=expires_at,
|
|
217
220
|
expires_in=expires_in,
|
|
218
221
|
)
|
|
219
|
-
|
|
220
|
-
def _get_service_name(self, addr: str) -> str:
|
|
221
|
-
addr_parts = addr.split(".")
|
|
222
|
-
number_of_parts = 3
|
|
223
|
-
service_position_in_address = 1
|
|
224
|
-
if isinstance(addr_parts, list) and len(addr_parts) >= number_of_parts:
|
|
225
|
-
return "/" + addr_parts[service_position_in_address]
|
|
226
|
-
msg = f"Invalid StandCloud address: {addr}"
|
|
227
|
-
raise StandCloudError(msg)
|
|
@@ -18,9 +18,9 @@ app.state.pytest_wrp = PyTestWrapper()
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class Status(str, Enum):
|
|
21
|
-
"""
|
|
21
|
+
"""HardPy status.
|
|
22
22
|
|
|
23
|
-
Statuses, that can be returned by HardPy
|
|
23
|
+
Statuses, that can be returned by HardPy API.
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
26
|
STOPPED = "stopped"
|
|
@@ -31,6 +31,16 @@ class Status(str, Enum):
|
|
|
31
31
|
ERROR = "error"
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
@app.get("/api/hardpy_config")
|
|
35
|
+
def hardpy_config() -> dict:
|
|
36
|
+
"""Get config of HardPy.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
dict: HardPy config
|
|
40
|
+
"""
|
|
41
|
+
return app.state.pytest_wrp.get_config()
|
|
42
|
+
|
|
43
|
+
|
|
34
44
|
@app.get("/api/start")
|
|
35
45
|
def start_pytest() -> dict:
|
|
36
46
|
"""Start pytest subprocess.
|
|
@@ -68,6 +78,18 @@ def collect_pytest() -> dict:
|
|
|
68
78
|
return {"status": Status.BUSY}
|
|
69
79
|
|
|
70
80
|
|
|
81
|
+
@app.get("/api/status")
|
|
82
|
+
def status() -> dict:
|
|
83
|
+
"""Get pytest subprocess status.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
dict[str, RunStatus]: run status
|
|
87
|
+
"""
|
|
88
|
+
is_running = app.state.pytest_wrp.is_running()
|
|
89
|
+
status = Status.BUSY if is_running else Status.READY
|
|
90
|
+
return {"status": status}
|
|
91
|
+
|
|
92
|
+
|
|
71
93
|
@app.get("/api/couch")
|
|
72
94
|
def couch_connection() -> dict:
|
|
73
95
|
"""Get couchdb connection string.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": {
|
|
3
3
|
"main.css": "/static/css/main.e8a862f1.css",
|
|
4
|
-
"main.js": "/static/js/main.
|
|
4
|
+
"main.js": "/static/js/main.fb8b84a3.js",
|
|
5
5
|
"blueprint-icons-all-paths-loader.js": "/static/js/blueprint-icons-all-paths-loader.0aa89747.chunk.js",
|
|
6
6
|
"blueprint-icons-split-paths-by-size-loader.js": "/static/js/blueprint-icons-split-paths-by-size-loader.52a072d3.chunk.js",
|
|
7
7
|
"static/js/808.ce070002.chunk.js": "/static/js/808.ce070002.chunk.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"static/media/logo_smol.png": "/static/media/logo_smol.5b16f92447a4a9e80331.png",
|
|
22
22
|
"index.html": "/index.html",
|
|
23
23
|
"main.e8a862f1.css.map": "/static/css/main.e8a862f1.css.map",
|
|
24
|
-
"main.
|
|
24
|
+
"main.fb8b84a3.js.map": "/static/js/main.fb8b84a3.js.map",
|
|
25
25
|
"blueprint-icons-all-paths-loader.0aa89747.chunk.js.map": "/static/js/blueprint-icons-all-paths-loader.0aa89747.chunk.js.map",
|
|
26
26
|
"blueprint-icons-split-paths-by-size-loader.52a072d3.chunk.js.map": "/static/js/blueprint-icons-split-paths-by-size-loader.52a072d3.chunk.js.map",
|
|
27
27
|
"808.ce070002.chunk.js.map": "/static/js/808.ce070002.chunk.js.map",
|
|
@@ -31,6 +31,6 @@
|
|
|
31
31
|
},
|
|
32
32
|
"entrypoints": [
|
|
33
33
|
"static/css/main.e8a862f1.css",
|
|
34
|
-
"static/js/main.
|
|
34
|
+
"static/js/main.fb8b84a3.js"
|
|
35
35
|
]
|
|
36
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>HardPy Operator Panel</title><script defer="defer" src="/static/js/main.
|
|
1
|
+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>HardPy Operator Panel</title><script defer="defer" src="/static/js/main.fb8b84a3.js"></script><link href="/static/css/main.e8a862f1.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|