From 225a345baa29ed4cf8fa74e18b43c82194830c78 Mon Sep 17 00:00:00 2001 From: Palash gupta Date: Tue, 29 Mar 2022 00:02:16 +0530 Subject: [PATCH 01/46] chore: getTagValue api is added --- frontend/src/api/trace/getTagValue.ts | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 frontend/src/api/trace/getTagValue.ts diff --git a/frontend/src/api/trace/getTagValue.ts b/frontend/src/api/trace/getTagValue.ts new file mode 100644 index 0000000000..25156d32ef --- /dev/null +++ b/frontend/src/api/trace/getTagValue.ts @@ -0,0 +1,28 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/trace/getTagValue'; + +const getTagValue = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/getTagValues`, { + start: props.start.toString(), + end: props.end.toString(), + tagKey: props.tagKey, + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getTagValue; From d4d1104a5328c574165f99da2ed02d9fe9c834f1 Mon Sep 17 00:00:00 2001 From: Palash gupta Date: Tue, 29 Mar 2022 00:02:56 +0530 Subject: [PATCH 02/46] WIP: value suggestion is added --- .../AllTags/Tag/DebounceSelect/index.tsx | 63 +++++++++++++++++++ .../Trace/Search/AllTags/Tag/config.ts | 32 ++++++++++ .../Trace/Search/AllTags/Tag/index.tsx | 25 +++++--- .../Trace/Search/AllTags/Tag/styles.ts | 6 -- frontend/src/types/api/trace/getTagValue.ts | 11 ++++ 5 files changed, 123 insertions(+), 14 deletions(-) create mode 100644 frontend/src/container/Trace/Search/AllTags/Tag/DebounceSelect/index.tsx create mode 100644 frontend/src/container/Trace/Search/AllTags/Tag/config.ts create mode 100644 frontend/src/types/api/trace/getTagValue.ts diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/DebounceSelect/index.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/DebounceSelect/index.tsx new file mode 100644 index 0000000000..1971947820 --- /dev/null +++ b/frontend/src/container/Trace/Search/AllTags/Tag/DebounceSelect/index.tsx @@ -0,0 +1,63 @@ +import { Select, Spin } from 'antd'; +import { SelectProps } from 'antd/es/select'; +import debounce from 'lodash-es/debounce'; +import React, { useRef, useState } from 'react'; + +export interface DebounceSelectProps + extends Omit, 'options' | 'children'> { + fetchOptions: (search: string) => Promise; + debounceTimeout: number; +} + +function DebounceSelect< + ValueType extends { + key?: string; + label: React.ReactNode; + value: string | number; + } = never +>({ + fetchOptions, + debounceTimeout = 800, + ...props +}: DebounceSelectProps): JSX.Element { + const [fetching, setFetching] = useState(false); + const [options, setOptions] = useState([]); + const fetchRef = useRef(0); + + const debounceFetcher = React.useMemo(() => { + const loadOptions = (value: string): void => { + fetchRef.current += 1; + const fetchId = fetchRef.current; + setOptions([]); + setFetching(true); + + fetchOptions(value).then((newOptions) => { + if (fetchId !== fetchRef.current) { + // for fetch callback order + return; + } + + setOptions(newOptions); + setFetching(false); + }); + }; + + return debounce(loadOptions, debounceTimeout); + }, [fetchOptions, debounceTimeout]); + + return ( + + labelInValue + filterOption={false} + onSearch={debounceFetcher} + notFoundContent={fetching ? : null} + style={{ minWidth: '170px' }} + // as all other props are from SelectProps only + // eslint-disable-next-line react/jsx-props-no-spreading + {...props} + options={options} + /> + ); +} + +export default DebounceSelect; diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/config.ts b/frontend/src/container/Trace/Search/AllTags/Tag/config.ts new file mode 100644 index 0000000000..07ee042621 --- /dev/null +++ b/frontend/src/container/Trace/Search/AllTags/Tag/config.ts @@ -0,0 +1,32 @@ +import getTagValue from 'api/trace/getTagValue'; + +// Usage of DebounceSelect +export interface TagValue { + label: string; + value: string; +} + +export async function fetchTag( + globalStart: number, + globalEnd: number, + tagKey: string, +): Promise { + const response = await getTagValue({ + end: globalEnd, + start: globalStart, + tagKey, + }); + + if (response.statusCode !== 200 || !response.payload) { + return []; + } + + console.log(response.payload); + + return [ + { + label: 'asd', + value: 'asd', + }, + ]; +} diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx index e6ad17956f..8bcf15757a 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx +++ b/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx @@ -3,14 +3,12 @@ import { Select } from 'antd'; import React from 'react'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; +import { GlobalReducer } from 'types/reducer/globalTime'; import { TraceReducer } from 'types/reducer/trace'; -import { - Container, - IconContainer, - SelectComponent, - ValueSelect, -} from './styles'; +import { fetchTag, TagValue } from './config'; +import DebounceSelect from './DebounceSelect'; +import { Container, IconContainer, SelectComponent } from './styles'; import TagsKey from './TagKey'; const { Option } = Select; @@ -35,6 +33,9 @@ const AllMenu: AllMenuProps[] = [ function SingleTags(props: AllTagsProps): JSX.Element { const traces = useSelector((state) => state.traces); + const globalReducer = useSelector( + (state) => state.globalTime, + ); const { tag, onCloseHandler, setLocalSelectedTags, index } = props; const { @@ -80,7 +81,15 @@ function SingleTags(props: AllTagsProps): JSX.Element { ))} - => + fetchTag(globalReducer.minTime, globalReducer.maxTime, selectedKey[0]) + } + debounceTimeout={300} + mode="tags" + /> + + {/* { setLocalSelectedTags((tags) => [ @@ -94,7 +103,7 @@ function SingleTags(props: AllTagsProps): JSX.Element { ]); }} mode="tags" - /> + /> */} onDeleteTagHandler(index)}> diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/styles.ts b/frontend/src/container/Trace/Search/AllTags/Tag/styles.ts index 8da702197d..347bc287f2 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/styles.ts +++ b/frontend/src/container/Trace/Search/AllTags/Tag/styles.ts @@ -15,12 +15,6 @@ export const SelectComponent = styled(Select)` } `; -export const ValueSelect = styled(Select)` - &&& { - width: 100%; - } -`; - export const Container = styled.div` &&& { display: flex; diff --git a/frontend/src/types/api/trace/getTagValue.ts b/frontend/src/types/api/trace/getTagValue.ts new file mode 100644 index 0000000000..221be8dfba --- /dev/null +++ b/frontend/src/types/api/trace/getTagValue.ts @@ -0,0 +1,11 @@ +import { GlobalReducer } from 'types/reducer/globalTime'; + +export interface Props { + start: GlobalReducer['minTime']; + end: GlobalReducer['maxTime']; + tagKey: string; +} + +export interface PayloadProps { + key: string; +} From 5556d1d6fc8f87f343669a36ba0b49745f57f6f9 Mon Sep 17 00:00:00 2001 From: Palash gupta Date: Tue, 29 Mar 2022 09:59:50 +0530 Subject: [PATCH 03/46] feat: tag value suggestion is updated --- .../AllTags/Tag/DebounceSelect/index.tsx | 2 +- .../Trace/Search/AllTags/Tag/config.ts | 12 ++++------- .../Trace/Search/AllTags/Tag/index.tsx | 21 ++++++++++--------- frontend/src/types/api/trace/getTagValue.ts | 6 ++++-- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/DebounceSelect/index.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/DebounceSelect/index.tsx index 1971947820..2576e782e0 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/DebounceSelect/index.tsx +++ b/frontend/src/container/Trace/Search/AllTags/Tag/DebounceSelect/index.tsx @@ -51,7 +51,7 @@ function DebounceSelect< filterOption={false} onSearch={debounceFetcher} notFoundContent={fetching ? : null} - style={{ minWidth: '170px' }} + style={{ width: '170px' }} // as all other props are from SelectProps only // eslint-disable-next-line react/jsx-props-no-spreading {...props} diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/config.ts b/frontend/src/container/Trace/Search/AllTags/Tag/config.ts index 07ee042621..ce414a5f15 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/config.ts +++ b/frontend/src/container/Trace/Search/AllTags/Tag/config.ts @@ -21,12 +21,8 @@ export async function fetchTag( return []; } - console.log(response.payload); - - return [ - { - label: 'asd', - value: 'asd', - }, - ]; + return response.payload.map((e) => ({ + label: e.tagValues, + value: e.tagValues, + })); } diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx index 8bcf15757a..37b42347a1 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx +++ b/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx @@ -83,27 +83,22 @@ function SingleTags(props: AllTagsProps): JSX.Element { => - fetchTag(globalReducer.minTime, globalReducer.maxTime, selectedKey[0]) + fetchTag(globalReducer.minTime, globalReducer.maxTime, selectedKey[index]) } debounceTimeout={300} - mode="tags" - /> - - {/* { + onSelect={(value: Value): void => { setLocalSelectedTags((tags) => [ ...tags.slice(0, index), { Key: selectedKey, Operator: selectedOperator, - Values: value as string[], + Values: [...selectedValues, value.value], }, ...tags.slice(index + 1, tags.length), ]); }} - mode="tags" - /> */} + mode="multiple" + /> onDeleteTagHandler(index)}> @@ -121,4 +116,10 @@ interface AllTagsProps { >; } +interface Value { + key: string; + label: string; + value: string; +} + export default SingleTags; diff --git a/frontend/src/types/api/trace/getTagValue.ts b/frontend/src/types/api/trace/getTagValue.ts index 221be8dfba..e90975d1d5 100644 --- a/frontend/src/types/api/trace/getTagValue.ts +++ b/frontend/src/types/api/trace/getTagValue.ts @@ -6,6 +6,8 @@ export interface Props { tagKey: string; } -export interface PayloadProps { - key: string; +interface Value { + tagValues: string; } + +export type PayloadProps = Value[]; From 739946fa47d2ead6aef98e963ab71262eb7df3b6 Mon Sep 17 00:00:00 2001 From: Pranshu Chittora Date: Tue, 29 Mar 2022 16:05:08 +0530 Subject: [PATCH 04/46] fix: over memory allocation on Graph on big time range --- .../src/components/Graph/Plugin/EmptyGraph.ts | 17 ++++++++++ frontend/src/components/Graph/hasData.ts | 19 +++++++++++ frontend/src/components/Graph/index.tsx | 13 ++++++-- .../GridGraphLayout/Graph/FullView/index.tsx | 32 ------------------- 4 files changed, 47 insertions(+), 34 deletions(-) create mode 100644 frontend/src/components/Graph/Plugin/EmptyGraph.ts create mode 100644 frontend/src/components/Graph/hasData.ts diff --git a/frontend/src/components/Graph/Plugin/EmptyGraph.ts b/frontend/src/components/Graph/Plugin/EmptyGraph.ts new file mode 100644 index 0000000000..0f0577bb80 --- /dev/null +++ b/frontend/src/components/Graph/Plugin/EmptyGraph.ts @@ -0,0 +1,17 @@ +import { grey } from '@ant-design/colors'; +import { Chart } from 'chart.js'; + +export const emptyGraph = { + id: 'emptyChart', + afterDraw(chart: Chart): void { + const { height, width, ctx } = chart; + chart.clear(); + ctx.save(); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.font = '1.5rem sans-serif'; + ctx.fillStyle = `${grey.primary}`; + ctx.fillText('No data to display', width / 2, height / 2); + ctx.restore(); + }, +}; diff --git a/frontend/src/components/Graph/hasData.ts b/frontend/src/components/Graph/hasData.ts new file mode 100644 index 0000000000..acaa4fac1e --- /dev/null +++ b/frontend/src/components/Graph/hasData.ts @@ -0,0 +1,19 @@ +/* eslint-disable no-restricted-syntax */ +import { ChartData } from 'chart.js'; + +export const hasData = (data: ChartData): boolean => { + const { datasets = [] } = data; + let hasData = false; + try { + for (const dataset of datasets) { + if (dataset.data.length > 0 && dataset.data.some((item) => item !== 0)) { + hasData = true; + break; + } + } + } catch (error) { + console.error(error); + } + + return hasData; +}; diff --git a/frontend/src/components/Graph/index.tsx b/frontend/src/components/Graph/index.tsx index a8f668235d..e51ff21616 100644 --- a/frontend/src/components/Graph/index.tsx +++ b/frontend/src/components/Graph/index.tsx @@ -27,7 +27,9 @@ import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; import AppReducer from 'types/reducer/app'; +import { hasData } from './hasData'; import { legend } from './Plugin'; +import { emptyGraph } from './Plugin/EmptyGraph'; import { LegendsContainer } from './styles'; import { useXAxisTimeUnit } from './xAxisConfig'; import { getYAxisFormattedValue } from './yAxisConfig'; @@ -128,6 +130,7 @@ function Graph({ grid: { display: true, color: getGridColor(), + drawTicks: true, }, adapters: { date: chartjsAdapter, @@ -180,12 +183,18 @@ function Graph({ } }, }; - + const chartHasData = hasData(data); + const chartPlugins = []; + if (chartHasData) { + chartPlugins.push(legend(name, data.datasets.length > 3)); + } else { + chartPlugins.push(emptyGraph); + } lineChartRef.current = new Chart(chartRef.current, { type, data, options, - plugins: [legend(name, data.datasets.length > 3)], + plugins: chartPlugins, }); } }, [ diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx b/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx index 0f0ce7b1fb..6e38e05536 100644 --- a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx +++ b/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx @@ -166,38 +166,6 @@ function FullView({ ); } - if (state.loading === false && state.payload.datasets.length === 0) { - return ( - <> - {fullViewOptions && ( - - - - - )} - - {noDataGraph ? ( - - ) : ( - - No Data - - )} - - ); - } - return ( <> {fullViewOptions && ( From 5a5aca211381e40676cfcba3208ccb193751aea8 Mon Sep 17 00:00:00 2001 From: Pranshu Chittora Date: Tue, 29 Mar 2022 16:06:27 +0530 Subject: [PATCH 05/46] chore: remove unused code --- .../Graph/FullView/EmptyGraph.tsx | 91 ------------------- .../GridGraphLayout/Graph/FullView/index.tsx | 1 - 2 files changed, 92 deletions(-) delete mode 100644 frontend/src/container/GridGraphLayout/Graph/FullView/EmptyGraph.tsx diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/EmptyGraph.tsx b/frontend/src/container/GridGraphLayout/Graph/FullView/EmptyGraph.tsx deleted file mode 100644 index c284d83b7c..0000000000 --- a/frontend/src/container/GridGraphLayout/Graph/FullView/EmptyGraph.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import Graph, { GraphOnClickHandler } from 'components/Graph'; -import { timePreferance } from 'container/NewWidget/RightContainer/timeItems'; -import GetMaxMinTime from 'lib/getMaxMinTime'; -import { colors } from 'lib/getRandomColor'; -import getStartAndEndTime from 'lib/getStartAndEndTime'; -import getTimeString from 'lib/getTimeString'; -import React, { useCallback } from 'react'; -import { useSelector } from 'react-redux'; -import { AppState } from 'store/reducers'; -import { Widgets } from 'types/api/dashboard/getAll'; -import { GlobalReducer } from 'types/reducer/globalTime'; - -function EmptyGraph({ - selectedTime, - widget, - onClickHandler, -}: EmptyGraphProps): JSX.Element { - const { minTime, maxTime, loading } = useSelector( - (state) => state.globalTime, - ); - - const maxMinTime = GetMaxMinTime({ - graphType: widget.panelTypes, - maxTime, - minTime, - }); - - const { end, start } = getStartAndEndTime({ - type: selectedTime.enum, - maxTime: maxMinTime.maxTime, - minTime: maxMinTime.minTime, - }); - - const dateFunction = useCallback(() => { - if (!loading) { - const dates: Date[] = []; - - const startString = getTimeString(start); - const endString = getTimeString(end); - - const parsedStart = parseInt(startString, 10); - const parsedEnd = parseInt(endString, 10); - - let startDate = parsedStart; - const endDate = parsedEnd; - - while (endDate >= startDate) { - const newDate = new Date(startDate); - - startDate += 20000; - - dates.push(newDate); - } - return dates; - } - return []; - }, [start, end, loading]); - - const date = dateFunction(); - - return ( - - ); -} - -interface EmptyGraphProps { - selectedTime: timePreferance; - widget: Widgets; - onClickHandler: GraphOnClickHandler | undefined; -} - -export default EmptyGraph; diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx b/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx index 6e38e05536..fcc656318c 100644 --- a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx +++ b/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx @@ -23,7 +23,6 @@ import { AppState } from 'store/reducers'; import { Widgets } from 'types/api/dashboard/getAll'; import { GlobalReducer } from 'types/reducer/globalTime'; -import EmptyGraph from './EmptyGraph'; import { NotFoundContainer, TimeContainer } from './styles'; function FullView({ From 1d28ceb3d78095801161b4242b651d6fcf08cb57 Mon Sep 17 00:00:00 2001 From: Ahsan Barkati Date: Fri, 1 Apr 2022 11:22:25 +0530 Subject: [PATCH 06/46] feat(query-service): Add cold storage support in getTTL API (#922) * Add cold storage support in getTTL API --- .../app/clickhouseReader/reader.go | 72 ++++++++++-------- pkg/query-service/app/parser.go | 2 +- pkg/query-service/model/response.go | 6 +- pkg/query-service/tests/cold_storage_test.go | 72 ++++++++++++++++++ .../tests/test-deploy/data/signoz.db | Bin 0 -> 32768 bytes 5 files changed, 119 insertions(+), 33 deletions(-) create mode 100644 pkg/query-service/tests/test-deploy/data/signoz.db diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 167aa6236a..de49aa0e49 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -13,6 +13,7 @@ import ( "net/http" "net/url" "os" + "regexp" "sort" "strconv" "strings" @@ -44,8 +45,8 @@ import ( "github.com/prometheus/prometheus/util/strutil" "go.signoz.io/query-service/constants" - "go.signoz.io/query-service/model" am "go.signoz.io/query-service/integrations/alertManager" + "go.signoz.io/query-service/model" "go.uber.org/zap" ) @@ -75,7 +76,7 @@ type ClickHouseReader struct { remoteStorage *remote.Storage ruleManager *rules.Manager promConfig *config.Config - alertManager am.Manager + alertManager am.Manager } // NewTraceReader returns a TraceReader for the database @@ -95,7 +96,7 @@ func NewReader(localDB *sqlx.DB) *ClickHouseReader { return &ClickHouseReader{ db: db, localDB: localDB, - alertManager: alertManager, + alertManager: alertManager, operationsTable: options.primary.OperationsTable, indexTable: options.primary.IndexTable, errorTable: options.primary.ErrorTable, @@ -850,7 +851,6 @@ func (r *ClickHouseReader) EditChannel(receiver *am.Receiver, id string) (*am.Re } - func (r *ClickHouseReader) CreateChannel(receiver *am.Receiver) (*am.Receiver, *model.ApiError) { tx, err := r.localDB.Begin() @@ -860,8 +860,8 @@ func (r *ClickHouseReader) CreateChannel(receiver *am.Receiver) (*am.Receiver, * channel_type := getChannelType(receiver) receiverString, _ := json.Marshal(receiver) - - // todo: check if the channel name already exists, raise an error if so + + // todo: check if the channel name already exists, raise an error if so { stmt, err := tx.Prepare(`INSERT INTO notification_channels (created_at, updated_at, name, type, data) VALUES($1,$2,$3,$4,$5);`) @@ -884,7 +884,7 @@ func (r *ClickHouseReader) CreateChannel(receiver *am.Receiver) (*am.Receiver, * tx.Rollback() return nil, apiError } - + err = tx.Commit() if err != nil { zap.S().Errorf("Error in commiting transaction for INSERT to notification_channels\n", err) @@ -2602,7 +2602,6 @@ func (r *ClickHouseReader) GetDisks(ctx context.Context) (*[]model.DiskItem, *mo fmt.Errorf("error while getting disks. Err=%v", err)} } - zap.S().Infof("Got response: %+v\n", diskItems) return &diskItems, nil @@ -2610,29 +2609,33 @@ func (r *ClickHouseReader) GetDisks(ctx context.Context) (*[]model.DiskItem, *mo func (r *ClickHouseReader) GetTTL(ctx context.Context, ttlParams *model.GetTTLParams) (*model.GetTTLResponseItem, *model.ApiError) { - parseTTL := func(queryResp string) int { - values := strings.Split(queryResp, " ") - N := len(values) - ttlIdx := -1 + parseTTL := func(queryResp string) (int, int) { - for i := 0; i < N; i++ { - if strings.Contains(values[i], "toIntervalSecond") { - ttlIdx = i - break + zap.S().Debugf("Parsing TTL from: %s", queryResp) + deleteTTLExp := regexp.MustCompile(`toIntervalSecond\(([0-9]*)\)`) + moveTTLExp := regexp.MustCompile(`toIntervalSecond\(([0-9]*)\) TO VOLUME`) + + var delTTL, moveTTL int = -1, -1 + + m := deleteTTLExp.FindStringSubmatch(queryResp) + if len(m) > 1 { + seconds_int, err := strconv.Atoi(m[1]) + if err != nil { + return -1, -1 } - } - if ttlIdx == -1 { - return ttlIdx + delTTL = seconds_int / 3600 } - output := strings.SplitN(values[ttlIdx], "(", 2) - timePart := strings.Trim(output[1], ")") - seconds_int, err := strconv.Atoi(timePart) - if err != nil { - return -1 + m = moveTTLExp.FindStringSubmatch(queryResp) + if len(m) > 1 { + seconds_int, err := strconv.Atoi(m[1]) + if err != nil { + return -1, -1 + } + moveTTL = seconds_int / 3600 } - ttl_hrs := seconds_int / 3600 - return ttl_hrs + + return delTTL, moveTTL } getMetricsTTL := func() (*model.DBResponseTTL, *model.ApiError) { @@ -2671,7 +2674,8 @@ func (r *ClickHouseReader) GetTTL(ctx context.Context, ttlParams *model.GetTTLPa return nil, err } - return &model.GetTTLResponseItem{TracesTime: parseTTL(dbResp.EngineFull)}, nil + delTTL, moveTTL := parseTTL(dbResp.EngineFull) + return &model.GetTTLResponseItem{TracesTime: delTTL, TracesMoveTime: moveTTL}, nil case constants.MetricsTTL: dbResp, err := getMetricsTTL() @@ -2679,7 +2683,9 @@ func (r *ClickHouseReader) GetTTL(ctx context.Context, ttlParams *model.GetTTLPa return nil, err } - return &model.GetTTLResponseItem{MetricsTime: parseTTL(dbResp.EngineFull)}, nil + delTTL, moveTTL := parseTTL(dbResp.EngineFull) + return &model.GetTTLResponseItem{MetricsTime: delTTL, MetricsMoveTime: moveTTL}, nil + } db1, err := getTracesTTL() if err != nil { @@ -2690,9 +2696,15 @@ func (r *ClickHouseReader) GetTTL(ctx context.Context, ttlParams *model.GetTTLPa if err != nil { return nil, err } + tracesDelTTL, tracesMoveTTL := parseTTL(db1.EngineFull) + metricsDelTTL, metricsMoveTTL := parseTTL(db2.EngineFull) - return &model.GetTTLResponseItem{TracesTime: parseTTL(db1.EngineFull), MetricsTime: parseTTL(db2.EngineFull)}, nil - + return &model.GetTTLResponseItem{ + TracesTime: tracesDelTTL, + TracesMoveTime: tracesMoveTTL, + MetricsTime: metricsDelTTL, + MetricsMoveTime: metricsMoveTTL, + }, nil } func (r *ClickHouseReader) GetErrors(ctx context.Context, queryParams *model.GetErrorsParams) (*[]model.Error, *model.ApiError) { diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index 3e3abc6c79..ae9abc5204 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -914,7 +914,7 @@ func parseTTLParams(r *http.Request) (*model.TTLParams, error) { if err != nil { return nil, fmt.Errorf("Not a valid toCold TTL duration %v", toColdDuration) } - if toColdParsed.Seconds() >= durationParsed.Seconds() { + if toColdParsed.Seconds() != 0 && toColdParsed.Seconds() >= durationParsed.Seconds() { return nil, fmt.Errorf("Delete TTL should be greater than cold storage move TTL.") } } diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index bbee7224e9..8d4bd4b766 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -283,8 +283,10 @@ type DBResponseTTL struct { } type GetTTLResponseItem struct { - MetricsTime int `json:"metrics_ttl_duration_hrs"` - TracesTime int `json:"traces_ttl_duration_hrs"` + MetricsTime int `json:"metrics_ttl_duration_hrs,omitempty"` + MetricsMoveTime int `json:"metrics_move_ttl_duration_hrs,omitempty"` + TracesTime int `json:"traces_ttl_duration_hrs,omitempty"` + TracesMoveTime int `json:"traces_move_ttl_duration_hrs,omitempty"` } type DBResponseMinMaxDuration struct { diff --git a/pkg/query-service/tests/cold_storage_test.go b/pkg/query-service/tests/cold_storage_test.go index f748db30dc..8159805a56 100644 --- a/pkg/query-service/tests/cold_storage_test.go +++ b/pkg/query-service/tests/cold_storage_test.go @@ -1,6 +1,7 @@ package tests import ( + "encoding/json" "fmt" "io/ioutil" "net/http" @@ -8,6 +9,7 @@ import ( "time" "github.com/stretchr/testify/require" + "go.signoz.io/query-service/model" ) const ( @@ -102,6 +104,76 @@ func TestSetTTL(t *testing.T) { fmt.Printf("=== Found %d objects in Minio\n", count) } +func getTTL(t *testing.T, table string) *model.GetTTLResponseItem { + req := endpoint + fmt.Sprintf("/api/v1/settings/ttl?type=%s", table) + if len(table) == 0 { + req = endpoint + "/api/v1/settings/ttl" + } + + resp, err := client.Get(req) + require.NoError(t, err) + + defer resp.Body.Close() + b, err := ioutil.ReadAll(resp.Body) + require.NoError(t, err) + + res := &model.GetTTLResponseItem{} + require.NoError(t, json.Unmarshal(b, res)) + return res +} + +func TestGetTTL(t *testing.T) { + r, err := setTTL("traces", "s3", "3600s", "7200s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp := getTTL(t, "traces") + require.Equal(t, 1, resp.TracesMoveTime) + require.Equal(t, 2, resp.TracesTime) + + r, err = setTTL("metrics", "s3", "3600s", "7200s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp = getTTL(t, "metrics") + require.Equal(t, 1, resp.MetricsMoveTime) + require.Equal(t, 2, resp.MetricsTime) + + r, err = setTTL("traces", "s3", "36000s", "72000s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp = getTTL(t, "") + require.Equal(t, 10, resp.TracesMoveTime) + require.Equal(t, 20, resp.TracesTime) + require.Equal(t, 1, resp.MetricsMoveTime) + require.Equal(t, 2, resp.MetricsTime) + + r, err = setTTL("metrics", "s3", "15h", "50h") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp = getTTL(t, "") + require.Equal(t, 10, resp.TracesMoveTime) + require.Equal(t, 20, resp.TracesTime) + require.Equal(t, 15, resp.MetricsMoveTime) + require.Equal(t, 50, resp.MetricsTime) + + r, err = setTTL("metrics", "s3", "0s", "0s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + r, err = setTTL("traces", "s3", "0s", "0s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp = getTTL(t, "") + require.Equal(t, 0, resp.TracesMoveTime) + require.Equal(t, 0, resp.TracesTime) + require.Equal(t, 0, resp.MetricsMoveTime) + require.Equal(t, 0, resp.MetricsTime) +} + func TestMain(m *testing.M) { if err := startCluster(); err != nil { fmt.Println(err) diff --git a/pkg/query-service/tests/test-deploy/data/signoz.db b/pkg/query-service/tests/test-deploy/data/signoz.db new file mode 100644 index 0000000000000000000000000000000000000000..c19319ab3476b0101d7f73bdf31e884b5e9f3590 GIT binary patch literal 32768 zcmeI(-%i?490%}Lgo+ztvWwmx?;zR@22po6<~m12RBW?(v2+J|h)tly{z>Mg_z=6^ zv+Nmsh-G_#UGB7stcVb@n>G116x#DUJ@ofEr%g_vrzZ`|XQb=6ed?2nur7$A@R|@o z5Yl{I;p=Ew<{RNUTEx-DjL)?2{Pc4!|67or91Ckd^556K=6*f(a^Lta76?E90uX=z z1Rwwb2qa!$NF+&C6!F^gX}iY)kGaOcWnJbnyTiQ6{gu6zTGLdb)!sBzG8rX>)oj)> zNxiA5`)Z3Ex9W$r))_fa&qz(zj_OT*_@Ua=ws^fD;59Y%qehxXeAOF`u+;KuwqsxR zoxra!UwmG4@5c)(q2 ztJ&gkGcCzGJK}W^5!sG!b*&EdEyp%G=hU`Y&zoA9iD6BxvXEHEWjq_x;1i!PE#K<1 zm|YNM`*_@@{D7D^(VO)XT@A_n%fUo2EHzn=@vpq^iu)djg4Cz+_PAC(laW8|h=OIC z?2C8NZ$07p=KmY;|fB*y_009U< z00Izzz&ruW|K|xI4gm;200Izz00bZa0SG_<0uV^P0G|I(ejg$O2tWV=5P$##AOHaf zKmY;|fWSO~+voppg#69CnutRH0uX=z1Rwwb2tWV=5P$##An;!a6vecluVlAbrNpX~ cD& Date: Fri, 1 Apr 2022 15:12:30 +0530 Subject: [PATCH 07/46] feat: S3 TTL support --- frontend/src/api/disks/getDisks.ts | 24 +++ .../container/GeneralSettings/Retention.tsx | 142 +++++++++--------- .../src/container/GeneralSettings/index.tsx | 113 +++++++++++--- .../src/container/GeneralSettings/styles.ts | 16 +- .../src/container/GeneralSettings/utils.ts | 41 +++++ frontend/src/lib/convertIntoHr.ts | 4 +- frontend/src/lib/getSettingsPeroid.ts | 4 +- .../src/pages/AlertChannelCreate/index.tsx | 4 +- frontend/src/pages/AllAlertChannels/index.tsx | 5 +- frontend/src/pages/Settings/index.tsx | 4 +- frontend/src/types/api/disks/getDisks.ts | 4 + 11 files changed, 241 insertions(+), 120 deletions(-) create mode 100644 frontend/src/api/disks/getDisks.ts create mode 100644 frontend/src/container/GeneralSettings/utils.ts create mode 100644 frontend/src/types/api/disks/getDisks.ts diff --git a/frontend/src/api/disks/getDisks.ts b/frontend/src/api/disks/getDisks.ts new file mode 100644 index 0000000000..70da1bddc9 --- /dev/null +++ b/frontend/src/api/disks/getDisks.ts @@ -0,0 +1,24 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { IDiskType } from 'types/api/disks/getDisks'; + +const getDisks = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get(`/disks`); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getDisks; diff --git a/frontend/src/container/GeneralSettings/Retention.tsx b/frontend/src/container/GeneralSettings/Retention.tsx index b2f5cb5a09..64591b9b59 100644 --- a/frontend/src/container/GeneralSettings/Retention.tsx +++ b/frontend/src/container/GeneralSettings/Retention.tsx @@ -1,108 +1,108 @@ import { DownOutlined } from '@ant-design/icons'; -import { Button, Menu } from 'antd'; -import React from 'react'; +import { Button, Col, Menu, Row, Select } from 'antd'; +import { find } from 'lodash-es'; +import React, { useCallback, useEffect, useState } from 'react'; -import { SettingPeroid } from '.'; +import { Dropdown, Input, RetentionContainer, Typography } from './styles'; import { - Dropdown, - Input, - RetentionContainer, - TextContainer, - Typography, -} from './styles'; + convertHoursValueToRelevantUnit, + ITimeUnit, + SettingPeriod, + TimeUnits, +} from './utils'; + +const { Option } = Select; function Retention({ retentionValue, - setRentionValue, - selectedRetentionPeroid, - setSelectedRetentionPeroid, + setRetentionValue, text, }: RetentionProps): JSX.Element { - const options: Option[] = [ - { - key: 'hr', - value: 'Hrs', - }, - { - key: 'day', - value: 'Days', - }, - { - key: 'month', - value: 'Months', - }, - ]; - - const onClickHandler = ( - e: { key: string }, - func: React.Dispatch>, - ): void => { - const selected = e.key as SettingPeroid; - func(selected); - }; - - const menu = ( - onClickHandler(e, setSelectedRetentionPeroid)}> - {options.map((option) => ( - {option.value} - ))} - + const { + value: initialValue, + timeUnitValue: initialTimeUnitValue, + } = convertHoursValueToRelevantUnit(retentionValue); + const [selectedTimeUnit, setSelectTimeUnit] = useState(initialTimeUnitValue); + const [selectedValue, setSelectedValue] = useState( + initialValue, ); - const currentSelectedOption = (option: SettingPeroid): string | undefined => { - return options.find((e) => e.key === option)?.value; + const menuItems = TimeUnits.map((option) => ( + + )); + + const currentSelectedOption = (option: SettingPeriod): void => { + const selectedValue = find(TimeUnits, (e) => e.value === option)?.value; + if (selectedValue) setSelectTimeUnit(selectedValue); }; + useEffect(() => { + const inverseMultiplier = find( + TimeUnits, + (timeUnit) => timeUnit.value === selectedTimeUnit, + )?.multiplier; + if (selectedValue && inverseMultiplier) { + setRetentionValue(selectedValue * (1 / inverseMultiplier)); + } + }, [selectedTimeUnit, selectedValue, setRetentionValue]); + const onChangeHandler = ( e: React.ChangeEvent, - func: React.Dispatch>, + func: React.Dispatch>, ): void => { const { value } = e.target; const integerValue = parseInt(value, 10); if (value.length > 0 && integerValue.toString() === value) { - const parsedValue = Math.abs(integerValue).toString(); + const parsedValue = Math.abs(integerValue); func(parsedValue); } if (value.length === 0) { - func(''); + func(null); } }; return ( - - {text} - - - onChangeHandler(e, setRentionValue)} - /> - - - - + + + + {text} + + + +
+ = 0 ? selectedValue : ''} + onChange={(e): void => onChangeHandler(e, setSelectedValue)} + style={{ width: 75 }} + /> + +
+ +
); } -interface Option { - key: SettingPeroid; - value: string; -} - interface RetentionProps { - retentionValue: string; + retentionValue: number; text: string; - setRentionValue: React.Dispatch>; - selectedRetentionPeroid: SettingPeroid; - setSelectedRetentionPeroid: React.Dispatch< - React.SetStateAction - >; + setRetentionValue: React.Dispatch>; } export default Retention; diff --git a/frontend/src/container/GeneralSettings/index.tsx b/frontend/src/container/GeneralSettings/index.tsx index e741e81886..7796e38d5f 100644 --- a/frontend/src/container/GeneralSettings/index.tsx +++ b/frontend/src/container/GeneralSettings/index.tsx @@ -1,4 +1,5 @@ -import { Button, Modal, notification, Typography } from 'antd'; +import { Button, Col, Modal, notification, Row, Typography } from 'antd'; +import getDisks from 'api/disks/getDisks'; import getRetentionperoidApi from 'api/settings/getRetention'; import setRetentionApi from 'api/settings/setRetention'; import Spinner from 'components/Spinner'; @@ -12,7 +13,6 @@ import { PayloadProps } from 'types/api/settings/getRetention'; import Retention from './Retention'; import { ButtonContainer, - Container, ErrorText, ErrorTextContainer, ToolTipContainer, @@ -22,16 +22,15 @@ function GeneralSettings(): JSX.Element { const [ selectedMetricsPeroid, setSelectedMetricsPeroid, - ] = useState('month'); + ] = useState('month'); const [notifications, Element] = notification.useNotification(); - const [retentionPeroidMetrics, setRetentionPeroidMetrics] = useState( '', ); const [modal, setModal] = useState(false); const [postApiLoading, setPostApiLoading] = useState(false); - const [selectedTracePeroid, setSelectedTracePeroid] = useState( + const [selectedTracePeroid, setSelectedTracePeroid] = useState( 'hr', ); @@ -39,6 +38,30 @@ function GeneralSettings(): JSX.Element { const [isDefaultMetrics, setIsDefaultMetrics] = useState(false); const [isDefaultTrace, setIsDefaultTrace] = useState(false); + const [availableDisks, setAvailableDisks] = useState(null); + + useEffect(() => { + getDisks().then((resp) => console.log({ disks: resp })) + }, []) + const currentTTLValues = { + metrics_ttl_duration_hrs: 24 * 30 * 10, + metrics_move_ttl_duration_hrs: -1, + traces_ttl_duration_hrs: -1, + traces_move_ttl_duration_hrs: -1, + }; + const [metricsTotalRetentionPeriod, setMetricsTotalRetentionPeriod] = useState< + number | null + >(currentTTLValues.metrics_ttl_duration_hrs); + const [metricsS3RetentionPeriod, setMetricsS3RetentionPeriod] = useState< + number | null + >(currentTTLValues.metrics_move_ttl_duration_hrs); + const [tracesTotalRetentionPeriod, setTracesTotalRetentionPeriod] = useState< + number | null + >(currentTTLValues.traces_ttl_duration_hrs); + const [tracesS3RetentionPeriod, setTracesS3RetentionPeriod] = useState< + number | null + >(currentTTLValues.traces_move_ttl_duration_hrs); + const onModalToggleHandler = (): void => { setModal((modal) => !modal); }; @@ -65,6 +88,39 @@ function GeneralSettings(): JSX.Element { setIsDefaultTrace(false); } }; + // const retentionRenderConfig = () => { }; + const renderConfig = [ + { + name: 'Metrics', + retentionFields: [ + { + name: 'Total Retention Period', + value: metricsTotalRetentionPeriod, + setValue: setMetricsTotalRetentionPeriod, + }, + { + name: `Move to S3\n(should be lower than total retention period)`, + value: metricsS3RetentionPeriod, + setValue: setMetricsS3RetentionPeriod, + }, + ], + }, + { + name: 'Traces', + retentionFields: [ + { + name: 'Total Retention Period', + value: tracesTotalRetentionPeriod, + setValue: setTracesTotalRetentionPeriod, + }, + { + name: `Move to S3\n(should be lower than total retention period)`, + value: tracesS3RetentionPeriod, + setValue: setTracesS3RetentionPeriod, + }, + ], + }, + ]; useEffect(() => { if (!loading && payload !== undefined) { @@ -96,7 +152,7 @@ function GeneralSettings(): JSX.Element { const retentionMetricsValue = retentionPeroidMetrics === '0' && - (payload?.metrics_ttl_duration_hrs || 0) < 0 + (payload?.metrics_ttl_duration_hrs || 0) < 0 ? payload?.metrics_ttl_duration_hrs || 0 : parseInt(retentionPeroidMetrics, 10); @@ -180,9 +236,8 @@ function GeneralSettings(): JSX.Element { const errorText = getErrorText(); return ( - + {Element} - {errorText ? ( {errorText} @@ -204,22 +259,30 @@ function GeneralSettings(): JSX.Element { /> )} + + {renderConfig.map((category): JSX.Element | null => { + if ( + Array.isArray(category.retentionFields) && + category.retentionFields.length > 0 + ) { + return ( + + {category.name} - - - + {category.retentionFields.map((retentionField) => ( + + ))} + + ); + } + return null; + })} + - + ); } -export type SettingPeroid = 'hr' | 'day' | 'month'; +export type SettingPeriod = 'hr' | 'day' | 'month'; export default GeneralSettings; diff --git a/frontend/src/container/GeneralSettings/styles.ts b/frontend/src/container/GeneralSettings/styles.ts index 5233ce7e50..417fdb894e 100644 --- a/frontend/src/container/GeneralSettings/styles.ts +++ b/frontend/src/container/GeneralSettings/styles.ts @@ -1,16 +1,13 @@ import { + Col, Dropdown as DropDownComponent, Input as InputComponent, Typography as TypographyComponent, } from 'antd'; import styled from 'styled-components'; -export const RetentionContainer = styled.div` - width: 50%; - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1rem; +export const RetentionContainer = styled(Col)` + margin: 0.75rem 0; `; export const Input = styled(InputComponent)` @@ -37,13 +34,6 @@ export const ButtonContainer = styled.div` } `; -export const Container = styled.div` - &&& { - display: flex; - flex-direction: column; - } -`; - export const Dropdown = styled(DropDownComponent)` &&& { display: flex; diff --git a/frontend/src/container/GeneralSettings/utils.ts b/frontend/src/container/GeneralSettings/utils.ts new file mode 100644 index 0000000000..caf0502d85 --- /dev/null +++ b/frontend/src/container/GeneralSettings/utils.ts @@ -0,0 +1,41 @@ +export type SettingPeriod = 'hr' | 'day' | 'month'; + +export interface ITimeUnit { + value: SettingPeriod; + key: string; + multiplier: number; +} +export const TimeUnits: ITimeUnit[] = [ + { + value: 'hr', + key: 'Hours', + multiplier: 1, + }, + { + value: 'day', + key: 'Days', + multiplier: 1 / 24, + }, + { + value: 'month', + key: 'Months', + multiplier: 1 / (24 * 30), + }, +]; + +export const convertHoursValueToRelevantUnit = ( + value: number, +): { value: number; timeUnitValue: SettingPeriod } => { + for (let idx = TimeUnits.length - 1; idx >= 0; idx -= 1) { + const timeUnit = TimeUnits[idx]; + const convertedValue = timeUnit.multiplier * value; + + if ( + convertedValue >= 1 && + convertedValue === parseInt(`${convertedValue}`, 10) + ) { + return { value: convertedValue, timeUnitValue: timeUnit.value }; + } + } + return { value, timeUnitValue: TimeUnits[0].value }; +}; diff --git a/frontend/src/lib/convertIntoHr.ts b/frontend/src/lib/convertIntoHr.ts index cd2d059e94..f7e408c2b1 100644 --- a/frontend/src/lib/convertIntoHr.ts +++ b/frontend/src/lib/convertIntoHr.ts @@ -1,6 +1,6 @@ -import { SettingPeroid } from 'container/GeneralSettings'; +import { SettingPeriod } from 'container/GeneralSettings'; -const converIntoHr = (value: number, peroid: SettingPeroid): number => { +const converIntoHr = (value: number, peroid: SettingPeriod): number => { if (peroid === 'day') { return value * 24; } diff --git a/frontend/src/lib/getSettingsPeroid.ts b/frontend/src/lib/getSettingsPeroid.ts index 72da4c3abb..0ea60ca0d5 100644 --- a/frontend/src/lib/getSettingsPeroid.ts +++ b/frontend/src/lib/getSettingsPeroid.ts @@ -1,4 +1,4 @@ -import { SettingPeroid } from 'container/GeneralSettings'; +import { SettingPeriod } from 'container/GeneralSettings'; const getSettingsPeroid = (hr: number): PayloadProps => { if (hr <= 0) { @@ -30,7 +30,7 @@ const getSettingsPeroid = (hr: number): PayloadProps => { interface PayloadProps { value: number; - peroid: SettingPeroid; + peroid: SettingPeriod; } export default getSettingsPeroid; diff --git a/frontend/src/pages/AlertChannelCreate/index.tsx b/frontend/src/pages/AlertChannelCreate/index.tsx index 59b2361cf8..1f316fe860 100644 --- a/frontend/src/pages/AlertChannelCreate/index.tsx +++ b/frontend/src/pages/AlertChannelCreate/index.tsx @@ -15,7 +15,7 @@ function SettingsPage(): JSX.Element { routes: [ { Component: GeneralSettings, - name: 'General Settings', + name: 'General', route: ROUTES.SETTINGS, }, { @@ -27,7 +27,7 @@ function SettingsPage(): JSX.Element { }, ], activeKey: - pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels', + pathName === ROUTES.SETTINGS ? 'General' : 'Alert Channels', }} /> ); diff --git a/frontend/src/pages/AllAlertChannels/index.tsx b/frontend/src/pages/AllAlertChannels/index.tsx index 8e64751064..b1d3820f66 100644 --- a/frontend/src/pages/AllAlertChannels/index.tsx +++ b/frontend/src/pages/AllAlertChannels/index.tsx @@ -14,7 +14,7 @@ function AllAlertChannels(): JSX.Element { routes: [ { Component: GeneralSettings, - name: 'General Settings', + name: 'General', route: ROUTES.SETTINGS, }, { @@ -23,8 +23,7 @@ function AllAlertChannels(): JSX.Element { route: ROUTES.ALL_CHANNELS, }, ], - activeKey: - pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels', + activeKey: pathName === ROUTES.SETTINGS ? 'General' : 'Alert Channels', }} /> ); diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index e661fb6193..2b23cec2ec 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -14,7 +14,7 @@ function SettingsPage(): JSX.Element { routes: [ { Component: GeneralSettings, - name: 'General Settings', + name: 'General', route: ROUTES.SETTINGS, }, { @@ -24,7 +24,7 @@ function SettingsPage(): JSX.Element { }, ], activeKey: - pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General Settings', + pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General', }} /> ); diff --git a/frontend/src/types/api/disks/getDisks.ts b/frontend/src/types/api/disks/getDisks.ts new file mode 100644 index 0000000000..fc917db7d9 --- /dev/null +++ b/frontend/src/types/api/disks/getDisks.ts @@ -0,0 +1,4 @@ +export interface IDiskType { + name: string; + type: string; +} From 1b28a4e6f504a7f780bcd67508bf2e5d23e85a10 Mon Sep 17 00:00:00 2001 From: palash-signoz Date: Fri, 1 Apr 2022 15:43:58 +0530 Subject: [PATCH 08/46] chore: links are updated for all dashboard and promql (#908) --- frontend/src/container/ListOfDashboard/index.tsx | 2 +- .../container/NewWidget/LeftContainer/QuerySection/Query.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/ListOfDashboard/index.tsx b/frontend/src/container/ListOfDashboard/index.tsx index 8828d748f2..492da5c846 100644 --- a/frontend/src/container/ListOfDashboard/index.tsx +++ b/frontend/src/container/ListOfDashboard/index.tsx @@ -157,7 +157,7 @@ function ListOfAllDashboard(): JSX.Element { diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/Query.tsx b/frontend/src/container/NewWidget/LeftContainer/QuerySection/Query.tsx index 97aa24d180..916771eece 100644 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/Query.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/QuerySection/Query.tsx @@ -114,7 +114,7 @@ function Query({ From d085506d3e16f71f46de37d8289c1fd5b762495a Mon Sep 17 00:00:00 2001 From: palash-signoz Date: Fri, 1 Apr 2022 15:47:39 +0530 Subject: [PATCH 09/46] bug: logged in check is added in the useEffect (#921) --- frontend/src/container/AppLayout/index.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 84f7d237e2..5a9051c215 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -27,6 +27,12 @@ function AppLayout(props: AppLayoutProps): JSX.Element { } }, [isLoggedIn, isSignUpPage]); + useEffect(() => { + if (isLoggedIn) { + history.push(ROUTES.APPLICATION); + } + }, [isLoggedIn]); + return ( {!isSignUpPage && } From 4dc668fd133dab6496047dd719cc30f79bd92ffb Mon Sep 17 00:00:00 2001 From: Pranshu Chittora Date: Fri, 1 Apr 2022 17:43:56 +0530 Subject: [PATCH 10/46] fix: remove unused props --- .../src/container/GridGraphLayout/Graph/FullView/index.tsx | 3 --- .../src/container/MetricsApplication/Tabs/Application.tsx | 2 -- frontend/src/container/MetricsApplication/Tabs/DBCall.tsx | 2 -- frontend/src/container/MetricsApplication/Tabs/External.tsx | 4 ---- 4 files changed, 11 deletions(-) diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx b/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx index fcc656318c..ba8a66a82d 100644 --- a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx +++ b/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx @@ -29,7 +29,6 @@ function FullView({ widget, fullViewOptions = true, onClickHandler, - noDataGraph = false, name, yAxisUnit, }: FullViewProps): JSX.Element { @@ -210,7 +209,6 @@ interface FullViewProps { widget: Widgets; fullViewOptions?: boolean; onClickHandler?: GraphOnClickHandler; - noDataGraph?: boolean; name: string; yAxisUnit?: string; } @@ -218,7 +216,6 @@ interface FullViewProps { FullView.defaultProps = { fullViewOptions: undefined, onClickHandler: undefined, - noDataGraph: undefined, yAxisUnit: undefined, }; diff --git a/frontend/src/container/MetricsApplication/Tabs/Application.tsx b/frontend/src/container/MetricsApplication/Tabs/Application.tsx index 774d8d7118..8cf4c2c3dd 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Application.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Application.tsx @@ -179,7 +179,6 @@ function Application({ getWidget }: DashboardProps): JSX.Element { { onClickhandler(event, element, chart, data, 'Request'); @@ -214,7 +213,6 @@ function Application({ getWidget }: DashboardProps): JSX.Element { { onClickhandler(ChartEvent, activeElements, chart, data, 'Error'); diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx index 5b918338e4..20206c21d7 100644 --- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx @@ -17,7 +17,6 @@ function DBCall({ getWidget }: DBCallProps): JSX.Element { External Call duration(by Address) Date: Fri, 1 Apr 2022 17:59:44 +0530 Subject: [PATCH 11/46] bug: no service and loading check are added (#934) --- .../src/modules/Servicemap/ServiceMap.tsx | 28 +++++------ frontend/src/store/actions/serviceMap.ts | 46 ++++++++++--------- frontend/src/store/actions/types.ts | 14 ++++-- frontend/src/store/reducers/serviceMap.ts | 7 +++ 4 files changed, 57 insertions(+), 38 deletions(-) diff --git a/frontend/src/modules/Servicemap/ServiceMap.tsx b/frontend/src/modules/Servicemap/ServiceMap.tsx index 9d07892fa5..7408fe206d 100644 --- a/frontend/src/modules/Servicemap/ServiceMap.tsx +++ b/frontend/src/modules/Servicemap/ServiceMap.tsx @@ -1,12 +1,13 @@ /* eslint-disable */ //@ts-nocheck +import { Card } from 'antd'; import Spinner from 'components/Spinner'; import React, { useEffect, useRef } from 'react'; import { ForceGraph2D } from 'react-force-graph'; import { connect } from 'react-redux'; import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { getDetailedServiceMapItems, getServiceMapItems } from 'store/actions'; +import { getDetailedServiceMapItems, ServiceMapStore } from 'store/actions'; import { AppState } from 'store/reducers'; import styled from 'styled-components'; import { GlobalTime } from 'types/actions/globalTime'; @@ -31,9 +32,8 @@ const Container = styled.div` `; interface ServiceMapProps extends RouteComponentProps { - serviceMap: serviceMapStore; + serviceMap: ServiceMapStore; globalTime: GlobalTime; - getServiceMapItems: (time: GlobalTime) => void; getDetailedServiceMapItems: (time: GlobalTime) => void; } interface graphNode { @@ -53,29 +53,32 @@ export interface graphDataType { function ServiceMap(props: ServiceMapProps): JSX.Element { const fgRef = useRef(); - const { - getDetailedServiceMapItems, - getServiceMapItems, - globalTime, - serviceMap, - } = props; + const { getDetailedServiceMapItems, globalTime, serviceMap } = props; useEffect(() => { /* Call the apis only when the route is loaded. Check this issue: https://github.com/SigNoz/signoz/issues/110 */ - getServiceMapItems(globalTime); getDetailedServiceMapItems(globalTime); - }, [globalTime, getServiceMapItems, getDetailedServiceMapItems]); + }, [globalTime, getDetailedServiceMapItems]); useEffect(() => { fgRef.current && fgRef.current.d3Force('charge').strength(-400); }); - if (!serviceMap.items.length || !serviceMap.services.length) { + + if (serviceMap.loading) { return ; } + if (!serviceMap.loading && serviceMap.items.length === 0) { + return ( + + No Service Found + + ); + } + const zoomToService = (value: string): void => { fgRef && fgRef.current && @@ -149,7 +152,6 @@ const mapStateToProps = ( export default withRouter( connect(mapStateToProps, { - getServiceMapItems, getDetailedServiceMapItems, })(ServiceMap), ); diff --git a/frontend/src/store/actions/serviceMap.ts b/frontend/src/store/actions/serviceMap.ts index be5a84a239..a6f079a7eb 100644 --- a/frontend/src/store/actions/serviceMap.ts +++ b/frontend/src/store/actions/serviceMap.ts @@ -7,6 +7,7 @@ import { ActionTypes } from './types'; export interface ServiceMapStore { items: ServicesMapItem[]; services: ServicesItem[]; + loading: boolean; } export interface ServicesItem { @@ -37,38 +38,39 @@ export interface ServicesAction { payload: ServicesItem[]; } -export const getServiceMapItems = (globalTime: GlobalTime) => { - return async (dispatch: Dispatch): Promise => { - dispatch({ - type: ActionTypes.getServiceMapItems, - payload: [], - }); - - const requestString = `/serviceMapDependencies?start=${globalTime.minTime}&end=${globalTime.maxTime}`; - - const response = await api.get(requestString); - - dispatch({ - type: ActionTypes.getServiceMapItems, - payload: response.data, - }); +export interface ServiceMapLoading { + type: ActionTypes.serviceMapLoading; + payload: { + loading: ServiceMapStore['loading']; }; -}; +} export const getDetailedServiceMapItems = (globalTime: GlobalTime) => { return async (dispatch: Dispatch): Promise => { - dispatch({ - type: ActionTypes.getServices, - payload: [], - }); - const requestString = `/services?start=${globalTime.minTime}&end=${globalTime.maxTime}`; - const response = await api.get(requestString); + const serviceMapDependencies = `/serviceMapDependencies?start=${globalTime.minTime}&end=${globalTime.maxTime}`; + + const [serviceMapDependenciesResponse, response] = await Promise.all([ + api.get(serviceMapDependencies), + api.get(requestString), + ]); dispatch({ type: ActionTypes.getServices, payload: response.data, }); + + dispatch({ + type: ActionTypes.getServiceMapItems, + payload: serviceMapDependenciesResponse.data, + }); + + dispatch({ + type: ActionTypes.serviceMapLoading, + payload: { + loading: false, + }, + }); }; }; diff --git a/frontend/src/store/actions/types.ts b/frontend/src/store/actions/types.ts index c15ea00286..702997d49b 100644 --- a/frontend/src/store/actions/types.ts +++ b/frontend/src/store/actions/types.ts @@ -1,14 +1,22 @@ -import { ServiceMapItemAction, ServicesAction } from './serviceMap'; +import { + ServiceMapItemAction, + ServiceMapLoading, + ServicesAction, +} from './serviceMap'; import { GetUsageDataAction } from './usage'; export enum ActionTypes { - updateTraceFilters = 'UPDATE_TRACES_FILTER', updateTimeInterval = 'UPDATE_TIME_INTERVAL', getServiceMapItems = 'GET_SERVICE_MAP_ITEMS', getServices = 'GET_SERVICES', getUsageData = 'GET_USAGE_DATE', fetchTraces = 'FETCH_TRACES', fetchTraceItem = 'FETCH_TRACE_ITEM', + serviceMapLoading = 'UPDATE_SERVICE_MAP_LOADING', } -export type Action = GetUsageDataAction | ServicesAction | ServiceMapItemAction; +export type Action = + | GetUsageDataAction + | ServicesAction + | ServiceMapItemAction + | ServiceMapLoading; diff --git a/frontend/src/store/reducers/serviceMap.ts b/frontend/src/store/reducers/serviceMap.ts index ef7cd21496..18ec21a9ec 100644 --- a/frontend/src/store/reducers/serviceMap.ts +++ b/frontend/src/store/reducers/serviceMap.ts @@ -3,6 +3,7 @@ import { Action, ActionTypes, ServiceMapStore } from 'store/actions'; const initialState: ServiceMapStore = { items: [], services: [], + loading: true, }; export const ServiceMapReducer = ( @@ -20,6 +21,12 @@ export const ServiceMapReducer = ( ...state, services: action.payload, }; + case ActionTypes.serviceMapLoading: { + return { + ...state, + loading: action.payload.loading, + }; + } default: return state; } From 53e7037f48ec4d8abf8ac33a1b15c5996d7bb5bd Mon Sep 17 00:00:00 2001 From: Naman Jain Date: Sat, 2 Apr 2022 16:15:03 +0530 Subject: [PATCH 12/46] fix: run go vet to fix some issues with json tag (#936) Co-authored-by: Naman Jain --- pkg/query-service/godruid/queries.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/query-service/godruid/queries.go b/pkg/query-service/godruid/queries.go index 5fb518f908..ffdeee002d 100644 --- a/pkg/query-service/godruid/queries.go +++ b/pkg/query-service/godruid/queries.go @@ -62,7 +62,7 @@ type QueryScan struct { Limit int64 `json:"limit,omitempty"` Offset int64 `json:"offset,omitempty"` BatchSize int64 `json:"batchSize,omitempty"` - Order string `json:"order",omitempty` + Order string `json:"order,omitempty"` ResultFormat string `json:"resultFormat"` Context map[string]interface{} `json:"context,omitempty"` @@ -189,7 +189,7 @@ type TimeBoundaryItem struct { type TimeBoundary struct { MinTime string `json:"minTime"` - MaxTime string `json:"minTime"` + MaxTime string `json:"maxTime"` } func (q *QueryTimeBoundary) setup() { q.QueryType = "timeBoundary" } From 32e8e489280cb7832ef7532d87febc8bc2555497 Mon Sep 17 00:00:00 2001 From: Palash gupta Date: Mon, 4 Apr 2022 08:24:28 +0530 Subject: [PATCH 13/46] chore: behaviour for dropdown is updated --- .../AllTags/Tag/DebounceSelect/index.tsx | 63 ----------------- .../Trace/Search/AllTags/Tag/TagValue.tsx | 69 +++++++++++++++++++ .../Trace/Search/AllTags/Tag/config.ts | 28 -------- .../Trace/Search/AllTags/Tag/index.tsx | 38 ++++------ 4 files changed, 81 insertions(+), 117 deletions(-) delete mode 100644 frontend/src/container/Trace/Search/AllTags/Tag/DebounceSelect/index.tsx create mode 100644 frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx delete mode 100644 frontend/src/container/Trace/Search/AllTags/Tag/config.ts diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/DebounceSelect/index.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/DebounceSelect/index.tsx deleted file mode 100644 index 2576e782e0..0000000000 --- a/frontend/src/container/Trace/Search/AllTags/Tag/DebounceSelect/index.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { Select, Spin } from 'antd'; -import { SelectProps } from 'antd/es/select'; -import debounce from 'lodash-es/debounce'; -import React, { useRef, useState } from 'react'; - -export interface DebounceSelectProps - extends Omit, 'options' | 'children'> { - fetchOptions: (search: string) => Promise; - debounceTimeout: number; -} - -function DebounceSelect< - ValueType extends { - key?: string; - label: React.ReactNode; - value: string | number; - } = never ->({ - fetchOptions, - debounceTimeout = 800, - ...props -}: DebounceSelectProps): JSX.Element { - const [fetching, setFetching] = useState(false); - const [options, setOptions] = useState([]); - const fetchRef = useRef(0); - - const debounceFetcher = React.useMemo(() => { - const loadOptions = (value: string): void => { - fetchRef.current += 1; - const fetchId = fetchRef.current; - setOptions([]); - setFetching(true); - - fetchOptions(value).then((newOptions) => { - if (fetchId !== fetchRef.current) { - // for fetch callback order - return; - } - - setOptions(newOptions); - setFetching(false); - }); - }; - - return debounce(loadOptions, debounceTimeout); - }, [fetchOptions, debounceTimeout]); - - return ( - - labelInValue - filterOption={false} - onSearch={debounceFetcher} - notFoundContent={fetching ? : null} - style={{ width: '170px' }} - // as all other props are from SelectProps only - // eslint-disable-next-line react/jsx-props-no-spreading - {...props} - options={options} - /> - ); -} - -export default DebounceSelect; diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx new file mode 100644 index 0000000000..378001bec4 --- /dev/null +++ b/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx @@ -0,0 +1,69 @@ +import { Select } from 'antd'; +import { DefaultOptionType } from 'antd/lib/select'; +import getTagValue from 'api/trace/getTagValue'; +import useFetch from 'hooks/useFetch'; +import React from 'react'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { PayloadProps, Props } from 'types/api/trace/getTagValue'; +import { GlobalReducer } from 'types/reducer/globalTime'; +import { TraceReducer } from 'types/reducer/trace'; + +import { Value } from '.'; +import { SelectComponent } from './styles'; + +function TagValue(props: TagValueProps): JSX.Element { + const { tag, setLocalSelectedTags, index, tagKey } = props; + const { + Key: selectedKey, + Operator: selectedOperator, + Values: selectedValues, + } = tag; + + const globalReducer = useSelector( + (state) => state.globalTime, + ); + + const valueSuggestion = useFetch(getTagValue, { + end: globalReducer.maxTime, + start: globalReducer.minTime, + tagKey, + }); + + return ( + { + if (typeof value === 'string') { + setLocalSelectedTags((tags) => [ + ...tags.slice(0, index), + { + Key: selectedKey, + Operator: selectedOperator, + Values: [...selectedValues, value], + }, + ...tags.slice(index + 1, tags.length), + ]); + } + }} + loading={valueSuggestion.loading || false} + > + {valueSuggestion.payload && + valueSuggestion.payload.map((suggestion) => ( + + {suggestion.tagValues} + + ))} + + ); +} + +interface TagValueProps { + index: number; + tag: FlatArray; + setLocalSelectedTags: React.Dispatch< + React.SetStateAction + >; + tagKey: string; +} + +export default TagValue; diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/config.ts b/frontend/src/container/Trace/Search/AllTags/Tag/config.ts deleted file mode 100644 index ce414a5f15..0000000000 --- a/frontend/src/container/Trace/Search/AllTags/Tag/config.ts +++ /dev/null @@ -1,28 +0,0 @@ -import getTagValue from 'api/trace/getTagValue'; - -// Usage of DebounceSelect -export interface TagValue { - label: string; - value: string; -} - -export async function fetchTag( - globalStart: number, - globalEnd: number, - tagKey: string, -): Promise { - const response = await getTagValue({ - end: globalEnd, - start: globalStart, - tagKey, - }); - - if (response.statusCode !== 200 || !response.payload) { - return []; - } - - return response.payload.map((e) => ({ - label: e.tagValues, - value: e.tagValues, - })); -} diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx index 37b42347a1..ad9b0e7972 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx +++ b/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx @@ -3,13 +3,11 @@ import { Select } from 'antd'; import React from 'react'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; -import { GlobalReducer } from 'types/reducer/globalTime'; import { TraceReducer } from 'types/reducer/trace'; -import { fetchTag, TagValue } from './config'; -import DebounceSelect from './DebounceSelect'; import { Container, IconContainer, SelectComponent } from './styles'; import TagsKey from './TagKey'; +import TagValue from './TagValue'; const { Option } = Select; @@ -33,9 +31,6 @@ const AllMenu: AllMenuProps[] = [ function SingleTags(props: AllTagsProps): JSX.Element { const traces = useSelector((state) => state.traces); - const globalReducer = useSelector( - (state) => state.globalTime, - ); const { tag, onCloseHandler, setLocalSelectedTags, index } = props; const { @@ -69,7 +64,6 @@ function SingleTags(props: AllTagsProps): JSX.Element { tag={tag} setLocalSelectedTags={setLocalSelectedTags} /> - e.key === selectedOperator)?.value || ''} @@ -81,24 +75,16 @@ function SingleTags(props: AllTagsProps): JSX.Element { ))} - => - fetchTag(globalReducer.minTime, globalReducer.maxTime, selectedKey[index]) - } - debounceTimeout={300} - onSelect={(value: Value): void => { - setLocalSelectedTags((tags) => [ - ...tags.slice(0, index), - { - Key: selectedKey, - Operator: selectedOperator, - Values: [...selectedValues, value.value], - }, - ...tags.slice(index + 1, tags.length), - ]); - }} - mode="multiple" - /> + {selectedKey[0] ? ( + + ) : ( + + )} onDeleteTagHandler(index)}> @@ -116,7 +102,7 @@ interface AllTagsProps { >; } -interface Value { +export interface Value { key: string; label: string; value: string; From 6c4c814b3ff79e137803f629d81a5e560265e48d Mon Sep 17 00:00:00 2001 From: palash-signoz Date: Mon, 4 Apr 2022 10:25:15 +0530 Subject: [PATCH 14/46] bug: pathname check is added (#948) --- frontend/src/container/AppLayout/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 5a9051c215..5230ca5bae 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -28,10 +28,10 @@ function AppLayout(props: AppLayoutProps): JSX.Element { }, [isLoggedIn, isSignUpPage]); useEffect(() => { - if (isLoggedIn) { + if (isLoggedIn && pathname === ROUTES.SIGN_UP) { history.push(ROUTES.APPLICATION); } - }, [isLoggedIn]); + }, [isLoggedIn, pathname]); return ( From 8064ae1f37679b521aad3d84c33c7e6005605df5 Mon Sep 17 00:00:00 2001 From: Pranshu Chittora Date: Mon, 4 Apr 2022 15:06:06 +0530 Subject: [PATCH 15/46] feat: ttl api integration --- frontend/src/api/settings/setRetention.ts | 6 +- .../container/GeneralSettings/Retention.tsx | 6 +- .../src/container/GeneralSettings/index.tsx | 198 ++++++++---------- .../src/types/api/settings/getRetention.ts | 2 + .../src/types/api/settings/setRetention.ts | 6 +- 5 files changed, 98 insertions(+), 120 deletions(-) diff --git a/frontend/src/api/settings/setRetention.ts b/frontend/src/api/settings/setRetention.ts index dcf0a0f2c3..da8fdf515b 100644 --- a/frontend/src/api/settings/setRetention.ts +++ b/frontend/src/api/settings/setRetention.ts @@ -8,9 +8,9 @@ const setRetention = async ( props: Props, ): Promise | ErrorResponse> => { try { - const response = await axios.post( - `/settings/ttl?duration=${props.duration}&type=${props.type}`, - ); + const response = await axios.post(`/settings/ttl`, { + props, + }); return { statusCode: 200, diff --git a/frontend/src/container/GeneralSettings/Retention.tsx b/frontend/src/container/GeneralSettings/Retention.tsx index 64591b9b59..653597c3f7 100644 --- a/frontend/src/container/GeneralSettings/Retention.tsx +++ b/frontend/src/container/GeneralSettings/Retention.tsx @@ -17,6 +17,7 @@ function Retention({ retentionValue, setRetentionValue, text, + hide, }: RetentionProps): JSX.Element { const { value: initialValue, @@ -64,7 +65,9 @@ function Retention({ func(null); } }; - + if (hide) { + return null; + } return ( @@ -103,6 +106,7 @@ interface RetentionProps { retentionValue: number; text: string; setRetentionValue: React.Dispatch>; + hide: boolean; } export default Retention; diff --git a/frontend/src/container/GeneralSettings/index.tsx b/frontend/src/container/GeneralSettings/index.tsx index 7796e38d5f..db2c4f3ec8 100644 --- a/frontend/src/container/GeneralSettings/index.tsx +++ b/frontend/src/container/GeneralSettings/index.tsx @@ -1,13 +1,15 @@ import { Button, Col, Modal, notification, Row, Typography } from 'antd'; import getDisks from 'api/disks/getDisks'; -import getRetentionperoidApi from 'api/settings/getRetention'; +import getRetentionPeriodApi from 'api/settings/getRetention'; import setRetentionApi from 'api/settings/setRetention'; import Spinner from 'components/Spinner'; import TextToolTip from 'components/TextToolTip'; import useFetch from 'hooks/useFetch'; import convertIntoHr from 'lib/convertIntoHr'; import getSettingsPeroid from 'lib/getSettingsPeroid'; -import React, { useCallback, useEffect, useState } from 'react'; +import { find } from 'lodash-es'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { IDiskType } from 'types/api/disks/getDisks'; import { PayloadProps } from 'types/api/settings/getRetention'; import Retention from './Retention'; @@ -24,43 +26,36 @@ function GeneralSettings(): JSX.Element { setSelectedMetricsPeroid, ] = useState('month'); const [notifications, Element] = notification.useNotification(); - const [retentionPeroidMetrics, setRetentionPeroidMetrics] = useState( - '', - ); + const [modal, setModal] = useState(false); const [postApiLoading, setPostApiLoading] = useState(false); - const [selectedTracePeroid, setSelectedTracePeroid] = useState( - 'hr', - ); - - const [retentionPeroidTrace, setRetentionPeroidTrace] = useState(''); const [isDefaultMetrics, setIsDefaultMetrics] = useState(false); const [isDefaultTrace, setIsDefaultTrace] = useState(false); - const [availableDisks, setAvailableDisks] = useState(null); + const [availableDisks, setAvailableDisks] = useState(null); useEffect(() => { - getDisks().then((resp) => console.log({ disks: resp })) - }, []) - const currentTTLValues = { - metrics_ttl_duration_hrs: 24 * 30 * 10, - metrics_move_ttl_duration_hrs: -1, - traces_ttl_duration_hrs: -1, - traces_move_ttl_duration_hrs: -1, - }; + getDisks().then((response) => setAvailableDisks(response)); + }, []); + + const { payload: currentTTLValues, loading, error, errorMessage } = useFetch< + PayloadProps, + undefined + >(getRetentionPeriodApi, undefined); + const [metricsTotalRetentionPeriod, setMetricsTotalRetentionPeriod] = useState< number | null - >(currentTTLValues.metrics_ttl_duration_hrs); + >(currentTTLValues?.metrics_ttl_duration_hrs); const [metricsS3RetentionPeriod, setMetricsS3RetentionPeriod] = useState< number | null - >(currentTTLValues.metrics_move_ttl_duration_hrs); + >(currentTTLValues?.metrics_move_ttl_duration_hrs); const [tracesTotalRetentionPeriod, setTracesTotalRetentionPeriod] = useState< number | null - >(currentTTLValues.traces_ttl_duration_hrs); + >(currentTTLValues?.traces_ttl_duration_hrs); const [tracesS3RetentionPeriod, setTracesS3RetentionPeriod] = useState< number | null - >(currentTTLValues.traces_move_ttl_duration_hrs); + >(currentTTLValues?.traces_move_ttl_duration_hrs); const onModalToggleHandler = (): void => { setModal((modal) => !modal); @@ -70,11 +65,6 @@ function GeneralSettings(): JSX.Element { onModalToggleHandler(); }, []); - const { payload, loading, error, errorMessage } = useFetch< - PayloadProps, - undefined - >(getRetentionperoidApi, undefined); - const checkMetricTraceDefault = (trace: number, metric: number): void => { if (metric === -1) { setIsDefaultMetrics(true); @@ -89,99 +79,78 @@ function GeneralSettings(): JSX.Element { } }; // const retentionRenderConfig = () => { }; - const renderConfig = [ - { - name: 'Metrics', - retentionFields: [ - { - name: 'Total Retention Period', - value: metricsTotalRetentionPeriod, - setValue: setMetricsTotalRetentionPeriod, - }, - { - name: `Move to S3\n(should be lower than total retention period)`, - value: metricsS3RetentionPeriod, - setValue: setMetricsS3RetentionPeriod, - }, - ], - }, - { - name: 'Traces', - retentionFields: [ - { - name: 'Total Retention Period', - value: tracesTotalRetentionPeriod, - setValue: setTracesTotalRetentionPeriod, - }, - { - name: `Move to S3\n(should be lower than total retention period)`, - value: tracesS3RetentionPeriod, - setValue: setTracesS3RetentionPeriod, - }, - ], - }, - ]; - - useEffect(() => { - if (!loading && payload !== undefined) { - const { - metrics_ttl_duration_hrs: metricTllDuration, - traces_ttl_duration_hrs: traceTllDuration, - } = payload; - - checkMetricTraceDefault(traceTllDuration, metricTllDuration); - - const traceValue = getSettingsPeroid(traceTllDuration); - const metricsValue = getSettingsPeroid(metricTllDuration); - - setRetentionPeroidTrace(traceValue.value.toString()); - setSelectedTracePeroid(traceValue.peroid); - - setRetentionPeroidMetrics(metricsValue.value.toString()); - setSelectedMetricsPeroid(metricsValue.peroid); - } - }, [setSelectedMetricsPeroid, loading, payload]); + const renderConfig = useMemo(() => { + const s3Enabled = !!find( + availableDisks, + (disks: IDiskType) => disks?.type === 's3', + ); + return [ + { + name: 'Metrics', + retentionFields: [ + { + name: 'Total Retention Period', + value: metricsTotalRetentionPeriod, + setValue: setMetricsTotalRetentionPeriod, + }, + { + name: `Move to S3\n(should be lower than total retention period)`, + value: metricsS3RetentionPeriod, + setValue: setMetricsS3RetentionPeriod, + hide: !s3Enabled, + }, + ], + }, + { + name: 'Traces', + retentionFields: [ + { + name: 'Total Retention Period', + value: tracesTotalRetentionPeriod, + setValue: setTracesTotalRetentionPeriod, + }, + { + name: `Move to S3\n(should be lower than total retention period)`, + value: tracesS3RetentionPeriod, + setValue: setTracesS3RetentionPeriod, + hide: !s3Enabled, + }, + ], + }, + ]; + }, [ + availableDisks, + metricsS3RetentionPeriod, + metricsTotalRetentionPeriod, + tracesS3RetentionPeriod, + tracesTotalRetentionPeriod, + ]); const onOkHandler = async (): Promise => { try { setPostApiLoading(true); - const retentionTraceValue = - retentionPeroidTrace === '0' && (payload?.traces_ttl_duration_hrs || 0) < 0 - ? payload?.traces_ttl_duration_hrs || 0 - : parseInt(retentionPeroidTrace, 10); - - const retentionMetricsValue = - retentionPeroidMetrics === '0' && - (payload?.metrics_ttl_duration_hrs || 0) < 0 - ? payload?.metrics_ttl_duration_hrs || 0 - : parseInt(retentionPeroidMetrics, 10); - - const [tracesResponse, metricsResponse] = await Promise.all([ - setRetentionApi({ - duration: `${convertIntoHr(retentionTraceValue, selectedTracePeroid)}h`, - type: 'traces', - }), - setRetentionApi({ - duration: `${convertIntoHr( - retentionMetricsValue, - selectedMetricsPeroid, - )}h`, - type: 'metrics', - }), - ]); - - if ( - tracesResponse.statusCode === 200 && - metricsResponse.statusCode === 200 - ) { + // const retentionTraceValue = + // retentionPeroidTrace === '0' && (payload?.traces_ttl_duration_hrs || 0) < 0 + // ? payload?.traces_ttl_duration_hrs || 0 + // : parseInt(retentionPeroidTrace, 10); + // const retentionMetricsValue = + // retentionPeroidMetrics === '0' && + // (payload?.metrics_ttl_duration_hrs || 0) < 0 + // ? payload?.metrics_ttl_duration_hrs || 0 + // : parseInt(retentionPeroidMetrics, 10); + const apiResponse = await setRetentionApi({ + metrics_ttl_duration_hrs: metricsTotalRetentionPeriod || -1, + metrics_move_ttl_duration_hrs: metricsS3RetentionPeriod || -1, + traces_move_ttl_duration_hrs: tracesS3RetentionPeriod || -1, + traces_ttl_duration_hrs: tracesTotalRetentionPeriod || -1, + }); + if (apiResponse.statusCode === 200) { notifications.success({ message: 'Success!', placement: 'topRight', description: 'Congrats. The retention periods were updated correctly.', }); - - checkMetricTraceDefault(retentionTraceValue, retentionMetricsValue); - + // checkMetricTraceDefault(retentionTraceValue, retentionMetricsValue); onModalToggleHandler(); } else { notifications.error({ @@ -206,7 +175,7 @@ function GeneralSettings(): JSX.Element { return {errorMessage}; } - if (loading || payload === undefined) { + if (loading || currentTTLValues === undefined) { return ; } @@ -230,7 +199,7 @@ function GeneralSettings(): JSX.Element { }; const isDisabledHandler = (): boolean => { - return !!(retentionPeroidTrace === '' || retentionPeroidMetrics === ''); + return false; }; const errorText = getErrorText(); @@ -275,6 +244,7 @@ function GeneralSettings(): JSX.Element { text={retentionField.name} retentionValue={retentionField.value} setRetentionValue={retentionField.setValue} + hide={!!retentionField.hide} /> ))} diff --git a/frontend/src/types/api/settings/getRetention.ts b/frontend/src/types/api/settings/getRetention.ts index e6fd1eae43..f3ba539416 100644 --- a/frontend/src/types/api/settings/getRetention.ts +++ b/frontend/src/types/api/settings/getRetention.ts @@ -1,4 +1,6 @@ export interface PayloadProps { metrics_ttl_duration_hrs: number; + metrics_move_ttl_duration_hrs: number; traces_ttl_duration_hrs: number; + traces_move_ttl_duration_hrs: number; } diff --git a/frontend/src/types/api/settings/setRetention.ts b/frontend/src/types/api/settings/setRetention.ts index 6e82f8ef2a..9c2ced48f8 100644 --- a/frontend/src/types/api/settings/setRetention.ts +++ b/frontend/src/types/api/settings/setRetention.ts @@ -1,6 +1,8 @@ export interface Props { - type: 'metrics' | 'traces'; - duration: string; + metrics_ttl_duration_hrs: number; + metrics_move_ttl_duration_hrs: number; + traces_ttl_duration_hrs: number; + traces_move_ttl_duration_hrs: number; } export interface PayloadProps { From 5be1eb58b22962aadbb303349e918696470c002d Mon Sep 17 00:00:00 2001 From: Pranshu Chittora Date: Mon, 4 Apr 2022 15:26:29 +0530 Subject: [PATCH 16/46] feat: ttl api integration --- frontend/src/api/settings/setRetention.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/api/settings/setRetention.ts b/frontend/src/api/settings/setRetention.ts index da8fdf515b..6cb8c436a2 100644 --- a/frontend/src/api/settings/setRetention.ts +++ b/frontend/src/api/settings/setRetention.ts @@ -8,9 +8,7 @@ const setRetention = async ( props: Props, ): Promise | ErrorResponse> => { try { - const response = await axios.post(`/settings/ttl`, { - props, - }); + const response = await axios.post(`/settings/ttl`, props); return { statusCode: 200, From fd83cea9a032e17b9a02c790b30bdfb51a927b8e Mon Sep 17 00:00:00 2001 From: Ankit Nayan Date: Mon, 4 Apr 2022 17:07:21 +0530 Subject: [PATCH 17/46] chore: removing version api from being tracked (#950) --- pkg/query-service/telemetry/ignoredPaths.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/query-service/telemetry/ignoredPaths.go b/pkg/query-service/telemetry/ignoredPaths.go index 3fc2f1898a..e6adf197ff 100644 --- a/pkg/query-service/telemetry/ignoredPaths.go +++ b/pkg/query-service/telemetry/ignoredPaths.go @@ -2,7 +2,8 @@ package telemetry func IgnoredPaths() map[string]struct{} { ignoredPaths := map[string]struct{}{ - "/api/v1/tags": struct{}{}, + "/api/v1/tags": struct{}{}, + "/api/v1/version": struct{}{}, } return ignoredPaths From a0efa63185900baa7c3a60cbdb13be20e2924ee3 Mon Sep 17 00:00:00 2001 From: Nishidh Jain <61869195+NishidhJain@users.noreply.github.com> Date: Mon, 4 Apr 2022 17:35:44 +0530 Subject: [PATCH 18/46] Fix(FE) : Ask for confirmation before deleting any dashboard from dashboard list (#534) * A confirmation dialog will pop up before deleting any dashboard Co-authored-by: Palash gupta --- .../TableComponents/DeleteButton.tsx | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx b/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx index f0b2d183df..fd9ef16001 100644 --- a/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx +++ b/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx @@ -1,4 +1,6 @@ -import React, { useCallback } from 'react'; +import { ExclamationCircleOutlined } from '@ant-design/icons'; +import { Modal } from 'antd'; +import React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators, Dispatch } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; @@ -8,14 +10,29 @@ import AppActions from 'types/actions'; import { Data } from '../index'; import { TableLinkText } from './styles'; -function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element { - const onClickHandler = useCallback(() => { - deleteDashboard({ - uuid: id, - }); - }, [id, deleteDashboard]); +const { confirm } = Modal; - return Delete; +function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element { + const openConfirmationDialog = (): void => { + confirm({ + title: 'Do you really want to delete this dashboard?', + icon: , + onOk() { + deleteDashboard({ + uuid: id, + }); + }, + okText: 'Delete', + okButtonProps: { danger: true }, + centered: true, + }); + }; + + return ( + + Delete + + ); } interface DispatchProps { From 24d6a1e7b2133354a475cbfa5252ad745d609b4f Mon Sep 17 00:00:00 2001 From: Pranshu Chittora Date: Mon, 4 Apr 2022 19:38:23 +0530 Subject: [PATCH 19/46] feat: s3 ttl validation --- frontend/src/api/settings/setRetention.ts | 8 +- .../container/GeneralSettings/Retention.tsx | 19 +- .../src/container/GeneralSettings/index.tsx | 277 +++++++++++------- .../src/types/api/settings/setRetention.ts | 8 +- 4 files changed, 195 insertions(+), 117 deletions(-) diff --git a/frontend/src/api/settings/setRetention.ts b/frontend/src/api/settings/setRetention.ts index 6cb8c436a2..62aa3559e5 100644 --- a/frontend/src/api/settings/setRetention.ts +++ b/frontend/src/api/settings/setRetention.ts @@ -8,7 +8,13 @@ const setRetention = async ( props: Props, ): Promise | ErrorResponse> => { try { - const response = await axios.post(`/settings/ttl`, props); + const response = await axios.post( + `/settings/ttl?duration=${props.totalDuration}&type=${props.type}${ + props.coldStorage + ? `&coldStorage=${props.coldStorage};toColdDuration=${props.toColdDuration}` + : '' + }`, + ); return { statusCode: 200, diff --git a/frontend/src/container/GeneralSettings/Retention.tsx b/frontend/src/container/GeneralSettings/Retention.tsx index 653597c3f7..e6cef0383d 100644 --- a/frontend/src/container/GeneralSettings/Retention.tsx +++ b/frontend/src/container/GeneralSettings/Retention.tsx @@ -24,9 +24,17 @@ function Retention({ timeUnitValue: initialTimeUnitValue, } = convertHoursValueToRelevantUnit(retentionValue); const [selectedTimeUnit, setSelectTimeUnit] = useState(initialTimeUnitValue); - const [selectedValue, setSelectedValue] = useState( - initialValue, - ); + const [selectedValue, setSelectedValue] = useState(null); + + useEffect(() => { + setSelectedValue(initialValue); + }, [initialValue]); + + + useEffect(() => { + setSelectTimeUnit(initialTimeUnitValue); + }, [initialTimeUnitValue]); + const menuItems = TimeUnits.map((option) => (