Skip to content

Commit

Permalink
[APM] pull out components and util functions into own files and added…
Browse files Browse the repository at this point in the history
… tests
  • Loading branch information
ogupte committed Dec 4, 2018
1 parent 03b37b5 commit 5012db3
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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 { EuiLink } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
import { Stackframe } from '../../../../typings/APMDoc';
import { px, units } from '../../../style/variables';
import { CodePreview } from '../../shared/CodePreview';
// @ts-ignore
import { Ellipsis } from '../../shared/Icons';
import { FrameHeading } from './FrameHeading';
import { hasSourceLines } from './stacktraceUtils';

const LibraryFrameToggle = styled.div`
margin: 0 0 ${px(units.plus)} 0;
user-select: none;
`;

interface Props {
visible?: boolean;
stackframes: Stackframe[];
codeLanguage?: string;
onClick: () => void;
}

export const LibraryFrames: React.SFC<Props> = ({
visible,
stackframes,
codeLanguage,
onClick
}) => {
return (
<div>
<LibraryFrameToggle>
<EuiLink onClick={onClick}>
<Ellipsis horizontal={visible} style={{ marginRight: units.half }} />{' '}
{stackframes.length} library frames
</EuiLink>
</LibraryFrameToggle>

<div>
{visible &&
stackframes.map(
(stackframe, i) =>
hasSourceLines(stackframe) ? (
<CodePreview
key={i}
stackframe={stackframe}
isLibraryFrame
codeLanguage={codeLanguage}
/>
) : (
<FrameHeading key={i} stackframe={stackframe} isLibraryFrame />
)
)}
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 { Stackframe } from '../../../../../typings/APMDoc';
import { getCollapsedLibraryFrames, hasSourceLines } from '../stacktraceUtils';
import stacktracesMock from './stacktraces.json';

const stackframeMockWithSource = stacktracesMock[0];
const stackframeMockWithoutSource = stacktracesMock[1];

describe('stactraceUtils', () => {
describe('getCollapsedLibraryFrames', () => {
it('should collapse the library frames into a set of grouped, nested stackframes', () => {
const result = getCollapsedLibraryFrames(stacktracesMock as Stackframe[]);
expect(result.length).toBe(3);
expect(result[0].libraryFrame).toBe(false);
expect(result[1].libraryFrame).toBe(true);
expect(result[1].stackframes).toHaveLength(2); // two nested stackframes
expect(result[2].libraryFrame).toBe(false);
});
});

describe('hasSourceLines', () => {
it('should return true given a stackframe with a source context', () => {
const result = hasSourceLines(stackframeMockWithSource as Stackframe);
expect(result).toBe(true);
});
it('should return false given a stackframe with no source context', () => {
const result = hasSourceLines(stackframeMockWithoutSource as Stackframe);
expect(result).toBe(false);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
[
{
"function": "<anonymous>",
"libraryFrame": false,
"excludeFromGrouping": false,
"context": {
"pre": ["", "app.get('/log-error', function (req, res) {"],
"post": [
" if (err) {",
" res.status(500).send('could not capture error: ' + err.message)"
]
},
"line": {
"number": 17,
"context": " apm.captureError(new Error('foo'), function (err) {"
},
"filename": "server/coffee.js",
"absPath": "/app/server/coffee.js"
},
{
"function": "get",
"libraryFrame": true,
"excludeFromGrouping": false,
"context": {},
"line": {
"number": 123,
"context": ""
},
"filename": "express/get.js",
"absPath": "/node_modules/express/get.js"
},
{
"function": "use",
"libraryFrame": true,
"excludeFromGrouping": false,
"context": {
"pre": ["", "app.use('/log-error', function (req, res) {"],
"post": [
" if (err) {",
" res.status(500).send('could not capture error: ' + err.message)"
]
},
"line": {
"number": 234,
"context": " apm.captureError(new Error('foo'), function (err) {"
},
"filename": "express/use.js",
"absPath": "/node_modules/express/use.js"
},
{
"function": "handleCoffee",
"libraryFrame": false,
"excludeFromGrouping": false,
"context": {
"pre": ["", ""],
"post": [
" reply(getCoffee(req.id));"
]
},
"line": {
"number": 45,
"context": " handleCoffee(req => {"
},
"filename": "server/handleCoffee.js",
"absPath": "/app/server/handleCoffee.js"
}
]
99 changes: 9 additions & 90 deletions x-pack/plugins/apm/public/components/shared/Stacktrace/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,20 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { EuiLink, EuiTitle } from '@elastic/eui';
import { get, isEmpty } from 'lodash';
import { EuiTitle } from '@elastic/eui';
import { isEmpty } from 'lodash';
import React, { PureComponent } from 'react';
import styled from 'styled-components';
import { Stackframe } from '../../../../typings/APMDoc';
import { px, units } from '../../../style/variables';
import { CodePreview } from '../../shared/CodePreview';
import { EmptyMessage } from '../../shared/EmptyMessage';
// @ts-ignore
import { Ellipsis } from '../../shared/Icons';
import { FrameHeading } from './FrameHeading';

export interface StackframeCollapsed extends Stackframe {
libraryFrame?: boolean;
stackframes?: Stackframe[];
}

const LibraryFrameToggle = styled.div`
margin: 0 0 ${px(units.plus)} 0;
user-select: none;
`;

function getCollapsedLibraryFrames(
stackframes: Stackframe[]
): StackframeCollapsed[] {
return stackframes.reduce((acc: any, stackframe: StackframeCollapsed) => {
if (!stackframe.libraryFrame) {
return [...acc, stackframe];
}

// current stackframe is library frame
const prevItem: StackframeCollapsed = acc[acc.length - 1];
if (!get(prevItem, 'libraryFrame')) {
return [...acc, { libraryFrame: true, stackframes: [stackframe] }];
}

return [
...acc.slice(0, -1),
{
...prevItem,
stackframes: prevItem.stackframes
? [...prevItem.stackframes, stackframe]
: [stackframe]
}
];
}, []);
}

function hasSourceLines(stackframe: Stackframe) {
return (
!isEmpty(stackframe.context) || !isEmpty(get(stackframe, 'line.context'))
);
}
import { LibraryFrames } from './LibraryFrames';
import {
getCollapsedLibraryFrames,
hasSourceLines,
StackframeCollapsed
} from './stacktraceUtils';

interface Props {
stackframes?: StackframeCollapsed[];
Expand Down Expand Up @@ -125,7 +86,7 @@ export class Stacktrace extends PureComponent<Props, State> {
}

return (
<Libraryframes
<LibraryFrames
key={i}
visible={libraryframes[i]}
stackframes={item.stackframes || []}
Expand All @@ -138,45 +99,3 @@ export class Stacktrace extends PureComponent<Props, State> {
);
}
}

interface LibraryframesProps {
visible?: boolean;
stackframes: Stackframe[];
codeLanguage?: string;
onClick: () => void;
}

const Libraryframes: React.SFC<LibraryframesProps> = ({
visible,
stackframes,
codeLanguage,
onClick
}) => {
return (
<div>
<LibraryFrameToggle>
<EuiLink onClick={onClick}>
<Ellipsis horizontal={visible} style={{ marginRight: units.half }} />{' '}
{stackframes.length} library frames
</EuiLink>
</LibraryFrameToggle>

<div>
{visible &&
stackframes.map(
(stackframe, i) =>
hasSourceLines(stackframe) ? (
<CodePreview
key={i}
stackframe={stackframe}
isLibraryFrame
codeLanguage={codeLanguage}
/>
) : (
<FrameHeading key={i} stackframe={stackframe} isLibraryFrame />
)
)}
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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 { get, isEmpty } from 'lodash';
import { Stackframe } from '../../../../typings/APMDoc';

export interface StackframeCollapsed extends Stackframe {
libraryFrame?: boolean;
stackframes?: Stackframe[];
}

export function getCollapsedLibraryFrames(
stackframes: Stackframe[]
): StackframeCollapsed[] {
return stackframes.reduce((acc: any, stackframe: StackframeCollapsed) => {
if (!stackframe.libraryFrame) {
return [...acc, stackframe];
}

// current stackframe is library frame
const prevItem: StackframeCollapsed = acc[acc.length - 1];
if (!get(prevItem, 'libraryFrame')) {
return [...acc, { libraryFrame: true, stackframes: [stackframe] }];
}

return [
...acc.slice(0, -1),
{
...prevItem,
stackframes: prevItem.stackframes
? [...prevItem.stackframes, stackframe]
: [stackframe]
}
];
}, []);
}

export function hasSourceLines(stackframe: Stackframe) {
return (
!isEmpty(stackframe.context) || !isEmpty(get(stackframe, 'line.context'))
);
}

0 comments on commit 5012db3

Please sign in to comment.