From 92ba46b2f55b2a8cad73eb3af2137b5695ced89a Mon Sep 17 00:00:00 2001
From: Rajat Dabade
Date: Thu, 28 Sep 2023 09:15:12 +0530
Subject: [PATCH 01/34] fix: copy to clipboard without quotes (#3605)
---
frontend/src/container/LogDetailedView/TableView.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/frontend/src/container/LogDetailedView/TableView.tsx b/frontend/src/container/LogDetailedView/TableView.tsx
index 28910afbde..92840bde45 100644
--- a/frontend/src/container/LogDetailedView/TableView.tsx
+++ b/frontend/src/container/LogDetailedView/TableView.tsx
@@ -164,6 +164,8 @@ function TableView({
width: 70,
ellipsis: false,
render: (field, record): JSX.Element => {
+ const textToCopy = field.slice(1, -1);
+
if (record.field === 'body') {
const parsedBody = recursiveParseJSON(field);
if (!isEmpty(parsedBody)) {
@@ -174,7 +176,7 @@ function TableView({
}
return (
-
+
{field}
);
From 05ea814c615116d074b82d6df2654234bda5ffc1 Mon Sep 17 00:00:00 2001
From: Rajat Dabade
Date: Thu, 28 Sep 2023 13:32:55 +0530
Subject: [PATCH 02/34] refactor: wrap tooltip text and remain file for
tooltips (#3647)
---
.../TextToolTip/TextToolTip.style.scss | 3 +
.../components/TextToolTip/TextToolTip.tsx | 89 +++++++++++++++++++
.../TextToolTip/{styles.ts => constant.ts} | 0
frontend/src/components/TextToolTip/index.tsx | 86 +-----------------
4 files changed, 93 insertions(+), 85 deletions(-)
create mode 100644 frontend/src/components/TextToolTip/TextToolTip.style.scss
create mode 100644 frontend/src/components/TextToolTip/TextToolTip.tsx
rename frontend/src/components/TextToolTip/{styles.ts => constant.ts} (100%)
diff --git a/frontend/src/components/TextToolTip/TextToolTip.style.scss b/frontend/src/components/TextToolTip/TextToolTip.style.scss
new file mode 100644
index 0000000000..192d98264c
--- /dev/null
+++ b/frontend/src/components/TextToolTip/TextToolTip.style.scss
@@ -0,0 +1,3 @@
+.overlay--text-wrap {
+ white-space: pre-wrap;
+}
\ No newline at end of file
diff --git a/frontend/src/components/TextToolTip/TextToolTip.tsx b/frontend/src/components/TextToolTip/TextToolTip.tsx
new file mode 100644
index 0000000000..6c8fad783e
--- /dev/null
+++ b/frontend/src/components/TextToolTip/TextToolTip.tsx
@@ -0,0 +1,89 @@
+import './TextToolTip.style.scss';
+
+import { blue, grey } from '@ant-design/colors';
+import {
+ QuestionCircleFilled,
+ QuestionCircleOutlined,
+} from '@ant-design/icons';
+import { Tooltip } from 'antd';
+import { themeColors } from 'constants/theme';
+import { useIsDarkMode } from 'hooks/useDarkMode';
+import { useMemo } from 'react';
+import { popupContainer } from 'utils/selectPopupContainer';
+
+import { style } from './constant';
+
+function TextToolTip({
+ text,
+ url,
+ useFilledIcon = true,
+ urlText,
+}: TextToolTipProps): JSX.Element {
+ const isDarkMode = useIsDarkMode();
+
+ const onClickHandler = (
+ event: React.MouseEvent,
+ ): void => {
+ event.stopPropagation();
+ };
+
+ const overlay = useMemo(
+ () => (
+
+ ),
+ [text, url, urlText],
+ );
+
+ const iconStyle = useMemo(
+ () => ({
+ ...style,
+ color: isDarkMode ? themeColors.whiteCream : grey[0],
+ }),
+ [isDarkMode],
+ );
+
+ const iconOutlinedStyle = useMemo(
+ () => ({
+ ...style,
+ color: isDarkMode ? themeColors.navyBlue : blue[6],
+ }),
+ [isDarkMode],
+ );
+
+ return (
+
+ {useFilledIcon ? (
+
+ ) : (
+
+ )}
+
+ );
+}
+
+TextToolTip.defaultProps = {
+ url: '',
+ urlText: '',
+ useFilledIcon: true,
+};
+interface TextToolTipProps {
+ url?: string;
+ text: string;
+ useFilledIcon?: boolean;
+ urlText?: string;
+}
+
+export default TextToolTip;
diff --git a/frontend/src/components/TextToolTip/styles.ts b/frontend/src/components/TextToolTip/constant.ts
similarity index 100%
rename from frontend/src/components/TextToolTip/styles.ts
rename to frontend/src/components/TextToolTip/constant.ts
diff --git a/frontend/src/components/TextToolTip/index.tsx b/frontend/src/components/TextToolTip/index.tsx
index 72f631a872..c40a841fd0 100644
--- a/frontend/src/components/TextToolTip/index.tsx
+++ b/frontend/src/components/TextToolTip/index.tsx
@@ -1,87 +1,3 @@
-import { blue, grey } from '@ant-design/colors';
-import {
- QuestionCircleFilled,
- QuestionCircleOutlined,
-} from '@ant-design/icons';
-import { Tooltip } from 'antd';
-import { themeColors } from 'constants/theme';
-import { useIsDarkMode } from 'hooks/useDarkMode';
-import { useMemo } from 'react';
-import { popupContainer } from 'utils/selectPopupContainer';
-
-import { style } from './styles';
-
-function TextToolTip({
- text,
- url,
- useFilledIcon = true,
- urlText,
-}: TextToolTipProps): JSX.Element {
- const isDarkMode = useIsDarkMode();
-
- const onClickHandler = (
- event: React.MouseEvent,
- ): void => {
- event.stopPropagation();
- };
-
- const overlay = useMemo(
- () => (
-
- ),
- [text, url, urlText],
- );
-
- const iconStyle = useMemo(
- () => ({
- ...style,
- color: isDarkMode ? themeColors.whiteCream : grey[0],
- }),
- [isDarkMode],
- );
-
- const iconOutlinedStyle = useMemo(
- () => ({
- ...style,
- color: isDarkMode ? themeColors.navyBlue : blue[6],
- }),
- [isDarkMode],
- );
-
- return (
-
- {useFilledIcon ? (
-
- ) : (
-
- )}
-
- );
-}
-
-TextToolTip.defaultProps = {
- url: '',
- urlText: '',
- useFilledIcon: true,
-};
-interface TextToolTipProps {
- url?: string;
- text: string;
- useFilledIcon?: boolean;
- urlText?: string;
-}
+import TextToolTip from './TextToolTip';
export default TextToolTip;
From 3d0fbd0065e6ae775fa8ecf380fb69393aff5145 Mon Sep 17 00:00:00 2001
From: Prashant Shahi
Date: Fri, 29 Sep 2023 17:10:14 +0530
Subject: [PATCH 03/34] perf(frontend): :hammer: improve frontend build time
(#3653)
Signed-off-by: Prashant Shahi
---
Makefile | 16 ++++++++++++++--
frontend/.dockerignore | 1 -
frontend/Dockerfile | 31 +++++--------------------------
3 files changed, 19 insertions(+), 29 deletions(-)
diff --git a/Makefile b/Makefile
index 7d976341c1..d4df55c063 100644
--- a/Makefile
+++ b/Makefile
@@ -37,10 +37,22 @@ LD_FLAGS=-X ${buildHash}=${BUILD_HASH} -X ${buildTime}=${BUILD_TIME} -X ${buildV
DEV_LD_FLAGS=-X ${licenseSignozIo}=${DEV_LICENSE_SIGNOZ_IO}
all: build-push-frontend build-push-query-service
+
+# Steps to build static files of frontend
+build-frontend-static:
+ @echo "------------------"
+ @echo "--> Building frontend static files"
+ @echo "------------------"
+ @cd $(FRONTEND_DIRECTORY) && \
+ rm -rf build && \
+ CI=1 yarn install && \
+ yarn build && \
+ ls -l build
+
# Steps to build and push docker image of frontend
.PHONY: build-frontend-amd64 build-push-frontend
# Step to build docker image of frontend in amd64 (used in build pipeline)
-build-frontend-amd64:
+build-frontend-amd64: build-frontend-static
@echo "------------------"
@echo "--> Building frontend docker image for amd64"
@echo "------------------"
@@ -49,7 +61,7 @@ build-frontend-amd64:
--build-arg TARGETPLATFORM="linux/amd64" .
# Step to build and push docker image of frontend(used in push pipeline)
-build-push-frontend:
+build-push-frontend: build-frontend-static
@echo "------------------"
@echo "--> Building and pushing frontend docker image"
@echo "------------------"
diff --git a/frontend/.dockerignore b/frontend/.dockerignore
index a106e6a11e..840adcb19f 100644
--- a/frontend/.dockerignore
+++ b/frontend/.dockerignore
@@ -1,4 +1,3 @@
node_modules
.vscode
-build
.git
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
index 3209052799..ddbf9edc19 100644
--- a/frontend/Dockerfile
+++ b/frontend/Dockerfile
@@ -1,38 +1,17 @@
-# Builder stage
-FROM node:16.15.0 as builder
+FROM nginx:1.25.2-alpine
# Add Maintainer Info
LABEL maintainer="signoz"
-ARG TARGETOS=linux
-ARG TARGETARCH
-
+# Set working directory
WORKDIR /frontend
-# Copy the package.json and .yarnrc files prior to install dependencies
-COPY package.json ./
-# Copy lock file
-COPY yarn.lock ./
-COPY .yarnrc ./
-
-# Install the dependencies and make the folder
-RUN CI=1 yarn install
-
-COPY . .
-
-# Build the project and copy the files
-RUN yarn build
-
-
-FROM nginx:1.25.2-alpine
-
-COPY conf/default.conf /etc/nginx/conf.d/default.conf
-
# Remove default nginx index page
RUN rm -rf /usr/share/nginx/html/*
-# Copy from the stahg 1
-COPY --from=builder /frontend/build /usr/share/nginx/html
+# Copy custom nginx config and static files
+COPY conf/default.conf /etc/nginx/conf.d/default.conf
+COPY build /usr/share/nginx/html
EXPOSE 3301
From 9f751688cc039af92d721ded426f2b28ce004010 Mon Sep 17 00:00:00 2001
From: Nityananda Gohain
Date: Fri, 29 Sep 2023 18:20:40 +0530
Subject: [PATCH 04/34] fix: limit issue fixed when using contains (#3649)
---
pkg/query-service/app/logs/v3/query_builder.go | 2 +-
pkg/query-service/app/logs/v3/query_builder_test.go | 4 ++--
pkg/query-service/app/querier/helper.go | 3 ++-
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/pkg/query-service/app/logs/v3/query_builder.go b/pkg/query-service/app/logs/v3/query_builder.go
index 5deb2c26f3..ff989fa61d 100644
--- a/pkg/query-service/app/logs/v3/query_builder.go
+++ b/pkg/query-service/app/logs/v3/query_builder.go
@@ -280,7 +280,7 @@ func buildLogsQuery(panelType v3.PanelType, start, end, step int64, mq *v3.Build
}
if graphLimitQtype == constants.SecondQueryGraphLimit {
- filterSubQuery = filterSubQuery + " AND " + fmt.Sprintf("(%s) GLOBAL IN (", getSelectKeys(mq.AggregateOperator, mq.GroupBy)) + "%s)"
+ filterSubQuery = filterSubQuery + " AND " + fmt.Sprintf("(%s) GLOBAL IN (", getSelectKeys(mq.AggregateOperator, mq.GroupBy)) + "#LIMIT_PLACEHOLDER)"
}
aggregationKey := ""
diff --git a/pkg/query-service/app/logs/v3/query_builder_test.go b/pkg/query-service/app/logs/v3/query_builder_test.go
index 79f103a0a3..4c23b5f6a9 100644
--- a/pkg/query-service/app/logs/v3/query_builder_test.go
+++ b/pkg/query-service/app/logs/v3/query_builder_test.go
@@ -1193,7 +1193,7 @@ var testPrepLogsQueryData = []struct {
Limit: 2,
},
TableName: "logs",
- ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string_value[indexOf(attributes_string_key, 'method')] as method, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360000000000 AND timestamp <= 1680066420000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND indexOf(attributes_string_key, 'method') > 0 AND has(attributes_string_key, 'name') AND (method) GLOBAL IN (%s) group by method,ts order by value DESC",
+ ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string_value[indexOf(attributes_string_key, 'method')] as method, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360000000000 AND timestamp <= 1680066420000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND indexOf(attributes_string_key, 'method') > 0 AND has(attributes_string_key, 'name') AND (method) GLOBAL IN (#LIMIT_PLACEHOLDER) group by method,ts order by value DESC",
Options: Options{GraphLimitQtype: constants.SecondQueryGraphLimit},
},
{
@@ -1216,7 +1216,7 @@ var testPrepLogsQueryData = []struct {
Limit: 2,
},
TableName: "logs",
- ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string_value[indexOf(attributes_string_key, 'method')] as method, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360000000000 AND timestamp <= 1680066420000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND indexOf(attributes_string_key, 'method') > 0 AND has(attributes_string_key, 'name') AND (method) GLOBAL IN (%s) group by method,ts order by method ASC",
+ ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string_value[indexOf(attributes_string_key, 'method')] as method, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360000000000 AND timestamp <= 1680066420000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND indexOf(attributes_string_key, 'method') > 0 AND has(attributes_string_key, 'name') AND (method) GLOBAL IN (#LIMIT_PLACEHOLDER) group by method,ts order by method ASC",
Options: Options{GraphLimitQtype: constants.SecondQueryGraphLimit},
},
// Live tail
diff --git a/pkg/query-service/app/querier/helper.go b/pkg/query-service/app/querier/helper.go
index 4d809a18f3..82bf2cc464 100644
--- a/pkg/query-service/app/querier/helper.go
+++ b/pkg/query-service/app/querier/helper.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
+ "strings"
"sync"
"time"
@@ -64,7 +65,7 @@ func (q *querier) runBuilderQuery(
ch <- channelResult{Err: err, Name: queryName, Query: placeholderQuery, Series: nil}
return
}
- query = fmt.Sprintf(placeholderQuery, limitQuery)
+ query = strings.Replace(placeholderQuery, "#LIMIT_PLACEHOLDER", limitQuery, 1)
} else {
query, err = logsV3.PrepareLogsQuery(
params.Start,
From 81b10d126a6d51b3381df1f187b819225f2b3473 Mon Sep 17 00:00:00 2001
From: Palash Gupta
Date: Mon, 2 Oct 2023 05:04:04 +0000
Subject: [PATCH 05/34] feat: has and nhas filters is now enabled (#3567)
---
.../src/container/LogDetailedView/config.ts | 13 ++++
.../container/LogDetailedView/util.test.ts | 4 +-
.../src/container/LogDetailedView/utils.tsx | 77 +++++++++++--------
.../filters/QueryBuilderSearch/index.tsx | 5 +-
4 files changed, 59 insertions(+), 40 deletions(-)
create mode 100644 frontend/src/container/LogDetailedView/config.ts
diff --git a/frontend/src/container/LogDetailedView/config.ts b/frontend/src/container/LogDetailedView/config.ts
new file mode 100644
index 0000000000..cd34023699
--- /dev/null
+++ b/frontend/src/container/LogDetailedView/config.ts
@@ -0,0 +1,13 @@
+import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
+
+export const typeToArrayTypeMapper: { [key in DataTypes]: DataTypes } = {
+ [DataTypes.String]: DataTypes.ArrayString,
+ [DataTypes.Float64]: DataTypes.ArrayFloat64,
+ [DataTypes.Int64]: DataTypes.ArrayInt64,
+ [DataTypes.bool]: DataTypes.ArrayBool,
+ [DataTypes.EMPTY]: DataTypes.EMPTY,
+ [DataTypes.ArrayFloat64]: DataTypes.ArrayFloat64,
+ [DataTypes.ArrayInt64]: DataTypes.ArrayInt64,
+ [DataTypes.ArrayString]: DataTypes.ArrayString,
+ [DataTypes.ArrayBool]: DataTypes.ArrayBool,
+};
diff --git a/frontend/src/container/LogDetailedView/util.test.ts b/frontend/src/container/LogDetailedView/util.test.ts
index 4f080e23c1..d5918f2bca 100644
--- a/frontend/src/container/LogDetailedView/util.test.ts
+++ b/frontend/src/container/LogDetailedView/util.test.ts
@@ -176,8 +176,8 @@ describe('Get Data Types utils', () => {
});
// Edge cases
- it('should return Int64 for empty array input', () => {
- expect(getDataTypes([])).toBe(DataTypes.Int64);
+ it('should return Empty for empty array input', () => {
+ expect(getDataTypes([])).toBe(DataTypes.EMPTY);
});
it('should handle mixed array (return based on first element)', () => {
diff --git a/frontend/src/container/LogDetailedView/utils.tsx b/frontend/src/container/LogDetailedView/utils.tsx
index 02890e7dc9..5530c970d3 100644
--- a/frontend/src/container/LogDetailedView/utils.tsx
+++ b/frontend/src/container/LogDetailedView/utils.tsx
@@ -5,6 +5,7 @@ import { ILog, ILogAggregateAttributesResources } from 'types/api/logs/log';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import BodyTitleRenderer from './BodyTitleRenderer';
+import { typeToArrayTypeMapper } from './config';
import { AnyObject, IFieldAttributes } from './LogDetailedView.types';
export const recursiveParseJSON = (obj: string): Record => {
@@ -107,40 +108,6 @@ export function flattenObject(obj: AnyObject, prefix = ''): AnyObject {
}, {});
}
-const isFloat = (num: number): boolean => num % 1 !== 0;
-
-export const getDataTypes = (value: unknown): DataTypes => {
- if (typeof value === 'string') {
- return DataTypes.String;
- }
-
- if (typeof value === 'number') {
- return isFloat(value) ? DataTypes.Float64 : DataTypes.Int64;
- }
-
- if (typeof value === 'boolean') {
- return DataTypes.bool;
- }
-
- if (Array.isArray(value)) {
- const firstElement = value[0];
-
- if (typeof firstElement === 'string') {
- return DataTypes.ArrayString;
- }
-
- if (typeof firstElement === 'boolean') {
- return DataTypes.ArrayBool;
- }
-
- if (typeof firstElement === 'number') {
- return isFloat(firstElement) ? DataTypes.ArrayFloat64 : DataTypes.ArrayInt64;
- }
- }
-
- return DataTypes.Int64;
-};
-
export const generateFieldKeyForArray = (
fieldKey: string,
dataType: DataTypes,
@@ -217,3 +184,45 @@ export const aggregateAttributesResourcesToString = (logData: ILog): string => {
return JSON.stringify(outputJson, null, 2);
};
+
+const isFloat = (num: number): boolean => num % 1 !== 0;
+
+const isBooleanString = (str: string): boolean =>
+ str.toLowerCase() === 'true' || str.toLowerCase() === 'false';
+
+const determineType = (val: unknown): DataTypes => {
+ if (typeof val === 'string') {
+ if (isBooleanString(val)) {
+ return DataTypes.bool;
+ }
+
+ const numberValue = parseFloat(val);
+
+ if (!Number.isNaN(numberValue)) {
+ return isFloat(numberValue) ? DataTypes.Float64 : DataTypes.Int64;
+ }
+
+ return DataTypes.String;
+ }
+
+ if (typeof val === 'number') {
+ return isFloat(val) ? DataTypes.Float64 : DataTypes.Int64;
+ }
+
+ if (typeof val === 'boolean') {
+ return DataTypes.bool;
+ }
+
+ return DataTypes.EMPTY;
+};
+
+export const getDataTypes = (value: unknown): DataTypes => {
+ const getArrayType = (elementType: DataTypes): DataTypes =>
+ typeToArrayTypeMapper[elementType] || DataTypes.EMPTY;
+
+ if (Array.isArray(value)) {
+ return getArrayType(determineType(value[0]));
+ }
+
+ return determineType(value);
+};
diff --git a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/index.tsx b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/index.tsx
index 4b2c4fe6b3..59c693b6e6 100644
--- a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/index.tsx
+++ b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/index.tsx
@@ -87,10 +87,7 @@ function QueryBuilderSearch({
handleSearch(value);
};
- const isDisabled =
- !!searchValue ||
- OPERATORS.HAS === tagOperator ||
- OPERATORS.NHAS === tagOperator;
+ const isDisabled = !!searchValue;
return (
From 11863040bba95dc239cdbe61045a1d130ef4a1ac Mon Sep 17 00:00:00 2001
From: Palash Gupta
Date: Thu, 5 Oct 2023 10:34:17 +0530
Subject: [PATCH 06/34] fix: alerts is now migrated to new alerts page (#3669)
---
frontend/src/container/CreateAlertRule/defaults.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/frontend/src/container/CreateAlertRule/defaults.ts b/frontend/src/container/CreateAlertRule/defaults.ts
index 2ac2f3a7b8..8517d9b18c 100644
--- a/frontend/src/container/CreateAlertRule/defaults.ts
+++ b/frontend/src/container/CreateAlertRule/defaults.ts
@@ -3,6 +3,7 @@ import {
initialQueryPromQLData,
PANEL_TYPES,
} from 'constants/queryBuilder';
+import ROUTES from 'constants/routes';
import { AlertTypes } from 'types/api/alerts/alertTypes';
import {
AlertDef,
@@ -77,7 +78,7 @@ export const logAlertDefaults: AlertDef = {
},
labels: {
severity: 'warning',
- details: `${window.location.protocol}//${window.location.host}/logs`,
+ details: `${window.location.protocol}//${window.location.host}${ROUTES.LOGS_EXPLORER}`,
},
annotations: defaultAnnotations,
evalWindow: defaultEvalWindow,
From 0ad5d671405ae7e98d61cd68ea4d45d654ef0509 Mon Sep 17 00:00:00 2001
From: Raj Kamal Singh <1133322+rkssisodiya@users.noreply.github.com>
Date: Thu, 5 Oct 2023 14:27:41 +0530
Subject: [PATCH 07/34] QS: Collector simulator (#3656)
* feat: get collectorsimulator started and add inmemoryreceiver
* feat: add collectorsimulator/inmemoryexporter
* feat: add collectorsimulator.SimulateLogsProcessing
* chore: clean up collector simulator code a little
* chore: update go.sum entries for cors
* chore: add collectorsimulator tests to make cmd
* chore: move to latest dependency version for collectorsimulator
* chore: revert to dependency versions matching signoz-otel-col
* chore: cleanup: reorganize collectorsimulator logic
* chore: some more cleanup
* chore: some more cleanup
* chore: some more cleanup
* chore: redo go.mod
---
Makefile | 1 +
go.mod | 38 ++-
go.sum | 90 +++++++
.../collectorsimulator/collectorsimulator.go | 234 ++++++++++++++++++
.../inmemoryexporter/config.go | 16 ++
.../inmemoryexporter/config_test.go | 48 ++++
.../inmemoryexporter/exporter.go | 86 +++++++
.../inmemoryexporter/exporter_test.go | 67 +++++
.../inmemoryexporter/factory.go | 34 +++
.../inmemoryexporter/factory_test.go | 28 +++
.../inmemoryreceiver/config.go | 16 ++
.../inmemoryreceiver/config_test.go | 48 ++++
.../inmemoryreceiver/factory.go | 41 +++
.../inmemoryreceiver/factory_test.go | 29 +++
.../inmemoryreceiver/receiver.go | 64 +++++
.../inmemoryreceiver/receiver_test.go | 68 +++++
pkg/query-service/collectorsimulator/logs.go | 122 +++++++++
.../collectorsimulator/logs_test.go | 113 +++++++++
18 files changed, 1139 insertions(+), 4 deletions(-)
create mode 100644 pkg/query-service/collectorsimulator/collectorsimulator.go
create mode 100644 pkg/query-service/collectorsimulator/inmemoryexporter/config.go
create mode 100644 pkg/query-service/collectorsimulator/inmemoryexporter/config_test.go
create mode 100644 pkg/query-service/collectorsimulator/inmemoryexporter/exporter.go
create mode 100644 pkg/query-service/collectorsimulator/inmemoryexporter/exporter_test.go
create mode 100644 pkg/query-service/collectorsimulator/inmemoryexporter/factory.go
create mode 100644 pkg/query-service/collectorsimulator/inmemoryexporter/factory_test.go
create mode 100644 pkg/query-service/collectorsimulator/inmemoryreceiver/config.go
create mode 100644 pkg/query-service/collectorsimulator/inmemoryreceiver/config_test.go
create mode 100644 pkg/query-service/collectorsimulator/inmemoryreceiver/factory.go
create mode 100644 pkg/query-service/collectorsimulator/inmemoryreceiver/factory_test.go
create mode 100644 pkg/query-service/collectorsimulator/inmemoryreceiver/receiver.go
create mode 100644 pkg/query-service/collectorsimulator/inmemoryreceiver/receiver_test.go
create mode 100644 pkg/query-service/collectorsimulator/logs.go
create mode 100644 pkg/query-service/collectorsimulator/logs_test.go
diff --git a/Makefile b/Makefile
index d4df55c063..0d46bfa161 100644
--- a/Makefile
+++ b/Makefile
@@ -165,3 +165,4 @@ test:
go test ./pkg/query-service/formatter/...
go test ./pkg/query-service/tests/integration/...
go test ./pkg/query-service/rules/...
+ go test ./pkg/query-service/collectorsimulator/...
diff --git a/go.mod b/go.mod
index 2402e92498..9d22d922c6 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ require (
github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb
github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974
github.com/SigNoz/zap_otlp/zap_otlp_sync v0.0.0-20230822164844-1b861a431974
- github.com/antonmedv/expr v1.12.4
+ github.com/antonmedv/expr v1.12.5
github.com/auth0/go-jwt-middleware v1.0.1
github.com/cespare/xxhash v1.1.0
github.com/coreos/go-oidc/v3 v3.4.0
@@ -37,7 +37,7 @@ require (
github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f
github.com/prometheus/common v0.44.0
github.com/prometheus/prometheus v2.5.0+incompatible
- github.com/rs/cors v1.8.2
+ github.com/rs/cors v1.9.0
github.com/russellhaering/gosaml2 v0.9.0
github.com/russellhaering/goxmldsig v1.2.0
github.com/samber/lo v1.38.1
@@ -47,7 +47,7 @@ require (
github.com/soheilhy/cmux v0.1.5
github.com/srikanthccv/ClickHouse-go-mock v0.4.0
github.com/stretchr/testify v1.8.4
- go.opentelemetry.io/collector/confmap v0.70.0
+ go.opentelemetry.io/collector/confmap v0.79.0
go.opentelemetry.io/otel v1.17.0
go.opentelemetry.io/otel/sdk v1.16.0
go.uber.org/multierr v1.11.0
@@ -65,6 +65,7 @@ require (
)
require (
+ contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
@@ -77,6 +78,7 @@ require (
github.com/beevik/etree v1.1.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
+ github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dennwc/varint v1.0.0 // indirect
@@ -89,8 +91,10 @@ require (
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
+ github.com/go-ole/go-ole v1.2.6 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
+ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
@@ -98,6 +102,7 @@ require (
github.com/gosimple/unidecode v1.0.0 // indirect
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
@@ -106,6 +111,7 @@ require (
github.com/klauspost/compress v1.16.7 // indirect
github.com/klauspost/cpuid v1.2.3 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
+ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/minio/md5-simd v1.1.0 // indirect
@@ -116,28 +122,51 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
+ github.com/observiq/ctimefmt v1.0.0 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
+ github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.79.0 // indirect
+ github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.79.0 // indirect
+ github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.79.0 // indirect
github.com/paulmach/orb v0.10.0 // indirect
github.com/pierrec/lz4/v4 v4.1.18 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common/sigv4 v0.1.0 // indirect
github.com/prometheus/procfs v0.11.0 // indirect
+ github.com/prometheus/statsd_exporter v0.22.7 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/segmentio/backo-go v1.0.1 // indirect
+ github.com/shirou/gopsutil/v3 v3.23.4 // indirect
+ github.com/shoenig/go-m1cpu v0.1.5 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/smarty/assertions v1.15.0 // indirect
+ github.com/spf13/cobra v1.7.0 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+ github.com/tklauser/go-sysconf v0.3.11 // indirect
+ github.com/tklauser/numcpus v0.6.0 // indirect
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
- go.opentelemetry.io/collector/featuregate v0.70.0 // indirect
+ github.com/yusufpapurcu/wmi v1.2.2 // indirect
+ go.opencensus.io v0.24.0 // indirect
+ go.opentelemetry.io/collector v0.79.0 // indirect
+ go.opentelemetry.io/collector/component v0.79.0 // indirect
+ go.opentelemetry.io/collector/consumer v0.79.0 // indirect
+ go.opentelemetry.io/collector/exporter v0.79.0 // indirect
+ go.opentelemetry.io/collector/featuregate v1.0.0-rcv0012 // indirect
go.opentelemetry.io/collector/pdata v1.0.0-rcv0014 // indirect
+ go.opentelemetry.io/collector/receiver v0.79.0 // indirect
go.opentelemetry.io/collector/semconv v0.81.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect
+ go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect
+ go.opentelemetry.io/otel/bridge/opencensus v0.39.0 // indirect
+ go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect
go.opentelemetry.io/otel/metric v1.17.0 // indirect
+ go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect
go.opentelemetry.io/otel/trace v1.17.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
@@ -146,6 +175,7 @@ require (
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/time v0.3.0 // indirect
+ gonum.org/v1/gonum v0.13.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 // indirect
diff --git a/go.sum b/go.sum
index 6283ae2486..e4fb091cf3 100644
--- a/go.sum
+++ b/go.sum
@@ -53,6 +53,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
+contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg=
+contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw=
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
@@ -90,6 +92,7 @@ github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20O
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
+github.com/Mottl/ctimefmt v0.0.0-20190803144728-fd2ac23a585a/go.mod h1:eyj2WSIdoPMPs2eNTLpSmM6Nzqo4V80/d6jHpnJ1SAI=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb h1:bneLSKPf9YUSFmafKx32bynV6QrzViL/s+ZDvQxH1E4=
@@ -114,6 +117,8 @@ github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antonmedv/expr v1.12.4 h1:YRkeF7r0cejMS47bDYe3Jyes7L9t1AhpunC+Duq+R9k=
github.com/antonmedv/expr v1.12.4/go.mod h1:FPC8iWArxls7axbVLsW+kpg1mz29A1b2M6jt+hZfDkU=
+github.com/antonmedv/expr v1.12.5 h1:Fq4okale9swwL3OeLLs9WD9H6GbgBLJyN/NUHRv+n0E=
+github.com/antonmedv/expr v1.12.5/go.mod h1:FPC8iWArxls7axbVLsW+kpg1mz29A1b2M6jt+hZfDkU=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
@@ -146,6 +151,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
+github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
+github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@@ -173,6 +180,7 @@ github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -244,12 +252,14 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
+github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -258,6 +268,8 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8=
@@ -291,6 +303,8 @@ github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -469,6 +483,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/ionos-cloud/sdk-go/v6 v6.1.8 h1:493wE/BkZxJf7x79UCE0cYGPZoqQcPiEBALvt7uVGY0=
github.com/ionos-cloud/sdk-go/v6 v6.1.8/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
@@ -527,6 +543,8 @@ github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/linode/linodego v1.19.0 h1:n4WJrcr9+30e9JGZ6DI0nZbm5SdAj1kSwvvt/998YUw=
github.com/linode/linodego v1.19.0/go.mod h1:XZFR+yJ9mm2kwf6itZ6SCpu+6w3KnIevV0Uu5HNWJgQ=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
@@ -593,6 +611,8 @@ github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnu
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
+github.com/observiq/ctimefmt v1.0.0 h1:r7vTJ+Slkrt9fZ67mkf+mA6zAdR5nGIJRMTzkUyvilk=
+github.com/observiq/ctimefmt v1.0.0/go.mod h1:mxi62//WbSpG/roCO1c6MqZ7zQTvjVtYheqHN3eOjvc=
github.com/oklog/oklog v0.3.2 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
@@ -614,6 +634,12 @@ github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E=
github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
github.com/open-telemetry/opamp-go v0.5.0 h1:2YFbb6G4qBkq3yTRdVb5Nfz9hKHW/ldUyex352e1J7g=
github.com/open-telemetry/opamp-go v0.5.0/go.mod h1:IMdeuHGVc5CjKSu5/oNV0o+UmiXuahoHvoZ4GOmAI9M=
+github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.79.0 h1:OZPeakqoSZ1yRlmGBlWi9kISx/9PJzlNLGLutFPOQY0=
+github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.79.0/go.mod h1:VOHKYi1wm+/c2wZA3mY1Grd4eYP8uS//EV0yHBbGfGw=
+github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.79.0 h1:o1aUgN0pA5Sc0s2bOUy7vDoNyJ6D6qdHihXk3BKyf58=
+github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.79.0/go.mod h1:t8I2umZdg81AQmncs7fVHw1YMzSol3A7ecsc2lfqgaM=
+github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.79.0 h1:EpuwiWvq1hqS4PAp/+kMvWVkM4o+PRGtTGSDLpmIeME=
+github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.79.0/go.mod h1:0dccj1BrKVG00hvt2f70tu7Re1YjAl5Jpy2lduSrLnI=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
@@ -648,11 +674,16 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f h1:h0p1aZ9F5d6IXOygysob3g4B07b+HuVUQC0VJKD8wA4=
github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
+github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
+github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
+github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -665,6 +696,9 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
+github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
+github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
+github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4=
@@ -673,8 +707,12 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk=
github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
+github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0=
+github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI=
github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
@@ -687,11 +725,14 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
+github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
+github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russellhaering/gosaml2 v0.9.0 h1:CNMnH42z/GirrKjdmNrSS6bAAs47F9bPdl4PfRmVOIk=
github.com/russellhaering/gosaml2 v0.9.0/go.mod h1:byViER/1YPUa0Puj9ROZblpoq2jsE7h/CJmitzX0geU=
github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg=
github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
@@ -706,6 +747,12 @@ github.com/segmentio/backo-go v1.0.1 h1:68RQccglxZeyURy93ASB/2kc9QudzgIDexJ927N+
github.com/segmentio/backo-go v1.0.1/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc=
github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI=
github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE=
+github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
+github.com/shirou/gopsutil/v3 v3.23.4 h1:hZwmDxZs7Ewt75DV81r4pFMqbq+di2cbt9FsQBqLD2o=
+github.com/shirou/gopsutil/v3 v3.23.4/go.mod h1:ZcGxyfzAMRevhUR2+cfhXDH6gQdFYE/t8j1nsU4mPI8=
+github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ=
+github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
+github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -729,6 +776,8 @@ github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
+github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/srikanthccv/ClickHouse-go-mock v0.4.0 h1:tLk7qoDLg7Z5YD5mOmNqjRDbsm6ehJVXOFvSnG+gQAg=
@@ -746,10 +795,16 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
+github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
+github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
+github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
@@ -767,6 +822,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
+github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
@@ -778,22 +835,46 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
+go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
+go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
+go.opentelemetry.io/collector v0.79.0 h1:Lra7U0ilMor1g5WVkO3YZ0kZYsvzAtGN+Uq+CmC96JY=
+go.opentelemetry.io/collector v0.79.0/go.mod h1:O2Vfwykphq9VqdATZiAypjnJMS3WFBXwFSe/0ujo38Q=
+go.opentelemetry.io/collector/component v0.79.0 h1:ZKLJ4qa0AngmyGp1RQBJgl6OIP6mxdfrVpbz09h/W34=
+go.opentelemetry.io/collector/component v0.79.0/go.mod h1:rX0gixMemcXZTZaML5zUiT+5txZUYkWnACscJkFVj18=
go.opentelemetry.io/collector/confmap v0.70.0 h1:GJDaM7c3yFyT7Zv6l2/5ahwaqPCvtC92Ii8Bg2AVdjU=
go.opentelemetry.io/collector/confmap v0.70.0/go.mod h1:8//JWR2TMChLH35Az0mGFrCskEIP6POgZJK6iRRhzeM=
+go.opentelemetry.io/collector/confmap v0.79.0 h1:a4XVde3lLP81BiSbt8AzVD6pvQBX8YkrB9ZtMSHKv1A=
+go.opentelemetry.io/collector/confmap v0.79.0/go.mod h1:cKr2c7lVtEJCuMOncUPlcROJBbTFaHiPjYp1Y8RbL+Q=
+go.opentelemetry.io/collector/consumer v0.79.0 h1:V/4PCvbTw2Bt+lYb/ogac0g/nCCb3oKnmz+jM3t5Dyk=
+go.opentelemetry.io/collector/consumer v0.79.0/go.mod h1:VfqIyUI5K20zXx3mfVN+skmA+V3sV5fNorJ5TaIOj/U=
+go.opentelemetry.io/collector/exporter v0.79.0 h1:PxhKgWf1AkZvN1PjiJT5xiO+pKZA9Y4fyuMs5aNFuEA=
+go.opentelemetry.io/collector/exporter v0.79.0/go.mod h1:qlXiqnOUeHelpAwk03f8nB5+91UIqlA7udSBsj9bJ3M=
go.opentelemetry.io/collector/featuregate v0.70.0 h1:Xr6hrMT/++SjTm06nreex8WlpgFhYJ7S0yRVn1OvVf8=
go.opentelemetry.io/collector/featuregate v0.70.0/go.mod h1:ih+oCwrHW3bLac/qnPUzes28yDCDmh8WzsAKKauwCYI=
+go.opentelemetry.io/collector/featuregate v1.0.0-rcv0012 h1:pSO81lfikGEgRXHepmOGy2o6WWCly427UJCgMJC5c8g=
+go.opentelemetry.io/collector/featuregate v1.0.0-rcv0012/go.mod h1:/kVAsGUCyJXIDSgHftCN63QiwAEVHRLX2Kh/S+dqgHY=
go.opentelemetry.io/collector/pdata v1.0.0-rcv0014 h1:iT5qH0NLmkGeIdDtnBogYDx7L58t6CaWGL378DEo2QY=
go.opentelemetry.io/collector/pdata v1.0.0-rcv0014/go.mod h1:BRvDrx43kiSoUx3mr7SoA7h9B8+OY99mUK+CZSQFWW4=
+go.opentelemetry.io/collector/receiver v0.79.0 h1:Ag4hciAYklQWDpKbnmqhfh9zJlUskWvThpCpphp12b4=
+go.opentelemetry.io/collector/receiver v0.79.0/go.mod h1:+/xe0VoYl6Mli+KQTZWBR2apqFsbioAAqu7abzKDskI=
go.opentelemetry.io/collector/semconv v0.81.0 h1:lCYNNo3powDvFIaTPP2jDKIrBiV1T92NK4QgL/aHYXw=
go.opentelemetry.io/collector/semconv v0.81.0/go.mod h1:TlYPtzvsXyHOgr5eATi43qEMqwSmIziivJB2uctKswo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8=
+go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo=
+go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc=
go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM=
go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0=
+go.opentelemetry.io/otel/bridge/opencensus v0.39.0 h1:YHivttTaDhbZIHuPlg1sWsy2P5gj57vzqPfkHItgbwQ=
+go.opentelemetry.io/otel/bridge/opencensus v0.39.0/go.mod h1:vZ4537pNjFDXEx//WldAR6Ro2LC8wwmFC76njAXwNPE=
+go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA=
+go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y=
go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc=
go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o=
go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE=
go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4=
+go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI=
+go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI=
go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ=
go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
@@ -977,6 +1058,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1008,6 +1090,7 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1034,6 +1117,7 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1043,10 +1127,13 @@ golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -1138,6 +1225,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
+gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM=
+gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -1320,6 +1409,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
diff --git a/pkg/query-service/collectorsimulator/collectorsimulator.go b/pkg/query-service/collectorsimulator/collectorsimulator.go
new file mode 100644
index 0000000000..c4537cf3ee
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/collectorsimulator.go
@@ -0,0 +1,234 @@
+package collectorsimulator
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "strings"
+
+ "github.com/google/uuid"
+ "github.com/knadh/koanf/parsers/yaml"
+ "github.com/pkg/errors"
+ "go.opentelemetry.io/collector/component"
+ "go.opentelemetry.io/collector/confmap"
+ "go.opentelemetry.io/collector/confmap/provider/yamlprovider"
+ "go.opentelemetry.io/collector/connector"
+ "go.opentelemetry.io/collector/exporter"
+ "go.opentelemetry.io/collector/extension"
+ "go.opentelemetry.io/collector/otelcol"
+ "go.opentelemetry.io/collector/processor"
+ "go.opentelemetry.io/collector/receiver"
+ "go.opentelemetry.io/collector/service"
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+
+ "go.signoz.io/signoz/pkg/query-service/collectorsimulator/inmemoryexporter"
+ "go.signoz.io/signoz/pkg/query-service/collectorsimulator/inmemoryreceiver"
+ "go.signoz.io/signoz/pkg/query-service/model"
+)
+
+// Puts together a collector service with inmemory receiver and exporter
+// for simulating processing of signal data through an otel collector
+type CollectorSimulator struct {
+ // collector service to be used for the simulation
+ collectorSvc *service.Service
+
+ // Buffer where collectorSvc will log errors.
+ collectorErrorLogsBuffer *bytes.Buffer
+
+ // error channel where collector components will report fatal errors
+ // Gets passed in as AsyncErrorChannel in service.Settings when creating a collector service.
+ collectorErrorChannel chan error
+
+ // Unique ids of inmemory receiver and exporter instances that
+ // will be created by collectorSvc
+ inMemoryReceiverId string
+ inMemoryExporterId string
+}
+
+func NewCollectorSimulator(
+ ctx context.Context,
+ signalType component.DataType,
+ processorFactories map[component.Type]processor.Factory,
+ processorConfigs []ProcessorConfig,
+) (*CollectorSimulator, *model.ApiError) {
+ // Put together collector component factories for use in the simulation
+ receiverFactories, err := receiver.MakeFactoryMap(inmemoryreceiver.NewFactory())
+ if err != nil {
+ return nil, model.InternalError(errors.Wrap(err, "could not create receiver factories."))
+ }
+ exporterFactories, err := exporter.MakeFactoryMap(inmemoryexporter.NewFactory())
+ if err != nil {
+ return nil, model.InternalError(errors.Wrap(err, "could not create processor factories."))
+ }
+ factories := otelcol.Factories{
+ Receivers: receiverFactories,
+ Processors: processorFactories,
+ Exporters: exporterFactories,
+ }
+
+ // Prepare collector config yaml for simulation
+ inMemoryReceiverId := uuid.NewString()
+ inMemoryExporterId := uuid.NewString()
+
+ collectorConfYaml, err := generateSimulationConfig(
+ signalType, inMemoryReceiverId, processorConfigs, inMemoryExporterId,
+ )
+ if err != nil {
+ return nil, model.BadRequest(errors.Wrap(err, "could not generate collector config"))
+ }
+
+ // Parse and validate collector config
+ yamlP := yamlprovider.New()
+ confProvider, err := otelcol.NewConfigProvider(otelcol.ConfigProviderSettings{
+ ResolverSettings: confmap.ResolverSettings{
+ URIs: []string{"yaml:" + string(collectorConfYaml)},
+ Providers: map[string]confmap.Provider{yamlP.Scheme(): yamlP},
+ },
+ })
+ if err != nil {
+ return nil, model.BadRequest(errors.Wrap(err, "could not create config provider."))
+ }
+ collectorCfg, err := confProvider.Get(ctx, factories)
+ if err != nil {
+ return nil, model.BadRequest(errors.Wrap(err, "failed to parse collector config"))
+ }
+
+ if err = collectorCfg.Validate(); err != nil {
+ return nil, model.BadRequest(errors.Wrap(err, "invalid collector config"))
+ }
+
+ // Build and start collector service.
+ collectorErrChan := make(chan error)
+ var collectorErrBuf bytes.Buffer
+ svcSettings := service.Settings{
+ Receivers: receiver.NewBuilder(collectorCfg.Receivers, factories.Receivers),
+ Processors: processor.NewBuilder(collectorCfg.Processors, factories.Processors),
+ Exporters: exporter.NewBuilder(collectorCfg.Exporters, factories.Exporters),
+ Connectors: connector.NewBuilder(collectorCfg.Connectors, factories.Connectors),
+ Extensions: extension.NewBuilder(collectorCfg.Extensions, factories.Extensions),
+ AsyncErrorChannel: collectorErrChan,
+ LoggingOptions: []zap.Option{
+ zap.ErrorOutput(zapcore.AddSync(&collectorErrBuf)),
+ },
+ }
+
+ collectorSvc, err := service.New(ctx, svcSettings, collectorCfg.Service)
+ if err != nil {
+ return nil, model.InternalError(errors.Wrap(err, "could not instantiate collector service"))
+ }
+
+ return &CollectorSimulator{
+ inMemoryReceiverId: inMemoryReceiverId,
+ inMemoryExporterId: inMemoryExporterId,
+ collectorSvc: collectorSvc,
+ collectorErrorLogsBuffer: &collectorErrBuf,
+ collectorErrorChannel: collectorErrChan,
+ }, nil
+}
+
+func (l *CollectorSimulator) Start(ctx context.Context) (
+ func(), *model.ApiError,
+) {
+ // Calling collectorSvc.Start below will in turn call Start on
+ // inmemory receiver and exporter instances created by collectorSvc
+ //
+ // inmemory components are indexed in a global map after Start is called
+ // on them and will have to be cleaned up to ensure there is no memory leak
+ cleanupFn := func() {
+ inmemoryreceiver.CleanupInstance(l.inMemoryReceiverId)
+ inmemoryexporter.CleanupInstance(l.inMemoryExporterId)
+ }
+
+ err := l.collectorSvc.Start(ctx)
+ if err != nil {
+ return cleanupFn, model.InternalError(errors.Wrap(err, "could not start collector service for simulation"))
+ }
+
+ return cleanupFn, nil
+}
+
+func (l *CollectorSimulator) GetReceiver() *inmemoryreceiver.InMemoryReceiver {
+ return inmemoryreceiver.GetReceiverInstance(l.inMemoryReceiverId)
+}
+
+func (l *CollectorSimulator) GetExporter() *inmemoryexporter.InMemoryExporter {
+ return inmemoryexporter.GetExporterInstance(l.inMemoryExporterId)
+}
+
+func (l *CollectorSimulator) Shutdown(ctx context.Context) (
+ simulationErrs []string, apiErr *model.ApiError,
+) {
+ shutdownErr := l.collectorSvc.Shutdown(ctx)
+
+ // Collect all errors logged or reported by collectorSvc
+ simulationErrs = []string{}
+ close(l.collectorErrorChannel)
+ for reportedErr := range l.collectorErrorChannel {
+ simulationErrs = append(simulationErrs, reportedErr.Error())
+ }
+
+ if l.collectorErrorLogsBuffer.Len() > 0 {
+ errBufText := strings.TrimSpace(l.collectorErrorLogsBuffer.String())
+ errBufLines := strings.Split(errBufText, "\n")
+ simulationErrs = append(simulationErrs, errBufLines...)
+ }
+
+ if shutdownErr != nil {
+ return simulationErrs, model.InternalError(errors.Wrap(
+ shutdownErr, "could not shutdown the collector service",
+ ))
+ }
+ return simulationErrs, nil
+}
+
+func generateSimulationConfig(
+ signalType component.DataType,
+ receiverId string,
+ processorConfigs []ProcessorConfig,
+ exporterId string,
+) ([]byte, error) {
+ baseConf := fmt.Sprintf(`
+ receivers:
+ memory:
+ id: %s
+ exporters:
+ memory:
+ id: %s
+ service:
+ telemetry:
+ metrics:
+ level: none
+ logs:
+ level: error
+ `, receiverId, exporterId)
+
+ simulationConf, err := yaml.Parser().Unmarshal([]byte(baseConf))
+ if err != nil {
+ return nil, err
+ }
+
+ processors := map[string]interface{}{}
+ procNamesInOrder := []string{}
+ for _, processorConf := range processorConfigs {
+ processors[processorConf.Name] = processorConf.Config
+ procNamesInOrder = append(procNamesInOrder, processorConf.Name)
+ }
+ simulationConf["processors"] = processors
+
+ svc := simulationConf["service"].(map[string]interface{})
+ svc["pipelines"] = map[string]interface{}{
+ string(signalType): map[string]interface{}{
+ "receivers": []string{"memory"},
+ "processors": procNamesInOrder,
+ "exporters": []string{"memory"},
+ },
+ }
+
+ simulationConfYaml, err := yaml.Parser().Marshal(simulationConf)
+ if err != nil {
+ return nil, err
+ }
+
+ return simulationConfYaml, nil
+}
diff --git a/pkg/query-service/collectorsimulator/inmemoryexporter/config.go b/pkg/query-service/collectorsimulator/inmemoryexporter/config.go
new file mode 100644
index 0000000000..5b23b041ce
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/inmemoryexporter/config.go
@@ -0,0 +1,16 @@
+package inmemoryexporter
+
+import "fmt"
+
+type Config struct {
+ // Unique id for the exporter.
+ // Useful for getting a hold of the exporter in code that doesn't control its instantiation.
+ Id string `mapstructure:"id"`
+}
+
+func (c *Config) Validate() error {
+ if len(c.Id) < 1 {
+ return fmt.Errorf("inmemory exporter: id is required")
+ }
+ return nil
+}
diff --git a/pkg/query-service/collectorsimulator/inmemoryexporter/config_test.go b/pkg/query-service/collectorsimulator/inmemoryexporter/config_test.go
new file mode 100644
index 0000000000..29749757dc
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/inmemoryexporter/config_test.go
@@ -0,0 +1,48 @@
+package inmemoryexporter
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "go.opentelemetry.io/collector/component"
+ "go.opentelemetry.io/collector/confmap"
+)
+
+func TestValidate(t *testing.T) {
+ tests := []struct {
+ name string
+ rawConf *confmap.Conf
+ errorExpected bool
+ }{
+ {
+ name: "with id",
+ rawConf: confmap.NewFromStringMap(map[string]interface{}{
+ "id": "test_exporter",
+ }),
+ errorExpected: false,
+ },
+ {
+ name: "empty id",
+ rawConf: confmap.NewFromStringMap(map[string]interface{}{
+ "id": "",
+ }),
+ errorExpected: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ factory := NewFactory()
+ cfg := factory.CreateDefaultConfig()
+ err := component.UnmarshalConfig(tt.rawConf, cfg)
+ require.NoError(t, err, "could not UnmarshalConfig")
+
+ err = component.ValidateConfig(cfg)
+ if tt.errorExpected {
+ require.NotNilf(t, err, "Invalid config did not return validation error: %v", cfg)
+ } else {
+ require.NoErrorf(t, err, "Valid config returned validation error: %v", cfg)
+ }
+ })
+ }
+}
diff --git a/pkg/query-service/collectorsimulator/inmemoryexporter/exporter.go b/pkg/query-service/collectorsimulator/inmemoryexporter/exporter.go
new file mode 100644
index 0000000000..3cff186016
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/inmemoryexporter/exporter.go
@@ -0,0 +1,86 @@
+package inmemoryexporter
+
+import (
+ "context"
+ "fmt"
+ "sync"
+
+ "go.opentelemetry.io/collector/component"
+ "go.opentelemetry.io/collector/consumer"
+ "go.opentelemetry.io/collector/pdata/plog"
+)
+
+// An in-memory exporter for testing and generating previews.
+type InMemoryExporter struct {
+ // Unique identifier for the exporter.
+ id string
+ // mu protects the data below
+ mu sync.Mutex
+ // slice of pdata.Logs that were received by this exporter.
+ logs []plog.Logs
+}
+
+// ConsumeLogs implements component.LogsExporter.
+func (e *InMemoryExporter) ConsumeLogs(ctx context.Context, ld plog.Logs) error {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+
+ e.logs = append(e.logs, ld)
+ return nil
+}
+
+func (e *InMemoryExporter) GetLogs() []plog.Logs {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+
+ return e.logs
+}
+
+func (e *InMemoryExporter) ResetLogs() {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+
+ e.logs = nil
+}
+
+func (e *InMemoryExporter) Capabilities() consumer.Capabilities {
+ return consumer.Capabilities{MutatesData: false}
+}
+
+// Keep track of all exporter instances in the process.
+// Useful for getting a hold of the exporter in scenarios where one doesn't
+// create the instances. Eg: bringing up a collector service from collector config
+var allExporterInstances map[string]*InMemoryExporter
+var allExportersLock sync.Mutex
+
+func init() {
+ allExporterInstances = make(map[string]*InMemoryExporter)
+}
+
+func GetExporterInstance(id string) *InMemoryExporter {
+ return allExporterInstances[id]
+}
+
+func CleanupInstance(exporterId string) {
+ allExportersLock.Lock()
+ defer allExportersLock.Unlock()
+
+ delete(allExporterInstances, exporterId)
+}
+
+func (e *InMemoryExporter) Start(ctx context.Context, host component.Host) error {
+ allExportersLock.Lock()
+ defer allExportersLock.Unlock()
+
+ if allExporterInstances[e.id] != nil {
+ return fmt.Errorf("exporter with id %s is already running", e.id)
+ }
+
+ allExporterInstances[e.id] = e
+ return nil
+}
+
+func (e *InMemoryExporter) Shutdown(ctx context.Context) error {
+ CleanupInstance(e.id)
+ return nil
+}
diff --git a/pkg/query-service/collectorsimulator/inmemoryexporter/exporter_test.go b/pkg/query-service/collectorsimulator/inmemoryexporter/exporter_test.go
new file mode 100644
index 0000000000..4fe4753d72
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/inmemoryexporter/exporter_test.go
@@ -0,0 +1,67 @@
+package inmemoryexporter
+
+import (
+ "context"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/stretchr/testify/require"
+ "go.opentelemetry.io/collector/component"
+ "go.opentelemetry.io/collector/component/componenttest"
+ "go.opentelemetry.io/collector/confmap"
+ "go.opentelemetry.io/collector/exporter"
+)
+
+func TestExporterLifecycle(t *testing.T) {
+ require := require.New(t)
+ testExporterId := uuid.NewString()
+
+ // Should be able to get a hold of the exporter after starting it.
+ require.Nil(GetExporterInstance(testExporterId))
+
+ constructed, err := makeTestExporter(testExporterId)
+ require.Nil(err, "could not make test exporter")
+
+ err = constructed.Start(context.Background(), componenttest.NewNopHost())
+ require.Nil(err, "could not start test exporter")
+
+ testExporter := GetExporterInstance(testExporterId)
+ require.NotNil(testExporter, "could not get exporter instance by Id")
+
+ // Should not be able to start 2 exporters with the same id
+ constructed2, err := makeTestExporter(testExporterId)
+ require.Nil(err, "could not create second exporter with same id")
+
+ err = constructed2.Start(context.Background(), componenttest.NewNopHost())
+ require.NotNil(err, "should not be able to start another exporter with same id before shutting down the previous one")
+
+ // Should not be able to get a hold of an exporter after shutdown
+ testExporter.Shutdown(context.Background())
+ require.Nil(GetExporterInstance(testExporterId), "should not be able to find exporter instance after shutdown")
+
+ // Should be able to start a new exporter with same id after shutting down
+ constructed3, err := makeTestExporter(testExporterId)
+ require.Nil(err, "could not make exporter with same Id after shutting down previous one")
+
+ err = constructed3.Start(context.Background(), componenttest.NewNopHost())
+ require.Nil(err, "should be able to start another exporter with same id after shutting down the previous one")
+
+ testExporter3 := GetExporterInstance(testExporterId)
+ require.NotNil(testExporter3, "could not get exporter instance by Id")
+
+ testExporter3.Shutdown(context.Background())
+ require.Nil(GetExporterInstance(testExporterId))
+}
+
+func makeTestExporter(exporterId string) (exporter.Logs, error) {
+ factory := NewFactory()
+
+ cfg := factory.CreateDefaultConfig()
+ component.UnmarshalConfig(confmap.NewFromStringMap(
+ map[string]interface{}{"id": exporterId}), cfg,
+ )
+
+ return factory.CreateLogsExporter(
+ context.Background(), exporter.CreateSettings{}, cfg,
+ )
+}
diff --git a/pkg/query-service/collectorsimulator/inmemoryexporter/factory.go b/pkg/query-service/collectorsimulator/inmemoryexporter/factory.go
new file mode 100644
index 0000000000..7752693060
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/inmemoryexporter/factory.go
@@ -0,0 +1,34 @@
+package inmemoryexporter
+
+import (
+ "context"
+
+ "github.com/google/uuid"
+ "github.com/pkg/errors"
+ "go.opentelemetry.io/collector/component"
+ "go.opentelemetry.io/collector/exporter"
+)
+
+func createDefaultConfig() component.Config {
+ return &Config{
+ Id: uuid.NewString(),
+ }
+}
+
+func createLogsExporter(
+ _ context.Context, _ exporter.CreateSettings, config component.Config,
+) (exporter.Logs, error) {
+ if err := component.ValidateConfig(config); err != nil {
+ return nil, errors.Wrap(err, "invalid inmemory exporter config")
+ }
+ return &InMemoryExporter{
+ id: config.(*Config).Id,
+ }, nil
+}
+
+func NewFactory() exporter.Factory {
+ return exporter.NewFactory(
+ "memory",
+ createDefaultConfig,
+ exporter.WithLogs(createLogsExporter, component.StabilityLevelBeta))
+}
diff --git a/pkg/query-service/collectorsimulator/inmemoryexporter/factory_test.go b/pkg/query-service/collectorsimulator/inmemoryexporter/factory_test.go
new file mode 100644
index 0000000000..1a9481169a
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/inmemoryexporter/factory_test.go
@@ -0,0 +1,28 @@
+package inmemoryexporter
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "go.opentelemetry.io/collector/component/componenttest"
+ "go.opentelemetry.io/collector/exporter"
+)
+
+func TestCreateDefaultConfig(t *testing.T) {
+ factory := NewFactory()
+ cfg := factory.CreateDefaultConfig()
+ assert.NotNil(t, cfg, "failed to create default config")
+ assert.NoError(t, componenttest.CheckConfigStruct(cfg))
+}
+
+func TestCreateLogsExporter(t *testing.T) {
+ factory := NewFactory()
+ cfg := factory.CreateDefaultConfig()
+
+ te, err := factory.CreateLogsExporter(
+ context.Background(), exporter.CreateSettings{}, cfg,
+ )
+ assert.NoError(t, err)
+ assert.NotNil(t, te)
+}
diff --git a/pkg/query-service/collectorsimulator/inmemoryreceiver/config.go b/pkg/query-service/collectorsimulator/inmemoryreceiver/config.go
new file mode 100644
index 0000000000..6df842ce3e
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/inmemoryreceiver/config.go
@@ -0,0 +1,16 @@
+package inmemoryreceiver
+
+import "fmt"
+
+type Config struct {
+ // Unique id for the receiver.
+ // Useful for getting a hold of the receiver in code that doesn't control its instantiation.
+ Id string `mapstructure:"id"`
+}
+
+func (c *Config) Validate() error {
+ if len(c.Id) < 1 {
+ return fmt.Errorf("inmemory receiver: id is required")
+ }
+ return nil
+}
diff --git a/pkg/query-service/collectorsimulator/inmemoryreceiver/config_test.go b/pkg/query-service/collectorsimulator/inmemoryreceiver/config_test.go
new file mode 100644
index 0000000000..a0daf71c45
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/inmemoryreceiver/config_test.go
@@ -0,0 +1,48 @@
+package inmemoryreceiver
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "go.opentelemetry.io/collector/component"
+ "go.opentelemetry.io/collector/confmap"
+)
+
+func TestValidate(t *testing.T) {
+ tests := []struct {
+ name string
+ rawConf *confmap.Conf
+ errorExpected bool
+ }{
+ {
+ name: "with id",
+ rawConf: confmap.NewFromStringMap(map[string]interface{}{
+ "id": "test_receiver",
+ }),
+ errorExpected: false,
+ },
+ {
+ name: "empty id",
+ rawConf: confmap.NewFromStringMap(map[string]interface{}{
+ "id": "",
+ }),
+ errorExpected: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ factory := NewFactory()
+ cfg := factory.CreateDefaultConfig()
+ err := component.UnmarshalConfig(tt.rawConf, cfg)
+ require.NoError(t, err, "could not UnmarshalConfig")
+
+ err = component.ValidateConfig(cfg)
+ if tt.errorExpected {
+ require.NotNilf(t, err, "Invalid config did not return validation error: %v", cfg)
+ } else {
+ require.NoErrorf(t, err, "Valid config returned validation error: %v", cfg)
+ }
+ })
+ }
+}
diff --git a/pkg/query-service/collectorsimulator/inmemoryreceiver/factory.go b/pkg/query-service/collectorsimulator/inmemoryreceiver/factory.go
new file mode 100644
index 0000000000..9db222cc43
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/inmemoryreceiver/factory.go
@@ -0,0 +1,41 @@
+package inmemoryreceiver
+
+import (
+ "context"
+
+ "github.com/google/uuid"
+ "github.com/pkg/errors"
+ "go.opentelemetry.io/collector/component"
+ "go.opentelemetry.io/collector/consumer"
+ "go.opentelemetry.io/collector/receiver"
+)
+
+func createDefaultConfig() component.Config {
+ return &Config{
+ Id: uuid.NewString(),
+ }
+}
+
+func createLogsReceiver(
+ _ context.Context,
+ _ receiver.CreateSettings,
+ config component.Config,
+ consumer consumer.Logs,
+) (receiver.Logs, error) {
+ if err := component.ValidateConfig(config); err != nil {
+ return nil, errors.Wrap(err, "invalid inmemory receiver config")
+ }
+ return &InMemoryReceiver{
+ id: config.(*Config).Id,
+ nextConsumer: consumer,
+ }, nil
+
+}
+
+// NewFactory creates a new OTLP receiver factory.
+func NewFactory() receiver.Factory {
+ return receiver.NewFactory(
+ "memory",
+ createDefaultConfig,
+ receiver.WithLogs(createLogsReceiver, component.StabilityLevelBeta))
+}
diff --git a/pkg/query-service/collectorsimulator/inmemoryreceiver/factory_test.go b/pkg/query-service/collectorsimulator/inmemoryreceiver/factory_test.go
new file mode 100644
index 0000000000..7bdcd80bee
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/inmemoryreceiver/factory_test.go
@@ -0,0 +1,29 @@
+package inmemoryreceiver
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "go.opentelemetry.io/collector/component/componenttest"
+ "go.opentelemetry.io/collector/consumer/consumertest"
+ "go.opentelemetry.io/collector/receiver"
+)
+
+func TestCreateDefaultConfig(t *testing.T) {
+ factory := NewFactory()
+ cfg := factory.CreateDefaultConfig()
+ assert.NotNil(t, cfg, "failed to create default config")
+ assert.NoError(t, componenttest.CheckConfigStruct(cfg))
+}
+
+func TestCreateLogsReceiver(t *testing.T) {
+ factory := NewFactory()
+ cfg := factory.CreateDefaultConfig()
+
+ te, err := factory.CreateLogsReceiver(
+ context.Background(), receiver.CreateSettings{}, cfg, consumertest.NewNop(),
+ )
+ assert.NoError(t, err)
+ assert.NotNil(t, te)
+}
diff --git a/pkg/query-service/collectorsimulator/inmemoryreceiver/receiver.go b/pkg/query-service/collectorsimulator/inmemoryreceiver/receiver.go
new file mode 100644
index 0000000000..d4b0a2abfe
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/inmemoryreceiver/receiver.go
@@ -0,0 +1,64 @@
+package inmemoryreceiver
+
+import (
+ "context"
+ "fmt"
+ "sync"
+
+ "go.opentelemetry.io/collector/component"
+ "go.opentelemetry.io/collector/consumer"
+ "go.opentelemetry.io/collector/pdata/plog"
+)
+
+// In memory receiver for testing and simulation
+type InMemoryReceiver struct {
+ // Unique identifier for the receiver.
+ id string
+
+ nextConsumer consumer.Logs
+}
+
+func (r *InMemoryReceiver) ConsumeLogs(ctx context.Context, ld plog.Logs) error {
+ return r.nextConsumer.ConsumeLogs(ctx, ld)
+}
+
+func (r *InMemoryReceiver) Capabilities() consumer.Capabilities {
+ return consumer.Capabilities{MutatesData: false}
+}
+
+// Keep track of all receiver instances in the process.
+// Useful for getting a hold of the receiver in scenarios where one doesn't
+// create the instances. Eg: bringing up a collector service from collector config
+var allReceiverInstances map[string]*InMemoryReceiver
+var allReceiversLock sync.Mutex
+
+func init() {
+ allReceiverInstances = make(map[string]*InMemoryReceiver)
+}
+
+func CleanupInstance(receiverId string) {
+ allReceiversLock.Lock()
+ defer allReceiversLock.Unlock()
+ delete(allReceiverInstances, receiverId)
+}
+
+func (r *InMemoryReceiver) Start(ctx context.Context, host component.Host) error {
+ allReceiversLock.Lock()
+ defer allReceiversLock.Unlock()
+
+ if allReceiverInstances[r.id] != nil {
+ return fmt.Errorf("receiver with id %s is already running", r.id)
+ }
+
+ allReceiverInstances[r.id] = r
+ return nil
+}
+
+func (r *InMemoryReceiver) Shutdown(ctx context.Context) error {
+ CleanupInstance(r.id)
+ return nil
+}
+
+func GetReceiverInstance(id string) *InMemoryReceiver {
+ return allReceiverInstances[id]
+}
diff --git a/pkg/query-service/collectorsimulator/inmemoryreceiver/receiver_test.go b/pkg/query-service/collectorsimulator/inmemoryreceiver/receiver_test.go
new file mode 100644
index 0000000000..4fe7169cc7
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/inmemoryreceiver/receiver_test.go
@@ -0,0 +1,68 @@
+package inmemoryreceiver
+
+import (
+ "context"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/stretchr/testify/require"
+ "go.opentelemetry.io/collector/component"
+ "go.opentelemetry.io/collector/component/componenttest"
+ "go.opentelemetry.io/collector/confmap"
+ "go.opentelemetry.io/collector/consumer/consumertest"
+ "go.opentelemetry.io/collector/receiver"
+)
+
+func TestReceiverLifecycle(t *testing.T) {
+ require := require.New(t)
+ testReceiverId := uuid.NewString()
+
+ // Should be able to get a hold of the receiver after starting it.
+ require.Nil(GetReceiverInstance(testReceiverId), "receiver instance should not exist before Start()")
+
+ constructed, err := makeTestLogReceiver(testReceiverId)
+ require.Nil(err, "could not make test receiver")
+
+ err = constructed.Start(context.Background(), componenttest.NewNopHost())
+ require.Nil(err, "could not start test receiver")
+
+ testReceiver := GetReceiverInstance(testReceiverId)
+ require.NotNil(testReceiver, "could not get receiver instance by Id")
+
+ // Should not be able to start 2 receivers with the same id
+ constructed2, err := makeTestLogReceiver(testReceiverId)
+ require.Nil(err, "could not create second receiver with same id")
+
+ err = constructed2.Start(context.Background(), componenttest.NewNopHost())
+ require.NotNil(err, "should not be able to start another receiver with same id before shutting down the previous one")
+
+ // Should not be able to get a hold of an receiver after shutdown
+ testReceiver.Shutdown(context.Background())
+ require.Nil(GetReceiverInstance(testReceiverId), "should not be able to find inmemory receiver after shutdown")
+
+ // Should be able to start a new receiver with same id after shutting down
+ constructed3, err := makeTestLogReceiver(testReceiverId)
+ require.Nil(err, "could not make receiver with same Id after shutting down old one")
+
+ err = constructed3.Start(context.Background(), componenttest.NewNopHost())
+ require.Nil(err, "should be able to start another receiver with same id after shutting down the previous one")
+
+ testReceiver3 := GetReceiverInstance(testReceiverId)
+ require.NotNil(testReceiver3, "could not get receiver instance by Id")
+
+ testReceiver3.Shutdown(context.Background())
+ require.Nil(GetReceiverInstance(testReceiverId))
+}
+
+func makeTestLogReceiver(receiverId string) (receiver.Logs, error) {
+ factory := NewFactory()
+
+ cfg := factory.CreateDefaultConfig()
+ component.UnmarshalConfig(confmap.NewFromStringMap(
+ map[string]interface{}{"id": receiverId}), cfg,
+ )
+
+ return factory.CreateLogsReceiver(
+ context.Background(), receiver.CreateSettings{}, cfg, consumertest.NewNop(),
+ )
+}
diff --git a/pkg/query-service/collectorsimulator/logs.go b/pkg/query-service/collectorsimulator/logs.go
new file mode 100644
index 0000000000..ab445f79eb
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/logs.go
@@ -0,0 +1,122 @@
+package collectorsimulator
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/pkg/errors"
+ "go.opentelemetry.io/collector/component"
+ "go.opentelemetry.io/collector/pdata/plog"
+ "go.opentelemetry.io/collector/processor"
+ "go.signoz.io/signoz/pkg/query-service/model"
+)
+
+type ProcessorConfig struct {
+ Name string
+ Config map[string]interface{}
+}
+
+// Simulate processing of logs through the otel collector.
+// Useful for testing, validation and generating previews.
+func SimulateLogsProcessing(
+ ctx context.Context,
+ processorFactories map[component.Type]processor.Factory,
+ processorConfigs []ProcessorConfig,
+ logs []plog.Logs,
+ timeout time.Duration,
+) (
+ outputLogs []plog.Logs, collectorErrs []string, apiErr *model.ApiError,
+) {
+ // Construct and start a simulator (wraps a collector service)
+ simulator, apiErr := NewCollectorSimulator(
+ ctx, component.DataTypeLogs, processorFactories, processorConfigs,
+ )
+ if apiErr != nil {
+ return nil, nil, model.WrapApiError(apiErr, "could not create logs processing simulator")
+ }
+
+ simulatorCleanup, apiErr := simulator.Start(ctx)
+ // We can not rely on collector service to shutdown successfully and cleanup refs to inmemory components.
+ defer simulatorCleanup()
+ if apiErr != nil {
+ return nil, nil, apiErr
+ }
+
+ // Do the simulation
+ for _, plog := range logs {
+ apiErr = SendLogsToSimulator(ctx, simulator, plog)
+ if apiErr != nil {
+ return nil, nil, model.WrapApiError(apiErr, "could not consume logs for simulation")
+ }
+ }
+
+ result, apiErr := GetProcessedLogsFromSimulator(
+ simulator, len(logs), timeout,
+ )
+ if apiErr != nil {
+ return nil, nil, model.InternalError(model.WrapApiError(apiErr,
+ "could not get processed logs from simulator",
+ ))
+ }
+
+ // Shut down the simulator
+ simulationErrs, apiErr := simulator.Shutdown(ctx)
+ if apiErr != nil {
+ return nil, simulationErrs, model.WrapApiError(apiErr,
+ "could not shutdown logs processing simulator",
+ )
+ }
+
+ return result, simulationErrs, nil
+}
+
+func SendLogsToSimulator(
+ ctx context.Context,
+ simulator *CollectorSimulator,
+ plog plog.Logs,
+) *model.ApiError {
+ receiver := simulator.GetReceiver()
+ if receiver == nil {
+ return model.InternalError(fmt.Errorf("could not find in memory receiver for simulator"))
+ }
+ if err := receiver.ConsumeLogs(ctx, plog); err != nil {
+ return model.InternalError(errors.Wrap(err,
+ "inmemory receiver could not consume logs for simulation",
+ ))
+ }
+ return nil
+}
+
+func GetProcessedLogsFromSimulator(
+ simulator *CollectorSimulator,
+ minLogCount int,
+ timeout time.Duration,
+) (
+ []plog.Logs, *model.ApiError,
+) {
+ exporter := simulator.GetExporter()
+ if exporter == nil {
+ return nil, model.InternalError(fmt.Errorf("could not find in memory exporter for simulator"))
+ }
+
+ // Must do a time based wait to ensure all logs come through.
+ // For example, logstransformprocessor does internal batching and it
+ // takes (processorCount * batchTime) for logs to get through.
+ startTsMillis := time.Now().UnixMilli()
+ for {
+ elapsedMillis := time.Now().UnixMilli() - startTsMillis
+ if elapsedMillis > timeout.Milliseconds() {
+ break
+ }
+
+ exportedLogs := exporter.GetLogs()
+ if len(exportedLogs) >= minLogCount {
+ return exportedLogs, nil
+ }
+
+ time.Sleep(50 * time.Millisecond)
+ }
+
+ return exporter.GetLogs(), nil
+}
diff --git a/pkg/query-service/collectorsimulator/logs_test.go b/pkg/query-service/collectorsimulator/logs_test.go
new file mode 100644
index 0000000000..796d19f00f
--- /dev/null
+++ b/pkg/query-service/collectorsimulator/logs_test.go
@@ -0,0 +1,113 @@
+package collectorsimulator
+
+import (
+ "context"
+ "testing"
+ "time"
+
+ "github.com/knadh/koanf/parsers/yaml"
+ "github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor"
+ "github.com/stretchr/testify/require"
+ "go.opentelemetry.io/collector/pdata/plog"
+ "go.opentelemetry.io/collector/processor"
+)
+
+func TestLogsProcessingSimulation(t *testing.T) {
+ require := require.New(t)
+
+ inputLogs := []plog.Logs{
+ makeTestPlog("test log 1", map[string]string{
+ "method": "GET",
+ }),
+ makeTestPlog("test log 2", map[string]string{
+ "method": "POST",
+ }),
+ }
+
+ testLogstransformConf1, err := yaml.Parser().Unmarshal([]byte(`
+ operators:
+ - type: router
+ id: router_signoz
+ routes:
+ - output: add
+ expr: attributes.method == "GET"
+ default: noop
+ - type: add
+ id: add
+ field: attributes.test
+ value: test-value-get
+ - type: noop
+ id: noop
+ `))
+ require.Nil(err, "could not unmarshal test logstransform op config")
+ testProcessor1 := ProcessorConfig{
+ Name: "logstransform/test",
+ Config: testLogstransformConf1,
+ }
+
+ testLogstransformConf2, err := yaml.Parser().Unmarshal([]byte(`
+ operators:
+ - type: router
+ id: router_signoz
+ routes:
+ - output: add
+ expr: attributes.method == "POST"
+ default: noop
+ - type: add
+ id: add
+ field: attributes.test
+ value: test-value-post
+ - type: noop
+ id: noop
+ `))
+ require.Nil(err, "could not unmarshal test logstransform op config")
+ testProcessor2 := ProcessorConfig{
+ Name: "logstransform/test2",
+ Config: testLogstransformConf2,
+ }
+
+ processorFactories, err := processor.MakeFactoryMap(
+ logstransformprocessor.NewFactory(),
+ )
+ require.Nil(err, "could not create processors factory map")
+
+ outputLogs, collectorErrs, apiErr := SimulateLogsProcessing(
+ context.Background(),
+ processorFactories,
+ []ProcessorConfig{testProcessor1, testProcessor2},
+ inputLogs,
+ 300*time.Millisecond,
+ )
+ require.Nil(apiErr, apiErr.ToError().Error())
+ require.Equal(len(collectorErrs), 0)
+
+ for _, l := range outputLogs {
+ rl := l.ResourceLogs().At(0)
+ sl := rl.ScopeLogs().At(0)
+ record := sl.LogRecords().At(0)
+ method, exists := record.Attributes().Get("method")
+ require.True(exists)
+ testVal, exists := record.Attributes().Get("test")
+ require.True(exists)
+ if method.Str() == "GET" {
+ require.Equal(testVal.Str(), "test-value-get")
+ } else {
+ require.Equal(testVal.Str(), "test-value-post")
+ }
+ }
+}
+
+func makeTestPlog(body string, attrsStr map[string]string) plog.Logs {
+ pl := plog.NewLogs()
+ rl := pl.ResourceLogs().AppendEmpty()
+
+ scopeLog := rl.ScopeLogs().AppendEmpty()
+ slRecord := scopeLog.LogRecords().AppendEmpty()
+ slRecord.Body().SetStr(body)
+ slAttribs := slRecord.Attributes()
+ for k, v := range attrsStr {
+ slAttribs.PutStr(k, v)
+ }
+
+ return pl
+}
From a306fb64cbf79f1a1f1a4d64d5a5df4580ef74b3 Mon Sep 17 00:00:00 2001
From: Yunus M
Date: Thu, 5 Oct 2023 21:59:40 +0530
Subject: [PATCH 08/34] feat: update analytics endpoints (#3674)
Co-authored-by: Palash Gupta
---
frontend/src/index.html.ejs | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/frontend/src/index.html.ejs b/frontend/src/index.html.ejs
index abfd8258bb..227506006b 100644
--- a/frontend/src/index.html.ejs
+++ b/frontend/src/index.html.ejs
@@ -173,14 +173,20 @@
t.type = 'text/javascript';
t.async = !0;
t.src =
- 'https://cdn.segment.com/analytics.js/v1/' + key + '/analytics.min.js';
+ 'https://analytics-cdn.signoz.io/analytics.js/v1/' +
+ key +
+ '/analytics.min.js';
var n = document.getElementsByTagName('script')[0];
n.parentNode.insertBefore(t, n);
analytics._loadOptions = i;
};
analytics._writeKey = SEGMENT_ID;
analytics.SNIPPET_VERSION = '4.16.1';
- analytics.load(SEGMENT_ID);
+ analytics.load(SEGMENT_ID, {
+ integrations: {
+ 'Segment.io': { apiHost: 'analytics-api.signoz.io/v1' },
+ },
+ });
analytics.page();
}
})();
From abed60bdfa558c7b35f1d104339b18322bc8828f Mon Sep 17 00:00:00 2001
From: Nityananda Gohain
Date: Fri, 6 Oct 2023 09:26:37 +0530
Subject: [PATCH 09/34] fix: exists check for json filters added (#3675)
* fix: exists check for json filters added
* fix: comment updated
---
pkg/query-service/app/logs/v3/json_filter.go | 58 +++++++++++++------
.../app/logs/v3/json_filter_test.go | 30 +++++++---
.../app/logs/v3/query_builder_test.go | 2 +-
3 files changed, 63 insertions(+), 27 deletions(-)
diff --git a/pkg/query-service/app/logs/v3/json_filter.go b/pkg/query-service/app/logs/v3/json_filter.go
index 345da5a013..abb0fe6712 100644
--- a/pkg/query-service/app/logs/v3/json_filter.go
+++ b/pkg/query-service/app/logs/v3/json_filter.go
@@ -52,11 +52,13 @@ var jsonLogOperators = map[v3.FilterOperator]string{
v3.FilterOperatorNotRegex: "NOT match(%s, %s)",
v3.FilterOperatorIn: "IN",
v3.FilterOperatorNotIn: "NOT IN",
+ v3.FilterOperatorExists: "JSON_EXISTS(%s, '$.%s')",
+ v3.FilterOperatorNotExists: "NOT JSON_EXISTS(%s, '$.%s')",
v3.FilterOperatorHas: "has(%s, %s)",
v3.FilterOperatorNotHas: "NOT has(%s, %s)",
}
-func getJSONFilterKey(key v3.AttributeKey, isArray bool) (string, error) {
+func getJSONFilterKey(key v3.AttributeKey, op v3.FilterOperator, isArray bool) (string, error) {
keyArr := strings.Split(key.Key, ".")
if len(keyArr) < 2 {
return "", fmt.Errorf("incorrect key, should contain at least 2 parts")
@@ -67,6 +69,10 @@ func getJSONFilterKey(key v3.AttributeKey, isArray bool) (string, error) {
return "", fmt.Errorf("only body can be the root key")
}
+ if op == v3.FilterOperatorExists || op == v3.FilterOperatorNotExists {
+ return keyArr[0], nil
+ }
+
var dataType string
var ok bool
if dataType, ok = dataTypeMapping[string(key.DataType)]; !ok {
@@ -99,29 +105,45 @@ func GetJSONFilter(item v3.FilterItem) (string, error) {
dataType = v3.AttributeKeyDataType(val)
}
- key, err := getJSONFilterKey(item.Key, isArray)
+ key, err := getJSONFilterKey(item.Key, item.Operator, isArray)
if err != nil {
return "", err
}
// non array
- value, err := utils.ValidateAndCastValue(item.Value, dataType)
- if err != nil {
- return "", fmt.Errorf("failed to validate and cast value for %s: %v", item.Key.Key, err)
- }
-
op := v3.FilterOperator(strings.ToLower(strings.TrimSpace(string(item.Operator))))
- if logsOp, ok := jsonLogOperators[op]; ok {
- switch op {
- case v3.FilterOperatorRegex, v3.FilterOperatorNotRegex, v3.FilterOperatorHas, v3.FilterOperatorNotHas:
- fmtVal := utils.ClickHouseFormattedValue(value)
- return fmt.Sprintf(logsOp, key, fmtVal), nil
- case v3.FilterOperatorContains, v3.FilterOperatorNotContains:
- return fmt.Sprintf("%s %s '%%%s%%'", key, logsOp, item.Value), nil
- default:
- fmtVal := utils.ClickHouseFormattedValue(value)
- return fmt.Sprintf("%s %s %s", key, logsOp, fmtVal), nil
+
+ var value interface{}
+ if op != v3.FilterOperatorExists && op != v3.FilterOperatorNotExists {
+ value, err = utils.ValidateAndCastValue(item.Value, dataType)
+ if err != nil {
+ return "", fmt.Errorf("failed to validate and cast value for %s: %v", item.Key.Key, err)
}
}
- return "", fmt.Errorf("unsupported operator: %s", op)
+
+ var filter string
+ if logsOp, ok := jsonLogOperators[op]; ok {
+ switch op {
+ case v3.FilterOperatorExists, v3.FilterOperatorNotExists:
+ filter = fmt.Sprintf(logsOp, key, strings.Join(strings.Split(item.Key.Key, ".")[1:], "."))
+ case v3.FilterOperatorRegex, v3.FilterOperatorNotRegex, v3.FilterOperatorHas, v3.FilterOperatorNotHas:
+ fmtVal := utils.ClickHouseFormattedValue(value)
+ filter = fmt.Sprintf(logsOp, key, fmtVal)
+ case v3.FilterOperatorContains, v3.FilterOperatorNotContains:
+ filter = fmt.Sprintf("%s %s '%%%s%%'", key, logsOp, item.Value)
+ default:
+ fmtVal := utils.ClickHouseFormattedValue(value)
+ filter = fmt.Sprintf("%s %s %s", key, logsOp, fmtVal)
+ }
+ } else {
+ return "", fmt.Errorf("unsupported operator: %s", op)
+ }
+
+ // add exists check for non array items as default values of int/float/bool will corrupt the results
+ if !isArray && !(item.Operator == v3.FilterOperatorExists || item.Operator == v3.FilterOperatorNotExists) {
+ existsFilter := fmt.Sprintf("JSON_EXISTS(body, '$.%s')", strings.Join(strings.Split(item.Key.Key, ".")[1:], "."))
+ filter = fmt.Sprintf("%s AND %s", existsFilter, filter)
+ }
+
+ return filter, nil
}
diff --git a/pkg/query-service/app/logs/v3/json_filter_test.go b/pkg/query-service/app/logs/v3/json_filter_test.go
index 455d705b1d..2f9a7f22da 100644
--- a/pkg/query-service/app/logs/v3/json_filter_test.go
+++ b/pkg/query-service/app/logs/v3/json_filter_test.go
@@ -12,6 +12,7 @@ var testGetJSONFilterKeyData = []struct {
Key v3.AttributeKey
IsArray bool
ClickhouseKey string
+ Operator v3.FilterOperator
Error bool
}{
{
@@ -129,7 +130,7 @@ var testGetJSONFilterKeyData = []struct {
func TestGetJSONFilterKey(t *testing.T) {
for _, tt := range testGetJSONFilterKeyData {
Convey("testgetKey", t, func() {
- columnName, err := getJSONFilterKey(tt.Key, tt.IsArray)
+ columnName, err := getJSONFilterKey(tt.Key, tt.Operator, tt.IsArray)
if tt.Error {
So(err, ShouldNotBeNil)
} else {
@@ -209,7 +210,7 @@ var testGetJSONFilterData = []struct {
Operator: "=",
Value: "hello",
},
- Filter: "JSON_VALUE(body, '$.message') = 'hello'",
+ Filter: "JSON_EXISTS(body, '$.message') AND JSON_VALUE(body, '$.message') = 'hello'",
},
{
Name: "eq operator number",
@@ -222,7 +223,7 @@ var testGetJSONFilterData = []struct {
Operator: "=",
Value: 1,
},
- Filter: "JSONExtract(JSON_VALUE(body, '$.status'), '" + INT64 + "') = 1",
+ Filter: "JSON_EXISTS(body, '$.status') AND JSONExtract(JSON_VALUE(body, '$.status'), '" + INT64 + "') = 1",
},
{
Name: "neq operator number",
@@ -235,7 +236,7 @@ var testGetJSONFilterData = []struct {
Operator: "=",
Value: 1.1,
},
- Filter: "JSONExtract(JSON_VALUE(body, '$.status'), '" + FLOAT64 + "') = 1.100000",
+ Filter: "JSON_EXISTS(body, '$.status') AND JSONExtract(JSON_VALUE(body, '$.status'), '" + FLOAT64 + "') = 1.100000",
},
{
Name: "eq operator bool",
@@ -248,7 +249,7 @@ var testGetJSONFilterData = []struct {
Operator: "=",
Value: true,
},
- Filter: "JSONExtract(JSON_VALUE(body, '$.boolkey'), '" + BOOL + "') = true",
+ Filter: "JSON_EXISTS(body, '$.boolkey') AND JSONExtract(JSON_VALUE(body, '$.boolkey'), '" + BOOL + "') = true",
},
{
Name: "greater than operator",
@@ -261,7 +262,7 @@ var testGetJSONFilterData = []struct {
Operator: ">",
Value: 1,
},
- Filter: "JSONExtract(JSON_VALUE(body, '$.status'), '" + INT64 + "') > 1",
+ Filter: "JSON_EXISTS(body, '$.status') AND JSONExtract(JSON_VALUE(body, '$.status'), '" + INT64 + "') > 1",
},
{
Name: "regex operator",
@@ -274,7 +275,7 @@ var testGetJSONFilterData = []struct {
Operator: "regex",
Value: "a*",
},
- Filter: "match(JSON_VALUE(body, '$.message'), 'a*')",
+ Filter: "JSON_EXISTS(body, '$.message') AND match(JSON_VALUE(body, '$.message'), 'a*')",
},
{
Name: "contains operator",
@@ -287,7 +288,20 @@ var testGetJSONFilterData = []struct {
Operator: "contains",
Value: "a",
},
- Filter: "JSON_VALUE(body, '$.message') ILIKE '%a%'",
+ Filter: "JSON_EXISTS(body, '$.message') AND JSON_VALUE(body, '$.message') ILIKE '%a%'",
+ },
+ {
+ Name: "exists",
+ FilterItem: v3.FilterItem{
+ Key: v3.AttributeKey{
+ Key: "body.message",
+ DataType: "string",
+ IsJSON: true,
+ },
+ Operator: "exists",
+ Value: "",
+ },
+ Filter: "JSON_EXISTS(body, '$.message')",
},
}
diff --git a/pkg/query-service/app/logs/v3/query_builder_test.go b/pkg/query-service/app/logs/v3/query_builder_test.go
index 4c23b5f6a9..8ae90e2e0f 100644
--- a/pkg/query-service/app/logs/v3/query_builder_test.go
+++ b/pkg/query-service/app/logs/v3/query_builder_test.go
@@ -908,7 +908,7 @@ var testBuildLogsQueryData = []struct {
},
},
TableName: "logs",
- ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as name, toFloat64(count(*)) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND JSON_VALUE(body, '$.message') ILIKE '%a%' AND indexOf(attributes_string_key, 'name') > 0 group by name order by name DESC",
+ ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as name, toFloat64(count(*)) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND JSON_EXISTS(body, '$.message') AND JSON_VALUE(body, '$.message') ILIKE '%a%' AND indexOf(attributes_string_key, 'name') > 0 group by name order by name DESC",
},
{
Name: "TABLE: Test count with JSON Filter Array, groupBy, orderBy",
From 321cba2af5c8b9ee568753def939c440cddd79e7 Mon Sep 17 00:00:00 2001
From: Wayne Zhou <170492961@qq.com>
Date: Fri, 6 Oct 2023 12:00:52 +0800
Subject: [PATCH 10/34] docs: update the chinese readme to latest (#3670)
---
README.zh-cn.md | 203 ++++++++++++++++++++++++++++++------------------
1 file changed, 129 insertions(+), 74 deletions(-)
diff --git a/README.zh-cn.md b/README.zh-cn.md
index aaa89551bf..32b6328fcb 100644
--- a/README.zh-cn.md
+++ b/README.zh-cn.md
@@ -1,170 +1,225 @@
-
-
+
-
监视你的应用,并可排查已部署应用中的问题,这是一个开源的可替代DataDog、NewRelic的方案
+监控你的应用,并且可排查已部署应用的问题,这是一个可替代 DataDog、NewRelic 的开源方案
-
+
-##
+
-SigNoz帮助开发人员监控应用并排查已部署应用中的问题。SigNoz使用分布式追踪来增加软件技术栈的可见性。
+##
-👉 你能看到一些性能指标,服务、外部api调用、每个终端(endpoint)的p99延迟和错误率。
+SigNoz 帮助开发人员监控应用并排查已部署应用的问题。你可以使用 SigNoz 实现如下能力:
-👉 通过准确的追踪来确定是什么引起了问题,并且可以看到每个独立请求的帧图(framegraph),这样你就能找到根本原因。
+👉 在同一块面板上,可视化 Metrics, Traces 和 Logs 内容。
-👉 聚合trace数据来获得业务相关指标。
+👉 你可以关注服务的 p99 延迟和错误率, 包括外部 API 调用和个别的端点。
-
-
-
-
-
+👉 你可以找到问题的根因,通过提取相关问题的 traces 日志、单独查看请求 traces 的火焰图详情。
+
+👉 执行 trace 数据聚合,以获取业务相关的 metrics
+
+👉 对日志过滤和查询,通过日志的属性建立看板和告警
+
+👉 通过 Python,java,Ruby 和 Javascript 自动记录异常
+
+👉 轻松的自定义查询和设置告警
+
+### 应用 Metrics 展示
+
+
+
+### 分布式追踪
+
+
+
+
+
+### 日志管理
+
+
+
+### 基础设施监控
+
+
+
+### 异常监控
+
+
+
+### 告警
+
+
-
+## 加入我们 Slack 社区
-## 加入我们的Slack社区
-
-来[Slack](https://signoz.io/slack) 跟我们打声招呼👋
+来 [Slack](https://signoz.io/slack) 和我们打招呼吧 👋
-
+## 特性:
-## 功能:
+- 为 metrics, traces and logs 制定统一的 UI。 无需切换 Prometheus 到 Jaeger 去查找问题,也无需使用想 Elastic 这样的日志工具分开你的 metrics 和 traces
-- 应用概览指标(metrics),如RPS, p50/p90/p99延迟率分位值,错误率等。
-- 应用中最慢的终端(endpoint)
-- 查看特定请求的trace数据来分析下游服务问题、慢数据库查询问题 及调用第三方服务如支付网关的问题
-- 通过服务名称、操作、延迟、错误、标签来过滤traces。
-- 聚合trace数据(events/spans)来得到业务相关指标。比如,你可以通过过滤条件`customer_type: gold` or `deployment_version: v2` or `external_call: paypal` 来获取指定业务的错误率和p99延迟
-- 为metrics和trace提供统一的UI。排查问题不需要在Prometheus和Jaeger之间切换。
+- 默认统计应用的 metrics 数据,像 RPS (每秒请求数), 50th/90th/99th 的分位数延迟数据,还有相关的错误率
+
+- 找到应用中最慢的端点
+
+- 查看准确的请求跟踪数据,找到下游服务的问题了,比如 DB 慢查询,或者调用第三方的支付网关等
+
+- 通过 服务名、操作方式、延迟、错误、标签/注释 过滤 traces 数据
+
+- 通过聚合 trace 数据而获得业务相关的 metrics。 比如你可以通过 `customer_type: gold` 或者 `deployment_version: v2` 或者 `external_call: paypal` 获取错误率和 P99 延迟数据
+
+- 原生支持 OpenTelemetry 日志,高级日志查询,自动收集 k8s 相关日志
+
+- 快如闪电的日志分析 ([Logs Perf. Benchmark](https://signoz.io/blog/logs-performance-benchmark/))
+
+- 可视化点到点的基础设施性能,提取有所有类型机器的 metrics 数据
+
+- 轻易自定义告警查询
-
+## 为什么使用 SigNoz?
-## 为何选择SigNoz?
+作为开发者, 我们发现 SaaS 厂商对一些大家想要的小功能都是闭源的,这种行为真的让人有点恼火。 闭源厂商还会在月底给你一张没有明细的巨额账单。
-作为开发人员,我们发现依赖闭源的SaaS厂商提供的每个小功能有些麻烦,闭源厂商通常会给你一份巨额月付账单,但不提供足够的透明度,你不知道你为哪些功能付费。
+我们想做一个自托管并且可开源的工具,像 DataDog 和 NewRelic 那样, 为那些担心数据隐私和安全的公司提供第三方服务。
-我们想做一个自服务的开源版本的工具,类似于DataDog和NewRelic,用于那些对客户数据流入第三方有隐私和安全担忧的厂商。
+作为开源的项目,你完全可以自己掌控你的配置、样本和更新。你同样可以基于 SigNoz 拓展特定的业务模块。
-开源也让你对配置、采样和正常运行时间有完整的控制,你可以在SigNoz基础上构建模块来满足特定的商业需求。
+### 支持的编程语言:
-### 语言支持
-
-我们支持[OpenTelemetry](https://opentelemetry.io)库,你可以使用它来装备应用。也就是说SigNoz支持任何支持OpenTelemetry库的框架和语言。 主要支持语言包括:
+我们支持 [OpenTelemetry](https://opentelemetry.io)。作为一个观测你应用的库文件。所以任何 OpenTelemetry 支持的框架和语言,对于 SigNoz 也同样支持。 一些主要支持的语言如下:
- Java
- Python
- NodeJS
- Go
+- PHP
+- .NET
+- Ruby
+- Elixir
+- Rust
-你可以在这个文档里找到完整的语言列表 - https://opentelemetry.io/docs/
+你可以在这里找到全部支持的语言列表 - https://opentelemetry.io/docs/
-
+## 让我们开始吧
-## 入门
+### 使用 Docker 部署
+请一步步跟随 [这里](https://signoz.io/docs/install/docker/) 通过 docker 来安装。
-### 使用Docker部署
-
-请按照[这里](https://signoz.io/docs/install/docker/)列出的步骤使用Docker来安装
-
-如果你遇到任何问题,这个[排查指南](https://signoz.io/docs/install/troubleshooting/)会对你有帮助。
+这个 [排障说明书](https://signoz.io/docs/install/troubleshooting/) 可以帮助你解决碰到的问题。
 
+### 使用 Helm 在 Kubernetes 部署
-### 使用Helm在Kubernetes上部署
-
-请跟着[这里](https://signoz.io/docs/deployment/helm_chart)的步骤使用helm charts安装
+请一步步跟随 [这里](https://signoz.io/docs/deployment/helm_chart) 通过 helm 来安装
-
-
-## 与其他方案的比较
+## 比较相似的工具
### SigNoz vs Prometheus
-如果你只是需要监控指标(metrics),那Prometheus是不错的,但如果你要无缝的在metrics和traces之间切换,那目前把Prometheus & Jaeger串起来的体验并不好。
+Prometheus 是一个针对 metrics 监控的强大工具。但是如果你想无缝的切换 metrics 和 traces 查询,你当前大概率需要在 Prometheus 和 Jaeger 之间切换。
-我们的目标是为metrics和traces提供统一的UI - 类似于Datadog这样的Saas厂提供的方案。并且能够对trace进行过滤和聚合,这是目前Jaeger缺失的功能。
+我们的目标是提供一个客户观测 metrics 和 traces 整合的 UI。就像 SaaS 供应商 DataDog,它提供很多 jaeger 缺失的功能,比如针对 traces 过滤功能和聚合功能。
 
### SigNoz vs Jaeger
-Jaeger只做分布式追踪(distributed tracing),SigNoz则支持metrics,traces,logs ,即可视化的三大支柱。
+Jaeger 仅仅是一个分布式追踪系统。 但是 SigNoz 可以提供 metrics, traces 和 logs 所有的观测。
-并且SigNoz有一些Jaeger没有的高级功能:
+而且, SigNoz 相较于 Jaeger 拥有更对的高级功能:
-- Jaegar UI无法在traces或过滤的traces上展示metrics。
-- Jaeger不能对过滤的traces做聚合操作。例如,拥有tag为customer_type='premium'的所有请求的p99延迟。而这个功能在SigNoz这儿是很容易实现。
+- Jaegar UI 不能提供任何基于 traces 的 metrics 查询和过滤。
+
+- Jaeger 不能针对过滤的 traces 做聚合。 比如, p99 延迟的请求有个标签是 customer_type='premium'。 而这些在 SigNoz 可以轻松做到。
+
+ 
+
+### SigNoz vs Elastic
+
+- SigNoz 的日志管理是基于 ClickHouse 实现的,可以使日志的聚合更加高效,因为它是基于 OLAP 的数据仓储。
+
+- 与 Elastic 相比,可以节省 50% 的资源成本
+
+我们已经公布了 Elastic 和 SigNoz 的性能对比。 请点击 [这里](https://signoz.io/blog/logs-performance-benchmark/?utm_source=github-readme&utm_medium=logs-benchmark)
+
+ 
+
+### SigNoz vs Loki
+
+- SigNoz 支持大容量高基数的聚合,但是 loki 是不支持的。
+
+- SigNoz 支持索引的高基数查询,并且对索引没有数量限制,而 Loki 会在添加部分索引后到达最大上限。
+
+- 相较于 SigNoz,Loki 在搜索大量数据下既困难又缓慢。
+
+我们已经发布了基准测试对比 Loki 和 SigNoz 性能。请点击 [这里](https://signoz.io/blog/logs-performance-benchmark/?utm_source=github-readme&utm_medium=logs-benchmark)
-
-
## 贡献
+我们 ❤️ 你的贡献,无论大小。 请先阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 再开始给 SigNoz 做贡献。
-我们 ❤️ 任何贡献无论大小。 请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 然后开始给Signoz做贡献。
+如果你不知道如何开始? 只需要在 [slack 社区](https://signoz.io/slack) 通过 `#contributing` 频道联系我们。
-还不清楚怎么开始? 只需在[slack社区](https://signoz.io/slack)的`#contributing`频道里ping我们。
+### 项目维护人员
-### Project maintainers
-
-#### Backend
+#### 后端
- [Ankit Nayan](https://github.com/ankitnayan)
- [Nityananda Gohain](https://github.com/nityanandagohain)
- [Srikanth Chekuri](https://github.com/srikanthccv)
- [Vishal Sharma](https://github.com/makeavish)
-#### Frontend
+#### 前端
- [Palash Gupta](https://github.com/palashgdev)
-#### DevOps
+#### 运维开发
- [Prashant Shahi](https://github.com/prashant-shahi)
-
-
## 文档
-文档在这里:https://signoz.io/docs/. 如果你觉得有任何不清楚或者有文档缺失,请在Github里发一个问题,并使用标签 `documentation` 或者在社区stack频道里告诉我们。
+你可以通过 https://signoz.io/docs/ 找到相关文档。如果你需要阐述问题或者发现一些确实的事件, 通过标签为 `documentation` 提交 Github 问题。或者通过 slack 社区频道。
-
-
## 社区
-加入[slack community](https://signoz.io/slack),了解更多关于分布式跟踪、可观察性(observability),以及SigNoz。同时与其他用户和贡献者一起交流。
+加入 [slack 社区](https://signoz.io/slack) 去了解更多关于分布式追踪、可观测性系统 。或者与 SigNoz 其他用户和贡献者交流。
-如果你有任何想法、问题或者反馈,请在[Github Discussions](https://github.com/SigNoz/signoz/discussions)分享给我们。
+如果你有任何想法、问题、或者任何反馈, 请通过 [Github Discussions](https://github.com/SigNoz/signoz/discussions) 分享。
-最后,感谢我们这些优秀的贡献者们。
+不管怎么样,感谢这个项目的所有贡献者!
-
-
-
From 587034f573d7a8e47b98dfa9b3a885c7135dd3b7 Mon Sep 17 00:00:00 2001
From: Rajat Dabade
Date: Fri, 6 Oct 2023 15:10:13 +0530
Subject: [PATCH 11/34] [Refactor]: graph manager to scss and fix the height
issue (#3671)
* refactor: graph manager to scss and fix the height issue
* refactor: updated scss
---
.../Graph/FullView/GraphManager.styles.scss | 21 +++++++++++++
.../Graph/FullView/GraphManager.tsx | 30 ++++++++-----------
.../GridGraphLayout/Graph/FullView/styles.ts | 20 -------------
3 files changed, 34 insertions(+), 37 deletions(-)
create mode 100644 frontend/src/container/GridGraphLayout/Graph/FullView/GraphManager.styles.scss
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/GraphManager.styles.scss b/frontend/src/container/GridGraphLayout/Graph/FullView/GraphManager.styles.scss
new file mode 100644
index 0000000000..2d594aa8a9
--- /dev/null
+++ b/frontend/src/container/GridGraphLayout/Graph/FullView/GraphManager.styles.scss
@@ -0,0 +1,21 @@
+.graph-manager-container {
+ margin-top: 1.25rem;
+ display: flex;
+ align-items: flex-end;
+ overflow-x: scroll;
+
+ .filter-table-container {
+ flex-basis: 80%;
+ }
+
+ .save-cancel-container {
+ flex-basis: 20%;
+ display: flex;
+ justify-content: flex-end;
+ }
+
+ .save-cancel-button {
+ margin: 0 0.313rem;
+ }
+
+}
\ No newline at end of file
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/GraphManager.tsx b/frontend/src/container/GridGraphLayout/Graph/FullView/GraphManager.tsx
index 61abbf2aa6..31f528a410 100644
--- a/frontend/src/container/GridGraphLayout/Graph/FullView/GraphManager.tsx
+++ b/frontend/src/container/GridGraphLayout/Graph/FullView/GraphManager.tsx
@@ -1,3 +1,5 @@
+import './GraphManager.styles.scss';
+
import { Button, Input } from 'antd';
import { CheckboxChangeEvent } from 'antd/es/checkbox';
import { ResizeTable } from 'components/ResizeTable';
@@ -8,12 +10,6 @@ import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { eventEmitter } from 'utils/getEventEmitter';
import { getGraphVisibilityStateOnDataChange } from '../utils';
-import {
- FilterTableAndSaveContainer,
- FilterTableContainer,
- SaveCancelButtonContainer,
- SaveContainer,
-} from './styles';
import { getGraphManagerTableColumns } from './TableRender/GraphManagerColumns';
import { ExtendedChartDataset, GraphManagerProps } from './types';
import {
@@ -169,30 +165,30 @@ function GraphManager({
const dataSource = tableDataSet.filter((item) => item.show);
return (
-
-
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
+
+
+
);
}
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/styles.ts b/frontend/src/container/GridGraphLayout/Graph/FullView/styles.ts
index 9e5bd09541..b73a2e9112 100644
--- a/frontend/src/container/GridGraphLayout/Graph/FullView/styles.ts
+++ b/frontend/src/container/GridGraphLayout/Graph/FullView/styles.ts
@@ -31,26 +31,6 @@ export const GraphContainer = styled.div`
isGraphLegendToggleAvailable ? '50%' : '100%'};
`;
-export const FilterTableAndSaveContainer = styled.div`
- margin-top: 1.875rem;
- display: flex;
- align-items: flex-end;
-`;
-
-export const FilterTableContainer = styled.div`
- flex-basis: 80%;
-`;
-
-export const SaveContainer = styled.div`
- flex-basis: 20%;
- display: flex;
- justify-content: flex-end;
-`;
-
-export const SaveCancelButtonContainer = styled.span`
- margin: 0 0.313rem;
-`;
-
export const LabelContainer = styled.button`
max-width: 18.75rem;
cursor: pointer;
From 0e04b779a94740e741fc0e49800c3633b76e8030 Mon Sep 17 00:00:00 2001
From: Palash Gupta
Date: Fri, 6 Oct 2023 17:32:17 +0530
Subject: [PATCH 12/34] feat: lowercase operators support in the where clause
is updated (#3657)
* feat: lowercase operators suuport in the where clause is updated
* feat: options is now updated
* chore: log message is updated
* chore: auto completed is updated
* chore: tagRegex is updated
* feat: update regex to math operators and text operators
* chore: operator is updated
* chore: options is updated
---------
Co-authored-by: Yunus A M
---
frontend/src/constants/queryBuilder.ts | 12 +++
.../filters/QueryBuilderSearch/utils.ts | 79 +++++--------------
.../src/hooks/queryBuilder/useAutoComplete.ts | 6 +-
.../src/hooks/queryBuilder/useOperatorType.ts | 12 +++
frontend/src/hooks/queryBuilder/useOptions.ts | 36 +++++----
.../useSetCurrentKeyAndOperator.ts | 34 +++-----
frontend/src/hooks/queryBuilder/useTag.ts | 1 +
7 files changed, 81 insertions(+), 99 deletions(-)
diff --git a/frontend/src/constants/queryBuilder.ts b/frontend/src/constants/queryBuilder.ts
index c53873bc5c..2ab6d365fd 100644
--- a/frontend/src/constants/queryBuilder.ts
+++ b/frontend/src/constants/queryBuilder.ts
@@ -278,23 +278,35 @@ export const QUERY_BUILDER_SEARCH_VALUES = {
export const OPERATORS = {
IN: 'IN',
+ in: 'in',
NIN: 'NOT_IN',
+ not_in: 'not_in',
LIKE: 'LIKE',
+ like: 'like',
NLIKE: 'NOT_LIKE',
+ not_like: 'not_like',
REGEX: 'REGEX',
+ regex: 'regex',
NREGEX: 'NOT_REGEX',
+ nregex: 'not_regex',
'=': '=',
'!=': '!=',
EXISTS: 'EXISTS',
+ exists: 'exists',
NOT_EXISTS: 'NOT_EXISTS',
+ not_exists: 'not_exists',
CONTAINS: 'CONTAINS',
+ contains: 'contains',
NOT_CONTAINS: 'NOT_CONTAINS',
+ not_contains: 'not_contains',
'>=': '>=',
'>': '>',
'<=': '<=',
'<': '<',
HAS: 'HAS',
+ has: 'has',
NHAS: 'NHAS',
+ nhas: 'nhas',
};
export const QUERY_BUILDER_OPERATORS_BY_TYPES = {
diff --git a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/utils.ts b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/utils.ts
index 1859254fc4..dbe95d631f 100644
--- a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/utils.ts
+++ b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/utils.ts
@@ -3,25 +3,42 @@ import { parse } from 'papaparse';
import { orderByValueDelimiter } from '../OrderByFilter/utils';
+const operators = /=|!=|>=|>|<=|<$/;
+
// eslint-disable-next-line no-useless-escape
-export const tagRegexp = /^\s*(.*?)\s*(IN|NOT_IN|LIKE|NOT_LIKE|REGEX|NOT_REGEX|=|!=|EXISTS|NOT_EXISTS|CONTAINS|NOT_CONTAINS|>=|>|<=|<|HAS|NHAS)\s*(.*)$/g;
+export const tagRegexpV1 = /^\s*(.*?)\s*(IN|in|NOT_IN|nin|LIKE|like|NOT_LIKE|nlike|REGEX|regex|NOT_REGEX|nregex|=|!=|EXISTS|exists|NOT_EXISTS|nexists|CONTAINS|contains|NOT_CONTAINS|ncontains|>=|>|<=|<|HAS|has|NHAS|nhas)\s*(.*)$/g;
+
+export const tagRegexpV2 = /^\s*(.+?)\s+(IN|in|NOT_IN|nin|LIKE|like|NOT_LIKE|nlike|REGEX|regex|NOT_REGEX|nregex|EXISTS|exists|NOT_EXISTS|nexists|CONTAINS|contains|NOT_CONTAINS|ncontains|HAS|has|NHAS|nhas|=|!=|>=|>|<=|<)\s*(.*)$/g;
export function isInNInOperator(value: string): boolean {
return value === OPERATORS.IN || value === OPERATORS.NIN;
}
+function endsWithOperator(inputString: string): boolean {
+ return operators.test(inputString);
+}
+
interface ITagToken {
tagKey: string;
tagOperator: string;
tagValue: string[];
}
+export function getMatchRegex(str: string): RegExp {
+ if (endsWithOperator(str)) {
+ return tagRegexpV1;
+ }
+
+ return tagRegexpV2;
+}
+
export function getTagToken(tag: string): ITagToken {
- const matches = tag?.matchAll(tagRegexp);
+ const matches = tag?.matchAll(getMatchRegex(tag));
const [match] = matches ? Array.from(matches) : [];
if (match) {
const [, matchTagKey, matchTagOperator, matchTagValue] = match;
+
return {
tagKey: matchTagKey,
tagOperator: matchTagOperator,
@@ -51,65 +68,11 @@ export function getRemovePrefixFromKey(tag: string): string {
}
export function getOperatorValue(op: string): string {
- switch (op) {
- case 'IN':
- return 'in';
- case 'NOT_IN':
- return 'nin';
- case OPERATORS.REGEX:
- return 'regex';
- case OPERATORS.HAS:
- return 'has';
- case OPERATORS.NHAS:
- return 'nhas';
- case OPERATORS.NREGEX:
- return 'nregex';
- case 'LIKE':
- return 'like';
- case 'NOT_LIKE':
- return 'nlike';
- case 'EXISTS':
- return 'exists';
- case 'NOT_EXISTS':
- return 'nexists';
- case 'CONTAINS':
- return 'contains';
- case 'NOT_CONTAINS':
- return 'ncontains';
- default:
- return op;
- }
+ return op.toLocaleLowerCase();
}
export function getOperatorFromValue(op: string): string {
- switch (op) {
- case 'in':
- return 'IN';
- case 'nin':
- return 'NOT_IN';
- case 'like':
- return 'LIKE';
- case 'regex':
- return OPERATORS.REGEX;
- case 'nregex':
- return OPERATORS.NREGEX;
- case 'nlike':
- return 'NOT_LIKE';
- case 'exists':
- return 'EXISTS';
- case 'nexists':
- return 'NOT_EXISTS';
- case 'contains':
- return 'CONTAINS';
- case 'ncontains':
- return 'NOT_CONTAINS';
- case 'has':
- return OPERATORS.HAS;
- case 'nhas':
- return OPERATORS.NHAS;
- default:
- return op;
- }
+ return op.toLocaleLowerCase();
}
export function replaceStringWithMaxLength(
diff --git a/frontend/src/hooks/queryBuilder/useAutoComplete.ts b/frontend/src/hooks/queryBuilder/useAutoComplete.ts
index dad262757a..9562f5e4d7 100644
--- a/frontend/src/hooks/queryBuilder/useAutoComplete.ts
+++ b/frontend/src/hooks/queryBuilder/useAutoComplete.ts
@@ -1,8 +1,8 @@
import {
+ getMatchRegex,
getRemovePrefixFromKey,
getTagToken,
replaceStringWithMaxLength,
- tagRegexp,
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
import { Option } from 'container/QueryBuilder/type';
import { parse } from 'papaparse';
@@ -33,7 +33,7 @@ export const useAutoComplete = (
searchKey,
);
- const [key, operator, result] = useSetCurrentKeyAndOperator(searchValue, keys);
+ const [key, operator, result] = useSetCurrentKeyAndOperator(searchValue);
const handleSearch = (value: string): void => {
const prefixFreeValue = getRemovePrefixFromKey(getTagToken(value).tagKey);
@@ -58,7 +58,7 @@ export const useAutoComplete = (
(value: string): void => {
if (isMulti) {
setSearchValue((prev: string) => {
- const matches = prev?.matchAll(tagRegexp);
+ const matches = prev?.matchAll(getMatchRegex(prev));
const [match] = matches ? Array.from(matches) : [];
const [, , , matchTagValue] = match;
const data = parse(matchTagValue).data.flat();
diff --git a/frontend/src/hooks/queryBuilder/useOperatorType.ts b/frontend/src/hooks/queryBuilder/useOperatorType.ts
index 94de55df92..ff3a4ad115 100644
--- a/frontend/src/hooks/queryBuilder/useOperatorType.ts
+++ b/frontend/src/hooks/queryBuilder/useOperatorType.ts
@@ -8,23 +8,35 @@ export type OperatorType =
const operatorTypeMapper: Record = {
[OPERATORS.IN]: 'MULTIPLY_VALUE',
+ [OPERATORS.in]: 'MULTIPLY_VALUE',
[OPERATORS.NIN]: 'MULTIPLY_VALUE',
+ [OPERATORS.not_in]: 'MULTIPLY_VALUE',
[OPERATORS.EXISTS]: 'NON_VALUE',
+ [OPERATORS.exists]: 'NON_VALUE',
[OPERATORS.NOT_EXISTS]: 'NON_VALUE',
+ [OPERATORS.not_exists]: 'NON_VALUE',
[OPERATORS['<=']]: 'SINGLE_VALUE',
[OPERATORS['<']]: 'SINGLE_VALUE',
[OPERATORS['>=']]: 'SINGLE_VALUE',
[OPERATORS['>']]: 'SINGLE_VALUE',
[OPERATORS.LIKE]: 'SINGLE_VALUE',
+ [OPERATORS.like]: 'SINGLE_VALUE',
[OPERATORS.NLIKE]: 'SINGLE_VALUE',
+ [OPERATORS.not_like]: 'SINGLE_VALUE',
[OPERATORS.REGEX]: 'SINGLE_VALUE',
+ [OPERATORS.regex]: 'SINGLE_VALUE',
[OPERATORS.NREGEX]: 'SINGLE_VALUE',
+ [OPERATORS.nregex]: 'SINGLE_VALUE',
[OPERATORS.CONTAINS]: 'SINGLE_VALUE',
+ [OPERATORS.contains]: 'SINGLE_VALUE',
[OPERATORS.NOT_CONTAINS]: 'SINGLE_VALUE',
+ [OPERATORS.not_contains]: 'SINGLE_VALUE',
[OPERATORS['=']]: 'SINGLE_VALUE',
[OPERATORS['!=']]: 'SINGLE_VALUE',
[OPERATORS.HAS]: 'SINGLE_VALUE',
+ [OPERATORS.has]: 'SINGLE_VALUE',
[OPERATORS.NHAS]: 'SINGLE_VALUE',
+ [OPERATORS.nhas]: 'SINGLE_VALUE',
};
export const useOperatorType = (operator: string): OperatorType =>
diff --git a/frontend/src/hooks/queryBuilder/useOptions.ts b/frontend/src/hooks/queryBuilder/useOptions.ts
index 82dc2c1e24..548ea2de0b 100644
--- a/frontend/src/hooks/queryBuilder/useOptions.ts
+++ b/frontend/src/hooks/queryBuilder/useOptions.ts
@@ -81,8 +81,8 @@ export const useOptions = (
const getKeyOperatorOptions = useCallback(
(key: string) => {
const operatorsOptions = operators?.map((operator) => ({
- value: `${key} ${operator} `,
- label: `${key} ${operator} `,
+ value: `${key} ${operator.toLowerCase()} `,
+ label: `${key} ${operator.toLowerCase()} `,
}));
if (whereClauseConfig) {
return [
@@ -148,26 +148,28 @@ export const useOptions = (
return useMemo(
() =>
- (
- options.filter(
+ options
+ .filter(
(option, index, self) =>
index ===
self.findIndex(
(o) => o.label === option.label && o.value === option.value, // to remove duplicate & empty options from list
) && option.value !== '',
- ) || []
- ).map((option) => {
- const { tagValue } = getTagToken(searchValue);
- if (isMulti) {
- return {
- ...option,
- selected: tagValue
- .filter((i) => i.trim().replace(/^\s+/, '') === option.value)
- .includes(option.value),
- };
- }
- return option;
- }),
+ )
+ .map((option) => {
+ const { tagValue } = getTagToken(searchValue);
+ if (isMulti) {
+ return {
+ ...option,
+ selected: Array.isArray(tagValue)
+ ? tagValue
+ ?.filter((i) => i.trim().replace(/^\s+/, '') === option.value)
+ ?.includes(option.value)
+ : String(tagValue).includes(option.value),
+ };
+ }
+ return option;
+ }),
[isMulti, options, searchValue],
);
};
diff --git a/frontend/src/hooks/queryBuilder/useSetCurrentKeyAndOperator.ts b/frontend/src/hooks/queryBuilder/useSetCurrentKeyAndOperator.ts
index 2da205b349..afaaa8ccab 100644
--- a/frontend/src/hooks/queryBuilder/useSetCurrentKeyAndOperator.ts
+++ b/frontend/src/hooks/queryBuilder/useSetCurrentKeyAndOperator.ts
@@ -1,32 +1,24 @@
-import {
- getRemovePrefixFromKey,
- getTagToken,
-} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
-import { useMemo } from 'react';
-import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
+import { getTagToken } from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
+import { useMemo, useRef } from 'react';
type ICurrentKeyAndOperator = [string, string, string[]];
export const useSetCurrentKeyAndOperator = (
value: string,
- keys: BaseAutocompleteData[],
): ICurrentKeyAndOperator => {
- const [key, operator, result] = useMemo(() => {
- let key = '';
- let operator = '';
+ const keyRef = useRef('');
+ const operatorRef = useRef('');
+
+ const result = useMemo(() => {
let result: string[] = [];
const { tagKey, tagOperator, tagValue } = getTagToken(value);
- const isSuggestKey = keys?.some(
- (el) => el?.key === getRemovePrefixFromKey(tagKey),
- );
- if (isSuggestKey || keys.length === 0) {
- key = tagKey || '';
- operator = tagOperator || '';
- result = tagValue || [];
- }
- return [key, operator, result];
- }, [value, keys]);
+ keyRef.current = tagKey || '';
+ operatorRef.current = tagOperator || '';
+ result = tagValue || [];
- return [key, operator, result];
+ return result;
+ }, [value]);
+
+ return [keyRef.current, operatorRef.current, result];
};
diff --git a/frontend/src/hooks/queryBuilder/useTag.ts b/frontend/src/hooks/queryBuilder/useTag.ts
index 268a01e0c6..45a9417403 100644
--- a/frontend/src/hooks/queryBuilder/useTag.ts
+++ b/frontend/src/hooks/queryBuilder/useTag.ts
@@ -74,6 +74,7 @@ export const useTag = (
const handleAddTag = useCallback(
(value: string): void => {
const { tagKey } = getTagToken(value);
+
const [key, id] = tagKey.split('-');
if (id === 'custom') {
From e12aef136a44e270bf4db410ee3b38906ec5ba97 Mon Sep 17 00:00:00 2001
From: Prashant Shahi
Date: Sat, 7 Oct 2023 21:23:53 +0545
Subject: [PATCH 13/34] perf(query-service): :hammer: improve backend build
time (#3658)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* perf(query-service): :hammer: improve backend build time
* chore(query-service): 🔧 address comments on image build time
---------
Signed-off-by: Prashant Shahi
---
Makefile | 69 ++++++++++++-------
.../docker-compose-local.yaml | 6 +-
ee/query-service/Dockerfile | 37 +++-------
pkg/query-service/Dockerfile | 38 ++--------
4 files changed, 63 insertions(+), 87 deletions(-)
diff --git a/Makefile b/Makefile
index 0d46bfa161..01ac0e33ab 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,7 @@ BUILD_HASH ?= $(shell git rev-parse --short HEAD)
BUILD_TIME ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
BUILD_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD)
DEV_LICENSE_SIGNOZ_IO ?= https://staging-license.signoz.io/api/v1
+DEV_BUILD ?= "" # set to any non-empty value to enable dev build
# Internal variables or constants.
FRONTEND_DIRECTORY ?= frontend
@@ -15,15 +16,15 @@ QUERY_SERVICE_DIRECTORY ?= pkg/query-service
EE_QUERY_SERVICE_DIRECTORY ?= ee/query-service
STANDALONE_DIRECTORY ?= deploy/docker/clickhouse-setup
SWARM_DIRECTORY ?= deploy/docker-swarm/clickhouse-setup
-LOCAL_GOOS ?= $(shell go env GOOS)
-LOCAL_GOARCH ?= $(shell go env GOARCH)
+
+GOOS ?= $(shell go env GOOS)
+GOARCH ?= $(shell go env GOARCH)
+GOPATH ?= $(shell go env GOPATH)
REPONAME ?= signoz
DOCKER_TAG ?= $(subst v,,$(BUILD_VERSION))
-
FRONTEND_DOCKER_IMAGE ?= frontend
QUERY_SERVICE_DOCKER_IMAGE ?= query-service
-DEV_BUILD ?= ""
# Build-time Go variables
PACKAGE?=go.signoz.io/signoz
@@ -69,49 +70,71 @@ build-push-frontend: build-frontend-static
docker buildx build --file Dockerfile --progress plain --push --platform linux/arm64,linux/amd64 \
--tag $(REPONAME)/$(FRONTEND_DOCKER_IMAGE):$(DOCKER_TAG) .
+# Steps to build static binary of query service
+.PHONY: build-query-service-static
+build-query-service-static:
+ @echo "------------------"
+ @echo "--> Building query-service static binary"
+ @echo "------------------"
+ @if [ $(DEV_BUILD) != "" ]; then \
+ cd $(QUERY_SERVICE_DIRECTORY) && \
+ CGO_ENABLED=1 go build -tags timetzdata -a -o ./bin/query-service-${GOOS}-${GOARCH} \
+ -ldflags "-linkmode external -extldflags '-static' -s -w ${LD_FLAGS} ${DEV_LD_FLAGS}"; \
+ else \
+ cd $(QUERY_SERVICE_DIRECTORY) && \
+ CGO_ENABLED=1 go build -tags timetzdata -a -o ./bin/query-service-${GOOS}-${GOARCH} \
+ -ldflags "-linkmode external -extldflags '-static' -s -w ${LD_FLAGS}"; \
+ fi
+
+.PHONY: build-query-service-static-amd64
+build-query-service-static-amd64:
+ make GOARCH=amd64 build-query-service-static
+
+.PHONY: build-query-service-static-arm64
+build-query-service-static-arm64:
+ make CC=aarch64-linux-gnu-gcc GOARCH=arm64 build-query-service-static
+
+# Steps to build static binary of query service for all platforms
+.PHONY: build-query-service-static-all
+build-query-service-static-all: build-query-service-static-amd64 build-query-service-static-arm64
+
# Steps to build and push docker image of query service
-.PHONY: build-query-service-amd64 build-push-query-service
+.PHONY: build-query-service-amd64 build-push-query-service
# Step to build docker image of query service in amd64 (used in build pipeline)
-build-query-service-amd64:
+build-query-service-amd64: build-query-service-static-amd64
@echo "------------------"
@echo "--> Building query-service docker image for amd64"
@echo "------------------"
@docker build --file $(QUERY_SERVICE_DIRECTORY)/Dockerfile \
- -t $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) \
- --build-arg TARGETPLATFORM="linux/amd64" --build-arg LD_FLAGS="$(LD_FLAGS)" .
+ --tag $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) \
+ --build-arg TARGETPLATFORM="linux/amd64" .
# Step to build and push docker image of query in amd64 and arm64 (used in push pipeline)
-build-push-query-service:
+build-push-query-service: build-query-service-static-all
@echo "------------------"
@echo "--> Building and pushing query-service docker image"
@echo "------------------"
@docker buildx build --file $(QUERY_SERVICE_DIRECTORY)/Dockerfile --progress plain \
- --push --platform linux/arm64,linux/amd64 --build-arg LD_FLAGS="$(LD_FLAGS)" \
+ --push --platform linux/arm64,linux/amd64 \
--tag $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) .
# Step to build EE docker image of query service in amd64 (used in build pipeline)
-build-ee-query-service-amd64:
+build-ee-query-service-amd64: build-query-service-static-amd64
@echo "------------------"
@echo "--> Building query-service docker image for amd64"
@echo "------------------"
- @if [ $(DEV_BUILD) != "" ]; then \
- docker build --file $(EE_QUERY_SERVICE_DIRECTORY)/Dockerfile \
- -t $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) \
- --build-arg TARGETPLATFORM="linux/amd64" --build-arg LD_FLAGS="${LD_FLAGS} ${DEV_LD_FLAGS}" .; \
- else \
- docker build --file $(EE_QUERY_SERVICE_DIRECTORY)/Dockerfile \
- -t $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) \
- --build-arg TARGETPLATFORM="linux/amd64" --build-arg LD_FLAGS="$(LD_FLAGS)" .; \
- fi
+ @docker build --file $(EE_QUERY_SERVICE_DIRECTORY)/Dockerfile \
+ --tag $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) \
+ --build-arg TARGETPLATFORM="linux/amd64" .
# Step to build and push EE docker image of query in amd64 and arm64 (used in push pipeline)
-build-push-ee-query-service:
+build-push-ee-query-service: build-query-service-static-all
@echo "------------------"
@echo "--> Building and pushing query-service docker image"
@echo "------------------"
@docker buildx build --file $(EE_QUERY_SERVICE_DIRECTORY)/Dockerfile \
--progress plain --push --platform linux/arm64,linux/amd64 \
- --build-arg LD_FLAGS="$(LD_FLAGS)" --tag $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) .
+ --tag $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) .
dev-setup:
mkdir -p /var/lib/signoz
@@ -122,7 +145,7 @@ dev-setup:
@echo "------------------"
run-local:
- @LOCAL_GOOS=$(LOCAL_GOOS) LOCAL_GOARCH=$(LOCAL_GOARCH) docker-compose -f \
+ @docker-compose -f \
$(STANDALONE_DIRECTORY)/docker-compose-core.yaml -f $(STANDALONE_DIRECTORY)/docker-compose-local.yaml \
up --build -d
diff --git a/deploy/docker/clickhouse-setup/docker-compose-local.yaml b/deploy/docker/clickhouse-setup/docker-compose-local.yaml
index 2c7b9a5c46..78aa72ff75 100644
--- a/deploy/docker/clickhouse-setup/docker-compose-local.yaml
+++ b/deploy/docker/clickhouse-setup/docker-compose-local.yaml
@@ -8,7 +8,7 @@ services:
dockerfile: "./Dockerfile"
args:
LDFLAGS: ""
- TARGETPLATFORM: "${LOCAL_GOOS}/${LOCAL_GOARCH}"
+ TARGETPLATFORM: "${GOOS}/${GOARCH}"
container_name: signoz-query-service
environment:
- ClickHouseUrl=tcp://clickhouse:9000
@@ -52,8 +52,8 @@ services:
context: "../../../frontend"
dockerfile: "./Dockerfile"
args:
- TARGETOS: "${LOCAL_GOOS}"
- TARGETPLATFORM: "${LOCAL_GOARCH}"
+ TARGETOS: "${GOOS}"
+ TARGETPLATFORM: "${GOARCH}"
container_name: signoz-frontend
environment:
- FRONTEND_API_ENDPOINT=http://query-service:8080
diff --git a/ee/query-service/Dockerfile b/ee/query-service/Dockerfile
index 258b0869f7..09e2701aa5 100644
--- a/ee/query-service/Dockerfile
+++ b/ee/query-service/Dockerfile
@@ -1,43 +1,23 @@
-FROM golang:1.21-bookworm AS builder
-
-# LD_FLAGS is passed as argument from Makefile. It will be empty, if no argument passed
-ARG LD_FLAGS
-ARG TARGETPLATFORM
-
-ENV CGO_ENABLED=1
-ENV GOPATH=/go
-
-RUN export GOOS=$(echo ${TARGETPLATFORM} | cut -d / -f1) && \
- export GOARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2)
-
-# Prepare and enter src directory
-WORKDIR /go/src/github.com/signoz/signoz
-
-# Add the sources and proceed with build
-ADD . .
-RUN cd ee/query-service \
- && go build -tags timetzdata -a -o ./bin/query-service \
- -ldflags "-linkmode external -extldflags '-static' -s -w $LD_FLAGS" \
- && chmod +x ./bin/query-service
-
-
# use a minimal alpine image
-FROM alpine:3.16.7
+FROM alpine:3.17
# Add Maintainer Info
LABEL maintainer="signoz"
+# define arguments that can be passed during build time
+ARG TARGETOS TARGETARCH
+
# add ca-certificates in case you need them
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
# set working directory
WORKDIR /root
-# copy the binary from builder
-COPY --from=builder /go/src/github.com/signoz/signoz/ee/query-service/bin/query-service .
+# copy the query-service binary
+COPY ee/pkg/query-service/bin/query-service-${TARGETOS}-${TARGETARCH} /root/query-service
# copy prometheus YAML config
-COPY pkg/query-service/config/prometheus.yml /root/config/prometheus.yml
+COPY ee/pkg/query-service/config/prometheus.yml /root/config/prometheus.yml
# Make query-service executable for non-root users
RUN chmod 755 /root /root/query-service
@@ -45,7 +25,6 @@ RUN chmod 755 /root /root/query-service
# run the binary
ENTRYPOINT ["./query-service"]
-CMD ["-config", "../config/prometheus.yml"]
-# CMD ["./query-service -config /root/config/prometheus.yml"]
+CMD ["-config", "/root/config/prometheus.yml"]
EXPOSE 8080
diff --git a/pkg/query-service/Dockerfile b/pkg/query-service/Dockerfile
index e616ad5d12..9d62c5cc62 100644
--- a/pkg/query-service/Dockerfile
+++ b/pkg/query-service/Dockerfile
@@ -1,45 +1,20 @@
-FROM golang:1.21-bookworm AS builder
-
-# LD_FLAGS is passed as argument from Makefile. It will be empty, if no argument passed
-ARG LD_FLAGS
-ARG TARGETPLATFORM
-
-ENV CGO_ENABLED=1
-ENV GOPATH=/go
-
-RUN export GOOS=$(echo ${TARGETPLATFORM} | cut -d / -f1) && \
- export GOARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2)
-
-# Prepare and enter src directory
-WORKDIR /go/src/github.com/signoz/signoz
-
-# Cache dependencies
-ADD go.mod .
-ADD go.sum .
-RUN go mod download -x
-
-# Add the sources and proceed with build
-ADD . .
-RUN cd pkg/query-service \
- && go build -tags timetzdata -a -o ./bin/query-service \
- -ldflags "-linkmode external -extldflags '-static' -s -w $LD_FLAGS" \
- && chmod +x ./bin/query-service
-
-
# use a minimal alpine image
-FROM alpine:3.16.7
+FROM alpine:3.17
# Add Maintainer Info
LABEL maintainer="signoz"
+# define arguments that can be passed during build time
+ARG TARGETOS TARGETARCH
+
# add ca-certificates in case you need them
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
# set working directory
WORKDIR /root
-# copy the binary from builder
-COPY --from=builder /go/src/github.com/signoz/signoz/pkg/query-service/bin/query-service .
+# copy the query-service binary
+COPY pkg/query-service/bin/query-service-${TARGETOS}-${TARGETARCH} /root/query-service
# copy prometheus YAML config
COPY pkg/query-service/config/prometheus.yml /root/config/prometheus.yml
@@ -51,6 +26,5 @@ RUN chmod 755 /root /root/query-service
ENTRYPOINT ["./query-service"]
CMD ["-config", "/root/config/prometheus.yml"]
-# CMD ["./query-service -config /root/config/prometheus.yml"]
EXPOSE 8080
From d7d4000240695c35a8d8a3b335b0eecd0796c823 Mon Sep 17 00:00:00 2001
From: Prashant Shahi
Date: Sun, 8 Oct 2023 00:44:39 +0545
Subject: [PATCH 14/34] =?UTF-8?q?chore(query-service):=20=F0=9F=94=A7=20up?=
=?UTF-8?q?date=20workflows=20and=20build=20files=20as=20per=20optimizatio?=
=?UTF-8?q?n=20changes=20(#3686)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Prashant Shahi
---
.github/workflows/push.yaml | 10 ++++++++++
.github/workflows/staging-deployment.yaml | 1 +
.github/workflows/testing-deployment.yaml | 1 +
Makefile | 12 ++++--------
ee/query-service/Dockerfile | 4 ++--
5 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml
index 98ee1e0fc4..a4c03d014c 100644
--- a/.github/workflows/push.yaml
+++ b/.github/workflows/push.yaml
@@ -42,6 +42,11 @@ jobs:
else
echo "DOCKER_TAG=${{ steps.branch-name.outputs.current_branch }}-oss" >> $GITHUB_ENV
fi
+ - name: Install cross-compilation tools
+ run: |
+ set -ex
+ sudo apt-get update
+ sudo apt-get install -y gcc-aarch64-linux-gnu musl-tools
- name: Build and push docker image
run: make build-push-query-service
@@ -77,6 +82,11 @@ jobs:
else
echo "DOCKER_TAG=${{ steps.branch-name.outputs.current_branch }}" >> $GITHUB_ENV
fi
+ - name: Install cross-compilation tools
+ run: |
+ set -ex
+ sudo apt-get update
+ sudo apt-get install -y gcc-aarch64-linux-gnu musl-tools
- name: Build and push docker image
run: make build-push-ee-query-service
diff --git a/.github/workflows/staging-deployment.yaml b/.github/workflows/staging-deployment.yaml
index 6de51f4733..21ea7a3c75 100644
--- a/.github/workflows/staging-deployment.yaml
+++ b/.github/workflows/staging-deployment.yaml
@@ -26,6 +26,7 @@ jobs:
echo "GITHUB_SHA: ${GITHUB_SHA}"
export DOCKER_TAG="${GITHUB_SHA:0:7}" # needed for child process to access it
export OTELCOL_TAG="main"
+ export PATH="/usr/local/go/bin/:$PATH" # needed for Golang to work
docker system prune --force
docker pull signoz/signoz-otel-collector:main
cd ~/signoz
diff --git a/.github/workflows/testing-deployment.yaml b/.github/workflows/testing-deployment.yaml
index d65a4e8bbc..799222ee3e 100644
--- a/.github/workflows/testing-deployment.yaml
+++ b/.github/workflows/testing-deployment.yaml
@@ -26,6 +26,7 @@ jobs:
echo "GITHUB_SHA: ${GITHUB_SHA}"
export DOCKER_TAG="${GITHUB_SHA:0:7}" # needed for child process to access it
export DEV_BUILD="1"
+ export PATH="/usr/local/go/bin/:$PATH" # needed for Golang to work
docker system prune --force
cd ~/signoz
git status
diff --git a/Makefile b/Makefile
index 01ac0e33ab..8e62b6cb5c 100644
--- a/Makefile
+++ b/Makefile
@@ -119,22 +119,18 @@ build-push-query-service: build-query-service-static-all
--tag $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) .
# Step to build EE docker image of query service in amd64 (used in build pipeline)
-build-ee-query-service-amd64: build-query-service-static-amd64
+build-ee-query-service-amd64:
@echo "------------------"
@echo "--> Building query-service docker image for amd64"
@echo "------------------"
- @docker build --file $(EE_QUERY_SERVICE_DIRECTORY)/Dockerfile \
- --tag $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) \
- --build-arg TARGETPLATFORM="linux/amd64" .
+ make QUERY_SERVICE_DIRECTORY=${EE_QUERY_SERVICE_DIRECTORY} build-query-service-amd64
# Step to build and push EE docker image of query in amd64 and arm64 (used in push pipeline)
-build-push-ee-query-service: build-query-service-static-all
+build-push-ee-query-service: build-ee-query-service-static-all
@echo "------------------"
@echo "--> Building and pushing query-service docker image"
@echo "------------------"
- @docker buildx build --file $(EE_QUERY_SERVICE_DIRECTORY)/Dockerfile \
- --progress plain --push --platform linux/arm64,linux/amd64 \
- --tag $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) .
+ make QUERY_SERVICE_DIRECTORY=${EE_QUERY_SERVICE_DIRECTORY} build-push-query-service
dev-setup:
mkdir -p /var/lib/signoz
diff --git a/ee/query-service/Dockerfile b/ee/query-service/Dockerfile
index 09e2701aa5..46b2186ec4 100644
--- a/ee/query-service/Dockerfile
+++ b/ee/query-service/Dockerfile
@@ -14,10 +14,10 @@ RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
WORKDIR /root
# copy the query-service binary
-COPY ee/pkg/query-service/bin/query-service-${TARGETOS}-${TARGETARCH} /root/query-service
+COPY ee/query-service/bin/query-service-${TARGETOS}-${TARGETARCH} /root/query-service
# copy prometheus YAML config
-COPY ee/pkg/query-service/config/prometheus.yml /root/config/prometheus.yml
+COPY pkg/query-service/config/prometheus.yml /root/config/prometheus.yml
# Make query-service executable for non-root users
RUN chmod 755 /root /root/query-service
From 9e91375632c7b61564bd59e3ce2bbd3a4c39edde Mon Sep 17 00:00:00 2001
From: Raj Kamal Singh <1133322+rkssisodiya@users.noreply.github.com>
Date: Sun, 8 Oct 2023 14:49:16 +0530
Subject: [PATCH 15/34] Logs pipeline editor - filter preview (#3683)
* feat: get started with Logs Filter Preview
* chore: rename PipelineFilterPreview -> PipelineFilterSummary
* chore: initial styles for pipeline filter preview
* feat: wire up logs fetching for pipeline filter preview
* feat: show empty preview if filter is empty
* feat: get logs preview table display started
* feat: use simple div + style based display for logs preview
* feat: log preview item expand action
* feat: move preview below filter and make filter last i/p in pipeline form
* feat: add duration selector for logs filter preview
* feat: add matched logs count to pipeline filter preview
* chore: reorganize preview logs list into its own file
* chore: cleanup
* chore: revert type export from useGetQueryRange.ts
* chore: get all tests passing
* chore: address review comments: import cloneDeep directly
* chore: address review comments: avoid inline handler func, return JSX.Element | null
* chore: address review comments: move preview interval selector helper into its own folder
* chore: address feedback: fix cloneDeep import
* chore: address feedback: avoid inline handler and remove eslint supression
---
.../index.tsx} | 35 +++++++--
.../FormFields/FilterInput/styles.scss | 3 +
.../Preview/LogsFilterPreview/index.tsx | 43 +++++++++++
.../Preview/LogsFilterPreview/styles.scss | 18 +++++
.../Preview/components/LogsList/index.tsx | 52 +++++++++++++
.../Preview/components/LogsList/styles.scss | 46 +++++++++++
.../components/LogsCountInInterval/index.tsx | 55 ++++++++++++++
.../LogsCountInInterval/styles.scss | 3 +
.../PreviewIntervalSelector/index.tsx | 45 +++++++++++
.../PreviewIntervalSelector/styles.scss | 4 +
.../Preview/components/SampleLogs/index.tsx | 76 +++++++++++++++++++
.../Preview/components/SampleLogs/styles.scss | 7 ++
.../index.tsx | 8 +-
.../styles.scss | 0
.../TableComponents/index.tsx | 4 +-
.../PipelinePage/PipelineListsView/config.ts | 16 ++--
.../TopNav/DateTimeSelection/config.ts | 6 +-
17 files changed, 398 insertions(+), 23 deletions(-)
rename frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/{FilterInput.tsx => FilterInput/index.tsx} (64%)
create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/FilterInput/styles.scss
create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/LogsFilterPreview/index.tsx
create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/LogsFilterPreview/styles.scss
create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/index.tsx
create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/styles.scss
create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/components/LogsCountInInterval/index.tsx
create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/components/LogsCountInInterval/styles.scss
create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/index.tsx
create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/styles.scss
create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/SampleLogs/index.tsx
create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/SampleLogs/styles.scss
rename frontend/src/container/PipelinePage/PipelineListsView/TableComponents/{PipelineFilterPreview => PipelineFilterSummary}/index.tsx (72%)
rename frontend/src/container/PipelinePage/PipelineListsView/TableComponents/{PipelineFilterPreview => PipelineFilterSummary}/styles.scss (100%)
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/FilterInput.tsx b/frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/FilterInput/index.tsx
similarity index 64%
rename from frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/FilterInput.tsx
rename to frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/FilterInput/index.tsx
index ef6b93f11c..94466aa197 100644
--- a/frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/FilterInput.tsx
+++ b/frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/FilterInput/index.tsx
@@ -1,3 +1,5 @@
+import './styles.scss';
+
import { Form } from 'antd';
import { initialQueryBuilderFormValuesMap } from 'constants/queryBuilder';
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
@@ -5,9 +7,10 @@ import isEqual from 'lodash-es/isEqual';
import { useTranslation } from 'react-i18next';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
-import { ProcessorFormField } from '../../AddNewProcessor/config';
-import { formValidationRules } from '../../config';
-import { FormLabelStyle } from '../styles';
+import { ProcessorFormField } from '../../../AddNewProcessor/config';
+import { formValidationRules } from '../../../config';
+import LogsFilterPreview from '../../../Preview/LogsFilterPreview';
+import { FormLabelStyle } from '../../styles';
function TagFilterInput({
value,
@@ -41,9 +44,27 @@ interface TagFilterInputProps {
placeholder: string;
}
+function TagFilterInputWithLogsResultPreview({
+ value,
+ onChange,
+ placeholder,
+}: TagFilterInputProps): JSX.Element {
+ return (
+ <>
+
+
+
+
+ >
+ );
+}
+
function FilterInput({ fieldData }: FilterInputProps): JSX.Element {
const { t } = useTranslation('pipeline');
-
return (
- {/* Antd form will supply value and onChange to here.
+ {/* Antd form will supply value and onChange here.
// @ts-ignore */}
-
+
);
}
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/FilterInput/styles.scss b/frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/FilterInput/styles.scss
new file mode 100644
index 0000000000..8508da2799
--- /dev/null
+++ b/frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/FilterInput/styles.scss
@@ -0,0 +1,3 @@
+.pipeline-filter-input-preview-container {
+ margin-top: 1rem;
+}
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/Preview/LogsFilterPreview/index.tsx b/frontend/src/container/PipelinePage/PipelineListsView/Preview/LogsFilterPreview/index.tsx
new file mode 100644
index 0000000000..c4bd7be438
--- /dev/null
+++ b/frontend/src/container/PipelinePage/PipelineListsView/Preview/LogsFilterPreview/index.tsx
@@ -0,0 +1,43 @@
+import './styles.scss';
+
+import { RelativeDurationOptions } from 'container/TopNav/DateTimeSelection/config';
+import { useState } from 'react';
+import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
+
+import PreviewIntervalSelector from '../components/PreviewIntervalSelector';
+import SampleLogs from '../components/SampleLogs';
+
+function LogsFilterPreview({ filter }: LogsFilterPreviewProps): JSX.Element {
+ const last1HourInterval = RelativeDurationOptions[3].value;
+ const [previewTimeInterval, setPreviewTimeInterval] = useState(
+ last1HourInterval,
+ );
+
+ const isEmptyFilter = (filter?.items?.length || 0) < 1;
+
+ return (
+
+
+
Filtered Logs Preview
+
+
+
+ {isEmptyFilter ? (
+
Please select a filter
+ ) : (
+
+ )}
+
+
+ );
+}
+
+interface LogsFilterPreviewProps {
+ filter: TagFilter;
+}
+
+export default LogsFilterPreview;
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/Preview/LogsFilterPreview/styles.scss b/frontend/src/container/PipelinePage/PipelineListsView/Preview/LogsFilterPreview/styles.scss
new file mode 100644
index 0000000000..725a12e59a
--- /dev/null
+++ b/frontend/src/container/PipelinePage/PipelineListsView/Preview/LogsFilterPreview/styles.scss
@@ -0,0 +1,18 @@
+.logs-filter-preview-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 8px;
+}
+
+.logs-filter-preview-content {
+ position: relative;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ width: 100%;
+ height: 8rem;
+ overflow: hidden;
+ border: 1px solid rgba(253, 253, 253, 0.12);
+}
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/index.tsx b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/index.tsx
new file mode 100644
index 0000000000..122d5e2b3a
--- /dev/null
+++ b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/index.tsx
@@ -0,0 +1,52 @@
+import './styles.scss';
+
+import { ExpandAltOutlined } from '@ant-design/icons';
+import LogDetail from 'components/LogDetail';
+import dayjs from 'dayjs';
+import { useActiveLog } from 'hooks/logs/useActiveLog';
+import { ILog } from 'types/api/logs/log';
+
+function LogsList({ logs }: LogsListProps): JSX.Element {
+ const {
+ activeLog,
+ onSetActiveLog,
+ onClearActiveLog,
+ onAddToQuery,
+ } = useActiveLog();
+
+ const makeLogDetailsHandler = (log: ILog) => (): void => onSetActiveLog(log);
+
+ return (
+
+ {logs.map((log) => (
+
+
+ {dayjs(String(log.timestamp)).format('MMM DD HH:mm:ss.SSS')}
+
+
{log.body}
+
+
+
+
+ ))}
+
+
+ );
+}
+
+interface LogsListProps {
+ logs: ILog[];
+}
+
+export default LogsList;
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/styles.scss b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/styles.scss
new file mode 100644
index 0000000000..80cec9ef28
--- /dev/null
+++ b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/styles.scss
@@ -0,0 +1,46 @@
+.logs-preview-list-container {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ align-items: stretch;
+
+ box-sizing: border-box;
+ padding: 0.25rem 0.5rem;
+}
+
+.logs-preview-list-item {
+ width: 100%;
+ position: relative;
+
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ flex-grow: 1;
+}
+
+.logs-preview-list-item:not(:first-child) {
+ border-top: 1px solid rgba(253, 253, 253, 0.12);
+}
+
+.logs-preview-list-item-timestamp {
+ margin-right: 0.75rem;
+ white-space: nowrap;
+}
+
+.logs-preview-list-item-body {
+ flex-grow: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.logs-preview-list-item-expand{
+ margin-left: 0.75rem;
+ color: #1677ff;
+ padding: 0.25rem 0.375rem;
+ cursor: pointer;
+ font-size: 12px;
+}
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/components/LogsCountInInterval/index.tsx b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/components/LogsCountInInterval/index.tsx
new file mode 100644
index 0000000000..63ee3ff3c0
--- /dev/null
+++ b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/components/LogsCountInInterval/index.tsx
@@ -0,0 +1,55 @@
+import './styles.scss';
+
+import {
+ initialFilters,
+ initialQueriesMap,
+ PANEL_TYPES,
+} from 'constants/queryBuilder';
+import { Time } from 'container/TopNav/DateTimeSelection/config';
+import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
+import cloneDeep from 'lodash-es/cloneDeep';
+import { useMemo } from 'react';
+import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
+import { LogsAggregatorOperator } from 'types/common/queryBuilder';
+
+function LogsCountInInterval({
+ filter,
+ timeInterval,
+}: LogsCountInIntervalProps): JSX.Element | null {
+ const query = useMemo(() => {
+ const q = cloneDeep(initialQueriesMap.logs);
+ q.builder.queryData[0] = {
+ ...q.builder.queryData[0],
+ filters: filter || initialFilters,
+ aggregateOperator: LogsAggregatorOperator.COUNT,
+ };
+ return q;
+ }, [filter]);
+
+ const result = useGetQueryRange({
+ graphType: PANEL_TYPES.TABLE,
+ query,
+ selectedTime: 'GLOBAL_TIME',
+ globalSelectedInterval: timeInterval,
+ });
+
+ if (!result.isFetched) {
+ return null;
+ }
+
+ const count =
+ result?.data?.payload?.data?.newResult?.data?.result?.[0]?.series?.[0]
+ ?.values?.[0]?.value;
+ return (
+
+ {count} matches in
+
+ );
+}
+
+interface LogsCountInIntervalProps {
+ filter: TagFilter;
+ timeInterval: Time;
+}
+
+export default LogsCountInInterval;
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/components/LogsCountInInterval/styles.scss b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/components/LogsCountInInterval/styles.scss
new file mode 100644
index 0000000000..2074e176a6
--- /dev/null
+++ b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/components/LogsCountInInterval/styles.scss
@@ -0,0 +1,3 @@
+.logs-filter-preview-matched-logs-count {
+ margin-right: 0.5rem;
+}
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/index.tsx b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/index.tsx
new file mode 100644
index 0000000000..a40f1d0376
--- /dev/null
+++ b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/index.tsx
@@ -0,0 +1,45 @@
+import './styles.scss';
+
+import { Select } from 'antd';
+import {
+ RelativeDurationOptions,
+ Time,
+} from 'container/TopNav/DateTimeSelection/config';
+import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
+
+import LogsCountInInterval from './components/LogsCountInInterval';
+
+function PreviewIntervalSelector({
+ previewFilter,
+ value,
+ onChange,
+}: PreviewIntervalSelectorProps): JSX.Element {
+ const onSelectInterval = (value: unknown): void => onChange(value as Time);
+
+ const isEmptyFilter = (previewFilter?.items?.length || 0) < 1;
+
+ return (
+
+ {!isEmptyFilter && (
+
+ )}
+
+
+
+
+ );
+}
+
+interface PreviewIntervalSelectorProps {
+ value: Time;
+ onChange: (interval: Time) => void;
+ previewFilter: TagFilter;
+}
+
+export default PreviewIntervalSelector;
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/styles.scss b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/styles.scss
new file mode 100644
index 0000000000..d2bc3347ea
--- /dev/null
+++ b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/styles.scss
@@ -0,0 +1,4 @@
+.logs-filter-preview-time-interval-summary {
+ display: flex;
+ align-items: center;
+}
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/SampleLogs/index.tsx b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/SampleLogs/index.tsx
new file mode 100644
index 0000000000..82d7fba4fc
--- /dev/null
+++ b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/SampleLogs/index.tsx
@@ -0,0 +1,76 @@
+import './styles.scss';
+
+import {
+ initialFilters,
+ initialQueriesMap,
+ PANEL_TYPES,
+} from 'constants/queryBuilder';
+import { Time } from 'container/TopNav/DateTimeSelection/config';
+import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
+import cloneDeep from 'lodash-es/cloneDeep';
+import { useMemo } from 'react';
+import { ILog } from 'types/api/logs/log';
+import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
+import { LogsAggregatorOperator } from 'types/common/queryBuilder';
+
+import LogsList from '../LogsList';
+
+function SampleLogs({ filter, timeInterval }: SampleLogsProps): JSX.Element {
+ const sampleLogsQuery = useMemo(() => {
+ const q = cloneDeep(initialQueriesMap.logs);
+ q.builder.queryData[0] = {
+ ...q.builder.queryData[0],
+ filters: filter || initialFilters,
+ aggregateOperator: LogsAggregatorOperator.NOOP,
+ orderBy: [{ columnName: 'timestamp', order: 'desc' }],
+ limit: 5,
+ };
+ return q;
+ }, [filter]);
+
+ const sampleLogsResponse = useGetQueryRange({
+ graphType: PANEL_TYPES.LIST,
+ query: sampleLogsQuery,
+ selectedTime: 'GLOBAL_TIME',
+ globalSelectedInterval: timeInterval,
+ });
+
+ if (sampleLogsResponse?.isError) {
+ return (
+
+ could not fetch logs for filter
+
+ );
+ }
+
+ if (sampleLogsResponse?.isFetching) {
+ return Loading...
;
+ }
+
+ if ((filter?.items?.length || 0) < 1) {
+ return (
+ Please select a filter
+ );
+ }
+
+ const logsList =
+ sampleLogsResponse?.data?.payload?.data?.newResult?.data?.result[0]?.list ||
+ [];
+
+ if (logsList.length < 1) {
+ return No logs found
;
+ }
+
+ const logs: ILog[] = logsList.map((item) => ({
+ ...item.data,
+ timestamp: item.timestamp,
+ }));
+ return ;
+}
+
+interface SampleLogsProps {
+ filter: TagFilter;
+ timeInterval: Time;
+}
+
+export default SampleLogs;
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/SampleLogs/styles.scss b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/SampleLogs/styles.scss
new file mode 100644
index 0000000000..815a8ce45f
--- /dev/null
+++ b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/SampleLogs/styles.scss
@@ -0,0 +1,7 @@
+.sample-logs-notice-container {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineFilterPreview/index.tsx b/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineFilterSummary/index.tsx
similarity index 72%
rename from frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineFilterPreview/index.tsx
rename to frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineFilterSummary/index.tsx
index b33ed6c087..6217f168b0 100644
--- a/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineFilterPreview/index.tsx
+++ b/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineFilterSummary/index.tsx
@@ -3,9 +3,9 @@ import './styles.scss';
import { queryFilterTags } from 'hooks/queryBuilder/useTag';
import { PipelineData } from 'types/api/pipeline/def';
-function PipelineFilterPreview({
+function PipelineFilterSummary({
filter,
-}: PipelineFilterPreviewProps): JSX.Element {
+}: PipelineFilterSummaryProps): JSX.Element {
return (
{queryFilterTags(filter).map((tag) => (
@@ -17,8 +17,8 @@ function PipelineFilterPreview({
);
}
-interface PipelineFilterPreviewProps {
+interface PipelineFilterSummaryProps {
filter: PipelineData['filter'];
}
-export default PipelineFilterPreview;
+export default PipelineFilterSummary;
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineFilterPreview/styles.scss b/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineFilterSummary/styles.scss
similarity index 100%
rename from frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineFilterPreview/styles.scss
rename to frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineFilterSummary/styles.scss
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/index.tsx b/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/index.tsx
index 25ec206566..8fc4b5b6eb 100644
--- a/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/index.tsx
+++ b/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/index.tsx
@@ -4,7 +4,7 @@ import { PipelineData, ProcessorData } from 'types/api/pipeline/def';
import { PipelineIndexIcon } from '../AddNewProcessor/styles';
import { ColumnDataStyle, ListDataStyle, ProcessorIndexIcon } from '../styles';
-import PipelineFilterPreview from './PipelineFilterPreview';
+import PipelineFilterSummary from './PipelineFilterSummary';
const componentMap: ComponentMap = {
orderId: ({ record }) =>
{record},
@@ -15,7 +15,7 @@ const componentMap: ComponentMap = {
),
id: ({ record }) =>
{record},
name: ({ record }) =>
{record},
- filter: ({ record }) =>
,
+ filter: ({ record }) =>
,
};
function TableComponents({
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/config.ts b/frontend/src/container/PipelinePage/PipelineListsView/config.ts
index baecbb8d14..6fc56c48b5 100644
--- a/frontend/src/container/PipelinePage/PipelineListsView/config.ts
+++ b/frontend/src/container/PipelinePage/PipelineListsView/config.ts
@@ -14,25 +14,25 @@ import NameInput from './AddNewPipeline/FormFields/NameInput';
export const pipelineFields = [
{
id: 1,
- fieldName: 'Filter',
- placeholder: 'pipeline_filter_placeholder',
- name: 'filter',
- component: FilterInput,
- },
- {
- id: 2,
fieldName: 'Name',
placeholder: 'pipeline_name_placeholder',
name: 'name',
component: NameInput,
},
{
- id: 4,
+ id: 2,
fieldName: 'Description',
placeholder: 'pipeline_description_placeholder',
name: 'description',
component: DescriptionTextArea,
},
+ {
+ id: 3,
+ fieldName: 'Filter',
+ placeholder: 'pipeline_filter_placeholder',
+ name: 'filter',
+ component: FilterInput,
+ },
];
export const tagInputStyle: React.CSSProperties = {
diff --git a/frontend/src/container/TopNav/DateTimeSelection/config.ts b/frontend/src/container/TopNav/DateTimeSelection/config.ts
index 2aaf3e1c19..38c6c06611 100644
--- a/frontend/src/container/TopNav/DateTimeSelection/config.ts
+++ b/frontend/src/container/TopNav/DateTimeSelection/config.ts
@@ -41,7 +41,7 @@ export interface Option {
label: string;
}
-export const ServiceMapOptions: Option[] = [
+export const RelativeDurationOptions: Option[] = [
{ value: '5min', label: 'Last 5 min' },
{ value: '15min', label: 'Last 15 min' },
{ value: '30min', label: 'Last 30 min' },
@@ -53,7 +53,7 @@ export const ServiceMapOptions: Option[] = [
export const getDefaultOption = (route: string): Time => {
if (route === ROUTES.SERVICE_MAP) {
- return ServiceMapOptions[2].value;
+ return RelativeDurationOptions[2].value;
}
if (route === ROUTES.APPLICATION) {
return Options[2].value;
@@ -63,7 +63,7 @@ export const getDefaultOption = (route: string): Time => {
export const getOptions = (routes: string): Option[] => {
if (routes === ROUTES.SERVICE_MAP) {
- return ServiceMapOptions;
+ return RelativeDurationOptions;
}
return Options;
};
From b14f800fee4449edbb6c00e74c8576e633c13b20 Mon Sep 17 00:00:00 2001
From: Prashant Shahi
Date: Sun, 8 Oct 2023 23:11:58 +0545
Subject: [PATCH 16/34] =?UTF-8?q?ci:=20=F0=9F=91=B7=20pin=20Go=20v1.21=20a?=
=?UTF-8?q?nd=20bump=20up=20actions/*=20in=20GH=20build/push=20workflows?=
=?UTF-8?q?=20(#3687)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* ci: 👷 pin Go v1.21 in GH build/push workflows
* chore: 💚 update actions/* to v4
---------
Signed-off-by: Prashant Shahi
---
.github/workflows/build.yaml | 12 ++++++------
.github/workflows/codeql.yaml | 2 +-
.github/workflows/commitlint.yml | 2 +-
.github/workflows/create-issue-on-pr-merge.yml | 4 ++--
.github/workflows/dependency-review.yml | 2 +-
.github/workflows/e2e-k3s.yaml | 2 +-
.github/workflows/playwright.yaml | 4 ++--
.github/workflows/push.yaml | 16 ++++++++++++----
.github/workflows/sonar.yml | 2 +-
Makefile | 2 +-
10 files changed, 28 insertions(+), 20 deletions(-)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index f5f5e5610a..1d8d4e7b70 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install dependencies
run: cd frontend && yarn install
- name: Run ESLint
@@ -31,7 +31,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Create .env file
run: |
echo 'INTERCOM_APP_ID="${{ secrets.INTERCOM_APP_ID }}"' > frontend/.env
@@ -54,12 +54,12 @@ jobs:
build-query-service:
runs-on: ubuntu-latest
steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
- name: Setup golang
uses: actions/setup-go@v4
with:
go-version: "1.21"
- - name: Checkout code
- uses: actions/checkout@v3
- name: Run tests
shell: bash
run: |
@@ -72,12 +72,12 @@ jobs:
build-ee-query-service:
runs-on: ubuntu-latest
steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
- name: Setup golang
uses: actions/setup-go@v4
with:
go-version: "1.21"
- - name: Checkout code
- uses: actions/checkout@v3
- name: Build EE query-service image
shell: bash
run: |
diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml
index 14a0c127aa..be02f3bb82 100644
--- a/.github/workflows/codeql.yaml
+++ b/.github/workflows/codeql.yaml
@@ -39,7 +39,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml
index b624a90b9f..3a38338cf0 100644
--- a/.github/workflows/commitlint.yml
+++ b/.github/workflows/commitlint.yml
@@ -7,7 +7,7 @@ jobs:
lint-commits:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: wagoid/commitlint-github-action@v5
diff --git a/.github/workflows/create-issue-on-pr-merge.yml b/.github/workflows/create-issue-on-pr-merge.yml
index 2b0c849ffa..2a79618d12 100644
--- a/.github/workflows/create-issue-on-pr-merge.yml
+++ b/.github/workflows/create-issue-on-pr-merge.yml
@@ -12,11 +12,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Codebase
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
repository: signoz/gh-bot
- name: Use Node v16
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
node-version: 16
- name: Setup Cache & Install Dependencies
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
index 053a8733dc..be454590f3 100644
--- a/.github/workflows/dependency-review.yml
+++ b/.github/workflows/dependency-review.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: 'Dependency Review'
with:
fail-on-severity: high
diff --git a/.github/workflows/e2e-k3s.yaml b/.github/workflows/e2e-k3s.yaml
index 71061bfc73..770a2f4df3 100644
--- a/.github/workflows/e2e-k3s.yaml
+++ b/.github/workflows/e2e-k3s.yaml
@@ -13,7 +13,7 @@ jobs:
DOCKER_TAG: pull-${{ github.event.number }}
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Build query-service image
env:
diff --git a/.github/workflows/playwright.yaml b/.github/workflows/playwright.yaml
index d6c05dfd6f..9ad3ef4313 100644
--- a/.github/workflows/playwright.yaml
+++ b/.github/workflows/playwright.yaml
@@ -9,8 +9,8 @@ jobs:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-node@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
with:
node-version: "16.x"
- name: Install dependencies
diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml
index a4c03d014c..f8eb005883 100644
--- a/.github/workflows/push.yaml
+++ b/.github/workflows/push.yaml
@@ -14,7 +14,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
+ - name: Setup golang
+ uses: actions/setup-go@v4
+ with:
+ go-version: "1.21"
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
@@ -54,7 +58,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
+ - name: Setup golang
+ uses: actions/setup-go@v4
+ with:
+ go-version: "1.21"
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
@@ -94,7 +102,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install dependencies
working-directory: frontend
run: yarn install
@@ -138,7 +146,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Create .env file
run: |
echo 'INTERCOM_APP_ID="${{ secrets.INTERCOM_APP_ID }}"' > frontend/.env
diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml
index 742768525f..8c62c12d1b 100644
--- a/.github/workflows/sonar.yml
+++ b/.github/workflows/sonar.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Sonar analysis
diff --git a/Makefile b/Makefile
index 8e62b6cb5c..5213c4597a 100644
--- a/Makefile
+++ b/Makefile
@@ -126,7 +126,7 @@ build-ee-query-service-amd64:
make QUERY_SERVICE_DIRECTORY=${EE_QUERY_SERVICE_DIRECTORY} build-query-service-amd64
# Step to build and push EE docker image of query in amd64 and arm64 (used in push pipeline)
-build-push-ee-query-service: build-ee-query-service-static-all
+build-push-ee-query-service:
@echo "------------------"
@echo "--> Building and pushing query-service docker image"
@echo "------------------"
From e7a5eb7b2228f9f561927420af156e6c5f2f896d Mon Sep 17 00:00:00 2001
From: Palash Gupta
Date: Sun, 8 Oct 2023 23:21:17 +0530
Subject: [PATCH 17/34] feat: new dashboard page is updated (#3385)
* feat: dashboard widget page is refactored
* chore: key is updated
* chore: delete widget is updated
* chore: naming of the file is updated
* feat: dashboard changes are updated and selected dashboard and dashboardId is added
* chore: dashboard widget page is updated
* feat: setlayout is updated
* chore: selected dashboard is updated
* chore: dashboard is updated
* fix: feedback is updated
* chore: comments are resolved
* chore: empty widget id is updated
* fix: variables is updated
* chore: dashboard variable and name,description is now updated in hooks
* chore: build is fixed
* chore: loading experience is updated
* chore: title is updated
* fix: dashboard variables and other changes are updated
* feat: dashboard reducer is removed
* feat: widget header is updated
* feat: widget header is updated
* chore: dashboard is updated
* chore: feedback is updated
* fix: issues are fixed
* chore: delete is updated
* chore: warning message is updated
* chore: warning message is updated
* chore: widget graph component
* feat: dashboard condition is updated
* chore: getChartData is updated
* chore: widget details page is updated
* feat: tab sync is updated
* chore: layout is updated
* chore: labels is updated
* chore: message is updated
* chore: warining message is updated
---------
Co-authored-by: Rajat Dabade
Co-authored-by: Vishal Sharma
---
frontend/public/locales/en-GB/dashboard.json | 9 +-
frontend/public/locales/en/dashboard.json | 9 +-
frontend/src/AppRoutes/index.tsx | 33 +-
frontend/src/api/dashboard/delete.ts | 25 +-
frontend/src/api/dashboard/get.ts | 27 +-
frontend/src/constants/reactQueryKeys.ts | 2 +
.../FormAlertRules/ChartPreview/index.tsx | 2 +-
.../EmptyWidget/index.tsx | 0
.../EmptyWidget/styles.ts | 0
.../FullView/GraphManager.styles.scss | 0
.../GridCard/FullView/GraphManager.tsx | 136 +++++++
.../FullView/TableRender/CustomCheckBox.tsx | 15 +-
.../FullView/TableRender/GetLabel.tsx | 0
.../TableRender/GraphManagerColumns.tsx} | 15 +-
.../GridCard}/FullView/TableRender/Label.tsx | 0
.../GridCard}/FullView/contants.ts | 0
.../GridCard}/FullView/index.tsx | 57 ++-
.../GridCard}/FullView/styles.ts | 0
.../GridCard}/FullView/types.ts | 37 +-
.../GridCard}/FullView/utils.ts | 5 -
.../GridCard}/Graph.test.tsx | 0
.../GridCard/WidgetGraphComponent.tsx | 277 +++++++++++++
.../GridCard}/__mock__/mockChartData.ts | 0
.../GridCard}/__mock__/mockLegendEntryData.ts | 0
.../GridCardLayout/GridCard/index.tsx | 133 ++++++
.../GridCard}/styles.ts | 0
.../GridCard}/types.ts | 23 +-
.../GridCard}/utils.ts | 0
.../GridCardLayout/GridCardLayout.tsx | 131 ++++++
.../WidgetHeader/DisplayThreshold.tsx | 0
.../WidgetHeader/config.ts | 9 +-
.../WidgetHeader/contants.ts | 0
.../WidgetHeader/index.tsx | 22 +-
.../WidgetHeader/styles.ts | 0
.../WidgetHeader/types.ts | 0
.../WidgetHeader/utils.ts | 0
.../src/container/GridCardLayout/config.ts | 17 +
.../src/container/GridCardLayout/index.tsx | 35 ++
.../styles.ts | 0
.../src/container/GridCardLayout/types.ts | 6 +
.../Graph/FullView/GraphManager.tsx | 203 ----------
.../FullView/TableRender/GetCheckBox.tsx | 27 --
.../Graph/WidgetGraphComponent.tsx | 334 ---------------
.../container/GridGraphLayout/Graph/index.tsx | 187 ---------
.../container/GridGraphLayout/GraphLayout.tsx | 113 ------
.../src/container/GridGraphLayout/config.ts | 8 -
.../src/container/GridGraphLayout/index.tsx | 383 ------------------
.../src/container/GridGraphLayout/utils.ts | 78 ----
.../ListOfDashboard/ImportJSON/index.tsx | 21 +-
.../TableComponents/DeleteButton.tsx | 57 +--
.../src/container/ListOfDashboard/index.tsx | 49 +--
.../LiveLogs/LiveLogsContainer/index.tsx | 2 +-
.../src/container/LogsExplorerChart/index.tsx | 2 +-
.../MetricsApplication.factory.ts | 2 +
.../MetricsApplication/Tabs/DBCall.tsx | 7 +-
.../MetricsApplication/Tabs/External.tsx | 10 +-
.../MetricsApplication/Tabs/Overview.tsx | 4 +-
.../Tabs/Overview/ApDex/ApDexMetrics.tsx | 5 +-
.../Tabs/Overview/ApDex/ApDexTraces.tsx | 3 +-
.../Tabs/Overview/ServiceOverview.tsx | 4 +-
.../Tabs/Overview/TopLevelOperations.tsx | 5 +-
.../Tabs/Overview/TopOperationMetrics.tsx | 9 +-
.../MetricsApplication/TopOperationsTable.tsx | 4 +-
.../src/container/MetricsApplication/types.ts | 1 +
.../NewDashboard/ComponentsSlider/index.tsx | 138 ++++---
.../DashboardSettings/General/index.tsx | 99 ++---
.../DashboardSettings/Variables/index.tsx | 86 ++--
.../NewDashboard/DashboardSettings/index.tsx | 10 +-
.../DashboardVariablesSelection/index.tsx | 99 +++--
.../DescriptionOfDashboard/SettingsDrawer.tsx | 4 +-
.../DescriptionOfDashboard/index.tsx | 34 +-
.../NewDashboard/GridGraphs/index.tsx | 12 +-
.../LeftContainer/QuerySection/index.tsx | 89 ++--
.../LeftContainer/WidgetGraph/WidgetGraph.tsx | 57 +--
.../LeftContainer/WidgetGraph/index.tsx | 15 +-
frontend/src/container/NewWidget/index.tsx | 203 +++++-----
.../src/container/ServiceApplication/types.ts | 2 +-
.../src/container/ServiceApplication/utils.ts | 2 +-
.../TimeSeriesView/TimeSeriesView.tsx | 2 +-
.../hooks/dashboard/useDeleteDashboard.tsx | 15 +
.../hooks/dashboard/useUpdateDashboard.tsx | 13 +-
.../hooks/queryBuilder/useGetQueriesRange.ts | 8 +-
.../hooks/queryBuilder/useGetQueryRange.ts | 6 +-
.../queryBuilder/useGetWidgetQueryRange.ts | 7 +-
frontend/src/hooks/useChartMutable.ts | 2 +-
frontend/src/hooks/useTabFocus.tsx | 28 ++
.../getDashboardVariables.ts | 48 +--
.../dashboard/getQueryResults.ts | 5 +-
.../dashboard/prepareQueryRangePayload.ts | 0
frontend/src/lib/getChartData.ts | 56 ++-
.../queryBuilderMappers/mapQueryDataToApi.ts | 2 +-
frontend/src/pages/DashboardWidget/index.tsx | 71 +---
frontend/src/pages/NewDashboard/index.tsx | 61 +--
.../src/providers/Dashboard/Dashboard.tsx | 231 +++++++++++
frontend/src/providers/Dashboard/types.ts | 18 +
frontend/src/providers/Dashboard/util.ts | 22 +
.../actions/dashboard/applySettingsToPanel.ts | 25 --
.../actions/dashboard/deleteDashboard.ts | 49 ---
.../store/actions/dashboard/deleteQuery.ts | 17 -
.../store/actions/dashboard/deleteWidget.ts | 70 ----
.../store/actions/dashboard/getDashboard.ts | 68 ----
frontend/src/store/actions/dashboard/index.ts | 6 -
.../store/actions/dashboard/saveDashboard.ts | 174 --------
.../actions/dashboard/toggleAddWidget.ts | 17 -
.../store/actions/dashboard/toggleEditMode.ts | 10 -
.../actions/dashboard/updateDashboardTitle.ts | 55 ---
.../store/actions/dashboard/updateQuery.ts | 21 -
.../dashboard/updatedDashboardVariables.ts | 39 --
frontend/src/store/actions/index.ts | 1 -
frontend/src/store/reducers/dashboard.ts | 331 ---------------
frontend/src/store/reducers/index.ts | 2 -
frontend/src/types/actions/dashboard.ts | 189 ---------
frontend/src/types/actions/index.ts | 2 -
frontend/src/types/api/dashboard/delete.ts | 4 +
frontend/src/types/reducer/dashboards.ts | 10 -
.../src/utils/dashboard/selectedDashboard.ts | 18 -
116 files changed, 1809 insertions(+), 3287 deletions(-)
rename frontend/src/container/{GridGraphLayout => GridCardLayout}/EmptyWidget/index.tsx (100%)
rename frontend/src/container/{GridGraphLayout => GridCardLayout}/EmptyWidget/styles.ts (100%)
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/FullView/GraphManager.styles.scss (100%)
create mode 100644 frontend/src/container/GridCardLayout/GridCard/FullView/GraphManager.tsx
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/FullView/TableRender/CustomCheckBox.tsx (59%)
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/FullView/TableRender/GetLabel.tsx (100%)
rename frontend/src/container/{GridGraphLayout/Graph/FullView/TableRender/GraphManagerColumns.ts => GridCardLayout/GridCard/FullView/TableRender/GraphManagerColumns.tsx} (87%)
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/FullView/TableRender/Label.tsx (100%)
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/FullView/contants.ts (100%)
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/FullView/index.tsx (80%)
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/FullView/styles.ts (100%)
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/FullView/types.ts (76%)
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/FullView/utils.ts (94%)
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/Graph.test.tsx (100%)
create mode 100644 frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/__mock__/mockChartData.ts (100%)
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/__mock__/mockLegendEntryData.ts (100%)
create mode 100644 frontend/src/container/GridCardLayout/GridCard/index.tsx
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/styles.ts (100%)
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/types.ts (67%)
rename frontend/src/container/{GridGraphLayout/Graph => GridCardLayout/GridCard}/utils.ts (100%)
create mode 100644 frontend/src/container/GridCardLayout/GridCardLayout.tsx
rename frontend/src/container/{GridGraphLayout => GridCardLayout}/WidgetHeader/DisplayThreshold.tsx (100%)
rename frontend/src/container/{GridGraphLayout => GridCardLayout}/WidgetHeader/config.ts (61%)
rename frontend/src/container/{GridGraphLayout => GridCardLayout}/WidgetHeader/contants.ts (100%)
rename frontend/src/container/{GridGraphLayout => GridCardLayout}/WidgetHeader/index.tsx (92%)
rename frontend/src/container/{GridGraphLayout => GridCardLayout}/WidgetHeader/styles.ts (100%)
rename frontend/src/container/{GridGraphLayout => GridCardLayout}/WidgetHeader/types.ts (100%)
rename frontend/src/container/{GridGraphLayout => GridCardLayout}/WidgetHeader/utils.ts (100%)
create mode 100644 frontend/src/container/GridCardLayout/config.ts
create mode 100644 frontend/src/container/GridCardLayout/index.tsx
rename frontend/src/container/{GridGraphLayout => GridCardLayout}/styles.ts (100%)
create mode 100644 frontend/src/container/GridCardLayout/types.ts
delete mode 100644 frontend/src/container/GridGraphLayout/Graph/FullView/GraphManager.tsx
delete mode 100644 frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/GetCheckBox.tsx
delete mode 100644 frontend/src/container/GridGraphLayout/Graph/WidgetGraphComponent.tsx
delete mode 100644 frontend/src/container/GridGraphLayout/Graph/index.tsx
delete mode 100644 frontend/src/container/GridGraphLayout/GraphLayout.tsx
delete mode 100644 frontend/src/container/GridGraphLayout/config.ts
delete mode 100644 frontend/src/container/GridGraphLayout/index.tsx
delete mode 100644 frontend/src/container/GridGraphLayout/utils.ts
create mode 100644 frontend/src/hooks/dashboard/useDeleteDashboard.tsx
create mode 100644 frontend/src/hooks/useTabFocus.tsx
rename frontend/src/{store/actions => lib}/dashboard/getQueryResults.ts (99%)
rename frontend/src/{store/actions => lib}/dashboard/prepareQueryRangePayload.ts (100%)
create mode 100644 frontend/src/providers/Dashboard/Dashboard.tsx
create mode 100644 frontend/src/providers/Dashboard/types.ts
create mode 100644 frontend/src/providers/Dashboard/util.ts
delete mode 100644 frontend/src/store/actions/dashboard/applySettingsToPanel.ts
delete mode 100644 frontend/src/store/actions/dashboard/deleteDashboard.ts
delete mode 100644 frontend/src/store/actions/dashboard/deleteQuery.ts
delete mode 100644 frontend/src/store/actions/dashboard/deleteWidget.ts
delete mode 100644 frontend/src/store/actions/dashboard/getDashboard.ts
delete mode 100644 frontend/src/store/actions/dashboard/index.ts
delete mode 100644 frontend/src/store/actions/dashboard/saveDashboard.ts
delete mode 100644 frontend/src/store/actions/dashboard/toggleAddWidget.ts
delete mode 100644 frontend/src/store/actions/dashboard/toggleEditMode.ts
delete mode 100644 frontend/src/store/actions/dashboard/updateDashboardTitle.ts
delete mode 100644 frontend/src/store/actions/dashboard/updateQuery.ts
delete mode 100644 frontend/src/store/actions/dashboard/updatedDashboardVariables.ts
delete mode 100644 frontend/src/store/reducers/dashboard.ts
delete mode 100644 frontend/src/types/actions/dashboard.ts
delete mode 100644 frontend/src/types/reducer/dashboards.ts
delete mode 100644 frontend/src/utils/dashboard/selectedDashboard.ts
diff --git a/frontend/public/locales/en-GB/dashboard.json b/frontend/public/locales/en-GB/dashboard.json
index b643f4727d..b69113483d 100644
--- a/frontend/public/locales/en-GB/dashboard.json
+++ b/frontend/public/locales/en-GB/dashboard.json
@@ -13,5 +13,12 @@
"import_dashboard_by_pasting": "Import dashboard by pasting JSON or importing JSON file",
"error_loading_json": "Error loading JSON file",
"empty_json_not_allowed": "Empty JSON is not allowed",
- "new_dashboard_title": "Sample Title"
+ "new_dashboard_title": "Sample Title",
+ "layout_saved_successfully": "Layout saved successfully",
+ "add_panel": "Add Panel",
+ "save_layout": "Save Layout",
+ "variable_updated_successfully": "Variable updated successfully",
+ "error_while_updating_variable": "Error while updating variable",
+ "dashboard_has_been_updated": "Dashboard has been updated",
+ "do_you_want_to_refresh_the_dashboard": "Do you want to refresh the dashboard?"
}
diff --git a/frontend/public/locales/en/dashboard.json b/frontend/public/locales/en/dashboard.json
index b643f4727d..b69113483d 100644
--- a/frontend/public/locales/en/dashboard.json
+++ b/frontend/public/locales/en/dashboard.json
@@ -13,5 +13,12 @@
"import_dashboard_by_pasting": "Import dashboard by pasting JSON or importing JSON file",
"error_loading_json": "Error loading JSON file",
"empty_json_not_allowed": "Empty JSON is not allowed",
- "new_dashboard_title": "Sample Title"
+ "new_dashboard_title": "Sample Title",
+ "layout_saved_successfully": "Layout saved successfully",
+ "add_panel": "Add Panel",
+ "save_layout": "Save Layout",
+ "variable_updated_successfully": "Variable updated successfully",
+ "error_while_updating_variable": "Error while updating variable",
+ "dashboard_has_been_updated": "Dashboard has been updated",
+ "do_you_want_to_refresh_the_dashboard": "Do you want to refresh the dashboard?"
}
diff --git a/frontend/src/AppRoutes/index.tsx b/frontend/src/AppRoutes/index.tsx
index 56ba19fdf4..c2a0db3da1 100644
--- a/frontend/src/AppRoutes/index.tsx
+++ b/frontend/src/AppRoutes/index.tsx
@@ -12,6 +12,7 @@ import useGetFeatureFlag from 'hooks/useGetFeatureFlag';
import { NotificationProvider } from 'hooks/useNotifications';
import { ResourceProvider } from 'hooks/useResourceAttribute';
import history from 'lib/history';
+import { DashboardProvider } from 'providers/Dashboard/Dashboard';
import { QueryBuilderProvider } from 'providers/QueryBuilder';
import { Suspense, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
@@ -110,22 +111,24 @@ function App(): JSX.Element {
-
- }>
-
- {routes.map(({ path, component, exact }) => (
-
- ))}
+
+
+ }>
+
+ {routes.map(({ path, component, exact }) => (
+
+ ))}
-
-
-
-
+
+
+
+
+
diff --git a/frontend/src/api/dashboard/delete.ts b/frontend/src/api/dashboard/delete.ts
index 2f7c8f16b9..8faf711383 100644
--- a/frontend/src/api/dashboard/delete.ts
+++ b/frontend/src/api/dashboard/delete.ts
@@ -1,24 +1,9 @@
import axios from 'api';
-import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
-import { AxiosError } from 'axios';
-import { ErrorResponse, SuccessResponse } from 'types/api';
-import { Props } from 'types/api/dashboard/delete';
+import { PayloadProps, Props } from 'types/api/dashboard/delete';
-const deleteDashboard = async (
- props: Props,
-): Promise | ErrorResponse> => {
- try {
- const response = await axios.delete(`/dashboards/${props.uuid}`);
-
- return {
- statusCode: 200,
- error: null,
- message: response.data.status,
- payload: response.data.data,
- };
- } catch (error) {
- return ErrorResponseHandler(error as AxiosError);
- }
-};
+const deleteDashboard = (props: Props): Promise =>
+ axios
+ .delete(`/dashboards/${props.uuid}`)
+ .then((response) => response.data);
export default deleteDashboard;
diff --git a/frontend/src/api/dashboard/get.ts b/frontend/src/api/dashboard/get.ts
index 6c9c953e7d..9b10f6467d 100644
--- a/frontend/src/api/dashboard/get.ts
+++ b/frontend/src/api/dashboard/get.ts
@@ -1,24 +1,11 @@
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/dashboard/get';
+import { ApiResponse } from 'types/api';
+import { Props } from 'types/api/dashboard/get';
+import { Dashboard } from 'types/api/dashboard/getAll';
-const get = async (
- props: Props,
-): Promise | ErrorResponse> => {
- try {
- const response = await axios.get(`/dashboards/${props.uuid}`);
-
- return {
- statusCode: 200,
- error: null,
- message: response.data.status,
- payload: response.data.data,
- };
- } catch (error) {
- return ErrorResponseHandler(error as AxiosError);
- }
-};
+const get = (props: Props): Promise =>
+ axios
+ .get>(`/dashboards/${props.uuid}`)
+ .then((res) => res.data.data);
export default get;
diff --git a/frontend/src/constants/reactQueryKeys.ts b/frontend/src/constants/reactQueryKeys.ts
index 1f984ebd46..9a368510a1 100644
--- a/frontend/src/constants/reactQueryKeys.ts
+++ b/frontend/src/constants/reactQueryKeys.ts
@@ -3,5 +3,7 @@ export const REACT_QUERY_KEY = {
GET_QUERY_RANGE: 'GET_QUERY_RANGE',
GET_ALL_DASHBOARDS: 'GET_ALL_DASHBOARDS',
GET_TRIGGERED_ALERTS: 'GET_TRIGGERED_ALERTS',
+ DASHBOARD_BY_ID: 'DASHBOARD_BY_ID',
GET_FEATURES_FLAGS: 'GET_FEATURES_FLAGS',
+ DELETE_DASHBOARD: 'DELETE_DASHBOARD',
};
diff --git a/frontend/src/container/FormAlertRules/ChartPreview/index.tsx b/frontend/src/container/FormAlertRules/ChartPreview/index.tsx
index f6bf35cbd7..d73003f06e 100644
--- a/frontend/src/container/FormAlertRules/ChartPreview/index.tsx
+++ b/frontend/src/container/FormAlertRules/ChartPreview/index.tsx
@@ -127,7 +127,7 @@ function ChartPreview({
(
+ getDefaultTableDataSet(data),
+ );
+
+ const { notifications } = useNotifications();
+
+ const checkBoxOnChangeHandler = useCallback(
+ (e: CheckboxChangeEvent, index: number): void => {
+ const newStates = [...graphsVisibilityStates];
+
+ newStates[index] = e.target.checked;
+
+ lineChartRef?.current?.toggleGraph(index, e.target.checked);
+
+ setGraphsVisibilityStates([...newStates]);
+ },
+ [graphsVisibilityStates, setGraphsVisibilityStates, lineChartRef],
+ );
+
+ const labelClickedHandler = useCallback(
+ (labelIndex: number): void => {
+ const newGraphVisibilityStates = Array(data.datasets.length).fill(
+ false,
+ );
+ newGraphVisibilityStates[labelIndex] = true;
+
+ newGraphVisibilityStates.forEach((state, index) => {
+ lineChartRef?.current?.toggleGraph(index, state);
+ parentChartRef?.current?.toggleGraph(index, state);
+ });
+ setGraphsVisibilityStates(newGraphVisibilityStates);
+ },
+ [
+ data.datasets.length,
+ setGraphsVisibilityStates,
+ lineChartRef,
+ parentChartRef,
+ ],
+ );
+
+ const columns = getGraphManagerTableColumns({
+ data,
+ checkBoxOnChangeHandler,
+ graphVisibilityState: graphsVisibilityStates || [],
+ labelClickedHandler,
+ yAxisUnit,
+ });
+
+ const filterHandler = useCallback(
+ (event: React.ChangeEvent): void => {
+ const value = event.target.value.toString().toLowerCase();
+ const updatedDataSet = tableDataSet.map((item) => {
+ if (item.label?.toLocaleLowerCase().includes(value)) {
+ return { ...item, show: true };
+ }
+ return { ...item, show: false };
+ });
+ setTableDataSet(updatedDataSet);
+ },
+ [tableDataSet],
+ );
+
+ const saveHandler = useCallback((): void => {
+ saveLegendEntriesToLocalStorage({
+ data,
+ graphVisibilityState: graphsVisibilityStates || [],
+ name,
+ });
+ notifications.success({
+ message: 'The updated graphs & legends are saved',
+ });
+ if (onToggleModelHandler) {
+ onToggleModelHandler();
+ }
+ }, [data, graphsVisibilityStates, name, notifications, onToggleModelHandler]);
+
+ const dataSource = tableDataSet.filter((item) => item.show);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+GraphManager.defaultProps = {
+ graphVisibilityStateHandler: undefined,
+};
+
+export default memo(GraphManager);
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/CustomCheckBox.tsx b/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/CustomCheckBox.tsx
similarity index 59%
rename from frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/CustomCheckBox.tsx
rename to frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/CustomCheckBox.tsx
index 22ae630bb8..eda971c1e4 100644
--- a/frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/CustomCheckBox.tsx
+++ b/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/CustomCheckBox.tsx
@@ -1,3 +1,4 @@
+import { grey } from '@ant-design/colors';
import { Checkbox, ConfigProvider } from 'antd';
import { CheckboxChangeEvent } from 'antd/es/checkbox';
@@ -6,7 +7,7 @@ import { CheckBoxProps } from '../types';
function CustomCheckBox({
data,
index,
- graphVisibilityState,
+ graphVisibilityState = [],
checkBoxOnChangeHandler,
}: CheckBoxProps): JSX.Element {
const { datasets } = data;
@@ -15,17 +16,21 @@ function CustomCheckBox({
checkBoxOnChangeHandler(e, index);
};
+ const color = datasets[index]?.borderColor?.toString() || grey[0];
+
+ const isChecked = graphVisibilityState[index] || false;
+
return (
-
+
);
}
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/GetLabel.tsx b/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/GetLabel.tsx
similarity index 100%
rename from frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/GetLabel.tsx
rename to frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/GetLabel.tsx
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/GraphManagerColumns.ts b/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/GraphManagerColumns.tsx
similarity index 87%
rename from frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/GraphManagerColumns.ts
rename to frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/GraphManagerColumns.tsx
index cc10f83f00..3702a9b3e0 100644
--- a/frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/GraphManagerColumns.ts
+++ b/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/GraphManagerColumns.tsx
@@ -5,7 +5,7 @@ import { ChartData } from 'chart.js';
import { ColumnsKeyAndDataIndex, ColumnsTitle } from '../contants';
import { DataSetProps } from '../types';
import { getGraphManagerTableHeaderTitle } from '../utils';
-import { getCheckBox } from './GetCheckBox';
+import CustomCheckBox from './CustomCheckBox';
import { getLabel } from './GetLabel';
export const getGraphManagerTableColumns = ({
@@ -20,11 +20,14 @@ export const getGraphManagerTableColumns = ({
width: 50,
dataIndex: ColumnsKeyAndDataIndex.Index,
key: ColumnsKeyAndDataIndex.Index,
- ...getCheckBox({
- checkBoxOnChangeHandler,
- graphVisibilityState,
- data,
- }),
+ render: (_: string, __: DataSetProps, index: number): JSX.Element => (
+
+ ),
},
{
title: ColumnsTitle[ColumnsKeyAndDataIndex.Label],
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/Label.tsx b/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/Label.tsx
similarity index 100%
rename from frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/Label.tsx
rename to frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/Label.tsx
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/contants.ts b/frontend/src/container/GridCardLayout/GridCard/FullView/contants.ts
similarity index 100%
rename from frontend/src/container/GridGraphLayout/Graph/FullView/contants.ts
rename to frontend/src/container/GridCardLayout/GridCard/FullView/contants.ts
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx b/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx
similarity index 80%
rename from frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx
rename to frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx
index 1437a29157..02391bdc0c 100644
--- a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx
+++ b/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx
@@ -12,17 +12,16 @@ import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
import { useChartMutable } from 'hooks/useChartMutable';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import getChartData from 'lib/getChartData';
+import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime';
-import { toggleGraphsVisibilityInChart } from '../utils';
import { PANEL_TYPES_VS_FULL_VIEW_TABLE } from './contants';
import GraphManager from './GraphManager';
import { GraphContainer, TimeContainer } from './styles';
import { FullViewProps } from './types';
-import { getIsGraphLegendToggleAvailable } from './utils';
function FullView({
widget,
@@ -34,45 +33,29 @@ function FullView({
isDependedDataLoaded = false,
graphsVisibilityStates,
onToggleModelHandler,
+ setGraphsVisibilityStates,
+ parentChartRef,
}: FullViewProps): JSX.Element {
const { selectedTime: globalSelectedTime } = useSelector<
AppState,
GlobalReducer
>((state) => state.globalTime);
+ const { selectedDashboard } = useDashboard();
+
const getSelectedTime = useCallback(
() =>
timeItems.find((e) => e.enum === (widget?.timePreferance || 'GLOBAL_TIME')),
[widget],
);
- const canModifyChart = useChartMutable({
- panelType: widget.panelTypes,
- panelTypeAndGraphManagerVisibility: PANEL_TYPES_VS_FULL_VIEW_TABLE,
- });
-
const lineChartRef = useRef();
- useEffect(() => {
- if (graphsVisibilityStates && canModifyChart && lineChartRef.current) {
- toggleGraphsVisibilityInChart({
- graphsVisibilityStates,
- lineChartRef,
- });
- }
- }, [graphsVisibilityStates, canModifyChart]);
-
const [selectedTime, setSelectedTime] = useState({
name: getSelectedTime()?.name || '',
enum: widget?.timePreferance || 'GLOBAL_TIME',
});
- const queryKey = useMemo(
- () =>
- `FullViewGetMetricsQueryRange-${selectedTime.enum}-${globalSelectedTime}-${widget.id}`,
- [selectedTime, globalSelectedTime, widget],
- );
-
const updatedQuery = useStepInterval(widget?.query);
const response = useGetQueryRange(
@@ -81,14 +64,19 @@ function FullView({
graphType: widget.panelTypes,
query: updatedQuery,
globalSelectedInterval: globalSelectedTime,
- variables: getDashboardVariables(),
+ variables: getDashboardVariables(selectedDashboard?.data.variables),
},
{
- queryKey,
+ queryKey: `FullViewGetMetricsQueryRange-${selectedTime.enum}-${globalSelectedTime}-${widget.id}`,
enabled: !isDependedDataLoaded,
},
);
+ const canModifyChart = useChartMutable({
+ panelType: widget.panelTypes,
+ panelTypeAndGraphManagerVisibility: PANEL_TYPES_VS_FULL_VIEW_TABLE,
+ });
+
const chartDataSet = useMemo(
() =>
getChartData({
@@ -101,9 +89,14 @@ function FullView({
[response],
);
- const isGraphLegendToggleAvailable = getIsGraphLegendToggleAvailable(
- widget.panelTypes,
- );
+ useEffect(() => {
+ if (!response.isFetching && lineChartRef.current) {
+ graphsVisibilityStates?.forEach((e, i) => {
+ lineChartRef?.current?.toggleGraph(i, e);
+ parentChartRef?.current?.toggleGraph(i, e);
+ });
+ }
+ }, [graphsVisibilityStates, parentChartRef, response.isFetching]);
if (response.isFetching) {
return ;
@@ -128,10 +121,10 @@ function FullView({
)}
-
+
)}
>
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/styles.ts b/frontend/src/container/GridCardLayout/GridCard/FullView/styles.ts
similarity index 100%
rename from frontend/src/container/GridGraphLayout/Graph/FullView/styles.ts
rename to frontend/src/container/GridCardLayout/GridCard/FullView/styles.ts
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/types.ts b/frontend/src/container/GridCardLayout/GridCard/FullView/types.ts
similarity index 76%
rename from frontend/src/container/GridGraphLayout/Graph/FullView/types.ts
rename to frontend/src/container/GridCardLayout/GridCard/FullView/types.ts
index 7d329e1399..ae686496e5 100644
--- a/frontend/src/container/GridGraphLayout/Graph/FullView/types.ts
+++ b/frontend/src/container/GridCardLayout/GridCard/FullView/types.ts
@@ -1,7 +1,8 @@
import { CheckboxChangeEvent } from 'antd/es/checkbox';
import { ChartData, ChartDataset } from 'chart.js';
-import { GraphOnClickHandler } from 'components/Graph/types';
+import { GraphOnClickHandler, ToggleGraphProps } from 'components/Graph/types';
import { PANEL_TYPES } from 'constants/queryBuilder';
+import { MutableRefObject } from 'react';
import { Widgets } from 'types/api/dashboard/getAll';
export interface DataSetProps {
@@ -40,20 +41,6 @@ export interface LabelProps {
label: string;
}
-export interface GraphManagerProps {
- data: ChartData;
- name: string;
- yAxisUnit?: string;
- onToggleModelHandler?: () => void;
-}
-
-export interface CheckBoxProps {
- data: ChartData;
- index: number;
- graphVisibilityState: boolean[];
- checkBoxOnChangeHandler: (e: CheckboxChangeEvent, index: number) => void;
-}
-
export interface FullViewProps {
widget: Widgets;
fullViewOptions?: boolean;
@@ -64,6 +51,26 @@ export interface FullViewProps {
isDependedDataLoaded?: boolean;
graphsVisibilityStates?: boolean[];
onToggleModelHandler?: GraphManagerProps['onToggleModelHandler'];
+ setGraphsVisibilityStates: (graphsVisibilityStates: boolean[]) => void;
+ parentChartRef: GraphManagerProps['lineChartRef'];
+}
+
+export interface GraphManagerProps {
+ data: ChartData;
+ name: string;
+ yAxisUnit?: string;
+ onToggleModelHandler?: () => void;
+ setGraphsVisibilityStates: FullViewProps['setGraphsVisibilityStates'];
+ graphsVisibilityStates: FullViewProps['graphsVisibilityStates'];
+ lineChartRef?: MutableRefObject;
+ parentChartRef?: MutableRefObject;
+}
+
+export interface CheckBoxProps {
+ data: ChartData;
+ index: number;
+ graphVisibilityState: boolean[];
+ checkBoxOnChangeHandler: (e: CheckboxChangeEvent, index: number) => void;
}
export interface SaveLegendEntriesToLocalStoreProps {
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/utils.ts b/frontend/src/container/GridCardLayout/GridCard/FullView/utils.ts
similarity index 94%
rename from frontend/src/container/GridGraphLayout/Graph/FullView/utils.ts
rename to frontend/src/container/GridCardLayout/GridCard/FullView/utils.ts
index 256bc39050..b1ffb3a032 100644
--- a/frontend/src/container/GridGraphLayout/Graph/FullView/utils.ts
+++ b/frontend/src/container/GridCardLayout/GridCard/FullView/utils.ts
@@ -1,6 +1,5 @@
import { ChartData, ChartDataset } from 'chart.js';
import { LOCALSTORAGE } from 'constants/localStorage';
-import { PANEL_TYPES } from 'constants/queryBuilder';
import {
ExtendedChartDataset,
@@ -110,10 +109,6 @@ export const saveLegendEntriesToLocalStorage = ({
}
};
-export const getIsGraphLegendToggleAvailable = (
- panelType: PANEL_TYPES,
-): boolean => panelType === PANEL_TYPES.TIME_SERIES;
-
export const getGraphManagerTableHeaderTitle = (
title: string,
yAxisUnit?: string,
diff --git a/frontend/src/container/GridGraphLayout/Graph/Graph.test.tsx b/frontend/src/container/GridCardLayout/GridCard/Graph.test.tsx
similarity index 100%
rename from frontend/src/container/GridGraphLayout/Graph/Graph.test.tsx
rename to frontend/src/container/GridCardLayout/GridCard/Graph.test.tsx
diff --git a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx
new file mode 100644
index 0000000000..c011ca2471
--- /dev/null
+++ b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx
@@ -0,0 +1,277 @@
+import { Typography } from 'antd';
+import { ToggleGraphProps } from 'components/Graph/types';
+import { SOMETHING_WENT_WRONG } from 'constants/api';
+import GridPanelSwitch from 'container/GridPanelSwitch';
+import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
+import { useNotifications } from 'hooks/useNotifications';
+import createQueryParams from 'lib/createQueryParams';
+import history from 'lib/history';
+import { useDashboard } from 'providers/Dashboard/Dashboard';
+import {
+ Dispatch,
+ SetStateAction,
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from 'react';
+import { useSelector } from 'react-redux';
+import { useLocation } from 'react-router-dom';
+import { AppState } from 'store/reducers';
+import { Dashboard } from 'types/api/dashboard/getAll';
+import AppReducer from 'types/reducer/app';
+import { v4 } from 'uuid';
+
+import WidgetHeader from '../WidgetHeader';
+import FullView from './FullView';
+import { FullViewContainer, Modal } from './styles';
+import { WidgetGraphComponentProps } from './types';
+import { getGraphVisibilityStateOnDataChange } from './utils';
+
+function WidgetGraphComponent({
+ data,
+ widget,
+ queryResponse,
+ errorMessage,
+ name,
+ onDragSelect,
+ onClickHandler,
+ threshold,
+ headerMenuList,
+ isWarning,
+}: WidgetGraphComponentProps): JSX.Element {
+ const [deleteModal, setDeleteModal] = useState(false);
+ const [modal, setModal] = useState(false);
+ const [hovered, setHovered] = useState(false);
+ const { notifications } = useNotifications();
+ const { pathname } = useLocation();
+
+ const lineChartRef = useRef();
+
+ const { graphVisibilityStates: localStoredVisibilityStates } = useMemo(
+ () =>
+ getGraphVisibilityStateOnDataChange({
+ data,
+ isExpandedName: true,
+ name,
+ }),
+ [data, name],
+ );
+
+ useEffect(() => {
+ if (!lineChartRef.current) return;
+
+ localStoredVisibilityStates.forEach((state, index) => {
+ lineChartRef.current?.toggleGraph(index, state);
+ });
+ }, [localStoredVisibilityStates]);
+
+ const { setLayouts, selectedDashboard, setSelectedDashboard } = useDashboard();
+
+ const [graphsVisibilityStates, setGraphsVisibilityStates] = useState<
+ boolean[]
+ >(localStoredVisibilityStates);
+
+ const { featureResponse } = useSelector(
+ (state) => state.app,
+ );
+ const onToggleModal = useCallback(
+ (func: Dispatch>) => {
+ func((value) => !value);
+ },
+ [],
+ );
+
+ const updateDashboardMutation = useUpdateDashboard();
+
+ const onDeleteHandler = (): void => {
+ if (!selectedDashboard) return;
+
+ const updatedWidgets = selectedDashboard?.data?.widgets?.filter(
+ (e) => e.id !== widget.id,
+ );
+
+ const updatedLayout =
+ selectedDashboard.data.layout?.filter((e) => e.i !== widget.id) || [];
+
+ const updatedSelectedDashboard: Dashboard = {
+ ...selectedDashboard,
+ data: {
+ ...selectedDashboard.data,
+ widgets: updatedWidgets,
+ layout: updatedLayout,
+ },
+ uuid: selectedDashboard.uuid,
+ };
+
+ updateDashboardMutation.mutateAsync(updatedSelectedDashboard, {
+ onSuccess: (updatedDashboard) => {
+ if (setLayouts) setLayouts(updatedDashboard.payload?.data?.layout || []);
+ if (setSelectedDashboard && updatedDashboard.payload) {
+ setSelectedDashboard(updatedDashboard.payload);
+ }
+ featureResponse.refetch();
+ },
+ onError: () => {
+ notifications.error({
+ message: SOMETHING_WENT_WRONG,
+ });
+ },
+ });
+ };
+
+ const onCloneHandler = async (): Promise => {
+ if (!selectedDashboard) return;
+
+ const uuid = v4();
+
+ const layout = [
+ ...(selectedDashboard.data.layout || []),
+ {
+ i: uuid,
+ w: 6,
+ x: 0,
+ h: 2,
+ y: 0,
+ },
+ ];
+
+ updateDashboardMutation.mutateAsync(
+ {
+ ...selectedDashboard,
+ data: {
+ ...selectedDashboard.data,
+ layout,
+ widgets: [
+ ...(selectedDashboard.data.widgets || []),
+ {
+ ...{
+ ...widget,
+ id: uuid,
+ },
+ },
+ ],
+ },
+ },
+ {
+ onSuccess: () => {
+ notifications.success({
+ message: 'Panel cloned successfully, redirecting to new copy.',
+ });
+ const queryParams = {
+ graphType: widget?.panelTypes,
+ widgetId: uuid,
+ };
+ history.push(`${pathname}/new?${createQueryParams(queryParams)}`);
+ },
+ },
+ );
+ };
+
+ const handleOnView = (): void => {
+ onToggleModal(setModal);
+ };
+
+ const handleOnDelete = (): void => {
+ onToggleModal(setDeleteModal);
+ };
+
+ const onDeleteModelHandler = (): void => {
+ onToggleModal(setDeleteModal);
+ };
+
+ const onToggleModelHandler = (): void => {
+ onToggleModal(setModal);
+ };
+
+ return (
+ {
+ setHovered(true);
+ }}
+ onFocus={(): void => {
+ setHovered(true);
+ }}
+ onMouseOut={(): void => {
+ setHovered(false);
+ }}
+ onBlur={(): void => {
+ setHovered(false);
+ }}
+ >
+
+ Are you sure you want to delete this widget
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+WidgetGraphComponent.defaultProps = {
+ yAxisUnit: undefined,
+ setLayout: undefined,
+ onDragSelect: undefined,
+ onClickHandler: undefined,
+};
+
+export default WidgetGraphComponent;
diff --git a/frontend/src/container/GridGraphLayout/Graph/__mock__/mockChartData.ts b/frontend/src/container/GridCardLayout/GridCard/__mock__/mockChartData.ts
similarity index 100%
rename from frontend/src/container/GridGraphLayout/Graph/__mock__/mockChartData.ts
rename to frontend/src/container/GridCardLayout/GridCard/__mock__/mockChartData.ts
diff --git a/frontend/src/container/GridGraphLayout/Graph/__mock__/mockLegendEntryData.ts b/frontend/src/container/GridCardLayout/GridCard/__mock__/mockLegendEntryData.ts
similarity index 100%
rename from frontend/src/container/GridGraphLayout/Graph/__mock__/mockLegendEntryData.ts
rename to frontend/src/container/GridCardLayout/GridCard/__mock__/mockLegendEntryData.ts
diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx
new file mode 100644
index 0000000000..59e58fb74a
--- /dev/null
+++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx
@@ -0,0 +1,133 @@
+import { Skeleton } from 'antd';
+import { PANEL_TYPES } from 'constants/queryBuilder';
+import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
+import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
+import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
+import getChartData from 'lib/getChartData';
+import isEmpty from 'lodash-es/isEmpty';
+import { useDashboard } from 'providers/Dashboard/Dashboard';
+import { memo, useMemo, useState } from 'react';
+import { useInView } from 'react-intersection-observer';
+import { useDispatch, useSelector } from 'react-redux';
+import { UpdateTimeInterval } from 'store/actions';
+import { AppState } from 'store/reducers';
+import { GlobalReducer } from 'types/reducer/globalTime';
+
+import EmptyWidget from '../EmptyWidget';
+import { MenuItemKeys } from '../WidgetHeader/contants';
+import { GridCardGraphProps } from './types';
+import WidgetGraphComponent from './WidgetGraphComponent';
+
+function GridCardGraph({
+ widget,
+ name,
+ onClickHandler,
+ headerMenuList = [MenuItemKeys.View],
+ isQueryEnabled,
+ threshold,
+}: GridCardGraphProps): JSX.Element {
+ const dispatch = useDispatch();
+ const [errorMessage, setErrorMessage] = useState();
+
+ const onDragSelect = (start: number, end: number): void => {
+ const startTimestamp = Math.trunc(start);
+ const endTimestamp = Math.trunc(end);
+
+ if (startTimestamp !== endTimestamp) {
+ dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
+ }
+ };
+
+ const { ref: graphRef, inView: isGraphVisible } = useInView({
+ threshold: 0,
+ triggerOnce: true,
+ initialInView: false,
+ });
+
+ const { selectedDashboard } = useDashboard();
+
+ const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector<
+ AppState,
+ GlobalReducer
+ >((state) => state.globalTime);
+
+ const updatedQuery = useStepInterval(widget?.query);
+
+ const isEmptyWidget =
+ widget?.id === PANEL_TYPES.EMPTY_WIDGET || isEmpty(widget);
+
+ const queryResponse = useGetQueryRange(
+ {
+ selectedTime: widget?.timePreferance,
+ graphType: widget?.panelTypes,
+ query: updatedQuery,
+ globalSelectedInterval,
+ variables: getDashboardVariables(selectedDashboard?.data.variables),
+ },
+ {
+ queryKey: [
+ maxTime,
+ minTime,
+ globalSelectedInterval,
+ selectedDashboard?.data?.variables,
+ widget?.query,
+ widget?.panelTypes,
+ ],
+ keepPreviousData: true,
+ enabled: isGraphVisible && !isEmptyWidget && isQueryEnabled,
+ refetchOnMount: false,
+ onError: (error) => {
+ setErrorMessage(error.message);
+ },
+ },
+ );
+
+ const chartData = useMemo(
+ () =>
+ getChartData({
+ queryData: [
+ {
+ queryData: queryResponse?.data?.payload?.data?.result || [],
+ },
+ ],
+ createDataset: undefined,
+ isWarningLimit: true,
+ }),
+ [queryResponse],
+ );
+
+ const isEmptyLayout = widget?.id === PANEL_TYPES.EMPTY_WIDGET;
+
+ if (queryResponse.isLoading) {
+ return ;
+ }
+
+ return (
+
+
+
+ {isEmptyLayout && }
+
+ );
+}
+
+GridCardGraph.defaultProps = {
+ onDragSelect: undefined,
+ onClickHandler: undefined,
+ isQueryEnabled: true,
+ threshold: undefined,
+ headerMenuList: [MenuItemKeys.View],
+};
+
+export default memo(GridCardGraph);
diff --git a/frontend/src/container/GridGraphLayout/Graph/styles.ts b/frontend/src/container/GridCardLayout/GridCard/styles.ts
similarity index 100%
rename from frontend/src/container/GridGraphLayout/Graph/styles.ts
rename to frontend/src/container/GridCardLayout/GridCard/styles.ts
diff --git a/frontend/src/container/GridGraphLayout/Graph/types.ts b/frontend/src/container/GridCardLayout/GridCard/types.ts
similarity index 67%
rename from frontend/src/container/GridGraphLayout/Graph/types.ts
rename to frontend/src/container/GridCardLayout/GridCard/types.ts
index 49b637a178..fccf488dc8 100644
--- a/frontend/src/container/GridGraphLayout/Graph/types.ts
+++ b/frontend/src/container/GridCardLayout/GridCard/types.ts
@@ -1,15 +1,11 @@
import { ChartData } from 'chart.js';
import { GraphOnClickHandler, ToggleGraphProps } from 'components/Graph/types';
-import { Dispatch, MutableRefObject, ReactNode, SetStateAction } from 'react';
-import { Layout } from 'react-grid-layout';
+import { MutableRefObject, ReactNode } from 'react';
import { UseQueryResult } from 'react-query';
-import { DeleteWidgetProps } from 'store/actions/dashboard/deleteWidget';
-import AppActions from 'types/actions';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
-import { LayoutProps } from '..';
import { MenuItemKeys } from '../WidgetHeader/contants';
import { LegendEntryProps } from './FullView/types';
@@ -18,15 +14,7 @@ export interface GraphVisibilityLegendEntryProps {
legendEntry: LegendEntryProps[];
}
-export interface DispatchProps {
- deleteWidget: ({
- widgetId,
- }: DeleteWidgetProps) => (dispatch: Dispatch) => void;
-}
-
-export interface WidgetGraphComponentProps extends DispatchProps {
- enableModel: boolean;
- enableWidgetHeader: boolean;
+export interface WidgetGraphComponentProps {
widget: Widgets;
queryResponse: UseQueryResult<
SuccessResponse | ErrorResponse
@@ -34,21 +22,16 @@ export interface WidgetGraphComponentProps extends DispatchProps {
errorMessage: string | undefined;
data: ChartData;
name: string;
- yAxisUnit?: string;
- layout?: Layout[];
- setLayout?: Dispatch>;
onDragSelect?: (start: number, end: number) => void;
onClickHandler?: GraphOnClickHandler;
threshold?: ReactNode;
headerMenuList: MenuItemKeys[];
+ isWarning: boolean;
}
export interface GridCardGraphProps {
widget: Widgets;
name: string;
- yAxisUnit: string | undefined;
- layout?: Layout[];
- setLayout?: Dispatch>;
onDragSelect?: (start: number, end: number) => void;
onClickHandler?: GraphOnClickHandler;
threshold?: ReactNode;
diff --git a/frontend/src/container/GridGraphLayout/Graph/utils.ts b/frontend/src/container/GridCardLayout/GridCard/utils.ts
similarity index 100%
rename from frontend/src/container/GridGraphLayout/Graph/utils.ts
rename to frontend/src/container/GridCardLayout/GridCard/utils.ts
diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.tsx b/frontend/src/container/GridCardLayout/GridCardLayout.tsx
new file mode 100644
index 0000000000..ac60e54146
--- /dev/null
+++ b/frontend/src/container/GridCardLayout/GridCardLayout.tsx
@@ -0,0 +1,131 @@
+import { PlusOutlined, SaveFilled } from '@ant-design/icons';
+import { SOMETHING_WENT_WRONG } from 'constants/api';
+import { PANEL_TYPES } from 'constants/queryBuilder';
+import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
+import useComponentPermission from 'hooks/useComponentPermission';
+import { useIsDarkMode } from 'hooks/useDarkMode';
+import { useNotifications } from 'hooks/useNotifications';
+import { useDashboard } from 'providers/Dashboard/Dashboard';
+import { useTranslation } from 'react-i18next';
+import { useSelector } from 'react-redux';
+import { AppState } from 'store/reducers';
+import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
+import AppReducer from 'types/reducer/app';
+
+import { headerMenuList } from './config';
+import GridCard from './GridCard';
+import {
+ Button,
+ ButtonContainer,
+ Card,
+ CardContainer,
+ ReactGridLayout,
+} from './styles';
+import { GraphLayoutProps } from './types';
+
+function GraphLayout({
+ onAddPanelHandler,
+ widgets,
+}: GraphLayoutProps): JSX.Element {
+ const { selectedDashboard, layouts, setLayouts } = useDashboard();
+ const { t } = useTranslation(['dashboard']);
+
+ const { featureResponse, role } = useSelector(
+ (state) => state.app,
+ );
+
+ const isDarkMode = useIsDarkMode();
+
+ const updateDashboardMutation = useUpdateDashboard();
+
+ const { notifications } = useNotifications();
+
+ const [saveLayoutPermission, addPanelPermission] = useComponentPermission(
+ ['save_layout', 'add_panel'],
+ role,
+ );
+
+ const onSaveHandler = (): void => {
+ if (!selectedDashboard) return;
+
+ const updatedDashboard: Dashboard = {
+ ...selectedDashboard,
+ data: {
+ ...selectedDashboard.data,
+ layout: layouts.filter((e) => e.i !== PANEL_TYPES.EMPTY_WIDGET),
+ },
+ uuid: selectedDashboard.uuid,
+ };
+
+ updateDashboardMutation.mutate(updatedDashboard, {
+ onSuccess: () => {
+ featureResponse.refetch();
+
+ notifications.success({
+ message: t('dashboard:layout_saved_successfully'),
+ });
+ },
+ onError: () => {
+ notifications.error({
+ message: SOMETHING_WENT_WRONG,
+ });
+ },
+ });
+ };
+
+ return (
+ <>
+
+ {saveLayoutPermission && (
+ }
+ disabled={updateDashboardMutation.isLoading}
+ >
+ {t('dashboard:save_layout')}
+
+ )}
+
+ {addPanelPermission && (
+ }>
+ {t('dashboard:add_panel')}
+
+ )}
+
+
+
+ {layouts.map((layout) => {
+ const { i: id } = layout;
+ const currentWidget = (widgets || [])?.find((e) => e.id === id);
+
+ return (
+
+
+
+
+
+ );
+ })}
+
+ >
+ );
+}
+
+export default GraphLayout;
diff --git a/frontend/src/container/GridGraphLayout/WidgetHeader/DisplayThreshold.tsx b/frontend/src/container/GridCardLayout/WidgetHeader/DisplayThreshold.tsx
similarity index 100%
rename from frontend/src/container/GridGraphLayout/WidgetHeader/DisplayThreshold.tsx
rename to frontend/src/container/GridCardLayout/WidgetHeader/DisplayThreshold.tsx
diff --git a/frontend/src/container/GridGraphLayout/WidgetHeader/config.ts b/frontend/src/container/GridCardLayout/WidgetHeader/config.ts
similarity index 61%
rename from frontend/src/container/GridGraphLayout/WidgetHeader/config.ts
rename to frontend/src/container/GridCardLayout/WidgetHeader/config.ts
index 1e15697049..68e9dd5b6c 100644
--- a/frontend/src/container/GridGraphLayout/WidgetHeader/config.ts
+++ b/frontend/src/container/GridCardLayout/WidgetHeader/config.ts
@@ -3,7 +3,11 @@ import { CSSProperties } from 'react';
const positionCss: CSSProperties['position'] = 'absolute';
-export const spinnerStyles = { position: positionCss, right: '0.5rem' };
+export const spinnerStyles = {
+ position: positionCss,
+ top: '0',
+ right: '0',
+};
export const tooltipStyles = {
fontSize: '1rem',
top: '0.313rem',
@@ -21,3 +25,6 @@ export const overlayStyles: CSSProperties = {
justifyContent: 'center',
position: 'absolute',
};
+
+export const WARNING_MESSAGE =
+ 'Too many timeseries in the result. UI has restricted to showing the top 20. Please check the query if this is needed and contact support@signoz.io if you need to show >20 timeseries in the panel';
diff --git a/frontend/src/container/GridGraphLayout/WidgetHeader/contants.ts b/frontend/src/container/GridCardLayout/WidgetHeader/contants.ts
similarity index 100%
rename from frontend/src/container/GridGraphLayout/WidgetHeader/contants.ts
rename to frontend/src/container/GridCardLayout/WidgetHeader/contants.ts
diff --git a/frontend/src/container/GridGraphLayout/WidgetHeader/index.tsx b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx
similarity index 92%
rename from frontend/src/container/GridGraphLayout/WidgetHeader/index.tsx
rename to frontend/src/container/GridCardLayout/WidgetHeader/index.tsx
index 20ad222e55..a916534082 100644
--- a/frontend/src/container/GridGraphLayout/WidgetHeader/index.tsx
+++ b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx
@@ -5,10 +5,12 @@ import {
EditFilled,
ExclamationCircleOutlined,
FullscreenOutlined,
+ WarningOutlined,
} from '@ant-design/icons';
import { Dropdown, MenuProps, Tooltip, Typography } from 'antd';
import Spinner from 'components/Spinner';
import { QueryParams } from 'constants/query';
+import { PANEL_TYPES } from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import useComponentPermission from 'hooks/useComponentPermission';
import history from 'lib/history';
@@ -27,6 +29,7 @@ import {
overlayStyles,
spinnerStyles,
tooltipStyles,
+ WARNING_MESSAGE,
} from './config';
import { MENUITEM_KEYS_VS_LABELS, MenuItemKeys } from './contants';
import {
@@ -52,6 +55,7 @@ interface IWidgetHeaderProps {
errorMessage: string | undefined;
threshold?: ReactNode;
headerMenuList?: MenuItemKeys[];
+ isWarning: boolean;
}
function WidgetHeader({
@@ -65,7 +69,8 @@ function WidgetHeader({
errorMessage,
threshold,
headerMenuList,
-}: IWidgetHeaderProps): JSX.Element {
+ isWarning,
+}: IWidgetHeaderProps): JSX.Element | null {
const [localHover, setLocalHover] = useState(false);
const [isOpen, setIsOpen] = useState(false);
@@ -126,7 +131,7 @@ function WidgetHeader({
icon: ,
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.View],
isVisible: headerMenuList?.includes(MenuItemKeys.View) || false,
- disabled: queryResponse.isLoading,
+ disabled: queryResponse.isFetching,
},
{
key: MenuItemKeys.Edit,
@@ -158,7 +163,7 @@ function WidgetHeader({
disabled: false,
},
],
- [queryResponse.isLoading, headerMenuList, editWidget, deleteWidget],
+ [headerMenuList, queryResponse.isFetching, editWidget, deleteWidget],
);
const updatedMenuList = useMemo(() => generateMenuList(actions), [actions]);
@@ -175,6 +180,10 @@ function WidgetHeader({
[updatedMenuList, onMenuItemSelectHandler],
);
+ if (widget.id === PANEL_TYPES.EMPTY_WIDGET) {
+ return null;
+ }
+
return (
+
{threshold}
{queryResponse.isFetching && !queryResponse.isError && (
@@ -211,6 +221,12 @@ function WidgetHeader({
)}
+
+ {isWarning && (
+
+
+
+ )}
);
}
diff --git a/frontend/src/container/GridGraphLayout/WidgetHeader/styles.ts b/frontend/src/container/GridCardLayout/WidgetHeader/styles.ts
similarity index 100%
rename from frontend/src/container/GridGraphLayout/WidgetHeader/styles.ts
rename to frontend/src/container/GridCardLayout/WidgetHeader/styles.ts
diff --git a/frontend/src/container/GridGraphLayout/WidgetHeader/types.ts b/frontend/src/container/GridCardLayout/WidgetHeader/types.ts
similarity index 100%
rename from frontend/src/container/GridGraphLayout/WidgetHeader/types.ts
rename to frontend/src/container/GridCardLayout/WidgetHeader/types.ts
diff --git a/frontend/src/container/GridGraphLayout/WidgetHeader/utils.ts b/frontend/src/container/GridCardLayout/WidgetHeader/utils.ts
similarity index 100%
rename from frontend/src/container/GridGraphLayout/WidgetHeader/utils.ts
rename to frontend/src/container/GridCardLayout/WidgetHeader/utils.ts
diff --git a/frontend/src/container/GridCardLayout/config.ts b/frontend/src/container/GridCardLayout/config.ts
new file mode 100644
index 0000000000..3fa9e8e569
--- /dev/null
+++ b/frontend/src/container/GridCardLayout/config.ts
@@ -0,0 +1,17 @@
+import { PANEL_TYPES } from 'constants/queryBuilder';
+import { MenuItemKeys } from 'container/GridCardLayout/WidgetHeader/contants';
+
+export const headerMenuList = [
+ MenuItemKeys.View,
+ MenuItemKeys.Clone,
+ MenuItemKeys.Delete,
+ MenuItemKeys.Edit,
+];
+
+export const EMPTY_WIDGET_LAYOUT = {
+ i: PANEL_TYPES.EMPTY_WIDGET,
+ w: 6,
+ x: 0,
+ h: 2,
+ y: 0,
+};
diff --git a/frontend/src/container/GridCardLayout/index.tsx b/frontend/src/container/GridCardLayout/index.tsx
new file mode 100644
index 0000000000..e715d7d539
--- /dev/null
+++ b/frontend/src/container/GridCardLayout/index.tsx
@@ -0,0 +1,35 @@
+import { useDashboard } from 'providers/Dashboard/Dashboard';
+import { useCallback } from 'react';
+import { Layout } from 'react-grid-layout';
+
+import { EMPTY_WIDGET_LAYOUT } from './config';
+import GraphLayoutContainer from './GridCardLayout';
+
+function GridGraph(): JSX.Element {
+ const {
+ selectedDashboard,
+ setLayouts,
+ handleToggleDashboardSlider,
+ } = useDashboard();
+
+ const { data } = selectedDashboard || {};
+ const { widgets } = data || {};
+
+ const onEmptyWidgetHandler = useCallback(() => {
+ handleToggleDashboardSlider(true);
+
+ setLayouts((preLayout: Layout[]) => [
+ EMPTY_WIDGET_LAYOUT,
+ ...(preLayout || []),
+ ]);
+ }, [handleToggleDashboardSlider, setLayouts]);
+
+ return (
+
+ );
+}
+
+export default GridGraph;
diff --git a/frontend/src/container/GridGraphLayout/styles.ts b/frontend/src/container/GridCardLayout/styles.ts
similarity index 100%
rename from frontend/src/container/GridGraphLayout/styles.ts
rename to frontend/src/container/GridCardLayout/styles.ts
diff --git a/frontend/src/container/GridCardLayout/types.ts b/frontend/src/container/GridCardLayout/types.ts
new file mode 100644
index 0000000000..0d2b678af6
--- /dev/null
+++ b/frontend/src/container/GridCardLayout/types.ts
@@ -0,0 +1,6 @@
+import { Widgets } from 'types/api/dashboard/getAll';
+
+export interface GraphLayoutProps {
+ onAddPanelHandler: VoidFunction;
+ widgets?: Widgets[];
+}
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/GraphManager.tsx b/frontend/src/container/GridGraphLayout/Graph/FullView/GraphManager.tsx
deleted file mode 100644
index 31f528a410..0000000000
--- a/frontend/src/container/GridGraphLayout/Graph/FullView/GraphManager.tsx
+++ /dev/null
@@ -1,203 +0,0 @@
-import './GraphManager.styles.scss';
-
-import { Button, Input } from 'antd';
-import { CheckboxChangeEvent } from 'antd/es/checkbox';
-import { ResizeTable } from 'components/ResizeTable';
-import { Events } from 'constants/events';
-import { useNotifications } from 'hooks/useNotifications';
-import isEqual from 'lodash-es/isEqual';
-import { memo, useCallback, useEffect, useMemo, useState } from 'react';
-import { eventEmitter } from 'utils/getEventEmitter';
-
-import { getGraphVisibilityStateOnDataChange } from '../utils';
-import { getGraphManagerTableColumns } from './TableRender/GraphManagerColumns';
-import { ExtendedChartDataset, GraphManagerProps } from './types';
-import {
- getDefaultTableDataSet,
- saveLegendEntriesToLocalStorage,
-} from './utils';
-
-function GraphManager({
- data,
- name,
- yAxisUnit,
- onToggleModelHandler,
-}: GraphManagerProps): JSX.Element {
- const {
- graphVisibilityStates: localstoredVisibilityStates,
- legendEntry,
- } = useMemo(
- () =>
- getGraphVisibilityStateOnDataChange({
- data,
- isExpandedName: false,
- name,
- }),
- [data, name],
- );
-
- const [graphVisibilityState, setGraphVisibilityState] = useState(
- localstoredVisibilityStates,
- );
-
- const [tableDataSet, setTableDataSet] = useState(
- getDefaultTableDataSet(data),
- );
-
- const { notifications } = useNotifications();
-
- // useEffect for updating graph visibility state on data change
- useEffect(() => {
- const newGraphVisibilityStates = Array(data.datasets.length).fill(
- true,
- );
- data.datasets.forEach((dataset, i) => {
- const index = legendEntry.findIndex(
- (entry) => entry.label === dataset.label,
- );
- if (index !== -1) {
- newGraphVisibilityStates[i] = legendEntry[index].show;
- }
- });
- eventEmitter.emit(Events.UPDATE_GRAPH_VISIBILITY_STATE, {
- name,
- graphVisibilityStates: newGraphVisibilityStates,
- });
- setGraphVisibilityState(newGraphVisibilityStates);
- }, [data, name, legendEntry]);
-
- // useEffect for listening to events event graph legend is clicked
- useEffect(() => {
- const eventListener = eventEmitter.on(
- Events.UPDATE_GRAPH_MANAGER_TABLE,
- (data) => {
- if (data.name === name) {
- const newGraphVisibilityStates = graphVisibilityState;
- newGraphVisibilityStates[data.index] = !newGraphVisibilityStates[
- data.index
- ];
- eventEmitter.emit(Events.UPDATE_GRAPH_VISIBILITY_STATE, {
- name,
- graphVisibilityStates: newGraphVisibilityStates,
- });
- setGraphVisibilityState([...newGraphVisibilityStates]);
- }
- },
- );
- return (): void => {
- eventListener.off(Events.UPDATE_GRAPH_MANAGER_TABLE);
- };
- }, [graphVisibilityState, name]);
-
- const checkBoxOnChangeHandler = useCallback(
- (e: CheckboxChangeEvent, index: number): void => {
- graphVisibilityState[index] = e.target.checked;
- setGraphVisibilityState([...graphVisibilityState]);
- eventEmitter.emit(Events.UPDATE_GRAPH_VISIBILITY_STATE, {
- name,
- graphVisibilityStates: [...graphVisibilityState],
- });
- },
- [graphVisibilityState, name],
- );
-
- const labelClickedHandler = useCallback(
- (labelIndex: number): void => {
- const newGraphVisibilityStates = Array(data.datasets.length).fill(
- false,
- );
- newGraphVisibilityStates[labelIndex] = true;
- setGraphVisibilityState([...newGraphVisibilityStates]);
- eventEmitter.emit(Events.UPDATE_GRAPH_VISIBILITY_STATE, {
- name,
- graphVisibilityStates: newGraphVisibilityStates,
- });
- },
- [data.datasets.length, name],
- );
-
- const columns = useMemo(
- () =>
- getGraphManagerTableColumns({
- data,
- checkBoxOnChangeHandler,
- graphVisibilityState,
- labelClickedHandler,
- yAxisUnit,
- }),
- [
- checkBoxOnChangeHandler,
- data,
- graphVisibilityState,
- labelClickedHandler,
- yAxisUnit,
- ],
- );
-
- const filterHandler = useCallback(
- (event: React.ChangeEvent): void => {
- const value = event.target.value.toString().toLowerCase();
- const updatedDataSet = tableDataSet.map((item) => {
- if (item.label?.toLocaleLowerCase().includes(value)) {
- return { ...item, show: true };
- }
- return { ...item, show: false };
- });
- setTableDataSet(updatedDataSet);
- },
- [tableDataSet],
- );
-
- const saveHandler = useCallback((): void => {
- saveLegendEntriesToLocalStorage({
- data,
- graphVisibilityState,
- name,
- });
- notifications.success({
- message: 'The updated graphs & legends are saved',
- });
- if (onToggleModelHandler) {
- onToggleModelHandler();
- }
- }, [data, graphVisibilityState, name, notifications, onToggleModelHandler]);
-
- const dataSource = tableDataSet.filter((item) => item.show);
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-GraphManager.defaultProps = {
- graphVisibilityStateHandler: undefined,
-};
-
-export default memo(
- GraphManager,
- (prevProps, nextProps) =>
- isEqual(prevProps.data, nextProps.data) && prevProps.name === nextProps.name,
-);
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/GetCheckBox.tsx b/frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/GetCheckBox.tsx
deleted file mode 100644
index 55485be5ad..0000000000
--- a/frontend/src/container/GridGraphLayout/Graph/FullView/TableRender/GetCheckBox.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { CheckboxChangeEvent } from 'antd/es/checkbox';
-import { ColumnType } from 'antd/es/table';
-import { ChartData } from 'chart.js';
-
-import { DataSetProps } from '../types';
-import CustomCheckBox from './CustomCheckBox';
-
-export const getCheckBox = ({
- data,
- checkBoxOnChangeHandler,
- graphVisibilityState,
-}: GetCheckBoxProps): ColumnType => ({
- render: (index: number): JSX.Element => (
-
- ),
-});
-
-interface GetCheckBoxProps {
- data: ChartData;
- checkBoxOnChangeHandler: (e: CheckboxChangeEvent, index: number) => void;
- graphVisibilityState: boolean[];
-}
diff --git a/frontend/src/container/GridGraphLayout/Graph/WidgetGraphComponent.tsx b/frontend/src/container/GridGraphLayout/Graph/WidgetGraphComponent.tsx
deleted file mode 100644
index f40d1e4190..0000000000
--- a/frontend/src/container/GridGraphLayout/Graph/WidgetGraphComponent.tsx
+++ /dev/null
@@ -1,334 +0,0 @@
-import { Typography } from 'antd';
-import { ToggleGraphProps } from 'components/Graph/types';
-import { Events } from 'constants/events';
-import GridPanelSwitch from 'container/GridPanelSwitch';
-import { useChartMutable } from 'hooks/useChartMutable';
-import { useNotifications } from 'hooks/useNotifications';
-import createQueryParams from 'lib/createQueryParams';
-import history from 'lib/history';
-import { isEmpty, isEqual } from 'lodash-es';
-import {
- Dispatch,
- memo,
- SetStateAction,
- useCallback,
- useEffect,
- useMemo,
- useRef,
- useState,
-} from 'react';
-import { useTranslation } from 'react-i18next';
-import { connect, useSelector } from 'react-redux';
-import { useLocation } from 'react-router-dom';
-import { bindActionCreators } from 'redux';
-import { ThunkDispatch } from 'redux-thunk';
-import { DeleteWidget } from 'store/actions/dashboard/deleteWidget';
-import { AppState } from 'store/reducers';
-import AppActions from 'types/actions';
-import AppReducer from 'types/reducer/app';
-import DashboardReducer from 'types/reducer/dashboards';
-import { eventEmitter } from 'utils/getEventEmitter';
-import { v4 } from 'uuid';
-
-import { UpdateDashboard } from '../utils';
-import WidgetHeader from '../WidgetHeader';
-import FullView from './FullView';
-import { PANEL_TYPES_VS_FULL_VIEW_TABLE } from './FullView/contants';
-import { FullViewContainer, Modal } from './styles';
-import { DispatchProps, WidgetGraphComponentProps } from './types';
-import {
- getGraphVisibilityStateOnDataChange,
- toggleGraphsVisibilityInChart,
-} from './utils';
-
-function WidgetGraphComponent({
- enableModel,
- enableWidgetHeader,
- data,
- widget,
- queryResponse,
- errorMessage,
- name,
- yAxisUnit,
- layout = [],
- deleteWidget,
- setLayout,
- onDragSelect,
- onClickHandler,
- threshold,
- headerMenuList,
-}: WidgetGraphComponentProps): JSX.Element {
- const [deleteModal, setDeleteModal] = useState(false);
- const [modal, setModal] = useState(false);
- const [hovered, setHovered] = useState(false);
- const { notifications } = useNotifications();
- const { t } = useTranslation(['common']);
- const { pathname } = useLocation();
-
- const { graphVisibilityStates: localstoredVisibilityStates } = useMemo(
- () =>
- getGraphVisibilityStateOnDataChange({
- data,
- isExpandedName: true,
- name,
- }),
- [data, name],
- );
-
- const [graphsVisibilityStates, setGraphsVisilityStates] = useState(
- localstoredVisibilityStates,
- );
-
- const { dashboards } = useSelector(
- (state) => state.dashboards,
- );
- const [selectedDashboard] = dashboards;
-
- const canModifyChart = useChartMutable({
- panelType: widget.panelTypes,
- panelTypeAndGraphManagerVisibility: PANEL_TYPES_VS_FULL_VIEW_TABLE,
- });
-
- const lineChartRef = useRef();
-
- // Updating the visibility state of the graph on data change according to global time range
- useEffect(() => {
- if (canModifyChart) {
- const newGraphVisibilityState = getGraphVisibilityStateOnDataChange({
- data,
- isExpandedName: true,
- name,
- });
- setGraphsVisilityStates(newGraphVisibilityState.graphVisibilityStates);
- }
- }, [canModifyChart, data, name]);
-
- useEffect(() => {
- const eventListener = eventEmitter.on(
- Events.UPDATE_GRAPH_VISIBILITY_STATE,
- (data) => {
- if (data.name === `${name}expanded` && canModifyChart) {
- setGraphsVisilityStates([...data.graphVisibilityStates]);
- }
- },
- );
- return (): void => {
- eventListener.off(Events.UPDATE_GRAPH_VISIBILITY_STATE);
- };
- }, [canModifyChart, name]);
-
- useEffect(() => {
- if (canModifyChart && lineChartRef.current) {
- toggleGraphsVisibilityInChart({
- graphsVisibilityStates,
- lineChartRef,
- });
- }
- }, [graphsVisibilityStates, canModifyChart]);
-
- const { featureResponse } = useSelector(
- (state) => state.app,
- );
- const onToggleModal = useCallback(
- (func: Dispatch>) => {
- func((value) => !value);
- },
- [],
- );
-
- const onDeleteHandler = useCallback(() => {
- const isEmptyWidget = widget?.id === 'empty' || isEmpty(widget);
- const widgetId = isEmptyWidget ? layout[0].i : widget?.id;
-
- featureResponse
- .refetch()
- .then(() => {
- deleteWidget({ widgetId, setLayout });
- onToggleModal(setDeleteModal);
- })
- .catch(() => {
- notifications.error({
- message: t('common:something_went_wrong'),
- });
- });
- }, [
- widget,
- layout,
- featureResponse,
- deleteWidget,
- setLayout,
- onToggleModal,
- notifications,
- t,
- ]);
-
- const onCloneHandler = async (): Promise => {
- const uuid = v4();
-
- const layout = [
- {
- i: uuid,
- w: 6,
- x: 0,
- h: 2,
- y: 0,
- },
- ...(selectedDashboard.data.layout || []),
- ];
-
- if (widget) {
- await UpdateDashboard(
- {
- data: selectedDashboard.data,
- generateWidgetId: uuid,
- graphType: widget?.panelTypes,
- selectedDashboard,
- layout,
- widgetData: widget,
- isRedirected: false,
- },
- notifications,
- ).then(() => {
- notifications.success({
- message: 'Panel cloned successfully, redirecting to new copy.',
- });
-
- const queryParams = {
- graphType: widget?.panelTypes,
- widgetId: uuid,
- };
- history.push(`${pathname}/new?${createQueryParams(queryParams)}`);
- });
- }
- };
-
- const handleOnView = (): void => {
- onToggleModal(setModal);
- };
-
- const handleOnDelete = (): void => {
- onToggleModal(setDeleteModal);
- };
-
- const onDeleteModelHandler = (): void => {
- onToggleModal(setDeleteModal);
- };
-
- const onToggleModelHandler = (): void => {
- onToggleModal(setModal);
- };
-
- const getModals = (): JSX.Element => (
- <>
-
- Are you sure you want to delete this widget
-
-
-
-
-
-
-
- >
- );
-
- return (
- {
- setHovered(true);
- }}
- onFocus={(): void => {
- setHovered(true);
- }}
- onMouseOut={(): void => {
- setHovered(false);
- }}
- onBlur={(): void => {
- setHovered(false);
- }}
- >
- {enableModel && getModals()}
- {!isEmpty(widget) && data && (
- <>
- {enableWidgetHeader && (
-
-
-
- )}
-
- >
- )}
-
- );
-}
-
-WidgetGraphComponent.defaultProps = {
- yAxisUnit: undefined,
- layout: undefined,
- setLayout: undefined,
- onDragSelect: undefined,
- onClickHandler: undefined,
-};
-
-const mapDispatchToProps = (
- dispatch: ThunkDispatch,
-): DispatchProps => ({
- deleteWidget: bindActionCreators(DeleteWidget, dispatch),
-});
-
-export default connect(
- null,
- mapDispatchToProps,
-)(
- memo(
- WidgetGraphComponent,
- (prevProps, nextProps) =>
- isEqual(prevProps.data, nextProps.data) && prevProps.name === nextProps.name,
- ),
-);
diff --git a/frontend/src/container/GridGraphLayout/Graph/index.tsx b/frontend/src/container/GridGraphLayout/Graph/index.tsx
deleted file mode 100644
index 94b9d10252..0000000000
--- a/frontend/src/container/GridGraphLayout/Graph/index.tsx
+++ /dev/null
@@ -1,187 +0,0 @@
-import { ChartData } from 'chart.js';
-import Spinner from 'components/Spinner';
-import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
-import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
-import usePreviousValue from 'hooks/usePreviousValue';
-import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
-import getChartData from 'lib/getChartData';
-import isEmpty from 'lodash-es/isEmpty';
-import { memo, useMemo, useState } from 'react';
-import { useInView } from 'react-intersection-observer';
-import { useSelector } from 'react-redux';
-import { AppState } from 'store/reducers';
-import DashboardReducer from 'types/reducer/dashboards';
-import { GlobalReducer } from 'types/reducer/globalTime';
-import { getSelectedDashboardVariable } from 'utils/dashboard/selectedDashboard';
-
-import EmptyWidget from '../EmptyWidget';
-import { MenuItemKeys } from '../WidgetHeader/contants';
-import { GridCardGraphProps } from './types';
-import WidgetGraphComponent from './WidgetGraphComponent';
-
-function GridCardGraph({
- widget,
- name,
- yAxisUnit,
- layout = [],
- setLayout,
- onDragSelect,
- onClickHandler,
- headerMenuList = [MenuItemKeys.View],
- isQueryEnabled,
- threshold,
-}: GridCardGraphProps): JSX.Element {
- const { isAddWidget } = useSelector(
- (state) => state.dashboards,
- );
-
- const { ref: graphRef, inView: isGraphVisible } = useInView({
- threshold: 0,
- triggerOnce: true,
- initialInView: false,
- });
-
- const [errorMessage, setErrorMessage] = useState('');
-
- const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector<
- AppState,
- GlobalReducer
- >((state) => state.globalTime);
- const { dashboards } = useSelector(
- (state) => state.dashboards,
- );
-
- const variables = getSelectedDashboardVariable(dashboards);
-
- const updatedQuery = useStepInterval(widget?.query);
-
- const isEmptyWidget = useMemo(
- () => widget?.id === 'empty' || isEmpty(widget),
- [widget],
- );
-
- const queryResponse = useGetQueryRange(
- {
- selectedTime: widget?.timePreferance,
- graphType: widget?.panelTypes,
- query: updatedQuery,
- globalSelectedInterval,
- variables: getDashboardVariables(),
- },
- {
- queryKey: [
- `GetMetricsQueryRange-${widget?.timePreferance}-${globalSelectedInterval}-${widget?.id}`,
- maxTime,
- minTime,
- globalSelectedInterval,
- variables,
- widget?.query,
- widget?.panelTypes,
- ],
- keepPreviousData: true,
- enabled: isGraphVisible && !isEmptyWidget && isQueryEnabled && !isAddWidget,
- refetchOnMount: false,
- onError: (error) => {
- setErrorMessage(error.message);
- },
- },
- );
-
- const chartData = useMemo(
- () =>
- getChartData({
- queryData: [
- {
- queryData: queryResponse?.data?.payload?.data?.result || [],
- },
- ],
- }),
- [queryResponse],
- );
-
- const prevChartDataSetRef = usePreviousValue(chartData);
-
- const isEmptyLayout = widget?.id === 'empty' || isEmpty(widget);
-
- if (queryResponse.isRefetching || queryResponse.isLoading) {
- return ;
- }
-
- if ((queryResponse.isError && !isEmptyLayout) || !isQueryEnabled) {
- return (
-
- {!isEmpty(widget) && prevChartDataSetRef && (
-
- )}
-
- );
- }
-
- if (!isEmpty(widget) && prevChartDataSetRef?.labels) {
- return (
-
-
-
- );
- }
-
- return (
-
- {!isEmpty(widget) && !!queryResponse.data?.payload && (
-
- )}
-
- {isEmptyLayout && }
-
- );
-}
-
-GridCardGraph.defaultProps = {
- onDragSelect: undefined,
- onClickHandler: undefined,
- isQueryEnabled: true,
- threshold: undefined,
- headerMenuList: [MenuItemKeys.View],
-};
-
-export default memo(GridCardGraph);
diff --git a/frontend/src/container/GridGraphLayout/GraphLayout.tsx b/frontend/src/container/GridGraphLayout/GraphLayout.tsx
deleted file mode 100644
index 6fc6aca63f..0000000000
--- a/frontend/src/container/GridGraphLayout/GraphLayout.tsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import { PlusOutlined, SaveFilled } from '@ant-design/icons';
-import { PANEL_TYPES } from 'constants/queryBuilder';
-import useComponentPermission from 'hooks/useComponentPermission';
-import { useIsDarkMode } from 'hooks/useDarkMode';
-import { Dispatch, SetStateAction } from 'react';
-import { Layout } from 'react-grid-layout';
-import { useSelector } from 'react-redux';
-import { AppState } from 'store/reducers';
-import { Widgets } from 'types/api/dashboard/getAll';
-import AppReducer from 'types/reducer/app';
-import DashboardReducer from 'types/reducer/dashboards';
-
-import { LayoutProps, State } from '.';
-import {
- Button,
- ButtonContainer,
- Card,
- CardContainer,
- ReactGridLayout,
-} from './styles';
-
-function GraphLayout({
- layouts,
- saveLayoutState,
- onLayoutSaveHandler,
- addPanelLoading,
- onAddPanelHandler,
- onLayoutChangeHandler,
- widgets,
- setLayout,
-}: GraphLayoutProps): JSX.Element {
- const { isAddWidget } = useSelector(
- (state) => state.dashboards,
- );
- const { role } = useSelector((state) => state.app);
- const isDarkMode = useIsDarkMode();
-
- const [saveLayoutPermission, addPanelPermission] = useComponentPermission(
- ['save_layout', 'add_panel'],
- role,
- );
-
- return (
- <>
-
- {saveLayoutPermission && (
-
- )}
-
- {addPanelPermission && (
- }
- >
- Add Panel
-
- )}
-
-
-
- {layouts.map(({ Component, ...rest }) => {
- const currentWidget = (widgets || [])?.find((e) => e.id === rest.i);
-
- return (
-
-
-
-
-
- );
- })}
-
- >
- );
-}
-
-interface GraphLayoutProps {
- layouts: LayoutProps[];
- saveLayoutState: State;
- onLayoutSaveHandler: (layout: Layout[]) => Promise;
- addPanelLoading: boolean;
- onAddPanelHandler: VoidFunction;
- onLayoutChangeHandler: (layout: Layout[]) => Promise;
- widgets: Widgets[] | undefined;
- setLayout: Dispatch>;
-}
-
-export default GraphLayout;
diff --git a/frontend/src/container/GridGraphLayout/config.ts b/frontend/src/container/GridGraphLayout/config.ts
deleted file mode 100644
index 0357c7795c..0000000000
--- a/frontend/src/container/GridGraphLayout/config.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { MenuItemKeys } from 'container/GridGraphLayout/WidgetHeader/contants';
-
-export const headerMenuList = [
- MenuItemKeys.View,
- MenuItemKeys.Clone,
- MenuItemKeys.Delete,
- MenuItemKeys.Edit,
-];
diff --git a/frontend/src/container/GridGraphLayout/index.tsx b/frontend/src/container/GridGraphLayout/index.tsx
deleted file mode 100644
index fe65154fd9..0000000000
--- a/frontend/src/container/GridGraphLayout/index.tsx
+++ /dev/null
@@ -1,383 +0,0 @@
-/* eslint-disable react/no-unstable-nested-components */
-
-import updateDashboardApi from 'api/dashboard/update';
-import { PANEL_TYPES } from 'constants/queryBuilder';
-import useComponentPermission from 'hooks/useComponentPermission';
-import { useNotifications } from 'hooks/useNotifications';
-import {
- Dispatch,
- SetStateAction,
- useCallback,
- useEffect,
- useState,
-} from 'react';
-import { Layout } from 'react-grid-layout';
-import { useTranslation } from 'react-i18next';
-import { connect, useDispatch, useSelector } from 'react-redux';
-import { bindActionCreators, Dispatch as ReduxDispatch } from 'redux';
-import { ThunkDispatch } from 'redux-thunk';
-import { AppDispatch } from 'store';
-import { UpdateTimeInterval } from 'store/actions';
-import {
- ToggleAddWidget,
- ToggleAddWidgetProps,
-} from 'store/actions/dashboard/toggleAddWidget';
-import { AppState } from 'store/reducers';
-import AppActions from 'types/actions';
-import { UPDATE_DASHBOARD } from 'types/actions/dashboard';
-import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
-import AppReducer from 'types/reducer/app';
-import DashboardReducer from 'types/reducer/dashboards';
-
-import { headerMenuList } from './config';
-import Graph from './Graph';
-import GraphLayoutContainer from './GraphLayout';
-import { UpdateDashboard } from './utils';
-
-export const getPreLayouts = (
- widgets: Widgets[] | undefined,
- layout: Layout[],
-): LayoutProps[] =>
- layout.map((e, index) => ({
- ...e,
- Component: ({ setLayout }: ComponentProps): JSX.Element => {
- const widget = widgets?.find((widget) => widget.id === e.i);
-
- return (
-
- );
- },
- }));
-
-function GridGraph(props: Props): JSX.Element {
- const { toggleAddWidget } = props;
- const [addPanelLoading, setAddPanelLoading] = useState(false);
- const { t } = useTranslation(['common']);
- const { dashboards, isAddWidget } = useSelector(
- (state) => state.dashboards,
- );
- const { role } = useSelector((state) => state.app);
-
- const [saveLayoutPermission] = useComponentPermission(['save_layout'], role);
- const [saveLayoutState, setSaveLayoutState] = useState({
- loading: false,
- error: false,
- errorMessage: '',
- payload: [],
- });
- const [selectedDashboard] = dashboards;
- const { data } = selectedDashboard;
- const { widgets } = data;
- const dispatch: AppDispatch = useDispatch>();
-
- const [layouts, setLayout] = useState(
- getPreLayouts(widgets, selectedDashboard.data.layout || []),
- );
-
- const onDragSelect = useCallback(
- (start: number, end: number) => {
- const startTimestamp = Math.trunc(start);
- const endTimestamp = Math.trunc(end);
-
- if (startTimestamp !== endTimestamp) {
- dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
- }
- },
- [dispatch],
- );
-
- const { notifications } = useNotifications();
-
- useEffect(() => {
- (async (): Promise => {
- if (!isAddWidget) {
- const isEmptyLayoutPresent = layouts.find((e) => e.i === 'empty');
- if (isEmptyLayoutPresent) {
- // non empty layout
- const updatedLayout = layouts.filter((e) => e.i !== 'empty');
- // non widget
- const updatedWidget = widgets?.filter((e) => e.id !== 'empty');
- setLayout(updatedLayout);
-
- const updatedDashboard: Dashboard = {
- ...selectedDashboard,
- data: {
- ...selectedDashboard.data,
- layout: updatedLayout,
- widgets: updatedWidget,
- },
- };
-
- await updateDashboardApi({
- data: updatedDashboard.data,
- uuid: updatedDashboard.uuid,
- });
-
- dispatch({
- type: UPDATE_DASHBOARD,
- payload: updatedDashboard,
- });
- }
- }
- })();
- }, [dispatch, isAddWidget, layouts, selectedDashboard, widgets]);
-
- const { featureResponse } = useSelector(
- (state) => state.app,
- );
-
- const errorMessage = t('common:something_went_wrong');
-
- const onLayoutSaveHandler = useCallback(
- async (layout: Layout[]) => {
- try {
- setSaveLayoutState((state) => ({
- ...state,
- error: false,
- errorMessage: '',
- loading: true,
- }));
-
- featureResponse
- .refetch()
- .then(async () => {
- const updatedDashboard: Dashboard = {
- ...selectedDashboard,
- data: {
- title: data.title,
- description: data.description,
- name: data.name,
- tags: data.tags,
- widgets: data.widgets,
- variables: data.variables,
- layout,
- },
- uuid: selectedDashboard.uuid,
- };
- // Save layout only when users has the has the permission to do so.
- if (saveLayoutPermission) {
- const response = await updateDashboardApi(updatedDashboard);
- if (response.statusCode === 200) {
- setSaveLayoutState((state) => ({
- ...state,
- error: false,
- errorMessage: '',
- loading: false,
- }));
- dispatch({
- type: UPDATE_DASHBOARD,
- payload: updatedDashboard,
- });
- } else {
- setSaveLayoutState((state) => ({
- ...state,
- error: true,
- errorMessage: response.error || errorMessage,
- loading: false,
- }));
- }
- }
- })
- .catch(() => {
- setSaveLayoutState((state) => ({
- ...state,
- error: true,
- errorMessage,
- loading: false,
- }));
- notifications.error({
- message: errorMessage,
- });
- });
- } catch (error) {
- notifications.error({
- message: errorMessage,
- });
- }
- },
- [
- data.description,
- data.name,
- data.tags,
- data.title,
- data.variables,
- data.widgets,
- dispatch,
- errorMessage,
- featureResponse,
- notifications,
- saveLayoutPermission,
- selectedDashboard,
- ],
- );
-
- const setLayoutFunction = useCallback(
- (layout: Layout[]) => {
- setLayout(
- layout.map((e) => {
- const currentWidget =
- widgets?.find((widget) => widget.id === e.i) || ({} as Widgets);
-
- return {
- ...e,
- Component: (): JSX.Element => (
-
- ),
- };
- }),
- );
- },
- [widgets, onDragSelect],
- );
-
- const onEmptyWidgetHandler = useCallback(async () => {
- try {
- const id = 'empty';
-
- const layout = [
- {
- i: id,
- w: 6,
- x: 0,
- h: 2,
- y: 0,
- },
- ...(data.layout || []),
- ];
-
- await UpdateDashboard(
- {
- data,
- generateWidgetId: id,
- graphType: PANEL_TYPES.EMPTY_WIDGET,
- selectedDashboard,
- layout,
- isRedirected: false,
- },
- notifications,
- );
-
- setLayoutFunction(layout);
- } catch (error) {
- notifications.error({
- message: error instanceof Error ? error.toString() : errorMessage,
- });
- }
- }, [data, selectedDashboard, setLayoutFunction, notifications, errorMessage]);
-
- const onLayoutChangeHandler = async (layout: Layout[]): Promise => {
- setLayoutFunction(layout);
-
- // await onLayoutSaveHandler(layout);
- };
-
- const onAddPanelHandler = useCallback(() => {
- try {
- setAddPanelLoading(true);
- featureResponse
- .refetch()
- .then(() => {
- const isEmptyLayoutPresent =
- layouts.find((e) => e.i === 'empty') !== undefined;
-
- if (!isEmptyLayoutPresent) {
- onEmptyWidgetHandler()
- .then(() => {
- setAddPanelLoading(false);
- toggleAddWidget(true);
- })
- .catch(() => {
- notifications.error({
- message: errorMessage,
- });
- });
- } else {
- toggleAddWidget(true);
- setAddPanelLoading(false);
- }
- })
- .catch(() =>
- notifications.error({
- message: errorMessage,
- }),
- );
- } catch (error) {
- notifications.error({
- message: errorMessage,
- });
- }
- }, [
- featureResponse,
- layouts,
- onEmptyWidgetHandler,
- toggleAddWidget,
- notifications,
- errorMessage,
- ]);
-
- useEffect(
- () => (): void => {
- toggleAddWidget(false);
- },
- [toggleAddWidget],
- );
-
- return (
-
- );
-}
-
-interface ComponentProps {
- setLayout: Dispatch>;
-}
-
-export interface LayoutProps extends Layout {
- Component: (props: ComponentProps) => JSX.Element;
-}
-
-export interface State {
- loading: boolean;
- error: boolean;
- payload: Layout[];
- errorMessage: string;
-}
-
-interface DispatchProps {
- toggleAddWidget: (
- props: ToggleAddWidgetProps,
- ) => (dispatch: ReduxDispatch) => void;
-}
-
-const mapDispatchToProps = (
- dispatch: ThunkDispatch,
-): DispatchProps => ({
- toggleAddWidget: bindActionCreators(ToggleAddWidget, dispatch),
-});
-
-type Props = DispatchProps;
-
-export default connect(null, mapDispatchToProps)(GridGraph);
diff --git a/frontend/src/container/GridGraphLayout/utils.ts b/frontend/src/container/GridGraphLayout/utils.ts
deleted file mode 100644
index a18fe52886..0000000000
--- a/frontend/src/container/GridGraphLayout/utils.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { NotificationInstance } from 'antd/es/notification/interface';
-import updateDashboardApi from 'api/dashboard/update';
-import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
-import { Layout } from 'react-grid-layout';
-import store from 'store';
-import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
-
-export const UpdateDashboard = async (
- {
- data,
- graphType,
- generateWidgetId,
- layout,
- selectedDashboard,
- isRedirected,
- widgetData,
- }: UpdateDashboardProps,
- notify: NotificationInstance,
-): Promise => {
- const copyTitle = `${widgetData?.title} - Copy`;
- const updatedSelectedDashboard: Dashboard = {
- ...selectedDashboard,
- data: {
- title: data.title,
- description: data.description,
- name: data.name,
- tags: data.tags,
- variables: data.variables,
- widgets: [
- ...(data.widgets || []),
- {
- description: widgetData?.description || '',
- id: generateWidgetId,
- isStacked: false,
- nullZeroValues: widgetData?.nullZeroValues || '',
- opacity: '',
- panelTypes: graphType,
- query: widgetData?.query || initialQueriesMap.metrics,
- timePreferance: widgetData?.timePreferance || 'GLOBAL_TIME',
- title: widgetData ? copyTitle : '',
- yAxisUnit: widgetData?.yAxisUnit,
- },
- ],
- layout,
- },
- uuid: selectedDashboard.uuid,
- };
-
- const response = await updateDashboardApi(updatedSelectedDashboard);
-
- if (response.payload) {
- store.dispatch({
- type: 'UPDATE_DASHBOARD',
- payload: response.payload,
- });
- }
-
- if (isRedirected) {
- if (response.statusCode === 200) {
- return response.payload;
- }
- notify.error({
- message: response.error || 'Something went wrong',
- });
- return undefined;
- }
- return undefined;
-};
-
-interface UpdateDashboardProps {
- data: Dashboard['data'];
- graphType: PANEL_TYPES;
- generateWidgetId: string;
- layout: Layout[];
- selectedDashboard: Dashboard;
- isRedirected: boolean;
- widgetData?: Widgets;
-}
diff --git a/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx b/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx
index 3a7f406b4e..aa658d56cc 100644
--- a/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx
+++ b/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx
@@ -9,11 +9,7 @@ import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { useDispatch } from 'react-redux';
import { generatePath } from 'react-router-dom';
-import { Dispatch } from 'redux';
-import AppActions from 'types/actions';
-import { FLUSH_DASHBOARD } from 'types/actions/dashboard';
import { DashboardData } from 'types/api/dashboard/getAll';
import { EditorContainer, FooterContainer } from './styles';
@@ -31,8 +27,6 @@ function ImportJSON({
);
const [isFeatureAlert, setIsFeatureAlert] = useState(false);
- const dispatch = useDispatch>();
-
const [dashboardCreating, setDashboardCreating] = useState(false);
const [editorValue, setEditorValue] = useState('');
@@ -77,16 +71,11 @@ function ImportJSON({
});
if (response.statusCode === 200) {
- dispatch({
- type: FLUSH_DASHBOARD,
- });
- setTimeout(() => {
- history.push(
- generatePath(ROUTES.DASHBOARD, {
- dashboardId: response.payload.uuid,
- }),
- );
- }, 10);
+ history.push(
+ generatePath(ROUTES.DASHBOARD, {
+ dashboardId: response.payload.uuid,
+ }),
+ );
} else if (response.error === 'feature usage exceeded') {
setIsFeatureAlert(true);
notifications.error({
diff --git a/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx b/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx
index 39d3c02a23..03c0ac9912 100644
--- a/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx
+++ b/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx
@@ -1,37 +1,36 @@
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Modal } from 'antd';
+import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
+import { useDeleteDashboard } from 'hooks/dashboard/useDeleteDashboard';
import { useCallback } from 'react';
-import { connect } from 'react-redux';
-import { bindActionCreators, Dispatch } from 'redux';
-import { ThunkDispatch } from 'redux-thunk';
-import { DeleteDashboard, DeleteDashboardProps } from 'store/actions';
-import AppActions from 'types/actions';
+import { useQueryClient } from 'react-query';
import { Data } from '../index';
import { TableLinkText } from './styles';
-function DeleteButton({
- deleteDashboard,
- id,
- refetchDashboardList,
-}: DeleteButtonProps): JSX.Element {
+function DeleteButton({ id }: Data): JSX.Element {
const [modal, contextHolder] = Modal.useModal();
+ const queryClient = useQueryClient();
+
+ const deleteDashboardMutation = useDeleteDashboard(id);
+
const openConfirmationDialog = useCallback((): void => {
modal.confirm({
title: 'Do you really want to delete this dashboard?',
icon: ,
onOk() {
- deleteDashboard({
- uuid: id,
- refetch: refetchDashboardList,
+ deleteDashboardMutation.mutateAsync(undefined, {
+ onSuccess: () => {
+ queryClient.invalidateQueries([REACT_QUERY_KEY.GET_ALL_DASHBOARDS]);
+ },
});
},
okText: 'Delete',
okButtonProps: { danger: true },
centered: true,
});
- }, [modal, deleteDashboard, id, refetchDashboardList]);
+ }, [modal, deleteDashboardMutation, queryClient]);
return (
<>
@@ -44,37 +43,12 @@ function DeleteButton({
);
}
-interface DispatchProps {
- deleteDashboard: ({
- uuid,
- }: DeleteDashboardProps) => (dispatch: Dispatch) => void;
-}
-
-const mapDispatchToProps = (
- dispatch: ThunkDispatch,
-): DispatchProps => ({
- deleteDashboard: bindActionCreators(DeleteDashboard, dispatch),
-});
-
-export type DeleteButtonProps = Data & DispatchProps;
-
-const WrapperDeleteButton = connect(null, mapDispatchToProps)(DeleteButton);
-
// This is to avoid the type collision
function Wrapper(props: Data): JSX.Element {
- const {
- createdBy,
- description,
- id,
- key,
- refetchDashboardList,
- lastUpdatedTime,
- name,
- tags,
- } = props;
+ const { createdBy, description, id, key, lastUpdatedTime, name, tags } = props;
return (
-
);
diff --git a/frontend/src/container/ListOfDashboard/index.tsx b/frontend/src/container/ListOfDashboard/index.tsx
index 6041b8baf4..87f47e54af 100644
--- a/frontend/src/container/ListOfDashboard/index.tsx
+++ b/frontend/src/container/ListOfDashboard/index.tsx
@@ -17,21 +17,11 @@ import SearchFilter from 'container/ListOfDashboard/SearchFilter';
import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard';
import useComponentPermission from 'hooks/useComponentPermission';
import history from 'lib/history';
-import {
- Dispatch,
- Key,
- useCallback,
- useEffect,
- useMemo,
- useState,
-} from 'react';
+import { Key, useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { UseQueryResult } from 'react-query';
-import { useDispatch, useSelector } from 'react-redux';
+import { useSelector } from 'react-redux';
import { generatePath } from 'react-router-dom';
import { AppState } from 'store/reducers';
-import AppActions from 'types/actions';
-import { GET_ALL_DASHBOARD_SUCCESS } from 'types/actions/dashboard';
import { Dashboard } from 'types/api/dashboard/getAll';
import AppReducer from 'types/reducer/app';
import { popupContainer } from 'utils/selectPopupContainer';
@@ -40,9 +30,7 @@ import ImportJSON from './ImportJSON';
import { ButtonContainer, NewDashboardButton, TableContainer } from './styles';
import Createdby from './TableComponents/CreatedBy';
import DateComponent from './TableComponents/Date';
-import DeleteButton, {
- DeleteButtonProps,
-} from './TableComponents/DeleteButton';
+import DeleteButton from './TableComponents/DeleteButton';
import Name from './TableComponents/Name';
import Tags from './TableComponents/Tags';
@@ -53,7 +41,6 @@ function ListOfAllDashboard(): JSX.Element {
refetch: refetchDashboardList,
} = useGetAllDashboard();
- const dispatch = useDispatch>();
const { role } = useSelector((state) => state.app);
const [action, createNewDashboard, newDashboard] = useComponentPermission(
@@ -134,31 +121,12 @@ function ListOfAllDashboard(): JSX.Element {
title: 'Action',
dataIndex: '',
width: 40,
- render: ({
- createdBy,
- description,
- id,
- key,
- lastUpdatedTime,
- name,
- tags,
- }: DeleteButtonProps) => (
-
- ),
+ render: DeleteButton,
});
}
return tableColumns;
- }, [action, refetchDashboardList]);
+ }, [action]);
const data: Data[] =
filteredDashboards?.map((e) => ({
@@ -186,10 +154,6 @@ function ListOfAllDashboard(): JSX.Element {
});
if (response.statusCode === 200) {
- dispatch({
- type: GET_ALL_DASHBOARD_SUCCESS,
- payload: [],
- });
history.push(
generatePath(ROUTES.DASHBOARD, {
dashboardId: response.payload.uuid,
@@ -210,7 +174,7 @@ function ListOfAllDashboard(): JSX.Element {
errorMessage: (error as AxiosError).toString() || 'Something went Wrong',
});
}
- }, [newDashboardState, t, dispatch]);
+ }, [newDashboardState, t]);
const getText = useCallback(() => {
if (!newDashboardState.error && !newDashboardState.loading) {
@@ -352,7 +316,6 @@ export interface Data {
createdBy: string;
lastUpdatedTime: string;
id: string;
- refetchDashboardList: UseQueryResult['refetch'];
}
export default ListOfAllDashboard;
diff --git a/frontend/src/container/LiveLogs/LiveLogsContainer/index.tsx b/frontend/src/container/LiveLogs/LiveLogsContainer/index.tsx
index 49a071d17e..e47ae535a6 100644
--- a/frontend/src/container/LiveLogs/LiveLogsContainer/index.tsx
+++ b/frontend/src/container/LiveLogs/LiveLogsContainer/index.tsx
@@ -11,11 +11,11 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import useDebouncedFn from 'hooks/useDebouncedFunction';
import { useEventSourceEvent } from 'hooks/useEventSourceEvent';
import { useNotifications } from 'hooks/useNotifications';
+import { prepareQueryRangePayload } from 'lib/dashboard/prepareQueryRangePayload';
import { useEventSource } from 'providers/EventSource';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
-import { prepareQueryRangePayload } from 'store/actions/dashboard/prepareQueryRangePayload';
import { AppState } from 'store/reducers';
import { ILog } from 'types/api/logs/log';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
diff --git a/frontend/src/container/LogsExplorerChart/index.tsx b/frontend/src/container/LogsExplorerChart/index.tsx
index a64f8eb382..ec329907f3 100644
--- a/frontend/src/container/LogsExplorerChart/index.tsx
+++ b/frontend/src/container/LogsExplorerChart/index.tsx
@@ -48,7 +48,7 @@ function LogsExplorerChart({
) : (
({
description: '',
id: v4(),
@@ -17,4 +18,5 @@ export const getWidgetQueryBuilder = ({
query,
timePreferance: 'GLOBAL_TIME',
title,
+ yAxisUnit,
});
diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx
index 83e6da6db2..15af9981c0 100644
--- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx
@@ -1,6 +1,6 @@
import { Col } from 'antd';
import { PANEL_TYPES } from 'constants/queryBuilder';
-import Graph from 'container/GridGraphLayout/Graph/';
+import Graph from 'container/GridCardLayout/GridCard';
import {
databaseCallsAvgDuration,
databaseCallsRPS,
@@ -65,6 +65,7 @@ function DBCall(): JSX.Element {
},
title: GraphTitle.DATABASE_CALLS_RPS,
panelTypes: PANEL_TYPES.TIME_SERIES,
+ yAxisUnit: 'reqps',
}),
[servicename, tagFilterItems],
);
@@ -83,6 +84,7 @@ function DBCall(): JSX.Element {
},
title: GraphTitle.DATABASE_CALLS_AVG_DURATION,
panelTypes: PANEL_TYPES.TIME_SERIES,
+ yAxisUnit: 'ms',
}),
[servicename, tagFilterItems],
);
@@ -107,7 +109,6 @@ function DBCall(): JSX.Element {
{
onGraphClickHandler(setSelectedTimeStamp)(
ChartEvent,
@@ -135,12 +136,12 @@ function DBCall(): JSX.Element {
>
View Traces
+
{
onGraphClickHandler(setSelectedTimeStamp)(
ChartEvent,
diff --git a/frontend/src/container/MetricsApplication/Tabs/External.tsx b/frontend/src/container/MetricsApplication/Tabs/External.tsx
index 3abe8d4ca4..cda6e9275c 100644
--- a/frontend/src/container/MetricsApplication/Tabs/External.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/External.tsx
@@ -1,6 +1,6 @@
import { Col } from 'antd';
import { PANEL_TYPES } from 'constants/queryBuilder';
-import Graph from 'container/GridGraphLayout/Graph/';
+import Graph from 'container/GridCardLayout/GridCard';
import {
externalCallDuration,
externalCallDurationByAddress,
@@ -56,6 +56,7 @@ function External(): JSX.Element {
},
title: GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE,
panelTypes: PANEL_TYPES.TIME_SERIES,
+ yAxisUnit: '%',
}),
[servicename, tagFilterItems],
);
@@ -80,6 +81,7 @@ function External(): JSX.Element {
},
title: GraphTitle.EXTERNAL_CALL_DURATION,
panelTypes: PANEL_TYPES.TIME_SERIES,
+ yAxisUnit: 'ms',
}),
[servicename, tagFilterItems],
);
@@ -100,6 +102,7 @@ function External(): JSX.Element {
},
title: GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS,
panelTypes: PANEL_TYPES.TIME_SERIES,
+ yAxisUnit: 'reqps',
}),
[servicename, tagFilterItems],
);
@@ -120,6 +123,7 @@ function External(): JSX.Element {
},
title: GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS,
panelTypes: PANEL_TYPES.TIME_SERIES,
+ yAxisUnit: 'ms',
}),
[servicename, tagFilterItems],
);
@@ -146,7 +150,6 @@ function External(): JSX.Element {
{
onGraphClickHandler(setSelectedTimeStamp)(
ChartEvent,
@@ -181,7 +184,6 @@ function External(): JSX.Element {
{
onGraphClickHandler(setSelectedTimeStamp)(
ChartEvent,
@@ -217,7 +219,6 @@ function External(): JSX.Element {
{
onGraphClickHandler(setSelectedTimeStamp)(
ChartEvent,
@@ -252,7 +253,6 @@ function External(): JSX.Element {
{
onGraphClickHandler(setSelectedTimeStamp)(
ChartEvent,
diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx
index b7ab171ccd..d67053e1e0 100644
--- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx
@@ -131,6 +131,7 @@ function Application(): JSX.Element {
},
title: GraphTitle.RATE_PER_OPS,
panelTypes: PANEL_TYPES.TIME_SERIES,
+ yAxisUnit: 'ops',
}),
[servicename, tagFilterItems, topLevelOperationsRoute],
);
@@ -151,6 +152,7 @@ function Application(): JSX.Element {
},
title: GraphTitle.ERROR_PERCENTAGE,
panelTypes: PANEL_TYPES.TIME_SERIES,
+ yAxisUnit: '%',
}),
[servicename, tagFilterItems, topLevelOperationsRoute],
);
@@ -222,7 +224,6 @@ function Application(): JSX.Element {
topLevelOperationsIsError={topLevelOperationsIsError}
name="operations_per_sec"
widget={operationPerSecWidget}
- yAxisUnit="ops"
opName="Rate"
/>
@@ -267,7 +268,6 @@ function Application(): JSX.Element {
topLevelOperationsIsError={topLevelOperationsIsError}
name="error_percentage_%"
widget={errorPercentageWidget}
- yAxisUnit="%"
opName="Error"
/>
diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx
index b20613bae7..ade8a1bec3 100644
--- a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx
@@ -6,8 +6,8 @@ import {
apDexToolTipUrlText,
} from 'constants/apDex';
import { PANEL_TYPES } from 'constants/queryBuilder';
-import Graph from 'container/GridGraphLayout/Graph';
-import DisplayThreshold from 'container/GridGraphLayout/WidgetHeader/DisplayThreshold';
+import Graph from 'container/GridCardLayout/GridCard';
+import DisplayThreshold from 'container/GridCardLayout/WidgetHeader/DisplayThreshold';
import { GraphTitle } from 'container/MetricsApplication/constant';
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
import { apDexMetricsQueryBuilderQueries } from 'container/MetricsApplication/MetricsPageQueries/OverviewQueries';
@@ -87,7 +87,6 @@ function ApDexMetrics({
widget={apDexMetricsWidget}
onDragSelect={onDragSelect}
onClickHandler={handleGraphClick('ApDex')}
- yAxisUnit=""
threshold={threshold}
isQueryEnabled={isQueryEnabled}
/>
diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexTraces.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexTraces.tsx
index bf6297785f..1b2e5ba0cd 100644
--- a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexTraces.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexTraces.tsx
@@ -1,7 +1,7 @@
// This component is not been used in the application as we support only metrics for ApDex as of now.
// This component is been kept for future reference.
import { PANEL_TYPES } from 'constants/queryBuilder';
-import Graph from 'container/GridGraphLayout/Graph';
+import Graph from 'container/GridCardLayout/GridCard';
import { GraphTitle } from 'container/MetricsApplication/constant';
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
import { apDexTracesQueryBuilderQueries } from 'container/MetricsApplication/MetricsPageQueries/OverviewQueries';
@@ -52,7 +52,6 @@ function ApDexTraces({
widget={apDexTracesWidget}
onDragSelect={onDragSelect}
onClickHandler={handleGraphClick('ApDex')}
- yAxisUnit=""
threshold={thresholdValue}
isQueryEnabled={isQueryEnabled}
/>
diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx
index 52014ac48b..cb124f545a 100644
--- a/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx
@@ -1,7 +1,7 @@
import Spinner from 'components/Spinner';
import { FeatureKeys } from 'constants/features';
import { PANEL_TYPES } from 'constants/queryBuilder';
-import Graph from 'container/GridGraphLayout/Graph/';
+import Graph from 'container/GridCardLayout/GridCard';
import { GraphTitle } from 'container/MetricsApplication/constant';
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
import { latency } from 'container/MetricsApplication/MetricsPageQueries/OverviewQueries';
@@ -59,6 +59,7 @@ function ServiceOverview({
},
title: GraphTitle.LATENCY,
panelTypes: PANEL_TYPES.TIME_SERIES,
+ yAxisUnit: 'ns',
}),
[servicename, isSpanMetricEnable, topLevelOperationsRoute, tagFilterItems],
);
@@ -93,7 +94,6 @@ function ServiceOverview({
name="service_latency"
onDragSelect={onDragSelect}
widget={latencyWidget}
- yAxisUnit="ns"
onClickHandler={handleGraphClick('Service')}
isQueryEnabled={isQueryEnabled}
/>
diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx
index 903ff3a15f..f71a0f7301 100644
--- a/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx
@@ -2,7 +2,7 @@ import { Typography } from 'antd';
import axios from 'axios';
import Spinner from 'components/Spinner';
import { SOMETHING_WENT_WRONG } from 'constants/api';
-import Graph from 'container/GridGraphLayout/Graph/';
+import Graph from 'container/GridCardLayout/GridCard';
import { Card, GraphContainer } from 'container/MetricsApplication/styles';
import { Widgets } from 'types/api/dashboard/getAll';
@@ -17,7 +17,6 @@ function TopLevelOperation({
onDragSelect,
handleGraphClick,
widget,
- yAxisUnit,
}: TopLevelOperationProps): JSX.Element {
return (
@@ -37,7 +36,6 @@ function TopLevelOperation({
name={name}
widget={widget}
onClickHandler={handleGraphClick(opName)}
- yAxisUnit={yAxisUnit}
onDragSelect={onDragSelect}
/>
)}
@@ -56,7 +54,6 @@ interface TopLevelOperationProps {
onDragSelect: (start: number, end: number) => void;
handleGraphClick: (type: string) => ClickHandlerType;
widget: Widgets;
- yAxisUnit: string;
}
export default TopLevelOperation;
diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/TopOperationMetrics.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/TopOperationMetrics.tsx
index 4ca191b68c..b0385e2ab0 100644
--- a/frontend/src/container/MetricsApplication/Tabs/Overview/TopOperationMetrics.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/Overview/TopOperationMetrics.tsx
@@ -7,9 +7,7 @@ import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
import { useNotifications } from 'hooks/useNotifications';
import useResourceAttribute from 'hooks/useResourceAttribute';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
-import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
-import { isEmpty } from 'lodash-es';
import { ReactNode, useMemo } from 'react';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
@@ -58,10 +56,7 @@ function TopOperationMetrics(): JSX.Element {
const updatedQuery = useStepInterval(keyOperationWidget.query);
- const isEmptyWidget = useMemo(
- () => keyOperationWidget.id === 'empty' || isEmpty(keyOperationWidget),
- [keyOperationWidget],
- );
+ const isEmptyWidget = keyOperationWidget.id === PANEL_TYPES.EMPTY_WIDGET;
const { data, isLoading } = useGetQueryRange(
{
@@ -69,7 +64,7 @@ function TopOperationMetrics(): JSX.Element {
graphType: keyOperationWidget?.panelTypes,
query: updatedQuery,
globalSelectedInterval,
- variables: getDashboardVariables(),
+ variables: {},
},
{
queryKey: [
diff --git a/frontend/src/container/MetricsApplication/TopOperationsTable.tsx b/frontend/src/container/MetricsApplication/TopOperationsTable.tsx
index e753a965a5..4c895716e7 100644
--- a/frontend/src/container/MetricsApplication/TopOperationsTable.tsx
+++ b/frontend/src/container/MetricsApplication/TopOperationsTable.tsx
@@ -10,7 +10,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { getErrorRate, navigateToTrace } from './utils';
-function TopOperationsTable(props: TopOperationsTableProps): JSX.Element {
+function TopOperationsTable({ data }: TopOperationsTableProps): JSX.Element {
const { minTime, maxTime } = useSelector(
(state) => state.globalTime,
);
@@ -20,8 +20,6 @@ function TopOperationsTable(props: TopOperationsTableProps): JSX.Element {
convertRawQueriesToTraceSelectedTags(queries) || [],
);
- const { data } = props;
-
const params = useParams<{ servicename: string }>();
const handleOnClick = (operation: string): void => {
diff --git a/frontend/src/container/MetricsApplication/types.ts b/frontend/src/container/MetricsApplication/types.ts
index d9a7251745..f87ce66a2a 100644
--- a/frontend/src/container/MetricsApplication/types.ts
+++ b/frontend/src/container/MetricsApplication/types.ts
@@ -8,6 +8,7 @@ export interface GetWidgetQueryBuilderProps {
query: Widgets['query'];
title?: ReactNode;
panelTypes: Widgets['panelTypes'];
+ yAxisUnit?: Widgets['yAxisUnit'];
}
export interface NavigateToTraceProps {
diff --git a/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx b/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx
index da6e28ccca..c9c1c6dde1 100644
--- a/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx
+++ b/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx
@@ -1,67 +1,97 @@
+import { SOMETHING_WENT_WRONG } from 'constants/api';
import { QueryParams } from 'constants/query';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
+import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useNotifications } from 'hooks/useNotifications';
import createQueryParams from 'lib/createQueryParams';
import history from 'lib/history';
-import { CSSProperties, useCallback } from 'react';
-import { connect, useSelector } from 'react-redux';
-import { useLocation } from 'react-router-dom';
-import { bindActionCreators, Dispatch } from 'redux';
-import { ThunkDispatch } from 'redux-thunk';
-import {
- ToggleAddWidget,
- ToggleAddWidgetProps,
-} from 'store/actions/dashboard/toggleAddWidget';
-import { AppState } from 'store/reducers';
-import AppActions from 'types/actions';
-import DashboardReducer from 'types/reducer/dashboards';
+import { useDashboard } from 'providers/Dashboard/Dashboard';
+import { CSSProperties } from 'react';
+import { v4 as uuid } from 'uuid';
import menuItems from './menuItems';
import { Card, Container, Text } from './styles';
-function DashboardGraphSlider({ toggleAddWidget }: Props): JSX.Element {
- const { dashboards } = useSelector(
- (state) => state.dashboards,
- );
+function DashboardGraphSlider(): JSX.Element {
+ const isDarkMode = useIsDarkMode();
- const { pathname } = useLocation();
+ const {
+ handleToggleDashboardSlider,
+ layouts,
+ selectedDashboard,
+ } = useDashboard();
+
+ const { data } = selectedDashboard || {};
const { notifications } = useNotifications();
- const [selectedDashboard] = dashboards;
- const { data } = selectedDashboard;
+ const updateDashboardMutation = useUpdateDashboard();
- const onClickHandler = useCallback(
- (name: PANEL_TYPES) => (): void => {
- try {
- const emptyLayout = data.layout?.find((e) => e.i === 'empty');
+ const onClickHandler = (name: PANEL_TYPES) => (): void => {
+ const id = uuid();
- if (emptyLayout === undefined) {
- notifications.error({
- message: 'Please click on Add Panel Button',
+ updateDashboardMutation.mutateAsync(
+ {
+ uuid: selectedDashboard?.uuid || '',
+ data: {
+ title: data?.title || '',
+ variables: data?.variables || {},
+ description: data?.description || '',
+ name: data?.name || '',
+ tags: data?.tags || [],
+ layout: [
+ {
+ i: id,
+ w: 6,
+ x: 0,
+ h: 2,
+ y: 0,
+ },
+ ...(layouts.filter((layout) => layout.i !== PANEL_TYPES.EMPTY_WIDGET) ||
+ []),
+ ],
+ widgets: [
+ ...(data?.widgets || []),
+ {
+ id,
+ title: '',
+ description: '',
+ isStacked: false,
+ nullZeroValues: '',
+ opacity: '',
+ panelTypes: name,
+ query: initialQueriesMap.metrics,
+ timePreferance: 'GLOBAL_TIME',
+ },
+ ],
+ },
+ },
+ {
+ onSuccess: (data) => {
+ if (data.payload) {
+ handleToggleDashboardSlider(false);
+
+ const queryParams = {
+ graphType: name,
+ widgetId: id,
+ [QueryParams.compositeQuery]: JSON.stringify(initialQueriesMap.metrics),
+ };
+
+ history.push(
+ `${history.location.pathname}/new?${createQueryParams(queryParams)}`,
+ );
+ }
+ },
+ onError: () => {
+ notifications.success({
+ message: SOMETHING_WENT_WRONG,
});
- return;
- }
+ },
+ },
+ );
+ };
- toggleAddWidget(false);
-
- const queryParams = {
- graphType: name,
- widgetId: emptyLayout.i,
- [QueryParams.compositeQuery]: JSON.stringify(initialQueriesMap.metrics),
- };
-
- history.push(`${pathname}/new?${createQueryParams(queryParams)}`);
- } catch (error) {
- notifications.error({
- message: 'Something went wrong',
- });
- }
- },
- [data, toggleAddWidget, notifications, pathname],
- );
- const isDarkMode = useIsDarkMode();
const fillColor: CSSProperties['color'] = isDarkMode ? 'white' : 'black';
return (
@@ -76,18 +106,4 @@ function DashboardGraphSlider({ toggleAddWidget }: Props): JSX.Element {
);
}
-interface DispatchProps {
- toggleAddWidget: (
- props: ToggleAddWidgetProps,
- ) => (dispatch: Dispatch) => void;
-}
-
-const mapDispatchToProps = (
- dispatch: ThunkDispatch,
-): DispatchProps => ({
- toggleAddWidget: bindActionCreators(ToggleAddWidget, dispatch),
-});
-
-type Props = DispatchProps;
-
-export default connect(null, mapDispatchToProps)(DashboardGraphSlider);
+export default DashboardGraphSlider;
diff --git a/frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx b/frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx
index a8f1892fa4..3f6eec23b4 100644
--- a/frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx
+++ b/frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx
@@ -1,33 +1,23 @@
import { SaveOutlined } from '@ant-design/icons';
import { Col, Divider, Input, Space, Typography } from 'antd';
+import { SOMETHING_WENT_WRONG } from 'constants/api';
import AddTags from 'container/NewDashboard/DashboardSettings/General/AddTags';
-import { useCallback, useState } from 'react';
+import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
+import { useNotifications } from 'hooks/useNotifications';
+import { useDashboard } from 'providers/Dashboard/Dashboard';
+import { useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { connect, useSelector } from 'react-redux';
-import { bindActionCreators, Dispatch } from 'redux';
-import { ThunkDispatch } from 'redux-thunk';
-import {
- UpdateDashboardTitleDescriptionTags,
- UpdateDashboardTitleDescriptionTagsProps,
-} from 'store/actions';
-import { AppState } from 'store/reducers';
-import AppActions from 'types/actions';
-import DashboardReducer from 'types/reducer/dashboards';
import { Button } from './styles';
-function GeneralDashboardSettings({
- updateDashboardTitleDescriptionTags,
-}: DescriptionOfDashboardProps): JSX.Element {
- const { dashboards } = useSelector(
- (state) => state.dashboards,
- );
+function GeneralDashboardSettings(): JSX.Element {
+ const { selectedDashboard, setSelectedDashboard } = useDashboard();
- const [selectedDashboard] = dashboards;
- const selectedData = selectedDashboard.data;
- const { title } = selectedData;
- const { tags } = selectedData;
- const { description } = selectedData;
+ const updateDashboardMutation = useUpdateDashboard();
+
+ const selectedData = selectedDashboard?.data;
+
+ const { title = '', tags = [], description = '' } = selectedData || {};
const [updatedTitle, setUpdatedTitle] = useState(title);
const [updatedTags, setUpdatedTags] = useState(tags || []);
@@ -37,27 +27,35 @@ function GeneralDashboardSettings({
const { t } = useTranslation('common');
- const onSaveHandler = useCallback(() => {
- const dashboard = selectedDashboard;
- // @TODO need to update this function to take title,description,tags only
- updateDashboardTitleDescriptionTags({
- dashboard: {
- ...dashboard,
+ const { notifications } = useNotifications();
+
+ const onSaveHandler = (): void => {
+ if (!selectedDashboard) return;
+
+ updateDashboardMutation.mutateAsync(
+ {
+ ...selectedDashboard,
data: {
- ...dashboard.data,
+ ...selectedDashboard.data,
description: updatedDescription,
tags: updatedTags,
title: updatedTitle,
},
},
- });
- }, [
- updatedTitle,
- updatedTags,
- updatedDescription,
- selectedDashboard,
- updateDashboardTitleDescriptionTags,
- ]);
+ {
+ onSuccess: (updatedDashboard) => {
+ if (updatedDashboard.payload) {
+ setSelectedDashboard(updatedDashboard.payload);
+ }
+ },
+ onError: () => {
+ notifications.error({
+ message: SOMETHING_WENT_WRONG,
+ });
+ },
+ },
+ );
+ };
return (
@@ -83,7 +81,13 @@ function GeneralDashboardSettings({
-
} onClick={onSaveHandler} type="primary">
+
}
+ onClick={onSaveHandler}
+ type="primary"
+ >
{t('save')}
@@ -92,21 +96,4 @@ function GeneralDashboardSettings({
);
}
-interface DispatchProps {
- updateDashboardTitleDescriptionTags: (
- props: UpdateDashboardTitleDescriptionTagsProps,
- ) => (dispatch: Dispatch) => void;
-}
-
-const mapDispatchToProps = (
- dispatch: ThunkDispatch,
-): DispatchProps => ({
- updateDashboardTitleDescriptionTags: bindActionCreators(
- UpdateDashboardTitleDescriptionTags,
- dispatch,
- ),
-});
-
-type DescriptionOfDashboardProps = DispatchProps;
-
-export default connect(null, mapDispatchToProps)(GeneralDashboardSettings);
+export default GeneralDashboardSettings;
diff --git a/frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx b/frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx
index 3f90bf565b..388dffc286 100644
--- a/frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx
+++ b/frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx
@@ -1,39 +1,28 @@
import { blue, red } from '@ant-design/colors';
import { PlusOutlined } from '@ant-design/icons';
import { Button, Modal, Row, Space, Tag } from 'antd';
-import { NotificationInstance } from 'antd/es/notification/interface';
import { ResizeTable } from 'components/ResizeTable';
+import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
import { useNotifications } from 'hooks/useNotifications';
+import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useRef, useState } from 'react';
-import { connect, useSelector } from 'react-redux';
-import { bindActionCreators, Dispatch } from 'redux';
-import { ThunkDispatch } from 'redux-thunk';
-import { UpdateDashboardVariables } from 'store/actions/dashboard/updatedDashboardVariables';
-import { AppState } from 'store/reducers';
-import AppActions from 'types/actions';
-import { IDashboardVariable } from 'types/api/dashboard/getAll';
-import DashboardReducer from 'types/reducer/dashboards';
+import { useTranslation } from 'react-i18next';
+import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll';
import { TVariableViewMode } from './types';
import VariableItem from './VariableItem/VariableItem';
-function VariablesSetting({
- updateDashboardVariables,
-}: DispatchProps): JSX.Element {
+function VariablesSetting(): JSX.Element {
const variableToDelete = useRef(null);
const [deleteVariableModal, setDeleteVariableModal] = useState(false);
- const { dashboards } = useSelector(
- (state) => state.dashboards,
- );
+ const { t } = useTranslation(['dashboard']);
+
+ const { selectedDashboard, setSelectedDashboard } = useDashboard();
const { notifications } = useNotifications();
- const [selectedDashboard] = dashboards;
-
- const {
- data: { variables = {} },
- } = selectedDashboard;
+ const { variables = {} } = selectedDashboard?.data || {};
const variablesTableData = Object.keys(variables).map((variableName) => ({
key: variableName,
@@ -64,6 +53,41 @@ function VariablesSetting({
setVariableViewMode(viewType);
};
+ const updateMutation = useUpdateDashboard();
+
+ const updateVariables = (
+ updatedVariablesData: Dashboard['data']['variables'],
+ ): void => {
+ if (!selectedDashboard) {
+ return;
+ }
+
+ updateMutation.mutateAsync(
+ {
+ ...selectedDashboard,
+ data: {
+ ...selectedDashboard.data,
+ variables: updatedVariablesData,
+ },
+ },
+ {
+ onSuccess: (updatedDashboard) => {
+ if (updatedDashboard.payload) {
+ setSelectedDashboard(updatedDashboard.payload);
+ notifications.success({
+ message: t('variable_updated_successfully'),
+ });
+ }
+ },
+ onError: () => {
+ notifications.error({
+ message: t('error_while_updating_variable'),
+ });
+ },
+ },
+ );
+ };
+
const onVariableSaveHandler = (
name: string,
variableData: IDashboardVariable,
@@ -79,7 +103,7 @@ function VariablesSetting({
if (oldName) {
delete newVariables[oldName];
}
- updateDashboardVariables(newVariables, notifications);
+ updateVariables(newVariables);
onDoneVariableViewMode();
};
@@ -91,7 +115,7 @@ function VariablesSetting({
const handleDeleteConfirm = (): void => {
const newVariables = { ...variables };
if (variableToDelete?.current) delete newVariables[variableToDelete?.current];
- updateDashboardVariables(newVariables, notifications);
+ updateVariables(newVariables);
variableToDelete.current = null;
setDeleteVariableModal(false);
};
@@ -182,20 +206,4 @@ function VariablesSetting({
);
}
-interface DispatchProps {
- updateDashboardVariables: (
- props: Record,
- notify: NotificationInstance,
- ) => (dispatch: Dispatch) => void;
-}
-
-const mapDispatchToProps = (
- dispatch: ThunkDispatch,
-): DispatchProps => ({
- updateDashboardVariables: bindActionCreators(
- UpdateDashboardVariables,
- dispatch,
- ),
-});
-
-export default connect(null, mapDispatchToProps)(VariablesSetting);
+export default VariablesSetting;
diff --git a/frontend/src/container/NewDashboard/DashboardSettings/index.tsx b/frontend/src/container/NewDashboard/DashboardSettings/index.tsx
index 50a69495fa..5a1bc6afac 100644
--- a/frontend/src/container/NewDashboard/DashboardSettings/index.tsx
+++ b/frontend/src/container/NewDashboard/DashboardSettings/index.tsx
@@ -3,12 +3,12 @@ import { Tabs } from 'antd';
import GeneralDashboardSettings from './General';
import VariablesSetting from './Variables';
-function DashboardSettingsContent(): JSX.Element {
- const items = [
- { label: 'General', key: 'general', children: },
- { label: 'Variables', key: 'variables', children: },
- ];
+const items = [
+ { label: 'General', key: 'general', children: },
+ { label: 'Variables', key: 'variables', children: },
+];
+function DashboardSettingsContent(): JSX.Element {
return ;
}
diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/index.tsx b/frontend/src/container/NewDashboard/DashboardVariablesSelection/index.tsx
index d318e684f8..561af111ae 100644
--- a/frontend/src/container/NewDashboard/DashboardVariablesSelection/index.tsx
+++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/index.tsx
@@ -1,34 +1,25 @@
import { Row } from 'antd';
-import { NotificationInstance } from 'antd/es/notification/interface';
+import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
import { useNotifications } from 'hooks/useNotifications';
import { map, sortBy } from 'lodash-es';
+import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useState } from 'react';
-import { connect, useSelector } from 'react-redux';
-import { bindActionCreators, Dispatch } from 'redux';
-import { ThunkDispatch } from 'redux-thunk';
-import { UpdateDashboardVariables } from 'store/actions/dashboard/updatedDashboardVariables';
+import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
-import AppActions from 'types/actions';
-import { IDashboardVariable } from 'types/api/dashboard/getAll';
+import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll';
import AppReducer from 'types/reducer/app';
-import DashboardReducer from 'types/reducer/dashboards';
import VariableItem from './VariableItem';
-function DashboardVariableSelection({
- updateDashboardVariables,
-}: DispatchProps): JSX.Element {
- const { dashboards } = useSelector(
- (state) => state.dashboards,
- );
- const [selectedDashboard] = dashboards;
- const {
- data: { variables = {} },
- } = selectedDashboard;
+function DashboardVariableSelection(): JSX.Element | null {
+ const { selectedDashboard, setSelectedDashboard } = useDashboard();
+
+ const { data } = selectedDashboard || {};
+
+ const { variables } = data || {};
const [update, setUpdate] = useState(false);
const [lastUpdatedVar, setLastUpdatedVar] = useState('');
- const { notifications } = useNotifications();
const { role } = useSelector((state) => state.app);
@@ -37,6 +28,42 @@ function DashboardVariableSelection({
setUpdate(!update);
};
+ const updateMutation = useUpdateDashboard();
+ const { notifications } = useNotifications();
+
+ const updateVariables = (
+ updatedVariablesData: Dashboard['data']['variables'],
+ ): void => {
+ if (!selectedDashboard) {
+ return;
+ }
+
+ updateMutation.mutateAsync(
+ {
+ ...selectedDashboard,
+ data: {
+ ...selectedDashboard.data,
+ variables: updatedVariablesData,
+ },
+ },
+ {
+ onSuccess: (updatedDashboard) => {
+ if (updatedDashboard.payload) {
+ setSelectedDashboard(updatedDashboard.payload);
+ notifications.success({
+ message: 'Variable updated successfully',
+ });
+ }
+ },
+ onError: () => {
+ notifications.error({
+ message: 'Error while updating variable',
+ });
+ },
+ },
+ );
+ };
+
const onValueUpdate = (
name: string,
value: IDashboardVariable['selectedValue'],
@@ -44,8 +71,8 @@ function DashboardVariableSelection({
const updatedVariablesData = { ...variables };
updatedVariablesData[name].selectedValue = value;
- if (role !== 'VIEWER') {
- updateDashboardVariables(updatedVariablesData, notifications);
+ if (role !== 'VIEWER' && selectedDashboard) {
+ updateVariables(updatedVariablesData);
}
onVarChanged(name);
@@ -58,13 +85,17 @@ function DashboardVariableSelection({
updatedVariablesData[name].allSelected = value;
if (role !== 'VIEWER') {
- updateDashboardVariables(updatedVariablesData, notifications);
+ updateVariables(updatedVariablesData);
}
onVarChanged(name);
};
+ if (!variables) {
+ return null;
+ }
+
return (
-
+
{map(sortBy(Object.keys(variables)), (variableName) => (
))}
@@ -83,20 +114,4 @@ function DashboardVariableSelection({
);
}
-interface DispatchProps {
- updateDashboardVariables: (
- props: Parameters[0],
- notify: NotificationInstance,
- ) => (dispatch: Dispatch) => void;
-}
-
-const mapDispatchToProps = (
- dispatch: ThunkDispatch,
-): DispatchProps => ({
- updateDashboardVariables: bindActionCreators(
- UpdateDashboardVariables,
- dispatch,
- ),
-});
-
-export default connect(null, mapDispatchToProps)(DashboardVariableSelection);
+export default DashboardVariableSelection;
diff --git a/frontend/src/container/NewDashboard/DescriptionOfDashboard/SettingsDrawer.tsx b/frontend/src/container/NewDashboard/DescriptionOfDashboard/SettingsDrawer.tsx
index 72a10a3f30..fc51efd69a 100644
--- a/frontend/src/container/NewDashboard/DescriptionOfDashboard/SettingsDrawer.tsx
+++ b/frontend/src/container/NewDashboard/DescriptionOfDashboard/SettingsDrawer.tsx
@@ -6,7 +6,7 @@ import DashboardSettingsContent from '../DashboardSettings';
import { DrawerContainer } from './styles';
function SettingsDrawer(): JSX.Element {
- const [visible, setVisible] = useState(false); // TODO Make it False
+ const [visible, setVisible] = useState(false);
const showDrawer = (): void => {
setVisible(true);
@@ -25,7 +25,7 @@ function SettingsDrawer(): JSX.Element {
placement="right"
width="70%"
onClose={onClose}
- visible={visible}
+ open={visible}
>
diff --git a/frontend/src/container/NewDashboard/DescriptionOfDashboard/index.tsx b/frontend/src/container/NewDashboard/DescriptionOfDashboard/index.tsx
index 2faafd7cbb..3c6ca326a3 100644
--- a/frontend/src/container/NewDashboard/DescriptionOfDashboard/index.tsx
+++ b/frontend/src/container/NewDashboard/DescriptionOfDashboard/index.tsx
@@ -1,25 +1,22 @@
import { ShareAltOutlined } from '@ant-design/icons';
import { Button, Card, Col, Row, Space, Tag, Typography } from 'antd';
import useComponentPermission from 'hooks/useComponentPermission';
+import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import AppReducer from 'types/reducer/app';
-import DashboardReducer from 'types/reducer/dashboards';
import DashboardVariableSelection from '../DashboardVariablesSelection';
import SettingsDrawer from './SettingsDrawer';
import ShareModal from './ShareModal';
function DescriptionOfDashboard(): JSX.Element {
- const { dashboards } = useSelector(
- (state) => state.dashboards,
- );
+ const { selectedDashboard } = useDashboard();
- const [selectedDashboard] = dashboards;
- const selectedData = selectedDashboard.data;
- const { title, tags, description } = selectedData;
+ const selectedData = selectedDashboard?.data;
+ const { title, tags, description } = selectedData || {};
const [isJSONModalVisible, isIsJSONModalVisible] = useState(false);
@@ -34,26 +31,29 @@ function DescriptionOfDashboard(): JSX.Element {
return (
-
+
{title}
{description}
+
- {tags?.map((e) => (
- {e}
+ {tags?.map((tag) => (
+ {tag}
))}
+
-
+ {selectedData && (
+
+ )}
+
{editDashboard && }