collibra-connector 1.0.19__py3-none-any.whl → 1.1.0__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.
@@ -0,0 +1,597 @@
1
+ """
2
+ Command-Line Interface for Collibra Connector.
3
+
4
+ This module provides a CLI tool for interacting with Collibra
5
+ directly from the terminal without writing code.
6
+
7
+ Installation:
8
+ pip install collibra-connector[cli]
9
+
10
+ Usage:
11
+ collibra-sdk --help
12
+ collibra-sdk search "Business Term" --format json
13
+ collibra-sdk export-domain --id "uuid" --output report.csv
14
+ collibra-sdk get-asset --id "uuid"
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import csv
19
+ import json
20
+ import os
21
+ import sys
22
+ from typing import Any, Dict, List, Optional
23
+
24
+ try:
25
+ import click
26
+ CLICK_AVAILABLE = True
27
+ except ImportError:
28
+ CLICK_AVAILABLE = False
29
+
30
+ if CLICK_AVAILABLE:
31
+ from click import Context, echo, style, secho
32
+
33
+
34
+ def get_connector():
35
+ """Create connector from environment variables."""
36
+ from .connector import CollibraConnector
37
+
38
+ api = os.environ.get("COLLIBRA_URL")
39
+ username = os.environ.get("COLLIBRA_USERNAME")
40
+ password = os.environ.get("COLLIBRA_PASSWORD")
41
+
42
+ if not all([api, username, password]):
43
+ raise click.ClickException(
44
+ "Missing environment variables. Set:\n"
45
+ " COLLIBRA_URL=https://your-instance.collibra.com\n"
46
+ " COLLIBRA_USERNAME=your-username\n"
47
+ " COLLIBRA_PASSWORD=your-password"
48
+ )
49
+
50
+ return CollibraConnector(api=api, username=username, password=password)
51
+
52
+
53
+ def format_output(data: Any, fmt: str) -> str:
54
+ """Format output data based on format type."""
55
+ if fmt == "json":
56
+ return json.dumps(data, indent=2, default=str)
57
+ elif fmt == "table":
58
+ if isinstance(data, list) and data:
59
+ # Simple table format
60
+ headers = list(data[0].keys())
61
+ lines = ["\t".join(headers)]
62
+ for item in data:
63
+ lines.append("\t".join(str(item.get(h, "")) for h in headers))
64
+ return "\n".join(lines)
65
+ return str(data)
66
+ elif fmt == "csv":
67
+ if isinstance(data, list) and data:
68
+ import io
69
+ output = io.StringIO()
70
+ writer = csv.DictWriter(output, fieldnames=data[0].keys())
71
+ writer.writeheader()
72
+ writer.writerows(data)
73
+ return output.getvalue()
74
+ return str(data)
75
+ else:
76
+ return str(data)
77
+
78
+
79
+ if CLICK_AVAILABLE:
80
+ @click.group()
81
+ @click.version_option(version="1.1.0", prog_name="collibra-sdk")
82
+ @click.pass_context
83
+ def cli(ctx: Context) -> None:
84
+ """
85
+ Collibra SDK - Command line interface for Collibra Data Governance.
86
+
87
+ Configure connection using environment variables:
88
+
89
+ export COLLIBRA_URL=https://your-instance.collibra.com
90
+
91
+ export COLLIBRA_USERNAME=your-username
92
+
93
+ export COLLIBRA_PASSWORD=your-password
94
+ """
95
+ ctx.ensure_object(dict)
96
+
97
+ # ==========================================================================
98
+ # Connection Commands
99
+ # ==========================================================================
100
+
101
+ @cli.command("test")
102
+ def test_connection() -> None:
103
+ """Test the connection to Collibra."""
104
+ try:
105
+ conn = get_connector()
106
+ if conn.test_connection():
107
+ secho("Connection successful!", fg="green")
108
+ else:
109
+ secho("Connection failed.", fg="red")
110
+ sys.exit(1)
111
+ except Exception as e:
112
+ secho(f"Error: {e}", fg="red")
113
+ sys.exit(1)
114
+
115
+ @cli.command("info")
116
+ def show_info() -> None:
117
+ """Show connection and version information."""
118
+ try:
119
+ conn = get_connector()
120
+ echo(f"Collibra URL: {conn.base_url}")
121
+ echo(f"SDK Version: {conn.get_version()}")
122
+
123
+ if conn.test_connection():
124
+ secho("Status: Connected", fg="green")
125
+ else:
126
+ secho("Status: Not Connected", fg="red")
127
+ except Exception as e:
128
+ secho(f"Error: {e}", fg="red")
129
+ sys.exit(1)
130
+
131
+ # ==========================================================================
132
+ # Search Commands
133
+ # ==========================================================================
134
+
135
+ @cli.command("search")
136
+ @click.argument("query")
137
+ @click.option("--type", "-t", "asset_type", help="Filter by asset type name")
138
+ @click.option("--domain", "-d", "domain_id", help="Filter by domain ID")
139
+ @click.option("--community", "-c", "community_id", help="Filter by community ID")
140
+ @click.option("--limit", "-l", default=10, help="Maximum results (default: 10)")
141
+ @click.option("--format", "-f", "fmt", default="table",
142
+ type=click.Choice(["json", "table", "csv"]),
143
+ help="Output format")
144
+ def search_assets(
145
+ query: str,
146
+ asset_type: Optional[str],
147
+ domain_id: Optional[str],
148
+ community_id: Optional[str],
149
+ limit: int,
150
+ fmt: str
151
+ ) -> None:
152
+ """
153
+ Search for assets in Collibra.
154
+
155
+ Examples:
156
+
157
+ collibra-sdk search "Customer"
158
+
159
+ collibra-sdk search "Product*" --limit 50 --format json
160
+
161
+ collibra-sdk search "Order" --type "Business Term"
162
+ """
163
+ try:
164
+ conn = get_connector()
165
+
166
+ # Prepare filters
167
+ type_ids = None
168
+ if asset_type:
169
+ # Look up type ID by name
170
+ types = conn.metadata.get_asset_types(name=asset_type, limit=1)
171
+ if types.get("results"):
172
+ type_ids = [types["results"][0]["id"]]
173
+ else:
174
+ secho(f"Asset type not found: {asset_type}", fg="yellow")
175
+
176
+ domain_ids = [domain_id] if domain_id else None
177
+ community_ids = [community_id] if community_id else None
178
+
179
+ # Execute search
180
+ result = conn.search.find_assets(
181
+ query=query,
182
+ limit=limit,
183
+ type_ids=type_ids,
184
+ domain_ids=domain_ids,
185
+ community_ids=community_ids
186
+ )
187
+
188
+ # Format results
189
+ items = []
190
+ for item in result.get("results", []):
191
+ resource = item.get("resource", {})
192
+ items.append({
193
+ "id": resource.get("id"),
194
+ "name": resource.get("name") or resource.get("displayName"),
195
+ "type": resource.get("resourceType"),
196
+ "score": item.get("score", 0)
197
+ })
198
+
199
+ if not items:
200
+ echo("No results found.")
201
+ return
202
+
203
+ echo(f"Found {result.get('total', 0)} results (showing {len(items)}):\n")
204
+ echo(format_output(items, fmt))
205
+
206
+ except Exception as e:
207
+ secho(f"Error: {e}", fg="red")
208
+ sys.exit(1)
209
+
210
+ # ==========================================================================
211
+ # Asset Commands
212
+ # ==========================================================================
213
+
214
+ @cli.command("get-asset")
215
+ @click.option("--id", "asset_id", required=True, help="Asset UUID")
216
+ @click.option("--format", "-f", "fmt", default="json",
217
+ type=click.Choice(["json", "table"]),
218
+ help="Output format")
219
+ @click.option("--full", is_flag=True, help="Include attributes and relations")
220
+ def get_asset(asset_id: str, fmt: str, full: bool) -> None:
221
+ """
222
+ Get details for a specific asset.
223
+
224
+ Examples:
225
+
226
+ collibra-sdk get-asset --id "abc-123-uuid"
227
+
228
+ collibra-sdk get-asset --id "abc-123-uuid" --full
229
+ """
230
+ try:
231
+ conn = get_connector()
232
+
233
+ if full:
234
+ result = conn.asset.get_full_profile(asset_id)
235
+ # Flatten for display
236
+ output = {
237
+ "id": result["asset"].get("id"),
238
+ "name": result["asset"].get("name"),
239
+ "display_name": result["asset"].get("displayName"),
240
+ "type": result["asset"].get("type", {}).get("name"),
241
+ "status": result["asset"].get("status", {}).get("name"),
242
+ "domain": result["asset"].get("domain", {}).get("name"),
243
+ "attributes": result.get("attributes", {}),
244
+ "relations_outgoing": result["relations"].get("outgoing_count", 0),
245
+ "relations_incoming": result["relations"].get("incoming_count", 0),
246
+ "responsibilities": result.get("responsibilities", [])
247
+ }
248
+ else:
249
+ output = conn.asset.get_asset(asset_id)
250
+
251
+ echo(format_output(output, fmt))
252
+
253
+ except Exception as e:
254
+ secho(f"Error: {e}", fg="red")
255
+ sys.exit(1)
256
+
257
+ @cli.command("list-assets")
258
+ @click.option("--domain", "-d", "domain_id", help="Filter by domain ID")
259
+ @click.option("--community", "-c", "community_id", help="Filter by community ID")
260
+ @click.option("--type", "-t", "type_ids", multiple=True, help="Filter by type ID(s)")
261
+ @click.option("--limit", "-l", default=100, help="Maximum results")
262
+ @click.option("--format", "-f", "fmt", default="table",
263
+ type=click.Choice(["json", "table", "csv"]),
264
+ help="Output format")
265
+ def list_assets(
266
+ domain_id: Optional[str],
267
+ community_id: Optional[str],
268
+ type_ids: tuple,
269
+ limit: int,
270
+ fmt: str
271
+ ) -> None:
272
+ """
273
+ List assets with optional filters.
274
+
275
+ Examples:
276
+
277
+ collibra-sdk list-assets --domain "uuid"
278
+
279
+ collibra-sdk list-assets --limit 50 --format csv > assets.csv
280
+ """
281
+ try:
282
+ conn = get_connector()
283
+
284
+ result = conn.asset.find_assets(
285
+ domain_id=domain_id,
286
+ community_id=community_id,
287
+ asset_type_ids=list(type_ids) if type_ids else None,
288
+ limit=limit
289
+ )
290
+
291
+ items = []
292
+ for asset in result.get("results", []):
293
+ items.append({
294
+ "id": asset.get("id"),
295
+ "name": asset.get("name"),
296
+ "type": asset.get("type", {}).get("name"),
297
+ "status": asset.get("status", {}).get("name"),
298
+ "domain": asset.get("domain", {}).get("name")
299
+ })
300
+
301
+ echo(f"Total: {result.get('total', 0)} assets\n")
302
+ echo(format_output(items, fmt))
303
+
304
+ except Exception as e:
305
+ secho(f"Error: {e}", fg="red")
306
+ sys.exit(1)
307
+
308
+ # ==========================================================================
309
+ # Export Commands
310
+ # ==========================================================================
311
+
312
+ @cli.command("export-domain")
313
+ @click.option("--id", "domain_id", required=True, help="Domain UUID")
314
+ @click.option("--output", "-o", "output_file", required=True,
315
+ help="Output file path (.csv or .json)")
316
+ @click.option("--include-attributes", is_flag=True, default=True,
317
+ help="Include asset attributes")
318
+ @click.option("--include-relations", is_flag=True, default=False,
319
+ help="Include relation summaries")
320
+ @click.option("--limit", "-l", default=1000, help="Maximum assets to export")
321
+ def export_domain(
322
+ domain_id: str,
323
+ output_file: str,
324
+ include_attributes: bool,
325
+ include_relations: bool,
326
+ limit: int
327
+ ) -> None:
328
+ """
329
+ Export all assets in a domain to a file.
330
+
331
+ Examples:
332
+
333
+ collibra-sdk export-domain --id "uuid" --output assets.csv
334
+
335
+ collibra-sdk export-domain --id "uuid" --output data.json --include-relations
336
+ """
337
+ try:
338
+ from .helpers import DataFrameExporter
339
+
340
+ conn = get_connector()
341
+ exporter = DataFrameExporter(conn)
342
+
343
+ echo(f"Exporting domain {domain_id}...")
344
+
345
+ df = exporter.assets_to_dataframe(
346
+ domain_id=domain_id,
347
+ limit=limit,
348
+ include_attributes=include_attributes,
349
+ include_relations=include_relations
350
+ )
351
+
352
+ if output_file.endswith(".csv"):
353
+ df.to_csv(output_file, index=False)
354
+ elif output_file.endswith(".json"):
355
+ df.to_json(output_file, orient="records", indent=2)
356
+ elif output_file.endswith(".xlsx"):
357
+ df.to_excel(output_file, index=False)
358
+ else:
359
+ df.to_csv(output_file, index=False)
360
+
361
+ secho(f"Exported {len(df)} assets to {output_file}", fg="green")
362
+
363
+ except ImportError:
364
+ secho("Pandas is required for export. Install with: pip install pandas", fg="red")
365
+ sys.exit(1)
366
+ except Exception as e:
367
+ secho(f"Error: {e}", fg="red")
368
+ sys.exit(1)
369
+
370
+ @cli.command("export-community")
371
+ @click.option("--id", "community_id", required=True, help="Community UUID")
372
+ @click.option("--output", "-o", "output_file", required=True,
373
+ help="Output file path")
374
+ @click.option("--include-attributes", is_flag=True, default=True)
375
+ @click.option("--limit", "-l", default=1000)
376
+ def export_community(
377
+ community_id: str,
378
+ output_file: str,
379
+ include_attributes: bool,
380
+ limit: int
381
+ ) -> None:
382
+ """
383
+ Export all assets in a community to a file.
384
+
385
+ Examples:
386
+
387
+ collibra-sdk export-community --id "uuid" --output assets.csv
388
+ """
389
+ try:
390
+ from .helpers import DataFrameExporter
391
+
392
+ conn = get_connector()
393
+ exporter = DataFrameExporter(conn)
394
+
395
+ echo(f"Exporting community {community_id}...")
396
+
397
+ df = exporter.assets_to_dataframe(
398
+ community_id=community_id,
399
+ limit=limit,
400
+ include_attributes=include_attributes
401
+ )
402
+
403
+ if output_file.endswith(".csv"):
404
+ df.to_csv(output_file, index=False)
405
+ elif output_file.endswith(".json"):
406
+ df.to_json(output_file, orient="records", indent=2)
407
+ else:
408
+ df.to_csv(output_file, index=False)
409
+
410
+ secho(f"Exported {len(df)} assets to {output_file}", fg="green")
411
+
412
+ except ImportError:
413
+ secho("Pandas required. Install with: pip install pandas", fg="red")
414
+ sys.exit(1)
415
+ except Exception as e:
416
+ secho(f"Error: {e}", fg="red")
417
+ sys.exit(1)
418
+
419
+ # ==========================================================================
420
+ # Community/Domain Commands
421
+ # ==========================================================================
422
+
423
+ @cli.command("list-communities")
424
+ @click.option("--format", "-f", "fmt", default="table",
425
+ type=click.Choice(["json", "table", "csv"]))
426
+ @click.option("--limit", "-l", default=100)
427
+ def list_communities(fmt: str, limit: int) -> None:
428
+ """List all communities."""
429
+ try:
430
+ conn = get_connector()
431
+ result = conn.community.find_communities(limit=limit)
432
+
433
+ items = []
434
+ for comm in result.get("results", []):
435
+ items.append({
436
+ "id": comm.get("id"),
437
+ "name": comm.get("name"),
438
+ "description": (comm.get("description") or "")[:50],
439
+ "parent": comm.get("parent", {}).get("name") if comm.get("parent") else None
440
+ })
441
+
442
+ echo(f"Total: {result.get('total', 0)} communities\n")
443
+ echo(format_output(items, fmt))
444
+
445
+ except Exception as e:
446
+ secho(f"Error: {e}", fg="red")
447
+ sys.exit(1)
448
+
449
+ @cli.command("list-domains")
450
+ @click.option("--community", "-c", "community_id", help="Filter by community ID")
451
+ @click.option("--format", "-f", "fmt", default="table",
452
+ type=click.Choice(["json", "table", "csv"]))
453
+ @click.option("--limit", "-l", default=100)
454
+ def list_domains(community_id: Optional[str], fmt: str, limit: int) -> None:
455
+ """List all domains."""
456
+ try:
457
+ conn = get_connector()
458
+ result = conn.domain.find_domains(community_id=community_id, limit=limit)
459
+
460
+ items = []
461
+ for domain in result.get("results", []):
462
+ items.append({
463
+ "id": domain.get("id"),
464
+ "name": domain.get("name"),
465
+ "type": domain.get("type", {}).get("name"),
466
+ "community": domain.get("community", {}).get("name")
467
+ })
468
+
469
+ echo(f"Total: {result.get('total', 0)} domains\n")
470
+ echo(format_output(items, fmt))
471
+
472
+ except Exception as e:
473
+ secho(f"Error: {e}", fg="red")
474
+ sys.exit(1)
475
+
476
+ # ==========================================================================
477
+ # Metadata Commands
478
+ # ==========================================================================
479
+
480
+ @cli.command("list-asset-types")
481
+ @click.option("--format", "-f", "fmt", default="table",
482
+ type=click.Choice(["json", "table", "csv"]))
483
+ @click.option("--limit", "-l", default=100)
484
+ def list_asset_types(fmt: str, limit: int) -> None:
485
+ """List all asset types."""
486
+ try:
487
+ conn = get_connector()
488
+ result = conn.metadata.get_asset_types()
489
+
490
+ items = []
491
+ for at in result.get("results", [])[:limit]: # Apply limit manually
492
+ items.append({
493
+ "id": at.get("id"),
494
+ "name": at.get("name"),
495
+ "public_id": at.get("publicId"),
496
+ "description": (at.get("description") or "")[:40]
497
+ })
498
+
499
+ echo(f"Total: {result.get('total', 0)} asset types (showing {len(items)})\n")
500
+ echo(format_output(items, fmt))
501
+
502
+ except Exception as e:
503
+ secho(f"Error: {e}", fg="red")
504
+ sys.exit(1)
505
+
506
+ @cli.command("list-statuses")
507
+ @click.option("--format", "-f", "fmt", default="table",
508
+ type=click.Choice(["json", "table", "csv"]))
509
+ def list_statuses(fmt: str) -> None:
510
+ """List all statuses."""
511
+ try:
512
+ conn = get_connector()
513
+ result = conn.metadata.get_statuses()
514
+
515
+ items = []
516
+ for status in result.get("results", []):
517
+ items.append({
518
+ "id": status.get("id"),
519
+ "name": status.get("name"),
520
+ "description": (status.get("description") or "")[:40]
521
+ })
522
+
523
+ echo(format_output(items, fmt))
524
+
525
+ except Exception as e:
526
+ secho(f"Error: {e}", fg="red")
527
+ sys.exit(1)
528
+
529
+ # ==========================================================================
530
+ # Bulk Operations
531
+ # ==========================================================================
532
+
533
+ @cli.command("bulk-update-status")
534
+ @click.option("--input", "-i", "input_file", required=True,
535
+ help="CSV file with 'id' column")
536
+ @click.option("--status-id", required=True, help="New status UUID")
537
+ @click.option("--dry-run", is_flag=True, help="Preview without making changes")
538
+ def bulk_update_status(input_file: str, status_id: str, dry_run: bool) -> None:
539
+ """
540
+ Update status for multiple assets from a CSV file.
541
+
542
+ The input file should have an 'id' column with asset UUIDs.
543
+
544
+ Examples:
545
+
546
+ collibra-sdk bulk-update-status --input assets.csv --status-id "uuid"
547
+
548
+ collibra-sdk bulk-update-status --input assets.csv --status-id "uuid" --dry-run
549
+ """
550
+ try:
551
+ import pandas as pd
552
+
553
+ conn = get_connector()
554
+ df = pd.read_csv(input_file)
555
+
556
+ if "id" not in df.columns:
557
+ secho("Error: CSV must have an 'id' column", fg="red")
558
+ sys.exit(1)
559
+
560
+ asset_ids = df["id"].tolist()
561
+ echo(f"Found {len(asset_ids)} assets to update")
562
+
563
+ if dry_run:
564
+ echo("Dry run - no changes made")
565
+ return
566
+
567
+ success = 0
568
+ errors = 0
569
+
570
+ with click.progressbar(asset_ids, label="Updating assets") as bar:
571
+ for asset_id in bar:
572
+ try:
573
+ conn.asset.change_asset(asset_id=asset_id, status_id=status_id)
574
+ success += 1
575
+ except Exception:
576
+ errors += 1
577
+
578
+ secho(f"Updated: {success}, Errors: {errors}", fg="green" if errors == 0 else "yellow")
579
+
580
+ except ImportError:
581
+ secho("Pandas required. Install with: pip install pandas", fg="red")
582
+ sys.exit(1)
583
+ except Exception as e:
584
+ secho(f"Error: {e}", fg="red")
585
+ sys.exit(1)
586
+
587
+
588
+ def main() -> None:
589
+ """Entry point for the CLI."""
590
+ if not CLICK_AVAILABLE:
591
+ print("CLI requires 'click' package. Install with: pip install click")
592
+ sys.exit(1)
593
+ cli()
594
+
595
+
596
+ if __name__ == "__main__":
597
+ main()