mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* Move graph to NP * Styles * Clean up * Fix eslint * Fix ESlint * Fix path * Fix container height * Clean up * Update index.ts * Update graph_client_workspace.js * Refactoring * Remove unused methods * Update graph_client_workspace.test.js * Rename npData to data * Move Readme * Inline parsing discover url * Remove import of legacy styles * Update README Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
a6f2b2e4f2
commit
d78150da17
121 changed files with 339 additions and 451 deletions
|
@ -4,31 +4,19 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
// @ts-ignore
|
||||
import migrations from './migrations';
|
||||
import mappings from './mappings.json';
|
||||
import { LegacyPluginInitializer } from '../../../../src/legacy/plugin_discovery/types';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
|
||||
export const graph: LegacyPluginInitializer = kibana => {
|
||||
return new kibana.Plugin({
|
||||
id: 'graph',
|
||||
configPrefix: 'xpack.graph',
|
||||
publicDir: resolve(__dirname, 'public'),
|
||||
require: ['kibana', 'elasticsearch', 'xpack_main'],
|
||||
uiExports: {
|
||||
app: {
|
||||
title: 'Graph',
|
||||
order: 9000,
|
||||
icon: 'plugins/graph/icon.png',
|
||||
euiIconType: 'graphApp',
|
||||
main: 'plugins/graph/index',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
},
|
||||
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
|
||||
mappings,
|
||||
migrations,
|
||||
},
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 6.1 KiB |
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { npSetup, npStart } from 'ui/new_platform';
|
||||
import { LicensingPluginSetup } from '../../../../plugins/licensing/public';
|
||||
import { GraphPlugin } from './plugin';
|
||||
import { GraphSetup } from '../../../../plugins/graph/public';
|
||||
|
||||
type XpackNpSetupDeps = typeof npSetup.plugins & {
|
||||
licensing: LicensingPluginSetup;
|
||||
graph: GraphSetup;
|
||||
};
|
||||
|
||||
(async () => {
|
||||
const instance = new GraphPlugin();
|
||||
instance.setup(npSetup.core, {
|
||||
...(npSetup.plugins as XpackNpSetupDeps),
|
||||
});
|
||||
instance.start(npStart.core, {
|
||||
npData: npStart.plugins.data,
|
||||
navigation: npStart.plugins.navigation,
|
||||
});
|
||||
})();
|
|
@ -1,9 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import 'ace';
|
||||
|
||||
export { configureAppAngularModule } from '../../../../../src/plugins/kibana_legacy/public';
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
// NP type imports
|
||||
import {
|
||||
AppMountParameters,
|
||||
CoreSetup,
|
||||
CoreStart,
|
||||
Plugin,
|
||||
SavedObjectsClientContract,
|
||||
} from 'src/core/public';
|
||||
import { Plugin as DataPlugin } from 'src/plugins/data/public';
|
||||
import { Storage } from '../../../../../src/plugins/kibana_utils/public';
|
||||
import { LicensingPluginSetup } from '../../../../plugins/licensing/public';
|
||||
import { NavigationPublicPluginStart as NavigationStart } from '../../../../../src/plugins/navigation/public';
|
||||
import { initAngularBootstrap } from '../../../../../src/plugins/kibana_legacy/public';
|
||||
import { GraphSetup } from '../../../../plugins/graph/public';
|
||||
|
||||
export interface GraphPluginStartDependencies {
|
||||
npData: ReturnType<DataPlugin['start']>;
|
||||
navigation: NavigationStart;
|
||||
}
|
||||
|
||||
export interface GraphPluginSetupDependencies {
|
||||
licensing: LicensingPluginSetup;
|
||||
graph: GraphSetup;
|
||||
}
|
||||
|
||||
export class GraphPlugin implements Plugin {
|
||||
private navigationStart: NavigationStart | null = null;
|
||||
private npDataStart: ReturnType<DataPlugin['start']> | null = null;
|
||||
private savedObjectsClient: SavedObjectsClientContract | null = null;
|
||||
|
||||
setup(core: CoreSetup, { licensing, graph }: GraphPluginSetupDependencies) {
|
||||
initAngularBootstrap();
|
||||
core.application.register({
|
||||
id: 'graph',
|
||||
title: 'Graph',
|
||||
mount: async (params: AppMountParameters) => {
|
||||
const [coreStart] = await core.getStartServices();
|
||||
const { renderApp } = await import('./application');
|
||||
return renderApp({
|
||||
...params,
|
||||
licensing,
|
||||
navigation: this.navigationStart!,
|
||||
npData: this.npDataStart!,
|
||||
savedObjectsClient: this.savedObjectsClient!,
|
||||
addBasePath: core.http.basePath.prepend,
|
||||
getBasePath: core.http.basePath.get,
|
||||
canEditDrillDownUrls: graph.config.canEditDrillDownUrls,
|
||||
graphSavePolicy: graph.config.savePolicy,
|
||||
storage: new Storage(window.localStorage),
|
||||
capabilities: coreStart.application.capabilities.graph,
|
||||
coreStart,
|
||||
chrome: coreStart.chrome,
|
||||
config: coreStart.uiSettings,
|
||||
toastNotifications: coreStart.notifications.toasts,
|
||||
indexPatterns: this.npDataStart!.indexPatterns,
|
||||
overlays: coreStart.overlays,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
start(core: CoreStart, { npData, navigation }: GraphPluginStartDependencies) {
|
||||
this.navigationStart = navigation;
|
||||
this.npDataStart = npData;
|
||||
this.savedObjectsClient = core.savedObjects.client;
|
||||
}
|
||||
|
||||
stop() {}
|
||||
}
|
|
@ -8,7 +8,7 @@ Graph shows only up in the side bar if your server is running on a platinum or t
|
|||
|
||||
* Run tests `node x-pack/scripts/jest.js --watch plugins/graph`
|
||||
* Run type check `node scripts/type_check.js --project=x-pack/tsconfig.json`
|
||||
* Run linter `node scripts/eslint.js x-pack/legacy/plugins/graph`
|
||||
* Run linter `node scripts/eslint.js x-pack/plugins/graph`
|
||||
* Run functional tests (make sure to stop dev server)
|
||||
* Server `cd x-pack && node ./scripts/functional_tests_server.js`
|
||||
* Tests `cd x-pack && node ../scripts/functional_test_runner.js --config ./test/functional/config.js --grep=graph`
|
||||
|
@ -21,7 +21,6 @@ Currently most of the state handling is done by a central angular controller. Th
|
|||
|
||||
* `angular/` contains all code using javascript and angular. Rewriting this code in typescript and react is currently ongoing. When the migration is finished, this folder will go away
|
||||
* `components/` contains react components for various parts of the interface. Components can hold local UI state (e.g. current form data), everything else should be passed in from the caller. Styles should reside in a component-specific stylesheet
|
||||
* `hacks/` contains files that need to run before the actual app is started. When moving to the new platform, this folder will go away.
|
||||
* `services/` contains functions that encapsule other parts of Kibana. Stateful dependencies are passed in from the outside. Components should not rely on services directly but have callbacks passed in. Once the migration to redux/saga is complete, only sagas will use services.
|
||||
* `helpers/` contains side effect free helper functions that can be imported and used from components and services
|
||||
* `state_management/` contains reducers, action creators, selectors and sagas. It also exports the central store creator
|
|
@ -4,7 +4,7 @@
|
|||
"kibanaVersion": "kibana",
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["licensing"],
|
||||
"requiredPlugins": ["licensing", "data", "navigation"],
|
||||
"optionalPlugins": ["home"],
|
||||
"configPath": ["xpack", "graph"]
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
// Kibana wrapper
|
||||
const d3 = require('d3');
|
||||
|
||||
|
@ -79,7 +81,7 @@ module.exports = (function() {
|
|||
self.redo = reverseOperation.undo;
|
||||
}
|
||||
|
||||
function GroupOperation(receiver, orphan, vm) {
|
||||
function GroupOperation(receiver, orphan) {
|
||||
const self = this;
|
||||
self.receiver = receiver;
|
||||
self.orphan = orphan;
|
||||
|
@ -91,7 +93,7 @@ module.exports = (function() {
|
|||
};
|
||||
}
|
||||
|
||||
function UnGroupOperation(parent, child, vm) {
|
||||
function UnGroupOperation(parent, child) {
|
||||
const self = this;
|
||||
self.parent = parent;
|
||||
self.child = child;
|
||||
|
@ -152,9 +154,7 @@ module.exports = (function() {
|
|||
if (lastOps) {
|
||||
this.stopLayout();
|
||||
this.redoLog.push(lastOps);
|
||||
for (const i in lastOps) {
|
||||
lastOps[i].undo();
|
||||
}
|
||||
lastOps.forEach(ops => ops.undo());
|
||||
this.runLayout();
|
||||
}
|
||||
};
|
||||
|
@ -163,29 +163,22 @@ module.exports = (function() {
|
|||
if (lastOps) {
|
||||
this.stopLayout();
|
||||
this.undoLog.push(lastOps);
|
||||
for (const i in lastOps) {
|
||||
lastOps[i].redo();
|
||||
}
|
||||
lastOps.forEach(ops => ops.redo());
|
||||
this.runLayout();
|
||||
}
|
||||
};
|
||||
|
||||
//Determines if 2 nodes are connected via an edge
|
||||
this.areLinked = function(a, b) {
|
||||
if (a == b) return true;
|
||||
const allEdges = this.edges;
|
||||
for (const e in allEdges) {
|
||||
if (e.source == a) {
|
||||
if (e.target == b) {
|
||||
return true;
|
||||
}
|
||||
if (a === b) return true;
|
||||
this.edges.forEach(e => {
|
||||
if (e.source === a && e.target === b) {
|
||||
return true;
|
||||
}
|
||||
if (e.source == b) {
|
||||
if (e.target == a) {
|
||||
return true;
|
||||
}
|
||||
if (e.source === b && e.target === a) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
};
|
||||
|
||||
|
@ -193,47 +186,43 @@ module.exports = (function() {
|
|||
|
||||
this.selectAll = function() {
|
||||
self.selectedNodes = [];
|
||||
for (const n in self.nodes) {
|
||||
const node = self.nodes[n];
|
||||
if (node.parent == undefined) {
|
||||
self.nodes.forEach(node => {
|
||||
if (node.parent === undefined) {
|
||||
node.isSelected = true;
|
||||
self.selectedNodes.push(node);
|
||||
} else {
|
||||
node.isSelected = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.selectNone = function() {
|
||||
self.selectedNodes = [];
|
||||
for (const n in self.nodes) {
|
||||
const node = self.nodes[n];
|
||||
self.nodes.forEach(node => {
|
||||
node.isSelected = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.selectInvert = function() {
|
||||
self.selectedNodes = [];
|
||||
for (const n in self.nodes) {
|
||||
const node = self.nodes[n];
|
||||
if (node.parent != undefined) {
|
||||
continue;
|
||||
self.nodes.forEach(node => {
|
||||
if (node.parent !== undefined) {
|
||||
return;
|
||||
}
|
||||
node.isSelected = !node.isSelected;
|
||||
if (node.isSelected) {
|
||||
self.selectedNodes.push(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.selectNodes = function(nodes) {
|
||||
for (const n in nodes) {
|
||||
const node = nodes[n];
|
||||
nodes.forEach(node => {
|
||||
node.isSelected = true;
|
||||
if (self.selectedNodes.indexOf(node) < 0) {
|
||||
self.selectedNodes.push(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.selectNode = function(node) {
|
||||
|
@ -247,29 +236,27 @@ module.exports = (function() {
|
|||
let allAndGrouped = self.returnUnpackedGroupeds(self.selectedNodes);
|
||||
|
||||
// Nothing selected so process all nodes
|
||||
if (allAndGrouped.length == 0) {
|
||||
if (allAndGrouped.length === 0) {
|
||||
allAndGrouped = self.nodes.slice(0);
|
||||
}
|
||||
|
||||
const undoOperations = [];
|
||||
for (const i in allAndGrouped) {
|
||||
const node = allAndGrouped[i];
|
||||
allAndGrouped.forEach(node => {
|
||||
//We set selected to false because despite being deleted, node objects sit in an undo log
|
||||
node.isSelected = false;
|
||||
delete self.nodesMap[node.id];
|
||||
undoOperations.push(new ReverseOperation(new AddNodeOperation(node, self)));
|
||||
}
|
||||
});
|
||||
self.arrRemoveAll(self.nodes, allAndGrouped);
|
||||
self.arrRemoveAll(self.selectedNodes, allAndGrouped);
|
||||
|
||||
const danglingEdges = self.edges.filter(function(edge) {
|
||||
return self.nodes.indexOf(edge.source) < 0 || self.nodes.indexOf(edge.target) < 0;
|
||||
});
|
||||
for (const i in danglingEdges) {
|
||||
const edge = danglingEdges[i];
|
||||
danglingEdges.forEach(edge => {
|
||||
delete self.edgesMap[edge.id];
|
||||
undoOperations.push(new ReverseOperation(new AddEdgeOperation(edge, self)));
|
||||
}
|
||||
});
|
||||
self.addUndoLogEntry(undoOperations);
|
||||
self.arrRemoveAll(self.edges, danglingEdges);
|
||||
self.runLayout();
|
||||
|
@ -277,8 +264,7 @@ module.exports = (function() {
|
|||
|
||||
this.selectNeighbours = function() {
|
||||
const newSelections = [];
|
||||
for (const n in self.edges) {
|
||||
const edge = self.edges[n];
|
||||
self.edges.forEach(edge => {
|
||||
if (!edge.topSrc.isSelected) {
|
||||
if (self.selectedNodes.indexOf(edge.topTarget) >= 0) {
|
||||
if (newSelections.indexOf(edge.topSrc) < 0) {
|
||||
|
@ -293,18 +279,17 @@ module.exports = (function() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const i in newSelections) {
|
||||
const newlySelectedNode = newSelections[i];
|
||||
});
|
||||
newSelections.forEach(newlySelectedNode => {
|
||||
self.selectedNodes.push(newlySelectedNode);
|
||||
newlySelectedNode.isSelected = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.selectNone = function() {
|
||||
for (const n in self.selectedNodes) {
|
||||
self.selectedNodes[n].isSelected = false;
|
||||
}
|
||||
self.selectedNodes.forEach(node => {
|
||||
node.isSelected = false;
|
||||
});
|
||||
self.selectedNodes = [];
|
||||
};
|
||||
|
||||
|
@ -318,30 +303,25 @@ module.exports = (function() {
|
|||
};
|
||||
|
||||
this.colorSelected = function(colorNum) {
|
||||
const selections = self.getAllSelectedNodes();
|
||||
for (const i in selections) {
|
||||
selections[i].color = colorNum;
|
||||
}
|
||||
self.getAllSelectedNodes().forEach(node => {
|
||||
node.color = colorNum;
|
||||
});
|
||||
};
|
||||
|
||||
this.getSelectionsThatAreGrouped = function() {
|
||||
const result = [];
|
||||
const selections = self.selectedNodes;
|
||||
for (const i in selections) {
|
||||
const node = selections[i];
|
||||
self.selectedNodes.forEach(node => {
|
||||
if (node.numChildren > 0) {
|
||||
result.push(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
this.ungroupSelection = function() {
|
||||
const selections = self.getSelectionsThatAreGrouped();
|
||||
for (const i in selections) {
|
||||
const node = selections[i];
|
||||
self.getSelectionsThatAreGrouped().forEach(node => {
|
||||
self.ungroup(node);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.toggleNodeSelection = function(node) {
|
||||
|
@ -371,7 +351,7 @@ module.exports = (function() {
|
|||
if (result.indexOf(topLevelTarget) >= 0) {
|
||||
//visible top-level node is selected - add all nesteds starting from bottom up
|
||||
let target = edge.target;
|
||||
while (target.parent != undefined) {
|
||||
while (target.parent !== undefined) {
|
||||
if (result.indexOf(target) < 0) {
|
||||
result.push(target);
|
||||
}
|
||||
|
@ -382,7 +362,7 @@ module.exports = (function() {
|
|||
if (result.indexOf(topLevelSource) >= 0) {
|
||||
//visible top-level node is selected - add all nesteds starting from bottom up
|
||||
let source = edge.source;
|
||||
while (source.parent != undefined) {
|
||||
while (source.parent !== undefined) {
|
||||
if (result.indexOf(source) < 0) {
|
||||
result.push(source);
|
||||
}
|
||||
|
@ -425,22 +405,21 @@ module.exports = (function() {
|
|||
|
||||
this.getNeighbours = function(node) {
|
||||
const neighbourNodes = [];
|
||||
for (const e in self.edges) {
|
||||
const edge = self.edges[e];
|
||||
if (edge.topSrc == edge.topTarget) {
|
||||
continue;
|
||||
self.edges.forEach(edge => {
|
||||
if (edge.topSrc === edge.topTarget) {
|
||||
return;
|
||||
}
|
||||
if (edge.topSrc == node) {
|
||||
if (edge.topSrc === node) {
|
||||
if (neighbourNodes.indexOf(edge.topTarget) < 0) {
|
||||
neighbourNodes.push(edge.topTarget);
|
||||
}
|
||||
}
|
||||
if (edge.topTarget == node) {
|
||||
if (edge.topTarget === node) {
|
||||
if (neighbourNodes.indexOf(edge.topSrc) < 0) {
|
||||
neighbourNodes.push(edge.topSrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return neighbourNodes;
|
||||
};
|
||||
|
||||
|
@ -448,7 +427,7 @@ module.exports = (function() {
|
|||
this.buildNodeQuery = function(topLevelNode) {
|
||||
let containedNodes = [topLevelNode];
|
||||
containedNodes = self.returnUnpackedGroupeds(containedNodes);
|
||||
if (containedNodes.length == 1) {
|
||||
if (containedNodes.length === 1) {
|
||||
//Simple case - return a single-term query
|
||||
const tq = {};
|
||||
tq[topLevelNode.data.field] = topLevelNode.data.term;
|
||||
|
@ -457,17 +436,16 @@ module.exports = (function() {
|
|||
};
|
||||
}
|
||||
const termsByField = {};
|
||||
for (const i in containedNodes) {
|
||||
const node = containedNodes[i];
|
||||
containedNodes.forEach(node => {
|
||||
let termsList = termsByField[node.data.field];
|
||||
if (!termsList) {
|
||||
termsList = [];
|
||||
termsByField[node.data.field] = termsList;
|
||||
}
|
||||
termsList.push(node.data.term);
|
||||
}
|
||||
});
|
||||
//Single field case
|
||||
if (Object.keys(termsByField).length == 1) {
|
||||
if (Object.keys(termsByField).length === 1) {
|
||||
return {
|
||||
terms: termsByField,
|
||||
};
|
||||
|
@ -479,11 +457,13 @@ module.exports = (function() {
|
|||
},
|
||||
};
|
||||
for (const field in termsByField) {
|
||||
const tq = {};
|
||||
tq[field] = termsByField[field];
|
||||
q.bool.should.push({
|
||||
terms: tq,
|
||||
});
|
||||
if (termsByField.hasOwnProperty(field)) {
|
||||
const tq = {};
|
||||
tq[field] = termsByField[field];
|
||||
q.bool.should.push({
|
||||
terms: tq,
|
||||
});
|
||||
}
|
||||
}
|
||||
return q;
|
||||
};
|
||||
|
@ -503,39 +483,40 @@ module.exports = (function() {
|
|||
// is potentially a reduced set of nodes if the client has used any
|
||||
// grouping of nodes into parent nodes.
|
||||
const effectiveEdges = [];
|
||||
const edges = self.edges;
|
||||
for (const e in edges) {
|
||||
const edge = edges[e];
|
||||
self.edges.forEach(edge => {
|
||||
let topSrc = edge.source;
|
||||
let topTarget = edge.target;
|
||||
while (topSrc.parent != undefined) {
|
||||
while (topSrc.parent !== undefined) {
|
||||
topSrc = topSrc.parent;
|
||||
}
|
||||
while (topTarget.parent != undefined) {
|
||||
while (topTarget.parent !== undefined) {
|
||||
topTarget = topTarget.parent;
|
||||
}
|
||||
edge.topSrc = topSrc;
|
||||
edge.topTarget = topTarget;
|
||||
|
||||
if (topSrc != topTarget) {
|
||||
if (topSrc !== topTarget) {
|
||||
effectiveEdges.push({
|
||||
source: topSrc,
|
||||
target: topTarget,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
const visibleNodes = self.nodes.filter(function(n) {
|
||||
return n.parent == undefined;
|
||||
return n.parent === undefined;
|
||||
});
|
||||
//reset then roll-up all the counts
|
||||
const allNodes = self.nodes;
|
||||
for (const n in allNodes) {
|
||||
const node = allNodes[n];
|
||||
allNodes.forEach(node => {
|
||||
node.numChildren = 0;
|
||||
}
|
||||
});
|
||||
|
||||
for (const n in allNodes) {
|
||||
if (!allNodes.hasOwnProperty(n)) {
|
||||
continue;
|
||||
}
|
||||
let node = allNodes[n];
|
||||
while (node.parent != undefined) {
|
||||
while (node.parent !== undefined) {
|
||||
node = node.parent;
|
||||
node.numChildren = node.numChildren + 1;
|
||||
}
|
||||
|
@ -551,36 +532,34 @@ module.exports = (function() {
|
|||
.theta(0.99)
|
||||
.alpha(0.5)
|
||||
.size([800, 600])
|
||||
.on('tick', function(e) {
|
||||
.on('tick', function() {
|
||||
const nodeArray = self.nodes;
|
||||
let hasRollups = false;
|
||||
//Update the position of all "top level nodes"
|
||||
for (const i in nodeArray) {
|
||||
const n = nodeArray[i];
|
||||
nodeArray.forEach(n => {
|
||||
//Code to support roll-ups
|
||||
if (n.parent == undefined) {
|
||||
if (n.parent === undefined) {
|
||||
n.kx = n.x;
|
||||
n.ky = n.y;
|
||||
} else {
|
||||
hasRollups = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (hasRollups) {
|
||||
for (const i in nodeArray) {
|
||||
const n = nodeArray[i];
|
||||
nodeArray.forEach(n => {
|
||||
//Code to support roll-ups
|
||||
if (n.parent != undefined) {
|
||||
if (n.parent !== undefined) {
|
||||
// Is a grouped node - inherit parent's position so edges point into parent
|
||||
// d3 thinks it has moved it to x and y but we have final say using kx and ky.
|
||||
let topLevelNode = n.parent;
|
||||
while (topLevelNode.parent != undefined) {
|
||||
while (topLevelNode.parent !== undefined) {
|
||||
topLevelNode = topLevelNode.parent;
|
||||
}
|
||||
|
||||
n.kx = topLevelNode.x;
|
||||
n.ky = topLevelNode.y;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (self.changeHandler) {
|
||||
// Hook to allow any client to respond to position changes
|
||||
|
@ -597,11 +576,11 @@ module.exports = (function() {
|
|||
this.groupSelections = function(node) {
|
||||
const ops = [];
|
||||
self.nodes.forEach(function(otherNode) {
|
||||
if (otherNode != node && otherNode.isSelected && otherNode.parent == undefined) {
|
||||
if (otherNode !== node && otherNode.isSelected && otherNode.parent === undefined) {
|
||||
otherNode.parent = node;
|
||||
otherNode.isSelected = false;
|
||||
self.arrRemove(self.selectedNodes, otherNode);
|
||||
ops.push(new GroupOperation(node, otherNode, self));
|
||||
ops.push(new GroupOperation(node, otherNode));
|
||||
}
|
||||
});
|
||||
self.selectNone();
|
||||
|
@ -614,11 +593,11 @@ module.exports = (function() {
|
|||
const neighbours = self.getNeighbours(node);
|
||||
const ops = [];
|
||||
neighbours.forEach(function(otherNode) {
|
||||
if (otherNode != node && otherNode.parent == undefined) {
|
||||
if (otherNode !== node && otherNode.parent === undefined) {
|
||||
otherNode.parent = node;
|
||||
otherNode.isSelected = false;
|
||||
self.arrRemove(self.selectedNodes, otherNode);
|
||||
ops.push(new GroupOperation(node, otherNode, self));
|
||||
ops.push(new GroupOperation(node, otherNode));
|
||||
}
|
||||
});
|
||||
self.addUndoLogEntry(ops);
|
||||
|
@ -633,11 +612,11 @@ module.exports = (function() {
|
|||
const selClone = self.selectedNodes.slice();
|
||||
const ops = [];
|
||||
selClone.forEach(function(otherNode) {
|
||||
if (otherNode != targetNode && otherNode.parent == undefined) {
|
||||
if (otherNode !== targetNode && otherNode.parent === undefined) {
|
||||
otherNode.parent = targetNode;
|
||||
otherNode.isSelected = false;
|
||||
self.arrRemove(self.selectedNodes, otherNode);
|
||||
ops.push(new GroupOperation(targetNode, otherNode, self));
|
||||
ops.push(new GroupOperation(targetNode, otherNode));
|
||||
}
|
||||
});
|
||||
self.addUndoLogEntry(ops);
|
||||
|
@ -647,9 +626,9 @@ module.exports = (function() {
|
|||
this.ungroup = function(node) {
|
||||
const ops = [];
|
||||
self.nodes.forEach(function(other) {
|
||||
if (other.parent == node) {
|
||||
if (other.parent === node) {
|
||||
other.parent = undefined;
|
||||
ops.push(new UnGroupOperation(node, other, self));
|
||||
ops.push(new UnGroupOperation(node, other));
|
||||
}
|
||||
});
|
||||
self.addUndoLogEntry(ops);
|
||||
|
@ -669,12 +648,11 @@ module.exports = (function() {
|
|||
danglingEdges.push(edge);
|
||||
}
|
||||
});
|
||||
for (const n in selection) {
|
||||
const node = selection[n];
|
||||
selection.forEach(node => {
|
||||
delete self.nodesMap[node.id];
|
||||
self.blacklistedNodes.push(node);
|
||||
node.isSelected = false;
|
||||
}
|
||||
});
|
||||
self.arrRemoveAll(self.nodes, selection);
|
||||
self.arrRemoveAll(self.edges, danglingEdges);
|
||||
self.selectedNodes = [];
|
||||
|
@ -722,9 +700,7 @@ module.exports = (function() {
|
|||
for (let hopNum = 0; hopNum < numHops; hopNum++) {
|
||||
const arr = [];
|
||||
|
||||
for (const f in fieldsChoice) {
|
||||
const field = fieldsChoice[f].name;
|
||||
const hopSize = fieldsChoice[f].hopSize;
|
||||
fieldsChoice.forEach(({ name: field, hopSize }) => {
|
||||
const excludes = excludeNodesByField[field];
|
||||
const stepField = {
|
||||
field: field,
|
||||
|
@ -735,7 +711,7 @@ module.exports = (function() {
|
|||
stepField.exclude = excludes;
|
||||
}
|
||||
arr.push(stepField);
|
||||
}
|
||||
});
|
||||
step.vertices = arr;
|
||||
if (hopNum < numHops - 1) {
|
||||
// if (s < (stepSizes.length - 1)) {
|
||||
|
@ -814,8 +790,7 @@ module.exports = (function() {
|
|||
|
||||
//Remove nodes we already have
|
||||
const dedupedNodes = [];
|
||||
for (const o in newData.nodes) {
|
||||
const node = newData.nodes[o];
|
||||
newData.nodes.forEach(node => {
|
||||
//Assign an ID
|
||||
node.id = self.makeNodeId(node.field, node.term);
|
||||
if (!this.nodesMap[node.id]) {
|
||||
|
@ -825,14 +800,13 @@ module.exports = (function() {
|
|||
}
|
||||
dedupedNodes.push(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (dedupedNodes.length > 0 && this.options.nodeLabeller) {
|
||||
// A hook for client code to attach labels etc to newly introduced nodes.
|
||||
this.options.nodeLabeller(dedupedNodes);
|
||||
}
|
||||
|
||||
for (const o in dedupedNodes) {
|
||||
const dedupedNode = dedupedNodes[o];
|
||||
dedupedNodes.forEach(dedupedNode => {
|
||||
let label = dedupedNode.term;
|
||||
if (dedupedNode.label) {
|
||||
label = dedupedNode.label;
|
||||
|
@ -856,10 +830,9 @@ module.exports = (function() {
|
|||
this.nodes.push(node);
|
||||
lastOps.push(new AddNodeOperation(node, self));
|
||||
this.nodesMap[node.id] = node;
|
||||
}
|
||||
});
|
||||
|
||||
for (const o in newData.edges) {
|
||||
const edge = newData.edges[o];
|
||||
newData.edges.forEach(edge => {
|
||||
const src = newData.nodes[edge.source];
|
||||
const target = newData.nodes[edge.target];
|
||||
edge.id = this.makeEdgeId(src.id, target.id);
|
||||
|
@ -873,7 +846,7 @@ module.exports = (function() {
|
|||
existingEdge.weight = Math.max(existingEdge.weight, edge.weight);
|
||||
//TODO update width too?
|
||||
existingEdge.doc_count = Math.max(existingEdge.doc_count, edge.doc_count);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
const newEdge = {
|
||||
source: srcWrapperObj,
|
||||
|
@ -890,7 +863,7 @@ module.exports = (function() {
|
|||
this.edgesMap[newEdge.id] = newEdge;
|
||||
this.edges.push(newEdge);
|
||||
lastOps.push(new AddEdgeOperation(newEdge, self));
|
||||
}
|
||||
});
|
||||
|
||||
if (lastOps.length > 0) {
|
||||
self.addUndoLogEntry(lastOps);
|
||||
|
@ -907,7 +880,7 @@ module.exports = (function() {
|
|||
self.arrRemove(self.selectedNodes, child);
|
||||
}
|
||||
child.parent = parent;
|
||||
self.addUndoLogEntry([new GroupOperation(parent, child, self)]);
|
||||
self.addUndoLogEntry([new GroupOperation(parent, child)]);
|
||||
self.runLayout();
|
||||
};
|
||||
|
||||
|
@ -922,7 +895,7 @@ module.exports = (function() {
|
|||
|
||||
this.expandSelecteds = function(targetOptions = {}) {
|
||||
let startNodes = self.getAllSelectedNodes();
|
||||
if (startNodes.length == 0) {
|
||||
if (startNodes.length === 0) {
|
||||
startNodes = self.nodes;
|
||||
}
|
||||
const clone = startNodes.slice();
|
||||
|
@ -1000,11 +973,13 @@ module.exports = (function() {
|
|||
const primaryVertices = [];
|
||||
const secondaryVertices = [];
|
||||
for (const fieldName in nodesByField) {
|
||||
primaryVertices.push({
|
||||
field: fieldName,
|
||||
include: nodesByField[fieldName],
|
||||
min_doc_count: parseInt(self.options.exploreControls.minDocCount),
|
||||
});
|
||||
if (nodesByField.hasOwnProperty(fieldName)) {
|
||||
primaryVertices.push({
|
||||
field: fieldName,
|
||||
include: nodesByField[fieldName],
|
||||
min_doc_count: parseInt(self.options.exploreControls.minDocCount),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let targetFields = this.options.vertex_fields;
|
||||
|
@ -1013,11 +988,11 @@ module.exports = (function() {
|
|||
}
|
||||
|
||||
//Identify target fields
|
||||
for (const f in targetFields) {
|
||||
const fieldName = targetFields[f].name;
|
||||
targetFields.forEach(targetField => {
|
||||
const fieldName = targetField.name;
|
||||
// Sometimes the target field is disabled from loading new hops so we need to use the last valid figure
|
||||
const hopSize =
|
||||
targetFields[f].hopSize > 0 ? targetFields[f].hopSize : targetFields[f].lastValidHopSize;
|
||||
targetField.hopSize > 0 ? targetField.hopSize : targetField.lastValidHopSize;
|
||||
|
||||
const fieldHop = {
|
||||
field: fieldName,
|
||||
|
@ -1026,7 +1001,7 @@ module.exports = (function() {
|
|||
};
|
||||
fieldHop.exclude = excludeNodesByField[fieldName];
|
||||
secondaryVertices.push(fieldHop);
|
||||
}
|
||||
});
|
||||
|
||||
const request = {
|
||||
controls: self.buildControls(),
|
||||
|
@ -1038,33 +1013,27 @@ module.exports = (function() {
|
|||
self.lastRequest = JSON.stringify(request, null, '\t');
|
||||
graphExplorer(self.options.indexName, request, function(data) {
|
||||
self.lastResponse = JSON.stringify(data, null, '\t');
|
||||
const nodes = [];
|
||||
const edges = [];
|
||||
|
||||
//Label fields with a field number for CSS styling
|
||||
for (const n in data.vertices) {
|
||||
const node = data.vertices[n];
|
||||
for (const f in targetFields) {
|
||||
const fieldDef = targetFields[f];
|
||||
if (node.field == fieldDef.name) {
|
||||
data.vertices.forEach(node => {
|
||||
targetFields.some(fieldDef => {
|
||||
if (node.field === fieldDef.name) {
|
||||
node.color = fieldDef.color;
|
||||
node.icon = fieldDef.icon;
|
||||
node.fieldDef = fieldDef;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
// Size the edges based on the maximum weight
|
||||
const minLineSize = 2;
|
||||
const maxLineSize = 10;
|
||||
let maxEdgeWeight = 0.00000001;
|
||||
for (const e in data.connections) {
|
||||
const edge = data.connections[e];
|
||||
data.connections.forEach(edge => {
|
||||
maxEdgeWeight = Math.max(maxEdgeWeight, edge.weight);
|
||||
}
|
||||
for (const e in data.connections) {
|
||||
const edge = data.connections[e];
|
||||
edges.push({
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
|
@ -1072,7 +1041,7 @@ module.exports = (function() {
|
|||
weight: edge.weight,
|
||||
width: Math.max(minLineSize, (edge.weight / maxEdgeWeight) * maxLineSize),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add the new nodes and edges into the existing workspace's graph
|
||||
self.mergeGraph({
|
||||
|
@ -1087,8 +1056,7 @@ module.exports = (function() {
|
|||
let trimmedEdges = [];
|
||||
const maxNumEdgesToReturn = 5;
|
||||
//Trim here to just the new edges that are most interesting.
|
||||
for (const o in newEdges) {
|
||||
const edge = newEdges[o];
|
||||
newEdges.forEach(edge => {
|
||||
const src = newNodes[edge.source];
|
||||
const target = newNodes[edge.target];
|
||||
const srcId = src.field + '..' + src.term;
|
||||
|
@ -1097,25 +1065,25 @@ module.exports = (function() {
|
|||
const existingSrcNode = self.nodesMap[srcId];
|
||||
const existingTargetNode = self.nodesMap[targetId];
|
||||
if (existingSrcNode != null && existingTargetNode != null) {
|
||||
if (existingSrcNode.parent != undefined && existingTargetNode.parent != undefined) {
|
||||
if (existingSrcNode.parent !== undefined && existingTargetNode.parent !== undefined) {
|
||||
// both nodes are rolled-up and grouped so this edge would not be a visible
|
||||
// change to the graph - lose it in favour of any other visible ones.
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
console.log('Error? Missing nodes ' + srcId + ' or ' + targetId, self.nodesMap);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
const existingEdge = self.edgesMap[id];
|
||||
if (existingEdge) {
|
||||
existingEdge.weight = Math.max(existingEdge.weight, edge.weight);
|
||||
existingEdge.doc_count = Math.max(existingEdge.doc_count, edge.doc_count);
|
||||
continue;
|
||||
return;
|
||||
} else {
|
||||
trimmedEdges.push(edge);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (trimmedEdges.length > maxNumEdgesToReturn) {
|
||||
//trim to only the most interesting ones
|
||||
trimmedEdges.sort(function(a, b) {
|
||||
|
@ -1132,12 +1100,11 @@ module.exports = (function() {
|
|||
if (!startNodes) {
|
||||
nodes = self.nodes;
|
||||
}
|
||||
for (const bs in nodes) {
|
||||
const node = nodes[bs];
|
||||
if (node.parent == undefined) {
|
||||
nodes.forEach(node => {
|
||||
if (node.parent === undefined) {
|
||||
shoulds.push(self.buildNodeQuery(node));
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
bool: {
|
||||
should: shoulds,
|
||||
|
@ -1256,7 +1223,7 @@ module.exports = (function() {
|
|||
const t2 = keyedBuckets[ids[1]].doc_count;
|
||||
const t1AndT2 = bucket.doc_count;
|
||||
// Calc the significant_terms score to prioritize selection of interesting links
|
||||
bucket.weight = self.JLHScore(
|
||||
bucket.weight = self.jLHScore(
|
||||
t1AndT2,
|
||||
Math.max(t1, t2),
|
||||
Math.min(t1, t2),
|
||||
|
@ -1276,7 +1243,7 @@ module.exports = (function() {
|
|||
return;
|
||||
}
|
||||
const ids = bucket.key.split('|');
|
||||
if (ids.length == 2) {
|
||||
if (ids.length === 2) {
|
||||
// Bucket represents an edge
|
||||
const srcNode = nodesForLinking[ids[0]];
|
||||
const targetNode = nodesForLinking[ids[1]];
|
||||
|
@ -1340,16 +1307,18 @@ module.exports = (function() {
|
|||
txtsByFieldType[node.data.field] = txt;
|
||||
});
|
||||
for (const field in txtsByFieldType) {
|
||||
likeQueries.push({
|
||||
more_like_this: {
|
||||
like: txtsByFieldType[field],
|
||||
min_term_freq: 1,
|
||||
minimum_should_match: '20%',
|
||||
min_doc_freq: 1,
|
||||
boost_terms: 2,
|
||||
max_query_terms: 25,
|
||||
},
|
||||
});
|
||||
if (txtsByFieldType.hasOwnProperty(field)) {
|
||||
likeQueries.push({
|
||||
more_like_this: {
|
||||
like: txtsByFieldType[field],
|
||||
min_term_freq: 1,
|
||||
minimum_should_match: '20%',
|
||||
min_doc_freq: 1,
|
||||
boost_terms: 2,
|
||||
max_query_terms: 25,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const excludeNodesByField = {};
|
||||
|
@ -1397,10 +1366,10 @@ module.exports = (function() {
|
|||
};
|
||||
|
||||
this.getSelectedIntersections = function(callback) {
|
||||
if (self.selectedNodes.length == 0) {
|
||||
if (self.selectedNodes.length === 0) {
|
||||
return self.getAllIntersections(callback, self.nodes);
|
||||
}
|
||||
if (self.selectedNodes.length == 1) {
|
||||
if (self.selectedNodes.length === 1) {
|
||||
const selectedNode = self.selectedNodes[0];
|
||||
const neighbourNodes = self.getNeighbours(selectedNode);
|
||||
neighbourNodes.push(selectedNode);
|
||||
|
@ -1409,7 +1378,7 @@ module.exports = (function() {
|
|||
return self.getAllIntersections(callback, self.getAllSelectedNodes());
|
||||
};
|
||||
|
||||
this.JLHScore = function(subsetFreq, subsetSize, supersetFreq, supersetSize) {
|
||||
this.jLHScore = function(subsetFreq, subsetSize, supersetFreq, supersetSize) {
|
||||
const subsetProbability = subsetFreq / subsetSize;
|
||||
const supersetProbability = supersetFreq / supersetSize;
|
||||
|
||||
|
@ -1432,7 +1401,7 @@ module.exports = (function() {
|
|||
this.getAllIntersections = function(callback, nodes) {
|
||||
//Ensure these are all top-level nodes only
|
||||
nodes = nodes.filter(function(n) {
|
||||
return n.parent == undefined;
|
||||
return n.parent === undefined;
|
||||
});
|
||||
|
||||
const allQueries = nodes.map(function(node) {
|
||||
|
@ -1468,44 +1437,42 @@ module.exports = (function() {
|
|||
},
|
||||
},
|
||||
};
|
||||
for (const n in allQueries) {
|
||||
allQueries.forEach((query, n) => {
|
||||
// Add aggs to get intersection stats with root node.
|
||||
request.aggs.sources.filters.filters['bg' + n] = allQueries[n];
|
||||
request.aggs.sources.aggs.targets.filters.filters['fg' + n] = allQueries[n];
|
||||
}
|
||||
const dataForServer = JSON.stringify(request);
|
||||
request.aggs.sources.filters.filters['bg' + n] = query;
|
||||
request.aggs.sources.aggs.targets.filters.filters['fg' + n] = query;
|
||||
});
|
||||
searcher(self.options.indexName, request, function(data) {
|
||||
const termIntersects = [];
|
||||
const fullDocCounts = [];
|
||||
const allDocCount = data.aggregations.all.doc_count;
|
||||
|
||||
// Gather the background stats for all nodes.
|
||||
for (const n in nodes) {
|
||||
nodes.forEach((rootNode, n) => {
|
||||
fullDocCounts.push(data.aggregations.sources.buckets['bg' + n].doc_count);
|
||||
}
|
||||
for (const n in nodes) {
|
||||
const rootNode = nodes[n];
|
||||
});
|
||||
|
||||
nodes.forEach((rootNode, n) => {
|
||||
const t1 = fullDocCounts[n];
|
||||
const baseAgg = data.aggregations.sources.buckets['bg' + n].targets.buckets;
|
||||
for (const l in nodes) {
|
||||
nodes.forEach((leafNode, l) => {
|
||||
const t2 = fullDocCounts[l];
|
||||
const leafNode = nodes[l];
|
||||
if (l == n) {
|
||||
continue;
|
||||
if (l === n) {
|
||||
return;
|
||||
}
|
||||
if (t1 > t2) {
|
||||
// We should get the same stats for t2->t1 from the t1->t2 bucket path
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
if (t1 == t2) {
|
||||
if (t1 === t2) {
|
||||
if (rootNode.id > leafNode.id) {
|
||||
// We should get the same stats for t2->t1 from the t1->t2 bucket path
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
const t1AndT2 = baseAgg['fg' + l].doc_count;
|
||||
if (t1AndT2 == 0) {
|
||||
continue;
|
||||
if (t1AndT2 === 0) {
|
||||
return;
|
||||
}
|
||||
const neighbourNode = nodes[l];
|
||||
let t1Label = rootNode.data.label;
|
||||
|
@ -1521,7 +1488,7 @@ module.exports = (function() {
|
|||
// var mergeConfidence=t1AndT2/t1;
|
||||
|
||||
// So using Significance heuristic instead
|
||||
const mergeConfidence = self.JLHScore(t1AndT2, t2, t1, allDocCount);
|
||||
const mergeConfidence = self.jLHScore(t1AndT2, t2, t1, allDocCount);
|
||||
|
||||
const termIntersect = {
|
||||
id1: rootNode.id,
|
||||
|
@ -1536,16 +1503,16 @@ module.exports = (function() {
|
|||
overlap: t1AndT2,
|
||||
};
|
||||
termIntersects.push(termIntersect);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
termIntersects.sort(function(a, b) {
|
||||
if (b.mergeConfidence != a.mergeConfidence) {
|
||||
if (b.mergeConfidence !== a.mergeConfidence) {
|
||||
return b.mergeConfidence - a.mergeConfidence;
|
||||
}
|
||||
// If of equal similarity use the size of the overlap as
|
||||
// a measure of magnitude/significance for tie-breaker.
|
||||
|
||||
if (b.overlap != a.overlap) {
|
||||
if (b.overlap !== a.overlap) {
|
||||
return b.overlap - a.overlap;
|
||||
}
|
||||
//All other things being equal we now favour where t2 NOT t1 is small.
|
||||
|
@ -1563,32 +1530,28 @@ module.exports = (function() {
|
|||
self.lastRequest = JSON.stringify(request, null, '\t');
|
||||
graphExplorer(self.options.indexName, request, function(data) {
|
||||
self.lastResponse = JSON.stringify(data, null, '\t');
|
||||
const nodes = [];
|
||||
const edges = [];
|
||||
//Label the nodes with field number for CSS styling
|
||||
for (const n in data.vertices) {
|
||||
const node = data.vertices[n];
|
||||
for (const f in self.options.vertex_fields) {
|
||||
const fieldDef = self.options.vertex_fields[f];
|
||||
if (node.field == fieldDef.name) {
|
||||
data.vertices.forEach(node => {
|
||||
self.options.vertex_fields.some(fieldDef => {
|
||||
if (node.field === fieldDef.name) {
|
||||
node.color = fieldDef.color;
|
||||
node.icon = fieldDef.icon;
|
||||
node.fieldDef = fieldDef;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
//Size the edges depending on weight
|
||||
const minLineSize = 2;
|
||||
const maxLineSize = 10;
|
||||
let maxEdgeWeight = 0.00000001;
|
||||
for (const e in data.connections) {
|
||||
const edge = data.connections[e];
|
||||
data.connections.forEach(edge => {
|
||||
maxEdgeWeight = Math.max(maxEdgeWeight, edge.weight);
|
||||
}
|
||||
for (const e in data.connections) {
|
||||
const edge = data.connections[e];
|
||||
});
|
||||
data.connections.forEach(edge => {
|
||||
edges.push({
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
|
@ -1596,7 +1559,7 @@ module.exports = (function() {
|
|||
weight: edge.weight,
|
||||
width: Math.max(minLineSize, (edge.weight / maxEdgeWeight) * maxLineSize),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
self.mergeGraph(
|
||||
{
|
|
@ -77,7 +77,7 @@ describe('graphui-workspace', function() {
|
|||
},
|
||||
],
|
||||
};
|
||||
workspace.simpleSearch('myquery', {}, 2);
|
||||
workspace.simpleSearch('myquery', undefined, 2);
|
||||
|
||||
expect(workspace.nodes.length).toEqual(2);
|
||||
expect(workspace.edges.length).toEqual(1);
|
||||
|
@ -119,7 +119,7 @@ describe('graphui-workspace', function() {
|
|||
},
|
||||
],
|
||||
};
|
||||
workspace.simpleSearch('myquery', {}, 2);
|
||||
workspace.simpleSearch('myquery', undefined, 2);
|
||||
|
||||
expect(workspace.nodes.length).toEqual(2);
|
||||
expect(workspace.edges.length).toEqual(1);
|
||||
|
@ -201,7 +201,7 @@ describe('graphui-workspace', function() {
|
|||
},
|
||||
],
|
||||
};
|
||||
workspace.simpleSearch('myquery', {}, 2);
|
||||
workspace.simpleSearch('myquery', undefined, 2);
|
||||
|
||||
expect(workspace.selectedNodes.length).toEqual(0);
|
||||
|
||||
|
@ -264,7 +264,7 @@ describe('graphui-workspace', function() {
|
|||
},
|
||||
],
|
||||
};
|
||||
workspace.simpleSearch('myquery', {}, 2);
|
||||
workspace.simpleSearch('myquery', undefined, 2);
|
||||
|
||||
expect(workspace.nodes.length).toEqual(2);
|
||||
|
||||
|
@ -320,7 +320,7 @@ describe('graphui-workspace', function() {
|
|||
},
|
||||
],
|
||||
};
|
||||
workspace.simpleSearch('myquery', {}, 2);
|
||||
workspace.simpleSearch('myquery', undefined, 2);
|
||||
|
||||
expect(workspace.nodes.length).toEqual(2);
|
||||
|
|
@ -6,13 +6,12 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import 'ace';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { isColorDark, hexToRgb } from '@elastic/eui';
|
||||
|
||||
import { toMountPoint } from '../../../../../src/plugins/kibana_react/public';
|
||||
import { showSaveModal } from '../../../../../src/plugins/saved_objects/public';
|
||||
import { toMountPoint } from '../../../../src/plugins/kibana_react/public';
|
||||
import { showSaveModal } from '../../../../src/plugins/saved_objects/public';
|
||||
|
||||
import appTemplate from './angular/templates/index.html';
|
||||
import listingTemplate from './angular/templates/listing_ng_wrapper.html';
|
||||
|
@ -41,7 +40,7 @@ export function initGraphApp(angularModule, deps) {
|
|||
indexPatterns,
|
||||
addBasePath,
|
||||
getBasePath,
|
||||
npData,
|
||||
data,
|
||||
config,
|
||||
savedWorkspaceLoader,
|
||||
capabilities,
|
||||
|
@ -107,7 +106,7 @@ export function initGraphApp(angularModule, deps) {
|
|||
.when('/home', {
|
||||
template: listingTemplate,
|
||||
badge: getReadonlyBadge,
|
||||
controller($location, $scope) {
|
||||
controller: function($location, $scope) {
|
||||
$scope.listingLimit = config.get('savedObjects:listingLimit');
|
||||
$scope.create = () => {
|
||||
$location.url(getNewPath());
|
||||
|
@ -249,6 +248,7 @@ export function initGraphApp(angularModule, deps) {
|
|||
|
||||
const store = createGraphStore({
|
||||
basePath: getBasePath(),
|
||||
addBasePath,
|
||||
indexPatternProvider: $scope.indexPatternProvider,
|
||||
indexPatterns: $route.current.locals.indexPatterns,
|
||||
createWorkspace: (indexPattern, exploreControls) => {
|
||||
|
@ -301,7 +301,7 @@ export function initGraphApp(angularModule, deps) {
|
|||
});
|
||||
|
||||
// register things on scope passed down to react components
|
||||
$scope.pluginDataStart = npData;
|
||||
$scope.pluginDataStart = data;
|
||||
$scope.storage = storage;
|
||||
$scope.coreStart = coreStart;
|
||||
$scope.loading = false;
|
||||
|
@ -420,11 +420,13 @@ export function initGraphApp(angularModule, deps) {
|
|||
while (found) {
|
||||
found = false;
|
||||
for (const i in $scope.detail.mergeCandidates) {
|
||||
const mc = $scope.detail.mergeCandidates[i];
|
||||
if (mc.id1 === childId || mc.id2 === childId) {
|
||||
$scope.detail.mergeCandidates.splice(i, 1);
|
||||
found = true;
|
||||
break;
|
||||
if ($scope.detail.mergeCandidates.hasOwnProperty(i)) {
|
||||
const mc = $scope.detail.mergeCandidates[i];
|
||||
if (mc.id1 === childId || mc.id2 === childId) {
|
||||
$scope.detail.mergeCandidates.splice(i, 1);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -434,8 +436,7 @@ export function initGraphApp(angularModule, deps) {
|
|||
|
||||
$scope.handleMergeCandidatesCallback = function(termIntersects) {
|
||||
const mergeCandidates = [];
|
||||
for (const i in termIntersects) {
|
||||
const ti = termIntersects[i];
|
||||
termIntersects.forEach(ti => {
|
||||
mergeCandidates.push({
|
||||
id1: ti.id1,
|
||||
id2: ti.id2,
|
||||
|
@ -445,7 +446,7 @@ export function initGraphApp(angularModule, deps) {
|
|||
v2: ti.v2,
|
||||
overlap: ti.overlap,
|
||||
});
|
||||
}
|
||||
});
|
||||
$scope.detail = { mergeCandidates };
|
||||
};
|
||||
|
|
@ -9,34 +9,36 @@
|
|||
// They can stay even after NP cutover
|
||||
import angular from 'angular';
|
||||
import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
|
||||
|
||||
import '../../../../webpackShims/ace';
|
||||
// required for i18nIdDirective
|
||||
import 'angular-sanitize';
|
||||
// type imports
|
||||
import {
|
||||
AppMountContext,
|
||||
ChromeStart,
|
||||
LegacyCoreStart,
|
||||
CoreStart,
|
||||
PluginInitializerContext,
|
||||
SavedObjectsClientContract,
|
||||
ToastsStart,
|
||||
IUiSettingsClient,
|
||||
OverlayStart,
|
||||
} from 'kibana/public';
|
||||
import { configureAppAngularModule } from './legacy_imports';
|
||||
// @ts-ignore
|
||||
import { initGraphApp } from './app';
|
||||
import {
|
||||
Plugin as DataPlugin,
|
||||
IndexPatternsContract,
|
||||
} from '../../../../../src/plugins/data/public';
|
||||
import { LicensingPluginSetup } from '../../../../plugins/licensing/public';
|
||||
import { checkLicense } from '../../../../plugins/graph/common/check_license';
|
||||
import { NavigationPublicPluginStart as NavigationStart } from '../../../../../src/plugins/navigation/public';
|
||||
import { Plugin as DataPlugin, IndexPatternsContract } from '../../../../src/plugins/data/public';
|
||||
import { LicensingPluginSetup } from '../../licensing/public';
|
||||
import { checkLicense } from '../common/check_license';
|
||||
import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public';
|
||||
import { createSavedWorkspacesLoader } from './services/persistence/saved_workspace_loader';
|
||||
import { Storage } from '../../../../../src/plugins/kibana_utils/public';
|
||||
import { Storage } from '../../../../src/plugins/kibana_utils/public';
|
||||
import {
|
||||
addAppRedirectMessageToUrl,
|
||||
configureAppAngularModule,
|
||||
createTopNavDirective,
|
||||
createTopNavHelper,
|
||||
} from '../../../../../src/plugins/kibana_legacy/public';
|
||||
import { addAppRedirectMessageToUrl } from '../../../../../src/plugins/kibana_legacy/public';
|
||||
} from '../../../../src/plugins/kibana_legacy/public';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
/**
|
||||
* These are dependencies of the Graph app besides the base dependencies
|
||||
|
@ -45,6 +47,8 @@ import { addAppRedirectMessageToUrl } from '../../../../../src/plugins/kibana_le
|
|||
* itself changes
|
||||
*/
|
||||
export interface GraphDependencies {
|
||||
pluginInitializerContext: PluginInitializerContext;
|
||||
core: CoreStart;
|
||||
element: HTMLElement;
|
||||
appBasePath: string;
|
||||
capabilities: Record<string, boolean | Record<string, boolean>>;
|
||||
|
@ -55,7 +59,7 @@ export interface GraphDependencies {
|
|||
config: IUiSettingsClient;
|
||||
toastNotifications: ToastsStart;
|
||||
indexPatterns: IndexPatternsContract;
|
||||
npData: ReturnType<DataPlugin['start']>;
|
||||
data: ReturnType<DataPlugin['start']>;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
addBasePath: (url: string) => string;
|
||||
getBasePath: () => string;
|
||||
|
@ -67,7 +71,11 @@ export interface GraphDependencies {
|
|||
|
||||
export const renderApp = ({ appBasePath, element, ...deps }: GraphDependencies) => {
|
||||
const graphAngularModule = createLocalAngularModule(deps.navigation);
|
||||
configureAppAngularModule(graphAngularModule, deps.coreStart as LegacyCoreStart, true);
|
||||
configureAppAngularModule(
|
||||
graphAngularModule,
|
||||
{ core: deps.core, env: deps.pluginInitializerContext.env },
|
||||
true
|
||||
);
|
||||
|
||||
const licenseSubscription = deps.licensing.license$.subscribe(license => {
|
||||
const info = checkLicense(license);
|
||||
|
@ -81,7 +89,7 @@ export const renderApp = ({ appBasePath, element, ...deps }: GraphDependencies)
|
|||
|
||||
const savedWorkspaceLoader = createSavedWorkspacesLoader({
|
||||
chrome: deps.coreStart.chrome,
|
||||
indexPatterns: deps.npData.indexPatterns,
|
||||
indexPatterns: deps.data.indexPatterns,
|
||||
overlays: deps.coreStart.overlays,
|
||||
savedObjectsClient: deps.coreStart.savedObjects.client,
|
||||
basePath: deps.coreStart.http.basePath,
|
||||
|
@ -113,6 +121,7 @@ function mountGraphApp(appBasePath: string, element: HTMLElement) {
|
|||
// make angular-within-angular possible
|
||||
const $injector = angular.bootstrap(mountpoint, [moduleName]);
|
||||
element.appendChild(mountpoint);
|
||||
element.setAttribute('class', 'kbnLocalApplicationWrapper');
|
||||
return $injector;
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ import { GraphStore } from '../state_management';
|
|||
import { GuidancePanel } from './guidance_panel';
|
||||
import { GraphTitle } from './graph_title';
|
||||
|
||||
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
export interface GraphAppProps extends SearchBarProps {
|
||||
coreStart: CoreStart;
|
|
@ -29,7 +29,7 @@ import classNames from 'classnames';
|
|||
import { WorkspaceField } from '../../types';
|
||||
import { iconChoices } from '../../helpers/style_choices';
|
||||
import { LegacyIcon } from '../legacy_icon';
|
||||
import { FieldIcon } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
import { FieldIcon } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { UpdateableFieldProperties } from './field_manager';
|
||||
|
||||
import { isEqual } from '../helpers';
|
|
@ -9,7 +9,7 @@ import { EuiPopover, EuiSelectable, EuiBadge } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import classNames from 'classnames';
|
||||
import { WorkspaceField } from '../../types';
|
||||
import { FieldIcon } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
import { FieldIcon } from '../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
export interface FieldPickerProps {
|
||||
fieldMap: Record<string, WorkspaceField>;
|
|
@ -30,7 +30,7 @@ import {
|
|||
import { IndexPatternSavedObject } from '../../types';
|
||||
import { openSourceModal } from '../../services/source_modal';
|
||||
|
||||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
export interface GuidancePanelProps {
|
||||
onFillWorkspace: () => void;
|
|
@ -10,7 +10,7 @@ import React, { Fragment } from 'react';
|
|||
import { EuiEmptyPrompt, EuiLink, EuiButton } from '@elastic/eui';
|
||||
|
||||
import { CoreStart, ApplicationStart } from 'kibana/public';
|
||||
import { TableListView } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { TableListView } from '../../../../../src/plugins/kibana_react/public';
|
||||
import { GraphWorkspaceSavedObject } from '../types';
|
||||
|
||||
export interface ListingProps {
|
|
@ -7,10 +7,7 @@
|
|||
import React, { useState } from 'react';
|
||||
import { EuiFormRow, EuiTextArea, EuiCallOut, EuiSpacer, EuiSwitch } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
SavedObjectSaveModal,
|
||||
OnSaveProps,
|
||||
} from '../../../../../../src/plugins/saved_objects/public';
|
||||
import { SavedObjectSaveModal, OnSaveProps } from '../../../../../src/plugins/saved_objects/public';
|
||||
|
||||
import { GraphSavePolicy } from '../types/config';
|
||||
|
|
@ -9,9 +9,9 @@ import { SearchBar, OuterSearchBarProps } from './search_bar';
|
|||
import React, { ReactElement } from 'react';
|
||||
import { CoreStart } from 'src/core/public';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { IndexPattern, QueryStringInput } from '../../../../../../src/plugins/data/public';
|
||||
import { IndexPattern, QueryStringInput } from '../../../../../src/plugins/data/public';
|
||||
|
||||
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
|
||||
import { openSourceModal } from '../services/source_modal';
|
|
@ -18,14 +18,14 @@ import {
|
|||
IndexpatternDatasource,
|
||||
} from '../state_management';
|
||||
|
||||
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { useKibana } from '../../../../../src/plugins/kibana_react/public';
|
||||
import {
|
||||
IndexPattern,
|
||||
QueryStringInput,
|
||||
IDataPluginServices,
|
||||
Query,
|
||||
esKuery,
|
||||
} from '../../../../../../src/plugins/data/public';
|
||||
} from '../../../../../src/plugins/data/public';
|
||||
|
||||
export interface OuterSearchBarProps {
|
||||
isLoading: boolean;
|
|
@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import React from 'react';
|
||||
|
||||
import { CoreStart } from 'src/core/public';
|
||||
import { SavedObjectFinderUi } from '../../../../../../src/plugins/saved_objects/public';
|
||||
import { SavedObjectFinderUi } from '../../../../../src/plugins/saved_objects/public';
|
||||
import { IndexPatternSavedObject } from '../types';
|
||||
|
||||
export interface SourcePickerProps {
|
|
@ -1,6 +1,3 @@
|
|||
// Import the EUI global scope so we can use EUI constants
|
||||
@import 'src/legacy/ui/public/styles/_styling_constants';
|
||||
|
||||
/* Graph plugin styles */
|
||||
|
||||
// Prefix all styles with "gph" to avoid conflicts.
|
|
@ -10,5 +10,3 @@ import { ConfigSchema } from '../config';
|
|||
|
||||
export const plugin = (initializerContext: PluginInitializerContext<ConfigSchema>) =>
|
||||
new GraphPlugin(initializerContext);
|
||||
|
||||
export { GraphSetup } from './plugin';
|
||||
|
|
|
@ -6,8 +6,14 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { CoreSetup, CoreStart } from 'kibana/public';
|
||||
import { Plugin } from 'src/core/public';
|
||||
import { AppMountParameters, Plugin } from 'src/core/public';
|
||||
import { PluginInitializerContext } from 'kibana/public';
|
||||
|
||||
import { Storage } from '../../../../src/plugins/kibana_utils/public';
|
||||
import { initAngularBootstrap } from '../../../../src/plugins/kibana_legacy/public';
|
||||
import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public';
|
||||
import { DataPublicPluginStart } from '../../../../src/plugins/data/public';
|
||||
|
||||
import { toggleNavLink } from './services/toggle_nav_link';
|
||||
import { LicensingPluginSetup } from '../../licensing/public';
|
||||
import { checkLicense } from '../common/check_license';
|
||||
|
@ -15,6 +21,7 @@ import {
|
|||
FeatureCatalogueCategory,
|
||||
HomePublicPluginSetup,
|
||||
} from '../../../../src/plugins/home/public';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
import { ConfigSchema } from '../config';
|
||||
|
||||
export interface GraphPluginSetupDependencies {
|
||||
|
@ -22,12 +29,21 @@ export interface GraphPluginSetupDependencies {
|
|||
home?: HomePublicPluginSetup;
|
||||
}
|
||||
|
||||
export class GraphPlugin implements Plugin<{ config: Readonly<ConfigSchema> }, void> {
|
||||
export interface GraphPluginStartDependencies {
|
||||
navigation: NavigationStart;
|
||||
data: DataPublicPluginStart;
|
||||
}
|
||||
|
||||
export class GraphPlugin
|
||||
implements Plugin<void, void, GraphPluginSetupDependencies, GraphPluginStartDependencies> {
|
||||
private licensing: LicensingPluginSetup | null = null;
|
||||
|
||||
constructor(private initializerContext: PluginInitializerContext<ConfigSchema>) {}
|
||||
|
||||
setup(core: CoreSetup, { licensing, home }: GraphPluginSetupDependencies) {
|
||||
setup(
|
||||
core: CoreSetup<GraphPluginStartDependencies>,
|
||||
{ licensing, home }: GraphPluginSetupDependencies
|
||||
) {
|
||||
this.licensing = licensing;
|
||||
|
||||
if (home) {
|
||||
|
@ -44,15 +60,42 @@ export class GraphPlugin implements Plugin<{ config: Readonly<ConfigSchema> }, v
|
|||
});
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* The configuration is temporarily exposed to allow the legacy graph plugin to consume
|
||||
* the setting. Once the graph plugin is migrated completely, this will become an implementation
|
||||
* detail.
|
||||
* @deprecated
|
||||
*/
|
||||
config: this.initializerContext.config.get(),
|
||||
};
|
||||
const config = this.initializerContext.config.get();
|
||||
|
||||
initAngularBootstrap();
|
||||
core.application.register({
|
||||
id: 'graph',
|
||||
title: 'Graph',
|
||||
order: 9000,
|
||||
appRoute: '/app/graph',
|
||||
euiIconType: 'graphApp',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
mount: async (params: AppMountParameters) => {
|
||||
const [coreStart, pluginsStart] = await core.getStartServices();
|
||||
const { renderApp } = await import('./application');
|
||||
return renderApp({
|
||||
...params,
|
||||
pluginInitializerContext: this.initializerContext,
|
||||
licensing,
|
||||
core: coreStart,
|
||||
navigation: pluginsStart.navigation,
|
||||
data: pluginsStart.data,
|
||||
savedObjectsClient: coreStart.savedObjects.client,
|
||||
addBasePath: core.http.basePath.prepend,
|
||||
getBasePath: core.http.basePath.get,
|
||||
canEditDrillDownUrls: config.canEditDrillDownUrls,
|
||||
graphSavePolicy: config.savePolicy,
|
||||
storage: new Storage(window.localStorage),
|
||||
capabilities: coreStart.application.capabilities.graph,
|
||||
coreStart,
|
||||
chrome: coreStart.chrome,
|
||||
config: coreStart.uiSettings,
|
||||
toastNotifications: coreStart.notifications.toasts,
|
||||
indexPatterns: pluginsStart.data!.indexPatterns,
|
||||
overlays: coreStart.overlays,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
start(core: CoreStart) {
|
||||
|
@ -66,5 +109,3 @@ export class GraphPlugin implements Plugin<{ config: Readonly<ConfigSchema> }, v
|
|||
|
||||
stop() {}
|
||||
}
|
||||
|
||||
export type GraphSetup = ReturnType<GraphPlugin['setup']>;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { IndexPatternProvider } from '../types';
|
||||
import { IndexPattern } from '../../../../../../src/plugins/data/public';
|
||||
import { IndexPattern } from '../../../../../src/plugins/data/public';
|
||||
|
||||
export function createCachedIndexPatternProvider(
|
||||
indexPatternGetter: (id: string) => Promise<IndexPattern>
|
|
@ -8,7 +8,7 @@ import { GraphWorkspaceSavedObject, Workspace } from '../../types';
|
|||
import { savedWorkspaceToAppState } from './deserialize';
|
||||
import { createWorkspace } from '../../angular/graph_client_workspace';
|
||||
import { outlinkEncoders } from '../../helpers/outlink_encoders';
|
||||
import { IndexPattern } from '../../../../../../../src/plugins/data/public';
|
||||
import { IndexPattern } from '../../../../../../src/plugins/data/public';
|
||||
|
||||
describe('deserialize', () => {
|
||||
let savedWorkspace: GraphWorkspaceSavedObject;
|
|
@ -27,7 +27,7 @@ import {
|
|||
import {
|
||||
IndexPattern,
|
||||
indexPatterns as indexPatternsUtils,
|
||||
} from '../../../../../../../src/plugins/data/public';
|
||||
} from '../../../../../../src/plugins/data/public';
|
||||
|
||||
const defaultAdvancedSettings: AdvancedSettings = {
|
||||
useSignificance: true,
|
|
@ -9,7 +9,7 @@ import {
|
|||
SavedObject,
|
||||
createSavedObjectClass,
|
||||
SavedObjectKibanaServices,
|
||||
} from '../../../../../../../src/plugins/saved_objects/public';
|
||||
} from '../../../../../../src/plugins/saved_objects/public';
|
||||
|
||||
export interface SavedWorkspace extends SavedObject {
|
||||
wsState?: string;
|
|
@ -7,7 +7,7 @@
|
|||
import { IBasePath } from 'kibana/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { SavedObjectKibanaServices } from '../../../../../../../src/plugins/saved_objects/public';
|
||||
import { SavedObjectKibanaServices } from '../../../../../../src/plugins/saved_objects/public';
|
||||
import { createSavedWorkspaceClass } from './saved_workspace';
|
||||
|
||||
export function createSavedWorkspacesLoader(
|
|
@ -17,7 +17,7 @@ import {
|
|||
setDatasource,
|
||||
requestDatasource,
|
||||
} from './datasource';
|
||||
import { IndexPattern } from '../../../../../../src/plugins/data/public';
|
||||
import { IndexPattern } from '../../../../../src/plugins/data/public';
|
||||
|
||||
/**
|
||||
* Saga loading field information when the datasource is switched. This will overwrite current settings
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue