Skip to content

Commit

Permalink
Resolver component w/ sample data (#53619)
Browse files Browse the repository at this point in the history
Resolver is a map. It shows processes that ran on a computer. The processes are drawn as nodes and lines connect processes with their parents.

Resolver is not yet implemented in Kibana. This PR adds a 'map' type UX. The user can click and drag to pan the map and zoom using trackpad pinching (or ctrl and mousewheel.)

There is no code providing actual data. Sample data is included. The sample data is used to draw a map. The fundamental info needed is:

process names
the parent of a process
With this info we can topologically lay out the processes. The sample data isn't yet in a realistic format. We'll be fixing that soon.

Related issue: elastic/endpoint-app-team#30
  • Loading branch information
Robert Austin authored Jan 14, 2020
1 parent c622a2f commit 387da98
Show file tree
Hide file tree
Showing 44 changed files with 4,865 additions and 50 deletions.
14 changes: 14 additions & 0 deletions x-pack/plugins/endpoint/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"author": "Elastic",
"name": "endpoint",
"version": "0.0.0",
"private": true,
"license": "Elastic-License",
"scripts": {},
"dependencies": {
"react-redux": "^7.1.0"
},
"devDependencies": {
"@types/react-redux": "^7.1.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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 { CameraAction } from './store/camera';
import { DataAction } from './store/data';

export type ResolverAction = CameraAction | DataAction;
35 changes: 18 additions & 17 deletions x-pack/plugins/endpoint/public/embeddables/resolver/embeddable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,32 @@
* you may not use this file except in compliance with the Elastic License.
*/

import {
EmbeddableInput,
IContainer,
Embeddable,
} from '../../../../../../src/plugins/embeddable/public';
import ReactDOM from 'react-dom';
import React from 'react';
import { AppRoot } from './view';
import { storeFactory } from './store';
import { Embeddable } from '../../../../../../src/plugins/embeddable/public';

export class ResolverEmbeddable extends Embeddable {
public readonly type = 'resolver';
constructor(initialInput: EmbeddableInput, parent?: IContainer) {
super(
// Input state is irrelevant to this embeddable, just pass it along.
initialInput,
// Initial output state - this embeddable does not do anything with output, so just
// pass along an empty object.
{},
// Optional parent component, this embeddable can optionally be rendered inside a container.
parent
);
}
private lastRenderTarget?: Element;

public render(node: HTMLElement) {
node.innerHTML = '<div data-test-subj="resolverEmbeddable">Welcome from Resolver</div>';
if (this.lastRenderTarget !== undefined) {
ReactDOM.unmountComponentAtNode(this.lastRenderTarget);
}
this.lastRenderTarget = node;
const { store } = storeFactory();
ReactDOM.render(<AppRoot store={store} />, node);
}

public reload(): void {
throw new Error('Method not implemented.');
}

public destroy(): void {
if (this.lastRenderTarget !== undefined) {
ReactDOM.unmountComponentAtNode(this.lastRenderTarget);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
*/

import { i18n } from '@kbn/i18n';
import { ResolverEmbeddable } from './';
import {
EmbeddableFactory,
EmbeddableInput,
IContainer,
EmbeddableInput,
} from '../../../../../../src/plugins/embeddable/public';
import { ResolverEmbeddable } from './embeddable';

export class ResolverEmbeddableFactory extends EmbeddableFactory {
public readonly type = 'resolver';
Expand All @@ -20,7 +20,7 @@ export class ResolverEmbeddableFactory extends EmbeddableFactory {
}

public async create(initialInput: EmbeddableInput, parent?: IContainer) {
return new ResolverEmbeddable(initialInput, parent);
return new ResolverEmbeddable(initialInput, {}, parent);
}

public getDisplayName() {
Expand Down
12 changes: 12 additions & 0 deletions x-pack/plugins/endpoint/public/embeddables/resolver/lib/math.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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.
*/

/**
* Return `value` unless it is less than `minimum`, in which case return `minimum` or unless it is greater than `maximum`, in which case return `maximum`.
*/
export function clamp(value: number, minimum: number, maximum: number) {
return Math.max(Math.min(value, maximum), minimum);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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 { multiply } from './matrix3';
describe('matrix3', () => {
it('can multiply two matrix3s', () => {
expect(multiply([1, 2, 3, 4, 5, 6, 7, 8, 9], [10, 11, 12, 13, 14, 15, 16, 17, 18])).toEqual([
84,
90,
96,
201,
216,
231,
318,
342,
366,
]);
});
});
56 changes: 56 additions & 0 deletions x-pack/plugins/endpoint/public/embeddables/resolver/lib/matrix3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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 { Matrix3 } from '../types';

/**
* Return a new matrix which is the product of the first and second matrix.
*/
export function multiply(
[a11, a12, a13, a21, a22, a23, a31, a32, a33]: Matrix3,
[b11, b12, b13, b21, b22, b23, b31, b32, b33]: Matrix3
): Matrix3 {
const s11 = a11 * b11 + a12 * b21 + a13 * b31;
const s12 = a11 * b12 + a12 * b22 + a13 * b32;
const s13 = a11 * b13 + a12 * b23 + a13 * b33;

const s21 = a21 * b11 + a22 * b21 + a23 * b31;
const s22 = a21 * b12 + a22 * b22 + a23 * b32;
const s23 = a21 * b13 + a22 * b23 + a23 * b33;

const s31 = a31 * b11 + a32 * b21 + a33 * b31;
const s32 = a31 * b12 + a32 * b22 + a33 * b32;
const s33 = a31 * b13 + a32 * b23 + a33 * b33;

// prettier-ignore
return [
s11, s12, s13,
s21, s22, s23,
s31, s32, s33,
];
}

/**
* Return a new matrix which is the sum of the two passed in.
*/
export function add(
[a11, a12, a13, a21, a22, a23, a31, a32, a33]: Matrix3,
[b11, b12, b13, b21, b22, b23, b31, b32, b33]: Matrix3
): Matrix3 {
return [
a11 + b11,
a12 + b12,
a13 + b13,

a21 + b21,
a22 + b22,
a23 + b23,

a31 + b31,
a32 + b32,
a33 + b33,
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { applyMatrix3 } from './vector2';
import { scalingTransformation } from './transformation';

describe('transforms', () => {
it('applying a scale matrix to a vector2 can invert the y value', () => {
expect(applyMatrix3([1, 2], scalingTransformation([1, -1]))).toEqual([1, -2]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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 { Matrix3, Vector2 } from '../types';

/**
* The inverse of `orthographicProjection`.
*/
export function inverseOrthographicProjection(
top: number,
right: number,
bottom: number,
left: number
): Matrix3 {
const m11 = (right - left) / 2;
const m13 = (right + left) / (right - left);

const m22 = (top - bottom) / 2;
const m23 = (top + bottom) / (top - bottom);

return [m11, 0, m13, 0, m22, m23, 0, 0, 0];
}

/**
* Adjust x, y to be bounded, in scale, of a clipping plane defined by top, right, bottom, left.
*
* See explanation:
* https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix
* https://en.wikipedia.org/wiki/Orthographic_projection
*/
export function orthographicProjection(
top: number,
right: number,
bottom: number,
left: number
): Matrix3 {
const m11 = 2 / (right - left); // adjust x scale to match ndc (-1, 1) bounds
const m13 = -((right + left) / (right - left));

const m22 = 2 / (top - bottom); // adjust y scale to match ndc (-1, 1) bounds
const m23 = -((top + bottom) / (top - bottom));

return [m11, 0, m13, 0, m22, m23, 0, 0, 0];
}

/**
* Returns a 2D transformation matrix that when applied to a vector will scale the vector by `x` and `y` in their respective axises.
* See https://en.wikipedia.org/wiki/Scaling_(geometry)#Matrix_representation
*/
export function scalingTransformation([x, y]: Vector2): Matrix3 {
// prettier-ignore
return [
x, 0, 0,
0, y, 0,
0, 0, 0
]
}

/**
* Returns a 2D transformation matrix that when applied to a vector will translate by `x` and `y` in their respective axises.
* See https://en.wikipedia.org/wiki/Translation_(geometry)#Matrix_representation
*/
export function translationTransformation([x, y]: Vector2): Matrix3 {
// prettier-ignore
return [
1, 0, x,
0, 1, y,
0, 0, 1
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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.
*/

/**
* Sequences a tree, yielding children returned by the `children` function. Sequencing is done in 'depth first preorder' fashion. See https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_(NLR)
*/
export function* depthFirstPreorder<T>(root: T, children: (parent: T) => T[]): Iterable<T> {
const nodesToVisit = [root];
while (nodesToVisit.length !== 0) {
const currentNode = nodesToVisit.shift();
if (currentNode !== undefined) {
nodesToVisit.unshift(...(children(currentNode) || []));
yield currentNode;
}
}
}

/**
* Sequences a tree, yielding children returned by the `children` function. Sequencing is done in 'level order' fashion.
*/
export function* levelOrder<T>(root: T, children: (parent: T) => T[]): Iterable<T> {
let level = [root];
while (level.length !== 0) {
let nextLevel = [];
for (const node of level) {
yield node;
nextLevel.push(...(children(node) || []));
}
level = nextLevel;
nextLevel = [];
}
}
52 changes: 52 additions & 0 deletions x-pack/plugins/endpoint/public/embeddables/resolver/lib/vector2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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 { Vector2, Matrix3 } from '../types';

/**
* Returns a vector which is the sum of `a` and `b`.
*/
export function add(a: Vector2, b: Vector2): Vector2 {
return [a[0] + b[0], a[1] + b[1]];
}

/**
* Returns a vector which is the difference of `a` and `b`.
*/
export function subtract(a: Vector2, b: Vector2): Vector2 {
return [a[0] - b[0], a[1] - b[1]];
}

/**
* Returns a vector which is the quotient of `a` and `b`.
*/
export function divide(a: Vector2, b: Vector2): Vector2 {
return [a[0] / b[0], a[1] / b[1]];
}

/**
* Returns a vector which is the result of applying a 2D transformation matrix to the provided vector.
*/
export function applyMatrix3([x, y]: Vector2, [m11, m12, m13, m21, m22, m23]: Matrix3): Vector2 {
return [x * m11 + y * m12 + m13, x * m21 + y * m22 + m23];
}

/**
* Returns the distance between two vectors
*/
export function distance(a: Vector2, b: Vector2) {
const [x1, y1] = a;
const [x2, y2] = b;
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}

/**
* Returns the angle between two vectors
*/
export function angle(a: Vector2, b: Vector2) {
const deltaX = b[0] - a[0];
const deltaY = b[1] - a[1];
return Math.atan2(deltaY, deltaX);
}
Loading

0 comments on commit 387da98

Please sign in to comment.