You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
6.2 KiB

# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved.
"""
This module accepts a classic GT json and produces UDF GT json
"""
from .utils import dict_merge
from .layout import LayoutConverter
from .visualization import VizConverter
from .datasource import DataSourceConverter
from .udf_definition_builder import UDFDefinitionBuilder
class GTConverter(object):
def __init__(
self, session_key=None, layout_converter=None, viz_converter=None, datasource_converter=None, services=[]):
"""
Initializer
:param session_key: splunkd session key
:param layout_converter: LayoutConverter object for overriding default behavior
:param viz_converter: VizConverter object for overriding default behavior
:param datasource_converter: DataSourceConverter object for overriding default behavior
:param services: Optional prefetched list of ITSI services
"""
self.gt = None
self.layout_converter = layout_converter if layout_converter else LayoutConverter()
self.viz_converter = viz_converter if viz_converter else VizConverter(session_key=session_key)
self.datasource_converter = datasource_converter if datasource_converter else \
DataSourceConverter(session_key=session_key, services=services)
def parse_glasstable(self, gt):
"""
Main method
:param gt: input glass table
:return: converted UDF json
"""
self.gt = gt
if not self.should_convert():
return self.gt
# start with a clean slate
self.definition_builder = UDFDefinitionBuilder()
self.parse_metadata()
layout_options = self.layout_converter.convert(self.gt)
if layout_options:
self.definition_builder.update_layout_options(layout_options)
self.parse_contents()
return self.definition_builder.get_definition()
def should_convert(self):
"""
Should provided json be converted at all
:return: Boolean
"""
if not self.gt:
return False
content = self.gt.get('content')
gt_version = self.gt.get('gt_version')
return content is not None and gt_version != 'beta'
def parse_metadata(self):
title = self.gt.get('title')
description = self.gt.get('description')
self.definition_builder.update_meta(title, description)
def parse_contents(self):
"""
Goes over all original widgets and does vis, layout and datasource conversion
"""
content = self.gt.get('content')
scale_factor = self.layout_converter.get_scale_factor()
for item in content:
viz_item = self.convert_viz(item, scale_factor)
if viz_item:
self.definition_builder.add_visualization(viz_item)
layout_item = self.layout_converter.convert_item(item)
if layout_item:
self.definition_builder.add_layout_item(layout_item)
if item.get('labelFlag') and len(item.get('label')) > 0:
label = self.create_label(item, layout_item)
if label:
self.definition_builder.add_visualization(label['viz'])
self.definition_builder.add_layout_item(label['layout'])
if self.is_searchable(item) and item.get('searchFigure'):
ds_id = 'ds_%s' % str(item.get('id'))
viz_item = self.viz_bind_datasource(viz_item, ds_id)
self.definition_builder.update_visualization(item.get('id'), viz_item)
data_source = self.datasource_converter.convert(item, ds_id)
if data_source:
self.definition_builder.add_data_source(data_source)
def convert_viz(self, item, scale_factor):
"""
Produces a UDF vis object
:param item: original GT widget
:param scale_factor: scale factor applied to the classic GT
:return: id-wrapped converted object
"""
viz_item = None
viz = self.viz_converter.convert(item, scale_factor)
if viz:
viz_item = {}
viz_id = 'viz_%s' % str(item.get('id'))
viz_item[viz_id] = viz
return viz_item
def viz_bind_datasource(self, viz_item, ds_id):
"""
Adds a datasource id to the viz definition.
:param viz_item: viz_item object
:param ds_id: datasource id to bind
:return:
"""
ds = {
'dataSources': {
'primary': ds_id
}
}
first_key = list(viz_item.keys())[0]
dict_merge(viz_item[first_key], ds)
return viz_item
def is_searchable(self, item):
"""
Can current widget support datasources
:return: Bool
"""
supported = [
'SingleValue',
'Sparkline',
'SingleValueDelta',
'PolyIcon',
'Gauge',
'Rectangle',
'Ellipse',
'Line'
]
return item.get('name') in supported
def create_label(self, item, layout_item):
"""
Create a label widget for an item
:param item: gt item
:param layout_item: converted layout item for gt item
:return: {'viz': viz, 'layout': layout} or None
"""
if item.get('name') == 'Connection':
# we don't support converting connection label
return None
label_id = 'viz_%s_label' % item.get('id')
label_location = item.get('labelLocation')
label_def = self.viz_converter.create_label(item)
if not label_def:
return None
label_viz = {
label_id: label_def
}
if item.get('name') == 'Line':
label_layout = {
'item': label_id,
'position': self.layout_converter.find_label_position_line(
label_location, layout_item['position'], item)
}
else:
label_layout = {
'item': label_id,
'position': self.layout_converter.find_label_position(label_location, layout_item['position'])
}
return {'viz': label_viz, 'layout': label_layout}