mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-25 12:34:27 +08:00
commit
7603e0ebe0
1
.github/workflows/staging-deployment.yaml
vendored
1
.github/workflows/staging-deployment.yaml
vendored
@ -29,6 +29,7 @@ jobs:
|
|||||||
export PATH="/usr/local/go/bin/:$PATH" # needed for Golang to work
|
export PATH="/usr/local/go/bin/:$PATH" # needed for Golang to work
|
||||||
docker system prune --force
|
docker system prune --force
|
||||||
docker pull signoz/signoz-otel-collector:main
|
docker pull signoz/signoz-otel-collector:main
|
||||||
|
docker pull signoz/signoz/signoz-schema-migrator:main
|
||||||
cd ~/signoz
|
cd ~/signoz
|
||||||
git status
|
git status
|
||||||
git add .
|
git add .
|
||||||
|
@ -146,7 +146,7 @@ services:
|
|||||||
condition: on-failure
|
condition: on-failure
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.32.1
|
image: signoz/query-service:0.33.0
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
"-config=/root/config/prometheus.yml",
|
"-config=/root/config/prometheus.yml",
|
||||||
@ -186,7 +186,7 @@ services:
|
|||||||
<<: *db-depend
|
<<: *db-depend
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.32.1
|
image: signoz/frontend:0.33.0
|
||||||
deploy:
|
deploy:
|
||||||
restart_policy:
|
restart_policy:
|
||||||
condition: on-failure
|
condition: on-failure
|
||||||
@ -199,7 +199,7 @@ services:
|
|||||||
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
otel-collector:
|
otel-collector:
|
||||||
image: signoz/signoz-otel-collector:0.79.12
|
image: signoz/signoz-otel-collector:0.79.13
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
"--config=/etc/otel-collector-config.yaml",
|
"--config=/etc/otel-collector-config.yaml",
|
||||||
@ -237,7 +237,7 @@ services:
|
|||||||
- query-service
|
- query-service
|
||||||
|
|
||||||
otel-collector-migrator:
|
otel-collector-migrator:
|
||||||
image: signoz/signoz-schema-migrator:0.79.12
|
image: signoz/signoz-schema-migrator:0.79.13
|
||||||
deploy:
|
deploy:
|
||||||
restart_policy:
|
restart_policy:
|
||||||
condition: on-failure
|
condition: on-failure
|
||||||
@ -250,7 +250,7 @@ services:
|
|||||||
# - clickhouse-3
|
# - clickhouse-3
|
||||||
|
|
||||||
otel-collector-metrics:
|
otel-collector-metrics:
|
||||||
image: signoz/signoz-otel-collector:0.79.12
|
image: signoz/signoz-otel-collector:0.79.13
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
"--config=/etc/otel-collector-metrics-config.yaml",
|
"--config=/etc/otel-collector-metrics-config.yaml",
|
||||||
|
@ -63,38 +63,33 @@ receivers:
|
|||||||
processors:
|
processors:
|
||||||
logstransform/internal:
|
logstransform/internal:
|
||||||
operators:
|
operators:
|
||||||
|
- type: regex_parser
|
||||||
|
id: traceid
|
||||||
|
# https://regex101.com/r/MMfNjk/1
|
||||||
|
regex: '(?i)(trace(-|_||)id("|=| |-|:)*)(?P<trace_id>[A-Fa-f0-9]+)'
|
||||||
|
parse_from: body
|
||||||
|
parse_to: attributes.temp_trace
|
||||||
|
if: 'body matches "(?i)(trace(-|_||)id(\"|=| |-|:)*)(?P<trace_id>[A-Fa-f0-9]+)"'
|
||||||
|
output: spanid
|
||||||
|
- type: regex_parser
|
||||||
|
id: spanid
|
||||||
|
# https://regex101.com/r/uXSwLc/1
|
||||||
|
regex: '(?i)(span(-|_||)id("|=| |-|:)*)(?P<span_id>[A-Fa-f0-9]+)'
|
||||||
|
parse_from: body
|
||||||
|
parse_to: attributes.temp_trace
|
||||||
|
if: 'body matches "(?i)(span(-|_||)id(\"|=| |-|:)*)(?P<span_id>[A-Fa-f0-9]+)"'
|
||||||
|
output: trace_parser
|
||||||
- type: trace_parser
|
- type: trace_parser
|
||||||
if: '"trace_id" in attributes or "span_id" in attributes'
|
id: trace_parser
|
||||||
trace_id:
|
trace_id:
|
||||||
parse_from: attributes.trace_id
|
parse_from: attributes.temp_trace.trace_id
|
||||||
span_id:
|
span_id:
|
||||||
parse_from: attributes.span_id
|
parse_from: attributes.temp_trace.span_id
|
||||||
output: remove_trace_id
|
output: remove_temp
|
||||||
- type: trace_parser
|
- type: remove
|
||||||
if: '"traceId" in attributes or "spanId" in attributes'
|
id: remove_temp
|
||||||
trace_id:
|
field: attributes.temp_trace
|
||||||
parse_from: attributes.traceId
|
if: '"temp_trace" in attributes'
|
||||||
span_id:
|
|
||||||
parse_from: attributes.spanId
|
|
||||||
output: remove_traceId
|
|
||||||
- id: remove_traceId
|
|
||||||
type: remove
|
|
||||||
if: '"traceId" in attributes'
|
|
||||||
field: attributes.traceId
|
|
||||||
output: remove_spanId
|
|
||||||
- id: remove_spanId
|
|
||||||
type: remove
|
|
||||||
if: '"spanId" in attributes'
|
|
||||||
field: attributes.spanId
|
|
||||||
- id: remove_trace_id
|
|
||||||
type: remove
|
|
||||||
if: '"trace_id" in attributes'
|
|
||||||
field: attributes.trace_id
|
|
||||||
output: remove_span_id
|
|
||||||
- id: remove_span_id
|
|
||||||
type: remove
|
|
||||||
if: '"span_id" in attributes'
|
|
||||||
field: attributes.span_id
|
|
||||||
batch:
|
batch:
|
||||||
send_batch_size: 10000
|
send_batch_size: 10000
|
||||||
send_batch_max_size: 11000
|
send_batch_max_size: 11000
|
||||||
|
@ -66,7 +66,7 @@ services:
|
|||||||
- --storage.path=/data
|
- --storage.path=/data
|
||||||
|
|
||||||
otel-collector-migrator:
|
otel-collector-migrator:
|
||||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.79.12}
|
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.79.13}
|
||||||
container_name: otel-migrator
|
container_name: otel-migrator
|
||||||
command:
|
command:
|
||||||
- "--dsn=tcp://clickhouse:9000"
|
- "--dsn=tcp://clickhouse:9000"
|
||||||
@ -81,7 +81,7 @@ services:
|
|||||||
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
||||||
otel-collector:
|
otel-collector:
|
||||||
container_name: signoz-otel-collector
|
container_name: signoz-otel-collector
|
||||||
image: signoz/signoz-otel-collector:0.79.12
|
image: signoz/signoz-otel-collector:0.79.13
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
"--config=/etc/otel-collector-config.yaml",
|
"--config=/etc/otel-collector-config.yaml",
|
||||||
@ -118,7 +118,7 @@ services:
|
|||||||
|
|
||||||
otel-collector-metrics:
|
otel-collector-metrics:
|
||||||
container_name: signoz-otel-collector-metrics
|
container_name: signoz-otel-collector-metrics
|
||||||
image: signoz/signoz-otel-collector:0.79.12
|
image: signoz/signoz-otel-collector:0.79.13
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
"--config=/etc/otel-collector-metrics-config.yaml",
|
"--config=/etc/otel-collector-metrics-config.yaml",
|
||||||
|
@ -164,7 +164,7 @@ services:
|
|||||||
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:${DOCKER_TAG:-0.32.1}
|
image: signoz/query-service:${DOCKER_TAG:-0.33.0}
|
||||||
container_name: signoz-query-service
|
container_name: signoz-query-service
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
@ -203,7 +203,7 @@ services:
|
|||||||
<<: *db-depend
|
<<: *db-depend
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:${DOCKER_TAG:-0.32.1}
|
image: signoz/frontend:${DOCKER_TAG:-0.33.0}
|
||||||
container_name: signoz-frontend
|
container_name: signoz-frontend
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
@ -215,7 +215,7 @@ services:
|
|||||||
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
otel-collector-migrator:
|
otel-collector-migrator:
|
||||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.79.12}
|
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.79.13}
|
||||||
container_name: otel-migrator
|
container_name: otel-migrator
|
||||||
command:
|
command:
|
||||||
- "--dsn=tcp://clickhouse:9000"
|
- "--dsn=tcp://clickhouse:9000"
|
||||||
@ -229,7 +229,7 @@ services:
|
|||||||
|
|
||||||
|
|
||||||
otel-collector:
|
otel-collector:
|
||||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.12}
|
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.13}
|
||||||
container_name: signoz-otel-collector
|
container_name: signoz-otel-collector
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
@ -269,7 +269,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
otel-collector-metrics:
|
otel-collector-metrics:
|
||||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.12}
|
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.13}
|
||||||
container_name: signoz-otel-collector-metrics
|
container_name: signoz-otel-collector-metrics
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
|
@ -64,38 +64,33 @@ receivers:
|
|||||||
processors:
|
processors:
|
||||||
logstransform/internal:
|
logstransform/internal:
|
||||||
operators:
|
operators:
|
||||||
|
- type: regex_parser
|
||||||
|
id: traceid
|
||||||
|
# https://regex101.com/r/MMfNjk/1
|
||||||
|
regex: '(?i)(trace(-|_||)id("|=| |-|:)*)(?P<trace_id>[A-Fa-f0-9]+)'
|
||||||
|
parse_from: body
|
||||||
|
parse_to: attributes.temp_trace
|
||||||
|
if: 'body matches "(?i)(trace(-|_||)id(\"|=| |-|:)*)(?P<trace_id>[A-Fa-f0-9]+)"'
|
||||||
|
output: spanid
|
||||||
|
- type: regex_parser
|
||||||
|
id: spanid
|
||||||
|
# https://regex101.com/r/uXSwLc/1
|
||||||
|
regex: '(?i)(span(-|_||)id("|=| |-|:)*)(?P<span_id>[A-Fa-f0-9]+)'
|
||||||
|
parse_from: body
|
||||||
|
parse_to: attributes.temp_trace
|
||||||
|
if: 'body matches "(?i)(span(-|_||)id(\"|=| |-|:)*)(?P<span_id>[A-Fa-f0-9]+)"'
|
||||||
|
output: trace_parser
|
||||||
- type: trace_parser
|
- type: trace_parser
|
||||||
if: '"trace_id" in attributes or "span_id" in attributes'
|
id: trace_parser
|
||||||
trace_id:
|
trace_id:
|
||||||
parse_from: attributes.trace_id
|
parse_from: attributes.temp_trace.trace_id
|
||||||
span_id:
|
span_id:
|
||||||
parse_from: attributes.span_id
|
parse_from: attributes.temp_trace.span_id
|
||||||
output: remove_trace_id
|
output: remove_temp
|
||||||
- type: trace_parser
|
- type: remove
|
||||||
if: '"traceId" in attributes or "spanId" in attributes'
|
id: remove_temp
|
||||||
trace_id:
|
field: attributes.temp_trace
|
||||||
parse_from: attributes.traceId
|
if: '"temp_trace" in attributes'
|
||||||
span_id:
|
|
||||||
parse_from: attributes.spanId
|
|
||||||
output: remove_traceId
|
|
||||||
- id: remove_traceId
|
|
||||||
type: remove
|
|
||||||
if: '"traceId" in attributes'
|
|
||||||
field: attributes.traceId
|
|
||||||
output: remove_spanId
|
|
||||||
- id: remove_spanId
|
|
||||||
type: remove
|
|
||||||
if: '"spanId" in attributes'
|
|
||||||
field: attributes.spanId
|
|
||||||
- id: remove_trace_id
|
|
||||||
type: remove
|
|
||||||
if: '"trace_id" in attributes'
|
|
||||||
field: attributes.trace_id
|
|
||||||
output: remove_span_id
|
|
||||||
- id: remove_span_id
|
|
||||||
type: remove
|
|
||||||
if: '"span_id" in attributes'
|
|
||||||
field: attributes.span_id
|
|
||||||
batch:
|
batch:
|
||||||
send_batch_size: 10000
|
send_batch_size: 10000
|
||||||
send_batch_max_size: 11000
|
send_batch_max_size: 11000
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# use a minimal alpine image
|
# use a minimal alpine image
|
||||||
FROM alpine:3.17
|
FROM alpine:3.18.3
|
||||||
|
|
||||||
# Add Maintainer Info
|
# Add Maintainer Info
|
||||||
LABEL maintainer="signoz"
|
LABEL maintainer="signoz"
|
||||||
|
@ -37,9 +37,9 @@
|
|||||||
"processor_field_placeholder": "Field",
|
"processor_field_placeholder": "Field",
|
||||||
"processor_value_placeholder": "Value",
|
"processor_value_placeholder": "Value",
|
||||||
"processor_description_placeholder": "example rule: %{word:first}",
|
"processor_description_placeholder": "example rule: %{word:first}",
|
||||||
"processor_trace_id_placeholder": "Trace Id Parce From",
|
"processor_trace_id_placeholder": "Parse Trace ID from",
|
||||||
"processor_span_id_placeholder": "Span id Parse From",
|
"processor_span_id_placeholder": "Parse Span ID from",
|
||||||
"processor_trace_flags_placeholder": "Trace flags parse from",
|
"processor_trace_flags_placeholder": "Parse Trace flags from",
|
||||||
"processor_from_placeholder": "From",
|
"processor_from_placeholder": "From",
|
||||||
"processor_to_placeholder": "To"
|
"processor_to_placeholder": "To"
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
"LOGS": "SigNoz | Logs",
|
"LOGS": "SigNoz | Logs",
|
||||||
"LOGS_EXPLORER": "SigNoz | Logs Explorer",
|
"LOGS_EXPLORER": "SigNoz | Logs Explorer",
|
||||||
"LIVE_LOGS": "SigNoz | Live Logs",
|
"LIVE_LOGS": "SigNoz | Live Logs",
|
||||||
|
"LOGS_PIPELINES": "SigNoz | Logs Pipelines",
|
||||||
"HOME_PAGE": "Open source Observability Platform | SigNoz",
|
"HOME_PAGE": "Open source Observability Platform | SigNoz",
|
||||||
"PASSWORD_RESET": "SigNoz | Password Reset",
|
"PASSWORD_RESET": "SigNoz | Password Reset",
|
||||||
"LIST_LICENSES": "SigNoz | List of Licenses",
|
"LIST_LICENSES": "SigNoz | List of Licenses",
|
||||||
|
@ -39,10 +39,12 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
|||||||
[pathname],
|
[pathname],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: licensesData } = useLicense();
|
const {
|
||||||
|
data: licensesData,
|
||||||
|
isFetching: isFetchingLicensesData,
|
||||||
|
} = useLicense();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
user,
|
|
||||||
isUserFetching,
|
isUserFetching,
|
||||||
isUserFetchingError,
|
isUserFetchingError,
|
||||||
isLoggedIn: isLoggedInState,
|
isLoggedIn: isLoggedInState,
|
||||||
@ -116,7 +118,7 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
|||||||
if (
|
if (
|
||||||
localStorageUserAuthToken &&
|
localStorageUserAuthToken &&
|
||||||
localStorageUserAuthToken.refreshJwt &&
|
localStorageUserAuthToken.refreshJwt &&
|
||||||
user?.userId === ''
|
isUserFetching
|
||||||
) {
|
) {
|
||||||
handleUserLoginIfTokenPresent(key);
|
handleUserLoginIfTokenPresent(key);
|
||||||
} else {
|
} else {
|
||||||
@ -131,28 +133,34 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
|||||||
|
|
||||||
if (path && path !== ROUTES.WORKSPACE_LOCKED) {
|
if (path && path !== ROUTES.WORKSPACE_LOCKED) {
|
||||||
history.push(ROUTES.WORKSPACE_LOCKED);
|
history.push(ROUTES.WORKSPACE_LOCKED);
|
||||||
}
|
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_USER_IS_FETCH,
|
type: UPDATE_USER_IS_FETCH,
|
||||||
payload: {
|
payload: {
|
||||||
isUserFetching: false,
|
isUserFetching: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isFetchingLicensesData) {
|
||||||
|
const shouldBlockWorkspace = licensesData?.payload?.workSpaceBlock;
|
||||||
|
|
||||||
|
if (shouldBlockWorkspace) {
|
||||||
|
navigateToWorkSpaceBlocked(currentRoute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isFetchingLicensesData]);
|
||||||
|
|
||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async (): Promise<void> => {
|
(async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const shouldBlockWorkspace = licensesData?.payload?.workSpaceBlock;
|
|
||||||
|
|
||||||
if (currentRoute) {
|
if (currentRoute) {
|
||||||
const { isPrivate, key } = currentRoute;
|
const { isPrivate, key } = currentRoute;
|
||||||
|
|
||||||
if (shouldBlockWorkspace) {
|
if (isPrivate && key !== ROUTES.WORKSPACE_LOCKED) {
|
||||||
navigateToWorkSpaceBlocked(currentRoute);
|
|
||||||
} else if (isPrivate) {
|
|
||||||
handlePrivateRoutes(key);
|
handlePrivateRoutes(key);
|
||||||
} else {
|
} else {
|
||||||
// no need to fetch the user and make user fetching false
|
// no need to fetch the user and make user fetching false
|
||||||
|
@ -13,6 +13,7 @@ import useLicense, { LICENSE_PLAN_KEY } from 'hooks/useLicense';
|
|||||||
import { NotificationProvider } from 'hooks/useNotifications';
|
import { NotificationProvider } from 'hooks/useNotifications';
|
||||||
import { ResourceProvider } from 'hooks/useResourceAttribute';
|
import { ResourceProvider } from 'hooks/useResourceAttribute';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
|
import { identity, pickBy } from 'lodash-es';
|
||||||
import { DashboardProvider } from 'providers/Dashboard/Dashboard';
|
import { DashboardProvider } from 'providers/Dashboard/Dashboard';
|
||||||
import { QueryBuilderProvider } from 'providers/QueryBuilder';
|
import { QueryBuilderProvider } from 'providers/QueryBuilder';
|
||||||
import { Suspense, useEffect, useState } from 'react';
|
import { Suspense, useEffect, useState } from 'react';
|
||||||
@ -90,13 +91,19 @@ function App(): JSX.Element {
|
|||||||
const orgName =
|
const orgName =
|
||||||
org && Array.isArray(org) && org.length > 0 ? org[0].name : '';
|
org && Array.isArray(org) && org.length > 0 ? org[0].name : '';
|
||||||
|
|
||||||
|
const { name, email } = user;
|
||||||
|
|
||||||
const identifyPayload = {
|
const identifyPayload = {
|
||||||
email: user?.email,
|
email,
|
||||||
name: user?.name,
|
name,
|
||||||
company_name: orgName,
|
company_name: orgName,
|
||||||
role,
|
role,
|
||||||
|
source: 'signoz-ui',
|
||||||
};
|
};
|
||||||
const domain = extractDomain(user?.email);
|
|
||||||
|
const sanitizedIdentifyPayload = pickBy(identifyPayload, identity);
|
||||||
|
|
||||||
|
const domain = extractDomain(email);
|
||||||
|
|
||||||
const hostNameParts = hostname.split('.');
|
const hostNameParts = hostname.split('.');
|
||||||
|
|
||||||
@ -106,13 +113,14 @@ function App(): JSX.Element {
|
|||||||
data_region: hostNameParts[1],
|
data_region: hostNameParts[1],
|
||||||
tenant_url: hostname,
|
tenant_url: hostname,
|
||||||
company_domain: domain,
|
company_domain: domain,
|
||||||
|
source: 'signoz-ui',
|
||||||
};
|
};
|
||||||
|
|
||||||
window.analytics.identify(user?.email, identifyPayload);
|
window.analytics.identify(email, sanitizedIdentifyPayload);
|
||||||
|
|
||||||
window.analytics.group(domain, groupTraits);
|
window.analytics.group(domain, groupTraits);
|
||||||
|
|
||||||
window.clarity('identify', user.email, user.name);
|
window.clarity('identify', email, name);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -282,10 +282,10 @@ const routes: AppRoutes[] = [
|
|||||||
isPrivate: false,
|
isPrivate: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ROUTES.PIPELINES,
|
path: ROUTES.LOGS_PIPELINES,
|
||||||
exact: true,
|
exact: true,
|
||||||
component: PipelinePage,
|
component: PipelinePage,
|
||||||
key: 'PIPELINES',
|
key: 'LOGS_PIPELINES',
|
||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -299,7 +299,7 @@ const routes: AppRoutes[] = [
|
|||||||
path: ROUTES.WORKSPACE_LOCKED,
|
path: ROUTES.WORKSPACE_LOCKED,
|
||||||
exact: true,
|
exact: true,
|
||||||
component: WorkspaceBlocked,
|
component: WorkspaceBlocked,
|
||||||
isPrivate: false,
|
isPrivate: true,
|
||||||
key: 'WORKSPACE_LOCKED',
|
key: 'WORKSPACE_LOCKED',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
11
frontend/src/components/DropDown/DropDown.styles.scss
Normal file
11
frontend/src/components/DropDown/DropDown.styles.scss
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
.dropdown-button {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-button--dark {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-icon {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
30
frontend/src/components/DropDown/DropDown.tsx
Normal file
30
frontend/src/components/DropDown/DropDown.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import './DropDown.styles.scss';
|
||||||
|
|
||||||
|
import { EllipsisOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Dropdown, MenuProps } from 'antd';
|
||||||
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
|
|
||||||
|
function DropDown({ element }: { element: JSX.Element[] }): JSX.Element {
|
||||||
|
const isDarkMode = useIsDarkMode();
|
||||||
|
|
||||||
|
const items: MenuProps['items'] = element.map(
|
||||||
|
(e: JSX.Element, index: number) => ({
|
||||||
|
label: e,
|
||||||
|
key: index,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dropdown menu={{ items }}>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
className={!isDarkMode ? 'dropdown-button--dark' : 'dropdown-button'}
|
||||||
|
onClick={(e): void => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<EllipsisOutlined className="dropdown-icon" />
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DropDown;
|
@ -0,0 +1,25 @@
|
|||||||
|
.DynamicColumnTable {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.dynamicColumnTable-button {
|
||||||
|
align-self: flex-end;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamicColumnsTable-items {
|
||||||
|
display: flex;
|
||||||
|
width: 10.625rem;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.dynamicColumnsTable-items {
|
||||||
|
flex-direction: column;
|
||||||
|
width: auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
112
frontend/src/components/ResizeTable/DynamicColumnTable.tsx
Normal file
112
frontend/src/components/ResizeTable/DynamicColumnTable.tsx
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/* eslint-disable react/jsx-props-no-spreading */
|
||||||
|
import './DynamicColumnTable.syles.scss';
|
||||||
|
|
||||||
|
import { SettingOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Dropdown, MenuProps, Switch } from 'antd';
|
||||||
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
|
import { memo, useEffect, useState } from 'react';
|
||||||
|
import { popupContainer } from 'utils/selectPopupContainer';
|
||||||
|
|
||||||
|
import ResizeTable from './ResizeTable';
|
||||||
|
import { DynamicColumnTableProps } from './types';
|
||||||
|
import {
|
||||||
|
getNewColumnData,
|
||||||
|
getVisibleColumns,
|
||||||
|
setVisibleColumns,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
function DynamicColumnTable({
|
||||||
|
tablesource,
|
||||||
|
columns,
|
||||||
|
dynamicColumns,
|
||||||
|
onDragColumn,
|
||||||
|
...restProps
|
||||||
|
}: DynamicColumnTableProps): JSX.Element {
|
||||||
|
const [columnsData, setColumnsData] = useState<ColumnsType | undefined>(
|
||||||
|
columns,
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const visibleColumns = getVisibleColumns({
|
||||||
|
tablesource,
|
||||||
|
columnsData: columns,
|
||||||
|
dynamicColumns,
|
||||||
|
});
|
||||||
|
setColumnsData((prevColumns) =>
|
||||||
|
prevColumns
|
||||||
|
? [
|
||||||
|
...prevColumns.slice(0, prevColumns.length - 1),
|
||||||
|
...visibleColumns,
|
||||||
|
prevColumns[prevColumns.length - 1],
|
||||||
|
]
|
||||||
|
: undefined,
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onToggleHandler = (index: number) => (
|
||||||
|
checked: boolean,
|
||||||
|
event: React.MouseEvent<HTMLButtonElement>,
|
||||||
|
): void => {
|
||||||
|
event.stopPropagation();
|
||||||
|
setVisibleColumns({
|
||||||
|
tablesource,
|
||||||
|
dynamicColumns,
|
||||||
|
index,
|
||||||
|
checked,
|
||||||
|
});
|
||||||
|
setColumnsData((prevColumns) =>
|
||||||
|
getNewColumnData({
|
||||||
|
checked,
|
||||||
|
index,
|
||||||
|
prevColumns,
|
||||||
|
dynamicColumns,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const items: MenuProps['items'] =
|
||||||
|
dynamicColumns?.map((column, index) => ({
|
||||||
|
label: (
|
||||||
|
<div className="dynamicColumnsTable-items">
|
||||||
|
<div>{column.title?.toString()}</div>
|
||||||
|
<Switch
|
||||||
|
checked={columnsData?.findIndex((c) => c.key === column.key) !== -1}
|
||||||
|
onChange={onToggleHandler(index)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
key: index,
|
||||||
|
type: 'checkbox',
|
||||||
|
})) || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="DynamicColumnTable">
|
||||||
|
{dynamicColumns && (
|
||||||
|
<Dropdown
|
||||||
|
getPopupContainer={popupContainer}
|
||||||
|
menu={{ items }}
|
||||||
|
trigger={['click']}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className="dynamicColumnTable-button"
|
||||||
|
size="middle"
|
||||||
|
icon={<SettingOutlined />}
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ResizeTable
|
||||||
|
columns={columnsData}
|
||||||
|
onDragColumn={onDragColumn}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicColumnTable.defaultProps = {
|
||||||
|
onDragColumn: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(DynamicColumnTable);
|
@ -0,0 +1,15 @@
|
|||||||
|
import { Typography } from 'antd';
|
||||||
|
|
||||||
|
import Time from './Time';
|
||||||
|
|
||||||
|
function DateComponent(
|
||||||
|
CreatedOrUpdateTime: string | number | Date,
|
||||||
|
): JSX.Element {
|
||||||
|
if (CreatedOrUpdateTime === null) {
|
||||||
|
return <Typography> - </Typography>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Time CreatedOrUpdateTime={CreatedOrUpdateTime} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DateComponent;
|
@ -2,16 +2,15 @@ import { Typography } from 'antd';
|
|||||||
import convertDateToAmAndPm from 'lib/convertDateToAmAndPm';
|
import convertDateToAmAndPm from 'lib/convertDateToAmAndPm';
|
||||||
import getFormattedDate from 'lib/getFormatedDate';
|
import getFormattedDate from 'lib/getFormatedDate';
|
||||||
|
|
||||||
import { Data } from '..';
|
function Time({ CreatedOrUpdateTime }: DateProps): JSX.Element {
|
||||||
|
const time = new Date(CreatedOrUpdateTime);
|
||||||
function DateComponent(lastUpdatedTime: Data['lastUpdatedTime']): JSX.Element {
|
|
||||||
const time = new Date(lastUpdatedTime);
|
|
||||||
|
|
||||||
const date = getFormattedDate(time);
|
const date = getFormattedDate(time);
|
||||||
|
|
||||||
const timeString = `${date} ${convertDateToAmAndPm(time)}`;
|
const timeString = `${date} ${convertDateToAmAndPm(time)}`;
|
||||||
|
|
||||||
return <Typography>{timeString}</Typography>;
|
return <Typography>{timeString}</Typography>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DateComponent;
|
type DateProps = {
|
||||||
|
CreatedOrUpdateTime: string | number | Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Time;
|
11
frontend/src/components/ResizeTable/contants.ts
Normal file
11
frontend/src/components/ResizeTable/contants.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const TableDataSource = {
|
||||||
|
Alert: 'alert',
|
||||||
|
Dashboard: 'dashboard',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const DynamicColumnsKey = {
|
||||||
|
CreatedAt: 'createdAt',
|
||||||
|
CreatedBy: 'createdBy',
|
||||||
|
UpdatedAt: 'updatedAt',
|
||||||
|
UpdatedBy: 'updatedBy',
|
||||||
|
};
|
@ -1,6 +1,43 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { TableProps } from 'antd';
|
import { TableProps } from 'antd';
|
||||||
|
import { ColumnsType } from 'antd/es/table';
|
||||||
|
import { ColumnGroupType, ColumnType } from 'antd/lib/table';
|
||||||
|
|
||||||
|
import { TableDataSource } from './contants';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export interface ResizeTableProps extends TableProps<any> {
|
export interface ResizeTableProps extends TableProps<any> {
|
||||||
onDragColumn?: (fromIndex: number, toIndex: number) => void;
|
onDragColumn?: (fromIndex: number, toIndex: number) => void;
|
||||||
}
|
}
|
||||||
|
export interface DynamicColumnTableProps extends TableProps<any> {
|
||||||
|
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
|
||||||
|
dynamicColumns: TableProps<any>['columns'];
|
||||||
|
onDragColumn?: (fromIndex: number, toIndex: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GetVisibleColumnsFunction = (
|
||||||
|
props: GetVisibleColumnProps,
|
||||||
|
) => (ColumnGroupType<any> | ColumnType<any>)[];
|
||||||
|
|
||||||
|
export type GetVisibleColumnProps = {
|
||||||
|
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
|
||||||
|
dynamicColumns?: ColumnsType<any>;
|
||||||
|
columnsData?: ColumnsType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SetVisibleColumnsProps = {
|
||||||
|
checked: boolean;
|
||||||
|
index: number;
|
||||||
|
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
|
||||||
|
dynamicColumns?: ColumnsType<any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type GetNewColumnDataProps = {
|
||||||
|
prevColumns?: ColumnsType;
|
||||||
|
checked: boolean;
|
||||||
|
dynamicColumns?: ColumnsType<any>;
|
||||||
|
index: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetNewColumnDataFunction = (
|
||||||
|
props: GetNewColumnDataProps,
|
||||||
|
) => ColumnsType | undefined;
|
||||||
|
77
frontend/src/components/ResizeTable/utils.ts
Normal file
77
frontend/src/components/ResizeTable/utils.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { DynamicColumnsKey } from './contants';
|
||||||
|
import {
|
||||||
|
GetNewColumnDataFunction,
|
||||||
|
GetVisibleColumnsFunction,
|
||||||
|
SetVisibleColumnsProps,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
export const getVisibleColumns: GetVisibleColumnsFunction = ({
|
||||||
|
tablesource,
|
||||||
|
dynamicColumns,
|
||||||
|
columnsData,
|
||||||
|
}) => {
|
||||||
|
let columnVisibilityData: { [key: string]: boolean };
|
||||||
|
try {
|
||||||
|
const storedData = localStorage.getItem(tablesource);
|
||||||
|
if (typeof storedData === 'string' && dynamicColumns) {
|
||||||
|
columnVisibilityData = JSON.parse(storedData);
|
||||||
|
return dynamicColumns.filter((column) => {
|
||||||
|
if (column.key && !columnsData?.find((c) => c.key === column.key)) {
|
||||||
|
return columnVisibilityData[column.key];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialColumnVisibility: Record<string, boolean> = {};
|
||||||
|
Object.values(DynamicColumnsKey).forEach((key) => {
|
||||||
|
initialColumnVisibility[key] = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
localStorage.setItem(tablesource, JSON.stringify(initialColumnVisibility));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setVisibleColumns = ({
|
||||||
|
checked,
|
||||||
|
index,
|
||||||
|
tablesource,
|
||||||
|
dynamicColumns,
|
||||||
|
}: SetVisibleColumnsProps): void => {
|
||||||
|
try {
|
||||||
|
const storedData = localStorage.getItem(tablesource);
|
||||||
|
if (typeof storedData === 'string' && dynamicColumns) {
|
||||||
|
const columnVisibilityData = JSON.parse(storedData);
|
||||||
|
const { key } = dynamicColumns[index];
|
||||||
|
if (key) {
|
||||||
|
columnVisibilityData[key] = checked;
|
||||||
|
}
|
||||||
|
localStorage.setItem(tablesource, JSON.stringify(columnVisibilityData));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getNewColumnData: GetNewColumnDataFunction = ({
|
||||||
|
prevColumns,
|
||||||
|
checked,
|
||||||
|
dynamicColumns,
|
||||||
|
index,
|
||||||
|
}) => {
|
||||||
|
if (checked && dynamicColumns) {
|
||||||
|
return prevColumns
|
||||||
|
? [
|
||||||
|
...prevColumns.slice(0, prevColumns.length - 1),
|
||||||
|
dynamicColumns[index],
|
||||||
|
prevColumns[prevColumns.length - 1],
|
||||||
|
]
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
return prevColumns && dynamicColumns
|
||||||
|
? prevColumns.filter((column) => dynamicColumns[index].title !== column.title)
|
||||||
|
: undefined;
|
||||||
|
};
|
@ -0,0 +1,10 @@
|
|||||||
|
.label-column {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.label-column--tag {
|
||||||
|
white-space: normal;
|
||||||
|
margin: 0.2rem 0.2rem;
|
||||||
|
}
|
||||||
|
}
|
54
frontend/src/components/TableRenderer/LabelColumn.tsx
Normal file
54
frontend/src/components/TableRenderer/LabelColumn.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import './LabelColumn.styles.scss';
|
||||||
|
|
||||||
|
import { Popover, Tag } from 'antd';
|
||||||
|
import { popupContainer } from 'utils/selectPopupContainer';
|
||||||
|
|
||||||
|
import { LabelColumnProps } from './TableRenderer.types';
|
||||||
|
import TagWithToolTip from './TagWithToolTip';
|
||||||
|
|
||||||
|
function LabelColumn({ labels, value, color }: LabelColumnProps): JSX.Element {
|
||||||
|
const newLabels = labels.length > 3 ? labels.slice(0, 3) : labels;
|
||||||
|
const remainingLabels = labels.length > 3 ? labels.slice(3) : [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="label-column">
|
||||||
|
{newLabels.map(
|
||||||
|
(label: string): JSX.Element => (
|
||||||
|
<TagWithToolTip key={label} label={label} color={color} value={value} />
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
{remainingLabels.length > 0 && (
|
||||||
|
<Popover
|
||||||
|
getPopupContainer={popupContainer}
|
||||||
|
placement="bottomRight"
|
||||||
|
showArrow={false}
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
{labels.map(
|
||||||
|
(label: string): JSX.Element => (
|
||||||
|
<TagWithToolTip
|
||||||
|
key={label}
|
||||||
|
label={label}
|
||||||
|
color={color}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
trigger="hover"
|
||||||
|
>
|
||||||
|
<Tag className="label-column--tag" color={color}>
|
||||||
|
+{remainingLabels.length}
|
||||||
|
</Tag>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelColumn.defaultProps = {
|
||||||
|
value: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LabelColumn;
|
@ -0,0 +1,5 @@
|
|||||||
|
export type LabelColumnProps = {
|
||||||
|
labels: string[];
|
||||||
|
color?: string;
|
||||||
|
value?: { [key: string]: string };
|
||||||
|
};
|
36
frontend/src/components/TableRenderer/TagWithToolTip.tsx
Normal file
36
frontend/src/components/TableRenderer/TagWithToolTip.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Tag, Tooltip } from 'antd';
|
||||||
|
|
||||||
|
import { getLabelRenderingValue } from './utils';
|
||||||
|
|
||||||
|
function TagWithToolTip({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
color,
|
||||||
|
}: TagWithToolTipProps): JSX.Element {
|
||||||
|
const tooltipTitle =
|
||||||
|
value && value[label] ? `${label}: ${value[label]}` : label;
|
||||||
|
return (
|
||||||
|
<div key={label}>
|
||||||
|
<Tooltip title={tooltipTitle}>
|
||||||
|
<Tag className="label-column--tag" color={color}>
|
||||||
|
{getLabelRenderingValue(label, value && value[label])}
|
||||||
|
</Tag>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type TagWithToolTipProps = {
|
||||||
|
label: string;
|
||||||
|
color?: string;
|
||||||
|
value?: {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
TagWithToolTip.defaultProps = {
|
||||||
|
value: undefined,
|
||||||
|
color: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TagWithToolTip;
|
@ -16,6 +16,28 @@ export const generatorResizeTableColumns = <T>({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const getLabelRenderingValue = (
|
||||||
|
label: string,
|
||||||
|
value?: string,
|
||||||
|
): string => {
|
||||||
|
const maxLength = 20;
|
||||||
|
|
||||||
|
if (label.length > maxLength) {
|
||||||
|
return `${label.slice(0, maxLength)}...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
const remainingSpace = maxLength - label.length;
|
||||||
|
let newValue = value;
|
||||||
|
if (value.length > remainingSpace) {
|
||||||
|
newValue = `${value.slice(0, remainingSpace)}...`;
|
||||||
|
}
|
||||||
|
return `${label}: ${newValue}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return label;
|
||||||
|
};
|
||||||
|
|
||||||
interface GeneratorResizeTableColumnsProp<T> {
|
interface GeneratorResizeTableColumnsProp<T> {
|
||||||
baseColumnOptions: ColumnsType<T>;
|
baseColumnOptions: ColumnsType<T>;
|
||||||
dynamicColumnOption: { key: string; columnOption: ColumnType<T> }[];
|
dynamicColumnOption: { key: string; columnOption: ColumnType<T> }[];
|
||||||
|
@ -31,13 +31,12 @@ const ROUTES = {
|
|||||||
LOGS: '/logs',
|
LOGS: '/logs',
|
||||||
LOGS_EXPLORER: '/logs-explorer',
|
LOGS_EXPLORER: '/logs-explorer',
|
||||||
LIVE_LOGS: '/logs-explorer/live',
|
LIVE_LOGS: '/logs-explorer/live',
|
||||||
|
LOGS_PIPELINES: '/pipelines',
|
||||||
HOME_PAGE: '/',
|
HOME_PAGE: '/',
|
||||||
PASSWORD_RESET: '/password-reset',
|
PASSWORD_RESET: '/password-reset',
|
||||||
LIST_LICENSES: '/licenses',
|
LIST_LICENSES: '/licenses',
|
||||||
LOGS_INDEX_FIELDS: '/logs-explorer/index-fields',
|
LOGS_INDEX_FIELDS: '/logs-explorer/index-fields',
|
||||||
LOGS_PIPELINE: '/logs-explorer/pipeline',
|
|
||||||
TRACE_EXPLORER: '/trace-explorer',
|
TRACE_EXPLORER: '/trace-explorer',
|
||||||
PIPELINES: '/pipelines',
|
|
||||||
BILLING: '/billing',
|
BILLING: '/billing',
|
||||||
SUPPORT: '/support',
|
SUPPORT: '/support',
|
||||||
WORKSPACE_LOCKED: '/workspace-locked',
|
WORKSPACE_LOCKED: '/workspace-locked',
|
||||||
|
4
frontend/src/container/Download/Download.styles.scss
Normal file
4
frontend/src/container/Download/Download.styles.scss
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.download-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
77
frontend/src/container/Download/Download.tsx
Normal file
77
frontend/src/container/Download/Download.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import './Download.styles.scss';
|
||||||
|
|
||||||
|
import { CloudDownloadOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Dropdown, MenuProps } from 'antd';
|
||||||
|
import { Excel } from 'antd-table-saveas-excel';
|
||||||
|
import { unparse } from 'papaparse';
|
||||||
|
|
||||||
|
import { DownloadProps } from './Download.types';
|
||||||
|
|
||||||
|
function Download({ data, isLoading, fileName }: DownloadProps): JSX.Element {
|
||||||
|
const downloadExcelFile = (): void => {
|
||||||
|
const headers = Object.keys(Object.assign({}, ...data)).map((item) => {
|
||||||
|
const updatedTitle = item
|
||||||
|
.split('_')
|
||||||
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
|
.join(' ');
|
||||||
|
return {
|
||||||
|
title: updatedTitle,
|
||||||
|
dataIndex: item,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const excel = new Excel();
|
||||||
|
excel
|
||||||
|
.addSheet(fileName)
|
||||||
|
.addColumns(headers)
|
||||||
|
.addDataSource(data, {
|
||||||
|
str2Percent: true,
|
||||||
|
})
|
||||||
|
.saveAs(`${fileName}.xlsx`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadCsvFile = (): void => {
|
||||||
|
const csv = unparse(data);
|
||||||
|
const csvBlob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
||||||
|
const csvUrl = URL.createObjectURL(csvBlob);
|
||||||
|
const downloadLink = document.createElement('a');
|
||||||
|
downloadLink.href = csvUrl;
|
||||||
|
downloadLink.download = `${fileName}.csv`;
|
||||||
|
downloadLink.click();
|
||||||
|
downloadLink.remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
const menu: MenuProps = {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: 'download-as-excel',
|
||||||
|
label: 'Excel',
|
||||||
|
onClick: downloadExcelFile,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'download-as-csv',
|
||||||
|
label: 'CSV',
|
||||||
|
onClick: downloadCsvFile,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dropdown menu={menu} trigger={['click']}>
|
||||||
|
<Button
|
||||||
|
className="download-button"
|
||||||
|
loading={isLoading}
|
||||||
|
size="small"
|
||||||
|
type="link"
|
||||||
|
>
|
||||||
|
<CloudDownloadOutlined />
|
||||||
|
Download
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Download.defaultProps = {
|
||||||
|
isLoading: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Download;
|
10
frontend/src/container/Download/Download.types.ts
Normal file
10
frontend/src/container/Download/Download.types.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export type DownloadOptions = {
|
||||||
|
isDownloadEnabled: boolean;
|
||||||
|
fileName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DownloadProps = {
|
||||||
|
data: Record<string, string>[];
|
||||||
|
isLoading?: boolean;
|
||||||
|
fileName: string;
|
||||||
|
};
|
@ -2,6 +2,7 @@ import { Form, Select } from 'antd';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AlertDef, Labels } from 'types/api/alerts/def';
|
import { AlertDef, Labels } from 'types/api/alerts/def';
|
||||||
import { requireErrorMessage } from 'utils/form/requireErrorMessage';
|
import { requireErrorMessage } from 'utils/form/requireErrorMessage';
|
||||||
|
import { popupContainer } from 'utils/selectPopupContainer';
|
||||||
|
|
||||||
import ChannelSelect from './ChannelSelect';
|
import ChannelSelect from './ChannelSelect';
|
||||||
import LabelSelect from './labels';
|
import LabelSelect from './labels';
|
||||||
@ -36,6 +37,7 @@ function BasicInfo({ alertDef, setAlertDef }: BasicInfoProps): JSX.Element {
|
|||||||
name={['labels', 'severity']}
|
name={['labels', 'severity']}
|
||||||
>
|
>
|
||||||
<SeveritySelect
|
<SeveritySelect
|
||||||
|
getPopupContainer={popupContainer}
|
||||||
defaultValue="critical"
|
defaultValue="critical"
|
||||||
onChange={(value: unknown | string): void => {
|
onChange={(value: unknown | string): void => {
|
||||||
const s = (value as string) || 'critical';
|
const s = (value as string) || 'critical';
|
||||||
|
@ -9,9 +9,12 @@ import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
|||||||
import getChartData from 'lib/getChartData';
|
import getChartData from 'lib/getChartData';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
import { AlertDef } from 'types/api/alerts/def';
|
import { AlertDef } from 'types/api/alerts/def';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import { ChartContainer, FailedMessageContainer } from './styles';
|
import { ChartContainer, FailedMessageContainer } from './styles';
|
||||||
import { covertIntoDataFormats } from './utils';
|
import { covertIntoDataFormats } from './utils';
|
||||||
@ -41,6 +44,9 @@ function ChartPreview({
|
|||||||
}: ChartPreviewProps): JSX.Element | null {
|
}: ChartPreviewProps): JSX.Element | null {
|
||||||
const { t } = useTranslation('alerts');
|
const { t } = useTranslation('alerts');
|
||||||
const threshold = alertDef?.condition.target || 0;
|
const threshold = alertDef?.condition.target || 0;
|
||||||
|
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||||
|
(state) => state.globalTime,
|
||||||
|
);
|
||||||
|
|
||||||
const thresholdValue = covertIntoDataFormats({
|
const thresholdValue = covertIntoDataFormats({
|
||||||
value: threshold,
|
value: threshold,
|
||||||
@ -100,6 +106,8 @@ function ChartPreview({
|
|||||||
'chartPreview',
|
'chartPreview',
|
||||||
userQueryKey || JSON.stringify(query),
|
userQueryKey || JSON.stringify(query),
|
||||||
selectedInterval,
|
selectedInterval,
|
||||||
|
minTime,
|
||||||
|
maxTime,
|
||||||
],
|
],
|
||||||
retry: false,
|
retry: false,
|
||||||
enabled: canQuery,
|
enabled: canQuery,
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
defaultMatchType,
|
defaultMatchType,
|
||||||
} from 'types/api/alerts/def';
|
} from 'types/api/alerts/def';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
|
import { popupContainer } from 'utils/selectPopupContainer';
|
||||||
|
|
||||||
import { FormContainer, InlineSelect, StepHeading } from './styles';
|
import { FormContainer, InlineSelect, StepHeading } from './styles';
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ function RuleOptions({
|
|||||||
|
|
||||||
const renderCompareOps = (): JSX.Element => (
|
const renderCompareOps = (): JSX.Element => (
|
||||||
<InlineSelect
|
<InlineSelect
|
||||||
|
getPopupContainer={popupContainer}
|
||||||
defaultValue={defaultCompareOp}
|
defaultValue={defaultCompareOp}
|
||||||
value={alertDef.condition?.op}
|
value={alertDef.condition?.op}
|
||||||
style={{ minWidth: '120px' }}
|
style={{ minWidth: '120px' }}
|
||||||
@ -69,6 +71,7 @@ function RuleOptions({
|
|||||||
|
|
||||||
const renderThresholdMatchOpts = (): JSX.Element => (
|
const renderThresholdMatchOpts = (): JSX.Element => (
|
||||||
<InlineSelect
|
<InlineSelect
|
||||||
|
getPopupContainer={popupContainer}
|
||||||
defaultValue={defaultMatchType}
|
defaultValue={defaultMatchType}
|
||||||
style={{ minWidth: '130px' }}
|
style={{ minWidth: '130px' }}
|
||||||
value={alertDef.condition?.matchType}
|
value={alertDef.condition?.matchType}
|
||||||
@ -83,6 +86,7 @@ function RuleOptions({
|
|||||||
|
|
||||||
const renderPromMatchOpts = (): JSX.Element => (
|
const renderPromMatchOpts = (): JSX.Element => (
|
||||||
<InlineSelect
|
<InlineSelect
|
||||||
|
getPopupContainer={popupContainer}
|
||||||
defaultValue={defaultMatchType}
|
defaultValue={defaultMatchType}
|
||||||
style={{ minWidth: '130px' }}
|
style={{ minWidth: '130px' }}
|
||||||
value={alertDef.condition?.matchType}
|
value={alertDef.condition?.matchType}
|
||||||
@ -94,6 +98,7 @@ function RuleOptions({
|
|||||||
|
|
||||||
const renderEvalWindows = (): JSX.Element => (
|
const renderEvalWindows = (): JSX.Element => (
|
||||||
<InlineSelect
|
<InlineSelect
|
||||||
|
getPopupContainer={popupContainer}
|
||||||
defaultValue={defaultEvalWindow}
|
defaultValue={defaultEvalWindow}
|
||||||
style={{ minWidth: '120px' }}
|
style={{ minWidth: '120px' }}
|
||||||
value={alertDef.evalWindow}
|
value={alertDef.evalWindow}
|
||||||
@ -180,6 +185,7 @@ function RuleOptions({
|
|||||||
|
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Select
|
<Select
|
||||||
|
getPopupContainer={popupContainer}
|
||||||
allowClear
|
allowClear
|
||||||
showSearch
|
showSearch
|
||||||
options={categorySelectOptions}
|
options={categorySelectOptions}
|
||||||
|
@ -44,7 +44,6 @@ import {
|
|||||||
StyledLeftContainer,
|
StyledLeftContainer,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
import UserGuide from './UserGuide';
|
import UserGuide from './UserGuide';
|
||||||
import { getUpdatedStepInterval, toChartInterval } from './utils';
|
|
||||||
|
|
||||||
function FormAlertRules({
|
function FormAlertRules({
|
||||||
alertType,
|
alertType,
|
||||||
@ -55,9 +54,10 @@ function FormAlertRules({
|
|||||||
// init namespace for translations
|
// init namespace for translations
|
||||||
const { t } = useTranslation('alerts');
|
const { t } = useTranslation('alerts');
|
||||||
|
|
||||||
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector<
|
||||||
(state) => state.globalTime,
|
AppState,
|
||||||
);
|
GlobalReducer
|
||||||
|
>((state) => state.globalTime);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
currentQuery,
|
currentQuery,
|
||||||
@ -354,16 +354,6 @@ function FormAlertRules({
|
|||||||
<BasicInfo alertDef={alertDef} setAlertDef={setAlertDef} />
|
<BasicInfo alertDef={alertDef} setAlertDef={setAlertDef} />
|
||||||
);
|
);
|
||||||
|
|
||||||
const updatedStagedQuery = useMemo((): Query | null => {
|
|
||||||
const newQuery: Query | null = stagedQuery;
|
|
||||||
if (newQuery) {
|
|
||||||
newQuery.builder.queryData[0].stepInterval = getUpdatedStepInterval(
|
|
||||||
alertDef.evalWindow,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return newQuery;
|
|
||||||
}, [alertDef.evalWindow, stagedQuery]);
|
|
||||||
|
|
||||||
const renderQBChartPreview = (): JSX.Element => (
|
const renderQBChartPreview = (): JSX.Element => (
|
||||||
<ChartPreview
|
<ChartPreview
|
||||||
headline={
|
headline={
|
||||||
@ -373,10 +363,9 @@ function FormAlertRules({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
name=""
|
name=""
|
||||||
query={updatedStagedQuery}
|
query={stagedQuery}
|
||||||
selectedInterval={toChartInterval(alertDef.evalWindow)}
|
selectedInterval={globalSelectedInterval}
|
||||||
alertDef={alertDef}
|
alertDef={alertDef}
|
||||||
allowSelectedIntervalForStepGen
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -405,7 +394,7 @@ function FormAlertRules({
|
|||||||
name="Chart Preview"
|
name="Chart Preview"
|
||||||
query={stagedQuery}
|
query={stagedQuery}
|
||||||
alertDef={alertDef}
|
alertDef={alertDef}
|
||||||
selectedInterval={toChartInterval(alertDef.evalWindow)}
|
selectedInterval={globalSelectedInterval}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Typography } from 'antd';
|
import { Skeleton, Typography } from 'antd';
|
||||||
import { ToggleGraphProps } from 'components/Graph/types';
|
import { ToggleGraphProps } from 'components/Graph/types';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||||
@ -249,20 +249,23 @@ function WidgetGraphComponent({
|
|||||||
isWarning={isWarning}
|
isWarning={isWarning}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<GridPanelSwitch
|
{queryResponse.isLoading && <Skeleton />}
|
||||||
panelType={widget.panelTypes}
|
{queryResponse.isSuccess && (
|
||||||
data={data}
|
<GridPanelSwitch
|
||||||
isStacked={widget.isStacked}
|
panelType={widget.panelTypes}
|
||||||
opacity={widget.opacity}
|
data={data}
|
||||||
title={' '}
|
isStacked={widget.isStacked}
|
||||||
name={name}
|
opacity={widget.opacity}
|
||||||
yAxisUnit={widget.yAxisUnit}
|
title={' '}
|
||||||
onClickHandler={onClickHandler}
|
name={name}
|
||||||
onDragSelect={onDragSelect}
|
yAxisUnit={widget.yAxisUnit}
|
||||||
panelData={queryResponse.data?.payload?.data.newResult.data.result || []}
|
onClickHandler={onClickHandler}
|
||||||
query={widget.query}
|
onDragSelect={onDragSelect}
|
||||||
ref={lineChartRef}
|
panelData={queryResponse.data?.payload?.data.newResult.data.result || []}
|
||||||
/>
|
query={widget.query}
|
||||||
|
ref={lineChartRef}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { Skeleton } from 'antd';
|
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||||
import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
||||||
@ -99,10 +98,6 @@ function GridCardGraph({
|
|||||||
|
|
||||||
const isEmptyLayout = widget?.id === PANEL_TYPES.EMPTY_WIDGET;
|
const isEmptyLayout = widget?.id === PANEL_TYPES.EMPTY_WIDGET;
|
||||||
|
|
||||||
if (queryResponse.isLoading) {
|
|
||||||
return <Skeleton />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span ref={graphRef}>
|
<span ref={graphRef}>
|
||||||
<WidgetGraphComponent
|
<WidgetGraphComponent
|
||||||
|
@ -3,7 +3,14 @@ import { PlusOutlined } from '@ant-design/icons';
|
|||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import saveAlertApi from 'api/alerts/save';
|
import saveAlertApi from 'api/alerts/save';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import DropDown from 'components/DropDown/DropDown';
|
||||||
|
import {
|
||||||
|
DynamicColumnsKey,
|
||||||
|
TableDataSource,
|
||||||
|
} from 'components/ResizeTable/contants';
|
||||||
|
import DynamicColumnTable from 'components/ResizeTable/DynamicColumnTable';
|
||||||
|
import DateComponent from 'components/ResizeTable/TableComponent/DateComponent';
|
||||||
|
import LabelColumn from 'components/TableRenderer/LabelColumn';
|
||||||
import TextToolTip from 'components/TextToolTip';
|
import TextToolTip from 'components/TextToolTip';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@ -22,7 +29,7 @@ import { GettableAlert } from 'types/api/alerts/get';
|
|||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
import DeleteAlert from './DeleteAlert';
|
import DeleteAlert from './DeleteAlert';
|
||||||
import { Button, ButtonContainer, ColumnButton, StyledTag } from './styles';
|
import { Button, ButtonContainer, ColumnButton } from './styles';
|
||||||
import Status from './TableComponents/Status';
|
import Status from './TableComponents/Status';
|
||||||
import ToggleAlertState from './ToggleAlertState';
|
import ToggleAlertState from './ToggleAlertState';
|
||||||
|
|
||||||
@ -121,6 +128,51 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const dynamicColumns: ColumnsType<GettableAlert> = [
|
||||||
|
{
|
||||||
|
title: 'Created At',
|
||||||
|
dataIndex: 'createAt',
|
||||||
|
width: 80,
|
||||||
|
key: DynamicColumnsKey.CreatedAt,
|
||||||
|
align: 'center',
|
||||||
|
sorter: (a: GettableAlert, b: GettableAlert): number => {
|
||||||
|
const prev = new Date(a.createAt).getTime();
|
||||||
|
const next = new Date(b.createAt).getTime();
|
||||||
|
|
||||||
|
return prev - next;
|
||||||
|
},
|
||||||
|
render: DateComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Created By',
|
||||||
|
dataIndex: 'createBy',
|
||||||
|
width: 80,
|
||||||
|
key: DynamicColumnsKey.CreatedBy,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Updated At',
|
||||||
|
dataIndex: 'updateAt',
|
||||||
|
width: 80,
|
||||||
|
key: DynamicColumnsKey.UpdatedAt,
|
||||||
|
align: 'center',
|
||||||
|
sorter: (a: GettableAlert, b: GettableAlert): number => {
|
||||||
|
const prev = new Date(a.updateAt).getTime();
|
||||||
|
const next = new Date(b.updateAt).getTime();
|
||||||
|
|
||||||
|
return prev - next;
|
||||||
|
},
|
||||||
|
render: DateComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Updated By',
|
||||||
|
dataIndex: 'updateBy',
|
||||||
|
width: 80,
|
||||||
|
key: DynamicColumnsKey.UpdatedBy,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const columns: ColumnsType<GettableAlert> = [
|
const columns: ColumnsType<GettableAlert> = [
|
||||||
{
|
{
|
||||||
title: 'Status',
|
title: 'Status',
|
||||||
@ -178,13 +230,7 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<LabelColumn labels={withOutSeverityKeys} value={value} color="magenta" />
|
||||||
{withOutSeverityKeys.map((e) => (
|
|
||||||
<StyledTag key={e} color="magenta">
|
|
||||||
{e}: {value[e]}
|
|
||||||
</StyledTag>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -195,20 +241,30 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
title: 'Action',
|
title: 'Action',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
width: 120,
|
width: 10,
|
||||||
render: (id: GettableAlert['id'], record): JSX.Element => (
|
render: (id: GettableAlert['id'], record): JSX.Element => (
|
||||||
<>
|
<DropDown
|
||||||
<ToggleAlertState disabled={record.disabled} setData={setData} id={id} />
|
element={[
|
||||||
|
<ToggleAlertState
|
||||||
<ColumnButton onClick={onEditHandler(record)} type="link">
|
key="1"
|
||||||
Edit
|
disabled={record.disabled}
|
||||||
</ColumnButton>
|
setData={setData}
|
||||||
<ColumnButton onClick={onCloneHandler(record)} type="link">
|
id={id}
|
||||||
Clone
|
/>,
|
||||||
</ColumnButton>
|
<ColumnButton key="2" onClick={onEditHandler(record)} type="link">
|
||||||
|
Edit
|
||||||
<DeleteAlert notifications={notificationsApi} setData={setData} id={id} />
|
</ColumnButton>,
|
||||||
</>
|
<ColumnButton key="3" onClick={onCloneHandler(record)} type="link">
|
||||||
|
Clone
|
||||||
|
</ColumnButton>,
|
||||||
|
<DeleteAlert
|
||||||
|
key="4"
|
||||||
|
notifications={notificationsApi}
|
||||||
|
setData={setData}
|
||||||
|
id={id}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -229,7 +285,13 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
<ResizeTable columns={columns} rowKey="id" dataSource={data} />
|
<DynamicColumnTable
|
||||||
|
tablesource={TableDataSource.Alert}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="id"
|
||||||
|
dataSource={data}
|
||||||
|
dynamicColumns={dynamicColumns}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Button as ButtonComponent, Tag } from 'antd';
|
import { Button as ButtonComponent } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const ButtonContainer = styled.div`
|
export const ButtonContainer = styled.div`
|
||||||
@ -23,9 +23,3 @@ export const ColumnButton = styled(ButtonComponent)`
|
|||||||
margin-right: 1.5em;
|
margin-right: 1.5em;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const StyledTag = styled(Tag)`
|
|
||||||
&&& {
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
@ -10,6 +10,8 @@ describe('executeSearchQueries', () => {
|
|||||||
uuid: uuid(),
|
uuid: uuid(),
|
||||||
created_at: '',
|
created_at: '',
|
||||||
updated_at: '',
|
updated_at: '',
|
||||||
|
created_by: '',
|
||||||
|
updated_by: '',
|
||||||
data: {
|
data: {
|
||||||
title: 'first dashboard',
|
title: 'first dashboard',
|
||||||
variables: {},
|
variables: {},
|
||||||
@ -20,6 +22,8 @@ describe('executeSearchQueries', () => {
|
|||||||
uuid: uuid(),
|
uuid: uuid(),
|
||||||
created_at: '',
|
created_at: '',
|
||||||
updated_at: '',
|
updated_at: '',
|
||||||
|
created_by: '',
|
||||||
|
updated_by: '',
|
||||||
data: {
|
data: {
|
||||||
title: 'second dashboard',
|
title: 'second dashboard',
|
||||||
variables: {},
|
variables: {},
|
||||||
@ -30,6 +34,8 @@ describe('executeSearchQueries', () => {
|
|||||||
uuid: uuid(),
|
uuid: uuid(),
|
||||||
created_at: '',
|
created_at: '',
|
||||||
updated_at: '',
|
updated_at: '',
|
||||||
|
created_by: '',
|
||||||
|
updated_by: '',
|
||||||
data: {
|
data: {
|
||||||
title: 'third dashboard (with special characters +?\\)',
|
title: 'third dashboard (with special characters +?\\)',
|
||||||
variables: {},
|
variables: {},
|
||||||
|
@ -45,18 +45,30 @@ function DeleteButton({ id }: Data): JSX.Element {
|
|||||||
|
|
||||||
// This is to avoid the type collision
|
// This is to avoid the type collision
|
||||||
function Wrapper(props: Data): JSX.Element {
|
function Wrapper(props: Data): JSX.Element {
|
||||||
const { createdBy, description, id, key, lastUpdatedTime, name, tags } = props;
|
const {
|
||||||
|
createdAt,
|
||||||
|
description,
|
||||||
|
id,
|
||||||
|
key,
|
||||||
|
lastUpdatedTime,
|
||||||
|
name,
|
||||||
|
tags,
|
||||||
|
createdBy,
|
||||||
|
lastUpdatedBy,
|
||||||
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DeleteButton
|
<DeleteButton
|
||||||
{...{
|
{...{
|
||||||
createdBy,
|
createdAt,
|
||||||
description,
|
description,
|
||||||
id,
|
id,
|
||||||
key,
|
key,
|
||||||
lastUpdatedTime,
|
lastUpdatedTime,
|
||||||
name,
|
name,
|
||||||
tags,
|
tags,
|
||||||
|
createdBy,
|
||||||
|
lastUpdatedBy,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -10,7 +10,12 @@ import {
|
|||||||
import { ItemType } from 'antd/es/menu/hooks/useItems';
|
import { ItemType } from 'antd/es/menu/hooks/useItems';
|
||||||
import createDashboard from 'api/dashboard/create';
|
import createDashboard from 'api/dashboard/create';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import {
|
||||||
|
DynamicColumnsKey,
|
||||||
|
TableDataSource,
|
||||||
|
} from 'components/ResizeTable/contants';
|
||||||
|
import DynamicColumnTable from 'components/ResizeTable/DynamicColumnTable';
|
||||||
|
import LabelColumn from 'components/TableRenderer/LabelColumn';
|
||||||
import TextToolTip from 'components/TextToolTip';
|
import TextToolTip from 'components/TextToolTip';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import SearchFilter from 'container/ListOfDashboard/SearchFilter';
|
import SearchFilter from 'container/ListOfDashboard/SearchFilter';
|
||||||
@ -26,13 +31,11 @@ import { Dashboard } from 'types/api/dashboard/getAll';
|
|||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
import { popupContainer } from 'utils/selectPopupContainer';
|
import { popupContainer } from 'utils/selectPopupContainer';
|
||||||
|
|
||||||
|
import DateComponent from '../../components/ResizeTable/TableComponent/DateComponent';
|
||||||
import ImportJSON from './ImportJSON';
|
import ImportJSON from './ImportJSON';
|
||||||
import { ButtonContainer, NewDashboardButton, TableContainer } from './styles';
|
import { ButtonContainer, NewDashboardButton, TableContainer } from './styles';
|
||||||
import Createdby from './TableComponents/CreatedBy';
|
|
||||||
import DateComponent from './TableComponents/Date';
|
|
||||||
import DeleteButton from './TableComponents/DeleteButton';
|
import DeleteButton from './TableComponents/DeleteButton';
|
||||||
import Name from './TableComponents/Name';
|
import Name from './TableComponents/Name';
|
||||||
import Tags from './TableComponents/Tags';
|
|
||||||
|
|
||||||
function ListOfAllDashboard(): JSX.Element {
|
function ListOfAllDashboard(): JSX.Element {
|
||||||
const {
|
const {
|
||||||
@ -71,48 +74,66 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const dynamicColumns: TableColumnProps<Data>[] = [
|
||||||
|
{
|
||||||
|
title: 'Created At',
|
||||||
|
dataIndex: 'createdAt',
|
||||||
|
width: 30,
|
||||||
|
key: DynamicColumnsKey.CreatedAt,
|
||||||
|
sorter: (a: Data, b: Data): number => {
|
||||||
|
console.log({ a });
|
||||||
|
const prev = new Date(a.createdAt).getTime();
|
||||||
|
const next = new Date(b.createdAt).getTime();
|
||||||
|
|
||||||
|
return prev - next;
|
||||||
|
},
|
||||||
|
render: DateComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Created By',
|
||||||
|
dataIndex: 'createdBy',
|
||||||
|
width: 30,
|
||||||
|
key: DynamicColumnsKey.CreatedBy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Last Updated Time',
|
||||||
|
width: 30,
|
||||||
|
dataIndex: 'lastUpdatedTime',
|
||||||
|
key: DynamicColumnsKey.UpdatedAt,
|
||||||
|
sorter: (a: Data, b: Data): number => {
|
||||||
|
const prev = new Date(a.lastUpdatedTime).getTime();
|
||||||
|
const next = new Date(b.lastUpdatedTime).getTime();
|
||||||
|
|
||||||
|
return prev - next;
|
||||||
|
},
|
||||||
|
render: DateComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Last Updated By',
|
||||||
|
dataIndex: 'lastUpdatedBy',
|
||||||
|
width: 30,
|
||||||
|
key: DynamicColumnsKey.UpdatedBy,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
const tableColumns: TableColumnProps<Data>[] = [
|
const tableColumns: TableColumnProps<Data>[] = [
|
||||||
{
|
{
|
||||||
title: 'Name',
|
title: 'Name',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
width: 100,
|
width: 40,
|
||||||
render: Name,
|
render: Name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Description',
|
title: 'Description',
|
||||||
width: 100,
|
width: 50,
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Tags (can be multiple)',
|
title: 'Tags (can be multiple)',
|
||||||
dataIndex: 'tags',
|
dataIndex: 'tags',
|
||||||
width: 80,
|
width: 50,
|
||||||
render: Tags,
|
render: (value): JSX.Element => <LabelColumn labels={value} />,
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Created At',
|
|
||||||
dataIndex: 'createdBy',
|
|
||||||
width: 80,
|
|
||||||
sorter: (a: Data, b: Data): number => {
|
|
||||||
const prev = new Date(a.createdBy).getTime();
|
|
||||||
const next = new Date(b.createdBy).getTime();
|
|
||||||
|
|
||||||
return prev - next;
|
|
||||||
},
|
|
||||||
render: Createdby,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Last Updated Time',
|
|
||||||
width: 90,
|
|
||||||
dataIndex: 'lastUpdatedTime',
|
|
||||||
sorter: (a: Data, b: Data): number => {
|
|
||||||
const prev = new Date(a.lastUpdatedTime).getTime();
|
|
||||||
const next = new Date(b.lastUpdatedTime).getTime();
|
|
||||||
|
|
||||||
return prev - next;
|
|
||||||
},
|
|
||||||
render: DateComponent,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -130,13 +151,15 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
|
|
||||||
const data: Data[] =
|
const data: Data[] =
|
||||||
filteredDashboards?.map((e) => ({
|
filteredDashboards?.map((e) => ({
|
||||||
createdBy: e.created_at,
|
createdAt: e.created_at,
|
||||||
description: e.data.description || '',
|
description: e.data.description || '',
|
||||||
id: e.uuid,
|
id: e.uuid,
|
||||||
lastUpdatedTime: e.updated_at,
|
lastUpdatedTime: e.updated_at,
|
||||||
name: e.data.title,
|
name: e.data.title,
|
||||||
tags: e.data.tags || [],
|
tags: e.data.tags || [],
|
||||||
key: e.uuid,
|
key: e.uuid,
|
||||||
|
createdBy: e.created_by,
|
||||||
|
lastUpdatedBy: e.updated_by,
|
||||||
refetchDashboardList,
|
refetchDashboardList,
|
||||||
})) || [];
|
})) || [];
|
||||||
|
|
||||||
@ -290,7 +313,9 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
uploadedGrafana={uploadedGrafana}
|
uploadedGrafana={uploadedGrafana}
|
||||||
onModalHandler={(): void => onModalHandler(false)}
|
onModalHandler={(): void => onModalHandler(false)}
|
||||||
/>
|
/>
|
||||||
<ResizeTable
|
<DynamicColumnTable
|
||||||
|
tablesource={TableDataSource.Dashboard}
|
||||||
|
dynamicColumns={dynamicColumns}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
pagination={{
|
pagination={{
|
||||||
pageSize: 9,
|
pageSize: 9,
|
||||||
@ -314,7 +339,9 @@ export interface Data {
|
|||||||
description: string;
|
description: string;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
createdBy: string;
|
createdBy: string;
|
||||||
|
createdAt: string;
|
||||||
lastUpdatedTime: string;
|
lastUpdatedTime: string;
|
||||||
|
lastUpdatedBy: string;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import { CloudDownloadOutlined, FastBackwardOutlined } from '@ant-design/icons';
|
import { FastBackwardOutlined } from '@ant-design/icons';
|
||||||
import { Button, Divider, Dropdown, MenuProps } from 'antd';
|
import { Button, Divider } from 'antd';
|
||||||
import { Excel } from 'antd-table-saveas-excel';
|
|
||||||
import Controls from 'container/Controls';
|
import Controls from 'container/Controls';
|
||||||
|
import Download from 'container/Download/Download';
|
||||||
import { getGlobalTime } from 'container/LogsSearchFilter/utils';
|
import { getGlobalTime } from 'container/LogsSearchFilter/utils';
|
||||||
import { getMinMax } from 'container/TopNav/AutoRefresh/config';
|
import { getMinMax } from 'container/TopNav/AutoRefresh/config';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { Pagination } from 'hooks/queryPagination';
|
import { Pagination } from 'hooks/queryPagination';
|
||||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||||
import { OrderPreferenceItems } from 'pages/Logs/config';
|
import { OrderPreferenceItems } from 'pages/Logs/config';
|
||||||
import { unparse } from 'papaparse';
|
import { memo, useMemo } from 'react';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
@ -23,7 +22,7 @@ import {
|
|||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { ILogsReducer } from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
import { Container, DownloadLogButton } from './styles';
|
import { Container } from './styles';
|
||||||
|
|
||||||
function LogControls(): JSX.Element | null {
|
function LogControls(): JSX.Element | null {
|
||||||
const {
|
const {
|
||||||
@ -97,58 +96,6 @@ function LogControls(): JSX.Element | null {
|
|||||||
[logs],
|
[logs],
|
||||||
);
|
);
|
||||||
|
|
||||||
const downloadExcelFile = useCallback((): void => {
|
|
||||||
const headers = Object.keys(Object.assign({}, ...flattenLogData)).map(
|
|
||||||
(item) => {
|
|
||||||
const updatedTitle = item
|
|
||||||
.split('_')
|
|
||||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
||||||
.join(' ');
|
|
||||||
return {
|
|
||||||
title: updatedTitle,
|
|
||||||
dataIndex: item,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const excel = new Excel();
|
|
||||||
excel
|
|
||||||
.addSheet('log_data')
|
|
||||||
.addColumns(headers)
|
|
||||||
.addDataSource(flattenLogData, {
|
|
||||||
str2Percent: true,
|
|
||||||
})
|
|
||||||
.saveAs('log_data.xlsx');
|
|
||||||
}, [flattenLogData]);
|
|
||||||
|
|
||||||
const downloadCsvFile = useCallback((): void => {
|
|
||||||
const csv = unparse(flattenLogData);
|
|
||||||
const csvBlob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
|
||||||
const csvUrl = URL.createObjectURL(csvBlob);
|
|
||||||
const downloadLink = document.createElement('a');
|
|
||||||
downloadLink.href = csvUrl;
|
|
||||||
downloadLink.download = 'log_data.csv';
|
|
||||||
downloadLink.click();
|
|
||||||
downloadLink.remove();
|
|
||||||
}, [flattenLogData]);
|
|
||||||
|
|
||||||
const menu: MenuProps = useMemo(
|
|
||||||
() => ({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
key: 'download-as-excel',
|
|
||||||
label: 'Excel',
|
|
||||||
onClick: downloadExcelFile,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'download-as-csv',
|
|
||||||
label: 'CSV',
|
|
||||||
onClick: downloadCsvFile,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
[downloadCsvFile, downloadExcelFile],
|
|
||||||
);
|
|
||||||
|
|
||||||
const isLoading = isLogsLoading || isLoadingAggregate;
|
const isLoading = isLogsLoading || isLoadingAggregate;
|
||||||
|
|
||||||
if (liveTail !== 'STOPPED') {
|
if (liveTail !== 'STOPPED') {
|
||||||
@ -157,12 +104,7 @@ function LogControls(): JSX.Element | null {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Dropdown menu={menu} trigger={['click']}>
|
<Download data={flattenLogData} isLoading={isLoading} fileName="log_data" />
|
||||||
<DownloadLogButton loading={isLoading} size="small" type="link">
|
|
||||||
<CloudDownloadOutlined />
|
|
||||||
Download
|
|
||||||
</DownloadLogButton>
|
|
||||||
</Dropdown>
|
|
||||||
<Button
|
<Button
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -92,8 +92,8 @@ function Application(): JSX.Element {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
data: topLevelOperations,
|
data: topLevelOperations,
|
||||||
isLoading: topLevelOperationsLoading,
|
|
||||||
error: topLevelOperationsError,
|
error: topLevelOperationsError,
|
||||||
|
isLoading: topLevelOperationsIsLoading,
|
||||||
isError: topLevelOperationsIsError,
|
isError: topLevelOperationsIsError,
|
||||||
} = useQuery<ServiceDataProps>({
|
} = useQuery<ServiceDataProps>({
|
||||||
queryKey: [servicename, minTime, maxTime, selectedTags],
|
queryKey: [servicename, minTime, maxTime, selectedTags],
|
||||||
@ -199,7 +199,7 @@ function Application(): JSX.Element {
|
|||||||
selectedTimeStamp={selectedTimeStamp}
|
selectedTimeStamp={selectedTimeStamp}
|
||||||
selectedTraceTags={selectedTraceTags}
|
selectedTraceTags={selectedTraceTags}
|
||||||
topLevelOperationsRoute={topLevelOperationsRoute}
|
topLevelOperationsRoute={topLevelOperationsRoute}
|
||||||
topLevelOperationsLoading={topLevelOperationsLoading}
|
topLevelOperationsIsLoading={topLevelOperationsIsLoading}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
@ -220,11 +220,11 @@ function Application(): JSX.Element {
|
|||||||
handleGraphClick={handleGraphClick}
|
handleGraphClick={handleGraphClick}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
topLevelOperationsError={topLevelOperationsError}
|
topLevelOperationsError={topLevelOperationsError}
|
||||||
topLevelOperationsLoading={topLevelOperationsLoading}
|
|
||||||
topLevelOperationsIsError={topLevelOperationsIsError}
|
topLevelOperationsIsError={topLevelOperationsIsError}
|
||||||
name="operations_per_sec"
|
name="operations_per_sec"
|
||||||
widget={operationPerSecWidget}
|
widget={operationPerSecWidget}
|
||||||
opName="Rate"
|
opName="Rate"
|
||||||
|
topLevelOperationsIsLoading={topLevelOperationsIsLoading}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
@ -264,11 +264,11 @@ function Application(): JSX.Element {
|
|||||||
handleGraphClick={handleGraphClick}
|
handleGraphClick={handleGraphClick}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
topLevelOperationsError={topLevelOperationsError}
|
topLevelOperationsError={topLevelOperationsError}
|
||||||
topLevelOperationsLoading={topLevelOperationsLoading}
|
|
||||||
topLevelOperationsIsError={topLevelOperationsIsError}
|
topLevelOperationsIsError={topLevelOperationsIsError}
|
||||||
name="error_percentage_%"
|
name="error_percentage_%"
|
||||||
widget={errorPercentageWidget}
|
widget={errorPercentageWidget}
|
||||||
opName="Error"
|
opName="Error"
|
||||||
|
topLevelOperationsIsLoading={topLevelOperationsIsLoading}
|
||||||
/>
|
/>
|
||||||
</ColErrorContainer>
|
</ColErrorContainer>
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import Spinner from 'components/Spinner';
|
|
||||||
import { FeatureKeys } from 'constants/features';
|
import { FeatureKeys } from 'constants/features';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import Graph from 'container/GridCardLayout/GridCard';
|
import Graph from 'container/GridCardLayout/GridCard';
|
||||||
@ -25,7 +24,7 @@ function ServiceOverview({
|
|||||||
selectedTraceTags,
|
selectedTraceTags,
|
||||||
selectedTimeStamp,
|
selectedTimeStamp,
|
||||||
topLevelOperationsRoute,
|
topLevelOperationsRoute,
|
||||||
topLevelOperationsLoading,
|
topLevelOperationsIsLoading,
|
||||||
}: ServiceOverviewProps): JSX.Element {
|
}: ServiceOverviewProps): JSX.Element {
|
||||||
const { servicename } = useParams<IServiceName>();
|
const { servicename } = useParams<IServiceName>();
|
||||||
|
|
||||||
@ -64,15 +63,8 @@ function ServiceOverview({
|
|||||||
[servicename, isSpanMetricEnable, topLevelOperationsRoute, tagFilterItems],
|
[servicename, isSpanMetricEnable, topLevelOperationsRoute, tagFilterItems],
|
||||||
);
|
);
|
||||||
|
|
||||||
const isQueryEnabled = topLevelOperationsRoute.length > 0;
|
const isQueryEnabled =
|
||||||
|
!topLevelOperationsIsLoading && topLevelOperationsRoute.length > 0;
|
||||||
if (topLevelOperationsLoading) {
|
|
||||||
return (
|
|
||||||
<Card>
|
|
||||||
<Spinner height="40vh" tip="Loading..." />
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -109,7 +101,7 @@ interface ServiceOverviewProps {
|
|||||||
onDragSelect: (start: number, end: number) => void;
|
onDragSelect: (start: number, end: number) => void;
|
||||||
handleGraphClick: (type: string) => ClickHandlerType;
|
handleGraphClick: (type: string) => ClickHandlerType;
|
||||||
topLevelOperationsRoute: string[];
|
topLevelOperationsRoute: string[];
|
||||||
topLevelOperationsLoading: boolean;
|
topLevelOperationsIsLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ServiceOverview;
|
export default ServiceOverview;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Spinner from 'components/Spinner';
|
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import Graph from 'container/GridCardLayout/GridCard';
|
import Graph from 'container/GridCardLayout/GridCard';
|
||||||
import { Card, GraphContainer } from 'container/MetricsApplication/styles';
|
import { Card, GraphContainer } from 'container/MetricsApplication/styles';
|
||||||
@ -13,10 +12,10 @@ function TopLevelOperation({
|
|||||||
opName,
|
opName,
|
||||||
topLevelOperationsIsError,
|
topLevelOperationsIsError,
|
||||||
topLevelOperationsError,
|
topLevelOperationsError,
|
||||||
topLevelOperationsLoading,
|
|
||||||
onDragSelect,
|
onDragSelect,
|
||||||
handleGraphClick,
|
handleGraphClick,
|
||||||
widget,
|
widget,
|
||||||
|
topLevelOperationsIsLoading,
|
||||||
}: TopLevelOperationProps): JSX.Element {
|
}: TopLevelOperationProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
@ -28,17 +27,13 @@ function TopLevelOperation({
|
|||||||
</Typography>
|
</Typography>
|
||||||
) : (
|
) : (
|
||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
{topLevelOperationsLoading && (
|
<Graph
|
||||||
<Spinner size="large" tip="Loading..." height="40vh" />
|
name={name}
|
||||||
)}
|
widget={widget}
|
||||||
{!topLevelOperationsLoading && (
|
onClickHandler={handleGraphClick(opName)}
|
||||||
<Graph
|
onDragSelect={onDragSelect}
|
||||||
name={name}
|
isQueryEnabled={!topLevelOperationsIsLoading}
|
||||||
widget={widget}
|
/>
|
||||||
onClickHandler={handleGraphClick(opName)}
|
|
||||||
onDragSelect={onDragSelect}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</GraphContainer>
|
</GraphContainer>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
@ -50,10 +45,10 @@ interface TopLevelOperationProps {
|
|||||||
opName: string;
|
opName: string;
|
||||||
topLevelOperationsIsError: boolean;
|
topLevelOperationsIsError: boolean;
|
||||||
topLevelOperationsError: unknown;
|
topLevelOperationsError: unknown;
|
||||||
topLevelOperationsLoading: boolean;
|
|
||||||
onDragSelect: (start: number, end: number) => void;
|
onDragSelect: (start: number, end: number) => void;
|
||||||
handleGraphClick: (type: string) => ClickHandlerType;
|
handleGraphClick: (type: string) => ClickHandlerType;
|
||||||
widget: Widgets;
|
widget: Widgets;
|
||||||
|
topLevelOperationsIsLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TopLevelOperation;
|
export default TopLevelOperation;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import getTopOperations from 'api/metrics/getTopOperations';
|
import getTopOperations from 'api/metrics/getTopOperations';
|
||||||
import Spinner from 'components/Spinner';
|
|
||||||
import TopOperationsTable from 'container/MetricsApplication/TopOperationsTable';
|
import TopOperationsTable from 'container/MetricsApplication/TopOperationsTable';
|
||||||
import useResourceAttribute from 'hooks/useResourceAttribute';
|
import useResourceAttribute from 'hooks/useResourceAttribute';
|
||||||
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
|
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
|
||||||
@ -36,12 +35,7 @@ function TopOperation(): JSX.Element {
|
|||||||
|
|
||||||
const topOperationData = data || [];
|
const topOperationData = data || [];
|
||||||
|
|
||||||
return (
|
return <TopOperationsTable data={topOperationData} isLoading={isLoading} />;
|
||||||
<>
|
|
||||||
{isLoading && <Spinner size="large" tip="Loading..." height="40vh" />}
|
|
||||||
{!isLoading && <TopOperationsTable data={topOperationData} />}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TopOperation;
|
export default TopOperation;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import { topOperationMetricsDownloadOptions } from 'container/MetricsApplication/constant';
|
||||||
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
||||||
import { topOperationQueries } from 'container/MetricsApplication/MetricsPageQueries/TopOperationQueries';
|
import { topOperationQueries } from 'container/MetricsApplication/MetricsPageQueries/TopOperationQueries';
|
||||||
import { QueryTable } from 'container/QueryTable';
|
import { QueryTable } from 'container/QueryTable';
|
||||||
@ -109,6 +110,7 @@ function TopOperationMetrics(): JSX.Element {
|
|||||||
queryTableData={queryTableData}
|
queryTableData={queryTableData}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
renderColumnCell={renderColumnCell}
|
renderColumnCell={renderColumnCell}
|
||||||
|
downloadOption={topOperationMetricsDownloadOptions}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
.top-operation {
|
||||||
|
position: relative;
|
||||||
|
.top-operation--download {
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
right: 0px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,32 @@
|
|||||||
import { Tooltip, Typography } from 'antd';
|
import './TopOperationsTable.styles.scss';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
|
||||||
|
import { SearchOutlined } from '@ant-design/icons';
|
||||||
|
import { InputRef, Tooltip, Typography } from 'antd';
|
||||||
|
import { ColumnsType, ColumnType } from 'antd/lib/table';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
|
import Download from 'container/Download/Download';
|
||||||
|
import { filterDropdown } from 'container/ServiceApplication/Filter/FilterDropdown';
|
||||||
import useResourceAttribute from 'hooks/useResourceAttribute';
|
import useResourceAttribute from 'hooks/useResourceAttribute';
|
||||||
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
|
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
|
||||||
|
import { useRef } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import { getErrorRate, navigateToTrace } from './utils';
|
import { IServiceName } from './Tabs/types';
|
||||||
|
import {
|
||||||
|
convertedTracesToDownloadData,
|
||||||
|
getErrorRate,
|
||||||
|
navigateToTrace,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
function TopOperationsTable({ data }: TopOperationsTableProps): JSX.Element {
|
function TopOperationsTable({
|
||||||
|
data,
|
||||||
|
isLoading,
|
||||||
|
}: TopOperationsTableProps): JSX.Element {
|
||||||
|
const searchInput = useRef<InputRef>(null);
|
||||||
|
const { servicename } = useParams<IServiceName>();
|
||||||
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
@ -34,19 +50,35 @@ function TopOperationsTable({ data }: TopOperationsTableProps): JSX.Element {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getSearchOption = (): ColumnType<TopOperationList> => ({
|
||||||
|
filterDropdown,
|
||||||
|
filterIcon: <SearchOutlined />,
|
||||||
|
onFilter: (value, record): boolean =>
|
||||||
|
record.name
|
||||||
|
.toString()
|
||||||
|
.toLowerCase()
|
||||||
|
.includes((value as string).toLowerCase()),
|
||||||
|
onFilterDropdownOpenChange: (visible): void => {
|
||||||
|
if (visible) {
|
||||||
|
setTimeout(() => searchInput.current?.select(), 100);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render: (text: string): JSX.Element => (
|
||||||
|
<Tooltip placement="topLeft" title={text}>
|
||||||
|
<Typography.Link onClick={(): void => handleOnClick(text)}>
|
||||||
|
{text}
|
||||||
|
</Typography.Link>
|
||||||
|
</Tooltip>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
const columns: ColumnsType<TopOperationList> = [
|
const columns: ColumnsType<TopOperationList> = [
|
||||||
{
|
{
|
||||||
title: 'Name',
|
title: 'Name',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
width: 100,
|
width: 100,
|
||||||
render: (text: string): JSX.Element => (
|
...getSearchOption(),
|
||||||
<Tooltip placement="topLeft" title={text}>
|
|
||||||
<Typography.Link onClick={(): void => handleOnClick(text)}>
|
|
||||||
{text}
|
|
||||||
</Typography.Link>
|
|
||||||
</Tooltip>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'P50 (in ms)',
|
title: 'P50 (in ms)',
|
||||||
@ -92,15 +124,27 @@ function TopOperationsTable({ data }: TopOperationsTableProps): JSX.Element {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const downloadableData = convertedTracesToDownloadData(data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResizeTable
|
<div className="top-operation">
|
||||||
columns={columns}
|
<div className="top-operation--download">
|
||||||
showHeader
|
<Download
|
||||||
title={(): string => 'Key Operations'}
|
data={downloadableData}
|
||||||
tableLayout="fixed"
|
isLoading={isLoading}
|
||||||
dataSource={data}
|
fileName={`top-operations-${servicename}`}
|
||||||
rowKey="name"
|
/>
|
||||||
/>
|
</div>
|
||||||
|
<ResizeTable
|
||||||
|
columns={columns}
|
||||||
|
loading={isLoading}
|
||||||
|
showHeader
|
||||||
|
title={(): string => 'Key Operations'}
|
||||||
|
tableLayout="fixed"
|
||||||
|
dataSource={data}
|
||||||
|
rowKey="name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +159,7 @@ export interface TopOperationList {
|
|||||||
|
|
||||||
interface TopOperationsTableProps {
|
interface TopOperationsTableProps {
|
||||||
data: TopOperationList[];
|
data: TopOperationList[];
|
||||||
|
isLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TopOperationsTable;
|
export default TopOperationsTable;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { DownloadOptions } from 'container/Download/Download.types';
|
||||||
|
|
||||||
export const legend = {
|
export const legend = {
|
||||||
address: '{{address}}',
|
address: '{{address}}',
|
||||||
};
|
};
|
||||||
@ -67,3 +69,8 @@ export enum WidgetKeys {
|
|||||||
SignozExternalCallLatencySum = 'signoz_external_call_latency_sum',
|
SignozExternalCallLatencySum = 'signoz_external_call_latency_sum',
|
||||||
Signoz_latency_bucket = 'signoz_latency_bucket',
|
Signoz_latency_bucket = 'signoz_latency_bucket',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const topOperationMetricsDownloadOptions: DownloadOptions = {
|
||||||
|
isDownloadEnabled: true,
|
||||||
|
fileName: 'top-operation',
|
||||||
|
} as const;
|
||||||
|
@ -35,3 +35,19 @@ export const getNearestHighestBucketValue = (
|
|||||||
|
|
||||||
export const convertMilSecToNanoSec = (value: number): number =>
|
export const convertMilSecToNanoSec = (value: number): number =>
|
||||||
value * 1000000000;
|
value * 1000000000;
|
||||||
|
|
||||||
|
export const convertedTracesToDownloadData = (
|
||||||
|
originalData: TopOperationList[],
|
||||||
|
): Record<string, string>[] =>
|
||||||
|
originalData.map((item) => {
|
||||||
|
const newObj: Record<string, string> = {
|
||||||
|
Name: item.name,
|
||||||
|
'P50 (in ms)': (item.p50 / 1000000).toFixed(2),
|
||||||
|
'P95 (in ms)': (item.p95 / 1000000).toFixed(2),
|
||||||
|
'P99 (in ms)': (item.p99 / 1000000).toFixed(2),
|
||||||
|
'Number of calls': item.numCalls.toString(),
|
||||||
|
'Error Rate (%)': getErrorRate(item).toFixed(2),
|
||||||
|
};
|
||||||
|
|
||||||
|
return newObj;
|
||||||
|
});
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
CloseCircleFilled,
|
CloseCircleFilled,
|
||||||
ExclamationCircleFilled,
|
ExclamationCircleFilled,
|
||||||
LoadingOutlined,
|
LoadingOutlined,
|
||||||
|
MinusCircleFilled,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Spin } from 'antd';
|
import { Spin } from 'antd';
|
||||||
|
|
||||||
@ -16,6 +17,8 @@ export function getDeploymentStage(value: string): string {
|
|||||||
return 'Dirty';
|
return 'Dirty';
|
||||||
case 'FAILED':
|
case 'FAILED':
|
||||||
return 'Failed';
|
return 'Failed';
|
||||||
|
case 'UNKNOWN':
|
||||||
|
return 'Unknown';
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@ -33,6 +36,8 @@ export function getDeploymentStageIcon(value: string): JSX.Element {
|
|||||||
return <ExclamationCircleFilled />;
|
return <ExclamationCircleFilled />;
|
||||||
case 'FAILED':
|
case 'FAILED':
|
||||||
return <CloseCircleFilled />;
|
return <CloseCircleFilled />;
|
||||||
|
case 'UNKNOWN':
|
||||||
|
return <MinusCircleFilled />;
|
||||||
default:
|
default:
|
||||||
return <span />;
|
return <span />;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Input } from 'antd';
|
import { Input } from 'antd';
|
||||||
import React, { Dispatch, SetStateAction, useCallback } from 'react';
|
import { debounce } from 'lodash-es';
|
||||||
|
import { BaseSyntheticEvent, Dispatch, SetStateAction } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
function PipelinesSearchSection({
|
function PipelinesSearchSection({
|
||||||
@ -7,18 +8,18 @@ function PipelinesSearchSection({
|
|||||||
}: PipelinesSearchSectionProps): JSX.Element {
|
}: PipelinesSearchSectionProps): JSX.Element {
|
||||||
const { t } = useTranslation(['pipeline']);
|
const { t } = useTranslation(['pipeline']);
|
||||||
|
|
||||||
const onSeachHandler = useCallback(
|
const handleSearch = (searchEv: BaseSyntheticEvent): void => {
|
||||||
(event: React.SetStateAction<string>) => {
|
setPipelineSearchValue(searchEv?.target?.value || '');
|
||||||
setPipelineSearchValue(event);
|
};
|
||||||
},
|
|
||||||
[setPipelineSearchValue],
|
const debouncedHandleSearch = debounce(handleSearch, 300);
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Input.Search
|
<Input
|
||||||
|
type="text"
|
||||||
allowClear
|
allowClear
|
||||||
placeholder={t('search_pipeline_placeholder')}
|
placeholder={t('search_pipeline_placeholder')}
|
||||||
onSearch={onSeachHandler}
|
onChange={debouncedHandleSearch}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,7 @@ function DescriptionTextArea({
|
|||||||
label={<FormLabelStyle>{fieldData.fieldName}</FormLabelStyle>}
|
label={<FormLabelStyle>{fieldData.fieldName}</FormLabelStyle>}
|
||||||
key={fieldData.id}
|
key={fieldData.id}
|
||||||
>
|
>
|
||||||
<Input.TextArea
|
<Input.TextArea rows={3} placeholder={t(fieldData.placeholder)} />
|
||||||
rows={3}
|
|
||||||
name={fieldData.name}
|
|
||||||
placeholder={t(fieldData.placeholder)}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ function NameInput({ fieldData }: NameInputProps): JSX.Element {
|
|||||||
rules={formValidationRules}
|
rules={formValidationRules}
|
||||||
name={fieldData.name}
|
name={fieldData.name}
|
||||||
>
|
>
|
||||||
<Input name={fieldData.name} placeholder={t(fieldData.placeholder)} />
|
<Input placeholder={t(fieldData.placeholder)} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,9 @@ function NameInput({ fieldData }: NameInputProps): JSX.Element {
|
|||||||
name={fieldData.name}
|
name={fieldData.name}
|
||||||
initialValue={fieldData.initialValue}
|
initialValue={fieldData.initialValue}
|
||||||
rules={fieldData.rules ? fieldData.rules : formValidationRules}
|
rules={fieldData.rules ? fieldData.rules : formValidationRules}
|
||||||
|
dependencies={fieldData.dependencies || []}
|
||||||
>
|
>
|
||||||
<Input placeholder={t(fieldData.placeholder)} name={fieldData.name} />
|
<Input placeholder={t(fieldData.placeholder)} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</FormWrapper>
|
</FormWrapper>
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -20,11 +20,7 @@ function ParsingRulesTextArea({
|
|||||||
name={fieldData.name}
|
name={fieldData.name}
|
||||||
label={<ModalFooterTitle>{fieldData.fieldName}</ModalFooterTitle>}
|
label={<ModalFooterTitle>{fieldData.fieldName}</ModalFooterTitle>}
|
||||||
>
|
>
|
||||||
<Input.TextArea
|
<Input.TextArea rows={4} placeholder={t(fieldData.placeholder)} />
|
||||||
rows={4}
|
|
||||||
name={fieldData.name}
|
|
||||||
placeholder={t(fieldData.placeholder)}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</FormWrapper>
|
</FormWrapper>
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import { Rule, RuleRender } from 'antd/es/form';
|
||||||
|
import { NamePath } from 'antd/es/form/interface';
|
||||||
|
|
||||||
type ProcessorType = {
|
type ProcessorType = {
|
||||||
key: string;
|
key: string;
|
||||||
value: string;
|
value: string;
|
||||||
@ -8,11 +11,11 @@ type ProcessorType = {
|
|||||||
|
|
||||||
export const processorTypes: Array<ProcessorType> = [
|
export const processorTypes: Array<ProcessorType> = [
|
||||||
{ key: 'grok_parser', value: 'grok_parser', label: 'Grok' },
|
{ key: 'grok_parser', value: 'grok_parser', label: 'Grok' },
|
||||||
{ key: 'json_parser', value: 'json_parser', label: 'Json Parser' },
|
|
||||||
{ key: 'regex_parser', value: 'regex_parser', label: 'Regex' },
|
{ key: 'regex_parser', value: 'regex_parser', label: 'Regex' },
|
||||||
|
{ key: 'json_parser', value: 'json_parser', label: 'Json Parser' },
|
||||||
|
{ key: 'trace_parser', value: 'trace_parser', label: 'Trace Parser' },
|
||||||
{ key: 'add', value: 'add', label: 'Add' },
|
{ key: 'add', value: 'add', label: 'Add' },
|
||||||
{ key: 'remove', value: 'remove', label: 'Remove' },
|
{ key: 'remove', value: 'remove', label: 'Remove' },
|
||||||
{ key: 'trace_parser', value: 'trace_parser', label: 'Trace Parser' },
|
|
||||||
// { key: 'retain', value: 'retain', label: 'Retain' }, @Chintan - Commented as per Nitya's suggestion
|
// { key: 'retain', value: 'retain', label: 'Retain' }, @Chintan - Commented as per Nitya's suggestion
|
||||||
{ key: 'move', value: 'move', label: 'Move' },
|
{ key: 'move', value: 'move', label: 'Move' },
|
||||||
{ key: 'copy', value: 'copy', label: 'Copy' },
|
{ key: 'copy', value: 'copy', label: 'Copy' },
|
||||||
@ -24,11 +27,30 @@ export type ProcessorFormField = {
|
|||||||
id: number;
|
id: number;
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
name: string;
|
name: string | NamePath;
|
||||||
rules?: Array<{ [key: string]: boolean }>;
|
rules?: Array<Rule>;
|
||||||
initialValue?: string;
|
initialValue?: string;
|
||||||
|
dependencies?: Array<string | NamePath>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const traceParserFieldValidator: RuleRender = (form) => ({
|
||||||
|
validator: (): Promise<void> => {
|
||||||
|
const parseFromValues = [
|
||||||
|
['trace_id', 'parse_from'],
|
||||||
|
['span_id', 'parse_from'],
|
||||||
|
['trace_flags', 'parse_from'],
|
||||||
|
].map((np) => form.getFieldValue(np));
|
||||||
|
|
||||||
|
if (!parseFromValues.some((v) => v?.length > 0)) {
|
||||||
|
return Promise.reject(
|
||||||
|
new Error('At least one of the trace parser fields must be specified.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const commonFields = [
|
const commonFields = [
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
@ -152,21 +174,36 @@ export const processorFields: { [key: string]: Array<ProcessorFormField> } = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
fieldName: 'Trace Id Parce From',
|
fieldName: 'Parse Trace Id From',
|
||||||
placeholder: 'processor_trace_id_placeholder',
|
placeholder: 'processor_trace_id_placeholder',
|
||||||
name: 'traceId',
|
name: ['trace_id', 'parse_from'],
|
||||||
|
rules: [traceParserFieldValidator],
|
||||||
|
dependencies: [
|
||||||
|
['span_id', 'parse_from'],
|
||||||
|
['trace_flags', 'parse_from'],
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
fieldName: 'Span id Parse From',
|
fieldName: 'Parse Span Id From',
|
||||||
placeholder: 'processor_span_id_placeholder',
|
placeholder: 'processor_span_id_placeholder',
|
||||||
name: 'spanId',
|
name: ['span_id', 'parse_from'],
|
||||||
|
rules: [traceParserFieldValidator],
|
||||||
|
dependencies: [
|
||||||
|
['trace_id', 'parse_from'],
|
||||||
|
['trace_flags', 'parse_from'],
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
fieldName: 'Trace flags parse from',
|
fieldName: 'Parse Trace flags From',
|
||||||
placeholder: 'processor_trace_flags_placeholder',
|
placeholder: 'processor_trace_flags_placeholder',
|
||||||
name: 'traceFlags',
|
name: ['trace_flags', 'parse_from'],
|
||||||
|
rules: [traceParserFieldValidator],
|
||||||
|
dependencies: [
|
||||||
|
['trace_id', 'parse_from'],
|
||||||
|
['span_id', 'parse_from'],
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
retain: [
|
retain: [
|
||||||
|
@ -4,6 +4,6 @@ import NameInput from './FormFields/NameInput';
|
|||||||
export const renderProcessorForm = (
|
export const renderProcessorForm = (
|
||||||
processorType: string,
|
processorType: string,
|
||||||
): Array<JSX.Element> =>
|
): Array<JSX.Element> =>
|
||||||
processorFields[processorType]?.map((fieldName: ProcessorFormField) => (
|
processorFields[processorType]?.map((fieldData: ProcessorFormField) => (
|
||||||
<NameInput key={fieldName.id} fieldData={fieldName} />
|
<NameInput key={fieldData.id} fieldData={fieldData} />
|
||||||
));
|
));
|
||||||
|
@ -14,8 +14,8 @@ import {
|
|||||||
|
|
||||||
import { tableComponents } from '../config';
|
import { tableComponents } from '../config';
|
||||||
import { ModalFooterTitle } from '../styles';
|
import { ModalFooterTitle } from '../styles';
|
||||||
import { AlertMessage } from '.';
|
|
||||||
import { processorColumns } from './config';
|
import { processorColumns } from './config';
|
||||||
|
import { AlertMessage } from './PipelineListsView';
|
||||||
import { FooterButton, StyledTable } from './styles';
|
import { FooterButton, StyledTable } from './styles';
|
||||||
import DragAction from './TableComponents/DragAction';
|
import DragAction from './TableComponents/DragAction';
|
||||||
import ProcessorActions from './TableComponents/ProcessorActions';
|
import ProcessorActions from './TableComponents/ProcessorActions';
|
||||||
|
@ -0,0 +1,489 @@
|
|||||||
|
import { ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { Modal, Table } from 'antd';
|
||||||
|
import { ExpandableConfig } from 'antd/es/table/interface';
|
||||||
|
import savePipeline from 'api/pipeline/post';
|
||||||
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
|
import cloneDeep from 'lodash-es/cloneDeep';
|
||||||
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
|
import { DndProvider } from 'react-dnd';
|
||||||
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import {
|
||||||
|
ActionMode,
|
||||||
|
ActionType,
|
||||||
|
Pipeline,
|
||||||
|
PipelineData,
|
||||||
|
ProcessorData,
|
||||||
|
} from 'types/api/pipeline/def';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
|
import { tableComponents } from '../config';
|
||||||
|
import AddNewPipeline from './AddNewPipeline';
|
||||||
|
import AddNewProcessor from './AddNewProcessor';
|
||||||
|
import { pipelineColumns } from './config';
|
||||||
|
import ModeAndConfiguration from './ModeAndConfiguration';
|
||||||
|
import PipelineExpanView from './PipelineExpandView';
|
||||||
|
import SaveConfigButton from './SaveConfigButton';
|
||||||
|
import {
|
||||||
|
AlertContentWrapper,
|
||||||
|
AlertModalTitle,
|
||||||
|
Container,
|
||||||
|
FooterButton,
|
||||||
|
} from './styles';
|
||||||
|
import DragAction from './TableComponents/DragAction';
|
||||||
|
import PipelineActions from './TableComponents/PipelineActions';
|
||||||
|
import PreviewAction from './TableComponents/PipelineActions/components/PreviewAction';
|
||||||
|
import TableExpandIcon from './TableComponents/TableExpandIcon';
|
||||||
|
import {
|
||||||
|
getDataOnSearch,
|
||||||
|
getEditedDataSource,
|
||||||
|
getElementFromArray,
|
||||||
|
getRecordIndex,
|
||||||
|
getTableColumn,
|
||||||
|
getUpdatedRow,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
function PipelineListsView({
|
||||||
|
isActionType,
|
||||||
|
setActionType,
|
||||||
|
isActionMode,
|
||||||
|
setActionMode,
|
||||||
|
pipelineData,
|
||||||
|
refetchPipelineLists,
|
||||||
|
pipelineSearchValue,
|
||||||
|
}: PipelineListsViewProps): JSX.Element {
|
||||||
|
const { t } = useTranslation(['pipeline', 'common']);
|
||||||
|
const [modal, contextHolder] = Modal.useModal();
|
||||||
|
const { notifications } = useNotifications();
|
||||||
|
const [prevPipelineData, setPrevPipelineData] = useState<Array<PipelineData>>(
|
||||||
|
cloneDeep(pipelineData?.pipelines || []),
|
||||||
|
);
|
||||||
|
const [currPipelineData, setCurrPipelineData] = useState<Array<PipelineData>>(
|
||||||
|
cloneDeep(pipelineData?.pipelines || []),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [expandedPipelineId, setExpandedPipelineId] = useState<
|
||||||
|
string | undefined
|
||||||
|
>(undefined);
|
||||||
|
const expandedPipelineData = useCallback(
|
||||||
|
() => currPipelineData?.find((p) => p.id === expandedPipelineId),
|
||||||
|
[currPipelineData, expandedPipelineId],
|
||||||
|
);
|
||||||
|
const setExpandedPipelineData = useCallback(
|
||||||
|
(newData: PipelineData): void => {
|
||||||
|
if (expandedPipelineId) {
|
||||||
|
const pipelineIdx = currPipelineData?.findIndex(
|
||||||
|
(p) => p.id === expandedPipelineId,
|
||||||
|
);
|
||||||
|
if (pipelineIdx >= 0) {
|
||||||
|
const newPipelineData = [...currPipelineData];
|
||||||
|
newPipelineData[pipelineIdx] = newData;
|
||||||
|
setCurrPipelineData(newPipelineData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[expandedPipelineId, currPipelineData],
|
||||||
|
);
|
||||||
|
|
||||||
|
const [
|
||||||
|
selectedProcessorData,
|
||||||
|
setSelectedProcessorData,
|
||||||
|
] = useState<ProcessorData>();
|
||||||
|
|
||||||
|
const [
|
||||||
|
selectedPipelineData,
|
||||||
|
setSelectedPipelineData,
|
||||||
|
] = useState<PipelineData>();
|
||||||
|
|
||||||
|
const [expandedRowKeys, setExpandedRowKeys] = useState<Array<string>>();
|
||||||
|
const [showSaveButton, setShowSaveButton] = useState<string>();
|
||||||
|
const isEditingActionMode = isActionMode === ActionMode.Editing;
|
||||||
|
|
||||||
|
const visibleCurrPipelines = useMemo((): Array<PipelineData> => {
|
||||||
|
if (pipelineSearchValue === '') {
|
||||||
|
return currPipelineData;
|
||||||
|
}
|
||||||
|
return currPipelineData.filter((data) =>
|
||||||
|
getDataOnSearch(data as never, pipelineSearchValue),
|
||||||
|
);
|
||||||
|
}, [currPipelineData, pipelineSearchValue]);
|
||||||
|
|
||||||
|
const handleAlert = useCallback(
|
||||||
|
({ title, descrition, buttontext, onCancel, onOk }: AlertMessage) => {
|
||||||
|
modal.confirm({
|
||||||
|
title: <AlertModalTitle>{title}</AlertModalTitle>,
|
||||||
|
icon: <ExclamationCircleOutlined />,
|
||||||
|
content: <AlertContentWrapper>{descrition}</AlertContentWrapper>,
|
||||||
|
okText: <span>{buttontext}</span>,
|
||||||
|
cancelText: <span>{t('cancel')}</span>,
|
||||||
|
onOk,
|
||||||
|
onCancel,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[modal, t],
|
||||||
|
);
|
||||||
|
|
||||||
|
const pipelineEditAction = useCallback(
|
||||||
|
(record: PipelineData) => (): void => {
|
||||||
|
setActionType(ActionType.EditPipeline);
|
||||||
|
setSelectedPipelineData(record);
|
||||||
|
},
|
||||||
|
[setActionType],
|
||||||
|
);
|
||||||
|
|
||||||
|
const pipelineDeleteHandler = useCallback(
|
||||||
|
(record: PipelineData) => (): void => {
|
||||||
|
setShowSaveButton(ActionMode.Editing);
|
||||||
|
const filteredData = getElementFromArray(currPipelineData, record, 'id');
|
||||||
|
filteredData.forEach((item, index) => {
|
||||||
|
const obj = item;
|
||||||
|
obj.orderId = index + 1;
|
||||||
|
});
|
||||||
|
setCurrPipelineData(filteredData);
|
||||||
|
},
|
||||||
|
[currPipelineData],
|
||||||
|
);
|
||||||
|
|
||||||
|
const pipelineDeleteAction = useCallback(
|
||||||
|
(record: PipelineData) => (): void => {
|
||||||
|
handleAlert({
|
||||||
|
title: `${t('delete_pipeline')} : ${record.name}?`,
|
||||||
|
descrition: t('delete_pipeline_description'),
|
||||||
|
buttontext: t('delete'),
|
||||||
|
onOk: pipelineDeleteHandler(record),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[handleAlert, pipelineDeleteHandler, t],
|
||||||
|
);
|
||||||
|
|
||||||
|
const processorEditAction = useCallback(
|
||||||
|
(record: ProcessorData) => (): void => {
|
||||||
|
setActionType(ActionType.EditProcessor);
|
||||||
|
setSelectedProcessorData(record);
|
||||||
|
},
|
||||||
|
[setActionType],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onSwitchPipelineChange = useCallback(
|
||||||
|
(checked: boolean, record: PipelineData): void => {
|
||||||
|
setShowSaveButton(ActionMode.Editing);
|
||||||
|
const findRecordIndex = getRecordIndex(currPipelineData, record, 'id');
|
||||||
|
const updateSwitch = {
|
||||||
|
...currPipelineData[findRecordIndex],
|
||||||
|
enabled: checked,
|
||||||
|
};
|
||||||
|
const editedPipelineData = getEditedDataSource(
|
||||||
|
currPipelineData,
|
||||||
|
record,
|
||||||
|
'id',
|
||||||
|
updateSwitch,
|
||||||
|
);
|
||||||
|
setCurrPipelineData(editedPipelineData);
|
||||||
|
},
|
||||||
|
[currPipelineData],
|
||||||
|
);
|
||||||
|
|
||||||
|
const columns = useMemo(() => {
|
||||||
|
const fieldColumns = getTableColumn(pipelineColumns);
|
||||||
|
if (isEditingActionMode) {
|
||||||
|
fieldColumns.push(
|
||||||
|
{
|
||||||
|
title: 'Actions',
|
||||||
|
dataIndex: 'smartAction',
|
||||||
|
key: 'smartAction',
|
||||||
|
align: 'center',
|
||||||
|
render: (_value, record): JSX.Element => (
|
||||||
|
<PipelineActions
|
||||||
|
pipeline={record}
|
||||||
|
editAction={pipelineEditAction(record)}
|
||||||
|
deleteAction={pipelineDeleteAction(record)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
dataIndex: 'enabled',
|
||||||
|
key: 'enabled',
|
||||||
|
render: (value, record) => (
|
||||||
|
<DragAction
|
||||||
|
isEnabled={value}
|
||||||
|
onChange={(checked: boolean): void =>
|
||||||
|
onSwitchPipelineChange(checked, record)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
fieldColumns.push({
|
||||||
|
title: 'Actions',
|
||||||
|
dataIndex: 'smartAction',
|
||||||
|
key: 'smartAction',
|
||||||
|
align: 'center',
|
||||||
|
render: (_value, record): JSX.Element => (
|
||||||
|
<PreviewAction pipeline={record} />
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return fieldColumns;
|
||||||
|
}, [
|
||||||
|
isEditingActionMode,
|
||||||
|
pipelineEditAction,
|
||||||
|
pipelineDeleteAction,
|
||||||
|
onSwitchPipelineChange,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const updatePipelineSequence = useCallback(
|
||||||
|
(updatedRow: PipelineData[]) => (): void => {
|
||||||
|
setShowSaveButton(ActionMode.Editing);
|
||||||
|
setCurrPipelineData(updatedRow);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onCancelPipelineSequence = useCallback(
|
||||||
|
(rawData: PipelineData[]) => (): void => {
|
||||||
|
setCurrPipelineData(rawData);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const movePipelineRow = useCallback(
|
||||||
|
(dragIndex: number, hoverIndex: number) => {
|
||||||
|
if (currPipelineData && isEditingActionMode) {
|
||||||
|
const rawData = currPipelineData;
|
||||||
|
|
||||||
|
const updatedRows = getUpdatedRow(
|
||||||
|
currPipelineData,
|
||||||
|
visibleCurrPipelines[dragIndex].orderId - 1,
|
||||||
|
visibleCurrPipelines[hoverIndex].orderId - 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
updatedRows.forEach((item, index) => {
|
||||||
|
const obj = item;
|
||||||
|
obj.orderId = index + 1;
|
||||||
|
});
|
||||||
|
handleAlert({
|
||||||
|
title: t('reorder_pipeline'),
|
||||||
|
descrition: t('reorder_pipeline_description'),
|
||||||
|
buttontext: t('reorder'),
|
||||||
|
onOk: updatePipelineSequence(updatedRows),
|
||||||
|
onCancel: onCancelPipelineSequence(rawData),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
currPipelineData,
|
||||||
|
isEditingActionMode,
|
||||||
|
visibleCurrPipelines,
|
||||||
|
handleAlert,
|
||||||
|
t,
|
||||||
|
updatePipelineSequence,
|
||||||
|
onCancelPipelineSequence,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const expandedRowView = useCallback(
|
||||||
|
(): JSX.Element => (
|
||||||
|
<PipelineExpanView
|
||||||
|
handleAlert={handleAlert}
|
||||||
|
isActionMode={isActionMode}
|
||||||
|
setActionType={setActionType}
|
||||||
|
processorEditAction={processorEditAction}
|
||||||
|
setShowSaveButton={setShowSaveButton}
|
||||||
|
expandedPipelineData={expandedPipelineData()}
|
||||||
|
setExpandedPipelineData={setExpandedPipelineData}
|
||||||
|
prevPipelineData={prevPipelineData}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
[
|
||||||
|
handleAlert,
|
||||||
|
processorEditAction,
|
||||||
|
isActionMode,
|
||||||
|
expandedPipelineData,
|
||||||
|
setActionType,
|
||||||
|
prevPipelineData,
|
||||||
|
setExpandedPipelineData,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onExpand = useCallback(
|
||||||
|
(expanded: boolean, record: PipelineData): void => {
|
||||||
|
const keys = [];
|
||||||
|
if (expanded && record.id) {
|
||||||
|
keys.push(record?.id);
|
||||||
|
}
|
||||||
|
setExpandedRowKeys(keys);
|
||||||
|
setExpandedPipelineId(record.id);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const getExpandIcon = (
|
||||||
|
expanded: boolean,
|
||||||
|
onExpand: (record: PipelineData, e: React.MouseEvent<HTMLElement>) => void,
|
||||||
|
record: PipelineData,
|
||||||
|
): JSX.Element => (
|
||||||
|
<TableExpandIcon expanded={expanded} onExpand={onExpand} record={record} />
|
||||||
|
);
|
||||||
|
|
||||||
|
const addNewPipelineHandler = useCallback((): void => {
|
||||||
|
setActionType(ActionType.AddPipeline);
|
||||||
|
}, [setActionType]);
|
||||||
|
|
||||||
|
const footer = useCallback((): JSX.Element | undefined => {
|
||||||
|
if (isEditingActionMode) {
|
||||||
|
return (
|
||||||
|
<FooterButton
|
||||||
|
type="link"
|
||||||
|
onClick={addNewPipelineHandler}
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
>
|
||||||
|
{t('add_new_pipeline')}
|
||||||
|
</FooterButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}, [isEditingActionMode, addNewPipelineHandler, t]);
|
||||||
|
|
||||||
|
const onSaveConfigurationHandler = useCallback(async () => {
|
||||||
|
const modifiedPipelineData = currPipelineData.map((item: PipelineData) => {
|
||||||
|
const pipelineData = { ...item };
|
||||||
|
delete pipelineData?.id;
|
||||||
|
return pipelineData;
|
||||||
|
});
|
||||||
|
const response = await savePipeline({
|
||||||
|
data: { pipelines: modifiedPipelineData },
|
||||||
|
});
|
||||||
|
if (response.statusCode === 200) {
|
||||||
|
refetchPipelineLists();
|
||||||
|
setActionMode(ActionMode.Viewing);
|
||||||
|
setShowSaveButton(undefined);
|
||||||
|
setCurrPipelineData(response.payload?.pipelines || []);
|
||||||
|
setPrevPipelineData(response.payload?.pipelines || []);
|
||||||
|
} else {
|
||||||
|
modifiedPipelineData.forEach((item: PipelineData) => {
|
||||||
|
const pipelineData = item;
|
||||||
|
pipelineData.id = v4();
|
||||||
|
return pipelineData;
|
||||||
|
});
|
||||||
|
setActionMode(ActionMode.Editing);
|
||||||
|
setShowSaveButton(ActionMode.Editing);
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: response.error || t('something_went_wrong'),
|
||||||
|
});
|
||||||
|
setCurrPipelineData(modifiedPipelineData);
|
||||||
|
setPrevPipelineData(modifiedPipelineData);
|
||||||
|
}
|
||||||
|
}, [currPipelineData, notifications, refetchPipelineLists, setActionMode, t]);
|
||||||
|
|
||||||
|
const onCancelConfigurationHandler = useCallback((): void => {
|
||||||
|
setActionMode(ActionMode.Viewing);
|
||||||
|
setShowSaveButton(undefined);
|
||||||
|
prevPipelineData.forEach((item, index) => {
|
||||||
|
const obj = item;
|
||||||
|
obj.orderId = index + 1;
|
||||||
|
if (obj.config) {
|
||||||
|
obj.config?.forEach((configItem, index) => {
|
||||||
|
const config = configItem;
|
||||||
|
config.orderId = index + 1;
|
||||||
|
});
|
||||||
|
for (let i = 0; i < obj.config.length - 1; i += 1) {
|
||||||
|
obj.config[i].output = obj.config[i + 1].id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setCurrPipelineData(prevPipelineData);
|
||||||
|
setExpandedRowKeys([]);
|
||||||
|
}, [prevPipelineData, setActionMode]);
|
||||||
|
|
||||||
|
const onRowHandler = (
|
||||||
|
_data: PipelineData,
|
||||||
|
index?: number,
|
||||||
|
): React.HTMLAttributes<unknown> =>
|
||||||
|
({
|
||||||
|
index,
|
||||||
|
moveRow: movePipelineRow,
|
||||||
|
} as React.HTMLAttributes<unknown>);
|
||||||
|
|
||||||
|
const expandableConfig: ExpandableConfig<PipelineData> = {
|
||||||
|
expandedRowKeys,
|
||||||
|
onExpand,
|
||||||
|
expandIcon: ({ expanded, onExpand, record }: ExpandRowConfig) =>
|
||||||
|
getExpandIcon(expanded, onExpand, record),
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{contextHolder}
|
||||||
|
<AddNewPipeline
|
||||||
|
isActionType={isActionType}
|
||||||
|
setActionType={setActionType}
|
||||||
|
selectedPipelineData={selectedPipelineData}
|
||||||
|
setShowSaveButton={setShowSaveButton}
|
||||||
|
setCurrPipelineData={setCurrPipelineData}
|
||||||
|
currPipelineData={currPipelineData}
|
||||||
|
/>
|
||||||
|
<AddNewProcessor
|
||||||
|
isActionType={isActionType}
|
||||||
|
setActionType={setActionType}
|
||||||
|
selectedProcessorData={selectedProcessorData}
|
||||||
|
setShowSaveButton={setShowSaveButton}
|
||||||
|
expandedPipelineData={expandedPipelineData()}
|
||||||
|
setExpandedPipelineData={setExpandedPipelineData}
|
||||||
|
/>
|
||||||
|
<Container>
|
||||||
|
<ModeAndConfiguration
|
||||||
|
isActionMode={isActionMode}
|
||||||
|
version={pipelineData?.version}
|
||||||
|
/>
|
||||||
|
<DndProvider backend={HTML5Backend}>
|
||||||
|
<Table
|
||||||
|
rowKey="id"
|
||||||
|
columns={columns}
|
||||||
|
expandedRowRender={expandedRowView}
|
||||||
|
expandable={expandableConfig}
|
||||||
|
components={tableComponents}
|
||||||
|
dataSource={visibleCurrPipelines}
|
||||||
|
onRow={onRowHandler}
|
||||||
|
footer={footer}
|
||||||
|
pagination={false}
|
||||||
|
/>
|
||||||
|
</DndProvider>
|
||||||
|
{showSaveButton && (
|
||||||
|
<SaveConfigButton
|
||||||
|
onSaveConfigurationHandler={onSaveConfigurationHandler}
|
||||||
|
onCancelConfigurationHandler={onCancelConfigurationHandler}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PipelineListsViewProps {
|
||||||
|
isActionType: string;
|
||||||
|
setActionType: (actionType?: ActionType) => void;
|
||||||
|
isActionMode: string;
|
||||||
|
setActionMode: (actionMode: ActionMode) => void;
|
||||||
|
pipelineData: Pipeline;
|
||||||
|
refetchPipelineLists: VoidFunction;
|
||||||
|
pipelineSearchValue: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExpandRowConfig {
|
||||||
|
expanded: boolean;
|
||||||
|
onExpand: (record: PipelineData, e: React.MouseEvent<HTMLElement>) => void;
|
||||||
|
record: PipelineData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AlertMessage {
|
||||||
|
title: string;
|
||||||
|
descrition: string;
|
||||||
|
buttontext: string;
|
||||||
|
onOk: VoidFunction;
|
||||||
|
onCancel?: VoidFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PipelineListsView;
|
@ -3,7 +3,6 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -11,8 +10,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.logs-preview-list-item {
|
.logs-preview-list-item {
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 2rem;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@ -1,489 +1,3 @@
|
|||||||
import { ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
import PipelineListsView from './PipelineListsView';
|
||||||
import { Modal, Table } from 'antd';
|
|
||||||
import { ExpandableConfig } from 'antd/es/table/interface';
|
|
||||||
import savePipeline from 'api/pipeline/post';
|
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
|
||||||
import cloneDeep from 'lodash-es/cloneDeep';
|
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
|
||||||
import { DndProvider } from 'react-dnd';
|
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import {
|
|
||||||
ActionMode,
|
|
||||||
ActionType,
|
|
||||||
Pipeline,
|
|
||||||
PipelineData,
|
|
||||||
ProcessorData,
|
|
||||||
} from 'types/api/pipeline/def';
|
|
||||||
import { v4 } from 'uuid';
|
|
||||||
|
|
||||||
import { tableComponents } from '../config';
|
|
||||||
import AddNewPipeline from './AddNewPipeline';
|
|
||||||
import AddNewProcessor from './AddNewProcessor';
|
|
||||||
import { pipelineColumns } from './config';
|
|
||||||
import ModeAndConfiguration from './ModeAndConfiguration';
|
|
||||||
import PipelineExpanView from './PipelineExpandView';
|
|
||||||
import SaveConfigButton from './SaveConfigButton';
|
|
||||||
import {
|
|
||||||
AlertContentWrapper,
|
|
||||||
AlertModalTitle,
|
|
||||||
Container,
|
|
||||||
FooterButton,
|
|
||||||
} from './styles';
|
|
||||||
import DragAction from './TableComponents/DragAction';
|
|
||||||
import PipelineActions from './TableComponents/PipelineActions';
|
|
||||||
import PreviewAction from './TableComponents/PipelineActions/components/PreviewAction';
|
|
||||||
import TableExpandIcon from './TableComponents/TableExpandIcon';
|
|
||||||
import {
|
|
||||||
getDataOnSearch,
|
|
||||||
getEditedDataSource,
|
|
||||||
getElementFromArray,
|
|
||||||
getRecordIndex,
|
|
||||||
getTableColumn,
|
|
||||||
getUpdatedRow,
|
|
||||||
} from './utils';
|
|
||||||
|
|
||||||
function PipelineListsView({
|
|
||||||
isActionType,
|
|
||||||
setActionType,
|
|
||||||
isActionMode,
|
|
||||||
setActionMode,
|
|
||||||
pipelineData,
|
|
||||||
refetchPipelineLists,
|
|
||||||
pipelineSearchValue,
|
|
||||||
}: PipelineListsViewProps): JSX.Element {
|
|
||||||
const { t } = useTranslation(['pipeline', 'common']);
|
|
||||||
const [modal, contextHolder] = Modal.useModal();
|
|
||||||
const { notifications } = useNotifications();
|
|
||||||
const [prevPipelineData, setPrevPipelineData] = useState<Array<PipelineData>>(
|
|
||||||
cloneDeep(pipelineData?.pipelines),
|
|
||||||
);
|
|
||||||
const [currPipelineData, setCurrPipelineData] = useState<Array<PipelineData>>(
|
|
||||||
cloneDeep(pipelineData?.pipelines),
|
|
||||||
);
|
|
||||||
|
|
||||||
const [expandedPipelineId, setExpandedPipelineId] = useState<
|
|
||||||
string | undefined
|
|
||||||
>(undefined);
|
|
||||||
const expandedPipelineData = useCallback(
|
|
||||||
() => currPipelineData.find((p) => p.id === expandedPipelineId),
|
|
||||||
[currPipelineData, expandedPipelineId],
|
|
||||||
);
|
|
||||||
const setExpandedPipelineData = useCallback(
|
|
||||||
(newData: PipelineData): void => {
|
|
||||||
if (expandedPipelineId) {
|
|
||||||
const pipelineIdx = currPipelineData.findIndex(
|
|
||||||
(p) => p.id === expandedPipelineId,
|
|
||||||
);
|
|
||||||
if (pipelineIdx >= 0) {
|
|
||||||
const newPipelineData = [...currPipelineData];
|
|
||||||
newPipelineData[pipelineIdx] = newData;
|
|
||||||
setCurrPipelineData(newPipelineData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[expandedPipelineId, currPipelineData],
|
|
||||||
);
|
|
||||||
|
|
||||||
const [
|
|
||||||
selectedProcessorData,
|
|
||||||
setSelectedProcessorData,
|
|
||||||
] = useState<ProcessorData>();
|
|
||||||
|
|
||||||
const [
|
|
||||||
selectedPipelineData,
|
|
||||||
setSelectedPipelineData,
|
|
||||||
] = useState<PipelineData>();
|
|
||||||
|
|
||||||
const [expandedRowKeys, setExpandedRowKeys] = useState<Array<string>>();
|
|
||||||
const [showSaveButton, setShowSaveButton] = useState<string>();
|
|
||||||
const isEditingActionMode = isActionMode === ActionMode.Editing;
|
|
||||||
|
|
||||||
const visibleCurrPipelines = useMemo((): Array<PipelineData> => {
|
|
||||||
if (pipelineSearchValue === '') {
|
|
||||||
return currPipelineData;
|
|
||||||
}
|
|
||||||
return currPipelineData.filter((data) =>
|
|
||||||
getDataOnSearch(data as never, pipelineSearchValue),
|
|
||||||
);
|
|
||||||
}, [currPipelineData, pipelineSearchValue]);
|
|
||||||
|
|
||||||
const handleAlert = useCallback(
|
|
||||||
({ title, descrition, buttontext, onCancel, onOk }: AlertMessage) => {
|
|
||||||
modal.confirm({
|
|
||||||
title: <AlertModalTitle>{title}</AlertModalTitle>,
|
|
||||||
icon: <ExclamationCircleOutlined />,
|
|
||||||
content: <AlertContentWrapper>{descrition}</AlertContentWrapper>,
|
|
||||||
okText: <span>{buttontext}</span>,
|
|
||||||
cancelText: <span>{t('cancel')}</span>,
|
|
||||||
onOk,
|
|
||||||
onCancel,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[modal, t],
|
|
||||||
);
|
|
||||||
|
|
||||||
const pipelineEditAction = useCallback(
|
|
||||||
(record: PipelineData) => (): void => {
|
|
||||||
setActionType(ActionType.EditPipeline);
|
|
||||||
setSelectedPipelineData(record);
|
|
||||||
},
|
|
||||||
[setActionType],
|
|
||||||
);
|
|
||||||
|
|
||||||
const pipelineDeleteHandler = useCallback(
|
|
||||||
(record: PipelineData) => (): void => {
|
|
||||||
setShowSaveButton(ActionMode.Editing);
|
|
||||||
const filteredData = getElementFromArray(currPipelineData, record, 'id');
|
|
||||||
filteredData.forEach((item, index) => {
|
|
||||||
const obj = item;
|
|
||||||
obj.orderId = index + 1;
|
|
||||||
});
|
|
||||||
setCurrPipelineData(filteredData);
|
|
||||||
},
|
|
||||||
[currPipelineData],
|
|
||||||
);
|
|
||||||
|
|
||||||
const pipelineDeleteAction = useCallback(
|
|
||||||
(record: PipelineData) => (): void => {
|
|
||||||
handleAlert({
|
|
||||||
title: `${t('delete_pipeline')} : ${record.name}?`,
|
|
||||||
descrition: t('delete_pipeline_description'),
|
|
||||||
buttontext: t('delete'),
|
|
||||||
onOk: pipelineDeleteHandler(record),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[handleAlert, pipelineDeleteHandler, t],
|
|
||||||
);
|
|
||||||
|
|
||||||
const processorEditAction = useCallback(
|
|
||||||
(record: ProcessorData) => (): void => {
|
|
||||||
setActionType(ActionType.EditProcessor);
|
|
||||||
setSelectedProcessorData(record);
|
|
||||||
},
|
|
||||||
[setActionType],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onSwitchPipelineChange = useCallback(
|
|
||||||
(checked: boolean, record: PipelineData): void => {
|
|
||||||
setShowSaveButton(ActionMode.Editing);
|
|
||||||
const findRecordIndex = getRecordIndex(currPipelineData, record, 'id');
|
|
||||||
const updateSwitch = {
|
|
||||||
...currPipelineData[findRecordIndex],
|
|
||||||
enabled: checked,
|
|
||||||
};
|
|
||||||
const editedPipelineData = getEditedDataSource(
|
|
||||||
currPipelineData,
|
|
||||||
record,
|
|
||||||
'id',
|
|
||||||
updateSwitch,
|
|
||||||
);
|
|
||||||
setCurrPipelineData(editedPipelineData);
|
|
||||||
},
|
|
||||||
[currPipelineData],
|
|
||||||
);
|
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
|
||||||
const fieldColumns = getTableColumn(pipelineColumns);
|
|
||||||
if (isEditingActionMode) {
|
|
||||||
fieldColumns.push(
|
|
||||||
{
|
|
||||||
title: 'Actions',
|
|
||||||
dataIndex: 'smartAction',
|
|
||||||
key: 'smartAction',
|
|
||||||
align: 'center',
|
|
||||||
render: (_value, record): JSX.Element => (
|
|
||||||
<PipelineActions
|
|
||||||
pipeline={record}
|
|
||||||
editAction={pipelineEditAction(record)}
|
|
||||||
deleteAction={pipelineDeleteAction(record)}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '',
|
|
||||||
dataIndex: 'enabled',
|
|
||||||
key: 'enabled',
|
|
||||||
render: (value, record) => (
|
|
||||||
<DragAction
|
|
||||||
isEnabled={value}
|
|
||||||
onChange={(checked: boolean): void =>
|
|
||||||
onSwitchPipelineChange(checked, record)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
fieldColumns.push({
|
|
||||||
title: 'Actions',
|
|
||||||
dataIndex: 'smartAction',
|
|
||||||
key: 'smartAction',
|
|
||||||
align: 'center',
|
|
||||||
render: (_value, record): JSX.Element => (
|
|
||||||
<PreviewAction pipeline={record} />
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return fieldColumns;
|
|
||||||
}, [
|
|
||||||
isEditingActionMode,
|
|
||||||
pipelineEditAction,
|
|
||||||
pipelineDeleteAction,
|
|
||||||
onSwitchPipelineChange,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const updatePipelineSequence = useCallback(
|
|
||||||
(updatedRow: PipelineData[]) => (): void => {
|
|
||||||
setShowSaveButton(ActionMode.Editing);
|
|
||||||
setCurrPipelineData(updatedRow);
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onCancelPipelineSequence = useCallback(
|
|
||||||
(rawData: PipelineData[]) => (): void => {
|
|
||||||
setCurrPipelineData(rawData);
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const movePipelineRow = useCallback(
|
|
||||||
(dragIndex: number, hoverIndex: number) => {
|
|
||||||
if (currPipelineData && isEditingActionMode) {
|
|
||||||
const rawData = currPipelineData;
|
|
||||||
|
|
||||||
const updatedRows = getUpdatedRow(
|
|
||||||
currPipelineData,
|
|
||||||
visibleCurrPipelines[dragIndex].orderId - 1,
|
|
||||||
visibleCurrPipelines[hoverIndex].orderId - 1,
|
|
||||||
);
|
|
||||||
|
|
||||||
updatedRows.forEach((item, index) => {
|
|
||||||
const obj = item;
|
|
||||||
obj.orderId = index + 1;
|
|
||||||
});
|
|
||||||
handleAlert({
|
|
||||||
title: t('reorder_pipeline'),
|
|
||||||
descrition: t('reorder_pipeline_description'),
|
|
||||||
buttontext: t('reorder'),
|
|
||||||
onOk: updatePipelineSequence(updatedRows),
|
|
||||||
onCancel: onCancelPipelineSequence(rawData),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[
|
|
||||||
currPipelineData,
|
|
||||||
isEditingActionMode,
|
|
||||||
visibleCurrPipelines,
|
|
||||||
handleAlert,
|
|
||||||
t,
|
|
||||||
updatePipelineSequence,
|
|
||||||
onCancelPipelineSequence,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
const expandedRowView = useCallback(
|
|
||||||
(): JSX.Element => (
|
|
||||||
<PipelineExpanView
|
|
||||||
handleAlert={handleAlert}
|
|
||||||
isActionMode={isActionMode}
|
|
||||||
setActionType={setActionType}
|
|
||||||
processorEditAction={processorEditAction}
|
|
||||||
setShowSaveButton={setShowSaveButton}
|
|
||||||
expandedPipelineData={expandedPipelineData()}
|
|
||||||
setExpandedPipelineData={setExpandedPipelineData}
|
|
||||||
prevPipelineData={prevPipelineData}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[
|
|
||||||
handleAlert,
|
|
||||||
processorEditAction,
|
|
||||||
isActionMode,
|
|
||||||
expandedPipelineData,
|
|
||||||
setActionType,
|
|
||||||
prevPipelineData,
|
|
||||||
setExpandedPipelineData,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onExpand = useCallback(
|
|
||||||
(expanded: boolean, record: PipelineData): void => {
|
|
||||||
const keys = [];
|
|
||||||
if (expanded && record.id) {
|
|
||||||
keys.push(record?.id);
|
|
||||||
}
|
|
||||||
setExpandedRowKeys(keys);
|
|
||||||
setExpandedPipelineId(record.id);
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const getExpandIcon = (
|
|
||||||
expanded: boolean,
|
|
||||||
onExpand: (record: PipelineData, e: React.MouseEvent<HTMLElement>) => void,
|
|
||||||
record: PipelineData,
|
|
||||||
): JSX.Element => (
|
|
||||||
<TableExpandIcon expanded={expanded} onExpand={onExpand} record={record} />
|
|
||||||
);
|
|
||||||
|
|
||||||
const addNewPipelineHandler = useCallback((): void => {
|
|
||||||
setActionType(ActionType.AddPipeline);
|
|
||||||
}, [setActionType]);
|
|
||||||
|
|
||||||
const footer = useCallback((): JSX.Element | undefined => {
|
|
||||||
if (isEditingActionMode) {
|
|
||||||
return (
|
|
||||||
<FooterButton
|
|
||||||
type="link"
|
|
||||||
onClick={addNewPipelineHandler}
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
>
|
|
||||||
{t('add_new_pipeline')}
|
|
||||||
</FooterButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}, [isEditingActionMode, addNewPipelineHandler, t]);
|
|
||||||
|
|
||||||
const onSaveConfigurationHandler = useCallback(async () => {
|
|
||||||
const modifiedPipelineData = currPipelineData.map((item: PipelineData) => {
|
|
||||||
const pipelineData = { ...item };
|
|
||||||
delete pipelineData?.id;
|
|
||||||
return pipelineData;
|
|
||||||
});
|
|
||||||
const response = await savePipeline({
|
|
||||||
data: { pipelines: modifiedPipelineData },
|
|
||||||
});
|
|
||||||
if (response.statusCode === 200) {
|
|
||||||
refetchPipelineLists();
|
|
||||||
setActionMode(ActionMode.Viewing);
|
|
||||||
setShowSaveButton(undefined);
|
|
||||||
setCurrPipelineData(response.payload?.pipelines);
|
|
||||||
setPrevPipelineData(response.payload?.pipelines);
|
|
||||||
} else {
|
|
||||||
modifiedPipelineData.forEach((item: PipelineData) => {
|
|
||||||
const pipelineData = item;
|
|
||||||
pipelineData.id = v4();
|
|
||||||
return pipelineData;
|
|
||||||
});
|
|
||||||
setActionMode(ActionMode.Editing);
|
|
||||||
setShowSaveButton(ActionMode.Editing);
|
|
||||||
notifications.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: response.error || t('something_went_wrong'),
|
|
||||||
});
|
|
||||||
setCurrPipelineData(modifiedPipelineData);
|
|
||||||
setPrevPipelineData(modifiedPipelineData);
|
|
||||||
}
|
|
||||||
}, [currPipelineData, notifications, refetchPipelineLists, setActionMode, t]);
|
|
||||||
|
|
||||||
const onCancelConfigurationHandler = useCallback((): void => {
|
|
||||||
setActionMode(ActionMode.Viewing);
|
|
||||||
setShowSaveButton(undefined);
|
|
||||||
prevPipelineData.forEach((item, index) => {
|
|
||||||
const obj = item;
|
|
||||||
obj.orderId = index + 1;
|
|
||||||
if (obj.config) {
|
|
||||||
obj.config?.forEach((configItem, index) => {
|
|
||||||
const config = configItem;
|
|
||||||
config.orderId = index + 1;
|
|
||||||
});
|
|
||||||
for (let i = 0; i < obj.config.length - 1; i += 1) {
|
|
||||||
obj.config[i].output = obj.config[i + 1].id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setCurrPipelineData(prevPipelineData);
|
|
||||||
setExpandedRowKeys([]);
|
|
||||||
}, [prevPipelineData, setActionMode]);
|
|
||||||
|
|
||||||
const onRowHandler = (
|
|
||||||
_data: PipelineData,
|
|
||||||
index?: number,
|
|
||||||
): React.HTMLAttributes<unknown> =>
|
|
||||||
({
|
|
||||||
index,
|
|
||||||
moveRow: movePipelineRow,
|
|
||||||
} as React.HTMLAttributes<unknown>);
|
|
||||||
|
|
||||||
const expandableConfig: ExpandableConfig<PipelineData> = {
|
|
||||||
expandedRowKeys,
|
|
||||||
onExpand,
|
|
||||||
expandIcon: ({ expanded, onExpand, record }: ExpandRowConfig) =>
|
|
||||||
getExpandIcon(expanded, onExpand, record),
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{contextHolder}
|
|
||||||
<AddNewPipeline
|
|
||||||
isActionType={isActionType}
|
|
||||||
setActionType={setActionType}
|
|
||||||
selectedPipelineData={selectedPipelineData}
|
|
||||||
setShowSaveButton={setShowSaveButton}
|
|
||||||
setCurrPipelineData={setCurrPipelineData}
|
|
||||||
currPipelineData={currPipelineData}
|
|
||||||
/>
|
|
||||||
<AddNewProcessor
|
|
||||||
isActionType={isActionType}
|
|
||||||
setActionType={setActionType}
|
|
||||||
selectedProcessorData={selectedProcessorData}
|
|
||||||
setShowSaveButton={setShowSaveButton}
|
|
||||||
expandedPipelineData={expandedPipelineData()}
|
|
||||||
setExpandedPipelineData={setExpandedPipelineData}
|
|
||||||
/>
|
|
||||||
<Container>
|
|
||||||
<ModeAndConfiguration
|
|
||||||
isActionMode={isActionMode}
|
|
||||||
version={pipelineData?.version}
|
|
||||||
/>
|
|
||||||
<DndProvider backend={HTML5Backend}>
|
|
||||||
<Table
|
|
||||||
rowKey="id"
|
|
||||||
columns={columns}
|
|
||||||
expandedRowRender={expandedRowView}
|
|
||||||
expandable={expandableConfig}
|
|
||||||
components={tableComponents}
|
|
||||||
dataSource={visibleCurrPipelines}
|
|
||||||
onRow={onRowHandler}
|
|
||||||
footer={footer}
|
|
||||||
pagination={false}
|
|
||||||
/>
|
|
||||||
</DndProvider>
|
|
||||||
{showSaveButton && (
|
|
||||||
<SaveConfigButton
|
|
||||||
onSaveConfigurationHandler={onSaveConfigurationHandler}
|
|
||||||
onCancelConfigurationHandler={onCancelConfigurationHandler}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PipelineListsViewProps {
|
|
||||||
isActionType: string;
|
|
||||||
setActionType: (actionType?: ActionType) => void;
|
|
||||||
isActionMode: string;
|
|
||||||
setActionMode: (actionMode: ActionMode) => void;
|
|
||||||
pipelineData: Pipeline;
|
|
||||||
refetchPipelineLists: VoidFunction;
|
|
||||||
pipelineSearchValue: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ExpandRowConfig {
|
|
||||||
expanded: boolean;
|
|
||||||
onExpand: (record: PipelineData, e: React.MouseEvent<HTMLElement>) => void;
|
|
||||||
record: PipelineData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AlertMessage {
|
|
||||||
title: string;
|
|
||||||
descrition: string;
|
|
||||||
buttontext: string;
|
|
||||||
onOk: VoidFunction;
|
|
||||||
onCancel?: VoidFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PipelineListsView;
|
export default PipelineListsView;
|
||||||
|
@ -82,78 +82,42 @@ exports[`PipelinePage container test should render PipelinePageLayout section 1`
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
class="ant-input-group-wrapper ant-input-search css-dev-only-do-not-override-1i536d8"
|
class="ant-input-affix-wrapper css-dev-only-do-not-override-1i536d8"
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
class="ant-input css-dev-only-do-not-override-1i536d8"
|
||||||
|
placeholder="search_pipeline_placeholder"
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
<span
|
<span
|
||||||
class="ant-input-wrapper ant-input-group css-dev-only-do-not-override-1i536d8"
|
class="ant-input-suffix"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="ant-input-affix-wrapper css-dev-only-do-not-override-1i536d8"
|
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
<input
|
|
||||||
class="ant-input css-dev-only-do-not-override-1i536d8"
|
|
||||||
placeholder="search_pipeline_placeholder"
|
|
||||||
type="text"
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
<span
|
<span
|
||||||
class="ant-input-suffix"
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle"
|
||||||
|
role="img"
|
||||||
>
|
>
|
||||||
<span
|
<svg
|
||||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
aria-hidden="true"
|
||||||
role="button"
|
data-icon="close-circle"
|
||||||
tabindex="-1"
|
fill="currentColor"
|
||||||
|
focusable="false"
|
||||||
|
height="1em"
|
||||||
|
viewBox="64 64 896 896"
|
||||||
|
width="1em"
|
||||||
>
|
>
|
||||||
<span
|
<path
|
||||||
aria-label="close-circle"
|
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||||
class="anticon anticon-close-circle"
|
/>
|
||||||
role="img"
|
</svg>
|
||||||
>
|
|
||||||
<svg
|
|
||||||
aria-hidden="true"
|
|
||||||
data-icon="close-circle"
|
|
||||||
fill="currentColor"
|
|
||||||
focusable="false"
|
|
||||||
height="1em"
|
|
||||||
viewBox="64 64 896 896"
|
|
||||||
width="1em"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span
|
|
||||||
class="ant-input-group-addon"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="ant-btn css-dev-only-do-not-override-1i536d8 ant-btn-default ant-btn-icon-only ant-input-search-button"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
aria-label="search"
|
|
||||||
class="anticon anticon-search"
|
|
||||||
role="img"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
aria-hidden="true"
|
|
||||||
data-icon="search"
|
|
||||||
fill="currentColor"
|
|
||||||
focusable="false"
|
|
||||||
height="1em"
|
|
||||||
viewBox="64 64 896 896"
|
|
||||||
width="1em"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
.c0 {
|
.c0 {
|
||||||
|
@ -3,78 +3,42 @@
|
|||||||
exports[`PipelinePage container test should render PipelinesSearchSection section 1`] = `
|
exports[`PipelinePage container test should render PipelinesSearchSection section 1`] = `
|
||||||
<DocumentFragment>
|
<DocumentFragment>
|
||||||
<span
|
<span
|
||||||
class="ant-input-group-wrapper ant-input-search css-dev-only-do-not-override-1i536d8"
|
class="ant-input-affix-wrapper css-dev-only-do-not-override-1i536d8"
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
class="ant-input css-dev-only-do-not-override-1i536d8"
|
||||||
|
placeholder="search_pipeline_placeholder"
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
<span
|
<span
|
||||||
class="ant-input-wrapper ant-input-group css-dev-only-do-not-override-1i536d8"
|
class="ant-input-suffix"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="ant-input-affix-wrapper css-dev-only-do-not-override-1i536d8"
|
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
<input
|
|
||||||
class="ant-input css-dev-only-do-not-override-1i536d8"
|
|
||||||
placeholder="search_pipeline_placeholder"
|
|
||||||
type="text"
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
<span
|
<span
|
||||||
class="ant-input-suffix"
|
aria-label="close-circle"
|
||||||
|
class="anticon anticon-close-circle"
|
||||||
|
role="img"
|
||||||
>
|
>
|
||||||
<span
|
<svg
|
||||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
aria-hidden="true"
|
||||||
role="button"
|
data-icon="close-circle"
|
||||||
tabindex="-1"
|
fill="currentColor"
|
||||||
|
focusable="false"
|
||||||
|
height="1em"
|
||||||
|
viewBox="64 64 896 896"
|
||||||
|
width="1em"
|
||||||
>
|
>
|
||||||
<span
|
<path
|
||||||
aria-label="close-circle"
|
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||||
class="anticon anticon-close-circle"
|
/>
|
||||||
role="img"
|
</svg>
|
||||||
>
|
|
||||||
<svg
|
|
||||||
aria-hidden="true"
|
|
||||||
data-icon="close-circle"
|
|
||||||
fill="currentColor"
|
|
||||||
focusable="false"
|
|
||||||
height="1em"
|
|
||||||
viewBox="64 64 896 896"
|
|
||||||
width="1em"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span
|
|
||||||
class="ant-input-group-addon"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="ant-btn css-dev-only-do-not-override-1i536d8 ant-btn-default ant-btn-icon-only ant-input-search-button"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
aria-label="search"
|
|
||||||
class="anticon anticon-search"
|
|
||||||
role="img"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
aria-hidden="true"
|
|
||||||
data-icon="search"
|
|
||||||
fill="currentColor"
|
|
||||||
focusable="false"
|
|
||||||
height="1em"
|
|
||||||
viewBox="64 64 896 896"
|
|
||||||
width="1em"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</DocumentFragment>
|
</DocumentFragment>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Select, SelectProps, Space } from 'antd';
|
import { Select, SelectProps, Space } from 'antd';
|
||||||
import { getCategorySelectOptionByName } from 'container/NewWidget/RightContainer/alertFomatCategories';
|
import { getCategorySelectOptionByName } from 'container/NewWidget/RightContainer/alertFomatCategories';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
import { popupContainer } from 'utils/selectPopupContainer';
|
||||||
|
|
||||||
import { categoryToSupport } from './config';
|
import { categoryToSupport } from './config';
|
||||||
import { DefaultLabel, selectStyles } from './styles';
|
import { DefaultLabel, selectStyles } from './styles';
|
||||||
@ -31,6 +32,7 @@ function BuilderUnitsFilter({
|
|||||||
<Space>
|
<Space>
|
||||||
<DefaultLabel>Y-axis unit</DefaultLabel>
|
<DefaultLabel>Y-axis unit</DefaultLabel>
|
||||||
<Select
|
<Select
|
||||||
|
getPopupContainer={popupContainer}
|
||||||
style={selectStyles}
|
style={selectStyles}
|
||||||
onChange={onChangeHandler}
|
onChange={onChangeHandler}
|
||||||
value={selectedValue}
|
value={selectedValue}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { TableProps } from 'antd';
|
import { TableProps } from 'antd';
|
||||||
import { ColumnsType } from 'antd/es/table';
|
import { ColumnsType } from 'antd/es/table';
|
||||||
|
import { DownloadOptions } from 'container/Download/Download.types';
|
||||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
@ -14,4 +15,5 @@ export type QueryTableProps = Omit<
|
|||||||
renderActionCell?: (record: RowData) => ReactNode;
|
renderActionCell?: (record: RowData) => ReactNode;
|
||||||
modifyColumns?: (columns: ColumnsType<RowData>) => ColumnsType<RowData>;
|
modifyColumns?: (columns: ColumnsType<RowData>) => ColumnsType<RowData>;
|
||||||
renderColumnCell?: Record<string, (record: RowData) => ReactNode>;
|
renderColumnCell?: Record<string, (record: RowData) => ReactNode>;
|
||||||
|
downloadOption?: DownloadOptions;
|
||||||
};
|
};
|
||||||
|
9
frontend/src/container/QueryTable/QueryTable.styles.scss
Normal file
9
frontend/src/container/QueryTable/QueryTable.styles.scss
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.query-table {
|
||||||
|
position: relative;
|
||||||
|
.query-table--download {
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
right: 0px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,14 @@
|
|||||||
|
import './QueryTable.styles.scss';
|
||||||
|
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
|
import Download from 'container/Download/Download';
|
||||||
|
import { IServiceName } from 'container/MetricsApplication/Tabs/types';
|
||||||
import { createTableColumnsFromQuery } from 'lib/query/createTableColumnsFromQuery';
|
import { createTableColumnsFromQuery } from 'lib/query/createTableColumnsFromQuery';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { QueryTableProps } from './QueryTable.intefaces';
|
import { QueryTableProps } from './QueryTable.intefaces';
|
||||||
|
import { createDownloadableData } from './utils';
|
||||||
|
|
||||||
export function QueryTable({
|
export function QueryTable({
|
||||||
queryTableData,
|
queryTableData,
|
||||||
@ -10,8 +16,12 @@ export function QueryTable({
|
|||||||
renderActionCell,
|
renderActionCell,
|
||||||
modifyColumns,
|
modifyColumns,
|
||||||
renderColumnCell,
|
renderColumnCell,
|
||||||
|
downloadOption,
|
||||||
...props
|
...props
|
||||||
}: QueryTableProps): JSX.Element {
|
}: QueryTableProps): JSX.Element {
|
||||||
|
const { isDownloadEnabled = false, fileName = '' } = downloadOption || {};
|
||||||
|
const { servicename } = useParams<IServiceName>();
|
||||||
|
const { loading } = props;
|
||||||
const { columns, dataSource } = useMemo(
|
const { columns, dataSource } = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createTableColumnsFromQuery({
|
createTableColumnsFromQuery({
|
||||||
@ -23,16 +33,29 @@ export function QueryTable({
|
|||||||
[query, queryTableData, renderActionCell, renderColumnCell],
|
[query, queryTableData, renderActionCell, renderColumnCell],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const downloadableData = createDownloadableData(dataSource);
|
||||||
|
|
||||||
const tableColumns = modifyColumns ? modifyColumns(columns) : columns;
|
const tableColumns = modifyColumns ? modifyColumns(columns) : columns;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResizeTable
|
<div className="query-table">
|
||||||
columns={tableColumns}
|
{isDownloadEnabled && (
|
||||||
tableLayout="fixed"
|
<div className="query-table--download">
|
||||||
dataSource={dataSource}
|
<Download
|
||||||
scroll={{ x: true }}
|
data={downloadableData}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
fileName={`${fileName}-${servicename}`}
|
||||||
{...props}
|
isLoading={loading as boolean}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<ResizeTable
|
||||||
|
columns={tableColumns}
|
||||||
|
tableLayout="fixed"
|
||||||
|
dataSource={dataSource}
|
||||||
|
scroll={{ x: true }}
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
14
frontend/src/container/QueryTable/utils.ts
Normal file
14
frontend/src/container/QueryTable/utils.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||||
|
|
||||||
|
export function createDownloadableData(
|
||||||
|
inputData: RowData[],
|
||||||
|
): Record<string, string>[] {
|
||||||
|
return inputData.map((row) => ({
|
||||||
|
Name: String(row.operation || ''),
|
||||||
|
'P50 (in ns)': String(row.A || ''),
|
||||||
|
'P90 (in ns)': String(row.B || ''),
|
||||||
|
'P99 (in ns)': String(row.C || ''),
|
||||||
|
'Number Of Calls': String(row.F || ''),
|
||||||
|
'Error Rate (%)': String(row.F1 && row.F1 !== 'N/A' ? row.F1 : '0'),
|
||||||
|
}));
|
||||||
|
}
|
@ -7,13 +7,20 @@ import ROUTES from 'constants/routes';
|
|||||||
import useLicense, { LICENSE_PLAN_KEY } from 'hooks/useLicense';
|
import useLicense, { LICENSE_PLAN_KEY } from 'hooks/useLicense';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { LifeBuoy } from 'lucide-react';
|
import { LifeBuoy } from 'lucide-react';
|
||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { sideBarCollapse } from 'store/actions/app';
|
import { sideBarCollapse } from 'store/actions/app';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
import { USER_ROLES } from 'types/roles';
|
||||||
import { checkVersionState, isCloudUser, isEECloudUser } from 'utils/app';
|
import { checkVersionState, isCloudUser, isEECloudUser } from 'utils/app';
|
||||||
|
|
||||||
import { routeConfig, styles } from './config';
|
import { routeConfig, styles } from './config';
|
||||||
@ -33,6 +40,7 @@ import {
|
|||||||
|
|
||||||
function SideNav(): JSX.Element {
|
function SideNav(): JSX.Element {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const [menuItems, setMenuItems] = useState(defaultMenuItems);
|
||||||
const [collapsed, setCollapsed] = useState<boolean>(
|
const [collapsed, setCollapsed] = useState<boolean>(
|
||||||
getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
||||||
);
|
);
|
||||||
@ -44,36 +52,45 @@ function SideNav(): JSX.Element {
|
|||||||
featureResponse,
|
featureResponse,
|
||||||
} = useSelector<AppState, AppReducer>((state) => state.app);
|
} = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
|
||||||
const { data } = useLicense();
|
const { data, isFetching } = useLicense();
|
||||||
|
|
||||||
let secondaryMenuItems: MenuItem[] = [];
|
let secondaryMenuItems: MenuItem[] = [];
|
||||||
|
|
||||||
const isOnBasicPlan =
|
useEffect((): void => {
|
||||||
data?.payload?.licenses?.some(
|
const isOnboardingEnabled =
|
||||||
(license) =>
|
featureResponse.data?.find(
|
||||||
license.isCurrent && license.planKey === LICENSE_PLAN_KEY.BASIC_PLAN,
|
(feature) => feature.name === FeatureKeys.ONBOARDING,
|
||||||
) || data?.payload?.licenses === null;
|
)?.active || false;
|
||||||
|
|
||||||
const menuItems = useMemo(
|
if (!isOnboardingEnabled || !isCloudUser()) {
|
||||||
() =>
|
let items = [...menuItems];
|
||||||
defaultMenuItems.filter((item) => {
|
|
||||||
const isOnboardingEnabled =
|
|
||||||
featureResponse.data?.find(
|
|
||||||
(feature) => feature.name === FeatureKeys.ONBOARDING,
|
|
||||||
)?.active || false;
|
|
||||||
|
|
||||||
if (role !== 'ADMIN' || isOnBasicPlan) {
|
items = items.filter((item) => item.key !== ROUTES.GET_STARTED);
|
||||||
return item.key !== ROUTES.BILLING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isOnboardingEnabled || !isCloudUser()) {
|
setMenuItems(items);
|
||||||
return item.key !== ROUTES.GET_STARTED;
|
}
|
||||||
}
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [featureResponse.data]);
|
||||||
|
|
||||||
return true;
|
// using a separate useEffect as the license fetching call takes few milliseconds
|
||||||
}),
|
useEffect(() => {
|
||||||
[featureResponse.data, isOnBasicPlan, role],
|
if (!isFetching) {
|
||||||
);
|
let items = [...menuItems];
|
||||||
|
|
||||||
|
const isOnBasicPlan =
|
||||||
|
data?.payload?.licenses?.some(
|
||||||
|
(license) =>
|
||||||
|
license.isCurrent && license.planKey === LICENSE_PLAN_KEY.BASIC_PLAN,
|
||||||
|
) || data?.payload?.licenses === null;
|
||||||
|
|
||||||
|
if (role !== USER_ROLES.ADMIN || isOnBasicPlan) {
|
||||||
|
items = items.filter((item) => item.key !== ROUTES.BILLING);
|
||||||
|
}
|
||||||
|
|
||||||
|
setMenuItems(items);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [data?.payload?.licenses, isFetching, role]);
|
||||||
|
|
||||||
const { pathname, search } = useLocation();
|
const { pathname, search } = useLocation();
|
||||||
|
|
||||||
@ -121,6 +138,7 @@ function SideNav(): JSX.Element {
|
|||||||
key: SecondaryMenuItemKey.Support,
|
key: SecondaryMenuItemKey.Support,
|
||||||
label: 'Support',
|
label: 'Support',
|
||||||
icon: <LifeBuoy />,
|
icon: <LifeBuoy />,
|
||||||
|
onClick: onClickMenuHandler,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
@ -172,7 +190,6 @@ function SideNav(): JSX.Element {
|
|||||||
mode="vertical"
|
mode="vertical"
|
||||||
style={styles}
|
style={styles}
|
||||||
items={secondaryMenuItems}
|
items={secondaryMenuItems}
|
||||||
onClick={onClickMenuHandler}
|
|
||||||
/>
|
/>
|
||||||
</Sider>
|
</Sider>
|
||||||
);
|
);
|
||||||
|
@ -45,6 +45,6 @@ export const routeConfig: Record<string, QueryParams[]> = {
|
|||||||
[ROUTES.USAGE_EXPLORER]: [QueryParams.resourceAttributes],
|
[ROUTES.USAGE_EXPLORER]: [QueryParams.resourceAttributes],
|
||||||
[ROUTES.VERSION]: [QueryParams.resourceAttributes],
|
[ROUTES.VERSION]: [QueryParams.resourceAttributes],
|
||||||
[ROUTES.TRACE_EXPLORER]: [QueryParams.resourceAttributes],
|
[ROUTES.TRACE_EXPLORER]: [QueryParams.resourceAttributes],
|
||||||
[ROUTES.PIPELINES]: [QueryParams.resourceAttributes],
|
[ROUTES.LOGS_PIPELINES]: [QueryParams.resourceAttributes],
|
||||||
[ROUTES.WORKSPACE_LOCKED]: [QueryParams.resourceAttributes],
|
[ROUTES.WORKSPACE_LOCKED]: [QueryParams.resourceAttributes],
|
||||||
};
|
};
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
LineChartOutlined,
|
LineChartOutlined,
|
||||||
MenuOutlined,
|
MenuOutlined,
|
||||||
RocketOutlined,
|
RocketOutlined,
|
||||||
|
SearchOutlined,
|
||||||
SettingOutlined,
|
SettingOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@ -35,6 +36,18 @@ const menuItems: SidebarMenu[] = [
|
|||||||
key: ROUTES.LOGS_EXPLORER,
|
key: ROUTES.LOGS_EXPLORER,
|
||||||
label: 'Logs',
|
label: 'Logs',
|
||||||
icon: <AlignLeftOutlined />,
|
icon: <AlignLeftOutlined />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: ROUTES.LOGS_EXPLORER,
|
||||||
|
icon: <SearchOutlined />,
|
||||||
|
label: 'Logs Explorer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: ROUTES.LOGS_PIPELINES,
|
||||||
|
icon: <DeploymentUnitOutlined />,
|
||||||
|
label: 'Logs Pipelines',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: ROUTES.ALL_DASHBOARD,
|
key: ROUTES.ALL_DASHBOARD,
|
||||||
|
@ -23,7 +23,7 @@ const breadcrumbNameMap = {
|
|||||||
[ROUTES.LOGS]: 'Logs',
|
[ROUTES.LOGS]: 'Logs',
|
||||||
[ROUTES.LOGS_EXPLORER]: 'Logs Explorer',
|
[ROUTES.LOGS_EXPLORER]: 'Logs Explorer',
|
||||||
[ROUTES.LIVE_LOGS]: 'Live View',
|
[ROUTES.LIVE_LOGS]: 'Live View',
|
||||||
[ROUTES.PIPELINES]: 'Pipelines',
|
[ROUTES.LOGS_PIPELINES]: 'Logs Pipelines',
|
||||||
[ROUTES.BILLING]: 'Billing',
|
[ROUTES.BILLING]: 'Billing',
|
||||||
[ROUTES.SUPPORT]: 'Support',
|
[ROUTES.SUPPORT]: 'Support',
|
||||||
[ROUTES.WORKSPACE_LOCKED]: 'Workspace Locked',
|
[ROUTES.WORKSPACE_LOCKED]: 'Workspace Locked',
|
||||||
|
@ -80,10 +80,7 @@ export const routesToSkip = [
|
|||||||
ROUTES.ORG_SETTINGS,
|
ROUTES.ORG_SETTINGS,
|
||||||
ROUTES.INGESTION_SETTINGS,
|
ROUTES.INGESTION_SETTINGS,
|
||||||
ROUTES.ERROR_DETAIL,
|
ROUTES.ERROR_DETAIL,
|
||||||
ROUTES.ALERTS_NEW,
|
ROUTES.LOGS_PIPELINES,
|
||||||
ROUTES.EDIT_ALERTS,
|
|
||||||
ROUTES.LIST_ALL_ALERT,
|
|
||||||
ROUTES.PIPELINES,
|
|
||||||
ROUTES.BILLING,
|
ROUTES.BILLING,
|
||||||
ROUTES.SUPPORT,
|
ROUTES.SUPPORT,
|
||||||
ROUTES.WORKSPACE_LOCKED,
|
ROUTES.WORKSPACE_LOCKED,
|
||||||
|
@ -34,8 +34,8 @@ function Version(): JSX.Element {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card style={{ margin: '16px 0' }}>
|
||||||
<Typography.Title ellipsis level={4}>
|
<Typography.Title ellipsis level={4} style={{ marginTop: 0 }}>
|
||||||
{t('version')}
|
{t('version')}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
export const LICENSE_PLAN_KEY = {
|
export const LICENSE_PLAN_KEY = {
|
||||||
ENTERPRISE_PLAN: 'ENTERPRISE_PLAN',
|
ENTERPRISE_PLAN: 'ENTERPRISE_PLAN',
|
||||||
BASIC_PLAN: 'BASIC_PLAN ',
|
BASIC_PLAN: 'BASIC_PLAN',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LICENSE_PLAN_STATUS = {
|
export const LICENSE_PLAN_STATUS = {
|
||||||
|
@ -7,6 +7,40 @@ export const licensesSuccessResponse = {
|
|||||||
workSpaceBlock: false,
|
workSpaceBlock: false,
|
||||||
trialConvertedToSubscription: false,
|
trialConvertedToSubscription: false,
|
||||||
gracePeriodEnd: -1,
|
gracePeriodEnd: -1,
|
||||||
|
licenses: [
|
||||||
|
{
|
||||||
|
key: 'testKeyId1',
|
||||||
|
activationId: 'testActivationId1',
|
||||||
|
ValidationMessage: '',
|
||||||
|
isCurrent: false,
|
||||||
|
planKey: 'ENTERPRISE_PLAN',
|
||||||
|
ValidFrom: '2022-10-13T13:58:51Z',
|
||||||
|
ValidUntil: '2023-10-13T19:57:37Z',
|
||||||
|
status: 'VALID',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'testKeyId2',
|
||||||
|
activationId: 'testActivationId2',
|
||||||
|
ValidationMessage: '',
|
||||||
|
isCurrent: true,
|
||||||
|
planKey: 'ENTERPRISE_PLAN',
|
||||||
|
ValidFrom: '2023-09-12T11:55:43Z',
|
||||||
|
ValidUntil: '2024-09-11T17:34:29Z',
|
||||||
|
status: 'VALID',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const licensesSuccessWorkspaceLockedResponse = {
|
||||||
|
status: 'success',
|
||||||
|
data: {
|
||||||
|
trialStart: 1695992049,
|
||||||
|
trialEnd: 1697806449,
|
||||||
|
onTrial: false,
|
||||||
|
workSpaceBlock: true,
|
||||||
|
trialConvertedToSubscription: false,
|
||||||
|
gracePeriodEnd: -1,
|
||||||
licenses: [
|
licenses: [
|
||||||
{
|
{
|
||||||
key: 'testKeyId1',
|
key: 'testKeyId1',
|
||||||
|
@ -1,46 +1,70 @@
|
|||||||
|
import { licensesSuccessWorkspaceLockedResponse } from 'mocks-server/__mockdata__/licenses';
|
||||||
|
import { server } from 'mocks-server/server';
|
||||||
|
import { rest } from 'msw';
|
||||||
import { act, render, screen } from 'tests/test-utils';
|
import { act, render, screen } from 'tests/test-utils';
|
||||||
|
|
||||||
import WorkspaceLocked from '.';
|
import WorkspaceLocked from '.';
|
||||||
|
|
||||||
describe('WorkspaceLocked', () => {
|
describe('WorkspaceLocked', () => {
|
||||||
|
const apiURL = 'http://localhost/api/v2/licenses';
|
||||||
|
|
||||||
test('Should render the component', async () => {
|
test('Should render the component', async () => {
|
||||||
|
server.use(
|
||||||
|
rest.get(apiURL, (req, res, ctx) =>
|
||||||
|
res(ctx.status(200), ctx.json(licensesSuccessWorkspaceLockedResponse)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
render(<WorkspaceLocked />);
|
render(<WorkspaceLocked />);
|
||||||
});
|
});
|
||||||
const workspaceLocked = screen.getByRole('heading', {
|
|
||||||
|
const workspaceLocked = await screen.findByRole('heading', {
|
||||||
name: /workspace locked/i,
|
name: /workspace locked/i,
|
||||||
});
|
});
|
||||||
expect(workspaceLocked).toBeInTheDocument();
|
expect(workspaceLocked).toBeInTheDocument();
|
||||||
|
|
||||||
const gotQuestionText = screen.getByText(/got question?/i);
|
const gotQuestionText = await screen.findByText(/got question?/i);
|
||||||
expect(gotQuestionText).toBeInTheDocument();
|
expect(gotQuestionText).toBeInTheDocument();
|
||||||
|
|
||||||
const contactUsLink = screen.getByRole('link', {
|
const contactUsLink = await screen.findByRole('link', {
|
||||||
name: /contact us/i,
|
name: /contact us/i,
|
||||||
});
|
});
|
||||||
expect(contactUsLink).toBeInTheDocument();
|
expect(contactUsLink).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Render for Admin', async () => {
|
test('Render for Admin', async () => {
|
||||||
|
server.use(
|
||||||
|
rest.get(apiURL, (req, res, ctx) =>
|
||||||
|
res(ctx.status(200), ctx.json(licensesSuccessWorkspaceLockedResponse)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
render(<WorkspaceLocked />);
|
render(<WorkspaceLocked />);
|
||||||
const contactAdminMessage = screen.queryByText(
|
const contactAdminMessage = await screen.queryByText(
|
||||||
/please contact your administrator for further help/i,
|
/please contact your administrator for further help/i,
|
||||||
);
|
);
|
||||||
expect(contactAdminMessage).not.toBeInTheDocument();
|
expect(contactAdminMessage).not.toBeInTheDocument();
|
||||||
const updateCreditCardBtn = screen.getByRole('button', {
|
const updateCreditCardBtn = await screen.findByRole('button', {
|
||||||
name: /update credit card/i,
|
name: /update credit card/i,
|
||||||
});
|
});
|
||||||
expect(updateCreditCardBtn).toBeInTheDocument();
|
expect(updateCreditCardBtn).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Render for non Admin', async () => {
|
test('Render for non Admin', async () => {
|
||||||
|
server.use(
|
||||||
|
rest.get(apiURL, (req, res, ctx) =>
|
||||||
|
res(ctx.status(200), ctx.json(licensesSuccessWorkspaceLockedResponse)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
render(<WorkspaceLocked />, {}, 'VIEWER');
|
render(<WorkspaceLocked />, {}, 'VIEWER');
|
||||||
const updateCreditCardBtn = screen.queryByRole('button', {
|
const updateCreditCardBtn = await screen.queryByRole('button', {
|
||||||
name: /update credit card/i,
|
name: /update credit card/i,
|
||||||
});
|
});
|
||||||
expect(updateCreditCardBtn).not.toBeInTheDocument();
|
expect(updateCreditCardBtn).not.toBeInTheDocument();
|
||||||
|
|
||||||
const contactAdminMessage = screen.getByText(
|
const contactAdminMessage = await screen.findByText(
|
||||||
/please contact your administrator for further help/i,
|
/please contact your administrator for further help/i,
|
||||||
);
|
);
|
||||||
expect(contactAdminMessage).toBeInTheDocument();
|
expect(contactAdminMessage).toBeInTheDocument();
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
import './WorkspaceLocked.styles.scss';
|
import './WorkspaceLocked.styles.scss';
|
||||||
|
|
||||||
import { CreditCardOutlined, LockOutlined } from '@ant-design/icons';
|
import { CreditCardOutlined, LockOutlined } from '@ant-design/icons';
|
||||||
import { Button, Card, Typography } from 'antd';
|
import { Button, Card, Skeleton, Typography } from 'antd';
|
||||||
import updateCreditCardApi from 'api/billing/checkout';
|
import updateCreditCardApi from 'api/billing/checkout';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
import useLicense from 'hooks/useLicense';
|
import useLicense from 'hooks/useLicense';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
|
import history from 'lib/history';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
@ -22,16 +24,28 @@ export default function WorkspaceBlocked(): JSX.Element {
|
|||||||
|
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
const { isFetching, data: licensesData } = useLicense();
|
const {
|
||||||
|
isFetching: isFetchingLicenseData,
|
||||||
|
isLoading: isLoadingLicenseData,
|
||||||
|
data: licensesData,
|
||||||
|
} = useLicense();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const activeValidLicense =
|
if (!isFetchingLicenseData) {
|
||||||
licensesData?.payload?.licenses?.find(
|
const shouldBlockWorkspace = licensesData?.payload?.workSpaceBlock;
|
||||||
(license) => license.isCurrent === true,
|
|
||||||
) || null;
|
|
||||||
|
|
||||||
setActiveLicense(activeValidLicense);
|
if (!shouldBlockWorkspace) {
|
||||||
}, [isFetching, licensesData]);
|
history.push(ROUTES.APPLICATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeValidLicense =
|
||||||
|
licensesData?.payload?.licenses?.find(
|
||||||
|
(license) => license.isCurrent === true,
|
||||||
|
) || null;
|
||||||
|
|
||||||
|
setActiveLicense(activeValidLicense);
|
||||||
|
}
|
||||||
|
}, [isFetchingLicenseData, licensesData]);
|
||||||
|
|
||||||
const { mutate: updateCreditCard, isLoading } = useMutation(
|
const { mutate: updateCreditCard, isLoading } = useMutation(
|
||||||
updateCreditCardApi,
|
updateCreditCardApi,
|
||||||
@ -62,36 +76,41 @@ export default function WorkspaceBlocked(): JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="workspace-locked-container">
|
<Card className="workspace-locked-container">
|
||||||
<LockOutlined style={{ fontSize: '36px', color: '#08c' }} />
|
{isLoadingLicenseData || !licensesData?.payload?.workSpaceBlock ? (
|
||||||
<Typography.Title level={4}> Workspace Locked </Typography.Title>
|
<Skeleton />
|
||||||
|
) : (
|
||||||
<Typography.Paragraph className="workpace-locked-details">
|
<>
|
||||||
You have been locked out of your workspace because your trial ended without
|
<LockOutlined style={{ fontSize: '36px', color: '#08c' }} />
|
||||||
an upgrade to a paid plan. Your data will continue to be ingested till{' '}
|
<Typography.Title level={4}> Workspace Locked </Typography.Title>
|
||||||
{getFormattedDate(licensesData?.payload?.gracePeriodEnd || Date.now())} , at
|
<Typography.Paragraph className="workpace-locked-details">
|
||||||
which point we will drop all the ingested data and terminate the account.
|
You have been locked out of your workspace because your trial ended
|
||||||
{!isAdmin && 'Please contact your administrator for further help'}
|
without an upgrade to a paid plan. Your data will continue to be ingested
|
||||||
</Typography.Paragraph>
|
till{' '}
|
||||||
|
{getFormattedDate(licensesData?.payload?.gracePeriodEnd || Date.now())} ,
|
||||||
{isAdmin && (
|
at which point we will drop all the ingested data and terminate the
|
||||||
<Button
|
account.
|
||||||
className="update-credit-card-btn"
|
{!isAdmin && 'Please contact your administrator for further help'}
|
||||||
type="primary"
|
</Typography.Paragraph>
|
||||||
icon={<CreditCardOutlined />}
|
{isAdmin && (
|
||||||
size="middle"
|
<Button
|
||||||
loading={isLoading}
|
className="update-credit-card-btn"
|
||||||
onClick={handleUpdateCreditCard}
|
type="primary"
|
||||||
>
|
icon={<CreditCardOutlined />}
|
||||||
Update Credit Card
|
size="middle"
|
||||||
</Button>
|
loading={isLoading}
|
||||||
|
onClick={handleUpdateCreditCard}
|
||||||
|
>
|
||||||
|
Update Credit Card
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<div className="contact-us">
|
||||||
|
Got Questions?
|
||||||
|
<span>
|
||||||
|
<a href="mailto:support@signoz.io"> Contact Us </a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="contact-us">
|
|
||||||
Got Questions?
|
|
||||||
<span>
|
|
||||||
<a href="mailto:support@signoz.io"> Contact Us </a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,10 @@ export interface GettableAlert extends AlertDef {
|
|||||||
alert: string;
|
alert: string;
|
||||||
state: string;
|
state: string;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
createAt: string;
|
||||||
|
createBy: string;
|
||||||
|
updateAt: string;
|
||||||
|
updateBy: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PayloadProps = {
|
export type PayloadProps = {
|
||||||
|
@ -42,6 +42,8 @@ export interface Dashboard {
|
|||||||
uuid: string;
|
uuid: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
|
created_by: string;
|
||||||
|
updated_by: string;
|
||||||
data: DashboardData;
|
data: DashboardData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,17 @@ export interface ProcessorData {
|
|||||||
on_error?: string;
|
on_error?: string;
|
||||||
field?: string;
|
field?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
|
|
||||||
|
// trace parser fields.
|
||||||
|
trace_id?: {
|
||||||
|
parse_from: string;
|
||||||
|
};
|
||||||
|
span_id?: {
|
||||||
|
parse_from: string;
|
||||||
|
};
|
||||||
|
trace_flags?: {
|
||||||
|
parse_from: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PipelineData {
|
export interface PipelineData {
|
||||||
|
@ -3,3 +3,9 @@ export type VIEWER = 'VIEWER';
|
|||||||
export type EDITOR = 'EDITOR';
|
export type EDITOR = 'EDITOR';
|
||||||
|
|
||||||
export type ROLES = ADMIN | VIEWER | EDITOR;
|
export type ROLES = ADMIN | VIEWER | EDITOR;
|
||||||
|
|
||||||
|
export const USER_ROLES = {
|
||||||
|
ADMIN: 'ADMIN',
|
||||||
|
VIEWER: 'VIEWER',
|
||||||
|
EDITOR: 'EDITOR',
|
||||||
|
};
|
||||||
|
@ -75,9 +75,8 @@ export const routePermission: Record<keyof typeof ROUTES, ROLES[]> = {
|
|||||||
LIVE_LOGS: ['ADMIN', 'EDITOR', 'VIEWER'],
|
LIVE_LOGS: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
LIST_LICENSES: ['ADMIN'],
|
LIST_LICENSES: ['ADMIN'],
|
||||||
LOGS_INDEX_FIELDS: ['ADMIN', 'EDITOR', 'VIEWER'],
|
LOGS_INDEX_FIELDS: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
LOGS_PIPELINE: ['ADMIN', 'EDITOR', 'VIEWER'],
|
LOGS_PIPELINES: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
TRACE_EXPLORER: ['ADMIN', 'EDITOR', 'VIEWER'],
|
TRACE_EXPLORER: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
PIPELINES: ['ADMIN', 'EDITOR', 'VIEWER'],
|
|
||||||
GET_STARTED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
GET_STARTED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
WORKSPACE_LOCKED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
WORKSPACE_LOCKED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
BILLING: ['ADMIN', 'EDITOR', 'VIEWER'],
|
BILLING: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
|
@ -218,6 +218,16 @@
|
|||||||
"@jridgewell/trace-mapping" "^0.3.17"
|
"@jridgewell/trace-mapping" "^0.3.17"
|
||||||
jsesc "^2.5.1"
|
jsesc "^2.5.1"
|
||||||
|
|
||||||
|
"@babel/generator@^7.23.0":
|
||||||
|
version "7.23.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420"
|
||||||
|
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.23.0"
|
||||||
|
"@jridgewell/gen-mapping" "^0.3.2"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.17"
|
||||||
|
jsesc "^2.5.1"
|
||||||
|
|
||||||
"@babel/helper-annotate-as-pure@^7.15.4", "@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.18.6":
|
"@babel/helper-annotate-as-pure@^7.15.4", "@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.18.6":
|
||||||
version "7.18.6"
|
version "7.18.6"
|
||||||
resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz"
|
resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz"
|
||||||
@ -354,6 +364,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz"
|
resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz"
|
||||||
integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
|
integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
|
||||||
|
|
||||||
|
"@babel/helper-environment-visitor@^7.22.20":
|
||||||
|
version "7.22.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
|
||||||
|
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
|
||||||
|
|
||||||
"@babel/helper-environment-visitor@^7.22.5":
|
"@babel/helper-environment-visitor@^7.22.5":
|
||||||
version "7.22.5"
|
version "7.22.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98"
|
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98"
|
||||||
@ -382,6 +397,14 @@
|
|||||||
"@babel/template" "^7.22.5"
|
"@babel/template" "^7.22.5"
|
||||||
"@babel/types" "^7.22.5"
|
"@babel/types" "^7.22.5"
|
||||||
|
|
||||||
|
"@babel/helper-function-name@^7.23.0":
|
||||||
|
version "7.23.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
|
||||||
|
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/template" "^7.22.15"
|
||||||
|
"@babel/types" "^7.23.0"
|
||||||
|
|
||||||
"@babel/helper-hoist-variables@^7.18.6":
|
"@babel/helper-hoist-variables@^7.18.6":
|
||||||
version "7.18.6"
|
version "7.18.6"
|
||||||
resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz"
|
resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz"
|
||||||
@ -593,6 +616,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz#601fa28e4cc06786c18912dca138cec73b882044"
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz#601fa28e4cc06786c18912dca138cec73b882044"
|
||||||
integrity sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==
|
integrity sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==
|
||||||
|
|
||||||
|
"@babel/helper-validator-identifier@^7.22.20":
|
||||||
|
version "7.22.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
|
||||||
|
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
|
||||||
|
|
||||||
"@babel/helper-validator-identifier@^7.22.5":
|
"@babel/helper-validator-identifier@^7.22.5":
|
||||||
version "7.22.5"
|
version "7.22.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193"
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193"
|
||||||
@ -692,6 +720,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.15.tgz#d34592bfe288a32e741aa0663dbc4829fcd55160"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.15.tgz#d34592bfe288a32e741aa0663dbc4829fcd55160"
|
||||||
integrity sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA==
|
integrity sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA==
|
||||||
|
|
||||||
|
"@babel/parser@^7.23.0":
|
||||||
|
version "7.23.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
|
||||||
|
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
|
||||||
|
|
||||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
|
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
|
||||||
version "7.18.6"
|
version "7.18.6"
|
||||||
resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz"
|
resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz"
|
||||||
@ -2047,51 +2080,19 @@
|
|||||||
"@babel/parser" "^7.22.5"
|
"@babel/parser" "^7.22.5"
|
||||||
"@babel/types" "^7.22.5"
|
"@babel/types" "^7.22.5"
|
||||||
|
|
||||||
"@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2":
|
"@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.22.11", "@babel/traverse@^7.22.15", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2":
|
||||||
version "7.21.4"
|
version "7.23.2"
|
||||||
resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz"
|
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
|
||||||
integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==
|
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
|
||||||
dependencies:
|
|
||||||
"@babel/code-frame" "^7.21.4"
|
|
||||||
"@babel/generator" "^7.21.4"
|
|
||||||
"@babel/helper-environment-visitor" "^7.18.9"
|
|
||||||
"@babel/helper-function-name" "^7.21.0"
|
|
||||||
"@babel/helper-hoist-variables" "^7.18.6"
|
|
||||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
|
||||||
"@babel/parser" "^7.21.4"
|
|
||||||
"@babel/types" "^7.21.4"
|
|
||||||
debug "^4.1.0"
|
|
||||||
globals "^11.1.0"
|
|
||||||
|
|
||||||
"@babel/traverse@^7.22.11":
|
|
||||||
version "7.22.11"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.11.tgz#71ebb3af7a05ff97280b83f05f8865ac94b2027c"
|
|
||||||
integrity sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==
|
|
||||||
dependencies:
|
|
||||||
"@babel/code-frame" "^7.22.10"
|
|
||||||
"@babel/generator" "^7.22.10"
|
|
||||||
"@babel/helper-environment-visitor" "^7.22.5"
|
|
||||||
"@babel/helper-function-name" "^7.22.5"
|
|
||||||
"@babel/helper-hoist-variables" "^7.22.5"
|
|
||||||
"@babel/helper-split-export-declaration" "^7.22.6"
|
|
||||||
"@babel/parser" "^7.22.11"
|
|
||||||
"@babel/types" "^7.22.11"
|
|
||||||
debug "^4.1.0"
|
|
||||||
globals "^11.1.0"
|
|
||||||
|
|
||||||
"@babel/traverse@^7.22.15":
|
|
||||||
version "7.22.15"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.15.tgz#75be4d2d6e216e880e93017f4e2389aeb77ef2d9"
|
|
||||||
integrity sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ==
|
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.22.13"
|
"@babel/code-frame" "^7.22.13"
|
||||||
"@babel/generator" "^7.22.15"
|
"@babel/generator" "^7.23.0"
|
||||||
"@babel/helper-environment-visitor" "^7.22.5"
|
"@babel/helper-environment-visitor" "^7.22.20"
|
||||||
"@babel/helper-function-name" "^7.22.5"
|
"@babel/helper-function-name" "^7.23.0"
|
||||||
"@babel/helper-hoist-variables" "^7.22.5"
|
"@babel/helper-hoist-variables" "^7.22.5"
|
||||||
"@babel/helper-split-export-declaration" "^7.22.6"
|
"@babel/helper-split-export-declaration" "^7.22.6"
|
||||||
"@babel/parser" "^7.22.15"
|
"@babel/parser" "^7.23.0"
|
||||||
"@babel/types" "^7.22.15"
|
"@babel/types" "^7.23.0"
|
||||||
debug "^4.1.0"
|
debug "^4.1.0"
|
||||||
globals "^11.1.0"
|
globals "^11.1.0"
|
||||||
|
|
||||||
@ -2122,6 +2123,15 @@
|
|||||||
"@babel/helper-validator-identifier" "^7.22.15"
|
"@babel/helper-validator-identifier" "^7.22.15"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
|
"@babel/types@^7.23.0":
|
||||||
|
version "7.23.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
|
||||||
|
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-string-parser" "^7.22.5"
|
||||||
|
"@babel/helper-validator-identifier" "^7.22.20"
|
||||||
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@bcoe/v8-coverage@^0.2.3":
|
"@bcoe/v8-coverage@^0.2.3":
|
||||||
version "0.2.3"
|
version "0.2.3"
|
||||||
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz"
|
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz"
|
||||||
|
14
go.mod
14
go.mod
@ -5,6 +5,7 @@ go 1.21
|
|||||||
require (
|
require (
|
||||||
github.com/ClickHouse/clickhouse-go/v2 v2.14.0
|
github.com/ClickHouse/clickhouse-go/v2 v2.14.0
|
||||||
github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb
|
github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb
|
||||||
|
github.com/SigNoz/signoz-otel-collector v0.79.12
|
||||||
github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974
|
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/SigNoz/zap_otlp/zap_otlp_sync v0.0.0-20230822164844-1b861a431974
|
||||||
github.com/antonmedv/expr v1.12.5
|
github.com/antonmedv/expr v1.12.5
|
||||||
@ -64,7 +65,7 @@ require (
|
|||||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
|
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
|
||||||
golang.org/x/net v0.17.0
|
golang.org/x/net v0.17.0
|
||||||
golang.org/x/oauth2 v0.10.0
|
golang.org/x/oauth2 v0.10.0
|
||||||
google.golang.org/grpc v1.57.0
|
google.golang.org/grpc v1.57.1
|
||||||
google.golang.org/protobuf v1.31.0
|
google.golang.org/protobuf v1.31.0
|
||||||
gopkg.in/segmentio/analytics-go.v3 v3.1.0
|
gopkg.in/segmentio/analytics-go.v3 v3.1.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
@ -93,7 +94,7 @@ require (
|
|||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
|
github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect
|
||||||
github.com/go-faster/city v1.0.1 // indirect
|
github.com/go-faster/city v1.0.1 // indirect
|
||||||
github.com/go-faster/errors v0.6.1 // indirect
|
github.com/go-faster/errors v0.6.1 // indirect
|
||||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||||
@ -147,17 +148,18 @@ require (
|
|||||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||||
github.com/segmentio/asm v1.2.0 // indirect
|
github.com/segmentio/asm v1.2.0 // indirect
|
||||||
github.com/segmentio/backo-go v1.0.1 // indirect
|
github.com/segmentio/backo-go v1.0.1 // indirect
|
||||||
github.com/shirou/gopsutil/v3 v3.23.4 // indirect
|
github.com/shirou/gopsutil/v3 v3.23.5 // indirect
|
||||||
github.com/shoenig/go-m1cpu v0.1.5 // indirect
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
github.com/shopspring/decimal v1.3.1 // indirect
|
github.com/shopspring/decimal v1.3.1 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
github.com/sirupsen/logrus v1.9.2 // indirect
|
||||||
github.com/smarty/assertions v1.15.0 // indirect
|
github.com/smarty/assertions v1.15.0 // indirect
|
||||||
github.com/spf13/cobra v1.7.0 // indirect
|
github.com/spf13/cobra v1.7.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||||
|
github.com/vjeantet/grok v1.0.1 // indirect
|
||||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
|
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.opentelemetry.io/collector/featuregate v1.0.0-rcv0012 // indirect
|
go.opentelemetry.io/collector/featuregate v1.0.0-rcv0012 // indirect
|
||||||
go.opentelemetry.io/collector/semconv v0.81.0 // indirect
|
go.opentelemetry.io/collector/semconv v0.81.0 // indirect
|
||||||
|
72
go.sum
72
go.sum
@ -99,6 +99,8 @@ github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb h1:bneLSKPf9YUSFm
|
|||||||
github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb/go.mod h1:JznGDNg9x1cujDKa22RaQOimOvvEfy3nxzDGd8XDgmA=
|
github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb/go.mod h1:JznGDNg9x1cujDKa22RaQOimOvvEfy3nxzDGd8XDgmA=
|
||||||
github.com/SigNoz/prometheus v1.9.78 h1:bB3yuDrRzi/Mv00kWayR9DZbyjTuGfendSqISyDcXiY=
|
github.com/SigNoz/prometheus v1.9.78 h1:bB3yuDrRzi/Mv00kWayR9DZbyjTuGfendSqISyDcXiY=
|
||||||
github.com/SigNoz/prometheus v1.9.78/go.mod h1:MffmFu2qFILQrOHehx3D0XjYtaZMVfI+Ppeiv98x4Ww=
|
github.com/SigNoz/prometheus v1.9.78/go.mod h1:MffmFu2qFILQrOHehx3D0XjYtaZMVfI+Ppeiv98x4Ww=
|
||||||
|
github.com/SigNoz/signoz-otel-collector v0.79.12 h1:0yDMhcN7Taa8WrFv8YrHRaDvRxHqLfp5c6w1TSEWk+I=
|
||||||
|
github.com/SigNoz/signoz-otel-collector v0.79.12/go.mod h1:MXjHt3atjTAF2Wrqu0W7Xx+oJ1yb8UfpsNu+A8Ssjtg=
|
||||||
github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc=
|
github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc=
|
||||||
github.com/SigNoz/zap_otlp v0.1.0/go.mod h1:lcHvbDbRgvDnPxo9lDlaL1JK2PyOyouP/C3ynnYIvyo=
|
github.com/SigNoz/zap_otlp v0.1.0/go.mod h1:lcHvbDbRgvDnPxo9lDlaL1JK2PyOyouP/C3ynnYIvyo=
|
||||||
github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 h1:PKVgdf83Yw+lZJbFtNGBgqXiXNf3+kOXW2qZ7Ms7OaY=
|
github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 h1:PKVgdf83Yw+lZJbFtNGBgqXiXNf3+kOXW2qZ7Ms7OaY=
|
||||||
@ -196,8 +198,8 @@ github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m3
|
|||||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
|
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
|
||||||
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 h1:IPrmumsT9t5BS7XcPhgsCTlkWbYg80SEXUzDpReaU6Y=
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11/go.mod h1:a6bNUGTbQBsY6VRHTr4h/rkOXjl244DyRD0tx3fgq4Q=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
@ -205,8 +207,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
|||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
|
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
|
||||||
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
|
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
|
||||||
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
|
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ=
|
||||||
github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
@ -229,8 +231,9 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga
|
|||||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
|
github.com/form3tech-oss/jwt-go v3.2.5+incompatible h1:/l4kBbb4/vGSsdtB5nUe8L7B9mImVMaBPw9L/0TBHU8=
|
||||||
|
github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||||
@ -270,8 +273,8 @@ 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-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 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
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=
|
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||||
github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||||
@ -280,8 +283,9 @@ github.com/go-redis/redismock/v8 v8.11.5 h1:RJFIiua58hrBrSpXhnGX3on79AU3S271H4Zh
|
|||||||
github.com/go-redis/redismock/v8 v8.11.5/go.mod h1:UaAU9dEe1C+eGr+FHV5prCWIt0hafyPWbGMEWE0UWdA=
|
github.com/go-redis/redismock/v8 v8.11.5/go.mod h1:UaAU9dEe1C+eGr+FHV5prCWIt0hafyPWbGMEWE0UWdA=
|
||||||
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
||||||
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
@ -337,8 +341,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
|||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
|
github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0=
|
||||||
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
|
github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
@ -358,8 +362,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
|||||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
@ -443,8 +447,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
|
|||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||||
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
|
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||||
@ -472,6 +476,7 @@ github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoI
|
|||||||
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
|
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
|
||||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
|
github.com/hetznercloud/hcloud-go v1.41.0 h1:KJGFRRc68QiVu4PrEP5BmCQVveCP2CM26UGQUKGpIUs=
|
||||||
github.com/hetznercloud/hcloud-go/v2 v2.0.0 h1:Sg1DJ+MAKvbYAqaBaq9tPbwXBS2ckPIaMtVdUjKu+4g=
|
github.com/hetznercloud/hcloud-go/v2 v2.0.0 h1:Sg1DJ+MAKvbYAqaBaq9tPbwXBS2ckPIaMtVdUjKu+4g=
|
||||||
github.com/hetznercloud/hcloud-go/v2 v2.0.0/go.mod h1:4iUG2NG8b61IAwNx6UsMWQ6IfIf/i1RsG0BbsKAyR5Q=
|
github.com/hetznercloud/hcloud-go/v2 v2.0.0/go.mod h1:4iUG2NG8b61IAwNx6UsMWQ6IfIf/i1RsG0BbsKAyR5Q=
|
||||||
github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs=
|
github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs=
|
||||||
@ -479,8 +484,8 @@ github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEF
|
|||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
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 h1:493wE/BkZxJf7x79UCE0cYGPZoqQcPiEBALvt7uVGY0=
|
||||||
@ -537,8 +542,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
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/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/linode/linodego v1.19.0 h1:n4WJrcr9+30e9JGZ6DI0nZbm5SdAj1kSwvvt/998YUw=
|
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/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 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||||
@ -750,12 +756,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/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 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI=
|
||||||
github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE=
|
github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE=
|
||||||
github.com/shirou/gopsutil/v3 v3.23.4 h1:hZwmDxZs7Ewt75DV81r4pFMqbq+di2cbt9FsQBqLD2o=
|
github.com/shirou/gopsutil/v3 v3.23.5 h1:5SgDCeQ0KW0S4N0znjeM/eFHXXOKyv2dVNgRq/c9P6Y=
|
||||||
github.com/shirou/gopsutil/v3 v3.23.4/go.mod h1:ZcGxyfzAMRevhUR2+cfhXDH6gQdFYE/t8j1nsU4mPI8=
|
github.com/shirou/gopsutil/v3 v3.23.5/go.mod h1:Ng3Maa27Q2KARVJ0SPZF5NdrQSC3XHKP8IIWrHgMeLY=
|
||||||
github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ=
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||||
github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
|
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||||
github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
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/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=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
@ -763,8 +769,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
|||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
|
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
||||||
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
@ -777,8 +783,9 @@ github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sS
|
|||||||
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
||||||
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
|
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
|
||||||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
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/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
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/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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
@ -800,6 +807,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
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/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/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
|
||||||
@ -811,6 +819,8 @@ github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ
|
|||||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
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 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=
|
||||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
||||||
|
github.com/vjeantet/grok v1.0.1 h1:2rhIR7J4gThTgcZ1m2JY4TrJZNgjn985U28kT2wQrJ4=
|
||||||
|
github.com/vjeantet/grok v1.0.1/go.mod h1:ax1aAchzC6/QMXMcyzHQGZWaW1l195+uMYIkCWPCNIo=
|
||||||
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
|
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
|
||||||
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
|
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
@ -825,8 +835,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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
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/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.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
|
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/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=
|
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
|
||||||
@ -1136,7 +1146,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
@ -1395,8 +1405,8 @@ google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11
|
|||||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||||
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||||
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||||
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
|
google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg=
|
||||||
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
|
google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# use a minimal alpine image
|
# use a minimal alpine image
|
||||||
FROM alpine:3.17
|
FROM alpine:3.18.3
|
||||||
|
|
||||||
# Add Maintainer Info
|
# Add Maintainer Info
|
||||||
LABEL maintainer="signoz"
|
LABEL maintainer="signoz"
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"go.signoz.io/signoz/pkg/query-service/agentConf/sqlite"
|
"go.signoz.io/signoz/pkg/query-service/agentConf/sqlite"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
"go.signoz.io/signoz/pkg/query-service/model"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -61,6 +62,13 @@ func (r *Repo) GetConfigHistory(
|
|||||||
return nil, model.InternalError(err)
|
return nil, model.InternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
incompleteStatuses := []DeployStatus{DeployInitiated, Deploying}
|
||||||
|
for idx := 1; idx < len(c); idx++ {
|
||||||
|
if slices.Contains(incompleteStatuses, c[idx].DeployStatus) {
|
||||||
|
c[idx].DeployStatus = DeployStatusUnknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,11 +18,12 @@ const (
|
|||||||
type DeployStatus string
|
type DeployStatus string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PendingDeploy DeployStatus = "DIRTY"
|
PendingDeploy DeployStatus = "DIRTY"
|
||||||
Deploying DeployStatus = "DEPLOYING"
|
Deploying DeployStatus = "DEPLOYING"
|
||||||
Deployed DeployStatus = "DEPLOYED"
|
Deployed DeployStatus = "DEPLOYED"
|
||||||
DeployInitiated DeployStatus = "IN_PROGRESS"
|
DeployInitiated DeployStatus = "IN_PROGRESS"
|
||||||
DeployFailed DeployStatus = "FAILED"
|
DeployFailed DeployStatus = "FAILED"
|
||||||
|
DeployStatusUnknown DeployStatus = "UNKNOWN"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigVersion struct {
|
type ConfigVersion struct {
|
||||||
|
@ -3493,10 +3493,16 @@ func (r *ClickHouseReader) UpdateLogField(ctx context.Context, field *model.Upda
|
|||||||
return &model.ApiError{Err: err, Typ: model.ErrorInternal}
|
return &model.ApiError{Err: err, Typ: model.ErrorInternal}
|
||||||
}
|
}
|
||||||
|
|
||||||
query = fmt.Sprintf("ALTER TABLE %s.%s ON CLUSTER %s ADD COLUMN IF NOT EXISTS %s %s MATERIALIZED -1",
|
defaultValueDistributed := "-1"
|
||||||
|
if strings.ToLower(field.DataType) == "bool" {
|
||||||
|
defaultValueDistributed = "false"
|
||||||
|
field.IndexType = "set(2)"
|
||||||
|
}
|
||||||
|
query = fmt.Sprintf("ALTER TABLE %s.%s ON CLUSTER %s ADD COLUMN IF NOT EXISTS %s %s MATERIALIZED %s",
|
||||||
r.logsDB, r.logsTable,
|
r.logsDB, r.logsTable,
|
||||||
r.cluster,
|
r.cluster,
|
||||||
colname, field.DataType,
|
colname, field.DataType,
|
||||||
|
defaultValueDistributed,
|
||||||
)
|
)
|
||||||
err = r.db.Exec(ctx, query)
|
err = r.db.Exec(ctx, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -140,7 +140,7 @@ func (ic *LogParsingPipelineController) PreviewLogsPipelines(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *PipelinesPreviewRequest,
|
request *PipelinesPreviewRequest,
|
||||||
) (*PipelinesPreviewResponse, *model.ApiError) {
|
) (*PipelinesPreviewResponse, *model.ApiError) {
|
||||||
result, err := SimulatePipelinesProcessing(
|
result, _, err := SimulatePipelinesProcessing(
|
||||||
ctx, request.Pipelines, request.Logs,
|
ctx, request.Pipelines, request.Logs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ type PipelineOperator struct {
|
|||||||
ID string `json:"id,omitempty" yaml:"id,omitempty"`
|
ID string `json:"id,omitempty" yaml:"id,omitempty"`
|
||||||
Output string `json:"output,omitempty" yaml:"output,omitempty"`
|
Output string `json:"output,omitempty" yaml:"output,omitempty"`
|
||||||
OnError string `json:"on_error,omitempty" yaml:"on_error,omitempty"`
|
OnError string `json:"on_error,omitempty" yaml:"on_error,omitempty"`
|
||||||
|
If string `json:"if,omitempty" yaml:"if,omitempty"`
|
||||||
|
|
||||||
// don't need the following in the final config
|
// don't need the following in the final config
|
||||||
OrderId int `json:"orderId" yaml:"-"`
|
OrderId int `json:"orderId" yaml:"-"`
|
||||||
@ -48,20 +49,20 @@ type PipelineOperator struct {
|
|||||||
Name string `json:"name,omitempty" yaml:"-"`
|
Name string `json:"name,omitempty" yaml:"-"`
|
||||||
|
|
||||||
// optional keys depending on the type
|
// optional keys depending on the type
|
||||||
ParseTo string `json:"parse_to,omitempty" yaml:"parse_to,omitempty"`
|
ParseTo string `json:"parse_to,omitempty" yaml:"parse_to,omitempty"`
|
||||||
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
|
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
|
||||||
Regex string `json:"regex,omitempty" yaml:"regex,omitempty"`
|
Regex string `json:"regex,omitempty" yaml:"regex,omitempty"`
|
||||||
ParseFrom string `json:"parse_from,omitempty" yaml:"parse_from,omitempty"`
|
ParseFrom string `json:"parse_from,omitempty" yaml:"parse_from,omitempty"`
|
||||||
Timestamp *TimestampParser `json:"timestamp,omitempty" yaml:"timestamp,omitempty"`
|
Timestamp *TimestampParser `json:"timestamp,omitempty" yaml:"timestamp,omitempty"`
|
||||||
TraceParser *TraceParser `json:"trace_parser,omitempty" yaml:"trace_parser,omitempty"`
|
*TraceParser `yaml:",inline,omitempty"`
|
||||||
Field string `json:"field,omitempty" yaml:"field,omitempty"`
|
Field string `json:"field,omitempty" yaml:"field,omitempty"`
|
||||||
Value string `json:"value,omitempty" yaml:"value,omitempty"`
|
Value string `json:"value,omitempty" yaml:"value,omitempty"`
|
||||||
From string `json:"from,omitempty" yaml:"from,omitempty"`
|
From string `json:"from,omitempty" yaml:"from,omitempty"`
|
||||||
To string `json:"to,omitempty" yaml:"to,omitempty"`
|
To string `json:"to,omitempty" yaml:"to,omitempty"`
|
||||||
Expr string `json:"expr,omitempty" yaml:"expr,omitempty"`
|
Expr string `json:"expr,omitempty" yaml:"expr,omitempty"`
|
||||||
Routes *[]Route `json:"routes,omitempty" yaml:"routes,omitempty"`
|
Routes *[]Route `json:"routes,omitempty" yaml:"routes,omitempty"`
|
||||||
Fields []string `json:"fields,omitempty" yaml:"fields,omitempty"`
|
Fields []string `json:"fields,omitempty" yaml:"fields,omitempty"`
|
||||||
Default string `json:"default,omitempty" yaml:"default,omitempty"`
|
Default string `json:"default,omitempty" yaml:"default,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimestampParser struct {
|
type TimestampParser struct {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package logparsingpipeline
|
package logparsingpipeline
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||||
"go.signoz.io/signoz/pkg/query-service/queryBuilderToExpr"
|
"go.signoz.io/signoz/pkg/query-service/queryBuilderToExpr"
|
||||||
@ -72,6 +75,39 @@ func getOperators(ops []PipelineOperator) []PipelineOperator {
|
|||||||
if len(filteredOp) > 0 {
|
if len(filteredOp) > 0 {
|
||||||
filteredOp[len(filteredOp)-1].Output = operator.ID
|
filteredOp[len(filteredOp)-1].Output = operator.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if operator.Type == "regex_parser" {
|
||||||
|
parseFromParts := strings.Split(operator.ParseFrom, ".")
|
||||||
|
parseFromPath := strings.Join(parseFromParts, "?.")
|
||||||
|
operator.If = fmt.Sprintf(
|
||||||
|
`%s != nil && %s matches "%s"`,
|
||||||
|
parseFromPath,
|
||||||
|
parseFromPath,
|
||||||
|
strings.ReplaceAll(
|
||||||
|
strings.ReplaceAll(operator.Regex, `\`, `\\`),
|
||||||
|
`"`, `\"`,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
} else if operator.Type == "json_parser" {
|
||||||
|
parseFromParts := strings.Split(operator.ParseFrom, ".")
|
||||||
|
parseFromPath := strings.Join(parseFromParts, "?.")
|
||||||
|
operator.If = fmt.Sprintf(`%s != nil && %s matches "^\\s*{.*}\\s*$"`, parseFromPath, parseFromPath)
|
||||||
|
|
||||||
|
} else if operator.Type == "move" || operator.Type == "copy" {
|
||||||
|
fromParts := strings.Split(operator.From, ".")
|
||||||
|
fromPath := strings.Join(fromParts, "?.")
|
||||||
|
operator.If = fmt.Sprintf(`%s != nil`, fromPath)
|
||||||
|
|
||||||
|
} else if operator.Type == "remove" {
|
||||||
|
fieldParts := strings.Split(operator.Field, ".")
|
||||||
|
fieldPath := strings.Join(fieldParts, "?.")
|
||||||
|
operator.If = fmt.Sprintf(`%s != nil`, fieldPath)
|
||||||
|
|
||||||
|
} else if operator.Type == "trace_parser" {
|
||||||
|
cleanTraceParser(&operator)
|
||||||
|
}
|
||||||
|
|
||||||
filteredOp = append(filteredOp, operator)
|
filteredOp = append(filteredOp, operator)
|
||||||
} else if i == len(ops)-1 && len(filteredOp) != 0 {
|
} else if i == len(ops)-1 && len(filteredOp) != 0 {
|
||||||
filteredOp[len(filteredOp)-1].Output = ""
|
filteredOp[len(filteredOp)-1].Output = ""
|
||||||
@ -79,3 +115,15 @@ func getOperators(ops []PipelineOperator) []PipelineOperator {
|
|||||||
}
|
}
|
||||||
return filteredOp
|
return filteredOp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanTraceParser(operator *PipelineOperator) {
|
||||||
|
if operator.TraceId != nil && len(operator.TraceId.ParseFrom) < 1 {
|
||||||
|
operator.TraceId = nil
|
||||||
|
}
|
||||||
|
if operator.SpanId != nil && len(operator.SpanId.ParseFrom) < 1 {
|
||||||
|
operator.SpanId = nil
|
||||||
|
}
|
||||||
|
if operator.TraceFlags != nil && len(operator.TraceFlags.ParseFrom) < 1 {
|
||||||
|
operator.TraceFlags = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
package logparsingpipeline
|
package logparsingpipeline
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/entry"
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/model"
|
||||||
|
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var prepareProcessorTestData = []struct {
|
var prepareProcessorTestData = []struct {
|
||||||
@ -195,3 +203,161 @@ func TestPreparePipelineProcessor(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNoCollectorErrorsFromProcessorsForMismatchedLogs(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
testPipelineFilter := &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: []v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "method",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "GET",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
makeTestPipeline := func(config []PipelineOperator) Pipeline {
|
||||||
|
return Pipeline{
|
||||||
|
OrderId: 1,
|
||||||
|
Name: "pipeline1",
|
||||||
|
Alias: "pipeline1",
|
||||||
|
Enabled: true,
|
||||||
|
Filter: testPipelineFilter,
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makeTestLog := func(
|
||||||
|
body string,
|
||||||
|
attributes map[string]string,
|
||||||
|
) model.SignozLog {
|
||||||
|
attributes["method"] = "GET"
|
||||||
|
|
||||||
|
testTraceId, err := utils.RandomHex(16)
|
||||||
|
require.Nil(err)
|
||||||
|
|
||||||
|
testSpanId, err := utils.RandomHex(8)
|
||||||
|
require.Nil(err)
|
||||||
|
|
||||||
|
return model.SignozLog{
|
||||||
|
Timestamp: uint64(time.Now().UnixNano()),
|
||||||
|
Body: body,
|
||||||
|
Attributes_string: attributes,
|
||||||
|
Resources_string: attributes,
|
||||||
|
SeverityText: entry.Info.String(),
|
||||||
|
SeverityNumber: uint8(entry.Info),
|
||||||
|
SpanID: testSpanId,
|
||||||
|
TraceID: testTraceId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
Name string
|
||||||
|
Operator PipelineOperator
|
||||||
|
NonMatchingLog model.SignozLog
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"regex processor should ignore log with missing field",
|
||||||
|
PipelineOperator{
|
||||||
|
ID: "regex",
|
||||||
|
Type: "regex_parser",
|
||||||
|
Enabled: true,
|
||||||
|
Name: "regex parser",
|
||||||
|
ParseFrom: "attributes.test_regex_target",
|
||||||
|
ParseTo: "attributes",
|
||||||
|
Regex: `^\s*(?P<json_data>{.*})\s*$`,
|
||||||
|
},
|
||||||
|
makeTestLog("mismatching log", map[string]string{}),
|
||||||
|
}, {
|
||||||
|
"regex processor should ignore non-matching log",
|
||||||
|
PipelineOperator{
|
||||||
|
ID: "regex",
|
||||||
|
Type: "regex_parser",
|
||||||
|
Enabled: true,
|
||||||
|
Name: "regex parser",
|
||||||
|
ParseFrom: "body",
|
||||||
|
ParseTo: "attributes",
|
||||||
|
Regex: `^\s*(?P<body_json>{.*})\s*$`,
|
||||||
|
},
|
||||||
|
makeTestLog("mismatching log", map[string]string{}),
|
||||||
|
}, {
|
||||||
|
"json parser should ignore logs with missing field.",
|
||||||
|
PipelineOperator{
|
||||||
|
ID: "json",
|
||||||
|
Type: "json_parser",
|
||||||
|
Enabled: true,
|
||||||
|
Name: "json parser",
|
||||||
|
ParseFrom: "attributes.test_json",
|
||||||
|
ParseTo: "attributes",
|
||||||
|
},
|
||||||
|
makeTestLog("mismatching log", map[string]string{}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json parser should ignore log with non JSON target field value",
|
||||||
|
PipelineOperator{
|
||||||
|
ID: "json",
|
||||||
|
Type: "json_parser",
|
||||||
|
Enabled: true,
|
||||||
|
Name: "json parser",
|
||||||
|
ParseFrom: "attributes.test_json",
|
||||||
|
ParseTo: "attributes",
|
||||||
|
},
|
||||||
|
makeTestLog("mismatching log", map[string]string{
|
||||||
|
"test_json": "bad json",
|
||||||
|
}),
|
||||||
|
}, {
|
||||||
|
"move parser should ignore non matching logs",
|
||||||
|
PipelineOperator{
|
||||||
|
ID: "move",
|
||||||
|
Type: "move",
|
||||||
|
Enabled: true,
|
||||||
|
Name: "move",
|
||||||
|
From: "attributes.test1",
|
||||||
|
To: "attributes.test2",
|
||||||
|
},
|
||||||
|
makeTestLog("mismatching log", map[string]string{}),
|
||||||
|
}, {
|
||||||
|
"copy parser should ignore non matching logs",
|
||||||
|
PipelineOperator{
|
||||||
|
ID: "copy",
|
||||||
|
Type: "copy",
|
||||||
|
Enabled: true,
|
||||||
|
Name: "copy",
|
||||||
|
From: "attributes.test1",
|
||||||
|
To: "attributes.test2",
|
||||||
|
},
|
||||||
|
makeTestLog("mismatching log", map[string]string{}),
|
||||||
|
}, {
|
||||||
|
"remove parser should ignore non matching logs",
|
||||||
|
PipelineOperator{
|
||||||
|
ID: "remove",
|
||||||
|
Type: "remove",
|
||||||
|
Enabled: true,
|
||||||
|
Name: "remove",
|
||||||
|
Field: "attributes.test",
|
||||||
|
},
|
||||||
|
makeTestLog("mismatching log", map[string]string{}),
|
||||||
|
},
|
||||||
|
// TODO(Raj): see if there is an error scenario for grok parser.
|
||||||
|
// TODO(Raj): see if there is an error scenario for trace parser.
|
||||||
|
// TODO(Raj): see if there is an error scenario for Add operator.
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
testPipelines := []Pipeline{makeTestPipeline([]PipelineOperator{testCase.Operator})}
|
||||||
|
|
||||||
|
result, collectorErrorLogs, err := SimulatePipelinesProcessing(
|
||||||
|
context.Background(),
|
||||||
|
testPipelines,
|
||||||
|
[]model.SignozLog{testCase.NonMatchingLog},
|
||||||
|
)
|
||||||
|
require.Nil(err)
|
||||||
|
require.Equal(0, len(collectorErrorLogs), strings.Join(collectorErrorLogs, "\n"))
|
||||||
|
require.Equal(1, len(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -137,20 +137,35 @@ func isValidOperator(op PipelineOperator) error {
|
|||||||
if op.Field == "" {
|
if op.Field == "" {
|
||||||
return fmt.Errorf(fmt.Sprintf("field of %s remove operator cannot be empty", op.ID))
|
return fmt.Errorf(fmt.Sprintf("field of %s remove operator cannot be empty", op.ID))
|
||||||
}
|
}
|
||||||
case "traceParser":
|
case "trace_parser":
|
||||||
if op.TraceParser == nil {
|
if op.TraceParser == nil {
|
||||||
return fmt.Errorf(fmt.Sprintf("field of %s remove operator cannot be empty", op.ID))
|
return fmt.Errorf(fmt.Sprintf("field of %s remove operator cannot be empty", op.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if op.TraceParser.SpanId.ParseFrom == "" && op.TraceParser.TraceId.ParseFrom == "" && op.TraceParser.TraceFlags.ParseFrom == "" {
|
hasTraceIdParseFrom := (op.TraceParser.TraceId != nil && op.TraceParser.TraceId.ParseFrom != "")
|
||||||
return fmt.Errorf(fmt.Sprintf("one of trace_id,span_id,parse_from of %s traceParser operator must be present", op.ID))
|
hasSpanIdParseFrom := (op.TraceParser.SpanId != nil && op.TraceParser.SpanId.ParseFrom != "")
|
||||||
|
hasTraceFlagsParseFrom := (op.TraceParser.TraceFlags != nil && op.TraceParser.TraceFlags.ParseFrom != "")
|
||||||
|
|
||||||
|
if !(hasTraceIdParseFrom || hasSpanIdParseFrom || hasTraceFlagsParseFrom) {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("one of trace_id, span_id, trace_flags of %s trace_parser operator must be present", op.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hasTraceIdParseFrom && !isValidOtelValue(op.TraceParser.TraceId.ParseFrom) {
|
||||||
|
return fmt.Errorf("trace id can't be parsed from %s", op.TraceParser.TraceId.ParseFrom)
|
||||||
|
}
|
||||||
|
if hasSpanIdParseFrom && !isValidOtelValue(op.TraceParser.SpanId.ParseFrom) {
|
||||||
|
return fmt.Errorf("span id can't be parsed from %s", op.TraceParser.SpanId.ParseFrom)
|
||||||
|
}
|
||||||
|
if hasTraceFlagsParseFrom && !isValidOtelValue(op.TraceParser.TraceFlags.ParseFrom) {
|
||||||
|
return fmt.Errorf("trace flags can't be parsed from %s", op.TraceParser.TraceFlags.ParseFrom)
|
||||||
|
}
|
||||||
|
|
||||||
case "retain":
|
case "retain":
|
||||||
if len(op.Fields) == 0 {
|
if len(op.Fields) == 0 {
|
||||||
return fmt.Errorf(fmt.Sprintf("fields of %s retain operator cannot be empty", op.ID))
|
return fmt.Errorf(fmt.Sprintf("fields of %s retain operator cannot be empty", op.ID))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf(fmt.Sprintf("operator type %s not supported for %s, use one of (grok_parser, regex_parser, copy, move, add, remove, traceParser, retain)", op.Type, op.ID))
|
return fmt.Errorf(fmt.Sprintf("operator type %s not supported for %s, use one of (grok_parser, regex_parser, copy, move, add, remove, trace_parser, retain)", op.Type, op.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isValidOtelValue(op.ParseFrom) ||
|
if !isValidOtelValue(op.ParseFrom) ||
|
||||||
|
@ -250,6 +250,31 @@ var operatorTest = []struct {
|
|||||||
ParseTo: "attributes",
|
ParseTo: "attributes",
|
||||||
},
|
},
|
||||||
IsValid: false,
|
IsValid: false,
|
||||||
|
}, {
|
||||||
|
Name: "Trace Parser - invalid - no trace_parser spec",
|
||||||
|
Operator: PipelineOperator{
|
||||||
|
ID: "trace",
|
||||||
|
Type: "trace_parser",
|
||||||
|
},
|
||||||
|
IsValid: false,
|
||||||
|
}, {
|
||||||
|
Name: "Trace Parser - invalid - no ParseFrom specified",
|
||||||
|
Operator: PipelineOperator{
|
||||||
|
ID: "trace",
|
||||||
|
Type: "trace_parser",
|
||||||
|
TraceParser: &TraceParser{},
|
||||||
|
},
|
||||||
|
IsValid: false,
|
||||||
|
}, {
|
||||||
|
Name: "Trace Parser - invalid - bad parsefrom attribute",
|
||||||
|
Operator: PipelineOperator{
|
||||||
|
ID: "trace",
|
||||||
|
Type: "trace_parser",
|
||||||
|
TraceParser: &TraceParser{
|
||||||
|
TraceId: &ParseFrom{ParseFrom: "trace_id"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
IsValid: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
_ "github.com/SigNoz/signoz-otel-collector/pkg/parser/grok"
|
||||||
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor"
|
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"go.opentelemetry.io/collector/pdata/pcommon"
|
"go.opentelemetry.io/collector/pdata/pcommon"
|
||||||
@ -22,11 +22,11 @@ func SimulatePipelinesProcessing(
|
|||||||
pipelines []Pipeline,
|
pipelines []Pipeline,
|
||||||
logs []model.SignozLog,
|
logs []model.SignozLog,
|
||||||
) (
|
) (
|
||||||
[]model.SignozLog, *model.ApiError,
|
output []model.SignozLog, collectorErrorLogs []string, apiErr *model.ApiError,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if len(pipelines) < 1 {
|
if len(pipelines) < 1 {
|
||||||
return logs, nil
|
return logs, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collector simulation does not guarantee that logs will come
|
// Collector simulation does not guarantee that logs will come
|
||||||
@ -45,7 +45,7 @@ func SimulatePipelinesProcessing(
|
|||||||
// Simulate processing of logs through an otel collector
|
// Simulate processing of logs through an otel collector
|
||||||
processorConfigs, err := collectorProcessorsForPipelines(pipelines)
|
processorConfigs, err := collectorProcessorsForPipelines(pipelines)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, model.BadRequest(errors.Wrap(
|
return nil, nil, model.BadRequest(errors.Wrap(
|
||||||
err, "could not prepare otel processors for pipelines",
|
err, "could not prepare otel processors for pipelines",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@ func SimulatePipelinesProcessing(
|
|||||||
logstransformprocessor.NewFactory(),
|
logstransformprocessor.NewFactory(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, model.InternalError(errors.Wrap(
|
return nil, nil, model.InternalError(errors.Wrap(
|
||||||
err, "could not construct processor factory map",
|
err, "could not construct processor factory map",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -74,10 +74,9 @@ func SimulatePipelinesProcessing(
|
|||||||
simulatorInputPLogs,
|
simulatorInputPLogs,
|
||||||
timeout,
|
timeout,
|
||||||
)
|
)
|
||||||
collectorErrsText := strings.Join(collectorErrs, "\n")
|
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
return nil, model.WrapApiError(apiErr, fmt.Sprintf(
|
return nil, collectorErrs, model.WrapApiError(apiErr, fmt.Sprintf(
|
||||||
"could not simulate log pipelines processing.\nCollector errors: %s\n", collectorErrsText,
|
"could not simulate log pipelines processing.\nCollector errors",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +92,7 @@ func SimulatePipelinesProcessing(
|
|||||||
delete(sigLog.Attributes_int64, inputOrderAttribute)
|
delete(sigLog.Attributes_int64, inputOrderAttribute)
|
||||||
}
|
}
|
||||||
|
|
||||||
return outputSignozLogs, nil
|
return outputSignozLogs, collectorErrs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectorProcessorsForPipelines(pipelines []Pipeline) (
|
func collectorProcessorsForPipelines(pipelines []Pipeline) (
|
||||||
|
@ -2,6 +2,8 @@ package logparsingpipeline
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -10,6 +12,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
"go.signoz.io/signoz/pkg/query-service/model"
|
||||||
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPipelinePreview(t *testing.T) {
|
func TestPipelinePreview(t *testing.T) {
|
||||||
@ -101,7 +104,7 @@ func TestPipelinePreview(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
result, err := SimulatePipelinesProcessing(
|
result, collectorErrorLogs, err := SimulatePipelinesProcessing(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
testPipelines,
|
testPipelines,
|
||||||
[]model.SignozLog{
|
[]model.SignozLog{
|
||||||
@ -111,6 +114,7 @@ func TestPipelinePreview(t *testing.T) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
|
require.Equal(0, len(collectorErrorLogs))
|
||||||
require.Equal(2, len(result))
|
require.Equal(2, len(result))
|
||||||
|
|
||||||
// matching log should have been modified as expected.
|
// matching log should have been modified as expected.
|
||||||
@ -141,6 +145,175 @@ func TestPipelinePreview(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGrokParsingPreview(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
testPipelines := []Pipeline{
|
||||||
|
{
|
||||||
|
OrderId: 1,
|
||||||
|
Name: "pipeline1",
|
||||||
|
Alias: "pipeline1",
|
||||||
|
Enabled: true,
|
||||||
|
Filter: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: []v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "method",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "GET",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Config: []PipelineOperator{
|
||||||
|
{
|
||||||
|
OrderId: 1,
|
||||||
|
ID: "grok",
|
||||||
|
Type: "grok_parser",
|
||||||
|
Enabled: true,
|
||||||
|
Name: "test grok parser",
|
||||||
|
OnError: "send",
|
||||||
|
ParseFrom: "body",
|
||||||
|
ParseTo: "attributes",
|
||||||
|
Pattern: "%{TIMESTAMP_ISO8601:timestamp}%{SPACE}%{WORD:log_level}%{SPACE}%{NOTSPACE:location}%{SPACE}%{GREEDYDATA:message}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testLog := makeTestLogEntry(
|
||||||
|
"2023-10-26T04:38:00.602Z INFO route/server.go:71 HTTP request received",
|
||||||
|
map[string]string{
|
||||||
|
"method": "GET",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
result, collectorErrorLogs, err := SimulatePipelinesProcessing(
|
||||||
|
context.Background(),
|
||||||
|
testPipelines,
|
||||||
|
[]model.SignozLog{
|
||||||
|
testLog,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
require.Nil(err)
|
||||||
|
require.Equal(0, len(collectorErrorLogs))
|
||||||
|
require.Equal(1, len(result))
|
||||||
|
processed := result[0]
|
||||||
|
|
||||||
|
require.Equal("INFO", processed.Attributes_string["log_level"])
|
||||||
|
require.Equal("route/server.go:71", processed.Attributes_string["location"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTraceParsingPreview(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
testPipelines := []Pipeline{
|
||||||
|
{
|
||||||
|
OrderId: 1,
|
||||||
|
Name: "pipeline1",
|
||||||
|
Alias: "pipeline1",
|
||||||
|
Enabled: true,
|
||||||
|
Filter: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: []v3.FilterItem{
|
||||||
|
{
|
||||||
|
Key: v3.AttributeKey{
|
||||||
|
Key: "method",
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeTag,
|
||||||
|
},
|
||||||
|
Operator: "=",
|
||||||
|
Value: "GET",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Config: []PipelineOperator{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start with JSON serialized trace parser to validate deserialization too
|
||||||
|
var traceParserOp PipelineOperator
|
||||||
|
err := json.Unmarshal([]byte(`
|
||||||
|
{
|
||||||
|
"orderId": 1,
|
||||||
|
"enabled": true,
|
||||||
|
"type": "trace_parser",
|
||||||
|
"name": "Test trace parser",
|
||||||
|
"id": "test-trace-parser",
|
||||||
|
"trace_id": {
|
||||||
|
"parse_from": "attributes.test_trace_id"
|
||||||
|
},
|
||||||
|
"span_id": {
|
||||||
|
"parse_from": "attributes.test_span_id"
|
||||||
|
},
|
||||||
|
"trace_flags": {
|
||||||
|
"parse_from": "attributes.test_trace_flags"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`), &traceParserOp)
|
||||||
|
require.Nil(err)
|
||||||
|
testPipelines[0].Config = append(testPipelines[0].Config, traceParserOp)
|
||||||
|
|
||||||
|
testTraceId, err := utils.RandomHex(16)
|
||||||
|
require.Nil(err)
|
||||||
|
|
||||||
|
testSpanId, err := utils.RandomHex(8)
|
||||||
|
require.Nil(err)
|
||||||
|
|
||||||
|
testTraceFlags, err := utils.RandomHex(1)
|
||||||
|
require.Nil(err)
|
||||||
|
|
||||||
|
testLog := model.SignozLog{
|
||||||
|
Timestamp: uint64(time.Now().UnixNano()),
|
||||||
|
Body: "test log",
|
||||||
|
Attributes_string: map[string]string{
|
||||||
|
"method": "GET",
|
||||||
|
"test_trace_id": testTraceId,
|
||||||
|
"test_span_id": testSpanId,
|
||||||
|
"test_trace_flags": testTraceFlags,
|
||||||
|
},
|
||||||
|
SpanID: "",
|
||||||
|
TraceID: "",
|
||||||
|
TraceFlags: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
result, collectorErrorLogs, err := SimulatePipelinesProcessing(
|
||||||
|
context.Background(),
|
||||||
|
testPipelines,
|
||||||
|
[]model.SignozLog{
|
||||||
|
testLog,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
require.Nil(err)
|
||||||
|
require.Equal(1, len(result))
|
||||||
|
require.Equal(0, len(collectorErrorLogs))
|
||||||
|
processed := result[0]
|
||||||
|
|
||||||
|
require.Equal(testTraceId, processed.TraceID)
|
||||||
|
require.Equal(testSpanId, processed.SpanID)
|
||||||
|
|
||||||
|
expectedTraceFlags, err := strconv.ParseUint(testTraceFlags, 16, 16)
|
||||||
|
require.Nil(err)
|
||||||
|
require.Equal(uint32(expectedTraceFlags), processed.TraceFlags)
|
||||||
|
|
||||||
|
// trace parser should work even if parse_from value is empty
|
||||||
|
testPipelines[0].Config[0].SpanId.ParseFrom = ""
|
||||||
|
result, collectorErrorLogs, err = SimulatePipelinesProcessing(
|
||||||
|
context.Background(),
|
||||||
|
testPipelines,
|
||||||
|
[]model.SignozLog{
|
||||||
|
testLog,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
require.Nil(err)
|
||||||
|
require.Equal(1, len(result))
|
||||||
|
require.Equal(0, len(collectorErrorLogs))
|
||||||
|
require.Equal("", result[0].SpanID)
|
||||||
|
}
|
||||||
|
|
||||||
func makeTestLogEntry(
|
func makeTestLogEntry(
|
||||||
body string,
|
body string,
|
||||||
attributes map[string]string,
|
attributes map[string]string,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user