|
1 | | -import json |
2 | | -import sys |
3 | | -from typing import Dict, Generator, Optional |
4 | | - |
5 | | -import marshmallow |
6 | | -import yaml |
7 | | -from marshmallow import Schema |
8 | | -from rich.console import Console |
9 | | -from rich.live import Live |
10 | | -from rich.table import Table |
| 1 | +import argparse |
11 | 2 |
|
12 | 3 | from probely_cli.cli.commands.findings.schemas import FindingsApiFiltersSchema |
13 | | -from probely_cli.cli.common import OutputEnum |
14 | | -from probely_cli.cli.formatters import ( |
15 | | - get_printable_enum_value, |
16 | | - get_printable_date, |
17 | | - get_printable_labels, |
18 | | -) |
| 4 | +from probely_cli.cli.common import prepare_filters_for_api |
| 5 | +from probely_cli.cli.enums import EntityTypeEnum, OutputEnum |
| 6 | +from probely_cli.cli.renderers import OutputRenderer |
19 | 7 | from probely_cli.exceptions import ProbelyCLIValidation |
20 | | -from probely_cli.sdk.common import FindingSeverityEnum |
21 | | -from probely_cli.sdk.findings import list_findings |
22 | | -from probely_cli.sdk.findings import retrieve_findings |
23 | | - |
24 | | - |
25 | | -DEFAULT_LAST_FOUND_DATE_VALUE = "NO_DATE" |
26 | | - |
27 | | - |
28 | | -def finding_filters_handler(args): |
29 | | - filters_schema: Schema = FindingsApiFiltersSchema() |
30 | | - try: |
31 | | - api_ready_filters = filters_schema.load(vars(args)) |
32 | | - except marshmallow.ValidationError as e: |
33 | | - raise ProbelyCLIValidation(str(e)) |
34 | | - |
35 | | - return api_ready_filters |
36 | | - |
37 | | - |
38 | | -def create_findings_table(show_header: bool = False) -> Table: |
39 | | - """ |
40 | | - Initializes and returns a Rich Table for displaying Findings. |
41 | | - """ |
42 | | - table = Table(show_header=show_header, box=None) |
43 | | - |
44 | | - table.add_column("ID", width=18) |
45 | | - table.add_column("TARGET_ID", width=12) |
46 | | - table.add_column("SEVERITY", width=8) |
47 | | - table.add_column("TITLE", width=48, no_wrap=True) |
48 | | - table.add_column("LAST_FOUND", width=16) |
49 | | - table.add_column("STATE", width=8) |
50 | | - table.add_column("LABELS", width=16) |
51 | | - |
52 | | - return table |
53 | | - |
54 | | - |
55 | | -def add_finding_to_table(table: Table, finding: Dict) -> None: |
56 | | - """ |
57 | | - Adds a single finding as a row to the provided Rich Table. |
58 | | - """ |
59 | | - target = finding.get("target") |
60 | | - |
61 | | - table.add_row( |
62 | | - f"{target['id']}-{finding['id']}", # Composite Finding ID |
63 | | - target["id"], # target_id |
64 | | - get_printable_enum_value(FindingSeverityEnum, finding["severity"]), # severity |
65 | | - finding["definition"]["name"], # title |
66 | | - get_printable_date( |
67 | | - finding["last_found"], DEFAULT_LAST_FOUND_DATE_VALUE |
68 | | - ), # last_found |
69 | | - finding["state"], # state |
70 | | - get_printable_labels(finding["labels"]), # labels |
71 | | - ) |
72 | | - |
73 | | - |
74 | | -def render_json_output(findings: Generator[Dict, None, None], console: Console) -> None: |
75 | | - console.print("[") |
76 | | - first = True |
77 | | - for finding in findings: |
78 | | - if not first: |
79 | | - console.print(",") |
80 | | - console.print(json.dumps(finding, indent=2)) |
81 | | - first = False |
82 | | - console.print("]") |
83 | | - |
84 | | - |
85 | | -def render_yaml_output(findings: Generator[Dict, None, None], console: Console) -> None: |
86 | | - for finding in findings: |
87 | | - console.print(yaml.dump([finding], indent=2, width=sys.maxsize)) |
| 8 | +from probely_cli.sdk.findings import list_findings, retrieve_findings |
88 | 9 |
|
89 | 10 |
|
90 | | -def render_table_output( |
91 | | - findings: Generator[Dict, None, None], console: Console |
92 | | -) -> None: |
93 | | - table = create_findings_table(show_header=True) |
94 | | - with Live(table, refresh_per_second=1, console=console): |
95 | | - for finding in findings: |
96 | | - add_finding_to_table(table, finding) |
97 | | - |
98 | | - |
99 | | -def render_output( |
100 | | - findings: Generator[Dict, None, None], |
101 | | - output_type: Optional[OutputEnum], |
102 | | - console: Console, |
103 | | -) -> None: |
104 | | - if output_type == OutputEnum.JSON: |
105 | | - render_json_output(findings, console) |
106 | | - elif output_type == OutputEnum.YAML: |
107 | | - render_yaml_output(findings, console) |
108 | | - else: |
109 | | - render_table_output(findings, console) |
110 | | - |
111 | | - |
112 | | -def findings_get_command_handler(args): |
113 | | - api_filters = finding_filters_handler(args) |
114 | | - if api_filters and args.findings_ids: |
115 | | - raise ProbelyCLIValidation("filters and finding ids are mutually exclusive.") |
| 11 | +def findings_get_command_handler(args: argparse.Namespace): |
| 12 | + filters = prepare_filters_for_api(FindingsApiFiltersSchema, args) |
| 13 | + if filters and args.findings_ids: |
| 14 | + raise ProbelyCLIValidation("filters and Finding IDs are mutually exclusive.") |
116 | 15 |
|
117 | 16 | if args.findings_ids: |
118 | 17 | findings_generator = retrieve_findings(findings_ids=args.findings_ids) |
119 | 18 | else: |
120 | | - findings_generator = list_findings(findings_filters=api_filters) |
| 19 | + findings_generator = list_findings(findings_filters=filters) |
121 | 20 |
|
122 | 21 | output_type = OutputEnum[args.output] if args.output else None |
123 | | - render_output(findings_generator, output_type, args.console) |
| 22 | + renderer = OutputRenderer( |
| 23 | + records=findings_generator, |
| 24 | + output_type=output_type, |
| 25 | + console=args.console, |
| 26 | + entity_type=EntityTypeEnum.FINDING, |
| 27 | + ) |
| 28 | + renderer.render() |
0 commit comments