Skip to content

Commit

Permalink
Merge branch 'main' into fixes-flaky-spaces-data-before-all
Browse files Browse the repository at this point in the history
  • Loading branch information
jeramysoucy authored Dec 14, 2022
2 parents eed1d10 + b74e7f9 commit 0a63fc3
Show file tree
Hide file tree
Showing 61 changed files with 1,560 additions and 706 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { BehaviorSubject, Observable } from 'rxjs';
import { isEqual } from 'lodash';
import { getIndexPatterns } from '../utils';
import { IndexPatternTableItem } from '../types';
import { stateSelectorFactory } from '../state_helpers';

export interface DataViewTableControllerState {
isLoadingDataViews: boolean;
isLoadingHasData: boolean;
hasDataView: boolean;
hasEsData: boolean;
dataViews: IndexPatternTableItem[];
}

export interface DataViewTableControllerConstructorArgs {
services: {
dataViews: DataViewsPublicPluginStart;
};
config: {
defaultDataView: string;
};
}

export const dataViewTableControllerStateDefaults = {
isLoadingDataViews: false,
isLoadingHasData: true,
hasDataView: false,
hasEsData: false,
dataViews: [],
};

const selectIndexPattern = (state: DataViewTableControllerState) => state.dataViews;
const selectHasDataView = (state: DataViewTableControllerState) => state.hasDataView;
const selectHasEsData = (state: DataViewTableControllerState) => state.hasEsData;
const selectIsLoadingIndexPatterns = (state: DataViewTableControllerState) =>
state.isLoadingDataViews;
const selectIsLoadingDataState = (state: DataViewTableControllerState) => state.isLoadingHasData;

