unitysvc-services 0.1.1__py3-none-any.whl → 0.2.0__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/format_data.py +2 -7
- unitysvc_services/list.py +14 -43
- unitysvc_services/models/base.py +34 -102
- unitysvc_services/models/listing_v1.py +3 -9
- unitysvc_services/models/provider_v1.py +2 -6
- unitysvc_services/models/seller_v1.py +2 -6
- unitysvc_services/populate.py +2 -6
- unitysvc_services/publisher.py +237 -162
- unitysvc_services/query.py +22 -120
- unitysvc_services/update.py +4 -13
- unitysvc_services/utils.py +2 -6
- unitysvc_services/validator.py +19 -56
- {unitysvc_services-0.1.1.dist-info → unitysvc_services-0.2.0.dist-info}/METADATA +37 -38
- unitysvc_services-0.2.0.dist-info/RECORD +23 -0
- unitysvc_services-0.1.1.dist-info/RECORD +0 -23
- {unitysvc_services-0.1.1.dist-info → unitysvc_services-0.2.0.dist-info}/WHEEL +0 -0
- {unitysvc_services-0.1.1.dist-info → unitysvc_services-0.2.0.dist-info}/entry_points.txt +0 -0
- {unitysvc_services-0.1.1.dist-info → unitysvc_services-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {unitysvc_services-0.1.1.dist-info → unitysvc_services-0.2.0.dist-info}/top_level.txt +0 -0
unitysvc_services/query.py
CHANGED
@@ -27,13 +27,9 @@ class ServiceDataQuery:
|
|
27
27
|
ValueError: If base_url or api_key is not provided
|
28
28
|
"""
|
29
29
|
if not base_url:
|
30
|
-
raise ValueError(
|
31
|
-
"Backend URL not provided. Use --backend-url or set UNITYSVC_BACKEND_URL env var."
|
32
|
-
)
|
30
|
+
raise ValueError("UNITYSVC_BASE_URL environment variable not set.")
|
33
31
|
if not api_key:
|
34
|
-
raise ValueError(
|
35
|
-
"API key not provided. Use --api-key or set UNITYSVC_API_KEY env var."
|
36
|
-
)
|
32
|
+
raise ValueError("UNITYSVC_API_KEY environment variable not set.")
|
37
33
|
|
38
34
|
self.base_url = base_url.rstrip("/")
|
39
35
|
self.api_key = api_key
|
@@ -98,40 +94,22 @@ class ServiceDataQuery:
|
|
98
94
|
self.close()
|
99
95
|
|
100
96
|
@staticmethod
|
101
|
-
def from_env(
|
102
|
-
|
103
|
-
) -> "ServiceDataQuery":
|
104
|
-
"""Create ServiceDataQuery from environment variables or arguments.
|
105
|
-
|
106
|
-
Args:
|
107
|
-
backend_url: Optional backend URL (falls back to UNITYSVC_BACKEND_URL env var)
|
108
|
-
api_key: Optional API key (falls back to UNITYSVC_API_KEY env var)
|
97
|
+
def from_env() -> "ServiceDataQuery":
|
98
|
+
"""Create ServiceDataQuery from environment variables.
|
109
99
|
|
110
100
|
Returns:
|
111
101
|
ServiceDataQuery instance
|
112
102
|
|
113
103
|
Raises:
|
114
|
-
ValueError: If required
|
104
|
+
ValueError: If required environment variables are not set
|
115
105
|
"""
|
116
|
-
|
117
|
-
|
118
|
-
return ServiceDataQuery(base_url=
|
106
|
+
backend_url = os.getenv("UNITYSVC_BASE_URL") or ""
|
107
|
+
api_key = os.getenv("UNITYSVC_API_KEY") or ""
|
108
|
+
return ServiceDataQuery(base_url=backend_url, api_key=api_key)
|
119
109
|
|
120
110
|
|
121
111
|
@app.command("sellers")
|
122
112
|
def query_sellers(
|
123
|
-
backend_url: str | None = typer.Option(
|
124
|
-
None,
|
125
|
-
"--backend-url",
|
126
|
-
"-u",
|
127
|
-
help="UnitySVC backend URL (default: from UNITYSVC_BACKEND_URL env var)",
|
128
|
-
),
|
129
|
-
api_key: str | None = typer.Option(
|
130
|
-
None,
|
131
|
-
"--api-key",
|
132
|
-
"-k",
|
133
|
-
help="API key for authentication (default: from UNITYSVC_API_KEY env var)",
|
134
|
-
),
|
135
113
|
format: str = typer.Option(
|
136
114
|
"table",
|
137
115
|
"--format",
|
@@ -141,7 +119,7 @@ def query_sellers(
|
|
141
119
|
):
|
142
120
|
"""Query all sellers from the backend."""
|
143
121
|
try:
|
144
|
-
with ServiceDataQuery.from_env(
|
122
|
+
with ServiceDataQuery.from_env() as query:
|
145
123
|
sellers = query.list_sellers()
|
146
124
|
|
147
125
|
if format == "json":
|
@@ -179,18 +157,6 @@ def query_sellers(
|
|
179
157
|
|
180
158
|
@app.command("providers")
|
181
159
|
def query_providers(
|
182
|
-
backend_url: str | None = typer.Option(
|
183
|
-
None,
|
184
|
-
"--backend-url",
|
185
|
-
"-u",
|
186
|
-
help="UnitySVC backend URL (default: from UNITYSVC_BACKEND_URL env var)",
|
187
|
-
),
|
188
|
-
api_key: str | None = typer.Option(
|
189
|
-
None,
|
190
|
-
"--api-key",
|
191
|
-
"-k",
|
192
|
-
help="API key for authentication (default: from UNITYSVC_API_KEY env var)",
|
193
|
-
),
|
194
160
|
format: str = typer.Option(
|
195
161
|
"table",
|
196
162
|
"--format",
|
@@ -200,7 +166,7 @@ def query_providers(
|
|
200
166
|
):
|
201
167
|
"""Query all providers from the backend."""
|
202
168
|
try:
|
203
|
-
with ServiceDataQuery.from_env(
|
169
|
+
with ServiceDataQuery.from_env() as query:
|
204
170
|
providers = query.list_providers()
|
205
171
|
|
206
172
|
if format == "json":
|
@@ -234,18 +200,6 @@ def query_providers(
|
|
234
200
|
|
235
201
|
@app.command("offerings")
|
236
202
|
def query_offerings(
|
237
|
-
backend_url: str | None = typer.Option(
|
238
|
-
None,
|
239
|
-
"--backend-url",
|
240
|
-
"-u",
|
241
|
-
help="UnitySVC backend URL (default: from UNITYSVC_BACKEND_URL env var)",
|
242
|
-
),
|
243
|
-
api_key: str | None = typer.Option(
|
244
|
-
None,
|
245
|
-
"--api-key",
|
246
|
-
"-k",
|
247
|
-
help="API key for authentication (default: from UNITYSVC_API_KEY env var)",
|
248
|
-
),
|
249
203
|
format: str = typer.Option(
|
250
204
|
"table",
|
251
205
|
"--format",
|
@@ -255,7 +209,7 @@ def query_offerings(
|
|
255
209
|
):
|
256
210
|
"""Query all service offerings from UnitySVC backend."""
|
257
211
|
try:
|
258
|
-
with ServiceDataQuery.from_env(
|
212
|
+
with ServiceDataQuery.from_env() as query:
|
259
213
|
offerings = query.list_service_offerings()
|
260
214
|
|
261
215
|
if format == "json":
|
@@ -283,33 +237,17 @@ def query_offerings(
|
|
283
237
|
)
|
284
238
|
|
285
239
|
console.print(table)
|
286
|
-
console.print(
|
287
|
-
f"\n[green]Total:[/green] {len(offerings)} service offering(s)"
|
288
|
-
)
|
240
|
+
console.print(f"\n[green]Total:[/green] {len(offerings)} service offering(s)")
|
289
241
|
except ValueError as e:
|
290
242
|
console.print(f"[red]✗[/red] {e}", style="bold red")
|
291
243
|
raise typer.Exit(code=1)
|
292
244
|
except Exception as e:
|
293
|
-
console.print(
|
294
|
-
f"[red]✗[/red] Failed to query service offerings: {e}", style="bold red"
|
295
|
-
)
|
245
|
+
console.print(f"[red]✗[/red] Failed to query service offerings: {e}", style="bold red")
|
296
246
|
raise typer.Exit(code=1)
|
297
247
|
|
298
248
|
|
299
249
|
@app.command("listings")
|
300
250
|
def query_listings(
|
301
|
-
backend_url: str | None = typer.Option(
|
302
|
-
None,
|
303
|
-
"--backend-url",
|
304
|
-
"-u",
|
305
|
-
help="UnitySVC backend URL (default: from UNITYSVC_BACKEND_URL env var)",
|
306
|
-
),
|
307
|
-
api_key: str | None = typer.Option(
|
308
|
-
None,
|
309
|
-
"--api-key",
|
310
|
-
"-k",
|
311
|
-
help="API key for authentication (default: from UNITYSVC_API_KEY env var)",
|
312
|
-
),
|
313
251
|
format: str = typer.Option(
|
314
252
|
"table",
|
315
253
|
"--format",
|
@@ -319,7 +257,7 @@ def query_listings(
|
|
319
257
|
):
|
320
258
|
"""Query all service listings from UnitySVC backend."""
|
321
259
|
try:
|
322
|
-
with ServiceDataQuery.from_env(
|
260
|
+
with ServiceDataQuery.from_env() as query:
|
323
261
|
listings = query.list_service_listings()
|
324
262
|
|
325
263
|
if format == "json":
|
@@ -336,9 +274,7 @@ def query_listings(
|
|
336
274
|
table.add_column("Interfaces")
|
337
275
|
|
338
276
|
for listing in listings:
|
339
|
-
interfaces_count = len(
|
340
|
-
listing.get("user_access_interfaces", [])
|
341
|
-
)
|
277
|
+
interfaces_count = len(listing.get("user_access_interfaces", []))
|
342
278
|
table.add_row(
|
343
279
|
str(listing.get("id", "N/A")),
|
344
280
|
str(listing.get("service_id", "N/A")),
|
@@ -348,33 +284,17 @@ def query_listings(
|
|
348
284
|
)
|
349
285
|
|
350
286
|
console.print(table)
|
351
|
-
console.print(
|
352
|
-
f"\n[green]Total:[/green] {len(listings)} service listing(s)"
|
353
|
-
)
|
287
|
+
console.print(f"\n[green]Total:[/green] {len(listings)} service listing(s)")
|
354
288
|
except ValueError as e:
|
355
289
|
console.print(f"[red]✗[/red] {e}", style="bold red")
|
356
290
|
raise typer.Exit(code=1)
|
357
291
|
except Exception as e:
|
358
|
-
console.print(
|
359
|
-
f"[red]✗[/red] Failed to query service listings: {e}", style="bold red"
|
360
|
-
)
|
292
|
+
console.print(f"[red]✗[/red] Failed to query service listings: {e}", style="bold red")
|
361
293
|
raise typer.Exit(code=1)
|
362
294
|
|
363
295
|
|
364
296
|
@app.command("interfaces")
|
365
297
|
def query_interfaces(
|
366
|
-
backend_url: str | None = typer.Option(
|
367
|
-
None,
|
368
|
-
"--backend-url",
|
369
|
-
"-u",
|
370
|
-
help="UnitySVC backend URL (default: from UNITYSVC_BACKEND_URL env var)",
|
371
|
-
),
|
372
|
-
api_key: str | None = typer.Option(
|
373
|
-
None,
|
374
|
-
"--api-key",
|
375
|
-
"-k",
|
376
|
-
help="API key for authentication (default: from UNITYSVC_API_KEY env var)",
|
377
|
-
),
|
378
298
|
format: str = typer.Option(
|
379
299
|
"table",
|
380
300
|
"--format",
|
@@ -384,7 +304,7 @@ def query_interfaces(
|
|
384
304
|
):
|
385
305
|
"""Query all access interfaces from UnitySVC backend (private endpoint)."""
|
386
306
|
try:
|
387
|
-
with ServiceDataQuery.from_env(
|
307
|
+
with ServiceDataQuery.from_env() as query:
|
388
308
|
data = query.list_access_interfaces()
|
389
309
|
|
390
310
|
if format == "json":
|
@@ -413,33 +333,17 @@ def query_interfaces(
|
|
413
333
|
)
|
414
334
|
|
415
335
|
console.print(table)
|
416
|
-
console.print(
|
417
|
-
f"\n[green]Total:[/green] {data.get('count', 0)} access interface(s)"
|
418
|
-
)
|
336
|
+
console.print(f"\n[green]Total:[/green] {data.get('count', 0)} access interface(s)")
|
419
337
|
except ValueError as e:
|
420
338
|
console.print(f"[red]✗[/red] {e}", style="bold red")
|
421
339
|
raise typer.Exit(code=1)
|
422
340
|
except Exception as e:
|
423
|
-
console.print(
|
424
|
-
f"[red]✗[/red] Failed to query access interfaces: {e}", style="bold red"
|
425
|
-
)
|
341
|
+
console.print(f"[red]✗[/red] Failed to query access interfaces: {e}", style="bold red")
|
426
342
|
raise typer.Exit(code=1)
|
427
343
|
|
428
344
|
|
429
345
|
@app.command("documents")
|
430
346
|
def query_documents(
|
431
|
-
backend_url: str | None = typer.Option(
|
432
|
-
None,
|
433
|
-
"--backend-url",
|
434
|
-
"-u",
|
435
|
-
help="UnitySVC backend URL (default: from UNITYSVC_BACKEND_URL env var)",
|
436
|
-
),
|
437
|
-
api_key: str | None = typer.Option(
|
438
|
-
None,
|
439
|
-
"--api-key",
|
440
|
-
"-k",
|
441
|
-
help="API key for authentication (default: from UNITYSVC_API_KEY env var)",
|
442
|
-
),
|
443
347
|
format: str = typer.Option(
|
444
348
|
"table",
|
445
349
|
"--format",
|
@@ -449,7 +353,7 @@ def query_documents(
|
|
449
353
|
):
|
450
354
|
"""Query all documents from UnitySVC backend (private endpoint)."""
|
451
355
|
try:
|
452
|
-
with ServiceDataQuery.from_env(
|
356
|
+
with ServiceDataQuery.from_env() as query:
|
453
357
|
data = query.list_documents()
|
454
358
|
|
455
359
|
if format == "json":
|
@@ -478,9 +382,7 @@ def query_documents(
|
|
478
382
|
)
|
479
383
|
|
480
384
|
console.print(table)
|
481
|
-
console.print(
|
482
|
-
f"\n[green]Total:[/green] {data.get('count', 0)} document(s)"
|
483
|
-
)
|
385
|
+
console.print(f"\n[green]Total:[/green] {data.get('count', 0)} document(s)")
|
484
386
|
except ValueError as e:
|
485
387
|
console.print(f"[red]✗[/red] {e}", style="bold red")
|
486
388
|
raise typer.Exit(code=1)
|
unitysvc_services/update.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
"""Update command group - update local data files."""
|
2
2
|
|
3
|
-
import os
|
4
3
|
from pathlib import Path
|
5
4
|
from typing import Any
|
6
5
|
|
@@ -46,7 +45,7 @@ def update_offering(
|
|
46
45
|
None,
|
47
46
|
"--data-dir",
|
48
47
|
"-d",
|
49
|
-
help="Directory containing data files (default:
|
48
|
+
help="Directory containing data files (default: current directory)",
|
50
49
|
),
|
51
50
|
):
|
52
51
|
"""
|
@@ -83,11 +82,7 @@ def update_offering(
|
|
83
82
|
|
84
83
|
# Set data directory
|
85
84
|
if data_dir is None:
|
86
|
-
|
87
|
-
if data_dir_str:
|
88
|
-
data_dir = Path(data_dir_str)
|
89
|
-
else:
|
90
|
-
data_dir = Path.cwd() / "data"
|
85
|
+
data_dir = Path.cwd()
|
91
86
|
|
92
87
|
if not data_dir.is_absolute():
|
93
88
|
data_dir = Path.cwd() / data_dir
|
@@ -181,7 +176,7 @@ def update_listing(
|
|
181
176
|
None,
|
182
177
|
"--data-dir",
|
183
178
|
"-d",
|
184
|
-
help="Directory containing data files (default:
|
179
|
+
help="Directory containing data files (default: current directory)",
|
185
180
|
),
|
186
181
|
):
|
187
182
|
"""
|
@@ -227,11 +222,7 @@ def update_listing(
|
|
227
222
|
|
228
223
|
# Set data directory
|
229
224
|
if data_dir is None:
|
230
|
-
|
231
|
-
if data_dir_str:
|
232
|
-
data_dir = Path(data_dir_str)
|
233
|
-
else:
|
234
|
-
data_dir = Path.cwd() / "data"
|
225
|
+
data_dir = Path.cwd()
|
235
226
|
|
236
227
|
if not data_dir.is_absolute():
|
237
228
|
data_dir = Path.cwd() / data_dir
|
unitysvc_services/utils.py
CHANGED
@@ -56,9 +56,7 @@ def write_data_file(file_path: Path, data: dict[str, Any], format: str) -> None:
|
|
56
56
|
|
57
57
|
|
58
58
|
@lru_cache(maxsize=128)
|
59
|
-
def find_data_files(
|
60
|
-
data_dir: Path, extensions: tuple[str, ...] | None = None
|
61
|
-
) -> list[Path]:
|
59
|
+
def find_data_files(data_dir: Path, extensions: tuple[str, ...] | None = None) -> list[Path]:
|
62
60
|
"""
|
63
61
|
Find all data files in a directory with specified extensions.
|
64
62
|
|
@@ -208,9 +206,7 @@ def resolve_provider_name(file_path: Path) -> str | None:
|
|
208
206
|
return None
|
209
207
|
|
210
208
|
|
211
|
-
def resolve_service_name_for_listing(
|
212
|
-
listing_file: Path, listing_data: dict[str, Any]
|
213
|
-
) -> str | None:
|
209
|
+
def resolve_service_name_for_listing(listing_file: Path, listing_data: dict[str, Any]) -> str | None:
|
214
210
|
"""
|
215
211
|
Resolve the service name for a listing file.
|
216
212
|
|
unitysvc_services/validator.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
"""Data validation module for unitysvc_services."""
|
2
2
|
|
3
3
|
import json
|
4
|
-
import os
|
5
4
|
import re
|
6
5
|
import tomllib as toml
|
7
6
|
from pathlib import Path
|
@@ -60,16 +59,12 @@ class DataValidator:
|
|
60
59
|
if "anyOf" in obj:
|
61
60
|
any_of = obj["anyOf"]
|
62
61
|
# Count non-null items for the check
|
63
|
-
non_null_items = [
|
64
|
-
item for item in any_of if item.get("type") != "null"
|
65
|
-
]
|
62
|
+
non_null_items = [item for item in any_of if item.get("type") != "null"]
|
66
63
|
has_plain_string = any(
|
67
|
-
item.get("type") == "string" and "format" not in item
|
68
|
-
for item in non_null_items
|
64
|
+
item.get("type") == "string" and "format" not in item for item in non_null_items
|
69
65
|
)
|
70
66
|
has_uri_string = any(
|
71
|
-
item.get("type") == "string" and item.get("format") == "uri"
|
72
|
-
for item in non_null_items
|
67
|
+
item.get("type") == "string" and item.get("format") == "uri" for item in non_null_items
|
73
68
|
)
|
74
69
|
|
75
70
|
# Check for Union[str, HttpUrl] or Union[str, HttpUrl, None]
|
@@ -84,9 +79,7 @@ class DataValidator:
|
|
84
79
|
|
85
80
|
# Check other schema structures
|
86
81
|
for key, value in obj.items():
|
87
|
-
if key not in ["properties", "anyOf"] and isinstance(
|
88
|
-
value, dict | list
|
89
|
-
):
|
82
|
+
if key not in ["properties", "anyOf"] and isinstance(value, dict | list):
|
90
83
|
traverse_schema(value, path)
|
91
84
|
|
92
85
|
elif isinstance(obj, list):
|
@@ -96,9 +89,7 @@ class DataValidator:
|
|
96
89
|
traverse_schema(schema)
|
97
90
|
return union_fields
|
98
91
|
|
99
|
-
def validate_file_references(
|
100
|
-
self, data: dict[str, Any], file_path: Path, union_fields: set[str]
|
101
|
-
) -> list[str]:
|
92
|
+
def validate_file_references(self, data: dict[str, Any], file_path: Path, union_fields: set[str]) -> list[str]:
|
102
93
|
"""
|
103
94
|
Validate that file references in Union[str, HttpUrl] fields exist.
|
104
95
|
|
@@ -120,9 +111,7 @@ class DataValidator:
|
|
120
111
|
):
|
121
112
|
# Empty string is not a valid file reference
|
122
113
|
if value == "":
|
123
|
-
errors.append(
|
124
|
-
f"Empty string in field '{new_path}' is not a valid file reference or URL"
|
125
|
-
)
|
114
|
+
errors.append(f"Empty string in field '{new_path}' is not a valid file reference or URL")
|
126
115
|
# It's a file reference, must be relative path
|
127
116
|
elif Path(value).is_absolute():
|
128
117
|
errors.append(
|
@@ -172,9 +161,7 @@ class DataValidator:
|
|
172
161
|
check_field(data, str(file_path))
|
173
162
|
return errors
|
174
163
|
|
175
|
-
def validate_name_consistency(
|
176
|
-
self, data: dict[str, Any], file_path: Path, schema_name: str
|
177
|
-
) -> list[str]:
|
164
|
+
def validate_name_consistency(self, data: dict[str, Any], file_path: Path, schema_name: str) -> list[str]:
|
178
165
|
"""Validate that the name field matches the directory name."""
|
179
166
|
errors: list[str] = []
|
180
167
|
|
@@ -199,9 +186,7 @@ class DataValidator:
|
|
199
186
|
elif file_path.name in ["service.json", "service.toml"]:
|
200
187
|
# For service.json, the service directory should match the service name
|
201
188
|
service_directory_name = file_path.parent.name
|
202
|
-
if self._normalize_name(name_value) != self._normalize_name(
|
203
|
-
service_directory_name
|
204
|
-
):
|
189
|
+
if self._normalize_name(name_value) != self._normalize_name(service_directory_name):
|
205
190
|
normalized_name = self._normalize_name(name_value)
|
206
191
|
errors.append(
|
207
192
|
f"Service name '{name_value}' does not match "
|
@@ -220,9 +205,7 @@ class DataValidator:
|
|
220
205
|
normalized = normalized.strip("-")
|
221
206
|
return normalized
|
222
207
|
|
223
|
-
def load_data_file(
|
224
|
-
self, file_path: Path
|
225
|
-
) -> tuple[dict[str, Any] | None, list[str]]:
|
208
|
+
def load_data_file(self, file_path: Path) -> tuple[dict[str, Any] | None, list[str]]:
|
226
209
|
"""Load data from JSON or TOML file."""
|
227
210
|
errors: list[str] = []
|
228
211
|
|
@@ -237,9 +220,7 @@ class DataValidator:
|
|
237
220
|
return None, [f"Unsupported file format: {file_path.suffix}"]
|
238
221
|
return data, errors
|
239
222
|
except Exception as e:
|
240
|
-
format_name = {".json": "JSON", ".toml": "TOML"}.get(
|
241
|
-
file_path.suffix, "data"
|
242
|
-
)
|
223
|
+
format_name = {".json": "JSON", ".toml": "TOML"}.get(file_path.suffix, "data")
|
243
224
|
return None, [f"Failed to parse {format_name}: {e}"]
|
244
225
|
|
245
226
|
def validate_data_file(self, file_path: Path) -> tuple[bool, list[str]]:
|
@@ -268,17 +249,13 @@ class DataValidator:
|
|
268
249
|
|
269
250
|
# Validate against schema with format checking enabled
|
270
251
|
try:
|
271
|
-
validator = Draft7Validator(
|
272
|
-
schema, format_checker=Draft7Validator.FORMAT_CHECKER
|
273
|
-
)
|
252
|
+
validator = Draft7Validator(schema, format_checker=Draft7Validator.FORMAT_CHECKER)
|
274
253
|
validator.check_schema(schema) # Validate the schema itself
|
275
254
|
validation_errors = list(validator.iter_errors(data))
|
276
255
|
for error in validation_errors:
|
277
256
|
errors.append(f"Schema validation error: {error.message}")
|
278
257
|
if error.absolute_path:
|
279
|
-
errors.append(
|
280
|
-
f" Path: {'.'.join(str(p) for p in error.absolute_path)}"
|
281
|
-
)
|
258
|
+
errors.append(f" Path: {'.'.join(str(p) for p in error.absolute_path)}")
|
282
259
|
except Exception as e:
|
283
260
|
errors.append(f"Validation error: {e}")
|
284
261
|
|
@@ -346,9 +323,7 @@ class DataValidator:
|
|
346
323
|
"No seller file found. Each repository must have exactly one data file using the 'seller_v1' schema."
|
347
324
|
)
|
348
325
|
elif len(seller_files) > 1:
|
349
|
-
errors.append(
|
350
|
-
f"Found {len(seller_files)} seller files, but only one is allowed per repository:"
|
351
|
-
)
|
326
|
+
errors.append(f"Found {len(seller_files)} seller files, but only one is allowed per repository:")
|
352
327
|
for seller_file in seller_files:
|
353
328
|
errors.append(f" - {seller_file}")
|
354
329
|
|
@@ -400,9 +375,7 @@ class DataValidator:
|
|
400
375
|
)
|
401
376
|
|
402
377
|
except Exception as e:
|
403
|
-
warnings.append(
|
404
|
-
f"Error checking provider status in {provider_file}: {e}"
|
405
|
-
)
|
378
|
+
warnings.append(f"Error checking provider status in {provider_file}: {e}")
|
406
379
|
|
407
380
|
# Return True (valid) but with warnings
|
408
381
|
return True, warnings
|
@@ -525,9 +498,7 @@ class DataValidator:
|
|
525
498
|
if schema == "service_v1":
|
526
499
|
service_name = data.get("name")
|
527
500
|
if not service_name:
|
528
|
-
raise DataValidationError(
|
529
|
-
f"Service file {file_path} missing 'name' field"
|
530
|
-
)
|
501
|
+
raise DataValidationError(f"Service file {file_path} missing 'name' field")
|
531
502
|
|
532
503
|
# Check for duplicate service names in same directory
|
533
504
|
if service_name in services:
|
@@ -555,9 +526,7 @@ class DataValidator:
|
|
555
526
|
if service_name:
|
556
527
|
# If service_name is explicitly defined, it must match a service in the directory
|
557
528
|
if service_name not in services:
|
558
|
-
available_services = (
|
559
|
-
", ".join(services.keys()) if services else "none"
|
560
|
-
)
|
529
|
+
available_services = ", ".join(services.keys()) if services else "none"
|
561
530
|
raise DataValidationError(
|
562
531
|
f"Listing file {listing_file} references service_name '{service_name}' "
|
563
532
|
f"which does not exist in the same directory.\n"
|
@@ -621,7 +590,7 @@ console = Console()
|
|
621
590
|
def validate(
|
622
591
|
data_dir: Path | None = typer.Argument(
|
623
592
|
None,
|
624
|
-
help="Directory containing data files to validate (default:
|
593
|
+
help="Directory containing data files to validate (default: current directory)",
|
625
594
|
),
|
626
595
|
):
|
627
596
|
"""
|
@@ -634,11 +603,7 @@ def validate(
|
|
634
603
|
"""
|
635
604
|
# Determine data directory
|
636
605
|
if data_dir is None:
|
637
|
-
|
638
|
-
if data_dir_str:
|
639
|
-
data_dir = Path(data_dir_str)
|
640
|
-
else:
|
641
|
-
data_dir = Path.cwd() / "data"
|
606
|
+
data_dir = Path.cwd()
|
642
607
|
|
643
608
|
if not data_dir.exists():
|
644
609
|
console.print(f"[red]✗[/red] Data directory not found: {data_dir}")
|
@@ -668,9 +633,7 @@ def validate(
|
|
668
633
|
validation_errors.extend(directory_errors)
|
669
634
|
|
670
635
|
if validation_errors:
|
671
|
-
console.print(
|
672
|
-
f"[red]✗ Validation failed with {len(validation_errors)} error(s):[/red]"
|
673
|
-
)
|
636
|
+
console.print(f"[red]✗ Validation failed with {len(validation_errors)} error(s):[/red]")
|
674
637
|
console.print()
|
675
638
|
for i, error in enumerate(validation_errors, 1):
|
676
639
|
console.print(f"[red]{i}.[/red] {error}")
|
@@ -1,10 +1,10 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: unitysvc-services
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.0
|
4
4
|
Summary: SDK for digital service providers on UnitySVC
|
5
5
|
Author-email: Bo Peng <bo.peng@unitysvc.com>
|
6
6
|
Maintainer-email: Bo Peng <bo.peng@unitysvc.com>
|
7
|
-
License: MIT
|
7
|
+
License-Expression: MIT
|
8
8
|
Project-URL: bugs, https://github.com/unitysvc/unitysvc-services/issues
|
9
9
|
Project-URL: changelog, https://github.com/unitysvc/unitysvc-services/blob/master/changelog.md
|
10
10
|
Project-URL: homepage, https://github.com/unitysvc/unitysvc-services
|
@@ -53,11 +53,11 @@ Client library and CLI tools for digital service providers to interact with the
|
|
53
53
|
|
54
54
|
UnitySVC Provider SDK enables digital service providers to manage their service offerings through a **local-first, version-controlled workflow**:
|
55
55
|
|
56
|
-
-
|
57
|
-
-
|
58
|
-
-
|
59
|
-
-
|
60
|
-
-
|
56
|
+
- **Define** service data using schema-validated files (JSON/TOML)
|
57
|
+
- **Manage** everything locally in git-controlled directories
|
58
|
+
- **Validate** data against schemas before publishing
|
59
|
+
- **Publish** to UnitySVC platform when ready
|
60
|
+
- **Automate** with populate scripts for dynamic catalogs
|
61
61
|
|
62
62
|
## Installation
|
63
63
|
|
@@ -79,14 +79,13 @@ unitysvc_services init seller my-marketplace
|
|
79
79
|
unitysvc_services validate
|
80
80
|
unitysvc_services format
|
81
81
|
|
82
|
-
# Publish to platform
|
83
|
-
export
|
82
|
+
# Publish to platform (publishes all: sellers, providers, offerings, listings)
|
83
|
+
export UNITYSVC_BASE_URL="https://api.unitysvc.com/api/v1"
|
84
84
|
export UNITYSVC_API_KEY="your-api-key"
|
85
|
+
unitysvc_services publish
|
85
86
|
|
87
|
+
# Or publish specific types only
|
86
88
|
unitysvc_services publish providers
|
87
|
-
unitysvc_services publish sellers
|
88
|
-
unitysvc_services publish offerings
|
89
|
-
unitysvc_services publish listings
|
90
89
|
|
91
90
|
# Verify
|
92
91
|
unitysvc_services query offerings
|
@@ -94,12 +93,12 @@ unitysvc_services query offerings
|
|
94
93
|
|
95
94
|
## Key Features
|
96
95
|
|
97
|
-
-
|
98
|
-
-
|
99
|
-
-
|
100
|
-
-
|
101
|
-
-
|
102
|
-
-
|
96
|
+
- 📋 **Pydantic Models** - Type-safe data models for all entities
|
97
|
+
- ✅ **Data Validation** - Comprehensive schema validation
|
98
|
+
- 🔄 **Local-First** - Work offline, commit to git, publish when ready
|
99
|
+
- 🚀 **CLI Tools** - Complete command-line interface
|
100
|
+
- 🤖 **Automation** - Script-based service generation
|
101
|
+
- 📝 **Multiple Formats** - Support for JSON and TOML
|
103
102
|
|
104
103
|
## Workflows
|
105
104
|
|
@@ -135,34 +134,34 @@ See [Data Structure Documentation](https://unitysvc-services.readthedocs.io/en/l
|
|
135
134
|
|
136
135
|
## CLI Commands
|
137
136
|
|
138
|
-
| Command
|
139
|
-
|
140
|
-
| `init`
|
141
|
-
| `list`
|
142
|
-
| `query`
|
143
|
-
| `publish`
|
144
|
-
| `update`
|
145
|
-
| `validate` | Validate data consistency
|
146
|
-
| `format`
|
147
|
-
| `populate` | Execute provider populate scripts
|
137
|
+
| Command | Description |
|
138
|
+
| ---------- | -------------------------------------- |
|
139
|
+
| `init` | Initialize new data files from schemas |
|
140
|
+
| `list` | List local data files |
|
141
|
+
| `query` | Query backend API for published data |
|
142
|
+
| `publish` | Publish data to backend |
|
143
|
+
| `update` | Update local file fields |
|
144
|
+
| `validate` | Validate data consistency |
|
145
|
+
| `format` | Format data files |
|
146
|
+
| `populate` | Execute provider populate scripts |
|
148
147
|
|
149
148
|
Run `unitysvc_services --help` or see [CLI Reference](https://unitysvc-services.readthedocs.io/en/latest/cli-reference/) for complete documentation.
|
150
149
|
|
151
150
|
## Documentation
|
152
151
|
|
153
|
-
-
|
154
|
-
-
|
155
|
-
-
|
156
|
-
-
|
157
|
-
-
|
158
|
-
-
|
152
|
+
- **[Getting Started](https://unitysvc-services.readthedocs.io/en/latest/getting-started/)** - Installation and first steps
|
153
|
+
- **[Data Structure](https://unitysvc-services.readthedocs.io/en/latest/data-structure/)** - File organization rules
|
154
|
+
- **[Workflows](https://unitysvc-services.readthedocs.io/en/latest/workflows/)** - Manual and automated patterns
|
155
|
+
- **[CLI Reference](https://unitysvc-services.readthedocs.io/en/latest/cli-reference/)** - All commands and options
|
156
|
+
- **[File Schemas](https://unitysvc-services.readthedocs.io/en/latest/file-schemas/)** - Schema specifications
|
157
|
+
- **[Python API](https://unitysvc-services.readthedocs.io/en/latest/api-reference/)** - Programmatic usage
|
159
158
|
|
160
159
|
## Links
|
161
160
|
|
162
|
-
-
|
163
|
-
-
|
164
|
-
-
|
165
|
-
-
|
161
|
+
- **PyPI**: https://pypi.org/project/unitysvc-services/
|
162
|
+
- **Documentation**: https://unitysvc-services.readthedocs.io
|
163
|
+
- **Source Code**: https://github.com/unitysvc/unitysvc-services
|
164
|
+
- **Issue Tracker**: https://github.com/unitysvc/unitysvc-services/issues
|
166
165
|
|
167
166
|
## License
|
168
167
|
|