diff --git a/zipkin-lens/.eslintrc.js b/zipkin-lens/.eslintrc.js index 7290a970a7e..a3665302924 100644 --- a/zipkin-lens/.eslintrc.js +++ b/zipkin-lens/.eslintrc.js @@ -12,7 +12,12 @@ * the License. */ module.exports = { - extends: 'airbnb-typescript', + extends: [ + 'airbnb-typescript', + 'plugin:prettier/recommended', + 'prettier/@typescript-eslint', + 'prettier/react', + ], rules: { 'import/prefer-default-export': 'off', 'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx', '.tsx'] }], @@ -23,7 +28,6 @@ module.exports = { 'react/no-unescaped-entities': 'off', 'max-classes-per-file': 'off', 'no-console': 0, - 'no-mixed-operators': ['error', { allowSamePrecedence: true }], 'no-underscore-dangle': ['off'], 'no-continue': ['off'], 'prefer-destructuring': ['error', { diff --git a/zipkin-lens/.prettierrc.js b/zipkin-lens/.prettierrc.js new file mode 100644 index 00000000000..3dd3318d972 --- /dev/null +++ b/zipkin-lens/.prettierrc.js @@ -0,0 +1,18 @@ +/* + * Copyright 2015-2020 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +module.exports = { + arrowParens: 'always', + singleQuote: true, + trailingComma: 'all', +}; diff --git a/zipkin-lens/package-lock.json b/zipkin-lens/package-lock.json index 8b34f304835..90913ed3a56 100644 --- a/zipkin-lens/package-lock.json +++ b/zipkin-lens/package-lock.json @@ -5763,6 +5763,14 @@ } } }, + "eslint-config-prettier": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz", + "integrity": "sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg==", + "requires": { + "get-stdin": "^6.0.0" + } + }, "eslint-config-react-app": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.2.0.tgz", @@ -6083,6 +6091,14 @@ } } }, + "eslint-plugin-prettier": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz", + "integrity": "sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==", + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-plugin-react": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.18.0.tgz", @@ -6476,6 +6492,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==" + }, "fast-glob": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", @@ -6955,6 +6976,11 @@ "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==" + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -12024,6 +12050,19 @@ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==" + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-bytes": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz", diff --git a/zipkin-lens/package.json b/zipkin-lens/package.json index 58e3c0602a2..650d42ae757 100755 --- a/zipkin-lens/package.json +++ b/zipkin-lens/package.json @@ -41,9 +41,11 @@ "enzyme": "^3.7.0", "enzyme-adapter-react-16": "^1.7.0", "eslint-config-airbnb-typescript": "^7.0.0", + "eslint-config-prettier": "^6.10.0", "eslint-plugin-import": "^2.16.0", "eslint-plugin-jest": "^22.3.0", "eslint-plugin-jsx-a11y": "^6.2.1", + "eslint-plugin-prettier": "^3.1.2", "eslint-plugin-react": "^7.12.4", "eslint-plugin-react-hooks": "^1.6.1", "fetch-mock": "^7.3.0", @@ -52,6 +54,7 @@ "moment": "^2.24.0", "mvy": "0.2.1", "node-fetch": "^2.3.0", + "prettier": "^1.19.1", "prop-types": "^15.6.2", "query-string": "^6.1.0", "rc-slider": "^8.6.9", diff --git a/zipkin-lens/src/actions/autocomplete-values-action.js b/zipkin-lens/src/actions/autocomplete-values-action.js index f67061cfa42..f31b2b28f67 100644 --- a/zipkin-lens/src/actions/autocomplete-values-action.js +++ b/zipkin-lens/src/actions/autocomplete-values-action.js @@ -29,7 +29,9 @@ export const fetchAutocompleteValuesFailure = () => ({ type: types.FETCH_AUTOCOMPLETE_VALUES_FAILURE, }); -export const fetchAutocompleteValues = (autocompleteKey) => async (dispatch) => { +export const fetchAutocompleteValues = (autocompleteKey) => async ( + dispatch, +) => { dispatch(fetchAutocompleteValuesRequest()); try { const query = queryString.stringify({ key: autocompleteKey }); diff --git a/zipkin-lens/src/actions/autocomplete-values-action.test.js b/zipkin-lens/src/actions/autocomplete-values-action.test.js index d34a09cb15b..f95105a7b5c 100644 --- a/zipkin-lens/src/actions/autocomplete-values-action.test.js +++ b/zipkin-lens/src/actions/autocomplete-values-action.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -44,8 +44,10 @@ describe('autocomplete-values async actions', () => { ]; const store = mockStore(); - return store.dispatch(actions.fetchAutocompleteValues('environment')).then(() => { - expect(store.getActions()).toEqual(expectedActions); - }); + return store + .dispatch(actions.fetchAutocompleteValues('environment')) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + }); }); }); diff --git a/zipkin-lens/src/actions/global-search-action.test.js b/zipkin-lens/src/actions/global-search-action.test.js index cfa31a38d0a..a9665f12bb1 100644 --- a/zipkin-lens/src/actions/global-search-action.test.js +++ b/zipkin-lens/src/actions/global-search-action.test.js @@ -26,7 +26,9 @@ describe('global search actions', () => { type: types.GLOBAL_SEARCH_SET_LOOKBACK_CONDITION, lookbackCondition, }; - expect(actions.setLookbackCondition(lookbackCondition)).toEqual(expectedAction); + expect(actions.setLookbackCondition(lookbackCondition)).toEqual( + expectedAction, + ); }); it('should create an action to set the limit condition', () => { @@ -67,7 +69,9 @@ describe('global search actions', () => { index, conditionKey, }; - expect(actions.changeConditionKey(index, conditionKey)).toEqual(expectedAction); + expect(actions.changeConditionKey(index, conditionKey)).toEqual( + expectedAction, + ); }); it('should create an action to change the value of conditions', () => { @@ -78,6 +82,8 @@ describe('global search actions', () => { index, conditionValue, }; - expect(actions.changeConditionValue(index, conditionValue)).toEqual(expectedAction); + expect(actions.changeConditionValue(index, conditionValue)).toEqual( + expectedAction, + ); }); }); diff --git a/zipkin-lens/src/actions/trace-action.js b/zipkin-lens/src/actions/trace-action.js index 2315557e9cc..30f0931ae8c 100644 --- a/zipkin-lens/src/actions/trace-action.js +++ b/zipkin-lens/src/actions/trace-action.js @@ -31,17 +31,19 @@ export const loadTraceFailure = () => ({ type: types.TRACE_LOAD_FAILURE, }); -const calculateCorrectedTrace = async (trace) => treeCorrectedForClockSkew(trace); +const calculateCorrectedTrace = async (trace) => + treeCorrectedForClockSkew(trace); -const calculateDetailedTraceSummary = async (correctedTrace) => buildDetailedTraceSummary( - correctedTrace, -); +const calculateDetailedTraceSummary = async (correctedTrace) => + buildDetailedTraceSummary(correctedTrace); export const loadTrace = (traceId, correctedTraceMap) => async (dispatch) => { dispatch(loadTraceRequest()); if (correctedTraceMap[traceId]) { - const detailedTraceSummary = await calculateDetailedTraceSummary(correctedTraceMap[traceId]); + const detailedTraceSummary = await calculateDetailedTraceSummary( + correctedTraceMap[traceId], + ); dispatch(loadTraceSuccess(detailedTraceSummary)); } else { try { @@ -52,7 +54,9 @@ export const loadTrace = (traceId, correctedTraceMap) => async (dispatch) => { } const trace = await res.json(); const correctedTrace = await calculateCorrectedTrace(trace); - const detailedTraceSummary = await calculateDetailedTraceSummary(correctedTrace); + const detailedTraceSummary = await calculateDetailedTraceSummary( + correctedTrace, + ); dispatch(loadTraceSuccess(detailedTraceSummary)); } catch (err) { diff --git a/zipkin-lens/src/actions/trace-action.test.js b/zipkin-lens/src/actions/trace-action.test.js index 11f1270a7b4..329a646ff17 100644 --- a/zipkin-lens/src/actions/trace-action.test.js +++ b/zipkin-lens/src/actions/trace-action.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -74,9 +74,11 @@ describe('trace async actions', () => { ]; const store = mockStore({}); - return store.dispatch(actions.loadTrace('d050e0d52326cf81', {})).then(() => { - expect(store.getActions()).toEqual(expectedActions); - }); + return store + .dispatch(actions.loadTrace('d050e0d52326cf81', {})) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + }); }); it('create TRACE_LOAD_SUCCESS when calculating summary has been done', () => { @@ -91,13 +93,17 @@ describe('trace async actions', () => { }, { type: types.TRACE_LOAD_SUCCESS, - traceSummary: buildDetailedTraceSummary(correctedTraceMap.d050e0d52326cf81), + traceSummary: buildDetailedTraceSummary( + correctedTraceMap.d050e0d52326cf81, + ), }, ]; const store = mockStore({}); - return store.dispatch(actions.loadTrace('d050e0d52326cf81', correctedTraceMap)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - }); + return store + .dispatch(actions.loadTrace('d050e0d52326cf81', correctedTraceMap)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + }); }); }); diff --git a/zipkin-lens/src/actions/trace-viewer.test.js b/zipkin-lens/src/actions/trace-viewer.test.js index 3a6df538aba..243ae62b08c 100644 --- a/zipkin-lens/src/actions/trace-viewer.test.js +++ b/zipkin-lens/src/actions/trace-viewer.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -29,8 +29,8 @@ describe('trace viewer actions', () => { type: types.TRACE_VIEWER__LOAD_TRACE_FAILURE, message: 'This is an error message', }; - expect( - actions.loadTraceFailure('This is an error message'), - ).toEqual(expectedAction); + expect(actions.loadTraceFailure('This is an error message')).toEqual( + expectedAction, + ); }); }); diff --git a/zipkin-lens/src/actions/traces-action.js b/zipkin-lens/src/actions/traces-action.js index 63bac5ade16..65aff886e21 100644 --- a/zipkin-lens/src/actions/traces-action.js +++ b/zipkin-lens/src/actions/traces-action.js @@ -55,7 +55,10 @@ const calculateTraceSummaries = async (traces, serviceName) => { correctedTraceMap[traceId] = trace; }); - const traceSummaries = buildTraceSummaries(serviceName, correctedTraces.map(buildTraceSummary)); + const traceSummaries = buildTraceSummaries( + serviceName, + correctedTraces.map(buildTraceSummary), + ); return { traceSummaries, @@ -75,12 +78,14 @@ export const loadTraces = (params) => async (dispatch) => { } const traces = await res.json(); - const { - traceSummaries, - correctedTraceMap, - } = await calculateTraceSummaries(traces, query.serviceName); + const { traceSummaries, correctedTraceMap } = await calculateTraceSummaries( + traces, + query.serviceName, + ); - dispatch(loadTracesSuccess(traces, traceSummaries, correctedTraceMap, params)); + dispatch( + loadTracesSuccess(traces, traceSummaries, correctedTraceMap, params), + ); } catch (err) { dispatch(loadTracesFailure()); } diff --git a/zipkin-lens/src/actions/traces-action.test.js b/zipkin-lens/src/actions/traces-action.test.js index 3b9a85344a1..0352f4461b0 100644 --- a/zipkin-lens/src/actions/traces-action.test.js +++ b/zipkin-lens/src/actions/traces-action.test.js @@ -18,7 +18,11 @@ import fetchMock from 'fetch-mock'; import * as actions from './traces-action'; import * as types from '../constants/action-types'; import * as api from '../constants/api'; -import { traceSummary as buildTraceSummary, traceSummaries as buildTraceSummaries, treeCorrectedForClockSkew } from '../zipkin'; +import { + traceSummary as buildTraceSummary, + traceSummaries as buildTraceSummaries, + treeCorrectedForClockSkew, +} from '../zipkin'; const middlewares = [thunk]; const mockStore = configureMockStore(middlewares); @@ -65,7 +69,10 @@ describe('traces async actions', () => { const [{ traceId }] = rawTraces[index]; correctedTraceMap[traceId] = trace; }); - const traceSummaries = buildTraceSummaries(null, correctedTraces.map(buildTraceSummary)); + const traceSummaries = buildTraceSummaries( + null, + correctedTraces.map(buildTraceSummary), + ); const expectedActions = [ { @@ -84,12 +91,16 @@ describe('traces async actions', () => { ]; const store = mockStore({}); - return store.dispatch(actions.loadTraces({ - serviceName: 'serviceA', - spanName: 'span1', - })).then(() => { - expect(store.getActions()).toEqual(expectedActions); - }); + return store + .dispatch( + actions.loadTraces({ + serviceName: 'serviceA', + spanName: 'span1', + }), + ) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + }); }); it('clearTraces dispatches action', () => { diff --git a/zipkin-lens/src/colors/index.js b/zipkin-lens/src/colors/index.js index dd2ffb3c0f2..e4ee25bd166 100644 --- a/zipkin-lens/src/colors/index.js +++ b/zipkin-lens/src/colors/index.js @@ -47,13 +47,15 @@ export const allColors = [ colors.blueGrey, ]; -export const allColorThemes = allColors.map((color) => createMuiTheme({ - palette: { - primary: { - main: color[500], +export const allColorThemes = allColors.map((color) => + createMuiTheme({ + palette: { + primary: { + main: color[500], + }, }, - }, -})); + }), +); /* eslint no-bitwise: ["error", { "allow": ["<<", "|="] }] */ const generateHash = (str) => { @@ -61,7 +63,7 @@ const generateHash = (str) => { if (str.length === 0) return hash; for (let i = 0; i < str.length; i += 1) { const c = str.charCodeAt(i); - hash = ((hash << 5) - hash) + c; + hash = (hash << 5) - hash + c; hash |= 0; // Convert to 32bit integer } return Math.abs(hash); // Only positive number. @@ -72,9 +74,8 @@ export const selectServiceTheme = (serviceName) => { return allColorThemes[hash % allColors.length]; }; -export const selectServiceColor = (serviceName) => selectServiceTheme( - serviceName, -).palette.primary.dark; +export const selectServiceColor = (serviceName) => + selectServiceTheme(serviceName).palette.primary.dark; export const selectColorByErrorType = (errorType) => { switch (errorType) { diff --git a/zipkin-lens/src/components/App/App.jsx b/zipkin-lens/src/components/App/App.jsx index dea8ad06b40..cdd373d8d36 100644 --- a/zipkin-lens/src/components/App/App.jsx +++ b/zipkin-lens/src/components/App/App.jsx @@ -54,33 +54,27 @@ const App = () => { - { - (config) => ( - - - - - - - - - - - - ) - } + {(config) => ( + + + + + + + + + + + + )} diff --git a/zipkin-lens/src/components/App/LanguageSelector.jsx b/zipkin-lens/src/components/App/LanguageSelector.jsx index 33002d4d145..97f18705239 100644 --- a/zipkin-lens/src/components/App/LanguageSelector.jsx +++ b/zipkin-lens/src/components/App/LanguageSelector.jsx @@ -59,14 +59,17 @@ const LanguageSelector = () => { const currentLocale = i18n.locale; - const onLanguageClick = useCallback((e) => { - const { locale } = e.currentTarget.dataset; - if (locale === currentLocale) { - return; - } - setLocale(locale); - window.location.reload(); - }, [currentLocale]); + const onLanguageClick = useCallback( + (e) => { + const { locale } = e.currentTarget.dataset; + if (locale === currentLocale) { + return; + } + setLocale(locale); + window.location.reload(); + }, + [currentLocale], + ); return ( <> diff --git a/zipkin-lens/src/components/App/LanguageSelector.test.js b/zipkin-lens/src/components/App/LanguageSelector.test.js index 3c21b5ad1a1..25f79b284e6 100644 --- a/zipkin-lens/src/components/App/LanguageSelector.test.js +++ b/zipkin-lens/src/components/App/LanguageSelector.test.js @@ -43,7 +43,9 @@ describe('', () => { fireEvent.click(changeLanguageButton); - const languageList = await waitForElement(() => queryByTestId('language-list')); + const languageList = await waitForElement(() => + queryByTestId('language-list'), + ); expect(changeLanguageButton).toBeInTheDocument(); expect(languageList).toBeInTheDocument(); diff --git a/zipkin-lens/src/components/App/Layout.jsx b/zipkin-lens/src/components/App/Layout.jsx index 64dbe4e4057..673d8b734bf 100644 --- a/zipkin-lens/src/components/App/Layout.jsx +++ b/zipkin-lens/src/components/App/Layout.jsx @@ -16,7 +16,10 @@ import { useLingui } from '@lingui/react'; import PropTypes from 'prop-types'; import React from 'react'; import { - faQuestionCircle, faSearch, faProjectDiagram, faHome, + faQuestionCircle, + faSearch, + faProjectDiagram, + faHome, } from '@fortawesome/free-solid-svg-icons'; import Box from '@material-ui/core/Box'; import Drawer from '@material-ui/core/Drawer'; @@ -81,21 +84,46 @@ const Layout = ({ children }) => { return ( - + - {i18n._(t`Zipkin`)} + {i18n._(t`Zipkin`)} - - + + {config.supportUrl && ( - + )} - + @@ -106,7 +134,6 @@ const Layout = ({ children }) => { ); }; - Layout.propTypes = propTypes; export default Layout; diff --git a/zipkin-lens/src/components/App/SidebarMenu.jsx b/zipkin-lens/src/components/App/SidebarMenu.jsx index 377ccb1bc29..bbbd3766c6c 100644 --- a/zipkin-lens/src/components/App/SidebarMenu.jsx +++ b/zipkin-lens/src/components/App/SidebarMenu.jsx @@ -43,45 +43,39 @@ const useStyles = makeStyles((theme) => ({ }, })); -export const SidebarMenuImpl = React.forwardRef(({ - title, - path, - icon, - ...others -}, ref) => { - const classes = useStyles(); - const location = useLocation(); +export const SidebarMenuImpl = React.forwardRef( + ({ title, path, icon, ...others }, ref) => { + const classes = useStyles(); + const location = useLocation(); - const listItemProps = {}; - if (path.startsWith('https://') || path.startsWith('http://')) { - listItemProps.component = 'a'; - listItemProps.href = path; - listItemProps.target = '_blank'; - listItemProps.rel = 'no-opener'; - } else { - listItemProps.component = Link; - listItemProps.to = path; - } + const listItemProps = {}; + if (path.startsWith('https://') || path.startsWith('http://')) { + listItemProps.component = 'a'; + listItemProps.href = path; + listItemProps.target = '_blank'; + listItemProps.rel = 'no-opener'; + } else { + listItemProps.component = Link; + listItemProps.to = path; + } - return ( - - - - - - ); -}); + return ( + + + + + + ); + }, +); SidebarMenuImpl.propTypes = propTypes; diff --git a/zipkin-lens/src/components/App/SidebarMenu.test.jsx b/zipkin-lens/src/components/App/SidebarMenu.test.jsx index 3e9c5839158..7238e38fade 100644 --- a/zipkin-lens/src/components/App/SidebarMenu.test.jsx +++ b/zipkin-lens/src/components/App/SidebarMenu.test.jsx @@ -21,7 +21,12 @@ import SidebarMenu from './SidebarMenu'; describe('', () => { it('renders relative link to be non-external', () => { const { getByTestId } = render( - , + , ); const link = getByTestId('sidebar-menu-link'); expect(link.href).toEqual('http://localhost/traces'); @@ -30,7 +35,12 @@ describe('', () => { it('renders absolute link to be external', () => { const { getByTestId } = render( - , + , ); const link = getByTestId('sidebar-menu-link'); expect(link.href).toEqual('https://github.com/openzipkin'); diff --git a/zipkin-lens/src/components/Common/ServiceBadge.jsx b/zipkin-lens/src/components/Common/ServiceBadge.jsx index d9f4f778863..f612170e65e 100644 --- a/zipkin-lens/src/components/Common/ServiceBadge.jsx +++ b/zipkin-lens/src/components/Common/ServiceBadge.jsx @@ -63,40 +63,35 @@ const defaultProps = { onDelete: null, }; -const ServiceBadgeImpl = ({ - serviceName, - count, - onClick, - onDelete, -}) => { +const ServiceBadgeImpl = ({ serviceName, count, onClick, onDelete }) => { const classes = useStyles(); - const label = useMemo( - () => `${serviceName}${count ? ` (${count})` : ''}`, - [count, serviceName], - ); + const label = useMemo(() => `${serviceName}${count ? ` (${count})` : ''}`, [ + count, + serviceName, + ]); return ( {label} - { - onDelete ? ( - - - - ) : null - } + {onDelete ? ( + + + + ) : null} ); diff --git a/zipkin-lens/src/components/Common/ServiceBadge.test.js b/zipkin-lens/src/components/Common/ServiceBadge.test.js index c402269885a..f226df740cd 100644 --- a/zipkin-lens/src/components/Common/ServiceBadge.test.js +++ b/zipkin-lens/src/components/Common/ServiceBadge.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -19,17 +19,13 @@ import ServiceBadge from './ServiceBadge'; describe('', () => { describe('should render a label correctly', () => { it('only serviceName', () => { - const wrapper = mount( - , - ); + const wrapper = mount(); const item = wrapper.find('[data-test="badge"]').first(); expect(item.text()).toBe('serviceA'); }); it('with count', () => { - const wrapper = mount( - , - ); + const wrapper = mount(); const item = wrapper.find('[data-test="badge"]').first(); expect(item.text()).toBe('serviceA (8)'); }); @@ -37,7 +33,11 @@ describe('', () => { it('should render delete button when onDelete is set', () => { const wrapper = mount( - {}} onDelete={() => {}} />, + {}} + onDelete={() => {}} + />, ); const items = wrapper.find('[data-test="delete-button"]'); expect(items.hostNodes().length).toBe(1); diff --git a/zipkin-lens/src/components/Common/TraceIdSearchInput.jsx b/zipkin-lens/src/components/Common/TraceIdSearchInput.jsx index 37dc4e63e3d..e48d47b6b5f 100644 --- a/zipkin-lens/src/components/Common/TraceIdSearchInput.jsx +++ b/zipkin-lens/src/components/Common/TraceIdSearchInput.jsx @@ -42,13 +42,16 @@ export const TraceIdSearchInputImpl = ({ history }) => { setTraceId(event.target.value); }, []); - const handleKeyDown = useCallback((event) => { - if (event.key === 'Enter') { - history.push({ - pathname: `/traces/${traceId}`, - }); - } - }, [history, traceId]); + const handleKeyDown = useCallback( + (event) => { + if (event.key === 'Enter') { + history.push({ + pathname: `/traces/${traceId}`, + }); + } + }, + [history, traceId], + ); return ( diff --git a/zipkin-lens/src/components/Common/TraceIdSearchInput.test.js b/zipkin-lens/src/components/Common/TraceIdSearchInput.test.js index 02f17aa4a42..e903e0780eb 100644 --- a/zipkin-lens/src/components/Common/TraceIdSearchInput.test.js +++ b/zipkin-lens/src/components/Common/TraceIdSearchInput.test.js @@ -25,7 +25,9 @@ describe('', () => { }); it('should render Tooltip when hovered', async () => { - const { findByText, getByTestId } = render(); + const { findByText, getByTestId } = render( + , + ); fireEvent.mouseEnter(getByTestId('search-input-text')); const tooltipText = await findByText('Search by Trace ID'); @@ -33,12 +35,16 @@ describe('', () => { }); it('should render TextField', () => { - const { getByTestId } = render(); + const { getByTestId } = render( + , + ); expect(getByTestId('search-input-text')).toBeInTheDocument(); }); it('should call push when Enter is pushed', () => { - const { getByTestId } = render(); + const { getByTestId } = render( + , + ); const input = getByTestId('search-input-text'); fireEvent.keyDown(input, { key: 'Enter' }); diff --git a/zipkin-lens/src/components/Common/TraceJsonUploader.jsx b/zipkin-lens/src/components/Common/TraceJsonUploader.jsx index ff391f46a82..415609d17ec 100644 --- a/zipkin-lens/src/components/Common/TraceJsonUploader.jsx +++ b/zipkin-lens/src/components/Common/TraceJsonUploader.jsx @@ -23,7 +23,6 @@ import { makeStyles } from '@material-ui/styles'; import Button from '@material-ui/core/Button'; import Tooltip from '@material-ui/core/Tooltip'; - import { ensureV2TraceData } from '../../util/trace'; import { loadTrace, loadTraceFailure } from '../../actions/trace-viewer-action'; @@ -53,44 +52,49 @@ const TraceJsonUploader = ({ history }) => { } }, []); - const handleFileChange = useCallback((event) => { - const fileReader = new FileReader(); - - const goToTraceViewerPage = () => { - history.push({ pathname: '/traceViewer' }); - }; - - fileReader.onload = () => { - const { result } = fileReader; - - let rawTraceData; - try { - rawTraceData = JSON.parse(result); - } catch (error) { - dispatch(loadTraceFailure(i18n._(t`This file does not contain JSON`))); + const handleFileChange = useCallback( + (event) => { + const fileReader = new FileReader(); + + const goToTraceViewerPage = () => { + history.push({ pathname: '/traceViewer' }); + }; + + fileReader.onload = () => { + const { result } = fileReader; + + let rawTraceData; + try { + rawTraceData = JSON.parse(result); + } catch (error) { + dispatch( + loadTraceFailure(i18n._(t`This file does not contain JSON`)), + ); + goToTraceViewerPage(); + return; + } + + try { + ensureV2TraceData(rawTraceData); + dispatch(loadTrace(rawTraceData)); + } catch (error) { + dispatch(loadTraceFailure(i18n._(t`Only V2 format is supported`))); + } goToTraceViewerPage(); - return; - } + }; - try { - ensureV2TraceData(rawTraceData); - dispatch(loadTrace(rawTraceData)); - } catch (error) { - dispatch(loadTraceFailure(i18n._(t`Only V2 format is supported`))); - } - goToTraceViewerPage(); - }; - - fileReader.onabort = () => { - dispatch(loadTraceFailure(i18n._(t`Failed to load this file`))); - goToTraceViewerPage(); - }; + fileReader.onabort = () => { + dispatch(loadTraceFailure(i18n._(t`Failed to load this file`))); + goToTraceViewerPage(); + }; - fileReader.onerror = fileReader.onabort; + fileReader.onerror = fileReader.onabort; - const [file] = event.target.files; - fileReader.readAsText(file); - }, [dispatch, history, i18n]); + const [file] = event.target.files; + fileReader.readAsText(file); + }, + [dispatch, history, i18n], + ); return ( <> @@ -101,7 +105,11 @@ const TraceJsonUploader = ({ history }) => { onChange={handleFileChange} /> - diff --git a/zipkin-lens/src/components/DependenciesPage/DependenciesGraph.jsx b/zipkin-lens/src/components/DependenciesPage/DependenciesGraph.jsx index dd66f4fe2ab..4f6f3fa9162 100644 --- a/zipkin-lens/src/components/DependenciesPage/DependenciesGraph.jsx +++ b/zipkin-lens/src/components/DependenciesPage/DependenciesGraph.jsx @@ -12,11 +12,7 @@ * the License. */ import PropTypes from 'prop-types'; -import React, { - useCallback, - useMemo, - useReducer, -} from 'react'; +import React, { useCallback, useMemo, useReducer } from 'react'; import ReactSelect from 'react-select'; import Box from '@material-ui/core/Box'; import { makeStyles } from '@material-ui/styles'; @@ -41,8 +37,10 @@ const filterNodes = (object, value) => { if (object.name === value) { return true; } - return object.incomingConnections.find((conn) => conn.source.name === value) - || object.outgoingConnections.find((conn) => conn.target.name === value); + return ( + object.incomingConnections.find((conn) => conn.source.name === value) || + object.outgoingConnections.find((conn) => conn.target.name === value) + ); }; const useStyles = makeStyles({ @@ -97,91 +95,101 @@ const defaultProps = { selectedNodeName: '', }; -const DependenciesGraph = React.memo(({ - selectedNodeName, - onNodeClick, - edges, - nodes, - updated, -}) => { - const classes = useStyles(); - const [filter, selectFilter] = useReducer((_, selected) => (selected ? selected.value : ''), ''); +const DependenciesGraph = React.memo( + ({ selectedNodeName, onNodeClick, edges, nodes, updated }) => { + const classes = useStyles(); + const [filter, selectFilter] = useReducer( + (_, selected) => (selected ? selected.value : ''), + '', + ); - const handleObjectHighlight = useCallback((highlightedObject) => { - if (!highlightedObject) { - onNodeClick(null); - return; - } - if (highlightedObject.type === 'node' && highlightedObject.getName() !== selectedNodeName) { - onNodeClick(highlightedObject.getName()); - } - }, [onNodeClick, selectedNodeName]); + const handleObjectHighlight = useCallback( + (highlightedObject) => { + if (!highlightedObject) { + onNodeClick(null); + return; + } + if ( + highlightedObject.type === 'node' && + highlightedObject.getName() !== selectedNodeName + ) { + onNodeClick(highlightedObject.getName()); + } + }, + [onNodeClick, selectedNodeName], + ); - const maxVolume = useMemo(() => { - if (edges.length > 0) { - return edges.map((edge) => edge.metrics.normal + edge.metrics.danger) - .reduce((a, b) => Math.max(a, b)); - } - return 0; - }, [edges]); + const maxVolume = useMemo(() => { + if (edges.length > 0) { + return edges + .map((edge) => edge.metrics.normal + edge.metrics.danger) + .reduce((a, b) => Math.max(a, b)); + } + return 0; + }, [edges]); - const filterOptions = useMemo(() => nodes.map((node) => ({ - value: node.name, - label: node.name, - })), [nodes]); + const filterOptions = useMemo( + () => + nodes.map((node) => ({ + value: node.name, + label: node.name, + })), + [nodes], + ); - return ( - - - - + + + + - - ); -}); + ); + }, +); DependenciesGraph.propTypes = propTypes; DependenciesGraph.defaultProps = defaultProps; diff --git a/zipkin-lens/src/components/DependenciesPage/DependenciesPage.jsx b/zipkin-lens/src/components/DependenciesPage/DependenciesPage.jsx index c5a831bf299..c39d6ec1caa 100644 --- a/zipkin-lens/src/components/DependenciesPage/DependenciesPage.jsx +++ b/zipkin-lens/src/components/DependenciesPage/DependenciesPage.jsx @@ -12,12 +12,7 @@ * the License. */ import PropTypes from 'prop-types'; -import React, { - useMemo, - useCallback, - useState, - useEffect, -} from 'react'; +import React, { useMemo, useCallback, useState, useEffect } from 'react'; import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; import { AutoSizer } from 'react-virtualized'; @@ -90,91 +85,104 @@ const propTypes = { history: PropTypes.shape({ push: PropTypes.func.isRequired }).isRequired, }; -export const DependenciesPageImpl = React.memo(({ - isLoading, - dependencies, - fetchDependencies, - clearDependencies, - location, - history, -}) => { - const classes = useStyles(); - const graph = useMemo(() => new Graph(dependencies), [dependencies]); - const isGraphExists = graph.allNodes().length !== 0; - const [nodeName, setNodeName] = useState(null); - const [timeRange, setTimeRange] = useState({ - startTime: moment().subtract(1, 'days'), - endTime: moment(), - }); - const targetEdges = useMemo( - () => (nodeName ? graph.getTargetEdges(nodeName) : []), - [nodeName, graph], - ); - const sourceEdges = useMemo( - () => (nodeName ? graph.getSourceEdges(nodeName) : []), - [nodeName, graph], - ); - - const handleStartTimeChange = useCallback((startTime) => { - setTimeRange({ ...timeRange, startTime }); - }, [timeRange]); - - const handleEndTimeChange = useCallback((endTime) => { - setTimeRange({ ...timeRange, endTime }); - }, [timeRange]); - - const handleFindButtonClick = useCallback(() => { - const startTs = timeRange.startTime.valueOf(); - const endTs = timeRange.endTime.valueOf(); - const lookback = endTs - startTs; - fetchDependencies({ lookback, endTs }); - history.push({ - pathname: '/dependency', - search: buildQueryParameters({ startTs, endTs }), +export const DependenciesPageImpl = React.memo( + ({ + isLoading, + dependencies, + fetchDependencies, + clearDependencies, + location, + history, + }) => { + const classes = useStyles(); + const graph = useMemo(() => new Graph(dependencies), [dependencies]); + const isGraphExists = graph.allNodes().length !== 0; + const [nodeName, setNodeName] = useState(null); + const [timeRange, setTimeRange] = useState({ + startTime: moment().subtract(1, 'days'), + endTime: moment(), }); - }, [fetchDependencies, timeRange, history]); + const targetEdges = useMemo( + () => (nodeName ? graph.getTargetEdges(nodeName) : []), + [nodeName, graph], + ); + const sourceEdges = useMemo( + () => (nodeName ? graph.getSourceEdges(nodeName) : []), + [nodeName, graph], + ); - const handleNodeClick = useCallback((newNodeName) => { - setNodeName(newNodeName); - }, []); + const handleStartTimeChange = useCallback( + (startTime) => { + setTimeRange({ ...timeRange, startTime }); + }, + [timeRange], + ); - useEffect(() => { - const queryParams = new URLSearchParams(location.search); + const handleEndTimeChange = useCallback( + (endTime) => { + setTimeRange({ ...timeRange, endTime }); + }, + [timeRange], + ); - const startTs = queryParams.get('startTs'); - const endTs = queryParams.get('endTs'); - if (startTs && endTs) { - setTimeRange({ - startTime: moment(parseInt(startTs, 10)), - endTime: moment(parseInt(endTs, 10)), - }); + const handleFindButtonClick = useCallback(() => { + const startTs = timeRange.startTime.valueOf(); + const endTs = timeRange.endTime.valueOf(); const lookback = endTs - startTs; fetchDependencies({ lookback, endTs }); - } - return clearDependencies; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - let content; - if (isLoading) { - content = ( - - - - ); - } else if (!isGraphExists) { - content = ( - - - - ); - } else { - content = ( - - - - { - ({ width, height }) => ( + history.push({ + pathname: '/dependency', + search: buildQueryParameters({ startTs, endTs }), + }); + }, [fetchDependencies, timeRange, history]); + + const handleNodeClick = useCallback((newNodeName) => { + setNodeName(newNodeName); + }, []); + + useEffect(() => { + const queryParams = new URLSearchParams(location.search); + + const startTs = queryParams.get('startTs'); + const endTs = queryParams.get('endTs'); + if (startTs && endTs) { + setTimeRange({ + startTime: moment(parseInt(startTs, 10)), + endTime: moment(parseInt(endTs, 10)), + }); + const lookback = endTs - startTs; + fetchDependencies({ lookback, endTs }); + } + return clearDependencies; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + let content; + if (isLoading) { + content = ( + + + + ); + } else if (!isGraphExists) { + content = ( + + + + ); + } else { + content = ( + + + + {({ width, height }) => ( - ) - } - - - { - nodeName ? ( - + )} + + + {nodeName ? ( + - { - ({ width, height }) => ( - - - - ) - } + {({ width, height }) => ( + + + + )} - ) : null - } - + ) : null} + + ); + } + + return ( + <> + + {content} + ); - } - - return ( - <> - - {content} - - ); -}); + }, +); DependenciesPageImpl.propTypes = propTypes; @@ -236,10 +243,10 @@ const mapStateToProps = (state) => ({ }); const mapDispatchToProps = (dispatch) => ({ - fetchDependencies: (params) => dispatch( - dependenciesActionCreators.fetchDependencies(params), - ), - clearDependencies: () => dispatch(dependenciesActionCreators.clearDependencies()), + fetchDependencies: (params) => + dispatch(dependenciesActionCreators.fetchDependencies(params)), + clearDependencies: () => + dispatch(dependenciesActionCreators.clearDependencies()), }); export default connect( diff --git a/zipkin-lens/src/components/DependenciesPage/DependenciesPage.test.jsx b/zipkin-lens/src/components/DependenciesPage/DependenciesPage.test.jsx index 034effd5fde..29565679760 100644 --- a/zipkin-lens/src/components/DependenciesPage/DependenciesPage.test.jsx +++ b/zipkin-lens/src/components/DependenciesPage/DependenciesPage.test.jsx @@ -17,28 +17,38 @@ import { cleanup, fireEvent } from '@testing-library/react'; import { DependenciesPageImpl } from './DependenciesPage'; import render from '../../test/util/render-with-default-settings'; -jest.mock('./VizceralExt', () => jest.fn(({ objectHighlighted }) => ( -
- - )) - } -
-))); + ))} + + )), +); describe('', () => { const exampleDependencies = [ @@ -67,7 +77,9 @@ describe('', () => { }); it('should render a loading indicator when isLoading is true', () => { - const { queryAllByTestId } = render(); + const { queryAllByTestId } = render( + , + ); const loadingIndicators = queryAllByTestId('loading-indicator'); expect(loadingIndicators.length).toBe(1); }); diff --git a/zipkin-lens/src/components/DependenciesPage/DependenciesPageHeader.jsx b/zipkin-lens/src/components/DependenciesPage/DependenciesPageHeader.jsx index 62f465c2f59..83fa378955d 100644 --- a/zipkin-lens/src/components/DependenciesPage/DependenciesPageHeader.jsx +++ b/zipkin-lens/src/components/DependenciesPage/DependenciesPageHeader.jsx @@ -76,57 +76,59 @@ const propTypes = { onFindButtonClick: PropTypes.func.isRequired, }; -const DependenciesPageHeader = React.memo(({ - startTime, - endTime, - onStartTimeChange, - onEndTimeChange, - onFindButtonClick, -}) => { - const classes = useStyles(); - const { i18n } = useLingui(); +const DependenciesPageHeader = React.memo( + ({ + startTime, + endTime, + onStartTimeChange, + onEndTimeChange, + onFindButtonClick, + }) => { + const classes = useStyles(); + const { i18n } = useLingui(); - return ( - - - - Dependencies - - - - + return ( + + + + Dependencies + + + + + + + + + - + + - - - - - - - - - ); -}); + ); + }, +); DependenciesPageHeader.propTypes = propTypes; diff --git a/zipkin-lens/src/components/DependenciesPage/DoughnutGraph.jsx b/zipkin-lens/src/components/DependenciesPage/DoughnutGraph.jsx index 3084337e826..c1dad04096e 100644 --- a/zipkin-lens/src/components/DependenciesPage/DoughnutGraph.jsx +++ b/zipkin-lens/src/components/DependenciesPage/DoughnutGraph.jsx @@ -42,10 +42,14 @@ const propTypes = { const DoughnutGraph = React.memo(({ edgeNames, edgeData }) => { const classes = useStyles(); - const data = useMemo(() => edgeNames.map((name, index) => ({ - name, - value: edgeData[index], - })), [edgeNames, edgeData]); + const data = useMemo( + () => + edgeNames.map((name, index) => ({ + name, + value: edgeData[index], + })), + [edgeNames, edgeData], + ); const renderLabel = useCallback(({ index }) => data[index].name, [data]); @@ -53,27 +57,26 @@ const DoughnutGraph = React.memo(({ edgeNames, edgeData }) => { - { - ({ width }) => ( - - - { - data.map((entry) => ( - - )) - } - - - ) - } + {({ width }) => ( + + + {data.map((entry) => ( + + ))} + + + )} diff --git a/zipkin-lens/src/components/DependenciesPage/EdgeData.jsx b/zipkin-lens/src/components/DependenciesPage/EdgeData.jsx index 4eae4e67101..f7ccc92e45d 100644 --- a/zipkin-lens/src/components/DependenciesPage/EdgeData.jsx +++ b/zipkin-lens/src/components/DependenciesPage/EdgeData.jsx @@ -38,11 +38,7 @@ const propTypes = { errorCount: PropTypes.number.isRequired, }; -const EdgeData = React.memo(({ - nodeName, - normalCount, - errorCount, -}) => { +const EdgeData = React.memo(({ nodeName, normalCount, errorCount }) => { const classes = useStyles(); return ( @@ -52,20 +48,12 @@ const EdgeData = React.memo(({ - - NORMAL - - - {normalCount} - + NORMAL + {normalCount} - - ERROR - - - {errorCount} - + ERROR + {errorCount}
diff --git a/zipkin-lens/src/components/DependenciesPage/ExplainBox.jsx b/zipkin-lens/src/components/DependenciesPage/ExplainBox.jsx index 0af6cb48e78..b020d6368be 100644 --- a/zipkin-lens/src/components/DependenciesPage/ExplainBox.jsx +++ b/zipkin-lens/src/components/DependenciesPage/ExplainBox.jsx @@ -48,7 +48,9 @@ const ExplainBox = React.memo(() => { Search Dependencies - Please select the start and end time. Then, click the search button. + + Please select the start and end time. Then, click the search button. +
); diff --git a/zipkin-lens/src/components/DependenciesPage/NodeDetail.jsx b/zipkin-lens/src/components/DependenciesPage/NodeDetail.jsx index 5fd9e391d0b..0a70aa413d4 100644 --- a/zipkin-lens/src/components/DependenciesPage/NodeDetail.jsx +++ b/zipkin-lens/src/components/DependenciesPage/NodeDetail.jsx @@ -65,132 +65,132 @@ const useStyles = makeStyles((theme) => ({ const propTypes = { serviceName: PropTypes.string.isRequired, - targetEdges: PropTypes.arrayOf(PropTypes.shape({ - target: PropTypes.string.isRequired, - })).isRequired, - sourceEdges: PropTypes.arrayOf(PropTypes.shape({ - source: PropTypes.string.isRequired, - })).isRequired, + targetEdges: PropTypes.arrayOf( + PropTypes.shape({ + target: PropTypes.string.isRequired, + }), + ).isRequired, + sourceEdges: PropTypes.arrayOf( + PropTypes.shape({ + source: PropTypes.string.isRequired, + }), + ).isRequired, minHeight: PropTypes.number.isRequired, startTime: PropTypes.shape({}).isRequired, endTime: PropTypes.shape({}).isRequired, history: PropTypes.shape({ push: PropTypes.func.isRequired }).isRequired, }; -const NodeDetail = React.memo(({ - serviceName, - targetEdges, - sourceEdges, - minHeight, - startTime, - endTime, - history, -}) => { - const classes = useStyles(); - const outputEdgeNames = useMemo( - () => targetEdges.map((edge) => edge.target), - [targetEdges], - ); - const outputEdgeData = useMemo( - () => targetEdges.map((edge) => edge.metrics.normal + edge.metrics.danger), - [targetEdges], - ); - const inputEdgeNames = useMemo( - () => sourceEdges.map((edge) => edge.source), - [sourceEdges], - ); - const inputEdgeData = useMemo( - () => sourceEdges.map((edge) => edge.metrics.normal + edge.metrics.danger), - [sourceEdges], - ); +const NodeDetail = React.memo( + ({ + serviceName, + targetEdges, + sourceEdges, + minHeight, + startTime, + endTime, + history, + }) => { + const classes = useStyles(); + const outputEdgeNames = useMemo( + () => targetEdges.map((edge) => edge.target), + [targetEdges], + ); + const outputEdgeData = useMemo( + () => + targetEdges.map((edge) => edge.metrics.normal + edge.metrics.danger), + [targetEdges], + ); + const inputEdgeNames = useMemo( + () => sourceEdges.map((edge) => edge.source), + [sourceEdges], + ); + const inputEdgeData = useMemo( + () => + sourceEdges.map((edge) => edge.metrics.normal + edge.metrics.danger), + [sourceEdges], + ); - const handleSearchTracesButtonClick = useCallback(() => { - history.push({ - pathname: '/', - search: buildQueryParameters({ - serviceName, - startTs: startTime.valueOf(), - endTs: endTime.valueOf(), - lookback: 'custom', - }), - }); - }, [serviceName, startTime, endTime, history]); + const handleSearchTracesButtonClick = useCallback(() => { + history.push({ + pathname: '/', + search: buildQueryParameters({ + serviceName, + startTs: startTime.valueOf(), + endTs: endTime.valueOf(), + lookback: 'custom', + }), + }); + }, [serviceName, startTime, endTime, history]); - return ( - - - - {serviceName} - - - - - - - USES - - - (traced requests) - + return ( + + + {serviceName} + - - { - targetEdges.length === 0 ? ( + + + USES + (traced requests) + + + {targetEdges.length === 0 ? ( Not found... ) : ( <> - { - targetEdges.map((edge) => ( - - )) - } - + {targetEdges.map((edge) => ( + + ))} + - ) - } - - - - - - USED - - - (traced requests) - + )} + - - { - sourceEdges.length === 0 ? ( + + + USED + (traced requests) + + + {sourceEdges.length === 0 ? ( Not found... ) : ( <> - { - sourceEdges.map((edge) => ( - - )) - } - + {sourceEdges.map((edge) => ( + + ))} + - ) - } - + )} + + - - ); -}); + ); + }, +); NodeDetail.propTypes = propTypes; diff --git a/zipkin-lens/src/components/DependenciesPage/NodeDetail.test.js b/zipkin-lens/src/components/DependenciesPage/NodeDetail.test.js index 189d7547e40..d017bbdcb21 100644 --- a/zipkin-lens/src/components/DependenciesPage/NodeDetail.test.js +++ b/zipkin-lens/src/components/DependenciesPage/NodeDetail.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -57,9 +57,7 @@ describe('', () => { }); it('should render "Not found" message only when targetEdges or sourceEdges are present', () => { - const { queryAllByText } = render( - , - ); + const { queryAllByText } = render(); const notFoundMessages = queryAllByText('Not found...'); expect(notFoundMessages.length).toBe(0); }); diff --git a/zipkin-lens/src/components/DiscoverPage/DiscoverPage.jsx b/zipkin-lens/src/components/DiscoverPage/DiscoverPage.jsx index d4fea24183c..23a31aaf5e7 100644 --- a/zipkin-lens/src/components/DiscoverPage/DiscoverPage.jsx +++ b/zipkin-lens/src/components/DiscoverPage/DiscoverPage.jsx @@ -44,7 +44,10 @@ import * as remoteServicesActionCreators from '../../actions/remote-services-act import * as spansActionCreators from '../../actions/spans-action'; import * as autocompleteKeysActionCreators from '../../actions/autocomplete-keys-action'; import * as globalSearchActionCreators from '../../actions/global-search-action'; -import { globalSearchLookbackConditionPropTypes, globalSearchConditionsPropTypes } from '../../prop-types'; +import { + globalSearchLookbackConditionPropTypes, + globalSearchConditionsPropTypes, +} from '../../prop-types'; import ExplainBox from './ExplainBox'; @@ -143,26 +146,33 @@ const DiscoverPageContent = ({ const findTraces = useCallback(() => { const currentTs = moment().valueOf(); - const queryParameters = buildQueryParameters(buildCommonQueryParameters( - conditions, - lookbackCondition, - limitCondition, - currentTs, - )); + const queryParameters = buildQueryParameters( + buildCommonQueryParameters( + conditions, + lookbackCondition, + limitCondition, + currentTs, + ), + ); history.push({ search: queryParameters }); - loadTraces(buildTracesApiQueryParameters( - conditions, - lookbackCondition, - limitCondition, - currentTs, - )); + loadTraces( + buildTracesApiQueryParameters( + conditions, + lookbackCondition, + limitCondition, + currentTs, + ), + ); }, [loadTraces, conditions, lookbackCondition, limitCondition, history]); - const handleKeyDown = useCallback((event) => { - if (document.activeElement.tagName === 'BODY' && event.key === 'Enter') { - findTraces(); - } - }, [findTraces]); + const handleKeyDown = useCallback( + (event) => { + if (document.activeElement.tagName === 'BODY' && event.key === 'Enter') { + findTraces(); + } + }, + [findTraces], + ); useEffect(() => { window.addEventListener('keydown', handleKeyDown); @@ -186,7 +196,11 @@ const DiscoverPageContent = ({ setLookbackCondition({ value: lookbackConditionFromUrl.value, endTs: lookbackConditionFromUrl.endTs || moment().valueOf(), - startTs: lookbackConditionFromUrl.startTs || moment().subtract(15, 'minutes').valueOf(), + startTs: + lookbackConditionFromUrl.startTs || + moment() + .subtract(15, 'minutes') + .valueOf(), }); } setLimitCondition(limitConditionFromUrl || 10); @@ -205,9 +219,10 @@ const DiscoverPageContent = ({ const currentTs = lookbackConditionFromUrl.endTs || moment().valueOf(); // Fetch traces only if one or more conditions are set. - if (!isEmpty(conditionsFromUrl) - || !isEmpty(lookbackConditionFromUrl) - || !!limitConditionFromUrl + if ( + !isEmpty(conditionsFromUrl) || + !isEmpty(lookbackConditionFromUrl) || + !!limitConditionFromUrl ) { const apiQueryParams = buildTracesApiQueryParameters( conditionsFromUrl, @@ -229,7 +244,10 @@ const DiscoverPageContent = ({ let content; if (isLoading) { content = ( - + ); @@ -294,9 +312,9 @@ const DiscoverPageImpl = (props) => { - Searching has been disabled via the searchEnabled property. - You can still view specific traces of which you know the trace id - by entering it in the "trace id..." textbox on the top-right. + Searching has been disabled via the searchEnabled property. You + can still view specific traces of which you know the trace id by + entering it in the "trace id..." textbox on the top-right. @@ -317,33 +335,23 @@ const mapStateToProps = (state) => ({ }); const mapDispatchToProps = (dispatch) => ({ - loadTraces: (params) => dispatch( - tracesActionCreators.loadTraces(params), - ), - clearTraces: () => dispatch( - tracesActionCreators.clearTraces(), - ), - fetchServices: () => dispatch( - servicesActionCreators.fetchServices(), - ), - fetchRemoteServices: (serviceName) => dispatch( - remoteServicesActionCreators.fetchRemoteServices(serviceName), - ), - fetchSpans: (serviceName) => dispatch( - spansActionCreators.fetchSpans(serviceName), - ), - fetchAutocompleteKeys: () => dispatch( - autocompleteKeysActionCreators.fetchAutocompleteKeys(), - ), - setConditions: (conditions) => dispatch( - globalSearchActionCreators.setConditions(conditions), - ), - setLookbackCondition: (lookbackCondition) => dispatch( - globalSearchActionCreators.setLookbackCondition(lookbackCondition), - ), - setLimitCondition: (limitCondition) => dispatch( - globalSearchActionCreators.setLimitCondition(limitCondition), - ), + loadTraces: (params) => dispatch(tracesActionCreators.loadTraces(params)), + clearTraces: () => dispatch(tracesActionCreators.clearTraces()), + fetchServices: () => dispatch(servicesActionCreators.fetchServices()), + fetchRemoteServices: (serviceName) => + dispatch(remoteServicesActionCreators.fetchRemoteServices(serviceName)), + fetchSpans: (serviceName) => + dispatch(spansActionCreators.fetchSpans(serviceName)), + fetchAutocompleteKeys: () => + dispatch(autocompleteKeysActionCreators.fetchAutocompleteKeys()), + setConditions: (conditions) => + dispatch(globalSearchActionCreators.setConditions(conditions)), + setLookbackCondition: (lookbackCondition) => + dispatch( + globalSearchActionCreators.setLookbackCondition(lookbackCondition), + ), + setLimitCondition: (limitCondition) => + dispatch(globalSearchActionCreators.setLimitCondition(limitCondition)), }); export default connect( diff --git a/zipkin-lens/src/components/DiscoverPage/DiscoverPage.test.jsx b/zipkin-lens/src/components/DiscoverPage/DiscoverPage.test.jsx index 5837af7abac..cc72b475ee5 100644 --- a/zipkin-lens/src/components/DiscoverPage/DiscoverPage.test.jsx +++ b/zipkin-lens/src/components/DiscoverPage/DiscoverPage.test.jsx @@ -21,8 +21,12 @@ describe('', () => { it('renders search box with default config', () => { const { getByText, queryByText } = render(); expect(getByText('Search Traces')).toBeInTheDocument(); - expect(queryByText('Searching has been disabled via the searchEnabled property', { exact: false })) - .not.toBeInTheDocument(); + expect( + queryByText( + 'Searching has been disabled via the searchEnabled property', + { exact: false }, + ), + ).not.toBeInTheDocument(); }); it('does not render search box with searchEnabled=false config', () => { @@ -30,7 +34,10 @@ describe('', () => { uiConfig: { searchEnabled: false }, }); expect(queryByText('Search Traces')).not.toBeInTheDocument(); - expect(getByText('Searching has been disabled via the searchEnabled property', { exact: false })) - .toBeInTheDocument(); + expect( + getByText('Searching has been disabled via the searchEnabled property', { + exact: false, + }), + ).toBeInTheDocument(); }); }); diff --git a/zipkin-lens/src/components/DiscoverPage/ExplainBox.jsx b/zipkin-lens/src/components/DiscoverPage/ExplainBox.jsx index de09a8594f5..841fa61ed21 100644 --- a/zipkin-lens/src/components/DiscoverPage/ExplainBox.jsx +++ b/zipkin-lens/src/components/DiscoverPage/ExplainBox.jsx @@ -49,7 +49,10 @@ const ExplainBox = React.memo(() => { Search Traces - Please select criteria in the search bar. Then, click the search button. + + Please select criteria in the search bar. Then, click the search + button. + ); diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilter.jsx b/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilter.jsx index d52e3bb07e5..d46fc66e9c0 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilter.jsx +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilter.jsx @@ -31,7 +31,10 @@ const ServiceFilter = ({ filters, ...props }) => { const [anchorEl, setAnchorEl] = useState(null); const { i18n } = useLingui(); - const handleButtonClick = useCallback((event) => setAnchorEl(event.currentTarget), []); + const handleButtonClick = useCallback( + (event) => setAnchorEl(event.currentTarget), + [], + ); const handleMenuClose = useCallback(() => setAnchorEl(null), []); diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilter.test.js b/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilter.test.js index 8fa066fdc75..fad4b3bc2d3 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilter.test.js +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilter.test.js @@ -38,12 +38,16 @@ describe('', () => { const button = getByTestId('button'); fireEvent.click(button); - const popover = await waitForElement(() => getByTestId('service-filter-popover')); + const popover = await waitForElement(() => + getByTestId('service-filter-popover'), + ); expect(popover).toBeInTheDocument(); }); it('should hide badge if the number of filters is 1', () => { - const { getByTestId } = render(); + const { getByTestId } = render( + , + ); const badgeWrapper = getByTestId('badge'); const badge = badgeWrapper.querySelector('.MuiBadge-badge'); @@ -69,11 +73,15 @@ describe('', () => { it('should not show a service name when there are not any filters', () => { const { getByTestId } = render(); - expect(within(getByTestId('button-text')).getByText('Filter')).toBeInTheDocument(); + expect( + within(getByTestId('button-text')).getByText('Filter'), + ).toBeInTheDocument(); }); it('should show the first service name when there are some filters', () => { const { getByTestId } = render(); - expect(within(getByTestId('button-text')).getByText('service-A')).toBeInTheDocument(); + expect( + within(getByTestId('button-text')).getByText('service-A'), + ).toBeInTheDocument(); }); }); diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilterPopover.jsx b/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilterPopover.jsx index 8ec17eea1ce..91662dedfff 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilterPopover.jsx +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilterPopover.jsx @@ -90,9 +90,10 @@ const ServiceFilterPopover = ({ const handleTextChange = (e) => setFilterText(e.target.value); const filteredServiceNames = useMemo( - () => allServiceNames - .filter((serviceName) => !filters.includes(serviceName)) - .filter((serviceName) => serviceName.includes(filterText)), + () => + allServiceNames + .filter((serviceName) => !filters.includes(serviceName)) + .filter((serviceName) => serviceName.includes(filterText)), [allServiceNames, filters, filterText], ); @@ -119,29 +120,28 @@ const ServiceFilterPopover = ({ data-testid="text-field" />
- { - filters.length > 0 - ? ( - - { - filters.map((filter) => ( - - onDeleteFilter(filter)} /> - - )) - } + {filters.length > 0 ? ( + + {filters.map((filter) => ( + + onDeleteFilter(filter)} + /> - ) - : null - } + ))} + + ) : null} - { - filteredServiceNames.map((serviceName) => ( - onAddFilter(serviceName)} key={serviceName}> - - - )) - } + {filteredServiceNames.map((serviceName) => ( + onAddFilter(serviceName)} + key={serviceName} + > + + + ))} ); diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilterPopover.test.js b/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilterPopover.test.js index 669e74a180f..c2a05be0572 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilterPopover.test.js +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/ServiceFilterPopover.test.js @@ -41,15 +41,17 @@ describe('', () => { const input = within(getByTestId('text-field')).getByRole('textbox'); fireEvent.change(input, { target: { value: 'service-A' } }); - const updatedInput = await waitForElement( - () => within(getByTestId('text-field')).getByRole('textbox'), + const updatedInput = await waitForElement(() => + within(getByTestId('text-field')).getByRole('textbox'), ); expect(updatedInput.value).toEqual('service-A'); }); it('should not show filter list when there are not any filters', () => { - const { queryByTestId } = render(); + const { queryByTestId } = render( + , + ); expect(queryByTestId('filters')).not.toBeInTheDocument(); }); }); diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTab.jsx b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTab.jsx index ce73675d3fd..40b896cec6c 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTab.jsx +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTab.jsx @@ -37,10 +37,16 @@ const useStyles = makeStyles((theme) => ({ }, })); -export const TracesTab = ({ traceSummaries }) => { // Export for testing. +export const TracesTab = ({ traceSummaries }) => { + // Export for testing. const classes = useStyles(); - const allServiceNames = useMemo(() => extractAllServiceNames(traceSummaries), [traceSummaries]); - const [sortingMethod, setSortingMethod] = useState(sortingMethods.LONGEST_FIRST); + const allServiceNames = useMemo( + () => extractAllServiceNames(traceSummaries), + [traceSummaries], + ); + const [sortingMethod, setSortingMethod] = useState( + sortingMethods.LONGEST_FIRST, + ); const handleSortingMethodChange = useCallback((newSortingMethod) => { setSortingMethod(newSortingMethod); @@ -48,28 +54,46 @@ export const TracesTab = ({ traceSummaries }) => { // Export for testing. const [filters, setFilters] = useState([]); - const handleAddFilter = useCallback((filter) => { - if (!filters.includes(filter)) { - setFilters([ - ...filters, - filter, - ]); - } - }, [filters]); + const handleAddFilter = useCallback( + (filter) => { + if (!filters.includes(filter)) { + setFilters([...filters, filter]); + } + }, + [filters], + ); - const handleDeleteFilter = useCallback((filter) => { - setFilters(filters.filter((f) => f !== filter)); - }, [filters]); + const handleDeleteFilter = useCallback( + (filter) => { + setFilters(filters.filter((f) => f !== filter)); + }, + [filters], + ); - const filteredTraceSummaries = useMemo(() => sortTraceSummaries( - filterTraceSummaries(traceSummaries, filters), - sortingMethod, - ), [filters, traceSummaries, sortingMethod]); + const filteredTraceSummaries = useMemo( + () => + sortTraceSummaries( + filterTraceSummaries(traceSummaries, filters), + sortingMethod, + ), + [filters, traceSummaries, sortingMethod], + ); return ( - - + + ({ // React-Redux already provides two hooks API, useSelector and useDispatch. // However, I think that using them would make tests more complex, so for now // I decided to keep using connect API. -export default connect( - mapStateToProps, - null, -)(TracesTab); +export default connect(mapStateToProps, null)(TracesTab); diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTab.test.jsx b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTab.test.jsx index 91f536ea3a7..c69381287be 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTab.test.jsx +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTab.test.jsx @@ -17,7 +17,7 @@ import render from '../../../test/util/render-with-default-settings'; import { TracesTab } from './TracesTab'; -jest.mock('./TracesTable', () => () => (
TracesTable
)); +jest.mock('./TracesTable', () => () =>
TracesTable
); afterAll(() => { jest.restoreAllMocks(); diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTable.jsx b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTable.jsx index d05eedbf793..db6cdea013e 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTable.jsx +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTable.jsx @@ -40,21 +40,17 @@ const TracesTable = ({ /> - { - ({ height, width }) => ( - - { - traceSummaries.map((traceSummary) => ( - - )) - } - - ) - } + {({ height, width }) => ( + + {traceSummaries.map((traceSummary) => ( + + ))} + + )} diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableHead.jsx b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableHead.jsx index dcc22dccd97..53c14acc418 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableHead.jsx +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableHead.jsx @@ -93,8 +93,12 @@ const TracesTableHead = ({ sortingMethod, onSortingMethodChange }) => { > Start Time   - {sortingMethod === sortingMethods.OLDEST_FIRST && } - {sortingMethod === sortingMethods.NEWEST_FIRST && } + {sortingMethod === sortingMethods.OLDEST_FIRST && ( + + )} + {sortingMethod === sortingMethods.NEWEST_FIRST && ( + + )} { > Duration   - {sortingMethod === sortingMethods.LONGEST_FIRST && } - {sortingMethod === sortingMethods.SHORTEST_FIRST && } + {sortingMethod === sortingMethods.LONGEST_FIRST && ( + + )} + {sortingMethod === sortingMethods.SHORTEST_FIRST && ( + + )} ); diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableHead.test.js b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableHead.test.js index 038c85829b6..4f8378f2ed7 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableHead.test.js +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableHead.test.js @@ -38,7 +38,9 @@ describe('', () => { const startTimeCell = getByTestId('start-time'); fireEvent.click(startTimeCell); expect(onSortingMethodChange).toHaveBeenCalledTimes(1); - expect(onSortingMethodChange).toHaveBeenCalledWith(sortingMethods.NEWEST_FIRST); + expect(onSortingMethodChange).toHaveBeenCalledWith( + sortingMethods.NEWEST_FIRST, + ); }); it('StartTime cell Newest -> StartTime cell Oldest', () => { @@ -52,7 +54,9 @@ describe('', () => { const startTimeCell = getByTestId('start-time'); fireEvent.click(startTimeCell); expect(onSortingMethodChange).toHaveBeenCalledTimes(1); - expect(onSortingMethodChange).toHaveBeenCalledWith(sortingMethods.OLDEST_FIRST); + expect(onSortingMethodChange).toHaveBeenCalledWith( + sortingMethods.OLDEST_FIRST, + ); }); it('Duration cell Longest -> Duration cell Shortest', () => { @@ -66,7 +70,9 @@ describe('', () => { const durationCell = getByTestId('duration'); fireEvent.click(durationCell); expect(onSortingMethodChange).toHaveBeenCalledTimes(1); - expect(onSortingMethodChange).toHaveBeenCalledWith(sortingMethods.SHORTEST_FIRST); + expect(onSortingMethodChange).toHaveBeenCalledWith( + sortingMethods.SHORTEST_FIRST, + ); }); it('StartTime cell -> Duration cell', () => { @@ -80,7 +86,9 @@ describe('', () => { const durationCell = getByTestId('duration'); fireEvent.click(durationCell); expect(onSortingMethodChange).toHaveBeenCalledTimes(1); - expect(onSortingMethodChange).toHaveBeenCalledWith(sortingMethods.LONGEST_FIRST); + expect(onSortingMethodChange).toHaveBeenCalledWith( + sortingMethods.LONGEST_FIRST, + ); }); }); }); diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableRow.jsx b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableRow.jsx index 7d09efbf1cf..2f6a503fa17 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableRow.jsx +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableRow.jsx @@ -28,7 +28,8 @@ import { selectColorByInfoClass } from '../../../colors'; export function rootServiceAndSpanName(root) { const { span } = root; if (span) { - const serviceName = getServiceName(span.localEndpoint) || getServiceName(span.remoteEndpoint); + const serviceName = + getServiceName(span.localEndpoint) || getServiceName(span.remoteEndpoint); return { serviceName: serviceName || 'unknown', spanName: span.name || 'unknown', @@ -109,7 +110,9 @@ export const TracesTableRowImpl = ({ width={`${traceSummary.width}%`} height="100%" className={classes.durationBar} - style={{ backgroundColor: selectColorByInfoClass(traceSummary.infoClass) }} + style={{ + backgroundColor: selectColorByInfoClass(traceSummary.infoClass), + }} data-testid="duration-bar" /> @@ -124,12 +127,8 @@ export const TracesTableRowImpl = ({ {traceSummary.traceId} - - {startTime.format('MM/DD HH:mm:ss:SSS')} - - - {`(${startTime.fromNow()})`} - + {startTime.format('MM/DD HH:mm:ss:SSS')} + {`(${startTime.fromNow()})`} {traceSummary.durationStr} @@ -139,20 +138,18 @@ export const TracesTableRowImpl = ({ {/* In HTML5, anchor tag including interactive content is invalid. So ServiceBadge which has onClick callback cannot be surrounded by Link. */} - { - traceSummary.serviceSummaries.map((serviceSummary) => ( - - { - onAddFilter(serviceSummary.serviceName); - event.stopPropagation(); - }} - /> - - )) - } + {traceSummary.serviceSummaries.map((serviceSummary) => ( + + { + onAddFilter(serviceSummary.serviceName); + event.stopPropagation(); + }} + /> + + ))}
); @@ -164,7 +161,4 @@ const mapStateToProps = (state) => ({ correctedTraceMap: state.traces.correctedTraceMap, }); -export default connect( - mapStateToProps, - null, -)(withRouter(TracesTableRowImpl)); +export default connect(mapStateToProps, null)(withRouter(TracesTableRowImpl)); diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableRow.test.js b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableRow.test.js index b7429f73a15..3b16a0a77a8 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableRow.test.js +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/TracesTableRow.test.js @@ -45,7 +45,10 @@ describe('rootServiceAndSpanName', () => { it('should return unknown serviceName when missing localEndpoint.serviceName', () => { const root = new SpanNode({ - traceId: '1', id: '1', name: 'get', localEndpoint: { }, + traceId: '1', + id: '1', + name: 'get', + localEndpoint: {}, }); expect(rootServiceAndSpanName(root)).toEqual({ @@ -55,7 +58,11 @@ describe('rootServiceAndSpanName', () => { }); it('should return unknown spanName when missing span.name', () => { - const root = new SpanNode({ traceId: '1', id: '1', localEndpoint: { serviceName: 'frontend' } }); + const root = new SpanNode({ + traceId: '1', + id: '1', + localEndpoint: { serviceName: 'frontend' }, + }); expect(rootServiceAndSpanName(root)).toEqual({ serviceName: 'frontend', @@ -117,7 +124,9 @@ describe('', () => { // width and background-color are changed by their props. expect(durationBar).toHaveStyle('width: 80%'); expect(durationBar).toHaveStyle( - `background-color: ${selectColorByInfoClass(commonProps.traceSummary.infoClass)}`, + `background-color: ${selectColorByInfoClass( + commonProps.traceSummary.infoClass, + )}`, ); }); diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/util.js b/zipkin-lens/src/components/DiscoverPage/TracesTab/util.js index 617f4b7dc3f..3a695b672e2 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/util.js +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/util.js @@ -49,15 +49,16 @@ export const extractAllServiceNames = (traceSummaries) => { return Array.from(new Set(result)); // For uniqueness }; -export const filterTraceSummaries = (traceSummaries, filters) => traceSummaries.filter( - (traceSummary) => { +export const filterTraceSummaries = (traceSummaries, filters) => + traceSummaries.filter((traceSummary) => { for (let i = 0; i < filters.length; i += 1) { - if (!traceSummary.serviceSummaries.find( - (serviceSummary) => serviceSummary.serviceName === filters[i], - )) { + if ( + !traceSummary.serviceSummaries.find( + (serviceSummary) => serviceSummary.serviceName === filters[i], + ) + ) { return false; } } return true; - }, -); + }); diff --git a/zipkin-lens/src/components/DiscoverPage/TracesTab/util.test.js b/zipkin-lens/src/components/DiscoverPage/TracesTab/util.test.js index c35640e2b32..26ab09b2142 100644 --- a/zipkin-lens/src/components/DiscoverPage/TracesTab/util.test.js +++ b/zipkin-lens/src/components/DiscoverPage/TracesTab/util.test.js @@ -19,9 +19,8 @@ import { } from './util'; describe('sortTraceSummaries', () => { - const pairsToTraceSummaries = (pairs) => pairs.map( - (pair) => ({ duration: pair[0], timestamp: pair[1] }), - ); + const pairsToTraceSummaries = (pairs) => + pairs.map((pair) => ({ duration: pair[0], timestamp: pair[1] })); const input = pairsToTraceSummaries([ [1, 3], // [duration, timestamp] @@ -30,75 +29,89 @@ describe('sortTraceSummaries', () => { ]); it('LONGEST_FIRST', () => { - expect(sortTraceSummaries(input, sortingMethods.LONGEST_FIRST)).toEqual(pairsToTraceSummaries([ - [3, 2], - [2, 1], - [1, 3], - ])); + expect(sortTraceSummaries(input, sortingMethods.LONGEST_FIRST)).toEqual( + pairsToTraceSummaries([ + [3, 2], + [2, 1], + [1, 3], + ]), + ); // original traceSummaries should not be changed. - expect(input).toEqual(pairsToTraceSummaries([ - [1, 3], - [3, 2], - [2, 1], - ])); + expect(input).toEqual( + pairsToTraceSummaries([ + [1, 3], + [3, 2], + [2, 1], + ]), + ); }); it('SHORTEST', () => { - expect(sortTraceSummaries(input, sortingMethods.SHORTEST_FIRST)).toEqual(pairsToTraceSummaries([ - [1, 3], - [2, 1], - [3, 2], - ])); + expect(sortTraceSummaries(input, sortingMethods.SHORTEST_FIRST)).toEqual( + pairsToTraceSummaries([ + [1, 3], + [2, 1], + [3, 2], + ]), + ); }); it('NEWEST', () => { - expect(sortTraceSummaries(input, sortingMethods.NEWEST_FIRST)).toEqual(pairsToTraceSummaries([ - [1, 3], - [3, 2], - [2, 1], - ])); + expect(sortTraceSummaries(input, sortingMethods.NEWEST_FIRST)).toEqual( + pairsToTraceSummaries([ + [1, 3], + [3, 2], + [2, 1], + ]), + ); }); it('OLDEST', () => { - expect(sortTraceSummaries(input, sortingMethods.OLDEST_FIRST)).toEqual(pairsToTraceSummaries([ - [2, 1], - [3, 2], - [1, 3], - ])); + expect(sortTraceSummaries(input, sortingMethods.OLDEST_FIRST)).toEqual( + pairsToTraceSummaries([ + [2, 1], + [3, 2], + [1, 3], + ]), + ); }); it('otherwise', () => { - expect(sortTraceSummaries(input, 'OTHERWISE')).toEqual(pairsToTraceSummaries([ - [1, 3], - [3, 2], - [2, 1], - ])); + expect(sortTraceSummaries(input, 'OTHERWISE')).toEqual( + pairsToTraceSummaries([ + [1, 3], + [3, 2], + [2, 1], + ]), + ); }); }); describe('extractAllServiceNames', () => { it('should return all service names', () => { - expect(extractAllServiceNames([ - { - serviceSummaries: [ - { serviceName: 'service-A' }, - { serviceName: 'service-B' }, - ], - }, - { - serviceSummaries: [ - { serviceName: 'service-C' }, - { serviceName: 'service-D' }, - ], - }, - { - serviceSummaries: [ - { serviceName: 'service-B' }, - { serviceName: 'service-E' }, - { serviceName: 'service-A' }, - ], - }, - ])).toEqual([ + expect( + extractAllServiceNames([ + { + serviceSummaries: [ + { serviceName: 'service-A' }, + { serviceName: 'service-B' }, + ], + }, + { + serviceSummaries: [ + { serviceName: 'service-C' }, + { serviceName: 'service-D' }, + ], + }, + { + serviceSummaries: [ + { serviceName: 'service-B' }, + { serviceName: 'service-E' }, + { serviceName: 'service-A' }, + ], + }, + ]), + ).toEqual([ 'service-A', 'service-B', 'service-C', @@ -137,9 +150,7 @@ describe('filterTraceSummaries', () => { ], }, { - serviceSummaries: [ - { serviceName: 'service-G' }, - ], + serviceSummaries: [{ serviceName: 'service-G' }], }, { serviceSummaries: [ @@ -149,7 +160,9 @@ describe('filterTraceSummaries', () => { ], }, ]; - expect(filterTraceSummaries(traceSummaries, ['service-A', 'service-B'])).toEqual([ + expect( + filterTraceSummaries(traceSummaries, ['service-A', 'service-B']), + ).toEqual([ { serviceSummaries: [ { serviceName: 'service-A' }, diff --git a/zipkin-lens/src/components/DiscoverPage/api.js b/zipkin-lens/src/components/DiscoverPage/api.js index 5c33714c6a3..1448adc9b8d 100644 --- a/zipkin-lens/src/components/DiscoverPage/api.js +++ b/zipkin-lens/src/components/DiscoverPage/api.js @@ -34,7 +34,8 @@ export const buildCommonQueryParameters = ( case 'tags': tagConditions.push(condition.value); break; - default: // autocompleteTags + default: + // autocompleteTags autocompleteTagConditions.push(`${condition.key}=${condition.value}`); break; } @@ -81,7 +82,8 @@ export const buildTracesApiQueryParameters = ( case 'tags': annotationQueryConditions.push(condition.value); break; - default: // autocompleteTags + default: + // autocompleteTags annotationQueryConditions.push(`${condition.key}=${condition.value}`); break; } diff --git a/zipkin-lens/src/components/DiscoverPage/api.test.js b/zipkin-lens/src/components/DiscoverPage/api.test.js index d0db0641d98..db56bea1457 100644 --- a/zipkin-lens/src/components/DiscoverPage/api.test.js +++ b/zipkin-lens/src/components/DiscoverPage/api.test.js @@ -29,7 +29,8 @@ describe('buildCommonQueryParameters', () => { { key: 'spanName', value: 'spanA' }, { key: 'minDuration', value: 10 }, { key: 'maxDuration', value: 100 }, - ], { + ], + { value: '1h', }, 15, @@ -55,7 +56,8 @@ describe('buildCommonQueryParameters', () => { { key: 'spanName', value: 'spanA' }, { key: 'minDuration', value: 10 }, { key: 'maxDuration', value: 100 }, - ], { + ], + { value: 'custom', endTs: 1547098357716, startTs: 1547098357701, @@ -84,7 +86,8 @@ describe('buildCommonQueryParameters', () => { { key: 'spanName', value: 'spanA' }, { key: 'minDuration', value: 10 }, { key: 'maxDuration', value: 100 }, - ], { + ], + { value: '1h', endTs: 1547098357716, }, @@ -104,9 +107,8 @@ describe('buildCommonQueryParameters', () => { it('should return right query parameters with a tags', () => { const queryParameters = buildCommonQueryParameters( - [ - { key: 'tags', value: 'key=value' }, - ], { + [{ key: 'tags', value: 'key=value' }], + { value: '1h', }, 15, @@ -126,7 +128,8 @@ describe('buildCommonQueryParameters', () => { { key: 'tags', value: 'key1=value1' }, { key: 'tags', value: 'key2' }, // no value { key: 'tags', value: 'key3=value3' }, - ], { + ], + { value: '1h', }, 15, @@ -150,7 +153,8 @@ describe('buildTracesApiQueryParameters', () => { { key: 'spanName', value: 'spanA' }, { key: 'minDuration', value: 10 }, { key: 'maxDuration', value: 100 }, - ], { + ], + { value: 3600000, }, 15, @@ -176,7 +180,8 @@ describe('buildTracesApiQueryParameters', () => { { key: 'spanName', value: 'spanA' }, { key: 'minDuration', value: 10 }, { key: 'maxDuration', value: 100 }, - ], { + ], + { value: 'custom', endTs: 1547098357716, startTs: 1547098357710, // lookback == 6 @@ -204,7 +209,8 @@ describe('buildTracesApiQueryParameters', () => { { key: 'spanName', value: 'spanA' }, { key: 'minDuration', value: 10 }, { key: 'maxDuration', value: 100 }, - ], { + ], + { value: 3600000, endTs: 1547098357716, }, @@ -226,9 +232,11 @@ describe('buildTracesApiQueryParameters', () => { const apiQueryParameters = buildTracesApiQueryParameters( [ { - key: 'tags', value: 'key1=value1 and key2 and key3=value3', + key: 'tags', + value: 'key1=value1 and key2 and key3=value3', }, - ], { + ], + { value: 3600000, }, 15, @@ -273,12 +281,10 @@ describe('buildDependenciesApiQueryParameters', () => { }); it('should return right query parameters when the currentTs is not specified', () => { - const apiQueryParameters = buildDependenciesApiQueryParameters( - { - value: 3600000, - endTs: 1547098357716, - }, - ); + const apiQueryParameters = buildDependenciesApiQueryParameters({ + value: 3600000, + endTs: 1547098357716, + }); expect(apiQueryParameters).toEqual({ endTs: 1547098357716, lookback: 3600000, @@ -288,52 +294,64 @@ describe('buildDependenciesApiQueryParameters', () => { describe('extractConditionsFromQueryParameters', () => { it('should return right conditions', () => { - const { conditions } = extractConditionsFromQueryParameters({ - serviceName: 'serviceA', - remoteServiceName: 'serviceB', - spanName: 'spanA', - minDuration: '10', - maxDuration: '100', - tags: 'key1=value1 and key2 and key3=value3', - }, []); - expect(conditions.sort()).toEqual([ - { key: 'serviceName', value: 'serviceA' }, - { key: 'remoteServiceName', value: 'serviceB' }, - { key: 'spanName', value: 'spanA' }, - { key: 'minDuration', value: 10 }, - { key: 'maxDuration', value: 100 }, - { key: 'tags', value: 'key1=value1' }, - { key: 'tags', value: 'key2' }, - { key: 'tags', value: 'key3=value3' }, - ].sort()); + const { conditions } = extractConditionsFromQueryParameters( + { + serviceName: 'serviceA', + remoteServiceName: 'serviceB', + spanName: 'spanA', + minDuration: '10', + maxDuration: '100', + tags: 'key1=value1 and key2 and key3=value3', + }, + [], + ); + expect(conditions.sort()).toEqual( + [ + { key: 'serviceName', value: 'serviceA' }, + { key: 'remoteServiceName', value: 'serviceB' }, + { key: 'spanName', value: 'spanA' }, + { key: 'minDuration', value: 10 }, + { key: 'maxDuration', value: 100 }, + { key: 'tags', value: 'key1=value1' }, + { key: 'tags', value: 'key2' }, + { key: 'tags', value: 'key3=value3' }, + ].sort(), + ); }); it('should return right conditions with autocompleteTags', () => { - const { conditions } = extractConditionsFromQueryParameters({ - serviceName: 'serviceA', - remoteServiceName: 'serviceB', - spanName: 'spanA', - minDuration: '10', - maxDuration: '100', - tags: 'key1=value1 and key2 and key3=value3', - autocompleteTags: 'key4=value4 and key5=value5', - }, []); - expect(conditions.sort()).toEqual([ - { key: 'serviceName', value: 'serviceA' }, - { key: 'remoteServiceName', value: 'serviceB' }, - { key: 'spanName', value: 'spanA' }, - { key: 'minDuration', value: 10 }, - { key: 'maxDuration', value: 100 }, - { key: 'tags', value: 'key1=value1' }, - { key: 'tags', value: 'key2' }, - { key: 'tags', value: 'key3=value3' }, - { key: 'key4', value: 'value4' }, - { key: 'key5', value: 'value5' }, - ].sort()); + const { conditions } = extractConditionsFromQueryParameters( + { + serviceName: 'serviceA', + remoteServiceName: 'serviceB', + spanName: 'spanA', + minDuration: '10', + maxDuration: '100', + tags: 'key1=value1 and key2 and key3=value3', + autocompleteTags: 'key4=value4 and key5=value5', + }, + [], + ); + expect(conditions.sort()).toEqual( + [ + { key: 'serviceName', value: 'serviceA' }, + { key: 'remoteServiceName', value: 'serviceB' }, + { key: 'spanName', value: 'spanA' }, + { key: 'minDuration', value: 10 }, + { key: 'maxDuration', value: 100 }, + { key: 'tags', value: 'key1=value1' }, + { key: 'tags', value: 'key2' }, + { key: 'tags', value: 'key3=value3' }, + { key: 'key4', value: 'value4' }, + { key: 'key5', value: 'value5' }, + ].sort(), + ); }); it('should return the right limit condition', () => { - const { limitCondition } = extractConditionsFromQueryParameters({ limit: '15' }); + const { limitCondition } = extractConditionsFromQueryParameters({ + limit: '15', + }); expect(limitCondition).toEqual(15); }); diff --git a/zipkin-lens/src/components/GlobalSearch/GlobalSearchCondition.jsx b/zipkin-lens/src/components/GlobalSearch/GlobalSearchCondition.jsx index c848baedcdf..0bbfd5dba05 100644 --- a/zipkin-lens/src/components/GlobalSearch/GlobalSearchCondition.jsx +++ b/zipkin-lens/src/components/GlobalSearch/GlobalSearchCondition.jsx @@ -78,62 +78,47 @@ const GlobalSearchCondition = ({ conditionIndex, addCondition }) => { isKeyFocusedRef.current = isKeyFocused; isValueFocusedRef.current = isValueFocused; - const deleteWhenValueIsEmpty = useCallback( - () => { - setTimeout(() => { - if ( - !isKeyFocusedRef.current - && !isValueFocusedRef.current - && !conditionsRef.current[conditionIndex].value - ) { - dispatch(deleteCondition(conditionIndex)); - } - }, 0); - }, - [conditionIndex, dispatch], - ); + const deleteWhenValueIsEmpty = useCallback(() => { + setTimeout(() => { + if ( + !isKeyFocusedRef.current && + !isValueFocusedRef.current && + !conditionsRef.current[conditionIndex].value + ) { + dispatch(deleteCondition(conditionIndex)); + } + }, 0); + }, [conditionIndex, dispatch]); const handleKeyFocus = useCallback(() => setIsKeyFocused(true), []); - const handleKeyBlur = useCallback( - () => { - setIsKeyFocused(false); - // If the user blurs with en empty value, delete this condition component. - // This behavior improves usability, - deleteWhenValueIsEmpty(); - }, - [deleteWhenValueIsEmpty], - ); + const handleKeyBlur = useCallback(() => { + setIsKeyFocused(false); + // If the user blurs with en empty value, delete this condition component. + // This behavior improves usability, + deleteWhenValueIsEmpty(); + }, [deleteWhenValueIsEmpty]); const handleValueFocus = useCallback(() => setIsValueFocused(true), []); - const handleValueBlur = useCallback( - () => { - setIsValueFocused(false); - // If the user blurs with en empty value, delete this condition component. - // This behavior improves usability, - deleteWhenValueIsEmpty(); - }, - [deleteWhenValueIsEmpty], - ); + const handleValueBlur = useCallback(() => { + setIsValueFocused(false); + // If the user blurs with en empty value, delete this condition component. + // This behavior improves usability, + deleteWhenValueIsEmpty(); + }, [deleteWhenValueIsEmpty]); - const handleDeleteButtonClick = useCallback( - () => { - dispatch(deleteCondition(conditionIndex)); - }, - [conditionIndex, dispatch], - ); + const handleDeleteButtonClick = useCallback(() => { + dispatch(deleteCondition(conditionIndex)); + }, [conditionIndex, dispatch]); const valueRef = useRef(null); - const focusValue = useCallback( - () => { - // Delay is needed to avoid calling focus - // until the value element is mounted. - // If don't delay, focus cannot be executed. - setTimeout(() => valueRef.current.focus(), 0); - }, - [], - ); + const focusValue = useCallback(() => { + // Delay is needed to avoid calling focus + // until the value element is mounted. + // If don't delay, focus cannot be executed. + setTimeout(() => valueRef.current.focus(), 0); + }, []); return ( diff --git a/zipkin-lens/src/components/GlobalSearch/GlobalSearchConditionKey.jsx b/zipkin-lens/src/components/GlobalSearch/GlobalSearchConditionKey.jsx index 9289b4b7d76..95205e0fac0 100644 --- a/zipkin-lens/src/components/GlobalSearch/GlobalSearchConditionKey.jsx +++ b/zipkin-lens/src/components/GlobalSearch/GlobalSearchConditionKey.jsx @@ -16,8 +16,14 @@ import React, { useMemo, useCallback } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import ReactSelect from 'react-select'; -import { buildConditionKeyOptions, retrieveDefaultConditionValue } from './util'; -import { changeConditionKey, changeConditionValue } from '../../actions/global-search-action'; +import { + buildConditionKeyOptions, + retrieveDefaultConditionValue, +} from './util'; +import { + changeConditionKey, + changeConditionValue, +} from '../../actions/global-search-action'; import { fetchAutocompleteValues } from '../../actions/autocomplete-values-action'; import { theme } from '../../colors'; @@ -38,16 +44,16 @@ const GlobalSearchConditionKey = ({ }) => { const dispatch = useDispatch(); - const autocompleteKeys = useSelector((state) => state.autocompleteKeys.autocompleteKeys); + const autocompleteKeys = useSelector( + (state) => state.autocompleteKeys.autocompleteKeys, + ); const conditions = useSelector((state) => state.globalSearch.conditions); const { key: conditionKey } = conditions[conditionIndex]; const clearConditionValue = useCallback( - (idx, key) => dispatch(changeConditionValue( - idx, - retrieveDefaultConditionValue(key), - )), + (idx, key) => + dispatch(changeConditionValue(idx, retrieveDefaultConditionValue(key))), [dispatch], ); @@ -61,28 +67,31 @@ const GlobalSearchConditionKey = ({ } focusValue(); }, - [autocompleteKeys, clearConditionValue, conditionIndex, dispatch, focusValue], + [ + autocompleteKeys, + clearConditionValue, + conditionIndex, + dispatch, + focusValue, + ], ); const options = useMemo( - () => buildConditionKeyOptions( - conditionKey, - conditions, - autocompleteKeys, - ).map((opt) => ({ - value: opt.conditionKey, - label: opt.conditionKey, - isDisabled: opt.isDisabled, - })), + () => + buildConditionKeyOptions(conditionKey, conditions, autocompleteKeys).map( + (opt) => ({ + value: opt.conditionKey, + label: opt.conditionKey, + isDisabled: opt.isDisabled, + }), + ), [conditionKey, conditions, autocompleteKeys], ); const styles = { control: (base) => ({ ...base, - width: isFocused - ? '15rem' - : '12rem', + width: isFocused ? '15rem' : '12rem', height: '2.4rem', minHeight: '2.4rem', border: 0, @@ -90,7 +99,9 @@ const GlobalSearchConditionKey = ({ borderTopRightRadius: 0, borderBottomLeftRadius: '0.2rem', borderBottomRightRadius: 0, - backgroundColor: isFocused ? theme.palette.primary.dark : theme.palette.primary.main, + backgroundColor: isFocused + ? theme.palette.primary.dark + : theme.palette.primary.main, '&:hover': { backgroundColor: theme.palette.primary.dark, }, diff --git a/zipkin-lens/src/components/GlobalSearch/GlobalSearchConditionList.jsx b/zipkin-lens/src/components/GlobalSearch/GlobalSearchConditionList.jsx index 213b4c704d1..1fd1eb33a07 100644 --- a/zipkin-lens/src/components/GlobalSearch/GlobalSearchConditionList.jsx +++ b/zipkin-lens/src/components/GlobalSearch/GlobalSearchConditionList.jsx @@ -20,7 +20,10 @@ import { makeStyles } from '@material-ui/styles'; import Box from '@material-ui/core/Box'; import Button from '@material-ui/core/Button'; -import { retrieveNextConditionKey, retrieveDefaultConditionValue } from './util'; +import { + retrieveNextConditionKey, + retrieveDefaultConditionValue, +} from './util'; import GlobalSearchCondition from './GlobalSearchCondition'; import { addCondition } from '../../actions/global-search-action'; @@ -44,20 +47,26 @@ const GlobalSearchConditionList = () => { const dispatch = useDispatch(); const conditions = useSelector((state) => state.globalSearch.conditions); - const autocompleteKeys = useSelector((state) => state.autocompleteKeys.autocompleteKeys); + const autocompleteKeys = useSelector( + (state) => state.autocompleteKeys.autocompleteKeys, + ); - const addNewCondition = useCallback( - () => { - const nextConditionKey = retrieveNextConditionKey(conditions, autocompleteKeys); - dispatch(addCondition({ + const addNewCondition = useCallback(() => { + const nextConditionKey = retrieveNextConditionKey( + conditions, + autocompleteKeys, + ); + dispatch( + addCondition({ key: nextConditionKey, value: retrieveDefaultConditionValue(nextConditionKey), - })); - }, - [autocompleteKeys, conditions, dispatch], - ); + }), + ); + }, [autocompleteKeys, conditions, dispatch]); - const handleAddButtonClick = useMemo(() => addNewCondition, [addNewCondition]); + const handleAddButtonClick = useMemo(() => addNewCondition, [ + addNewCondition, + ]); return ( { px={0.75} py={0.5} > - { - conditions.length === 0 - ? ( - - Please select the criteria for your trace lookup - - ) - : conditions.map((condition, conditionIndex) => ( - - - - )) - } + {conditions.length === 0 ? ( + + Please select the criteria for your trace lookup + + ) : ( + conditions.map((condition, conditionIndex) => ( + + + + )) + )} diff --git a/zipkin-lens/src/components/TracePage/SpanDetail/SpanAnnotations.test.js b/zipkin-lens/src/components/TracePage/SpanDetail/SpanAnnotations.test.js index a8698f036ac..158c7ee7e9d 100644 --- a/zipkin-lens/src/components/TracePage/SpanDetail/SpanAnnotations.test.js +++ b/zipkin-lens/src/components/TracePage/SpanDetail/SpanAnnotations.test.js @@ -24,10 +24,7 @@ describe('', () => { const span = { spanId: '74280ae0c10d8062', - serviceNames: [ - 'servicea', - 'serviceb', - ], + serviceNames: ['servicea', 'serviceb'], annotations: [ { isDerived: true, @@ -73,9 +70,7 @@ describe('', () => { timestamp: 1470150004074202, duration: 94539, serviceName: 'serviceb', - childIds: [ - '43210ae0c10d1234', - ], + childIds: ['43210ae0c10d1234'], depth: 3, depthClass: 1, width: 58.45916966571439, @@ -85,18 +80,24 @@ describe('', () => { it('should not render annotation data when mounted', () => { const { queryByTestId } = render(); - expect(queryByTestId('span-annotations--annotation')).not.toBeInTheDocument(); + expect( + queryByTestId('span-annotations--annotation'), + ).not.toBeInTheDocument(); }); - it('should change the toggle button\'s text when the toggle button is clicked', () => { + it("should change the toggle button's text when the toggle button is clicked", () => { const { getByTestId } = render(); - expect(getByTestId('span-annotations--toggle-button')) - .toHaveTextContent('show all annotations'); + expect(getByTestId('span-annotations--toggle-button')).toHaveTextContent( + 'show all annotations', + ); fireEvent.click(getByTestId('span-annotations--toggle-button')); - expect(getByTestId('span-annotations--toggle-button')).toHaveTextContent('hide annotations'); + expect(getByTestId('span-annotations--toggle-button')).toHaveTextContent( + 'hide annotations', + ); fireEvent.click(getByTestId('span-annotations--toggle-button')); - expect(getByTestId('span-annotations--toggle-button')) - .toHaveTextContent('show all annotations'); + expect(getByTestId('span-annotations--toggle-button')).toHaveTextContent( + 'show all annotations', + ); }); it('should show the only one annotation data when an annotation circle is clicked', () => { @@ -105,17 +106,22 @@ describe('', () => { ); fireEvent.click(getByTestId('span-annotations--toggle-button')); expect(queryAllByTestId('span-annotations--annotation')).toHaveLength(4); - expect(getByTestId('span-annotations--toggle-button')).toHaveTextContent('hide annotations'); + expect(getByTestId('span-annotations--toggle-button')).toHaveTextContent( + 'hide annotations', + ); // Click an annotation circle. fireEvent.click(getAllByTestId('span-annotation-graph--circle')[0]); expect(queryAllByTestId('span-annotations--annotation')).toHaveLength(1); - expect(getByTestId('span-annotations--toggle-button')) - .toHaveTextContent('show all annotations'); + expect(getByTestId('span-annotations--toggle-button')).toHaveTextContent( + 'show all annotations', + ); }); it('should unselect when the same circle is clicked twice', () => { - const { getAllByTestId, queryAllByTestId } = render(); + const { getAllByTestId, queryAllByTestId } = render( + , + ); fireEvent.click(getAllByTestId('span-annotation-graph--circle')[0]); expect(queryAllByTestId('span-annotations--annotation')).toHaveLength(1); fireEvent.click(getAllByTestId('span-annotation-graph--circle')[0]); diff --git a/zipkin-lens/src/components/TracePage/SpanDetail/SpanDetail.jsx b/zipkin-lens/src/components/TracePage/SpanDetail/SpanDetail.jsx index 46ee1951bc9..4456c128609 100644 --- a/zipkin-lens/src/components/TracePage/SpanDetail/SpanDetail.jsx +++ b/zipkin-lens/src/components/TracePage/SpanDetail/SpanDetail.jsx @@ -85,21 +85,17 @@ const SpanDetail = React.memo(({ span, minHeight }) => { const spanIds = ( - { - [ - { label: i18n._(t`Span ID`), value: span.spanId }, - { label: i18n._(t`Parent ID`), value: span.parentId }, - ].map((entry) => ( - - - {`${entry.label}:`} - - - {entry.value || None} - + {[ + { label: i18n._(t`Span ID`), value: span.spanId }, + { label: i18n._(t`Parent ID`), value: span.parentId }, + ].map((entry) => ( + + {`${entry.label}:`} + + {entry.value || None} - )) - } + + ))} ); diff --git a/zipkin-lens/src/components/TracePage/SpanDetail/SpanTags.jsx b/zipkin-lens/src/components/TracePage/SpanDetail/SpanTags.jsx index 2c5acb6f9cf..41b7ef6a1b4 100644 --- a/zipkin-lens/src/components/TracePage/SpanDetail/SpanTags.jsx +++ b/zipkin-lens/src/components/TracePage/SpanDetail/SpanTags.jsx @@ -45,20 +45,18 @@ const SpanTags = React.memo(({ tags }) => { - { - tags.map((tag) => ( - - - - {tag.key} - - - {tag.value} - - - - )) - } + {tags.map((tag) => ( + + + + {tag.key} + + + {tag.value} + + + + ))}
diff --git a/zipkin-lens/src/components/TracePage/SpanDetail/util.js b/zipkin-lens/src/components/TracePage/SpanDetail/util.js index 7f5d63cb12c..a11eda674da 100644 --- a/zipkin-lens/src/components/TracePage/SpanDetail/util.js +++ b/zipkin-lens/src/components/TracePage/SpanDetail/util.js @@ -12,7 +12,8 @@ * the License. */ // Annotation's value may not be unique, so timestamp is also used in annotation key. -export const generateAnnotationKey = (annotation) => `${annotation.value}-${annotation.timestamp}`; +export const generateAnnotationKey = (annotation) => + `${annotation.value}-${annotation.timestamp}`; // Tag's key may not be unique, so value is also used. export const generateTagKey = (tag) => `${tag.key}-${tag.value}`; diff --git a/zipkin-lens/src/components/TracePage/TracePage.jsx b/zipkin-lens/src/components/TracePage/TracePage.jsx index 1188bb621bb..ed0a046bf6e 100644 --- a/zipkin-lens/src/components/TracePage/TracePage.jsx +++ b/zipkin-lens/src/components/TracePage/TracePage.jsx @@ -45,64 +45,67 @@ const defaultProps = { correctedTraceMap: {}, }; -export const TracePageImpl = React.memo(({ - traceId, - traceSummary, - loadTrace, - isTraceViewerPage, - isLoading, - isMalformedFile, - errorMessage, - correctedTraceMap, -}) => { - useEffect(() => { - if (!isTraceViewerPage) { - loadTrace(traceId, correctedTraceMap); +export const TracePageImpl = React.memo( + ({ + traceId, + traceSummary, + loadTrace, + isTraceViewerPage, + isLoading, + isMalformedFile, + errorMessage, + correctedTraceMap, + }) => { + useEffect(() => { + if (!isTraceViewerPage) { + loadTrace(traceId, correctedTraceMap); + } + }, [traceId, isTraceViewerPage, loadTrace, correctedTraceMap]); + + if (isTraceViewerPage && isMalformedFile) { + return ( + <> + + + + ); } - }, [traceId, isTraceViewerPage, loadTrace, correctedTraceMap]); - - if (isTraceViewerPage && isMalformedFile) { - return ( - <> - - - - ); - } - if (!isTraceViewerPage && isLoading) { - return ( - <> - - - - - - ); - } + if (!isTraceViewerPage && isLoading) { + return ( + <> + + + + + + ); + } - if (!traceSummary && isTraceViewerPage) { - return ( - <> - - - - ); - } + if (!traceSummary && isTraceViewerPage) { + return ( + <> + + + + ); + } - if (!traceSummary && !isTraceViewerPage) { - return ( - <> - - - - ); - } + if (!traceSummary && !isTraceViewerPage) { + return ( + <> + + + + ); + } - return ( - - ); -}); + return ; + }, +); TracePageImpl.propTypes = propTypes; TracePageImpl.defaultProps = defaultProps; @@ -135,14 +138,10 @@ const mapStateToProps = (state, ownProps) => { }; const mapDispatchToProps = (dispatch) => ({ - loadTrace: (traceId, correctedTraceMap) => dispatch( - traceActionCreators.loadTrace(traceId, correctedTraceMap), - ), + loadTrace: (traceId, correctedTraceMap) => + dispatch(traceActionCreators.loadTrace(traceId, correctedTraceMap)), }); export default withRouter( - connect( - mapStateToProps, - mapDispatchToProps, - )(TracePageImpl), + connect(mapStateToProps, mapDispatchToProps)(TracePageImpl), ); diff --git a/zipkin-lens/src/components/TracePage/TracePage.test.jsx b/zipkin-lens/src/components/TracePage/TracePage.test.jsx index 551311d4819..e34ae74776e 100644 --- a/zipkin-lens/src/components/TracePage/TracePage.test.jsx +++ b/zipkin-lens/src/components/TracePage/TracePage.test.jsx @@ -17,7 +17,9 @@ import render from '../../test/util/render-with-default-settings'; import { TracePageImpl } from './TracePage'; -jest.mock('./TraceSummary', () => () => (
TraceSummary
)); +jest.mock('./TraceSummary', () => () => ( +
TraceSummary
+)); afterAll(() => { jest.restoreAllMocks(); }); diff --git a/zipkin-lens/src/components/TracePage/TraceSummary.jsx b/zipkin-lens/src/components/TracePage/TraceSummary.jsx index bfe318a98ae..20a3c5ea277 100644 --- a/zipkin-lens/src/components/TracePage/TraceSummary.jsx +++ b/zipkin-lens/src/components/TracePage/TraceSummary.jsx @@ -23,7 +23,8 @@ import SpanDetail from './SpanDetail'; import { detailedTraceSummaryPropTypes } from '../../prop-types'; import { hasRootSpan } from '../../util/trace'; -const findSpanIndex = (spans, spanId) => spans.findIndex((span) => span.spanId === spanId); +const findSpanIndex = (spans, spanId) => + spans.findIndex((span) => span.spanId === spanId); const propTypes = { traceSummary: detailedTraceSummaryPropTypes.isRequired, @@ -34,34 +35,44 @@ const TraceSummary = React.memo(({ traceSummary }) => { const [rootSpanIndex, setRootSpanIndex] = useState(0); const isRerooted = rootSpanIndex !== 0; const [currentSpanIndex, setCurrentSpanIndex] = useState(0); - const [childrenHiddenSpanIndices, setChildrenHiddenSpanIndices] = useState({}); + const [childrenHiddenSpanIndices, setChildrenHiddenSpanIndices] = useState( + {}, + ); const [isSpanDetailOpened, setIsSpanDetailOpened] = useState(true); const traceTimelineWidthPercent = isSpanDetailOpened ? 60 : 100; - const handleChildrenToggle = useCallback((spanId) => { - const spanIndex = findSpanIndex(traceSummary.spans, spanId); - setChildrenHiddenSpanIndices((prev) => ({ - ...prev, - [spanIndex]: !prev[spanIndex], - })); - }, [traceSummary.spans]); + const handleChildrenToggle = useCallback( + (spanId) => { + const spanIndex = findSpanIndex(traceSummary.spans, spanId); + setChildrenHiddenSpanIndices((prev) => ({ + ...prev, + [spanIndex]: !prev[spanIndex], + })); + }, + [traceSummary.spans], + ); const handleResetRerootButtonClick = useCallback(() => { setRootSpanIndex(0); }, []); - const handleTimelineRowClick = useCallback((spanId) => { - const idx = traceSummary.spans.findIndex((span) => span.spanId === spanId); - if (isRootedTrace && currentSpanIndex === idx) { - if (rootSpanIndex === idx) { - setRootSpanIndex(0); - } else { - setRootSpanIndex(idx); + const handleTimelineRowClick = useCallback( + (spanId) => { + const idx = traceSummary.spans.findIndex( + (span) => span.spanId === spanId, + ); + if (isRootedTrace && currentSpanIndex === idx) { + if (rootSpanIndex === idx) { + setRootSpanIndex(0); + } else { + setRootSpanIndex(idx); + } } - } - setCurrentSpanIndex(idx); - setIsSpanDetailOpened(true); - }, [currentSpanIndex, isRootedTrace, traceSummary.spans, rootSpanIndex]); + setCurrentSpanIndex(idx); + setIsSpanDetailOpened(true); + }, + [currentSpanIndex, isRootedTrace, traceSummary.spans, rootSpanIndex], + ); const rerootedTree = useMemo(() => { // If the trace does not have a root span, the trace is not filtered anymore @@ -100,24 +111,33 @@ const TraceSummary = React.memo(({ traceSummary }) => { }, [rerootedTree, childrenHiddenSpanIndices, traceSummary.spans]); const childrenHiddenSpanIds = React.useMemo( - () => Object.keys(childrenHiddenSpanIndices) - .filter((spanIndex) => !!childrenHiddenSpanIndices[spanIndex]) - .reduce((acc, spanIndex) => { - acc[traceSummary.spans[spanIndex].spanId] = true; - return acc; - }, {}), + () => + Object.keys(childrenHiddenSpanIndices) + .filter((spanIndex) => !!childrenHiddenSpanIndices[spanIndex]) + .reduce((acc, spanIndex) => { + acc[traceSummary.spans[spanIndex].spanId] = true; + return acc; + }, {}), [traceSummary.spans, childrenHiddenSpanIndices], ); // Find the minumum and maximum timestamps in the shown spans. - const startTs = useMemo(() => minBy(rerootedTree, 'timestamp').timestamp, [rerootedTree]); - const endTs = useMemo(() => rerootedTree.map((span) => { - let ts = span.timestamp; - if (span.duration) { - ts += span.duration; - } - return ts; - }).reduce((a, b) => Math.max(a, b)), [rerootedTree]); + const startTs = useMemo(() => minBy(rerootedTree, 'timestamp').timestamp, [ + rerootedTree, + ]); + const endTs = useMemo( + () => + rerootedTree + .map((span) => { + let ts = span.timestamp; + if (span.duration) { + ts += span.duration; + } + return ts; + }) + .reduce((a, b) => Math.max(a, b)), + [rerootedTree], + ); const handleSpanDetailToggle = useCallback(() => { setIsSpanDetailOpened((prev) => !prev); @@ -148,10 +168,17 @@ const TraceSummary = React.memo(({ traceSummary }) => { return ( <> - + - + { /> - { - ({ height, width }) => ( - - - - ) - } + {({ height, width }) => ( + + + + )} - { - ({ height, width }) => ( - - - - ) - } + {({ height, width }) => ( + + + + )} diff --git a/zipkin-lens/src/components/TracePage/TraceSummaryHeader.jsx b/zipkin-lens/src/components/TracePage/TraceSummaryHeader.jsx index 11631a0b3e5..62fb65c07c8 100644 --- a/zipkin-lens/src/components/TracePage/TraceSummaryHeader.jsx +++ b/zipkin-lens/src/components/TracePage/TraceSummaryHeader.jsx @@ -100,9 +100,10 @@ const TraceSummaryHeader = React.memo(({ traceSummary, rootSpanIndex }) => { const { i18n } = useLingui(); const config = useUiConfig(); - const logsUrl = (config.logsUrl && traceSummary) - ? config.logsUrl.replace('{traceId}', traceSummary.traceId) - : undefined; + const logsUrl = + config.logsUrl && traceSummary + ? config.logsUrl.replace('{traceId}', traceSummary.traceId) + : undefined; const handleSaveButtonClick = useCallback(() => { if (!traceSummary || !traceSummary.traceId) { @@ -124,48 +125,46 @@ const TraceSummaryHeader = React.memo(({ traceSummary, rootSpanIndex }) => { const traceInfo = traceSummary ? ( - { - [ - { label: i18n._(t`Duration`), value: traceSummary.durationStr }, - { label: i18n._(t`Services`), value: traceSummary.serviceNameAndSpanCounts.length }, - { label: i18n._(t`Depth`), value: traceSummary.depth }, - { label: i18n._(t`Total Spans`), value: traceSummary.spans.length }, - { - label: i18n._(t`Trace ID`), - value: rootSpanIndex === 0 + {[ + { label: i18n._(t`Duration`), value: traceSummary.durationStr }, + { + label: i18n._(t`Services`), + value: traceSummary.serviceNameAndSpanCounts.length, + }, + { label: i18n._(t`Depth`), value: traceSummary.depth }, + { label: i18n._(t`Total Spans`), value: traceSummary.spans.length }, + { + label: i18n._(t`Trace ID`), + value: + rootSpanIndex === 0 ? traceSummary.traceId : `${traceSummary.traceId} - ${traceSummary.spans[rootSpanIndex].spanId}`, - }, - ].map((entry) => ( - - - {`${entry.label}:`} - - - {entry.value} - - - )) - } + }, + ].map((entry) => ( + + {`${entry.label}:`} + {entry.value} + + ))} - ) :
; + ) : ( +
+ ); return ( - { - traceSummary ? ( - <> - - {traceSummary.rootSpan.serviceName} - - - {` : ${traceSummary.rootSpan.spanName}`} - - - ) : null - } + {traceSummary ? ( + <> + + {traceSummary.rootSpan.serviceName} + + + {` : ${traceSummary.rootSpan.spanName}`} + + + ) : null} @@ -178,15 +177,32 @@ const TraceSummaryHeader = React.memo(({ traceSummary, rootSpanIndex }) => { - {logsUrl && ( - diff --git a/zipkin-lens/src/components/TracePage/TraceTimeline/TimeMarker.jsx b/zipkin-lens/src/components/TracePage/TraceTimeline/TimeMarker.jsx index 0da82a9d057..da6df485c8e 100644 --- a/zipkin-lens/src/components/TracePage/TraceTimeline/TimeMarker.jsx +++ b/zipkin-lens/src/components/TracePage/TraceTimeline/TimeMarker.jsx @@ -14,7 +14,11 @@ import React from 'react'; import { makeStyles } from '@material-ui/styles'; -import { spanTreeWidthPercent, serviceNameWidthPercent, timelineWidthPercent } from '../sizing'; +import { + spanTreeWidthPercent, + serviceNameWidthPercent, + timelineWidthPercent, +} from '../sizing'; const numTimeMarkers = 4; @@ -31,8 +35,10 @@ const TimeMarker = React.memo(() => { for (let i = 0; i < numTimeMarkers; i += 1) { const portion = i / (numTimeMarkers - 1); - const xPercent = spanTreeWidthPercent - + serviceNameWidthPercent + (timelineWidthPercent * portion); + const xPercent = + spanTreeWidthPercent + + serviceNameWidthPercent + + timelineWidthPercent * portion; timeMarkers.push( { />, ); } - return ({timeMarkers}); + return {timeMarkers}; }); export default TimeMarker; diff --git a/zipkin-lens/src/components/TracePage/TraceTimeline/TraceTimeline.jsx b/zipkin-lens/src/components/TracePage/TraceTimeline/TraceTimeline.jsx index 59df8c49f5c..e92377fe783 100644 --- a/zipkin-lens/src/components/TracePage/TraceTimeline/TraceTimeline.jsx +++ b/zipkin-lens/src/components/TracePage/TraceTimeline/TraceTimeline.jsx @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -32,26 +32,26 @@ const propTypes = { endTs: PropTypes.number.isRequired, }; -const TraceTimeline = React.memo(({ - currentSpanId, - spans, - depth, - childrenHiddenSpanIds, - isRootedTrace, - onRowClick, - onChildrenToggle, - startTs, - endTs, -}) => ( - - - { - spans.map((span, idx) => ( +const TraceTimeline = React.memo( + ({ + currentSpanId, + spans, + depth, + childrenHiddenSpanIds, + isRootedTrace, + onRowClick, + onChildrenToggle, + startTs, + endTs, + }) => ( + + + {spans.map((span, idx) => ( - )) - } - { - isRootedTrace ? ( + ))} + {isRootedTrace ? ( - ) : null - } - -)); + ) : null} + + ), +); TraceTimeline.propTypes = propTypes; diff --git a/zipkin-lens/src/components/TracePage/TraceTimeline/TraceTimelineRow.jsx b/zipkin-lens/src/components/TracePage/TraceTimeline/TraceTimelineRow.jsx index 36337c5280a..44ae17ad775 100644 --- a/zipkin-lens/src/components/TracePage/TraceTimeline/TraceTimelineRow.jsx +++ b/zipkin-lens/src/components/TracePage/TraceTimeline/TraceTimelineRow.jsx @@ -69,8 +69,8 @@ const calculateLeftAndWidth = (startTs, endTs, spanDuration, spanTimestamp) => { const duration = endTs - startTs; if (spanDuration) { return { - width: Math.max(spanDuration / duration * 100, minWidth), - left: (spanTimestamp - startTs) / duration * 100, + width: Math.max((spanDuration / duration) * 100, minWidth), + left: ((spanTimestamp - startTs) / duration) * 100, }; } // If duration is 0, it can be considered that this span is the only @@ -86,79 +86,80 @@ const calculateLeftAndWidth = (startTs, endTs, spanDuration, spanTimestamp) => { // to display it in the UI. return { width: minWidth, - left: (spanTimestamp - startTs) / duration * 100, + left: ((spanTimestamp - startTs) / duration) * 100, }; }; -const TraceTimelineRow = React.memo(({ - span, - index, - onRowClick, - isFocused, - startTs, - endTs, -}) => { - const classes = useStyles(); - const { left, width } = useMemo( - () => calculateLeftAndWidth(startTs, endTs, span.duration, span.timestamp), - [startTs, endTs, span.duration, span.timestamp], - ); - const handleClick = useCallback(() => onRowClick(span.spanId), [onRowClick, span.spanId]); - const durationStr = span.durationStr ? `[${span.durationStr}]` : ''; - const isTextLeft = endTs - span.timestamp > (endTs - startTs) / 2; +const TraceTimelineRow = React.memo( + ({ span, index, onRowClick, isFocused, startTs, endTs }) => { + const classes = useStyles(); + const { left, width } = useMemo( + () => + calculateLeftAndWidth(startTs, endTs, span.duration, span.timestamp), + [startTs, endTs, span.duration, span.timestamp], + ); + const handleClick = useCallback(() => onRowClick(span.spanId), [ + onRowClick, + span.spanId, + ]); + const durationStr = span.durationStr ? `[${span.durationStr}]` : ''; + const isTextLeft = endTs - span.timestamp > (endTs - startTs) / 2; - return ( - - - - { - isTextLeft ? ( + return ( + + + + {isTextLeft ? ( {`${span.spanName} ${durationStr}`} ) : ( {`${span.spanName} ${durationStr}`} - ) - } - - - ); -}); + )} + + + ); + }, +); TraceTimelineRow.propTypes = propTypes; diff --git a/zipkin-lens/src/components/TracePage/TraceTimeline/TraceTree.jsx b/zipkin-lens/src/components/TracePage/TraceTimeline/TraceTree.jsx index 577ef653db3..ded428ececb 100644 --- a/zipkin-lens/src/components/TracePage/TraceTimeline/TraceTree.jsx +++ b/zipkin-lens/src/components/TracePage/TraceTimeline/TraceTree.jsx @@ -169,7 +169,11 @@ export const buildTraceTree = (spans, childrenHiddenSpanIds) => { // | // |---B horizontalLineDataList.push({ x: spans[0].depth, y: 0 }); - serviceNameDataList.push({ x: spans[0].depth, y: 0, serviceName: spans[0].serviceName }); + serviceNameDataList.push({ + x: spans[0].depth, + y: 0, + serviceName: spans[0].serviceName, + }); if (childrenHiddenSpanIds[spans[0].spanId]) { buttonDataList.push({ x: spans[0].depth, @@ -239,24 +243,22 @@ const useStyles = makeStyles((theme) => ({ }, })); -const TraceTree = React.memo(({ - spans, - depth, - childrenHiddenSpanIds, - onChildrenToggle, -}) => { - const classes = useStyles(); - const { - horizontalLineDataList, - verticalLineDataList, - buttonDataList, - serviceNameDataList, - } = useMemo(() => buildTraceTree(spans, childrenHiddenSpanIds), [spans, childrenHiddenSpanIds]); +const TraceTree = React.memo( + ({ spans, depth, childrenHiddenSpanIds, onChildrenToggle }) => { + const classes = useStyles(); + const { + horizontalLineDataList, + verticalLineDataList, + buttonDataList, + serviceNameDataList, + } = useMemo(() => buildTraceTree(spans, childrenHiddenSpanIds), [ + spans, + childrenHiddenSpanIds, + ]); - return ( - - { - horizontalLineDataList.map(({ x, y }) => ( + return ( + + {horizontalLineDataList.map(({ x, y }) => ( - )) - } - { - serviceNameDataList.map(({ x, y, serviceName }) => ( + ))} + {serviceNameDataList.map(({ x, y, serviceName }) => ( - )) - } - { - verticalLineDataList.map(({ x, y1, y2 }) => ( + ))} + {verticalLineDataList.map(({ x, y1, y2 }) => ( - )) - } - { - buttonDataList.map(({ - x, - y, - spanId, - isClosed, - }) => ( + ))} + {buttonDataList.map(({ x, y, spanId, isClosed }) => ( - { - isClosed ? ( - - ) : null - } + {isClosed ? ( + + ) : null} - )) - } - - ); -}); + ))} + + ); + }, +); TraceTree.propTypes = propTypes; diff --git a/zipkin-lens/src/components/TracePage/TraceTimelineHeader/TimeMarker.jsx b/zipkin-lens/src/components/TracePage/TraceTimelineHeader/TimeMarker.jsx index 0aefe6aa568..4bc66cc6328 100644 --- a/zipkin-lens/src/components/TracePage/TraceTimelineHeader/TimeMarker.jsx +++ b/zipkin-lens/src/components/TracePage/TraceTimelineHeader/TimeMarker.jsx @@ -17,7 +17,11 @@ import { makeStyles } from '@material-ui/styles'; import Box from '@material-ui/core/Box'; import classNames from 'classnames'; -import { spanTreeWidthPercent, timelineWidthPercent, serviceNameWidthPercent } from '../sizing'; +import { + spanTreeWidthPercent, + timelineWidthPercent, + serviceNameWidthPercent, +} from '../sizing'; import { formatDuration } from '../../../util/timestamp'; const propTypes = { @@ -50,7 +54,7 @@ const TimeMarker = React.memo(({ startTs, endTs }) => { const timeMarkers = []; for (let i = 0; i < numTimeMarkers; i += 1) { - const label = startTs + ((i / (numTimeMarkers - 1)) * (endTs - startTs)); + const label = startTs + (i / (numTimeMarkers - 1)) * (endTs - startTs); const portion = i / (numTimeMarkers - 1); timeMarkers.push( @@ -64,12 +68,9 @@ const TimeMarker = React.memo(({ startTs, endTs }) => { = 1 }, - ) - } + className={classNames(classes.label, { + [classes['label--last']]: portion >= 1, + })} data-testid="TimeMarker-label" > {formatDuration(label)} diff --git a/zipkin-lens/src/components/TracePage/TraceTimelineHeader/TimeMarker.test.jsx b/zipkin-lens/src/components/TracePage/TraceTimelineHeader/TimeMarker.test.jsx index e971e85aed4..e0a7b8d3f70 100644 --- a/zipkin-lens/src/components/TracePage/TraceTimelineHeader/TimeMarker.test.jsx +++ b/zipkin-lens/src/components/TracePage/TraceTimelineHeader/TimeMarker.test.jsx @@ -26,8 +26,8 @@ describe('', () => { const markers = queryAllByTestId('TimeMarker-marker'); expect(markers.length).toBe(4); expect(markers[0]).toHaveStyle('left: 0%'); - expect(markers[1]).toHaveStyle(`left: ${1 / 3 * 100}%`); - expect(markers[2]).toHaveStyle(`left: ${2 / 3 * 100}%`); + expect(markers[1]).toHaveStyle(`left: ${(1 / 3) * 100}%`); + expect(markers[2]).toHaveStyle(`left: ${(2 / 3) * 100}%`); expect(markers[3]).toHaveStyle('left: 100%'); }); it('should render labels correctly', () => { diff --git a/zipkin-lens/src/components/TracePage/TraceTimelineHeader/TraceTimelineHeader.jsx b/zipkin-lens/src/components/TracePage/TraceTimelineHeader/TraceTimelineHeader.jsx index 683aff49917..aa08475cdea 100644 --- a/zipkin-lens/src/components/TracePage/TraceTimelineHeader/TraceTimelineHeader.jsx +++ b/zipkin-lens/src/components/TracePage/TraceTimelineHeader/TraceTimelineHeader.jsx @@ -51,59 +51,59 @@ const useStyles = makeStyles((theme) => ({ }, })); -const TraceTimelineHeader = React.memo(({ - startTs, - endTs, - isRerooted, - isRootedTrace, - onResetRerootButtonClick, - isSpanDetailOpened, - onSpanDetailToggle, - onCollapseButtonClick, - onExpandButtonClick, -}) => { - const classes = useStyles(); +const TraceTimelineHeader = React.memo( + ({ + startTs, + endTs, + isRerooted, + isRootedTrace, + onResetRerootButtonClick, + isSpanDetailOpened, + onSpanDetailToggle, + onCollapseButtonClick, + onExpandButtonClick, + }) => { + const classes = useStyles(); - return ( - - - - - - - - { - (isRootedTrace && isRerooted) ? ( + return ( + + + + + + + + {isRootedTrace && isRerooted ? ( - ) : null - } + ) : null} + + - + - - - ); -}); + ); + }, +); TraceTimelineHeader.propTypes = propTypes; diff --git a/zipkin-lens/src/components/TracePage/sizing.js b/zipkin-lens/src/components/TracePage/sizing.js index b47fc3ae8fd..fcbc04bba75 100644 --- a/zipkin-lens/src/components/TracePage/sizing.js +++ b/zipkin-lens/src/components/TracePage/sizing.js @@ -31,16 +31,21 @@ export const spanTreeWidthPercent = 10; // % export const serviceNameWidthPercent = 18; // % export const timelineRightMarginPercent = 2; // % -export const timelineWidthPercent = 100 - (spanTreeWidthPercent + serviceNameWidthPercent + timelineRightMarginPercent); +export const timelineWidthPercent = + 100 - + (spanTreeWidthPercent + serviceNameWidthPercent + timelineRightMarginPercent); export const spanBarRowHeight = 40; // px export const spanBarHeight = spanBarRowHeight - 4; // px; -export const spanTreeLineWidthPercentPerDepth = (depth) => spanTreeWidthPercent / (depth + 1); // % +export const spanTreeLineWidthPercentPerDepth = (depth) => + spanTreeWidthPercent / (depth + 1); // % export const serviceNameBadgeWidth = serviceNameWidthPercent - 2; export const serviceNameBadgeHeight = 24; -export const serviceNameBadgeTranslate = `translate(16,${-serviceNameBadgeHeight / 2})`; // px +export const serviceNameBadgeTranslate = `translate(16,${-serviceNameBadgeHeight / + 2})`; // px export const spanToggleButtonLengthOfSide = 16; // px -export const spanToggleButtonTranslate = `translate(${-spanToggleButtonLengthOfSide / 2},${-spanToggleButtonLengthOfSide / 2})`; // px +export const spanToggleButtonTranslate = `translate(${-spanToggleButtonLengthOfSide / + 2},${-spanToggleButtonLengthOfSide / 2})`; // px // // _ _ _ @@ -62,8 +67,13 @@ export const spanToggleButtonTranslate = `translate(${-spanToggleButtonLengthOfS // export const spanBarRowOffsetY = (index) => index * spanBarRowHeight; // px export const spanBarOffsetY = (index) => spanBarRowOffsetY(index) + 2; // px -export const spanBarLinePosY = (index) => spanBarRowOffsetY(index) + (spanBarRowHeight / 2); // px -export const spanBarWidthPercent = (width) => timelineWidthPercent * (width / 100); // % -export const spanBarOffsetXPercent = (left) => spanTreeWidthPercent + serviceNameWidthPercent + (timelineWidthPercent * (left / 100)); // % +export const spanBarLinePosY = (index) => + spanBarRowOffsetY(index) + spanBarRowHeight / 2; // px +export const spanBarWidthPercent = (width) => + timelineWidthPercent * (width / 100); // % +export const spanBarOffsetXPercent = (left) => + spanTreeWidthPercent + + serviceNameWidthPercent + + timelineWidthPercent * (left / 100); // % export const timelineHeight = (spanCounts) => spanBarRowHeight * spanCounts; // px diff --git a/zipkin-lens/src/components/UiConfig/UiConfig.jsx b/zipkin-lens/src/components/UiConfig/UiConfig.jsx index 0b408deec2b..bc3d844f4f3 100644 --- a/zipkin-lens/src/components/UiConfig/UiConfig.jsx +++ b/zipkin-lens/src/components/UiConfig/UiConfig.jsx @@ -19,7 +19,9 @@ import fetchResource from '../../util/fetch-resource'; const ConfigContext = React.createContext(); -const configResource = fetchResource(fetch(UI_CONFIG).then((response) => response.json())); +const configResource = fetchResource( + fetch(UI_CONFIG).then((response) => response.json()), +); const propTypes = { children: PropTypes.element.isRequired, @@ -43,9 +45,7 @@ export const UiConfig = ({ children }) => { }); return ( - - {children} - + {children} ); }; diff --git a/zipkin-lens/src/components/UiConfig/UiConfig.test.js b/zipkin-lens/src/components/UiConfig/UiConfig.test.js index 0b6ac3ea065..c6dabc3c05c 100644 --- a/zipkin-lens/src/components/UiConfig/UiConfig.test.js +++ b/zipkin-lens/src/components/UiConfig/UiConfig.test.js @@ -38,9 +38,7 @@ const UiConfig = () => { - {(value) => ( -
{JSON.stringify(value)}
- )} + {(value) =>
{JSON.stringify(value)}
}
diff --git a/zipkin-lens/src/constants/action-types.js b/zipkin-lens/src/constants/action-types.js index b4b1d7c9296..a06c8942df5 100644 --- a/zipkin-lens/src/constants/action-types.js +++ b/zipkin-lens/src/constants/action-types.js @@ -39,21 +39,32 @@ export const FETCH_DEPENDENCIES_SUCCESS = 'FETCH_DEPENDENCIES_SUCCESS'; export const FETCH_DEPENDENCIES_FAILURE = 'FETCH_DEPENDENCIES_FAILURE'; export const CLEAR_DEPENDENCIES = 'CLEAR_DEPENDENCIES'; -export const FETCH_AUTOCOMPLETE_KEYS_REQUEST = 'FETCH_AUTOCOMPLETE_KEYS_REQUEST'; -export const FETCH_AUTOCOMPLETE_KEYS_SUCCESS = 'FETCH_AUTOCOMPLETE_KEYS_SUCCESS'; -export const FETCH_AUTOCOMPLETE_KEYS_FAILURE = 'FETCH_AUTOCOMPLETE_KEYS_FAILURE'; +export const FETCH_AUTOCOMPLETE_KEYS_REQUEST = + 'FETCH_AUTOCOMPLETE_KEYS_REQUEST'; +export const FETCH_AUTOCOMPLETE_KEYS_SUCCESS = + 'FETCH_AUTOCOMPLETE_KEYS_SUCCESS'; +export const FETCH_AUTOCOMPLETE_KEYS_FAILURE = + 'FETCH_AUTOCOMPLETE_KEYS_FAILURE'; -export const FETCH_AUTOCOMPLETE_VALUES_REQUEST = 'FETCH_AUTOCOMPLETE_VALUES_REQUEST'; -export const FETCH_AUTOCOMPLETE_VALUES_SUCCESS = 'FETCH_AUTOCOMPLETE_VALUES_SUCCESS'; -export const FETCH_AUTOCOMPLETE_VALUES_FAILURE = 'FETCH_AUTOCOMPLETE_VALUES_FAILURE'; +export const FETCH_AUTOCOMPLETE_VALUES_REQUEST = + 'FETCH_AUTOCOMPLETE_VALUES_REQUEST'; +export const FETCH_AUTOCOMPLETE_VALUES_SUCCESS = + 'FETCH_AUTOCOMPLETE_VALUES_SUCCESS'; +export const FETCH_AUTOCOMPLETE_VALUES_FAILURE = + 'FETCH_AUTOCOMPLETE_VALUES_FAILURE'; -export const GLOBAL_SEARCH_SET_LOOKBACK_CONDITION = 'GLOBAL_SEARCH_SET_LOOKBACK_CONDITION'; -export const GLOBAL_SEARCH_SET_LIMIT_CONDITION = 'GLOBAL_SEARCH_SET_LIMIT_CONDITION'; +export const GLOBAL_SEARCH_SET_LOOKBACK_CONDITION = + 'GLOBAL_SEARCH_SET_LOOKBACK_CONDITION'; +export const GLOBAL_SEARCH_SET_LIMIT_CONDITION = + 'GLOBAL_SEARCH_SET_LIMIT_CONDITION'; export const GLOBAL_SEARCH_ADD_CONDITION = 'GLOBAL_SEARCH_ADD_CONDITION'; export const GLOBAL_SEARCH_DELETE_CONDITION = 'GLOBAL_SEARCH_DELETE_CONDITION'; export const GLOBAL_SEARCH_SET_CONDITIONS = 'GLOBAL_SEARCH_SET_CONDITIONS'; -export const GLOBAL_SEARCH_CHANGE_CONDITION_KEY = 'GLOBAL_SEARCH_CHANGE_CONDITION_KEY'; -export const GLOBAL_SEARCH_CHANGE_CONDITION_VALUE = 'GLOBAL_SEARCH_CHANGE_CONDITION_VALUE'; +export const GLOBAL_SEARCH_CHANGE_CONDITION_KEY = + 'GLOBAL_SEARCH_CHANGE_CONDITION_KEY'; +export const GLOBAL_SEARCH_CHANGE_CONDITION_VALUE = + 'GLOBAL_SEARCH_CHANGE_CONDITION_VALUE'; export const TRACE_VIEWER__LOAD_TRACE = 'TRACE_VIEWER__LOAD_TRACE'; -export const TRACE_VIEWER__LOAD_TRACE_FAILURE = 'TRACE_VIEWER__LOAD_TRACE_FAILURE'; +export const TRACE_VIEWER__LOAD_TRACE_FAILURE = + 'TRACE_VIEWER__LOAD_TRACE_FAILURE'; diff --git a/zipkin-lens/src/hooks/index.js b/zipkin-lens/src/hooks/index.js index e5d532a2152..64e0949e36e 100644 --- a/zipkin-lens/src/hooks/index.js +++ b/zipkin-lens/src/hooks/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -16,9 +16,4 @@ import useEffectOnce from './useEffectOnce'; import useMount from './useMount'; import useUnmount from './useUnmount'; -export { - useDocumentTitle, - useEffectOnce, - useMount, - useUnmount, -}; +export { useDocumentTitle, useEffectOnce, useMount, useUnmount }; diff --git a/zipkin-lens/src/hooks/useMount.js b/zipkin-lens/src/hooks/useMount.js index 689f831b580..52bba74afa9 100644 --- a/zipkin-lens/src/hooks/useMount.js +++ b/zipkin-lens/src/hooks/useMount.js @@ -13,8 +13,9 @@ */ import useEffectOnce from './useEffectOnce'; -const useMount = (f) => useEffectOnce(() => { - f(); -}); +const useMount = (f) => + useEffectOnce(() => { + f(); + }); export default useMount; diff --git a/zipkin-lens/src/prop-types/index.js b/zipkin-lens/src/prop-types/index.js index 7956608acc4..b39ca673f82 100644 --- a/zipkin-lens/src/prop-types/index.js +++ b/zipkin-lens/src/prop-types/index.js @@ -18,9 +18,7 @@ export const spanTagPropTypes = PropTypes.shape({ value: PropTypes.string.isRequired, }); -export const spanTagsPropTypes = PropTypes.arrayOf( - spanTagPropTypes, -); +export const spanTagsPropTypes = PropTypes.arrayOf(spanTagPropTypes); export const spanAnnotationPropTypes = PropTypes.shape({ value: PropTypes.string.isRequired, @@ -54,9 +52,7 @@ export const detailedSpanPropTypes = PropTypes.shape({ left: PropTypes.number.isRequired, }); -export const detailedSpansPropTypes = PropTypes.arrayOf( - detailedSpanPropTypes, -); +export const detailedSpansPropTypes = PropTypes.arrayOf(detailedSpanPropTypes); export const spanServiceNameSummary = PropTypes.shape({ serviceName: PropTypes.string.isRequired, @@ -79,9 +75,7 @@ export const traceSummaryPropTypes = PropTypes.shape({ width: PropTypes.number.isRequired, }); -export const traceSummariesPropTypes = PropTypes.arrayOf( - traceSummaryPropTypes, -); +export const traceSummariesPropTypes = PropTypes.arrayOf(traceSummaryPropTypes); export const detailedTraceSummaryPropTypes = PropTypes.shape({ traceId: PropTypes.string.isRequired, @@ -95,14 +89,16 @@ export const detailedTraceSummaryPropTypes = PropTypes.shape({ }).isRequired, }); -export const globalSearchConditionsPropTypes = PropTypes.arrayOf(PropTypes.shape({ - key: PropTypes.string, - value: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number, - PropTypes.shape({}), - ]), -})); +export const globalSearchConditionsPropTypes = PropTypes.arrayOf( + PropTypes.shape({ + key: PropTypes.string, + value: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + PropTypes.shape({}), + ]), + }), +); export const globalSearchLookbackConditionPropTypes = PropTypes.shape({ value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), diff --git a/zipkin-lens/src/reducers/autocomplete-keys.test.js b/zipkin-lens/src/reducers/autocomplete-keys.test.js index 0447b8d48a8..6f04add8a84 100644 --- a/zipkin-lens/src/reducers/autocomplete-keys.test.js +++ b/zipkin-lens/src/reducers/autocomplete-keys.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -47,12 +47,15 @@ describe('autocomplete-keys reducer', () => { it('should handle FETCH_AUTOCOMPLETE_KEYS_FAILURE', () => { expect( - reducer({ - isLoading: true, - autocompleteKeys: ['environment', 'phase'], - }, { - type: types.FETCH_AUTOCOMPLETE_KEYS_FAILURE, - }), + reducer( + { + isLoading: true, + autocompleteKeys: ['environment', 'phase'], + }, + { + type: types.FETCH_AUTOCOMPLETE_KEYS_FAILURE, + }, + ), ).toEqual({ isLoading: false, autocompleteKeys: [], diff --git a/zipkin-lens/src/reducers/autocomplete-values.test.js b/zipkin-lens/src/reducers/autocomplete-values.test.js index 693adafa3a3..f318ffdcbbc 100644 --- a/zipkin-lens/src/reducers/autocomplete-values.test.js +++ b/zipkin-lens/src/reducers/autocomplete-values.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -47,12 +47,15 @@ describe('autocomplete-values reducer', () => { it('should handle FETCH_AUTOCOMPLETE_VALUES_FAILURE', () => { expect( - reducer({ - isLoading: true, - autocompleteValues: ['alpha', 'beta', 'release'], - }, { - type: types.FETCH_AUTOCOMPLETE_VALUES_FAILURE, - }), + reducer( + { + isLoading: true, + autocompleteValues: ['alpha', 'beta', 'release'], + }, + { + type: types.FETCH_AUTOCOMPLETE_VALUES_FAILURE, + }, + ), ).toEqual({ isLoading: false, autocompleteValues: [], diff --git a/zipkin-lens/src/reducers/dependencies.test.js b/zipkin-lens/src/reducers/dependencies.test.js index 95efbf3311c..0cdaeb4c3d0 100644 --- a/zipkin-lens/src/reducers/dependencies.test.js +++ b/zipkin-lens/src/reducers/dependencies.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -69,32 +69,35 @@ describe('dependencies reducer', () => { }); expect( - reducer({ - isLoading: true, - dependencies: [ - { - parent: 'serviceA', - child: 'serviceB', - callCount: 4, - errorCount: 1, - }, - ], - }, { - type: types.FETCH_DEPENDENCIES_SUCCESS, - dependencies: [ - { - parent: 'service1', - child: 'service2', - callCount: 100, - errorCount: 5, - }, - { - parent: 'service3', - child: 'service2', - callCount: 4, - }, - ], - }), + reducer( + { + isLoading: true, + dependencies: [ + { + parent: 'serviceA', + child: 'serviceB', + callCount: 4, + errorCount: 1, + }, + ], + }, + { + type: types.FETCH_DEPENDENCIES_SUCCESS, + dependencies: [ + { + parent: 'service1', + child: 'service2', + callCount: 100, + errorCount: 5, + }, + { + parent: 'service3', + child: 'service2', + callCount: 4, + }, + ], + }, + ), ).toEqual({ isLoading: false, dependencies: [ @@ -115,24 +118,27 @@ describe('dependencies reducer', () => { it('should handle FETCH_DEPENDENCIES_FAILURE', () => { expect( - reducer({ - isLoading: true, - dependencies: [ - { - parent: 'service1', - child: 'service2', - callCount: 100, - errorCount: 5, - }, - { - parent: 'service3', - child: 'service2', - callCount: 4, - }, - ], - }, { - type: types.FETCH_DEPENDENCIES_FAILURE, - }), + reducer( + { + isLoading: true, + dependencies: [ + { + parent: 'service1', + child: 'service2', + callCount: 100, + errorCount: 5, + }, + { + parent: 'service3', + child: 'service2', + callCount: 4, + }, + ], + }, + { + type: types.FETCH_DEPENDENCIES_FAILURE, + }, + ), ).toEqual({ isLoading: false, dependencies: [], @@ -141,24 +147,27 @@ describe('dependencies reducer', () => { it('should handle CLEAN_DEPENDENCIES', () => { expect( - reducer({ - isLoading: true, - dependencies: [ - { - parent: 'service1', - child: 'service2', - callCount: 100, - errorCount: 5, - }, - { - parent: 'service3', - child: 'service2', - callCount: 4, - }, - ], - }, { - type: types.CLEAR_DEPENDENCIES, - }), + reducer( + { + isLoading: true, + dependencies: [ + { + parent: 'service1', + child: 'service2', + callCount: 100, + errorCount: 5, + }, + { + parent: 'service3', + child: 'service2', + callCount: 4, + }, + ], + }, + { + type: types.CLEAR_DEPENDENCIES, + }, + ), ).toEqual({ isLoading: true, dependencies: [], diff --git a/zipkin-lens/src/reducers/global-search.js b/zipkin-lens/src/reducers/global-search.js index 3f8a78a3232..414d85bfef7 100644 --- a/zipkin-lens/src/reducers/global-search.js +++ b/zipkin-lens/src/reducers/global-search.js @@ -55,10 +55,7 @@ const createGlobalSearch = (config) => { }; return { ...state, - conditions: [ - ...state.conditions, - newCondition, - ], + conditions: [...state.conditions, newCondition], }; } case types.GLOBAL_SEARCH_DELETE_CONDITION: { diff --git a/zipkin-lens/src/reducers/index.js b/zipkin-lens/src/reducers/index.js index c47d9b866c2..203e53b3857 100644 --- a/zipkin-lens/src/reducers/index.js +++ b/zipkin-lens/src/reducers/index.js @@ -24,17 +24,18 @@ import autocompleteKeys from './autocomplete-keys'; import autocompleteValues from './autocomplete-values'; import traceViewer from './trace-viewer'; -const createReducer = (config) => combineReducers({ - remoteServices, - spans, - trace, - traces, - services, - dependencies, - globalSearch: createGlobalSearch(config), - autocompleteKeys, - autocompleteValues, - traceViewer, -}); +const createReducer = (config) => + combineReducers({ + remoteServices, + spans, + trace, + traces, + services, + dependencies, + globalSearch: createGlobalSearch(config), + autocompleteKeys, + autocompleteValues, + traceViewer, + }); export default createReducer; diff --git a/zipkin-lens/src/reducers/remote-services.js b/zipkin-lens/src/reducers/remote-services.js index cb965948a13..f6e4fcf5955 100644 --- a/zipkin-lens/src/reducers/remote-services.js +++ b/zipkin-lens/src/reducers/remote-services.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -24,7 +24,7 @@ const remoteServices = (state = initialState, action) => { return { ...state, isLoading: true, - remoteServices: [], /* Initialize remote services */ + remoteServices: [] /* Initialize remote services */, }; case types.FETCH_REMOTE_SERVICES_SUCCESS: return { diff --git a/zipkin-lens/src/reducers/remote-services.test.js b/zipkin-lens/src/reducers/remote-services.test.js index d9c05b378e9..ecfc441a0c1 100644 --- a/zipkin-lens/src/reducers/remote-services.test.js +++ b/zipkin-lens/src/reducers/remote-services.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -35,13 +35,24 @@ describe('remote services reducer', () => { it('should handle FETCH_REMOTE_SERVICES_SUCCESS', () => { expect( - reducer({ - isLoading: true, - remoteServices: ['remoteService1', 'remoteService2', 'remoteService3'], - }, { - type: types.FETCH_REMOTE_SERVICES_SUCCESS, - remoteServices: ['remoteServiceA', 'remoteServiceB', 'remoteServiceC'], - }), + reducer( + { + isLoading: true, + remoteServices: [ + 'remoteService1', + 'remoteService2', + 'remoteService3', + ], + }, + { + type: types.FETCH_REMOTE_SERVICES_SUCCESS, + remoteServices: [ + 'remoteServiceA', + 'remoteServiceB', + 'remoteServiceC', + ], + }, + ), ).toEqual({ isLoading: false, remoteServices: ['remoteServiceA', 'remoteServiceB', 'remoteServiceC'], @@ -50,12 +61,19 @@ describe('remote services reducer', () => { it('should handle FETCH_REMOTE_SERVICES_FAILURE', () => { expect( - reducer({ - isLoading: true, - remoteServices: ['remoteService1', 'remoteService2', 'remoteService3'], - }, { - type: types.FETCH_REMOTE_SERVICES_FAILURE, - }), + reducer( + { + isLoading: true, + remoteServices: [ + 'remoteService1', + 'remoteService2', + 'remoteService3', + ], + }, + { + type: types.FETCH_REMOTE_SERVICES_FAILURE, + }, + ), ).toEqual({ isLoading: false, remoteServices: [], @@ -64,12 +82,19 @@ describe('remote services reducer', () => { it('should handle CLEAR_REMOTE_SERVICES', () => { expect( - reducer({ - isLoading: false, - remoteServices: ['remoteService1', 'remoteService2', 'remoteService3'], - }, { - type: types.CLEAR_REMOTE_SERVICES, - }), + reducer( + { + isLoading: false, + remoteServices: [ + 'remoteService1', + 'remoteService2', + 'remoteService3', + ], + }, + { + type: types.CLEAR_REMOTE_SERVICES, + }, + ), ).toEqual({ isLoading: false, remoteServices: [], diff --git a/zipkin-lens/src/reducers/services.js b/zipkin-lens/src/reducers/services.js index c5bb5c6c6f3..fab02992347 100644 --- a/zipkin-lens/src/reducers/services.js +++ b/zipkin-lens/src/reducers/services.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -24,7 +24,7 @@ const services = (state = initialState, action) => { return { ...state, isLoading: true, - services: [], /* Initialize services */ + services: [] /* Initialize services */, }; case types.FETCH_SERVICES_SUCCESS: return { diff --git a/zipkin-lens/src/reducers/services.test.js b/zipkin-lens/src/reducers/services.test.js index 6ab3a295924..ad6d37d6e84 100644 --- a/zipkin-lens/src/reducers/services.test.js +++ b/zipkin-lens/src/reducers/services.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -35,13 +35,16 @@ describe('services reducer', () => { it('should handle FETCH_SERVICES_SUCCESS', () => { expect( - reducer({ - isLoading: true, - services: ['service1', 'service2', 'service3'], - }, { - type: types.FETCH_SERVICES_SUCCESS, - services: ['serviceA', 'serviceB', 'serviceC'], - }), + reducer( + { + isLoading: true, + services: ['service1', 'service2', 'service3'], + }, + { + type: types.FETCH_SERVICES_SUCCESS, + services: ['serviceA', 'serviceB', 'serviceC'], + }, + ), ).toEqual({ isLoading: false, services: ['serviceA', 'serviceB', 'serviceC'], @@ -50,12 +53,15 @@ describe('services reducer', () => { it('should handle FETCH_SERVICES_FAILURE', () => { expect( - reducer({ - isLoading: true, - services: ['service1', 'service2', 'service3'], - }, { - type: types.FETCH_SERVICES_FAILURE, - }), + reducer( + { + isLoading: true, + services: ['service1', 'service2', 'service3'], + }, + { + type: types.FETCH_SERVICES_FAILURE, + }, + ), ).toEqual({ isLoading: false, services: [], diff --git a/zipkin-lens/src/reducers/spans.js b/zipkin-lens/src/reducers/spans.js index a13ebc29b4a..657c0fd67e1 100644 --- a/zipkin-lens/src/reducers/spans.js +++ b/zipkin-lens/src/reducers/spans.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -24,7 +24,7 @@ const spans = (state = initialState, action) => { return { ...state, isLoading: true, - spans: [], /* Initialize spans */ + spans: [] /* Initialize spans */, }; case types.FETCH_SPANS_SUCCESS: return { diff --git a/zipkin-lens/src/reducers/spans.test.js b/zipkin-lens/src/reducers/spans.test.js index b1b7fc2d5d8..6d98ba4045b 100644 --- a/zipkin-lens/src/reducers/spans.test.js +++ b/zipkin-lens/src/reducers/spans.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -35,13 +35,16 @@ describe('spans reducer', () => { it('should handle FETCH_SPANS_SUCCESS', () => { expect( - reducer({ - isLoading: true, - spans: ['span1', 'span2', 'span3'], - }, { - type: types.FETCH_SPANS_SUCCESS, - spans: ['spanA', 'spanB', 'spanC'], - }), + reducer( + { + isLoading: true, + spans: ['span1', 'span2', 'span3'], + }, + { + type: types.FETCH_SPANS_SUCCESS, + spans: ['spanA', 'spanB', 'spanC'], + }, + ), ).toEqual({ isLoading: false, spans: ['spanA', 'spanB', 'spanC'], @@ -50,12 +53,15 @@ describe('spans reducer', () => { it('should handle FETCH_SPANS_FAILURE', () => { expect( - reducer({ - isLoading: true, - spans: ['span1', 'span2', 'span3'], - }, { - type: types.FETCH_SPANS_FAILURE, - }), + reducer( + { + isLoading: true, + spans: ['span1', 'span2', 'span3'], + }, + { + type: types.FETCH_SPANS_FAILURE, + }, + ), ).toEqual({ isLoading: false, spans: [], @@ -64,12 +70,15 @@ describe('spans reducer', () => { it('should handle CLEAR_SPANS', () => { expect( - reducer({ - isLoading: false, - spans: ['span1', 'span2', 'span3'], - }, { - type: types.CLEAR_SPANS, - }), + reducer( + { + isLoading: false, + spans: ['span1', 'span2', 'span3'], + }, + { + type: types.CLEAR_SPANS, + }, + ), ).toEqual({ isLoading: false, spans: [], diff --git a/zipkin-lens/src/reducers/trace.test.js b/zipkin-lens/src/reducers/trace.test.js index a7d28dcf0fa..0b02190d676 100644 --- a/zipkin-lens/src/reducers/trace.test.js +++ b/zipkin-lens/src/reducers/trace.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -35,17 +35,20 @@ describe('trace reducer', () => { it('should handle TRACE_LOAD_SUCCESS', () => { expect( - reducer({ - isLoading: true, - traceSummary: { - traceId: 'd050e0d52326cf81', + reducer( + { + isLoading: true, + traceSummary: { + traceId: 'd050e0d52326cf81', + }, }, - }, { - type: types.TRACE_LOAD_SUCCESS, - traceSummary: { - traceId: 'c020e0d52326cf84', + { + type: types.TRACE_LOAD_SUCCESS, + traceSummary: { + traceId: 'c020e0d52326cf84', + }, }, - }), + ), ).toEqual({ isLoading: false, traceSummary: { @@ -56,14 +59,17 @@ describe('trace reducer', () => { it('should handle TRACE_LOAD_FAILURE', () => { expect( - reducer({ - isLoading: true, - traceSummary: { - traceId: 'c020e0d52326cf84', + reducer( + { + isLoading: true, + traceSummary: { + traceId: 'c020e0d52326cf84', + }, }, - }, { - type: types.TRACE_LOAD_FAILURE, - }), + { + type: types.TRACE_LOAD_FAILURE, + }, + ), ).toEqual({ isLoading: false, traceSummary: null, diff --git a/zipkin-lens/src/reducers/traces.test.js b/zipkin-lens/src/reducers/traces.test.js index 8705d1c0b5d..dbc8e182c21 100644 --- a/zipkin-lens/src/reducers/traces.test.js +++ b/zipkin-lens/src/reducers/traces.test.js @@ -41,43 +41,50 @@ describe('traces reducer', () => { it('should handle TRACES_LOAD_SUCCESS', () => { expect( - reducer({ - isLoading: true, - traces: [ - [ + reducer( + { + isLoading: true, + traces: [ + [ + { + traceId: 'd050e0d52326cf81', // Omit details + }, + ], + ], + traceSummaries: [ { traceId: 'd050e0d52326cf81', // Omit details }, ], - ], - traceSummaries: [{ - traceId: 'd050e0d52326cf81', // Omit details - }], - correctedTraceMap: { - d050e0d52326cf81: {}, // Omit details - }, - lastQueryParams: { - serviceName: 'serviceA', + correctedTraceMap: { + d050e0d52326cf81: {}, // Omit details + }, + lastQueryParams: { + serviceName: 'serviceA', + }, }, - }, { - type: types.TRACES_LOAD_SUCCESS, - traces: [ - [ + { + type: types.TRACES_LOAD_SUCCESS, + traces: [ + [ + { + traceId: 'c020e0d52326cf84', // Omit details + }, + ], + ], + traceSummaries: [ { traceId: 'c020e0d52326cf84', // Omit details }, ], - ], - traceSummaries: [{ - traceId: 'c020e0d52326cf84', // Omit details - }], - correctedTraceMap: { - c020e0d52326cf84: {}, // Omit details - }, - lastQueryParams: { - serviceName: 'serviceB', + correctedTraceMap: { + c020e0d52326cf84: {}, // Omit details + }, + lastQueryParams: { + serviceName: 'serviceB', + }, }, - }), + ), ).toEqual({ isLoading: false, traces: [ @@ -87,9 +94,11 @@ describe('traces reducer', () => { }, ], ], - traceSummaries: [{ - traceId: 'c020e0d52326cf84', - }], + traceSummaries: [ + { + traceId: 'c020e0d52326cf84', + }, + ], correctedTraceMap: { c020e0d52326cf84: {}, }, @@ -101,18 +110,21 @@ describe('traces reducer', () => { it('should handle TRACES_LOAD_FAILURE', () => { expect( - reducer({ - isLoading: true, - traces: [ - [ - { - traceId: 'c020e0d52326cf84', - }, + reducer( + { + isLoading: true, + traces: [ + [ + { + traceId: 'c020e0d52326cf84', + }, + ], ], - ], - }, { - type: types.TRACES_LOAD_FAILURE, - }), + }, + { + type: types.TRACES_LOAD_FAILURE, + }, + ), ).toEqual({ isLoading: false, traces: [], @@ -123,17 +135,22 @@ describe('traces reducer', () => { }); it('should handle CLEAR_TRACES', () => { - expect(reducer({ - isLoading: true, - traces: [ - [ - { - traceId: 'c020e0d52326cf84', - }, - ], - ], - }, { - type: types.CLEAR_TRACES, - })).toEqual(initialState); + expect( + reducer( + { + isLoading: true, + traces: [ + [ + { + traceId: 'c020e0d52326cf84', + }, + ], + ], + }, + { + type: types.CLEAR_TRACES, + }, + ), + ).toEqual(initialState); }); }); diff --git a/zipkin-lens/src/setupTests.ts b/zipkin-lens/src/setupTests.ts index 13ebb260f1a..c5845271473 100644 --- a/zipkin-lens/src/setupTests.ts +++ b/zipkin-lens/src/setupTests.ts @@ -33,14 +33,15 @@ const { language } = window.navigator; // Mock out createRange until jest-environment-jsdom is updated to latest jsdom // https://github.com/mui-org/material-ui/issues/15726 -document.createRange = () => ({ - setStart: () => {}, - setEnd: () => {}, - commonAncestorContainer: { - nodeName: 'BODY', - ownerDocument: document, - }, -}) as any; // Only partial mock so don't enforce full type. +document.createRange = () => + ({ + setStart: () => {}, + setEnd: () => {}, + commonAncestorContainer: { + nodeName: 'BODY', + ownerDocument: document, + }, + } as any); // Only partial mock so don't enforce full type. beforeAll(() => { delete window.location; @@ -52,11 +53,17 @@ beforeAll(() => { beforeEach(() => { // Set english as browser locale by default. - Object.defineProperty(window.navigator, 'language', { value: 'en-US', configurable: true }); + Object.defineProperty(window.navigator, 'language', { + value: 'en-US', + configurable: true, + }); }); afterAll(() => { // Restore overrides for good measure. - Object.defineProperty(window.navigator, 'language', { value: language, configurable: true }); + Object.defineProperty(window.navigator, 'language', { + value: language, + configurable: true, + }); window.location = location; }); diff --git a/zipkin-lens/src/store/configure-store.js b/zipkin-lens/src/store/configure-store.js index 07fb66f3a57..7ba4c5f9ab8 100644 --- a/zipkin-lens/src/store/configure-store.js +++ b/zipkin-lens/src/store/configure-store.js @@ -17,8 +17,5 @@ import thunk from 'redux-thunk'; import createReducer from '../reducers'; export default function configureStore(config) { - return createStore( - createReducer(config), - applyMiddleware(thunk), - ); + return createStore(createReducer(config), applyMiddleware(thunk)); } diff --git a/zipkin-lens/src/test/util/render-with-default-settings.jsx b/zipkin-lens/src/test/util/render-with-default-settings.jsx index 973662d8c7e..dba92ea92c8 100644 --- a/zipkin-lens/src/test/util/render-with-default-settings.jsx +++ b/zipkin-lens/src/test/util/render-with-default-settings.jsx @@ -32,11 +32,14 @@ const i18n = setupI18n(); i18n.load('en', enMessages); i18n.activate('en'); -export default (ui, { - route = '/', - history = createMemoryHistory({ initialEntries: [route] }), - uiConfig = {}, -} = {}) => { +export default ( + ui, + { + route = '/', + history = createMemoryHistory({ initialEntries: [route] }), + uiConfig = {}, + } = {}, +) => { const store = configureStore({}); const filledConfig = { @@ -52,7 +55,9 @@ export default (ui, { ...uiConfig, }; - const wrapper = ({ children }) => ( // eslint-disable-line react/prop-types + const wrapper = ( + { children }, // eslint-disable-line react/prop-types + ) => ( diff --git a/zipkin-lens/src/util/dependencies-graph.js b/zipkin-lens/src/util/dependencies-graph.js index 22756500e97..77170a49da2 100644 --- a/zipkin-lens/src/util/dependencies-graph.js +++ b/zipkin-lens/src/util/dependencies-graph.js @@ -19,9 +19,7 @@ class Graph { this.edges = []; this._createdTs = moment().valueOf(); - rawDependencies.forEach( - (edge) => this.addEdge(edge), - ); + rawDependencies.forEach((edge) => this.addEdge(edge)); } addEdge(edge) { diff --git a/zipkin-lens/src/util/fetch-resource.js b/zipkin-lens/src/util/fetch-resource.js index 5a6ab0fbab3..9351d06a1c9 100644 --- a/zipkin-lens/src/util/fetch-resource.js +++ b/zipkin-lens/src/util/fetch-resource.js @@ -25,11 +25,14 @@ export default (promise) => { // In Javascript, there is no way to synchronously know whether a promise is resolved. Even if // it's already resolved, we are guaranteed to suspend once. Since it's unlikely the promise has // resolved at this point anyways, it's not a huge deal though. - promise.then((resp) => { - response = resp; - }, (err) => { - error = err; - }); + promise.then( + (resp) => { + response = resp; + }, + (err) => { + error = err; + }, + ); return { read() { diff --git a/zipkin-lens/src/util/locale.test.js b/zipkin-lens/src/util/locale.test.js index cc024c38469..e159e8944b1 100644 --- a/zipkin-lens/src/util/locale.test.js +++ b/zipkin-lens/src/util/locale.test.js @@ -18,7 +18,10 @@ import { getLocale, setLocale } from './locale'; // measure. const setLanguageForTest = (language) => { - Object.defineProperty(window.navigator, 'language', { value: language, configurable: true }); + Object.defineProperty(window.navigator, 'language', { + value: language, + configurable: true, + }); }; test('browser language en-US sets locale en', () => { diff --git a/zipkin-lens/src/util/timestamp.js b/zipkin-lens/src/util/timestamp.js index a949df0ea4f..86f0c35478f 100644 --- a/zipkin-lens/src/util/timestamp.js +++ b/zipkin-lens/src/util/timestamp.js @@ -26,11 +26,13 @@ export const formatDuration = (duration) => { return `${(duration / 1000000).toFixed(3)}s`; }; -export const formatTimestamp = (timestamp) => moment(timestamp / 1000).format('MM/DD HH:mm:ss.SSS'); +export const formatTimestamp = (timestamp) => + moment(timestamp / 1000).format('MM/DD HH:mm:ss.SSS'); // moment.js only supports millisecond precision, however our timestamps have // microsecond precision. So we use moment.js to generate the human readable time // with just milliseconds and then append the last 3 digits of the timestamp // which are the microseconds. // NOTE: a.timestamp % 1000 would save a string conversion but drops leading zeros. -export const formatTimestampMicros = (timestamp) => `${formatTimestamp(timestamp)}_${timestamp.toString().slice(-3)}`; +export const formatTimestampMicros = (timestamp) => + `${formatTimestamp(timestamp)}_${timestamp.toString().slice(-3)}`; diff --git a/zipkin-lens/src/util/trace.js b/zipkin-lens/src/util/trace.js index 162295c7fb1..72050c67b73 100644 --- a/zipkin-lens/src/util/trace.js +++ b/zipkin-lens/src/util/trace.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -19,7 +19,10 @@ export const ensureV2TraceData = (trace) => { if (!first.traceId || !first.id) { throw new Error('List implies at least traceId and id fields'); } - if (first.binaryAnnotations || (!first.localEndpoint && !first.remoteEndpoint && !first.tags)) { + if ( + first.binaryAnnotations || + (!first.localEndpoint && !first.remoteEndpoint && !first.tags) + ) { throw new Error( 'v1 format is not supported. For help, contact https://gitter.im/openzipkin/zipkin', ); @@ -28,8 +31,10 @@ export const ensureV2TraceData = (trace) => { export const hasRootSpan = (trace) => { switch (trace.length) { - case 0: return false; - case 1: return true; + case 0: + return false; + case 1: + return true; default: if (trace[0].depth < trace[1].depth) { return true; diff --git a/zipkin-lens/src/util/trace.test.js b/zipkin-lens/src/util/trace.test.js index 3c2ff605c2a..855525a796b 100644 --- a/zipkin-lens/src/util/trace.test.js +++ b/zipkin-lens/src/util/trace.test.js @@ -44,7 +44,9 @@ describe('ensureV2', () => { error = err; } - expect(error.message).toEqual('List implies at least traceId and id fields'); + expect(error.message).toEqual( + 'List implies at least traceId and id fields', + ); try { ensureV2TraceData([{ id: 'b' }]); diff --git a/zipkin-lens/src/zipkin/clock-skew.js b/zipkin-lens/src/zipkin/clock-skew.js index 314658444df..93fab65893a 100644 --- a/zipkin-lens/src/zipkin/clock-skew.js +++ b/zipkin-lens/src/zipkin/clock-skew.js @@ -29,7 +29,8 @@ class ClockSkew { } } -export function ipsMatch(a, b) { // export for testing +export function ipsMatch(a, b) { + // export for testing if (!a || !b) return false; if (a.ipv6 && b.ipv6 && a.ipv6 === b.ipv6) { return true; @@ -57,7 +58,8 @@ function adjustTimestamps(span, skew) { } /* Uses span kind to determine if there's clock skew. */ -export function getClockSkew(node) { // export for testing +export function getClockSkew(node) { + // export for testing const parent = node.parent ? node.parent.span : undefined; const child = node.span; if (!parent) return undefined; @@ -161,7 +163,9 @@ export function treeCorrectedForClockSkew(spans, debug = false) { if (debug) { /* eslint-disable no-console */ const prefix = 'skipping redundant root span'; - console.log(`${prefix}: traceId=${traceId}, rootSpanId=${rootSpanId}, spanId=${spanId}`); + console.log( + `${prefix}: traceId=${traceId}, rootSpanId=${rootSpanId}, spanId=${spanId}`, + ); } return trace; } diff --git a/zipkin-lens/src/zipkin/clock-skew.test.js b/zipkin-lens/src/zipkin/clock-skew.test.js index 1adc8439319..87a4d682c6f 100644 --- a/zipkin-lens/src/zipkin/clock-skew.test.js +++ b/zipkin-lens/src/zipkin/clock-skew.test.js @@ -79,75 +79,87 @@ describe('getClockSkew', () => { * adjusting it!) */ it('clock skew should only correct across different hosts', () => { - const parent = new SpanNode(clean({ - traceId: '1', - parentId: '2', - id: '3', - kind: 'CLIENT', - localEndpoint: frontend, - timestamp: 20, - duration: 20, - })); - const child = new SpanNode(clean({ - traceId: '1', - parentId: '2', - id: '3', - kind: 'SERVER', - localEndpoint: frontend, - timestamp: 10, // skew - duration: 10, - shared: true, - })); + const parent = new SpanNode( + clean({ + traceId: '1', + parentId: '2', + id: '3', + kind: 'CLIENT', + localEndpoint: frontend, + timestamp: 20, + duration: 20, + }), + ); + const child = new SpanNode( + clean({ + traceId: '1', + parentId: '2', + id: '3', + kind: 'SERVER', + localEndpoint: frontend, + timestamp: 10, // skew + duration: 10, + shared: true, + }), + ); parent.addChild(child); expect(getClockSkew(child)).toBeUndefined(); }); it('clock skew should only exist when client is behind server', () => { - const parent = new SpanNode(clean({ - traceId: '1', - parentId: '2', - id: '3', - kind: 'CLIENT', - localEndpoint: frontend, - timestamp: 20, - duration: 20, - })); - const child = new SpanNode(clean({ - traceId: '1', - parentId: '2', - id: '3', - kind: 'SERVER', - localEndpoint: backend, - timestamp: 30, // no skew - duration: 10, - shared: true, - })); + const parent = new SpanNode( + clean({ + traceId: '1', + parentId: '2', + id: '3', + kind: 'CLIENT', + localEndpoint: frontend, + timestamp: 20, + duration: 20, + }), + ); + const child = new SpanNode( + clean({ + traceId: '1', + parentId: '2', + id: '3', + kind: 'SERVER', + localEndpoint: backend, + timestamp: 30, // no skew + duration: 10, + shared: true, + }), + ); parent.addChild(child); expect(getClockSkew(child)).toBeUndefined(); }); it('clock skew should be attributed to the server endpoint', () => { - const parent = new SpanNode(clean({ - traceId: '1', - parentId: '2', - id: '3', - kind: 'CLIENT', - localEndpoint: frontend, - timestamp: 20, - duration: 20, - })); - const child = new SpanNode(clean({ - traceId: '1', - parentId: '2', - id: '3', - kind: 'SERVER', - localEndpoint: backend, - timestamp: 10, // skew - duration: 10, - shared: true, - })); + const parent = new SpanNode( + clean({ + traceId: '1', + parentId: '2', + id: '3', + kind: 'CLIENT', + localEndpoint: frontend, + timestamp: 20, + duration: 20, + }), + ); + const child = new SpanNode( + clean({ + traceId: '1', + parentId: '2', + id: '3', + kind: 'SERVER', + localEndpoint: backend, + timestamp: 10, // skew + duration: 10, + shared: true, + }), + ); parent.addChild(child); // Skew correction pushes the server side forward, so the skew endpoint is the server @@ -155,24 +167,28 @@ describe('getClockSkew', () => { }); it('clock skew should be attributed to the server endpoint even if missing shared flag', () => { - const parent = new SpanNode(clean({ - traceId: '1', - parentId: '2', - id: '3', - kind: 'CLIENT', - localEndpoint: frontend, - timestamp: 20, - duration: 20, - })); - const child = new SpanNode(clean({ - traceId: '1', - parentId: '2', - id: '3', - kind: 'SERVER', - localEndpoint: backend, - timestamp: 10, // skew - duration: 10, - })); + const parent = new SpanNode( + clean({ + traceId: '1', + parentId: '2', + id: '3', + kind: 'CLIENT', + localEndpoint: frontend, + timestamp: 20, + duration: 20, + }), + ); + const child = new SpanNode( + clean({ + traceId: '1', + parentId: '2', + id: '3', + kind: 'SERVER', + localEndpoint: backend, + timestamp: 10, // skew + duration: 10, + }), + ); parent.addChild(child); // Skew correction pushes the server side forward, so the skew endpoint is the server @@ -208,8 +224,9 @@ describe('getClockSkew', () => { parent.addChild(child); expect(getClockSkew(child).skew).toEqual( - server.timestamp - client.timestamp // how much the server is behind - - ((client.duration - server.duration) / 2), // center the server by splitting what's left + server.timestamp - + client.timestamp - // how much the server is behind + (client.duration - server.duration) / 2, // center the server by splitting what's left ); }); @@ -237,8 +254,9 @@ describe('getClockSkew', () => { parent.addChild(child); expect(getClockSkew(child).skew).toEqual( - server.timestamp - client.timestamp // how much server is behind - - 1, // assume it takes at least 1us to get to the server + server.timestamp - + client.timestamp - // how much server is behind + 1, // assume it takes at least 1us to get to the server ); }); @@ -266,8 +284,9 @@ describe('getClockSkew', () => { parent.addChild(child); expect(getClockSkew(child).skew).toEqual( - server.timestamp - client.timestamp // how much server is behind - - 1, // assume it takes at least 1us to get to the server + server.timestamp - + client.timestamp - // how much server is behind + 1, // assume it takes at least 1us to get to the server ); }); }); @@ -326,11 +345,13 @@ describe('treeCorrectedForClockSkew', () => { it('should skip on duplicate root', () => { const duplicate = []; skewedTrace.forEach((span) => duplicate.push(span)); - duplicate.push(clean({ - traceId: skewedTrace[0].traceId, - id: 'cafebabe', - name: 'curtain', - })); + duplicate.push( + clean({ + traceId: skewedTrace[0].traceId, + id: 'cafebabe', + name: 'curtain', + }), + ); const notCorrected = treeCorrectedForClockSkew(duplicate); expect(notCorrected).toEqual(new SpanNodeBuilder({}).build(duplicate)); }); diff --git a/zipkin-lens/src/zipkin/dependency-linker.js b/zipkin-lens/src/zipkin/dependency-linker.js index 25034c08ba3..608f06e28db 100644 --- a/zipkin-lens/src/zipkin/dependency-linker.js +++ b/zipkin-lens/src/zipkin/dependency-linker.js @@ -56,7 +56,8 @@ export class DependencyLinker { while (ancestor) { const maybeRemote = ancestor.span; if (maybeRemote && maybeRemote.kind) { - if (this._debug) console.log(`found remote ancestor ${JSON.stringify(maybeRemote)}`); + if (this._debug) + console.log(`found remote ancestor ${JSON.stringify(maybeRemote)}`); return maybeRemote; } ancestor = ancestor.parent; @@ -66,7 +67,9 @@ export class DependencyLinker { _addLink(parent, child, isError) { if (this.debug) { - console.log('incrementing ' + (isError ? 'error ' : '') + 'link ' + parent + ' -> ' + child); // eslint-disable-line prefer-template + console.log( + `incrementing ${isError ? 'error ' : ''}link ${parent} -> ${child}`, + ); // eslint-disable-line prefer-template } const key = keyString(parent, child); this._callCounts[key] = (this._callCounts[key] || 0) + 1; @@ -112,9 +115,11 @@ export class DependencyLinker { case 'CONSUMER': child = serviceName; parent = remoteServiceName; - if (current === traceTree) { // we are the root-most span. + if (current === traceTree) { + // we are the root-most span. if (!parent) { - if (debug) console.log('The client of the root span is unknown; skipping'); + if (debug) + console.log('The client of the root span is unknown; skipping'); continue; } } @@ -132,7 +137,8 @@ export class DependencyLinker { let isError = currentSpan.tags.error !== undefined; if (kind === 'PRODUCER' || kind === 'CONSUMER') { if (!parent || !child) { - if (debug) console.log('cannot link messaging span to its broker; skipping'); + if (debug) + console.log('cannot link messaging span to its broker; skipping'); } else { this._addLink(parent, child, isError); } @@ -142,11 +148,16 @@ export class DependencyLinker { // Local spans may be between the current node and its remote parent const remoteAncestor = this._firstRemoteAncestor(current); let remoteAncestorName; - if (remoteAncestor) remoteAncestorName = getServiceName(remoteAncestor.localEndpoint); + if (remoteAncestor) + remoteAncestorName = getServiceName(remoteAncestor.localEndpoint); if (remoteAncestor && remoteAncestorName) { // Some users accidentally put the remote service name on client annotations. // Check for this and backfill a link from the nearest remote to that service as necessary. - if (kind === 'CLIENT' && serviceName && remoteAncestorName !== serviceName) { + if ( + kind === 'CLIENT' && + serviceName && + remoteAncestorName !== serviceName + ) { if (debug) console.log('detected missing link to client span'); this._addLink(remoteAncestorName, serviceName, false); // we don't know if it is an error } @@ -155,8 +166,12 @@ export class DependencyLinker { // When an RPC is split between spans, we skip the child (server side). If our parent is a // client, we need to check it for errors. - if (!isError && remoteAncestor.kind === 'CLIENT' - && currentSpan.parentId && currentSpan.parentId === remoteAncestor.id) { + if ( + !isError && + remoteAncestor.kind === 'CLIENT' && + currentSpan.parentId && + currentSpan.parentId === remoteAncestor.id + ) { isError = isError || remoteAncestor.tags.error !== undefined; } } diff --git a/zipkin-lens/src/zipkin/dependency-linker.test.js b/zipkin-lens/src/zipkin/dependency-linker.test.js index 0f39cad2bfa..8c78d7c0664 100644 --- a/zipkin-lens/src/zipkin/dependency-linker.test.js +++ b/zipkin-lens/src/zipkin/dependency-linker.test.js @@ -18,16 +18,36 @@ const debug = false; // switch to enable console output during tests // in reverse order as reporting is more likely to occur this way const trace = [ { - traceId: 'a', parentId: 'b', id: 'c', kind: 'CLIENT', localEndpoint: { serviceName: 'app' }, remoteEndpoint: { serviceName: 'db' }, tags: { error: true }, + traceId: 'a', + parentId: 'b', + id: 'c', + kind: 'CLIENT', + localEndpoint: { serviceName: 'app' }, + remoteEndpoint: { serviceName: 'db' }, + tags: { error: true }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'SERVER', localEndpoint: { serviceName: 'app' }, remoteEndpoint: { serviceName: 'web' }, shared: true, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'SERVER', + localEndpoint: { serviceName: 'app' }, + remoteEndpoint: { serviceName: 'web' }, + shared: true, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'CLIENT', localEndpoint: { serviceName: 'web' }, remoteEndpoint: { serviceName: 'app' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'CLIENT', + localEndpoint: { serviceName: 'web' }, + remoteEndpoint: { serviceName: 'app' }, }, { - traceId: 'a', id: 'a', kind: 'SERVER', localEndpoint: { serviceName: 'web' }, + traceId: 'a', + id: 'a', + kind: 'SERVER', + localEndpoint: { serviceName: 'web' }, }, ]; @@ -53,7 +73,10 @@ describe('DependencyLinker', () => { expect(dependencyLinker.link()).toEqual([ { parent: 'web', child: 'app', callCount: 1 }, { - parent: 'app', child: 'db', callCount: 1, errorCount: 1, + parent: 'app', + child: 'db', + callCount: 1, + errorCount: 1, }, ]); }); @@ -65,7 +88,10 @@ describe('DependencyLinker', () => { expect(dependencyLinker.link()).toEqual([ { parent: 'web', child: 'app', callCount: 2 }, { - parent: 'app', child: 'db', callCount: 2, errorCount: 2, + parent: 'app', + child: 'db', + callCount: 2, + errorCount: 2, }, ]); }); @@ -88,7 +114,10 @@ describe('DependencyLinker', () => { expect(dependencyLinker.link()).toEqual([ { parent: 'web', child: 'app', callCount: 1 }, { - parent: 'app', child: 'db', callCount: 1, errorCount: 1, + parent: 'app', + child: 'db', + callCount: 1, + errorCount: 1, }, ]); }); @@ -103,7 +132,10 @@ describe('DependencyLinker', () => { expect(dependencyLinker.link()).toEqual([ { parent: 'web', child: 'app', callCount: 1 }, { - parent: 'app', child: 'db', callCount: 1, errorCount: 1, + parent: 'app', + child: 'db', + callCount: 1, + errorCount: 1, }, ]); }); @@ -111,10 +143,19 @@ describe('DependencyLinker', () => { it('should link messaging spans by broker', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'PRODUCER', localEndpoint: { serviceName: 'producer' }, remoteEndpoint: { serviceName: 'kafka' }, + traceId: 'a', + id: 'a', + kind: 'PRODUCER', + localEndpoint: { serviceName: 'producer' }, + remoteEndpoint: { serviceName: 'kafka' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'CONSUMER', localEndpoint: { serviceName: 'consumer' }, remoteEndpoint: { serviceName: 'kafka' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'CONSUMER', + localEndpoint: { serviceName: 'consumer' }, + remoteEndpoint: { serviceName: 'kafka' }, }, ]); @@ -127,10 +168,19 @@ describe('DependencyLinker', () => { it('should not conflate messaging when they have different brokers', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'PRODUCER', localEndpoint: { serviceName: 'producer' }, remoteEndpoint: { serviceName: 'kafka1' }, + traceId: 'a', + id: 'a', + kind: 'PRODUCER', + localEndpoint: { serviceName: 'producer' }, + remoteEndpoint: { serviceName: 'kafka1' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'CONSUMER', localEndpoint: { serviceName: 'consumer' }, remoteEndpoint: { serviceName: 'kafka2' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'CONSUMER', + localEndpoint: { serviceName: 'consumer' }, + remoteEndpoint: { serviceName: 'kafka2' }, }, ]); @@ -143,10 +193,17 @@ describe('DependencyLinker', () => { it('should not link messaging spans missing broker', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'PRODUCER', localEndpoint: { serviceName: 'producer' }, + traceId: 'a', + id: 'a', + kind: 'PRODUCER', + localEndpoint: { serviceName: 'producer' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'CONSUMER', localEndpoint: { serviceName: 'consumer' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'CONSUMER', + localEndpoint: { serviceName: 'consumer' }, }, ]); @@ -157,10 +214,18 @@ describe('DependencyLinker', () => { it('should not link producer spans when missing broker', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'PRODUCER', localEndpoint: { serviceName: 'producer' }, + traceId: 'a', + id: 'a', + kind: 'PRODUCER', + localEndpoint: { serviceName: 'producer' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'CONSUMER', localEndpoint: { serviceName: 'consumer' }, remoteEndpoint: { serviceName: 'kafka' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'CONSUMER', + localEndpoint: { serviceName: 'consumer' }, + remoteEndpoint: { serviceName: 'kafka' }, }, ]); @@ -172,10 +237,18 @@ describe('DependencyLinker', () => { it('should not link consumer spans when missing broker', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'PRODUCER', localEndpoint: { serviceName: 'producer' }, remoteEndpoint: { serviceName: 'kafka' }, + traceId: 'a', + id: 'a', + kind: 'PRODUCER', + localEndpoint: { serviceName: 'producer' }, + remoteEndpoint: { serviceName: 'kafka' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'CONSUMER', localEndpoint: { serviceName: 'consumer' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'CONSUMER', + localEndpoint: { serviceName: 'consumer' }, }, ]); @@ -188,10 +261,17 @@ describe('DependencyLinker', () => { it('should interpret producer -> server as RPC', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'PRODUCER', localEndpoint: { serviceName: 'producer' }, + traceId: 'a', + id: 'a', + kind: 'PRODUCER', + localEndpoint: { serviceName: 'producer' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'SERVER', localEndpoint: { serviceName: 'server' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'SERVER', + localEndpoint: { serviceName: 'server' }, }, ]); @@ -207,10 +287,17 @@ describe('DependencyLinker', () => { it('should interpret producer -> server as RPC, even sharing ID', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'PRODUCER', localEndpoint: { serviceName: 'producer' }, + traceId: 'a', + id: 'a', + kind: 'PRODUCER', + localEndpoint: { serviceName: 'producer' }, }, { - traceId: 'a', id: 'a', kind: 'SERVER', localEndpoint: { serviceName: 'server' }, shared: true, + traceId: 'a', + id: 'a', + kind: 'SERVER', + localEndpoint: { serviceName: 'server' }, + shared: true, }, ]); @@ -226,10 +313,17 @@ describe('DependencyLinker', () => { it('should not interpret client as producer', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'CLIENT', localEndpoint: { serviceName: 'client' }, + traceId: 'a', + id: 'a', + kind: 'CLIENT', + localEndpoint: { serviceName: 'client' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'CONSUMER', localEndpoint: { serviceName: 'consumer' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'CONSUMER', + localEndpoint: { serviceName: 'consumer' }, }, ]); @@ -243,10 +337,19 @@ describe('DependencyLinker', () => { it('should link spans directed by kind', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'SERVER', localEndpoint: { serviceName: 'server' }, remoteEndpoint: { serviceName: 'client' }, + traceId: 'a', + id: 'a', + kind: 'SERVER', + localEndpoint: { serviceName: 'server' }, + remoteEndpoint: { serviceName: 'client' }, }, { - traceId: 'a', id: 'a', kind: 'CLIENT', localEndpoint: { serviceName: 'client' }, remoteEndpoint: { serviceName: 'server' }, shared: true, + traceId: 'a', + id: 'a', + kind: 'CLIENT', + localEndpoint: { serviceName: 'client' }, + remoteEndpoint: { serviceName: 'server' }, + shared: true, }, ]); @@ -258,13 +361,26 @@ describe('DependencyLinker', () => { it('link calls to uninstrumented servers', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'SERVER', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + id: 'a', + kind: 'SERVER', + localEndpoint: { serviceName: 'frontend' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'CLIENT', localEndpoint: { serviceName: 'frontend' }, remoteEndpoint: { serviceName: 'backend' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'CLIENT', + localEndpoint: { serviceName: 'frontend' }, + remoteEndpoint: { serviceName: 'backend' }, }, { - traceId: 'a', parentId: 'a', id: 'c', kind: 'CLIENT', localEndpoint: { serviceName: 'frontend' }, remoteEndpoint: { serviceName: 'backend' }, + traceId: 'a', + parentId: 'a', + id: 'c', + kind: 'CLIENT', + localEndpoint: { serviceName: 'frontend' }, + remoteEndpoint: { serviceName: 'backend' }, }, ]); @@ -276,19 +392,36 @@ describe('DependencyLinker', () => { it('link calls to uninstrumented servers, including errors', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'SERVER', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + id: 'a', + kind: 'SERVER', + localEndpoint: { serviceName: 'frontend' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'CLIENT', localEndpoint: { serviceName: 'frontend' }, remoteEndpoint: { serviceName: 'backend' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'CLIENT', + localEndpoint: { serviceName: 'frontend' }, + remoteEndpoint: { serviceName: 'backend' }, }, { - traceId: 'a', parentId: 'a', id: 'c', kind: 'CLIENT', localEndpoint: { serviceName: 'frontend' }, remoteEndpoint: { serviceName: 'backend' }, tags: { error: '' }, + traceId: 'a', + parentId: 'a', + id: 'c', + kind: 'CLIENT', + localEndpoint: { serviceName: 'frontend' }, + remoteEndpoint: { serviceName: 'backend' }, + tags: { error: '' }, }, ]); expect(dependencyLinker.link()).toEqual([ { - parent: 'frontend', child: 'backend', callCount: 2, errorCount: 1, + parent: 'frontend', + child: 'backend', + callCount: 2, + errorCount: 1, }, ]); }); @@ -296,19 +429,34 @@ describe('DependencyLinker', () => { it('link incoming calls, using last RPC parent as service name', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'CLIENT', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + id: 'a', + kind: 'CLIENT', + localEndpoint: { serviceName: 'frontend' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'SERVER', localEndpoint: { serviceName: 'backend' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'SERVER', + localEndpoint: { serviceName: 'backend' }, }, { - traceId: 'a', parentId: 'a', id: 'c', kind: 'SERVER', localEndpoint: { serviceName: 'backend' }, tags: { error: '' }, + traceId: 'a', + parentId: 'a', + id: 'c', + kind: 'SERVER', + localEndpoint: { serviceName: 'backend' }, + tags: { error: '' }, }, ]); expect(dependencyLinker.link()).toEqual([ { - parent: 'frontend', child: 'backend', callCount: 2, errorCount: 1, + parent: 'frontend', + child: 'backend', + callCount: 2, + errorCount: 1, }, ]); }); @@ -320,10 +468,17 @@ describe('DependencyLinker', () => { it('should link single host spans as one call', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'CLIENT', localEndpoint: { serviceName: 'client' }, + traceId: 'a', + id: 'a', + kind: 'CLIENT', + localEndpoint: { serviceName: 'client' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'SERVER', localEndpoint: { serviceName: 'server' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'SERVER', + localEndpoint: { serviceName: 'server' }, }, ]); @@ -335,16 +490,28 @@ describe('DependencyLinker', () => { it('should link single host spans as one error', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'CLIENT', localEndpoint: { serviceName: 'client' }, tags: { error: '' }, + traceId: 'a', + id: 'a', + kind: 'CLIENT', + localEndpoint: { serviceName: 'client' }, + tags: { error: '' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'SERVER', localEndpoint: { serviceName: 'server' }, tags: { error: '' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'SERVER', + localEndpoint: { serviceName: 'server' }, + tags: { error: '' }, }, ]); expect(dependencyLinker.link()).toEqual([ { - parent: 'client', child: 'server', callCount: 1, errorCount: 1, + parent: 'client', + child: 'server', + callCount: 1, + errorCount: 1, }, ]); }); @@ -352,16 +519,28 @@ describe('DependencyLinker', () => { it('should link shared RPC span as one error', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'CLIENT', localEndpoint: { serviceName: 'client' }, tags: { error: '' }, + traceId: 'a', + id: 'a', + kind: 'CLIENT', + localEndpoint: { serviceName: 'client' }, + tags: { error: '' }, }, { - traceId: 'a', id: 'b', kind: 'SERVER', localEndpoint: { serviceName: 'server' }, tags: { error: '' }, shared: true, + traceId: 'a', + id: 'b', + kind: 'SERVER', + localEndpoint: { serviceName: 'server' }, + tags: { error: '' }, + shared: true, }, ]); expect(dependencyLinker.link()).toEqual([ { - parent: 'client', child: 'server', callCount: 1, errorCount: 1, + parent: 'client', + child: 'server', + callCount: 1, + errorCount: 1, }, ]); }); @@ -369,16 +548,26 @@ describe('DependencyLinker', () => { it('should prefer server name in RPC link', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'CLIENT', localEndpoint: { serviceName: 'client' }, remoteEndpoint: { serviceName: 'elephant' }, + traceId: 'a', + id: 'a', + kind: 'CLIENT', + localEndpoint: { serviceName: 'client' }, + remoteEndpoint: { serviceName: 'elephant' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'SERVER', localEndpoint: { serviceName: 'server' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'SERVER', + localEndpoint: { serviceName: 'server' }, }, ]); expect(dependencyLinker.link()).toEqual([ { - parent: 'client', child: 'server', callCount: 1, + parent: 'client', + child: 'server', + callCount: 1, }, ]); }); @@ -390,16 +579,31 @@ describe('DependencyLinker', () => { it('tolerates missing localEndpoint between server and client', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'SERVER', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + id: 'a', + kind: 'SERVER', + localEndpoint: { serviceName: 'frontend' }, }, { - traceId: 'a', parentId: 'a', id: 'b', + traceId: 'a', + parentId: 'a', + id: 'b', }, { - traceId: 'a', parentId: 'b', id: 'c', kind: 'CLIENT', localEndpoint: { serviceName: 'frontend' }, remoteEndpoint: { serviceName: 'backend' }, + traceId: 'a', + parentId: 'b', + id: 'c', + kind: 'CLIENT', + localEndpoint: { serviceName: 'frontend' }, + remoteEndpoint: { serviceName: 'backend' }, }, { - traceId: 'a', parentId: 'b', id: 'd', kind: 'CLIENT', localEndpoint: { serviceName: 'frontend' }, remoteEndpoint: { serviceName: 'backend' }, + traceId: 'a', + parentId: 'b', + id: 'd', + kind: 'CLIENT', + localEndpoint: { serviceName: 'frontend' }, + remoteEndpoint: { serviceName: 'backend' }, }, ]); @@ -411,13 +615,25 @@ describe('DependencyLinker', () => { it('should not link leaf nodes when remote service name is unknown', () => { putTrace([ { - traceId: 'a', parentId: 'a', id: 'b', kind: 'SERVER', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'SERVER', + localEndpoint: { serviceName: 'frontend' }, }, { - traceId: 'a', parentId: 'b', id: 'c', kind: 'CLIENT', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + parentId: 'b', + id: 'c', + kind: 'CLIENT', + localEndpoint: { serviceName: 'frontend' }, }, { - traceId: 'a', parentId: 'b', id: 'd', kind: 'CLIENT', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + parentId: 'b', + id: 'd', + kind: 'CLIENT', + localEndpoint: { serviceName: 'frontend' }, }, ]); @@ -427,16 +643,29 @@ describe('DependencyLinker', () => { it('should create links when missing intermediate endpoint data', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'SERVER', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + id: 'a', + kind: 'SERVER', + localEndpoint: { serviceName: 'frontend' }, }, { - traceId: 'a', parentId: 'a', id: 'b', // possibly missing client/server span + traceId: 'a', + parentId: 'a', + id: 'b', // possibly missing client/server span }, { - traceId: 'a', parentId: 'b', id: 'c', kind: 'CLIENT', localEndpoint: { serviceName: 'backend' }, + traceId: 'a', + parentId: 'b', + id: 'c', + kind: 'CLIENT', + localEndpoint: { serviceName: 'backend' }, }, { - traceId: 'a', parentId: 'b', id: 'd', kind: 'CLIENT', localEndpoint: { serviceName: 'backend' }, + traceId: 'a', + parentId: 'b', + id: 'd', + kind: 'CLIENT', + localEndpoint: { serviceName: 'backend' }, }, ]); @@ -448,14 +677,28 @@ describe('DependencyLinker', () => { it('should not attribute errors from uninstrumented links', () => { putTrace([ { - traceId: 'a', parentId: 'a', id: 'b', kind: 'CLIENT', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'CLIENT', + localEndpoint: { serviceName: 'frontend' }, }, // missing rpc span between here { - traceId: 'a', parentId: 'b', id: 'c', kind: 'CLIENT', localEndpoint: { serviceName: 'backend' }, tags: { error: '' }, + traceId: 'a', + parentId: 'b', + id: 'c', + kind: 'CLIENT', + localEndpoint: { serviceName: 'backend' }, + tags: { error: '' }, }, { - traceId: 'a', parentId: 'b', id: 'd', kind: 'CLIENT', localEndpoint: { serviceName: 'backend' }, tags: { error: '' }, + traceId: 'a', + parentId: 'b', + id: 'd', + kind: 'CLIENT', + localEndpoint: { serviceName: 'backend' }, + tags: { error: '' }, }, ]); @@ -468,16 +711,27 @@ describe('DependencyLinker', () => { it('should not count annotation error as errorCount', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'CLIENT', localEndpoint: { serviceName: 'client' }, annotations: [{ timestamp: 1, value: 'error' }], + traceId: 'a', + id: 'a', + kind: 'CLIENT', + localEndpoint: { serviceName: 'client' }, + annotations: [{ timestamp: 1, value: 'error' }], }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'SERVER', localEndpoint: { serviceName: 'server' }, annotations: [{ timestamp: 1, value: 'error' }], + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'SERVER', + localEndpoint: { serviceName: 'server' }, + annotations: [{ timestamp: 1, value: 'error' }], }, ]); expect(dependencyLinker.link()).toEqual([ { - parent: 'client', child: 'server', callCount: 1, + parent: 'client', + child: 'server', + callCount: 1, }, ]); }); @@ -485,10 +739,17 @@ describe('DependencyLinker', () => { it('should link loopback', () => { putTrace([ { - traceId: 'a', id: 'a', kind: 'CLIENT', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + id: 'a', + kind: 'CLIENT', + localEndpoint: { serviceName: 'frontend' }, }, { - traceId: 'a', parentId: 'a', id: 'b', kind: 'SERVER', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + parentId: 'a', + id: 'b', + kind: 'SERVER', + localEndpoint: { serviceName: 'frontend' }, }, ]); @@ -500,17 +761,29 @@ describe('DependencyLinker', () => { it('should treat remote service names missing kind as RPC', () => { putTrace([ { - traceId: 'a', parentId: 'a', id: 'b', localEndpoint: { serviceName: 'web' }, remoteEndpoint: { serviceName: 'app' }, + traceId: 'a', + parentId: 'a', + id: 'b', + localEndpoint: { serviceName: 'web' }, + remoteEndpoint: { serviceName: 'app' }, }, { - traceId: 'a', parentId: 'b', id: 'c', localEndpoint: { serviceName: 'app' }, remoteEndpoint: { serviceName: 'db' }, tags: { error: true }, + traceId: 'a', + parentId: 'b', + id: 'c', + localEndpoint: { serviceName: 'app' }, + remoteEndpoint: { serviceName: 'db' }, + tags: { error: true }, }, ]); expect(dependencyLinker.link()).toEqual([ { parent: 'web', child: 'app', callCount: 1 }, { - parent: 'app', child: 'db', callCount: 1, errorCount: 1, + parent: 'app', + child: 'db', + callCount: 1, + errorCount: 1, }, ]); }); @@ -519,22 +792,38 @@ describe('DependencyLinker', () => { it('should not link root RPC spans missing both service names', () => { [ { - traceId: 'a', id: 'a', kind: 'SERVER', + traceId: 'a', + id: 'a', + kind: 'SERVER', }, { - traceId: 'a', id: 'a', kind: 'SERVER', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + id: 'a', + kind: 'SERVER', + localEndpoint: { serviceName: 'frontend' }, }, { - traceId: 'a', id: 'a', kind: 'SERVER', remoteEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + id: 'a', + kind: 'SERVER', + remoteEndpoint: { serviceName: 'frontend' }, }, { - traceId: 'a', id: 'a', kind: 'CLIENT', + traceId: 'a', + id: 'a', + kind: 'CLIENT', }, { - traceId: 'a', id: 'a', kind: 'CLIENT', localEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + id: 'a', + kind: 'CLIENT', + localEndpoint: { serviceName: 'frontend' }, }, { - traceId: 'a', id: 'a', kind: 'CLIENT', remoteEndpoint: { serviceName: 'frontend' }, + traceId: 'a', + id: 'a', + kind: 'CLIENT', + remoteEndpoint: { serviceName: 'frontend' }, }, ].forEach((root) => { putTrace([root]); @@ -546,10 +835,18 @@ describe('DependencyLinker', () => { const parentId = 'a'; // missing putTrace([ { - traceId: 'a', parentId, id: 'b', kind: 'SERVER', localEndpoint: { serviceName: 'server1' }, + traceId: 'a', + parentId, + id: 'b', + kind: 'SERVER', + localEndpoint: { serviceName: 'server1' }, }, { - traceId: 'a', parentId, id: 'c', kind: 'SERVER', localEndpoint: { serviceName: 'server2' }, + traceId: 'a', + parentId, + id: 'c', + kind: 'SERVER', + localEndpoint: { serviceName: 'server2' }, }, ]); @@ -560,10 +857,21 @@ describe('DependencyLinker', () => { const parentId = 'a'; // missing putTrace([ { - traceId: 'a', parentId, id: 'b', kind: 'CLIENT', localEndpoint: { serviceName: 'web' }, remoteEndpoint: { serviceName: 'app' }, - }, - { - traceId: 'a', parentId, id: 'b', kind: 'SERVER', localEndpoint: { serviceName: 'app' }, remoteEndpoint: { serviceName: 'web' }, shared: true, + traceId: 'a', + parentId, + id: 'b', + kind: 'CLIENT', + localEndpoint: { serviceName: 'web' }, + remoteEndpoint: { serviceName: 'app' }, + }, + { + traceId: 'a', + parentId, + id: 'b', + kind: 'SERVER', + localEndpoint: { serviceName: 'app' }, + remoteEndpoint: { serviceName: 'web' }, + shared: true, }, ]); diff --git a/zipkin-lens/src/zipkin/index.js b/zipkin-lens/src/zipkin/index.js index 04e3214f45d..d30a4e9995b 100644 --- a/zipkin-lens/src/zipkin/index.js +++ b/zipkin-lens/src/zipkin/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 The OpenZipkin Authors + * Copyright 2015-2020 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -13,8 +13,4 @@ */ export { treeCorrectedForClockSkew } from './clock-skew'; export { getServiceName } from './span-row'; -export { - traceSummary, - traceSummaries, - detailedTraceSummary, -} from './trace'; +export { traceSummary, traceSummaries, detailedTraceSummary } from './trace'; diff --git a/zipkin-lens/src/zipkin/span-cleaner.js b/zipkin-lens/src/zipkin/span-cleaner.js index ade9af9b2f1..bbf731876b2 100644 --- a/zipkin-lens/src/zipkin/span-cleaner.js +++ b/zipkin-lens/src/zipkin/span-cleaner.js @@ -43,18 +43,24 @@ export function clean(span) { } res.id = id; - if (span.name && span.name !== '' && span.name !== 'unknown') res.name = span.name; + if (span.name && span.name !== '' && span.name !== 'unknown') + res.name = span.name; if (span.kind) res.kind = span.kind; if (span.timestamp) res.timestamp = span.timestamp; if (span.duration) res.duration = span.duration; - if (isEndpoint(span.localEndpoint)) res.localEndpoint = { ...span.localEndpoint }; - if (isEndpoint(span.remoteEndpoint)) res.remoteEndpoint = { ...span.remoteEndpoint }; + if (isEndpoint(span.localEndpoint)) + res.localEndpoint = { ...span.localEndpoint }; + if (isEndpoint(span.remoteEndpoint)) + res.remoteEndpoint = { ...span.remoteEndpoint }; res.annotations = span.annotations ? span.annotations.slice(0) : []; if (res.annotations.length > 1) { - res.annotations = sortBy(unionWith(res.annotations, isEqual), ['timestamp', 'value']); + res.annotations = sortBy(unionWith(res.annotations, isEqual), [ + 'timestamp', + 'value', + ]); } res.tags = span.tags || {}; @@ -97,8 +103,10 @@ export function merge(left, right) { } else if (right.annotations.length === 0) { res.annotations = left.annotations; } else { - res.annotations = sortBy(unionWith(left.annotations, right.annotations, isEqual), - ['timestamp', 'value']); + res.annotations = sortBy( + unionWith(left.annotations, right.annotations, isEqual), + ['timestamp', 'value'], + ); } res.tags = { ...left.tags, ...right.tags }; @@ -150,7 +158,8 @@ function compareShared(left, right) { return 0; } -export function cleanupComparator(left, right) { // exported for testing +export function cleanupComparator(left, right) { + // exported for testing const bySpanId = compare(left.id, right.id); if (bySpanId !== 0) return bySpanId; const byShared = compareShared(left, right); @@ -160,7 +169,11 @@ export function cleanupComparator(left, right) { // exported for testing function tryMerge(current, endpoint) { if (!endpoint) return true; - if (current.serviceName && endpoint.serviceName && current.serviceName !== endpoint.serviceName) { + if ( + current.serviceName && + endpoint.serviceName && + current.serviceName !== endpoint.serviceName + ) { return false; } if (current.ipv4 && endpoint.ipv4 && current.ipv4 !== endpoint.ipv4) { @@ -182,11 +195,14 @@ function tryMerge(current, endpoint) { } // sort by timestamp, then name, root/shared first in case of skew -export function spanComparator(a, b) { // exported for testing - if (!a.parentId && b.parentId) { // a is root +export function spanComparator(a, b) { + // exported for testing + if (!a.parentId && b.parentId) { + // a is root return -1; } - if (a.parentId && !b.parentId) { // b is root + if (a.parentId && !b.parentId) { + // b is root return 1; } @@ -228,13 +244,16 @@ export function mergeV2ById(spans) { span.traceId = traceId; } - const localEndpoint = span.localEndpoint ? ({ ...span.localEndpoint }) : {}; + const localEndpoint = span.localEndpoint ? { ...span.localEndpoint } : {}; while (i + 1 < length) { const next = result[i + 1]; if (next.id !== span.id) break; // This cautiously merges with the next span, if we think it was sent in multiple pieces. - if (span.shared === next.shared && tryMerge(localEndpoint, next.localEndpoint)) { + if ( + span.shared === next.shared && + tryMerge(localEndpoint, next.localEndpoint) + ) { span = merge(span, next); // remove the merged element diff --git a/zipkin-lens/src/zipkin/span-cleaner.test.js b/zipkin-lens/src/zipkin/span-cleaner.test.js index 809a52a24d1..3277125529c 100644 --- a/zipkin-lens/src/zipkin/span-cleaner.test.js +++ b/zipkin-lens/src/zipkin/span-cleaner.test.js @@ -11,12 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -import { - clean, - cleanupComparator, - merge, - mergeV2ById, -} from './span-cleaner'; +import { clean, cleanupComparator, merge, mergeV2ById } from './span-cleaner'; import yelpTrace from '../../testdata/yelp.json'; // endpoints from zipkin2.TestObjects @@ -64,7 +59,8 @@ const serverSpan = { shared: true, }; -const oneOfEach = { // has every field set +const oneOfEach = { + // has every field set traceId: '7180c278b62e8f6a216a2aea45d08fc9', parentId: '0000000000000001', id: '0000000000000002', @@ -837,9 +833,7 @@ describe('mergeV2ById', () => { id: '0000000000000002', duration: 207000, remoteEndpoint: backend, - annotations: [ - { timestamp: 1472470996403000, value: 'wr' }, - ], + annotations: [{ timestamp: 1472470996403000, value: 'wr' }], }, { traceId: '0000000000000001', @@ -848,9 +842,7 @@ describe('mergeV2ById', () => { kind: 'CLIENT', timestamp: 1472470996199000, localEndpoint: frontend, - annotations: [ - { timestamp: 1472470996238000, value: 'ws' }, - ], + annotations: [{ timestamp: 1472470996238000, value: 'ws' }], tags: { 'http.path': '/api', 'clnt/finagle.version': '6.45.0', @@ -989,10 +981,7 @@ describe('mergeV2ById', () => { }, ]); - expect(spans.map((s) => s.name)).toEqual([ - 'client', - 'server', - ]); + expect(spans.map((s) => s.name)).toEqual(['client', 'server']); }); // If instrumentation accidentally added shared flag on a server root span, delete it so that @@ -1047,8 +1036,8 @@ describe('cleanupComparator', () => { }, ]; - expect(spans.sort(cleanupComparator).map((s) => `${s.id}-${s.kind}`)).toEqual([ - '0000000000000004-CLIENT', '0000000000000004-SERVER', - ]); + expect( + spans.sort(cleanupComparator).map((s) => `${s.id}-${s.kind}`), + ).toEqual(['0000000000000004-CLIENT', '0000000000000004-SERVER']); }); }); diff --git a/zipkin-lens/src/zipkin/span-node.js b/zipkin-lens/src/zipkin/span-node.js index d90030b1884..47919360b77 100644 --- a/zipkin-lens/src/zipkin/span-node.js +++ b/zipkin-lens/src/zipkin/span-node.js @@ -54,7 +54,8 @@ export class SpanNode { // Adds the child IFF it isn't already a child. addChild(child) { if (!child) throw new Error('child was undefined'); - if (child === this) throw new Error(`circular dependency on ${this.toString()}`); + if (child === this) + throw new Error(`circular dependency on ${this.toString()}`); child._setParent(this); this._children.push(child); } @@ -63,7 +64,8 @@ export class SpanNode { queueRootMostSpans() { const queue = []; // since the input data could be headless, we first push onto the queue the root-most spans - if (typeof this.span === 'undefined') { // synthetic root + if (typeof this.span === 'undefined') { + // synthetic root this.children.forEach((child) => queue.push(child)); } else { queue.push(this); @@ -198,7 +200,8 @@ export class SpanNodeBuilder { // If there's no shared parent, fall back to normal case which is unqualified beyond ID. parent = span.parentId; } - } else if (this._rootSpan) { // we are root or don't know our parent + } else if (this._rootSpan) { + // we are root or don't know our parent if (this._debug) { const prefix = 'attributing span missing parent to root'; /* eslint-disable no-console */ @@ -258,7 +261,9 @@ export class SpanNodeBuilder { if (!this._rootSpan) { if (this._debug) { /* eslint-disable no-console */ - console.log(`substituting dummy node for missing root span: traceId=${traceId}`); + console.log( + `substituting dummy node for missing root span: traceId=${traceId}`, + ); } this._rootSpan = new SpanNode(); } @@ -269,7 +274,8 @@ export class SpanNodeBuilder { const child = this._keyToNode[key]; const parent = this._keyToNode[this._spanToParent[key]]; - if (!parent) { // Handle headless by attaching spans missing parents to root + if (!parent) { + // Handle headless by attaching spans missing parents to root this._rootSpan.addChild(child); } else { parent.addChild(child); diff --git a/zipkin-lens/src/zipkin/span-node.test.js b/zipkin-lens/src/zipkin/span-node.test.js index d849a187692..545e0af0291 100644 --- a/zipkin-lens/src/zipkin/span-node.test.js +++ b/zipkin-lens/src/zipkin/span-node.test.js @@ -98,9 +98,7 @@ describe('SpanNode', () => { const ids = []; a.traverse((s) => ids.push(s.id)); - expect(ids).toEqual([ - 'a', 'b', 'c', 'd', 'e', 'f', '1', '2', - ]); + expect(ids).toEqual(['a', 'b', 'c', 'd', 'e', 'f', '1', '2']); }); }); @@ -155,10 +153,16 @@ describe('SpanNodeBuilder', () => { const trace = [ { traceId: 'a', id: 'b', timestamp: 1 }, { - traceId: 'a', parentId: 'b', id: 'c', timestamp: 2, + traceId: 'a', + parentId: 'b', + id: 'c', + timestamp: 2, }, { - traceId: 'a', parentId: 'b', id: 'd', timestamp: 3, + traceId: 'a', + parentId: 'b', + id: 'd', + timestamp: 3, }, { traceId: 'a', id: 'e', timestamp: 4 }, { traceId: 'a', id: 'f', timestamp: 5 }, @@ -192,20 +196,16 @@ describe('SpanNodeBuilder', () => { // input should be well formed, but this ensures we are fine anyway it('should skip on cycle', () => { - const trace = [ - { traceId: 'a', parentId: 'b', id: 'b' }, - ]; + const trace = [{ traceId: 'a', parentId: 'b', id: 'b' }]; const root = new SpanNodeBuilder({}).build(trace); - expect(root.span).toEqual( - { - traceId: '000000000000000a', - id: '000000000000000b', - annotations: [], - tags: {}, - }, - ); + expect(root.span).toEqual({ + traceId: '000000000000000a', + id: '000000000000000b', + annotations: [], + tags: {}, + }); expect(root.children.length).toBe(0); }); @@ -213,18 +213,27 @@ describe('SpanNodeBuilder', () => { const trace = [ { traceId: 'a', id: '1' }, { - traceId: 'a', parentId: '1', id: 'a', timestamp: 2, + traceId: 'a', + parentId: '1', + id: 'a', + timestamp: 2, }, { - traceId: 'a', parentId: '1', id: 'b', timestamp: 1, + traceId: 'a', + parentId: '1', + id: 'b', + timestamp: 1, }, { traceId: 'a', parentId: '1', id: 'c' }, ].map(clean); const root = new SpanNodeBuilder({}).build(trace); - expect(root.children.map((n) => n.span)) - .toEqual([trace[3], trace[2], trace[1]]); // null first + expect(root.children.map((n) => n.span)).toEqual([ + trace[3], + trace[2], + trace[1], + ]); // null first }); it('should order children by timestamp when IPs change ', () => { diff --git a/zipkin-lens/src/zipkin/span-row.js b/zipkin-lens/src/zipkin/span-row.js index ac8264c4568..e681a41cbd6 100644 --- a/zipkin-lens/src/zipkin/span-row.js +++ b/zipkin-lens/src/zipkin/span-row.js @@ -18,7 +18,8 @@ import { ConstantNames } from './trace-constants'; // returns currentErrorType export function getErrorType(span, currentErrorType) { if (currentErrorType === 'critical') return currentErrorType; - if (span.tags.error !== undefined) { // empty error tag is ok + if (span.tags.error !== undefined) { + // empty error tag is ok return 'critical'; } if (span.annotations.findIndex((ann) => ann.value === 'error') !== -1) { @@ -29,9 +30,7 @@ export function getErrorType(span, currentErrorType) { export function formatEndpoint(endpoint) { if (!endpoint) return undefined; - const { - ipv4, ipv6, port, serviceName, - } = endpoint; + const { ipv4, ipv6, port, serviceName } = endpoint; if (ipv4 || ipv6) { const ip = ipv6 ? `[${ipv6}]` : ipv4; // arbitrarily prefer ipv6 const portString = port ? `:${port}` : ''; @@ -177,10 +176,16 @@ function parseAnnotationRows(span) { const annotations = []; // prefer empty to undefined for arrays if (beginAnnotation) { - annotations.push(toAnnotationRow({ - value: begin, - timestamp: startTs, - }, localFormatted, true)); + annotations.push( + toAnnotationRow( + { + value: begin, + timestamp: startTs, + }, + localFormatted, + true, + ), + ); } annotationsToAdd.forEach((a) => { @@ -190,10 +195,16 @@ function parseAnnotationRows(span) { }); if (endAnnotation) { - annotations.push(toAnnotationRow({ - value: end, - timestamp: endTs, - }, localFormatted, true)); + annotations.push( + toAnnotationRow( + { + value: end, + timestamp: endTs, + }, + localFormatted, + true, + ), + ); } return annotations; } @@ -215,7 +226,12 @@ function parseTagRows(span) { } // Ensure there's at least some data that will display the local address - if (!span.kind && span.annotations.length === 0 && localFormatted && keys.length === 0) { + if ( + !span.kind && + span.annotations.length === 0 && + localFormatted && + keys.length === 0 + ) { tagRows.push({ key: 'Local Address', value: localFormatted, @@ -250,14 +266,20 @@ function parseTagRows(span) { // This ensures we don't add duplicate annotations on merge function maybePushAnnotation(annotations, a) { - if (annotations.findIndex((b) => a.timestamp === b.timestamp && a.value === b.value) === -1) { + if ( + annotations.findIndex( + (b) => a.timestamp === b.timestamp && a.value === b.value, + ) === -1 + ) { annotations.push(a); } } // This ensures we only add rows for tags that are unique on key and value on merge function maybePushTag(tags, a) { - const sameKeyAndValue = tags.filter((b) => a.key === b.key && a.value === b.value); + const sameKeyAndValue = tags.filter( + (b) => a.key === b.key && a.value === b.value, + ); if (sameKeyAndValue.length === 0) { tags.push(a); return; @@ -289,7 +311,7 @@ export function getServiceName(endpoint) { } function isNullOrUndefined(ref) { - return typeof (ref) === 'undefined' || ref === null; + return typeof ref === 'undefined' || ref === null; } // Merges the data into a single span row, which is lacking presentation information @@ -311,7 +333,8 @@ export function newSpanRow(spansToMerge, isLeafSpan) { res.spanName = next.name; // prefer the server's span name } - if (next.shared) { // save off any shared timestamp, it is our second choice + if (next.shared) { + // save off any shared timestamp, it is our second choice if (!sharedTimestamp) sharedTimestamp = next.timestamp; if (!sharedDuration) sharedDuration = next.duration; } else { @@ -323,7 +346,12 @@ export function newSpanRow(spansToMerge, isLeafSpan) { const nextRemoteServiceName = getServiceName(next.remoteEndpoint); if (nextLocalServiceName && next.kind === 'SERVER') { res.serviceName = nextLocalServiceName; // prefer the server's service name - } else if (isLeafSpan && nextRemoteServiceName && next.kind === 'CLIENT' && !res.serviceName) { + } else if ( + isLeafSpan && + nextRemoteServiceName && + next.kind === 'CLIENT' && + !res.serviceName + ) { // use the client's remote service name only on leaf spans res.serviceName = nextRemoteServiceName; } else if (nextLocalServiceName && !res.serviceName) { @@ -333,7 +361,9 @@ export function newSpanRow(spansToMerge, isLeafSpan) { maybePushServiceName(res.serviceNames, nextLocalServiceName); maybePushServiceName(res.serviceNames, nextRemoteServiceName); - parseAnnotationRows(next).forEach((a) => maybePushAnnotation(res.annotations, a)); + parseAnnotationRows(next).forEach((a) => + maybePushAnnotation(res.annotations, a), + ); parseTagRows(next).forEach((t) => maybePushTag(res.tags, t)); res.errorType = getErrorType(next, res.errorType); diff --git a/zipkin-lens/src/zipkin/span-row.test.js b/zipkin-lens/src/zipkin/span-row.test.js index c829db460fc..d9a64754057 100644 --- a/zipkin-lens/src/zipkin/span-row.test.js +++ b/zipkin-lens/src/zipkin/span-row.test.js @@ -195,12 +195,17 @@ describe('SPAN v2 -> spanRow Conversion', () => { }); it('should not duplicate service names', () => { - const converted = newSpanRow([clean({ - traceId: '1', - id: '3', - localEndpoint: frontend, - remoteEndpoint: frontend, - })], false); + const converted = newSpanRow( + [ + clean({ + traceId: '1', + id: '3', + localEndpoint: frontend, + remoteEndpoint: frontend, + }), + ], + false, + ); expect(converted.serviceNames).toEqual(['frontend']); }); @@ -334,8 +339,9 @@ describe('SPAN v2 -> spanRow Conversion', () => { }); const converted = newSpanRow([v2], false); - expect(converted.tags) - .toEqual([{ key: 'Server Address', value: '192.168.99.101:9000 (backend)' }]); + expect(converted.tags).toEqual([ + { key: 'Server Address', value: '192.168.99.101:9000 (backend)' }, + ]); expect(converted.serviceName).toEqual('unknown'); expect(converted.serviceNames).toEqual(['backend']); }); @@ -410,8 +416,16 @@ describe('SPAN v2 -> spanRow Conversion', () => { }, ], tags: [ - { key: 'http.path', value: '/api', endpoints: ['192.168.99.101:9000 (backend)'] }, - { key: 'finagle.version', value: '6.45.0', endpoints: ['192.168.99.101:9000 (backend)'] }, + { + key: 'http.path', + value: '/api', + endpoints: ['192.168.99.101:9000 (backend)'], + }, + { + key: 'finagle.version', + value: '6.45.0', + endpoints: ['192.168.99.101:9000 (backend)'], + }, { key: 'Client Address', value: '127.0.0.1:8080 (frontend)' }, ], serviceName: 'backend', @@ -609,8 +623,9 @@ describe('SPAN v2 -> spanRow Conversion', () => { }); const converted = newSpanRow([v2], false); - expect(converted.tags) - .toEqual([{ key: 'Client Address', value: '127.0.0.1:8080 (frontend)' }]); + expect(converted.tags).toEqual([ + { key: 'Client Address', value: '127.0.0.1:8080 (frontend)' }, + ]); expect(converted.serviceName).toEqual('unknown'); expect(converted.serviceNames).toEqual(['frontend']); }); @@ -846,7 +861,9 @@ describe('SPAN v2 -> spanRow Conversion', () => { }); const spanRow = newSpanRow([v2], false); - expect(spanRow.tags.map((s) => s.value)).toEqual(['[2001:db8::c001]:80 (there)']); + expect(spanRow.tags.map((s) => s.value)).toEqual([ + '[2001:db8::c001]:80 (there)', + ]); }); it('should not require endpoint serviceName', () => { @@ -861,7 +878,9 @@ describe('SPAN v2 -> spanRow Conversion', () => { }); const spanRow = newSpanRow([v2], false); - expect(spanRow.annotations.map((s) => s.endpoint)).toEqual(['[2001:db8::c001]']); + expect(spanRow.annotations.map((s) => s.endpoint)).toEqual([ + '[2001:db8::c001]', + ]); }); it('converts client leaf spans using its remote service name', () => { @@ -1016,28 +1035,40 @@ describe('newSpanRow', () => { // originally zipkin2.v1.SpanConverterTest.mergeWhenTagsSentSeparately it('should add late server addr', () => { - const spanRow = newSpanRow([clientSpan, clean({ - traceId: '1', - id: '3', - remoteEndpoint: backend, - })], false); - - expect(spanRow.tags).toEqual( - [{ key: 'Server Address', value: '192.168.99.101:9000 (backend)' }], + const spanRow = newSpanRow( + [ + clientSpan, + clean({ + traceId: '1', + id: '3', + remoteEndpoint: backend, + }), + ], + false, ); + + expect(spanRow.tags).toEqual([ + { key: 'Server Address', value: '192.168.99.101:9000 (backend)' }, + ]); }); // originally zipkin2.v1.SpanConverterTest.mergePrefersServerSpanName it('should overwrite client name with server name', () => { - const spanRow = newSpanRow([clientSpan, clean({ - traceId: '1', - id: '3', - name: 'get /users/:userId', - timestamp: 1472470996238000, - kind: 'SERVER', - localEndpoint: backend, - shared: true, - })], false); + const spanRow = newSpanRow( + [ + clientSpan, + clean({ + traceId: '1', + id: '3', + name: 'get /users/:userId', + timestamp: 1472470996238000, + kind: 'SERVER', + localEndpoint: backend, + shared: true, + }), + ], + false, + ); expect(spanRow.spanName).toBe('get /users/:userId'); }); @@ -1085,99 +1116,128 @@ describe('newSpanRow', () => { }); it('should not overwrite client name with empty', () => { - const spanRow = newSpanRow([clientSpan, clean({ - traceId: '1', - id: '3', - timestamp: 1472470996238000, - kind: 'SERVER', - localEndpoint: backend, - shared: true, - })], false); + const spanRow = newSpanRow( + [ + clientSpan, + clean({ + traceId: '1', + id: '3', + timestamp: 1472470996238000, + kind: 'SERVER', + localEndpoint: backend, + shared: true, + }), + ], + false, + ); expect(spanRow.spanName).toBe(clientSpan.name); }); it('should dedupe annotations with same timestamp and value', () => { - const spanRow = newSpanRow([ - clean({ - traceId: '1', - parentId: '2', - id: '3', - kind: 'CLIENT', - localEndpoint: frontend, - annotations: [{ timestamp: 1, value: 'hit' }], - }), - clean({ - traceId: '1', - parentId: '2', - id: '3', - kind: 'CLIENT', - localEndpoint: frontend, - annotations: [{ timestamp: 1, value: 'hit' }], - }), - ], false); + const spanRow = newSpanRow( + [ + clean({ + traceId: '1', + parentId: '2', + id: '3', + kind: 'CLIENT', + localEndpoint: frontend, + annotations: [{ timestamp: 1, value: 'hit' }], + }), + clean({ + traceId: '1', + parentId: '2', + id: '3', + kind: 'CLIENT', + localEndpoint: frontend, + annotations: [{ timestamp: 1, value: 'hit' }], + }), + ], + false, + ); expect(spanRow.annotations).toEqual([ { - timestamp: 1, value: 'hit', endpoint: '127.0.0.1:8080 (frontend)', isDerived: false, + timestamp: 1, + value: 'hit', + endpoint: '127.0.0.1:8080 (frontend)', + isDerived: false, }, ]); }); it('should merge endpoints on shared tag', () => { - const spanRow = newSpanRow([ - clean({ - traceId: '1', - parentId: '2', - id: '3', - kind: 'CLIENT', - localEndpoint: frontend, - tags: { 'http.path': '/foo' }, - }), - clean({ - traceId: '1', - parentId: '2', - id: '3', - shared: true, - kind: 'SERVER', - localEndpoint: backend, - tags: { 'http.path': '/foo' }, - }), - ], false); + const spanRow = newSpanRow( + [ + clean({ + traceId: '1', + parentId: '2', + id: '3', + kind: 'CLIENT', + localEndpoint: frontend, + tags: { 'http.path': '/foo' }, + }), + clean({ + traceId: '1', + parentId: '2', + id: '3', + shared: true, + kind: 'SERVER', + localEndpoint: backend, + tags: { 'http.path': '/foo' }, + }), + ], + false, + ); expect(spanRow.tags).toEqual([ { key: 'http.path', value: '/foo', - endpoints: ['127.0.0.1:8080 (frontend)', '192.168.99.101:9000 (backend)'], + endpoints: [ + '127.0.0.1:8080 (frontend)', + '192.168.99.101:9000 (backend)', + ], }, ]); }); it('should show difference in tag values per endpoint', () => { - const spanRow = newSpanRow([ - clean({ - traceId: '1', - parentId: '2', - id: '3', - kind: 'CLIENT', - localEndpoint: frontend, - tags: { 'http.path': '/foo' }, - }), - clean({ - traceId: '1', - parentId: '2', - id: '3', - shared: true, - kind: 'SERVER', - localEndpoint: backend, - tags: { 'http.path': '/foo/redirected' }, - }), - ], false); + const spanRow = newSpanRow( + [ + clean({ + traceId: '1', + parentId: '2', + id: '3', + kind: 'CLIENT', + localEndpoint: frontend, + tags: { 'http.path': '/foo' }, + }), + clean({ + traceId: '1', + parentId: '2', + id: '3', + shared: true, + kind: 'SERVER', + localEndpoint: backend, + tags: { 'http.path': '/foo/redirected' }, + }), + ], + false, + ); expect(spanRow.tags).toEqual([ - { key: 'http.path', value: '/foo', endpoints: ['127.0.0.1:8080 (frontend)'] }, - { key: 'http.path', value: '/foo/redirected', endpoints: ['192.168.99.101:9000 (backend)'] }, + { + key: 'http.path', + value: '/foo', + endpoints: ['127.0.0.1:8080 (frontend)'], + }, + { + key: 'http.path', + value: '/foo/redirected', + endpoints: ['192.168.99.101:9000 (backend)'], + }, ]); }); @@ -1197,36 +1257,56 @@ describe('newSpanRow', () => { describe('formatEndpoint', () => { it('should format ip and port', () => { - expect(formatEndpoint({ ipv4: '150.151.152.153', port: 5000 })).toBe('150.151.152.153:5000'); + expect(formatEndpoint({ ipv4: '150.151.152.153', port: 5000 })).toBe( + '150.151.152.153:5000', + ); }); it('should not use port when missing or zero', () => { expect(formatEndpoint({ ipv4: '150.151.152.153' })).toBe('150.151.152.153'); - expect(formatEndpoint({ ipv4: '150.151.152.153', port: 0 })).toBe('150.151.152.153'); + expect(formatEndpoint({ ipv4: '150.151.152.153', port: 0 })).toBe( + '150.151.152.153', + ); }); it('should put service name in parenthesis', () => { - expect(formatEndpoint({ - ipv4: '150.151.152.153', port: 9042, serviceName: 'cassandra', - })).toBe('150.151.152.153:9042 (cassandra)'); - expect(formatEndpoint({ - ipv4: '150.151.152.153', serviceName: 'cassandra', - })).toBe('150.151.152.153 (cassandra)'); + expect( + formatEndpoint({ + ipv4: '150.151.152.153', + port: 9042, + serviceName: 'cassandra', + }), + ).toBe('150.151.152.153:9042 (cassandra)'); + expect( + formatEndpoint({ + ipv4: '150.151.152.153', + serviceName: 'cassandra', + }), + ).toBe('150.151.152.153 (cassandra)'); }); it('should not show empty service name', () => { - expect(formatEndpoint({ - ipv4: '150.151.152.153', port: 9042, serviceName: '', - })).toBe('150.151.152.153:9042'); - expect(formatEndpoint({ - ipv4: '150.151.152.153', serviceName: '', - })).toBe('150.151.152.153'); + expect( + formatEndpoint({ + ipv4: '150.151.152.153', + port: 9042, + serviceName: '', + }), + ).toBe('150.151.152.153:9042'); + expect( + formatEndpoint({ + ipv4: '150.151.152.153', + serviceName: '', + }), + ).toBe('150.151.152.153'); }); it('should show service name missing IP', () => { - expect(formatEndpoint({ - serviceName: 'rabbit', - })).toBe('rabbit'); + expect( + formatEndpoint({ + serviceName: 'rabbit', + }), + ).toBe('rabbit'); }); it('should not crash on no data', () => { @@ -1234,16 +1314,25 @@ describe('formatEndpoint', () => { }); it('should put ipv6 in brackets', () => { - expect(formatEndpoint({ - ipv6: '2001:db8::c001', port: 9042, serviceName: 'cassandra', - })).toBe('[2001:db8::c001]:9042 (cassandra)'); + expect( + formatEndpoint({ + ipv6: '2001:db8::c001', + port: 9042, + serviceName: 'cassandra', + }), + ).toBe('[2001:db8::c001]:9042 (cassandra)'); - expect(formatEndpoint({ - ipv6: '2001:db8::c001', port: 9042, - })).toBe('[2001:db8::c001]:9042'); + expect( + formatEndpoint({ + ipv6: '2001:db8::c001', + port: 9042, + }), + ).toBe('[2001:db8::c001]:9042'); - expect(formatEndpoint({ - ipv6: '2001:db8::c001', - })).toBe('[2001:db8::c001]'); + expect( + formatEndpoint({ + ipv6: '2001:db8::c001', + }), + ).toBe('[2001:db8::c001]'); }); }); diff --git a/zipkin-lens/src/zipkin/trace.js b/zipkin-lens/src/zipkin/trace.js index 5d4371be6e6..dec760c74dd 100644 --- a/zipkin-lens/src/zipkin/trace.js +++ b/zipkin-lens/src/zipkin/trace.js @@ -79,7 +79,8 @@ export function traceSummary(root) { addServiceNameTimestampDuration(span, groupedTimestamps); }); - if (timestamps.length === 0) throw new Error(`Trace ${traceId} is missing a timestamp`); + if (timestamps.length === 0) + throw new Error(`Trace ${traceId} is missing a timestamp`); return { traceId, @@ -113,12 +114,15 @@ export function totalDuration(timestampAndDurations) { const next = filtered[i]; const nextIntervalEnd = next.timestamp + next.duration; - if (nextIntervalEnd <= currentIntervalEnd) { // we are still in the interval + if (nextIntervalEnd <= currentIntervalEnd) { + // we are still in the interval continue; - } else if (next.timestamp <= currentIntervalEnd) { // we extending the interval + } else if (next.timestamp <= currentIntervalEnd) { + // we extending the interval result += nextIntervalEnd - currentIntervalEnd; currentIntervalEnd = nextIntervalEnd; - } else { // this is a new interval + } else { + // this is a new interval result += next.duration; currentIntervalEnd = nextIntervalEnd; } @@ -143,7 +147,8 @@ export function mkDurationStr(duration) { return `${duration.toFixed(0)}μs`; } if (duration < 1000000) { - if (duration % 1000 === 0) { // Sometimes spans are in milliseconds resolution + if (duration % 1000 === 0) { + // Sometimes spans are in milliseconds resolution return `${(duration / 1000).toFixed(0)}ms`; } return `${(duration / 1000).toFixed(3)}ms`; @@ -153,66 +158,77 @@ export function mkDurationStr(duration) { // maxSpanDurationStr is only used in index.mustache export function getServiceSummaries(groupedTimestamps) { - const services = Object.entries(groupedTimestamps) - .map(([serviceName, sts]) => ({ + const services = Object.entries(groupedTimestamps).map( + ([serviceName, sts]) => ({ serviceName, spanCount: sts.length, maxSpanDuration: Math.max(...sts.map((t) => t.duration)), - })); - return orderBy(services, ['maxSpanDuration', 'serviceName'], ['desc', 'asc']) - .map((summary) => ({ - serviceName: summary.serviceName, - spanCount: summary.spanCount, - maxSpanDurationStr: mkDurationStr(summary.maxSpanDuration), - })); + }), + ); + return orderBy( + services, + ['maxSpanDuration', 'serviceName'], + ['desc', 'asc'], + ).map((summary) => ({ + serviceName: summary.serviceName, + spanCount: summary.spanCount, + maxSpanDurationStr: mkDurationStr(summary.maxSpanDuration), + })); } export function traceSummaries(serviceName, summaries, utc = false) { const maxDuration = Math.max(...summaries.map((s) => s.duration)); - return summaries.map((t) => { - const { timestamp } = t; - - const res = { - traceId: t.traceId, // used to navigate to trace screen - timestamp, // used only for client-side sort - startTs: formatDate(timestamp, utc), - spanCount: t.spanCount, - }; - - const duration = t.duration || 0; - if (duration) { - // used to show the relative duration this trace was compared to others - res.width = parseInt(parseFloat(duration) / parseFloat(maxDuration) * 100, 10); - res.duration = duration / 1000; // used only for client-side sort - res.durationStr = mkDurationStr(duration); - } - - // groupedTimestamps is keyed by service name, if there are no service names in the trace, - // don't try to add data dependent on service names. - if (Object.keys(t.groupedTimestamps).length !== 0) { - res.serviceSummaries = getServiceSummaries(t.groupedTimestamps); + return summaries + .map((t) => { + const { timestamp } = t; + + const res = { + traceId: t.traceId, // used to navigate to trace screen + timestamp, // used only for client-side sort + startTs: formatDate(timestamp, utc), + spanCount: t.spanCount, + }; + + const duration = t.duration || 0; + if (duration) { + // used to show the relative duration this trace was compared to others + res.width = parseInt( + (parseFloat(duration) / parseFloat(maxDuration)) * 100, + 10, + ); + res.duration = duration / 1000; // used only for client-side sort + res.durationStr = mkDurationStr(duration); + } - // Only add a service percentage when there is a duration for it - if (serviceName && duration && t.groupedTimestamps[serviceName]) { - const serviceTime = totalDuration(t.groupedTimestamps[serviceName]); - // used for display and also client-side sort by service percentage - res.servicePercentage = parseInt(parseFloat(serviceTime) / parseFloat(duration) * 100, 10); + // groupedTimestamps is keyed by service name, if there are no service names in the trace, + // don't try to add data dependent on service names. + if (Object.keys(t.groupedTimestamps).length !== 0) { + res.serviceSummaries = getServiceSummaries(t.groupedTimestamps); + + // Only add a service percentage when there is a duration for it + if (serviceName && duration && t.groupedTimestamps[serviceName]) { + const serviceTime = totalDuration(t.groupedTimestamps[serviceName]); + // used for display and also client-side sort by service percentage + res.servicePercentage = parseInt( + (parseFloat(serviceTime) / parseFloat(duration)) * 100, + 10, + ); + } } - } - if (t.errorType !== 'none') res.infoClass = `trace-error-${t.errorType}`; - return res; - }).sort((t1, t2) => { - const durationComparison = t2.duration - t1.duration; - if (durationComparison === 0) { - return t1.traceId.localeCompare(t2.traceId); - } - return durationComparison; - }); + if (t.errorType !== 'none') res.infoClass = `trace-error-${t.errorType}`; + return res; + }) + .sort((t1, t2) => { + const durationComparison = t2.duration - t1.duration; + if (durationComparison === 0) { + return t1.traceId.localeCompare(t2.traceId); + } + return durationComparison; + }); } - function incrementEntry(dict, key) { if (dict[key]) { dict[key] += 1; // eslint-disable-line no-param-reassign @@ -233,15 +249,21 @@ function getTraceTimestampAndDuration(root) { } function addLayoutDetails( - spanRow, traceTimestamp, traceDuration, depth, childIds, -) { /* eslint-disable no-param-reassign */ + spanRow, + traceTimestamp, + traceDuration, + depth, + childIds, +) { + /* eslint-disable no-param-reassign */ spanRow.childIds = childIds; spanRow.depth = depth + 1; spanRow.depthClass = (depth - 1) % 6; // Add the correct width and duration string for the span - if (spanRow.duration) { // implies traceDuration, as trace duration is derived from spans - const width = traceDuration ? spanRow.duration / traceDuration * 100 : 0; + if (spanRow.duration) { + // implies traceDuration, as trace duration is derived from spans + const width = traceDuration ? (spanRow.duration / traceDuration) * 100 : 0; spanRow.width = width < 0.1 ? 0.1 : width; spanRow.durationStr = mkDurationStr(spanRow.duration); // bubble over the span in trace view } else { @@ -251,12 +273,15 @@ function addLayoutDetails( if (traceDuration) { // position the span at the correct offset in the trace diagram. - spanRow.left = ((spanRow.timestamp - traceTimestamp) / traceDuration * 100); + spanRow.left = ((spanRow.timestamp - traceTimestamp) / traceDuration) * 100; // position each annotation at the offset in the trace diagram. - spanRow.annotations.forEach((a) => { /* eslint-disable no-param-reassign */ + spanRow.annotations.forEach((a) => { + /* eslint-disable no-param-reassign */ // left offset here is from the span - a.left = spanRow.duration ? ((a.timestamp - spanRow.timestamp) / spanRow.duration * 100) : 0; + a.left = spanRow.duration + ? ((a.timestamp - spanRow.timestamp) / spanRow.duration) * 100 + : 0; // relative time is for the trace itself a.relativeTime = mkDurationStr(a.timestamp - traceTimestamp); a.width = 8; // size of the dot @@ -276,7 +301,8 @@ export function detailedTraceSummary(root, logsUrl) { }; const { timestamp, duration } = getTraceTimestampAndDuration(root); - if (!timestamp) throw new Error(`Trace ${modelview.traceId} is missing a timestamp`); + if (!timestamp) + throw new Error(`Trace ${modelview.traceId} is missing a timestamp`); while (queue.length > 0) { let current = queue.shift(); @@ -318,7 +344,9 @@ export function detailedTraceSummary(root, logsUrl) { // // TODO: We should only do this if it is a leaf span and a client or producer. If we are at the // bottom of the tree, it can be helpful to count also against a remote uninstrumented service. - spanRow.serviceNames.forEach((serviceName) => incrementEntry(serviceNameToCount, serviceName)); + spanRow.serviceNames.forEach((serviceName) => + incrementEntry(serviceNameToCount, serviceName), + ); modelview.spans.push(spanRow); } @@ -338,13 +366,16 @@ export function detailedTraceSummary(root, logsUrl) { modelview.serviceNameAndSpanCounts = Object.keys(serviceNameToCount) .sort() .map((serviceName) => ({ - serviceName, spanCount: serviceNameToCount[serviceName], + serviceName, + spanCount: serviceNameToCount[serviceName], })); // the zoom feature needs backups and timeMarkers regardless of if there is a trace duration modelview.spansBackup = modelview.spans; - modelview.timeMarkers = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0] - .map((p, index) => ({ index, time: mkDurationStr(duration * p) })); + modelview.timeMarkers = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0].map((p, index) => ({ + index, + time: mkDurationStr(duration * p), + })); modelview.timeMarkersBackup = modelview.timeMarkers; modelview.duration = duration; diff --git a/zipkin-lens/src/zipkin/trace.test.js b/zipkin-lens/src/zipkin/trace.test.js index 06698009431..fbcabfb57a7 100644 --- a/zipkin-lens/src/zipkin/trace.test.js +++ b/zipkin-lens/src/zipkin/trace.test.js @@ -99,61 +99,63 @@ const cleanedHttpTrace = treeCorrectedForClockSkew(httpTrace); describe('traceSummary', () => { it('should classify durations local to the endpoint', () => { - expect(traceSummary(cleanedHttpTrace).groupedTimestamps).toEqual( - { - frontend: [ - { timestamp: 1541138169255688, duration: 168731 }, - { timestamp: 1541138169297572, duration: 111121 }, - ], - backend: [ - { timestamp: 1541138169377997, duration: 26326 }, - ], - }, - ); + expect(traceSummary(cleanedHttpTrace).groupedTimestamps).toEqual({ + frontend: [ + { timestamp: 1541138169255688, duration: 168731 }, + { timestamp: 1541138169297572, duration: 111121 }, + ], + backend: [{ timestamp: 1541138169377997, duration: 26326 }], + }); }); // Ex netflix sometimes add annotations with no duration it('should backfill incomplete duration as zero instead of undefined', () => { - const testTrace = new SpanNode(clean({ - traceId: '2480ccca8df0fca5', - id: '2480ccca8df0fca5', - kind: 'CLIENT', - timestamp: 1541138169297572, - duration: 111121, - localEndpoint: frontend, - })); - testTrace.addChild(new SpanNode(clean({ - traceId: '2480ccca8df0fca5', - parentId: '2480ccca8df0fca5', - id: 'bf396325699c84bf', - timestamp: 1541138169377997, - localEndpoint: backend, - }))); - - expect(traceSummary(testTrace).groupedTimestamps).toEqual( - { - frontend: [ - { timestamp: 1541138169297572, duration: 111121 }, - ], - backend: [ - { timestamp: 1541138169377997, duration: 0 }, - ], - }, + const testTrace = new SpanNode( + clean({ + traceId: '2480ccca8df0fca5', + id: '2480ccca8df0fca5', + kind: 'CLIENT', + timestamp: 1541138169297572, + duration: 111121, + localEndpoint: frontend, + }), + ); + testTrace.addChild( + new SpanNode( + clean({ + traceId: '2480ccca8df0fca5', + parentId: '2480ccca8df0fca5', + id: 'bf396325699c84bf', + timestamp: 1541138169377997, + localEndpoint: backend, + }), + ), ); + + expect(traceSummary(testTrace).groupedTimestamps).toEqual({ + frontend: [{ timestamp: 1541138169297572, duration: 111121 }], + backend: [{ timestamp: 1541138169377997, duration: 0 }], + }); }); it('should throw error on trace missing timestamp', () => { let error; try { - traceSummary(new SpanNode(clean({ - traceId: '1e223ff1f80f1c69', - id: 'bf396325699c84bf', - }))); + traceSummary( + new SpanNode( + clean({ + traceId: '1e223ff1f80f1c69', + id: 'bf396325699c84bf', + }), + ), + ); } catch (err) { error = err; } - expect(error.message).toEqual('Trace 1e223ff1f80f1c69 is missing a timestamp'); + expect(error.message).toEqual( + 'Trace 1e223ff1f80f1c69 is missing a timestamp', + ); }); it('calculates timestamp and duration', () => { @@ -183,7 +185,11 @@ describe('traceSummariesToMustache', () => { it('should get service summaries, ordered descending by max span duration', () => { const model = traceSummaries(null, [summary]); expect(model[0].serviceSummaries).toEqual([ - { serviceName: 'frontend', spanCount: 2, maxSpanDurationStr: '168.731ms' }, + { + serviceName: 'frontend', + spanCount: 2, + maxSpanDurationStr: '168.731ms', + }, { serviceName: 'backend', spanCount: 1, maxSpanDurationStr: '26.326ms' }, ]); }); @@ -248,27 +254,39 @@ describe('traceSummariesToMustache', () => { const traceId1 = '9ed44141f679130b'; const traceId2 = '6ff1c14161f7bde1'; const traceId3 = '1234561234561234'; - const summary1 = traceSummary(new SpanNode(clean({ - traceId: traceId1, - name: 'get', - id: '6ff1c14161f7bde1', - timestamp: 1457186441657000, - duration: 4000, - }))); - const summary2 = traceSummary(new SpanNode(clean({ - traceId: traceId2, - name: 'get', - id: '9ed44141f679130b', - timestamp: 1457186568026000, - duration: 4000, - }))); - const summary3 = traceSummary(new SpanNode(clean({ - traceId: traceId3, - name: 'get', - id: '6677567324735', - timestamp: 1457186568027000, - duration: 3000, - }))); + const summary1 = traceSummary( + new SpanNode( + clean({ + traceId: traceId1, + name: 'get', + id: '6ff1c14161f7bde1', + timestamp: 1457186441657000, + duration: 4000, + }), + ), + ); + const summary2 = traceSummary( + new SpanNode( + clean({ + traceId: traceId2, + name: 'get', + id: '9ed44141f679130b', + timestamp: 1457186568026000, + duration: 4000, + }), + ), + ); + const summary3 = traceSummary( + new SpanNode( + clean({ + traceId: traceId3, + name: 'get', + id: '6677567324735', + timestamp: 1457186568027000, + duration: 3000, + }), + ), + ); const model = traceSummaries(null, [summary1, summary2, summary3]); expect(model[0].traceId).toBe(traceId2); @@ -328,7 +346,7 @@ describe('totalDuration', () => { { timestamp: 390, duration: 20 }, { timestamp: 400, duration: 30 }, // overlaps with above ]; - expect(totalDuration(asyncTrace)).toBe(300 + ((400 + 30) - 390)); + expect(totalDuration(asyncTrace)).toBe(300 + (400 + 30 - 390)); }); it('should ignore input missing duration', () => { @@ -351,7 +369,11 @@ describe('detailedTraceSummary', () => { it('should derive summary info', () => { const { - traceId, durationStr, depth, serviceNameAndSpanCounts, rootSpan, + traceId, + durationStr, + depth, + serviceNameAndSpanCounts, + rootSpan, } = detailedTraceSummary(cleanedHttpTrace); expect(traceId).toBe('bb1f0e21882325b8'); @@ -371,19 +393,28 @@ describe('detailedTraceSummary', () => { const { spans } = detailedTraceSummary(cleanedNetflixTrace); // the absolute values are not important, just checks that only the root span is at offset 0 - expect(spans.map((s) => s.left)).toEqual( - [0, 8.108108108108109, 16.216216216216218, 64.86486486486487], - ); + expect(spans.map((s) => s.left)).toEqual([ + 0, + 8.108108108108109, + 16.216216216216218, + 64.86486486486487, + ]); }); it('should derive summary info even when headless', () => { const headless = new SpanNode(); // headless as there's no root span // make a copy of the cleaned http trace as adding a child is a mutation - treeCorrectedForClockSkew(httpTrace).children.forEach((child) => headless.addChild(child)); + treeCorrectedForClockSkew(httpTrace).children.forEach((child) => + headless.addChild(child), + ); const { - traceId, durationStr, depth, serviceNameAndSpanCounts, rootSpan, + traceId, + durationStr, + depth, + serviceNameAndSpanCounts, + rootSpan, } = detailedTraceSummary(headless); expect(traceId).toBe('bb1f0e21882325b8'); @@ -400,73 +431,105 @@ describe('detailedTraceSummary', () => { }); it('should show human-readable annotation name', () => { - const { spans: [testSpan] } = detailedTraceSummary(cleanedHttpTrace); + const { + spans: [testSpan], + } = detailedTraceSummary(cleanedHttpTrace); expect(testSpan.annotations[0].value).toBe('Server Start'); expect(testSpan.annotations[1].value).toBe('Server Finish'); expect(testSpan.tags[4].key).toBe('Client Address'); }); it('should tolerate spans without annotations', () => { - const testTrace = new SpanNode(clean({ - traceId: '2480ccca8df0fca5', - name: 'get', - id: '2480ccca8df0fca5', - timestamp: 1457186385375000, - duration: 333000, - localEndpoint: { serviceName: 'zipkin-query', ipv4: '127.0.0.1', port: 9411 }, - tags: { lc: 'component' }, - })); - const { spans: [testSpan] } = detailedTraceSummary(testTrace); + const testTrace = new SpanNode( + clean({ + traceId: '2480ccca8df0fca5', + name: 'get', + id: '2480ccca8df0fca5', + timestamp: 1457186385375000, + duration: 333000, + localEndpoint: { + serviceName: 'zipkin-query', + ipv4: '127.0.0.1', + port: 9411, + }, + tags: { lc: 'component' }, + }), + ); + const { + spans: [testSpan], + } = detailedTraceSummary(testTrace); expect(testSpan.tags[0].key).toBe('Local Component'); }); it('should not include empty Local Component annotations', () => { - const testTrace = new SpanNode(clean({ - traceId: '2480ccca8df0fca5', - name: 'get', - id: '2480ccca8df0fca5', - timestamp: 1457186385375000, - duration: 333000, - localEndpoint: { serviceName: 'zipkin-query', ipv4: '127.0.0.1', port: 9411 }, - })); - const { spans: [testSpan] } = detailedTraceSummary(testTrace); + const testTrace = new SpanNode( + clean({ + traceId: '2480ccca8df0fca5', + name: 'get', + id: '2480ccca8df0fca5', + timestamp: 1457186385375000, + duration: 333000, + localEndpoint: { + serviceName: 'zipkin-query', + ipv4: '127.0.0.1', + port: 9411, + }, + }), + ); + const { + spans: [testSpan], + } = detailedTraceSummary(testTrace); // skips empty Local Component, but still shows it as an address expect(testSpan.tags[0].key).toBe('Local Address'); }); it('should tolerate spans without tags', () => { - const testTrace = new SpanNode(clean({ - traceId: '2480ccca8df0fca5', - name: 'get', - id: '2480ccca8df0fca5', - kind: 'SERVER', - timestamp: 1457186385375000, - duration: 333000, - localEndpoint: { serviceName: 'zipkin-query', ipv4: '127.0.0.1', port: 9411 }, - })); - const { spans: [testSpan] } = detailedTraceSummary(testTrace); + const testTrace = new SpanNode( + clean({ + traceId: '2480ccca8df0fca5', + name: 'get', + id: '2480ccca8df0fca5', + kind: 'SERVER', + timestamp: 1457186385375000, + duration: 333000, + localEndpoint: { + serviceName: 'zipkin-query', + ipv4: '127.0.0.1', + port: 9411, + }, + }), + ); + const { + spans: [testSpan], + } = detailedTraceSummary(testTrace); expect(testSpan.annotations[0].value).toBe('Server Start'); expect(testSpan.annotations[1].value).toBe('Server Finish'); }); // TODO: we should really only allocate remote endpoints when on an uninstrumented link it('should count spans for any endpoint', () => { - const testTrace = new SpanNode(clean({ - traceId: '2480ccca8df0fca5', - id: '2480ccca8df0fca5', - kind: 'CLIENT', - timestamp: 1, - localEndpoint: frontend, - remoteEndpoint: frontend, - })); - testTrace.addChild(new SpanNode(clean({ - traceId: '2480ccca8df0fca5', - parentId: '2480ccca8df0fca5', - id: 'bf396325699c84bf', - name: 'foo', - timestamp: 2, - localEndpoint: backend, - }))); + const testTrace = new SpanNode( + clean({ + traceId: '2480ccca8df0fca5', + id: '2480ccca8df0fca5', + kind: 'CLIENT', + timestamp: 1, + localEndpoint: frontend, + remoteEndpoint: frontend, + }), + ); + testTrace.addChild( + new SpanNode( + clean({ + traceId: '2480ccca8df0fca5', + parentId: '2480ccca8df0fca5', + id: 'bf396325699c84bf', + name: 'foo', + timestamp: 2, + localEndpoint: backend, + }), + ), + ); const { serviceNameAndSpanCounts } = detailedTraceSummary(testTrace); expect(serviceNameAndSpanCounts).toEqual([ @@ -476,21 +539,27 @@ describe('detailedTraceSummary', () => { }); it('should count spans with no timestamp or duration', () => { - const testTrace = new SpanNode(clean({ - traceId: '2480ccca8df0fca5', - id: '2480ccca8df0fca5', - kind: 'CLIENT', - timestamp: 1, // root always needs a timestamp - localEndpoint: frontend, - remoteEndpoint: frontend, - })); - testTrace.addChild(new SpanNode(clean({ - traceId: '2480ccca8df0fca5', - parentId: '2480ccca8df0fca5', - id: 'bf396325699c84bf', - name: 'foo', - localEndpoint: backend, - }))); + const testTrace = new SpanNode( + clean({ + traceId: '2480ccca8df0fca5', + id: '2480ccca8df0fca5', + kind: 'CLIENT', + timestamp: 1, // root always needs a timestamp + localEndpoint: frontend, + remoteEndpoint: frontend, + }), + ); + testTrace.addChild( + new SpanNode( + clean({ + traceId: '2480ccca8df0fca5', + parentId: '2480ccca8df0fca5', + id: 'bf396325699c84bf', + name: 'foo', + localEndpoint: backend, + }), + ), + ); const { serviceNameAndSpanCounts } = detailedTraceSummary(testTrace); expect(serviceNameAndSpanCounts).toEqual([ @@ -646,9 +715,15 @@ describe('detailedTraceSummary', () => { }, ]; - const { spans } = detailedTraceSummary(treeCorrectedForClockSkew(traceWithEndpointProblems)); + const { spans } = detailedTraceSummary( + treeCorrectedForClockSkew(traceWithEndpointProblems), + ); expect(spans.map((s) => s.timestamp)).toEqual([ - 1, 11000, 30000, 341172, 359175, // increasing order + 1, + 11000, + 30000, + 341172, + 359175, // increasing order ]); }); });