export class DataViewTableController {
constructor({
services: { dataViews },
config: { defaultDataView },
}: DataViewTableControllerConstructorArgs) {
this.dataViews = dataViews;
this.defaultDataView = defaultDataView;

const stateSelector = stateSelectorFactory(this.state$);

this.isLoadingIndexPatterns$ = stateSelector(selectIsLoadingIndexPatterns);
this.indexPatterns$ = stateSelector(selectIndexPattern, isEqual);
this.isLoadingDataState$ = stateSelector(selectIsLoadingDataState);
this.hasDataView$ = stateSelector(selectHasDataView);
this.hasESData$ = stateSelector(selectHasEsData);

this.loadDataViews();
}

private state: DataViewTableControllerState = {
...dataViewTableControllerStateDefaults,
};

private state$ = new BehaviorSubject<DataViewTableControllerState>(this.state);

private dataViews: DataViewsPublicPluginStart;
private defaultDataView: string;

isLoadingIndexPatterns$: Observable<boolean>;
indexPatterns$: Observable<IndexPatternTableItem[]>;
isLoadingDataState$: Observable<boolean>;
hasDataView$: Observable<boolean>;
hasESData$: Observable<boolean>;

private updateState = (newState: Partial<DataViewTableControllerState>) => {
this.state = { ...this.state, ...newState };
this.state$.next(this.state);
};

private loadHasData = async () => {
const hasDataViewPromise = this.dataViews.hasData.hasDataView().then((hasDataView) => {
this.updateState({ hasDataView });
});

const hasESDataPromise = this.dataViews.hasData.hasESData().then((hasEsData) => {
this.updateState({ hasEsData });
});

return Promise.all([hasDataViewPromise, hasESDataPromise]).then(() => {
this.updateState({ isLoadingHasData: false });
});
};

private getDataViews = async () => {
this.updateState({ isLoadingDataViews: true });
const dataViews = await getIndexPatterns(this.defaultDataView, this.dataViews);
this.updateState({ dataViews, isLoadingDataViews: false });
};

loadDataViews = async () => {
const loadHasDataPromise = this.loadHasData();
const getDataViewsPromise = this.getDataViews();
return Promise.all([loadHasDataPromise, getDataViewsPromise]);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,23 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { RouteComponentProps, withRouter, useLocation } from 'react-router-dom';
import React, { useEffect, useState, useCallback, useMemo } from 'react';
import useObservable from 'react-use/lib/useObservable';
import React, { useState, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { reactRouterNavigate, useKibana } from '@kbn/kibana-react-plugin/public';
import type { SpacesContextProps } from '@kbn/spaces-plugin/public';
import { NoDataViewsPromptComponent } from '@kbn/shared-ux-prompt-no-data-views';
import { EmptyIndexListPrompt } from '../empty_index_list_prompt';
import { IndexPatternManagmentContext } from '../../types';
import { IndexPatternTableItem } from '../types';
import { getIndexPatterns } from '../utils';
import { getListBreadcrumbs } from '../breadcrumbs';
import { SpacesList } from './spaces_list';
import { removeDataView, RemoveDataViewProps } from '../edit_index_pattern';
import { deleteModalMsg } from './delete_modal_msg';
import {
DataViewTableController,
dataViewTableControllerStateDefaults as defaults,
} from './data_view_table_controller';

const pagination = {
initialPageSize: 10,
Expand Down Expand Up @@ -63,11 +67,6 @@ interface Props extends RouteComponentProps {
showCreateDialog?: boolean;
}

interface DataState {
hasDataView?: boolean;
hasESData?: boolean;
}

const getEmptyFunctionComponent: React.FC<SpacesContextProps> = ({ children }) => <>{children}</>;

export const IndexPatternTable = ({
Expand All @@ -78,7 +77,6 @@ export const IndexPatternTable = ({
const {
setBreadcrumbs,
uiSettings,
indexPatternManagementStart,
application,
chrome,
dataViews,
Expand All @@ -88,29 +86,42 @@ export const IndexPatternTable = ({
docLinks,
} = useKibana<IndexPatternManagmentContext>().services;
const [query, setQuery] = useState('');
const [indexPatterns, setIndexPatterns] = useState<IndexPatternTableItem[]>([]);
const [isLoadingIndexPatterns, setIsLoadingIndexPatterns] = useState<boolean>(true);
const [showCreateDialog, setShowCreateDialog] = useState<boolean>(showCreateDialogProp);
const [selectedItems, setSelectedItems] = useState<IndexPatternTableItem[]>([]);
const [isLoadingDataState, setIsLoadingDataState] = useState<boolean>(true);
const [dataState, setDataState] = useState<DataState>({});
const [dataViewController] = useState(
() =>
new DataViewTableController({
services: { dataViews },
config: { defaultDataView: uiSettings.get('defaultIndex') },
})
);

const isLoadingIndexPatterns = useObservable(
dataViewController.isLoadingIndexPatterns$,
defaults.isLoadingDataViews
);
const indexPatterns = useObservable(dataViewController.indexPatterns$, defaults.dataViews);
const isLoadingDataState = useObservable(
dataViewController.isLoadingDataState$,
defaults.isLoadingHasData
);
const hasDataView = useObservable(dataViewController.hasDataView$, defaults.hasDataView);
const hasESData = useObservable(dataViewController.hasESData$, defaults.hasEsData);

const handleOnChange = ({ queryText, error }: { queryText: string; error: unknown }) => {
if (!error) {
setQuery(queryText);
}
};

const { hasDataView, hasESData } = dataState;

const renderDeleteButton = () => {
const clickHandler = removeDataView({
dataViews,
overlays,
uiSettings,
onDelete: () => {
setSelectedItems([]);
loadDataViews();
dataViewController.loadDataViews();
},
});
if (selectedItems.length === 0) {
Expand Down Expand Up @@ -148,30 +159,8 @@ export const IndexPatternTable = ({
},
};

const loadDataViews = useCallback(async () => {
setIsLoadingIndexPatterns(true);
setDataState({
hasDataView: await dataViews.hasData.hasDataView(),
hasESData: await dataViews.hasData.hasESData(),
});
setIsLoadingDataState(false);
const gettedIndexPatterns: IndexPatternTableItem[] = await getIndexPatterns(
uiSettings.get('defaultIndex'),
dataViews
);
setIndexPatterns(gettedIndexPatterns);
setIsLoadingIndexPatterns(false);
return gettedIndexPatterns;
}, [dataViews, uiSettings]);

setBreadcrumbs(getListBreadcrumbs());

useEffect(() => {
(async function () {
await loadDataViews();
})();
}, [indexPatternManagementStart, uiSettings, loadDataViews]);

chrome.docTitle.change(title);

const isRollup = new URLSearchParams(useLocation().search).get('type') === 'rollup';
Expand All @@ -185,7 +174,7 @@ export const IndexPatternTable = ({
dataViews,
uiSettings,
overlays,
onDelete: () => loadDataViews(),
onDelete: () => dataViewController.loadDataViews(),
});

const alertColumn = {
Expand Down Expand Up @@ -268,7 +257,7 @@ export const IndexPatternTable = ({
title={dataView.title}
refresh={() => {
dataViews.clearInstanceCache(dataView.id);
loadDataViews();
dataViewController.loadDataViews();
}}
/>
) : (
Expand Down Expand Up @@ -365,7 +354,7 @@ export const IndexPatternTable = ({
<>
<EuiSpacer size="xxl" />
<EmptyIndexListPrompt
onRefresh={loadDataViews}
onRefresh={dataViewController.loadDataViews}
createAnyway={() => setShowCreateDialog(true)}
canSaveIndexPattern={!!application.capabilities.indexPatterns.save}
navigateToApp={application.navigateToApp}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { map, distinctUntilChanged, Observable } from 'rxjs';

export const stateSelectorFactory =
<S>(state$: Observable<S>) =>
<R>(selector: (state: S) => R, equalityFn?: (arg0: R, arg1: R) => boolean) =>
state$.pipe(map(selector), distinctUntilChanged(equalityFn));
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { apm, timerange } from '@kbn/apm-synthtrace';

export function generateData({ from, to }: { from: number; to: number }) {
const range = timerange(from, to);
const synthGo1 = apm
.service({
name: 'synth-go-1',
environment: 'production',
agentName: 'go',
})
.instance('my-instance');

const synthIOS = apm
.service({
name: 'synth-ios',
environment: 'production',
agentName: 'iOS/swift',
})
.instance('my-instance');

const synthAndroid = apm
.service({
name: 'synth-android',
environment: 'production',
agentName: 'android/java',
})
.instance('my-instance');

return range.interval('1m').generator((timestamp) => {
return [
synthGo1
.transaction({ transactionName: 'GET /apple 🍎' })
.timestamp(timestamp)
.duration(1000)
.success(),
synthIOS
.transaction({ transactionName: 'GET /banana 🍌' })
.timestamp(timestamp)
.duration(1000)
.success(),
synthAndroid
.transaction({ transactionName: 'GET /apple 🍎' })
.timestamp(timestamp)
.duration(1000)
.success(),
];
});
}
Loading

0 comments on commit 0a63fc3

Please sign in to comment.