Merge branch 'develop' into 417-search-filter

This commit is contained in:
Palash 2022-06-23 18:43:21 +05:30 committed by GitHub
commit f57808bdb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 247 additions and 50 deletions

1
.github/CODEOWNERS vendored
View File

@ -4,3 +4,4 @@
* @ankitnayan * @ankitnayan
/frontend/ @palashgdev @pranshuchittora /frontend/ @palashgdev @pranshuchittora
/deploy/ @prashant-shahi /deploy/ @prashant-shahi
/pkg/query-service/ @srikanthccv @makeavish @nityanandagohain

View File

@ -17,6 +17,8 @@ jobs:
run: cd frontend && yarn install run: cd frontend && yarn install
- name: Run ESLint - name: Run ESLint
run: cd frontend && npm run lint run: cd frontend && npm run lint
- name: Run Jest
run: cd frontend && npm run jest
- name: TSC - name: TSC
run: yarn tsc run: yarn tsc
working-directory: ./frontend working-directory: ./frontend

View File

@ -2,3 +2,4 @@
* Adds custom matchers from the react testing library to all tests * Adds custom matchers from the react testing library to all tests
*/ */
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
import 'jest-styled-components';

View File

@ -159,6 +159,7 @@
"husky": "^7.0.4", "husky": "^7.0.4",
"is-ci": "^3.0.1", "is-ci": "^3.0.1",
"jest-playwright-preset": "^1.7.0", "jest-playwright-preset": "^1.7.0",
"jest-styled-components": "^7.0.8",
"less-plugin-npm-import": "^2.1.0", "less-plugin-npm-import": "^2.1.0",
"lint-staged": "^12.3.7", "lint-staged": "^12.3.7",
"portfinder-sync": "^0.0.2", "portfinder-sync": "^0.0.2",

View File

