unitysvc-services 0.2.1__py3-none-any.whl → 0.2.3__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,7 +19,7 @@ from .validator import DataValidator
19
19
  class ServiceDataPublisher:
20
20
  """Publishes service data to UnitySVC backend endpoints."""
21
21
 
22
- def __init__(self):
22
+ def __init__(self) -> None:
23
23
  self.base_url = os.environ.get("UNITYSVC_BASE_URL")
24
24
  if not self.base_url:
25
25
  raise ValueError("UNITYSVC_BASE_URL environment variable not set")
@@ -139,7 +139,7 @@ class ServiceDataPublisher:
139
139
 
140
140
  # Post to the endpoint
141
141
  response = self.client.post(
142
- f"{self.base_url}/publish/service_offering",
142
+ f"{self.base_url}/publish/offering",
143
143
  json=data_with_content,
144
144
  )
145
145
  response.raise_for_status()
@@ -250,7 +250,7 @@ class ServiceDataPublisher:
250
250
 
251
251
  # Post to the endpoint
252
252
  response = self.client.post(
253
- f"{self.base_url}/publish/service_listing",
253
+ f"{self.base_url}/publish/listing",
254
254
  json=data_with_content,
255
255
  )
256
256
  response.raise_for_status()
File without changes
@@ -16,7 +16,7 @@ console = Console()
16
16
  class ServiceDataQuery:
17
17
  """Query service data from UnitySVC backend endpoints."""
18
18
 
