unitysvc-services 0.1.4__py3-none-any.whl → 0.1.5__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/models/base.py +12 -0
- unitysvc_services/populate.py +18 -0
- unitysvc_services/publisher.py +329 -199
- unitysvc_services/query.py +310 -302
- unitysvc_services/test.py +769 -0
- unitysvc_services/utils.py +53 -0
- unitysvc_services/validator.py +19 -7
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.5.dist-info}/METADATA +40 -33
- unitysvc_services-0.1.5.dist-info/RECORD +26 -0
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.5.dist-info}/entry_points.txt +1 -0
- unitysvc_services-0.1.4.dist-info/RECORD +0 -25
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.5.dist-info}/WHEEL +0 -0
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.5.dist-info}/top_level.txt +0 -0
unitysvc_services/publisher.py
CHANGED
@@ -14,7 +14,7 @@ from rich.console import Console
|
|
14
14
|
|
15
15
|
from .api import UnitySvcAPI
|
16
16
|
from .models.base import ProviderStatusEnum, SellerStatusEnum
|
17
|
-
from .utils import convert_convenience_fields_to_documents, find_files_by_schema
|
17
|
+
from .utils import convert_convenience_fields_to_documents, find_files_by_schema, render_template_file
|
18
18
|
from .validator import DataValidator
|
19
19
|
|
20
20
|
|
@@ -59,31 +59,79 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
59
59
|
with open(full_path, "rb") as f:
|
60
60
|
return base64.b64encode(f.read()).decode("ascii")
|
61
61
|
|
62
|
-
def resolve_file_references(
|
63
|
-
|
62
|
+
def resolve_file_references(
|
63
|
+
self,
|
64
|
+
data: dict[str, Any],
|
65
|
+
base_path: Path,
|
66
|
+
listing: dict[str, Any] | None = None,
|
67
|
+
offering: dict[str, Any] | None = None,
|
68
|
+
provider: dict[str, Any] | None = None,
|
69
|
+
seller: dict[str, Any] | None = None,
|
70
|
+
) -> dict[str, Any]:
|
71
|
+
"""Recursively resolve file references and include content in data.
|
72
|
+
|
73
|
+
For Jinja2 template files (.j2), renders the template with provided context
|
74
|
+
and strips the .j2 extension from file_path.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
data: Data dictionary potentially containing file_path references
|
78
|
+
base_path: Base path for resolving relative file paths
|
79
|
+
listing: Listing data for template rendering (optional)
|
80
|
+
offering: Offering data for template rendering (optional)
|
81
|
+
provider: Provider data for template rendering (optional)
|
82
|
+
seller: Seller data for template rendering (optional)
|
83
|
+
|
84
|
+
Returns:
|
85
|
+
Data with file references resolved and content loaded
|
86
|
+
"""
|
64
87
|
result: dict[str, Any] = {}
|
65
88
|
|
66
89
|
for key, value in data.items():
|
67
90
|
if isinstance(value, dict):
|
68
91
|
# Recursively process nested dictionaries
|
69
|
-
result[key] = self.resolve_file_references(
|
92
|
+
result[key] = self.resolve_file_references(
|
93
|
+
value, base_path, listing=listing, offering=offering, provider=provider, seller=seller
|
94
|
+
)
|
70
95
|
elif isinstance(value, list):
|
71
96
|
# Process lists
|
72
97
|
result[key] = [
|
73
|
-
(
|
98
|
+
(
|
99
|
+
self.resolve_file_references(
|
100
|
+
item, base_path, listing=listing, offering=offering, provider=provider, seller=seller
|
101
|
+
)
|
102
|
+
if isinstance(item, dict)
|
103
|
+
else item
|
104
|
+
)
|
74
105
|
for item in value
|
75
106
|
]
|
76
107
|
elif key == "file_path" and isinstance(value, str):
|
77
|
-
# This is a file reference - load the content
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
108
|
+
# This is a file reference - load the content and render if template
|
109
|
+
full_path = base_path / value if not Path(value).is_absolute() else Path(value)
|
110
|
+
|
111
|
+
if not full_path.exists():
|
112
|
+
raise FileNotFoundError(f"File not found: {full_path}")
|
113
|
+
|
114
|
+
# Render template if applicable
|
115
|
+
try:
|
116
|
+
content, actual_filename = render_template_file(
|
117
|
+
full_path,
|
118
|
+
listing=listing,
|
119
|
+
offering=offering,
|
120
|
+
provider=provider,
|
121
|
+
seller=seller,
|
122
|
+
)
|
123
|
+
result["file_content"] = content
|
124
|
+
|
125
|
+
# Update file_path to remove .j2 extension if it was a template
|
126
|
+
if full_path.name.endswith(".j2"):
|
127
|
+
# Strip .j2 from the path
|
128
|
+
new_path = str(value)[:-3] # Remove last 3 characters (.j2)
|
129
|
+
result[key] = new_path
|
130
|
+
else:
|
131
|
+
result[key] = value
|
132
|
+
|
133
|
+
except Exception as e:
|
134
|
+
raise ValueError(f"Failed to load/render file content from '{value}': {e}")
|
87
135
|
else:
|
88
136
|
result[key] = value
|
89
137
|
|
@@ -242,16 +290,15 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
242
290
|
if "name" not in data or not data.get("name"):
|
243
291
|
data["name"] = listing_file.stem
|
244
292
|
|
245
|
-
#
|
246
|
-
base_path = listing_file.parent
|
247
|
-
data_with_content = self.resolve_file_references(data, base_path)
|
248
|
-
|
249
|
-
# Extract provider_name from directory structure
|
293
|
+
# Extract provider_name from directory structure (needed before loading provider data)
|
250
294
|
parts = listing_file.parts
|
251
295
|
try:
|
252
296
|
services_idx = parts.index("services")
|
253
297
|
provider_name = parts[services_idx - 1]
|
254
|
-
|
298
|
+
data["provider_name"] = provider_name
|
299
|
+
|
300
|
+
# Find provider directory to load provider data
|
301
|
+
provider_dir = Path(*parts[:services_idx])
|
255
302
|
except (ValueError, IndexError):
|
256
303
|
raise ValueError(
|
257
304
|
f"Cannot extract provider_name from path: {listing_file}. "
|
@@ -259,7 +306,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
259
306
|
)
|
260
307
|
|
261
308
|
# If service_name is not in listing data, find it from service files in the same directory
|
262
|
-
if "service_name" not in
|
309
|
+
if "service_name" not in data or not data["service_name"]:
|
263
310
|
# Find all service files in the same directory
|
264
311
|
service_files = find_files_by_schema(listing_file.parent, "service_v1")
|
265
312
|
|
@@ -269,7 +316,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
269
316
|
f"Listing files must be in the same directory as a service definition."
|
270
317
|
)
|
271
318
|
elif len(service_files) > 1:
|
272
|
-
service_names = [
|
319
|
+
service_names = [svc_data.get("name", "unknown") for _, _, svc_data in service_files]
|
273
320
|
raise ValueError(
|
274
321
|
f"Multiple services found in {listing_file.parent}: {', '.join(service_names)}. "
|
275
322
|
f"Please add 'service_name' field to {listing_file.name} to specify which "
|
@@ -278,11 +325,11 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
278
325
|
else:
|
279
326
|
# Exactly one service found - use it
|
280
327
|
_service_file, _format, service_data = service_files[0]
|
281
|
-
|
282
|
-
|
328
|
+
data["service_name"] = service_data.get("name")
|
329
|
+
data["service_version"] = service_data.get("version")
|
283
330
|
else:
|
284
331
|
# service_name is provided in listing data, find the matching service to get version
|
285
|
-
service_name =
|
332
|
+
service_name = data["service_name"]
|
286
333
|
service_files = find_files_by_schema(
|
287
334
|
listing_file.parent, "service_v1", field_filter=(("name", service_name),)
|
288
335
|
)
|
@@ -294,7 +341,14 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
294
341
|
|
295
342
|
# Get version from the found service
|
296
343
|
_service_file, _format, service_data = service_files[0]
|
297
|
-
|
344
|
+
data["service_version"] = service_data.get("version")
|
345
|
+
|
346
|
+
# Load provider data for template rendering
|
347
|
+
provider_files = find_files_by_schema(provider_dir, "provider_v1")
|
348
|
+
if provider_files:
|
349
|
+
_provider_file, _format, provider_data = provider_files[0]
|
350
|
+
else:
|
351
|
+
provider_data = {}
|
298
352
|
|
299
353
|
# Find seller_name from seller definition in the data directory
|
300
354
|
# Navigate up to find the data directory and look for seller file
|
@@ -332,11 +386,22 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
332
386
|
if not seller_name:
|
333
387
|
raise ValueError("Seller data missing 'name' field")
|
334
388
|
|
335
|
-
|
389
|
+
data["seller_name"] = seller_name
|
336
390
|
|
337
391
|
# Map listing_status to status if present
|
338
|
-
if "listing_status" in
|
339
|
-
|
392
|
+
if "listing_status" in data:
|
393
|
+
data["status"] = data.pop("listing_status")
|
394
|
+
|
395
|
+
# NOW resolve file references with all context (listing, offering, provider, seller)
|
396
|
+
base_path = listing_file.parent
|
397
|
+
data_with_content = self.resolve_file_references(
|
398
|
+
data,
|
399
|
+
base_path,
|
400
|
+
listing=data,
|
401
|
+
offering=service_data,
|
402
|
+
provider=provider_data,
|
403
|
+
seller=seller_data,
|
404
|
+
)
|
340
405
|
|
341
406
|
# Post to the endpoint using retry helper
|
342
407
|
context_info = (
|
@@ -344,7 +409,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
344
409
|
f"provider: {data_with_content.get('provider_name')}, "
|
345
410
|
f"seller: {data_with_content.get('seller_name')}"
|
346
411
|
)
|
347
|
-
|
412
|
+
result = await self._post_with_retry(
|
348
413
|
endpoint="/publish/listing",
|
349
414
|
data=data_with_content,
|
350
415
|
entity_type="listing",
|
@@ -353,29 +418,33 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
353
418
|
max_retries=max_retries,
|
354
419
|
)
|
355
420
|
|
421
|
+
# Add local metadata to result for display purposes
|
422
|
+
result["service_name"] = data_with_content.get("service_name")
|
423
|
+
result["provider_name"] = data_with_content.get("provider_name")
|
424
|
+
result["seller_name"] = data_with_content.get("seller_name")
|
425
|
+
|
426
|
+
return result
|
427
|
+
|
356
428
|
async def post_service_offering_async(self, data_file: Path, max_retries: int = 3) -> dict[str, Any]:
|
357
429
|
"""Async version of post_service_offering for concurrent publishing with retry logic."""
|
358
430
|
# Load the data file
|
359
431
|
data = self.load_data_file(data_file)
|
360
432
|
|
361
|
-
#
|
433
|
+
# Convert convenience fields first
|
362
434
|
base_path = data_file.parent
|
363
435
|
data = convert_convenience_fields_to_documents(
|
364
436
|
data, base_path, logo_field="logo", terms_field="terms_of_service"
|
365
437
|
)
|
366
438
|
|
367
|
-
# Resolve file references and include content
|
368
|
-
data_with_content = self.resolve_file_references(data, base_path)
|
369
|
-
|
370
439
|
# Extract provider_name from directory structure
|
371
440
|
# Find the 'services' directory and use its parent as provider_name
|
372
441
|
parts = data_file.parts
|
373
442
|
try:
|
374
443
|
services_idx = parts.index("services")
|
375
444
|
provider_name = parts[services_idx - 1]
|
376
|
-
|
445
|
+
data["provider_name"] = provider_name
|
377
446
|
|
378
|
-
# Find provider directory to check status
|
447
|
+
# Find provider directory to check status and load data
|
379
448
|
provider_dir = Path(*parts[:services_idx])
|
380
449
|
except (ValueError, IndexError):
|
381
450
|
raise ValueError(
|
@@ -383,7 +452,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
383
452
|
f"Expected path to contain .../{{provider_name}}/services/..."
|
384
453
|
)
|
385
454
|
|
386
|
-
#
|
455
|
+
# Load provider data for status check and template rendering
|
387
456
|
provider_files = find_files_by_schema(provider_dir, "provider_v1")
|
388
457
|
if provider_files:
|
389
458
|
# Should only be one provider file in the directory
|
@@ -395,10 +464,22 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
395
464
|
"reason": f"Provider status is '{provider_status}' - not publishing offering to backend",
|
396
465
|
"name": data.get("name", "unknown"),
|
397
466
|
}
|
467
|
+
else:
|
468
|
+
provider_data = {}
|
469
|
+
|
470
|
+
# NOW resolve file references with all context (offering, provider)
|
471
|
+
data_with_content = self.resolve_file_references(
|
472
|
+
data,
|
473
|
+
base_path,
|
474
|
+
listing=None,
|
475
|
+
offering=data,
|
476
|
+
provider=provider_data,
|
477
|
+
seller=None,
|
478
|
+
)
|
398
479
|
|
399
480
|
# Post to the endpoint using retry helper
|
400
481
|
context_info = f"provider: {data_with_content.get('provider_name')}"
|
401
|
-
|
482
|
+
result = await self._post_with_retry(
|
402
483
|
endpoint="/publish/offering",
|
403
484
|
data=data_with_content,
|
404
485
|
entity_type="offering",
|
@@ -407,6 +488,11 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
407
488
|
max_retries=max_retries,
|
408
489
|
)
|
409
490
|
|
491
|
+
# Add local metadata to result for display purposes
|
492
|
+
result["provider_name"] = data_with_content.get("provider_name")
|
493
|
+
|
494
|
+
return result
|
495
|
+
|
410
496
|
async def post_provider_async(self, data_file: Path, max_retries: int = 3) -> dict[str, Any]:
|
411
497
|
"""Async version of post_provider for concurrent publishing with retry logic."""
|
412
498
|
# Load the data file
|
@@ -428,8 +514,15 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
428
514
|
data, base_path, logo_field="logo", terms_field="terms_of_service"
|
429
515
|
)
|
430
516
|
|
431
|
-
# Resolve file references and include content
|
432
|
-
data_with_content = self.resolve_file_references(
|
517
|
+
# Resolve file references and include content with provider context
|
518
|
+
data_with_content = self.resolve_file_references(
|
519
|
+
data,
|
520
|
+
base_path,
|
521
|
+
listing=None,
|
522
|
+
offering=None,
|
523
|
+
provider=data,
|
524
|
+
seller=None,
|
525
|
+
)
|
433
526
|
|
434
527
|
# Post to the endpoint using retry helper
|
435
528
|
return await self._post_with_retry(
|
@@ -459,8 +552,15 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
459
552
|
base_path = data_file.parent
|
460
553
|
data = convert_convenience_fields_to_documents(data, base_path, logo_field="logo", terms_field=None)
|
461
554
|
|
462
|
-
# Resolve file references and include content
|
463
|
-
data_with_content = self.resolve_file_references(
|
555
|
+
# Resolve file references and include content with seller context
|
556
|
+
data_with_content = self.resolve_file_references(
|
557
|
+
data,
|
558
|
+
base_path,
|
559
|
+
listing=None,
|
560
|
+
offering=None,
|
561
|
+
provider=None,
|
562
|
+
seller=data,
|
563
|
+
)
|
464
564
|
|
465
565
|
# Post to the endpoint using retry helper
|
466
566
|
return await self._post_with_retry(
|
@@ -837,14 +937,6 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
837
937
|
|
838
938
|
return all_results
|
839
939
|
|
840
|
-
def __enter__(self):
|
841
|
-
"""Sync context manager entry for CLI usage."""
|
842
|
-
return self
|
843
|
-
|
844
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
845
|
-
"""Sync context manager exit for CLI usage."""
|
846
|
-
asyncio.run(self.aclose())
|
847
|
-
|
848
940
|
|
849
941
|
# CLI commands for publishing
|
850
942
|
app = typer.Typer(help="Publish data to backend")
|
@@ -896,60 +988,62 @@ def publish_callback(
|
|
896
988
|
console.print(f"[bold blue]Publishing all data from:[/bold blue] {data_path}")
|
897
989
|
console.print(f"[bold blue]Backend URL:[/bold blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
898
990
|
|
991
|
+
async def _publish_all_async():
|
992
|
+
async with ServiceDataPublisher() as publisher:
|
993
|
+
return await publisher.publish_all_models(data_path)
|
994
|
+
|
899
995
|
try:
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
996
|
+
all_results = asyncio.run(_publish_all_async())
|
997
|
+
|
998
|
+
# Display results for each data type
|
999
|
+
data_type_display_names = {
|
1000
|
+
"sellers": "Sellers",
|
1001
|
+
"providers": "Providers",
|
1002
|
+
"offerings": "Service Offerings",
|
1003
|
+
"listings": "Service Listings",
|
1004
|
+
}
|
1005
|
+
|
1006
|
+
for data_type in ["sellers", "providers", "offerings", "listings"]:
|
1007
|
+
display_name = data_type_display_names[data_type]
|
1008
|
+
results = all_results[data_type]
|
911
1009
|
|
912
|
-
for data_type in ["sellers", "providers", "offerings", "listings"]:
|
913
|
-
display_name = data_type_display_names[data_type]
|
914
|
-
results = all_results[data_type]
|
915
|
-
|
916
|
-
console.print(f"\n[bold cyan]{'=' * 60}[/bold cyan]")
|
917
|
-
console.print(f"[bold cyan]{display_name}[/bold cyan]")
|
918
|
-
console.print(f"[bold cyan]{'=' * 60}[/bold cyan]\n")
|
919
|
-
|
920
|
-
console.print(f" Total found: {results['total']}")
|
921
|
-
console.print(f" [green]✓ Success:[/green] {results['success']}")
|
922
|
-
console.print(f" [red]✗ Failed:[/red] {results['failed']}")
|
923
|
-
|
924
|
-
# Display errors if any
|
925
|
-
if results.get("errors"):
|
926
|
-
console.print(f"\n[bold red]Errors in {display_name}:[/bold red]")
|
927
|
-
for error in results["errors"]:
|
928
|
-
# Check if this is a skipped item
|
929
|
-
if isinstance(error, dict) and error.get("error", "").startswith("skipped"):
|
930
|
-
continue
|
931
|
-
console.print(f" [red]✗[/red] {error.get('file', 'unknown')}")
|
932
|
-
console.print(f" {error.get('error', 'unknown error')}")
|
933
|
-
|
934
|
-
# Final summary
|
935
1010
|
console.print(f"\n[bold cyan]{'=' * 60}[/bold cyan]")
|
936
|
-
console.print("[bold]
|
1011
|
+
console.print(f"[bold cyan]{display_name}[/bold cyan]")
|
937
1012
|
console.print(f"[bold cyan]{'=' * 60}[/bold cyan]\n")
|
938
|
-
|
939
|
-
console.print(f"
|
940
|
-
console.print(f" [
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
"
|
951
|
-
|
952
|
-
|
1013
|
+
|
1014
|
+
console.print(f" Total found: {results['total']}")
|
1015
|
+
console.print(f" [green]✓ Success:[/green] {results['success']}")
|
1016
|
+
console.print(f" [red]✗ Failed:[/red] {results['failed']}")
|
1017
|
+
|
1018
|
+
# Display errors if any
|
1019
|
+
if results.get("errors"):
|
1020
|
+
console.print(f"\n[bold red]Errors in {display_name}:[/bold red]")
|
1021
|
+
for error in results["errors"]:
|
1022
|
+
# Check if this is a skipped item
|
1023
|
+
if isinstance(error, dict) and error.get("error", "").startswith("skipped"):
|
1024
|
+
continue
|
1025
|
+
console.print(f" [red]✗[/red] {error.get('file', 'unknown')}")
|
1026
|
+
console.print(f" {error.get('error', 'unknown error')}")
|
1027
|
+
|
1028
|
+
# Final summary
|
1029
|
+
console.print(f"\n[bold cyan]{'=' * 60}[/bold cyan]")
|
1030
|
+
console.print("[bold]Final Publishing Summary[/bold]")
|
1031
|
+
console.print(f"[bold cyan]{'=' * 60}[/bold cyan]\n")
|
1032
|
+
console.print(f" Total found: {all_results['total_found']}")
|
1033
|
+
console.print(f" [green]✓ Success:[/green] {all_results['total_success']}")
|
1034
|
+
console.print(f" [red]✗ Failed:[/red] {all_results['total_failed']}")
|
1035
|
+
|
1036
|
+
if all_results["total_failed"] > 0:
|
1037
|
+
console.print(
|
1038
|
+
f"\n[yellow]⚠[/yellow] Completed with {all_results['total_failed']} failure(s)",
|
1039
|
+
style="bold yellow",
|
1040
|
+
)
|
1041
|
+
raise typer.Exit(code=1)
|
1042
|
+
else:
|
1043
|
+
console.print(
|
1044
|
+
"\n[green]✓[/green] All data published successfully!",
|
1045
|
+
style="bold green",
|
1046
|
+
)
|
953
1047
|
|
954
1048
|
except typer.Exit:
|
955
1049
|
raise
|
@@ -980,36 +1074,45 @@ def publish_providers(
|
|
980
1074
|
console.print(f"[red]✗[/red] Path not found: {data_path}", style="bold red")
|
981
1075
|
raise typer.Exit(code=1)
|
982
1076
|
|
983
|
-
|
984
|
-
|
1077
|
+
# Handle single file
|
1078
|
+
if data_path.is_file():
|
1079
|
+
console.print(f"[blue]Publishing provider:[/blue] {data_path}")
|
1080
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
1081
|
+
else:
|
1082
|
+
console.print(f"[blue]Scanning for providers in:[/blue] {data_path}")
|
1083
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
1084
|
+
|
1085
|
+
async def _publish_providers_async():
|
1086
|
+
async with ServiceDataPublisher() as publisher:
|
985
1087
|
# Handle single file
|
986
1088
|
if data_path.is_file():
|
987
|
-
|
988
|
-
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
989
|
-
result = asyncio.run(publisher.post_provider_async(data_path))
|
990
|
-
console.print("[green]✓[/green] Provider published successfully!")
|
991
|
-
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
1089
|
+
return await publisher.post_provider_async(data_path), True
|
992
1090
|
# Handle directory
|
993
1091
|
else:
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1092
|
+
return await publisher.publish_all_providers(data_path), False
|
1093
|
+
|
1094
|
+
try:
|
1095
|
+
result, is_single = asyncio.run(_publish_providers_async())
|
1096
|
+
|
1097
|
+
if is_single:
|
1098
|
+
console.print("[green]✓[/green] Provider published successfully!")
|
1099
|
+
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
1100
|
+
else:
|
1101
|
+
# Display summary
|
1102
|
+
console.print("\n[bold]Publishing Summary:[/bold]")
|
1103
|
+
console.print(f" Total found: {result['total']}")
|
1104
|
+
console.print(f" [green]✓ Success:[/green] {result['success']}")
|
1105
|
+
console.print(f" [red]✗ Failed:[/red] {result['failed']}")
|
1106
|
+
|
1107
|
+
# Display errors if any
|
1108
|
+
if result["errors"]:
|
1109
|
+
console.print("\n[bold red]Errors:[/bold red]")
|
1110
|
+
for error in result["errors"]:
|
1111
|
+
console.print(f" [red]✗[/red] {error['file']}")
|
1112
|
+
console.print(f" {error['error']}")
|
1113
|
+
|
1114
|
+
if result["failed"] > 0:
|
1115
|
+
raise typer.Exit(code=1)
|
1013
1116
|
|
1014
1117
|
except typer.Exit:
|
1015
1118
|
raise
|
@@ -1039,34 +1142,43 @@ def publish_sellers(
|
|
1039
1142
|
console.print(f"[red]✗[/red] Path not found: {data_path}", style="bold red")
|
1040
1143
|
raise typer.Exit(code=1)
|
1041
1144
|
|
1042
|
-
|
1043
|
-
|
1145
|
+
# Handle single file
|
1146
|
+
if data_path.is_file():
|
1147
|
+
console.print(f"[blue]Publishing seller:[/blue] {data_path}")
|
1148
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
1149
|
+
else:
|
1150
|
+
console.print(f"[blue]Scanning for sellers in:[/blue] {data_path}")
|
1151
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
1152
|
+
|
1153
|
+
async def _publish_sellers_async():
|
1154
|
+
async with ServiceDataPublisher() as publisher:
|
1044
1155
|
# Handle single file
|
1045
1156
|
if data_path.is_file():
|
1046
|
-
|
1047
|
-
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
1048
|
-
result = asyncio.run(publisher.post_seller_async(data_path))
|
1049
|
-
console.print("[green]✓[/green] Seller published successfully!")
|
1050
|
-
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
1157
|
+
return await publisher.post_seller_async(data_path), True
|
1051
1158
|
# Handle directory
|
1052
1159
|
else:
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1160
|
+
return await publisher.publish_all_sellers(data_path), False
|
1161
|
+
|
1162
|
+
try:
|
1163
|
+
result, is_single = asyncio.run(_publish_sellers_async())
|
1164
|
+
|
1165
|
+
if is_single:
|
1166
|
+
console.print("[green]✓[/green] Seller published successfully!")
|
1167
|
+
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
1168
|
+
else:
|
1169
|
+
console.print("\n[bold]Publishing Summary:[/bold]")
|
1170
|
+
console.print(f" Total found: {result['total']}")
|
1171
|
+
console.print(f" [green]✓ Success: {result['success']}[/green]")
|
1172
|
+
console.print(f" [red]✗ Failed: {result['failed']}[/red]")
|
1173
|
+
|
1174
|
+
if result["errors"]:
|
1175
|
+
console.print("\n[bold red]Errors:[/bold red]")
|
1176
|
+
for error in result["errors"]:
|
1177
|
+
console.print(f" [red]✗[/red] {error['file']}")
|
1178
|
+
console.print(f" {error['error']}")
|
1179
|
+
raise typer.Exit(code=1)
|
1180
|
+
else:
|
1181
|
+
console.print("\n[green]✓[/green] All sellers published successfully!")
|
1070
1182
|
|
1071
1183
|
except typer.Exit:
|
1072
1184
|
raise
|
@@ -1096,34 +1208,43 @@ def publish_offerings(
|
|
1096
1208
|
console.print(f"[red]✗[/red] Path not found: {data_path}", style="bold red")
|
1097
1209
|
raise typer.Exit(code=1)
|
1098
1210
|
|
1099
|
-
|
1100
|
-
|
1211
|
+
# Handle single file
|
1212
|
+
if data_path.is_file():
|
1213
|
+
console.print(f"[blue]Publishing service offering:[/blue] {data_path}")
|
1214
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
1215
|
+
else:
|
1216
|
+
console.print(f"[blue]Scanning for service offerings in:[/blue] {data_path}")
|
1217
|
+
console.print(f"[blue]Backend URL:[/bold blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
1218
|
+
|
1219
|
+
async def _publish_offerings_async():
|
1220
|
+
async with ServiceDataPublisher() as publisher:
|
1101
1221
|
# Handle single file
|
1102
1222
|
if data_path.is_file():
|
1103
|
-
|
1104
|
-
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
1105
|
-
result = asyncio.run(publisher.post_service_offering_async(data_path))
|
1106
|
-
console.print("[green]✓[/green] Service offering published successfully!")
|
1107
|
-
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
1223
|
+
return await publisher.post_service_offering_async(data_path), True
|
1108
1224
|
# Handle directory
|
1109
1225
|
else:
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1226
|
+
return await publisher.publish_all_offerings(data_path), False
|
1227
|
+
|
1228
|
+
try:
|
1229
|
+
result, is_single = asyncio.run(_publish_offerings_async())
|
1230
|
+
|
1231
|
+
if is_single:
|
1232
|
+
console.print("[green]✓[/green] Service offering published successfully!")
|
1233
|
+
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
1234
|
+
else:
|
1235
|
+
console.print("\n[bold]Publishing Summary:[/bold]")
|
1236
|
+
console.print(f" Total found: {result['total']}")
|
1237
|
+
console.print(f" [green]✓ Success: {result['success']}[/green]")
|
1238
|
+
console.print(f" [red]✗ Failed: {result['failed']}[/red]")
|
1239
|
+
|
1240
|
+
if result["errors"]:
|
1241
|
+
console.print("\n[bold red]Errors:[/bold red]")
|
1242
|
+
for error in result["errors"]:
|
1243
|
+
console.print(f" [red]✗[/red] {error['file']}")
|
1244
|
+
console.print(f" {error['error']}")
|
1245
|
+
raise typer.Exit(code=1)
|
1246
|
+
else:
|
1247
|
+
console.print("\n[green]✓[/green] All service offerings published successfully!")
|
1127
1248
|
|
1128
1249
|
except typer.Exit:
|
1129
1250
|
raise
|
@@ -1154,34 +1275,43 @@ def publish_listings(
|
|
1154
1275
|
console.print(f"[red]✗[/red] Path not found: {data_path}", style="bold red")
|
1155
1276
|
raise typer.Exit(code=1)
|
1156
1277
|
|
1157
|
-
|
1158
|
-
|
1278
|
+
# Handle single file
|
1279
|
+
if data_path.is_file():
|
1280
|
+
console.print(f"[blue]Publishing service listing:[/blue] {data_path}")
|
1281
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
1282
|
+
else:
|
1283
|
+
console.print(f"[blue]Scanning for service listings in:[/blue] {data_path}")
|
1284
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
1285
|
+
|
1286
|
+
async def _publish_listings_async():
|
1287
|
+
async with ServiceDataPublisher() as publisher:
|
1159
1288
|
# Handle single file
|
1160
1289
|
if data_path.is_file():
|
1161
|
-
|
1162
|
-
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
1163
|
-
result = asyncio.run(publisher.post_service_listing_async(data_path))
|
1164
|
-
console.print("[green]✓[/green] Service listing published successfully!")
|
1165
|
-
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
1290
|
+
return await publisher.post_service_listing_async(data_path), True
|
1166
1291
|
# Handle directory
|
1167
1292
|
else:
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1293
|
+
return await publisher.publish_all_listings(data_path), False
|
1294
|
+
|
1295
|
+
try:
|
1296
|
+
result, is_single = asyncio.run(_publish_listings_async())
|
1297
|
+
|
1298
|
+
if is_single:
|
1299
|
+
console.print("[green]✓[/green] Service listing published successfully!")
|
1300
|
+
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
1301
|
+
else:
|
1302
|
+
console.print("\n[bold]Publishing Summary:[/bold]")
|
1303
|
+
console.print(f" Total found: {result['total']}")
|
1304
|
+
console.print(f" [green]✓ Success: {result['success']}[/green]")
|
1305
|
+
console.print(f" [red]✗ Failed: {result['failed']}[/red]")
|
1306
|
+
|
1307
|
+
if result["errors"]:
|
1308
|
+
console.print("\n[bold red]Errors:[/bold red]")
|
1309
|
+
for error in result["errors"]:
|
1310
|
+
console.print(f" [red]✗[/red] {error['file']}")
|
1311
|
+
console.print(f" {error['error']}")
|
1312
|
+
raise typer.Exit(code=1)
|
1313
|
+
else:
|
1314
|
+
console.print("\n[green]✓[/green] All service listings published successfully!")
|
1185
1315
|
|
1186
1316
|
except typer.Exit:
|
1187
1317
|
raise
|