@ -2,8 +2,102 @@
exports[`Not Found page test should render Not Found page without errors 1`] = ` exports[`Not Found page test should render Not Found page without errors 1`] = `
<DocumentFragment> <DocumentFragment>
<div .c3 {
class="sc-gsDKAQ cLXpIa" border: 2px solid #2f80ed;
box-sizing: border-box;
border-radius: 10px;
width: 400px;
background: inherit;
font-style: normal;
font-weight: normal;
font-size: 24px;
line-height: 20px;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
padding-top: 14px;
padding-bottom: 14px;
color: #2f80ed;
}
.c0 {
min-height: 80vh;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c2 {
font-style: normal;
font-weight: 300;
font-size: 18px;
line-height: 20px;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-align: center;
color: #828282;
text-align: center;
margin: 0;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c1 {
min-height: 50px;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
margin-bottom: 30px;
margin-top: 20px;
}
<div
class="c0"
> >
<svg <svg
fill="none" fill="none"
@ -272,21 +366,21 @@ exports[`Not Found page test should render Not Found page without errors 1`] = `
</defs> </defs>
</svg> </svg>
<div <div
class="sc-hKwDye foaleg" class="c1"
> >
<p <p
class="sc-dkPtRN fcyVIq" class="c2"
> >
Ah, seems like we reached a dead end! Ah, seems like we reached a dead end!
</p> </p>
<p <p
class="sc-dkPtRN fcyVIq" class="c2"
> >
Page Not Found Page Not Found
</p> </p>
</div> </div>
<a <a
class="sc-bdvvtL dbTZkj" class="c3"
href="/application" href="/application"
tabindex="0" tabindex="0"
> >

View File

@ -12,7 +12,7 @@ const ROUTES = {
ALL_DASHBOARD: '/dashboard', ALL_DASHBOARD: '/dashboard',
DASHBOARD: '/dashboard/:dashboardId', DASHBOARD: '/dashboard/:dashboardId',
DASHBOARD_WIDGET: '/dashboard/:dashboardId/:widgetId', DASHBOARD_WIDGET: '/dashboard/:dashboardId/:widgetId',
EDIT_ALERTS: '/alerts/edit/:ruleId', EDIT_ALERTS: '/alerts/edit',
LIST_ALL_ALERT: '/alerts', LIST_ALL_ALERT: '/alerts',
ALERTS_NEW: '/alerts/new', ALERTS_NEW: '/alerts/new',
ALL_CHANNELS: '/settings/channels', ALL_CHANNELS: '/settings/channels',

View File

@ -57,7 +57,10 @@ function FullView({
time: timePreferenceType, time: timePreferenceType,
): { min: string | number; max: string | number } => { ): { min: string | number; max: string | number } => {
if (time === 'GLOBAL_TIME') { if (time === 'GLOBAL_TIME') {
const minMax = GetMinMax(globalSelectedTime); const minMax = GetMinMax(globalSelectedTime, [
minTime / 1000000,
maxTime / 1000000,
]);
return { return {
min: convertToNanoSecondsToSecond(minMax.minTime / 1000), min: convertToNanoSecondsToSecond(minMax.minTime / 1000),
max: convertToNanoSecondsToSecond(minMax.maxTime / 1000), max: convertToNanoSecondsToSecond(minMax.maxTime / 1000),

View File

@ -11,7 +11,6 @@ import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { UseQueryResult } from 'react-query'; import { UseQueryResult } from 'react-query';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { generatePath } from 'react-router-dom';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { ErrorResponse, SuccessResponse } from 'types/api'; import { ErrorResponse, SuccessResponse } from 'types/api';
import { Alerts } from 'types/api/alerts/getAll'; import { Alerts } from 'types/api/alerts/getAll';
@ -51,11 +50,7 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
const [notifications, Element] = notification.useNotification(); const [notifications, Element] = notification.useNotification();
const onEditHandler = (id: string): void => { const onEditHandler = (id: string): void => {
history.push( history.push(`${ROUTES.EDIT_ALERTS}?ruleId=${id}`);
generatePath(ROUTES.EDIT_ALERTS, {
ruleId: id,
}),
);
}; };
const columns: ColumnsType<Alerts> = [ const columns: ColumnsType<Alerts> = [

View File

@ -15,7 +15,7 @@ const menus: SidebarMenu[] = [
{ {
Icon: BarChartOutlined, Icon: BarChartOutlined,
to: ROUTES.APPLICATION, to: ROUTES.APPLICATION,
name: 'Metrics', name: 'Services',
}, },
{ {
Icon: AlignLeftOutlined, Icon: AlignLeftOutlined,

View File

@ -16,6 +16,7 @@ const breadcrumbNameMap = {
[ROUTES.ORG_SETTINGS]: 'Organization Settings', [ROUTES.ORG_SETTINGS]: 'Organization Settings',
[ROUTES.MY_SETTINGS]: 'My Settings', [ROUTES.MY_SETTINGS]: 'My Settings',
[ROUTES.ERROR_DETAIL]: 'Errors', [ROUTES.ERROR_DETAIL]: 'Errors',
[ROUTES.LIST_ALL_ALERT]: 'Alerts',
}; };
function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element { function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element {

View File

@ -1,7 +1,7 @@
import { Space, Tabs, Typography } from 'antd'; import { Tabs, Tooltip, Typography } from 'antd';
import { StyledSpace } from 'components/Styled'; import { StyledSpace } from 'components/Styled';
import useThemeMode from 'hooks/useThemeMode'; import useThemeMode from 'hooks/useThemeMode';
import React from 'react'; import React, { useMemo } from 'react';
import { ITraceTree } from 'types/api/trace/getTraceItem'; import { ITraceTree } from 'types/api/trace/getTraceItem';
import ErrorTag from './ErrorTag'; import ErrorTag from './ErrorTag';
@ -19,29 +19,38 @@ const { TabPane } = Tabs;
function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element { function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
const { tree } = props; const { tree } = props;
const { isDarkMode } = useThemeMode(); const { isDarkMode } = useThemeMode();
const OverLayComponentName = useMemo(() => tree?.name, [tree?.name]);
const OverLayComponentServiceName = useMemo(() => tree?.serviceName, [
tree?.serviceName,
]);
if (!tree) { if (!tree) {
return <div />; return <div />;
} }
const { name, tags, serviceName } = tree; const { tags } = tree;
return ( return (
<CardContainer> <CardContainer>
<StyledSpace <StyledSpace
styledclass={[styles.selectedSpanDetailsContainer]} styledclass={[styles.selectedSpanDetailsContainer, styles.overflow]}
direction="vertical" direction="vertical"
style={{ marginLeft: '0.5rem' }} style={{ marginLeft: '0.5rem' }}
> >
<strong> Details for selected Span </strong> <strong> Details for selected Span </strong>
<Space direction="vertical" size={2}>
<CustomTitle>Service</CustomTitle> <CustomTitle>Service</CustomTitle>
<CustomText>{serviceName}</CustomText> <Tooltip overlay={OverLayComponentServiceName}>
</Space> <CustomText ellipsis>{tree.serviceName}</CustomText>
<Space direction="vertical" size={2}> </Tooltip>
<CustomTitle>Operation</CustomTitle>
<CustomText>{name}</CustomText> <CustomTitle>Operation</CustomTitle>
</Space> <Tooltip overlay={OverLayComponentName}>
<CustomText ellipsis>{tree.name}</CustomText>
</Tooltip>
</StyledSpace> </StyledSpace>
<Tabs defaultActiveKey="1"> <Tabs defaultActiveKey="1">
<TabPane tab="Tags" key="1"> <TabPane tab="Tags" key="1">
{tags.length !== 0 ? ( {tags.length !== 0 ? (

View File

@ -1,7 +1,7 @@
import { Typography } from 'antd'; import { Space, Typography } from 'antd';
import styled, { css } from 'styled-components'; import styled, { css } from 'styled-components';
const { Text, Title, Paragraph } = Typography; const { Title, Paragraph } = Typography;
export const CustomTitle = styled(Title)` export const CustomTitle = styled(Title)`
&&& { &&& {
@ -9,7 +9,7 @@ export const CustomTitle = styled(Title)`
} }
`; `;
export const CustomText = styled(Text)` export const CustomText = styled(Paragraph)`
&&& { &&& {
color: #2d9cdb; color: #2d9cdb;
} }
@ -17,7 +17,6 @@ export const CustomText = styled(Text)`
export const CustomSubTitle = styled(Title)` export const CustomSubTitle = styled(Title)`
&&& { &&& {
/* color: #bdbdbd; */
font-size: 14px; font-size: 14px;
margin-bottom: 8px; margin-bottom: 8px;
} }
@ -44,6 +43,17 @@ export const CardContainer = styled.div`
width: 100%; width: 100%;
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
`;
export const CustomSpace = styled(Space)`
&&& {
.ant-space-item {
width: 100%;
}
}
`; `;
const removeMargin = css` const removeMargin = css`
@ -60,9 +70,21 @@ const selectedSpanDetailsContainer = css`
const spanEventsTabsContainer = css` const spanEventsTabsContainer = css`
margin-top: 1rem; margin-top: 1rem;
`; `;
const overflow = css`
width: 95%;
> div.ant-space-item:nth-child(4) {
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
`;
export const styles = { export const styles = {
removeMargin, removeMargin,
removePadding, removePadding,
selectedSpanDetailsContainer, selectedSpanDetailsContainer,
spanEventsTabsContainer, spanEventsTabsContainer,
overflow,
}; };

View File

@ -2,12 +2,30 @@
exports[`loads and displays greeting 1`] = ` exports[`loads and displays greeting 1`] = `
<DocumentFragment> <DocumentFragment>
<div .c1 {
class="sc-gsDKAQ jFDWPs" position: absolute;
top: 0px;
left: NaN%;
width: Infinity%;
height: 10px;
margin: 1px 0;
background-color: hsl(282.9,100%,60.7%);
border-radius: 5px;
z-index: 1;
}
.c0 {
position: relative;
width: 100%;
height: 120px;
}
<div
class="c0"
height="0" height="0"
> >
<div <div
class="sc-bdvvtL fyFVjh" class="c1"
title="" title=""
width="Infinity" width="Infinity"
/> />

View File

@ -1,23 +1,45 @@
import { notification } from 'antd';
import get from 'api/alerts/get'; import get from 'api/alerts/get';
import Spinner from 'components/Spinner'; import Spinner from 'components/Spinner';
import ROUTES from 'constants/routes';
import EditRulesContainer from 'container/EditRules'; import EditRulesContainer from 'container/EditRules';
import React from 'react'; import history from 'lib/history';
import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import { useParams } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
function EditRules(): JSX.Element { function EditRules(): JSX.Element {
const { ruleId } = useParams<EditRulesParam>(); const { search } = useLocation();
const params = new URLSearchParams(search);
const ruleId = params.get('ruleId');
const { t } = useTranslation('common'); const { t } = useTranslation('common');
const isValidRuleId = ruleId !== null && String(ruleId).length !== 0;
const { isLoading, data, isError } = useQuery(['ruleId', ruleId], { const { isLoading, data, isError } = useQuery(['ruleId', ruleId], {
queryFn: () => queryFn: () =>
get({ get({
id: parseInt(ruleId, 10), id: parseInt(ruleId || '', 10),
}), }),
enabled: isValidRuleId,
}); });
if (isError) { useEffect(() => {
if (!isValidRuleId) {
notification.error({
message: 'Rule Id is required',
});
history.replace(ROUTES.LIST_ALL_ALERT);
}
}, [isValidRuleId, ruleId]);
if (
(isError && !isValidRuleId) ||
ruleId == null ||
(data?.payload?.data === undefined && !isLoading)
) {
return <div>{data?.error || t('something_went_wrong')}</div>; return <div>{data?.error || t('something_went_wrong')}</div>;
} }
@ -28,8 +50,4 @@ function EditRules(): JSX.Element {
return <EditRulesContainer ruleId={ruleId} initialData={data.payload.data} />; return <EditRulesContainer ruleId={ruleId} initialData={data.payload.data} />;
} }
interface EditRulesParam {
ruleId: string;
}
export default EditRules; export default EditRules;

View File

@ -35,6 +35,7 @@
"playwright.config.ts", "playwright.config.ts",
"./commitlint.config.js", "./commitlint.config.js",
"./webpack.config.js", "./webpack.config.js",
"./webpack.config.prod.js" "./webpack.config.prod.js",
"./jest.setup.ts"
] ]
} }

View File

@ -8128,6 +8128,13 @@ jest-snapshot@^27.5.1:
pretty-format "^27.5.1" pretty-format "^27.5.1"
semver "^7.3.2" semver "^7.3.2"
jest-styled-components@^7.0.8:
version "7.0.8"
resolved "https://registry.yarnpkg.com/jest-styled-components/-/jest-styled-components-7.0.8.tgz#9ea3b43f002de060b4638fde3b422d14b3e3ec9f"
integrity sha512-0KE54d0yIzKcvtOv8eikyjG3rFRtKYUyQovaoha3nondtZzXYGB3bhsvYgEegU08Iry0ndWx2+g9f5ZzD4I+0Q==
dependencies:
css "^3.0.0"
jest-util@^26.6.2: jest-util@^26.6.2:
version "26.6.2" version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1"

View File

@ -592,21 +592,45 @@ func (r *ClickHouseReader) GetRulesFromDB() (*[]model.RuleResponseItem, *model.A
func (r *ClickHouseReader) GetRule(id string) (*model.RuleResponseItem, *model.ApiError) { func (r *ClickHouseReader) GetRule(id string) (*model.RuleResponseItem, *model.ApiError) {
idInt, _ := strconv.Atoi(id) idInt, err := strconv.Atoi(id)
if err != nil {
zap.S().Debug("Error in parsing param: ", err)
return nil, &model.ApiError{Typ: model.ErrorBadData, Err: err}
}
rule := &model.RuleResponseItem{} rule := &model.RuleResponseItem{}
query := fmt.Sprintf("SELECT id, updated_at, data FROM rules WHERE id=%d", idInt) query := "SELECT id, updated_at, data FROM rules WHERE id=?"
rows, err := r.localDB.Query(query, idInt)
err := r.localDB.Get(rule, query)
zap.S().Info(query)
if err != nil { if err != nil {
zap.S().Debug("Error in processing sql query: ", err) zap.S().Debug("Error in processing sql query: ", err)
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
} }
count := 0
// iterate over each row
for rows.Next() {
err = rows.Scan(&rule.Id, &rule.UpdatedAt, &rule.Data)
if err != nil {
zap.S().Debug(err)
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
}
count += 1
}
if count == 0 {
err = fmt.Errorf("no rule with id %d found", idInt)
zap.S().Debug(err)
return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: err}
}
if count > 1 {
err = fmt.Errorf("multiple rules with id %d found", idInt)
zap.S().Debug(err)
return nil, &model.ApiError{Typ: model.ErrorConflict, Err: err}
}
return rule, nil return rule, nil
} }