diff --git a/src/core_plugins/kibana/public/home/components/sample_data_set_card.js b/src/core_plugins/kibana/public/home/components/sample_data_set_card.js
index 4e8554e7324f8..a19eaf1c5dd8a 100644
--- a/src/core_plugins/kibana/public/home/components/sample_data_set_card.js
+++ b/src/core_plugins/kibana/public/home/components/sample_data_set_card.js
@@ -28,70 +28,40 @@ import {
EuiToolTip,
} from '@elastic/eui';
-import {
- installSampleDataSet,
- uninstallSampleDataSet
-} from '../sample_data_sets';
+export const INSTALLED_STATUS = 'installed';
+export const UNINSTALLED_STATUS = 'not_installed';
export class SampleDataSetCard extends React.Component {
- constructor(props) {
- super(props);
-
- this.state = {
- isProcessingRequest: false,
- };
- }
-
- startRequest = async () => {
- const {
- getConfig,
- setConfig,
- id,
- name,
- onRequestComplete,
- defaultIndex,
- clearIndexPatternsCache,
- } = this.props;
-
- this.setState({
- isProcessingRequest: true,
- });
-
- if (this.isInstalled()) {
- await uninstallSampleDataSet(id, name, defaultIndex, getConfig, setConfig, clearIndexPatternsCache);
- } else {
- await installSampleDataSet(id, name, defaultIndex, getConfig, setConfig, clearIndexPatternsCache);
- }
-
- onRequestComplete();
-
- this.setState({
- isProcessingRequest: false,
- });
- }
-
isInstalled = () => {
- if (this.props.status === 'installed') {
+ if (this.props.status === INSTALLED_STATUS) {
return true;
}
return false;
}
+ install = () => {
+ this.props.onInstall(this.props.id);
+ }
+
+ uninstall = () => {
+ this.props.onUninstall(this.props.id);
+ }
+
renderBtn = () => {
switch (this.props.status) {
- case 'installed':
+ case INSTALLED_STATUS:
return (
- {this.state.isProcessingRequest ? 'Removing' : 'Remove'}
+ {this.props.isProcessing ? 'Removing' : 'Remove'}
@@ -105,16 +75,16 @@ export class SampleDataSetCard extends React.Component {
);
- case 'not_installed':
+ case UNINSTALLED_STATUS:
return (
- {this.state.isProcessingRequest ? 'Adding' : 'Add'}
+ {this.props.isProcessing ? 'Adding' : 'Add'}
@@ -163,15 +133,13 @@ SampleDataSetCard.propTypes = {
name: PropTypes.string.isRequired,
launchUrl: PropTypes.string.isRequired,
status: PropTypes.oneOf([
- 'installed',
- 'not_installed',
+ INSTALLED_STATUS,
+ UNINSTALLED_STATUS,
'unknown',
]).isRequired,
+ isProcessing: PropTypes.bool.isRequired,
statusMsg: PropTypes.string,
- onRequestComplete: PropTypes.func.isRequired,
- getConfig: PropTypes.func.isRequired,
- setConfig: PropTypes.func.isRequired,
- clearIndexPatternsCache: PropTypes.func.isRequired,
- defaultIndex: PropTypes.string.isRequired,
previewUrl: PropTypes.string.isRequired,
+ onInstall: PropTypes.func.isRequired,
+ onUninstall: PropTypes.func.isRequired,
};
diff --git a/src/core_plugins/kibana/public/home/components/sample_data_set_cards.js b/src/core_plugins/kibana/public/home/components/sample_data_set_cards.js
new file mode 100644
index 0000000000000..4f50af9677876
--- /dev/null
+++ b/src/core_plugins/kibana/public/home/components/sample_data_set_cards.js
@@ -0,0 +1,211 @@
+/*
+ * 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 _ from 'lodash';
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ EuiFlexGrid,
+ EuiFlexItem,
+} from '@elastic/eui';
+
+import {
+ SampleDataSetCard,
+ INSTALLED_STATUS,
+ UNINSTALLED_STATUS,
+} from './sample_data_set_card';
+
+import { toastNotifications } from 'ui/notify';
+
+import {
+ listSampleDataSets,
+ installSampleDataSet,
+ uninstallSampleDataSet
+} from '../sample_data_sets';
+
+export class SampleDataSetCards extends React.Component {
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ sampleDataSets: [],
+ processingStatus: {},
+ };
+ }
+
+ componentWillUnmount() {
+ this._isMounted = false;
+ }
+
+ async componentDidMount() {
+ this._isMounted = true;
+
+ this.loadSampleDataSets();
+ }
+
+ loadSampleDataSets = async () => {
+ let sampleDataSets;
+ try {
+ sampleDataSets = await listSampleDataSets();
+ } catch (fetchError) {
+ toastNotifications.addDanger({
+ title: `Unable to load sample data sets list`,
+ text: `${fetchError.message}`,
+ });
+ sampleDataSets = [];
+ }
+
+ if (!this._isMounted) {
+ return;
+ }
+
+ this.setState({
+ sampleDataSets: sampleDataSets
+ .sort((a, b) => {
+ return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
+ }),
+ processingStatus: {},
+ });
+ }
+
+ install = async (id) => {
+ const {
+ getConfig,
+ setConfig,
+ clearIndexPatternsCache,
+ } = this.props;
+
+ const targetSampleDataSet = this.state.sampleDataSets.find((sampleDataSet) => {
+ return sampleDataSet.id === id;
+ });
+
+ this.setState((prevState) => ({
+ processingStatus: { ...prevState.processingStatus, [id]: true }
+ }));
+
+ try {
+ await installSampleDataSet(id, targetSampleDataSet.defaultIndex, getConfig, setConfig, clearIndexPatternsCache);
+ } catch (fetchError) {
+ if (this._isMounted) {
+ this.setState((prevState) => ({
+ processingStatus: { ...prevState.processingStatus, [id]: false }
+ }));
+ }
+ toastNotifications.addDanger({
+ title: `Unable to install sample data set: ${targetSampleDataSet.name}`,
+ text: `${fetchError.message}`,
+ });
+ return;
+ }
+
+ this.setState((prevState) => ({
+ processingStatus: { ...prevState.processingStatus, [id]: false },
+ sampleDataSets: prevState.sampleDataSets.map(sampleDataSet => {
+ if (sampleDataSet.id === id) {
+ sampleDataSet.status = INSTALLED_STATUS;
+ }
+ return sampleDataSet;
+ }),
+ }));
+ toastNotifications.addSuccess({
+ title: `${targetSampleDataSet.name} installed`,
+ ['data-test-subj']: 'sampleDataSetInstallToast'
+ });
+ }
+
+ uninstall = async (id) => {
+ const {
+ getConfig,
+ setConfig,
+ clearIndexPatternsCache,
+ } = this.props;
+
+ const targetSampleDataSet = this.state.sampleDataSets.find((sampleDataSet) => {
+ return sampleDataSet.id === id;
+ });
+
+ this.setState((prevState) => ({
+ processingStatus: { ...prevState.processingStatus, [id]: true }
+ }));
+
+ try {
+ await uninstallSampleDataSet(id, targetSampleDataSet.defaultIndex, getConfig, setConfig, clearIndexPatternsCache);
+ } catch (fetchError) {
+ if (this._isMounted) {
+ this.setState((prevState) => ({
+ processingStatus: { ...prevState.processingStatus, [id]: false }
+ }));
+ }
+ toastNotifications.addDanger({
+ title: `Unable to uninstall sample data set: ${targetSampleDataSet.name}`,
+ text: `${fetchError.message}`,
+ });
+ return;
+ }
+
+ this.setState((prevState) => ({
+ processingStatus: { ...prevState.processingStatus, [id]: false },
+ sampleDataSets: prevState.sampleDataSets.map(sampleDataSet => {
+ if (sampleDataSet.id === id) {
+ sampleDataSet.status = UNINSTALLED_STATUS;
+ }
+ return sampleDataSet;
+ }),
+ }));
+ toastNotifications.addSuccess({
+ title: `${targetSampleDataSet.name} uninstalled`,
+ ['data-test-subj']: 'sampleDataSetUninstallToast'
+ });
+ }
+
+ render() {
+ return (
+
+ {
+ this.state.sampleDataSets.map(sampleDataSet => {
+ return (
+
+
+
+ );
+ })
+ }
+
+ );
+ }
+}
+
+SampleDataSetCards.propTypes = {
+ getConfig: PropTypes.func.isRequired,
+ setConfig: PropTypes.func.isRequired,
+ clearIndexPatternsCache: PropTypes.func.isRequired,
+ addBasePath: PropTypes.func.isRequired,
+};
diff --git a/src/core_plugins/kibana/public/home/components/tutorial_directory.js b/src/core_plugins/kibana/public/home/components/tutorial_directory.js
index 77ca6e379d710..d1835e5546c43 100644
--- a/src/core_plugins/kibana/public/home/components/tutorial_directory.js
+++ b/src/core_plugins/kibana/public/home/components/tutorial_directory.js
@@ -21,7 +21,7 @@ import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { Synopsis } from './synopsis';
-import { SampleDataSetCard } from './sample_data_set_card';
+import { SampleDataSetCards } from './sample_data_set_cards';
import {
EuiPage,
@@ -36,7 +36,6 @@ import {
import { getTutorials } from '../load_tutorials';
-import { listSampleDataSets } from '../sample_data_sets';
const ALL_TAB_ID = 'all';
const SAMPLE_DATA_TAB_ID = 'sampleData';
@@ -70,7 +69,6 @@ export class TutorialDirectory extends React.Component {
this.state = {
selectedTabId: openTab,
tutorialCards: [],
- sampleDataSets: [],
};
}
@@ -81,8 +79,6 @@ export class TutorialDirectory extends React.Component {
async componentDidMount() {
this._isMounted = true;
- this.loadSampleDataSets();
-
const tutorialConfigs = await getTutorials();
if (!this._isMounted) {
@@ -126,20 +122,6 @@ export class TutorialDirectory extends React.Component {
});
}
- loadSampleDataSets = async () => {
- const sampleDataSets = await listSampleDataSets();
-
- if (!this._isMounted) {
- return;
- }
-
- this.setState({
- sampleDataSets: sampleDataSets.sort((a, b) => {
- return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
- }),
- });
- }
-
onSelectedTabChanged = id => {
this.setState({
selectedTabId: id,
@@ -158,57 +140,43 @@ export class TutorialDirectory extends React.Component {
));
}
- renderTab = () => {
+ renderTabContent = () => {
if (this.state.selectedTabId === SAMPLE_DATA_TAB_ID) {
- return this.renderSampleDataSetsTab();
- }
-
- return this.renderTutorialsTab();
- }
-
- renderTutorialsTab = () => {
- return this.state.tutorialCards
- .filter((tutorial) => {
- return this.state.selectedTabId === ALL_TAB_ID || this.state.selectedTabId === tutorial.category;
- })
- .map((tutorial) => {
- return (
-
-
-
- );
- });
- };
-
- renderSampleDataSetsTab = () => {
- return this.state.sampleDataSets.map(sampleDataSet => {
return (
-
-
-
+
);
- });
+ }
+
+ return (
+
+ {
+ this.state.tutorialCards
+ .filter((tutorial) => {
+ return this.state.selectedTabId === ALL_TAB_ID || this.state.selectedTabId === tutorial.category;
+ })
+ .map((tutorial) => {
+ return (
+
+
+
+ );
+ })
+ }
+
+ );
}
render() {
@@ -230,9 +198,7 @@ export class TutorialDirectory extends React.Component {
{this.renderTabs()}
-
- { this.renderTab() }
-
+ {this.renderTabContent()}
diff --git a/src/core_plugins/kibana/public/home/sample_data_sets.js b/src/core_plugins/kibana/public/home/sample_data_sets.js
index 363986623bbb9..84823e1c01fca 100644
--- a/src/core_plugins/kibana/public/home/sample_data_sets.js
+++ b/src/core_plugins/kibana/public/home/sample_data_sets.js
@@ -17,57 +17,16 @@
* under the License.
*/
-import chrome from 'ui/chrome';
-import { toastNotifications } from 'ui/notify';
+import { kfetch } from 'ui/kfetch';
-const sampleDataUrl = chrome.addBasePath('/api/sample_data');
-const headers = new Headers({
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- 'kbn-xsrf': 'kibana',
-});
+const sampleDataUrl = '/api/sample_data';
export async function listSampleDataSets() {
- try {
- const response = await fetch(sampleDataUrl, {
- method: 'get',
- credentials: 'include',
- headers: headers,
- });
-
- if (response.status >= 300) {
- throw new Error(`Request failed with status code: ${response.status}`);
- }
-
- return await response.json();
- } catch (err) {
- toastNotifications.addDanger({
- title: `Unable to load sample data sets list`,
- text: `${err.message}`,
- });
- return [];
- }
+ return await kfetch({ method: 'GET', pathname: sampleDataUrl });
}
-export async function installSampleDataSet(id, name, defaultIndex, getConfig, setConfig, clearIndexPatternsCache) {
- try {
- const response = await fetch(`${sampleDataUrl}/${id}`, {
- method: 'post',
- credentials: 'include',
- headers: headers,
- });
-
- if (response.status >= 300) {
- const body = await response.text();
- throw new Error(`Request failed with status code: ${response.status}, message: ${body}`);
- }
- } catch (err) {
- toastNotifications.addDanger({
- title: `Unable to install sample data set: ${name}`,
- text: `${err.message}`,
- });
- return;
- }
+export async function installSampleDataSet(id, defaultIndex, getConfig, setConfig, clearIndexPatternsCache) {
+ await kfetch({ method: 'POST', pathname: `${sampleDataUrl}/${id}` });
const existingDefaultIndex = await getConfig('defaultIndex');
if (existingDefaultIndex === null) {
@@ -75,31 +34,10 @@ export async function installSampleDataSet(id, name, defaultIndex, getConfig, se
}
clearIndexPatternsCache();
-
- toastNotifications.addSuccess({
- title: `${name} installed`,
- ['data-test-subj']: 'sampleDataSetInstallToast'
- });
}
-export async function uninstallSampleDataSet(id, name, defaultIndex, getConfig, setConfig, clearIndexPatternsCache) {
- try {
- const response = await fetch(`${sampleDataUrl}/${id}`, {
- method: 'delete',
- credentials: 'include',
- headers: headers,
- });
- if (response.status >= 300) {
- const body = await response.text();
- throw new Error(`Request failed with status code: ${response.status}, message: ${body}`);
- }
- } catch (err) {
- toastNotifications.addDanger({
- title: `Unable to uninstall sample data set`,
- text: `${err.message}`,
- });
- return;
- }
+export async function uninstallSampleDataSet(id, defaultIndex, getConfig, setConfig, clearIndexPatternsCache) {
+ await kfetch({ method: 'DELETE', pathname: `${sampleDataUrl}/${id}` });
const existingDefaultIndex = await getConfig('defaultIndex');
if (existingDefaultIndex && existingDefaultIndex === defaultIndex) {
@@ -107,9 +45,4 @@ export async function uninstallSampleDataSet(id, name, defaultIndex, getConfig,
}
clearIndexPatternsCache();
-
- toastNotifications.addSuccess({
- title: `${name} uninstalled`,
- ['data-test-subj']: 'sampleDataSetUninstallToast'
- });
}
diff --git a/src/server/sample_data/routes/uninstall.js b/src/server/sample_data/routes/uninstall.js
index d28b04dad61c2..98b03dad06010 100644
--- a/src/server/sample_data/routes/uninstall.js
+++ b/src/server/sample_data/routes/uninstall.js
@@ -63,7 +63,7 @@ export const createUninstallRoute = () => ({
}
}
- reply();
+ reply({});
}
}
});
diff --git a/test/functional/apps/home/_sample_data.js b/test/functional/apps/home/_sample_data.js
index 48cc3cf509936..ea8174e279ab6 100644
--- a/test/functional/apps/home/_sample_data.js
+++ b/test/functional/apps/home/_sample_data.js
@@ -41,11 +41,6 @@ export default function ({ getService, getPageObjects }) {
it('should install sample data set', async ()=> {
await PageObjects.home.addSampleDataSet('flights');
- await retry.try(async () => {
- const successToastExists = await PageObjects.home.doesSampleDataSetSuccessfulInstallToastExist();
- expect(successToastExists).to.be(true);
- });
-
const isInstalled = await PageObjects.home.isSampleDataSetInstalled('flights');
expect(isInstalled).to.be(true);
});
@@ -98,11 +93,6 @@ export default function ({ getService, getPageObjects }) {
describe('uninstall', () => {
it('should uninstall sample data set', async ()=> {
await PageObjects.home.removeSampleDataSet('flights');
- await retry.try(async () => {
- const successToastExists = await PageObjects.home.doesSampleDataSetSuccessfulUninstallToastExist();
- expect(successToastExists).to.be(true);
- });
-
const isInstalled = await PageObjects.home.isSampleDataSetInstalled('flights');
expect(isInstalled).to.be(false);
});
diff --git a/test/functional/page_objects/home_page.js b/test/functional/page_objects/home_page.js
index 77205e7bc3323..78dae43bf0684 100644
--- a/test/functional/page_objects/home_page.js
+++ b/test/functional/page_objects/home_page.js
@@ -53,10 +53,22 @@ export function HomePageProvider({ getService }) {
async addSampleDataSet(id) {
await testSubjects.click(`addSampleDataSet${id}`);
+ await this._waitForSampleDataLoadingAction(id);
}
async removeSampleDataSet(id) {
await testSubjects.click(`removeSampleDataSet${id}`);
+ await this._waitForSampleDataLoadingAction(id);
+ }
+
+ // loading action is either uninstall and install
+ async _waitForSampleDataLoadingAction(id) {
+ const sampleDataCard = await testSubjects.find(`sampleDataSetCard${id}`);
+ await retry.try(async () => {
+ // waitForDeletedByClassName needs to be inside retry because it will timeout at least once
+ // before action is complete
+ await sampleDataCard.waitForDeletedByClassName('euiLoadingSpinner');
+ });
}
async launchSampleDataSet(id) {