unitysvc-services 0.2.2__py3-none-any.whl → 0.2.4__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.
@@ -106,6 +106,11 @@ class ServiceDataPublisher:
106
106
 
107
107
  # Resolve file references and include content
108
108
  base_path = data_file.parent
109
+ data = convert_convenience_fields_to_documents(
110
+ data, base_path, logo_field="logo", terms_field="terms_of_service"
111
+ )
112
+
113
+ # Resolve file references and include content
109
114
  data_with_content = self.resolve_file_references(data, base_path)
110
115
 
111
116
  # Extract provider_name from directory structure
@@ -139,7 +144,7 @@ class ServiceDataPublisher:
139
144
 
140
145
  # Post to the endpoint
141
146
  response = self.client.post(
142
- f"{self.base_url}/publish/service_offering",
147
+ f"{self.base_url}/publish/offering",
143
148
  json=data_with_content,
144
149
  )
145
150
  response.raise_for_status()
@@ -250,7 +255,7 @@ class ServiceDataPublisher:
250
255
 
251
256
  # Post to the endpoint
252
257
  response = self.client.post(
253
- f"{self.base_url}/publish/service_listing",
258
+ f"{self.base_url}/publish/listing",
254
259
  json=data_with_content,
255
260
  )
256
261
  response.raise_for_status()
@@ -39,45 +39,65 @@ class ServiceDataQuery:
39
39
  timeout=30.0,
40
40
  )
41
41
 
42
- def list_service_offerings(self) -> list[dict[str, Any]]:
43
- """List all service offerings from the backend."""
44
- response = self.client.get(f"{self.base_url}/publish/service_offerings")
45
- response.raise_for_status()
46
- result = response.json()
47
- return result.get("data", result) if isinstance(result, dict) else result
42
+ def list_service_offerings(self, skip: int = 0, limit: int = 100) -> list[dict[str, Any]]:
43
+ """List all service offerings from the backend.
48
44
 
49
- def list_service_listings(self) -> list[dict[str, Any]]:
50
- """List all service listings from the backend."""
51
- response = self.client.get(f"{self.base_url}/publish/services")
45
+ Args:
46
+ skip: Number of records to skip (for pagination)
47
+ limit: Maximum number of records to return
48
+ """
49
+ response = self.client.get(
50
+ f"{self.base_url}/publish/offerings",
51
+ params={"skip": skip, "limit": limit}
52
+ )
52
53
  response.raise_for_status()
53
54
  result = response.json()
54
55
  return result.get("data", result) if isinstance(result, dict) else result
55
56
 
56
- def list_providers(self) -> list[dict[str, Any]]:
57
- """List all providers from the backend."""
58
- response = self.client.get(f"{self.base_url}/publish/providers")
57
+ def list_service_listings(self, skip: int = 0, limit: int = 100) -> list[dict[str, Any]]:
58
+ """List all service listings from the backend.
59
+
60
+ Args:
61
+ skip: Number of records to skip (for pagination)
62
+ limit: Maximum number of records to return
63
+ """
64
+ response = self.client.get(
65
+ f"{self.base_url}/publish/listings",
66
+ params={"skip": skip, "limit": limit}
67
+ )
59
68
  response.raise_for_status()
60
69
  result = response.json()
61
70
  return result.get("data", result) if isinstance(result, dict) else result
62
71
 
63
- def list_sellers(self) -> list[dict[str, Any]]:
64
- """List all sellers from the backend."""
65
- response = self.client.get(f"{self.base_url}/publish/sellers")
72
+ def list_providers(self, skip: int = 0, limit: int = 100) -> list[dict[str, Any]]:
73
+ """List all providers from the backend.
74
+
75
+ Args:
76
+ skip: Number of records to skip (for pagination)
77
+ limit: Maximum number of records to return
78
+ """
79
+ response = self.client.get(
80
+ f"{self.base_url}/publish/providers",
81
+ params={"skip": skip, "limit": limit}
82
+ )
66
83
  response.raise_for_status()
67
84
  result = response.json()
68
85
  return result.get("data", result) if isinstance(result, dict) else result
69
86
 
70
- def list_access_interfaces(self) -> dict[str, Any]:
71
- """List all access interfaces from the backend (private endpoint)."""
72
- response = self.client.get(f"{self.base_url}/private/access_interfaces")
73
- response.raise_for_status()
74
- return response.json()
87
+ def list_sellers(self, skip: int = 0, limit: int = 100) -> list[dict[str, Any]]:
88
+ """List all sellers from the backend.
75
89
 
76
- def list_documents(self) -> dict[str, Any]:
77
- """List all documents from the backend (private endpoint)."""
78
- response = self.client.get(f"{self.base_url}/private/documents")
90
+ Args:
91
+ skip: Number of records to skip (for pagination)
92
+ limit: Maximum number of records to return
93
+ """
94
+ response = self.client.get(
95
+ f"{self.base_url}/publish/sellers",
96
+ params={"skip": skip, "limit": limit}
97
+ )
79
98
  response.raise_for_status()
80
- return response.json()
99
+ result = response.json()
100
+ return result.get("data", result) if isinstance(result, dict) else result
81
101
 
82
102
  def close(self):
83
103
  """Close the HTTP client."""
@@ -100,37 +120,152 @@ def query_sellers(
100
120
  "-f",
101
121
  help="Output format: table, json",
102
122
  ),
123
+ fields: str = typer.Option(
124
+ "id,name,display_name,seller_type",
125
+ "--fields",
126
+ help=(
127
+ "Comma-separated list of fields to display. Available fields: "
128
+ "id, name, display_name, seller_type, contact_email, "
129
+ "secondary_contact_email, homepage, description, "
130
+ "business_registration, tax_id, account_manager_id, "
131
+ "created_at, updated_at, status"
132
+ ),
133
+ ),
134
+ skip: int = typer.Option(
135
+ 0,
136
+ "--skip",
137
+ help="Number of records to skip (for pagination)",
138
+ ),
139
+ limit: int = typer.Option(
140
+ 100,
141
+ "--limit",
142
+ help="Maximum number of records to return (default: 100)",
143
+ ),
103
144
  ):
104
- """Query all sellers from the backend."""
145
+ """Query all sellers from the backend.
146
+
147
+ Examples:
148
+ # Use default fields
149
+ unitysvc_services query sellers
150
+
151
+ # Show only specific fields
152
+ unitysvc_services query sellers --fields id,name,contact_email
153
+
154
+ # Retrieve more than 100 records
155
+ unitysvc_services query sellers --limit 500
156
+
157
+ # Pagination: skip first 100, get next 100
158
+ unitysvc_services query sellers --skip 100 --limit 100
159
+
160
+ # Show all available fields
161
+ unitysvc_services query sellers --fields \\
162
+ id,name,display_name,seller_type,contact_email,homepage,created_at,updated_at
163
+ """
164
+ # Parse fields list
165
+ field_list = [f.strip() for f in fields.split(",")]
166
+
167
+ # Define allowed fields from SellerPublic model
168
+ allowed_fields = {
169
+ "id",
170
+ "name",
171
+ "display_name",
172
+ "seller_type",
173
+ "contact_email",
174
+ "secondary_contact_email",
175
+ "homepage",
176
+ "description",
177
+ "business_registration",
178
+ "tax_id",
179
+ "account_manager_id",
180
+ "created_at",
181
+ "updated_at",
182
+ "status",
183
+ }
184
+
185
+ # Validate fields
186
+ invalid_fields = [f for f in field_list if f not in allowed_fields]
187
+ if invalid_fields:
188
+ console.print(
189
+ f"[red]✗[/red] Invalid field(s): {', '.join(invalid_fields)}",
190
+ style="bold red",
191
+ )
192
+ console.print(f"[yellow]Allowed fields:[/yellow] {', '.join(sorted(allowed_fields))}")
193
+ raise typer.Exit(code=1)
194
+
105
195
  try:
106
196
  with ServiceDataQuery() as query:
107
- sellers = query.list_sellers()
197
+ sellers = query.list_sellers(skip=skip, limit=limit)
108
198
 
109
199
  if format == "json":
110
- console.print(json.dumps(sellers, indent=2))
200
+ # For JSON, filter fields if not all are requested
201
+ if set(field_list) != allowed_fields:
202
+ filtered_sellers = [{k: v for k, v in seller.items() if k in field_list} for seller in sellers]
203
+ console.print(json.dumps(filtered_sellers, indent=2))
204
+ else:
205
+ console.print(json.dumps(sellers, indent=2))
111
206
  else:
112
207
  if not sellers:
113
208
  console.print("[yellow]No sellers found.[/yellow]")
114
209
  else:
115
210
  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
211
 
212
+ # Define column styles
213
+ field_styles = {
214
+ "id": "cyan",
215
+ "name": "green",
216
+ "display_name": "blue",
217
+ "seller_type": "magenta",
218
+ "contact_email": "yellow",
219
+ "secondary_contact_email": "yellow",
220
+ "homepage": "blue",
221
+ "description": "white",
222
+ "business_registration": "white",
223
+ "tax_id": "white",
224
+ "account_manager_id": "cyan",
225
+ "created_at": "white",
226
+ "updated_at": "white",
227
+ "status": "green",
228
+ }
229
+
230
+ # Define column headers
231
+ field_headers = {
232
+ "id": "ID",
233
+ "name": "Name",
234
+ "display_name": "Display Name",
235
+ "seller_type": "Type",
236
+ "contact_email": "Contact Email",
237
+ "secondary_contact_email": "Secondary Email",
238
+ "homepage": "Homepage",
239
+ "description": "Description",
240
+ "business_registration": "Business Reg",
241
+ "tax_id": "Tax ID",
242
+ "account_manager_id": "Account Manager ID",
243
+ "created_at": "Created At",
244
+ "updated_at": "Updated At",
245
+ "status": "Status",
246
+ }
247
+
248
+ # Add columns based on requested fields
249
+ for field in field_list:
250
+ header = field_headers.get(field, field.title())
251
+ style = field_styles.get(field, "white")
252
+ table.add_column(header, style=style)
253
+
254
+ # Add rows
123
255
  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
- )
256
+ row = []
257
+ for field in field_list:
258
+ value = seller.get(field)
259
+ if value is None:
260
+ row.append("N/A")
261
+ elif isinstance(value, dict | list):
262
+ row.append(str(value)[:50]) # Truncate complex types
263
+ else:
264
+ row.append(str(value))
265
+ table.add_row(*row)
132
266
 
133
267
  console.print(table)
268
+ console.print(f"\n[green]Total:[/green] {len(sellers)} seller(s)")
134
269
  except ValueError as e:
135
270
  console.print(f"[red]✗[/red] {e}", style="bold red")
136
271
  raise typer.Exit(code=1)
@@ -147,33 +282,140 @@ def query_providers(
147
282
  "-f",
148
283
  help="Output format: table, json",
149
284
  ),
285
+ fields: str = typer.Option(
286
+ "id,name,display_name,status",
287
+ "--fields",
288
+ help=(
289
+ "Comma-separated list of fields to display. Available fields: "
290
+ "id, name, display_name, contact_email, secondary_contact_email, "
291
+ "homepage, description, status, created_at, updated_at"
292
+ ),
293
+ ),
294
+ skip: int = typer.Option(
295
+ 0,
296
+ "--skip",
297
+ help="Number of records to skip (for pagination)",
298
+ ),
299
+ limit: int = typer.Option(
300
+ 100,
301
+ "--limit",
302
+ help="Maximum number of records to return (default: 100)",
303
+ ),
150
304
  ):
151
- """Query all providers from the backend."""
305
+ """Query all providers from the backend.
306
+
307
+ Examples:
308
+ # Use default fields
309
+ unitysvc_services query providers
310
+
311
+ # Retrieve more than 100 records
312
+ unitysvc_services query providers --limit 500
313
+
314
+ # Pagination: skip first 100, get next 100
315
+ unitysvc_services query providers --skip 100 --limit 100
316
+
317
+ # Show only specific fields
318
+ unitysvc_services query providers --fields id,name,contact_email
319
+
320
+ # Show all available fields
321
+ unitysvc_services query providers --fields \\
322
+ id,name,display_name,contact_email,homepage,status,created_at,updated_at
323
+ """
324
+ # Parse fields list
325
+ field_list = [f.strip() for f in fields.split(",")]
326
+
327
+ # Define allowed fields from ProviderPublic model
328
+ allowed_fields = {
329
+ "id",
330
+ "name",
331
+ "display_name",
332
+ "contact_email",
333
+ "secondary_contact_email",
334
+ "homepage",
335
+ "description",
336
+ "status",
337
+ "created_at",
338
+ "updated_at",
339
+ }
340
+
341
+ # Validate fields
342
+ invalid_fields = [f for f in field_list if f not in allowed_fields]
343
+ if invalid_fields:
344
+ console.print(
345
+ f"[red]✗[/red] Invalid field(s): {', '.join(invalid_fields)}",
346
+ style="bold red",
347
+ )
348
+ console.print(f"[yellow]Allowed fields:[/yellow] {', '.join(sorted(allowed_fields))}")
349
+ raise typer.Exit(code=1)
350
+
152
351
  try:
153
352
  with ServiceDataQuery() as query:
154
- providers = query.list_providers()
353
+ providers = query.list_providers(skip=skip, limit=limit)
155
354
 
156
355
  if format == "json":
157
- console.print(json.dumps(providers, indent=2))
356
+ # For JSON, filter fields if not all are requested
357
+ if set(field_list) != allowed_fields:
358
+ filtered_providers = [
359
+ {k: v for k, v in provider.items() if k in field_list} for provider in providers
360
+ ]
361
+ console.print(json.dumps(filtered_providers, indent=2))
362
+ else:
363
+ console.print(json.dumps(providers, indent=2))
158
364
  else:
159
365
  if not providers:
160
366
  console.print("[yellow]No providers found.[/yellow]")
161
367
  else:
162
368
  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
369
 
370
+ # Define column styles
371
+ field_styles = {
372
+ "id": "cyan",
373
+ "name": "green",
374
+ "display_name": "blue",
375
+ "contact_email": "yellow",
376
+ "secondary_contact_email": "yellow",
377
+ "homepage": "blue",
378
+ "description": "white",
379
+ "status": "green",
380
+ "created_at": "magenta",
381
+ "updated_at": "magenta",
382
+ }
383
+
384
+ # Define column headers
385
+ field_headers = {
386
+ "id": "ID",
387
+ "name": "Name",
388
+ "display_name": "Display Name",
389
+ "contact_email": "Contact Email",
390
+ "secondary_contact_email": "Secondary Email",
391
+ "homepage": "Homepage",
392
+ "description": "Description",
393
+ "status": "Status",
394
+ "created_at": "Created At",
395
+ "updated_at": "Updated At",
396
+ }
397
+
398
+ # Add columns based on requested fields
399
+ for field in field_list:
400
+ header = field_headers.get(field, field.title())
401
+ style = field_styles.get(field, "white")
402
+ table.add_column(header, style=style)
403
+
404
+ # Add rows
168
405
  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
- )
406
+ row = []
407
+ for field in field_list:
408
+ value = provider.get(field)
409
+ if value is None:
410
+ row.append("N/A")
411
+ elif isinstance(value, dict | list):
412
+ row.append(str(value)[:50]) # Truncate complex types
413
+ else:
414
+ row.append(str(value))
415
+ table.add_row(*row)
175
416
 
