Fix sample data for share-capable objects (#116378)
This commit is contained in:
parent
f2b9acf67b
commit
61ab4a3c59
|
@ -67,7 +67,6 @@ export class SampleDataSetCards extends React.Component {
|
||||||
sampleDataSets: sampleDataSets.sort((a, b) => {
|
sampleDataSets: sampleDataSets.sort((a, b) => {
|
||||||
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||||
}),
|
}),
|
||||||
processingStatus: {},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,6 +81,7 @@ export class SampleDataSetCards extends React.Component {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await installSampleDataSet(id, targetSampleDataSet.defaultIndex);
|
await installSampleDataSet(id, targetSampleDataSet.defaultIndex);
|
||||||
|
await this.loadSampleDataSets(); // reload the list of sample data sets
|
||||||
} catch (fetchError) {
|
} catch (fetchError) {
|
||||||
if (this._isMounted) {
|
if (this._isMounted) {
|
||||||
this.setState((prevState) => ({
|
this.setState((prevState) => ({
|
||||||
|
|
|
@ -19,7 +19,9 @@ export type {
|
||||||
export { FeatureCatalogueCategory } from './services';
|
export { FeatureCatalogueCategory } from './services';
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
|
AddDataTab,
|
||||||
FeatureCatalogueEntry,
|
FeatureCatalogueEntry,
|
||||||
|
FeatureCatalogueRegistry,
|
||||||
FeatureCatalogueSolution,
|
FeatureCatalogueSolution,
|
||||||
Environment,
|
Environment,
|
||||||
TutorialVariables,
|
TutorialVariables,
|
||||||
|
|
|
@ -7,8 +7,21 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type { HomeServerPluginSetup, HomeServerPluginStart } from './plugin';
|
export type { HomeServerPluginSetup, HomeServerPluginStart } from './plugin';
|
||||||
export type { TutorialProvider } from './services';
|
export { EmbeddableTypes, TutorialsCategory } from './services';
|
||||||
export type { SampleDatasetProvider, SampleDataRegistrySetup } from './services';
|
export type {
|
||||||
|
AppLinkData,
|
||||||
|
ArtifactsSchema,
|
||||||
|
TutorialProvider,
|
||||||
|
TutorialSchema,
|
||||||
|
InstructionSetSchema,
|
||||||
|
InstructionsSchema,
|
||||||
|
TutorialContext,
|
||||||
|
SampleDatasetProvider,
|
||||||
|
SampleDataRegistrySetup,
|
||||||
|
SampleDatasetDashboardPanel,
|
||||||
|
SampleObject,
|
||||||
|
ScopedTutorialContextFactory,
|
||||||
|
} from './services';
|
||||||
import { PluginInitializerContext, PluginConfigDescriptor } from 'kibana/server';
|
import { PluginInitializerContext, PluginConfigDescriptor } from 'kibana/server';
|
||||||
import { HomeServerPlugin } from './plugin';
|
import { HomeServerPlugin } from './plugin';
|
||||||
import { configSchema, ConfigSchema } from '../config';
|
import { configSchema, ConfigSchema } from '../config';
|
||||||
|
@ -23,10 +36,3 @@ export const config: PluginConfigDescriptor<ConfigSchema> = {
|
||||||
export const plugin = (initContext: PluginInitializerContext) => new HomeServerPlugin(initContext);
|
export const plugin = (initContext: PluginInitializerContext) => new HomeServerPlugin(initContext);
|
||||||
|
|
||||||
export { INSTRUCTION_VARIANT } from '../common/instruction_variant';
|
export { INSTRUCTION_VARIANT } from '../common/instruction_variant';
|
||||||
export { TutorialsCategory } from './services/tutorials';
|
|
||||||
export type {
|
|
||||||
ArtifactsSchema,
|
|
||||||
TutorialSchema,
|
|
||||||
InstructionSetSchema,
|
|
||||||
InstructionsSchema,
|
|
||||||
} from './services/tutorials';
|
|
||||||
|
|
|
@ -9,10 +9,9 @@
|
||||||
// provided to other plugins as APIs
|
// provided to other plugins as APIs
|
||||||
// should model the plugin lifecycle
|
// should model the plugin lifecycle
|
||||||
|
|
||||||
export { TutorialsRegistry } from './tutorials';
|
export { TutorialsRegistry, TutorialsCategory } from './tutorials';
|
||||||
export type { TutorialsRegistrySetup, TutorialsRegistryStart } from './tutorials';
|
|
||||||
|
|
||||||
export { TutorialsCategory } from './tutorials';
|
export type { TutorialsRegistrySetup, TutorialsRegistryStart } from './tutorials';
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
InstructionSetSchema,
|
InstructionSetSchema,
|
||||||
|
@ -22,12 +21,19 @@ export type {
|
||||||
ArtifactsSchema,
|
ArtifactsSchema,
|
||||||
TutorialSchema,
|
TutorialSchema,
|
||||||
TutorialProvider,
|
TutorialProvider,
|
||||||
|
TutorialContext,
|
||||||
TutorialContextFactory,
|
TutorialContextFactory,
|
||||||
ScopedTutorialContextFactory,
|
ScopedTutorialContextFactory,
|
||||||
} from './tutorials';
|
} from './tutorials';
|
||||||
|
|
||||||
export { SampleDataRegistry } from './sample_data';
|
export { EmbeddableTypes, SampleDataRegistry } from './sample_data';
|
||||||
|
|
||||||
export type { SampleDataRegistrySetup, SampleDataRegistryStart } from './sample_data';
|
export type {
|
||||||
|
AppLinkData,
|
||||||
export type { SampleDatasetSchema, SampleDatasetProvider } from './sample_data';
|
SampleDataRegistrySetup,
|
||||||
|
SampleDataRegistryStart,
|
||||||
|
SampleDatasetDashboardPanel,
|
||||||
|
SampleDatasetProvider,
|
||||||
|
SampleDatasetSchema,
|
||||||
|
SampleObject,
|
||||||
|
} from './sample_data';
|
||||||
|
|
|
@ -10,7 +10,7 @@ import path from 'path';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { getSavedObjects } from './saved_objects';
|
import { getSavedObjects } from './saved_objects';
|
||||||
import { fieldMappings } from './field_mappings';
|
import { fieldMappings } from './field_mappings';
|
||||||
import { SampleDatasetSchema, AppLinkSchema } from '../../lib/sample_dataset_registry_types';
|
import { SampleDatasetSchema } from '../../lib/sample_dataset_registry_types';
|
||||||
|
|
||||||
const ecommerceName = i18n.translate('home.sampleData.ecommerceSpecTitle', {
|
const ecommerceName = i18n.translate('home.sampleData.ecommerceSpecTitle', {
|
||||||
defaultMessage: 'Sample eCommerce orders',
|
defaultMessage: 'Sample eCommerce orders',
|
||||||
|
@ -18,7 +18,6 @@ const ecommerceName = i18n.translate('home.sampleData.ecommerceSpecTitle', {
|
||||||
const ecommerceDescription = i18n.translate('home.sampleData.ecommerceSpecDescription', {
|
const ecommerceDescription = i18n.translate('home.sampleData.ecommerceSpecDescription', {
|
||||||
defaultMessage: 'Sample data, visualizations, and dashboards for tracking eCommerce orders.',
|
defaultMessage: 'Sample data, visualizations, and dashboards for tracking eCommerce orders.',
|
||||||
});
|
});
|
||||||
const initialAppLinks = [] as AppLinkSchema[];
|
|
||||||
|
|
||||||
export const ecommerceSpecProvider = function (): SampleDatasetSchema {
|
export const ecommerceSpecProvider = function (): SampleDatasetSchema {
|
||||||
return {
|
return {
|
||||||
|
@ -28,7 +27,6 @@ export const ecommerceSpecProvider = function (): SampleDatasetSchema {
|
||||||
previewImagePath: '/plugins/home/assets/sample_data_resources/ecommerce/dashboard.png',
|
previewImagePath: '/plugins/home/assets/sample_data_resources/ecommerce/dashboard.png',
|
||||||
darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/ecommerce/dashboard_dark.png',
|
darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/ecommerce/dashboard_dark.png',
|
||||||
overviewDashboard: '722b74f0-b882-11e8-a6d9-e546fe2bba5f',
|
overviewDashboard: '722b74f0-b882-11e8-a6d9-e546fe2bba5f',
|
||||||
appLinks: initialAppLinks,
|
|
||||||
defaultIndex: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f',
|
defaultIndex: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f',
|
||||||
savedObjects: getSavedObjects(),
|
savedObjects: getSavedObjects(),
|
||||||
dataIndices: [
|
dataIndices: [
|
||||||
|
|
|
@ -140,9 +140,10 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
{
|
{
|
||||||
id: '9c6f83f0-bb4d-11e8-9c84-77068524bcab',
|
id: '9c6f83f0-bb4d-11e8-9c84-77068524bcab',
|
||||||
type: 'visualization',
|
type: 'visualization',
|
||||||
updated_at: '2018-10-01T15:13:03.270Z',
|
updated_at: '2021-10-28T15:07:24.077Z',
|
||||||
version: '1',
|
version: '1',
|
||||||
migrationVersion: {},
|
coreMigrationVersion: '8.0.0',
|
||||||
|
migrationVersion: { visualization: '8.0.0' },
|
||||||
attributes: {
|
attributes: {
|
||||||
title: i18n.translate('home.sampleData.ecommerceSpec.salesCountMapTitle', {
|
title: i18n.translate('home.sampleData.ecommerceSpec.salesCountMapTitle', {
|
||||||
defaultMessage: '[eCommerce] Sales Count Map',
|
defaultMessage: '[eCommerce] Sales Count Map',
|
||||||
|
@ -154,10 +155,16 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
version: 1,
|
version: 1,
|
||||||
kibanaSavedObjectMeta: {
|
kibanaSavedObjectMeta: {
|
||||||
searchSourceJSON:
|
searchSourceJSON:
|
||||||
'{"index":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","query":{"query":"","language":"kuery"},"filter":[]}',
|
'{"query":{"query":"","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
references: [],
|
references: [
|
||||||
|
{
|
||||||
|
id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f',
|
||||||
|
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||||
|
type: 'index-pattern',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
attributes: {
|
attributes: {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import path from 'path';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { getSavedObjects } from './saved_objects';
|
import { getSavedObjects } from './saved_objects';
|
||||||
import { fieldMappings } from './field_mappings';
|
import { fieldMappings } from './field_mappings';
|
||||||
import { SampleDatasetSchema, AppLinkSchema } from '../../lib/sample_dataset_registry_types';
|
import { SampleDatasetSchema } from '../../lib/sample_dataset_registry_types';
|
||||||
|
|
||||||
const flightsName = i18n.translate('home.sampleData.flightsSpecTitle', {
|
const flightsName = i18n.translate('home.sampleData.flightsSpecTitle', {
|
||||||
defaultMessage: 'Sample flight data',
|
defaultMessage: 'Sample flight data',
|
||||||
|
@ -18,7 +18,6 @@ const flightsName = i18n.translate('home.sampleData.flightsSpecTitle', {
|
||||||
const flightsDescription = i18n.translate('home.sampleData.flightsSpecDescription', {
|
const flightsDescription = i18n.translate('home.sampleData.flightsSpecDescription', {
|
||||||
defaultMessage: 'Sample data, visualizations, and dashboards for monitoring flight routes.',
|
defaultMessage: 'Sample data, visualizations, and dashboards for monitoring flight routes.',
|
||||||
});
|
});
|
||||||
const initialAppLinks = [] as AppLinkSchema[];
|
|
||||||
|
|
||||||
export const flightsSpecProvider = function (): SampleDatasetSchema {
|
export const flightsSpecProvider = function (): SampleDatasetSchema {
|
||||||
return {
|
return {
|
||||||
|
@ -28,7 +27,6 @@ export const flightsSpecProvider = function (): SampleDatasetSchema {
|
||||||
previewImagePath: '/plugins/home/assets/sample_data_resources/flights/dashboard.png',
|
previewImagePath: '/plugins/home/assets/sample_data_resources/flights/dashboard.png',
|
||||||
darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/flights/dashboard_dark.png',
|
darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/flights/dashboard_dark.png',
|
||||||
overviewDashboard: '7adfa750-4c81-11e8-b3d7-01146121b73d',
|
overviewDashboard: '7adfa750-4c81-11e8-b3d7-01146121b73d',
|
||||||
appLinks: initialAppLinks,
|
|
||||||
defaultIndex: 'd3d7af60-4c81-11e8-b3d7-01146121b73d',
|
defaultIndex: 'd3d7af60-4c81-11e8-b3d7-01146121b73d',
|
||||||
savedObjects: getSavedObjects(),
|
savedObjects: getSavedObjects(),
|
||||||
dataIndices: [
|
dataIndices: [
|
||||||
|
|
|
@ -10,7 +10,7 @@ import path from 'path';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { getSavedObjects } from './saved_objects';
|
import { getSavedObjects } from './saved_objects';
|
||||||
import { fieldMappings } from './field_mappings';
|
import { fieldMappings } from './field_mappings';
|
||||||
import { SampleDatasetSchema, AppLinkSchema } from '../../lib/sample_dataset_registry_types';
|
import { SampleDatasetSchema } from '../../lib/sample_dataset_registry_types';
|
||||||
|
|
||||||
const logsName = i18n.translate('home.sampleData.logsSpecTitle', {
|
const logsName = i18n.translate('home.sampleData.logsSpecTitle', {
|
||||||
defaultMessage: 'Sample web logs',
|
defaultMessage: 'Sample web logs',
|
||||||
|
@ -18,7 +18,6 @@ const logsName = i18n.translate('home.sampleData.logsSpecTitle', {
|
||||||
const logsDescription = i18n.translate('home.sampleData.logsSpecDescription', {
|
const logsDescription = i18n.translate('home.sampleData.logsSpecDescription', {
|
||||||
defaultMessage: 'Sample data, visualizations, and dashboards for monitoring web logs.',
|
defaultMessage: 'Sample data, visualizations, and dashboards for monitoring web logs.',
|
||||||
});
|
});
|
||||||
const initialAppLinks = [] as AppLinkSchema[];
|
|
||||||
|
|
||||||
export const GLOBE_ICON_PATH = '/plugins/home/assets/sample_data_resources/logs/icon.svg';
|
export const GLOBE_ICON_PATH = '/plugins/home/assets/sample_data_resources/logs/icon.svg';
|
||||||
export const logsSpecProvider = function (): SampleDatasetSchema {
|
export const logsSpecProvider = function (): SampleDatasetSchema {
|
||||||
|
@ -29,7 +28,6 @@ export const logsSpecProvider = function (): SampleDatasetSchema {
|
||||||
previewImagePath: '/plugins/home/assets/sample_data_resources/logs/dashboard.png',
|
previewImagePath: '/plugins/home/assets/sample_data_resources/logs/dashboard.png',
|
||||||
darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/logs/dashboard_dark.png',
|
darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/logs/dashboard_dark.png',
|
||||||
overviewDashboard: 'edf84fe0-e1a0-11e7-b6d5-4dc382ef7f5b',
|
overviewDashboard: 'edf84fe0-e1a0-11e7-b6d5-4dc382ef7f5b',
|
||||||
appLinks: initialAppLinks,
|
|
||||||
defaultIndex: '90943e30-9a47-11e8-b64d-95841ca0b247',
|
defaultIndex: '90943e30-9a47-11e8-b64d-95841ca0b247',
|
||||||
savedObjects: getSavedObjects(),
|
savedObjects: getSavedObjects(),
|
||||||
dataIndices: [
|
dataIndices: [
|
||||||
|
|
|
@ -14,9 +14,10 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
{
|
{
|
||||||
id: '06cf9c40-9ee8-11e7-8711-e7a007dcef99',
|
id: '06cf9c40-9ee8-11e7-8711-e7a007dcef99',
|
||||||
type: 'visualization',
|
type: 'visualization',
|
||||||
updated_at: '2018-08-29T13:22:17.617Z',
|
updated_at: '2021-10-28T15:07:36.622Z',
|
||||||
version: '1',
|
version: '1',
|
||||||
migrationVersion: {},
|
coreMigrationVersion: '8.0.0',
|
||||||
|
migrationVersion: { visualization: '8.0.0' },
|
||||||
attributes: {
|
attributes: {
|
||||||
title: i18n.translate('home.sampleData.logsSpec.visitorsMapTitle', {
|
title: i18n.translate('home.sampleData.logsSpec.visitorsMapTitle', {
|
||||||
defaultMessage: '[Logs] Visitors Map',
|
defaultMessage: '[Logs] Visitors Map',
|
||||||
|
@ -28,10 +29,16 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
version: 1,
|
version: 1,
|
||||||
kibanaSavedObjectMeta: {
|
kibanaSavedObjectMeta: {
|
||||||
searchSourceJSON:
|
searchSourceJSON:
|
||||||
'{"index":"90943e30-9a47-11e8-b64d-95841ca0b247","filter":[],"query":{"query":"","language":"kuery"}}',
|
'{"filter":[],"query":{"query":"","language":"kuery"},"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
references: [],
|
references: [
|
||||||
|
{
|
||||||
|
id: '90943e30-9a47-11e8-b64d-95841ca0b247',
|
||||||
|
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||||
|
type: 'index-pattern',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'cb099a20-ea66-11eb-9425-113343a037e3',
|
id: 'cb099a20-ea66-11eb-9425-113343a037e3',
|
||||||
|
@ -88,25 +95,32 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
{
|
{
|
||||||
id: '69a34b00-9ee8-11e7-8711-e7a007dcef99',
|
id: '69a34b00-9ee8-11e7-8711-e7a007dcef99',
|
||||||
type: 'visualization',
|
type: 'visualization',
|
||||||
updated_at: '2018-08-29T13:24:46.136Z',
|
updated_at: '2021-10-28T14:38:21.435Z',
|
||||||
version: '2',
|
version: '2',
|
||||||
migrationVersion: {},
|
coreMigrationVersion: '8.0.0',
|
||||||
|
migrationVersion: { visualization: '8.0.0' },
|
||||||
attributes: {
|
attributes: {
|
||||||
title: i18n.translate('home.sampleData.logsSpec.goalsTitle', {
|
title: i18n.translate('home.sampleData.logsSpec.goalsTitle', {
|
||||||
defaultMessage: '[Logs] Goals',
|
defaultMessage: '[Logs] Goals',
|
||||||
}),
|
}),
|
||||||
visState:
|
visState:
|
||||||
'{"title":"[Logs] Goals","type":"gauge","params":{"type":"gauge","addTooltip":true,"addLegend":false,"gauge":{"verticalSplit":false,"extendRange":true,"percentageMode":false,"gaugeType":"Arc","gaugeStyle":"Full","backStyle":"Full","orientation":"vertical","colorSchema":"Green to Red","gaugeColorMode":"Labels","colorsRange":[{"from":0,"to":500},{"from":500,"to":1000},{"from":1000,"to":1500}],"invertColors":true,"labels":{"show":false,"color":"black"},"scale":{"show":true,"labels":false,"color":"#333"},"type":"meter","style":{"bgWidth":0.9,"width":0.9,"mask":false,"bgMask":false,"maskBars":50,"bgFill":"#eee","bgColor":false,"subText":"visitors","fontSize":60,"labelColor":true}},"isDisplayWarning":false},"aggs":[{"id":"1","enabled":true,"type":"cardinality","schema":"metric","params":{"field":"clientip","customLabel":"Unique Visitors"}}]}',
|
'{"title":"[Logs] Goals","type":"gauge","params":{"type":"gauge","addTooltip":true,"addLegend":false,"gauge":{"extendRange":true,"percentageMode":false,"gaugeType":"Arc","gaugeStyle":"Full","backStyle":"Full","orientation":"vertical","colorSchema":"Green to Red","gaugeColorMode":"Labels","colorsRange":[{"from":0,"to":500},{"from":500,"to":1000},{"from":1000,"to":1500}],"invertColors":true,"labels":{"show":false,"color":"black"},"scale":{"show":true,"labels":false,"color":"#333"},"type":"meter","style":{"bgWidth":0.9,"width":0.9,"mask":false,"bgMask":false,"maskBars":50,"bgFill":"#eee","bgColor":false,"subText":"visitors","fontSize":60,"labelColor":true},"alignment":"horizontal"},"isDisplayWarning":false},"aggs":[{"id":"1","enabled":true,"type":"cardinality","schema":"metric","params":{"field":"clientip","customLabel":"Unique Visitors"}}]}',
|
||||||
uiStateJSON:
|
uiStateJSON:
|
||||||
'{"vis":{"defaultColors":{"0 - 500":"rgb(165,0,38)","500 - 1000":"rgb(255,255,190)","1000 - 1500":"rgb(0,104,55)"},"colors":{"75 - 100":"#629E51","50 - 75":"#EAB839","0 - 50":"#E24D42","0 - 100":"#E24D42","200 - 300":"#7EB26D","500 - 1000":"#E5AC0E","0 - 500":"#E24D42","1000 - 1500":"#7EB26D"},"legendOpen":true}}',
|
'{"vis":{"defaultColors":{"0 - 500":"rgb(165,0,38)","500 - 1000":"rgb(255,255,190)","1000 - 1500":"rgb(0,104,55)"},"colors":{"75 - 100":"#629E51","50 - 75":"#EAB839","0 - 50":"#E24D42","0 - 100":"#E24D42","200 - 300":"#7EB26D","500 - 1000":"#E5AC0E","0 - 500":"#E24D42","1000 - 1500":"#7EB26D"},"legendOpen":true}}',
|
||||||
description: '',
|
description: '',
|
||||||
version: 1,
|
version: 1,
|
||||||
kibanaSavedObjectMeta: {
|
kibanaSavedObjectMeta: {
|
||||||
searchSourceJSON:
|
searchSourceJSON:
|
||||||
'{"index":"90943e30-9a47-11e8-b64d-95841ca0b247","filter":[],"query":{"query":"","language":"kuery"}}',
|
'{"filter":[],"query":{"query":"","language":"kuery"},"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
references: [],
|
references: [
|
||||||
|
{
|
||||||
|
id: '90943e30-9a47-11e8-b64d-95841ca0b247',
|
||||||
|
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||||
|
type: 'index-pattern',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '7cbd2350-2223-11e8-b802-5bcf64c2cfb4',
|
id: '7cbd2350-2223-11e8-b802-5bcf64c2cfb4',
|
||||||
|
@ -366,13 +380,13 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
{
|
{
|
||||||
id: 'edf84fe0-e1a0-11e7-b6d5-4dc382ef7f5b',
|
id: 'edf84fe0-e1a0-11e7-b6d5-4dc382ef7f5b',
|
||||||
type: 'dashboard',
|
type: 'dashboard',
|
||||||
updated_at: '2021-07-21T21:43:43.870Z',
|
updated_at: '2021-10-28T15:07:36.622Z',
|
||||||
version: '3',
|
version: '3',
|
||||||
references: [
|
references: [
|
||||||
{
|
{
|
||||||
id: '06cf9c40-9ee8-11e7-8711-e7a007dcef99',
|
id: '06cf9c40-9ee8-11e7-8711-e7a007dcef99',
|
||||||
name: '4:panel_4',
|
name: '4:panel_4',
|
||||||
type: 'map',
|
type: 'visualization',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4eb6e500-e1c7-11e7-b6d5-4dc382ef7f5b',
|
id: '4eb6e500-e1c7-11e7-b6d5-4dc382ef7f5b',
|
||||||
|
|
|
@ -10,7 +10,12 @@ export { SampleDataRegistry } from './sample_data_registry';
|
||||||
|
|
||||||
export type { SampleDataRegistrySetup, SampleDataRegistryStart } from './sample_data_registry';
|
export type { SampleDataRegistrySetup, SampleDataRegistryStart } from './sample_data_registry';
|
||||||
|
|
||||||
|
export { EmbeddableTypes } from './lib/sample_dataset_registry_types';
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
SampleDatasetSchema,
|
AppLinkData,
|
||||||
|
SampleDatasetDashboardPanel,
|
||||||
SampleDatasetProvider,
|
SampleDatasetProvider,
|
||||||
|
SampleDatasetSchema,
|
||||||
|
SampleObject,
|
||||||
} from './lib/sample_dataset_registry_types';
|
} from './lib/sample_dataset_registry_types';
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const mockBuildNode = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('@kbn/es-query', () => {
|
||||||
|
return {
|
||||||
|
nodeTypes: {
|
||||||
|
function: {
|
||||||
|
buildNode: mockBuildNode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* 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 { mockBuildNode } from './find_sample_objects.test.mock';
|
||||||
|
|
||||||
|
import type { SavedObject, SavedObjectsFindResponse } from 'src/core/server';
|
||||||
|
import { savedObjectsClientMock, loggingSystemMock } from 'src/core/server/mocks';
|
||||||
|
import { findSampleObjects } from './find_sample_objects';
|
||||||
|
|
||||||
|
describe('findSampleObjects', () => {
|
||||||
|
function setup() {
|
||||||
|
const mockClient = savedObjectsClientMock.create();
|
||||||
|
const mockLogger = loggingSystemMock.createLogger();
|
||||||
|
return {
|
||||||
|
client: mockClient,
|
||||||
|
logger: mockLogger,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockBuildNode.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('searches for objects and returns expected results', async () => {
|
||||||
|
const { client, logger } = setup();
|
||||||
|
const obj1 = { type: 'obj-type-1', id: 'obj-id-1' };
|
||||||
|
const obj2 = { type: 'obj-type-2', id: 'obj-id-2' };
|
||||||
|
const obj3 = { type: 'obj-type-3', id: 'obj-id-3' };
|
||||||
|
const obj4 = { type: 'obj-type-3', id: 'obj-id-4' };
|
||||||
|
const objects = [obj1, obj2, obj3, obj4];
|
||||||
|
const params = { client, logger, objects };
|
||||||
|
|
||||||
|
client.bulkGet.mockResolvedValue({
|
||||||
|
saved_objects: [
|
||||||
|
obj1, // bulkGet success for obj1
|
||||||
|
{ ...obj2, error: { statusCode: 403 } }, // bulkGet failure - will not attempt to find by originId since the error is not 404
|
||||||
|
{ ...obj3, error: { statusCode: 404 } }, // bulkGet failure - will attempt to find by originId since the error is 404
|
||||||
|
{ ...obj4, error: { statusCode: 404 } }, // bulkGet failure - will attempt to find by originId since the error is 404
|
||||||
|
] as SavedObject[],
|
||||||
|
});
|
||||||
|
client.find.mockResolvedValue({
|
||||||
|
saved_objects: [{ type: obj4.type, id: 'obj-id-x', originId: obj4.id }], // find success for obj4
|
||||||
|
total: 1,
|
||||||
|
} as SavedObjectsFindResponse);
|
||||||
|
const result = await findSampleObjects(params);
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
{ ...obj1, foundObjectId: obj1.id },
|
||||||
|
{ ...obj2, foundObjectId: undefined },
|
||||||
|
{ ...obj3, foundObjectId: undefined },
|
||||||
|
{ ...obj4, foundObjectId: 'obj-id-x' },
|
||||||
|
]);
|
||||||
|
expect(client.bulkGet).toHaveBeenCalledWith(objects);
|
||||||
|
expect(mockBuildNode).toHaveBeenCalledTimes(3);
|
||||||
|
expect(mockBuildNode).toHaveBeenNthCalledWith(1, 'is', `${obj3.type}.originId`, obj3.id);
|
||||||
|
expect(mockBuildNode).toHaveBeenNthCalledWith(2, 'is', `${obj4.type}.originId`, obj4.id);
|
||||||
|
expect(mockBuildNode).toHaveBeenNthCalledWith(3, 'or', expect.any(Array));
|
||||||
|
expect(client.find).toHaveBeenCalledWith(expect.objectContaining({ type: ['obj-type-3'] })); // obj3 and obj4 have the same type; the type param is deduplicated
|
||||||
|
expect(logger.warn).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips find if there are no objects left to search for', async () => {
|
||||||
|
const { client, logger } = setup();
|
||||||
|
const obj1 = { type: 'obj-type-1', id: 'obj-id-1' };
|
||||||
|
const obj2 = { type: 'obj-type-2', id: 'obj-id-2' };
|
||||||
|
const objects = [obj1, obj2];
|
||||||
|
const params = { client, logger, objects };
|
||||||
|
|
||||||
|
client.bulkGet.mockResolvedValue({
|
||||||
|
saved_objects: [
|
||||||
|
obj1, // bulkGet success for obj1
|
||||||
|
{ ...obj2, error: { statusCode: 403 } }, // bulkGet failure - will not attempt to find by originId since the error is not 404
|
||||||
|
] as SavedObject[],
|
||||||
|
});
|
||||||
|
const result = await findSampleObjects(params);
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
{ ...obj1, foundObjectId: obj1.id },
|
||||||
|
{ ...obj2, foundObjectId: undefined },
|
||||||
|
]);
|
||||||
|
expect(client.bulkGet).toHaveBeenCalledWith(objects);
|
||||||
|
expect(mockBuildNode).not.toHaveBeenCalled();
|
||||||
|
expect(client.find).not.toHaveBeenCalled();
|
||||||
|
expect(logger.warn).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs expected warnings', async () => {
|
||||||
|
const { client, logger } = setup();
|
||||||
|
const obj1 = { type: 'obj-type-1', id: 'obj-id-1' };
|
||||||
|
const objects = [obj1];
|
||||||
|
const params = { client, logger, objects };
|
||||||
|
|
||||||
|
client.bulkGet.mockResolvedValue({
|
||||||
|
saved_objects: [
|
||||||
|
{ ...obj1, error: { statusCode: 404 } }, // bulkGet failure - will attempt to find by originId since the error is 404
|
||||||
|
] as SavedObject[],
|
||||||
|
});
|
||||||
|
client.find.mockResolvedValue({
|
||||||
|
saved_objects: [
|
||||||
|
{ type: obj1.type, id: 'obj-id-x', originId: obj1.id }, // find success for obj4
|
||||||
|
{ type: obj1.type, id: 'obj-id-y', originId: obj1.id }, // find success for obj4
|
||||||
|
],
|
||||||
|
total: 10001,
|
||||||
|
} as SavedObjectsFindResponse);
|
||||||
|
const result = await findSampleObjects(params);
|
||||||
|
expect(result).toEqual([{ ...obj1, foundObjectId: 'obj-id-x' }]); // obj-id-y is ignored
|
||||||
|
expect(client.bulkGet).toHaveBeenCalledWith(objects);
|
||||||
|
expect(mockBuildNode).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockBuildNode).toHaveBeenNthCalledWith(1, 'is', `${obj1.type}.originId`, obj1.id);
|
||||||
|
expect(mockBuildNode).toHaveBeenNthCalledWith(2, 'or', expect.any(Array));
|
||||||
|
expect(client.find).toHaveBeenCalledWith(expect.objectContaining({ type: ['obj-type-1'] }));
|
||||||
|
expect(logger.warn).toHaveBeenCalledTimes(2);
|
||||||
|
expect(logger.warn).toHaveBeenCalledWith(
|
||||||
|
'findSampleObjects got 10001 results, only using the first 10000'
|
||||||
|
);
|
||||||
|
expect(logger.warn).toHaveBeenCalledWith(
|
||||||
|
'Found two sample objects with the same origin "obj-id-1" (previously found "obj-id-x", ignoring "obj-id-y")'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* 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 * as esKuery from '@kbn/es-query';
|
||||||
|
import type { Logger, SavedObjectsClientContract } from 'src/core/server';
|
||||||
|
|
||||||
|
const MAX_OBJECTS_TO_FIND = 10000; // we only expect up to a few dozen, search for 10k to be safe; anything over this is ignored
|
||||||
|
|
||||||
|
export interface FindSampleObjectsParams {
|
||||||
|
client: SavedObjectsClientContract;
|
||||||
|
logger: Logger;
|
||||||
|
objects: SampleObject[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SampleObject {
|
||||||
|
type: string;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FindSampleObjectsResponseObject {
|
||||||
|
type: string;
|
||||||
|
id: string;
|
||||||
|
/** Contains a string if this sample data object was found, or undefined if it was not. */
|
||||||
|
foundObjectId: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an array of objects in a sample dataset, this function attempts to find if those objects exist in the current space.
|
||||||
|
* It attempts to find objects with an origin of the sample data (e.g., matching `id` or `originId`).
|
||||||
|
*/
|
||||||
|
export async function findSampleObjects({ client, logger, objects }: FindSampleObjectsParams) {
|
||||||
|
const bulkGetResponse = await client.bulkGet(objects);
|
||||||
|
|
||||||
|
let resultsMap = new Map<string, string>();
|
||||||
|
const objectsToFind: SampleObject[] = [];
|
||||||
|
objects.forEach((object, i) => {
|
||||||
|
const bulkGetResult = bulkGetResponse.saved_objects[i];
|
||||||
|
if (!bulkGetResult.error) {
|
||||||
|
const { type, id } = object;
|
||||||
|
const key = getObjKey(type, id);
|
||||||
|
resultsMap.set(key, id);
|
||||||
|
} else if (bulkGetResult.error.statusCode === 404) {
|
||||||
|
objectsToFind.push(object);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (objectsToFind.length > 0) {
|
||||||
|
const options = {
|
||||||
|
type: getUniqueTypes(objectsToFind),
|
||||||
|
filter: createKueryFilter(objectsToFind),
|
||||||
|
fields: ['title'], // we don't want to return all source fields, so we have to specify at least one source field
|
||||||
|
perPage: MAX_OBJECTS_TO_FIND,
|
||||||
|
};
|
||||||
|
const findResponse = await client.find(options);
|
||||||
|
if (findResponse.total > MAX_OBJECTS_TO_FIND) {
|
||||||
|
// As of this writing, it is not possible to encounter this scenario when using Kibana import or copy-to-space, because at most one
|
||||||
|
// object can exist in a given space. However, as of today, when objects are shareable you _could_ get Kibana into a state where
|
||||||
|
// multiple objects of the same origin exist in the same space.
|
||||||
|
// #116677 describes solutions to fully mitigate this edge case in the future.
|
||||||
|
logger.warn(
|
||||||
|
`findSampleObjects got ${findResponse.total} results, only using the first ${MAX_OBJECTS_TO_FIND}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
resultsMap = findResponse.saved_objects.reduce((acc, { type, id, originId }) => {
|
||||||
|
const key = getObjKey(type, originId!);
|
||||||
|
const existing = acc.get(key);
|
||||||
|
if (existing) {
|
||||||
|
// As of this writing, it is not possible to encounter this scenario when using Kibana import or copy-to-space, because at most one
|
||||||
|
// object can exist in a given space. However, as of today, when objects are shareable you _could_ get Kibana into a state where
|
||||||
|
// multiple objects of the same origin exist in the same space.
|
||||||
|
// #116677 describes solutions to fully mitigate this edge case in the future.
|
||||||
|
logger.warn(
|
||||||
|
`Found two sample objects with the same origin "${originId}" (previously found "${existing}", ignoring "${id}")`
|
||||||
|
);
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
return acc.set(key, id);
|
||||||
|
}, resultsMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return objects.map<FindSampleObjectsResponseObject>(({ type, id }) => {
|
||||||
|
const key = getObjKey(type, id);
|
||||||
|
return { type, id, foundObjectId: resultsMap.get(key) };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUniqueTypes(objects: SampleObject[]) {
|
||||||
|
return [...new Set(objects.map(({ type }) => type))];
|
||||||
|
}
|
||||||
|
|
||||||
|
function createKueryFilter(objects: SampleObject[]) {
|
||||||
|
const { buildNode } = esKuery.nodeTypes.function;
|
||||||
|
const kueryNodes = objects.map(({ type, id }) => buildNode('is', `${type}.originId`, id)); // the repository converts this node into "and (type is ..., originId is ...)"
|
||||||
|
return buildNode('or', kueryNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getObjKey(type: string, id: string) {
|
||||||
|
return `${type}:${id}`;
|
||||||
|
}
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { SampleDatasetSchema } from './sample_dataset_schema';
|
import type { SampleDatasetSchema } from './sample_dataset_schema';
|
||||||
export type { SampleDatasetSchema, AppLinkSchema, DataIndexSchema } from './sample_dataset_schema';
|
export type { SampleDatasetSchema, DataIndexSchema } from './sample_dataset_schema';
|
||||||
|
|
||||||
export enum DatasetStatusTypes {
|
export enum DatasetStatusTypes {
|
||||||
NOT_INSTALLED = 'not_installed',
|
NOT_INSTALLED = 'not_installed',
|
||||||
|
@ -28,3 +28,34 @@ export enum EmbeddableTypes {
|
||||||
VISUALIZE_EMBEDDABLE_TYPE = 'visualization',
|
VISUALIZE_EMBEDDABLE_TYPE = 'visualization',
|
||||||
}
|
}
|
||||||
export type SampleDatasetProvider = () => SampleDatasetSchema;
|
export type SampleDatasetProvider = () => SampleDatasetSchema;
|
||||||
|
|
||||||
|
/** This type is used to identify an object in a sample dataset. */
|
||||||
|
export interface SampleObject {
|
||||||
|
/** The type of the sample object. */
|
||||||
|
type: string;
|
||||||
|
/** The ID of the sample object. */
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type is used by consumers to register a new app link for a sample dataset.
|
||||||
|
*/
|
||||||
|
export interface AppLinkData {
|
||||||
|
/**
|
||||||
|
* The sample object that is used for this app link's path; if the path does not use an object ID, set this to null.
|
||||||
|
*/
|
||||||
|
sampleObject: SampleObject | null;
|
||||||
|
/**
|
||||||
|
* Function that returns the path for this app link. Note that the `objectId` can be different than the given `sampleObject.id`, depending
|
||||||
|
* on how the sample data was installed. If the `sampleObject` is null, the `objectId` argument will be an empty string.
|
||||||
|
*/
|
||||||
|
getPath: (objectId: string) => string;
|
||||||
|
/**
|
||||||
|
* The label for this app link.
|
||||||
|
*/
|
||||||
|
label: string;
|
||||||
|
/**
|
||||||
|
* The icon for this app link.
|
||||||
|
*/
|
||||||
|
icon: string;
|
||||||
|
}
|
||||||
|
|
|
@ -48,13 +48,6 @@ const dataIndexSchema = schema.object({
|
||||||
|
|
||||||
export type DataIndexSchema = TypeOf<typeof dataIndexSchema>;
|
export type DataIndexSchema = TypeOf<typeof dataIndexSchema>;
|
||||||
|
|
||||||
const appLinkSchema = schema.object({
|
|
||||||
path: schema.string(),
|
|
||||||
label: schema.string(),
|
|
||||||
icon: schema.string(),
|
|
||||||
});
|
|
||||||
export type AppLinkSchema = TypeOf<typeof appLinkSchema>;
|
|
||||||
|
|
||||||
export const sampleDataSchema = schema.object({
|
export const sampleDataSchema = schema.object({
|
||||||
id: schema.string({
|
id: schema.string({
|
||||||
validate(value: string) {
|
validate(value: string) {
|
||||||
|
@ -71,7 +64,6 @@ export const sampleDataSchema = schema.object({
|
||||||
|
|
||||||
// saved object id of main dashboard for sample data set
|
// saved object id of main dashboard for sample data set
|
||||||
overviewDashboard: schema.string(),
|
overviewDashboard: schema.string(),
|
||||||
appLinks: schema.arrayOf(appLinkSchema, { defaultValue: [] }),
|
|
||||||
|
|
||||||
// saved object id of default index-pattern for sample data set
|
// saved object id of default index-pattern for sample data set
|
||||||
defaultIndex: schema.string(),
|
defaultIndex: schema.string(),
|
||||||
|
|
13
src/plugins/home/server/services/sample_data/lib/utils.ts
Normal file
13
src/plugins/home/server/services/sample_data/lib/utils.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* 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 type { SampleObject } from './sample_dataset_registry_types';
|
||||||
|
|
||||||
|
export function getUniqueObjectTypes(objects: SampleObject[]) {
|
||||||
|
return [...new Set(objects.map(({ type }) => type))];
|
||||||
|
}
|
|
@ -6,13 +6,9 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Readable } from 'stream';
|
||||||
import { schema } from '@kbn/config-schema';
|
import { schema } from '@kbn/config-schema';
|
||||||
import type {
|
import { IRouter, Logger, IScopedClusterClient } from 'src/core/server';
|
||||||
IRouter,
|
|
||||||
Logger,
|
|
||||||
IScopedClusterClient,
|
|
||||||
SavedObjectsBulkCreateObject,
|
|
||||||
} from 'src/core/server';
|
|
||||||
import { SampleDatasetSchema } from '../lib/sample_dataset_registry_types';
|
import { SampleDatasetSchema } from '../lib/sample_dataset_registry_types';
|
||||||
import { createIndexName } from '../lib/create_index_name';
|
import { createIndexName } from '../lib/create_index_name';
|
||||||
import {
|
import {
|
||||||
|
@ -22,6 +18,8 @@ import {
|
||||||
} from '../lib/translate_timestamp';
|
} from '../lib/translate_timestamp';
|
||||||
import { loadData } from '../lib/load_data';
|
import { loadData } from '../lib/load_data';
|
||||||
import { SampleDataUsageTracker } from '../usage/usage';
|
import { SampleDataUsageTracker } from '../usage/usage';
|
||||||
|
import { getSavedObjectsClient } from './utils';
|
||||||
|
import { getUniqueObjectTypes } from '../lib/utils';
|
||||||
|
|
||||||
const insertDataIntoIndex = (
|
const insertDataIntoIndex = (
|
||||||
dataIndexConfig: any,
|
dataIndexConfig: any,
|
||||||
|
@ -143,35 +141,31 @@ export function createInstallRoute(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let createResults;
|
const { getImporter } = context.core.savedObjects;
|
||||||
|
const objectTypes = getUniqueObjectTypes(sampleDataset.savedObjects);
|
||||||
|
const savedObjectsClient = getSavedObjectsClient(context, objectTypes);
|
||||||
|
const importer = getImporter(savedObjectsClient);
|
||||||
|
|
||||||
|
const savedObjects = sampleDataset.savedObjects.map(({ version, ...obj }) => obj);
|
||||||
|
const readStream = Readable.from(savedObjects);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { getClient, typeRegistry } = context.core.savedObjects;
|
const { errors = [] } = await importer.import({
|
||||||
|
readStream,
|
||||||
const includedHiddenTypes = sampleDataset.savedObjects
|
overwrite: true,
|
||||||
.map((object) => object.type)
|
createNewCopies: false,
|
||||||
.filter((supportedType) => typeRegistry.isHidden(supportedType));
|
});
|
||||||
|
if (errors.length > 0) {
|
||||||
const client = getClient({ includedHiddenTypes });
|
const errMsg = `sample_data install errors while loading saved objects. Errors: ${JSON.stringify(
|
||||||
|
errors.map(({ type, id, error }) => ({ type, id, error })) // discard other fields
|
||||||
const savedObjects = sampleDataset.savedObjects as SavedObjectsBulkCreateObject[];
|
)}`;
|
||||||
createResults = await client.bulkCreate(
|
logger.warn(errMsg);
|
||||||
savedObjects.map(({ version, ...savedObject }) => savedObject),
|
return res.customError({ body: errMsg, statusCode: 500 });
|
||||||
{ overwrite: true }
|
}
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const errMsg = `bulkCreate failed, error: ${err.message}`;
|
const errMsg = `import failed, error: ${err.message}`;
|
||||||
throw new Error(errMsg);
|
throw new Error(errMsg);
|
||||||
}
|
}
|
||||||
const errors = createResults.saved_objects.filter((savedObjectCreateResult) => {
|
|
||||||
return Boolean(savedObjectCreateResult.error);
|
|
||||||
});
|
|
||||||
if (errors.length > 0) {
|
|
||||||
const errMsg = `sample_data install errors while loading saved objects. Errors: ${JSON.stringify(
|
|
||||||
errors
|
|
||||||
)}`;
|
|
||||||
logger.warn(errMsg);
|
|
||||||
return res.customError({ body: errMsg, statusCode: 403 });
|
|
||||||
}
|
|
||||||
usageTracker.addInstall(params.id);
|
usageTracker.addInstall(params.id);
|
||||||
|
|
||||||
// FINALLY
|
// FINALLY
|
||||||
|
|
|
@ -6,75 +6,122 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IRouter } from 'src/core/server';
|
import type { IRouter, Logger, RequestHandlerContext } from 'src/core/server';
|
||||||
import { SampleDatasetSchema } from '../lib/sample_dataset_registry_types';
|
import type { AppLinkData, SampleDatasetSchema } from '../lib/sample_dataset_registry_types';
|
||||||
import { createIndexName } from '../lib/create_index_name';
|
import { createIndexName } from '../lib/create_index_name';
|
||||||
|
import type { FindSampleObjectsResponseObject } from '../lib/find_sample_objects';
|
||||||
|
import { findSampleObjects } from '../lib/find_sample_objects';
|
||||||
|
import { getUniqueObjectTypes } from '../lib/utils';
|
||||||
|
import { getSavedObjectsClient } from './utils';
|
||||||
|
|
||||||
const NOT_INSTALLED = 'not_installed';
|
const NOT_INSTALLED = 'not_installed';
|
||||||
const INSTALLED = 'installed';
|
const INSTALLED = 'installed';
|
||||||
const UNKNOWN = 'unknown';
|
const UNKNOWN = 'unknown';
|
||||||
|
|
||||||
export const createListRoute = (router: IRouter, sampleDatasets: SampleDatasetSchema[]) => {
|
export const createListRoute = (
|
||||||
router.get({ path: '/api/sample_data', validate: false }, async (context, req, res) => {
|
router: IRouter,
|
||||||
const registeredSampleDatasets = sampleDatasets.map((sampleDataset) => {
|
sampleDatasets: SampleDatasetSchema[],
|
||||||
return {
|
appLinksMap: Map<string, AppLinkData[]>,
|
||||||
id: sampleDataset.id,
|
logger: Logger
|
||||||
name: sampleDataset.name,
|
) => {
|
||||||
description: sampleDataset.description,
|
router.get({ path: '/api/sample_data', validate: false }, async (context, _req, res) => {
|
||||||
previewImagePath: sampleDataset.previewImagePath,
|
const allExistingObjects = await findExistingSampleObjects(context, logger, sampleDatasets);
|
||||||
darkPreviewImagePath: sampleDataset.darkPreviewImagePath,
|
|
||||||
overviewDashboard: sampleDataset.overviewDashboard,
|
const registeredSampleDatasets = await Promise.all(
|
||||||
appLinks: sampleDataset.appLinks,
|
sampleDatasets.map(async (sampleDataset) => {
|
||||||
defaultIndex: sampleDataset.defaultIndex,
|
const existingObjects = allExistingObjects.get(sampleDataset.id)!;
|
||||||
dataIndices: sampleDataset.dataIndices.map(({ id }) => ({ id })),
|
const findObjectId = (type: string, id: string) =>
|
||||||
status: sampleDataset.status,
|
existingObjects.find((object) => object.type === type && object.id === id)
|
||||||
statusMsg: sampleDataset.statusMsg,
|
?.foundObjectId ?? id;
|
||||||
};
|
|
||||||
});
|
const appLinks = (appLinksMap.get(sampleDataset.id) ?? []).map((data) => {
|
||||||
const isInstalledPromises = registeredSampleDatasets.map(async (sampleDataset) => {
|
const { sampleObject, getPath, label, icon } = data;
|
||||||
for (let i = 0; i < sampleDataset.dataIndices.length; i++) {
|
if (sampleObject === null) {
|
||||||
const dataIndexConfig = sampleDataset.dataIndices[i];
|
return { path: getPath(''), label, icon };
|
||||||
const index = createIndexName(sampleDataset.id, dataIndexConfig.id);
|
|
||||||
try {
|
|
||||||
const { body: indexExists } =
|
|
||||||
await context.core.elasticsearch.client.asCurrentUser.indices.exists({
|
|
||||||
index,
|
|
||||||
});
|
|
||||||
if (!indexExists) {
|
|
||||||
sampleDataset.status = NOT_INSTALLED;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
const objectId = findObjectId(sampleObject.type, sampleObject.id);
|
||||||
|
return { path: getPath(objectId), label, icon };
|
||||||
|
});
|
||||||
|
const sampleDataStatus = await getSampleDatasetStatus(
|
||||||
|
context,
|
||||||
|
allExistingObjects,
|
||||||
|
sampleDataset
|
||||||
|
);
|
||||||
|
|
||||||
const { body: count } = await context.core.elasticsearch.client.asCurrentUser.count({
|
return {
|
||||||
index,
|
id: sampleDataset.id,
|
||||||
});
|
name: sampleDataset.name,
|
||||||
if (count.count === 0) {
|
description: sampleDataset.description,
|
||||||
sampleDataset.status = NOT_INSTALLED;
|
previewImagePath: sampleDataset.previewImagePath,
|
||||||
return;
|
darkPreviewImagePath: sampleDataset.darkPreviewImagePath,
|
||||||
}
|
overviewDashboard: findObjectId('dashboard', sampleDataset.overviewDashboard),
|
||||||
} catch (err) {
|
appLinks,
|
||||||
sampleDataset.status = UNKNOWN;
|
defaultIndex: findObjectId('index-pattern', sampleDataset.defaultIndex),
|
||||||
sampleDataset.statusMsg = err.message;
|
dataIndices: sampleDataset.dataIndices.map(({ id }) => ({ id })),
|
||||||
return;
|
...sampleDataStatus,
|
||||||
}
|
};
|
||||||
}
|
})
|
||||||
try {
|
);
|
||||||
await context.core.savedObjects.client.get('dashboard', sampleDataset.overviewDashboard);
|
|
||||||
} catch (err) {
|
|
||||||
if (context.core.savedObjects.client.errors.isNotFoundError(err)) {
|
|
||||||
sampleDataset.status = NOT_INSTALLED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sampleDataset.status = UNKNOWN;
|
|
||||||
sampleDataset.statusMsg = err.message;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sampleDataset.status = INSTALLED;
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(isInstalledPromises);
|
|
||||||
return res.ok({ body: registeredSampleDatasets });
|
return res.ok({ body: registeredSampleDatasets });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ExistingSampleObjects = Map<string, FindSampleObjectsResponseObject[]>;
|
||||||
|
async function findExistingSampleObjects(
|
||||||
|
context: RequestHandlerContext,
|
||||||
|
logger: Logger,
|
||||||
|
sampleDatasets: SampleDatasetSchema[]
|
||||||
|
) {
|
||||||
|
const objects = sampleDatasets
|
||||||
|
.map(({ savedObjects }) => savedObjects.map(({ type, id }) => ({ type, id })))
|
||||||
|
.flat();
|
||||||
|
const objectTypes = getUniqueObjectTypes(objects);
|
||||||
|
const client = getSavedObjectsClient(context, objectTypes);
|
||||||
|
const findSampleObjectsResult = await findSampleObjects({ client, logger, objects });
|
||||||
|
|
||||||
|
let objectCounter = 0;
|
||||||
|
return sampleDatasets.reduce<ExistingSampleObjects>((acc, { id, savedObjects }) => {
|
||||||
|
const datasetResults = savedObjects.map(() => findSampleObjectsResult[objectCounter++]);
|
||||||
|
return acc.set(id, datasetResults);
|
||||||
|
}, new Map());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: introduce PARTIALLY_INSTALLED status (#116677)
|
||||||
|
async function getSampleDatasetStatus(
|
||||||
|
context: RequestHandlerContext,
|
||||||
|
existingSampleObjects: ExistingSampleObjects,
|
||||||
|
sampleDataset: SampleDatasetSchema
|
||||||
|
): Promise<{ status: string; statusMsg?: string }> {
|
||||||
|
const dashboard = existingSampleObjects
|
||||||
|
.get(sampleDataset.id)!
|
||||||
|
.find(({ type, id }) => type === 'dashboard' && id === sampleDataset.overviewDashboard);
|
||||||
|
if (!dashboard?.foundObjectId) {
|
||||||
|
return { status: NOT_INSTALLED };
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < sampleDataset.dataIndices.length; i++) {
|
||||||
|
const dataIndexConfig = sampleDataset.dataIndices[i];
|
||||||
|
const index = createIndexName(sampleDataset.id, dataIndexConfig.id);
|
||||||
|
try {
|
||||||
|
const { body: indexExists } =
|
||||||
|
await context.core.elasticsearch.client.asCurrentUser.indices.exists({
|
||||||
|
index,
|
||||||
|
});
|
||||||
|
if (!indexExists) {
|
||||||
|
return { status: NOT_INSTALLED };
|
||||||
|
}
|
||||||
|
|
||||||
|
const { body: count } = await context.core.elasticsearch.client.asCurrentUser.count({
|
||||||
|
index,
|
||||||
|
});
|
||||||
|
if (count.count === 0) {
|
||||||
|
return { status: NOT_INSTALLED };
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return { status: UNKNOWN, statusMsg: err.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: INSTALLED };
|
||||||
|
}
|
||||||
|
|
|
@ -6,16 +6,20 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { isBoom } from '@hapi/boom';
|
||||||
import { schema } from '@kbn/config-schema';
|
import { schema } from '@kbn/config-schema';
|
||||||
import _ from 'lodash';
|
import type { IRouter, Logger } from 'src/core/server';
|
||||||
import { IRouter } from 'src/core/server';
|
|
||||||
import { SampleDatasetSchema } from '../lib/sample_dataset_registry_types';
|
import { SampleDatasetSchema } from '../lib/sample_dataset_registry_types';
|
||||||
import { createIndexName } from '../lib/create_index_name';
|
import { createIndexName } from '../lib/create_index_name';
|
||||||
import { SampleDataUsageTracker } from '../usage/usage';
|
import { SampleDataUsageTracker } from '../usage/usage';
|
||||||
|
import { findSampleObjects } from '../lib/find_sample_objects';
|
||||||
|
import { getUniqueObjectTypes } from '../lib/utils';
|
||||||
|
import { getSavedObjectsClient } from './utils';
|
||||||
|
|
||||||
export function createUninstallRoute(
|
export function createUninstallRoute(
|
||||||
router: IRouter,
|
router: IRouter,
|
||||||
sampleDatasets: SampleDatasetSchema[],
|
sampleDatasets: SampleDatasetSchema[],
|
||||||
|
logger: Logger,
|
||||||
usageTracker: SampleDataUsageTracker
|
usageTracker: SampleDataUsageTracker
|
||||||
): void {
|
): void {
|
||||||
router.delete(
|
router.delete(
|
||||||
|
@ -25,16 +29,7 @@ export function createUninstallRoute(
|
||||||
params: schema.object({ id: schema.string() }),
|
params: schema.object({ id: schema.string() }),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async (
|
async (context, request, response) => {
|
||||||
{
|
|
||||||
core: {
|
|
||||||
elasticsearch: { client: esClient },
|
|
||||||
savedObjects: { getClient: getSavedObjectsClient, typeRegistry },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
request,
|
|
||||||
response
|
|
||||||
) => {
|
|
||||||
const sampleDataset = sampleDatasets.find(({ id }) => id === request.params.id);
|
const sampleDataset = sampleDatasets.find(({ id }) => id === request.params.id);
|
||||||
|
|
||||||
if (!sampleDataset) {
|
if (!sampleDataset) {
|
||||||
|
@ -46,41 +41,46 @@ export function createUninstallRoute(
|
||||||
const index = createIndexName(sampleDataset.id, dataIndexConfig.id);
|
const index = createIndexName(sampleDataset.id, dataIndexConfig.id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await esClient.asCurrentUser.indices.delete({
|
// TODO: don't delete the index if sample data exists in other spaces (#116677)
|
||||||
index,
|
await context.core.elasticsearch.client.asCurrentUser.indices.delete({ index });
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return response.customError({
|
// if the index doesn't exist, ignore the error and proceed
|
||||||
statusCode: err.status,
|
if (err.body.status !== 404) {
|
||||||
body: {
|
return response.customError({
|
||||||
message: `Unable to delete sample data index "${index}", error: ${err.message}`,
|
statusCode: err.body.status,
|
||||||
},
|
body: {
|
||||||
});
|
message: `Unable to delete sample data index "${index}", error: ${err.body.error.type}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const includedHiddenTypes = sampleDataset.savedObjects
|
const objects = sampleDataset.savedObjects.map(({ type, id }) => ({ type, id }));
|
||||||
.map((object) => object.type)
|
const objectTypes = getUniqueObjectTypes(objects);
|
||||||
.filter((supportedType) => typeRegistry.isHidden(supportedType));
|
const client = getSavedObjectsClient(context, objectTypes);
|
||||||
|
const findSampleObjectsResult = await findSampleObjects({ client, logger, objects });
|
||||||
|
|
||||||
const savedObjectsClient = getSavedObjectsClient({ includedHiddenTypes });
|
const objectsToDelete = findSampleObjectsResult.filter(({ foundObjectId }) => foundObjectId);
|
||||||
|
const deletePromises = objectsToDelete.map(({ type, foundObjectId }) =>
|
||||||
const deletePromises = sampleDataset.savedObjects.map(({ type, id }) =>
|
client.delete(type, foundObjectId!).catch((err) => {
|
||||||
savedObjectsClient.delete(type, id)
|
// if the object doesn't exist, ignore the error and proceed
|
||||||
|
if (isBoom(err) && err.output.statusCode === 404) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.all(deletePromises);
|
await Promise.all(deletePromises);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// ignore 404s since users could have deleted some of the saved objects via the UI
|
return response.customError({
|
||||||
if (_.get(err, 'output.statusCode') !== 404) {
|
statusCode: err.body.status,
|
||||||
return response.customError({
|
body: {
|
||||||
statusCode: err.status,
|
message: `Unable to delete sample dataset saved objects, error: ${err.body.error.type}`,
|
||||||
body: {
|
},
|
||||||
message: `Unable to delete sample dataset saved objects, error: ${err.message}`,
|
});
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// track the usage operation in a non-blocking way
|
// track the usage operation in a non-blocking way
|
||||||
|
|
17
src/plugins/home/server/services/sample_data/routes/utils.ts
Normal file
17
src/plugins/home/server/services/sample_data/routes/utils.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* 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 type { RequestHandlerContext } from 'src/core/server';
|
||||||
|
|
||||||
|
export function getSavedObjectsClient(context: RequestHandlerContext, objectTypes: string[]) {
|
||||||
|
const { getClient, typeRegistry } = context.core.savedObjects;
|
||||||
|
const includedHiddenTypes = objectTypes.filter((supportedType) =>
|
||||||
|
typeRegistry.isHidden(supportedType)
|
||||||
|
);
|
||||||
|
return getClient({ includedHiddenTypes });
|
||||||
|
}
|
|
@ -11,8 +11,8 @@ import { SavedObject } from 'src/core/public';
|
||||||
import {
|
import {
|
||||||
SampleDatasetProvider,
|
SampleDatasetProvider,
|
||||||
SampleDatasetSchema,
|
SampleDatasetSchema,
|
||||||
AppLinkSchema,
|
|
||||||
SampleDatasetDashboardPanel,
|
SampleDatasetDashboardPanel,
|
||||||
|
AppLinkData,
|
||||||
} from './lib/sample_dataset_registry_types';
|
} from './lib/sample_dataset_registry_types';
|
||||||
import { sampleDataSchema } from './lib/sample_dataset_schema';
|
import { sampleDataSchema } from './lib/sample_dataset_schema';
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import { registerSampleDatasetWithIntegration } from './lib/register_with_integr
|
||||||
export class SampleDataRegistry {
|
export class SampleDataRegistry {
|
||||||
constructor(private readonly initContext: PluginInitializerContext) {}
|
constructor(private readonly initContext: PluginInitializerContext) {}
|
||||||
private readonly sampleDatasets: SampleDatasetSchema[] = [];
|
private readonly sampleDatasets: SampleDatasetSchema[] = [];
|
||||||
|
private readonly appLinksMap = new Map<string, AppLinkData[]>();
|
||||||
|
|
||||||
private registerSampleDataSet(specProvider: SampleDatasetProvider) {
|
private registerSampleDataSet(specProvider: SampleDatasetProvider) {
|
||||||
let value: SampleDatasetSchema;
|
let value: SampleDatasetSchema;
|
||||||
|
@ -69,14 +70,10 @@ export class SampleDataRegistry {
|
||||||
this.initContext.logger.get('sample_data', 'usage')
|
this.initContext.logger.get('sample_data', 'usage')
|
||||||
);
|
);
|
||||||
const router = core.http.createRouter();
|
const router = core.http.createRouter();
|
||||||
createListRoute(router, this.sampleDatasets);
|
const logger = this.initContext.logger.get('sampleData');
|
||||||
createInstallRoute(
|
createListRoute(router, this.sampleDatasets, this.appLinksMap, logger);
|
||||||
router,
|
createInstallRoute(router, this.sampleDatasets, logger, usageTracker);
|
||||||
this.sampleDatasets,
|
createUninstallRoute(router, this.sampleDatasets, logger, usageTracker);
|
||||||
this.initContext.logger.get('sampleData'),
|
|
||||||
usageTracker
|
|
||||||
);
|
|
||||||
createUninstallRoute(router, this.sampleDatasets, usageTracker);
|
|
||||||
|
|
||||||
this.registerSampleDataSet(flightsSpecProvider);
|
this.registerSampleDataSet(flightsSpecProvider);
|
||||||
this.registerSampleDataSet(logsSpecProvider);
|
this.registerSampleDataSet(logsSpecProvider);
|
||||||
|
@ -100,7 +97,7 @@ export class SampleDataRegistry {
|
||||||
sampleDataset.savedObjects = sampleDataset.savedObjects.concat(savedObjects);
|
sampleDataset.savedObjects = sampleDataset.savedObjects.concat(savedObjects);
|
||||||
},
|
},
|
||||||
|
|
||||||
addAppLinksToSampleDataset: (id: string, appLinks: AppLinkSchema[]) => {
|
addAppLinksToSampleDataset: (id: string, appLinks: AppLinkData[]) => {
|
||||||
const sampleDataset = this.sampleDatasets.find((dataset) => {
|
const sampleDataset = this.sampleDatasets.find((dataset) => {
|
||||||
return dataset.id === id;
|
return dataset.id === id;
|
||||||
});
|
});
|
||||||
|
@ -109,9 +106,8 @@ export class SampleDataRegistry {
|
||||||
throw new Error(`Unable to find sample dataset with id: ${id}`);
|
throw new Error(`Unable to find sample dataset with id: ${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleDataset.appLinks = sampleDataset.appLinks
|
const existingAppLinks = this.appLinksMap.get(id) ?? [];
|
||||||
? sampleDataset.appLinks.concat(appLinks)
|
this.appLinksMap.set(id, [...existingAppLinks, ...appLinks]);
|
||||||
: [];
|
|
||||||
},
|
},
|
||||||
|
|
||||||
replacePanelInSampleDatasetDashboard: ({
|
replacePanelInSampleDatasetDashboard: ({
|
||||||
|
|
|
@ -19,6 +19,7 @@ export type {
|
||||||
ArtifactsSchema,
|
ArtifactsSchema,
|
||||||
TutorialSchema,
|
TutorialSchema,
|
||||||
TutorialProvider,
|
TutorialProvider,
|
||||||
|
TutorialContext,
|
||||||
TutorialContextFactory,
|
TutorialContextFactory,
|
||||||
ScopedTutorialContextFactory,
|
ScopedTutorialContextFactory,
|
||||||
} from './lib/tutorials_registry_types';
|
} from './lib/tutorials_registry_types';
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import expect from '@kbn/expect';
|
import expect from '@kbn/expect';
|
||||||
|
import type { Response } from 'superagent';
|
||||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||||
|
|
||||||
export default function ({ getService }: FtrProviderContext) {
|
export default function ({ getService }: FtrProviderContext) {
|
||||||
|
@ -15,81 +16,142 @@ export default function ({ getService }: FtrProviderContext) {
|
||||||
const es = getService('es');
|
const es = getService('es');
|
||||||
|
|
||||||
const MILLISECOND_IN_WEEK = 1000 * 60 * 60 * 24 * 7;
|
const MILLISECOND_IN_WEEK = 1000 * 60 * 60 * 24 * 7;
|
||||||
|
const SPACES = ['default', 'other'];
|
||||||
|
const FLIGHTS_OVERVIEW_DASHBOARD_ID = '7adfa750-4c81-11e8-b3d7-01146121b73d'; // default ID of the flights overview dashboard
|
||||||
|
const FLIGHTS_CANVAS_APPLINK_PATH =
|
||||||
|
'/app/canvas#/workpad/workpad-a474e74b-aedc-47c3-894a-db77e62c41e0'; // includes default ID of the flights canvas applink path
|
||||||
|
|
||||||
describe('sample data apis', () => {
|
describe('sample data apis', () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await esArchiver.emptyKibanaIndex();
|
await esArchiver.emptyKibanaIndex();
|
||||||
});
|
});
|
||||||
describe('list', () => {
|
after(async () => {
|
||||||
it('should return list of sample data sets with installed status', async () => {
|
await esArchiver.emptyKibanaIndex();
|
||||||
const resp = await supertest.get(`/api/sample_data`).set('kbn-xsrf', 'kibana').expect(200);
|
|
||||||
|
|
||||||
expect(resp.body).to.be.an('array');
|
|
||||||
expect(resp.body.length).to.be.above(0);
|
|
||||||
expect(resp.body[0].status).to.be('not_installed');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('install', () => {
|
for (const space of SPACES) {
|
||||||
it('should return 404 if id does not match any sample data sets', async () => {
|
const apiPath = `/s/${space}/api/sample_data`;
|
||||||
await supertest.post(`/api/sample_data/xxxx`).set('kbn-xsrf', 'kibana').expect(404);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return 200 if success', async () => {
|
describe(`list in the ${space} space (before install)`, () => {
|
||||||
const resp = await supertest
|
it('should return list of sample data sets with installed status', async () => {
|
||||||
.post(`/api/sample_data/flights`)
|
const resp = await supertest.get(apiPath).set('kbn-xsrf', 'kibana').expect(200);
|
||||||
.set('kbn-xsrf', 'kibana')
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
expect(resp.body).to.eql({
|
const flightsData = findFlightsData(resp);
|
||||||
elasticsearchIndicesCreated: { kibana_sample_data_flights: 13059 },
|
expect(flightsData.status).to.be('not_installed');
|
||||||
kibanaSavedObjectsLoaded: 11,
|
// Check and make sure the sample dataset reflects the default object IDs, because no sample data objects exist.
|
||||||
|
// Instead of checking each object ID, we check the dashboard and canvas app link as representatives.
|
||||||
|
expect(flightsData.overviewDashboard).to.be(FLIGHTS_OVERVIEW_DASHBOARD_ID);
|
||||||
|
expect(flightsData.appLinks[0].path).to.be(FLIGHTS_CANVAS_APPLINK_PATH);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load elasticsearch index containing sample data with dates relative to current time', async () => {
|
describe(`install in the ${space} space`, () => {
|
||||||
const resp = await es.search<{ timestamp: string }>({
|
it('should return 404 if id does not match any sample data sets', async () => {
|
||||||
index: 'kibana_sample_data_flights',
|
await supertest.post(`${apiPath}/xxxx`).set('kbn-xsrf', 'kibana').expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
const doc = resp.hits.hits[0];
|
it('should return 200 if success', async () => {
|
||||||
const docMilliseconds = Date.parse(doc._source!.timestamp);
|
const resp = await supertest
|
||||||
const nowMilliseconds = Date.now();
|
.post(`${apiPath}/flights`)
|
||||||
const delta = Math.abs(nowMilliseconds - docMilliseconds);
|
.set('kbn-xsrf', 'kibana')
|
||||||
expect(delta).to.be.lessThan(MILLISECOND_IN_WEEK * 4);
|
.expect(200);
|
||||||
});
|
|
||||||
|
|
||||||
describe('parameters', () => {
|
expect(resp.body).to.eql({
|
||||||
it('should load elasticsearch index containing sample data with dates relative to now parameter', async () => {
|
elasticsearchIndicesCreated: { kibana_sample_data_flights: 13059 },
|
||||||
const nowString = `2000-01-01T00:00:00`;
|
kibanaSavedObjectsLoaded: 11,
|
||||||
await supertest
|
});
|
||||||
.post(`/api/sample_data/flights?now=${nowString}`)
|
});
|
||||||
.set('kbn-xsrf', 'kibana');
|
|
||||||
|
|
||||||
|
it('should load elasticsearch index containing sample data with dates relative to current time', async () => {
|
||||||
const resp = await es.search<{ timestamp: string }>({
|
const resp = await es.search<{ timestamp: string }>({
|
||||||
index: 'kibana_sample_data_flights',
|
index: 'kibana_sample_data_flights',
|
||||||
});
|
});
|
||||||
|
|
||||||
const doc = resp.hits.hits[0];
|
const doc = resp.hits.hits[0];
|
||||||
const docMilliseconds = Date.parse(doc._source!.timestamp);
|
const docMilliseconds = Date.parse(doc._source!.timestamp);
|
||||||
const nowMilliseconds = Date.parse(nowString);
|
const nowMilliseconds = Date.now();
|
||||||
const delta = Math.abs(nowMilliseconds - docMilliseconds);
|
const delta = Math.abs(nowMilliseconds - docMilliseconds);
|
||||||
expect(delta).to.be.lessThan(MILLISECOND_IN_WEEK * 4);
|
expect(delta).to.be.lessThan(MILLISECOND_IN_WEEK * 4);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('uninstall', () => {
|
describe('parameters', () => {
|
||||||
it('should uninstall sample data', async () => {
|
it('should load elasticsearch index containing sample data with dates relative to now parameter', async () => {
|
||||||
await supertest.delete(`/api/sample_data/flights`).set('kbn-xsrf', 'kibana').expect(204);
|
const nowString = `2000-01-01T00:00:00`;
|
||||||
});
|
await supertest.post(`${apiPath}/flights?now=${nowString}`).set('kbn-xsrf', 'kibana');
|
||||||
|
|
||||||
it('should remove elasticsearch index containing sample data', async () => {
|
const resp = await es.search<{ timestamp: string }>({
|
||||||
const resp = await es.indices.exists({
|
index: 'kibana_sample_data_flights',
|
||||||
index: 'kibana_sample_data_flights',
|
});
|
||||||
|
|
||||||
|
const doc = resp.hits.hits[0];
|
||||||
|
const docMilliseconds = Date.parse(doc._source!.timestamp);
|
||||||
|
const nowMilliseconds = Date.parse(nowString);
|
||||||
|
const delta = Math.abs(nowMilliseconds - docMilliseconds);
|
||||||
|
expect(delta).to.be.lessThan(MILLISECOND_IN_WEEK * 4);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
expect(resp).to.be(false);
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
describe(`list in the ${space} space (after install)`, () => {
|
||||||
|
it('should return list of sample data sets with installed status', async () => {
|
||||||
|
const resp = await supertest.get(apiPath).set('kbn-xsrf', 'kibana').expect(200);
|
||||||
|
|
||||||
|
const flightsData = findFlightsData(resp);
|
||||||
|
expect(flightsData.status).to.be('installed');
|
||||||
|
// Check and make sure the sample dataset reflects the existing object IDs in each space.
|
||||||
|
// Instead of checking each object ID, we check the dashboard and canvas app link as representatives.
|
||||||
|
if (space === 'default') {
|
||||||
|
expect(flightsData.overviewDashboard).to.be(FLIGHTS_OVERVIEW_DASHBOARD_ID);
|
||||||
|
expect(flightsData.appLinks[0].path).to.be(FLIGHTS_CANVAS_APPLINK_PATH);
|
||||||
|
} else {
|
||||||
|
// the sample data objects installed in the 'other' space had their IDs regenerated upon import
|
||||||
|
expect(flightsData.overviewDashboard).not.to.be(FLIGHTS_OVERVIEW_DASHBOARD_ID);
|
||||||
|
expect(flightsData.appLinks[0].path).not.to.be(FLIGHTS_CANVAS_APPLINK_PATH);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const space of SPACES) {
|
||||||
|
const apiPath = `/s/${space}/api/sample_data`;
|
||||||
|
|
||||||
|
describe(`uninstall in the ${space} space`, () => {
|
||||||
|
it('should uninstall sample data', async () => {
|
||||||
|
// Note: the second time this happens, the index has already been removed, but the uninstall works anyway
|
||||||
|
await supertest.delete(`${apiPath}/flights`).set('kbn-xsrf', 'kibana').expect(204);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove elasticsearch index containing sample data', async () => {
|
||||||
|
const resp = await es.indices.exists({
|
||||||
|
index: 'kibana_sample_data_flights',
|
||||||
|
});
|
||||||
|
expect(resp).to.be(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`list in the ${space} space (after uninstall)`, () => {
|
||||||
|
it('should return list of sample data sets with installed status', async () => {
|
||||||
|
const resp = await supertest.get(apiPath).set('kbn-xsrf', 'kibana').expect(200);
|
||||||
|
|
||||||
|
const flightsData = findFlightsData(resp);
|
||||||
|
expect(flightsData.status).to.be('not_installed');
|
||||||
|
// Check and make sure the sample dataset reflects the default object IDs, because no sample data objects exist.
|
||||||
|
// Instead of checking each object ID, we check the dashboard and canvas app link as representatives.
|
||||||
|
expect(flightsData.overviewDashboard).to.be(FLIGHTS_OVERVIEW_DASHBOARD_ID);
|
||||||
|
expect(flightsData.appLinks[0].path).to.be(FLIGHTS_CANVAS_APPLINK_PATH);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findFlightsData(response: Response) {
|
||||||
|
expect(response.body).to.be.an('array');
|
||||||
|
expect(response.body.length).to.be.above(0);
|
||||||
|
// @ts-expect-error Binding element 'id' implicitly has an 'any' type.
|
||||||
|
const flightsData = response.body.find(({ id }) => id === 'flights');
|
||||||
|
if (!flightsData) {
|
||||||
|
throw new Error('Could not find flights data');
|
||||||
|
}
|
||||||
|
return flightsData;
|
||||||
|
}
|
||||||
|
|
|
@ -28,11 +28,16 @@ export function loadSampleData(
|
||||||
return savedObject;
|
return savedObject;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const getPath = (objectId: string) => `/app/canvas#/workpad/${objectId}`;
|
||||||
|
|
||||||
addSavedObjectsToSampleDataset('ecommerce', updateCanvasWorkpadTimestamps(ecommerceSavedObjects));
|
addSavedObjectsToSampleDataset('ecommerce', updateCanvasWorkpadTimestamps(ecommerceSavedObjects));
|
||||||
addAppLinksToSampleDataset('ecommerce', [
|
addAppLinksToSampleDataset('ecommerce', [
|
||||||
{
|
{
|
||||||
path: '/app/canvas#/workpad/workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e',
|
sampleObject: {
|
||||||
|
type: 'canvas-workpad',
|
||||||
|
id: 'workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e',
|
||||||
|
},
|
||||||
|
getPath,
|
||||||
icon: 'canvasApp',
|
icon: 'canvasApp',
|
||||||
label,
|
label,
|
||||||
},
|
},
|
||||||
|
@ -41,7 +46,11 @@ export function loadSampleData(
|
||||||
addSavedObjectsToSampleDataset('flights', updateCanvasWorkpadTimestamps(flightsSavedObjects));
|
addSavedObjectsToSampleDataset('flights', updateCanvasWorkpadTimestamps(flightsSavedObjects));
|
||||||
addAppLinksToSampleDataset('flights', [
|
addAppLinksToSampleDataset('flights', [
|
||||||
{
|
{
|
||||||
path: '/app/canvas#/workpad/workpad-a474e74b-aedc-47c3-894a-db77e62c41e0',
|
sampleObject: {
|
||||||
|
type: 'canvas-workpad',
|
||||||
|
id: 'workpad-a474e74b-aedc-47c3-894a-db77e62c41e0',
|
||||||
|
},
|
||||||
|
getPath,
|
||||||
icon: 'canvasApp',
|
icon: 'canvasApp',
|
||||||
label,
|
label,
|
||||||
},
|
},
|
||||||
|
@ -50,7 +59,11 @@ export function loadSampleData(
|
||||||
addSavedObjectsToSampleDataset('logs', updateCanvasWorkpadTimestamps(webLogsSavedObjects));
|
addSavedObjectsToSampleDataset('logs', updateCanvasWorkpadTimestamps(webLogsSavedObjects));
|
||||||
addAppLinksToSampleDataset('logs', [
|
addAppLinksToSampleDataset('logs', [
|
||||||
{
|
{
|
||||||
path: '/app/canvas#/workpad/workpad-ad72a4e9-b422-480c-be6d-a64a0b79541d',
|
sampleObject: {
|
||||||
|
type: 'canvas-workpad',
|
||||||
|
id: 'workpad-ad72a4e9-b422-480c-be6d-a64a0b79541d',
|
||||||
|
},
|
||||||
|
getPath,
|
||||||
icon: 'canvasApp',
|
icon: 'canvasApp',
|
||||||
label,
|
label,
|
||||||
},
|
},
|
||||||
|
|
|
@ -348,7 +348,6 @@ const wsState: any = {
|
||||||
maxValuesPerDoc: 1,
|
maxValuesPerDoc: 1,
|
||||||
minDocCount: 3,
|
minDocCount: 3,
|
||||||
},
|
},
|
||||||
indexPatternRefName: 'indexPattern_0',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function registerEcommerceSampleData(sampleDataRegistry: SampleDataRegistrySetup) {
|
export function registerEcommerceSampleData(sampleDataRegistry: SampleDataRegistrySetup) {
|
||||||
|
@ -365,16 +364,11 @@ export function registerEcommerceSampleData(sampleDataRegistry: SampleDataRegist
|
||||||
numVertices: 12,
|
numVertices: 12,
|
||||||
version: 1,
|
version: 1,
|
||||||
wsState: JSON.stringify(JSON.stringify(wsState)),
|
wsState: JSON.stringify(JSON.stringify(wsState)),
|
||||||
|
legacyIndexPatternRef: 'kibana_sample_data_ecommerce',
|
||||||
},
|
},
|
||||||
references: [
|
references: [],
|
||||||
{
|
|
||||||
name: 'indexPattern_0',
|
|
||||||
type: 'index-pattern',
|
|
||||||
id: 'kibana_sample_data_ecommerce',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
migrationVersion: {
|
migrationVersion: {
|
||||||
'graph-workspace': '7.0.0',
|
'graph-workspace': '7.11.0',
|
||||||
},
|
},
|
||||||
updated_at: '2020-01-09T16:40:36.122Z',
|
updated_at: '2020-01-09T16:40:36.122Z',
|
||||||
},
|
},
|
||||||
|
@ -383,7 +377,11 @@ export function registerEcommerceSampleData(sampleDataRegistry: SampleDataRegist
|
||||||
export function registerEcommerceSampleDataLink(sampleDataRegistry: SampleDataRegistrySetup) {
|
export function registerEcommerceSampleDataLink(sampleDataRegistry: SampleDataRegistrySetup) {
|
||||||
sampleDataRegistry.addAppLinksToSampleDataset(datasetId, [
|
sampleDataRegistry.addAppLinksToSampleDataset(datasetId, [
|
||||||
{
|
{
|
||||||
path: createWorkspacePath('46fa9d30-319c-11ea-bbe4-818d9c786051'),
|
sampleObject: {
|
||||||
|
type: 'graph-workspace',
|
||||||
|
id: '46fa9d30-319c-11ea-bbe4-818d9c786051',
|
||||||
|
},
|
||||||
|
getPath: createWorkspacePath,
|
||||||
label: i18n.translate('xpack.graph.sampleData.label', { defaultMessage: 'Graph' }),
|
label: i18n.translate('xpack.graph.sampleData.label', { defaultMessage: 'Graph' }),
|
||||||
icon: APP_ICON,
|
icon: APP_ICON,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1602,7 +1602,6 @@ const wsState: any = {
|
||||||
maxValuesPerDoc: 1,
|
maxValuesPerDoc: 1,
|
||||||
minDocCount: 3,
|
minDocCount: 3,
|
||||||
},
|
},
|
||||||
indexPatternRefName: 'indexPattern_0',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function registerFlightsSampleData(sampleDataRegistry: SampleDataRegistrySetup) {
|
export function registerFlightsSampleData(sampleDataRegistry: SampleDataRegistrySetup) {
|
||||||
|
@ -1619,16 +1618,11 @@ export function registerFlightsSampleData(sampleDataRegistry: SampleDataRegistry
|
||||||
numVertices: 91,
|
numVertices: 91,
|
||||||
version: 1,
|
version: 1,
|
||||||
wsState: JSON.stringify(JSON.stringify(wsState)),
|
wsState: JSON.stringify(JSON.stringify(wsState)),
|
||||||
|
legacyIndexPatternRef: 'kibana_sample_data_flights',
|
||||||
},
|
},
|
||||||
references: [
|
references: [],
|
||||||
{
|
|
||||||
name: 'indexPattern_0',
|
|
||||||
type: 'index-pattern',
|
|
||||||
id: 'kibana_sample_data_flights',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
migrationVersion: {
|
migrationVersion: {
|
||||||
'graph-workspace': '7.0.0',
|
'graph-workspace': '7.11.0',
|
||||||
},
|
},
|
||||||
updated_at: '2020-01-09T15:55:24.013Z',
|
updated_at: '2020-01-09T15:55:24.013Z',
|
||||||
},
|
},
|
||||||
|
@ -1637,7 +1631,11 @@ export function registerFlightsSampleData(sampleDataRegistry: SampleDataRegistry
|
||||||
export function registerFlightsSampleDataLink(sampleDataRegistry: SampleDataRegistrySetup) {
|
export function registerFlightsSampleDataLink(sampleDataRegistry: SampleDataRegistrySetup) {
|
||||||
sampleDataRegistry.addAppLinksToSampleDataset(datasetId, [
|
sampleDataRegistry.addAppLinksToSampleDataset(datasetId, [
|
||||||
{
|
{
|
||||||
path: createWorkspacePath('5dc018d0-32f8-11ea-bbe4-818d9c786051'),
|
sampleObject: {
|
||||||
|
type: 'graph-workspace',
|
||||||
|
id: '5dc018d0-32f8-11ea-bbe4-818d9c786051',
|
||||||
|
},
|
||||||
|
getPath: createWorkspacePath,
|
||||||
label: i18n.translate('xpack.graph.sampleData.label', { defaultMessage: 'Graph' }),
|
label: i18n.translate('xpack.graph.sampleData.label', { defaultMessage: 'Graph' }),
|
||||||
icon: APP_ICON,
|
icon: APP_ICON,
|
||||||
},
|
},
|
||||||
|
|
|
@ -419,7 +419,6 @@ const wsState: any = {
|
||||||
maxValuesPerDoc: 1,
|
maxValuesPerDoc: 1,
|
||||||
minDocCount: 3,
|
minDocCount: 3,
|
||||||
},
|
},
|
||||||
indexPatternRefName: 'indexPattern_0',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function registerLogsSampleData(sampleDataRegistry: SampleDataRegistrySetup) {
|
export function registerLogsSampleData(sampleDataRegistry: SampleDataRegistrySetup) {
|
||||||
|
@ -436,16 +435,11 @@ export function registerLogsSampleData(sampleDataRegistry: SampleDataRegistrySet
|
||||||
numVertices: 27,
|
numVertices: 27,
|
||||||
version: 1,
|
version: 1,
|
||||||
wsState: JSON.stringify(JSON.stringify(wsState)),
|
wsState: JSON.stringify(JSON.stringify(wsState)),
|
||||||
|
legacyIndexPatternRef: 'kibana_sample_data_logs',
|
||||||
},
|
},
|
||||||
references: [
|
references: [],
|
||||||
{
|
|
||||||
name: 'indexPattern_0',
|
|
||||||
type: 'index-pattern',
|
|
||||||
id: 'kibana_sample_data_logs',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
migrationVersion: {
|
migrationVersion: {
|
||||||
'graph-workspace': '7.0.0',
|
'graph-workspace': '7.11.0',
|
||||||
},
|
},
|
||||||
updated_at: '2020-01-09T16:40:36.122Z',
|
updated_at: '2020-01-09T16:40:36.122Z',
|
||||||
},
|
},
|
||||||
|
@ -454,7 +448,11 @@ export function registerLogsSampleData(sampleDataRegistry: SampleDataRegistrySet
|
||||||
export function registerLogsSampleDataLink(sampleDataRegistry: SampleDataRegistrySetup) {
|
export function registerLogsSampleDataLink(sampleDataRegistry: SampleDataRegistrySetup) {
|
||||||
sampleDataRegistry.addAppLinksToSampleDataset(datasetId, [
|
sampleDataRegistry.addAppLinksToSampleDataset(datasetId, [
|
||||||
{
|
{
|
||||||
path: createWorkspacePath('e2141080-32fa-11ea-bbe4-818d9c786051'),
|
sampleObject: {
|
||||||
|
type: 'graph-workspace',
|
||||||
|
id: 'e2141080-32fa-11ea-bbe4-818d9c786051',
|
||||||
|
},
|
||||||
|
getPath: createWorkspacePath,
|
||||||
label: i18n.translate('xpack.graph.sampleData.label', { defaultMessage: 'Graph' }),
|
label: i18n.translate('xpack.graph.sampleData.label', { defaultMessage: 'Graph' }),
|
||||||
icon: APP_ICON,
|
icon: APP_ICON,
|
||||||
},
|
},
|
||||||
|
|
|
@ -158,7 +158,8 @@ export class InfraServerPlugin implements Plugin<InfraPluginSetup> {
|
||||||
|
|
||||||
plugins.home.sampleData.addAppLinksToSampleDataset('logs', [
|
plugins.home.sampleData.addAppLinksToSampleDataset('logs', [
|
||||||
{
|
{
|
||||||
path: `/app/logs`,
|
sampleObject: null, // indicates that there is no sample object associated with this app link's path
|
||||||
|
getPath: () => `/app/logs`,
|
||||||
label: logsSampleDataLinkLabel,
|
label: logsSampleDataLinkLabel,
|
||||||
icon: 'logsApp',
|
icon: 'logsApp',
|
||||||
},
|
},
|
||||||
|
|
|
@ -77,7 +77,11 @@ export class MapsPlugin implements Plugin {
|
||||||
|
|
||||||
home.sampleData.addAppLinksToSampleDataset('ecommerce', [
|
home.sampleData.addAppLinksToSampleDataset('ecommerce', [
|
||||||
{
|
{
|
||||||
path: getFullPath('2c9c1f60-1909-11e9-919b-ffe5949a18d2'),
|
sampleObject: {
|
||||||
|
type: MAP_SAVED_OBJECT_TYPE,
|
||||||
|
id: '2c9c1f60-1909-11e9-919b-ffe5949a18d2',
|
||||||
|
},
|
||||||
|
getPath: getFullPath,
|
||||||
label: sampleDataLinkLabel,
|
label: sampleDataLinkLabel,
|
||||||
icon: APP_ICON,
|
icon: APP_ICON,
|
||||||
},
|
},
|
||||||
|
@ -99,7 +103,11 @@ export class MapsPlugin implements Plugin {
|
||||||
|
|
||||||
home.sampleData.addAppLinksToSampleDataset('flights', [
|
home.sampleData.addAppLinksToSampleDataset('flights', [
|
||||||
{
|
{
|
||||||
path: getFullPath('5dd88580-1906-11e9-919b-ffe5949a18d2'),
|
sampleObject: {
|
||||||
|
type: MAP_SAVED_OBJECT_TYPE,
|
||||||
|
id: '5dd88580-1906-11e9-919b-ffe5949a18d2',
|
||||||
|
},
|
||||||
|
getPath: getFullPath,
|
||||||
label: sampleDataLinkLabel,
|
label: sampleDataLinkLabel,
|
||||||
icon: APP_ICON,
|
icon: APP_ICON,
|
||||||
},
|
},
|
||||||
|
@ -120,7 +128,11 @@ export class MapsPlugin implements Plugin {
|
||||||
home.sampleData.addSavedObjectsToSampleDataset('logs', getWebLogsSavedObjects());
|
home.sampleData.addSavedObjectsToSampleDataset('logs', getWebLogsSavedObjects());
|
||||||
home.sampleData.addAppLinksToSampleDataset('logs', [
|
home.sampleData.addAppLinksToSampleDataset('logs', [
|
||||||
{
|
{
|
||||||
path: getFullPath('de71f4f0-1902-11e9-919b-ffe5949a18d2'),
|
sampleObject: {
|
||||||
|
type: MAP_SAVED_OBJECT_TYPE,
|
||||||
|
id: 'de71f4f0-1902-11e9-919b-ffe5949a18d2',
|
||||||
|
},
|
||||||
|
getPath: getFullPath,
|
||||||
label: sampleDataLinkLabel,
|
label: sampleDataLinkLabel,
|
||||||
icon: APP_ICON,
|
icon: APP_ICON,
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,10 +15,16 @@ export function initSampleDataSets(mlLicense: MlLicense, plugins: PluginsSetup)
|
||||||
defaultMessage: 'ML jobs',
|
defaultMessage: 'ML jobs',
|
||||||
});
|
});
|
||||||
const { addAppLinksToSampleDataset } = plugins.home.sampleData;
|
const { addAppLinksToSampleDataset } = plugins.home.sampleData;
|
||||||
|
const getCreateJobPath = (jobId: string, dataViewId: string) =>
|
||||||
|
`/app/ml/modules/check_view_or_create?id=${jobId}&index=${dataViewId}`;
|
||||||
|
|
||||||
addAppLinksToSampleDataset('ecommerce', [
|
addAppLinksToSampleDataset('ecommerce', [
|
||||||
{
|
{
|
||||||
path: '/app/ml/modules/check_view_or_create?id=sample_data_ecommerce&index=ff959d40-b880-11e8-a6d9-e546fe2bba5f',
|
sampleObject: {
|
||||||
|
type: 'index-pattern',
|
||||||
|
id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f',
|
||||||
|
},
|
||||||
|
getPath: (objectId) => getCreateJobPath('sample_data_ecommerce', objectId),
|
||||||
label: sampleDataLinkLabel,
|
label: sampleDataLinkLabel,
|
||||||
icon: 'machineLearningApp',
|
icon: 'machineLearningApp',
|
||||||
},
|
},
|
||||||
|
@ -26,7 +32,11 @@ export function initSampleDataSets(mlLicense: MlLicense, plugins: PluginsSetup)
|
||||||
|
|
||||||
addAppLinksToSampleDataset('logs', [
|
addAppLinksToSampleDataset('logs', [
|
||||||
{
|
{
|
||||||
path: '/app/ml/modules/check_view_or_create?id=sample_data_weblogs&index=90943e30-9a47-11e8-b64d-95841ca0b247',
|
sampleObject: {
|
||||||
|
type: 'index-pattern',
|
||||||
|
id: '90943e30-9a47-11e8-b64d-95841ca0b247',
|
||||||
|
},
|
||||||
|
getPath: (objectId) => getCreateJobPath('sample_data_weblogs', objectId),
|
||||||
label: sampleDataLinkLabel,
|
label: sampleDataLinkLabel,
|
||||||
icon: 'machineLearningApp',
|
icon: 'machineLearningApp',
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue