# Copyright (C) 2005-2025 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}