176
417
  console.print(table)
418
+ console.print(f"\n[green]Total:[/green] {len(providers)} provider(s)")
177
419
  except ValueError as e:
178
420
  console.print(f"[red]✗[/red] {e}", style="bold red")
179
421
  raise typer.Exit(code=1)
@@ -190,35 +432,107 @@ def query_offerings(
190
432
  "-f",
191
433
  help="Output format: table, json",
192
434
  ),
435
+ fields: str = typer.Option(
436
+ "id,service_name,service_type,provider_name,status",
437
+ "--fields",
438
+ help=(
439
+ "Comma-separated list of fields to display. Available fields: "
440
+ "id, definition_id, provider_id, status, price, service_name, "
441
+ "service_type, provider_name"
442
+ ),
443
+ ),
444
+ skip: int = typer.Option(
445
+ 0,
446
+ "--skip",
447
+ help="Number of records to skip (for pagination)",
448
+ ),
449
+ limit: int = typer.Option(
450
+ 100,
451
+ "--limit",
452
+ help="Maximum number of records to return (default: 100)",
453
+ ),
193
454
  ):
194
- """Query all service offerings from UnitySVC backend."""
455
+ """Query all service offerings from UnitySVC backend.
456
+
457
+ Examples:
458
+ # Use default fields
459
+ unitysvc_services query offerings
460
+
461
+ # Show only specific fields
462
+ unitysvc_services query offerings --fields id,service_name,status
463
+
464
+ # Retrieve more than 100 records
465
+ unitysvc_services query offerings --limit 500
466
+
467
+ # Pagination: skip first 100, get next 100
468
+ unitysvc_services query offerings --skip 100 --limit 100
469
+
470
+ # Show all available fields
471
+ unitysvc_services query offerings --fields \\
472
+ id,service_name,service_type,provider_name,status,price,definition_id,provider_id
473
+ """
474
+ # Parse fields list
475
+ field_list = [f.strip() for f in fields.split(",")]
476
+
477
+ # Define allowed fields from ServiceOfferingPublic model
478
+ allowed_fields = {
479
+ "id",
480
+ "definition_id",
481
+ "provider_id",
482
+ "status",
483
+ "price",
484
+ "service_name",
485
+ "service_type",
486
+ "provider_name",
487
+ }
488
+
489
+ # Validate fields
490
+ invalid_fields = [f for f in field_list if f not in allowed_fields]
491
+ if invalid_fields:
492
+ console.print(
493
+ f"[red]Error:[/red] Invalid field(s): {', '.join(invalid_fields)}",
494
+ style="bold red",
495
+ )
496
+ console.print(f"[yellow]Available fields:[/yellow] {', '.join(sorted(allowed_fields))}")
497
+ raise typer.Exit(code=1)
498
+
195
499
  try:
196
500
  with ServiceDataQuery() as query:
197
- offerings = query.list_service_offerings()
501
+ offerings = query.list_service_offerings(skip=skip, limit=limit)
198
502
 
199
503
  if format == "json":
200
- console.print(json.dumps(offerings, indent=2))
504
+ # For JSON, filter fields if not all are requested
505
+ if set(field_list) != allowed_fields:
506
+ filtered_offerings = [
507
+ {k: v for k, v in offering.items() if k in field_list} for offering in offerings
508
+ ]
509
+ console.print(json.dumps(filtered_offerings, indent=2))
510
+ else:
511
+ console.print(json.dumps(offerings, indent=2))
201
512
  else:
202
513
  if not offerings:
203
514
  console.print("[yellow]No service offerings found.[/yellow]")
204
515
  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")
516
+ table = Table(title="Service Offerings")
212
517
 
518
+ # Add columns dynamically based on selected fields
519
+ for field in field_list:
520
+ # Capitalize and format field names for display
521
+ column_name = field.replace("_", " ").title()
522
+ table.add_column(column_name)
523
+
524
+ # Add rows
213
525
  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
