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.
@@ -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, base_url: str, api_key: str):
23
- self.base_url = base_url.rstrip("/")
24
- self.api_key = api_key
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.command("providers")
509
- def publish_providers(
510
- data_path: Path | None = typer.Argument(
511
- None,
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
- "--api-key",
523
- "-k",
524
- help="API key for authentication (default: from UNITYSVC_API_KEY env var)",
546
+ "--data-path",
547
+ "-d",
548
+ help="Path to data directory (default: current directory)",
525
549
  ),
526
550
  ):
527
- """Publish provider(s) from a file or directory."""
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
- data_path_str = os.getenv("UNITYSVC_DATA_DIR")
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
- # Get backend URL from argument or environment
545
- backend_url = backend_url or os.getenv("UNITYSVC_BACKEND_URL")
546
- if not backend_url:
547
- console.print(
548
- "[red]✗[/red] Backend URL not provided. Use --backend-url or set UNITYSVC_BACKEND_URL env var.",
549
- style="bold red",
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
- # Get API key from argument or environment
554
- api_key = api_key or os.getenv("UNITYSVC_API_KEY")
555
- if not api_key:
556
- console.print(
557
- "[red]✗[/red] API key not provided. Use --api-key or set UNITYSVC_API_KEY env var.",
558
- style="bold red",
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(backend_url, api_key) as publisher:
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] {backend_url}\n")
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] {backend_url}\n")
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.Argument(
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
- "--backend-url",
611
- "-u",
612
- help="UnitySVC backend URL (default: from UNITYSVC_BACKEND_URL env var)",
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
- data_path_str = os.getenv("UNITYSVC_DATA_DIR")
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(backend_url, api_key) as publisher:
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] {backend_url}\n")
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] {backend_url}\n")
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.Argument(
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
- "--backend-url",
702
- "-u",
703
- help="UnitySVC backend URL (default: from UNITYSVC_BACKEND_URL env var)",
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
- data_path_str = os.getenv("UNITYSVC_DATA_DIR")
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(backend_url, api_key) as publisher:
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] {backend_url}\n")
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
- f"[blue]Scanning for service offerings in:[/blue] {data_path}"
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.Argument(
824
+ data_path: Path | None = typer.Option(
793
825
  None,
794
- help="Path to service listing file or directory (default: ./data or UNITYSVC_DATA_DIR env var)",
795
- ),
796
- backend_url: str | None = typer.Option(
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
- data_path_str = os.getenv("UNITYSVC_DATA_DIR")
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(backend_url, api_key) as publisher:
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] {backend_url}\n")
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
- f"[blue]Scanning for service listings in:[/blue] {data_path}"
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)