unitysvc-services 0.1.4__py3-none-any.whl → 0.1.6__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.
- unitysvc_services/api.py +58 -15
- unitysvc_services/cli.py +2 -1
- unitysvc_services/populate.py +18 -0
- unitysvc_services/publisher.py +379 -199
- unitysvc_services/query.py +310 -302
- unitysvc_services/test.py +796 -0
- unitysvc_services/utils.py +118 -2
- unitysvc_services/validator.py +19 -7
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.6.dist-info}/METADATA +40 -33
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.6.dist-info}/RECORD +14 -13
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.6.dist-info}/entry_points.txt +1 -0
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.6.dist-info}/WHEEL +0 -0
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.6.dist-info}/licenses/LICENSE +0 -0
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.6.dist-info}/top_level.txt +0 -0
unitysvc_services/api.py
CHANGED
@@ -62,7 +62,8 @@ class UnitySvcAPI:
|
|
62
62
|
JSON response as dictionary
|
63
63
|
|
64
64
|
Raises:
|
65
|
-
|
65
|
+
httpx.HTTPStatusError: If HTTP status code indicates error (with response details)
|
66
|
+
RuntimeError: If curl command fails or times out
|
66
67
|
"""
|
67
68
|
url = f"{self.base_url}{endpoint}"
|
68
69
|
if params:
|
@@ -71,7 +72,8 @@ class UnitySvcAPI:
|
|
71
72
|
cmd = [
|
72
73
|
"curl",
|
73
74
|
"-s", # Silent mode
|
74
|
-
"-
|
75
|
+
"-w",
|
76
|
+
"\n%{http_code}", # Write status code on new line
|
75
77
|
"-H",
|
76
78
|
f"X-API-Key: {self.api_key}",
|
77
79
|
"-H",
|
@@ -86,15 +88,34 @@ class UnitySvcAPI:
|
|
86
88
|
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=30.0)
|
87
89
|
|
88
90
|
if proc.returncode != 0:
|
89
|
-
error_msg = stderr.decode().strip() if stderr else "
|
90
|
-
raise RuntimeError(f"
|
91
|
+
error_msg = stderr.decode().strip() if stderr else "Curl command failed"
|
92
|
+
raise RuntimeError(f"Curl error: {error_msg}")
|
91
93
|
|
94
|
+
# Parse response: last line is status code, rest is body
|
92
95
|
output = stdout.decode().strip()
|
93
|
-
|
96
|
+
lines = output.split("\n")
|
97
|
+
status_code = int(lines[-1])
|
98
|
+
body = "\n".join(lines[:-1])
|
99
|
+
|
100
|
+
# Parse JSON response
|
101
|
+
try:
|
102
|
+
response_data = json.loads(body) if body else {}
|
103
|
+
except json.JSONDecodeError:
|
104
|
+
response_data = {"error": body}
|
105
|
+
|
106
|
+
# Raise exception for non-2xx status codes (mimics httpx behavior)
|
107
|
+
if status_code < 200 or status_code >= 300:
|
108
|
+
# Create a mock response object to raise HTTPStatusError
|
109
|
+
mock_request = httpx.Request("GET", url)
|
110
|
+
mock_response = httpx.Response(status_code=status_code, content=body.encode(), request=mock_request)
|
111
|
+
raise httpx.HTTPStatusError(f"HTTP {status_code}", request=mock_request, response=mock_response)
|
112
|
+
|
113
|
+
return response_data
|
94
114
|
except TimeoutError:
|
95
115
|
raise RuntimeError("Request timed out after 30 seconds")
|
96
|
-
except
|
97
|
-
raise
|
116
|
+
except httpx.HTTPStatusError:
|
117
|
+
# Re-raise HTTP errors as-is
|
118
|
+
raise
|
98
119
|
|
99
120
|
async def _make_post_request_curl(
|
100
121
|
self, endpoint: str, json_data: dict[str, Any] | None = None, params: dict[str, Any] | None = None
|
@@ -110,7 +131,8 @@ class UnitySvcAPI:
|
|
110
131
|
JSON response as dictionary
|
111
132
|
|
112
133
|
Raises:
|
113
|
-
|
134
|
+
httpx.HTTPStatusError: If HTTP status code indicates error (with response details)
|
135
|
+
RuntimeError: If curl command fails or times out
|
114
136
|
"""
|
115
137
|
url = f"{self.base_url}{endpoint}"
|
116
138
|
if params:
|
@@ -119,7 +141,8 @@ class UnitySvcAPI:
|
|
119
141
|
cmd = [
|
120
142
|
"curl",
|
121
143
|
"-s", # Silent mode
|
122
|
-
"-
|
144
|
+
"-w",
|
145
|
+
"\n%{http_code}", # Write status code on new line
|
123
146
|
"-X",
|
124
147
|
"POST",
|
125
148
|
"-H",
|
@@ -142,15 +165,34 @@ class UnitySvcAPI:
|
|
142
165
|
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=30.0)
|
143
166
|
|
144
167
|
if proc.returncode != 0:
|
145
|
-
error_msg = stderr.decode().strip() if stderr else "
|
146
|
-
raise RuntimeError(f"
|
168
|
+
error_msg = stderr.decode().strip() if stderr else "Curl command failed"
|
169
|
+
raise RuntimeError(f"Curl error: {error_msg}")
|
147
170
|
|
171
|
+
# Parse response: last line is status code, rest is body
|
148
172
|
output = stdout.decode().strip()
|
149
|
-
|
173
|
+
lines = output.split("\n")
|
174
|
+
status_code = int(lines[-1])
|
175
|
+
body = "\n".join(lines[:-1])
|
176
|
+
|
177
|
+
# Parse JSON response
|
178
|
+
try:
|
179
|
+
response_data = json.loads(body) if body else {}
|
180
|
+
except json.JSONDecodeError:
|
181
|
+
response_data = {"error": body}
|
182
|
+
|
183
|
+
# Raise exception for non-2xx status codes (mimics httpx behavior)
|
184
|
+
if status_code < 200 or status_code >= 300:
|
185
|
+
# Create a mock response object to raise HTTPStatusError
|
186
|
+
mock_request = httpx.Request("POST", url)
|
187
|
+
mock_response = httpx.Response(status_code=status_code, content=body.encode(), request=mock_request)
|
188
|
+
raise httpx.HTTPStatusError(f"HTTP {status_code}", request=mock_request, response=mock_response)
|
189
|
+
|
190
|
+
return response_data
|
150
191
|
except TimeoutError:
|
151
192
|
raise RuntimeError("Request timed out after 30 seconds")
|
152
|
-
except
|
153
|
-
raise
|
193
|
+
except httpx.HTTPStatusError:
|
194
|
+
# Re-raise HTTP errors as-is
|
195
|
+
raise
|
154
196
|
|
155
197
|
async def get(self, endpoint: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
156
198
|
"""Make a GET request to the backend API with automatic curl fallback.
|
@@ -246,8 +288,9 @@ class UnitySvcAPI:
|
|
246
288
|
raise ValueError(f"Task {task_id} timed out after {timeout}s")
|
247
289
|
|
248
290
|
# Check task status using get() with automatic curl fallback
|
291
|
+
# Use UnitySvcAPI.get to ensure we call the async version, not sync wrapper
|
249
292
|
try:
|
250
|
-
status = await
|
293
|
+
status = await UnitySvcAPI.get(self, f"/tasks/{task_id}")
|
251
294
|
except Exception:
|
252
295
|
# Network error while checking status - retry
|
253
296
|
await asyncio.sleep(poll_interval)
|
unitysvc_services/cli.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
import typer
|
4
4
|
|
5
|
-
from . import format_data, populate, publisher, query, scaffold, update, validator
|
5
|
+
from . import format_data, populate, publisher, query, scaffold, test, update, validator
|
6
6
|
from . import list as list_cmd
|
7
7
|
|
8
8
|
app = typer.Typer()
|
@@ -14,6 +14,7 @@ app.add_typer(list_cmd.app, name="list")
|
|
14
14
|
app.add_typer(query.app, name="query")
|
15
15
|
app.add_typer(publisher.app, name="publish")
|
16
16
|
app.add_typer(update.app, name="update")
|
17
|
+
app.add_typer(test.app, name="test")
|
17
18
|
|
18
19
|
# Register standalone commands at root level
|
19
20
|
app.command("format")(format_data.format_data)
|
unitysvc_services/populate.py
CHANGED
@@ -9,6 +9,7 @@ from pathlib import Path
|
|
9
9
|
import typer
|
10
10
|
from rich.console import Console
|
11
11
|
|
12
|
+
from .format_data import format_data
|
12
13
|
from .utils import find_files_by_schema
|
13
14
|
|
14
15
|
app = typer.Typer(help="Populate services")
|
@@ -38,6 +39,9 @@ def populate(
|
|
38
39
|
|
39
40
|
This command scans provider files for 'services_populator' configuration and executes
|
40
41
|
the specified commands with environment variables from 'provider_access_info'.
|
42
|
+
|
43
|
+
After successful execution, automatically runs formatting on all generated files to
|
44
|
+
ensure they conform to the format specification (equivalent to running 'usvc format').
|
41
45
|
"""
|
42
46
|
# Set data directory
|
43
47
|
if data_dir is None:
|
@@ -178,5 +182,19 @@ def populate(
|
|
178
182
|
console.print(f" [yellow]⏭️ Skipped: {total_skipped}[/yellow]")
|
179
183
|
console.print(f" [red]✗ Failed: {total_failed}[/red]")
|
180
184
|
|
185
|
+
# Format generated files if any populate scripts executed successfully
|
186
|
+
if total_executed > 0 and not dry_run:
|
187
|
+
console.print("\n" + "=" * 50)
|
188
|
+
console.print("[bold cyan]Formatting generated files...[/bold cyan]")
|
189
|
+
console.print("[dim]Running automatic formatting to ensure data conforms to format specification[/dim]\n")
|
190
|
+
|
191
|
+
try:
|
192
|
+
# Run format command on the data directory
|
193
|
+
format_data(data_dir)
|
194
|
+
console.print("\n[green]✓ Formatting completed successfully[/green]")
|
195
|
+
except Exception as e:
|
196
|
+
console.print(f"\n[yellow]⚠ Warning: Formatting failed: {e}[/yellow]")
|
197
|
+
console.print("[dim]You may want to run 'usvc format' manually to fix formatting issues[/dim]")
|
198
|
+
|
181
199
|
if total_failed > 0:
|
182
200
|
raise typer.Exit(code=1)
|