Skip to content

Commit 5feff62

Browse files
committed
Search: Unlock JSON and YAML export formats
1 parent 53bac53 commit 5feff62

File tree

6 files changed

+88
-10
lines changed

6 files changed

+88
-10
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ in progress
1313
- History: Add ``id`` and ``uid`` dashboard attributes to report
1414
- History: Unlock YAML export format
1515
- History: Add new options ``--head``, ``--tail``, and ``--reverse``
16+
- Search: Unlock JSON and YAML export formats
1617

1718
2023-03-05 0.14.1
1819
=================

grafana_wtf/commands.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from grafana_wtf import __appname__, __version__
1212
from grafana_wtf.core import GrafanaWtf
13-
from grafana_wtf.report.data import output_results
13+
from grafana_wtf.report.data import output_results, DataSearchReport
1414
from grafana_wtf.report.textual import TextualSearchReport
1515
from grafana_wtf.report.tabular import TabularSearchReport, get_table_format, TabularEditHistoryReport
1616
from grafana_wtf.util import (
@@ -40,7 +40,7 @@ def run():
4040
--grafana-token=<grafana-token> Grafana API Key token
4141
--select-dashboard=<uuid> Restrict operation to dashboard by UID.
4242
Can be a list of comma-separated dashboard UIDs.
43-
--format=<format> Output format. [default: json]
43+
--format=<format> Output format. One of textual, tabular, json, yaml.
4444
--cache-ttl=<cache-ttl> Time-to-live for the request cache in seconds. [default: 300]
4545
--drop-cache Drop cache before requesting resources
4646
--concurrency=<concurrency> Run multiple requests in parallel. [default: 5]
@@ -176,6 +176,13 @@ def run():
176176
else:
177177
raise
178178

179+
# Compute default output format.
180+
if not options.format:
181+
if options.find or options.replace:
182+
options.format = "textual"
183+
else:
184+
options.format = "json"
185+
179186
# Sanity checks
180187
if grafana_url is None:
181188
raise DocoptExit(
@@ -195,6 +202,7 @@ def run():
195202
output_format = options["format"]
196203

197204
if options.find or options.replace:
205+
198206
if options.select_dashboard:
199207
# Restrict scan to list of dashboards.
200208
dashboard_uids = read_list(options.select_dashboard)
@@ -206,11 +214,13 @@ def run():
206214

207215
result = engine.search(options.search_expression or None)
208216

209-
if output_format.startswith("tabular"):
217+
if output_format.startswith("tab"):
210218
table_format = get_table_format(output_format)
211219
generator = partial(TabularSearchReport, tblfmt=table_format)
212-
else:
220+
elif output_format.startswith("text"):
213221
generator = TextualSearchReport
222+
else:
223+
generator = partial(DataSearchReport, format=output_format)
214224

215225
report = generator(grafana_url, verbose=options.verbose)
216226
report.display(options.search_expression, result)
@@ -220,6 +230,7 @@ def run():
220230
engine.clear_cache()
221231

222232
if options.log:
233+
223234
entries = engine.log(dashboard_uid=options.dashboard_uid)
224235
entries = sorted(entries, key=itemgetter("datetime"))
225236

@@ -237,14 +248,15 @@ def run():
237248
if options.reverse:
238249
entries = list(reversed(entries))
239250

240-
if output_format.startswith("tabular"):
251+
if output_format.startswith("tab"):
241252
report = TabularEditHistoryReport(data=entries)
242253
output = report.render(output_format)
243254
print(output)
244255
else:
245256
output_results(output_format, entries)
246257

247258
if options.explore and options.datasources:
259+
248260
results = engine.explore_datasources()
249261

250262
unused_count = len(results["unused"])

grafana_wtf/report/data.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
import json
2+
import logging
3+
from collections import OrderedDict
24
from typing import List
35

6+
from grafana_wtf.report.tabular import TabularSearchReport
47
from grafana_wtf.util import yaml_dump
58

9+
log = logging.getLogger(__name__)
10+
611

712
def output_results(output_format: str, results: List):
13+
output = serialize_results(output_format, results)
14+
print(output)
15+
16+
17+
def serialize_results(output_format: str, results: List):
18+
819
if output_format == "json":
920
output = json.dumps(results, indent=4)
1021

@@ -14,4 +25,25 @@ def output_results(output_format: str, results: List):
1425
else:
1526
raise ValueError(f'Unknown output format "{output_format}"')
1627

17-
print(output)
28+
return output
29+
30+
31+
class DataSearchReport(TabularSearchReport):
32+
def __init__(self, grafana_url, verbose=False, format=None):
33+
self.grafana_url = grafana_url
34+
self.verbose = verbose
35+
self.format = format
36+
37+
def display(self, expression, result):
38+
expression = expression or "*"
39+
log.info(f"Searching for expression '{expression}' at Grafana instance {self.grafana_url}")
40+
41+
output = OrderedDict(
42+
meta=OrderedDict(
43+
grafana=self.grafana_url,
44+
expression=expression,
45+
),
46+
datasources=self.get_output_items("Datasource", result.datasources, self.compute_url_datasource),
47+
dashboards=self.get_output_items("Dashboard", result.dashboards, self.compute_url_dashboard),
48+
)
49+
output_results(self.format, output)

grafana_wtf/report/tabular.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ def __init__(self, grafana_url, tblfmt="psql", verbose=False):
2525
super().__init__(grafana_url, verbose=verbose)
2626

2727
def output_items(self, label, items, url_callback):
28+
items_rows = self.get_output_items(label, items, url_callback)
29+
print(tabulate(items_rows, headers="keys", tablefmt=self.format))
30+
31+
def get_output_items(self, label, items, url_callback):
2832
items_rows = [
2933
{
3034
"Type": label,
@@ -33,7 +37,7 @@ def output_items(self, label, items, url_callback):
3337
}
3438
for item in items
3539
]
36-
print(tabulate(items_rows, headers="keys", tablefmt=self.format))
40+
return items_rows
3741

3842
def get_bibdata_dict(self, item, **kwargs):
3943
# Sanity checks.

grafana_wtf/report/textual.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ def __init__(self, grafana_url, verbose=False):
2121
def display(self, expression, result):
2222
expression = expression or "*"
2323
print('Searching for expression "{}" at Grafana instance {}'.format(_m(expression), self.grafana_url))
24-
self.output_items(_s("Data Sources"), result.datasources, self.compute_url_datasource)
25-
self.output_items(_s("Dashboards"), result.dashboards, self.compute_url_dashboard)
24+
self.output_items("Data Sources", result.datasources, self.compute_url_datasource)
25+
self.output_items("Dashboards", result.dashboards, self.compute_url_dashboard)
2626

2727
def output_items(self, label, items, url_callback):
2828
# Output section name (data source vs. dashboard).
29+
hits = len(items)
2930
print("=" * 42)
30-
print("{label}: {hits} hits.".format(hits=_m(len(items)), label=label))
31+
print(f"{_s(label)}: {_m(hits)} hits.")
3132
print("=" * 42)
3233
print()
3334

tests/test_commands.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,34 @@ def test_find_tabular_dashboard_success(ldi_resources, capsys):
143143
assert output_table_normalized == reference_table
144144

145145

146+
def test_find_format_json(ldi_resources, capsys):
147+
# Only provision specific dashboard(s).
148+
ldi_resources(dashboards=["tests/grafana/dashboards/ldi-v27.json", "tests/grafana/dashboards/ldi-v33.json"])
149+
150+
# Run command and capture output.
151+
set_command("find ldi_readings --format=json")
152+
grafana_wtf.commands.run()
153+
captured = capsys.readouterr()
154+
155+
# Verify output.
156+
data = json.loads(captured.out)
157+
assert len(data) == 3
158+
159+
160+
def test_find_format_yaml(ldi_resources, capsys):
161+
# Only provision specific dashboard(s).
162+
ldi_resources(dashboards=["tests/grafana/dashboards/ldi-v27.json", "tests/grafana/dashboards/ldi-v33.json"])
163+
164+
# Run command and capture output.
165+
set_command("find ldi_readings --format=yaml")
166+
grafana_wtf.commands.run()
167+
captured = capsys.readouterr()
168+
169+
# Verify output.
170+
data = yaml.safe_load(captured.out)
171+
assert len(data) == 3
172+
173+
146174
def test_replace_dashboard_success(ldi_resources, capsys):
147175
# Only provision specific dashboard(s).
148176
ldi_resources(dashboards=["tests/grafana/dashboards/ldi-v27.json", "tests/grafana/dashboards/ldi-v33.json"])

0 commit comments

Comments
 (0)