- )
526
+ row = []
527
+ for field in field_list:
528
+ value = offering.get(field)
529
+ if value is None:
530
+ row.append("N/A")
531
+ elif isinstance(value, dict | list):
532
+ row.append(str(value)[:50]) # Truncate complex types
533
+ else:
534
+ row.append(str(value))
535
+ table.add_row(*row)
222
536
 
223
537
  console.print(table)
224
538
  console.print(f"\n[green]Total:[/green] {len(offerings)} service offering(s)")
@@ -238,34 +552,113 @@ def query_listings(
238
552
  "-f",
239
553
  help="Output format: table, json",
240
554
  ),
555
+ fields: str = typer.Option(
556
+ "id,service_name,service_type,seller_name,listing_type,status",
557
+ "--fields",
558
+ help=(
559
+ "Comma-separated list of fields to display. Available fields: "
560
+ "id, offering_id, offering_status, seller_id, status, created_at, updated_at, "
561
+ "parameters_schema, parameters_ui_schema, tags, service_name, "
562
+ "service_type, provider_name, seller_name, listing_type"
563
+ ),
564
+ ),
565
+ skip: int = typer.Option(
566
+ 0,
567
+ "--skip",
568
+ help="Number of records to skip (for pagination)",
569
+ ),
570
+ limit: int = typer.Option(
571
+ 100,
572
+ "--limit",
573
+ help="Maximum number of records to return (default: 100)",
574
+ ),
241
575
  ):
242
- """Query all service listings from UnitySVC backend."""
576
+ """Query all service listings from UnitySVC backend.
577
+
578
+ Examples:
579
+ # Use default fields
580
+ unitysvc_services query listings
581
+
582
+ # Show only specific fields
583
+ unitysvc_services query listings --fields id,service_name,status
584
+
585
+ # Retrieve more than 100 records
586
+ unitysvc_services query listings --limit 500
587
+
588
+ # Pagination: skip first 100, get next 100
589
+ unitysvc_services query listings --skip 100 --limit 100
590
+
591
+ # Show all available fields
592
+ unitysvc_services query listings --fields \\
593
+ id,service_name,service_type,seller_name,listing_type,status,provider_name
594
+ """
595
+ # Parse fields list
596
+ field_list = [f.strip() for f in fields.split(",")]
597
+
598
+ # Define allowed fields from ServiceListingPublic model
599
+ allowed_fields = {
600
+ "id",
601
+ "offering_id",
602
+ "offering_status",
603
+ "seller_id",
604
+ "status",
605
+ "created_at",
606
+ "updated_at",
607
+ "parameters_schema",
608
+ "parameters_ui_schema",
609
+ "tags",
610
+ "service_name",
611
+ "service_type",
612
+ "provider_name",
613
+ "seller_name",
614
+ "listing_type",
615
+ }
616
+
617
+ # Validate fields
618
+ invalid_fields = [f for f in field_list if f not in allowed_fields]
619
+ if invalid_fields:
620
+ console.print(
621
+ f"[red]Error:[/red] Invalid field(s): {', '.join(invalid_fields)}",
622
+ style="bold red",
623
+ )
624
+ console.print(f"[yellow]Available fields:[/yellow] {', '.join(sorted(allowed_fields))}")
625
+ raise typer.Exit(code=1)
626
+
243
627
  try:
244
628
  with ServiceDataQuery() as query:
245
- listings = query.list_service_listings()
629
+ listings = query.list_service_listings(skip=skip, limit=limit)
246
630
 
247
631
  if format == "json":
248
- console.print(json.dumps(listings, indent=2))
632
+ # For JSON, filter fields if not all are requested
633
+ if set(field_list) != allowed_fields:
634
+ filtered_listings = [{k: v for k, v in listing.items() if k in field_list} for listing in listings]
635
+ console.print(json.dumps(filtered_listings, indent=2))
636
+ else:
637
+ console.print(json.dumps(listings, indent=2))
249
638
  else:
