[TSVB] [Performance]request for api/metrics/fields triggered after all UI changes (#34436)

* [TSVB] [Performance]request for api/metrics/fields triggered after all UI changes

Fix: #34433

* [TSVB] [Performance]request for api/metrics/fields triggered after all UI changes - prevent reRender in case of no changes

Fix: #34433

* should update data on switching tabs

* fix PR comments
This commit is contained in:
Alexey Antonov 2019-04-09 12:34:35 +03:00 committed by GitHub
parent 9bde4e4830
commit be8ea4a722
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 139 additions and 98 deletions

View file

@ -21,12 +21,14 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import * as Rx from 'rxjs';
import { share } from 'rxjs/operators';
import { isEqual, isEmpty } from 'lodash';
import VisEditorVisualization from './vis_editor_visualization';
import Visualization from './visualization';
import VisPicker from './vis_picker';
import PanelConfig from './panel_config';
import brushHandler from '../lib/create_brush_handler';
import { fetchIndexPatternFields } from '../lib/fetch_fields';
import { fetchFields } from '../lib/fetch_fields';
import { extractIndexPatterns } from '../lib/extract_index_patterns';
class VisEditor extends Component {
constructor(props) {
@ -37,7 +39,8 @@ class VisEditor extends Component {
model: props.visParams,
dirty: false,
autoApply: true,
visFields: props.visFields
visFields: props.visFields,
extractedIndexPatterns: [''],
};
this.onBrush = brushHandler(props.vis.API.timeFilter);
this.visDataSubject = new Rx.Subject();
@ -52,23 +55,45 @@ class VisEditor extends Component {
return this.props.config.get(...args);
};
handleUiState = (field, value) => {
handleUiState = (field, value) => {
this.props.vis.uiStateVal(field, value);
};
handleChange = async (partialModel) => {
const nextModel = { ...this.state.model, ...partialModel };
this.props.vis.params = nextModel;
if (this.state.autoApply) {
this.props.vis.updateState();
if (isEmpty(partialModel)) {
return;
}
const hasTypeChanged = partialModel.type && this.state.model.type !== partialModel.type;
const nextModel = {
...this.state.model,
...partialModel,
};
let dirty = true;
this.props.vis.params = nextModel;
if (this.state.autoApply || hasTypeChanged) {
this.props.vis.updateState();
dirty = false;
}
if (this.props.isEditorMode) {
const { params, fields } = this.props.vis;
const extractedIndexPatterns = extractIndexPatterns(params, fields);
if (!isEqual(this.state.extractedIndexPatterns, extractedIndexPatterns)) {
fetchFields(extractedIndexPatterns)
.then(visFields => this.setState({
visFields,
extractedIndexPatterns,
}));
}
}
this.setState({
dirty,
model: nextModel,
dirty: !this.state.autoApply
});
const { params, fields } = this.props.vis;
fetchIndexPatternFields(params, fields).then(visFields => {
this.setState({ visFields });
});
};
@ -109,7 +134,7 @@ class VisEditor extends Component {
return (
<div className="tvbEditor">
<div className="tvbEditor--hideForReporting">
<VisPicker model={model} onChange={this.handleChange} />
<VisPicker model={model} onChange={this.handleChange}/>
</div>
<VisEditorVisualization
dirty={this.state.dirty}
@ -152,7 +177,7 @@ class VisEditor extends Component {
}
VisEditor.defaultProps = {
visData: {}
visData: {},
};
VisEditor.propTypes = {

View file

@ -1,77 +1,89 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { I18nContext } from 'ui/i18n';
import chrome from 'ui/chrome';
import { fetchIndexPatternFields } from '../lib/fetch_fields';
function ReactEditorControllerProvider(Private, config) {
class ReactEditorController {
constructor(el, savedObj) {
this.el = el;
this.savedObj = savedObj;
this.vis = savedObj.vis;
this.vis.fields = {};
}
setDefaultIndexPattern = async () => {
const savedObjectsClient = chrome.getSavedObjectsClient();
const indexPattern = await savedObjectsClient.get('index-pattern', config.get('defaultIndex'));
this.vis.params.default_index_pattern = indexPattern.attributes.title;
};
async render(params) {
const Component = this.vis.type.editorConfig.component;
await this.setDefaultIndexPattern();
const visFields = await fetchIndexPatternFields(this.vis.params, this.vis.fields);
render(
<I18nContext>
<Component
config={config}
vis={this.vis}
visFields={visFields}
visParams={this.vis.params}
savedObj={this.savedObj}
timeRange={params.timeRange}
renderComplete={() => {}}
isEditorMode={true}
appState={params.appState}
/>
</I18nContext>,
this.el);
}
resize() {}
destroy() {
unmountComponentAtNode(this.el);
}
}
return {
name: 'react_editor',
handler: ReactEditorController
};
}
export { ReactEditorControllerProvider };
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { I18nContext } from 'ui/i18n';
import chrome from 'ui/chrome';
import { fetchIndexPatternFields } from '../lib/fetch_fields';
function ReactEditorControllerProvider(Private, config) {
class ReactEditorController {
constructor(el, savedObj) {
this.el = el;
this.state = {
savedObj: savedObj,
vis: savedObj.vis,
isLoaded: false,
};
}
fetchDefaultIndexPattern = async () => {
const savedObjectsClient = chrome.getSavedObjectsClient();
const indexPattern = await savedObjectsClient.get('index-pattern', config.get('defaultIndex'));
return indexPattern.attributes.title;
};
fetchDefaultParams = async () => {
this.state.vis.params.default_index_pattern = await this.fetchDefaultIndexPattern();
this.state.vis.fields = await fetchIndexPatternFields(this.state.vis);
this.state.isLoaded = true;
};
getComponent = () => {
return this.state.vis.type.editorConfig.component;
};
async render(params) {
const Component = this.getComponent();
!this.state.isLoaded && await this.fetchDefaultParams();
render(
<I18nContext>
<Component
config={config}
vis={this.state.vis}
visFields={this.state.vis.fields}
visParams={this.state.vis.params}
savedObj={this.state.savedObj}
timeRange={params.timeRange}
renderComplete={() => {}}
isEditorMode={true}
appState={params.appState}
/>
</I18nContext>,
this.el);
}
destroy() {
unmountComponentAtNode(this.el);
}
}
return {
name: 'react_editor',
handler: ReactEditorController,
};
}
export { ReactEditorControllerProvider };

View file

@ -16,8 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import { uniq } from 'lodash';
export function extractIndexPatterns(params, fetchedFields) {
const patternsToFetch = [];
@ -41,6 +41,9 @@ export function extractIndexPatterns(params, fetchedFields) {
});
}
return uniq(patternsToFetch);
if (patternsToFetch.length === 0) {
patternsToFetch.push('');
}
return uniq(patternsToFetch).sort();
}

View file

@ -30,27 +30,28 @@ export async function fetchFields(indexPatterns = ['*']) {
pathname: '/api/metrics/fields',
query: {
index: pattern,
}
},
});
}));
const fields = patterns.reduce((cumulatedFields, currentPattern, index) => {
return {
...cumulatedFields,
[currentPattern]: indexFields[index]
[currentPattern]: indexFields[index],
};
}, {});
return fields;
} catch(error) {
} catch (error) {
toastNotifications.addDanger({
title: i18n.translate('tsvb.fetchFields.loadIndexPatternFieldsErrorMessage', {
defaultMessage: 'Unable to load index_pattern fields'
defaultMessage: 'Unable to load index_pattern fields',
}),
text: error.message,
});
}
}
export async function fetchIndexPatternFields(params, fields) {
export async function fetchIndexPatternFields({ params, fields = {} }) {
const indexPatterns = extractIndexPatterns(params, fields);
return await fetchFields(indexPatterns);
}