unitysvc-services 0.1.1__py3-none-any.whl → 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +218 -226
- unitysvc_services/query.py +27 -141
- 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.1.dist-info}/METADATA +37 -38
- unitysvc_services-0.2.1.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.1.dist-info}/WHEEL +0 -0
- {unitysvc_services-0.1.1.dist-info → unitysvc_services-0.2.1.dist-info}/entry_points.txt +0 -0
- {unitysvc_services-0.1.1.dist-info → unitysvc_services-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {unitysvc_services-0.1.1.dist-info → unitysvc_services-0.2.1.dist-info}/top_level.txt +0 -0
unitysvc_services/publisher.py
CHANGED
@@ -19,12 +19,19 @@ from .validator import DataValidator
|
|
19
19
|
class ServiceDataPublisher:
|
20
20
|
"""Publishes service data to UnitySVC backend endpoints."""
|
21
21
|
|
22
|
-
def __init__(self
|
23
|
-
self.base_url =
|
24
|
-
self.
|
22
|
+
def __init__(self):
|
23
|
+
self.base_url = os.environ.get("UNITYSVC_BASE_URL")
|
24
|
+
if not self.base_url:
|
25
|
+
raise ValueError("UNITYSVC_BASE_URL environment variable not set")
|
26
|
+
|
27
|
+
self.api_key = os.environ.get("UNITYSVC_API_KEY")
|
28
|
+
if not self.api_key:
|
29
|
+
raise ValueError("UNITYSVC_API_KEY environment variable not set")
|
30
|
+
|
31
|
+
self.base_url = self.base_url.rstrip("/")
|
25
32
|
self.client = httpx.Client(
|
26
33
|
headers={
|
27
|
-
"X-API-Key": api_key,
|
34
|
+
"X-API-Key": self.api_key,
|
28
35
|
"Content-Type": "application/json",
|
29
36
|
},
|
30
37
|
timeout=30.0,
|
@@ -57,9 +64,7 @@ class ServiceDataPublisher:
|
|
57
64
|
with open(full_path, "rb") as f:
|
58
65
|
return base64.b64encode(f.read()).decode("ascii")
|
59
66
|
|
60
|
-
def resolve_file_references(
|
61
|
-
self, data: dict[str, Any], base_path: Path
|
62
|
-
) -> dict[str, Any]:
|
67
|
+
def resolve_file_references(self, data: dict[str, Any], base_path: Path) -> dict[str, Any]:
|
63
68
|
"""Recursively resolve file references and include content in data."""
|
64
69
|
result: dict[str, Any] = {}
|
65
70
|
|
@@ -70,11 +75,7 @@ class ServiceDataPublisher:
|
|
70
75
|
elif isinstance(value, list):
|
71
76
|
# Process lists
|
72
77
|
result[key] = [
|
73
|
-
(
|
74
|
-
self.resolve_file_references(item, base_path)
|
75
|
-
if isinstance(item, dict)
|
76
|
-
else item
|
77
|
-
)
|
78
|
+
(self.resolve_file_references(item, base_path) if isinstance(item, dict) else item)
|
78
79
|
for item in value
|
79
80
|
]
|
80
81
|
elif key == "file_path" and isinstance(value, str):
|
@@ -87,9 +88,7 @@ class ServiceDataPublisher:
|
|
87
88
|
content = self.load_file_content(Path(value), base_path)
|
88
89
|
result["file_content"] = content
|
89
90
|
except Exception as e:
|
90
|
-
raise ValueError(
|
91
|
-
f"Failed to load file content from '{value}': {e}"
|
92
|
-
)
|
91
|
+
raise ValueError(f"Failed to load file content from '{value}': {e}")
|
93
92
|
else:
|
94
93
|
result[key] = value
|
95
94
|
|
@@ -172,10 +171,7 @@ class ServiceDataPublisher:
|
|
172
171
|
)
|
173
172
|
|
174
173
|
# If service_name is not in listing data, find it from service files in the same directory
|
175
|
-
if
|
176
|
-
"service_name" not in data_with_content
|
177
|
-
or not data_with_content["service_name"]
|
178
|
-
):
|
174
|
+
if "service_name" not in data_with_content or not data_with_content["service_name"]:
|
179
175
|
# Find all service files in the same directory
|
180
176
|
service_files = find_files_by_schema(data_file.parent, "service_v1")
|
181
177
|
|
@@ -185,9 +181,7 @@ class ServiceDataPublisher:
|
|
185
181
|
f"Listing files must be in the same directory as a service definition."
|
186
182
|
)
|
187
183
|
elif len(service_files) > 1:
|
188
|
-
service_names = [
|
189
|
-
data.get("name", "unknown") for _, _, data in service_files
|
190
|
-
]
|
184
|
+
service_names = [data.get("name", "unknown") for _, _, data in service_files]
|
191
185
|
raise ValueError(
|
192
186
|
f"Multiple services found in {data_file.parent}: {', '.join(service_names)}. "
|
193
187
|
f"Please add 'service_name' field to {data_file.name} to specify which "
|
@@ -201,9 +195,7 @@ class ServiceDataPublisher:
|
|
201
195
|
else:
|
202
196
|
# service_name is provided in listing data, find the matching service to get version
|
203
197
|
service_name = data_with_content["service_name"]
|
204
|
-
service_files = find_files_by_schema(
|
205
|
-
data_file.parent, "service_v1", field_filter=(("name", service_name),)
|
206
|
-
)
|
198
|
+
service_files = find_files_by_schema(data_file.parent, "service_v1", field_filter=(("name", service_name),))
|
207
199
|
|
208
200
|
if not service_files:
|
209
201
|
raise ValueError(
|
@@ -320,9 +312,7 @@ class ServiceDataPublisher:
|
|
320
312
|
|
321
313
|
# Convert convenience fields (logo only for sellers, no terms_of_service)
|
322
314
|
base_path = data_file.parent
|
323
|
-
data = convert_convenience_fields_to_documents(
|
324
|
-
data, base_path, logo_field="logo", terms_field=None
|
325
|
-
)
|
315
|
+
data = convert_convenience_fields_to_documents(data, base_path, logo_field="logo", terms_field=None)
|
326
316
|
|
327
317
|
# Resolve file references and include content
|
328
318
|
data_with_content = self.resolve_file_references(data, base_path)
|
@@ -376,10 +366,7 @@ class ServiceDataPublisher:
|
|
376
366
|
"total": 0,
|
377
367
|
"success": 0,
|
378
368
|
"failed": 0,
|
379
|
-
"errors": [
|
380
|
-
{"file": "validation", "error": error}
|
381
|
-
for error in validation_errors
|
382
|
-
],
|
369
|
+
"errors": [{"file": "validation", "error": error} for error in validation_errors],
|
383
370
|
}
|
384
371
|
|
385
372
|
offering_files = self.find_offering_files(data_dir)
|
@@ -415,10 +402,7 @@ class ServiceDataPublisher:
|
|
415
402
|
"total": 0,
|
416
403
|
"success": 0,
|
417
404
|
"failed": 0,
|
418
|
-
"errors": [
|
419
|
-
{"file": "validation", "error": error}
|
420
|
-
for error in validation_errors
|
421
|
-
],
|
405
|
+
"errors": [{"file": "validation", "error": error} for error in validation_errors],
|
422
406
|
}
|
423
407
|
|
424
408
|
listing_files = self.find_listing_files(data_dir)
|
@@ -487,6 +471,55 @@ class ServiceDataPublisher:
|
|
487
471
|
|
488
472
|
return results
|
489
473
|
|
474
|
+
def publish_all_models(self, data_dir: Path) -> dict[str, Any]:
|
475
|
+
"""
|
476
|
+
Publish all data types in the correct order.
|
477
|
+
|
478
|
+
Publishing order:
|
479
|
+
1. Sellers - Must exist before listings
|
480
|
+
2. Providers - Must exist before offerings
|
481
|
+
3. Service Offerings - Must exist before listings
|
482
|
+
4. Service Listings - Depends on sellers, providers, and offerings
|
483
|
+
|
484
|
+
Returns a dict with results for each data type and overall summary.
|
485
|
+
"""
|
486
|
+
all_results: dict[str, Any] = {
|
487
|
+
"sellers": {},
|
488
|
+
"providers": {},
|
489
|
+
"offerings": {},
|
490
|
+
"listings": {},
|
491
|
+
"total_success": 0,
|
492
|
+
"total_failed": 0,
|
493
|
+
"total_found": 0,
|
494
|
+
}
|
495
|
+
|
496
|
+
# Publish in order: sellers -> providers -> offerings -> listings
|
497
|
+
publish_order = [
|
498
|
+
("sellers", self.publish_all_sellers),
|
499
|
+
("providers", self.publish_all_providers),
|
500
|
+
("offerings", self.publish_all_offerings),
|
501
|
+
("listings", self.publish_all_listings),
|
502
|
+
]
|
503
|
+
|
504
|
+
for data_type, publish_method in publish_order:
|
505
|
+
try:
|
506
|
+
results = publish_method(data_dir)
|
507
|
+
all_results[data_type] = results
|
508
|
+
all_results["total_success"] += results["success"]
|
509
|
+
all_results["total_failed"] += results["failed"]
|
510
|
+
all_results["total_found"] += results["total"]
|
511
|
+
except Exception as e:
|
512
|
+
# If a publish method fails catastrophically, record the error
|
513
|
+
all_results[data_type] = {
|
514
|
+
"total": 0,
|
515
|
+
"success": 0,
|
516
|
+
"failed": 1,
|
517
|
+
"errors": [{"file": "N/A", "error": str(e)}],
|
518
|
+
}
|
519
|
+
all_results["total_failed"] += 1
|
520
|
+
|
521
|
+
return all_results
|
522
|
+
|
490
523
|
def close(self):
|
491
524
|
"""Close the HTTP client."""
|
492
525
|
self.client.close()
|
@@ -505,34 +538,40 @@ app = typer.Typer(help="Publish data to backend")
|
|
505
538
|
console = Console()
|
506
539
|
|
507
540
|
|
508
|
-
@app.
|
509
|
-
def
|
510
|
-
|
511
|
-
|
512
|
-
help="Path to provider file or directory (default: ./data or UNITYSVC_DATA_DIR env var)",
|
513
|
-
),
|
514
|
-
backend_url: str | None = typer.Option(
|
515
|
-
None,
|
516
|
-
"--backend-url",
|
517
|
-
"-u",
|
518
|
-
help="UnitySVC backend URL (default: from UNITYSVC_BACKEND_URL env var)",
|
519
|
-
),
|
520
|
-
api_key: str | None = typer.Option(
|
541
|
+
@app.callback(invoke_without_command=True)
|
542
|
+
def publish_callback(
|
543
|
+
ctx: typer.Context,
|
544
|
+
data_path: Path | None = typer.Option(
|
521
545
|
None,
|
522
|
-
"--
|
523
|
-
"-
|
524
|
-
help="
|
546
|
+
"--data-path",
|
547
|
+
"-d",
|
548
|
+
help="Path to data directory (default: current directory)",
|
525
549
|
),
|
526
550
|
):
|
527
|
-
"""
|
528
|
-
|
551
|
+
"""
|
552
|
+
Publish data to backend.
|
553
|
+
|
554
|
+
When called without a subcommand, publishes all data types in order:
|
555
|
+
sellers → providers → offerings → listings.
|
556
|
+
|
557
|
+
Use subcommands to publish specific data types:
|
558
|
+
- providers: Publish only providers
|
559
|
+
- sellers: Publish only sellers
|
560
|
+
- offerings: Publish only service offerings
|
561
|
+
- listings: Publish only service listings
|
562
|
+
|
563
|
+
Required environment variables:
|
564
|
+
- UNITYSVC_BASE_URL: Backend API URL
|
565
|
+
- UNITYSVC_API_KEY: API key for authentication
|
566
|
+
"""
|
567
|
+
# If a subcommand was invoked, skip this callback logic
|
568
|
+
if ctx.invoked_subcommand is not None:
|
569
|
+
return
|
570
|
+
|
571
|
+
# No subcommand - publish all
|
529
572
|
# Set data path
|
530
573
|
if data_path is None:
|
531
|
-
|
532
|
-
if data_path_str:
|
533
|
-
data_path = Path(data_path_str)
|
534
|
-
else:
|
535
|
-
data_path = Path.cwd() / "data"
|
574
|
+
data_path = Path.cwd()
|
536
575
|
|
537
576
|
if not data_path.is_absolute():
|
538
577
|
data_path = Path.cwd() / data_path
|
@@ -541,37 +580,106 @@ def publish_providers(
|
|
541
580
|
console.print(f"[red]✗[/red] Path not found: {data_path}", style="bold red")
|
542
581
|
raise typer.Exit(code=1)
|
543
582
|
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
583
|
+
console.print(f"[bold blue]Publishing all data from:[/bold blue] {data_path}")
|
584
|
+
console.print(f"[bold blue]Backend URL:[/bold blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
585
|
+
|
586
|
+
try:
|
587
|
+
with ServiceDataPublisher() as publisher:
|
588
|
+
# Call the publish_all_models method
|
589
|
+
all_results = publisher.publish_all_models(data_path)
|
590
|
+
|
591
|
+
# Display results for each data type
|
592
|
+
data_type_display_names = {
|
593
|
+
"sellers": "Sellers",
|
594
|
+
"providers": "Providers",
|
595
|
+
"offerings": "Service Offerings",
|
596
|
+
"listings": "Service Listings",
|
597
|
+
}
|
598
|
+
|
599
|
+
for data_type in ["sellers", "providers", "offerings", "listings"]:
|
600
|
+
display_name = data_type_display_names[data_type]
|
601
|
+
results = all_results[data_type]
|
602
|
+
|
603
|
+
console.print(f"\n[bold cyan]{'=' * 60}[/bold cyan]")
|
604
|
+
console.print(f"[bold cyan]{display_name}[/bold cyan]")
|
605
|
+
console.print(f"[bold cyan]{'=' * 60}[/bold cyan]\n")
|
606
|
+
|
607
|
+
console.print(f" Total found: {results['total']}")
|
608
|
+
console.print(f" [green]✓ Success:[/green] {results['success']}")
|
609
|
+
console.print(f" [red]✗ Failed:[/red] {results['failed']}")
|
610
|
+
|
611
|
+
# Display errors if any
|
612
|
+
if results.get("errors"):
|
613
|
+
console.print(f"\n[bold red]Errors in {display_name}:[/bold red]")
|
614
|
+
for error in results["errors"]:
|
615
|
+
# Check if this is a skipped item
|
616
|
+
if isinstance(error, dict) and error.get("error", "").startswith("skipped"):
|
617
|
+
continue
|
618
|
+
console.print(f" [red]✗[/red] {error.get('file', 'unknown')}")
|
619
|
+
console.print(f" {error.get('error', 'unknown error')}")
|
620
|
+
|
621
|
+
# Final summary
|
622
|
+
console.print(f"\n[bold cyan]{'=' * 60}[/bold cyan]")
|
623
|
+
console.print("[bold]Final Publishing Summary[/bold]")
|
624
|
+
console.print(f"[bold cyan]{'=' * 60}[/bold cyan]\n")
|
625
|
+
console.print(f" Total found: {all_results['total_found']}")
|
626
|
+
console.print(f" [green]✓ Success:[/green] {all_results['total_success']}")
|
627
|
+
console.print(f" [red]✗ Failed:[/red] {all_results['total_failed']}")
|
628
|
+
|
629
|
+
if all_results["total_failed"] > 0:
|
630
|
+
console.print(
|
631
|
+
f"\n[yellow]⚠[/yellow] Completed with {all_results['total_failed']} failure(s)",
|
632
|
+
style="bold yellow",
|
633
|
+
)
|
634
|
+
raise typer.Exit(code=1)
|
635
|
+
else:
|
636
|
+
console.print(
|
637
|
+
"\n[green]✓[/green] All data published successfully!",
|
638
|
+
style="bold green",
|
639
|
+
)
|
640
|
+
|
641
|
+
except typer.Exit:
|
642
|
+
raise
|
643
|
+
except Exception as e:
|
644
|
+
console.print(f"[red]✗[/red] Failed to publish all data: {e}", style="bold red")
|
551
645
|
raise typer.Exit(code=1)
|
552
646
|
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
647
|
+
|
648
|
+
@app.command("providers")
|
649
|
+
def publish_providers(
|
650
|
+
data_path: Path | None = typer.Option(
|
651
|
+
None,
|
652
|
+
"--data-path",
|
653
|
+
"-d",
|
654
|
+
help="Path to provider file or directory (default: current directory)",
|
655
|
+
),
|
656
|
+
):
|
657
|
+
"""Publish provider(s) from a file or directory."""
|
658
|
+
|
659
|
+
# Set data path
|
660
|
+
if data_path is None:
|
661
|
+
data_path = Path.cwd()
|
662
|
+
|
663
|
+
if not data_path.is_absolute():
|
664
|
+
data_path = Path.cwd() / data_path
|
665
|
+
|
666
|
+
if not data_path.exists():
|
667
|
+
console.print(f"[red]✗[/red] Path not found: {data_path}", style="bold red")
|
560
668
|
raise typer.Exit(code=1)
|
561
669
|
|
562
670
|
try:
|
563
|
-
with ServiceDataPublisher(
|
671
|
+
with ServiceDataPublisher() as publisher:
|
564
672
|
# Handle single file
|
565
673
|
if data_path.is_file():
|
566
674
|
console.print(f"[blue]Publishing provider:[/blue] {data_path}")
|
567
|
-
console.print(f"[blue]Backend URL:[/blue] {
|
675
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
568
676
|
result = publisher.post_provider(data_path)
|
569
677
|
console.print("[green]✓[/green] Provider published successfully!")
|
570
678
|
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
571
679
|
# Handle directory
|
572
680
|
else:
|
573
681
|
console.print(f"[blue]Scanning for providers in:[/blue] {data_path}")
|
574
|
-
console.print(f"[blue]Backend URL:[/blue] {
|
682
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
575
683
|
results = publisher.publish_all_providers(data_path)
|
576
684
|
|
577
685
|
# Display summary
|
@@ -593,39 +701,23 @@ def publish_providers(
|
|
593
701
|
except typer.Exit:
|
594
702
|
raise
|
595
703
|
except Exception as e:
|
596
|
-
console.print(
|
597
|
-
f"[red]✗[/red] Failed to publish providers: {e}", style="bold red"
|
598
|
-
)
|
704
|
+
console.print(f"[red]✗[/red] Failed to publish providers: {e}", style="bold red")
|
599
705
|
raise typer.Exit(code=1)
|
600
706
|
|
601
707
|
|
602
708
|
@app.command("sellers")
|
603
709
|
def publish_sellers(
|
604
|
-
data_path: Path | None = typer.
|
605
|
-
None,
|
606
|
-
help="Path to seller file or directory (default: ./data or UNITYSVC_DATA_DIR env var)",
|
607
|
-
),
|
608
|
-
backend_url: str | None = typer.Option(
|
710
|
+
data_path: Path | None = typer.Option(
|
609
711
|
None,
|
610
|
-
"--
|
611
|
-
"-
|
612
|
-
help="
|
613
|
-
),
|
614
|
-
api_key: str | None = typer.Option(
|
615
|
-
None,
|
616
|
-
"--api-key",
|
617
|
-
"-k",
|
618
|
-
help="API key for authentication (default: from UNITYSVC_API_KEY env var)",
|
712
|
+
"--data-path",
|
713
|
+
"-d",
|
714
|
+
help="Path to seller file or directory (default: current directory)",
|
619
715
|
),
|
620
716
|
):
|
621
717
|
"""Publish seller(s) from a file or directory."""
|
622
718
|
# Set data path
|
623
719
|
if data_path is None:
|
624
|
-
|
625
|
-
if data_path_str:
|
626
|
-
data_path = Path(data_path_str)
|
627
|
-
else:
|
628
|
-
data_path = Path.cwd() / "data"
|
720
|
+
data_path = Path.cwd()
|
629
721
|
|
630
722
|
if not data_path.is_absolute():
|
631
723
|
data_path = Path.cwd() / data_path
|
@@ -634,37 +726,19 @@ def publish_sellers(
|
|
634
726
|
console.print(f"[red]✗[/red] Path not found: {data_path}", style="bold red")
|
635
727
|
raise typer.Exit(code=1)
|
636
728
|
|
637
|
-
# Get backend URL
|
638
|
-
backend_url = backend_url or os.getenv("UNITYSVC_BACKEND_URL")
|
639
|
-
if not backend_url:
|
640
|
-
console.print(
|
641
|
-
"[red]✗[/red] Backend URL not provided. Use --backend-url or set UNITYSVC_BACKEND_URL env var.",
|
642
|
-
style="bold red",
|
643
|
-
)
|
644
|
-
raise typer.Exit(code=1)
|
645
|
-
|
646
|
-
# Get API key
|
647
|
-
api_key = api_key or os.getenv("UNITYSVC_API_KEY")
|
648
|
-
if not api_key:
|
649
|
-
console.print(
|
650
|
-
"[red]✗[/red] API key not provided. Use --api-key or set UNITYSVC_API_KEY env var.",
|
651
|
-
style="bold red",
|
652
|
-
)
|
653
|
-
raise typer.Exit(code=1)
|
654
|
-
|
655
729
|
try:
|
656
|
-
with ServiceDataPublisher(
|
730
|
+
with ServiceDataPublisher() as publisher:
|
657
731
|
# Handle single file
|
658
732
|
if data_path.is_file():
|
659
733
|
console.print(f"[blue]Publishing seller:[/blue] {data_path}")
|
660
|
-
console.print(f"[blue]Backend URL:[/blue] {
|
734
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
661
735
|
result = publisher.post_seller(data_path)
|
662
736
|
console.print("[green]✓[/green] Seller published successfully!")
|
663
737
|
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
664
738
|
# Handle directory
|
665
739
|
else:
|
666
740
|
console.print(f"[blue]Scanning for sellers in:[/blue] {data_path}")
|
667
|
-
console.print(f"[blue]Backend URL:[/blue] {
|
741
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
668
742
|
results = publisher.publish_all_sellers(data_path)
|
669
743
|
|
670
744
|
console.print("\n[bold]Publishing Summary:[/bold]")
|
@@ -679,9 +753,7 @@ def publish_sellers(
|
|
679
753
|
console.print(f" {error['error']}")
|
680
754
|
raise typer.Exit(code=1)
|
681
755
|
else:
|
682
|
-
console.print(
|
683
|
-
"\n[green]✓[/green] All sellers published successfully!"
|
684
|
-
)
|
756
|
+
console.print("\n[green]✓[/green] All sellers published successfully!")
|
685
757
|
|
686
758
|
except typer.Exit:
|
687
759
|
raise
|
@@ -692,31 +764,17 @@ def publish_sellers(
|
|
692
764
|
|
693
765
|
@app.command("offerings")
|
694
766
|
def publish_offerings(
|
695
|
-
data_path: Path | None = typer.
|
696
|
-
None,
|
697
|
-
help="Path to service offering file or directory (default: ./data or UNITYSVC_DATA_DIR env var)",
|
698
|
-
),
|
699
|
-
backend_url: str | None = typer.Option(
|
767
|
+
data_path: Path | None = typer.Option(
|
700
768
|
None,
|
701
|
-
"--
|
702
|
-
"-
|
703
|
-
help="
|
704
|
-
),
|
705
|
-
api_key: str | None = typer.Option(
|
706
|
-
None,
|
707
|
-
"--api-key",
|
708
|
-
"-k",
|
709
|
-
help="API key for authentication (default: from UNITYSVC_API_KEY env var)",
|
769
|
+
"--data-path",
|
770
|
+
"-d",
|
771
|
+
help="Path to service offering file or directory (default: current directory)",
|
710
772
|
),
|
711
773
|
):
|
712
774
|
"""Publish service offering(s) from a file or directory."""
|
713
775
|
# Set data path
|
714
776
|
if data_path is None:
|
715
|
-
|
716
|
-
if data_path_str:
|
717
|
-
data_path = Path(data_path_str)
|
718
|
-
else:
|
719
|
-
data_path = Path.cwd() / "data"
|
777
|
+
data_path = Path.cwd()
|
720
778
|
|
721
779
|
if not data_path.is_absolute():
|
722
780
|
data_path = Path.cwd() / data_path
|
@@ -725,41 +783,19 @@ def publish_offerings(
|
|
725
783
|
console.print(f"[red]✗[/red] Path not found: {data_path}", style="bold red")
|
726
784
|
raise typer.Exit(code=1)
|
727
785
|
|
728
|
-
# Get backend URL from argument or environment
|
729
|
-
backend_url = backend_url or os.getenv("UNITYSVC_BACKEND_URL")
|
730
|
-
if not backend_url:
|
731
|
-
console.print(
|
732
|
-
"[red]✗[/red] Backend URL not provided. Use --backend-url or set UNITYSVC_BACKEND_URL env var.",
|
733
|
-
style="bold red",
|
734
|
-
)
|
735
|
-
raise typer.Exit(code=1)
|
736
|
-
|
737
|
-
# Get API key from argument or environment
|
738
|
-
api_key = api_key or os.getenv("UNITYSVC_API_KEY")
|
739
|
-
if not api_key:
|
740
|
-
console.print(
|
741
|
-
"[red]✗[/red] API key not provided. Use --api-key or set UNITYSVC_API_KEY env var.",
|
742
|
-
style="bold red",
|
743
|
-
)
|
744
|
-
raise typer.Exit(code=1)
|
745
|
-
|
746
786
|
try:
|
747
|
-
with ServiceDataPublisher(
|
787
|
+
with ServiceDataPublisher() as publisher:
|
748
788
|
# Handle single file
|
749
789
|
if data_path.is_file():
|
750
790
|
console.print(f"[blue]Publishing service offering:[/blue] {data_path}")
|
751
|
-
console.print(f"[blue]Backend URL:[/blue] {
|
791
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
752
792
|
result = publisher.post_service_offering(data_path)
|
753
|
-
console.print(
|
754
|
-
"[green]✓[/green] Service offering published successfully!"
|
755
|
-
)
|
793
|
+
console.print("[green]✓[/green] Service offering published successfully!")
|
756
794
|
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
757
795
|
# Handle directory
|
758
796
|
else:
|
759
|
-
console.print(
|
760
|
-
|
761
|
-
)
|
762
|
-
console.print(f"[blue]Backend URL:[/blue] {backend_url}\n")
|
797
|
+
console.print(f"[blue]Scanning for service offerings in:[/blue] {data_path}")
|
798
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
763
799
|
results = publisher.publish_all_offerings(data_path)
|
764
800
|
|
765
801
|
console.print("\n[bold]Publishing Summary:[/bold]")
|
@@ -774,47 +810,29 @@ def publish_offerings(
|
|
774
810
|
console.print(f" {error['error']}")
|
775
811
|
raise typer.Exit(code=1)
|
776
812
|
else:
|
777
|
-
console.print(
|
778
|
-
"\n[green]✓[/green] All service offerings published successfully!"
|
779
|
-
)
|
813
|
+
console.print("\n[green]✓[/green] All service offerings published successfully!")
|
780
814
|
|
781
815
|
except typer.Exit:
|
782
816
|
raise
|
783
817
|
except Exception as e:
|
784
|
-
console.print(
|
785
|
-
f"[red]✗[/red] Failed to publish service offerings: {e}", style="bold red"
|
786
|
-
)
|
818
|
+
console.print(f"[red]✗[/red] Failed to publish service offerings: {e}", style="bold red")
|
787
819
|
raise typer.Exit(code=1)
|
788
820
|
|
789
821
|
|
790
822
|
@app.command("listings")
|
791
823
|
def publish_listings(
|
792
|
-
data_path: Path | None = typer.
|
824
|
+
data_path: Path | None = typer.Option(
|
793
825
|
None,
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
None,
|
798
|
-
"--backend-url",
|
799
|
-
"-u",
|
800
|
-
help="UnitySVC backend URL (default: from UNITYSVC_BACKEND_URL env var)",
|
801
|
-
),
|
802
|
-
api_key: str | None = typer.Option(
|
803
|
-
None,
|
804
|
-
"--api-key",
|
805
|
-
"-k",
|
806
|
-
help="API key for authentication (default: from UNITYSVC_API_KEY env var)",
|
826
|
+
"--data-path",
|
827
|
+
"-d",
|
828
|
+
help="Path to service listing file or directory (default: current directory)",
|
807
829
|
),
|
808
830
|
):
|
809
831
|
"""Publish service listing(s) from a file or directory."""
|
810
832
|
|
811
833
|
# Set data path
|
812
834
|
if data_path is None:
|
813
|
-
|
814
|
-
if data_path_str:
|
815
|
-
data_path = Path(data_path_str)
|
816
|
-
else:
|
817
|
-
data_path = Path.cwd() / "data"
|
835
|
+
data_path = Path.cwd()
|
818
836
|
|
819
837
|
if not data_path.is_absolute():
|
820
838
|
data_path = Path.cwd() / data_path
|
@@ -823,41 +841,19 @@ def publish_listings(
|
|
823
841
|
console.print(f"[red]✗[/red] Path not found: {data_path}", style="bold red")
|
824
842
|
raise typer.Exit(code=1)
|
825
843
|
|
826
|
-
# Get backend URL from argument or environment
|
827
|
-
backend_url = backend_url or os.getenv("UNITYSVC_BACKEND_URL")
|
828
|
-
if not backend_url:
|
829
|
-
console.print(
|
830
|
-
"[red]✗[/red] Backend URL not provided. Use --backend-url or set UNITYSVC_BACKEND_URL env var.",
|
831
|
-
style="bold red",
|
832
|
-
)
|
833
|
-
raise typer.Exit(code=1)
|
834
|
-
|
835
|
-
# Get API key from argument or environment
|
836
|
-
api_key = api_key or os.getenv("UNITYSVC_API_KEY")
|
837
|
-
if not api_key:
|
838
|
-
console.print(
|
839
|
-
"[red]✗[/red] API key not provided. Use --api-key or set UNITYSVC_API_KEY env var.",
|
840
|
-
style="bold red",
|
841
|
-
)
|
842
|
-
raise typer.Exit(code=1)
|
843
|
-
|
844
844
|
try:
|
845
|
-
with ServiceDataPublisher(
|
845
|
+
with ServiceDataPublisher() as publisher:
|
846
846
|
# Handle single file
|
847
847
|
if data_path.is_file():
|
848
848
|
console.print(f"[blue]Publishing service listing:[/blue] {data_path}")
|
849
|
-
console.print(f"[blue]Backend URL:[/blue] {
|
849
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
850
850
|
result = publisher.post_service_listing(data_path)
|
851
|
-
console.print(
|
852
|
-
"[green]✓[/green] Service listing published successfully!"
|
853
|
-
)
|
851
|
+
console.print("[green]✓[/green] Service listing published successfully!")
|
854
852
|
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
855
853
|
# Handle directory
|
856
854
|
else:
|
857
|
-
console.print(
|
858
|
-
|
859
|
-
)
|
860
|
-
console.print(f"[blue]Backend URL:[/blue] {backend_url}\n")
|
855
|
+
console.print(f"[blue]Scanning for service listings in:[/blue] {data_path}")
|
856
|
+
console.print(f"[blue]Backend URL:[/blue] {os.getenv('UNITYSVC_BASE_URL', 'N/A')}\n")
|
861
857
|
results = publisher.publish_all_listings(data_path)
|
862
858
|
|
863
859
|
console.print("\n[bold]Publishing Summary:[/bold]")
|
@@ -872,14 +868,10 @@ def publish_listings(
|
|
872
868
|
console.print(f" {error['error']}")
|
873
869
|
raise typer.Exit(code=1)
|
874
870
|
else:
|
875
|
-
console.print(
|
876
|
-
"\n[green]✓[/green] All service listings published successfully!"
|
877
|
-
)
|
871
|
+
console.print("\n[green]✓[/green] All service listings published successfully!")
|
878
872
|
|
879
873
|
except typer.Exit:
|
880
874
|
raise
|
881
875
|
except Exception as e:
|
882
|
-
console.print(
|
883
|
-
f"[red]✗[/red] Failed to publish service listings: {e}", style="bold red"
|
884
|
-
)
|
876
|
+
console.print(f"[red]✗[/red] Failed to publish service listings: {e}", style="bold red")
|
885
877
|
raise typer.Exit(code=1)
|