Skip to content

Commit

Permalink
Merge branch 'master' into fix/index_management_tests
Browse files Browse the repository at this point in the history
  • Loading branch information
elasticmachine authored Mar 21, 2020
2 parents 0679427 + 0390251 commit ab95222
Show file tree
Hide file tree
Showing 27 changed files with 402 additions and 182 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ function useCytoscape(options: cytoscape.CytoscapeOptions) {
return [ref, cy] as [React.MutableRefObject<any>, cytoscape.Core | undefined];
}

function rotatePoint(
{ x, y }: { x: number; y: number },
degreesRotated: number
) {
const radiansPerDegree = Math.PI / 180;
const θ = radiansPerDegree * degreesRotated;
const cosθ = Math.cos(θ);
const sinθ = Math.sin(θ);
return {
x: x * cosθ - y * sinθ,
y: x * sinθ + y * cosθ
};
}

function getLayoutOptions(
selectedRoots: string[],
height: number,
Expand All @@ -71,10 +85,11 @@ function getLayoutOptions(
animate: true,
animationEasing: animationOptions.easing,
animationDuration: animationOptions.duration,
// Rotate nodes from top -> bottom to display left -> right
// @ts-ignore
transform: (node: any, { x, y }: cytoscape.Position) => ({ x: y, y: -x }),
// swap width/height of boundingBox to compensation for the rotation
// Rotate nodes counter-clockwise to transform layout from top→bottom to left→right.
// The extra 5° achieves the effect of separating overlapping taxi-styled edges.
transform: (node: any, pos: cytoscape.Position) => rotatePoint(pos, -95),
// swap width/height of boundingBox to compensate for the rotation
boundingBox: { x1: 0, y1: 0, w: height, h: width }
};
}
Expand Down Expand Up @@ -109,20 +124,31 @@ export function Cytoscape({
// is required and can trigger rendering when changed.
const divStyle = { ...style, height };

const dataHandler = useCallback<cytoscape.EventHandler>(
event => {
const resetConnectedEdgeStyle = useCallback(
(node?: cytoscape.NodeSingular) => {
if (cy) {
cy.edges().removeClass('highlight');

if (serviceName) {
const focusedNode = cy.getElementById(serviceName);
focusedNode.connectedEdges().addClass('highlight');
if (node) {
node.connectedEdges().addClass('highlight');
}
}
},
[cy]
);

// Add the "primary" class to the node if its id matches the serviceName.
if (cy.nodes().length > 0 && serviceName) {
cy.nodes().removeClass('primary');
cy.getElementById(serviceName).addClass('primary');
const dataHandler = useCallback<cytoscape.EventHandler>(
event => {
if (cy) {
if (serviceName) {
resetConnectedEdgeStyle(cy.getElementById(serviceName));
// Add the "primary" class to the node if its id matches the serviceName.
if (cy.nodes().length > 0) {
cy.nodes().removeClass('primary');
cy.getElementById(serviceName).addClass('primary');
}
} else {
resetConnectedEdgeStyle();
}
if (event.cy.elements().length > 0) {
const selectedRoots = selectRoots(event.cy);
Expand All @@ -141,7 +167,7 @@ export function Cytoscape({
}
}
},
[cy, serviceName, height, width]
[cy, resetConnectedEdgeStyle, serviceName, height, width]
);

// Trigger a custom "data" event when data changes
Expand All @@ -162,12 +188,20 @@ export function Cytoscape({
event.target.removeClass('hover');
event.target.connectedEdges().removeClass('nodeHover');
};
const selectHandler: cytoscape.EventHandler = event => {
resetConnectedEdgeStyle(event.target);
};
const unselectHandler: cytoscape.EventHandler = event => {
resetConnectedEdgeStyle();
};

if (cy) {
cy.on('data', dataHandler);
cy.ready(dataHandler);
cy.on('mouseover', 'edge, node', mouseoverHandler);
cy.on('mouseout', 'edge, node', mouseoutHandler);
cy.on('select', 'node', selectHandler);
cy.on('unselect', 'node', unselectHandler);
}

return () => {
Expand All @@ -181,7 +215,7 @@ export function Cytoscape({
cy.removeListener('mouseout', 'edge, node', mouseoutHandler);
}
};
}, [cy, dataHandler, serviceName]);
}, [cy, dataHandler, resetConnectedEdgeStyle, serviceName]);

return (
<CytoscapeContext.Provider value={cy}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,18 @@ const style: cytoscape.Stylesheet[] = [
{
selector: 'edge.nodeHover',
style: {
width: 4,
width: 2,
// @ts-ignore
'z-index': zIndexEdgeHover
'z-index': zIndexEdgeHover,
'line-color': theme.euiColorDarkShade,
'source-arrow-color': theme.euiColorDarkShade,
'target-arrow-color': theme.euiColorDarkShade
}
},
{
selector: 'node.hover',
style: {
'border-width': 4
'border-width': 2
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export function KueryBar() {
const disabled = /\/service-map$/.test(location.pathname);
const disabledPlaceholder = i18n.translate(
'xpack.apm.kueryBar.disabledPlaceholder',
{ defaultMessage: 'Search is not available for service maps' }
{ defaultMessage: 'Search is not available for service map' }
);

async function onChange(inputValue: string, selectionStart: number) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ export const importRulesRoute = (router: IRouter, config: LegacyServices['config
references,
note,
version,
lists,
anomalyThreshold,
machineLearningJobId,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,32 @@ describe('patch_rules_bulk', () => {
]);
});

test('allows ML Params to be patched', async () => {
const request = requestMock.create({
method: 'patch',
path: `${DETECTION_ENGINE_RULES_URL}/bulk_update`,
body: [
{
rule_id: 'my-rule-id',
anomaly_threshold: 4,
machine_learning_job_id: 'some_job_id',
},
],
});
await server.inject(request, context);

expect(clients.alertsClient.update).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
params: expect.objectContaining({
anomalyThreshold: 4,
machineLearningJobId: 'some_job_id',
}),
}),
})
);
});

test('returns 404 if alertClient is not available on the route', async () => {
context.alerting!.getAlertsClient = jest.fn();
const response = await server.inject(getPatchBulkRequest(), context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export const patchRulesBulkRoute = (router: IRouter) => {
references,
note,
version,
anomaly_threshold: anomalyThreshold,
machine_learning_job_id: machineLearningJobId,
} = payloadRule;
const idOrRuleIdOrUnknown = id ?? ruleId ?? '(unknown id)';
try {
Expand Down Expand Up @@ -111,6 +113,8 @@ export const patchRulesBulkRoute = (router: IRouter) => {
references,
note,
version,
anomalyThreshold,
machineLearningJobId,
});
if (rule != null) {
const ruleStatuses = await savedObjectsClient.find<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,30 @@ describe('patch_rules', () => {
status_code: 500,
});
});

test('allows ML Params to be patched', async () => {
const request = requestMock.create({
method: 'patch',
path: DETECTION_ENGINE_RULES_URL,
body: {
rule_id: 'my-rule-id',
anomaly_threshold: 4,
machine_learning_job_id: 'some_job_id',
},
});
await server.inject(request, context);

expect(clients.alertsClient.update).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
params: expect.objectContaining({
anomalyThreshold: 4,
machineLearningJobId: 'some_job_id',
}),
}),
})
);
});
});

describe('request validation', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export const patchRulesRoute = (router: IRouter) => {
references,
note,
version,
anomaly_threshold: anomalyThreshold,
machine_learning_job_id: machineLearningJobId,
} = request.body;
const siemResponse = buildSiemResponse(response);

Expand Down Expand Up @@ -108,6 +110,8 @@ export const patchRulesRoute = (router: IRouter) => {
references,
note,
version,
anomalyThreshold,
machineLearningJobId,
});
if (rule != null) {
const ruleStatuses = await savedObjectsClient.find<
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"rule_id": "machine-learning",
"anomaly_threshold": 10
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "Query with a machine learning job",
"description": "Query with a machine learning job",
"rule_id": "machine-learning",
"risk_score": 1,
"severity": "high",
"type": "machine_learning",
"machine_learning_job_id": "linux_anomalous_network_activity_ecs",
"anomaly_threshold": 50
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "Query with a machine learning job",
"description": "Query with a machine learning job",
"rule_id": "machine-learning",
"risk_score": 1,
"severity": "high",
"type": "machine_learning",
"machine_learning_job_id": "linux_anomalous_network_activity_ecs",
"anomaly_threshold": 100
}
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ Do **not** add the agent as a dependency to your application.',
),
commands: `java -javaagent:/path/to/elastic-apm-agent-<version>.jar \\
-Delastic.apm.service_name=my-application \\
-Delastic.apm.server_url=${apmServerUrl || 'http://localhost:8200'} \\
-Delastic.apm.server_urls=${apmServerUrl || 'http://localhost:8200'} \\
-Delastic.apm.secret_token=${secretToken} \\
-Delastic.apm.application_packages=org.example \\
-jar my-application.jar`.split('\n'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ type MiddlewareFactory<S = ResolverState> = (
) => (
api: MiddlewareAPI<Dispatch<ResolverAction>, S>
) => (next: Dispatch<ResolverAction>) => (action: ResolverAction) => unknown;
interface Lifecycle {
lifecycle: ResolverEvent[];
}
type ChildResponse = [Lifecycle];

function flattenEvents(events: ChildResponse): ResolverEvent[] {
return events
.map((child: Lifecycle) => child.lifecycle)
.reduce(
(accumulator: ResolverEvent[], value: ResolverEvent[]) => accumulator.concat(value),
[]
);
}

export const resolverMiddlewareFactory: MiddlewareFactory = context => {
return api => next => async (action: ResolverAction) => {
Expand Down Expand Up @@ -47,7 +60,7 @@ export const resolverMiddlewareFactory: MiddlewareFactory = context => {
query: { legacyEndpointID },
}),
]);
childEvents = children.length > 0 ? children.map((child: any) => child.lifecycle) : [];
childEvents = children.length > 0 ? flattenEvents(children) : [];
} else {
const uniquePid = action.payload.selectedEvent.process.entity_id;
const ppid = action.payload.selectedEvent.process.parent?.entity_id;
Expand All @@ -67,7 +80,7 @@ export const resolverMiddlewareFactory: MiddlewareFactory = context => {
getAncestors(ppid),
]);
}
childEvents = children.length > 0 ? children.map((child: any) => child.lifecycle) : [];
childEvents = children.length > 0 ? flattenEvents(children) : [];
response = [...lifecycle, ...childEvents, ...relatedEvents, ...ancestors];
api.dispatch({
type: 'serverReturnedResolverData',
Expand Down
Loading

0 comments on commit ab95222

Please sign in to comment.