Skip to content

Commit 2628749

Browse files
authored
Add JSON output support (#150)
* Output json * Tidy * Remove strenum
1 parent f39b6a0 commit 2628749

File tree

2 files changed

+50
-20
lines changed

2 files changed

+50
-20
lines changed

cert_host_scraper/cli.py

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
import json
23
import logging
34
import sys
45

@@ -31,6 +32,15 @@ def validate_status_code(
3132
return NO_STATUS_CODE_FILTER
3233

3334

35+
class Output:
36+
TABLE = "table"
37+
JSON = "json"
38+
39+
@classmethod
40+
def values(cls) -> list:
41+
return [cls.TABLE, cls.JSON]
42+
43+
3444
@click.group()
3545
@click.option("--debug", is_flag=True, help="Whether to enable debug level output")
3646
@click.version_option(__version__, message="%(version)s")
@@ -61,21 +71,28 @@ def cli(debug: bool):
6171
help="Number of URLs to process at once",
6272
default=20,
6373
)
74+
@click.option(
75+
"--output", type=click.Choice(Output.values()), required=True, default="table"
76+
)
6477
def search(
6578
search: str,
6679
status_code: int,
6780
timeout: int,
6881
clean: bool,
6982
strip: bool,
7083
batch_size: int,
84+
output: str,
7185
):
7286
"""
7387
Search the certificate transparency log.
7488
"""
7589
if strip:
7690
search = strip_url(search)
7791

78-
click.echo(f"Searching for {search}")
92+
display_json = output == Output.JSON
93+
94+
if not display_json:
95+
click.echo(f"Searching for {search}")
7996
options = Options(timeout, clean)
8097
results = []
8198
try:
@@ -84,11 +101,12 @@ def search(
84101
click.echo(f"Failed to search for results: {e}")
85102
sys.exit(1)
86103

87-
click.echo(f"Found {len(urls)} URLs for {search}")
104+
if not display_json:
105+
click.echo(f"Found {len(urls)} URLs for {search}")
88106
loop = asyncio.new_event_loop()
89107
asyncio.set_event_loop(loop)
90108
chunks = list(divide_chunks(urls, batch_size))
91-
for chunk_index in track(range(len(chunks)), "Checking URLs"):
109+
for chunk_index in track(range(len(chunks)), "Checking URLs", disable=display_json):
92110
chunk_result = loop.run_until_complete(
93111
asyncio.gather(*[validate_url(url, options) for url in chunks[chunk_index]])
94112
)
@@ -100,23 +118,30 @@ def search(
100118
else:
101119
display = result.scraped
102120

103-
table = Table(show_header=True, header_style="bold", box=box.MINIMAL)
104-
table.add_column("URL")
105-
table.add_column("Status Code")
106-
for url_result in display:
107-
display_code = str(url_result.status_code)
108-
if url_result.status_code == -1:
109-
display_code = "-"
110-
111-
url = url_result.url
112-
if url_result.status_code == 200:
113-
display_code = f"[green]{display_code}[/green]"
114-
url = f"[green]{url}[/green]"
115-
116-
table.add_row(url, display_code)
117-
118-
console = Console()
119-
console.print(table)
121+
if display_json:
122+
json_output = [
123+
{"url": url_result.url, "status_code": url_result.status_code}
124+
for url_result in display
125+
]
126+
click.echo(json.dumps(json_output, indent=2))
127+
else:
128+
table = Table(show_header=True, header_style="bold", box=box.MINIMAL)
129+
table.add_column("URL")
130+
table.add_column("Status Code")
131+
for url_result in display:
132+
display_code = str(url_result.status_code)
133+
if url_result.status_code == -1:
134+
display_code = "-"
135+
136+
url = url_result.url
137+
if url_result.status_code == 200:
138+
display_code = f"[green]{display_code}[/green]"
139+
url = f"[green]{url}[/green]"
140+
141+
table.add_row(url, display_code)
142+
143+
console = Console()
144+
console.print(table)
120145

121146

122147
if __name__ == "__main__":

tests/test_cli.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,8 @@ def test_search_upper_invalid_status_code(self):
4343
runner = CliRunner()
4444
result = runner.invoke(cli, ["search", "example.com", "--status-code", "600"])
4545
self.assertEqual(result.exit_code, 2)
46+
47+
def test_invalid_output(self):
48+
runner = CliRunner()
49+
result = runner.invoke(cli, ["search", "example.com", "--output", "csv"])
50+
self.assertEqual(result.exit_code, 2)

0 commit comments

Comments
 (0)