Skip to content

Commit

Permalink
Added ability to do profile requests again and render into UI (stylin…
Browse files Browse the repository at this point in the history
…g WiP)
  • Loading branch information
jloleysens committed Oct 23, 2019
1 parent 71df9e7 commit b571359
Show file tree
Hide file tree
Showing 15 changed files with 191 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface ContextValue {
http: HttpSetup;
notifications: NotificationsSetup;
licenseEnabled: boolean;
formatAngularHttpError: (message: string) => string;
formatAngularHttpError: (error: any) => string;
}

const AppContext = createContext<ContextValue>(null as any);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

export const searchResponse: any = [
{
id: ['L22w_FX2SbqlQYOP5QrYDg', '.apm-agent-configuration', '0'],
id: '[L22w_FX2SbqlQYOP5QrYDg] [.apm-agent-configuration] [0]',
searches: [
{
query: [
Expand Down Expand Up @@ -52,7 +52,7 @@ export const searchResponse: any = [
aggregations: [],
},
{
id: ['L22w_FX2SbqlQYOP5QrYDg', '.kibana_1', '0'],
id: '[L22w_FX2SbqlQYOP5QrYDg] [.kibana_1] [0]',
searches: [
{
query: [
Expand Down Expand Up @@ -129,7 +129,7 @@ export const searchResponse: any = [
aggregations: [],
},
{
id: ['L22w_FX2SbqlQYOP5QrYDg', '.kibana_task_manager_1', '0'],
id: '[L22w_FX2SbqlQYOP5QrYDg] [.kibana_task_manager_1] [0]',
searches: [
{
query: [
Expand Down Expand Up @@ -206,7 +206,7 @@ export const searchResponse: any = [
aggregations: [],
},
{
id: ['L22w_FX2SbqlQYOP5QrYDg', '.security-7', '0'],
id: '[L22w_FX2SbqlQYOP5QrYDg] [.security-7] [0]',
searches: [
{
query: [
Expand Down Expand Up @@ -256,7 +256,7 @@ export const searchResponse: any = [
aggregations: [],
},
{
id: ['L22w_FX2SbqlQYOP5QrYDg', 'kibana_sample_data_logs', '0'],
id: '[L22w_FX2SbqlQYOP5QrYDg] [kibana_sample_data_logs] [0]',
searches: [
{
query: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ export interface Props {
export const IndexDetails = ({ index, target }: Props) => {
const { time, name } = index;
return (
<EuiFlexGroup>
<EuiFlexGroup gutterSize="none">
{/* Time details group */}
<EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText className="prfDevTool__shardDetails--dim">
<EuiToolTip
position="bottom"
Expand All @@ -40,7 +40,7 @@ export const IndexDetails = ({ index, target }: Props) => {
</EuiText>
</EuiFlexItem>
{/* Index Title group */}
<EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText className="prfDevTool__shardDetails">
<h3>
<b>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,19 @@ export function mutateSearchTimesTree(shard: Shard) {

const initShards = (data: ShardSerialized[]) =>
produce<ShardSerialized[], Shard[]>(data, draft => {
return draft.map(s => ({
...s,
time: 0,
color: '',
relative: 0,
}));
return draft.map(s => {
const idMatch = s.id.match(/\[([^\]\[]*?)\]/g) || [];
const ids = idMatch.map(id => {
return id.replace('[', '').replace(']', '');
});
return {
...s,
id: ids,
time: 0,
color: '',
relative: 0,
};
});
});

export const calculateShardValues = (target: Targets) => (data: Shard[]) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ export const ProfileTree = ({ data, target, onHighlight }: Props) => {
return (
<HighlightContextProvider onHighlight={onHighlight}>
{sortedIndices.map(index => (
<EuiFlexGroup key={index.name} direction="column">
<EuiFlexItem>
<EuiFlexGroup gutterSize="none" key={index.name} direction="column">
<EuiFlexItem grow={false}>
<IndexDetails index={index} target={target} />
</EuiFlexItem>
<EuiSpacer />
<EuiFlexItem>
<EuiFlexItem grow={false}>
{index.shards.map(shard => (
<ShardDetails
key={shard.id[1]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const ShardDetails = ({ index, shard, operations }: Props) => {
const [shardVisibility, setShardVisibility] = useState<boolean>(false);

return (
<EuiFlexGroup direction="column">
<EuiFlexGroup gutterSize="none" direction="column">
<EuiFlexItem>
<EuiText className="prfDevTool__shardDetails--dim">
<EuiBadge style={{ '--prfDevToolProgressPercentage': shard.relative + '%' } as any}>
Expand All @@ -39,7 +39,9 @@ export const ShardDetails = ({ index, shard, operations }: Props) => {
{shard.id[2]}]
</EuiLink>
{shardVisibility
? operations.map(data => <ShardDetailTree index={index} shard={shard} data={data} />)
? operations.map(data => (
<ShardDetailTree key={shard.id[0]} index={index} shard={shard} data={data} />
))
: null}
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ export interface Props {
export const ShardDetailTree = ({ data, index, shard }: Props) => {
// Recursively render the tree structure
const renderOperations = (operation: Operation): JSX.Element => {
const parent = operation.parent;
const nextOperation = operation.treeRoot || operation;
const parent = nextOperation.parent;
const parentVisible = parent ? parent.visible : false;
return (
<>
<ShardDetailsTreeLeaf
shard={shard}
index={index}
parentVisible={parentVisible || operation.depth === 0}
operation={operation}
parentVisible={parentVisible || nextOperation.depth === 0}
operation={nextOperation}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ export const Main = () => {
[currentResponse]
);

const onProfileClick = () => {
setHighlightedDetails(null);
};

const onResponse = (resp: ShardSerialized[]) => {
setCurrentResponse(resp);
setActiveTab('searches');
};

return (
<>
<EuiPage className="prfDevTool__page">
Expand All @@ -59,7 +68,7 @@ export const Main = () => {
<EuiFlexGroup direction="row" className="prfDevTool__page__bodyGroup">
<EuiFlexItem>
<div className="prfDevTool__sense">
<ProfileQueryEditor onResponse={resp => setCurrentResponse(resp)} />
<ProfileQueryEditor onProfileClick={onProfileClick} onResponse={onResponse} />
</div>
</EuiFlexItem>
<EuiFlexItem grow={3}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,36 @@ import { ShardSerialized } from '../types';
import { useAppContext } from '../app_context';

interface Props {
onProfileClick: () => void;
onResponse: (response: ShardSerialized[]) => void;
}

export const ProfileQueryEditor = ({ onResponse }: Props) => {
const DEFAULT_INDEX_VALUE = '_all';

const INITIAL_EDITOR_VALUE = `{
"query":{
"match_all" : {}
}
}`;

export const ProfileQueryEditor = ({ onResponse, onProfileClick }: Props) => {
const editorValueGetter = useRef<() => string>(null as any);
const indexInputRef = useRef<HTMLInputElement>(null as any);
const typeInputRef = useRef<HTMLInputElement>(null as any);

const { licenseEnabled } = useAppContext();
const doProfile = useDoProfile();

const handleProfileClick = async () => {
// TODO: Finish adding request body
const result = await doProfile({});
onProfileClick();
const result = await doProfile({
query: editorValueGetter.current!(),
index: indexInputRef.current.value,
type: typeInputRef.current.value,
});
if (result === null) {
return;
}
onResponse(result);
};

Expand All @@ -34,15 +51,24 @@ export const ProfileQueryEditor = ({ onResponse }: Props) => {
<EuiFormRow
label={i18n.translate('xpack.searchProfiler.formIndexLabel', { defaultMessage: 'Index' })}
>
<EuiFieldText inputRef={ref => (indexInputRef.current = ref!)} />
<EuiFieldText
inputRef={ref => {
indexInputRef.current = ref!;
ref!.value = DEFAULT_INDEX_VALUE;
}}
/>
</EuiFormRow>
<EuiFormRow
label={i18n.translate('xpack.searchProfiler.formTypeLabel', { defaultMessage: 'Type' })}
>
<EuiFieldText inputRef={ref => (typeInputRef.current = ref!)} />
</EuiFormRow>
</EuiForm>
<Editor licenseEnabled={licenseEnabled} />
<Editor
valueGetterRef={editorValueGetter}
licenseEnabled={licenseEnabled}
initialValue={INITIAL_EDITOR_VALUE}
/>
<EuiButton onClick={() => handleProfileClick()}>
<EuiText>
{i18n.translate('xpack.searchProfiler.formProfileButtonLabel', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,32 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useRef, useEffect, useState } from 'react';
import React, { useRef, useEffect, useState, MutableRefObject } from 'react';
import { Editor as AceEditor } from 'brace';

import { initializeEditor } from './init_editor';
import { useUIAceKeyboardMode } from './use_ui_ace_keyboard_mode';

export interface Props {
licenseEnabled: boolean;
initialValue: string;
/**
* Hack to expose the editor instance's getValue(). This could be probably be better placed
* in React.Context.
*/
valueGetterRef: MutableRefObject<() => string | null>;
}

export const Editor = ({ licenseEnabled }: Props) => {
export const Editor = ({ licenseEnabled, initialValue, valueGetterRef }: Props) => {
const containerRef = useRef<HTMLDivElement>(null as any);
const editorInstanceRef = useRef<AceEditor>(null as any);
const [textArea, setTextArea] = useState<HTMLTextAreaElement | null>(null);
useUIAceKeyboardMode(textArea);
useEffect(() => {
const divEl = containerRef.current;
editorInstanceRef.current = initializeEditor({ el: divEl, licenseEnabled });
editorInstanceRef.current.setValue(initialValue, 1);
valueGetterRef.current = () => editorInstanceRef.current.getValue() as string;
setTextArea(containerRef.current!.querySelector('textarea'));
});
return <div ref={containerRef} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,57 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';

import { useAppContext } from '../app_context';
import { checkForParseErrors } from '../utils';
import { ShardSerialized } from '../types';

interface Args {
query: string;
index: string;
type: string;
}

export const useDoProfile = () => {
const { http, notifications, formatAngularHttpError } = useAppContext();
return async (requestBody: any) => {
const { http, notifications, formatAngularHttpError, licenseEnabled } = useAppContext();
return async ({ query, index, type }: Args): Promise<ShardSerialized[] | null> => {
if (!licenseEnabled) {
return null;
}
const { error, parsed } = checkForParseErrors(query);
if (error) {
notifications.toasts.addError(error, {
title: i18n.translate('xpack.searchProfiler.errorToastTitle', {
defaultMessage: 'JSON parse error',
}),
});
return null;
}
// Shortcut the network request if we have json with shards already...
if (parsed.profile && parsed.profile.shards) {
return parsed.profile.shards;
}

const payload: Record<string, any> = { query };

if (index == null || index === '') {
payload.index = '_all';
} else {
payload.index = index;
}

if (type != null && type !== '') {
payload.type = type;
}

try {
const resp = await http.post('../api/searchprofiler/profile', requestBody);
if (!resp.data.ok) {
const resp = await http.post('../api/searchprofiler/profile', {
body: JSON.stringify(payload),
headers: { 'Content-Type': 'application/json' },
});

if (!resp.ok) {
notifications.toasts.addDanger(resp.data.err.msg);
// TODO: Get this error feedback working again
// try {
Expand All @@ -23,11 +65,23 @@ export const useDoProfile = () => {
// } catch (e) {
// Best attempt, not a big deal if we can't highlight the line
// }
throw new Error(resp.data.err.msg);
return null;
}
return resp.data.resp.profile.shards;
return resp.resp.profile.shards;
} catch (e) {
notifications.toasts.addDanger(formatAngularHttpError(e));
try {
// Is this a known error type?
const errorString = formatAngularHttpError(e);
notifications.toasts.addError(e, { title: errorString });
} catch (_) {
// Otherwise just report the original error
notifications.toasts.addError(e, {
title: i18n.translate('xpack.searchProfiler.errorSomethingWentWrongTitle', {
defaultMessage: 'Something went wrong',
}),
});
}
return null;
}
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export interface Index {
}

export interface ShardSerialized {
id: string[];
id: string;
searches: Operation[];
aggregations: Operation[];
}
Expand Down
Loading

0 comments on commit b571359

Please sign in to comment.