unitysvc-services 0.1.10__py3-none-any.whl → 0.1.11__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.
Potentially problematic release.
This version of unitysvc-services might be problematic. Click here for more details.
- unitysvc_services/api.py +2 -2
- unitysvc_services/interactive_prompt.py +1129 -0
- unitysvc_services/populate.py +11 -4
- unitysvc_services/publisher.py +225 -97
- unitysvc_services/scaffold.py +143 -66
- unitysvc_services/test.py +21 -43
- {unitysvc_services-0.1.10.dist-info → unitysvc_services-0.1.11.dist-info}/METADATA +1 -1
- {unitysvc_services-0.1.10.dist-info → unitysvc_services-0.1.11.dist-info}/RECORD +12 -11
- {unitysvc_services-0.1.10.dist-info → unitysvc_services-0.1.11.dist-info}/WHEEL +0 -0
- {unitysvc_services-0.1.10.dist-info → unitysvc_services-0.1.11.dist-info}/entry_points.txt +0 -0
- {unitysvc_services-0.1.10.dist-info → unitysvc_services-0.1.11.dist-info}/licenses/LICENSE +0 -0
- {unitysvc_services-0.1.10.dist-info → unitysvc_services-0.1.11.dist-info}/top_level.txt +0 -0
unitysvc_services/publisher.py
CHANGED
|
@@ -190,7 +190,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
190
190
|
return result
|
|
191
191
|
|
|
192
192
|
async def post( # type: ignore[override]
|
|
193
|
-
self, endpoint: str, data: dict[str, Any], check_status: bool = True
|
|
193
|
+
self, endpoint: str, data: dict[str, Any], check_status: bool = True, dryrun: bool = False
|
|
194
194
|
) -> tuple[dict[str, Any], int]:
|
|
195
195
|
"""Make a POST request to the backend API with automatic curl fallback.
|
|
196
196
|
|
|
@@ -201,6 +201,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
201
201
|
endpoint: API endpoint path (e.g., "/publish/seller")
|
|
202
202
|
data: JSON data to post
|
|
203
203
|
check_status: Whether to raise on non-2xx status codes (default: True)
|
|
204
|
+
dryrun: If True, adds dryrun=true as query parameter
|
|
204
205
|
|
|
205
206
|
Returns:
|
|
206
207
|
Tuple of (JSON response, HTTP status code)
|
|
@@ -208,16 +209,19 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
208
209
|
Raises:
|
|
209
210
|
RuntimeError: If both httpx and curl fail
|
|
210
211
|
"""
|
|
212
|
+
# Build query parameters
|
|
213
|
+
params = {"dryrun": "true"} if dryrun else None
|
|
214
|
+
|
|
211
215
|
# Use base class client (self.client from UnitySvcQuery) with automatic curl fallback
|
|
212
216
|
# If we already know curl is needed, use it directly
|
|
213
217
|
if self.use_curl_fallback:
|
|
214
218
|
# Use base class curl fallback method
|
|
215
|
-
response_json = await super().post(endpoint, json_data=data)
|
|
219
|
+
response_json = await super().post(endpoint, json_data=data, params=params)
|
|
216
220
|
# Curl POST doesn't return status code separately, assume 2xx if no exception
|
|
217
221
|
status_code = 200
|
|
218
222
|
else:
|
|
219
223
|
try:
|
|
220
|
-
response = await self.client.post(f"{self.base_url}{endpoint}", json=data)
|
|
224
|
+
response = await self.client.post(f"{self.base_url}{endpoint}", json=data, params=params)
|
|
221
225
|
status_code = response.status_code
|
|
222
226
|
|
|
223
227
|
if check_status:
|
|
@@ -227,7 +231,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
227
231
|
except (httpx.ConnectError, OSError):
|
|
228
232
|
# Connection failed - switch to curl fallback and retry
|
|
229
233
|
self.use_curl_fallback = True
|
|
230
|
-
response_json = await super().post(endpoint, json_data=data)
|
|
234
|
+
response_json = await super().post(endpoint, json_data=data, params=params)
|
|
231
235
|
status_code = 200 # Assume success if curl didn't raise
|
|
232
236
|
|
|
233
237
|
return (response_json, status_code)
|
|
@@ -240,6 +244,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
240
244
|
entity_name: str,
|
|
241
245
|
context_info: str = "",
|
|
242
246
|
max_retries: int = 3,
|
|
247
|
+
dryrun: bool = False,
|
|
243
248
|
) -> dict[str, Any]:
|
|
244
249
|
"""
|
|
245
250
|
Generic retry wrapper for posting data to backend API with task polling.
|
|
@@ -257,6 +262,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
257
262
|
entity_name: Name of the entity being published (for error messages)
|
|
258
263
|
context_info: Additional context for error messages (e.g., provider, service info)
|
|
259
264
|
max_retries: Maximum number of retry attempts
|
|
265
|
+
dryrun: If True, runs in dry run mode (no actual changes)
|
|
260
266
|
|
|
261
267
|
Returns:
|
|
262
268
|
Response JSON from successful API call
|
|
@@ -268,7 +274,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
268
274
|
for attempt in range(max_retries):
|
|
269
275
|
try:
|
|
270
276
|
# Use the public post() method with automatic curl fallback
|
|
271
|
-
response_json, status_code = await self.post(endpoint, data, check_status=False)
|
|
277
|
+
response_json, status_code = await self.post(endpoint, data, check_status=False, dryrun=dryrun)
|
|
272
278
|
|
|
273
279
|
# Handle task-based response (HTTP 202)
|
|
274
280
|
if status_code == 202:
|
|
@@ -333,7 +339,9 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
333
339
|
raise last_exception
|
|
334
340
|
raise ValueError("Unexpected error in retry logic")
|
|
335
341
|
|
|
336
|
-
async def post_service_listing_async(
|
|
342
|
+
async def post_service_listing_async(
|
|
343
|
+
self, listing_file: Path, max_retries: int = 3, dryrun: bool = False
|
|
344
|
+
) -> dict[str, Any]:
|
|
337
345
|
"""Async version of post_service_listing for concurrent publishing with retry logic."""
|
|
338
346
|
# Load the listing data file
|
|
339
347
|
data = self.load_data_file(listing_file)
|
|
@@ -469,6 +477,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
469
477
|
entity_name=data.get("name", "unknown"),
|
|
470
478
|
context_info=context_info,
|
|
471
479
|
max_retries=max_retries,
|
|
480
|
+
dryrun=dryrun,
|
|
472
481
|
)
|
|
473
482
|
|
|
474
483
|
# Add local metadata to result for display purposes
|
|
@@ -478,7 +487,9 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
478
487
|
|
|
479
488
|
return result
|
|
480
489
|
|
|
481
|
-
async def post_service_offering_async(
|
|
490
|
+
async def post_service_offering_async(
|
|
491
|
+
self, data_file: Path, max_retries: int = 3, dryrun: bool = False
|
|
492
|
+
) -> dict[str, Any]:
|
|
482
493
|
"""Async version of post_service_offering for concurrent publishing with retry logic."""
|
|
483
494
|
# Load the data file
|
|
484
495
|
data = self.load_data_file(data_file)
|
|
@@ -539,6 +550,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
539
550
|
entity_name=data.get("name", "unknown"),
|
|
540
551
|
context_info=context_info,
|
|
541
552
|
max_retries=max_retries,
|
|
553
|
+
dryrun=dryrun,
|
|
542
554
|
)
|
|
543
555
|
|
|
544
556
|
# Add local metadata to result for display purposes
|
|
@@ -546,7 +558,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
546
558
|
|
|
547
559
|
return result
|
|
548
560
|
|
|
549
|
-
async def post_provider_async(self, data_file: Path, max_retries: int = 3) -> dict[str, Any]:
|
|
561
|
+
async def post_provider_async(self, data_file: Path, max_retries: int = 3, dryrun: bool = False) -> dict[str, Any]:
|
|
550
562
|
"""Async version of post_provider for concurrent publishing with retry logic."""
|
|
551
563
|
# Load the data file
|
|
552
564
|
data = self.load_data_file(data_file)
|
|
@@ -584,9 +596,10 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
584
596
|
entity_type="provider",
|
|
585
597
|
entity_name=data.get("name", "unknown"),
|
|
586
598
|
max_retries=max_retries,
|
|
599
|
+
dryrun=dryrun,
|
|
587
600
|
)
|
|
588
601
|
|
|
589
|
-
async def post_seller_async(self, data_file: Path, max_retries: int = 3) -> dict[str, Any]:
|
|
602
|
+
async def post_seller_async(self, data_file: Path, max_retries: int = 3, dryrun: bool = False) -> dict[str, Any]:
|
|
590
603
|
"""Async version of post_seller for concurrent publishing with retry logic."""
|
|
591
604
|
# Load the data file
|
|
592
605
|
data = self.load_data_file(data_file)
|
|
@@ -622,6 +635,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
622
635
|
entity_type="seller",
|
|
623
636
|
entity_name=data.get("name", "unknown"),
|
|
624
637
|
max_retries=max_retries,
|
|
638
|
+
dryrun=dryrun,
|
|
625
639
|
)
|
|
626
640
|
|
|
627
641
|
def find_offering_files(self, data_dir: Path) -> list[Path]:
|
|
@@ -651,11 +665,13 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
651
665
|
"created": ("[green]+[/green]", "green"),
|
|
652
666
|
"updated": ("[blue]~[/blue]", "blue"),
|
|
653
667
|
"unchanged": ("[dim]=[/dim]", "dim"),
|
|
668
|
+
"create": ("[yellow]?[/yellow]", "yellow"), # Dryrun: would be created
|
|
669
|
+
"update": ("[cyan]?[/cyan]", "cyan"), # Dryrun: would be updated
|
|
654
670
|
}
|
|
655
671
|
return status_map.get(status, ("[green]✓[/green]", "green"))
|
|
656
672
|
|
|
657
673
|
async def _publish_offering_task(
|
|
658
|
-
self, offering_file: Path, console: Console, semaphore: asyncio.Semaphore
|
|
674
|
+
self, offering_file: Path, console: Console, semaphore: asyncio.Semaphore, dryrun: bool = False
|
|
659
675
|
) -> tuple[Path, dict[str, Any] | Exception]:
|
|
660
676
|
"""
|
|
661
677
|
Async task to publish a single offering with concurrency control.
|
|
@@ -669,7 +685,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
669
685
|
offering_name = data.get("name", offering_file.stem)
|
|
670
686
|
|
|
671
687
|
# Publish the offering
|
|
672
|
-
result = await self.post_service_offering_async(offering_file)
|
|
688
|
+
result = await self.post_service_offering_async(offering_file, dryrun=dryrun)
|
|
673
689
|
|
|
674
690
|
# Print complete statement after publication
|
|
675
691
|
if result.get("skipped"):
|
|
@@ -691,12 +707,16 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
691
707
|
console.print(f" [red]✗[/red] Failed to publish offering: [cyan]{offering_name}[/cyan] - {str(e)}")
|
|
692
708
|
return (offering_file, e)
|
|
693
709
|
|
|
694
|
-
async def publish_all_offerings(self, data_dir: Path) -> dict[str, Any]:
|
|
710
|
+
async def publish_all_offerings(self, data_dir: Path, dryrun: bool = False) -> dict[str, Any]:
|
|
695
711
|
"""
|
|
696
712
|
Publish all service offerings found in a directory tree concurrently.
|
|
697
713
|
|
|
698
714
|
Validates data consistency before publishing.
|
|
699
715
|
Returns a summary of successes and failures.
|
|
716
|
+
|
|
717
|
+
Args:
|
|
718
|
+
data_dir: Directory to search for offering files
|
|
719
|
+
dryrun: If True, runs in dry run mode (no actual changes)
|
|
700
720
|
"""
|
|
701
721
|
# Validate all service directories first
|
|
702
722
|
schema_dir = Path(unitysvc_services.__file__).parent / "schema"
|
|
@@ -729,7 +749,10 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
729
749
|
# Run all offering publications concurrently with rate limiting
|
|
730
750
|
# Create semaphore to limit concurrent requests
|
|
731
751
|
semaphore = asyncio.Semaphore(self.max_concurrent_requests)
|
|
732
|
-
tasks = [
|
|
752
|
+
tasks = [
|
|
753
|
+
self._publish_offering_task(offering_file, console, semaphore, dryrun=dryrun)
|
|
754
|
+
for offering_file in offering_files
|
|
755
|
+
]
|
|
733
756
|
task_results = await asyncio.gather(*tasks)
|
|
734
757
|
|
|
735
758
|
# Process results
|
|
@@ -739,11 +762,11 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
739
762
|
results["errors"].append({"file": str(offering_file), "error": str(result)})
|
|
740
763
|
else:
|
|
741
764
|
results["success"] += 1
|
|
742
|
-
# Track status counts
|
|
765
|
+
# Track status counts (handle both normal and dryrun statuses)
|
|
743
766
|
status = result.get("status", "created")
|
|
744
|
-
if status
|
|
767
|
+
if status in ("created", "create"): # "create" is dryrun mode
|
|
745
768
|
results["created"] += 1
|
|
746
|
-
elif status
|
|
769
|
+
elif status in ("updated", "update"): # "update" is dryrun mode
|
|
747
770
|
results["updated"] += 1
|
|
748
771
|
elif status == "unchanged":
|
|
749
772
|
results["unchanged"] += 1
|
|
@@ -751,7 +774,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
751
774
|
return results
|
|
752
775
|
|
|
753
776
|
async def _publish_listing_task(
|
|
754
|
-
self, listing_file: Path, console: Console, semaphore: asyncio.Semaphore
|
|
777
|
+
self, listing_file: Path, console: Console, semaphore: asyncio.Semaphore, dryrun: bool = False
|
|
755
778
|
) -> tuple[Path, dict[str, Any] | Exception]:
|
|
756
779
|
"""
|
|
757
780
|
Async task to publish a single listing with concurrency control.
|
|
@@ -765,7 +788,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
765
788
|
listing_name = data.get("name", listing_file.stem)
|
|
766
789
|
|
|
767
790
|
# Publish the listing
|
|
768
|
-
result = await self.post_service_listing_async(listing_file)
|
|
791
|
+
result = await self.post_service_listing_async(listing_file, dryrun=dryrun)
|
|
769
792
|
|
|
770
793
|
# Print complete statement after publication
|
|
771
794
|
if result.get("skipped"):
|
|
@@ -788,7 +811,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
788
811
|
console.print(f" [red]✗[/red] Failed to publish listing: [cyan]{listing_file}[/cyan] - {str(e)}")
|
|
789
812
|
return (listing_file, e)
|
|
790
813
|
|
|
791
|
-
async def publish_all_listings(self, data_dir: Path) -> dict[str, Any]:
|
|
814
|
+
async def publish_all_listings(self, data_dir: Path, dryrun: bool = False) -> dict[str, Any]:
|
|
792
815
|
"""
|
|
793
816
|
Publish all service listings found in a directory tree concurrently.
|
|
794
817
|
|
|
@@ -826,7 +849,10 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
826
849
|
# Run all listing publications concurrently with rate limiting
|
|
827
850
|
# Create semaphore to limit concurrent requests
|
|
828
851
|
semaphore = asyncio.Semaphore(self.max_concurrent_requests)
|
|
829
|
-
tasks = [
|
|
852
|
+
tasks = [
|
|
853
|
+
self._publish_listing_task(listing_file, console, semaphore, dryrun=dryrun)
|
|
854
|
+
for listing_file in listing_files
|
|
855
|
+
]
|
|
830
856
|
task_results = await asyncio.gather(*tasks)
|
|
831
857
|
|
|
832
858
|
# Process results
|
|
@@ -836,11 +862,11 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
836
862
|
results["errors"].append({"file": str(listing_file), "error": str(result)})
|
|
837
863
|
else:
|
|
838
864
|
results["success"] += 1
|
|
839
|
-
# Track status counts
|
|
865
|
+
# Track status counts (handle both normal and dryrun statuses)
|
|
840
866
|
status = result.get("status", "created")
|
|
841
|
-
if status
|
|
867
|
+
if status in ("created", "create"): # "create" is dryrun mode
|
|
842
868
|
results["created"] += 1
|
|
843
|
-
elif status
|
|
869
|
+
elif status in ("updated", "update"): # "update" is dryrun mode
|
|
844
870
|
results["updated"] += 1
|
|
845
871
|
elif status == "unchanged":
|
|
846
872
|
results["unchanged"] += 1
|
|
@@ -848,7 +874,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
848
874
|
return results
|
|
849
875
|
|
|
850
876
|
async def _publish_provider_task(
|
|
851
|
-
self, provider_file: Path, console: Console, semaphore: asyncio.Semaphore
|
|
877
|
+
self, provider_file: Path, console: Console, semaphore: asyncio.Semaphore, dryrun: bool = False
|
|
852
878
|
) -> tuple[Path, dict[str, Any] | Exception]:
|
|
853
879
|
"""
|
|
854
880
|
Async task to publish a single provider with concurrency control.
|
|
@@ -862,7 +888,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
862
888
|
provider_name = data.get("name", provider_file.stem)
|
|
863
889
|
|
|
864
890
|
# Publish the provider
|
|
865
|
-
result = await self.post_provider_async(provider_file)
|
|
891
|
+
result = await self.post_provider_async(provider_file, dryrun=dryrun)
|
|
866
892
|
|
|
867
893
|
# Print complete statement after publication
|
|
868
894
|
if result.get("skipped"):
|
|
@@ -882,7 +908,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
882
908
|
console.print(f" [red]✗[/red] Failed to publish provider: [cyan]{provider_name}[/cyan] - {str(e)}")
|
|
883
909
|
return (provider_file, e)
|
|
884
910
|
|
|
885
|
-
async def publish_all_providers(self, data_dir: Path) -> dict[str, Any]:
|
|
911
|
+
async def publish_all_providers(self, data_dir: Path, dryrun: bool = False) -> dict[str, Any]:
|
|
886
912
|
"""
|
|
887
913
|
Publish all providers found in a directory tree concurrently.
|
|
888
914
|
|
|
@@ -907,7 +933,10 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
907
933
|
# Run all provider publications concurrently with rate limiting
|
|
908
934
|
# Create semaphore to limit concurrent requests
|
|
909
935
|
semaphore = asyncio.Semaphore(self.max_concurrent_requests)
|
|
910
|
-
tasks = [
|
|
936
|
+
tasks = [
|
|
937
|
+
self._publish_provider_task(provider_file, console, semaphore, dryrun=dryrun)
|
|
938
|
+
for provider_file in provider_files
|
|
939
|
+
]
|
|
911
940
|
task_results = await asyncio.gather(*tasks)
|
|
912
941
|
|
|
913
942
|
# Process results
|
|
@@ -917,11 +946,11 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
917
946
|
results["errors"].append({"file": str(provider_file), "error": str(result)})
|
|
918
947
|
else:
|
|
919
948
|
results["success"] += 1
|
|
920
|
-
# Track status counts
|
|
949
|
+
# Track status counts (handle both normal and dryrun statuses)
|
|
921
950
|
status = result.get("status", "created")
|
|
922
|
-
if status
|
|
951
|
+
if status in ("created", "create"): # "create" is dryrun mode
|
|
923
952
|
results["created"] += 1
|
|
924
|
-
elif status
|
|
953
|
+
elif status in ("updated", "update"): # "update" is dryrun mode
|
|
925
954
|
results["updated"] += 1
|
|
926
955
|
elif status == "unchanged":
|
|
927
956
|
results["unchanged"] += 1
|
|
@@ -929,7 +958,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
929
958
|
return results
|
|
930
959
|
|
|
931
960
|
async def _publish_seller_task(
|
|
932
|
-
self, seller_file: Path, console: Console, semaphore: asyncio.Semaphore
|
|
961
|
+
self, seller_file: Path, console: Console, semaphore: asyncio.Semaphore, dryrun: bool = False
|
|
933
962
|
) -> tuple[Path, dict[str, Any] | Exception]:
|
|
934
963
|
"""
|
|
935
964
|
Async task to publish a single seller with concurrency control.
|
|
@@ -943,7 +972,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
943
972
|
seller_name = data.get("name", seller_file.stem)
|
|
944
973
|
|
|
945
974
|
# Publish the seller
|
|
946
|
-
result = await self.post_seller_async(seller_file)
|
|
975
|
+
result = await self.post_seller_async(seller_file, dryrun=dryrun)
|
|
947
976
|
|
|
948
977
|
# Print complete statement after publication
|
|
949
978
|
if result.get("skipped"):
|
|
@@ -963,7 +992,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
963
992
|
console.print(f" [red]✗[/red] Failed to publish seller: [cyan]{seller_name}[/cyan] - {str(e)}")
|
|
964
993
|
return (seller_file, e)
|
|
965
994
|
|
|
966
|
-
async def publish_all_sellers(self, data_dir: Path) -> dict[str, Any]:
|
|
995
|
+
async def publish_all_sellers(self, data_dir: Path, dryrun: bool = False) -> dict[str, Any]:
|
|
967
996
|
"""
|
|
968
997
|
Publish all sellers found in a directory tree concurrently.
|
|
969
998
|
|
|
@@ -988,7 +1017,9 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
988
1017
|
# Run all seller publications concurrently with rate limiting
|
|
989
1018
|
# Create semaphore to limit concurrent requests
|
|
990
1019
|
semaphore = asyncio.Semaphore(self.max_concurrent_requests)
|
|
991
|
-
tasks = [
|
|
1020
|
+
tasks = [
|
|
1021
|
+
self._publish_seller_task(seller_file, console, semaphore, dryrun=dryrun) for seller_file in seller_files
|
|
1022
|
+
]
|
|
992
1023
|
task_results = await asyncio.gather(*tasks)
|
|
993
1024
|
|
|
994
1025
|
# Process results
|
|
@@ -998,18 +1029,18 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
998
1029
|
results["errors"].append({"file": str(seller_file), "error": str(result)})
|
|
999
1030
|
else:
|
|
1000
1031
|
results["success"] += 1
|
|
1001
|
-
# Track status counts
|
|
1032
|
+
# Track status counts (handle both normal and dryrun statuses)
|
|
1002
1033
|
status = result.get("status", "created")
|
|
1003
|
-
if status
|
|
1034
|
+
if status in ("created", "create"): # "create" is dryrun mode
|
|
1004
1035
|
results["created"] += 1
|
|
1005
|
-
elif status
|
|
1036
|
+
elif status in ("updated", "update"): # "update" is dryrun mode
|
|
1006
1037
|
results["updated"] += 1
|
|
1007
1038
|
elif status == "unchanged":
|
|
1008
1039
|
results["unchanged"] += 1
|
|
1009
1040
|
|
|
1010
1041
|
return results
|
|
1011
1042
|
|
|
1012
|
-
async def publish_all_models(self, data_dir: Path) -> dict[str, Any]:
|
|
1043
|
+
async def publish_all_models(self, data_dir: Path, dryrun: bool = False) -> dict[str, Any]:
|
|
1013
1044
|
"""
|
|
1014
1045
|
Publish all data types in the correct order.
|
|
1015
1046
|
|
|
@@ -1044,7 +1075,7 @@ class ServiceDataPublisher(UnitySvcAPI):
|
|
|
1044
1075
|
|
|
1045
1076
|
for data_type, publish_method in publish_order:
|
|
1046
1077
|
try:
|
|
1047
|
-
results = await publish_method(data_dir)
|
|
1078
|
+
results = await publish_method(data_dir, dryrun=dryrun)
|
|
1048
1079
|
all_results[data_type] = results
|
|
1049
1080
|
all_results["total_success"] += results["success"]
|
|
1050
1081
|
all_results["total_failed"] += results["failed"]
|
|
@@ -1079,6 +1110,11 @@ def publish_callback(
|
|
|
1079
1110
|
"-d",
|
|
1080
1111
|
help="Path to data directory (default: current directory)",
|
|
1081
1112
|
),
|
|
1113
|
+
dryrun: bool = typer.Option(
|
|
1114
|
+
False,
|
|
1115
|
+
"--dryrun",
|
|
1116
|
+
help="Run in dry run mode (no actual changes)",
|
|
1117
|
+
),
|
|
1082
1118
|
):
|
|
1083
1119
|
"""
|
|
1084
1120
|
Publish data to backend.
|
|
@@ -1117,7 +1153,7 @@ def publish_callback(
|
|
|
1117
1153
|
|
|
1118
1154
|
async def _publish_all_async():
|
|
1119
1155
|
async with ServiceDataPublisher() as publisher:
|
|
1120
|
-
return await publisher.publish_all_models(data_path)
|
|
1156
|
+
return await publisher.publish_all_models(data_path, dryrun=dryrun)
|
|
1121
1157
|
|
|
1122
1158
|
try:
|
|
1123
1159
|
all_results = asyncio.run(_publish_all_async())
|
|
@@ -1148,12 +1184,12 @@ def publish_callback(
|
|
|
1148
1184
|
|
|
1149
1185
|
table.add_row(
|
|
1150
1186
|
display_name,
|
|
1151
|
-
str(results[
|
|
1152
|
-
str(results[
|
|
1153
|
-
str(results[
|
|
1154
|
-
str(results.get(
|
|
1155
|
-
str(results.get(
|
|
1156
|
-
str(results.get(
|
|
1187
|
+
str(results["total"]),
|
|
1188
|
+
str(results["success"]),
|
|
1189
|
+
str(results["failed"]) if results["failed"] > 0 else "",
|
|
1190
|
+
str(results.get("created", 0)) if results.get("created", 0) > 0 else "",
|
|
1191
|
+
str(results.get("updated", 0)) if results.get("updated", 0) > 0 else "",
|
|
1192
|
+
str(results.get("unchanged", 0)) if results.get("unchanged", 0) > 0 else "",
|
|
1157
1193
|
)
|
|
1158
1194
|
|
|
1159
1195
|
# Add separator and total row
|
|
@@ -1162,10 +1198,10 @@ def publish_callback(
|
|
|
1162
1198
|
"[bold]Total[/bold]",
|
|
1163
1199
|
f"[bold]{all_results['total_found']}[/bold]",
|
|
1164
1200
|
f"[bold green]{all_results['total_success']}[/bold green]",
|
|
1165
|
-
f"[bold red]{all_results['total_failed']}[/bold red]",
|
|
1166
|
-
f"[bold green]{all_results['total_created']}[/bold green]",
|
|
1167
|
-
f"[bold blue]{all_results['total_updated']}[/bold blue]",
|
|
1168
|
-
f"[bold]{all_results['total_unchanged']}[/bold]",
|
|
1201
|
+
f"[bold red]{all_results['total_failed']}[/bold red]" if all_results["total_failed"] > 0 else "",
|
|
1202
|
+
f"[bold green]{all_results['total_created']}[/bold green]" if all_results["total_created"] > 0 else "",
|
|
1203
|
+
f"[bold blue]{all_results['total_updated']}[/bold blue]" if all_results["total_updated"] > 0 else "",
|
|
1204
|
+
f"[bold]{all_results['total_unchanged']}[/bold]" if all_results["total_unchanged"] > 0 else "",
|
|
1169
1205
|
)
|
|
1170
1206
|
|
|
1171
1207
|
console.print(table)
|
|
@@ -1196,10 +1232,16 @@ def publish_callback(
|
|
|
1196
1232
|
)
|
|
1197
1233
|
raise typer.Exit(code=1)
|
|
1198
1234
|
else:
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1235
|
+
if dryrun:
|
|
1236
|
+
console.print(
|
|
1237
|
+
"\n[green]✓[/green] Dry run completed successfully - no changes made!",
|
|
1238
|
+
style="bold green",
|
|
1239
|
+
)
|
|
1240
|
+
else:
|
|
1241
|
+
console.print(
|
|
1242
|
+
"\n[green]✓[/green] All data published successfully!",
|
|
1243
|
+
style="bold green",
|
|
1244
|
+
)
|
|
1203
1245
|
|
|
1204
1246
|
except typer.Exit:
|
|
1205
1247
|
raise
|
|
@@ -1216,6 +1258,11 @@ def publish_providers(
|
|
|
1216
1258
|
"-d",
|
|
1217
1259
|
help="Path to provider file or directory (default: current directory)",
|
|
1218
1260
|
),
|
|
1261
|
+
dryrun: bool = typer.Option(
|
|
1262
|
+
False,
|
|
1263
|
+
"--dryrun",
|
|
1264
|
+
help="Run in dry run mode (no actual changes)",
|
|
1265
|
+
),
|
|
1219
1266
|
):
|
|
1220
1267
|
"""Publish provider(s) from a file or directory."""
|
|
1221
1268
|
|
|
@@ -1242,10 +1289,10 @@ def publish_providers(
|
|
|
1242
1289
|
async with ServiceDataPublisher() as publisher:
|
|
1243
1290
|
# Handle single file
|
|
1244
1291
|
if data_path.is_file():
|
|
1245
|
-
return await publisher.post_provider_async(data_path), True
|
|
1292
|
+
return await publisher.post_provider_async(data_path, dryrun=dryrun), True
|
|
1246
1293
|
# Handle directory
|
|
1247
1294
|
else:
|
|
1248
|
-
return await publisher.publish_all_providers(data_path), False
|
|
1295
|
+
return await publisher.publish_all_providers(data_path, dryrun=dryrun), False
|
|
1249
1296
|
|
|
1250
1297
|
try:
|
|
1251
1298
|
result, is_single = asyncio.run(_publish_providers_async())
|
|
@@ -1255,14 +1302,27 @@ def publish_providers(
|
|
|
1255
1302
|
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
|
1256
1303
|
else:
|
|
1257
1304
|
# Display summary
|
|
1258
|
-
console.print("\n[bold]Publishing Summary
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1305
|
+
console.print("\n[bold cyan]Publishing Summary[/bold cyan]")
|
|
1306
|
+
table = Table(show_header=True, header_style="bold cyan", border_style="cyan")
|
|
1307
|
+
table.add_column("Type", style="cyan")
|
|
1308
|
+
table.add_column("Found", justify="right")
|
|
1309
|
+
table.add_column("Success", justify="right")
|
|
1310
|
+
table.add_column("Failed", justify="right")
|
|
1311
|
+
table.add_column("Created", justify="right")
|
|
1312
|
+
table.add_column("Updated", justify="right")
|
|
1313
|
+
table.add_column("Unchanged", justify="right")
|
|
1314
|
+
|
|
1315
|
+
table.add_row(
|
|
1316
|
+
"Providers",
|
|
1317
|
+
str(result["total"]),
|
|
1318
|
+
f"[green]{result['success']}[/green]",
|
|
1319
|
+
f"[red]{result['failed']}[/red]" if result["failed"] > 0 else "",
|
|
1320
|
+
f"[green]{result['created']}[/green]" if result["created"] > 0 else "",
|
|
1321
|
+
f"[blue]{result['updated']}[/blue]" if result["updated"] > 0 else "",
|
|
1322
|
+
f"[dim]{result['unchanged']}[/dim]" if result["unchanged"] > 0 else "",
|
|
1323
|
+
)
|
|
1324
|
+
|
|
1325
|
+
console.print(table)
|
|
1266
1326
|
|
|
1267
1327
|
# Display errors if any
|
|
1268
1328
|
if result["errors"]:
|
|
@@ -1273,6 +1333,11 @@ def publish_providers(
|
|
|
1273
1333
|
|
|
1274
1334
|
if result["failed"] > 0:
|
|
1275
1335
|
raise typer.Exit(code=1)
|
|
1336
|
+
else:
|
|
1337
|
+
if dryrun:
|
|
1338
|
+
console.print("\n[green]✓[/green] Dry run completed successfully - no changes made!")
|
|
1339
|
+
else:
|
|
1340
|
+
console.print("\n[green]✓[/green] All providers published successfully!")
|
|
1276
1341
|
|
|
1277
1342
|
except typer.Exit:
|
|
1278
1343
|
raise
|
|
@@ -1289,6 +1354,11 @@ def publish_sellers(
|
|
|
1289
1354
|
"-d",
|
|
1290
1355
|
help="Path to seller file or directory (default: current directory)",
|
|
1291
1356
|
),
|
|
1357
|
+
dryrun: bool = typer.Option(
|
|
1358
|
+
False,
|
|
1359
|
+
"--dryrun",
|
|
1360
|
+
help="Run in dry run mode (no actual changes)",
|
|
1361
|
+
),
|
|
1292
1362
|
):
|
|
1293
1363
|
"""Publish seller(s) from a file or directory."""
|
|
1294
1364
|
# Set data path
|
|
@@ -1314,10 +1384,10 @@ def publish_sellers(
|
|
|
1314
1384
|
async with ServiceDataPublisher() as publisher:
|
|
1315
1385
|
# Handle single file
|
|
1316
1386
|
if data_path.is_file():
|
|
1317
|
-
return await publisher.post_seller_async(data_path), True
|
|
1387
|
+
return await publisher.post_seller_async(data_path, dryrun=dryrun), True
|
|
1318
1388
|
# Handle directory
|
|
1319
1389
|
else:
|
|
1320
|
-
return await publisher.publish_all_sellers(data_path), False
|
|
1390
|
+
return await publisher.publish_all_sellers(data_path, dryrun=dryrun), False
|
|
1321
1391
|
|
|
1322
1392
|
try:
|
|
1323
1393
|
result, is_single = asyncio.run(_publish_sellers_async())
|
|
@@ -1326,14 +1396,27 @@ def publish_sellers(
|
|
|
1326
1396
|
console.print("[green]✓[/green] Seller published successfully!")
|
|
1327
1397
|
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
|
1328
1398
|
else:
|
|
1329
|
-
console.print("\n[bold]Publishing Summary
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1399
|
+
console.print("\n[bold cyan]Publishing Summary[/bold cyan]")
|
|
1400
|
+
table = Table(show_header=True, header_style="bold cyan", border_style="cyan")
|
|
1401
|
+
table.add_column("Type", style="cyan")
|
|
1402
|
+
table.add_column("Found", justify="right")
|
|
1403
|
+
table.add_column("Success", justify="right")
|
|
1404
|
+
table.add_column("Failed", justify="right")
|
|
1405
|
+
table.add_column("Created", justify="right")
|
|
1406
|
+
table.add_column("Updated", justify="right")
|
|
1407
|
+
table.add_column("Unchanged", justify="right")
|
|
1408
|
+
|
|
1409
|
+
table.add_row(
|
|
1410
|
+
"Sellers",
|
|
1411
|
+
str(result["total"]),
|
|
1412
|
+
f"[green]{result['success']}[/green]",
|
|
1413
|
+
f"[red]{result['failed']}[/red]" if result["failed"] > 0 else "",
|
|
1414
|
+
f"[green]{result['created']}[/green]" if result["created"] > 0 else "",
|
|
1415
|
+
f"[blue]{result['updated']}[/blue]" if result["updated"] > 0 else "",
|
|
1416
|
+
f"[dim]{result['unchanged']}[/dim]" if result["unchanged"] > 0 else "",
|
|
1417
|
+
)
|
|
1418
|
+
|
|
1419
|
+
console.print(table)
|
|
1337
1420
|
|
|
1338
1421
|
if result["errors"]:
|
|
1339
1422
|
console.print("\n[bold red]Errors:[/bold red]")
|
|
@@ -1342,7 +1425,10 @@ def publish_sellers(
|
|
|
1342
1425
|
console.print(f" {error['error']}")
|
|
1343
1426
|
raise typer.Exit(code=1)
|
|
1344
1427
|
else:
|
|
1345
|
-
|
|
1428
|
+
if dryrun:
|
|
1429
|
+
console.print("\n[green]✓[/green] Dry run completed successfully - no changes made!")
|
|
1430
|
+
else:
|
|
1431
|
+
console.print("\n[green]✓[/green] All sellers published successfully!")
|
|
1346
1432
|
|
|
1347
1433
|
except typer.Exit:
|
|
1348
1434
|
raise
|
|
@@ -1359,6 +1445,11 @@ def publish_offerings(
|
|
|
1359
1445
|
"-d",
|
|
1360
1446
|
help="Path to service offering file or directory (default: current directory)",
|
|
1361
1447
|
),
|
|
1448
|
+
dryrun: bool = typer.Option(
|
|
1449
|
+
False,
|
|
1450
|
+
"--dryrun",
|
|
1451
|
+
help="Run in dry run mode (no actual changes)",
|
|
1452
|
+
),
|
|
1362
1453
|
):
|
|
1363
1454
|
"""Publish service offering(s) from a file or directory."""
|
|
1364
1455
|
# Set data path
|
|
@@ -1384,10 +1475,10 @@ def publish_offerings(
|
|
|
1384
1475
|
async with ServiceDataPublisher() as publisher:
|
|
1385
1476
|
# Handle single file
|
|
1386
1477
|
if data_path.is_file():
|
|
1387
|
-
return await publisher.post_service_offering_async(data_path), True
|
|
1478
|
+
return await publisher.post_service_offering_async(data_path, dryrun=dryrun), True
|
|
1388
1479
|
# Handle directory
|
|
1389
1480
|
else:
|
|
1390
|
-
return await publisher.publish_all_offerings(data_path), False
|
|
1481
|
+
return await publisher.publish_all_offerings(data_path, dryrun=dryrun), False
|
|
1391
1482
|
|
|
1392
1483
|
try:
|
|
1393
1484
|
result, is_single = asyncio.run(_publish_offerings_async())
|
|
@@ -1396,14 +1487,27 @@ def publish_offerings(
|
|
|
1396
1487
|
console.print("[green]✓[/green] Service offering published successfully!")
|
|
1397
1488
|
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
|
1398
1489
|
else:
|
|
1399
|
-
console.print("\n[bold]Publishing Summary
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1490
|
+
console.print("\n[bold cyan]Publishing Summary[/bold cyan]")
|
|
1491
|
+
table = Table(show_header=True, header_style="bold cyan", border_style="cyan")
|
|
1492
|
+
table.add_column("Type", style="cyan")
|
|
1493
|
+
table.add_column("Found", justify="right")
|
|
1494
|
+
table.add_column("Success", justify="right")
|
|
1495
|
+
table.add_column("Failed", justify="right")
|
|
1496
|
+
table.add_column("Created", justify="right")
|
|
1497
|
+
table.add_column("Updated", justify="right")
|
|
1498
|
+
table.add_column("Unchanged", justify="right")
|
|
1499
|
+
|
|
1500
|
+
table.add_row(
|
|
1501
|
+
"Offerings",
|
|
1502
|
+
str(result["total"]),
|
|
1503
|
+
f"[green]{result['success']}[/green]",
|
|
1504
|
+
f"[red]{result['failed']}[/red]" if result["failed"] > 0 else "",
|
|
1505
|
+
f"[green]{result['created']}[/green]" if result["created"] > 0 else "",
|
|
1506
|
+
f"[blue]{result['updated']}[/blue]" if result["updated"] > 0 else "",
|
|
1507
|
+
f"[dim]{result['unchanged']}[/dim]" if result["unchanged"] > 0 else "",
|
|
1508
|
+
)
|
|
1509
|
+
|
|
1510
|
+
console.print(table)
|
|
1407
1511
|
|
|
1408
1512
|
if result["errors"]:
|
|
1409
1513
|
console.print("\n[bold red]Errors:[/bold red]")
|
|
@@ -1412,7 +1516,10 @@ def publish_offerings(
|
|
|
1412
1516
|
console.print(f" {error['error']}")
|
|
1413
1517
|
raise typer.Exit(code=1)
|
|
1414
1518
|
else:
|
|
1415
|
-
|
|
1519
|
+
if dryrun:
|
|
1520
|
+
console.print("\n[green]✓[/green] Dry run completed successfully - no changes made!")
|
|
1521
|
+
else:
|
|
1522
|
+
console.print("\n[green]✓[/green] All service offerings published successfully!")
|
|
1416
1523
|
|
|
1417
1524
|
except typer.Exit:
|
|
1418
1525
|
raise
|
|
@@ -1429,6 +1536,11 @@ def publish_listings(
|
|
|
1429
1536
|
"-d",
|
|
1430
1537
|
help="Path to service listing file or directory (default: current directory)",
|
|
1431
1538
|
),
|
|
1539
|
+
dryrun: bool = typer.Option(
|
|
1540
|
+
False,
|
|
1541
|
+
"--dryrun",
|
|
1542
|
+
help="Run in dry run mode (no actual changes)",
|
|
1543
|
+
),
|
|
1432
1544
|
):
|
|
1433
1545
|
"""Publish service listing(s) from a file or directory."""
|
|
1434
1546
|
|
|
@@ -1455,10 +1567,10 @@ def publish_listings(
|
|
|
1455
1567
|
async with ServiceDataPublisher() as publisher:
|
|
1456
1568
|
# Handle single file
|
|
1457
1569
|
if data_path.is_file():
|
|
1458
|
-
return await publisher.post_service_listing_async(data_path), True
|
|
1570
|
+
return await publisher.post_service_listing_async(data_path, dryrun=dryrun), True
|
|
1459
1571
|
# Handle directory
|
|
1460
1572
|
else:
|
|
1461
|
-
return await publisher.publish_all_listings(data_path), False
|
|
1573
|
+
return await publisher.publish_all_listings(data_path, dryrun=dryrun), False
|
|
1462
1574
|
|
|
1463
1575
|
try:
|
|
1464
1576
|
result, is_single = asyncio.run(_publish_listings_async())
|
|
@@ -1467,14 +1579,27 @@ def publish_listings(
|
|
|
1467
1579
|
console.print("[green]✓[/green] Service listing published successfully!")
|
|
1468
1580
|
console.print(f"[cyan]Response:[/cyan] {json.dumps(result, indent=2)}")
|
|
1469
1581
|
else:
|
|
1470
|
-
console.print("\n[bold]Publishing Summary
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1582
|
+
console.print("\n[bold cyan]Publishing Summary[/bold cyan]")
|
|
1583
|
+
table = Table(show_header=True, header_style="bold cyan", border_style="cyan")
|
|
1584
|
+
table.add_column("Type", style="cyan")
|
|
1585
|
+
table.add_column("Found", justify="right")
|
|
1586
|
+
table.add_column("Success", justify="right")
|
|
1587
|
+
table.add_column("Failed", justify="right")
|
|
1588
|
+
table.add_column("Created", justify="right")
|
|
1589
|
+
table.add_column("Updated", justify="right")
|
|
1590
|
+
table.add_column("Unchanged", justify="right")
|
|
1591
|
+
|
|
1592
|
+
table.add_row(
|
|
1593
|
+
"Listings",
|
|
1594
|
+
str(result["total"]),
|
|
1595
|
+
f"[green]{result['success']}[/green]",
|
|
1596
|
+
f"[red]{result['failed']}[/red]" if result["failed"] > 0 else "",
|
|
1597
|
+
f"[green]{result['created']}[/green]" if result["created"] > 0 else "",
|
|
1598
|
+
f"[blue]{result['updated']}[/blue]" if result["updated"] > 0 else "",
|
|
1599
|
+
f"[dim]{result['unchanged']}[/dim]" if result["unchanged"] > 0 else "",
|
|
1600
|
+
)
|
|
1601
|
+
|
|
1602
|
+
console.print(table)
|
|
1478
1603
|
|
|
1479
1604
|
if result["errors"]:
|
|
1480
1605
|
console.print("\n[bold red]Errors:[/bold red]")
|
|
@@ -1483,7 +1608,10 @@ def publish_listings(
|
|
|
1483
1608
|
console.print(f" {error['error']}")
|
|
1484
1609
|
raise typer.Exit(code=1)
|
|
1485
1610
|
else:
|
|
1486
|
-
|
|
1611
|
+
if dryrun:
|
|
1612
|
+
console.print("\n[green]✓[/green] Dry run completed successfully - no changes made!")
|
|
1613
|
+
else:
|
|
1614
|
+
console.print("\n[green]✓[/green] All service listings published successfully!")
|
|
1487
1615
|
|
|
1488
1616
|
except typer.Exit:
|
|
1489
1617
|
raise
|