[APM] Service map download in debug mode (#69350)
* [APM] Service map download in debug mode Add a download button when debug mode is enabled that downloads JSON of the map. Add an upload button to the Storybook.
This commit is contained in:
parent
338ae00384
commit
af5874d9d7
|
@ -56,18 +56,64 @@ function doZoom(cy: cytoscape.Core | undefined, increment: number) {
|
|||
}
|
||||
}
|
||||
|
||||
function useDebugDownloadUrl(cy?: cytoscape.Core) {
|
||||
const [downloadUrl, setDownloadUrl] = useState<string | undefined>(undefined);
|
||||
const debug = sessionStorage.getItem('apm_debug') === 'true';
|
||||
|
||||
// Handle elements changes to update the download URL
|
||||
useEffect(() => {
|
||||
const elementsHandler: cytoscape.EventHandler = (event) => {
|
||||
// @ts-ignore The `true` argument to `cy.json` is to flatten the elements
|
||||
// (instead of having them broken into nodes/edges.) DefinitelyTyped has
|
||||
// this wrong.
|
||||
const elementsJson = event.cy.json(true)?.elements.map((element) => ({
|
||||
data: element.data,
|
||||
}));
|
||||
setDownloadUrl(
|
||||
elementsJson.length > 0 && debug
|
||||
? `data:application/json;charset=utf-8,${encodeURIComponent(
|
||||
JSON.stringify({ elements: elementsJson }, null, ' ')
|
||||
)}`
|
||||
: undefined
|
||||
);
|
||||
};
|
||||
|
||||
if (cy) {
|
||||
cy.on('add remove', elementsHandler);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (cy) {
|
||||
cy.off('add remove', undefined, elementsHandler);
|
||||
}
|
||||
};
|
||||
}, [cy, debug]);
|
||||
|
||||
return downloadUrl;
|
||||
}
|
||||
|
||||
export function Controls() {
|
||||
const cy = useContext(CytoscapeContext);
|
||||
const { urlParams } = useUrlParams();
|
||||
const currentSearch = urlParams.kuery ?? '';
|
||||
const [zoom, setZoom] = useState((cy && cy.zoom()) || 1);
|
||||
const downloadUrl = useDebugDownloadUrl(cy);
|
||||
|
||||
// Handle zoom events
|
||||
useEffect(() => {
|
||||
const zoomHandler: cytoscape.EventHandler = (event) => {
|
||||
setZoom(event.cy.zoom());
|
||||
};
|
||||
|
||||
if (cy) {
|
||||
cy.on('zoom', (event) => {
|
||||
setZoom(event.cy.zoom());
|
||||
});
|
||||
cy.on('zoom', zoomHandler);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (cy) {
|
||||
cy.off('zoom', undefined, zoomHandler);
|
||||
}
|
||||
};
|
||||
}, [cy]);
|
||||
|
||||
function center() {
|
||||
|
@ -102,6 +148,9 @@ export function Controls() {
|
|||
const centerLabel = i18n.translate('xpack.apm.serviceMap.center', {
|
||||
defaultMessage: 'Center',
|
||||
});
|
||||
const downloadLabel = i18n.translate('xpack.apm.serviceMap.download', {
|
||||
defaultMessage: 'Download',
|
||||
});
|
||||
const viewFullMapLabel = i18n.translate('xpack.apm.serviceMap.viewFullMap', {
|
||||
defaultMessage: 'View full service map',
|
||||
});
|
||||
|
@ -165,6 +214,22 @@ export function Controls() {
|
|||
</EuiToolTip>
|
||||
</Panel>
|
||||
)}
|
||||
{downloadUrl && (
|
||||
<Panel hasShadow={true} paddingSize="none">
|
||||
<EuiToolTip
|
||||
anchorClassName="eui-displayInline"
|
||||
content={downloadLabel}
|
||||
>
|
||||
<Button
|
||||
aria-label={downloadLabel}
|
||||
color="text"
|
||||
download="service-map.json"
|
||||
href={downloadUrl}
|
||||
iconType="download"
|
||||
/>
|
||||
</EuiToolTip>
|
||||
</Panel>
|
||||
)}
|
||||
</ControlsContainer>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,9 +9,12 @@ import {
|
|||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiButton,
|
||||
EuiForm,
|
||||
EuiFieldNumber,
|
||||
EuiToolTip,
|
||||
EuiCodeEditor,
|
||||
EuiSpacer,
|
||||
EuiFilePicker,
|
||||
} from '@elastic/eui';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
@ -99,12 +102,14 @@ storiesOf(STORYBOOK_PATH, module).add(
|
|||
const [json, setJson] = useState<string>(
|
||||
getSessionJson() || JSON.stringify(exampleResponseTodo, null, 2)
|
||||
);
|
||||
const [error, setError] = useState<string | undefined>();
|
||||
|
||||
const [elements, setElements] = useState<any[]>([]);
|
||||
useEffect(() => {
|
||||
try {
|
||||
setElements(JSON.parse(json).elements);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} catch (e) {
|
||||
setError(e.message);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
@ -112,34 +117,76 @@ storiesOf(STORYBOOK_PATH, module).add(
|
|||
return (
|
||||
<div>
|
||||
<Cytoscape elements={elements} height={600} width={1340} />
|
||||
<EuiForm isInvalid={error !== undefined} error={error}>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiCodeEditor
|
||||
mode="json"
|
||||
theme="github"
|
||||
width="100%"
|
||||
value={json}
|
||||
setOptions={{ fontSize: '12px' }}
|
||||
onChange={(value) => {
|
||||
setJson(value);
|
||||
}}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup direction="column">
|
||||
<EuiFilePicker
|
||||
display={'large'}
|
||||
fullWidth={true}
|
||||
style={{ height: '100%' }}
|
||||
initialPromptText="Upload a JSON file"
|
||||
onChange={(event) => {
|
||||
const item = event?.item(0);
|
||||
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
onClick={() => {
|
||||
setElements(JSON.parse(json).elements);
|
||||
setSessionJson(json);
|
||||
}}
|
||||
>
|
||||
Render JSON
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiCodeEditor
|
||||
mode="json"
|
||||
theme="github"
|
||||
width="100%"
|
||||
value={json}
|
||||
setOptions={{ fontSize: '12px' }}
|
||||
onChange={(value) => {
|
||||
setJson(value);
|
||||
}}
|
||||
/>
|
||||
if (item) {
|
||||
const f = new FileReader();
|
||||
f.onload = (onloadEvent) => {
|
||||
const result = onloadEvent?.target?.result;
|
||||
if (typeof result === 'string') {
|
||||
setJson(result);
|
||||
}
|
||||
};
|
||||
f.readAsText(item);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiButton
|
||||
onClick={() => {
|
||||
try {
|
||||
setElements(JSON.parse(json).elements);
|
||||
setSessionJson(json);
|
||||
setError(undefined);
|
||||
} catch (e) {
|
||||
setError(e.message);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Render JSON
|
||||
</EuiButton>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiForm>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{
|
||||
info: { propTables: false, source: false },
|
||||
info: {
|
||||
propTables: false,
|
||||
source: false,
|
||||
text: `
|
||||
Enter JSON map data into the text box or upload a file and click "Render JSON" to see the results. You can enable a download button on the service map by putting
|
||||
|
||||
\`\`\`
|
||||
sessionStorage.setItem('apm_debug', 'true')
|
||||
\`\`\`
|
||||
|
||||
into the JavaScript console and reloading the page.`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue