unitysvc-services 0.1.24__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/__init__.py +4 -0
- unitysvc_services/api.py +421 -0
- unitysvc_services/cli.py +23 -0
- unitysvc_services/format_data.py +140 -0
- unitysvc_services/interactive_prompt.py +1132 -0
- unitysvc_services/list.py +216 -0
- unitysvc_services/models/__init__.py +71 -0
- unitysvc_services/models/base.py +1375 -0
- unitysvc_services/models/listing_data.py +118 -0
- unitysvc_services/models/listing_v1.py +56 -0
- unitysvc_services/models/provider_data.py +79 -0
- unitysvc_services/models/provider_v1.py +54 -0
- unitysvc_services/models/seller_data.py +120 -0
- unitysvc_services/models/seller_v1.py +42 -0
- unitysvc_services/models/service_data.py +114 -0
- unitysvc_services/models/service_v1.py +81 -0
- unitysvc_services/populate.py +207 -0
- unitysvc_services/publisher.py +1628 -0
- unitysvc_services/py.typed +0 -0
- unitysvc_services/query.py +688 -0
- unitysvc_services/scaffold.py +1103 -0
- unitysvc_services/schema/base.json +777 -0
- unitysvc_services/schema/listing_v1.json +1286 -0
- unitysvc_services/schema/provider_v1.json +952 -0
- unitysvc_services/schema/seller_v1.json +379 -0
- unitysvc_services/schema/service_v1.json +1306 -0
- unitysvc_services/test.py +965 -0
- unitysvc_services/unpublisher.py +505 -0
- unitysvc_services/update.py +287 -0
- unitysvc_services/utils.py +533 -0
- unitysvc_services/validator.py +731 -0
- unitysvc_services-0.1.24.dist-info/METADATA +184 -0
- unitysvc_services-0.1.24.dist-info/RECORD +37 -0
- unitysvc_services-0.1.24.dist-info/WHEEL +5 -0
- unitysvc_services-0.1.24.dist-info/entry_points.txt +3 -0
- unitysvc_services-0.1.24.dist-info/licenses/LICENSE +21 -0
- unitysvc_services-0.1.24.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""Update command group - update local data files."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
|
|
9
|
+
from .utils import find_file_by_schema_and_name, find_files_by_schema, write_data_file
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(help="Update local data files")
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@app.command("offering")
|
|
16
|
+
def update_offering(
|
|
17
|
+
name: str = typer.Option(
|
|
18
|
+
...,
|
|
19
|
+
"--name",
|
|
20
|
+
"-n",
|
|
21
|
+
help="Name of the service offering to update (matches 'name' field in service file)",
|
|
22
|
+
),
|
|
23
|
+
status: str | None = typer.Option(
|
|
24
|
+
None,
|
|
25
|
+
"--status",
|
|
26
|
+
"-s",
|
|
27
|
+
help="New upstream_status (uploading, ready, deprecated)",
|
|
28
|
+
),
|
|
29
|
+
display_name: str | None = typer.Option(
|
|
30
|
+
None,
|
|
31
|
+
"--display-name",
|
|
32
|
+
help="New display name for the offering",
|
|
33
|
+
),
|
|
34
|
+
description: str | None = typer.Option(
|
|
35
|
+
None,
|
|
36
|
+
"--description",
|
|
37
|
+
help="New description for the offering",
|
|
38
|
+
),
|
|
39
|
+
version: str | None = typer.Option(
|
|
40
|
+
None,
|
|
41
|
+
"--version",
|
|
42
|
+
help="New version for the offering",
|
|
43
|
+
),
|
|
44
|
+
data_dir: Path | None = typer.Option(
|
|
45
|
+
None,
|
|
46
|
+
"--data-dir",
|
|
47
|
+
"-d",
|
|
48
|
+
help="Directory containing data files (default: current directory)",
|
|
49
|
+
),
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
Update fields in a service offering's local data file.
|
|
53
|
+
|
|
54
|
+
Searches for files with schema 'service_v1' by offering name and updates the specified fields.
|
|
55
|
+
|
|
56
|
+
Allowed upstream_status values:
|
|
57
|
+
- uploading: Service is being uploaded (not ready)
|
|
58
|
+
- ready: Service is ready to be used
|
|
59
|
+
- deprecated: Service is deprecated from upstream
|
|
60
|
+
"""
|
|
61
|
+
# Validate status if provided
|
|
62
|
+
if status:
|
|
63
|
+
valid_statuses = ["uploading", "ready", "deprecated"]
|
|
64
|
+
if status not in valid_statuses:
|
|
65
|
+
console.print(
|
|
66
|
+
f"[red]✗[/red] Invalid status: {status}",
|
|
67
|
+
style="bold red",
|
|
68
|
+
)
|
|
69
|
+
console.print(f"[yellow]Allowed statuses:[/yellow] {', '.join(valid_statuses)}")
|
|
70
|
+
raise typer.Exit(code=1)
|
|
71
|
+
|
|
72
|
+
# Check if any update field is provided
|
|
73
|
+
if not any([status, display_name, description, version]):
|
|
74
|
+
console.print(
|
|
75
|
+
(
|
|
76
|
+
"[red]✗[/red] No fields to update. Provide at least one of: "
|
|
77
|
+
"--status, --display-name, --description, --version"
|
|
78
|
+
),
|
|
79
|
+
style="bold red",
|
|
80
|
+
)
|
|
81
|
+
raise typer.Exit(code=1)
|
|
82
|
+
|
|
83
|
+
# Set data directory
|
|
84
|
+
if data_dir is None:
|
|
85
|
+
data_dir = Path.cwd()
|
|
86
|
+
|
|
87
|
+
if not data_dir.is_absolute():
|
|
88
|
+
data_dir = Path.cwd() / data_dir
|
|
89
|
+
|
|
90
|
+
if not data_dir.exists():
|
|
91
|
+
console.print(f"[red]✗[/red] Data directory not found: {data_dir}", style="bold red")
|
|
92
|
+
raise typer.Exit(code=1)
|
|
93
|
+
|
|
94
|
+
console.print(f"[blue]Searching for offering:[/blue] {name}")
|
|
95
|
+
console.print(f"[blue]In directory:[/blue] {data_dir}\n")
|
|
96
|
+
|
|
97
|
+
# Find the matching offering file
|
|
98
|
+
result = find_file_by_schema_and_name(data_dir, "service_v1", "name", name)
|
|
99
|
+
|
|
100
|
+
if not result:
|
|
101
|
+
console.print(
|
|
102
|
+
f"[red]✗[/red] No offering found with name: {name}",
|
|
103
|
+
style="bold red",
|
|
104
|
+
)
|
|
105
|
+
raise typer.Exit(code=1)
|
|
106
|
+
|
|
107
|
+
matching_file, matching_format, data = result
|
|
108
|
+
|
|
109
|
+
# Update the fields
|
|
110
|
+
try:
|
|
111
|
+
updates: dict[str, tuple[Any, Any]] = {} # field: (old_value, new_value)
|
|
112
|
+
|
|
113
|
+
if status:
|
|
114
|
+
updates["upstream_status"] = (data.get("upstream_status", "unknown"), status)
|
|
115
|
+
data["upstream_status"] = status
|
|
116
|
+
|
|
117
|
+
if display_name:
|
|
118
|
+
updates["display_name"] = (data.get("display_name", ""), display_name)
|
|
119
|
+
data["display_name"] = display_name
|
|
120
|
+
|
|
121
|
+
if description:
|
|
122
|
+
updates["description"] = (data.get("description", ""), description)
|
|
123
|
+
data["description"] = description
|
|
124
|
+
|
|
125
|
+
if version:
|
|
126
|
+
updates["version"] = (data.get("version", ""), version)
|
|
127
|
+
data["version"] = version
|
|
128
|
+
|
|
129
|
+
# Write back in same format
|
|
130
|
+
write_data_file(matching_file, data, matching_format)
|
|
131
|
+
|
|
132
|
+
console.print("[green]✓[/green] Updated offering successfully!")
|
|
133
|
+
console.print(f"[cyan]File:[/cyan] {matching_file.relative_to(data_dir)}")
|
|
134
|
+
console.print(f"[cyan]Format:[/cyan] {matching_format.upper()}\n")
|
|
135
|
+
|
|
136
|
+
for field, (old, new) in updates.items():
|
|
137
|
+
console.print(f"[cyan]{field}:[/cyan]")
|
|
138
|
+
if len(str(old)) > 60 or len(str(new)) > 60:
|
|
139
|
+
console.print(f" [dim]Old:[/dim] {str(old)[:60]}...")
|
|
140
|
+
console.print(f" [dim]New:[/dim] {str(new)[:60]}...")
|
|
141
|
+
else:
|
|
142
|
+
console.print(f" [dim]Old:[/dim] {old}")
|
|
143
|
+
console.print(f" [dim]New:[/dim] {new}")
|
|
144
|
+
|
|
145
|
+
except Exception as e:
|
|
146
|
+
console.print(
|
|
147
|
+
f"[red]✗[/red] Failed to update offering: {e}",
|
|
148
|
+
style="bold red",
|
|
149
|
+
)
|
|
150
|
+
raise typer.Exit(code=1)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@app.command("listing")
|
|
154
|
+
def update_listing(
|
|
155
|
+
services: str = typer.Option(
|
|
156
|
+
...,
|
|
157
|
+
"--services",
|
|
158
|
+
"-n",
|
|
159
|
+
help="Name of the service (to search for listing files in service directory)",
|
|
160
|
+
),
|
|
161
|
+
status: str | None = typer.Option(
|
|
162
|
+
None,
|
|
163
|
+
"--status",
|
|
164
|
+
"-s",
|
|
165
|
+
help=(
|
|
166
|
+
"New listing_status (unknown, upstream_ready, downstream_ready, "
|
|
167
|
+
"ready, in_service, upstream_deprecated, deprecated)"
|
|
168
|
+
),
|
|
169
|
+
),
|
|
170
|
+
seller: str | None = typer.Option(
|
|
171
|
+
None,
|
|
172
|
+
"--seller",
|
|
173
|
+
help="Seller name to filter listings (updates only matching seller's listing)",
|
|
174
|
+
),
|
|
175
|
+
data_dir: Path | None = typer.Option(
|
|
176
|
+
None,
|
|
177
|
+
"--data-dir",
|
|
178
|
+
"-d",
|
|
179
|
+
help="Directory containing data files (default: current directory)",
|
|
180
|
+
),
|
|
181
|
+
):
|
|
182
|
+
"""
|
|
183
|
+
Update fields in service listing(s) local data files.
|
|
184
|
+
|
|
185
|
+
Searches for files with schema 'listing_v1' in the service directory and updates the specified fields.
|
|
186
|
+
|
|
187
|
+
Allowed listing_status values:
|
|
188
|
+
- unknown: Not yet determined
|
|
189
|
+
- upstream_ready: Upstream is ready to be used
|
|
190
|
+
- downstream_ready: Downstream is ready with proper routing, logging, and billing
|
|
191
|
+
- ready: Operationally ready (with docs, metrics, and pricing)
|
|
192
|
+
- in_service: Service is in service
|
|
193
|
+
- upstream_deprecated: Service is deprecated from upstream
|
|
194
|
+
- deprecated: Service is no longer offered to users
|
|
195
|
+
"""
|
|
196
|
+
# Validate status if provided
|
|
197
|
+
if status:
|
|
198
|
+
valid_statuses = [
|
|
199
|
+
"unknown",
|
|
200
|
+
"upstream_ready",
|
|
201
|
+
"downstream_ready",
|
|
202
|
+
"ready",
|
|
203
|
+
"in_service",
|
|
204
|
+
"upstream_deprecated",
|
|
205
|
+
"deprecated",
|
|
206
|
+
]
|
|
207
|
+
if status not in valid_statuses:
|
|
208
|
+
console.print(
|
|
209
|
+
f"[red]✗[/red] Invalid status: {status}",
|
|
210
|
+
style="bold red",
|
|
211
|
+
)
|
|
212
|
+
console.print(f"[yellow]Allowed statuses:[/yellow] {', '.join(valid_statuses)}")
|
|
213
|
+
raise typer.Exit(code=1)
|
|
214
|
+
|
|
215
|
+
# Check if any update field is provided
|
|
216
|
+
if not status:
|
|
217
|
+
console.print(
|
|
218
|
+
"[red]✗[/red] No fields to update. Provide at least one of: --status",
|
|
219
|
+
style="bold red",
|
|
220
|
+
)
|
|
221
|
+
raise typer.Exit(code=1)
|
|
222
|
+
|
|
223
|
+
# Set data directory
|
|
224
|
+
if data_dir is None:
|
|
225
|
+
data_dir = Path.cwd()
|
|
226
|
+
|
|
227
|
+
if not data_dir.is_absolute():
|
|
228
|
+
data_dir = Path.cwd() / data_dir
|
|
229
|
+
|
|
230
|
+
if not data_dir.exists():
|
|
231
|
+
console.print(f"[red]✗[/red] Data directory not found: {data_dir}", style="bold red")
|
|
232
|
+
raise typer.Exit(code=1)
|
|
233
|
+
|
|
234
|
+
console.print(f"[blue]Searching for service:[/blue] {services}")
|
|
235
|
+
console.print(f"[blue]In directory:[/blue] {data_dir}")
|
|
236
|
+
if seller:
|
|
237
|
+
console.print(f"[blue]Filtering by seller:[/blue] {seller}")
|
|
238
|
+
console.print()
|
|
239
|
+
|
|
240
|
+
# Build field filter
|
|
241
|
+
field_filter = {}
|
|
242
|
+
if seller:
|
|
243
|
+
field_filter["seller_name"] = seller
|
|
244
|
+
|
|
245
|
+
# Convert field_filter dict to tuple for caching
|
|
246
|
+
field_filter_tuple = tuple(sorted(field_filter.items())) if field_filter else None
|
|
247
|
+
|
|
248
|
+
# Find listing files matching criteria
|
|
249
|
+
listing_files = find_files_by_schema(data_dir, "listing_v1", path_filter=services, field_filter=field_filter_tuple)
|
|
250
|
+
|
|
251
|
+
if not listing_files:
|
|
252
|
+
console.print(
|
|
253
|
+
"[red]✗[/red] No listing files found matching criteria",
|
|
254
|
+
style="bold red",
|
|
255
|
+
)
|
|
256
|
+
raise typer.Exit(code=1)
|
|
257
|
+
|
|
258
|
+
# Update all matching listings
|
|
259
|
+
updated_count = 0
|
|
260
|
+
for listing_file, file_format, data in listing_files:
|
|
261
|
+
try:
|
|
262
|
+
old_status = data.get("listing_status", "unknown")
|
|
263
|
+
if status:
|
|
264
|
+
data["listing_status"] = status
|
|
265
|
+
|
|
266
|
+
# Write back in same format
|
|
267
|
+
write_data_file(listing_file, data, file_format)
|
|
268
|
+
|
|
269
|
+
console.print(f"[green]✓[/green] Updated: {listing_file.relative_to(data_dir)}")
|
|
270
|
+
console.print(f" [dim]Seller: {data.get('seller_name', 'N/A')}[/dim]")
|
|
271
|
+
console.print(f" [dim]Format: {file_format.upper()}[/dim]")
|
|
272
|
+
if status:
|
|
273
|
+
console.print(f" [dim]Old status: {old_status} → New status: {status}[/dim]")
|
|
274
|
+
console.print()
|
|
275
|
+
updated_count += 1
|
|
276
|
+
|
|
277
|
+
except Exception as e:
|
|
278
|
+
console.print(
|
|
279
|
+
f"[red]✗[/red] Failed to update {listing_file.relative_to(data_dir)}: {e}",
|
|
280
|
+
style="bold red",
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
if updated_count > 0:
|
|
284
|
+
console.print(f"[green]✓[/green] Successfully updated {updated_count} listing(s)")
|
|
285
|
+
else:
|
|
286
|
+
console.print("[red]✗[/red] No listings were updated", style="bold red")
|
|
287
|
+
raise typer.Exit(code=1)
|