19
- def __init__(self):
19
+ def __init__(self) -> None:
20
20
  """Initialize query client from environment variables.
21
21
 
22
22
  Raises:
@@ -41,14 +41,14 @@ class ServiceDataQuery:
41
41
 
42
42
  def list_service_offerings(self) -> list[dict[str, Any]]:
43
43
  """List all service offerings from the backend."""
44
- response = self.client.get(f"{self.base_url}/publish/service_offerings")
44
+ response = self.client.get(f"{self.base_url}/publish/offerings")
45
45
  response.raise_for_status()
46
46
  result = response.json()
47
47
  return result.get("data", result) if isinstance(result, dict) else result
48
48
 
49
49
  def list_service_listings(self) -> list[dict[str, Any]]:
50
50
  """List all service listings from the backend."""
51
- response = self.client.get(f"{self.base_url}/publish/services")
51
+ response = self.client.get(f"{self.base_url}/publish/listings")
52
52
  response.raise_for_status()
53
53
  result = response.json()
54
54
  return result.get("data", result) if isinstance(result, dict) else result
@@ -69,7 +69,7 @@ class ServiceDataQuery:
69
69
 
70
70
  def list_access_interfaces(self) -> dict[str, Any]:
71
71
  """List all access interfaces from the backend (private endpoint)."""
72
- response = self.client.get(f"{self.base_url}/private/access_interfaces")
72
+ response = self.client.get(f"{self.base_url}/private/interfaces")
73
73
  response.raise_for_status()
74
74
  return response.json()
75
75
 
@@ -100,37 +100,136 @@ def query_sellers(
100
100
  "-f",
101
101
  help="Output format: table, json",
102
102
  ),
103
+ fields: str = typer.Option(
104
+ "id,name,display_name,seller_type",
105
+ "--fields",
106
+ help=(
107
+ "Comma-separated list of fields to display. Available fields: "
108
+ "id, name, display_name, seller_type, contact_email, "
109
+ "secondary_contact_email, homepage, description, "
110
+ "business_registration, tax_id, account_manager_id, "
111
+ "created_at, updated_at, status"
112
+ ),
113
+ ),
103
114
  ):
104
- """Query all sellers from the backend."""
115
+ """Query all sellers from the backend.
116
+
117
+ Examples:
118
+ # Use default fields
119
+ unitysvc_services query sellers
120
+
121
+ # Show only specific fields
122
+ unitysvc_services query sellers --fields id,name,contact_email
123
+
124
+ # Show all available fields
125
+ unitysvc_services query sellers --fields \\
126
+ id,name,display_name,seller_type,contact_email,homepage,created_at,updated_at
127
+ """
128
+ # Parse fields list
129
+ field_list = [f.strip() for f in fields.split(",")]
130
+
131
+ # Define allowed fields from SellerPublic model
132
+ allowed_fields = {
133
+ "id",
134
+ "name",
135
+ "display_name",
136
+ "seller_type",
137
+ "contact_email",
138
+ "secondary_contact_email",
139
+ "homepage",
140
+ "description",
141
+ "business_registration",
142
+ "tax_id",
143
+ "account_manager_id",
144
+ "created_at",
145
+ "updated_at",
146
+ "status",
147
+ }
148
+
149
+ # Validate fields
150
+ invalid_fields = [f for f in field_list if f not in allowed_fields]
151
+ if invalid_fields:
152
+ console.print(
153
+ f"[red]✗[/red] Invalid field(s): {', '.join(invalid_fields)}",
154
+ style="bold red",
155
+ )
156
+ console.print(f"[yellow]Allowed fields:[/yellow] {', '.join(sorted(allowed_fields))}")
157
+ raise typer.Exit(code=1)
158
+
105
159
  try:
106
160
  with ServiceDataQuery() as query:
107
161
  sellers = query.list_sellers()
108
162
 
109
163
  if format == "json":
110
- console.print(json.dumps(sellers, indent=2))
164
+ # For JSON, filter fields if not all are requested
165
+ if set(field_list) != allowed_fields:
166
+ filtered_sellers = [{k: v for k, v in seller.items() if k in field_list} for seller in sellers]
167
+ console.print(json.dumps(filtered_sellers, indent=2))
168
+ else:
169
+ console.print(json.dumps(sellers, indent=2))
111
170
  else:
112
171
  if not sellers:
113
172
  console.print("[yellow]No sellers found.[/yellow]")
114
173
  else:
115
174
  table = Table(title="Sellers")
116
- table.add_column("ID", style="cyan")
117
- table.add_column("Name", style="green")
118
- table.add_column("Display Name", style="blue")
119
- table.add_column("Type", style="magenta")
120
- table.add_column("Contact Email", style="yellow")
121
- table.add_column("Active", style="white")
122
175
 
176
+ # Define column styles
177
+ field_styles = {
178
+ "id": "cyan",
179
+ "name": "green",
180
+ "display_name": "blue",
181
+ "seller_type": "magenta",
182
+ "contact_email": "yellow",
183
+ "secondary_contact_email": "yellow",
184
+ "homepage": "blue",
185
+ "description": "white",
186
+ "business_registration": "white",
187
+ "tax_id": "white",
188
+ "account_manager_id": "cyan",
189
+ "created_at": "white",
190
+ "updated_at": "white",
191
+ "status": "green",
192
+ }
193
+
194
+ # Define column headers
195
+ field_headers = {
196
+ "id": "ID",
197
+ "name": "Name",
198
+ "display_name": "Display Name",
199
+ "seller_type": "Type",
200
+ "contact_email": "Contact Email",
201
+ "secondary_contact_email": "Secondary Email",
202
+ "homepage": "Homepage",
203
+ "description": "Description",
204
+ "business_registration": "Business Reg",
205
+ "tax_id": "Tax ID",
206
+ "account_manager_id": "Account Manager ID",
207
+ "created_at": "Created At",
208
+ "updated_at": "Updated At",
209
+ "status": "Status",
210
+ }
211
+
212
+ # Add columns based on requested fields
213
+ for field in field_list:
214
+ header = field_headers.get(field, field.title())
215
+ style = field_styles.get(field, "white")
216
+ table.add_column(header, style=style)
217
+
218
+ # Add rows
123
219
  for seller in sellers:
124
- table.add_row(
125
- str(seller.get("id", "N/A")),
126
- seller.get("name", "N/A"),
127
- seller.get("display_name", "N/A"),
128
- seller.get("seller_type", "N/A"),
129
- seller.get("contact_email", "N/A"),
130
- "✓" if seller.get("is_active") else "✗",
131
- )
220
+ row = []
221
+ for field in field_list:
222
+ value = seller.get(field)
223
+ if value is None:
224
+ row.append("N/A")
225
+ elif isinstance(value, dict | list):
226
+ row.append(str(value)[:50]) # Truncate complex types
227
+ else:
228
+ row.append(str(value))
229
+ table.add_row(*row)
132
230
 
133
231
  console.print(table)
232
+ console.print(f"\n[green]Total:[/green] {len(sellers)} seller(s)")
134
233
  except ValueError as e:
135
234
  console.print(f"[red]✗[/red] {e}", style="bold red")
136
235
  raise typer.Exit(code=1)
@@ -147,33 +246,124 @@ def query_providers(
147
246
  "-f",
148
247
  help="Output format: table, json",
149
248
  ),
249
+ fields: str = typer.Option(
250
+ "id,name,display_name,status",
251
+ "--fields",
252
+ help=(
253
+ "Comma-separated list of fields to display. Available fields: "
254
+ "id, name, display_name, contact_email, secondary_contact_email, "
255
+ "homepage, description, status, created_at, updated_at"
256
+ ),
257
+ ),
150
258
  ):
151
- """Query all providers from the backend."""
259
+ """Query all providers from the backend.
260
+
261
+ Examples:
262
+ # Use default fields
263
+ unitysvc_services query providers
264
+
265
+ # Show only specific fields
266
+ unitysvc_services query providers --fields id,name,contact_email
267
+
268
+ # Show all available fields
269
+ unitysvc_services query providers --fields \\
270
+ id,name,display_name,contact_email,homepage,status,created_at,updated_at
271
+ """
272
+ # Parse fields list
273
+ field_list = [f.strip() for f in fields.split(",")]
274
+
275
+ # Define allowed fields from ProviderPublic model
276
+ allowed_fields = {
277
+ "id",
278
+ "name",
279
+ "display_name",
280
+ "contact_email",
281
+ "secondary_contact_email",
282
+ "homepage",
283
+ "description",
284
+ "status",
285
+ "created_at",
286
+ "updated_at",
287
+ }
288
+
289
+ # Validate fields
290
+ invalid_fields = [f for f in field_list if f not in allowed_fields]
291
+ if invalid_fields:
292
+ console.print(
293
+ f"[red]✗[/red] Invalid field(s): {', '.join(invalid_fields)}",
294
+ style="bold red",
295
+ )
296
+ console.print(f"[yellow]Allowed fields:[/yellow] {', '.join(sorted(allowed_fields))}")
297
+ raise typer.Exit(code=1)
298
+
152
299
  try:
153
300
  with ServiceDataQuery() as query:
154
301
  providers = query.list_providers()
155
302
 
156
303
  if format == "json":
157
- console.print(json.dumps(providers, indent=2))
304
+ # For JSON, filter fields if not all are requested
305
+ if set(field_list) != allowed_fields:
306
+ filtered_providers = [
307
+ {k: v for k, v in provider.items() if k in field_list} for provider in providers
308
+ ]
309
+ console.print(json.dumps(filtered_providers, indent=2))
310
+ else:
311
+ console.print(json.dumps(providers, indent=2))
158
312
  else:
159
313
  if not providers:
160
314
  console.print("[yellow]No providers found.[/yellow]")
161
315
  else:
162
316
  table = Table(title="Providers")
163
- table.add_column("ID", style="cyan")
164
- table.add_column("Name", style="green")
165
- table.add_column("Display Name", style="blue")
166
- table.add_column("Time Created", style="magenta")
167
317
 
318
+ # Define column styles
319
+ field_styles = {
320
+ "id": "cyan",
321
+ "name": "green",
322
+ "display_name": "blue",
323
+ "contact_email": "yellow",
324
+ "secondary_contact_email": "yellow",
325
+ "homepage": "blue",
326
+ "description": "white",
327
+ "status": "green",
328
+ "created_at": "magenta",
329
+ "updated_at": "magenta",
330
+ }
331
+
332
+ # Define column headers
333
+ field_headers = {
334
+ "id": "ID",
335
+ "name": "Name",
336
+ "display_name": "Display Name",
337
+ "contact_email": "Contact Email",
338
+ "secondary_contact_email": "Secondary Email",
339
+ "homepage": "Homepage",
340
+ "description": "Description",
341
+ "status": "Status",
342
+ "created_at": "Created At",
343
+ "updated_at": "Updated At",
344
+ }
345
+
346
+ # Add columns based on requested fields
347
+ for field in field_list:
348
+ header = field_headers.get(field, field.title())
349
+ style = field_styles.get(field, "white")
350
+ table.add_column(header, style=style)
351
+
352
+ # Add rows
168
353
  for provider in providers:
169
- table.add_row(
170
- str(provider.get("id", "N/A")),
171
- provider.get("name", "N/A"),
172
- provider.get("display_name", "N/A"),
173
- str(provider.get("time_created", "N/A")),
174
- )
354
+ row = []
355
+ for field in field_list:
356
+ value = provider.get(field)
357
+ if value is None:
358
+ row.append("N/A")
359
+ elif isinstance(value, dict | list):
360
+ row.append(str(value)[:50]) # Truncate complex types
361
+ else:
362
+ row.append(str(value))
363
+ table.add_row(*row)
175
364
 
176
365
  console.print(table)
366
+ console.print(f"\n[green]Total:[/green] {len(providers)} provider(s)")
177
367
  except ValueError as e:
178
368
  console.print(f"[red]✗[/red] {e}", style="bold red")
179
369
  raise typer.Exit(code=1)
@@ -190,35 +380,91 @@ def query_offerings(
190
380
  "-f",
191
381
  help="Output format: table, json",
192
382
  ),
383
+ fields: str = typer.Option(
384
+ "id,service_name,service_type,provider_name,status",
385
+ "--fields",
386
+ help=(
387
+ "Comma-separated list of fields to display. Available fields: "
388
+ "id, definition_id, provider_id, status, price, service_name, "
389
+ "service_type, provider_name"
390
+ ),
391
+ ),
193
392
  ):
194
- """Query all service offerings from UnitySVC backend."""
393
+ """Query all service offerings from UnitySVC backend.
394
+
395
+ Examples:
396
+ # Use default fields
397
+ unitysvc_services query offerings
398
+
399
+ # Show only specific fields
400
+ unitysvc_services query offerings --fields id,service_name,status
401
+
402
+ # Show all available fields
403
+ unitysvc_services query offerings --fields \\
404
+ id,service_name,service_type,provider_name,status,price,definition_id,provider_id
405
+ """
406
+ # Parse fields list
407
+ field_list = [f.strip() for f in fields.split(",")]
408
+
409
+ # Define allowed fields from ServiceOfferingPublic model
410
+ allowed_fields = {
411
+ "id",
412
+ "definition_id",
413
+ "provider_id",
414
+ "status",
415
+ "price",
416
+ "service_name",
417
+ "service_type",
418
+ "provider_name",
419
+ }
420
+
421
+ # Validate fields
422
+ invalid_fields = [f for f in field_list if f not in allowed_fields]
423
+ if invalid_fields:
424
+ console.print(
425
+ f"[red]Error:[/red] Invalid field(s): {', '.join(invalid_fields)}",
426
+ style="bold red",
427
+ )
428
+ console.print(f"[yellow]Available fields:[/yellow] {', '.join(sorted(allowed_fields))}")
429
+ raise typer.Exit(code=1)
430
+
195
431
  try:
196
432
  with ServiceDataQuery() as query:
197
433
  offerings = query.list_service_offerings()
198
434
 
199
435
  if format == "json":
200
- console.print(json.dumps(offerings, indent=2))
436
+ # For JSON, filter fields if not all are requested
437
+ if set(field_list) != allowed_fields:
438
+ filtered_offerings = [
439
+ {k: v for k, v in offering.items() if k in field_list} for offering in offerings
440
+ ]
441
+ console.print(json.dumps(filtered_offerings, indent=2))
442
+ else:
443
+ console.print(json.dumps(offerings, indent=2))
201
444
  else:
202
445
  if not offerings:
203
446
  console.print("[yellow]No service offerings found.[/yellow]")
204
447
  else:
205
- table = Table(title="Service Offerings", show_lines=True)
206
- table.add_column("ID", style="cyan")
207
- table.add_column("Name", style="green")
208
- table.add_column("Display Name", style="blue")
209
- table.add_column("Type", style="magenta")
210
- table.add_column("Status", style="yellow")
211
- table.add_column("Version")
448
+ table = Table(title="Service Offerings")
449
+
450
+ # Add columns dynamically based on selected fields
451
+ for field in field_list:
452
+ # Capitalize and format field names for display
453
+ column_name = field.replace("_", " ").title()
454
+ table.add_column(column_name)
212
455
 
456
+ # Add rows
213
457
  for offering in offerings:
214
- table.add_row(
215
- str(offering.get("id", "N/A")),
216
- offering.get("name", "N/A"),
217
- offering.get("display_name", "N/A"),
218
- offering.get("service_type", "N/A"),
219
- offering.get("upstream_status", "N/A"),
220
- offering.get("version", "N/A"),
221
- )
458
+ row = []
459
+ for field in field_list:
460
+ value = offering.get(field)
461
+ if value is None:
462
+ row.append("N/A")
463
+ elif isinstance(value, dict | list):
464
+ row.append(str(value)[:50]) # Truncate complex types
465
+ else:
466
+ row.append(str(value))
467
+ table.add_row(*row)
222
468
 
223
469
  console.print(table)
224
470
  console.print(f"\n[green]Total:[/green] {len(offerings)} service offering(s)")
@@ -238,34 +484,96 @@ def query_listings(
238
484
  "-f",
239
485
  help="Output format: table, json",
240
486
  ),
487
+ fields: str = typer.Option(
488
+ "id,service_name,service_type,seller_name,listing_type,status",
489
+ "--fields",
490
+ help=(
491
+ "Comma-separated list of fields to display. Available fields: "
492
+ "id, offering_id, seller_id, status, created_at, updated_at, "
493
+ "parameters_schema, parameters_ui_schema, tags, service_name, "
494
+ "service_type, provider_name, seller_name, listing_type"
495
+ ),
496
+ ),
241
497
  ):
242
- """Query all service listings from UnitySVC backend."""
498
+ """Query all service listings from UnitySVC backend.
499
+
500
+ Examples:
501
+ # Use default fields
502
+ unitysvc_services query listings
503
+
504
+ # Show only specific fields
505
+ unitysvc_services query listings --fields id,service_name,status
506
+
507
+ # Show all available fields
508
+ unitysvc_services query listings --fields \\
509
+ id,service_name,service_type,seller_name,listing_type,status,provider_name
510
+ """
511
+ # Parse fields list
512
+ field_list = [f.strip() for f in fields.split(",")]
513
+
514
+ # Define allowed fields from ServiceListingPublic model
515
+ allowed_fields = {
516
+ "id",
517
+ "offering_id",
518
+ "seller_id",
519
+ "status",
520
+ "created_at",
521
+ "updated_at",
522
+ "parameters_schema",
523
+ "parameters_ui_schema",
524
+ "tags",
525
+ "service_name",
526
+ "service_type",
527
+ "provider_name",
528
+ "seller_name",
529
+ "listing_type",
530
+ }
531
+
532
+ # Validate fields
533
+ invalid_fields = [f for f in field_list if f not in allowed_fields]
534
+ if invalid_fields:
535
+ console.print(
536
+ f"[red]Error:[/red] Invalid field(s): {', '.join(invalid_fields)}",
537
+ style="bold red",
538
+ )
539
+ console.print(f"[yellow]Available fields:[/yellow] {', '.join(sorted(allowed_fields))}")
540
+ raise typer.Exit(code=1)
541
+
243
542
  try:
244
543
  with ServiceDataQuery() as query:
245
544
  listings = query.list_service_listings()
246
545
 
247
546
  if format == "json":
248
- console.print(json.dumps(listings, indent=2))
547
+ # For JSON, filter fields if not all are requested
548
+ if set(field_list) != allowed_fields:
549
+ filtered_listings = [{k: v for k, v in listing.items() if k in field_list} for listing in listings]
550
+ console.print(json.dumps(filtered_listings, indent=2))
551
+ else:
552
+ console.print(json.dumps(listings, indent=2))
249
553
  else:
250
554
  if not listings:
251
555
  console.print("[yellow]No service listings found.[/yellow]")
252
556
  else:
253
- table = Table(title="Service Listings", show_lines=True)
254
- table.add_column("ID", style="cyan")
255
- table.add_column("Service ID", style="blue")
256
- table.add_column("Seller", style="green")
257
- table.add_column("Status", style="yellow")
258
- table.add_column("Interfaces")
557
+ table = Table(title="Service Listings")
259
558
 
559
+ # Add columns dynamically based on selected fields
560
+ for field in field_list:
561
+ # Capitalize and format field names for display
562
+ column_name = field.replace("_", " ").title()
563
+ table.add_column(column_name)
564
+
565
+ # Add rows
260
566
  for listing in listings:
261
- interfaces_count = len(listing.get("user_access_interfaces", []))
262
- table.add_row(
263
- str(listing.get("id", "N/A")),
264
- str(listing.get("service_id", "N/A")),
265
- listing.get("seller_name", "N/A"),
266
- listing.get("listing_status", "N/A"),
267
- str(interfaces_count),
268
- )
567
+ row = []
568
+ for field in field_list:
569
+ value = listing.get(field)
570
+ if value is None:
571
+ row.append("N/A")
572
+ elif isinstance(value, dict | list):
573
+ row.append(str(value)[:50]) # Truncate complex types
574
+ else:
575
+ row.append(str(value))
576
+ table.add_row(*row)
269
577
 
270
578
  console.print(table)
271
579
  console.print(f"\n[green]Total:[/green] {len(listings)} service listing(s)")
@@ -285,36 +593,101 @@ def query_interfaces(
285
593
  "-f",
286
594
  help="Output format: table, json",
287
595
  ),
596
+ fields: str = typer.Option(
597
+ "id,name,context_type,access_method,is_active",
598
+ "--fields",
599
+ help=(
600
+ "Comma-separated list of fields to display. Available fields: "
601
+ "id, entity_id, context_type, access_method, api_endpoint, name, "
602
+ "description, request_transformer, rate_limits, constraint, "
603
+ "is_active, is_primary, sort_order, created_at, updated_at"
604
+ ),
605
+ ),
288
606
  ):
289
- """Query all access interfaces from UnitySVC backend (private endpoint)."""
607
+ """Query all access interfaces from UnitySVC backend (private endpoint).
608
+
609
+ Examples:
610
+ # Use default fields
611
+ unitysvc_services query interfaces
612
+
613
+ # Show only specific fields
614
+ unitysvc_services query interfaces --fields id,name,access_method
615
+
616
+ # Show all available fields
617
+ unitysvc_services query interfaces --fields \\
618
+ id,name,context_type,access_method,entity_id,is_active,is_primary
619
+ """
620
+ # Parse fields list
621
+ field_list = [f.strip() for f in fields.split(",")]
622
+
623
+ # Define allowed fields from AccessInterfacePublic model
624
+ allowed_fields = {
625
+ "id",
626
+ "entity_id",
627
+ "context_type",
628
+ "access_method",
629
+ "api_endpoint",
630
+ "name",
631
+ "description",
632
+ "request_transformer",
633
+ "rate_limits",
634
+ "constraint",
635
+ "is_active",
636
+ "is_primary",
637
+ "sort_order",
638
+ "created_at",
639
+ "updated_at",
640
+ }
641
+
642
+ # Validate fields
643
+ invalid_fields = [f for f in field_list if f not in allowed_fields]
644
+ if invalid_fields:
645
+ console.print(
646
+ f"[red]Error:[/red] Invalid field(s): {', '.join(invalid_fields)}",
647
+ style="bold red",
648
+ )
649
+ console.print(f"[yellow]Available fields:[/yellow] {', '.join(sorted(allowed_fields))}")
650
+ raise typer.Exit(code=1)
651
+
290
652
  try:
291
653
  with ServiceDataQuery() as query:
292
654
  data = query.list_access_interfaces()
293
655
 
294
656
  if format == "json":
295
- console.print(json.dumps(data, indent=2))
657
+ # For JSON, filter fields if not all are requested
658
+ interfaces = data.get("data", [])
659
+ if set(field_list) != allowed_fields:
660
+ filtered_interfaces = [
661
+ {k: v for k, v in interface.items() if k in field_list} for interface in interfaces
662
+ ]
663
+ console.print(json.dumps({"data": filtered_interfaces, "count": data.get("count", 0)}, indent=2))
664
+ else:
665
+ console.print(json.dumps(data, indent=2))
296
666
  else:
297
667
  interfaces = data.get("data", [])
298
668
  if not interfaces:
299
669
  console.print("[yellow]No access interfaces found.[/yellow]")
300
670
  else:
301
- table = Table(title="Access Interfaces", show_lines=True)
302
- table.add_column("ID", style="cyan")
303
- table.add_column("Name", style="green")
304
- table.add_column("Context", style="blue")
305
- table.add_column("Entity ID", style="yellow")
306
- table.add_column("Method", style="magenta")
307
- table.add_column("Active", style="green")
671
+ table = Table(title="Access Interfaces")
672
+
673
+ # Add columns dynamically based on selected fields
674
+ for field in field_list:
675
+ # Capitalize and format field names for display
676
+ column_name = field.replace("_", " ").title()
677
+ table.add_column(column_name)
308
678
 
679
+ # Add rows
309
680
  for interface in interfaces:
310
- table.add_row(
311
- str(interface.get("id", "N/A"))[:8] + "...",
312
- interface.get("name", "N/A"),
313
- interface.get("context_type", "N/A"),
314
- str(interface.get("entity_id", "N/A"))[:8] + "...",
315
- interface.get("access_method", "N/A"),
316
- "✓" if interface.get("is_active") else "✗",
317
- )
681
+ row = []
682
+ for field in field_list:
683
+ value = interface.get(field)
684
+ if value is None:
685
+ row.append("N/A")
686
+ elif isinstance(value, dict | list):
687
+ row.append(str(value)[:50]) # Truncate complex types
688
+ else:
689
+ row.append(str(value))
690
+ table.add_row(*row)
318
691
 
319
692
  console.print(table)
320
693
  console.print(f"\n[green]Total:[/green] {data.get('count', 0)} access interface(s)")
@@ -334,36 +707,102 @@ def query_documents(
334
707
  "-f",
335
708
  help="Output format: table, json",
336
709
  ),
710
+ fields: str = typer.Option(
711
+ "id,title,category,mime_type,context_type,is_public",
712
+ "--fields",
713
+ help=(
714
+ "Comma-separated list of fields to display. Available fields: "
715
+ "id, entity_id, context_type, title, description, mime_type, "
716
+ "version, category, meta, sort_order, is_active, is_public, "
717
+ "external_url, object_key, filename, filesize, created_at, updated_at"
718
+ ),
719
+ ),
337
720
  ):
338
- """Query all documents from UnitySVC backend (private endpoint)."""
721
+ """Query all documents from UnitySVC backend (private endpoint).
722
+
723
+ Examples:
724
+ # Use default fields
725
+ unitysvc_services query documents
726
+
727
+ # Show only specific fields
728
+ unitysvc_services query documents --fields id,title,category
729
+
730
+ # Show all available fields
731
+ unitysvc_services query documents --fields \\
732
+ id,title,category,mime_type,context_type,is_public,filename,filesize
733
+ """
734
+ # Parse fields list
735
+ field_list = [f.strip() for f in fields.split(",")]
736
+
737
+ # Define allowed fields from DocumentPublic model
738
+ allowed_fields = {
739
+ "id",
740
+ "entity_id",
741
+ "context_type",
742
+ "title",
743
+ "description",
744
+ "mime_type",
745
+ "version",
746
+ "category",
747
+ "meta",
748
+ "sort_order",
749
+ "is_active",
750
+ "is_public",
751
+ "external_url",
752
+ "object_key",
753
+ "filename",
754
+ "filesize",
755
+ "created_at",
756
+ "updated_at",
757
+ }
758
+
759
+ # Validate fields
760
+ invalid_fields = [f for f in field_list if f not in allowed_fields]
761
+ if invalid_fields:
762
+ console.print(
763
+ f"[red]Error:[/red] Invalid field(s): {', '.join(invalid_fields)}",
764
+ style="bold red",
765
+ )
766
+ console.print(f"[yellow]Available fields:[/yellow] {', '.join(sorted(allowed_fields))}")
767
+ raise typer.Exit(code=1)
768
+
339
769
  try:
340
770
  with ServiceDataQuery() as query:
341
771
  data = query.list_documents()
342
772
 
343
773
  if format == "json":
344
- console.print(json.dumps(data, indent=2))
774
+ # For JSON, filter fields if not all are requested
775
+ documents = data.get("data", [])
776
+ if set(field_list) != allowed_fields:
777
+ filtered_documents = [{k: v for k, v in doc.items() if k in field_list} for doc in documents]
778
+ console.print(json.dumps({"data": filtered_documents, "count": data.get("count", 0)}, indent=2))
779
+ else:
780
+ console.print(json.dumps(data, indent=2))
345
781
  else:
346
782
  documents = data.get("data", [])
347
783
  if not documents:
348
784
  console.print("[yellow]No documents found.[/yellow]")
349
785
  else:
350
- table = Table(title="Documents", show_lines=True)
351
- table.add_column("ID", style="cyan")
352
- table.add_column("Title", style="green")
353
- table.add_column("Category", style="blue")
354
- table.add_column("MIME Type", style="yellow")
355
- table.add_column("Context", style="magenta")
356
- table.add_column("Public", style="red")
786
+ table = Table(title="Documents")
787
+
788
+ # Add columns dynamically based on selected fields
789
+ for field in field_list:
790
+ # Capitalize and format field names for display
791
+ column_name = field.replace("_", " ").title()
792
+ table.add_column(column_name)
357
793
 
794
+ # Add rows
358
795
  for doc in documents:
359
- table.add_row(
360
- str(doc.get("id", "N/A"))[:8] + "...",
361
- doc.get("title", "N/A")[:40],
362
- doc.get("category", "N/A"),
363
- doc.get("mime_type", "N/A"),
364
- doc.get("context_type", "N/A"),
365
- "✓" if doc.get("is_public") else "✗",
366
- )
796
+ row = []
797
+ for field in field_list:
798
+ value = doc.get(field)
799
+ if value is None:
800
+ row.append("N/A")
801
+ elif isinstance(value, dict | list):
802
+ row.append(str(value)[:50]) # Truncate complex types
803
+ else:
804
+ row.append(str(value))
805
+ table.add_row(*row)
367
806
 
368
807
  console.print(table)
369
808
  console.print(f"\n[green]Total:[/green] {data.get('count', 0)} document(s)")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitysvc-services
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: SDK for digital service providers on UnitySVC
5
5
  Author-email: Bo Peng <bo.peng@unitysvc.com>
6
6
  Maintainer-email: Bo Peng <bo.peng@unitysvc.com>
@@ -3,8 +3,9 @@ unitysvc_services/cli.py,sha256=OK0IZyAckxP15jRWU_W49hl3t7XcNRtd8BoDMyRKqNM,682
3
3
  unitysvc_services/format_data.py,sha256=Jl9Vj3fRX852fHSUa5DzO-oiFQwuQHC3WMCDNIlo1Lc,5460
4
4
  unitysvc_services/list.py,sha256=QDp9BByaoeFeJxXJN9RQ-jU99mH9Guq9ampfXCbpZmI,7033
5
5
  unitysvc_services/populate.py,sha256=zkcjIy8BWuQSO7JwiRNHKgGoxQvc3ujluUQdYixdBvY,6626
6
- unitysvc_services/publisher.py,sha256=srOwISXimWTbtIS9g6egLm7JHPl7wCvjoDlY17tQaeU,35375
7
- unitysvc_services/query.py,sha256=lxNxsgjuxJ0UEouYMorjG4MpklKi7UCYPKgdJH9MFTA,14553
6
+ unitysvc_services/publisher.py,sha256=qEkui7T7DQr_CfXzDvWMuTJrPpKbcldfwPi6Ocv-ILA,35367
7
+ unitysvc_services/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ unitysvc_services/query.py,sha256=p-fG1iosAuzdHYnaYmEe2zdHQVZzWk2LhWmu5B4sAdA,30203
8
9
  unitysvc_services/scaffold.py,sha256=Y73IX8vskImxSvxDgR0mvEFuAMYnBKfttn3bjcz3jmQ,40331
9
10
  unitysvc_services/update.py,sha256=K9swocTUnqqiSgARo6GmuzTzUySSpyqqPPW4xF7ZU-g,9659
10
11
  unitysvc_services/utils.py,sha256=GN0gkVTU8fOx2G0EbqnWmx8w9eFsoPfRprPjwCyPYkE,11371
@@ -15,9 +16,9 @@ unitysvc_services/models/listing_v1.py,sha256=CC_GXoN3NHJFzEQ3cBHDQpdUaBNpvLdHAR
15
16
  unitysvc_services/models/provider_v1.py,sha256=cYK5kDDmzQEnLvUC2C8dKz-ZXci7hVn3fjNrJkaSr10,2050
16
17
  unitysvc_services/models/seller_v1.py,sha256=SU4rqYAh9hE4EeUrEkqaVrLwusenV7MotPF77VcsRKo,3263
17
18
  unitysvc_services/models/service_v1.py,sha256=u16zqM3khrJoTw_v0d45tMcKXjko5k_v3w8xwUtZ6nM,2720
18
- unitysvc_services-0.2.1.dist-info/licenses/LICENSE,sha256=_p8V6A8OMPu2HIztn3O01v0-urZFwk0Dd3Yk_PTIlL8,1065
19
- unitysvc_services-0.2.1.dist-info/METADATA,sha256=KmRHbFRzKhpl0FGM8CCXE8nrAHMp5KJ0fdZBUvQHQgw,6515
20
- unitysvc_services-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
- unitysvc_services-0.2.1.dist-info/entry_points.txt,sha256=-vodnbPmo7QQmFu8jdG6sCyGRVM727w9Nhwp4Vwau_k,64
22
- unitysvc_services-0.2.1.dist-info/top_level.txt,sha256=GIotQj-Ro2ruR7eupM1r58PWqIHTAq647ORL7E2kneo,18
23
- unitysvc_services-0.2.1.dist-info/RECORD,,
19
+ unitysvc_services-0.2.3.dist-info/licenses/LICENSE,sha256=_p8V6A8OMPu2HIztn3O01v0-urZFwk0Dd3Yk_PTIlL8,1065
20
+ unitysvc_services-0.2.3.dist-info/METADATA,sha256=MqWBQxgCpX6dpqmS1K95RXr-2yhvS8YyHDqfE0mZiYU,6515
21
+ unitysvc_services-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
+ unitysvc_services-0.2.3.dist-info/entry_points.txt,sha256=-vodnbPmo7QQmFu8jdG6sCyGRVM727w9Nhwp4Vwau_k,64
23
+ unitysvc_services-0.2.3.dist-info/top_level.txt,sha256=GIotQj-Ro2ruR7eupM1r58PWqIHTAq647ORL7E2kneo,18
24
+ unitysvc_services-0.2.3.dist-info/RECORD,,