1// Libraries
2import React, { PureComponent } from 'react';
3
4// Types
5import {
6  CoreApp,
7  DataQuery,
8  DataSourceInstanceSettings,
9  EventBusExtended,
10  HistoryItem,
11  PanelData,
12} from '@grafana/data';
13import { QueryEditorRow } from './QueryEditorRow';
14import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd';
15import { getDataSourceSrv } from '@grafana/runtime';
16
17interface Props {
18  // The query configuration
19  queries: DataQuery[];
20  dsSettings: DataSourceInstanceSettings;
21
22  // Query editing
23  onQueriesChange: (queries: DataQuery[]) => void;
24  onAddQuery: (query: DataQuery) => void;
25  onRunQueries: () => void;
26
27  // Query Response Data
28  data: PanelData;
29
30  // Misc
31  app?: CoreApp;
32  history?: Array<HistoryItem<DataQuery>>;
33  eventBus?: EventBusExtended;
34}
35
36export class QueryEditorRows extends PureComponent<Props> {
37  onRemoveQuery = (query: DataQuery) => {
38    this.props.onQueriesChange(this.props.queries.filter((item) => item !== query));
39  };
40
41  onChangeQuery(query: DataQuery, index: number) {
42    const { queries, onQueriesChange } = this.props;
43
44    // update query in array
45    onQueriesChange(
46      queries.map((item, itemIndex) => {
47        if (itemIndex === index) {
48          return query;
49        }
50        return item;
51      })
52    );
53  }
54
55  onDataSourceChange(dataSource: DataSourceInstanceSettings, index: number) {
56    const { queries, onQueriesChange } = this.props;
57
58    onQueriesChange(
59      queries.map((item, itemIndex) => {
60        if (itemIndex !== index) {
61          return item;
62        }
63
64        if (item.datasource) {
65          const previous = getDataSourceSrv().getInstanceSettings(item.datasource);
66
67          if (previous?.type === dataSource.type) {
68            return {
69              ...item,
70              datasource: { uid: dataSource.uid },
71            };
72          }
73        }
74
75        return {
76          refId: item.refId,
77          hide: item.hide,
78          datasource: { uid: dataSource.uid },
79        };
80      })
81    );
82  }
83
84  onDragEnd = (result: DropResult) => {
85    const { queries, onQueriesChange } = this.props;
86
87    if (!result || !result.destination) {
88      return;
89    }
90
91    const startIndex = result.source.index;
92    const endIndex = result.destination.index;
93    if (startIndex === endIndex) {
94      return;
95    }
96
97    const update = Array.from(queries);
98    const [removed] = update.splice(startIndex, 1);
99    update.splice(endIndex, 0, removed);
100    onQueriesChange(update);
101  };
102
103  render() {
104    const { dsSettings, data, queries, app, history, eventBus } = this.props;
105
106    return (
107      <DragDropContext onDragEnd={this.onDragEnd}>
108        <Droppable droppableId="transformations-list" direction="vertical">
109          {(provided) => {
110            return (
111              <div ref={provided.innerRef} {...provided.droppableProps}>
112                {queries.map((query, index) => {
113                  const dataSourceSettings = getDataSourceSettings(query, dsSettings);
114                  const onChangeDataSourceSettings = dsSettings.meta.mixed
115                    ? (settings: DataSourceInstanceSettings) => this.onDataSourceChange(settings, index)
116                    : undefined;
117
118                  return (
119                    <QueryEditorRow
120                      id={query.refId}
121                      index={index}
122                      key={query.refId}
123                      data={data}
124                      query={query}
125                      dataSource={dataSourceSettings}
126                      onChangeDataSource={onChangeDataSourceSettings}
127                      onChange={(query) => this.onChangeQuery(query, index)}
128                      onRemoveQuery={this.onRemoveQuery}
129                      onAddQuery={this.props.onAddQuery}
130                      onRunQuery={this.props.onRunQueries}
131                      queries={queries}
132                      app={app}
133                      history={history}
134                      eventBus={eventBus}
135                    />
136                  );
137                })}
138                {provided.placeholder}
139              </div>
140            );
141          }}
142        </Droppable>
143      </DragDropContext>
144    );
145  }
146}
147
148const getDataSourceSettings = (
149  query: DataQuery,
150  groupSettings: DataSourceInstanceSettings
151): DataSourceInstanceSettings => {
152  if (!query.datasource) {
153    return groupSettings;
154  }
155  const querySettings = getDataSourceSrv().getInstanceSettings(query.datasource);
156  return querySettings || groupSettings;
157};
158