250
639
  if not listings:
251
640
  console.print("[yellow]No service listings found.[/yellow]")
252
641
  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")
642
+ table = Table(title="Service Listings")
259
643
 
644
+ # Add columns dynamically based on selected fields
645
+ for field in field_list:
646
+ # Capitalize and format field names for display
647
+ column_name = field.replace("_", " ").title()
648
+ table.add_column(column_name)
649
+
650
+ # Add rows
260
651
  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
- )
652
+ row = []
653
+ for field in field_list:
654
+ value = listing.get(field)
655
+ if value is None:
656
+ row.append("N/A")
657
+ elif isinstance(value, dict | list):
658
+ row.append(str(value)[:50]) # Truncate complex types
659
+ else:
660
+ row.append(str(value))
661
+ table.add_row(*row)
269
662
 
270
663
  console.print(table)
271
664
  console.print(f"\n[green]Total:[/green] {len(listings)} service listing(s)")
@@ -275,101 +668,3 @@ def query_listings(
275
668
  except Exception as e:
276
669
  console.print(f"[red]✗[/red] Failed to query service listings: {e}", style="bold red")
277
670
  raise typer.Exit(code=1)
278
-
279
-
280
- @app.command("interfaces")
281
- def query_interfaces(
282
- format: str = typer.Option(
283
- "table",
284
- "--format",
285
- "-f",
286
- help="Output format: table, json",
287
- ),
288
- ):
289
- """Query all access interfaces from UnitySVC backend (private endpoint)."""
290
- try:
291
- with ServiceDataQuery() as query:
292
- data = query.list_access_interfaces()
293
-
294
- if format == "json":
295
- console.print(json.dumps(data, indent=2))
296
- else:
297
- interfaces = data.get("data", [])
298
- if not interfaces:
299
- console.print("[yellow]No access interfaces found.[/yellow]")
300
- 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")
308
-
309
- 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
- )
318
-
319
- console.print(table)
320
- console.print(f"\n[green]Total:[/green] {data.get('count', 0)} access interface(s)")
321
- except ValueError as e:
322
- console.print(f"[red]✗[/red] {e}", style="bold red")
323
- raise typer.Exit(code=1)
324
- except Exception as e:
325
- console.print(f"[red]✗[/red] Failed to query access interfaces: {e}", style="bold red")
326
- raise typer.Exit(code=1)
327
-
328
-
329
- @app.command("documents")
330
- def query_documents(
331
- format: str = typer.Option(
332
- "table",
333
- "--format",
334
- "-f",
335
- help="Output format: table, json",
336
- ),
337
- ):
338
- """Query all documents from UnitySVC backend (private endpoint)."""
339
- try:
340
- with ServiceDataQuery() as query:
341
- data = query.list_documents()
342
-
343
- if format == "json":
344
- console.print(json.dumps(data, indent=2))
345
- else:
346
- documents = data.get("data", [])
347
- if not documents:
348
- console.print("[yellow]No documents found.[/yellow]")
349
- 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")
357
-
358
- 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
- )
367
-
368
- console.print(table)
369
- console.print(f"\n[green]Total:[/green] {data.get('count', 0)} document(s)")
370
- except ValueError as e:
371
- console.print(f"[red]✗[/red] {e}", style="bold red")
372
- raise typer.Exit(code=1)
373
- except Exception as e:
374
- console.print(f"[red]✗[/red] Failed to query documents: {e}", style="bold red")
375
- raise typer.Exit(code=1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitysvc-services
3
- Version: 0.2.2
3
+ Version: 0.2.4
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>
@@ -87,8 +87,11 @@ unitysvc_services publish
87
87
  # Or publish specific types only
88
88
  unitysvc_services publish providers
89
89
 
90
- # Verify
90
+ # Verify with default fields
91
91
  unitysvc_services query offerings
92
+
93
+ # Query with custom fields
94
+ unitysvc_services query providers --fields id,name,contact_email
92
95
  ```
93
96
 
94
97
  ## Key Features
@@ -3,9 +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=qLIEl2Ts_xCaCkIv8MC_8Up_fA6xIaK2aX4ZF_NVa8g,35383
6
+ unitysvc_services/publisher.py,sha256=hRqbN9rXeFLgfsjK9ViFQjPNbYR3trcqanPUecjcSjM,35567
7
7
  unitysvc_services/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- unitysvc_services/query.py,sha256=VHMaLCWAF5TyYhSkRnVzjhAZornGpSdET_QMvAi-G8k,14561
8
+ unitysvc_services/query.py,sha256=yfCIMTjQwantf9LxktpRcE05qrBIKQMkanpb9JYYsi0,24228
9
9
  unitysvc_services/scaffold.py,sha256=Y73IX8vskImxSvxDgR0mvEFuAMYnBKfttn3bjcz3jmQ,40331
10
10
  unitysvc_services/update.py,sha256=K9swocTUnqqiSgARo6GmuzTzUySSpyqqPPW4xF7ZU-g,9659
11
11
  unitysvc_services/utils.py,sha256=GN0gkVTU8fOx2G0EbqnWmx8w9eFsoPfRprPjwCyPYkE,11371
@@ -16,9 +16,9 @@ unitysvc_services/models/listing_v1.py,sha256=CC_GXoN3NHJFzEQ3cBHDQpdUaBNpvLdHAR
16
16
  unitysvc_services/models/provider_v1.py,sha256=cYK5kDDmzQEnLvUC2C8dKz-ZXci7hVn3fjNrJkaSr10,2050
17
17
  unitysvc_services/models/seller_v1.py,sha256=SU4rqYAh9hE4EeUrEkqaVrLwusenV7MotPF77VcsRKo,3263
18
18
  unitysvc_services/models/service_v1.py,sha256=u16zqM3khrJoTw_v0d45tMcKXjko5k_v3w8xwUtZ6nM,2720
19
- unitysvc_services-0.2.2.dist-info/licenses/LICENSE,sha256=_p8V6A8OMPu2HIztn3O01v0-urZFwk0Dd3Yk_PTIlL8,1065
20
- unitysvc_services-0.2.2.dist-info/METADATA,sha256=pmJ6eY72bkhsOuiKzAKUGspW6C9vSS6UwgMVHLBJQVY,6515
21
- unitysvc_services-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
- unitysvc_services-0.2.2.dist-info/entry_points.txt,sha256=-vodnbPmo7QQmFu8jdG6sCyGRVM727w9Nhwp4Vwau_k,64
23
- unitysvc_services-0.2.2.dist-info/top_level.txt,sha256=GIotQj-Ro2ruR7eupM1r58PWqIHTAq647ORL7E2kneo,18
24
- unitysvc_services-0.2.2.dist-info/RECORD,,
19
+ unitysvc_services-0.2.4.dist-info/licenses/LICENSE,sha256=_p8V6A8OMPu2HIztn3O01v0-urZFwk0Dd3Yk_PTIlL8,1065
20
+ unitysvc_services-0.2.4.dist-info/METADATA,sha256=FmrqIeMrb31pjtn7u0RyBz-tn-acfTo0omSIqLLwdNg,6628
21
+ unitysvc_services-0.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
+ unitysvc_services-0.2.4.dist-info/entry_points.txt,sha256=-vodnbPmo7QQmFu8jdG6sCyGRVM727w9Nhwp4Vwau_k,64
23
+ unitysvc_services-0.2.4.dist-info/top_level.txt,sha256=GIotQj-Ro2ruR7eupM1r58PWqIHTAq647ORL7E2kneo,18
24
+ unitysvc_services-0.2.4.dist-info/RECORD,,