diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index 19982c1c4f..e78a248ccb 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -146,7 +146,7 @@ services: condition: on-failure query-service: - image: signoz/query-service:0.33.1 + image: signoz/query-service:0.34.0 command: [ "-config=/root/config/prometheus.yml", @@ -186,7 +186,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:0.33.1 + image: signoz/frontend:0.34.0 deploy: restart_policy: condition: on-failure @@ -199,7 +199,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector: - image: signoz/signoz-otel-collector:0.79.13 + image: signoz/signoz-otel-collector:0.88.0 command: [ "--config=/etc/otel-collector-config.yaml", @@ -237,7 +237,7 @@ services: - query-service otel-collector-migrator: - image: signoz/signoz-schema-migrator:0.79.13 + image: signoz/signoz-schema-migrator:0.88.0 deploy: restart_policy: condition: on-failure @@ -250,7 +250,7 @@ services: # - clickhouse-3 otel-collector-metrics: - image: signoz/signoz-otel-collector:0.79.13 + image: signoz/signoz-otel-collector:0.88.0 command: [ "--config=/etc/otel-collector-metrics-config.yaml", diff --git a/deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml b/deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml index bc4fa3d3a8..29409919a7 100644 --- a/deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml +++ b/deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml @@ -61,35 +61,6 @@ receivers: job_name: otel-collector processors: - logstransform/internal: - operators: - - type: regex_parser - id: traceid - # https://regex101.com/r/MMfNjk/1 - regex: '(?i)(trace(-|_||)id("|=| |-|:)*)(?P[A-Fa-f0-9]+)' - parse_from: body - parse_to: attributes.temp_trace - if: 'body matches "(?i)(trace(-|_||)id(\"|=| |-|:)*)(?P[A-Fa-f0-9]+)"' - output: spanid - - type: regex_parser - id: spanid - # https://regex101.com/r/uXSwLc/1 - regex: '(?i)(span(-|_||)id("|=| |-|:)*)(?P[A-Fa-f0-9]+)' - parse_from: body - parse_to: attributes.temp_trace - if: 'body matches "(?i)(span(-|_||)id(\"|=| |-|:)*)(?P[A-Fa-f0-9]+)"' - output: trace_parser - - type: trace_parser - id: trace_parser - trace_id: - parse_from: attributes.temp_trace.trace_id - span_id: - parse_from: attributes.temp_trace.span_id - output: remove_temp - - type: remove - id: remove_temp - field: attributes.temp_trace - if: '"temp_trace" in attributes' batch: send_batch_size: 10000 send_batch_max_size: 11000 @@ -188,5 +159,5 @@ service: exporters: [prometheus] logs: receivers: [otlp, tcplog/docker] - processors: [logstransform/internal, batch] + processors: [batch] exporters: [clickhouselogsexporter] diff --git a/deploy/docker/clickhouse-setup/docker-compose-core.yaml b/deploy/docker/clickhouse-setup/docker-compose-core.yaml index c24bd1870d..08e671d3eb 100644 --- a/deploy/docker/clickhouse-setup/docker-compose-core.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose-core.yaml @@ -66,7 +66,7 @@ services: - --storage.path=/data otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.79.13} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.0} container_name: otel-migrator command: - "--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` otel-collector: container_name: signoz-otel-collector - image: signoz/signoz-otel-collector:0.79.13 + image: signoz/signoz-otel-collector:0.88.0 command: [ "--config=/etc/otel-collector-config.yaml", @@ -118,7 +118,7 @@ services: otel-collector-metrics: container_name: signoz-otel-collector-metrics - image: signoz/signoz-otel-collector:0.79.13 + image: signoz/signoz-otel-collector:0.88.0 command: [ "--config=/etc/otel-collector-metrics-config.yaml", diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index ac2e21bf55..b3f82d8e56 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.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` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.33.1} + image: signoz/query-service:${DOCKER_TAG:-0.34.0} container_name: signoz-query-service command: [ @@ -203,7 +203,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.33.1} + image: signoz/frontend:${DOCKER_TAG:-0.34.0} container_name: signoz-frontend restart: on-failure depends_on: @@ -215,7 +215,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.79.13} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.0} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -229,7 +229,7 @@ services: otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.13} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.0} container_name: signoz-otel-collector command: [ @@ -269,7 +269,7 @@ services: condition: service_healthy otel-collector-metrics: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.13} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.0} container_name: signoz-otel-collector-metrics command: [ diff --git a/deploy/docker/clickhouse-setup/otel-collector-config.yaml b/deploy/docker/clickhouse-setup/otel-collector-config.yaml index fe6da5f126..204dcd9511 100644 --- a/deploy/docker/clickhouse-setup/otel-collector-config.yaml +++ b/deploy/docker/clickhouse-setup/otel-collector-config.yaml @@ -62,35 +62,6 @@ receivers: processors: - logstransform/internal: - operators: - - type: regex_parser - id: traceid - # https://regex101.com/r/MMfNjk/1 - regex: '(?i)(trace(-|_||)id("|=| |-|:)*)(?P[A-Fa-f0-9]+)' - parse_from: body - parse_to: attributes.temp_trace - if: 'body matches "(?i)(trace(-|_||)id(\"|=| |-|:)*)(?P[A-Fa-f0-9]+)"' - output: spanid - - type: regex_parser - id: spanid - # https://regex101.com/r/uXSwLc/1 - regex: '(?i)(span(-|_||)id("|=| |-|:)*)(?P[A-Fa-f0-9]+)' - parse_from: body - parse_to: attributes.temp_trace - if: 'body matches "(?i)(span(-|_||)id(\"|=| |-|:)*)(?P[A-Fa-f0-9]+)"' - output: trace_parser - - type: trace_parser - id: trace_parser - trace_id: - parse_from: attributes.temp_trace.trace_id - span_id: - parse_from: attributes.temp_trace.span_id - output: remove_temp - - type: remove - id: remove_temp - field: attributes.temp_trace - if: '"temp_trace" in attributes' batch: send_batch_size: 10000 send_batch_max_size: 11000 @@ -193,5 +164,5 @@ service: exporters: [prometheus] logs: receivers: [otlp, tcplog/docker] - processors: [logstransform/internal, batch] + processors: [batch] exporters: [clickhouselogsexporter] \ No newline at end of file diff --git a/ee/query-service/app/api/api.go b/ee/query-service/app/api/api.go index a17eb7b79a..ddb617188f 100644 --- a/ee/query-service/app/api/api.go +++ b/ee/query-service/app/api/api.go @@ -160,6 +160,9 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router, am *baseapp.AuthMiddlew router.HandleFunc("/api/v1/billing", am.AdminAccess(ah.getBilling)).Methods(http.MethodGet) router.HandleFunc("/api/v1/portal", am.AdminAccess(ah.portalSession)).Methods(http.MethodPost) + router.HandleFunc("/api/v1/dashboards/{uuid}/lock", am.EditAccess(ah.lockDashboard)).Methods(http.MethodPut) + router.HandleFunc("/api/v1/dashboards/{uuid}/unlock", am.EditAccess(ah.unlockDashboard)).Methods(http.MethodPut) + router.HandleFunc("/api/v2/licenses", am.ViewAccess(ah.listLicensesV2)). Methods(http.MethodGet) diff --git a/ee/query-service/app/api/dashboard.go b/ee/query-service/app/api/dashboard.go new file mode 100644 index 0000000000..83c82a1477 --- /dev/null +++ b/ee/query-service/app/api/dashboard.go @@ -0,0 +1,51 @@ +package api + +import ( + "github.com/gorilla/mux" + "go.signoz.io/signoz/pkg/query-service/app/dashboards" + "go.signoz.io/signoz/pkg/query-service/auth" + "go.signoz.io/signoz/pkg/query-service/common" + "go.signoz.io/signoz/pkg/query-service/model" + "net/http" +) + +func (ah *APIHandler) lockDashboard(w http.ResponseWriter, r *http.Request) { + ah.lockUnlockDashboard(w, r, true) +} + +func (ah *APIHandler) unlockDashboard(w http.ResponseWriter, r *http.Request) { + ah.lockUnlockDashboard(w, r, false) +} + +func (ah *APIHandler) lockUnlockDashboard(w http.ResponseWriter, r *http.Request, lock bool) { + // Locking can only be done by the owner of the dashboard + // or an admin + + // - Fetch the dashboard + // - Check if the user is the owner or an admin + // - If yes, lock/unlock the dashboard + // - If no, return 403 + + // Get the dashboard UUID from the request + uuid := mux.Vars(r)["uuid"] + dashboard, err := dashboards.GetDashboard(r.Context(), uuid) + if err != nil { + RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, err.Error()) + return + } + + user := common.GetUserFromContext(r.Context()) + if !auth.IsAdmin(user) && (dashboard.CreateBy != nil && *dashboard.CreateBy != user.Email) { + RespondError(w, &model.ApiError{Typ: model.ErrorForbidden, Err: err}, "You are not authorized to lock/unlock this dashboard") + return + } + + // Lock/Unlock the dashboard + err = dashboards.LockUnlockDashboard(r.Context(), uuid, lock) + if err != nil { + RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, err.Error()) + return + } + + ah.Respond(w, "Dashboard updated successfully") +} diff --git a/ee/query-service/app/api/license.go b/ee/query-service/app/api/license.go index 66d108d468..e382461d54 100644 --- a/ee/query-service/app/api/license.go +++ b/ee/query-service/app/api/license.go @@ -52,7 +52,6 @@ func (ah *APIHandler) listLicenses(w http.ResponseWriter, r *http.Request) { } func (ah *APIHandler) applyLicense(w http.ResponseWriter, r *http.Request) { - ctx := context.Background() var l model.License if err := json.NewDecoder(r.Body).Decode(&l); err != nil { @@ -64,8 +63,7 @@ func (ah *APIHandler) applyLicense(w http.ResponseWriter, r *http.Request) { RespondError(w, model.BadRequest(fmt.Errorf("license key is required")), nil) return } - - license, apiError := ah.LM().Activate(ctx, l.Key) + license, apiError := ah.LM().Activate(r.Context(), l.Key) if apiError != nil { RespondError(w, apiError, nil) return diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go index 3d50ec5ede..bed3855f17 100644 --- a/ee/query-service/app/server.go +++ b/ee/query-service/app/server.go @@ -23,6 +23,7 @@ import ( "go.signoz.io/signoz/ee/query-service/constants" "go.signoz.io/signoz/ee/query-service/dao" "go.signoz.io/signoz/ee/query-service/interfaces" + "go.signoz.io/signoz/pkg/query-service/auth" baseInterface "go.signoz.io/signoz/pkg/query-service/interfaces" v3 "go.signoz.io/signoz/pkg/query-service/model/v3" @@ -437,7 +438,10 @@ func extractQueryRangeV3Data(path string, r *http.Request) (map[string]interface telemetry.GetInstance().AddActiveLogsUser() } data["dataSources"] = dataSources - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_QUERY_RANGE_V3, data, true) + userEmail, err := auth.GetEmailFromJwt(r.Context()) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_QUERY_RANGE_V3, data, userEmail, true) + } } return data, true } @@ -458,6 +462,8 @@ func getActiveLogs(path string, r *http.Request) { func (s *Server) analyticsMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := auth.AttachJwtToContext(r.Context(), r) + r = r.WithContext(ctx) route := mux.CurrentRoute(r) path, _ := route.GetPathTemplate() @@ -475,7 +481,10 @@ func (s *Server) analyticsMiddleware(next http.Handler) http.Handler { } if _, ok := telemetry.IgnoredPaths()[path]; !ok { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data) + userEmail, err := auth.GetEmailFromJwt(r.Context()) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail) + } } }) diff --git a/ee/query-service/license/manager.go b/ee/query-service/license/manager.go index 7a1be5118f..dcfa8235b1 100644 --- a/ee/query-service/license/manager.go +++ b/ee/query-service/license/manager.go @@ -10,6 +10,7 @@ import ( "sync" + "go.signoz.io/signoz/pkg/query-service/auth" baseconstants "go.signoz.io/signoz/pkg/query-service/constants" validate "go.signoz.io/signoz/ee/query-service/integrations/signozio" @@ -203,7 +204,7 @@ func (lm *Manager) Validate(ctx context.Context) (reterr error) { zap.S().Errorf("License validation completed with error", reterr) atomic.AddUint64(&lm.failedAttempts, 1) telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_LICENSE_CHECK_FAILED, - map[string]interface{}{"err": reterr.Error()}) + map[string]interface{}{"err": reterr.Error()}, "") } else { zap.S().Info("License validation completed with no errors") } @@ -259,8 +260,11 @@ func (lm *Manager) Validate(ctx context.Context) (reterr error) { func (lm *Manager) Activate(ctx context.Context, key string) (licenseResponse *model.License, errResponse *model.ApiError) { defer func() { if errResponse != nil { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_LICENSE_ACT_FAILED, - map[string]interface{}{"err": errResponse.Err.Error()}) + userEmail, err := auth.GetEmailFromJwt(ctx) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_LICENSE_ACT_FAILED, + map[string]interface{}{"err": errResponse.Err.Error()}, userEmail) + } } }() diff --git a/frontend/package.json b/frontend/package.json index 3e0a7f3981..0c79c47b1d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -36,7 +36,7 @@ "@uiw/react-md-editor": "3.23.5", "@xstate/react": "^3.0.0", "ansi-to-html": "0.7.2", - "antd": "5.0.5", + "antd": "5.11.0", "antd-table-saveas-excel": "2.2.1", "axios": "^0.21.0", "babel-eslint": "^10.1.0", @@ -80,11 +80,11 @@ "react-dnd-html5-backend": "16.0.1", "react-dom": "18.2.0", "react-drag-listview": "2.0.0", + "react-error-boundary": "4.0.11", "react-force-graph": "^1.43.0", "react-grid-layout": "^1.3.4", "react-helmet-async": "1.3.0", "react-i18next": "^11.16.1", - "react-intersection-observer": "9.4.1", "react-markdown": "8.0.7", "react-query": "^3.34.19", "react-redux": "^7.2.2", @@ -102,6 +102,7 @@ "ts-node": "^10.2.1", "tsconfig-paths-webpack-plugin": "^3.5.1", "typescript": "^4.0.5", + "uplot": "1.6.26", "uuid": "^8.3.2", "web-vitals": "^0.2.4", "webpack": "5.88.2", diff --git a/frontend/public/locales/en-GB/alerts.json b/frontend/public/locales/en-GB/alerts.json index 816b71e563..98e9780e6a 100644 --- a/frontend/public/locales/en-GB/alerts.json +++ b/frontend/public/locales/en-GB/alerts.json @@ -34,7 +34,7 @@ "button_returntorules": "Return to rules", "button_cancelchanges": "Cancel", "button_discard": "Discard", - "text_condition1": "Send a notification when the metric is", + "text_condition1": "Send a notification when", "text_condition2": "the threshold", "text_condition3": "during the last", "option_5min": "5 mins", @@ -109,5 +109,6 @@ "traces_based_alert_desc": "Send a notification when a condition occurs in the traces data.", "exceptions_based_alert": "Exceptions-based Alert", "exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.", - "field_unit": "Threshold unit" + "field_unit": "Threshold unit", + "selected_query_placeholder": "Select query" } diff --git a/frontend/public/locales/en-GB/rules.json b/frontend/public/locales/en-GB/rules.json index e67bd35273..c913dc08e0 100644 --- a/frontend/public/locales/en-GB/rules.json +++ b/frontend/public/locales/en-GB/rules.json @@ -1,85 +1,85 @@ { - "preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.", - "preview_chart_threshold_label": "Threshold", - "placeholder_label_key_pair": "Click here to enter a label (key value pairs)", - "button_yes": "Yes", - "button_no": "No", - "remove_label_confirm": "This action will remove all the labels. Do you want to proceed?", - "remove_label_success": "Labels cleared", - "alert_form_step1": "Step 1 - Define the metric", - "alert_form_step2": "Step 2 - Define Alert Conditions", - "alert_form_step3": "Step 3 - Alert Configuration", - "metric_query_max_limit": "Can not create query. You can create maximum of 5 queries", - "confirm_save_title": "Save Changes", - "confirm_save_content_part1": "Your alert built with", - "confirm_save_content_part2": "query will be saved. Press OK to confirm.", - "unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin", - "rule_created": "Rule created successfully", - "rule_edited": "Rule edited successfully", - "expression_missing": "expression is missing in {{where}}", - "metricname_missing": "metric name is missing in {{where}}", - "condition_required": "at least one metric condition is required", - "alertname_required": "alert name is required", - "promql_required": "promql expression is required when query format is set to PromQL", - "button_savechanges": "Save Rule", - "button_createrule": "Create Rule", - "button_returntorules": "Return to rules", - "button_cancelchanges": "Cancel", - "button_discard": "Discard", - "text_condition1": "Send a notification when the metric is", - "text_condition2": "the threshold", - "text_condition3": "during the last", - "option_5min": "5 mins", - "option_10min": "10 mins", - "option_15min": "15 mins", - "option_60min": "60 mins", - "option_4hours": "4 hours", - "option_24hours": "24 hours", - "field_threshold": "Alert Threshold", - "option_allthetimes": "all the times", - "option_atleastonce": "at least once", - "option_onaverage": "on average", - "option_intotal": "in total", - "option_above": "above", - "option_below": "below", - "option_equal": "is equal to", - "option_notequal": "not equal to", - "button_query": "Query", - "button_formula": "Formula", - "tab_qb": "Query Builder", - "tab_promql": "PromQL", - "title_confirm": "Confirm", - "button_ok": "Yes", - "button_cancel": "No", - "field_promql_expr": "PromQL Expression", - "field_alert_name": "Alert Name", - "field_alert_desc": "Alert Description", - "field_labels": "Labels", - "field_severity": "Severity", - "option_critical": "Critical", - "option_error": "Error", - "option_warning": "Warning", - "option_info": "Info", - "user_guide_headline": "Steps to create an Alert", - "user_guide_qb_step1": "Step 1 - Define the metric", - "user_guide_qb_step1a": "Choose a metric which you want to create an alert on", - "user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed", - "user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric", - "user_guide_qb_step1d": "Create a formula based on Queries if needed", - "user_guide_qb_step2": "Step 2 - Define Alert Conditions", - "user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value", - "user_guide_qb_step2b": "Enter the Alert threshold", - "user_guide_qb_step3": "Step 3 -Alert Configuration", - "user_guide_qb_step3a": "Set alert severity, name and descriptions", - "user_guide_qb_step3b": "Add tags to the alert in the Label field if needed", - "user_guide_pql_step1": "Step 1 - Define the metric", - "user_guide_pql_step1a": "Write a PromQL query for the metric", - "user_guide_pql_step1b": "Format the legends based on labels you want to highlight", - "user_guide_pql_step2": "Step 2 - Define Alert Conditions", - "user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value", - "user_guide_pql_step2b": "Enter the Alert threshold", - "user_guide_pql_step3": "Step 3 -Alert Configuration", - "user_guide_pql_step3a": "Set alert severity, name and descriptions", - "user_guide_pql_step3b": "Add tags to the alert in the Label field if needed", - "user_tooltip_more_help": "More details on how to create alerts" -} \ No newline at end of file + "preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.", + "preview_chart_threshold_label": "Threshold", + "placeholder_label_key_pair": "Click here to enter a label (key value pairs)", + "button_yes": "Yes", + "button_no": "No", + "remove_label_confirm": "This action will remove all the labels. Do you want to proceed?", + "remove_label_success": "Labels cleared", + "alert_form_step1": "Step 1 - Define the metric", + "alert_form_step2": "Step 2 - Define Alert Conditions", + "alert_form_step3": "Step 3 - Alert Configuration", + "metric_query_max_limit": "Can not create query. You can create maximum of 5 queries", + "confirm_save_title": "Save Changes", + "confirm_save_content_part1": "Your alert built with", + "confirm_save_content_part2": "query will be saved. Press OK to confirm.", + "unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin", + "rule_created": "Rule created successfully", + "rule_edited": "Rule edited successfully", + "expression_missing": "expression is missing in {{where}}", + "metricname_missing": "metric name is missing in {{where}}", + "condition_required": "at least one metric condition is required", + "alertname_required": "alert name is required", + "promql_required": "promql expression is required when query format is set to PromQL", + "button_savechanges": "Save Rule", + "button_createrule": "Create Rule", + "button_returntorules": "Return to rules", + "button_cancelchanges": "Cancel", + "button_discard": "Discard", + "text_condition1": "Send a notification when", + "text_condition2": "the threshold", + "text_condition3": "during the last", + "option_5min": "5 mins", + "option_10min": "10 mins", + "option_15min": "15 mins", + "option_60min": "60 mins", + "option_4hours": "4 hours", + "option_24hours": "24 hours", + "field_threshold": "Alert Threshold", + "option_allthetimes": "all the times", + "option_atleastonce": "at least once", + "option_onaverage": "on average", + "option_intotal": "in total", + "option_above": "above", + "option_below": "below", + "option_equal": "is equal to", + "option_notequal": "not equal to", + "button_query": "Query", + "button_formula": "Formula", + "tab_qb": "Query Builder", + "tab_promql": "PromQL", + "title_confirm": "Confirm", + "button_ok": "Yes", + "button_cancel": "No", + "field_promql_expr": "PromQL Expression", + "field_alert_name": "Alert Name", + "field_alert_desc": "Alert Description", + "field_labels": "Labels", + "field_severity": "Severity", + "option_critical": "Critical", + "option_error": "Error", + "option_warning": "Warning", + "option_info": "Info", + "user_guide_headline": "Steps to create an Alert", + "user_guide_qb_step1": "Step 1 - Define the metric", + "user_guide_qb_step1a": "Choose a metric which you want to create an alert on", + "user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed", + "user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric", + "user_guide_qb_step1d": "Create a formula based on Queries if needed", + "user_guide_qb_step2": "Step 2 - Define Alert Conditions", + "user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value", + "user_guide_qb_step2b": "Enter the Alert threshold", + "user_guide_qb_step3": "Step 3 -Alert Configuration", + "user_guide_qb_step3a": "Set alert severity, name and descriptions", + "user_guide_qb_step3b": "Add tags to the alert in the Label field if needed", + "user_guide_pql_step1": "Step 1 - Define the metric", + "user_guide_pql_step1a": "Write a PromQL query for the metric", + "user_guide_pql_step1b": "Format the legends based on labels you want to highlight", + "user_guide_pql_step2": "Step 2 - Define Alert Conditions", + "user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value", + "user_guide_pql_step2b": "Enter the Alert threshold", + "user_guide_pql_step3": "Step 3 -Alert Configuration", + "user_guide_pql_step3a": "Set alert severity, name and descriptions", + "user_guide_pql_step3b": "Add tags to the alert in the Label field if needed", + "user_tooltip_more_help": "More details on how to create alerts" +} diff --git a/frontend/public/locales/en/alerts.json b/frontend/public/locales/en/alerts.json index 816b71e563..98e9780e6a 100644 --- a/frontend/public/locales/en/alerts.json +++ b/frontend/public/locales/en/alerts.json @@ -34,7 +34,7 @@ "button_returntorules": "Return to rules", "button_cancelchanges": "Cancel", "button_discard": "Discard", - "text_condition1": "Send a notification when the metric is", + "text_condition1": "Send a notification when", "text_condition2": "the threshold", "text_condition3": "during the last", "option_5min": "5 mins", @@ -109,5 +109,6 @@ "traces_based_alert_desc": "Send a notification when a condition occurs in the traces data.", "exceptions_based_alert": "Exceptions-based Alert", "exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.", - "field_unit": "Threshold unit" + "field_unit": "Threshold unit", + "selected_query_placeholder": "Select query" } diff --git a/frontend/public/locales/en/dashboard.json b/frontend/public/locales/en/dashboard.json index b69113483d..7c4b894e76 100644 --- a/frontend/public/locales/en/dashboard.json +++ b/frontend/public/locales/en/dashboard.json @@ -20,5 +20,7 @@ "variable_updated_successfully": "Variable updated successfully", "error_while_updating_variable": "Error while updating variable", "dashboard_has_been_updated": "Dashboard has been updated", - "do_you_want_to_refresh_the_dashboard": "Do you want to refresh the dashboard?" + "do_you_want_to_refresh_the_dashboard": "Do you want to refresh the dashboard?", +"locked_dashboard_delete_tooltip_admin_author": "Dashboard is locked. Please unlock the dashboard to enable delete.", +"locked_dashboard_delete_tooltip_editor": "Dashboard is locked. Please contact admin to delete the dashboard." } diff --git a/frontend/public/locales/en/errorDetails.json b/frontend/public/locales/en/errorDetails.json index f29f4993c8..da68ed48ca 100644 --- a/frontend/public/locales/en/errorDetails.json +++ b/frontend/public/locales/en/errorDetails.json @@ -3,5 +3,7 @@ "see_error_in_trace_graph": "See the error in trace graph", "stack_trace": "Stacktrace", "older": "Older", - "newer": "Newer" + "newer": "Newer", + "something_went_wrong": "Oops !!! Something went wrong", + "contact_if_issue_exists": "Don't worry, our team is here to help. Please contact support if the issue persists." } diff --git a/frontend/public/locales/en/rules.json b/frontend/public/locales/en/rules.json index e67bd35273..c913dc08e0 100644 --- a/frontend/public/locales/en/rules.json +++ b/frontend/public/locales/en/rules.json @@ -1,85 +1,85 @@ { - "preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.", - "preview_chart_threshold_label": "Threshold", - "placeholder_label_key_pair": "Click here to enter a label (key value pairs)", - "button_yes": "Yes", - "button_no": "No", - "remove_label_confirm": "This action will remove all the labels. Do you want to proceed?", - "remove_label_success": "Labels cleared", - "alert_form_step1": "Step 1 - Define the metric", - "alert_form_step2": "Step 2 - Define Alert Conditions", - "alert_form_step3": "Step 3 - Alert Configuration", - "metric_query_max_limit": "Can not create query. You can create maximum of 5 queries", - "confirm_save_title": "Save Changes", - "confirm_save_content_part1": "Your alert built with", - "confirm_save_content_part2": "query will be saved. Press OK to confirm.", - "unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin", - "rule_created": "Rule created successfully", - "rule_edited": "Rule edited successfully", - "expression_missing": "expression is missing in {{where}}", - "metricname_missing": "metric name is missing in {{where}}", - "condition_required": "at least one metric condition is required", - "alertname_required": "alert name is required", - "promql_required": "promql expression is required when query format is set to PromQL", - "button_savechanges": "Save Rule", - "button_createrule": "Create Rule", - "button_returntorules": "Return to rules", - "button_cancelchanges": "Cancel", - "button_discard": "Discard", - "text_condition1": "Send a notification when the metric is", - "text_condition2": "the threshold", - "text_condition3": "during the last", - "option_5min": "5 mins", - "option_10min": "10 mins", - "option_15min": "15 mins", - "option_60min": "60 mins", - "option_4hours": "4 hours", - "option_24hours": "24 hours", - "field_threshold": "Alert Threshold", - "option_allthetimes": "all the times", - "option_atleastonce": "at least once", - "option_onaverage": "on average", - "option_intotal": "in total", - "option_above": "above", - "option_below": "below", - "option_equal": "is equal to", - "option_notequal": "not equal to", - "button_query": "Query", - "button_formula": "Formula", - "tab_qb": "Query Builder", - "tab_promql": "PromQL", - "title_confirm": "Confirm", - "button_ok": "Yes", - "button_cancel": "No", - "field_promql_expr": "PromQL Expression", - "field_alert_name": "Alert Name", - "field_alert_desc": "Alert Description", - "field_labels": "Labels", - "field_severity": "Severity", - "option_critical": "Critical", - "option_error": "Error", - "option_warning": "Warning", - "option_info": "Info", - "user_guide_headline": "Steps to create an Alert", - "user_guide_qb_step1": "Step 1 - Define the metric", - "user_guide_qb_step1a": "Choose a metric which you want to create an alert on", - "user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed", - "user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric", - "user_guide_qb_step1d": "Create a formula based on Queries if needed", - "user_guide_qb_step2": "Step 2 - Define Alert Conditions", - "user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value", - "user_guide_qb_step2b": "Enter the Alert threshold", - "user_guide_qb_step3": "Step 3 -Alert Configuration", - "user_guide_qb_step3a": "Set alert severity, name and descriptions", - "user_guide_qb_step3b": "Add tags to the alert in the Label field if needed", - "user_guide_pql_step1": "Step 1 - Define the metric", - "user_guide_pql_step1a": "Write a PromQL query for the metric", - "user_guide_pql_step1b": "Format the legends based on labels you want to highlight", - "user_guide_pql_step2": "Step 2 - Define Alert Conditions", - "user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value", - "user_guide_pql_step2b": "Enter the Alert threshold", - "user_guide_pql_step3": "Step 3 -Alert Configuration", - "user_guide_pql_step3a": "Set alert severity, name and descriptions", - "user_guide_pql_step3b": "Add tags to the alert in the Label field if needed", - "user_tooltip_more_help": "More details on how to create alerts" -} \ No newline at end of file + "preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.", + "preview_chart_threshold_label": "Threshold", + "placeholder_label_key_pair": "Click here to enter a label (key value pairs)", + "button_yes": "Yes", + "button_no": "No", + "remove_label_confirm": "This action will remove all the labels. Do you want to proceed?", + "remove_label_success": "Labels cleared", + "alert_form_step1": "Step 1 - Define the metric", + "alert_form_step2": "Step 2 - Define Alert Conditions", + "alert_form_step3": "Step 3 - Alert Configuration", + "metric_query_max_limit": "Can not create query. You can create maximum of 5 queries", + "confirm_save_title": "Save Changes", + "confirm_save_content_part1": "Your alert built with", + "confirm_save_content_part2": "query will be saved. Press OK to confirm.", + "unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin", + "rule_created": "Rule created successfully", + "rule_edited": "Rule edited successfully", + "expression_missing": "expression is missing in {{where}}", + "metricname_missing": "metric name is missing in {{where}}", + "condition_required": "at least one metric condition is required", + "alertname_required": "alert name is required", + "promql_required": "promql expression is required when query format is set to PromQL", + "button_savechanges": "Save Rule", + "button_createrule": "Create Rule", + "button_returntorules": "Return to rules", + "button_cancelchanges": "Cancel", + "button_discard": "Discard", + "text_condition1": "Send a notification when", + "text_condition2": "the threshold", + "text_condition3": "during the last", + "option_5min": "5 mins", + "option_10min": "10 mins", + "option_15min": "15 mins", + "option_60min": "60 mins", + "option_4hours": "4 hours", + "option_24hours": "24 hours", + "field_threshold": "Alert Threshold", + "option_allthetimes": "all the times", + "option_atleastonce": "at least once", + "option_onaverage": "on average", + "option_intotal": "in total", + "option_above": "above", + "option_below": "below", + "option_equal": "is equal to", + "option_notequal": "not equal to", + "button_query": "Query", + "button_formula": "Formula", + "tab_qb": "Query Builder", + "tab_promql": "PromQL", + "title_confirm": "Confirm", + "button_ok": "Yes", + "button_cancel": "No", + "field_promql_expr": "PromQL Expression", + "field_alert_name": "Alert Name", + "field_alert_desc": "Alert Description", + "field_labels": "Labels", + "field_severity": "Severity", + "option_critical": "Critical", + "option_error": "Error", + "option_warning": "Warning", + "option_info": "Info", + "user_guide_headline": "Steps to create an Alert", + "user_guide_qb_step1": "Step 1 - Define the metric", + "user_guide_qb_step1a": "Choose a metric which you want to create an alert on", + "user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed", + "user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric", + "user_guide_qb_step1d": "Create a formula based on Queries if needed", + "user_guide_qb_step2": "Step 2 - Define Alert Conditions", + "user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value", + "user_guide_qb_step2b": "Enter the Alert threshold", + "user_guide_qb_step3": "Step 3 -Alert Configuration", + "user_guide_qb_step3a": "Set alert severity, name and descriptions", + "user_guide_qb_step3b": "Add tags to the alert in the Label field if needed", + "user_guide_pql_step1": "Step 1 - Define the metric", + "user_guide_pql_step1a": "Write a PromQL query for the metric", + "user_guide_pql_step1b": "Format the legends based on labels you want to highlight", + "user_guide_pql_step2": "Step 2 - Define Alert Conditions", + "user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value", + "user_guide_pql_step2b": "Enter the Alert threshold", + "user_guide_pql_step3": "Step 3 -Alert Configuration", + "user_guide_pql_step3a": "Set alert severity, name and descriptions", + "user_guide_pql_step3b": "Add tags to the alert in the Label field if needed", + "user_tooltip_more_help": "More details on how to create alerts" +} diff --git a/frontend/public/locales/en/valueGraph.json b/frontend/public/locales/en/valueGraph.json new file mode 100644 index 0000000000..76aa50e309 --- /dev/null +++ b/frontend/public/locales/en/valueGraph.json @@ -0,0 +1,3 @@ +{ + "this_value_satisfies_multiple_thresholds": "This value satisfies multiple thresholds." +} \ No newline at end of file diff --git a/frontend/src/api/dashboard/lockDashboard.ts b/frontend/src/api/dashboard/lockDashboard.ts new file mode 100644 index 0000000000..3393de8fa3 --- /dev/null +++ b/frontend/src/api/dashboard/lockDashboard.ts @@ -0,0 +1,11 @@ +import axios from 'api'; +import { AxiosResponse } from 'axios'; + +interface LockDashboardProps { + uuid: string; +} + +const lockDashboard = (props: LockDashboardProps): Promise => + axios.put(`/dashboards/${props.uuid}/lock`); + +export default lockDashboard; diff --git a/frontend/src/api/dashboard/unlockDashboard.ts b/frontend/src/api/dashboard/unlockDashboard.ts new file mode 100644 index 0000000000..fd4ffbe41a --- /dev/null +++ b/frontend/src/api/dashboard/unlockDashboard.ts @@ -0,0 +1,11 @@ +import axios from 'api'; +import { AxiosResponse } from 'axios'; + +interface UnlockDashboardProps { + uuid: string; +} + +const unlockDashboard = (props: UnlockDashboardProps): Promise => + axios.put(`/dashboards/${props.uuid}/unlock`); + +export default unlockDashboard; diff --git a/frontend/src/components/DraggableTableRow/tests/__snapshots__/DraggableTableRow.test.tsx.snap b/frontend/src/components/DraggableTableRow/tests/__snapshots__/DraggableTableRow.test.tsx.snap index 4c70482cb6..984d943840 100644 --- a/frontend/src/components/DraggableTableRow/tests/__snapshots__/DraggableTableRow.test.tsx.snap +++ b/frontend/src/components/DraggableTableRow/tests/__snapshots__/DraggableTableRow.test.tsx.snap @@ -3,10 +3,10 @@ exports[`DraggableTableRow Snapshot test should render DraggableTableRow 1`] = `
- @@ -43,7 +43,7 @@ exports[`DraggableTableRow Snapshot test should render DraggableTableRow 1`] = ` class="ant-table-cell" >
{t('name_of_the_view')} -
+ void; export type ToggleGraphProps = { - toggleGraph(graphIndex: number, isVisible: boolean): void; + toggleGraph(graphIndex: number, isVisible: boolean, reference?: string): void; }; export type CustomChartOptions = ChartOptions & { diff --git a/frontend/src/components/Graph/yAxisConfig.ts b/frontend/src/components/Graph/yAxisConfig.ts index 5d1eeb5da7..a5eca12926 100644 --- a/frontend/src/components/Graph/yAxisConfig.ts +++ b/frontend/src/components/Graph/yAxisConfig.ts @@ -46,7 +46,7 @@ export const getYAxisFormattedValue = ( return `${parseFloat(value)}`; }; -export const getToolTipValue = (value: string, format: string): string => { +export const getToolTipValue = (value: string, format?: string): string => { try { return formattedValueToString( getValueFormat(format)(parseFloat(value), undefined, undefined, undefined), diff --git a/frontend/src/components/MessageTip/__snapshots__/MessageTip.test.tsx.snap b/frontend/src/components/MessageTip/__snapshots__/MessageTip.test.tsx.snap index 044b288468..8d671bb8b8 100644 --- a/frontend/src/components/MessageTip/__snapshots__/MessageTip.test.tsx.snap +++ b/frontend/src/components/MessageTip/__snapshots__/MessageTip.test.tsx.snap @@ -9,7 +9,7 @@ exports[`MessageTip custom action 1`] = ` } + +
-
- - - - - - -
); } diff --git a/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/CustomCheckBox.tsx b/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/CustomCheckBox.tsx index eda971c1e4..922510eaea 100644 --- a/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/CustomCheckBox.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/CustomCheckBox.tsx @@ -10,13 +10,11 @@ function CustomCheckBox({ graphVisibilityState = [], checkBoxOnChangeHandler, }: CheckBoxProps): JSX.Element { - const { datasets } = data; - const onChangeHandler = (e: CheckboxChangeEvent): void => { checkBoxOnChangeHandler(e, index); }; - const color = datasets[index]?.borderColor?.toString() || grey[0]; + const color = data[index]?.stroke?.toString() || grey[0]; const isChecked = graphVisibilityState[index] || false; diff --git a/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/GetLabel.tsx b/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/GetLabel.tsx index 4ce97d8af2..1cafd49bf5 100644 --- a/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/GetLabel.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/GetLabel.tsx @@ -6,7 +6,7 @@ import Label from './Label'; export const getLabel = ( labelClickedHandler: (labelIndex: number) => void, ): ColumnType => ({ - render: (label, record): JSX.Element => ( + render: (label: string, record): JSX.Element => (
+ +
+ {chartOptions && ( + - Refresh - - - )} + + + )} +
- - - - - {canModifyChart && ( + {canModifyChart && chartOptions && ( )} - +
); } diff --git a/frontend/src/container/GridCardLayout/GridCard/FullView/styles.ts b/frontend/src/container/GridCardLayout/GridCard/FullView/styles.ts index b73a2e9112..09ea5448b8 100644 --- a/frontend/src/container/GridCardLayout/GridCard/FullView/styles.ts +++ b/frontend/src/container/GridCardLayout/GridCard/FullView/styles.ts @@ -31,10 +31,11 @@ export const GraphContainer = styled.div` isGraphLegendToggleAvailable ? '50%' : '100%'}; `; -export const LabelContainer = styled.button` +export const LabelContainer = styled.button<{ isDarkMode?: boolean }>` max-width: 18.75rem; cursor: pointer; border: none; background-color: transparent; - color: ${themeColors.white}; + color: ${(props): string => + props.isDarkMode ? themeColors.white : themeColors.black}; `; diff --git a/frontend/src/container/GridCardLayout/GridCard/FullView/types.ts b/frontend/src/container/GridCardLayout/GridCard/FullView/types.ts index ae686496e5..66d6382675 100644 --- a/frontend/src/container/GridCardLayout/GridCard/FullView/types.ts +++ b/frontend/src/container/GridCardLayout/GridCard/FullView/types.ts @@ -1,9 +1,11 @@ import { CheckboxChangeEvent } from 'antd/es/checkbox'; -import { ChartData, ChartDataset } from 'chart.js'; -import { GraphOnClickHandler, ToggleGraphProps } from 'components/Graph/types'; +import { ToggleGraphProps } from 'components/Graph/types'; +import { UplotProps } from 'components/Uplot/Uplot'; import { PANEL_TYPES } from 'constants/queryBuilder'; -import { MutableRefObject } from 'react'; +import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; +import { Dispatch, MutableRefObject, SetStateAction } from 'react'; import { Widgets } from 'types/api/dashboard/getAll'; +import uPlot from 'uplot'; export interface DataSetProps { index: number; @@ -22,12 +24,13 @@ export interface LegendEntryProps { show: boolean; } -export type ExtendedChartDataset = ChartDataset & { +export type ExtendedChartDataset = uPlot.Series & { show: boolean; sum: number; avg: number; min: number; max: number; + index: number; }; export type PanelTypeAndGraphManagerVisibilityProps = Record< @@ -44,22 +47,22 @@ export interface LabelProps { export interface FullViewProps { widget: Widgets; fullViewOptions?: boolean; - onClickHandler?: GraphOnClickHandler; + onClickHandler?: OnClickPluginOpts['onClick']; name: string; yAxisUnit?: string; - onDragSelect?: (start: number, end: number) => void; + onDragSelect: (start: number, end: number) => void; isDependedDataLoaded?: boolean; graphsVisibilityStates?: boolean[]; onToggleModelHandler?: GraphManagerProps['onToggleModelHandler']; - setGraphsVisibilityStates: (graphsVisibilityStates: boolean[]) => void; + setGraphsVisibilityStates: Dispatch>; parentChartRef: GraphManagerProps['lineChartRef']; } -export interface GraphManagerProps { - data: ChartData; +export interface GraphManagerProps extends UplotProps { name: string; yAxisUnit?: string; onToggleModelHandler?: () => void; + options: uPlot.Options; setGraphsVisibilityStates: FullViewProps['setGraphsVisibilityStates']; graphsVisibilityStates: FullViewProps['graphsVisibilityStates']; lineChartRef?: MutableRefObject; @@ -67,14 +70,14 @@ export interface GraphManagerProps { } export interface CheckBoxProps { - data: ChartData; + data: ExtendedChartDataset[]; index: number; graphVisibilityState: boolean[]; checkBoxOnChangeHandler: (e: CheckboxChangeEvent, index: number) => void; } export interface SaveLegendEntriesToLocalStoreProps { - data: ChartData; + options: uPlot.Options; graphVisibilityState: boolean[]; name: string; } diff --git a/frontend/src/container/GridCardLayout/GridCard/FullView/utils.ts b/frontend/src/container/GridCardLayout/GridCard/FullView/utils.ts index b1ffb3a032..400394d26e 100644 --- a/frontend/src/container/GridCardLayout/GridCard/FullView/utils.ts +++ b/frontend/src/container/GridCardLayout/GridCard/FullView/utils.ts @@ -1,5 +1,5 @@ -import { ChartData, ChartDataset } from 'chart.js'; import { LOCALSTORAGE } from 'constants/localStorage'; +import uPlot from 'uplot'; import { ExtendedChartDataset, @@ -21,33 +21,23 @@ function convertToTwoDecimalsOrZero(value: number): number { } export const getDefaultTableDataSet = ( - data: ChartData, + options: uPlot.Options, + data: uPlot.AlignedData, ): ExtendedChartDataset[] => - data.datasets.map( - (item: ChartDataset): ExtendedChartDataset => { - if (item.data.length === 0) { - return { - ...item, - show: true, - sum: 0, - avg: 0, - max: 0, - min: 0, - }; - } - return { - ...item, - show: true, - sum: convertToTwoDecimalsOrZero( - (item.data as number[]).reduce((a, b) => a + b, 0), - ), - avg: convertToTwoDecimalsOrZero( - (item.data as number[]).reduce((a, b) => a + b, 0) / item.data.length, - ), - max: convertToTwoDecimalsOrZero(Math.max(...(item.data as number[]))), - min: convertToTwoDecimalsOrZero(Math.min(...(item.data as number[]))), - }; - }, + options.series.map( + (item: uPlot.Series, index: number): ExtendedChartDataset => ({ + ...item, + index, + show: true, + sum: convertToTwoDecimalsOrZero( + (data[index] as number[]).reduce((a, b) => a + b, 0), + ), + avg: convertToTwoDecimalsOrZero( + (data[index] as number[]).reduce((a, b) => a + b, 0) / data[index].length, + ), + max: convertToTwoDecimalsOrZero(Math.max(...(data[index] as number[]))), + min: convertToTwoDecimalsOrZero(Math.min(...(data[index] as number[]))), + }), ); export const getAbbreviatedLabel = (label: string): string => { @@ -58,22 +48,24 @@ export const getAbbreviatedLabel = (label: string): string => { return newLabel; }; -export const showAllDataSet = (data: ChartData): LegendEntryProps[] => - data.datasets.map( - (item): LegendEntryProps => ({ - label: item.label || '', - show: true, - }), - ); +export const showAllDataSet = (options: uPlot.Options): LegendEntryProps[] => + options.series + .map( + (item): LegendEntryProps => ({ + label: item.label || '', + show: true, + }), + ) + .filter((_, index) => index !== 0); export const saveLegendEntriesToLocalStorage = ({ - data, + options, graphVisibilityState, name, }: SaveLegendEntriesToLocalStoreProps): void => { const newLegendEntry = { name, - dataIndex: data.datasets.map( + dataIndex: options.series.map( (item, index): LegendEntryProps => ({ label: item.label || '', show: graphVisibilityState[index], diff --git a/frontend/src/container/GridCardLayout/GridCard/Graph.test.tsx b/frontend/src/container/GridCardLayout/GridCard/Graph.test.tsx deleted file mode 100644 index a8ff540cc5..0000000000 --- a/frontend/src/container/GridCardLayout/GridCard/Graph.test.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { mockTestData } from './__mock__/mockChartData'; -import { mocklegendEntryResult } from './__mock__/mockLegendEntryData'; -import { showAllDataSet } from './FullView/utils'; -import { getGraphVisibilityStateOnDataChange } from './utils'; - -describe('getGraphVisibilityStateOnDataChange', () => { - beforeEach(() => { - const localStorageMock = { - getItem: jest.fn(), - }; - Object.defineProperty(window, 'localStorage', { value: localStorageMock }); - }); - - it('should return the correct visibility state and legend entry', () => { - // Mock the localStorage behavior - const mockLocalStorageData = [ - { - name: 'exampleexpanded', - dataIndex: [ - { label: 'customer', show: true }, - { label: 'demo-app', show: false }, - ], - }, - ]; - jest - .spyOn(window.localStorage, 'getItem') - .mockReturnValue(JSON.stringify(mockLocalStorageData)); - - const result1 = getGraphVisibilityStateOnDataChange({ - data: mockTestData, - isExpandedName: true, - name: 'example', - }); - expect(result1.graphVisibilityStates).toEqual([true, false]); - expect(result1.legendEntry).toEqual(mocklegendEntryResult); - - const result2 = getGraphVisibilityStateOnDataChange({ - data: mockTestData, - isExpandedName: false, - name: 'example', - }); - expect(result2.graphVisibilityStates).toEqual( - Array(mockTestData.datasets.length).fill(true), - ); - expect(result2.legendEntry).toEqual(showAllDataSet(mockTestData)); - }); - - it('should return default values if localStorage data is not available', () => { - // Mock the localStorage behavior to return null - jest.spyOn(window.localStorage, 'getItem').mockReturnValue(null); - - const result = getGraphVisibilityStateOnDataChange({ - data: mockTestData, - isExpandedName: true, - name: 'example', - }); - expect(result.graphVisibilityStates).toEqual( - Array(mockTestData.datasets.length).fill(true), - ); - expect(result.legendEntry).toEqual(showAllDataSet(mockTestData)); - }); -}); diff --git a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx index 6ab2bb32a2..b10b49d8cf 100644 --- a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx @@ -25,21 +25,22 @@ import { v4 } from 'uuid'; import WidgetHeader from '../WidgetHeader'; import FullView from './FullView'; -import { FullViewContainer, Modal } from './styles'; +import { Modal } from './styles'; import { WidgetGraphComponentProps } from './types'; import { getGraphVisibilityStateOnDataChange } from './utils'; function WidgetGraphComponent({ - data, widget, queryResponse, errorMessage, name, - onDragSelect, onClickHandler, threshold, headerMenuList, isWarning, + data, + options, + onDragSelect, }: WidgetGraphComponentProps): JSX.Element { const [deleteModal, setDeleteModal] = useState(false); const [modal, setModal] = useState(false); @@ -48,15 +49,16 @@ function WidgetGraphComponent({ const { pathname } = useLocation(); const lineChartRef = useRef(); + const graphRef = useRef(null); const { graphVisibilityStates: localStoredVisibilityStates } = useMemo( () => getGraphVisibilityStateOnDataChange({ - data, + options, isExpandedName: true, name, }), - [data, name], + [options, name], ); const [graphsVisibilityStates, setGraphsVisibilityStates] = useState< @@ -64,6 +66,7 @@ function WidgetGraphComponent({ >(localStoredVisibilityStates); useEffect(() => { + setGraphsVisibilityStates(localStoredVisibilityStates); if (!lineChartRef.current) return; localStoredVisibilityStates.forEach((state, index) => { @@ -74,9 +77,10 @@ function WidgetGraphComponent({ const { setLayouts, selectedDashboard, setSelectedDashboard } = useDashboard(); - const { featureResponse } = useSelector( - (state) => state.app, + const featureResponse = useSelector( + (state) => state.app.featureResponse, ); + const onToggleModal = useCallback( (func: Dispatch>) => { func((value) => !value); @@ -133,7 +137,7 @@ function WidgetGraphComponent({ i: uuid, w: 6, x: 0, - h: 2, + h: 3, y: 0, }, ]; @@ -186,8 +190,22 @@ function WidgetGraphComponent({ onToggleModal(setModal); }; + if (queryResponse.isLoading || queryResponse.status === 'idle') { + return ( + + ); + } + return ( - { setHovered(true); }} @@ -200,6 +218,7 @@ function WidgetGraphComponent({ onBlur={(): void => { setHovered(false); }} + id={name} > - - - +
@@ -252,29 +270,28 @@ function WidgetGraphComponent({
{queryResponse.isLoading && } {queryResponse.isSuccess && ( - +
+ +
)} -
+
); } WidgetGraphComponent.defaultProps = { yAxisUnit: undefined, setLayout: undefined, - onDragSelect: undefined, onClickHandler: undefined, }; diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx index 0d30cea491..ec72477ad2 100644 --- a/frontend/src/container/GridCardLayout/GridCard/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx @@ -1,12 +1,15 @@ import { PANEL_TYPES } from 'constants/queryBuilder'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useStepInterval } from 'hooks/queryBuilder/useStepInterval'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { useResizeObserver } from 'hooks/useDimensions'; +import { useIntersectionObserver } from 'hooks/useIntersectionObserver'; import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; -import getChartData from 'lib/getChartData'; +import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartData'; +import { getUPlotChartData } from 'lib/uPlotLib/utils/getChartData'; import isEmpty from 'lodash-es/isEmpty'; -import { useDashboard } from 'providers/Dashboard/Dashboard'; -import { memo, useMemo, useState } from 'react'; -import { useInView } from 'react-intersection-observer'; +import _noop from 'lodash-es/noop'; +import { memo, useCallback, useMemo, useRef, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { UpdateTimeInterval } from 'store/actions'; import { AppState } from 'store/reducers'; @@ -20,30 +23,30 @@ import WidgetGraphComponent from './WidgetGraphComponent'; function GridCardGraph({ widget, name, - onClickHandler, + onClickHandler = _noop, headerMenuList = [MenuItemKeys.View], isQueryEnabled, threshold, + variables, }: GridCardGraphProps): JSX.Element { const dispatch = useDispatch(); const [errorMessage, setErrorMessage] = useState(); - const onDragSelect = (start: number, end: number): void => { - const startTimestamp = Math.trunc(start); - const endTimestamp = Math.trunc(end); + const onDragSelect = useCallback( + (start: number, end: number): void => { + const startTimestamp = Math.trunc(start); + const endTimestamp = Math.trunc(end); - if (startTimestamp !== endTimestamp) { - dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp])); - } - }; + if (startTimestamp !== endTimestamp) { + dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp])); + } + }, + [dispatch], + ); - const { ref: graphRef, inView: isGraphVisible } = useInView({ - threshold: 0, - triggerOnce: true, - initialInView: false, - }); + const graphRef = useRef(null); - const { selectedDashboard } = useDashboard(); + const isVisible = useIntersectionObserver(graphRef, undefined, true); const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector< AppState, @@ -61,20 +64,20 @@ function GridCardGraph({ graphType: widget?.panelTypes, query: updatedQuery, globalSelectedInterval, - variables: getDashboardVariables(selectedDashboard?.data.variables), + variables: getDashboardVariables(variables), }, { queryKey: [ maxTime, minTime, globalSelectedInterval, - selectedDashboard?.data?.variables, + variables, widget?.query, widget?.panelTypes, widget.timePreferance, ], keepPreviousData: true, - enabled: isGraphVisible && !isEmptyWidget && isQueryEnabled, + enabled: isVisible && !isEmptyWidget && isQueryEnabled, refetchOnMount: false, onError: (error) => { setErrorMessage(error.message); @@ -82,39 +85,63 @@ function GridCardGraph({ }, ); - const chartData = useMemo( - () => - getChartData({ - queryData: [ - { - queryData: queryResponse?.data?.payload?.data?.result || [], - }, - ], - createDataset: undefined, - isWarningLimit: widget.panelTypes === PANEL_TYPES.TIME_SERIES, - }), - [queryResponse, widget?.panelTypes], - ); - const isEmptyLayout = widget?.id === PANEL_TYPES.EMPTY_WIDGET; - return ( - - + const containerDimensions = useResizeObserver(graphRef); - {isEmptyLayout && } - + const chartData = getUPlotChartData(queryResponse?.data?.payload); + + const isDarkMode = useIsDarkMode(); + + const menuList = + widget.panelTypes === PANEL_TYPES.TABLE + ? headerMenuList.filter((menu) => menu !== MenuItemKeys.CreateAlerts) + : headerMenuList; + + const options = useMemo( + () => + getUPlotChartOptions({ + id: widget?.id, + apiResponse: queryResponse.data?.payload, + dimensions: containerDimensions, + isDarkMode, + onDragSelect, + yAxisUnit: widget?.yAxisUnit, + onClickHandler, + thresholds: widget.thresholds, + }), + [ + widget?.id, + widget?.yAxisUnit, + widget.thresholds, + queryResponse.data?.payload, + containerDimensions, + isDarkMode, + onDragSelect, + onClickHandler, + ], + ); + + return ( +
+ {isEmptyLayout ? ( + + ) : ( + + )} +
); } diff --git a/frontend/src/container/GridCardLayout/GridCard/types.ts b/frontend/src/container/GridCardLayout/GridCard/types.ts index fccf488dc8..ca68ea8540 100644 --- a/frontend/src/container/GridCardLayout/GridCard/types.ts +++ b/frontend/src/container/GridCardLayout/GridCard/types.ts @@ -1,10 +1,12 @@ -import { ChartData } from 'chart.js'; -import { GraphOnClickHandler, ToggleGraphProps } from 'components/Graph/types'; +import { ToggleGraphProps } from 'components/Graph/types'; +import { UplotProps } from 'components/Uplot/Uplot'; +import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { MutableRefObject, ReactNode } from 'react'; import { UseQueryResult } from 'react-query'; import { ErrorResponse, SuccessResponse } from 'types/api'; -import { Widgets } from 'types/api/dashboard/getAll'; +import { Dashboard, Widgets } from 'types/api/dashboard/getAll'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; +import uPlot from 'uplot'; import { MenuItemKeys } from '../WidgetHeader/contants'; import { LegendEntryProps } from './FullView/types'; @@ -14,16 +16,15 @@ export interface GraphVisibilityLegendEntryProps { legendEntry: LegendEntryProps[]; } -export interface WidgetGraphComponentProps { +export interface WidgetGraphComponentProps extends UplotProps { widget: Widgets; queryResponse: UseQueryResult< SuccessResponse | ErrorResponse >; errorMessage: string | undefined; - data: ChartData; name: string; - onDragSelect?: (start: number, end: number) => void; - onClickHandler?: GraphOnClickHandler; + onDragSelect: (start: number, end: number) => void; + onClickHandler?: OnClickPluginOpts['onClick']; threshold?: ReactNode; headerMenuList: MenuItemKeys[]; isWarning: boolean; @@ -33,14 +34,15 @@ export interface GridCardGraphProps { widget: Widgets; name: string; onDragSelect?: (start: number, end: number) => void; - onClickHandler?: GraphOnClickHandler; + onClickHandler?: OnClickPluginOpts['onClick']; threshold?: ReactNode; headerMenuList?: WidgetGraphComponentProps['headerMenuList']; isQueryEnabled: boolean; + variables?: Dashboard['data']['variables']; } export interface GetGraphVisibilityStateOnLegendClickProps { - data: ChartData; + options: uPlot.Options; isExpandedName: boolean; name: string; } diff --git a/frontend/src/container/GridCardLayout/GridCard/utils.ts b/frontend/src/container/GridCardLayout/GridCard/utils.ts index a97c1fa403..ebf9fa612e 100644 --- a/frontend/src/container/GridCardLayout/GridCard/utils.ts +++ b/frontend/src/container/GridCardLayout/GridCard/utils.ts @@ -1,3 +1,4 @@ +/* eslint-disable sonarjs/cognitive-complexity */ import { LOCALSTORAGE } from 'constants/localStorage'; import { LegendEntryProps } from './FullView/types'; @@ -9,13 +10,13 @@ import { } from './types'; export const getGraphVisibilityStateOnDataChange = ({ - data, + options, isExpandedName, name, }: GetGraphVisibilityStateOnLegendClickProps): GraphVisibilityLegendEntryProps => { const visibilityStateAndLegendEntry: GraphVisibilityLegendEntryProps = { - graphVisibilityStates: Array(data.datasets.length).fill(true), - legendEntry: showAllDataSet(data), + graphVisibilityStates: Array(options.series.length).fill(true), + legendEntry: showAllDataSet(options), }; if (localStorage.getItem(LOCALSTORAGE.GRAPH_VISIBILITY_STATES) !== null) { const legendGraphFromLocalStore = localStorage.getItem( @@ -35,17 +36,19 @@ export const getGraphVisibilityStateOnDataChange = ({ ); } - const newGraphVisibilityStates = Array(data.datasets.length).fill(true); + const newGraphVisibilityStates = Array(options.series.length).fill(true); legendFromLocalStore.forEach((item) => { const newName = isExpandedName ? `${name}expanded` : name; if (item.name === newName) { visibilityStateAndLegendEntry.legendEntry = item.dataIndex; - data.datasets.forEach((datasets, i) => { - const index = item.dataIndex.findIndex( - (dataKey) => dataKey.label === datasets.label, - ); - if (index !== -1) { - newGraphVisibilityStates[i] = item.dataIndex[index].show; + options.series.forEach((datasets, i) => { + if (i !== 0) { + const index = item.dataIndex.findIndex( + (dataKey) => dataKey.label === datasets.label, + ); + if (index !== -1) { + newGraphVisibilityStates[i] = item.dataIndex[index].show; + } } }); visibilityStateAndLegendEntry.graphVisibilityStates = newGraphVisibilityStates; diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.tsx b/frontend/src/container/GridCardLayout/GridCardLayout.tsx index 57b1f4222d..a6b1186880 100644 --- a/frontend/src/container/GridCardLayout/GridCardLayout.tsx +++ b/frontend/src/container/GridCardLayout/GridCardLayout.tsx @@ -11,8 +11,10 @@ import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; import { Dashboard, Widgets } from 'types/api/dashboard/getAll'; import AppReducer from 'types/reducer/app'; +import { ROLES, USER_ROLES } from 'types/roles'; +import { ComponentTypes } from 'utils/permission'; -import { headerMenuList } from './config'; +import { EditMenuAction, ViewMenuAction } from './config'; import GridCard from './GridCard'; import { Button, @@ -23,19 +25,21 @@ import { } from './styles'; import { GraphLayoutProps } from './types'; -function GraphLayout({ - onAddPanelHandler, - widgets, -}: GraphLayoutProps): JSX.Element { +function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element { const { selectedDashboard, layouts, setLayouts, setSelectedDashboard, + isDashboardLocked, } = useDashboard(); + const { data } = selectedDashboard || {}; + + const { widgets, variables } = data || {}; + const { t } = useTranslation(['dashboard']); - const { featureResponse, role } = useSelector( + const { featureResponse, role, user } = useSelector( (state) => state.app, ); @@ -45,9 +49,20 @@ function GraphLayout({ const { notifications } = useNotifications(); + let permissions: ComponentTypes[] = ['save_layout', 'add_panel']; + + if (isDashboardLocked) { + permissions = ['edit_locked_dashboard', 'add_panel_locked_dashboard']; + } + + const userRole: ROLES | null = + selectedDashboard?.created_by === user?.email + ? (USER_ROLES.AUTHOR as ROLES) + : role; + const [saveLayoutPermission, addPanelPermission] = useComponentPermission( - ['save_layout', 'add_panel'], - role, + permissions, + userRole, ); const onSaveHandler = (): void => { @@ -83,35 +98,42 @@ function GraphLayout({ }); }; + const widgetActions = !isDashboardLocked + ? [...ViewMenuAction, ...EditMenuAction] + : [...ViewMenuAction]; + return ( <> - - {saveLayoutPermission && ( - - )} + {!isDashboardLocked && ( + + {saveLayoutPermission && ( + + )} - {addPanelPermission && ( - - )} - + {addPanelPermission && ( + + )} + + )} e.id === id); return ( - - + + diff --git a/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx index a916534082..9d48113910 100644 --- a/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx +++ b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx @@ -1,4 +1,5 @@ import { + AlertOutlined, CopyOutlined, DeleteOutlined, DownOutlined, @@ -157,7 +158,7 @@ function WidgetHeader({ }, { key: MenuItemKeys.CreateAlerts, - icon: , + icon: , label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.CreateAlerts], isVisible: headerMenuList?.includes(MenuItemKeys.CreateAlerts) || false, disabled: false, @@ -168,9 +169,9 @@ function WidgetHeader({ const updatedMenuList = useMemo(() => generateMenuList(actions), [actions]); - const onClickHandler = useCallback(() => { - setIsOpen((open) => !open); - }, []); + const onClickHandler = (): void => { + setIsOpen(!isOpen); + }; const menu = useMemo( () => ({ diff --git a/frontend/src/container/GridCardLayout/WidgetHeader/styles.ts b/frontend/src/container/GridCardLayout/WidgetHeader/styles.ts index dd08ea8eb2..71d01fe6e5 100644 --- a/frontend/src/container/GridCardLayout/WidgetHeader/styles.ts +++ b/frontend/src/container/GridCardLayout/WidgetHeader/styles.ts @@ -5,10 +5,8 @@ import styled from 'styled-components'; export const HeaderContainer = styled.div<{ hover: boolean }>` width: 100%; text-align: center; - background: ${({ hover }): string => (hover ? `${grey[0]}66` : 'inherit')}; padding: 0.25rem 0; font-size: 0.8rem; - cursor: all-scroll; position: absolute; top: 0; left: 0; @@ -20,12 +18,6 @@ export const HeaderContentContainer = styled.span` text-align: center; `; -export const ArrowContainer = styled.span<{ hover: boolean }>` - visibility: ${({ hover }): string => (hover ? 'visible' : 'hidden')}; - position: absolute; - right: -1rem; -`; - export const ThesholdContainer = styled.span` margin-top: -0.3rem; `; @@ -39,8 +31,18 @@ export const DisplayThresholdContainer = styled.div` export const WidgetHeaderContainer = styled.div` display: flex; - flex-direction: row-reverse; align-items: center; + justify-content: flex-end; + align-items: center; + height: 30px; + width: 100%; + left: 0; +`; + +export const ArrowContainer = styled.span<{ hover: boolean }>` + visibility: ${({ hover }): string => (hover ? 'visible' : 'hidden')}; + position: absolute; + right: -1rem; `; export const Typography = styled(TypographyComponent)` diff --git a/frontend/src/container/GridCardLayout/config.ts b/frontend/src/container/GridCardLayout/config.ts index 3fa9e8e569..2801913df8 100644 --- a/frontend/src/container/GridCardLayout/config.ts +++ b/frontend/src/container/GridCardLayout/config.ts @@ -1,17 +1,21 @@ import { PANEL_TYPES } from 'constants/queryBuilder'; import { MenuItemKeys } from 'container/GridCardLayout/WidgetHeader/contants'; -export const headerMenuList = [ - MenuItemKeys.View, +export const ViewMenuAction = [MenuItemKeys.View]; + +export const EditMenuAction = [ MenuItemKeys.Clone, MenuItemKeys.Delete, MenuItemKeys.Edit, + MenuItemKeys.CreateAlerts, ]; +export const headerMenuList = [...ViewMenuAction]; + export const EMPTY_WIDGET_LAYOUT = { i: PANEL_TYPES.EMPTY_WIDGET, w: 6, x: 0, - h: 2, + h: 3, y: 0, }; diff --git a/frontend/src/container/GridCardLayout/index.tsx b/frontend/src/container/GridCardLayout/index.tsx index e715d7d539..a54daa313c 100644 --- a/frontend/src/container/GridCardLayout/index.tsx +++ b/frontend/src/container/GridCardLayout/index.tsx @@ -6,14 +6,7 @@ import { EMPTY_WIDGET_LAYOUT } from './config'; import GraphLayoutContainer from './GridCardLayout'; function GridGraph(): JSX.Element { - const { - selectedDashboard, - setLayouts, - handleToggleDashboardSlider, - } = useDashboard(); - - const { data } = selectedDashboard || {}; - const { widgets } = data || {}; + const { handleToggleDashboardSlider, setLayouts } = useDashboard(); const onEmptyWidgetHandler = useCallback(() => { handleToggleDashboardSlider(true); @@ -24,12 +17,7 @@ function GridGraph(): JSX.Element { ]); }, [handleToggleDashboardSlider, setLayouts]); - return ( - - ); + return ; } export default GridGraph; diff --git a/frontend/src/container/GridCardLayout/styles.ts b/frontend/src/container/GridCardLayout/styles.ts index a08ffdc361..79b7525b34 100644 --- a/frontend/src/container/GridCardLayout/styles.ts +++ b/frontend/src/container/GridCardLayout/styles.ts @@ -13,10 +13,11 @@ interface CardProps { export const Card = styled(CardComponent)` &&& { height: 100%; + overflow: hidden; } .ant-card-body { - height: 95%; + height: 90%; padding: 0; ${({ $panelType }): FlattenSimpleInterpolation => $panelType === PANEL_TYPES.TABLE @@ -34,29 +35,31 @@ interface Props { export const CardContainer = styled.div` overflow: auto; - :hover { - .react-resizable-handle { - position: absolute; - width: 20px; - height: 20px; - bottom: 0; - right: 0; - background-position: bottom right; - padding: 0 3px 3px 0; - background-repeat: no-repeat; - background-origin: content-box; - box-sizing: border-box; - cursor: se-resize; + &.enable-resize { + :hover { + .react-resizable-handle { + position: absolute; + width: 20px; + height: 20px; + bottom: 0; + right: 0; + background-position: bottom right; + padding: 0 3px 3px 0; + background-repeat: no-repeat; + background-origin: content-box; + box-sizing: border-box; + cursor: se-resize; - ${({ isDarkMode }): StyledCSS => { - const uri = `data:image/svg+xml,%3Csvg viewBox='0 0 6 6' style='background-color:%23ffffff00' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' xml:space='preserve' x='0px' y='0px' width='6px' height='6px'%0A%3E%3Cg opacity='0.302'%3E%3Cpath d='M 6 6 L 0 6 L 0 4.2 L 4 4.2 L 4.2 4.2 L 4.2 0 L 6 0 L 6 6 L 6 6 Z' fill='${ - isDarkMode ? 'white' : 'grey' - }'/%3E%3C/g%3E%3C/svg%3E`; + ${({ isDarkMode }): StyledCSS => { + const uri = `data:image/svg+xml,%3Csvg viewBox='0 0 6 6' style='background-color:%23ffffff00' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' xml:space='preserve' x='0px' y='0px' width='6px' height='6px'%0A%3E%3Cg opacity='0.302'%3E%3Cpath d='M 6 6 L 0 6 L 0 4.2 L 4 4.2 L 4.2 4.2 L 4.2 0 L 6 0 L 6 6 L 6 6 Z' fill='${ + isDarkMode ? 'white' : 'grey' + }'/%3E%3C/g%3E%3C/svg%3E`; - return css` - background-image: ${(): string => `url("${uri}")`}; - `; - }} + return css` + background-image: ${(): string => `url("${uri}")`}; + `; + }} + } } } `; diff --git a/frontend/src/container/GridCardLayout/types.ts b/frontend/src/container/GridCardLayout/types.ts index 0d2b678af6..a0e8e9aa13 100644 --- a/frontend/src/container/GridCardLayout/types.ts +++ b/frontend/src/container/GridCardLayout/types.ts @@ -1,6 +1,3 @@ -import { Widgets } from 'types/api/dashboard/getAll'; - export interface GraphLayoutProps { onAddPanelHandler: VoidFunction; - widgets?: Widgets[]; } diff --git a/frontend/src/container/GridPanelSwitch/index.tsx b/frontend/src/container/GridPanelSwitch/index.tsx index 60f0a618fa..511bf449e9 100644 --- a/frontend/src/container/GridPanelSwitch/index.tsx +++ b/frontend/src/container/GridPanelSwitch/index.tsx @@ -11,39 +11,20 @@ const GridPanelSwitch = forwardRef< GridPanelSwitchProps >( ( - { - panelType, - data, - title, - isStacked, - onClickHandler, - name, - yAxisUnit, - staticLine, - onDragSelect, - panelData, - query, - }, + { panelType, data, yAxisUnit, panelData, query, options, thresholds }, ref, ): JSX.Element | null => { const currentProps: PropsTypePropsMap = useMemo(() => { const result: PropsTypePropsMap = { [PANEL_TYPES.TIME_SERIES]: { - type: 'line', data, - title, - isStacked, - onClickHandler, - name, - yAxisUnit, - staticLine, - onDragSelect, + options, ref, }, [PANEL_TYPES.VALUE]: { - title, data, yAxisUnit, + thresholds, }, [PANEL_TYPES.TABLE]: { ...GRID_TABLE_CONFIG, data: panelData, query }, [PANEL_TYPES.LIST]: null, @@ -52,19 +33,7 @@ const GridPanelSwitch = forwardRef< }; return result; - }, [ - data, - isStacked, - name, - onClickHandler, - onDragSelect, - staticLine, - title, - yAxisUnit, - panelData, - query, - ref, - ]); + }, [data, options, ref, yAxisUnit, thresholds, panelData, query]); const Component = PANEL_TYPES_COMPONENT_MAP[panelType] as FC< PropsTypePropsMap[typeof panelType] diff --git a/frontend/src/container/GridPanelSwitch/types.ts b/frontend/src/container/GridPanelSwitch/types.ts index c7703aee56..df09d4b59d 100644 --- a/frontend/src/container/GridPanelSwitch/types.ts +++ b/frontend/src/container/GridPanelSwitch/types.ts @@ -1,34 +1,34 @@ -import { ChartData } from 'chart.js'; -import { - GraphOnClickHandler, - GraphProps, - StaticLineProps, -} from 'components/Graph/types'; +import { StaticLineProps, ToggleGraphProps } from 'components/Graph/types'; +import { UplotProps } from 'components/Uplot/Uplot'; import { GridTableComponentProps } from 'container/GridTableComponent/types'; import { GridValueComponentProps } from 'container/GridValueComponent/types'; +import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; +import { ForwardedRef } from 'react'; import { Widgets } from 'types/api/dashboard/getAll'; import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { QueryDataV3 } from 'types/api/widgets/getQuery'; +import uPlot from 'uplot'; import { PANEL_TYPES } from '../../constants/queryBuilder'; export type GridPanelSwitchProps = { panelType: PANEL_TYPES; - data: ChartData; - title?: Widgets['title']; - opacity?: string; - isStacked?: boolean; - onClickHandler?: GraphOnClickHandler; + data: uPlot.AlignedData; + options: uPlot.Options; + onClickHandler?: OnClickPluginOpts['onClick']; name: string; yAxisUnit?: string; staticLine?: StaticLineProps; onDragSelect?: (start: number, end: number) => void; panelData: QueryDataV3[]; query: Query; + thresholds?: Widgets['thresholds']; }; export type PropsTypePropsMap = { - [PANEL_TYPES.TIME_SERIES]: GraphProps; + [PANEL_TYPES.TIME_SERIES]: UplotProps & { + ref: ForwardedRef; + }; [PANEL_TYPES.VALUE]: GridValueComponentProps; [PANEL_TYPES.TABLE]: GridTableComponentProps; [PANEL_TYPES.TRACE]: null; diff --git a/frontend/src/container/GridValueComponent/index.tsx b/frontend/src/container/GridValueComponent/index.tsx index be9a3890ba..21e1c5ee60 100644 --- a/frontend/src/container/GridValueComponent/index.tsx +++ b/frontend/src/container/GridValueComponent/index.tsx @@ -12,17 +12,18 @@ function GridValueComponent({ data, title, yAxisUnit, + thresholds, }: GridValueComponentProps): JSX.Element { - const value = (((data.datasets[0] || []).data || [])[0] || 0) as number; + const value = ((data[1] || [])[0] || 0) as number; const location = useLocation(); const gridTitle = useMemo(() => generateGridTitle(title), [title]); const isDashboardPage = location.pathname.split('/').length === 3; - if (data.datasets.length === 0) { + if (data.length === 0) { return ( - + No Data ); @@ -33,8 +34,10 @@ function GridValueComponent({ {gridTitle} - + ` - height: ${({ isDashboardPage }): string => - isDashboardPage ? '100%' : '55vh'}; + +export const ValueContainer = styled.div` + height: 100%; display: flex; justify-content: center; align-items: center; diff --git a/frontend/src/container/GridValueComponent/types.ts b/frontend/src/container/GridValueComponent/types.ts index 94c3c04d4d..709cb3de28 100644 --- a/frontend/src/container/GridValueComponent/types.ts +++ b/frontend/src/container/GridValueComponent/types.ts @@ -1,8 +1,10 @@ -import { ChartData } from 'chart.js'; -import { ReactNode } from 'react'; +import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types'; +import uPlot from 'uplot'; export type GridValueComponentProps = { - data: ChartData; - title?: ReactNode; + data: uPlot.AlignedData; + options?: uPlot.Options; + title?: React.ReactNode; yAxisUnit?: string; + thresholds?: ThresholdProps[]; }; diff --git a/frontend/src/container/Header/index.tsx b/frontend/src/container/Header/index.tsx index a930143a9c..c7eb709452 100644 --- a/frontend/src/container/Header/index.tsx +++ b/frontend/src/container/Header/index.tsx @@ -104,7 +104,7 @@ function HeaderContainer(): JSX.Element { ); }; - const { data: licenseData, isFetching } = useLicense(); + const { data: licenseData, isFetching, status: licenseStatus } = useLicense(); const isLicenseActive = licenseData?.payload?.licenses?.find((e) => e.isCurrent)?.status === @@ -169,7 +169,7 @@ function HeaderContainer(): JSX.Element { - {!isLicenseActive && ( + {licenseStatus === 'success' && !isLicenseActive && ( diff --git a/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx b/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx index c68b0c2617..b6d51445f8 100644 --- a/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx +++ b/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx @@ -1,18 +1,27 @@ -import { ExclamationCircleOutlined } from '@ant-design/icons'; -import { Modal } from 'antd'; +import { DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons'; +import { Modal, Tooltip } from 'antd'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import { useDeleteDashboard } from 'hooks/dashboard/useDeleteDashboard'; import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; import { useQueryClient } from 'react-query'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import AppReducer from 'types/reducer/app'; +import { USER_ROLES } from 'types/roles'; import { Data } from '../index'; import { TableLinkText } from './styles'; -function DeleteButton({ id }: Data): JSX.Element { +function DeleteButton({ id, createdBy, isLocked }: Data): JSX.Element { const [modal, contextHolder] = Modal.useModal(); + const { role, user } = useSelector((state) => state.app); + const isAuthor = user?.email === createdBy; const queryClient = useQueryClient(); + const { t } = useTranslation(['dashboard']); + const deleteDashboardMutation = useDeleteDashboard(id); const openConfirmationDialog = useCallback((): void => { @@ -32,11 +41,33 @@ function DeleteButton({ id }: Data): JSX.Element { }); }, [modal, deleteDashboardMutation, queryClient]); + const getDeleteTooltipContent = (): string => { + if (isLocked) { + if (role === USER_ROLES.ADMIN || isAuthor) { + return t('dashboard:locked_dashboard_delete_tooltip_admin_author'); + } + + return t('dashboard:locked_dashboard_delete_tooltip_editor'); + } + + return ''; + }; + return ( <> - - Delete - + + { + if (!isLocked) { + openConfirmationDialog(); + } + }} + disabled={isLocked} + > + Delete + + {contextHolder} @@ -55,6 +86,7 @@ function Wrapper(props: Data): JSX.Element { tags, createdBy, lastUpdatedBy, + isLocked, } = props; return ( @@ -69,6 +101,7 @@ function Wrapper(props: Data): JSX.Element { tags, createdBy, lastUpdatedBy, + isLocked, }} /> ); diff --git a/frontend/src/container/ListOfDashboard/TableComponents/Name.tsx b/frontend/src/container/ListOfDashboard/TableComponents/Name.tsx index af53580926..a3f3427b6c 100644 --- a/frontend/src/container/ListOfDashboard/TableComponents/Name.tsx +++ b/frontend/src/container/ListOfDashboard/TableComponents/Name.tsx @@ -1,22 +1,28 @@ +import { LockFilled } from '@ant-design/icons'; import ROUTES from 'constants/routes'; import history from 'lib/history'; -import { generatePath } from 'react-router-dom'; import { Data } from '..'; import { TableLinkText } from './styles'; function Name(name: Data['name'], data: Data): JSX.Element { - const onClickHandler = (): void => { - const { id: DashboardId } = data; + const { id: DashboardId, isLocked } = data; - history.push( - generatePath(ROUTES.DASHBOARD, { - dashboardId: DashboardId, - }), - ); + const getLink = (): string => `${ROUTES.ALL_DASHBOARD}/${DashboardId}`; + + const onClickHandler = (event: React.MouseEvent): void => { + if (event.metaKey || event.ctrlKey) { + window.open(getLink(), '_blank'); + } else { + history.push(getLink()); + } }; - return {name}; + return ( + + {isLocked && } {name} + + ); } export default Name; diff --git a/frontend/src/container/ListOfDashboard/index.tsx b/frontend/src/container/ListOfDashboard/index.tsx index cf3fa02a80..a310c5be63 100644 --- a/frontend/src/container/ListOfDashboard/index.tsx +++ b/frontend/src/container/ListOfDashboard/index.tsx @@ -130,7 +130,7 @@ function ListOfAllDashboard(): JSX.Element { dataIndex: 'description', }, { - title: 'Tags (can be multiple)', + title: 'Tags', dataIndex: 'tags', width: 50, render: (value): JSX.Element => , @@ -159,6 +159,7 @@ function ListOfAllDashboard(): JSX.Element { tags: e.data.tags || [], key: e.uuid, createdBy: e.created_by, + isLocked: !!e.isLocked || false, lastUpdatedBy: e.updated_by, refetchDashboardList, })) || []; @@ -342,6 +343,7 @@ export interface Data { createdAt: string; lastUpdatedTime: string; lastUpdatedBy: string; + isLocked: boolean; id: string; } diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx index 15af9981c0..13d954de4b 100644 --- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx @@ -16,7 +16,7 @@ import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; import { EQueryType } from 'types/common/dashboard'; import { v4 as uuid } from 'uuid'; -import { GraphTitle } from '../constant'; +import { GraphTitle, MENU_ITEMS } from '../constant'; import { getWidgetQueryBuilder } from '../MetricsApplication.factory'; import { Card, GraphContainer, Row } from '../styles'; import { Button } from './styles'; @@ -104,17 +104,17 @@ function DBCall(): JSX.Element { > View Traces - + { + onClickHandler={(xValue, yValue, mouseX, mouseY): void => { onGraphClickHandler(setSelectedTimeStamp)( - ChartEvent, - activeElements, - chart, - data, + xValue, + yValue, + mouseX, + mouseY, 'database_call_rps', ); }} @@ -137,17 +137,18 @@ function DBCall(): JSX.Element { View Traces - + { + headerMenuList={MENU_ITEMS} + onClickHandler={(xValue, yValue, mouseX, mouseY): void => { onGraphClickHandler(setSelectedTimeStamp)( - ChartEvent, - activeElements, - chart, - data, + xValue, + yValue, + mouseX, + mouseY, 'database_call_avg_duration', ); }} diff --git a/frontend/src/container/MetricsApplication/Tabs/External.tsx b/frontend/src/container/MetricsApplication/Tabs/External.tsx index cda6e9275c..c02f4f1ce7 100644 --- a/frontend/src/container/MetricsApplication/Tabs/External.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/External.tsx @@ -17,7 +17,7 @@ import { useParams } from 'react-router-dom'; import { EQueryType } from 'types/common/dashboard'; import { v4 as uuid } from 'uuid'; -import { GraphTitle, legend } from '../constant'; +import { GraphTitle, legend, MENU_ITEMS } from '../constant'; import { getWidgetQueryBuilder } from '../MetricsApplication.factory'; import { Card, GraphContainer, Row } from '../styles'; import { Button } from './styles'; @@ -145,17 +145,18 @@ function External(): JSX.Element { > View Traces - + { + onClickHandler={(xValue, yValue, mouseX, mouseY): void => { onGraphClickHandler(setSelectedTimeStamp)( - ChartEvent, - activeElements, - chart, - data, + xValue, + yValue, + mouseX, + mouseY, 'external_call_error_percentage', ); }} @@ -179,17 +180,18 @@ function External(): JSX.Element { View Traces - + { + onClickHandler={(xValue, yValue, mouseX, mouseY): void => { onGraphClickHandler(setSelectedTimeStamp)( - ChartEvent, - activeElements, - chart, - data, + xValue, + yValue, + mouseX, + mouseY, 'external_call_duration', ); }} @@ -214,20 +216,21 @@ function External(): JSX.Element { > View Traces - + { + headerMenuList={MENU_ITEMS} + onClickHandler={(xValue, yValue, mouseX, mouseY): Promise => onGraphClickHandler(setSelectedTimeStamp)( - ChartEvent, - activeElements, - chart, - data, + xValue, + yValue, + mouseX, + mouseY, 'external_call_rps_by_address', - ); - }} + ) + } /> @@ -248,17 +251,18 @@ function External(): JSX.Element { View Traces - + { + headerMenuList={MENU_ITEMS} + onClickHandler={(xValue, yValue, mouseX, mouseY): void => { onGraphClickHandler(setSelectedTimeStamp)( - ChartEvent, - activeElements, - chart, - data, + xValue, + yValue, + mouseX, + mouseY, 'external_call_duration_by_address', ); }} diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index b91d1b1175..ff6460e3d7 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -1,7 +1,6 @@ import getTopLevelOperations, { ServiceDataProps, } from 'api/metrics/getTopLevelOperations'; -import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js'; import { FeatureKeys } from 'constants/features'; import { QueryParams } from 'constants/query'; import { PANEL_TYPES } from 'constants/queryBuilder'; @@ -15,6 +14,7 @@ import { resourceAttributesToTagFilterItems, } from 'hooks/useResourceAttribute/utils'; import history from 'lib/history'; +import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { useCallback, useMemo, useState } from 'react'; import { useQuery } from 'react-query'; import { useDispatch, useSelector } from 'react-redux'; @@ -73,20 +73,19 @@ function Application(): JSX.Element { const dispatch = useDispatch(); const handleGraphClick = useCallback( - (type: string): ClickHandlerType => ( - ChartEvent: ChartEvent, - activeElements: ActiveElement[], - chart: Chart, - data: ChartData, - ): void => { + (type: string): OnClickPluginOpts['onClick'] => ( + xValue, + yValue, + mouseX, + mouseY, + ): Promise => onGraphClickHandler(handleSetTimeStamp)( - ChartEvent, - activeElements, - chart, - data, + xValue, + yValue, + mouseX, + mouseY, type, - ); - }, + ), [handleSetTimeStamp], ); @@ -283,12 +282,6 @@ function Application(): JSX.Element { ); } -export type ClickHandlerType = ( - ChartEvent: ChartEvent, - activeElements: ActiveElement[], - chart: Chart, - data: ChartData, - type?: string, -) => void; +export type ClickHandlerType = () => void; export default Application; diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx index ade8a1bec3..ac4ae1e03b 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx @@ -77,7 +77,7 @@ function ApDexMetrics({ const isQueryEnabled = topLevelOperationsRoute.length > 0 && - metricsBuckets && + !!metricsBuckets && metricsBuckets?.length > 0 && delta !== undefined; diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetricsApplication.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetricsApplication.tsx index 0e1486b852..542a1e9e8d 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetricsApplication.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetricsApplication.tsx @@ -10,8 +10,8 @@ function ApDexMetricsApplication({ handleGraphClick, onDragSelect, tagFilterItems, - topLevelOperationsRoute, thresholdValue, + topLevelOperationsRoute, }: ApDexDataSwitcherProps): JSX.Element { const { data, isLoading, error } = useGetMetricMeta(metricMeta); useErrorNotification(error); @@ -22,11 +22,11 @@ function ApDexMetricsApplication({ return ( diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/index.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/index.tsx index f69e871bb6..d5e33435d8 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/index.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/index.tsx @@ -30,7 +30,7 @@ function ApDexApplication({ } return ( - + ClickHandlerType; + handleGraphClick: (type: string) => OnClickPluginOpts['onClick']; onDragSelect: (start: number, end: number) => void; topLevelOperationsRoute: string[]; tagFilterItems: TagFilterItem[]; diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx index 047fb9c4c4..b97a631b55 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx @@ -8,12 +8,12 @@ import { Card, GraphContainer } from 'container/MetricsApplication/styles'; import useFeatureFlag from 'hooks/useFeatureFlag'; import useResourceAttribute from 'hooks/useResourceAttribute'; import { resourceAttributesToTagFilterItems } from 'hooks/useResourceAttribute/utils'; +import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { useMemo } from 'react'; import { useParams } from 'react-router-dom'; import { EQueryType } from 'types/common/dashboard'; import { v4 as uuid } from 'uuid'; -import { ClickHandlerType } from '../Overview'; import { Button } from '../styles'; import { IServiceName } from '../types'; import { handleNonInQueryRange, onViewTracePopupClick } from '../util'; @@ -80,7 +80,7 @@ function ServiceOverview({ > View Traces - + void; - handleGraphClick: (type: string) => ClickHandlerType; + handleGraphClick: (type: string) => OnClickPluginOpts['onClick']; topLevelOperationsRoute: string[]; topLevelOperationsIsLoading: boolean; } diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx index 2bc02ee13c..1dbbf2ee98 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx @@ -3,10 +3,9 @@ import axios from 'axios'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import Graph from 'container/GridCardLayout/GridCard'; import { Card, GraphContainer } from 'container/MetricsApplication/styles'; +import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { Widgets } from 'types/api/dashboard/getAll'; -import { ClickHandlerType } from '../Overview'; - function TopLevelOperation({ name, opName, @@ -18,7 +17,7 @@ function TopLevelOperation({ topLevelOperationsIsLoading, }: TopLevelOperationProps): JSX.Element { return ( - + {topLevelOperationsIsError ? ( {axios.isAxiosError(topLevelOperationsError) @@ -46,7 +45,7 @@ interface TopLevelOperationProps { topLevelOperationsIsError: boolean; topLevelOperationsError: unknown; onDragSelect: (start: number, end: number) => void; - handleGraphClick: (type: string) => ClickHandlerType; + handleGraphClick: (type: string) => OnClickPluginOpts['onClick']; widget: Widgets; topLevelOperationsIsLoading: boolean; } diff --git a/frontend/src/container/MetricsApplication/Tabs/util.ts b/frontend/src/container/MetricsApplication/Tabs/util.ts index b33bbf0767..c44e7462f4 100644 --- a/frontend/src/container/MetricsApplication/Tabs/util.ts +++ b/frontend/src/container/MetricsApplication/Tabs/util.ts @@ -1,4 +1,3 @@ -import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js'; import { QueryParams } from 'constants/query'; import ROUTES from 'constants/routes'; import { routeConfig } from 'container/SideNav/config'; @@ -32,7 +31,8 @@ export function onViewTracePopupClick({ }: OnViewTracePopupClickProps): VoidFunction { return (): void => { const currentTime = timestamp; - const tPlusOne = timestamp + 60 * 1000; + + const tPlusOne = timestamp + 60; const urlParams = new URLSearchParams(window.location.search); urlParams.set(QueryParams.startTime, currentTime.toString()); @@ -54,37 +54,25 @@ export function onGraphClickHandler( setSelectedTimeStamp: (n: number) => void | Dispatch>, ) { return async ( - event: ChartEvent, - elements: ActiveElement[], - chart: Chart, - data: ChartData, - from: string, + xValue: number, + yValue: number, + mouseX: number, + mouseY: number, + type: string, ): Promise => { - if (event.native) { - const points = chart.getElementsAtEventForMode( - event.native, - 'nearest', - { intersect: false }, - true, - ); - const id = `${from}_button`; - const buttonElement = document.getElementById(id); + const id = `${type}_button`; - if (points.length !== 0) { - const firstPoint = points[0]; + const buttonElement = document.getElementById(id); - if (data.labels) { - const time = data?.labels[firstPoint.index] as Date; - if (buttonElement) { - buttonElement.style.display = 'block'; - buttonElement.style.left = `${firstPoint.element.x}px`; - buttonElement.style.top = `${firstPoint.element.y}px`; - setSelectedTimeStamp(time.getTime()); - } - } - } else if (buttonElement && buttonElement.style.display === 'block') { - buttonElement.style.display = 'none'; + if (xValue) { + if (buttonElement) { + buttonElement.style.display = 'block'; + buttonElement.style.left = `${mouseX}px`; + buttonElement.style.top = `${mouseY}px`; + setSelectedTimeStamp(xValue); } + } else if (buttonElement && buttonElement.style.display === 'block') { + buttonElement.style.display = 'none'; } }; } diff --git a/frontend/src/container/MetricsApplication/constant.ts b/frontend/src/container/MetricsApplication/constant.ts index 2949ee4c8d..a6d923c470 100644 --- a/frontend/src/container/MetricsApplication/constant.ts +++ b/frontend/src/container/MetricsApplication/constant.ts @@ -1,4 +1,5 @@ import { DownloadOptions } from 'container/Download/Download.types'; +import { MenuItemKeys } from 'container/GridCardLayout/WidgetHeader/contants'; export const legend = { address: '{{address}}', @@ -13,6 +14,8 @@ export const LATENCY_AGGREGATEOPERATOR_SPAN_METRICS = [ ]; export const OPERATION_LEGENDS = ['Operations']; +export const MENU_ITEMS = [MenuItemKeys.View, MenuItemKeys.CreateAlerts]; + export enum FORMULA { ERROR_PERCENTAGE = 'A*100/B', DATABASE_CALLS_AVG_DURATION = 'A/B', @@ -21,6 +24,8 @@ export enum FORMULA { APDEX_CUMULATIVE_SPAN_METRICS = '((B + C)/2)/A', } +export const TOP_LEVEL_OPERATIONS = ['{{.top_level_operations}}']; + export enum GraphTitle { APDEX = 'Apdex', LATENCY = 'Latency', diff --git a/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx b/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx index c9c1c6dde1..1a111cf4fe 100644 --- a/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx +++ b/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx @@ -45,7 +45,7 @@ function DashboardGraphSlider(): JSX.Element { i: id, w: 6, x: 0, - h: 2, + h: 3, y: 0, }, ...(layouts.filter((layout) => layout.i !== PANEL_TYPES.EMPTY_WIDGET) || diff --git a/frontend/src/container/NewDashboard/ComponentsSlider/styles.ts b/frontend/src/container/NewDashboard/ComponentsSlider/styles.ts index d7d3c6a7f2..29abafa3d5 100644 --- a/frontend/src/container/NewDashboard/ComponentsSlider/styles.ts +++ b/frontend/src/container/NewDashboard/ComponentsSlider/styles.ts @@ -3,11 +3,13 @@ import styled from 'styled-components'; export const Container = styled.div` display: flex; - gap: 0.6rem; + justify-content: right; + gap: 8px; `; export const Card = styled(CardComponent)` min-height: 10vh; + min-width: 120px; overflow-y: auto; cursor: pointer; diff --git a/frontend/src/container/NewDashboard/DescriptionOfDashboard/NameOfTheDashboard/index.tsx b/frontend/src/container/NewDashboard/DashboardDescription/DashboardName/index.tsx similarity index 72% rename from frontend/src/container/NewDashboard/DescriptionOfDashboard/NameOfTheDashboard/index.tsx rename to frontend/src/container/NewDashboard/DashboardDescription/DashboardName/index.tsx index 32f78d7ec2..53069c78aa 100644 --- a/frontend/src/container/NewDashboard/DescriptionOfDashboard/NameOfTheDashboard/index.tsx +++ b/frontend/src/container/NewDashboard/DashboardDescription/DashboardName/index.tsx @@ -1,10 +1,7 @@ import Input from 'components/Input'; import { ChangeEvent, Dispatch, SetStateAction, useCallback } from 'react'; -function NameOfTheDashboard({ - setName, - name, -}: NameOfTheDashboardProps): JSX.Element { +function DashboardName({ setName, name }: DashboardNameProps): JSX.Element { const onChangeHandler = useCallback( (e: ChangeEvent) => { setName(e.target.value); @@ -22,9 +19,9 @@ function NameOfTheDashboard({ ); } -interface NameOfTheDashboardProps { +interface DashboardNameProps { name: string; setName: Dispatch>; } -export default NameOfTheDashboard; +export default DashboardName; diff --git a/frontend/src/container/NewDashboard/DashboardDescription/Description.styles.scss b/frontend/src/container/NewDashboard/DashboardDescription/Description.styles.scss new file mode 100644 index 0000000000..a04f57f3f8 --- /dev/null +++ b/frontend/src/container/NewDashboard/DashboardDescription/Description.styles.scss @@ -0,0 +1,7 @@ +.dashboard-description { + display: -webkit-box; + -webkit-line-clamp: 2; /* Show up to 2 lines */ + -webkit-box-orient: vertical; + text-overflow: ellipsis; + overflow: hidden; +} diff --git a/frontend/src/container/NewDashboard/DescriptionOfDashboard/SettingsDrawer.tsx b/frontend/src/container/NewDashboard/DashboardDescription/SettingsDrawer.tsx similarity index 76% rename from frontend/src/container/NewDashboard/DescriptionOfDashboard/SettingsDrawer.tsx rename to frontend/src/container/NewDashboard/DashboardDescription/SettingsDrawer.tsx index fc51efd69a..44b697cc67 100644 --- a/frontend/src/container/NewDashboard/DescriptionOfDashboard/SettingsDrawer.tsx +++ b/frontend/src/container/NewDashboard/DashboardDescription/SettingsDrawer.tsx @@ -5,7 +5,7 @@ import { useState } from 'react'; import DashboardSettingsContent from '../DashboardSettings'; import { DrawerContainer } from './styles'; -function SettingsDrawer(): JSX.Element { +function SettingsDrawer({ drawerTitle }: { drawerTitle: string }): JSX.Element { const [visible, setVisible] = useState(false); const showDrawer = (): void => { @@ -18,12 +18,13 @@ function SettingsDrawer(): JSX.Element { return ( <> - diff --git a/frontend/src/container/NewDashboard/DescriptionOfDashboard/ShareModal.tsx b/frontend/src/container/NewDashboard/DashboardDescription/ShareModal.tsx similarity index 63% rename from frontend/src/container/NewDashboard/DescriptionOfDashboard/ShareModal.tsx rename to frontend/src/container/NewDashboard/DashboardDescription/ShareModal.tsx index d7ccd451e4..8826eccddc 100644 --- a/frontend/src/container/NewDashboard/DescriptionOfDashboard/ShareModal.tsx +++ b/frontend/src/container/NewDashboard/DashboardDescription/ShareModal.tsx @@ -1,4 +1,5 @@ -import { Button, Modal, Typography } from 'antd'; +import { CopyFilled, DownloadOutlined } from '@ant-design/icons'; +import { Button, Modal } from 'antd'; import Editor from 'components/Editor'; import { useNotifications } from 'hooks/useNotifications'; import { useEffect, useMemo, useState } from 'react'; @@ -6,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import { useCopyToClipboard } from 'react-use'; import { DashboardData } from 'types/api/dashboard/getAll'; -import { downloadObjectAsJson } from './util'; +import { downloadObjectAsJson } from './utils'; function ShareModal({ isJSONModalVisible, @@ -16,7 +17,6 @@ function ShareModal({ const getParsedValue = (): string => JSON.stringify(selectedData, null, 2); const [jsonValue, setJSONValue] = useState(getParsedValue()); - const [isViewJSON, setIsViewJSON] = useState(false); const { t } = useTranslation(['dashboard', 'common']); const [state, setCopy] = useCopyToClipboard(); const { notifications } = useNotifications(); @@ -39,44 +39,41 @@ function ShareModal({ } }, [state.error, state.value, t, notifications]); + // eslint-disable-next-line arrow-body-style const GetFooterComponent = useMemo(() => { - if (!isViewJSON) { - return ( - <> - - - - - ); - } return ( - + <> + + + + ); - }, [isViewJSON, jsonValue, selectedData, setCopy, t]); + }, [jsonValue, selectedData, setCopy, t]); return ( { onToggleHandler(); - setIsViewJSON(false); }} - width="70vw" + width="80vw" centered title={t('share', { ns: 'common', @@ -86,11 +83,11 @@ function ShareModal({ destroyOnClose footer={GetFooterComponent} > - {!isViewJSON ? ( - {t('export_dashboard')} - ) : ( - setJSONValue(value)} value={jsonValue} /> - )} + setJSONValue(value)} + value={jsonValue} + /> ); } diff --git a/frontend/src/container/NewDashboard/DashboardDescription/index.tsx b/frontend/src/container/NewDashboard/DashboardDescription/index.tsx new file mode 100644 index 0000000000..6eae3ff416 --- /dev/null +++ b/frontend/src/container/NewDashboard/DashboardDescription/index.tsx @@ -0,0 +1,122 @@ +import './Description.styles.scss'; + +import { LockFilled, ShareAltOutlined, UnlockFilled } from '@ant-design/icons'; +import { Button, Card, Col, Row, Space, Tag, Tooltip, Typography } from 'antd'; +import useComponentPermission from 'hooks/useComponentPermission'; +import { useDashboard } from 'providers/Dashboard/Dashboard'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { DashboardData } from 'types/api/dashboard/getAll'; +import AppReducer from 'types/reducer/app'; +import { USER_ROLES } from 'types/roles'; + +import DashboardVariableSelection from '../DashboardVariablesSelection'; +import SettingsDrawer from './SettingsDrawer'; +import ShareModal from './ShareModal'; + +function DashboardDescription(): JSX.Element { + const { + selectedDashboard, + isDashboardLocked, + handleDashboardLockToggle, + } = useDashboard(); + + const selectedData = selectedDashboard?.data || ({} as DashboardData); + + const { title = '', tags, description } = selectedData || {}; + + const [openDashboardJSON, setOpenDashboardJSON] = useState(false); + + const { t } = useTranslation('common'); + const { user, role } = useSelector((state) => state.app); + const [editDashboard] = useComponentPermission(['edit_dashboard'], role); + + let isAuthor = false; + + if (selectedDashboard && user && user.email) { + isAuthor = selectedDashboard?.created_by === user?.email; + } + + const onToggleHandler = (): void => { + setOpenDashboardJSON((state) => !state); + }; + + const handleLockDashboardToggle = (): void => { + handleDashboardLockToggle(!isDashboardLocked); + }; + + return ( + + + + + {isDashboardLocked && ( + +   + + )} + {title} + + {description && ( + {description} + )} + + {tags && ( +
+ {tags?.map((tag) => ( + {tag} + ))} +
+ )} + + + + + + + + {selectedData && ( + + )} + + + {!isDashboardLocked && editDashboard && ( + + )} + + {(isAuthor || role === USER_ROLES.ADMIN) && ( + + + + )} + + +
+
+ ); +} + +export default DashboardDescription; diff --git a/frontend/src/container/NewDashboard/DescriptionOfDashboard/styles.ts b/frontend/src/container/NewDashboard/DashboardDescription/styles.ts similarity index 85% rename from frontend/src/container/NewDashboard/DescriptionOfDashboard/styles.ts rename to frontend/src/container/NewDashboard/DashboardDescription/styles.ts index 43bcccef51..6dde0b3e57 100644 --- a/frontend/src/container/NewDashboard/DescriptionOfDashboard/styles.ts +++ b/frontend/src/container/NewDashboard/DashboardDescription/styles.ts @@ -14,7 +14,11 @@ export const Button = styled(ButtonComponent)` export const DrawerContainer = styled(Drawer)` .ant-drawer-header { - padding: 0; + padding: 16px; border: none; } + + .ant-drawer-body { + padding-top: 0; + } `; diff --git a/frontend/src/container/NewDashboard/DescriptionOfDashboard/util.ts b/frontend/src/container/NewDashboard/DashboardDescription/utils.ts similarity index 100% rename from frontend/src/container/NewDashboard/DescriptionOfDashboard/util.ts rename to frontend/src/container/NewDashboard/DashboardDescription/utils.ts diff --git a/frontend/src/container/NewDashboard/DashboardSettings/General/AddTags/index.tsx b/frontend/src/container/NewDashboard/DashboardSettings/General/AddTags/index.tsx index 15b89f4b8e..82c016aba0 100644 --- a/frontend/src/container/NewDashboard/DashboardSettings/General/AddTags/index.tsx +++ b/frontend/src/container/NewDashboard/DashboardSettings/General/AddTags/index.tsx @@ -104,7 +104,13 @@ function AddTags({ tags, setTags }: AddTagsProps): JSX.Element { {!inputVisible && ( } onClick={showInput}> - New Tag + + New Tag + )} diff --git a/frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx b/frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx index 3f6eec23b4..ad47526cf8 100644 --- a/frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx +++ b/frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx @@ -1,5 +1,5 @@ import { SaveOutlined } from '@ant-design/icons'; -import { Col, Divider, Input, Space, Typography } from 'antd'; +import { Col, Input, Space, Typography } from 'antd'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import AddTags from 'container/NewDashboard/DashboardSettings/General/AddTags'; import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard'; @@ -71,6 +71,7 @@ function GeneralDashboardSettings(): JSX.Element {
Description setUpdatedDescription(e.target.value)} /> @@ -80,8 +81,10 @@ function GeneralDashboardSettings(): JSX.Element {
- - - - - - ); -} - -export default DescriptionOfDashboard; diff --git a/frontend/src/container/NewDashboard/index.tsx b/frontend/src/container/NewDashboard/index.tsx index 5eff095c6f..ca10b6f89b 100644 --- a/frontend/src/container/NewDashboard/index.tsx +++ b/frontend/src/container/NewDashboard/index.tsx @@ -1,4 +1,4 @@ -import Description from './DescriptionOfDashboard'; +import Description from './DashboardDescription'; import GridGraphs from './GridGraphs'; function NewDashboard(): JSX.Element { diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/QueryHeader.tsx b/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/QueryHeader.tsx index a9d8fd9c36..e878c61b4d 100644 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/QueryHeader.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/QueryHeader.tsx @@ -33,21 +33,29 @@ function QueryHeader({ + + ); +} + +interface ColorSelectorProps { + thresholdColor?: string; + setColor: Dispatch>; +} + +ColorSelector.defaultProps = { + thresholdColor: undefined, +}; + +export default ColorSelector; diff --git a/frontend/src/container/NewWidget/RightContainer/Threshold/CustomColor.styles.scss b/frontend/src/container/NewWidget/RightContainer/Threshold/CustomColor.styles.scss new file mode 100644 index 0000000000..058052f2f6 --- /dev/null +++ b/frontend/src/container/NewWidget/RightContainer/Threshold/CustomColor.styles.scss @@ -0,0 +1,18 @@ +.custom-color-container { + display: flex; + gap: 10px; + align-items: center; + + .custom-color-typography-dark { + color: #fff !important; + } + + .custom-color-typography-light { + color: #000 !important; + } + + .custom-color-tag { + width: 20px; + height: 20px; + } +} \ No newline at end of file diff --git a/frontend/src/container/NewWidget/RightContainer/Threshold/CustomColor.tsx b/frontend/src/container/NewWidget/RightContainer/Threshold/CustomColor.tsx new file mode 100644 index 0000000000..d4cc67bda6 --- /dev/null +++ b/frontend/src/container/NewWidget/RightContainer/Threshold/CustomColor.tsx @@ -0,0 +1,26 @@ +import './CustomColor.styles.scss'; + +import { Typography } from 'antd'; +import { useIsDarkMode } from 'hooks/useDarkMode'; + +import { CustomColorProps } from './types'; + +function CustomColor({ color }: CustomColorProps): JSX.Element { + const isDarkMode = useIsDarkMode(); + return ( +
+
+ + {color} + +
+ ); +} + +export default CustomColor; diff --git a/frontend/src/container/NewWidget/RightContainer/Threshold/ShowCaseValue.styles.scss b/frontend/src/container/NewWidget/RightContainer/Threshold/ShowCaseValue.styles.scss new file mode 100644 index 0000000000..360387c333 --- /dev/null +++ b/frontend/src/container/NewWidget/RightContainer/Threshold/ShowCaseValue.styles.scss @@ -0,0 +1,13 @@ +.show-case-container { + padding: 5px 15px; + border-radius: 5px; +} + +.show-case-dark { + background-color: #141414; +} + +.show-case-light { + background-color: rgb(255, 255, 255); + border: 1px solid #141414; +} \ No newline at end of file diff --git a/frontend/src/container/NewWidget/RightContainer/Threshold/ShowCaseValue.tsx b/frontend/src/container/NewWidget/RightContainer/Threshold/ShowCaseValue.tsx new file mode 100644 index 0000000000..c71c5cbcfc --- /dev/null +++ b/frontend/src/container/NewWidget/RightContainer/Threshold/ShowCaseValue.tsx @@ -0,0 +1,23 @@ +import './ShowCaseValue.styles.scss'; + +import { useIsDarkMode } from 'hooks/useDarkMode'; + +import { ShowCaseValueProps } from './types'; + +function ShowCaseValue({ width, value }: ShowCaseValueProps): JSX.Element { + const isDarkMode = useIsDarkMode(); + return ( +
+ {value} +
+ ); +} + +export default ShowCaseValue; diff --git a/frontend/src/container/NewWidget/RightContainer/Threshold/Threshold.styles.scss b/frontend/src/container/NewWidget/RightContainer/Threshold/Threshold.styles.scss new file mode 100644 index 0000000000..dcf7acf88c --- /dev/null +++ b/frontend/src/container/NewWidget/RightContainer/Threshold/Threshold.styles.scss @@ -0,0 +1,58 @@ +.threshold-container { + + .threshold-card { + padding: 0px; + border-radius: 10px; + position: relative; + margin-top: 10px; + + .threshold-card-container { + display: flex; + flex-direction: column; + gap: 10px; + } + + .ant-typography { + font-size: 14px; + font-weight: 400; + line-height: 22px; + } + + .ant-typograph-dark { + color: #FFFFFF73; + } + + .ant-typograph-light { + color: #00000073; + } + + } + + .threshold-card-dark { + background-color: #1F1F1F; + } + + .threshold-card-light { + background-color: rgb(255, 255, 255); + } + + .threshold-action-button { + position: absolute; + right: 10px; + top: 10px; + } + + .threshold-action-icon { + font-size: 16px; + } + + .threshold-units-selector { + display: flex; + align-items: center; + } + + .threshold-color-picker { + display: flex; + flex-direction: column; + } +} \ No newline at end of file diff --git a/frontend/src/container/NewWidget/RightContainer/Threshold/Threshold.tsx b/frontend/src/container/NewWidget/RightContainer/Threshold/Threshold.tsx new file mode 100644 index 0000000000..1a2347c9d6 --- /dev/null +++ b/frontend/src/container/NewWidget/RightContainer/Threshold/Threshold.tsx @@ -0,0 +1,307 @@ +import './Threshold.styles.scss'; + +import { CheckOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons'; +import { + Card, + Divider, + Input, + InputNumber, + Select, + Space, + Typography, +} from 'antd'; +import { PANEL_TYPES } from 'constants/queryBuilder'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { useRef, useState } from 'react'; +import { useDrag, useDrop, XYCoord } from 'react-dnd'; + +import { + operatorOptions, + panelTypeVsDragAndDrop, + showAsOptions, + unitOptions, +} from '../constants'; +import ColorSelector from './ColorSelector'; +import CustomColor from './CustomColor'; +import ShowCaseValue from './ShowCaseValue'; +import { ThresholdProps } from './types'; + +function Threshold({ + index, + thresholdOperator = '>', + thresholdValue = 0, + isEditEnabled = false, + thresholdUnit = 'ms', + thresholdColor = 'Red', + thresholdFormat = 'Text', + thresholdDeleteHandler, + setThresholds, + keyIndex, + moveThreshold, + selectedGraph, + thresholdLabel = '', +}: ThresholdProps): JSX.Element { + const [isEditMode, setIsEditMode] = useState(isEditEnabled); + const [operator, setOperator] = useState( + thresholdOperator as string | number, + ); + const [value, setValue] = useState(thresholdValue); + const [unit, setUnit] = useState(thresholdUnit); + const [color, setColor] = useState(thresholdColor); + const [format, setFormat] = useState( + thresholdFormat, + ); + const [label, setLabel] = useState(thresholdLabel); + + const isDarkMode = useIsDarkMode(); + + const saveHandler = (): void => { + setIsEditMode(false); + if (setThresholds === undefined) { + return; + } + setThresholds((prevThresholds) => + prevThresholds.map((threshold) => { + if (threshold.index === index) { + return { + ...threshold, + isEditEnabled: false, + thresholdColor: color, + thresholdFormat: format, + thresholdOperator: operator as ThresholdProps['thresholdOperator'], + thresholdUnit: unit, + thresholdValue: value, + thresholdLabel: label, + }; + } + return threshold; + }), + ); + }; + + const editHandler = (): void => { + setIsEditMode(true); + }; + + const handleOperatorChange = (value: string | number): void => { + setOperator(value); + }; + + const handleValueChange = (value: number | null): void => { + if (value === null) { + return; + } + setValue(value); + }; + + const handleUnitChange = (value: string): void => { + setUnit(value); + }; + + const handlerFormatChange = ( + value: ThresholdProps['thresholdFormat'], + ): void => { + setFormat(value); + }; + + const deleteHandler = (): void => { + if (thresholdDeleteHandler) { + thresholdDeleteHandler(index); + } + }; + + const ref = useRef(null); + const [{ handlerId }, drop] = useDrop< + ThresholdProps, + void, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + { handlerId: any } + >({ + accept: 'Threshold', + collect(monitor) { + return { + handlerId: monitor.getHandlerId(), + }; + }, + hover(item: ThresholdProps, monitor) { + if (!ref.current) { + return; + } + const dragIndex = item.keyIndex; + const hoverIndex = keyIndex; + + if (dragIndex === hoverIndex) { + return; + } + + const hoverBoundingRect = ref.current?.getBoundingClientRect(); + + const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; + + const clientOffset = monitor.getClientOffset(); + + const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top; + + if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { + return; + } + + if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { + return; + } + + moveThreshold(dragIndex, hoverIndex); + // eslint-disable-next-line no-param-reassign + item.keyIndex = hoverIndex; + }, + }); + + const [{ isDragging }, drag] = useDrag({ + type: 'Threshold', + item: () => ({ keyIndex }), + collect: (monitor) => ({ + isDragging: monitor.isDragging(), + }), + }); + + const opacity = isDragging ? 0 : 1; + drag(drop(ref)); + const handleLabelChange = ( + event: React.ChangeEvent, + ): void => { + setLabel(event.target.value); + }; + + const backgroundColor = !isDarkMode ? '#ffffff' : '#141414'; + const allowDragAndDrop = panelTypeVsDragAndDrop[selectedGraph]; + + return ( +
+ +
+
+ {isEditMode ? ( + + ) : ( + + )} + + +
+
+ + {selectedGraph === PANEL_TYPES.TIME_SERIES && ( + <> + Label + {isEditMode ? ( + + ) : ( + + )} + + )} + {selectedGraph === PANEL_TYPES.VALUE && ( + <> + If value is + {isEditMode ? ( + + ) : ( + + )} + +
+
+ + Show with + + {isEditMode ? ( + <> + + diff --git a/frontend/src/container/OnboardingContainer/APM/Javascript/md-docs/express.md b/frontend/src/container/OnboardingContainer/APM/Javascript/md-docs/express.md index 28d2999047..ca724ee2c5 100644 --- a/frontend/src/container/OnboardingContainer/APM/Javascript/md-docs/express.md +++ b/frontend/src/container/OnboardingContainer/APM/Javascript/md-docs/express.md @@ -20,10 +20,10 @@ From VMs, there are two ways to send data to SigNoz Cloud. Step 1. Install OpenTelemetry packages ```bash -npm install --save @opentelemetry/api@^1.4.1 -npm install --save @opentelemetry/sdk-node@^0.39.1 -npm install --save @opentelemetry/auto-instrumentations-node@^0.37.0 -npm install --save @opentelemetry/exporter-trace-otlp-http@^0.39.1 +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 ``` Step 2. Create tracing.js file @@ -87,10 +87,10 @@ You can find instructions to install OTel Collector binary [here](https://signoz Step 1. Install OpenTelemetry packages ```js -npm install --save @opentelemetry/api@^1.4.1 -npm install --save @opentelemetry/sdk-node@^0.39.1 -npm install --save @opentelemetry/auto-instrumentations-node@^0.37.0 -npm install --save @opentelemetry/exporter-trace-otlp-http@^0.39.1 +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 ``` Step 2. Create tracing.js file @@ -148,10 +148,10 @@ Once you have set up OTel Collector agent, you can proceed with OpenTelemetry Ja Step 1. Install OpenTelemetry packages ```bash -npm install --save @opentelemetry/api@^1.4.1 -npm install --save @opentelemetry/sdk-node@^0.39.1 -npm install --save @opentelemetry/auto-instrumentations-node@^0.37.0 -npm install --save @opentelemetry/exporter-trace-otlp-http@^0.39.1 +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 ``` Step 2. Create tracing.js file diff --git a/frontend/src/container/OnboardingContainer/APM/Javascript/md-docs/javascript.md b/frontend/src/container/OnboardingContainer/APM/Javascript/md-docs/javascript.md index 7bb0198754..a98ef2fc42 100644 --- a/frontend/src/container/OnboardingContainer/APM/Javascript/md-docs/javascript.md +++ b/frontend/src/container/OnboardingContainer/APM/Javascript/md-docs/javascript.md @@ -18,10 +18,10 @@ From VMs, there are two ways to send data to SigNoz Cloud. Step 1. Install OpenTelemetry packages ```bash -npm install --save @opentelemetry/api@^1.4.1 -npm install --save @opentelemetry/sdk-node@^0.39.1 -npm install --save @opentelemetry/auto-instrumentations-node@^0.37.0 -npm install --save @opentelemetry/exporter-trace-otlp-http@^0.39.1 +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 ``` Step 2. Create tracing.js file @@ -86,10 +86,10 @@ You can find instructions to install OTel Collector binary [here](https://signoz Step 1. Install OpenTelemetry packages ```js -npm install --save @opentelemetry/api@^1.4.1 -npm install --save @opentelemetry/sdk-node@^0.39.1 -npm install --save @opentelemetry/auto-instrumentations-node@^0.37.0 -npm install --save @opentelemetry/exporter-trace-otlp-http@^0.39.1 +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 ``` Step 2. Create tracing.js file @@ -149,10 +149,10 @@ Once you have set up OTel Collector agent, you can proceed with OpenTelemetry Ja Step 1. Install OpenTelemetry packages ```js -npm install --save @opentelemetry/api@^1.4.1 -npm install --save @opentelemetry/sdk-node@^0.39.1 -npm install --save @opentelemetry/auto-instrumentations-node@^0.37.0 -npm install --save @opentelemetry/exporter-trace-otlp-http@^0.39.1 +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 ``` Step 2. Create tracing.js file diff --git a/frontend/src/container/OnboardingContainer/APM/Javascript/md-docs/nestjs.md b/frontend/src/container/OnboardingContainer/APM/Javascript/md-docs/nestjs.md index 3d23c81c8e..b947dc3b07 100644 --- a/frontend/src/container/OnboardingContainer/APM/Javascript/md-docs/nestjs.md +++ b/frontend/src/container/OnboardingContainer/APM/Javascript/md-docs/nestjs.md @@ -20,10 +20,10 @@ From VMs, there are two ways to send data to SigNoz Cloud. Step 1. Install OpenTelemetry packages ```bash -npm install --save @opentelemetry/api@^1.4.1 -npm install --save @opentelemetry/sdk-node@^0.39.1 -npm install --save @opentelemetry/auto-instrumentations-node@^0.37.0 -npm install --save @opentelemetry/exporter-trace-otlp-http@^0.39.1 +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 ``` Step 2. Create `tracer.ts` file @@ -31,40 +31,43 @@ Step 2. Create `tracer.ts` file This file will have your SigNoz cloud endpoint and service name configued as values of `url` and `SERVICE_NAME` respectively. ```js -'use strict' -const process = require('process'); -const opentelemetry = require('@opentelemetry/sdk-node'); -const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node'); -const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http'); -const {Resource} = require('@opentelemetry/resources'); -const {SemanticResourceAttributes} = require('@opentelemetry/semantic-conventions'); +'use strict'; +import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { Resource } from '@opentelemetry/resources'; +import * as opentelemetry from '@opentelemetry/sdk-node'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; + +// Configure the SDK to export telemetry data to the console +// Enable all auto-instrumentations from the meta package const exporterOptions = { - url: 'https://ingest.{{REGION}}.signoz.cloud:443/v1/traces' - } + url: 'https://ingest.{{REGION}}.signoz.cloud:443/v1/traces', +}; const traceExporter = new OTLPTraceExporter(exporterOptions); const sdk = new opentelemetry.NodeSDK({ traceExporter, instrumentations: [getNodeAutoInstrumentations()], resource: new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}' - }) - }); - - // initialize the SDK and register with the OpenTelemetry API - // this enables the API to record telemetry - sdk.start() - - // gracefully shut down the SDK on process exit - process.on('SIGTERM', () => { - sdk.shutdown() + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}', + }), +}); + +// initialize the SDK and register with the OpenTelemetry API +// this enables the API to record telemetry +sdk.start(); + +// gracefully shut down the SDK on process exit +process.on('SIGTERM', () => { + sdk + .shutdown() .then(() => console.log('Tracing terminated')) .catch((error) => console.log('Error terminating tracing', error)) .finally(() => process.exit(0)); - }); - - module.exports = sdk +}); + +export default sdk; ``` Step 3. Import the tracer module where your app starts `(Ex —> main.ts)` @@ -112,10 +115,10 @@ You can find instructions to install OTel Collector binary [here](https://signoz Step 1. Install OpenTelemetry packages ```js -npm install --save @opentelemetry/api@^1.4.1 -npm install --save @opentelemetry/sdk-node@^0.39.1 -npm install --save @opentelemetry/auto-instrumentations-node@^0.37.0 -npm install --save @opentelemetry/exporter-trace-otlp-http@^0.39.1 +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 ``` Step 2. Create `tracer.ts` file @@ -123,41 +126,43 @@ Step 2. Create `tracer.ts` file This file will have your service name configued as value for `SERVICE_NAME`. ```js -'use strict' -const process = require('process'); -//OpenTelemetry -const opentelemetry = require('@opentelemetry/sdk-node'); -const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node'); -const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http'); -const {Resource} = require('@opentelemetry/resources'); -const {SemanticResourceAttributes} = require('@opentelemetry/semantic-conventions'); +'use strict'; +import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { Resource } from '@opentelemetry/resources'; +import * as opentelemetry from '@opentelemetry/sdk-node'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; + +// Configure the SDK to export telemetry data to the console +// Enable all auto-instrumentations from the meta package const exporterOptions = { - url: 'http://localhost:4318/v1/traces' - } + url: 'http://localhost:4318/v1/traces', +}; const traceExporter = new OTLPTraceExporter(exporterOptions); const sdk = new opentelemetry.NodeSDK({ traceExporter, instrumentations: [getNodeAutoInstrumentations()], resource: new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}' - }) - }); - - // initialize the SDK and register with the OpenTelemetry API - // this enables the API to record telemetry - sdk.start() - - // gracefully shut down the SDK on process exit - process.on('SIGTERM', () => { - sdk.shutdown() + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}', + }), +}); + +// initialize the SDK and register with the OpenTelemetry API +// this enables the API to record telemetry +sdk.start(); + +// gracefully shut down the SDK on process exit +process.on('SIGTERM', () => { + sdk + .shutdown() .then(() => console.log('Tracing terminated')) .catch((error) => console.log('Error terminating tracing', error)) .finally(() => process.exit(0)); - }); - - module.exports = sdk +}); + +export default sdk; ``` Step 3. Import the tracer module where your app starts @@ -200,10 +205,10 @@ Once you have set up OTel Collector agent, you can proceed with OpenTelemetry Ja Step 1. Install OpenTelemetry packages ```bash -npm install --save @opentelemetry/api@^1.4.1 -npm install --save @opentelemetry/sdk-node@^0.39.1 -npm install --save @opentelemetry/auto-instrumentations-node@^0.37.0 -npm install --save @opentelemetry/exporter-trace-otlp-http@^0.39.1 +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 ``` Step 2. Create `tracer.ts` file @@ -211,41 +216,43 @@ Step 2. Create `tracer.ts` file This file will have your service name configued as value for `SERVICE_NAME`. ```js -'use strict' -const process = require('process'); -//OpenTelemetry -const opentelemetry = require('@opentelemetry/sdk-node'); -const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node'); -const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http'); -const {Resource} = require('@opentelemetry/resources'); -const {SemanticResourceAttributes} = require('@opentelemetry/semantic-conventions'); +'use strict'; +import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { Resource } from '@opentelemetry/resources'; +import * as opentelemetry from '@opentelemetry/sdk-node'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; + +// Configure the SDK to export telemetry data to the console +// Enable all auto-instrumentations from the meta package const exporterOptions = { - url: 'http://localhost:4318/v1/traces' - } + url: 'http://localhost:4318/v1/traces', +}; const traceExporter = new OTLPTraceExporter(exporterOptions); const sdk = new opentelemetry.NodeSDK({ traceExporter, instrumentations: [getNodeAutoInstrumentations()], resource: new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}' - }) - }); - - // initialize the SDK and register with the OpenTelemetry API - // this enables the API to record telemetry - sdk.start() - - // gracefully shut down the SDK on process exit - process.on('SIGTERM', () => { - sdk.shutdown() + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}', + }), +}); + +// initialize the SDK and register with the OpenTelemetry API +// this enables the API to record telemetry +sdk.start(); + +// gracefully shut down the SDK on process exit +process.on('SIGTERM', () => { + sdk + .shutdown() .then(() => console.log('Tracing terminated')) .catch((error) => console.log('Error terminating tracing', error)) .finally(() => process.exit(0)); - }); - - module.exports = sdk +}); + +export default sdk; ``` Step 3. Import the tracer module where your app starts diff --git a/frontend/src/container/OnboardingContainer/APM/RubyOnRails/ROR.styles.scss b/frontend/src/container/OnboardingContainer/APM/RubyOnRails/ROR.styles.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/container/OnboardingContainer/APM/RubyOnRails/ROR.tsx b/frontend/src/container/OnboardingContainer/APM/RubyOnRails/ROR.tsx new file mode 100644 index 0000000000..c19dbf34db --- /dev/null +++ b/frontend/src/container/OnboardingContainer/APM/RubyOnRails/ROR.tsx @@ -0,0 +1,68 @@ +import './ROR.styles.scss'; + +import { Form, Input } from 'antd'; +import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer'; +import Header from 'container/OnboardingContainer/common/Header/Header'; + +import { LangProps } from '../APM'; +import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus'; +import RORDocs from './RubyOnRails.md'; + +export default function RoR({ + ingestionInfo, + activeStep, +}: LangProps): JSX.Element { + const [form] = Form.useForm(); + const serviceName = Form.useWatch('Service Name', form); + + const variables = { + MYAPP: serviceName || '', + SIGNOZ_INGESTION_KEY: + ingestionInfo.SIGNOZ_INGESTION_KEY || '', + REGION: ingestionInfo.REGION || 'region', + }; + + return ( + <> + {activeStep === 2 && ( +
+
+ +
+
+
Service Name
+ + + + + + +
+
+ +
+ +
+
+ )} + {activeStep === 3 && ( + + )} + + ); +} diff --git a/frontend/src/container/OnboardingContainer/APM/RubyOnRails/RubyOnRails.md b/frontend/src/container/OnboardingContainer/APM/RubyOnRails/RubyOnRails.md new file mode 100644 index 0000000000..3f8eac1455 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/APM/RubyOnRails/RubyOnRails.md @@ -0,0 +1,178 @@ +## Send Traces to SigNoz Cloud + +### Application on VMs + +From VMs, there are two ways to send data to SigNoz Cloud. + +- Send traces directly to SigNoz Cloud (quick start) +- Send traces via OTel Collector binary (recommended) + +#### **Send traces directly to SigNoz Cloud** + +**Step 1. Install dependencies** + +Install dependencies related to OpenTelemetry SDK and exporter using gem. + +```go +gem install opentelemetry-sdk +gem install opentelemetry-exporter-otlp +gem install opentelemetry-instrumentation-all +``` + +Include the required packages into your gemfile. + +```go +gem 'opentelemetry-sdk' +gem 'opentelemetry-exporter-otlp' +gem 'opentelemetry-instrumentation-all' +``` + +Run the bundle install command: + +```go +bundle install +``` + +**Step 2. Initialize the OpenTelemetry SDK** + +Initialize the otel sdk by adding below lines to `config/environment.rb` of your Ruby on Rails application. + +```jsx +require 'opentelemetry/sdk' +require_relative 'application' + +OpenTelemetry::SDK.configure do |c| + c.use_all +end + +Rails.application.initialize! +``` + +**Step 3. Running your Ruby application** + +Run the application using the below: + +```jsx +OTEL_EXPORTER=otlp \ +OTEL_SERVICE_NAME={{MYAPP}} \ +OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.{{REGION}}.signoz.cloud:443 \ +OTEL_EXPORTER_OTLP_HEADERS=signoz-access-token={{SIGNOZ_INGESTION_KEY}} \ +rails server +``` + +--- +#### **Send traces via OTel Collector binary** + +OTel Collector binary helps to collect logs, hostmetrics, resource and infra attributes. It is recommended to install Otel Collector binary to collect and send traces to SigNoz cloud. You can correlate signals and have rich contextual data through this way. + +You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your VM. Once you are done setting up your OTel Collector binary, you can follow the below steps for instrumenting your Ruby on Rails application. + +**Step 1. Install dependencies** + +Install dependencies related to OpenTelemetry SDK and exporter using gem. + +```go +gem install opentelemetry-sdk +gem install opentelemetry-exporter-otlp +gem install opentelemetry-instrumentation-all +``` + +Include the required packages into your gemfile. + +```go +gem 'opentelemetry-sdk' +gem 'opentelemetry-exporter-otlp' +gem 'opentelemetry-instrumentation-all' +``` + +Run the bundle install command: + +```go +bundle install +``` + +**Step 2. Initialize the OpenTelemetry SDK** + +Initialize the otel sdk by adding below lines to `config/environment.rb` of your Ruby on Rails application. + +```jsx +require 'opentelemetry/sdk' +require_relative 'application' + +OpenTelemetry::SDK.configure do |c| + c.use_all +end + +Rails.application.initialize! +``` + +**Step 3. Running your Ruby application** + +Run the application using the below: + +```jsx +OTEL_EXPORTER=otlp \ +OTEL_SERVICE_NAME={{MYAPP}} \ +OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \ +rails server +``` + +In case you have OtelCollector Agent in different VM, replace localhost:4318 with `:4318`. + +--- + +### Applications Deployed on Kubernetes + +For Ruby on Rails application deployed on Kubernetes, you need to install OTel Collector agent in your k8s infra to collect and send traces to SigNoz Cloud. You can find the instructions to install OTel Collector agent [here](https://signoz.io/docs/tutorial/kubernetes-infra-metrics/). + + + +**Step 1. Install dependencies** + +Install dependencies related to OpenTelemetry SDK and exporter using gem. + +```go +gem install opentelemetry-sdk +gem install opentelemetry-exporter-otlp +gem install opentelemetry-instrumentation-all +``` + +Include the required packages into your gemfile. + +```go +gem 'opentelemetry-sdk' +gem 'opentelemetry-exporter-otlp' +gem 'opentelemetry-instrumentation-all' +``` + +Run the bundle install command: + +```go +bundle install +``` + +**Step 2. Initialize the OpenTelemetry SDK** + +Initialize the otel sdk by adding below lines to `config/environment.rb` of your Ruby on Rails application. + +```jsx +require 'opentelemetry/sdk' +require_relative 'application' + +OpenTelemetry::SDK.configure do |c| + c.use_all +end + +Rails.application.initialize! +``` + +**Step 3. Running your Ruby application** + +Run the application using the below: + +```jsx +OTEL_EXPORTER=otlp \ +OTEL_SERVICE_NAME={{MYAPP}} \ +OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \ +rails server +``` diff --git a/frontend/src/container/OnboardingContainer/APM/common/ConnectionStatus/ConnectionStatus.tsx b/frontend/src/container/OnboardingContainer/APM/common/ConnectionStatus/ConnectionStatus.tsx index de918dced1..02b139dfb4 100644 --- a/frontend/src/container/OnboardingContainer/APM/common/ConnectionStatus/ConnectionStatus.tsx +++ b/frontend/src/container/OnboardingContainer/APM/common/ConnectionStatus/ConnectionStatus.tsx @@ -24,7 +24,7 @@ interface ConnectionStatusProps { framework: string; } -const pollingInterval = 15000; +const pollingInterval = 10000; export default function ConnectionStatus({ serviceName, @@ -103,6 +103,16 @@ export default function ConnectionStatus({ imgClassName="supported-language-img" /> ); + case 'rails': + return ( +
+ ); default: return <> ; diff --git a/frontend/src/container/OnboardingContainer/LogsManagement/Docker/docker.md b/frontend/src/container/OnboardingContainer/LogsManagement/Docker/docker.md index 1c1025963a..d9be2ca45c 100644 --- a/frontend/src/container/OnboardingContainer/LogsManagement/Docker/docker.md +++ b/frontend/src/container/OnboardingContainer/LogsManagement/Docker/docker.md @@ -1,23 +1,30 @@ ## Collect Docker Container Logs in SigNoz Cloud -- Clone this [repository](https://github.com/SigNoz/docker-container-logs) +**Step 1. Clone this repository** -- Update `otel-collector-config.yaml` and set the values of `` and `{region}`. +Clone the GitHub repository as a first step to collect logs - Depending on the choice of your region for SigNoz cloud, the ingest endpoint will vary accordingly. +```bash +git clone https://github.com/SigNoz/docker-container-logs.git +``` - US - ingest.us.signoz.cloud:443 +**Step 2. Update your `.env` file** - IN - ingest.in.signoz.cloud:443 +In the repository that you cloned above, update `.env` file by putting the values of `` and `{region}`. - EU - ingest.eu.signoz.cloud:443 +Depending on the choice of your region for SigNoz cloud, the ingest endpoint will vary accordingly. +US - ingest.us.signoz.cloud:443 - - Start the containers +IN - ingest.in.signoz.cloud:443 + +EU - ingest.eu.signoz.cloud:443 + +**Step 3. Start the containers** - ```bash + ```bash docker compose up -d - ``` + ``` - - If there are no errors your logs will be exported and will be visible on the SigNoz UI. +If there are no errors your logs will be exported and will be visible on the SigNoz UI. diff --git a/frontend/src/container/OrganizationSettings/AuthDomains/AddDomain/index.tsx b/frontend/src/container/OrganizationSettings/AuthDomains/AddDomain/index.tsx index 0c23824aed..e06072043b 100644 --- a/frontend/src/container/OrganizationSettings/AuthDomains/AddDomain/index.tsx +++ b/frontend/src/container/OrganizationSettings/AuthDomains/AddDomain/index.tsx @@ -76,10 +76,9 @@ function AddDomain({ refetch }: Props): JSX.Element { destroyOnClose onCancel={(): void => setIsDomain(false)} > -
+ setActionMode(ActionMode.Editing); + const onEnterEditMode = (): void => { + setActionMode(ActionMode.Editing); + + trackEvent('Logs: Pipelines: Entered Edit Mode', { + source: 'signoz-ui', + }); + }; const onAddNewPipeline = (): void => { setActionMode(ActionMode.Editing); setActionType(ActionType.AddPipeline); + + trackEvent('Logs: Pipelines: Clicked Add New Pipeline', { + source: 'signoz-ui', + }); }; return ( diff --git a/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/index.tsx b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/index.tsx index c9ab5d8aeb..8281655952 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/index.tsx +++ b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/index.tsx @@ -7,6 +7,7 @@ import { PipelineData, ProcessorData, } from 'types/api/pipeline/def'; +import { v4 } from 'uuid'; import { ModalButtonWrapper, ModalTitle } from '../styles'; import { getEditedDataSource, getRecordIndex } from '../utils'; @@ -59,7 +60,7 @@ function AddNewProcessor({ const totalDataLength = expandedPipelineData?.config?.length || 0; const newProcessorData = { - id: values.name.replace(/\s/g, ''), + id: v4(), orderId: Number(totalDataLength || 0) + 1, type: processorType, enabled: true, @@ -73,12 +74,14 @@ function AddNewProcessor({ 'id', ); + const processorData = expandedPipelineData?.config?.[findRecordIndex]; + const updatedProcessorData = { - id: values.name.replace(/\s/g, ''), - orderId: expandedPipelineData?.config?.[findRecordIndex].orderId, + id: processorData?.id || v4(), + orderId: processorData?.orderId, type: processorType, - enabled: expandedPipelineData?.config?.[findRecordIndex].enabled, - output: expandedPipelineData?.config?.[findRecordIndex].output, + enabled: processorData?.enabled, + output: processorData?.output, ...values, }; diff --git a/frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx b/frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx index 1e5b1add33..22003fc4eb 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx +++ b/frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx @@ -11,6 +11,7 @@ import { PipelineData, ProcessorData, } from 'types/api/pipeline/def'; +import { trackEvent } from 'utils/segmentAnalytics'; import { tableComponents } from '../config'; import { ModalFooterTitle } from '../styles'; @@ -189,6 +190,10 @@ function PipelineExpandView({ const addNewProcessorHandler = useCallback((): void => { setActionType(ActionType.AddProcessor); + + trackEvent('Logs: Pipelines: Clicked Add New Processor', { + source: 'signoz-ui', + }); }, [setActionType]); const footer = useCallback((): JSX.Element | undefined => { @@ -239,7 +244,7 @@ function PipelineExpandView({ isDarkMode={isDarkMode} showHeader={false} columns={columns} - rowKey="name" + rowKey="id" size="small" components={tableComponents} dataSource={processorData} diff --git a/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx b/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx index e7211c059c..a9bf96d4dc 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx +++ b/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx @@ -15,6 +15,7 @@ import { PipelineData, ProcessorData, } from 'types/api/pipeline/def'; +import { trackEvent } from 'utils/segmentAnalytics'; import { v4 } from 'uuid'; import { tableComponents } from '../config'; @@ -329,6 +330,10 @@ function PipelineListsView({ const addNewPipelineHandler = useCallback((): void => { setActionType(ActionType.AddPipeline); + + trackEvent('Logs: Pipelines: Clicked Add New Pipeline', { + source: 'signoz-ui', + }); }, [setActionType]); const footer = useCallback((): JSX.Element | undefined => { @@ -359,8 +364,16 @@ function PipelineListsView({ refetchPipelineLists(); setActionMode(ActionMode.Viewing); setShowSaveButton(undefined); - setCurrPipelineData(response.payload?.pipelines || []); - setPrevPipelineData(response.payload?.pipelines || []); + + const pipelinesInDB = response.payload?.pipelines || []; + setCurrPipelineData(pipelinesInDB); + setPrevPipelineData(pipelinesInDB); + + trackEvent('Logs: Pipelines: Saved Pipelines', { + count: pipelinesInDB.length, + enabled: pipelinesInDB.filter((p) => p.enabled).length, + source: 'signoz-ui', + }); } else { modifiedPipelineData.forEach((item: PipelineData) => { const pipelineData = item; diff --git a/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineActions/components/PreviewAction.tsx b/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineActions/components/PreviewAction.tsx index 353feec486..0236a04737 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineActions/components/PreviewAction.tsx +++ b/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineActions/components/PreviewAction.tsx @@ -3,6 +3,7 @@ import { Divider, Modal } from 'antd'; import PipelineProcessingPreview from 'container/PipelinePage/PipelineListsView/Preview/PipelineProcessingPreview'; import { useState } from 'react'; import { PipelineData } from 'types/api/pipeline/def'; +import { trackEvent } from 'utils/segmentAnalytics'; import { iconStyle } from '../../../config'; @@ -18,9 +19,16 @@ function PreviewAction({ pipeline }: PreviewActionProps): JSX.Element | null { return null; } + const onOpenPreview = (): void => { + openModal(); + trackEvent('Logs: Pipelines: Clicked Preview Pipeline', { + source: 'signoz-ui', + }); + }; + return ( <> - +
@@ -154,10 +159,10 @@ exports[`PipelinePage container test should render PipelinePageLayout section 1`
- - Pipeline Name Filters Last Edited Edited By Actions @@ -234,7 +244,7 @@ exports[`PipelinePage container test should render PipelinePageLayout section 1` colspan="7" >
diff --git a/frontend/src/container/PipelinePage/tests/__snapshots__/TagInput.test.tsx.snap b/frontend/src/container/PipelinePage/tests/__snapshots__/TagInput.test.tsx.snap index 6a97ec9ad6..ad5076e5f1 100644 --- a/frontend/src/container/PipelinePage/tests/__snapshots__/TagInput.test.tsx.snap +++ b/frontend/src/container/PipelinePage/tests/__snapshots__/TagInput.test.tsx.snap @@ -14,14 +14,14 @@ exports[`Pipeline Page should render TagInput section 1`] = ` class="c0" > server app diff --git a/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetrics.test.tsx b/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetrics.test.tsx index 1047b16ca7..bb5901b8f7 100644 --- a/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetrics.test.tsx +++ b/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetrics.test.tsx @@ -40,8 +40,6 @@ describe('ServicesUsingMetrics', () => { ), ); render(); - const loading = screen.getByText(/Loading.../i); - expect(loading).toBeInTheDocument(); const sampleAppText = await screen.findByText(/SampleApp/i); expect(sampleAppText).toBeInTheDocument(); const testAppText = await screen.findByText(/TestApp/i); diff --git a/frontend/src/container/ServiceApplication/index.tsx b/frontend/src/container/ServiceApplication/index.tsx index 5cfce03ee2..9a5157061d 100644 --- a/frontend/src/container/ServiceApplication/index.tsx +++ b/frontend/src/container/ServiceApplication/index.tsx @@ -1,5 +1,7 @@ import { FeatureKeys } from 'constants/features'; import useFeatureFlag from 'hooks/useFeatureFlag'; +import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; +import { ErrorBoundary } from 'react-error-boundary'; import ServiceMetrics from './ServiceMetrics'; import ServiceTraces from './ServiceTraces'; @@ -10,9 +12,11 @@ function Services(): JSX.Element { ?.active; return ( - - {isSpanMetricEnabled ? : } - + + + {isSpanMetricEnabled ? : } + + ); } diff --git a/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx b/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx index 8594ccd90c..057aec82c4 100644 --- a/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx +++ b/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx @@ -1,7 +1,9 @@ -import Graph from 'components/Graph'; import Spinner from 'components/Spinner'; -import getChartData from 'lib/getChartData'; -import { useMemo } from 'react'; +import Uplot from 'components/Uplot'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartData'; +import { getUPlotChartData } from 'lib/uPlotLib/utils/getChartData'; +import { useMemo, useRef } from 'react'; import { SuccessResponse } from 'types/api'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; @@ -13,31 +15,45 @@ function TimeSeriesView({ isError, yAxisUnit, }: TimeSeriesViewProps): JSX.Element { - const chartData = useMemo( - () => - getChartData({ - queryData: [ - { - queryData: data?.payload?.data?.result || [], - }, - ], - }), - [data?.payload?.data?.result], - ); + const graphRef = useRef(null); + + const chartData = useMemo(() => getUPlotChartData(data?.payload), [ + data?.payload, + ]); + + const isDarkMode = useIsDarkMode(); + + const width = graphRef.current?.clientWidth + ? graphRef.current.clientWidth + : 700; + + const height = graphRef.current?.clientWidth + ? graphRef.current.clientHeight + : 300; + + const chartOptions = getUPlotChartOptions({ + yAxisUnit: yAxisUnit || '', + apiResponse: data?.payload, + dimensions: { + width, + height, + }, + isDarkMode, + }); return ( {isLoading && } {isError && {data?.error || 'Something went wrong'}} - {!isLoading && !isError && ( - - )} +
+ {!isLoading && !isError && chartData && chartOptions && ( + + )} +
); } diff --git a/frontend/src/container/TriggeredAlerts/NoFilterTable.tsx b/frontend/src/container/TriggeredAlerts/NoFilterTable.tsx index 84219daf12..f5aca8d4d8 100644 --- a/frontend/src/container/TriggeredAlerts/NoFilterTable.tsx +++ b/frontend/src/container/TriggeredAlerts/NoFilterTable.tsx @@ -1,7 +1,8 @@ /* eslint-disable react/display-name */ -import { Tag, Typography } from 'antd'; +import { Typography } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import { ResizeTable } from 'components/ResizeTable'; +import LabelColumn from 'components/TableRenderer/LabelColumn'; import AlertStatus from 'container/TriggeredAlerts/TableComponents/AlertStatus'; import convertDateToAmAndPm from 'lib/convertDateToAmAndPm'; import getFormattedDate from 'lib/getFormatedDate'; @@ -54,11 +55,7 @@ function NoFilterTable({ } return ( - <> - {withOutSeverityKeys.map((e) => ( - {`${e} : ${labels[e]}`} - ))} - + ); }, }, diff --git a/frontend/src/globalStyles.ts b/frontend/src/globalStyles.ts deleted file mode 100644 index 86dc8258ed..0000000000 --- a/frontend/src/globalStyles.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createGlobalStyle } from 'styled-components'; - -const GlobalStyles = createGlobalStyle` -#root, -html, -body { - height: 100%; - overflow: hidden; -} - -body { - padding: 0; - margin: 0; - box-sizing: border-box; -} -`; - -export default GlobalStyles; diff --git a/frontend/src/hooks/dashboard/utils.ts b/frontend/src/hooks/dashboard/utils.ts index 84fa59332d..ba02cea7a4 100644 --- a/frontend/src/hooks/dashboard/utils.ts +++ b/frontend/src/hooks/dashboard/utils.ts @@ -15,7 +15,7 @@ export const addEmptyWidgetInDashboardJSONWithQuery = ( i: 'empty', w: 6, x: 0, - h: 2, + h: 3, y: 0, }, ...(dashboard?.data?.layout || []), diff --git a/frontend/src/hooks/useDebounce.tsx b/frontend/src/hooks/useDebounce.tsx index 88214a43b1..496222804d 100644 --- a/frontend/src/hooks/useDebounce.tsx +++ b/frontend/src/hooks/useDebounce.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; -export default function useDebounce(value: T, delay: number): T { +export default function useDebounce(value: T, delay = 500): T { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { diff --git a/frontend/src/hooks/useDimensions.ts b/frontend/src/hooks/useDimensions.ts new file mode 100644 index 0000000000..da11ea7681 --- /dev/null +++ b/frontend/src/hooks/useDimensions.ts @@ -0,0 +1,39 @@ +import debounce from 'lodash-es/debounce'; +import { useEffect, useState } from 'react'; + +export type Dimensions = { + width: number; + height: number; +}; + +export function useResizeObserver( + ref: React.RefObject, + debounceTime = 300, +): Dimensions { + const [size, setSize] = useState({ + width: ref.current?.clientWidth || 0, + height: ref.current?.clientHeight || 0, + }); + + // eslint-disable-next-line consistent-return + useEffect(() => { + if (ref.current) { + const handleResize = debounce((entries: ResizeObserverEntry[]) => { + const entry = entries[0]; + if (entry) { + const { width, height } = entry.contentRect; + setSize({ width, height }); + } + }, debounceTime); + + const ro = new ResizeObserver(handleResize); + ro.observe(ref.current); + + return (): void => { + ro.disconnect(); + }; + } + }, [ref, debounceTime]); + + return size; +} diff --git a/frontend/src/hooks/useIntersectionObserver.ts b/frontend/src/hooks/useIntersectionObserver.ts new file mode 100644 index 0000000000..50a23608e9 --- /dev/null +++ b/frontend/src/hooks/useIntersectionObserver.ts @@ -0,0 +1,38 @@ +import { RefObject, useEffect, useState } from 'react'; + +export function useIntersectionObserver( + ref: RefObject, + options?: IntersectionObserverInit, + isObserverOnce?: boolean, +): boolean { + const [isIntersecting, setIntersecting] = useState(false); + + useEffect(() => { + const currentReference = ref?.current; + + const observer = new IntersectionObserver(([entry]) => { + if (entry.isIntersecting) { + setIntersecting(true); + + if (isObserverOnce) { + // Optionally: Once it becomes visible, we don't need to observe it anymore + observer.unobserve(entry.target); + } + } else { + setIntersecting(false); + } + }, options); + + if (currentReference) { + observer.observe(currentReference); + } + + return (): void => { + if (currentReference) { + observer.unobserve(currentReference); + } + }; + }, [ref, options, isObserverOnce]); + + return isIntersecting; +} diff --git a/frontend/src/index.html.ejs b/frontend/src/index.html.ejs index 227506006b..2bbd3ed880 100644 --- a/frontend/src/index.html.ejs +++ b/frontend/src/index.html.ejs @@ -56,6 +56,11 @@ href="https://fonts.googleapis.com/css?family=Fira+Code" rel="stylesheet" /> + + diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 712fb4cc58..d872b6adc2 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,9 +1,11 @@ import './ReactI18'; +import 'styles.scss'; import AppRoutes from 'AppRoutes'; -import GlobalStyles from 'globalStyles'; import { ThemeProvider } from 'hooks/useDarkMode'; +import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { createRoot } from 'react-dom/client'; +import { ErrorBoundary } from 'react-error-boundary'; import { HelmetProvider } from 'react-helmet-async'; import { QueryClient, QueryClientProvider } from 'react-query'; import { ReactQueryDevtools } from 'react-query/devtools'; @@ -24,18 +26,20 @@ if (container) { const root = createRoot(container); root.render( - - - - - - - - {process.env.NODE_ENV === 'development' && ( - - )} - - - , + + + + + + + + {process.env.NODE_ENV === 'development' && ( + + )} + + + + , + , ); } diff --git a/frontend/src/lib/getConvertedValue.ts b/frontend/src/lib/getConvertedValue.ts new file mode 100644 index 0000000000..8ebc2135ba --- /dev/null +++ b/frontend/src/lib/getConvertedValue.ts @@ -0,0 +1,283 @@ +const unitsMapping = [ + { + label: 'Data', + options: [ + { + label: 'bytes(IEC)', + value: 'bytes', + factor: 1, + }, + { + label: 'bytes(SI)', + value: 'decbytes', + factor: 1, + }, + { + label: 'bits(IEC)', + value: 'bits', + factor: 8, // 1 byte = 8 bits + }, + { + label: 'bits(SI)', + value: 'decbits', + factor: 8, // 1 byte = 8 bits + }, + { + label: 'kibibytes', + value: 'kbytes', + factor: 1024, + }, + { + label: 'kilobytes', + value: 'deckbytes', + factor: 1000, + }, + { + label: 'mebibytes', + value: 'mbytes', + factor: 1024 * 1024, + }, + { + label: 'megabytes', + value: 'decmbytes', + factor: 1000 * 1000, + }, + { + label: 'gibibytes', + value: 'gbytes', + factor: 1024 * 1024 * 1024, + }, + { + label: 'gigabytes', + value: 'decgbytes', + factor: 1000 * 1000 * 1000, + }, + { + label: 'tebibytes', + value: 'tbytes', + factor: 1024 * 1024 * 1024 * 1024, + }, + { + label: 'terabytes', + value: 'dectbytes', + factor: 1000 * 1000 * 1000 * 1000, + }, + { + label: 'pebibytes', + value: 'pbytes', + factor: 1024 * 1024 * 1024 * 1024 * 1024, + }, + { + label: 'petabytes', + value: 'decpbytes', + factor: 1000 * 1000 * 1000 * 1000 * 1000, + }, + ], + }, + { + label: 'DataRate', + options: [ + { + label: 'bytes/sec(IEC)', + value: 'binBps', + factor: 1, + }, + { + label: 'bytes/sec(SI)', + value: 'Bps', + factor: 1, + }, + { + label: 'bits/sec(IEC)', + value: 'binbps', + factor: 8, // 1 byte = 8 bits + }, + { + label: 'bits/sec(SI)', + value: 'bps', + factor: 8, // 1 byte = 8 bits + }, + { + label: 'kibibytes/sec', + value: 'KiBs', + factor: 1024, + }, + { + label: 'kibibits/sec', + value: 'Kibits', + factor: 8 * 1024, // 1 KiB = 8 Kibits + }, + { + label: 'kilobytes/sec', + value: 'KBs', + factor: 1000, + }, + { + label: 'kilobits/sec', + value: 'Kbits', + factor: 8 * 1000, // 1 KB = 8 Kbits + }, + { + label: 'mebibytes/sec', + value: 'MiBs', + factor: 1024 * 1024, + }, + { + label: 'mebibits/sec', + value: 'Mibits', + factor: 8 * 1024 * 1024, // 1 MiB = 8 Mibits + }, + // ... (other options) + ], + }, + { + label: 'Time', + options: [ + { + label: 'nanoseconds (ns)', + value: 'ns', + factor: 1, + }, + { + label: 'microseconds (µs)', + value: 'µs', + factor: 1000, // 1 ms = 1000 µs + }, + { + label: 'milliseconds (ms)', + value: 'ms', + factor: 1000 * 1000, // 1 s = 1000 ms + }, + { + label: 'seconds (s)', + value: 's', + factor: 1000 * 1000 * 1000, // 1 s = 1000 ms + }, + { + label: 'minutes (m)', + value: 'm', + factor: 60 * 1000 * 1000 * 1000, // 1 m = 60 s + }, + { + label: 'hours (h)', + value: 'h', + factor: 60 * 60 * 1000 * 1000 * 1000, // 1 h = 60 m + }, + { + label: 'days (d)', + value: 'd', + factor: 24 * 60 * 60 * 1000 * 1000 * 1000, // 1 d = 24 h + }, + ], + }, + { + label: 'Throughput', + options: [ + { + label: 'counts/sec (cps)', + value: 'cps', + factor: 1, + }, + { + label: 'ops/sec (ops)', + value: 'ops', + factor: 1, + }, + { + label: 'requests/sec (reqps)', + value: 'reqps', + factor: 1, + }, + { + label: 'reads/sec (rps)', + value: 'rps', + factor: 1, + }, + { + label: 'writes/sec (wps)', + value: 'wps', + factor: 1, + }, + { + label: 'I/O operations/sec (iops)', + value: 'iops', + factor: 1, + }, + { + label: 'counts/min (cpm)', + value: 'cpm', + factor: 60, // 1 cpm = 60 cps + }, + { + label: 'ops/min (opm)', + value: 'opm', + factor: 60, // 1 opm = 60 ops + }, + { + label: 'reads/min (rpm)', + value: 'rpm', + factor: 60, // 1 rpm = 60 rps + }, + { + label: 'writes/min (wpm)', + value: 'wpm', + factor: 60, // 1 wpm = 60 wps + }, + // ... (other options) + ], + }, + { + label: 'Miscellaneous', + options: [ + { + label: 'Percent (0.0-1.0)', + value: 'percentunit', + factor: 1, + }, + ], + }, + { + label: 'Boolean', + options: [ + { + label: 'True / False', + value: 'bool', + factor: 1, + }, + { + label: 'Yes / No', + value: 'bool_yes_no', + factor: 1, + }, + ], + }, +]; + +function findUnitObject( + unitValue: string, +): { label: string; value: string; factor: number } | null { + const unitObj = unitsMapping + .map((category) => category.options.find((unit) => unit.value === unitValue)) + .find(Boolean); + + return unitObj || null; +} + +export function convertValue( + value: number, + currentUnit: string, + targetUnit: string, +): number | null { + if (targetUnit === 'none') { + return value; + } + const currentUnitObj = findUnitObject(currentUnit); + const targetUnitObj = findUnitObject(targetUnit); + + if (currentUnitObj && targetUnitObj) { + const baseValue = value * currentUnitObj.factor; + + return baseValue / targetUnitObj.factor; + } + return null; +} diff --git a/frontend/src/lib/getRandomColor.ts b/frontend/src/lib/getRandomColor.ts index 6cca527b32..c24106fb96 100644 --- a/frontend/src/lib/getRandomColor.ts +++ b/frontend/src/lib/getRandomColor.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-bitwise */ import { Span } from 'types/api/trace/getTraceItem'; import { themeColors } from '../constants/theme'; @@ -13,6 +14,33 @@ const getRandomColor = (): string => { return colors[index]; }; +// eslint-disable-next-line @typescript-eslint/no-inferrable-types +export function hexToRgba(hex: string, alpha: number = 1): string { + // Create a new local variable to work with + let hexColor = hex; + + // Ensure the hex string has a "#" at the start + if (hexColor.charAt(0) === '#') { + hexColor = hexColor.slice(1); + } + + // Check if it's a shorthand hex code (e.g., #FFF) + if (hexColor.length === 3) { + const r = hexColor.charAt(0); + const g = hexColor.charAt(1); + const b = hexColor.charAt(2); + hexColor = r + r + g + g + b + b; + } + + // Parse the r, g, b values + const bigint = parseInt(hexColor, 16); + const r = (bigint >> 16) & 255; + const g = (bigint >> 8) & 255; + const b = bigint & 255; + + return `rgba(${r}, ${g}, ${b}, ${alpha})`; +} + export const SIGNOZ_UI_COLOR_HEX = 'signoz_ui_color_hex'; export const spanServiceNameToColorMapping = ( diff --git a/frontend/src/lib/uPlotLib/getUplotChartData.ts b/frontend/src/lib/uPlotLib/getUplotChartData.ts new file mode 100644 index 0000000000..98b0f14f89 --- /dev/null +++ b/frontend/src/lib/uPlotLib/getUplotChartData.ts @@ -0,0 +1,179 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +// @ts-nocheck +/* eslint-disable sonarjs/cognitive-complexity */ +import './uPlotLib.styles.scss'; + +import { FullViewProps } from 'container/GridCardLayout/GridCard/FullView/types'; +import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types'; +import { Dimensions } from 'hooks/useDimensions'; +import { convertValue } from 'lib/getConvertedValue'; +import _noop from 'lodash-es/noop'; +import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; +import uPlot from 'uplot'; + +import onClickPlugin, { OnClickPluginOpts } from './plugins/onClickPlugin'; +import tooltipPlugin from './plugins/tooltipPlugin'; +import getAxes from './utils/getAxes'; +import getSeries from './utils/getSeriesData'; + +interface GetUPlotChartOptions { + id?: string; + apiResponse?: MetricRangePayloadProps; + dimensions: Dimensions; + isDarkMode: boolean; + onDragSelect?: (startTime: number, endTime: number) => void; + yAxisUnit?: string; + onClickHandler?: OnClickPluginOpts['onClick']; + graphsVisibilityStates?: boolean[]; + setGraphsVisibilityStates?: FullViewProps['setGraphsVisibilityStates']; + thresholds?: ThresholdProps[]; + thresholdValue?: number; + thresholdText?: string; + fillSpans?: boolean; +} + +export const getUPlotChartOptions = ({ + id, + dimensions, + isDarkMode, + apiResponse, + onDragSelect, + yAxisUnit, + onClickHandler = _noop, + graphsVisibilityStates, + setGraphsVisibilityStates, + thresholds, + fillSpans, +}: GetUPlotChartOptions): uPlot.Options => ({ + id, + width: dimensions.width, + height: dimensions.height - 45, + // tzDate: (ts) => uPlot.tzDate(new Date(ts * 1e3), ''), // Pass timezone for 2nd param + legend: { + show: true, + live: false, + }, + focus: { + alpha: 0.3, + }, + cursor: { + focus: { + prox: 1e6, + bias: 1, + }, + points: { + size: (u, seriesIdx): number => u.series[seriesIdx].points.size * 2.5, + width: (u, seriesIdx, size): number => size / 4, + stroke: (u, seriesIdx): string => + `${u.series[seriesIdx].points.stroke(u, seriesIdx)}90`, + fill: (): string => '#fff', + }, + }, + padding: [16, 16, 16, 16], + scales: { + x: { + time: true, + auto: true, // Automatically adjust scale range + }, + y: { + auto: true, + }, + }, + plugins: [ + tooltipPlugin(apiResponse, yAxisUnit, fillSpans), + onClickPlugin({ + onClick: onClickHandler, + }), + ], + hooks: { + draw: [ + (u): void => { + thresholds?.forEach((threshold) => { + if (threshold.thresholdValue !== undefined) { + const { ctx } = u; + ctx.save(); + + const yPos = u.valToPos( + convertValue( + threshold.thresholdValue, + threshold.thresholdUnit, + yAxisUnit, + ), + 'y', + true, + ); + + ctx.strokeStyle = threshold.thresholdColor || 'red'; + ctx.lineWidth = 2; + ctx.setLineDash([10, 5]); + + ctx.beginPath(); + + const plotLeft = u.bbox.left; // left edge of the plot area + const plotRight = plotLeft + u.bbox.width; // right edge of the plot area + + ctx.moveTo(plotLeft, yPos); + ctx.lineTo(plotRight, yPos); + + ctx.stroke(); + + // Text configuration + if (threshold.thresholdLabel) { + const text = threshold.thresholdLabel; + const textX = plotRight - ctx.measureText(text).width - 20; + const textY = yPos - 15; + ctx.fillStyle = threshold.thresholdColor || 'red'; + ctx.fillText(text, textX, textY); + } + + ctx.restore(); + } + }); + }, + ], + setSelect: [ + (self): void => { + const selection = self.select; + if (selection) { + const startTime = self.posToVal(selection.left, 'x'); + const endTime = self.posToVal(selection.left + selection.width, 'x'); + + const diff = endTime - startTime; + + if (typeof onDragSelect === 'function' && diff > 0) { + onDragSelect(startTime * 1000, endTime * 1000); + } + } + }, + ], + ready: [ + (self): void => { + const legend = self.root.querySelector('.u-legend'); + if (legend) { + const seriesEls = legend.querySelectorAll('.u-label'); + const seriesArray = Array.from(seriesEls); + seriesArray.forEach((seriesEl, index) => { + seriesEl.addEventListener('click', () => { + if (graphsVisibilityStates) { + setGraphsVisibilityStates?.((prev) => { + const newGraphVisibilityStates = [...prev]; + newGraphVisibilityStates[index + 1] = !newGraphVisibilityStates[ + index + 1 + ]; + return newGraphVisibilityStates; + }); + } + }); + }); + } + }, + ], + }, + series: getSeries( + apiResponse, + apiResponse?.data.result, + graphsVisibilityStates, + fillSpans, + ), + axes: getAxes(isDarkMode, yAxisUnit), +}); diff --git a/frontend/src/lib/uPlotLib/placement.ts b/frontend/src/lib/uPlotLib/placement.ts new file mode 100644 index 0000000000..629e2dbcb2 --- /dev/null +++ b/frontend/src/lib/uPlotLib/placement.ts @@ -0,0 +1,114 @@ +/* eslint-disable radix */ +/* eslint-disable guard-for-in */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable no-var */ +/* eslint-disable vars-on-top */ +/* eslint-disable func-style */ +/* eslint-disable no-void */ +/* eslint-disable sonarjs/cognitive-complexity */ +/* eslint-disable func-names */ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +/* eslint-disable @typescript-eslint/no-unused-expressions */ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-sequences */ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +// @ts-nocheck + +// https://tobyzerner.github.io/placement.js/dist/index.js + +export const placement = (function () { + const e = { + size: ['height', 'width'], + clientSize: ['clientHeight', 'clientWidth'], + offsetSize: ['offsetHeight', 'offsetWidth'], + maxSize: ['maxHeight', 'maxWidth'], + before: ['top', 'left'], + marginBefore: ['marginTop', 'marginLeft'], + after: ['bottom', 'right'], + marginAfter: ['marginBottom', 'marginRight'], + scrollOffset: ['pageYOffset', 'pageXOffset'], + }; + function t(e) { + return { top: e.top, bottom: e.bottom, left: e.left, right: e.right }; + } + return function (o, r, f, a, i) { + void 0 === f && (f = 'bottom'), + void 0 === a && (a = 'center'), + void 0 === i && (i = {}), + (r instanceof Element || r instanceof Range) && + (r = t(r.getBoundingClientRect())); + const n = { + top: r.bottom, + bottom: r.top, + left: r.right, + right: r.left, + ...r, + }; + const s = { + top: 0, + left: 0, + bottom: window.innerHeight, + right: window.innerWidth, + }; + i.bound && + ((i.bound instanceof Element || i.bound instanceof Range) && + (i.bound = t(i.bound.getBoundingClientRect())), + Object.assign(s, i.bound)); + const l = getComputedStyle(o); + const m = {}; + const b = {}; + for (const g in e) + (m[g] = e[g][f === 'top' || f === 'bottom' ? 0 : 1]), + (b[g] = e[g][f === 'top' || f === 'bottom' ? 1 : 0]); + (o.style.position = 'absolute'), + (o.style.maxWidth = ''), + (o.style.maxHeight = ''); + const d = parseInt(l[b.marginBefore]); + const c = parseInt(l[b.marginAfter]); + const u = d + c; + const p = s[b.after] - s[b.before] - u; + const h = parseInt(l[b.maxSize]); + (!h || p < h) && (o.style[b.maxSize] = `${p}px`); + const x = parseInt(l[m.marginBefore]) + parseInt(l[m.marginAfter]); + const y = n[m.before] - s[m.before] - x; + const z = s[m.after] - n[m.after] - x; + ((f === m.before && o[m.offsetSize] > y) || + (f === m.after && o[m.offsetSize] > z)) && + (f = y > z ? m.before : m.after); + const S = f === m.before ? y : z; + const v = parseInt(l[m.maxSize]); + (!v || S < v) && (o.style[m.maxSize] = `${S}px`); + const w = window[m.scrollOffset]; + const O = function (e) { + return Math.max(s[m.before], Math.min(e, s[m.after] - o[m.offsetSize] - x)); + }; + f === m.before + ? ((o.style[m.before] = `${w + O(n[m.before] - o[m.offsetSize] - x)}px`), + (o.style[m.after] = 'auto')) + : ((o.style[m.before] = `${w + O(n[m.after])}px`), + (o.style[m.after] = 'auto')); + const B = window[b.scrollOffset]; + const I = function (e) { + return Math.max(s[b.before], Math.min(e, s[b.after] - o[b.offsetSize] - u)); + }; + switch (a) { + case 'start': + (o.style[b.before] = `${B + I(n[b.before] - d)}px`), + (o.style[b.after] = 'auto'); + break; + case 'end': + (o.style[b.before] = 'auto'), + (o.style[b.after] = `${ + B + I(document.documentElement[b.clientSize] - n[b.after] - c) + }px`); + break; + default: + var H = n[b.after] - n[b.before]; + (o.style[b.before] = `${ + B + I(n[b.before] + H / 2 - o[b.offsetSize] / 2 - d) + }px`), + (o.style[b.after] = 'auto'); + } + (o.dataset.side = f), (o.dataset.align = a); + }; +})(); diff --git a/frontend/src/lib/uPlotLib/plugins/onClickPlugin.ts b/frontend/src/lib/uPlotLib/plugins/onClickPlugin.ts new file mode 100644 index 0000000000..56a6f1e333 --- /dev/null +++ b/frontend/src/lib/uPlotLib/plugins/onClickPlugin.ts @@ -0,0 +1,39 @@ +export interface OnClickPluginOpts { + onClick: ( + xValue: number, + yValue: number, + mouseX: number, + mouseY: number, + ) => void; +} + +function onClickPlugin(opts: OnClickPluginOpts): uPlot.Plugin { + let handleClick: (event: MouseEvent) => void; + + const hooks: uPlot.Plugin['hooks'] = { + init: (u: uPlot) => { + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + handleClick = function (event: MouseEvent) { + const mouseX = event.offsetX + 40; + const mouseY = event.offsetY + 40; + + // Convert pixel positions to data values + const xValue = u.posToVal(mouseX, 'x'); + const yValue = u.posToVal(mouseY, 'y'); + + opts.onClick(xValue, yValue, mouseX, mouseY); + }; + + u.over.addEventListener('click', handleClick); + }, + destroy: (u: uPlot) => { + u.over.removeEventListener('click', handleClick); + }, + }; + + return { + hooks, + }; +} + +export default onClickPlugin; diff --git a/frontend/src/lib/uPlotLib/plugins/tooltipPlugin.ts b/frontend/src/lib/uPlotLib/plugins/tooltipPlugin.ts new file mode 100644 index 0000000000..2840b0ffc2 --- /dev/null +++ b/frontend/src/lib/uPlotLib/plugins/tooltipPlugin.ts @@ -0,0 +1,150 @@ +import { getToolTipValue } from 'components/Graph/yAxisConfig'; +import dayjs from 'dayjs'; +import customParseFormat from 'dayjs/plugin/customParseFormat'; +import getLabelName from 'lib/getLabelName'; +import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; + +import { colors } from '../../getRandomColor'; +import { placement } from '../placement'; + +dayjs.extend(customParseFormat); + +const createDivsFromArray = ( + seriesList: any[], + data: any[], + idx: number, + yAxisUnit?: string, + series?: uPlot.Options['series'], + fillSpans?: boolean, + // eslint-disable-next-line sonarjs/cognitive-complexity +): HTMLElement => { + const container = document.createElement('div'); + container.classList.add('tooltip-container'); + + if (Array.isArray(series) && series.length > 0) { + series.forEach((item, index) => { + const div = document.createElement('div'); + div.classList.add('tooltip-content-row'); + + if (index === 0) { + const formattedDate = dayjs(data[0][idx] * 1000).format( + 'MMM DD YYYY HH:mm:ss', + ); + + div.textContent = formattedDate; + div.classList.add('tooltip-content-header'); + } else if (fillSpans ? item.show : item.show && data[index][idx]) { + div.classList.add('tooltip-content'); + const color = colors[(index - 1) % colors.length]; + + const squareBox = document.createElement('div'); + squareBox.classList.add('pointSquare'); + + squareBox.style.borderColor = color; + + const text = document.createElement('div'); + text.classList.add('tooltip-data-point'); + + const { metric = {}, queryName = '', legend = '' } = + seriesList[index - 1] || {}; + + const label = getLabelName( + metric, + queryName || '', // query + legend || '', + ); + + const value = data[index][idx] || 0; + + const tooltipValue = getToolTipValue(value, yAxisUnit); + + text.textContent = `${label} : ${tooltipValue || 0}`; + text.style.color = color; + + div.appendChild(squareBox); + div.appendChild(text); + } + + container.appendChild(div); + }); + } + + return container; +}; + +const tooltipPlugin = ( + apiResponse: MetricRangePayloadProps | undefined, + yAxisUnit?: string, + fillSpans?: boolean, +): any => { + let over: HTMLElement; + let bound: HTMLElement; + let bLeft: any; + let bTop: any; + + const syncBounds = (): void => { + const bbox = over.getBoundingClientRect(); + bLeft = bbox.left; + bTop = bbox.top; + }; + + let overlay = document.getElementById('overlay'); + + if (!overlay) { + overlay = document.createElement('div'); + overlay.id = 'overlay'; + overlay.style.display = 'none'; + overlay.style.position = 'absolute'; + document.body.appendChild(overlay); + } + + const apiResult = apiResponse?.data?.result || []; + + return { + hooks: { + init: (u: any): void => { + over = u?.over; + bound = over; + over.onmouseenter = (): void => { + if (overlay) { + overlay.style.display = 'block'; + } + }; + over.onmouseleave = (): void => { + if (overlay) { + overlay.style.display = 'none'; + } + }; + }, + setSize: (): void => { + syncBounds(); + }, + setCursor: (u: { + cursor: { left: any; top: any; idx: any }; + data: any[]; + series: uPlot.Options['series']; + }): void => { + if (overlay) { + overlay.textContent = ''; + const { left, top, idx } = u.cursor; + + if (idx) { + const anchor = { left: left + bLeft, top: top + bTop }; + const content = createDivsFromArray( + apiResult, + u.data, + idx, + yAxisUnit, + u.series, + fillSpans, + ); + overlay.appendChild(content); + placement(overlay, anchor, 'right', 'start', { bound }); + } + } + }, + }, + }; +}; + +export default tooltipPlugin; diff --git a/frontend/src/lib/uPlotLib/uPlotLib.styles.scss b/frontend/src/lib/uPlotLib/uPlotLib.styles.scss new file mode 100644 index 0000000000..42bb247772 --- /dev/null +++ b/frontend/src/lib/uPlotLib/uPlotLib.styles.scss @@ -0,0 +1,29 @@ +.pointSquare { + width: 12px; + height: 12px; + background-color: transparent; + border: 2px solid white; + box-sizing: border-box; + border-radius: 50%; +} + +.tooltip-content-header { + margin-bottom: 8px; + font-size: 13px; +} + +.tooltip-data-point { + font-size: 11px; +} + +.tooltip-content { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 4px; + + .pointSquare, + .tooltip-data-point { + font-size: 13px !important; + } +} diff --git a/frontend/src/lib/uPlotLib/utils/getAxes.ts b/frontend/src/lib/uPlotLib/utils/getAxes.ts new file mode 100644 index 0000000000..8c4a926ccd --- /dev/null +++ b/frontend/src/lib/uPlotLib/utils/getAxes.ts @@ -0,0 +1,67 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +// @ts-nocheck +import { getToolTipValue } from 'components/Graph/yAxisConfig'; + +import getGridColor from './getGridColor'; + +const getAxes = (isDarkMode: boolean, yAxisUnit?: string): any => [ + { + stroke: isDarkMode ? 'white' : 'black', // Color of the axis line + grid: { + stroke: getGridColor(isDarkMode), // Color of the grid lines + dash: [10, 10], // Dash pattern for grid lines, + width: 0.5, // Width of the grid lines, + show: true, + }, + ticks: { + // stroke: isDarkMode ? 'white' : 'black', // Color of the tick lines + width: 0.3, // Width of the tick lines, + show: true, + }, + gap: 5, + }, + { + stroke: isDarkMode ? 'white' : 'black', // Color of the axis line + grid: { + stroke: getGridColor(isDarkMode), // Color of the grid lines + dash: [10, 10], // Dash pattern for grid lines, + width: 0.3, // Width of the grid lines + }, + ticks: { + // stroke: isDarkMode ? 'white' : 'black', // Color of the tick lines + width: 0.3, // Width of the tick lines + show: true, + }, + values: (_, t): string[] => + t.map((v) => { + const value = getToolTipValue(v.toString(), yAxisUnit); + + return `${value}`; + }), + gap: 5, + size: (self, values, axisIdx, cycleNum): number => { + const axis = self.axes[axisIdx]; + + // bail out, force convergence + if (cycleNum > 1) return axis._size; + + let axisSize = axis.ticks.size + axis.gap; + + // find longest value + const longestVal = (values ?? []).reduce( + (acc, val) => (val.length > acc.length ? val : acc), + '', + ); + + if (longestVal !== '' && self) { + // eslint-disable-next-line prefer-destructuring, no-param-reassign + self.ctx.font = axis.font[0]; + axisSize += self.ctx.measureText(longestVal).width / devicePixelRatio; + } + + return Math.ceil(axisSize); + }, + }, +]; + +export default getAxes; diff --git a/frontend/src/lib/uPlotLib/utils/getChartData.ts b/frontend/src/lib/uPlotLib/utils/getChartData.ts new file mode 100644 index 0000000000..b80ccec9dc --- /dev/null +++ b/frontend/src/lib/uPlotLib/utils/getChartData.ts @@ -0,0 +1,36 @@ +import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; + +export const getUPlotChartData = ( + apiResponse?: MetricRangePayloadProps, + fillSpans?: boolean, +): uPlot.AlignedData => { + const seriesList = apiResponse?.data?.result || []; + const uPlotData: uPlot.AlignedData = []; + + // sort seriesList + for (let index = 0; index < seriesList.length; index += 1) { + seriesList[index]?.values?.sort((a, b) => a[0] - b[0]); + } + + // timestamp + uPlotData.push(new Float64Array(seriesList[0]?.values?.map((v) => v[0]))); + + const numberOfTimestamps = uPlotData[0].length; + + // for each series, push the values + seriesList.forEach((series) => { + const seriesData = series?.values?.map((v) => parseFloat(v[1])) || []; + + // fill rest of the value with zero + if (seriesData.length < numberOfTimestamps && fillSpans) { + const diff = numberOfTimestamps - seriesData.length; + for (let i = 0; i < diff; i += 1) { + seriesData.push(0); + } + } + + uPlotData.push(new Float64Array(seriesData)); + }); + + return uPlotData; +}; diff --git a/frontend/src/lib/uPlotLib/utils/getGridColor.ts b/frontend/src/lib/uPlotLib/utils/getGridColor.ts new file mode 100644 index 0000000000..72557e192c --- /dev/null +++ b/frontend/src/lib/uPlotLib/utils/getGridColor.ts @@ -0,0 +1,8 @@ +const getGridColor = (isDarkMode: boolean): string => { + if (isDarkMode) { + return 'rgba(231,233,237,0.2)'; + } + return 'rgba(231,233,237,0.8)'; +}; + +export default getGridColor; diff --git a/frontend/src/lib/uPlotLib/utils/getRenderer.ts b/frontend/src/lib/uPlotLib/utils/getRenderer.ts new file mode 100644 index 0000000000..564a4532b0 --- /dev/null +++ b/frontend/src/lib/uPlotLib/utils/getRenderer.ts @@ -0,0 +1,31 @@ +import uPlot from 'uplot'; + +// Define type annotations for style and interp +export const drawStyles = { + line: 'line', + bars: 'bars', + barsLeft: 'barsLeft', + barsRight: 'barsRight', + points: 'points', +}; + +export const lineInterpolations = { + linear: 'linear', + stepAfter: 'stepAfter', + stepBefore: 'stepBefore', + spline: 'spline', +}; + +const { spline: splinePath } = uPlot.paths; + +const spline = splinePath && splinePath(); + +const getRenderer = (style: any, interp: any): any => { + if (style === drawStyles.line && interp === lineInterpolations.spline) { + return spline; + } + + return null; +}; + +export default getRenderer; diff --git a/frontend/src/lib/uPlotLib/utils/getSeriesData.ts b/frontend/src/lib/uPlotLib/utils/getSeriesData.ts new file mode 100644 index 0000000000..45d6796fbc --- /dev/null +++ b/frontend/src/lib/uPlotLib/utils/getSeriesData.ts @@ -0,0 +1,69 @@ +import getLabelName from 'lib/getLabelName'; +import { colors } from 'lib/getRandomColor'; +import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; +import { QueryData } from 'types/api/widgets/getQuery'; + +import getRenderer, { drawStyles, lineInterpolations } from './getRenderer'; + +const paths = ( + u: any, + seriesIdx: number, + idx0: number, + idx1: number, + extendGap: boolean, + buildClip: boolean, +): any => { + const s = u.series[seriesIdx]; + const style = s.drawStyle; + const interp = s.lineInterpolation; + + const renderer = getRenderer(style, interp); + + return renderer(u, seriesIdx, idx0, idx1, extendGap, buildClip); +}; + +const getSeries = ( + apiResponse?: MetricRangePayloadProps, + widgetMetaData: QueryData[] = [], + graphsVisibilityStates?: boolean[], +): uPlot.Options['series'] => { + const configurations: uPlot.Series[] = [ + { label: 'Timestamp', stroke: 'purple' }, + ]; + + const seriesList = apiResponse?.data.result || []; + + const newGraphVisibilityStates = graphsVisibilityStates?.slice(1); + + for (let i = 0; i < seriesList?.length; i += 1) { + const color = colors[i % colors.length]; // Use modulo to loop through colors if there are more series than colors + + const { metric = {}, queryName = '', legend = '' } = widgetMetaData[i] || {}; + + const label = getLabelName( + metric, + queryName || '', // query + legend || '', + ); + + const seriesObj: any = { + width: 1.4, + paths, + drawStyle: drawStyles.line, + lineInterpolation: lineInterpolations.spline, + show: newGraphVisibilityStates ? newGraphVisibilityStates[i] : true, + label, + stroke: color, + spanGaps: true, + points: { + show: false, + }, + }; + + configurations.push(seriesObj); + } + + return configurations; +}; + +export default getSeries; diff --git a/frontend/src/pages/DashboardWidget/index.tsx b/frontend/src/pages/DashboardWidget/index.tsx index 9dd439d86c..f26bc6b752 100644 --- a/frontend/src/pages/DashboardWidget/index.tsx +++ b/frontend/src/pages/DashboardWidget/index.tsx @@ -57,6 +57,7 @@ function DashboardWidget(): JSX.Element | null { ); } diff --git a/frontend/src/pages/ErrorBoundaryFallback/ErrorBoundaryFallback.styles.scss b/frontend/src/pages/ErrorBoundaryFallback/ErrorBoundaryFallback.styles.scss new file mode 100644 index 0000000000..78fd73bf0f --- /dev/null +++ b/frontend/src/pages/ErrorBoundaryFallback/ErrorBoundaryFallback.styles.scss @@ -0,0 +1,16 @@ +.error-boundary-fallback-container { + width: 100%; + + .actionBtn { + display: flex; + align-items: center; + gap: 4px; + } + + .title, + .actions { + display: flex; + align-items: center; + gap: 8px; + } +} diff --git a/frontend/src/pages/ErrorBoundaryFallback/ErrorBoundaryFallback.tsx b/frontend/src/pages/ErrorBoundaryFallback/ErrorBoundaryFallback.tsx new file mode 100644 index 0000000000..d8ceb5890f --- /dev/null +++ b/frontend/src/pages/ErrorBoundaryFallback/ErrorBoundaryFallback.tsx @@ -0,0 +1,54 @@ +import './ErrorBoundaryFallback.styles.scss'; + +import { BugOutlined, UndoOutlined } from '@ant-design/icons'; +import { Button, Card, Typography } from 'antd'; +import Slack from 'container/SideNav/Slack'; +import { useTranslation } from 'react-i18next'; + +function ErrorBoundaryFallback(): JSX.Element { + const { t } = useTranslation(['errorDetails']); + + const onClickSlackHandler = (): void => { + window.open('https://signoz.io/slack', '_blank'); + }; + + const handleReload = (): void => { + window.location.reload(); + }; + return ( + +
+ + + {t('something_went_wrong')} + +
+ + <> +

{t('contact_if_issue_exists')}

+ +
+ + + +
+ +
+ ); +} + +export default ErrorBoundaryFallback; diff --git a/frontend/src/pages/LogsExplorer/index.tsx b/frontend/src/pages/LogsExplorer/index.tsx index 3aa0f95d1b..d18eee199b 100644 --- a/frontend/src/pages/LogsExplorer/index.tsx +++ b/frontend/src/pages/LogsExplorer/index.tsx @@ -3,13 +3,15 @@ import ExplorerCard from 'components/ExplorerCard/ExplorerCard'; import LogExplorerQuerySection from 'container/LogExplorerQuerySection'; import LogsExplorerViews from 'container/LogsExplorerViews'; import LogsTopNav from 'container/LogsTopNav'; +import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; +import { ErrorBoundary } from 'react-error-boundary'; import { DataSource } from 'types/common/queryBuilder'; import { WrapperStyled } from './styles'; function LogsExplorer(): JSX.Element { return ( - <> + @@ -23,7 +25,7 @@ function LogsExplorer(): JSX.Element { - + ); } diff --git a/frontend/src/pages/MetricsApplication/ApDex/ApDexSettings.test.tsx b/frontend/src/pages/MetricsApplication/ApDex/ApDexSettings.test.tsx index daa95d1d56..82816bb534 100644 --- a/frontend/src/pages/MetricsApplication/ApDex/ApDexSettings.test.tsx +++ b/frontend/src/pages/MetricsApplication/ApDex/ApDexSettings.test.tsx @@ -28,7 +28,7 @@ describe('ApDexSettings', () => { }); it('should render the spinner when the data is loading', () => { - render( + const { container } = render( { />, ); - expect(screen.getByText('Loading...')).toBeInTheDocument(); + const loadingSpan = container.querySelector('[aria-label="loading"]'); + + // Assert that the loading span is found + expect(loadingSpan).toBeInTheDocument(); }); it('should close the popover when the cancel button is clicked', async () => { diff --git a/frontend/src/pages/Trace/index.tsx b/frontend/src/pages/Trace/index.tsx index acf8ec2eff..75bf58f1bd 100644 --- a/frontend/src/pages/Trace/index.tsx +++ b/frontend/src/pages/Trace/index.tsx @@ -9,7 +9,9 @@ import TraceTable from 'container/Trace/TraceTable'; import { useNotifications } from 'hooks/useNotifications'; import getStep from 'lib/getStep'; import history from 'lib/history'; +import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { MouseEventHandler, useCallback, useEffect, useState } from 'react'; +import { ErrorBoundary } from 'react-error-boundary'; import { connect, useDispatch, useSelector } from 'react-redux'; import { bindActionCreators, Dispatch } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; @@ -144,7 +146,7 @@ function Trace({ ); return ( - <> +
@@ -167,7 +169,7 @@ function Trace({ - + ); } diff --git a/frontend/src/pages/TracesExplorer/index.tsx b/frontend/src/pages/TracesExplorer/index.tsx index c83ade514a..42cbe1028b 100644 --- a/frontend/src/pages/TracesExplorer/index.tsx +++ b/frontend/src/pages/TracesExplorer/index.tsx @@ -13,7 +13,9 @@ import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; +import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { useCallback, useEffect, useMemo } from 'react'; +import { ErrorBoundary } from 'react-error-boundary'; import { Dashboard } from 'types/api/dashboard/getAll'; import { DataSource } from 'types/common/queryBuilder'; import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToDashboardLink'; @@ -168,28 +170,30 @@ function TracesExplorer(): JSX.Element { ]); return ( - <> - - - + + <> + + + - - - + + + + + - - - - - + + + ); } diff --git a/frontend/src/providers/Dashboard/Dashboard.tsx b/frontend/src/providers/Dashboard/Dashboard.tsx index 1074465d0b..c5678c18f0 100644 --- a/frontend/src/providers/Dashboard/Dashboard.tsx +++ b/frontend/src/providers/Dashboard/Dashboard.tsx @@ -1,11 +1,17 @@ -import { Modal } from 'antd'; +import Modal from 'antd/es/modal'; import get from 'api/dashboard/get'; +import lockDashboardApi from 'api/dashboard/lockDashboard'; +import unlockDashboardApi from 'api/dashboard/unlockDashboard'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import ROUTES from 'constants/routes'; import { getMinMax } from 'container/TopNav/AutoRefresh/config'; import dayjs, { Dayjs } from 'dayjs'; +import useAxiosError from 'hooks/useAxiosError'; import useTabVisibility from 'hooks/useTabFocus'; import { getUpdatedLayout } from 'lib/dashboard/getUpdatedLayout'; +import isEqual from 'lodash-es/isEqual'; +import isUndefined from 'lodash-es/isUndefined'; +import omitBy from 'lodash-es/omitBy'; import { createContext, PropsWithChildren, @@ -17,7 +23,7 @@ import { } from 'react'; import { Layout } from 'react-grid-layout'; import { useTranslation } from 'react-i18next'; -import { useQuery, UseQueryResult } from 'react-query'; +import { useMutation, useQuery, UseQueryResult } from 'react-query'; import { useDispatch, useSelector } from 'react-redux'; import { useRouteMatch } from 'react-router-dom'; import { Dispatch } from 'redux'; @@ -32,7 +38,9 @@ import { IDashboardContext } from './types'; const DashboardContext = createContext({ isDashboardSliderOpen: false, + isDashboardLocked: false, handleToggleDashboardSlider: () => {}, + handleDashboardLockToggle: () => {}, dashboardResponse: {} as UseQueryResult, selectedDashboard: {} as Dashboard, dashboardId: '', @@ -50,6 +58,9 @@ export function DashboardProvider({ children, }: PropsWithChildren): JSX.Element { const [isDashboardSliderOpen, setIsDashboardSlider] = useState(false); + + const [isDashboardLocked, setIsDashboardLocked] = useState(false); + const isDashboardPage = useRouteMatch({ path: ROUTES.DASHBOARD, exact: true, @@ -99,6 +110,8 @@ export function DashboardProvider({ onSuccess: (data) => { const updatedDate = dayjs(data.updated_at); + setIsDashboardLocked(data?.isLocked || false); + // on first render if (updatedTimeRef.current === null) { setSelectedDashboard(data); @@ -154,9 +167,18 @@ export function DashboardProvider({ dashboardRef.current = data; - setSelectedDashboard(data); + if (!isEqual(selectedDashboard, data)) { + setSelectedDashboard(data); + } - setLayouts(getUpdatedLayout(data.data.layout)); + if ( + !isEqual( + [omitBy(layouts, (value): boolean => isUndefined(value))[0]], + data.data.layout, + ) + ) { + setLayouts(getUpdatedLayout(data.data.layout)); + } } }, }, @@ -179,10 +201,39 @@ export function DashboardProvider({ setIsDashboardSlider(value); }; + const handleError = useAxiosError(); + + const { mutate: lockDashboard } = useMutation(lockDashboardApi, { + onSuccess: () => { + setIsDashboardSlider(false); + setIsDashboardLocked(true); + }, + onError: handleError, + }); + + const { mutate: unlockDashboard } = useMutation(unlockDashboardApi, { + onSuccess: () => { + setIsDashboardLocked(false); + }, + onError: handleError, + }); + + const handleDashboardLockToggle = async (value: boolean): Promise => { + if (selectedDashboard) { + if (value) { + lockDashboard(selectedDashboard); + } else { + unlockDashboard(selectedDashboard); + } + } + }; + const value: IDashboardContext = useMemo( () => ({ isDashboardSliderOpen, + isDashboardLocked, handleToggleDashboardSlider, + handleDashboardLockToggle, dashboardResponse, selectedDashboard, dashboardId, @@ -191,8 +242,10 @@ export function DashboardProvider({ setSelectedDashboard, updatedTimeRef, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [ isDashboardSliderOpen, + isDashboardLocked, dashboardResponse, selectedDashboard, dashboardId, diff --git a/frontend/src/providers/Dashboard/types.ts b/frontend/src/providers/Dashboard/types.ts index 9fc15c5f77..11e2da2212 100644 --- a/frontend/src/providers/Dashboard/types.ts +++ b/frontend/src/providers/Dashboard/types.ts @@ -5,7 +5,9 @@ import { Dashboard } from 'types/api/dashboard/getAll'; export interface IDashboardContext { isDashboardSliderOpen: boolean; + isDashboardLocked: boolean; handleToggleDashboardSlider: (value: boolean) => void; + handleDashboardLockToggle: (value: boolean) => void; dashboardResponse: UseQueryResult; selectedDashboard: Dashboard | undefined; dashboardId: string; diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss new file mode 100644 index 0000000000..10525a1f02 --- /dev/null +++ b/frontend/src/styles.scss @@ -0,0 +1,125 @@ +#root, +html, +body { + height: 100%; + overflow: hidden; +} + +body { + padding: 0; + margin: 0; + box-sizing: border-box; +} + +.u-legend { + max-height: 30px; // slicing the height of the widget Header height ; + overflow-y: auto; + overflow-x: hidden; + + &::-webkit-scrollbar { + width: 0.3rem; + } + &::-webkit-scrollbar-corner { + background: transparent; + } + &::-webkit-scrollbar-thumb { + background: rgb(136, 136, 136); + border-radius: 0.625rem; + } + &::-webkit-scrollbar-track { + background: transparent; + } + + tr.u-series { + th { + display: flex; + align-items: center; + gap: 4px; + font-size: 12px; + -webkit-font-smoothing: antialiased; + + .u-marker { + border-radius: 50%; + } + } + } +} + +/* Style the selected background */ +.u-select { + background: rgba(0, 0, 0, 0.5) !important; +} + +#overlay { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + font-size: 12px; + position: absolute; + margin: 0.5rem; + background: rgba(0, 0, 0, 0.9); + -webkit-font-smoothing: antialiased; + color: #fff; + z-index: 10000; + pointer-events: none; + overflow: auto; + max-height: 600px !important; + border-radius: 5px; + + .tooltip-container { + padding: 0.5rem; + } + + &::-webkit-scrollbar { + width: 0.3rem; + } + &::-webkit-scrollbar-corner { + background: transparent; + } + &::-webkit-scrollbar-thumb { + background: rgb(136, 136, 136); + border-radius: 0.625rem; + } + &::-webkit-scrollbar-track { + background: transparent; + } +} + +.tooltip-content-row { + display: flex; + align-items: center; + gap: 4px; +} + +.uplot { + width: 100%; + height: 100%; +} + +::-webkit-scrollbar { + height: 1rem; + width: 0.5rem; +} + +::-webkit-scrollbar:horizontal { + height: 0.5rem; + width: 1rem; +} + +::-webkit-scrollbar-track { + background-color: transparent; + border-radius: 9999px; +} + +::-webkit-scrollbar-thumb { + --tw-border-opacity: 1; + background-color: rgba(217, 217, 227, 0.8); + border-color: rgba(255, 255, 255, var(--tw-border-opacity)); + border-radius: 9999px; + border-width: 1px; +} + +::-webkit-scrollbar-thumb:hover { + --tw-bg-opacity: 1; + background-color: rgba(236, 236, 241, var(--tw-bg-opacity)); +} diff --git a/frontend/src/types/api/alerts/def.ts b/frontend/src/types/api/alerts/def.ts index 704a05765e..a5420e7ef0 100644 --- a/frontend/src/types/api/alerts/def.ts +++ b/frontend/src/types/api/alerts/def.ts @@ -25,10 +25,11 @@ export interface AlertDef { export interface RuleCondition { compositeQuery: ICompositeMetricQuery; - op?: string | undefined; - target?: number | undefined; - matchType?: string | undefined; - targetUnit?: string | undefined; + op?: string; + target?: number; + matchType?: string; + targetUnit?: string; + selectedQueryName?: string; } export interface Labels { diff --git a/frontend/src/types/api/dashboard/getAll.ts b/frontend/src/types/api/dashboard/getAll.ts index 0c872e02cc..2989107013 100644 --- a/frontend/src/types/api/dashboard/getAll.ts +++ b/frontend/src/types/api/dashboard/getAll.ts @@ -1,4 +1,5 @@ import { PANEL_TYPES } from 'constants/queryBuilder'; +import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types'; import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems'; import { ReactNode } from 'react'; import { Layout } from 'react-grid-layout'; @@ -45,6 +46,7 @@ export interface Dashboard { created_by: string; updated_by: string; data: DashboardData; + isLocked?: boolean; } export interface DashboardData { @@ -68,6 +70,8 @@ export interface IBaseWidget { timePreferance: timePreferenceType; stepSize?: number; yAxisUnit?: string; + thresholds?: ThresholdProps[]; + fillSpans?: boolean; } export interface Widgets extends IBaseWidget { query: Query; diff --git a/frontend/src/types/api/metrics/getApDex.ts b/frontend/src/types/api/metrics/getApDex.ts index 051f3994ff..60c82fefd0 100644 --- a/frontend/src/types/api/metrics/getApDex.ts +++ b/frontend/src/types/api/metrics/getApDex.ts @@ -10,5 +10,5 @@ export interface SetApDexPayloadProps { export interface MetricMetaProps { delta: boolean; - le: number[]; + le: number[] | null; } diff --git a/frontend/src/types/roles.ts b/frontend/src/types/roles.ts index 0ac7deaf24..18b4b52028 100644 --- a/frontend/src/types/roles.ts +++ b/frontend/src/types/roles.ts @@ -1,11 +1,13 @@ export type ADMIN = 'ADMIN'; export type VIEWER = 'VIEWER'; export type EDITOR = 'EDITOR'; +export type AUTHOR = 'AUTHOR'; -export type ROLES = ADMIN | VIEWER | EDITOR; +export type ROLES = ADMIN | VIEWER | EDITOR | AUTHOR; export const USER_ROLES = { ADMIN: 'ADMIN', VIEWER: 'VIEWER', EDITOR: 'EDITOR', + AUTHOR: 'AUTHOR', }; diff --git a/frontend/src/utils/permission/index.ts b/frontend/src/utils/permission/index.ts index 057eb2adc4..ee1a7a09e9 100644 --- a/frontend/src/utils/permission/index.ts +++ b/frontend/src/utils/permission/index.ts @@ -18,7 +18,9 @@ export type ComponentTypes = | 'new_alert_action' | 'edit_widget' | 'add_panel' - | 'page_pipelines'; + | 'page_pipelines' + | 'edit_locked_dashboard' + | 'add_panel_locked_dashboard'; export const componentPermission: Record = { current_org_settings: ['ADMIN'], @@ -30,14 +32,16 @@ export const componentPermission: Record = { add_new_channel: ['ADMIN'], set_retention_period: ['ADMIN'], action: ['ADMIN', 'EDITOR'], - save_layout: ['ADMIN', 'EDITOR'], - edit_dashboard: ['ADMIN', 'EDITOR'], - delete_widget: ['ADMIN', 'EDITOR'], + save_layout: ['ADMIN', 'EDITOR', 'AUTHOR'], + edit_dashboard: ['ADMIN', 'EDITOR', 'AUTHOR'], + delete_widget: ['ADMIN', 'EDITOR', 'AUTHOR'], new_dashboard: ['ADMIN', 'EDITOR'], new_alert_action: ['ADMIN'], edit_widget: ['ADMIN', 'EDITOR'], - add_panel: ['ADMIN', 'EDITOR'], + add_panel: ['ADMIN', 'EDITOR', 'AUTHOR'], page_pipelines: ['ADMIN', 'EDITOR'], + edit_locked_dashboard: ['ADMIN', 'AUTHOR'], + add_panel_locked_dashboard: ['ADMIN', 'AUTHOR'], }; export const routePermission: Record = { diff --git a/frontend/tests/fixtures/api/services/200.json b/frontend/tests/fixtures/api/services/200.json new file mode 100644 index 0000000000..be0f923075 --- /dev/null +++ b/frontend/tests/fixtures/api/services/200.json @@ -0,0 +1,68 @@ +[ + { + "serviceName": "redis", + "p99": 35396180, + "avgDuration": 15149389.806977473, + "numCalls": 22329, + "callRate": 12.615254237288136, + "numErrors": 4135, + "errorRate": 18.51851851851852, + "num4XX": 0, + "fourXXRate": 0 + }, + { + "serviceName": "frontend", + "p99": 1173509510.0000002, + "avgDuration": 747007254.5344619, + "numCalls": 1654, + "callRate": 0.9344632768361582, + "numErrors": 0, + "errorRate": 0, + "num4XX": 0, + "fourXXRate": 0 + }, + { + "serviceName": "mysql", + "p99": 776834620, + "avgDuration": 349280732.76904476, + "numCalls": 1654, + "callRate": 0.9344632768361582, + "numErrors": 0, + "errorRate": 0, + "num4XX": 0, + "fourXXRate": 0 + }, + { + "serviceName": "customer", + "p99": 776995390, + "avgDuration": 349451783.5550181, + "numCalls": 1654, + "callRate": 0.9344632768361582, + "numErrors": 0, + "errorRate": 0, + "num4XX": 0, + "fourXXRate": 0 + }, + { + "serviceName": "route", + "p99": 79617600.00000001, + "avgDuration": 50698870.85852479, + "numCalls": 16540, + "callRate": 9.344632768361581, + "numErrors": 0, + "errorRate": 0, + "num4XX": 0, + "fourXXRate": 0 + }, + { + "serviceName": "driver", + "p99": 241056990, + "avgDuration": 204975300.48367593, + "numCalls": 1654, + "callRate": 0.9344632768361582, + "numErrors": 0, + "errorRate": 0, + "num4XX": 0, + "fourXXRate": 0 + } +] diff --git a/frontend/tests/service/servicesLanding.spec.ts b/frontend/tests/service/servicesLanding.spec.ts new file mode 100644 index 0000000000..69797e0ede --- /dev/null +++ b/frontend/tests/service/servicesLanding.spec.ts @@ -0,0 +1,150 @@ +import { expect, Page, test } from '@playwright/test'; +import ROUTES from 'constants/routes'; + +import servicesSuccessResponse from '../fixtures/api/services/200.json'; +import { loginApi } from '../fixtures/common'; +import { SERVICE_TABLE_HEADERS } from './utils'; + +let page: Page; + +test.describe('Service flow', () => { + test.beforeEach(async ({ baseURL, browser }) => { + const context = await browser.newContext({ storageState: 'tests/auth.json' }); + const newPage = await context.newPage(); + + await loginApi(newPage); + + await newPage.goto(`${baseURL}${ROUTES.APPLICATION}`); + + page = newPage; + }); + + test('Services empty page', async ({ baseURL }) => { + // visit services page + await page.goto(`${baseURL}${ROUTES.APPLICATION}`); + + await page.route(`**/services`, (route) => + route.fulfill({ + status: 200, + json: [], + }), + ); + + // expect noData to be present + await expect(page.getByText('No data')).toBeVisible(); + }); + + test('Services table and service details page rendered with correct data', async ({ + baseURL, + }) => { + // visit services page + await page.goto(`${baseURL}${ROUTES.APPLICATION}`); + + // assert the URL of the services page + await expect(page).toHaveURL(`${baseURL}${ROUTES.APPLICATION}`); + + // mock the services list call to return non-empty data + await page.route(`**/services`, (route) => + route.fulfill({ + status: 200, + json: servicesSuccessResponse, + }), + ); + + // assert the presence of services breadcrumbs + const breadcrumbServicesText = await page + .locator('.ant-breadcrumb-link a[href="/services"]') + .nth(1) + .textContent(); + await expect(breadcrumbServicesText).toEqual('Services'); + + // expect the services headers to be loaded correctly + const p99Latency = page.locator( + `th:has-text("${SERVICE_TABLE_HEADERS.P99LATENCY}")`, + ); + + await expect(p99Latency).toBeVisible(); + const errorRate = await page.locator( + `th:has-text("${SERVICE_TABLE_HEADERS.ERROR_RATE}")`, + ); + + await expect(errorRate).toBeVisible(); + const operationsPerSecond = await page.locator( + `th:has-text("${SERVICE_TABLE_HEADERS.OPS_PER_SECOND}")`, + ); + + await expect(operationsPerSecond).toBeVisible(); + + // expect services to be listed in the table + const redisService = await page + .locator('a[href="/services/redis"]') + .isVisible(); + + expect(redisService).toBeTruthy(); + + // route to a service details page + await page.locator('a[href="/services/redis"]').click(); + + // wait for the network calls to be settled + await page.waitForLoadState('networkidle'); + + // render the overview tab + await page.getByRole('tab', { name: 'Overview' }).click(); + + // check the presence of different graphs on the overview tab + const latencyGraph = await page + .locator('[data-testid="service_latency"]') + .isVisible(); + + expect(latencyGraph).toBeTruthy(); + + const rateOps = await page + .locator('[data-testid="operations_per_sec"]') + .isVisible(); + + expect(rateOps).toBeTruthy(); + + const errorPercentage = await page + .locator('[data-testid="error_percentage_%"]') + .isVisible(); + + expect(errorPercentage).toBeTruthy(); + + // navigate to the DB call metrics and validate the tables + await page.getByRole('tab', { name: 'DB Call Metrics' }).click(); + + const databaseCallRps = await page + .locator('[data-testid="database_call_rps"]') + .isVisible(); + expect(databaseCallRps).toBeTruthy(); + + const databaseCallsAvgDuration = await page + .locator('[data-testid="database_call_avg_duration"]') + .isVisible(); + expect(databaseCallsAvgDuration).toBeTruthy(); + + // navigate to external metrics and validate the tables + + await page.getByRole('tab', { name: 'External Metrics' }).click(); + + const externalCallErrorPerc = await page + .locator('[data-testid="external_call_error_percentage"]') + .isVisible(); + expect(externalCallErrorPerc).toBeTruthy(); + + const externalCallDuration = await page + .locator('[data-testid="external_call_duration"]') + .isVisible(); + expect(externalCallDuration).toBeTruthy(); + + const externalCallRps = await page + .locator('[data-testid="external_call_rps_by_address"]') + .isVisible(); + expect(externalCallRps).toBeTruthy(); + + const externalCallDurationByAddress = await page + .locator('[data-testid="external_call_duration_by_address"]') + .isVisible(); + expect(externalCallDurationByAddress).toBeTruthy(); + }); +}); diff --git a/frontend/tests/service/utils.ts b/frontend/tests/service/utils.ts new file mode 100644 index 0000000000..644a532d15 --- /dev/null +++ b/frontend/tests/service/utils.ts @@ -0,0 +1,6 @@ +export const SERVICE_TABLE_HEADERS = { + APPLICATION: 'Applicaton', + P99LATENCY: 'P99 latency (in ms)', + ERROR_RATE: 'Error Rate (% of total)', + OPS_PER_SECOND: 'Operations Per Second', +}; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index c0da947052..a3c9a0449f 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -53,17 +53,24 @@ dependencies: "@ctrl/tinycolor" "^3.4.0" -"@ant-design/cssinjs@^1.0.0": - version "1.9.1" - resolved "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.9.1.tgz" - integrity sha512-CZt1vCMs/sY7RoacYuIkZwQmb8Bhp99ReNNE9Y8lnUzik8fmCdKAQA7ecvVOFwmNFdcBHga7ye/XIRrsbkiqWw== +"@ant-design/colors@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.0.0.tgz#eb7eecead124c3533aea05d61254f0a17f2b61b3" + integrity sha512-iVm/9PfGCbC0dSMBrz7oiEXZaaGH7ceU40OJEfKmyuzR9R5CRimJYPlRiFtMQGQcbNMea/ePcoIebi4ASGYXtg== + dependencies: + "@ctrl/tinycolor" "^3.4.0" + +"@ant-design/cssinjs@^1.17.2": + version "1.17.2" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.17.2.tgz#08e939cbe60e9e0e0f3f03cd53a52e4a7623ed1f" + integrity sha512-vu7lnfEx4Mf8MPzZxn506Zen3Nt4fRr2uutwvdCuTCN5IiU0lDdQ0tiJ24/rmB8+pefwjluYsbyzbQSbgfJy+A== dependencies: "@babel/runtime" "^7.11.1" "@emotion/hash" "^0.8.0" "@emotion/unitless" "^0.7.5" classnames "^2.3.1" csstype "^3.0.10" - rc-util "^5.27.0" + rc-util "^5.35.0" stylis "^4.0.13" "@ant-design/icons-svg@^4.2.1": @@ -71,7 +78,12 @@ resolved "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz" integrity sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw== -"@ant-design/icons@4.8.0", "@ant-design/icons@^4.7.0": +"@ant-design/icons-svg@^4.3.0": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.3.1.tgz#4b2f65a17d4d32b526baa6414aca2117382bf8da" + integrity sha512-4QBZg8ccyC6LPIRii7A0bZUk3+lEDCLnhB+FVsflGdcWPPmV+j3fire4AwwoqHV/BibgvBmR9ZIo4s867smv+g== + +"@ant-design/icons@4.8.0": version "4.8.0" resolved "https://registry.npmjs.org/@ant-design/icons/-/icons-4.8.0.tgz" integrity sha512-T89P2jG2vM7OJ0IfGx2+9FC5sQjtTzRSz+mCHTXkFn/ELZc2YpfStmYHmqzq2Jx55J0F7+O6i5/ZKFSVNWCKNg== @@ -82,16 +94,27 @@ classnames "^2.2.6" rc-util "^5.9.4" -"@ant-design/react-slick@~0.29.1": - version "0.29.2" - resolved "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-0.29.2.tgz" - integrity sha512-kgjtKmkGHa19FW21lHnAfyyH9AAoh35pBdcJ53rHmQ3O+cfFHGHnUbj/HFrRNJ5vIts09FKJVAD8RpaC+RaWfA== +"@ant-design/icons@^5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.2.6.tgz#2d4a9a37f531eb2a20cebec01d6fb69cf593900d" + integrity sha512-4wn0WShF43TrggskBJPRqCD0fcHbzTYjnaoskdiJrVHg86yxoZ8ZUqsXvyn4WUqehRiFKnaclOhqk9w4Ui2KVw== + dependencies: + "@ant-design/colors" "^7.0.0" + "@ant-design/icons-svg" "^4.3.0" + "@babel/runtime" "^7.11.2" + classnames "^2.2.6" + rc-util "^5.31.1" + +"@ant-design/react-slick@~1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.0.2.tgz#241bb412aeacf7ff5d50c61fa5db66773fde6b56" + integrity sha512-Wj8onxL/T8KQLFFiCA4t8eIRGpRR+UPgOdac2sYzonv+i0n3kXHmvHLLiOYL655DQx2Umii9Y9nNgL7ssu5haQ== dependencies: "@babel/runtime" "^7.10.4" classnames "^2.2.5" json2mq "^0.2.0" - lodash "^4.17.21" resize-observer-polyfill "^1.5.1" + throttle-debounce "^5.0.0" "@babel/code-frame@7.12.11": version "7.12.11" @@ -2046,6 +2069,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" + integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.3.1": version "7.23.1" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.1.tgz#72741dc4d413338a91dcb044a86f3c0bc402646d" @@ -2306,6 +2336,11 @@ resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.0.tgz" integrity sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ== +"@ctrl/tinycolor@^3.6.0", "@ctrl/tinycolor@^3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" + integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== + "@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" @@ -2901,6 +2936,24 @@ resolved "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== +"@rc-component/color-picker@~1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-1.4.1.tgz#dcab0b660e9c4ed63a7582db68ed4a77c862cb93" + integrity sha512-vh5EWqnsayZa/JwUznqDaPJz39jznx/YDbyBuVJntv735tKXKwEUZZb2jYEldOg+NKWZwtALjGMrNeGBmqFoEw== + dependencies: + "@babel/runtime" "^7.10.1" + "@ctrl/tinycolor" "^3.6.0" + classnames "^2.2.6" + rc-util "^5.30.0" + +"@rc-component/context@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-1.4.0.tgz#dc6fb021d6773546af8f016ae4ce9aea088395e8" + integrity sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w== + dependencies: + "@babel/runtime" "^7.10.1" + rc-util "^5.27.0" + "@rc-component/mini-decimal@^1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.0.1.tgz" @@ -2908,7 +2961,16 @@ dependencies: "@babel/runtime" "^7.18.0" -"@rc-component/portal@^1.0.0-6", "@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.0-9", "@rc-component/portal@^1.0.2": +"@rc-component/mutate-observer@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz#ee53cc88b78aade3cd0653609215a44779386fd8" + integrity sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw== + dependencies: + "@babel/runtime" "^7.18.0" + classnames "^2.3.2" + rc-util "^5.24.4" + +"@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.0-9", "@rc-component/portal@^1.0.2": version "1.1.1" resolved "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.1.tgz" integrity sha512-m8w3dFXX0H6UkJ4wtfrSwhe2/6M08uz24HHrF8pWfAXPwA9hwCuTE5per/C86KwNLouRpwFGcr7LfpHaa1F38g== @@ -2917,17 +2979,38 @@ classnames "^2.3.2" rc-util "^5.24.4" -"@rc-component/tour@~1.0.1-2": - version "1.0.4" - resolved "https://registry.npmjs.org/@rc-component/tour/-/tour-1.0.4.tgz" - integrity sha512-FwAh9twryS6Ava2mUqwJtbhIt0ObIZIgQOJK+XTl+pQvsmXtUGtbOif3/4FeVmncy7FEGH7mnlIjS4OLGkQC9A== +"@rc-component/portal@^1.1.0", "@rc-component/portal@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" + integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== + dependencies: + "@babel/runtime" "^7.18.0" + classnames "^2.3.2" + rc-util "^5.24.4" + +"@rc-component/tour@~1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.10.0.tgz#b05bc327438f1c583439e2d2dcc10ec0530aea19" + integrity sha512-voV0BKaTJbewB9LLgAHQ7tAGG7rgDkKQkZo82xw2gIk542hY+o7zwoqdN16oHhIKk7eG/xi+mdXrONT62Dt57A== dependencies: "@babel/runtime" "^7.18.0" "@rc-component/portal" "^1.0.0-9" + "@rc-component/trigger" "^1.3.6" classnames "^2.3.2" - rc-trigger "^5.3.4" rc-util "^5.24.4" +"@rc-component/trigger@^1.17.0", "@rc-component/trigger@^1.18.0", "@rc-component/trigger@^1.3.6", "@rc-component/trigger@^1.5.0", "@rc-component/trigger@^1.7.0": + version "1.18.1" + resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-1.18.1.tgz#149881ace55943f0b74ae0470dc9f05b8f0b5d51" + integrity sha512-bAcxJJ1Y+EJVgn8BRik7d8JjjAPND5zKkHQ3159zeR0gVoG4Z0RgEDAiXFFoie3/WpoJ9dRJyjrIpnH4Ef7PEg== + dependencies: + "@babel/runtime" "^7.23.2" + "@rc-component/portal" "^1.1.0" + classnames "^2.3.2" + rc-motion "^2.0.0" + rc-resize-observer "^1.3.1" + rc-util "^5.38.0" + "@react-dnd/asap@^5.0.1": version "5.0.2" resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-5.0.2.tgz#1f81f124c1cd6f39511c11a881cfb0f715343488" @@ -4295,57 +4378,59 @@ antd-table-saveas-excel@2.2.1: better-xlsx "^0.7.5" file-saver "^2.0.2" -antd@5.0.5: - version "5.0.5" - resolved "https://registry.npmjs.org/antd/-/antd-5.0.5.tgz" - integrity sha512-8jWUjZ65urNHZPg9/Ywa9V0PlNfqjhewKgSPF4nraN9X5v434lDJkRBQGN7meNixQ6aM2B/JhXPm9UaJ/tAQmA== +antd@5.11.0: + version "5.11.0" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.11.0.tgz#c0404412473f9ac14c5f75bc679fb805e59d4ec6" + integrity sha512-34T5Y6z+Ip+j4faXPTcanTFCLLpR4V0rLHtuz0lbN9gF4coGY/YYa8bhgwXrT6muW0Afwyo3NmbMF52hvIarog== dependencies: - "@ant-design/colors" "^6.0.0" - "@ant-design/cssinjs" "^1.0.0" - "@ant-design/icons" "^4.7.0" - "@ant-design/react-slick" "~0.29.1" + "@ant-design/colors" "^7.0.0" + "@ant-design/cssinjs" "^1.17.2" + "@ant-design/icons" "^5.2.6" + "@ant-design/react-slick" "~1.0.2" "@babel/runtime" "^7.18.3" - "@ctrl/tinycolor" "^3.4.0" - "@rc-component/tour" "~1.0.1-2" - classnames "^2.2.6" - copy-to-clipboard "^3.2.0" + "@ctrl/tinycolor" "^3.6.1" + "@rc-component/color-picker" "~1.4.1" + "@rc-component/mutate-observer" "^1.1.0" + "@rc-component/tour" "~1.10.0" + "@rc-component/trigger" "^1.18.0" + classnames "^2.3.2" + copy-to-clipboard "^3.3.3" dayjs "^1.11.1" - lodash "^4.17.21" - rc-cascader "~3.7.0" - rc-checkbox "~2.3.0" - rc-collapse "~3.4.2" - rc-dialog "~9.0.2" - rc-drawer "~6.0.0" - rc-dropdown "~4.0.0" - rc-field-form "~1.27.0" - rc-image "~5.12.0" - rc-input "~0.1.4" - rc-input-number "~7.4.0" - rc-mentions "~1.13.1" - rc-menu "~9.8.0" - rc-motion "^2.6.1" - rc-notification "~5.0.0-alpha.9" - rc-pagination "~3.2.0" - rc-picker "~3.1.1" - rc-progress "~3.4.1" - rc-rate "~2.9.0" - rc-resize-observer "^1.2.0" - rc-segmented "~2.1.0" - rc-select "~14.1.13" - rc-slider "~10.0.0" - rc-steps "~6.0.0-alpha.2" - rc-switch "~4.0.0" - rc-table "~7.26.0" - rc-tabs "~12.4.2" - rc-textarea "~0.4.5" - rc-tooltip "~5.2.0" - rc-tree "~5.7.0" - rc-tree-select "~5.5.4" - rc-trigger "^5.2.10" - rc-upload "~4.3.0" - rc-util "^5.25.2" - scroll-into-view-if-needed "^3.0.3" - shallowequal "^1.1.0" + qrcode.react "^3.1.0" + rc-cascader "~3.20.0" + rc-checkbox "~3.1.0" + rc-collapse "~3.7.1" + rc-dialog "~9.3.4" + rc-drawer "~6.5.2" + rc-dropdown "~4.1.0" + rc-field-form "~1.40.0" + rc-image "~7.3.2" + rc-input "~1.3.5" + rc-input-number "~8.4.0" + rc-mentions "~2.9.1" + rc-menu "~9.12.2" + rc-motion "^2.9.0" + rc-notification "~5.3.0" + rc-pagination "~3.7.0" + rc-picker "~3.14.6" + rc-progress "~3.5.1" + rc-rate "~2.12.0" + rc-resize-observer "^1.4.0" + rc-segmented "~2.2.2" + rc-select "~14.10.0" + rc-slider "~10.4.0" + rc-steps "~6.0.1" + rc-switch "~4.1.0" + rc-table "~7.35.2" + rc-tabs "~12.13.1" + rc-textarea "~1.5.1" + rc-tooltip "~6.1.2" + rc-tree "~5.8.2" + rc-tree-select "~5.15.0" + rc-upload "~4.3.5" + rc-util "^5.38.0" + scroll-into-view-if-needed "^3.1.0" + throttle-debounce "^5.0.0" anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.3" @@ -5782,7 +5867,7 @@ copy-anything@^2.0.1: dependencies: is-what "^3.14.1" -copy-to-clipboard@^3.2.0, copy-to-clipboard@^3.3.1: +copy-to-clipboard@^3.3.1, copy-to-clipboard@^3.3.3: version "3.3.3" resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz" integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== @@ -6517,11 +6602,6 @@ dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz" integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== -dom-align@^1.7.0: - version "1.12.4" - resolved "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz" - integrity sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw== - dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz" @@ -12229,6 +12309,11 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== +qrcode.react@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-3.1.0.tgz#5c91ddc0340f768316fbdb8fff2765134c2aecd8" + integrity sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q== + qs@6.11.0: version "6.11.0" resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" @@ -12316,52 +12401,41 @@ rbush@^3.0.1: dependencies: quickselect "^2.0.0" -rc-align@^4.0.0: - version "4.0.15" - resolved "https://registry.npmjs.org/rc-align/-/rc-align-4.0.15.tgz" - integrity sha512-wqJtVH60pka/nOX7/IspElA8gjPNQKIx/ZqJ6heATCkXpe1Zg4cPVrMD2vC96wjsFFL8WsmhPbx9tdMo1qqlIA== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - dom-align "^1.7.0" - rc-util "^5.26.0" - resize-observer-polyfill "^1.5.1" - -rc-cascader@~3.7.0: - version "3.7.2" - resolved "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.7.2.tgz" - integrity sha512-5nPEM76eMyikd0NFiy1gjwiB9m+bOzjY6Lnd5bVC6Ar3XLlOpOnlCcV3oBFWLN3f7B18tAGpaAVlT2uyEDCv9w== +rc-cascader@~3.20.0: + version "3.20.0" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.20.0.tgz#b270f9d84ed83417ee7309ef5e56e415f1586076" + integrity sha512-lkT9EEwOcYdjZ/jvhLoXGzprK1sijT3/Tp4BLxQQcHDZkkOzzwYQC9HgmKoJz0K7CukMfgvO9KqHeBdgE+pELw== dependencies: "@babel/runtime" "^7.12.5" array-tree-filter "^2.1.0" classnames "^2.3.1" - rc-select "~14.1.0" - rc-tree "~5.7.0" - rc-util "^5.6.1" + rc-select "~14.10.0" + rc-tree "~5.8.1" + rc-util "^5.37.0" -rc-checkbox@~2.3.0: - version "2.3.2" - resolved "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-2.3.2.tgz" - integrity sha512-afVi1FYiGv1U0JlpNH/UaEXdh6WUJjcWokj/nUN2TgG80bfG+MDdbfHKlLcNNba94mbjy2/SXJ1HDgrOkXGAjg== +rc-checkbox@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.1.0.tgz#6be0d9d8de2cc96fb5e37f9036a1c3e360d0a42d" + integrity sha512-PAwpJFnBa3Ei+5pyqMMXdcKYKNBMS+TvSDiLdDnARnMJHC8ESxwPfm4Ao1gJiKtWLdmGfigascnCpwrHFgoOBQ== dependencies: "@babel/runtime" "^7.10.1" - classnames "^2.2.1" + classnames "^2.3.2" + rc-util "^5.25.2" -rc-collapse@~3.4.2: - version "3.4.2" - resolved "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.4.2.tgz" - integrity sha512-jpTwLgJzkhAgp2Wpi3xmbTbbYExg6fkptL67Uu5LCRVEj6wqmy0DHTjjeynsjOLsppHGHu41t1ELntZ0lEvS/Q== +rc-collapse@~3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.7.1.tgz#bda1f7f80adccf3433c1c15d4d9f9ca09910c727" + integrity sha512-N/7ejyiTf3XElNJBBpxqnZBUuMsQWEOPjB2QkfNvZ/Ca54eAvJXuOD1EGbCWCk2m7v/MSxku7mRpdeaLOCd4Gg== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.3.4" - rc-util "^5.2.1" - shallowequal "^1.1.0" + rc-util "^5.27.0" -rc-dialog@~9.0.0, rc-dialog@~9.0.2: - version "9.0.2" - resolved "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.0.2.tgz" - integrity sha512-s3U+24xWUuB6Bn2Lk/Qt6rufy+uT+QvWkiFhNBcO9APLxcFFczWamaq7x9h8SCuhfc1nHcW4y8NbMsnAjNnWyg== +rc-dialog@~9.3.4: + version "9.3.4" + resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.3.4.tgz#e0decb3d4a0dbe36524a67ed2f8fe2daa4b7b73c" + integrity sha512-975X3018GhR+EjZFbxA2Z57SX5rnu0G0/OxFgMMvZK4/hQWEm3MHaNvP4wXpxYDoJsp+xUvVW+GB9CMMCm81jA== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/portal" "^1.0.0-8" @@ -12369,92 +12443,94 @@ rc-dialog@~9.0.0, rc-dialog@~9.0.2: rc-motion "^2.3.0" rc-util "^5.21.0" -rc-drawer@~6.0.0: - version "6.0.3" - resolved "https://registry.npmjs.org/rc-drawer/-/rc-drawer-6.0.3.tgz" - integrity sha512-u4RajgrnREKQH/21gB2JHZiA6ZECo0X0BbmDxAJEhKD9jUhlAbqMN5I9VWa4PSzi9ceLHUShqQcPAh2EJswffw== +rc-drawer@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-6.5.2.tgz#49c1f279261992f6d4653d32a03b14acd436d610" + integrity sha512-QckxAnQNdhh4vtmKN0ZwDf3iakO83W9eZcSKWYYTDv4qcD2fHhRAZJJ/OE6v2ZlQ2kSqCJX5gYssF4HJFvsEPQ== dependencies: "@babel/runtime" "^7.10.1" - "@rc-component/portal" "^1.0.0-6" + "@rc-component/portal" "^1.1.1" classnames "^2.2.6" rc-motion "^2.6.1" - rc-util "^5.21.2" + rc-util "^5.36.0" -rc-dropdown@~4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.0.1.tgz" - integrity sha512-OdpXuOcme1rm45cR0Jzgfl1otzmU4vuBVb+etXM8vcaULGokAKVpKlw8p6xzspG7jGd/XxShvq+N3VNEfk/l5g== +rc-dropdown@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.1.0.tgz#418a68939631520de80d0865d02b440eeeb4168e" + integrity sha512-VZjMunpBdlVzYpEdJSaV7WM7O0jf8uyDjirxXLZRNZ+tAC+NzD3PXPEtliFwGzVwBBdCmGuSqiS9DWcOLxQ9tw== dependencies: "@babel/runtime" "^7.18.3" + "@rc-component/trigger" "^1.7.0" classnames "^2.2.6" - rc-trigger "^5.3.1" rc-util "^5.17.0" -rc-field-form@~1.27.0: - version "1.27.4" - resolved "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.27.4.tgz" - integrity sha512-PQColQnZimGKArnOh8V2907+VzDCXcqtFvHgevDLtqWc/P7YASb/FqntSmdS8q3VND5SHX3Y1vgMIzY22/f/0Q== +rc-field-form@~1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.40.0.tgz#808dce06ebae1c3aea574e672b673533fc98f11f" + integrity sha512-OM3N01X2BYFGJDJcwpk9/BBtlwgveE7eh2SQAKIxVCt9KVWlODYJ9ypTHQdxchfDbeJKJKxMBFXlLAmyvlgPHg== dependencies: "@babel/runtime" "^7.18.0" async-validator "^4.1.0" - rc-util "^5.8.0" + rc-util "^5.32.2" -rc-image@~5.12.0: - version "5.12.2" - resolved "https://registry.npmjs.org/rc-image/-/rc-image-5.12.2.tgz" - integrity sha512-12OCOspbN2AW2L1w+7vnYc+k0RexenqfQZIvq3WyYODp9GnTN4GLV8juekm3Apc/pwdfBSp0The1FZ5KXEozhg== +rc-image@~7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.3.2.tgz#fd92ab9994552d4f42638af0810039d34ad32dba" + integrity sha512-ICEF6SWv9YKhDXxy1vrXcmf0TVvEcQWIww5Yg+f+mn7e4oGX7FNP4+FExwMjNO5UHBEuWrigbGhlCgI6yZZ1jg== dependencies: "@babel/runtime" "^7.11.2" "@rc-component/portal" "^1.0.2" classnames "^2.2.6" - rc-dialog "~9.0.0" + rc-dialog "~9.3.4" rc-motion "^2.6.2" - rc-util "^5.0.6" + rc-util "^5.34.1" -rc-input-number@~7.4.0: - version "7.4.2" - resolved "https://registry.npmjs.org/rc-input-number/-/rc-input-number-7.4.2.tgz" - integrity sha512-yGturTw7WGP+M1GbJ+UTAO7L4buxeW6oilhL9Sq3DezsRS8/9qec4UiXUbeoiX9bzvRXH11JvgskBtxSp4YSNg== +rc-input-number@~8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-8.4.0.tgz#f0d0caa2ce3a4e37f062556f9cb4c08c8c23322d" + integrity sha512-B6rziPOLRmeP7kcS5qbdC5hXvvDHYKV4vUxmahevYx2E6crS2bRi0xLDjhJ0E1HtOWo8rTmaE2EBJAkTCZOLdA== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/mini-decimal" "^1.0.1" classnames "^2.2.5" + rc-input "~1.3.5" rc-util "^5.28.0" -rc-input@~0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/rc-input/-/rc-input-0.1.4.tgz" - integrity sha512-FqDdNz+fV2dKNgfXzcSLKvC+jEs1709t7nD+WdfjrdSaOcefpgc7BUJYadc3usaING+b7ediMTfKxuJBsEFbXA== +rc-input@~1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.3.5.tgz#c69d3feb5172cc02ee5aaad86ae1ea8a73f864cb" + integrity sha512-SPPwbTJa5ACHNoDdGZF/70AOqqm1Rir3WleuFBKq+nFby1zvpnzvWsHJgzWOr6uJ0GNt8dTMzBrmVGQJkTXqqQ== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-util "^5.18.1" -rc-mentions@~1.13.1: - version "1.13.1" - resolved "https://registry.npmjs.org/rc-mentions/-/rc-mentions-1.13.1.tgz" - integrity sha512-FCkaWw6JQygtOz0+Vxz/M/NWqrWHB9LwqlY2RtcuFqWJNFK9njijOOzTSsBGANliGufVUzx/xuPHmZPBV0+Hgw== +rc-mentions@~2.9.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.9.1.tgz#cfe55913fd5bc156ef9814f38c1a2ceefee032ce" + integrity sha512-cZuElWr/5Ws0PXx1uxobxfYh4mqUw2FitfabR62YnWgm+WAfDyXZXqZg5DxXW+M1cgVvntrQgDDd9LrihrXzew== dependencies: - "@babel/runtime" "^7.10.1" + "@babel/runtime" "^7.22.5" + "@rc-component/trigger" "^1.5.0" classnames "^2.2.6" - rc-menu "~9.8.0" - rc-textarea "^0.4.0" - rc-trigger "^5.0.4" - rc-util "^5.22.5" + rc-input "~1.3.5" + rc-menu "~9.12.0" + rc-textarea "~1.5.0" + rc-util "^5.34.1" -rc-menu@~9.8.0: - version "9.8.4" - resolved "https://registry.npmjs.org/rc-menu/-/rc-menu-9.8.4.tgz" - integrity sha512-lmw2j8I2fhdIzHmC9ajfImfckt0WDb2KVJJBBRIsxPEw2kGkEfjLMUoB1NgiNT/Q5cC8PdjGOGQjHJIJMwyNMw== +rc-menu@~9.12.0, rc-menu@~9.12.2: + version "9.12.2" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.12.2.tgz#1bab34646421224eff5c5b7de993f8ea1238418e" + integrity sha512-NzloFH2pRUYmQ3S/YbJAvRkgCZaLvq0sRa5rgJtuIHLfPPprNHNyepeSlT64+dbVqI4qRWL44VN0lUCldCbbfg== dependencies: "@babel/runtime" "^7.10.1" + "@rc-component/trigger" "^1.17.0" classnames "2.x" rc-motion "^2.4.3" - rc-overflow "^1.2.8" - rc-trigger "^5.1.2" + rc-overflow "^1.3.1" rc-util "^5.27.0" -rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.0, rc-motion@^2.6.1, rc-motion@^2.6.2: +rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2: version "2.7.3" resolved "https://registry.npmjs.org/rc-motion/-/rc-motion-2.7.3.tgz" integrity sha512-2xUvo8yGHdOHeQbdI8BtBsCIrWKchEmFEIskf0nmHtJsou+meLd/JE+vnvSX2JxcBrJtXY2LuBpxAOxrbY/wMQ== @@ -12463,63 +12539,73 @@ rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motio classnames "^2.2.1" rc-util "^5.21.0" -rc-notification@~5.0.0-alpha.9: - version "5.0.3" - resolved "https://registry.npmjs.org/rc-notification/-/rc-notification-5.0.3.tgz" - integrity sha512-+wHbHu6RiTNtsZYx42WxWA+tC5m0qyKvJAauO4/6LIEyJspK8fRlFQz+OCFgFwGuNs3cOdo9tLs+cPfztSZwbQ== +rc-motion@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.0.tgz#9e18a1b8d61e528a97369cf9a7601e9b29205710" + integrity sha512-XIU2+xLkdIr1/h6ohPZXyPBMvOmuyFZQ/T0xnawz+Rh+gh4FINcnZmMT5UTIj6hgI0VLDjTaPeRd+smJeSPqiQ== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.21.0" + +rc-notification@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.3.0.tgz#e31c86fe2350598ade8cff383babd1befa7a94fe" + integrity sha512-WCf0uCOkZ3HGfF0p1H4Sgt7aWfipxORWTPp7o6prA3vxwtWhtug3GfpYls1pnBp4WA+j8vGIi5c2/hQRpGzPcQ== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" - rc-motion "^2.6.0" + rc-motion "^2.9.0" rc-util "^5.20.1" -rc-overflow@^1.0.0, rc-overflow@^1.2.8: - version "1.3.0" - resolved "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.3.0.tgz" - integrity sha512-p2Qt4SWPTHAYl4oAao1THy669Fm5q8pYBDBHRaFOekCvcdcrgIx0ByXQMEkyPm8wUDX4BK6aARWecvCRc/7CTA== +rc-overflow@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.3.2.tgz#72ee49e85a1308d8d4e3bd53285dc1f3e0bcce2c" + integrity sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-resize-observer "^1.0.0" - rc-util "^5.19.2" + rc-util "^5.37.0" -rc-pagination@~3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/rc-pagination/-/rc-pagination-3.2.0.tgz" - integrity sha512-5tIXjB670WwwcAJzAqp2J+cOBS9W3cH/WU1EiYwXljuZ4vtZXKlY2Idq8FZrnYBz8KhN3vwPo9CoV/SJS6SL1w== +rc-pagination@~3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-3.7.0.tgz#4c4332800688ec0fd3b2435c4772f7f8d4d7b50e" + integrity sha512-IxSzKapd13L91/195o1TPkKnCNw8gIR25UP1GCW/7c7n/slhld4npu2j2PB9IWjXm4SssaAaSAt2lscYog7wzg== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" + rc-util "^5.32.2" -rc-picker@~3.1.1: - version "3.1.5" - resolved "https://registry.npmjs.org/rc-picker/-/rc-picker-3.1.5.tgz" - integrity sha512-Hh3ml+u+5mxLfl4ahVWlRGiX5+0EJrALR6tSW9yP0eea+6j+YjvjfetbvuVidViMDMweZa38dr8HTfAFLG6GFw== +rc-picker@~3.14.6: + version "3.14.6" + resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-3.14.6.tgz#60fc34f9883272e10f6c593fa6d82e7e7a70781b" + integrity sha512-AdKKW0AqMwZsKvIpwUWDUnpuGKZVrbxVTZTNjcO+pViGkjC1EBcjMgxVe8tomOEaIHJL5Gd13vS8Rr3zzxWmag== dependencies: "@babel/runtime" "^7.10.1" + "@rc-component/trigger" "^1.5.0" classnames "^2.2.1" - rc-trigger "^5.0.4" - rc-util "^5.27.0" + rc-util "^5.30.0" -rc-progress@~3.4.1: - version "3.4.1" - resolved "https://registry.npmjs.org/rc-progress/-/rc-progress-3.4.1.tgz" - integrity sha512-eAFDHXlk8aWpoXl0llrenPMt9qKHQXphxcVsnKs0FHC6eCSk1ebJtyaVjJUzKe0233ogiLDeEFK1Uihz3s67hw== +rc-progress@~3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.5.1.tgz#a3cdfd2fe04eb5c3d43fa1c69e7dd70c73b102ae" + integrity sha512-V6Amx6SbLRwPin/oD+k1vbPrO8+9Qf8zW1T8A7o83HdNafEVvAxPV5YsgtKFP+Ud5HghLj33zKOcEHrcrUGkfw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" rc-util "^5.16.1" -rc-rate@~2.9.0: - version "2.9.2" - resolved "https://registry.npmjs.org/rc-rate/-/rc-rate-2.9.2.tgz" - integrity sha512-SaiZFyN8pe0Fgphv8t3+kidlej+cq/EALkAJAc3A0w0XcPaH2L1aggM8bhe1u6GAGuQNAoFvTLjw4qLPGRKV5g== +rc-rate@~2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.12.0.tgz#0182deffed3b009cdcc61660da8746c39ed91ed5" + integrity sha512-g092v5iZCdVzbjdn28FzvWebK2IutoVoiTeqoLTj9WM7SjA/gOJIw5/JFZMRyJYYVe1jLAU2UhAfstIpCNRozg== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" rc-util "^5.0.1" -rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.2.0: +rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0: version "1.3.1" resolved "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.3.1.tgz" integrity sha512-iFUdt3NNhflbY3mwySv5CA1TC06zdJ+pfo0oc27xpf4PIOvfZwZGtD9Kz41wGYqC4SLio93RVAirSSpYlV/uYg== @@ -12529,144 +12615,143 @@ rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.2.0: rc-util "^5.27.0" resize-observer-polyfill "^1.5.1" -rc-segmented@~2.1.0: - version "2.1.2" - resolved "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.1.2.tgz" - integrity sha512-qGo1bCr83ESXpXVOCXjFe1QJlCAQXyi9KCiy8eX3rIMYlTeJr/ftySIaTnYsitL18SvWf5ZEHsfqIWoX0EMfFQ== +rc-resize-observer@^1.3.1, rc-resize-observer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz#7bba61e6b3c604834980647cce6451914750d0cc" + integrity sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q== + dependencies: + "@babel/runtime" "^7.20.7" + classnames "^2.2.1" + rc-util "^5.38.0" + resize-observer-polyfill "^1.5.1" + +rc-segmented@~2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.2.2.tgz#a34f12ce6c0975fc3042ae7656bcd18e1744798e" + integrity sha512-Mq52M96QdHMsNdE/042ibT5vkcGcD5jxKp7HgPC2SRofpia99P5fkfHy1pEaajLMF/kj0+2Lkq1UZRvqzo9mSA== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-motion "^2.4.4" rc-util "^5.17.0" -rc-select@~14.1.0, rc-select@~14.1.13: - version "14.1.17" - resolved "https://registry.npmjs.org/rc-select/-/rc-select-14.1.17.tgz" - integrity sha512-6qQhMqtoUkkboRqXKKFRR5Nu1mrnw2mC1uxIBIczg7aiJ94qCZBg4Ww8OLT9f4xdyCgbFSGh6r3yB9EBsjoHGA== +rc-select@~14.10.0: + version "14.10.0" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.10.0.tgz#5f60e61ed7c9a83c8591616b1174a1c4ab2de0cd" + integrity sha512-TsIJTYafTTapCA32LLNpx/AD6ntepR1TG8jEVx35NiAAWCPymhUfuca8kRcUNd3WIGVMDcMKn9kkphoxEz+6Ag== dependencies: "@babel/runtime" "^7.10.1" + "@rc-component/trigger" "^1.5.0" classnames "2.x" rc-motion "^2.0.1" - rc-overflow "^1.0.0" - rc-trigger "^5.0.4" + rc-overflow "^1.3.1" rc-util "^5.16.1" - rc-virtual-list "^3.2.0" + rc-virtual-list "^3.5.2" -rc-slider@~10.0.0: - version "10.0.1" - resolved "https://registry.npmjs.org/rc-slider/-/rc-slider-10.0.1.tgz" - integrity sha512-igTKF3zBet7oS/3yNiIlmU8KnZ45npmrmHlUUio8PNbIhzMcsh+oE/r2UD42Y6YD2D/s+kzCQkzQrPD6RY435Q== +rc-slider@~10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.4.0.tgz#efc016583fdea5f5dfb4f3dc61b6755a19e5f453" + integrity sha512-ZlpWjFhOlEf0w4Ng31avFBkXNNBj60NAcTPaIoiCxBkJ29wOtHSPMqv9PZeEoqmx64bpJkgK7kPa47HG4LPzww== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" - rc-util "^5.18.1" - shallowequal "^1.1.0" + rc-util "^5.27.0" -rc-steps@~6.0.0-alpha.2: - version "6.0.0" - resolved "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.0.tgz" - integrity sha512-+KfMZIty40mYCQSDvYbZ1jwnuObLauTiIskT1hL4FFOBHP6ZOr8LK0m143yD3kEN5XKHSEX1DIwCj3AYZpoeNQ== +rc-steps@~6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-6.0.1.tgz#c2136cd0087733f6d509209a84a5c80dc29a274d" + integrity sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g== dependencies: "@babel/runtime" "^7.16.7" classnames "^2.2.3" rc-util "^5.16.1" -rc-switch@~4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/rc-switch/-/rc-switch-4.0.0.tgz" - integrity sha512-IfrYC99vN0gKaTyjQdqYuADU0eH00SAFHg3jOp8HrmUpJruhV1SohJzrCbPqPraZeX/6X/QKkdLfkdnUub05WA== +rc-switch@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-4.1.0.tgz#f37d81b4e0c5afd1274fd85367b17306bf25e7d7" + integrity sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg== dependencies: - "@babel/runtime" "^7.10.1" + "@babel/runtime" "^7.21.0" classnames "^2.2.1" - rc-util "^5.0.1" + rc-util "^5.30.0" -rc-table@~7.26.0: - version "7.26.0" - resolved "https://registry.npmjs.org/rc-table/-/rc-table-7.26.0.tgz" - integrity sha512-0cD8e6S+DTGAt5nBZQIPFYEaIukn17sfa5uFL98faHlH/whZzD8ii3dbFL4wmUDEL4BLybhYop+QUfZJ4CPvNQ== +rc-table@~7.35.2: + version "7.35.2" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.35.2.tgz#8fcd59e9342bf13fe131ca54c6105e71ab95588b" + integrity sha512-ZLIZdAEdfen21FI21xt2LDg9chQ7gc5Lpy4nkjWKPDgmQMnH0KJ8JQQzrd3zrEN16xzjiVdHHvRmi1RU8BtgYg== dependencies: "@babel/runtime" "^7.10.1" + "@rc-component/context" "^1.4.0" classnames "^2.2.5" rc-resize-observer "^1.1.0" - rc-util "^5.22.5" - shallowequal "^1.1.0" + rc-util "^5.37.0" + rc-virtual-list "^3.11.1" -rc-tabs@~12.4.2: - version "12.4.2" - resolved "https://registry.npmjs.org/rc-tabs/-/rc-tabs-12.4.2.tgz" - integrity sha512-FFlGwuTjQUznWzJtyhmHc6KAp5lRQFxKUv9Aj1UtsOYe2e7WGmuzcrd+/LQchuPe0VjhaZPdGkmFGcqGqNO6ow== +rc-tabs@~12.13.1: + version "12.13.1" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-12.13.1.tgz#e28c5652dfed4e72eb27a75a2691754afd3e5f68" + integrity sha512-83u3l2QkO0UznCzdBLEk9WnNcT+imtmDmMT993sUUEOGnNQAmqOdev0XjeqrcvsAMe9CDpAWDFd7L/RZw+LVJQ== dependencies: "@babel/runtime" "^7.11.2" classnames "2.x" - rc-dropdown "~4.0.0" - rc-menu "~9.8.0" + rc-dropdown "~4.1.0" + rc-menu "~9.12.0" rc-motion "^2.6.2" rc-resize-observer "^1.0.0" - rc-util "^5.16.0" + rc-util "^5.34.1" -rc-textarea@^0.4.0, rc-textarea@~0.4.5: - version "0.4.7" - resolved "https://registry.npmjs.org/rc-textarea/-/rc-textarea-0.4.7.tgz" - integrity sha512-IQPd1CDI3mnMlkFyzt2O4gQ2lxUsnBAeJEoZGJnkkXgORNqyM9qovdrCj9NzcRfpHgLdzaEbU3AmobNFGUznwQ== +rc-textarea@~1.5.0, rc-textarea@~1.5.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.5.2.tgz#8702464555ffcc16219289d4c1c4f721a79f54a9" + integrity sha512-VVwKYtkp5whZVhP+llX8zM8TtI3dv+BDA0FUbmBMGLaW/tuBJ7Yh35yPabO63V+Bi68xv17eI4hy+/4p2G0gFg== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" + rc-input "~1.3.5" rc-resize-observer "^1.0.0" - rc-util "^5.24.4" - shallowequal "^1.1.0" + rc-util "^5.27.0" -rc-tooltip@~5.2.0: - version "5.2.2" - resolved "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-5.2.2.tgz" - integrity sha512-jtQzU/18S6EI3lhSGoDYhPqNpWajMtS5VV/ld1LwyfrDByQpYmw/LW6U7oFXXLukjfDHQ7Ju705A82PRNFWYhg== +rc-tooltip@~6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.1.2.tgz#33923ecfb2cf24347975093cbd0b048ab33c9567" + integrity sha512-89zwvybvCxGJu3+gGF8w5AXd4HHk6hIN7K0vZbkzjilVaEAIWPqc1fcyeUeP71n3VCcw7pTL9LyFupFbrx8gHw== dependencies: "@babel/runtime" "^7.11.2" + "@rc-component/trigger" "^1.18.0" classnames "^2.3.1" - rc-trigger "^5.0.0" -rc-tree-select@~5.5.4: - version "5.5.5" - resolved "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.5.5.tgz" - integrity sha512-k2av7jF6tW9bIO4mQhaVdV4kJ1c54oxV3/hHVU+oD251Gb5JN+m1RbJFTMf1o0rAFqkvto33rxMdpafaGKQRJw== +rc-tree-select@~5.15.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.15.0.tgz#8591f1dd28b043dde6fa1ca30c7acb198b160a42" + integrity sha512-YJHfdO6azFnR0/JuNBZLDptGE4/RGfVeHAafUIYcm2T3RBkL1O8aVqiHvwIyLzdK59ry0NLrByd+3TkfpRM+9Q== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" - rc-select "~14.1.0" - rc-tree "~5.7.0" + rc-select "~14.10.0" + rc-tree "~5.8.1" rc-util "^5.16.1" -rc-tree@~5.7.0: - version "5.7.3" - resolved "https://registry.npmjs.org/rc-tree/-/rc-tree-5.7.3.tgz" - integrity sha512-Oql2S9+ZmT+mfTp5SNo1XM0QvkENjc0mPRFsHWRFSPuKird0OYMZZKmLznUJ+0aGDeFFWN42wiUZJtMFhrLgLw== +rc-tree@~5.8.1, rc-tree@~5.8.2: + version "5.8.2" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.8.2.tgz#ed3a3f7c56597bbeab3303407a9e1739bbf15621" + integrity sha512-xH/fcgLHWTLmrSuNphU8XAqV7CdaOQgm4KywlLGNoTMhDAcNR3GVNP6cZzb0GrKmIZ9yae+QLot/cAgUdPRMzg== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.0.1" rc-util "^5.16.1" - rc-virtual-list "^3.4.8" + rc-virtual-list "^3.5.1" -rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.2.10, rc-trigger@^5.3.1, rc-trigger@^5.3.4: - version "5.3.4" - resolved "https://registry.npmjs.org/rc-trigger/-/rc-trigger-5.3.4.tgz" - integrity sha512-mQv+vas0TwKcjAO2izNPkqR4j86OemLRmvL2nOzdP9OWNWA1ivoTt5hzFqYNW9zACwmTezRiN8bttrC7cZzYSw== - dependencies: - "@babel/runtime" "^7.18.3" - classnames "^2.2.6" - rc-align "^4.0.0" - rc-motion "^2.0.0" - rc-util "^5.19.2" - -rc-upload@~4.3.0: - version "4.3.4" - resolved "https://registry.npmjs.org/rc-upload/-/rc-upload-4.3.4.tgz" - integrity sha512-uVbtHFGNjHG/RyAfm9fluXB6pvArAGyAx8z7XzXXyorEgVIWj6mOlriuDm0XowDHYz4ycNK0nE0oP3cbFnzxiQ== +rc-upload@~4.3.5: + version "4.3.5" + resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.3.5.tgz#12fc69b2af74d08646a104828831bcaf44076eda" + integrity sha512-EHlKJbhkgFSQHliTj9v/2K5aEuFwfUQgZARzD7AmAPOneZEPiCNF3n6PEWIuqz9h7oq6FuXgdR67sC5BWFxJbA== dependencies: "@babel/runtime" "^7.18.3" classnames "^2.2.5" rc-util "^5.2.0" -rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.15.0, rc-util@^5.16.0, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.19.2, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.21.2, rc-util@^5.22.5, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.26.0, rc-util@^5.27.0, rc-util@^5.28.0, rc-util@^5.6.1, rc-util@^5.8.0, rc-util@^5.9.4: +rc-util@^5.0.1, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.28.0, rc-util@^5.9.4: version "5.30.0" resolved "https://registry.npmjs.org/rc-util/-/rc-util-5.30.0.tgz" integrity sha512-uaWpF/CZGyXuhQG71MWxkU+0bWkPEgqZUxEv251Cu7p3kpHDNm5+Ygu/U8ux0a/zbfGW8PsKcJL0XVBOMrlIZg== @@ -12674,15 +12759,23 @@ rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.15.0, rc-util@^5.16.0, rc-util@^5.16. "@babel/runtime" "^7.18.3" react-is "^16.12.0" -rc-virtual-list@^3.2.0, rc-virtual-list@^3.4.8: - version "3.4.13" - resolved "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.4.13.tgz" - integrity sha512-cPOVDmcNM7rH6ANotanMDilW/55XnFPw0Jh/GQYtrzZSy3AmWvCnqVNyNC/pgg3lfVmX2994dlzAhuUrd4jG7w== +rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0: + version "5.38.1" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.38.1.tgz#4915503b89855f5c5cd9afd4c72a7a17568777bb" + integrity sha512-e4ZMs7q9XqwTuhIK7zBIVFltUtMSjphuPPQXHoHlzRzNdOwUxDejo0Zls5HYaJfRKNURcsS/ceKVULlhjBrxng== + dependencies: + "@babel/runtime" "^7.18.3" + react-is "^18.2.0" + +rc-virtual-list@^3.11.1, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: + version "3.11.3" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.11.3.tgz#77d4e12e20c1ba314b43c0e37e118296674c5401" + integrity sha512-tu5UtrMk/AXonHwHxUogdXAWynaXsrx1i6dsgg+lOo/KJSF8oBAcprh1z5J3xgnPJD5hXxTL58F8s8onokdt0Q== dependencies: "@babel/runtime" "^7.20.0" classnames "^2.2.6" rc-resize-observer "^1.0.0" - rc-util "^5.15.0" + rc-util "^5.36.0" react-addons-update@15.6.3: version "15.6.3" @@ -12733,6 +12826,13 @@ react-draggable@^4.0.0, react-draggable@^4.0.3: clsx "^1.1.1" prop-types "^15.8.1" +react-error-boundary@4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-4.0.11.tgz#36bf44de7746714725a814630282fee83a7c9a1c" + integrity sha512-U13ul67aP5DOSPNSCWQ/eO0AQEYzEFkVljULQIjMV0KlffTAhxuDoBKdO0pb/JZ8mDhMKFZ9NZi0BmLGUiNphw== + dependencies: + "@babel/runtime" "^7.12.5" + react-fast-compare@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" @@ -12801,11 +12901,6 @@ react-i18next@^11.16.1: "@babel/runtime" "^7.14.5" html-parse-stringify "^3.0.1" -react-intersection-observer@9.4.1: - version "9.4.1" - resolved "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.4.1.tgz" - integrity sha512-IXpIsPe6BleFOEHKzKh5UjwRUaz/JYS0lT/HPsupWEQou2hDqjhLMStc5zyE3eQVT4Fk3FufM8Fw33qW1uyeiw== - react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" @@ -12816,7 +12911,7 @@ react-is@^17.0.1, react-is@^17.0.2: resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0: +react-is@^18.0.0, react-is@^18.2.0: version "18.2.0" resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== @@ -13622,10 +13717,10 @@ screenfull@^5.1.0: resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz" integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== -scroll-into-view-if-needed@^3.0.3: - version "3.0.10" - resolved "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.0.10.tgz" - integrity sha512-t44QCeDKAPf1mtQH3fYpWz8IM/DyvHLjs8wUvvwMYxk5moOqCzrMSxK6HQVD0QVmVjXFavoFIPRVrMuJPKAvtg== +scroll-into-view-if-needed@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz#fa9524518c799b45a2ef6bbffb92bcad0296d01f" + integrity sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ== dependencies: compute-scroll-into-view "^3.0.2" @@ -14482,6 +14577,11 @@ throttle-debounce@^3.0.1: resolved "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz" integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg== +throttle-debounce@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.0.tgz#a17a4039e82a2ed38a5e7268e4132d6960d41933" + integrity sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg== + through2@^4.0.0: version "4.0.2" resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz" @@ -14967,6 +15067,11 @@ uplot@1.6.24: resolved "https://registry.yarnpkg.com/uplot/-/uplot-1.6.24.tgz#dfa213fa7da92763261920ea972ed1a5f9f6af12" integrity sha512-WpH2BsrFrqxkMu+4XBvc0eCDsRBhzoq9crttYeSI0bfxpzR5YoSVzZXOKFVWcVC7sp/aDXrdDPbDZGCtck2PVg== +uplot@1.6.26: + version "1.6.26" + resolved "https://registry.yarnpkg.com/uplot/-/uplot-1.6.26.tgz#a6012fd141ad4a71741c75af0c71283d0ade45a7" + integrity sha512-qN0mveL6UsP40TnHzHAJkUQvpfA3y8zSLXtXKVlJo/sLfj2+vjan/Z3g81MCZjy/hEDUFNtnLftPmETDA4s7Rg== + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" diff --git a/go.mod b/go.mod index 535fa4ba65..bcc095ec50 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module go.signoz.io/signoz go 1.21 require ( - github.com/ClickHouse/clickhouse-go/v2 v2.14.0 + github.com/ClickHouse/clickhouse-go/v2 v2.15.0 github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb - github.com/SigNoz/signoz-otel-collector v0.79.13 + github.com/SigNoz/signoz-otel-collector v0.88.0 github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 github.com/SigNoz/zap_otlp/zap_otlp_sync v0.0.0-20230822164844-1b861a431974 - github.com/antonmedv/expr v1.12.5 + github.com/antonmedv/expr v1.15.3 github.com/auth0/go-jwt-middleware v1.0.1 github.com/cespare/xxhash v1.1.0 github.com/coreos/go-oidc/v3 v3.4.0 @@ -29,18 +29,18 @@ require ( github.com/mailru/easyjson v0.7.7 github.com/mattn/go-sqlite3 v2.0.3+incompatible github.com/minio/minio-go/v6 v6.0.57 - github.com/mitchellh/mapstructure v1.5.0 + github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 github.com/oklog/oklog v0.3.2 github.com/open-telemetry/opamp-go v0.5.0 - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.79.0 - github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.79.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.88.0 + github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.88.0 github.com/opentracing/opentracing-go v1.2.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f github.com/prometheus/common v0.44.0 github.com/prometheus/prometheus v2.5.0+incompatible - github.com/rs/cors v1.9.0 + github.com/rs/cors v1.10.1 github.com/russellhaering/gosaml2 v0.9.0 github.com/russellhaering/goxmldsig v1.2.0 github.com/samber/lo v1.38.1 @@ -50,27 +50,31 @@ require ( github.com/soheilhy/cmux v0.1.5 github.com/srikanthccv/ClickHouse-go-mock v0.4.0 github.com/stretchr/testify v1.8.4 - go.opentelemetry.io/collector v0.79.0 - go.opentelemetry.io/collector/component v0.79.0 - go.opentelemetry.io/collector/confmap v0.79.0 - go.opentelemetry.io/collector/consumer v0.79.0 - go.opentelemetry.io/collector/exporter v0.79.0 - go.opentelemetry.io/collector/pdata v1.0.0-rcv0014 - go.opentelemetry.io/collector/receiver v0.79.0 - go.opentelemetry.io/otel v1.18.0 - go.opentelemetry.io/otel/sdk v1.16.0 + go.opentelemetry.io/collector/component v0.88.0 + go.opentelemetry.io/collector/confmap v0.88.0 + go.opentelemetry.io/collector/connector v0.88.0 + go.opentelemetry.io/collector/consumer v0.88.0 + go.opentelemetry.io/collector/exporter v0.88.0 + go.opentelemetry.io/collector/extension v0.88.0 + go.opentelemetry.io/collector/otelcol v0.88.0 + go.opentelemetry.io/collector/pdata v1.0.0-rcv0017 + go.opentelemetry.io/collector/processor v0.88.0 + go.opentelemetry.io/collector/receiver v0.88.0 + go.opentelemetry.io/collector/service v0.88.0 + go.opentelemetry.io/otel v1.19.0 + go.opentelemetry.io/otel/sdk v1.19.0 go.uber.org/multierr v1.11.0 - go.uber.org/zap v1.25.0 + go.uber.org/zap v1.26.0 golang.org/x/crypto v0.14.0 golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 golang.org/x/net v0.17.0 - golang.org/x/oauth2 v0.10.0 - google.golang.org/grpc v1.57.1 + golang.org/x/oauth2 v0.13.0 + google.golang.org/grpc v1.59.0 google.golang.org/protobuf v1.31.0 gopkg.in/segmentio/analytics-go.v3 v3.1.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/apimachinery v0.27.3 + k8s.io/apimachinery v0.28.2 ) require ( @@ -82,8 +86,8 @@ require ( github.com/ClickHouse/ch-go v0.58.2 // indirect github.com/DATA-DOG/go-sqlmock v1.5.0 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/aws/aws-sdk-go v1.44.302 // indirect + github.com/andybalholm/brotli v1.0.6 // indirect + github.com/aws/aws-sdk-go v1.45.26 // indirect github.com/beevik/etree v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect @@ -110,16 +114,19 @@ require ( github.com/gorilla/websocket v1.5.0 // indirect github.com/gosimple/unidecode v1.0.0 // indirect github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/influxdata/go-syslog/v3 v3.0.1-0.20210608084020-ac565dc76ba6 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.1 // indirect github.com/klauspost/cpuid v1.2.3 // indirect + github.com/knadh/koanf/v2 v2.0.1 // indirect github.com/kylelemons/godebug v1.1.0 // indirect + github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect @@ -131,56 +138,65 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect - github.com/observiq/ctimefmt v1.0.0 // indirect github.com/oklog/run v1.1.0 // indirect github.com/oklog/ulid v1.3.1 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.79.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.88.0 // indirect github.com/paulmach/orb v0.10.0 // indirect github.com/pierrec/lz4/v4 v4.1.18 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/prometheus/client_golang v1.16.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect - github.com/prometheus/procfs v0.11.0 // indirect + github.com/prometheus/procfs v0.11.1 // indirect github.com/prometheus/statsd_exporter v0.22.7 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/segmentio/backo-go v1.0.1 // indirect - github.com/shirou/gopsutil/v3 v3.23.5 // indirect + github.com/shirou/gopsutil/v3 v3.23.9 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.2 // indirect github.com/smarty/assertions v1.15.0 // indirect github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/tklauser/go-sysconf v0.3.11 // indirect - github.com/tklauser/numcpus v0.6.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/vjeantet/grok v1.0.1 // indirect github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/collector/featuregate v1.0.0-rcv0012 // indirect - go.opentelemetry.io/collector/semconv v0.81.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect - go.opentelemetry.io/otel/bridge/opencensus v0.39.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect - go.opentelemetry.io/otel/metric v1.18.0 // indirect - go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect - go.opentelemetry.io/otel/trace v1.18.0 // indirect + go.opentelemetry.io/collector v0.88.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.88.0 // indirect + go.opentelemetry.io/collector/featuregate v1.0.0-rcv0017 // indirect + go.opentelemetry.io/collector/semconv v0.88.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.20.0 // indirect + go.opentelemetry.io/otel/bridge/opencensus v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.2.1 // indirect - golang.org/x/sync v0.3.0 // indirect + golang.org/x/sync v0.4.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect - gonum.org/v1/gonum v0.13.0 // indirect + gonum.org/v1/gonum v0.14.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect k8s.io/klog/v2 v2.100.1 // indirect diff --git a/go.sum b/go.sum index 7a98a2c8aa..5b74971911 100644 --- a/go.sum +++ b/go.sum @@ -86,21 +86,20 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/ch-go v0.58.2 h1:jSm2szHbT9MCAB1rJ3WuCJqmGLi5UTjlNu+f530UTS0= github.com/ClickHouse/ch-go v0.58.2/go.mod h1:Ap/0bEmiLa14gYjCiRkYGbXvbe8vwdrfTYWhsuQ99aw= -github.com/ClickHouse/clickhouse-go/v2 v2.14.0 h1:7pzOLkWTc+Kn6e8Q2jtkTT9G+7RFY6QCw1Kc2nQ5pW4= -github.com/ClickHouse/clickhouse-go/v2 v2.14.0/go.mod h1:PHqbMvJTQ0EI4a1vJhmbmL/Ajr+Cin2O+WJjnYctJvg= +github.com/ClickHouse/clickhouse-go/v2 v2.15.0 h1:G0hTKyO8fXXR1bGnZ0DY3vTG01xYfOGW76zgjg5tmC4= +github.com/ClickHouse/clickhouse-go/v2 v2.15.0/go.mod h1:kXt1SRq0PIRa6aKZD7TnFnY9PQKmc2b13sHtOYcK6cQ= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Mottl/ctimefmt v0.0.0-20190803144728-fd2ac23a585a/go.mod h1:eyj2WSIdoPMPs2eNTLpSmM6Nzqo4V80/d6jHpnJ1SAI= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb h1:bneLSKPf9YUSFmafKx32bynV6QrzViL/s+ZDvQxH1E4= 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/go.mod h1:MffmFu2qFILQrOHehx3D0XjYtaZMVfI+Ppeiv98x4Ww= -github.com/SigNoz/signoz-otel-collector v0.79.13 h1:An8tJwvIpbfyC2Gtxs/Z424jLbKO2a6W7UzQ64G5/zI= -github.com/SigNoz/signoz-otel-collector v0.79.13/go.mod h1:P6tjd7wTHgHvBk6lHAXR++EuQaGY2mGu0aQWyI086qs= +github.com/SigNoz/signoz-otel-collector v0.88.0 h1:ujcSkgc2nDIp/RoEfl1UjgadcPfsBXJkNaqJk2YRDek= +github.com/SigNoz/signoz-otel-collector v0.88.0/go.mod h1:KyEc6JSFS6f8Nw3UdSm4aGDGucEpQYZUdYwjvY8uMVc= 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/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 h1:PKVgdf83Yw+lZJbFtNGBgqXiXNf3+kOXW2qZ7Ms7OaY= @@ -114,11 +113,11 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= +github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antonmedv/expr v1.12.5 h1:Fq4okale9swwL3OeLLs9WD9H6GbgBLJyN/NUHRv+n0E= -github.com/antonmedv/expr v1.12.5/go.mod h1:FPC8iWArxls7axbVLsW+kpg1mz29A1b2M6jt+hZfDkU= +github.com/antonmedv/expr v1.15.3 h1:q3hOJZNvLvhqE8OHBs1cFRdbXFNKuA+bHmRaI+AmRmI= +github.com/antonmedv/expr v1.15.3/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= @@ -128,8 +127,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/auth0/go-jwt-middleware v1.0.1 h1:/fsQ4vRr4zod1wKReUH+0A3ySRjGiT9G34kypO/EKwI= github.com/auth0/go-jwt-middleware v1.0.1/go.mod h1:YSeUX3z6+TF2H+7padiEqNJ73Zy9vXW72U//IgN0BIM= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.302 h1:ST3ko6GrJKn3Xi+nAvxjG3uk/V1pW8KC52WLeIxqqNk= -github.com/aws/aws-sdk-go v1.44.302/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.45.26 h1:PJ2NJNY5N/yeobLYe1Y+xLdavBi67ZI8gvph6ftwVCg= +github.com/aws/aws-sdk-go v1.45.26/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= @@ -142,8 +141,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21 github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -196,8 +193,8 @@ github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= 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/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 h1:IPrmumsT9t5BS7XcPhgsCTlkWbYg80SEXUzDpReaU6Y= 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= @@ -207,8 +204,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/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/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= -github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= +github.com/emicklei/go-restful/v3 v3.10.2/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.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -225,8 +222,8 @@ github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBF github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= -github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= @@ -271,12 +268,12 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 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/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/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/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-redis/redismock/v8 v8.11.5 h1:RJFIiua58hrBrSpXhnGX3on79AU3S271H4ZhRI1wyVo= @@ -300,8 +297,8 @@ github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzq github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -341,8 +338,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 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 v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= -github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= 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.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -357,8 +354,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -417,11 +415,11 @@ github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= -github.com/hashicorp/consul/api v1.22.0 h1:ydEvDooB/A0c/xpsBd8GSt7P2/zYPBui4KrNip0xGjE= -github.com/hashicorp/consul/api v1.22.0/go.mod h1:zHpYgZ7TeYqS6zaszjwSt128OwESRpnhU9aGa6ue3Eg= +github.com/hashicorp/consul/api v1.25.1 h1:CqrdhYzc8XZuPnhIYZWH45toM0LB9ZeYr/gvpLVI3PE= +github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= @@ -447,8 +445,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-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.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= -github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/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.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= @@ -460,8 +458,8 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= -github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -476,7 +474,6 @@ 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/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/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/go.mod h1:4iUG2NG8b61IAwNx6UsMWQ6IfIf/i1RsG0BbsKAyR5Q= github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= @@ -484,10 +481,12 @@ 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/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/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/go-syslog/v3 v3.0.1-0.20210608084020-ac565dc76ba6 h1:s9ZL6ZhFF8y6ebnm1FLvobkzoIu5xwDQUcRPk/IEhpM= +github.com/influxdata/go-syslog/v3 v3.0.1-0.20210608084020-ac565dc76ba6/go.mod h1:aXdIdfn2OcGnMhOTojXmwZqXKgC3MU5riiNvzwwG9OY= github.com/ionos-cloud/sdk-go/v6 v6.1.8 h1:493wE/BkZxJf7x79UCE0cYGPZoqQcPiEBALvt7uVGY0= github.com/ionos-cloud/sdk-go/v6 v6.1.8/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -519,12 +518,14 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.1 h1:NE3C767s2ak2bweCZo3+rdP4U/HoyVXLv/X9f2gPS5g= +github.com/klauspost/compress v1.17.1/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= +github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g= +github.com/knadh/koanf/v2 v2.0.1/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -542,6 +543,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 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/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165 h1:bCiVCRCs1Heq84lurVinUPy19keqGEe4jh5vtK37jcg= +github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= 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= @@ -563,8 +566,8 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -593,8 +596,9 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmLKZf+SjVanKKhCgf3bg+511DmU9eDQTen7LLbY= +github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -615,8 +619,6 @@ github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnu github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/observiq/ctimefmt v1.0.0 h1:r7vTJ+Slkrt9fZ67mkf+mA6zAdR5nGIJRMTzkUyvilk= -github.com/observiq/ctimefmt v1.0.0/go.mod h1:mxi62//WbSpG/roCO1c6MqZ7zQTvjVtYheqHN3eOjvc= github.com/oklog/oklog v0.3.2 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= @@ -633,23 +635,22 @@ github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= -github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= github.com/open-telemetry/opamp-go v0.5.0 h1:2YFbb6G4qBkq3yTRdVb5Nfz9hKHW/ldUyex352e1J7g= github.com/open-telemetry/opamp-go v0.5.0/go.mod h1:IMdeuHGVc5CjKSu5/oNV0o+UmiXuahoHvoZ4GOmAI9M= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.79.0 h1:okH5+f5UyufgnQXcBib2qUaKoTwsETWxdO0VJOXdl6s= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.79.0/go.mod h1:uGs9o7HgZXhRIgdHWT+VreU2ew4a08KTU4ejQIw6dTM= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.79.0 h1:OZPeakqoSZ1yRlmGBlWi9kISx/9PJzlNLGLutFPOQY0= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.79.0/go.mod h1:VOHKYi1wm+/c2wZA3mY1Grd4eYP8uS//EV0yHBbGfGw= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.79.0 h1:YtkbJjknfMJ4UKXdaUBTw37QLxYXuVMmozqPFTM0XPI= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.79.0/go.mod h1:BFqnTfM5Neh5A5gYtsxh2615yp/t39vUj7oRUcjevFU= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.79.0 h1:Cao5mD7NFJsMLM40lisqJ4Iy3+v+JOq+8cR79/Uki94= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.79.0/go.mod h1:/wfeJfzu3oAkC2boitFR3dZcnwNtwzryI/SW1LIhDLo= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.79.0 h1:o1aUgN0pA5Sc0s2bOUy7vDoNyJ6D6qdHihXk3BKyf58= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.79.0/go.mod h1:t8I2umZdg81AQmncs7fVHw1YMzSol3A7ecsc2lfqgaM= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.79.0 h1:EpuwiWvq1hqS4PAp/+kMvWVkM4o+PRGtTGSDLpmIeME= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.79.0/go.mod h1:0dccj1BrKVG00hvt2f70tu7Re1YjAl5Jpy2lduSrLnI= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.88.0 h1:9gjzrpUlzGC5BebgO1cxb/9KQ9yuIIE6B+6wLySKVCQ= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.88.0/go.mod h1:GXfK9q6RosmltLUcOdrQMS3hF1RYuwIgFTIa4RRR5J4= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.88.0 h1:2HoGcjmHHIDMafd3Uj3flQJrV8TC2FAnUiTKD8FH0G8= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.88.0/go.mod h1:JvXKcDtcOQRkz/Sw1m27K4QA3OwMbUvifoeEX2NQC6k= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.88.0 h1:jqdkgfHXcjvk6L2CyTUv3Rn+whX3TfFWd0Mz4QNAV1c= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.88.0/go.mod h1:5QXLdN4gdjAEcMHNEK/RrDdp+FObca0bS4/pRauyZs8= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.88.0 h1:S1FEVDH5GEMZQuHg8jfv47lCHHDFVjZBpO/Yrb/vKpE= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.88.0/go.mod h1:IJqzjDv6ZFeu7cYGCUzQ5/3CuTPVIo3UAGK3o2jK/Sw= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.88.0 h1:Ezi3FyxGbetZ12yAinyif/aabc9J7VyFdOvdufPCaUU= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.88.0/go.mod h1:6nO6NG5H5V5YGRp5onf9JnitXwhMfNXLSfZNyVwRPuw= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.88.0 h1:meHyTMeWC3xYativnHwYdnT9XwHWtfjioPRqgzaDJXA= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.88.0/go.mod h1:Vhb+pyxTKFjAoLaaJCiYHbJS6o56vQEvnJDhh/ws6yY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= @@ -695,14 +696,14 @@ github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= @@ -720,8 +721,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk= -github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0= github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= @@ -734,8 +735,8 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= -github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russellhaering/gosaml2 v0.9.0 h1:CNMnH42z/GirrKjdmNrSS6bAAs47F9bPdl4PfRmVOIk= github.com/russellhaering/gosaml2 v0.9.0/go.mod h1:byViER/1YPUa0Puj9ROZblpoq2jsE7h/CJmitzX0geU= github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg= @@ -756,8 +757,8 @@ github.com/segmentio/backo-go v1.0.1 h1:68RQccglxZeyURy93ASB/2kc9QudzgIDexJ927N+ github.com/segmentio/backo-go v1.0.1/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc= github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE= -github.com/shirou/gopsutil/v3 v3.23.5 h1:5SgDCeQ0KW0S4N0znjeM/eFHXXOKyv2dVNgRq/c9P6Y= -github.com/shirou/gopsutil/v3 v3.23.5/go.mod h1:Ng3Maa27Q2KARVJ0SPZF5NdrQSC3XHKP8IIWrHgMeLY= +github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= +github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -807,15 +808,14 @@ 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.1/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/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= -github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= -github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= -github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= @@ -850,46 +850,76 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/collector v0.79.0 h1:Lra7U0ilMor1g5WVkO3YZ0kZYsvzAtGN+Uq+CmC96JY= -go.opentelemetry.io/collector v0.79.0/go.mod h1:O2Vfwykphq9VqdATZiAypjnJMS3WFBXwFSe/0ujo38Q= -go.opentelemetry.io/collector/component v0.79.0 h1:ZKLJ4qa0AngmyGp1RQBJgl6OIP6mxdfrVpbz09h/W34= -go.opentelemetry.io/collector/component v0.79.0/go.mod h1:rX0gixMemcXZTZaML5zUiT+5txZUYkWnACscJkFVj18= -go.opentelemetry.io/collector/confmap v0.79.0 h1:a4XVde3lLP81BiSbt8AzVD6pvQBX8YkrB9ZtMSHKv1A= -go.opentelemetry.io/collector/confmap v0.79.0/go.mod h1:cKr2c7lVtEJCuMOncUPlcROJBbTFaHiPjYp1Y8RbL+Q= -go.opentelemetry.io/collector/consumer v0.79.0 h1:V/4PCvbTw2Bt+lYb/ogac0g/nCCb3oKnmz+jM3t5Dyk= -go.opentelemetry.io/collector/consumer v0.79.0/go.mod h1:VfqIyUI5K20zXx3mfVN+skmA+V3sV5fNorJ5TaIOj/U= -go.opentelemetry.io/collector/exporter v0.79.0 h1:PxhKgWf1AkZvN1PjiJT5xiO+pKZA9Y4fyuMs5aNFuEA= -go.opentelemetry.io/collector/exporter v0.79.0/go.mod h1:qlXiqnOUeHelpAwk03f8nB5+91UIqlA7udSBsj9bJ3M= -go.opentelemetry.io/collector/extension/zpagesextension v0.79.0 h1:I1DIomwNnNecBBSKLR3O0OlEqbPNCtMw48tAfj39VKM= -go.opentelemetry.io/collector/extension/zpagesextension v0.79.0/go.mod h1:zlgkyg7I4Hu0ZahSjlTl2RBrju744PcPyQ7IDDCFQWI= -go.opentelemetry.io/collector/featuregate v1.0.0-rcv0012 h1:pSO81lfikGEgRXHepmOGy2o6WWCly427UJCgMJC5c8g= -go.opentelemetry.io/collector/featuregate v1.0.0-rcv0012/go.mod h1:/kVAsGUCyJXIDSgHftCN63QiwAEVHRLX2Kh/S+dqgHY= -go.opentelemetry.io/collector/pdata v1.0.0-rcv0014 h1:iT5qH0NLmkGeIdDtnBogYDx7L58t6CaWGL378DEo2QY= -go.opentelemetry.io/collector/pdata v1.0.0-rcv0014/go.mod h1:BRvDrx43kiSoUx3mr7SoA7h9B8+OY99mUK+CZSQFWW4= -go.opentelemetry.io/collector/receiver v0.79.0 h1:Ag4hciAYklQWDpKbnmqhfh9zJlUskWvThpCpphp12b4= -go.opentelemetry.io/collector/receiver v0.79.0/go.mod h1:+/xe0VoYl6Mli+KQTZWBR2apqFsbioAAqu7abzKDskI= -go.opentelemetry.io/collector/semconv v0.81.0 h1:lCYNNo3powDvFIaTPP2jDKIrBiV1T92NK4QgL/aHYXw= -go.opentelemetry.io/collector/semconv v0.81.0/go.mod h1:TlYPtzvsXyHOgr5eATi43qEMqwSmIziivJB2uctKswo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 h1:KfYpVmrjI7JuToy5k8XV3nkapjWx48k4E4JOtVstzQI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0/go.mod h1:SeQhzAEccGVZVEy7aH87Nh0km+utSpo1pTv6eMMop48= -go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= -go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc= -go.opentelemetry.io/contrib/zpages v0.42.0 h1:hFscXKQ9PTjyIVmAr6zIV8cMoiEeR9lPIwPVqHi8+5Q= -go.opentelemetry.io/contrib/zpages v0.42.0/go.mod h1:qRJBEfB0iwRKrYImq5qfwTolmY8HXvZBRucvhuTVQZw= -go.opentelemetry.io/otel v1.18.0 h1:TgVozPGZ01nHyDZxK5WGPFB9QexeTMXEH7+tIClWfzs= -go.opentelemetry.io/otel v1.18.0/go.mod h1:9lWqYO0Db579XzVuCKFNPDl4s73Voa+zEck3wHaAYQI= -go.opentelemetry.io/otel/bridge/opencensus v0.39.0 h1:YHivttTaDhbZIHuPlg1sWsy2P5gj57vzqPfkHItgbwQ= -go.opentelemetry.io/otel/bridge/opencensus v0.39.0/go.mod h1:vZ4537pNjFDXEx//WldAR6Ro2LC8wwmFC76njAXwNPE= -go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= -go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= -go.opentelemetry.io/otel/metric v1.18.0 h1:JwVzw94UYmbx3ej++CwLUQZxEODDj/pOuTCvzhtRrSQ= -go.opentelemetry.io/otel/metric v1.18.0/go.mod h1:nNSpsVDjWGfb7chbRLUNW+PBNdcSTHD4Uu5pfFMOI0k= -go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= -go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= -go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= -go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= -go.opentelemetry.io/otel/trace v1.18.0 h1:NY+czwbHbmndxojTEKiSMHkG2ClNH2PwmcHrdo0JY10= -go.opentelemetry.io/otel/trace v1.18.0/go.mod h1:T2+SGJGuYZY3bjj5rgh/hN7KIrlpWC5nS8Mjvzckz+0= +go.opentelemetry.io/collector v0.88.0 h1:I0lerJK1h88vk7enriSgLV+h7dM099G9FgwkfmIZaf0= +go.opentelemetry.io/collector v0.88.0/go.mod h1:we0quZ+4txHS3Sfb0VdjFv95KYLGmto4ZAThCHiYgGA= +go.opentelemetry.io/collector/component v0.88.0 h1:LU/1ov5D/O/gv9D2Uv88EjNKHn7DHcUCZn1qQsb/zgw= +go.opentelemetry.io/collector/component v0.88.0/go.mod h1:4utKxz4Lilym3SPxNXJHosdaTjT1aQxI+TCmnJO54pU= +go.opentelemetry.io/collector/config/confignet v0.88.0 h1:CbVZQpWC8Bm/BKo3x2mnQZVdQKClU0gCa6SVbRCc930= +go.opentelemetry.io/collector/config/confignet v0.88.0/go.mod h1:cpO8JYWGONaViOygKVw+Hd2UoBcn2cUiyi0WWeFTwJY= +go.opentelemetry.io/collector/config/configtelemetry v0.88.0 h1:54Z9uoSTpbkq3esDwHvJMChoUH8p/nfesG2xJTOXayY= +go.opentelemetry.io/collector/config/configtelemetry v0.88.0/go.mod h1:+LAXM5WFMW/UbTlAuSs6L/W72WC+q8TBJt/6z39FPOU= +go.opentelemetry.io/collector/confmap v0.88.0 h1:tOgY6NXMXAL2hz2+zVDQ0jvBlCUHprSf90bw5ktbdaI= +go.opentelemetry.io/collector/confmap v0.88.0/go.mod h1:CSJlMk1KRZloXAygpiPeCLpuQiLVDEZYbGsGHIKHeUg= +go.opentelemetry.io/collector/connector v0.88.0 h1:hUTSMexixSx4rWExBfr5p3YzDdj9a9+cUveaRK0EdnQ= +go.opentelemetry.io/collector/connector v0.88.0/go.mod h1:vkOHpyWNlHQVFHKUB4Dp1yYCIpAFnouZ2REupkzL/PU= +go.opentelemetry.io/collector/consumer v0.88.0 h1:l8Ty5UHhZ2U6WCp4yHt97uW6vN1vMP0JbFeQEaVnEgY= +go.opentelemetry.io/collector/consumer v0.88.0/go.mod h1:VVoafgyhjpO6fuJu12GqspmuLrn91JCOou0sOtb9GOg= +go.opentelemetry.io/collector/exporter v0.88.0 h1:bDXltsjQslhT7tlObQzKJiHuP5LDPeZHrkpUh4cT6Kk= +go.opentelemetry.io/collector/exporter v0.88.0/go.mod h1:0KQKlbUlYBwNJ9Dfapn6mRLhdhtM3tUlDGgN88oDVug= +go.opentelemetry.io/collector/extension v0.88.0 h1:/WH97pQYypL7ZC5OEccoE0gFs6fjBC/Uh9NuVEYEoZ0= +go.opentelemetry.io/collector/extension v0.88.0/go.mod h1:5wPlOyWtVJcZS9CMhFUnuRvNQ0XIoV/iUSaZWtCjoHA= +go.opentelemetry.io/collector/extension/zpagesextension v0.88.0 h1:cpkwzjhq6jfkVq3ltUl9wdb/8RrWbn0utHTCU3K5Mhc= +go.opentelemetry.io/collector/extension/zpagesextension v0.88.0/go.mod h1:8LPmV8UkQgDAfNaAizQqLzYnYibzQv81eBGKv0Mk6wU= +go.opentelemetry.io/collector/featuregate v1.0.0-rcv0017 h1:DtJQalPXMWQqT6jd2LZ1oKrOfLJJRCi+rh2LKnkj4Zo= +go.opentelemetry.io/collector/featuregate v1.0.0-rcv0017/go.mod h1:fLmJMf1AoHttkF8p5oJAc4o5ZpHu8yO5XYJ7gbLCLzo= +go.opentelemetry.io/collector/otelcol v0.88.0 h1:f2eRVLJY66w9WFj5iT1Tg6Qxtlljagov9v8TPStuK2g= +go.opentelemetry.io/collector/otelcol v0.88.0/go.mod h1:F85TtMPt+ySe29HD6DOyvsMFCV3onaB3VJzky7qrtzQ= +go.opentelemetry.io/collector/pdata v1.0.0-rcv0017 h1:AgALhc2VenoA5l1DvTdg7mkzaBGqoTSuMkAtjsttBFo= +go.opentelemetry.io/collector/pdata v1.0.0-rcv0017/go.mod h1:Rv9fOclA5AtM/JGm0d4jBOIAo1+jBA13UT5Bx0ovXi4= +go.opentelemetry.io/collector/processor v0.88.0 h1:5BUZaH+RhTpgTVqBZCrBnN/vl0M1CtwQsZ8ek4iH1lc= +go.opentelemetry.io/collector/processor v0.88.0/go.mod h1:2T5KxgBQxXuuyMu9dh+PIBxQ/geCFYcdnjmlWZx8o3E= +go.opentelemetry.io/collector/receiver v0.88.0 h1:MPvVAFOfjl0+Ylka7so8QoK8T2Za2471rv5t3sqbbSY= +go.opentelemetry.io/collector/receiver v0.88.0/go.mod h1:MIZ6jPPZ+I8XibZm6I3RAn9h7Wcy2ZJsPmtXd2BLr60= +go.opentelemetry.io/collector/semconv v0.88.0 h1:8TVP4hYaUC87S6CCLKNoSxsUE0ChldE4vqotvNHHUnE= +go.opentelemetry.io/collector/semconv v0.88.0/go.mod h1:j/8THcqVxFna1FpvA2zYIsUperEtOaRaqoLYIN4doWw= +go.opentelemetry.io/collector/service v0.88.0 h1:KSue2w94Tb2xjenlm+SC2y2g87hdhFJeHMT9pEshKAE= +go.opentelemetry.io/collector/service v0.88.0/go.mod h1:+Fov4arJzWl8SBuMonvM7gOrfK72G+d+2WotRjR5c2I= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= +go.opentelemetry.io/contrib/propagators/b3 v1.20.0 h1:Yty9Vs4F3D6/liF1o6FNt0PvN85h/BJJ6DQKJ3nrcM0= +go.opentelemetry.io/contrib/propagators/b3 v1.20.0/go.mod h1:On4VgbkqYL18kbJlWsa18+cMNe6rYpBnPi1ARI/BrsU= +go.opentelemetry.io/contrib/zpages v0.45.0 h1:jIwHHGoWzJoZdbIUtWdErjL85Gni6BignnAFqDtMRL4= +go.opentelemetry.io/contrib/zpages v0.45.0/go.mod h1:4mIdA5hqH6hEx9sZgV50qKfQO8aIYolUZboHmz+G7vw= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/bridge/opencensus v0.42.0 h1:QvC+bcZkWMphWPiVqRQygMj6M0/3TOuJEO+erRA7kI8= +go.opentelemetry.io/otel/bridge/opencensus v0.42.0/go.mod h1:XJojP7g5DqYdiyArix/H9i1XzPPlIUc9dGLKtF9copI= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 h1:ZtfnDL+tUrs1F0Pzfwbg2d59Gru9NCH3bgSHBM6LDwU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0/go.mod h1:hG4Fj/y8TR/tlEDREo8tWstl9fO9gcFkn4xrx0Io8xU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 h1:NmnYCiR0qNufkldjVvyQfZTHSdzeHoZ41zggMsdMcLM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0/go.mod h1:UVAO61+umUsHLtYb8KXXRoHtxUkdOPkYidzW3gipRLQ= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 h1:wNMDy/LVGLj2h3p6zg4d0gypKfWKSWI14E1C4smOgl8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0/go.mod h1:YfbDdXAAkemWJK3H/DshvlrxqFB2rtW4rY6ky/3x/H0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0 h1:jwV9iQdvp38fxXi8ZC+lNpxjK16MRcZlpDYvbuO1FiA= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0/go.mod h1:f3bYiqNqhoPxkvI2LrXqQVC546K7BuRDL/kKuxkujhA= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 h1:4jJuoeOo9W6hZnz+r046fyoH5kykZPRvKfUXJVfMpB0= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0/go.mod h1:/MtYTE1SfC2QIcE0bDot6fIX+h+WvXjgTqgn9P0LNPE= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 h1:Nw7Dv4lwvGrI68+wULbcq7su9K2cebeCUrDjVrUJHxM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0/go.mod h1:1MsF6Y7gTqosgoZvHlzcaaM8DIMNZgJh87ykokoNH7Y= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/sdk/metric v1.19.0 h1:EJoTO5qysMsYCa+w4UghwFV/ptQgqSL/8Ni+hx+8i1k= +go.opentelemetry.io/otel/sdk/metric v1.19.0/go.mod h1:XjG0jQyFJrv2PbMvwND7LwCEhsJzCzV5210euduKcKY= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= @@ -903,8 +933,8 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= -go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -955,8 +985,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1036,8 +1066,8 @@ golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1051,8 +1081,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1145,8 +1175,9 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= 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= @@ -1229,8 +1260,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= -golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1238,8 +1269,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM= -gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= +gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= +gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1366,12 +1397,12 @@ google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 h1:Au6te5hbKUV8pIYWHqOUZ1pva5qK/rwbIhoXEUB9Lu8= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= -google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 h1:lCbbUxUDD+DiXx9Q6F/ttL0aAu7N2pz8XnmMm8ZW4NE= -google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 h1:XUODHrpzJEUeWmVo/jfNTLj0YyVveOo28oE6vkFbkO4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU= +google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a h1:a2MQQVoTo96JC9PMGtGBymLp7+/RzpFc2yX/9WfFg1c= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1405,8 +1436,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.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= -google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= 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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1466,16 +1497,16 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= -k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= -k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= -k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= -k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8= -k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48= +k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw= +k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg= +k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= +k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= +k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY= +k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= k8s.io/utils v0.0.0-20230711102312-30195339c3c7 h1:ZgnF1KZsYxWIifwSNZFZgNtWE89WI5yiP5WwlfDoIyc= k8s.io/utils v0.0.0-20230711102312-30195339c3c7/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= @@ -1483,8 +1514,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 7b32fbff45..82ce3fc551 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -45,6 +45,7 @@ import ( "go.signoz.io/signoz/pkg/query-service/app/logs" "go.signoz.io/signoz/pkg/query-service/app/services" + "go.signoz.io/signoz/pkg/query-service/auth" "go.signoz.io/signoz/pkg/query-service/constants" am "go.signoz.io/signoz/pkg/query-service/integrations/alertManager" "go.signoz.io/signoz/pkg/query-service/interfaces" @@ -3606,7 +3607,10 @@ func (r *ClickHouseReader) GetLogs(ctx context.Context, params *model.LogsFilter "lenFilters": lenFilters, } if lenFilters != 0 { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_LOGS_FILTERS, data) + userEmail, err := auth.GetEmailFromJwt(ctx) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_LOGS_FILTERS, data, userEmail) + } } query := fmt.Sprintf("%s from %s.%s", constants.LogsSQLSelect, r.logsDB, r.logsTable) @@ -3646,7 +3650,10 @@ func (r *ClickHouseReader) TailLogs(ctx context.Context, client *model.LogsTailC "lenFilters": lenFilters, } if lenFilters != 0 { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_LOGS_FILTERS, data) + userEmail, err := auth.GetEmailFromJwt(ctx) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_LOGS_FILTERS, data, userEmail) + } } if err != nil { @@ -3736,7 +3743,10 @@ func (r *ClickHouseReader) AggregateLogs(ctx context.Context, params *model.Logs "lenFilters": lenFilters, } if lenFilters != 0 { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_LOGS_FILTERS, data) + userEmail, err := auth.GetEmailFromJwt(ctx) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_LOGS_FILTERS, data, userEmail) + } } query := "" diff --git a/pkg/query-service/app/dashboards/model.go b/pkg/query-service/app/dashboards/model.go index fa51864d4c..698b697279 100644 --- a/pkg/query-service/app/dashboards/model.go +++ b/pkg/query-service/app/dashboards/model.go @@ -128,6 +128,12 @@ func InitDB(dataSourceName string) (*sqlx.DB, error) { return nil, fmt.Errorf("error in adding column updated_by to dashboards table: %s", err.Error()) } + locked := `ALTER TABLE dashboards ADD COLUMN locked INTEGER DEFAULT 0;` + _, err = db.Exec(locked) + if err != nil && !strings.Contains(err.Error(), "duplicate column name") { + return nil, fmt.Errorf("error in adding column locked to dashboards table: %s", err.Error()) + } + return db, nil } @@ -141,6 +147,7 @@ type Dashboard struct { UpdateBy *string `json:"updated_by" db:"updated_by"` Title string `json:"-" db:"-"` Data Data `json:"data" db:"data"` + Locked *int `json:"isLocked" db:"locked"` } type Data map[string]interface{} @@ -239,6 +246,12 @@ func DeleteDashboard(ctx context.Context, uuid string, fm interfaces.FeatureLook return dErr } + if user := common.GetUserFromContext(ctx); user != nil { + if dashboard.Locked != nil && *dashboard.Locked == 1 { + return model.BadRequest(fmt.Errorf("dashboard is locked, please unlock the dashboard to be able to delete it")) + } + } + query := `DELETE FROM dashboards WHERE uuid=?` result, err := db.Exec(query, uuid) @@ -289,6 +302,14 @@ func UpdateDashboard(ctx context.Context, uuid string, data map[string]interface return nil, apiErr } + var userEmail string + if user := common.GetUserFromContext(ctx); user != nil { + userEmail = user.Email + if dashboard.Locked != nil && *dashboard.Locked == 1 { + return nil, model.BadRequest(fmt.Errorf("dashboard is locked, please unlock the dashboard to be able to edit it")) + } + } + // check if the count of trace and logs QB panel has changed, if yes, then check feature flag count existingCount, existingTotal := countTraceAndLogsPanel(dashboard.Data) newCount, newTotal := countTraceAndLogsPanel(data) @@ -306,10 +327,6 @@ func UpdateDashboard(ctx context.Context, uuid string, data map[string]interface } dashboard.UpdatedAt = time.Now() - var userEmail string - if user := common.GetUserFromContext(ctx); user != nil { - userEmail = user.Email - } dashboard.UpdateBy = &userEmail dashboard.Data = data @@ -327,6 +344,24 @@ func UpdateDashboard(ctx context.Context, uuid string, data map[string]interface return dashboard, nil } +func LockUnlockDashboard(ctx context.Context, uuid string, lock bool) *model.ApiError { + var query string + if lock { + query = `UPDATE dashboards SET locked=1 WHERE uuid=?;` + } else { + query = `UPDATE dashboards SET locked=0 WHERE uuid=?;` + } + + _, err := db.Exec(query, uuid) + + if err != nil { + zap.S().Errorf("Error in updating dashboard: ", uuid, err) + return &model.ApiError{Typ: model.ErrorExec, Err: err} + } + + return nil +} + func updateFeatureUsage(fm interfaces.FeatureLookup, usage int64) *model.ApiError { feature, err := fm.GetFeatureFlag(model.QueryBuilderPanels) if err != nil { diff --git a/pkg/query-service/app/explorer/db.go b/pkg/query-service/app/explorer/db.go index 7e520abfe8..e0fbee4e51 100644 --- a/pkg/query-service/app/explorer/db.go +++ b/pkg/query-service/app/explorer/db.go @@ -144,7 +144,7 @@ func CreateView(ctx context.Context, view v3.SavedView) (string, error) { createdAt := time.Now() updatedAt := time.Now() - email, err := getEmailFromJwt(ctx) + email, err := auth.GetEmailFromJwt(ctx) if err != nil { return "", err } @@ -205,7 +205,7 @@ func UpdateView(ctx context.Context, uuid_ string, view v3.SavedView) error { return fmt.Errorf("error in marshalling explorer query data: %s", err.Error()) } - email, err := getEmailFromJwt(ctx) + email, err := auth.GetEmailFromJwt(ctx) if err != nil { return err } @@ -228,17 +228,3 @@ func DeleteView(uuid_ string) error { } return nil } - -func getEmailFromJwt(ctx context.Context) (string, error) { - jwt, err := auth.ExtractJwtFromContext(ctx) - if err != nil { - return "", err - } - - claims, err := auth.ParseJWT(jwt) - if err != nil { - return "", err - } - - return claims["email"].(string), nil -} diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 824400cc51..a6f517bc25 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -1397,8 +1397,10 @@ func (aH *APIHandler) submitFeedback(w http.ResponseWriter, r *http.Request) { "email": email, "message": message, } - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_INPRODUCT_FEEDBACK, data) - + userEmail, err := auth.GetEmailFromJwt(r.Context()) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_INPRODUCT_FEEDBACK, data, userEmail) + } } func (aH *APIHandler) getTopOperations(w http.ResponseWriter, r *http.Request) { @@ -1476,8 +1478,11 @@ func (aH *APIHandler) getServices(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{ "number": len(*result), } + userEmail, err := auth.GetEmailFromJwt(r.Context()) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_NUMBER_OF_SERVICES, data, userEmail) + } - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_NUMBER_OF_SERVICES, data) if (data["number"] != 0) && (data["number"] != telemetry.DEFAULT_NUMBER_OF_SERVICES) { telemetry.GetInstance().AddActiveTracesUser() } @@ -1796,8 +1801,7 @@ func (aH *APIHandler) inviteUser(w http.ResponseWriter, r *http.Request) { return } - ctx := auth.AttachJwtToContext(context.Background(), r) - resp, err := auth.Invite(ctx, req) + resp, err := auth.Invite(r.Context(), req) if err != nil { RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil) return @@ -1822,8 +1826,7 @@ func (aH *APIHandler) getInvite(w http.ResponseWriter, r *http.Request) { func (aH *APIHandler) revokeInvite(w http.ResponseWriter, r *http.Request) { email := mux.Vars(r)["email"] - ctx := auth.AttachJwtToContext(context.Background(), r) - if err := auth.RevokeInvite(ctx, email); err != nil { + if err := auth.RevokeInvite(r.Context(), email); err != nil { RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil) return } @@ -2201,8 +2204,8 @@ func (aH *APIHandler) editOrg(w http.ResponseWriter, r *http.Request) { "isAnonymous": req.IsAnonymous, "organizationName": req.Name, } - - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_ORG_SETTINGS, data) + userEmail, err := auth.GetEmailFromJwt(r.Context()) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_ORG_SETTINGS, data, userEmail) aH.WriteJSON(w, r, map[string]string{"data": "org updated successfully"}) } @@ -2365,7 +2368,6 @@ func (aH *APIHandler) getLogs(w http.ResponseWriter, r *http.Request) { RespondError(w, apiErr, "Incorrect params") return } - res, apiErr := aH.reader.GetLogs(r.Context(), params) if apiErr != nil { RespondError(w, apiErr, "Failed to fetch logs from the DB") @@ -2426,7 +2428,6 @@ func (aH *APIHandler) logAggregate(w http.ResponseWriter, r *http.Request) { RespondError(w, apiErr, "Incorrect params") return } - res, apiErr := aH.reader.AggregateLogs(r.Context(), params) if apiErr != nil { RespondError(w, apiErr, "Failed to fetch logs aggregate from the DB") @@ -2559,8 +2560,6 @@ func (ah *APIHandler) CreateLogsPipeline(w http.ResponseWriter, r *http.Request) return } - ctx := auth.AttachJwtToContext(context.Background(), r) - createPipeline := func( ctx context.Context, postable []logparsingpipeline.PostablePipeline, @@ -2578,7 +2577,7 @@ func (ah *APIHandler) CreateLogsPipeline(w http.ResponseWriter, r *http.Request) return ah.LogsParsingPipelineController.ApplyPipelines(ctx, postable) } - res, err := createPipeline(ctx, req.Pipelines) + res, err := createPipeline(r.Context(), req.Pipelines) if err != nil { RespondError(w, err, nil) return @@ -2613,8 +2612,7 @@ func (aH *APIHandler) createSavedViews(w http.ResponseWriter, r *http.Request) { RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil) return } - ctx := auth.AttachJwtToContext(context.Background(), r) - uuid, err := explorer.CreateView(ctx, view) + uuid, err := explorer.CreateView(r.Context(), view) if err != nil { RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil) return @@ -2648,8 +2646,7 @@ func (aH *APIHandler) updateSavedView(w http.ResponseWriter, r *http.Request) { return } - ctx := auth.AttachJwtToContext(context.Background(), r) - err = explorer.UpdateView(ctx, viewID, view) + err = explorer.UpdateView(r.Context(), viewID, view) if err != nil { RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil) return diff --git a/pkg/query-service/app/logparsingpipeline/controller.go b/pkg/query-service/app/logparsingpipeline/controller.go index c4b1b0e3ff..7880ac27b7 100644 --- a/pkg/query-service/app/logparsingpipeline/controller.go +++ b/pkg/query-service/app/logparsingpipeline/controller.go @@ -133,14 +133,15 @@ type PipelinesPreviewRequest struct { } type PipelinesPreviewResponse struct { - OutputLogs []model.SignozLog `json:"logs"` + OutputLogs []model.SignozLog `json:"logs"` + CollectorLogs []string `json:"collectorLogs"` } func (ic *LogParsingPipelineController) PreviewLogsPipelines( ctx context.Context, request *PipelinesPreviewRequest, ) (*PipelinesPreviewResponse, *model.ApiError) { - result, _, err := SimulatePipelinesProcessing( + result, collectorLogs, err := SimulatePipelinesProcessing( ctx, request.Pipelines, request.Logs, ) @@ -149,7 +150,8 @@ func (ic *LogParsingPipelineController) PreviewLogsPipelines( } return &PipelinesPreviewResponse{ - OutputLogs: result, + OutputLogs: result, + CollectorLogs: collectorLogs, }, nil } diff --git a/pkg/query-service/app/logparsingpipeline/db.go b/pkg/query-service/app/logparsingpipeline/db.go index ae4effb590..df187f0de3 100644 --- a/pkg/query-service/app/logparsingpipeline/db.go +++ b/pkg/query-service/app/logparsingpipeline/db.go @@ -55,8 +55,8 @@ func (r *Repo) insertPipeline( )) } - jwt, err := auth.ExtractJwtFromContext(ctx) - if err != nil { + jwt, ok := auth.ExtractJwtFromContext(ctx) + if !ok { return nil, model.UnauthorizedError(err) } diff --git a/pkg/query-service/app/logparsingpipeline/pipelineBuilder_test.go b/pkg/query-service/app/logparsingpipeline/pipelineBuilder_test.go index 34afddccce..e696d24cc3 100644 --- a/pkg/query-service/app/logparsingpipeline/pipelineBuilder_test.go +++ b/pkg/query-service/app/logparsingpipeline/pipelineBuilder_test.go @@ -351,13 +351,72 @@ func TestNoCollectorErrorsFromProcessorsForMismatchedLogs(t *testing.T) { for _, testCase := range testCases { testPipelines := []Pipeline{makeTestPipeline([]PipelineOperator{testCase.Operator})} - result, collectorErrorLogs, err := SimulatePipelinesProcessing( + result, collectorWarnAndErrorLogs, err := SimulatePipelinesProcessing( context.Background(), testPipelines, []model.SignozLog{testCase.NonMatchingLog}, ) require.Nil(err) - require.Equal(0, len(collectorErrorLogs), strings.Join(collectorErrorLogs, "\n")) + require.Equal(0, len(collectorWarnAndErrorLogs), strings.Join(collectorWarnAndErrorLogs, "\n")) require.Equal(1, len(result)) } } + +func TestResourceFiltersWork(t *testing.T) { + require := require.New(t) + + testPipeline := Pipeline{ + OrderId: 1, + Name: "pipeline1", + Alias: "pipeline1", + Enabled: true, + Filter: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{ + { + Key: v3.AttributeKey{ + Key: "service", + DataType: v3.AttributeKeyDataTypeString, + Type: v3.AttributeKeyTypeResource, + }, + Operator: "=", + Value: "nginx", + }, + }, + }, + Config: []PipelineOperator{ + { + ID: "add", + Type: "add", + Enabled: true, + Name: "add", + Field: "attributes.test", + Value: "test-value", + }, + }, + } + + testLog := model.SignozLog{ + Timestamp: uint64(time.Now().UnixNano()), + Body: "test log", + Attributes_string: map[string]string{}, + Resources_string: map[string]string{ + "service": "nginx", + }, + SeverityText: entry.Info.String(), + SeverityNumber: uint8(entry.Info), + SpanID: "", + TraceID: "", + } + + result, collectorWarnAndErrorLogs, err := SimulatePipelinesProcessing( + context.Background(), + []Pipeline{testPipeline}, + []model.SignozLog{testLog}, + ) + require.Nil(err) + require.Equal(0, len(collectorWarnAndErrorLogs), strings.Join(collectorWarnAndErrorLogs, "\n")) + require.Equal(1, len(result)) + + require.Equal(result[0].Attributes_string["test"], "test-value") +} diff --git a/pkg/query-service/app/logparsingpipeline/preview.go b/pkg/query-service/app/logparsingpipeline/preview.go index c1863649e6..4f725cafa5 100644 --- a/pkg/query-service/app/logparsingpipeline/preview.go +++ b/pkg/query-service/app/logparsingpipeline/preview.go @@ -22,7 +22,7 @@ func SimulatePipelinesProcessing( pipelines []Pipeline, logs []model.SignozLog, ) ( - output []model.SignozLog, collectorErrorLogs []string, apiErr *model.ApiError, + output []model.SignozLog, collectorWarnAndErrorLogs []string, apiErr *model.ApiError, ) { if len(pipelines) < 1 { diff --git a/pkg/query-service/app/logparsingpipeline/preview_test.go b/pkg/query-service/app/logparsingpipeline/preview_test.go index 9911fd26c2..a7fa51732b 100644 --- a/pkg/query-service/app/logparsingpipeline/preview_test.go +++ b/pkg/query-service/app/logparsingpipeline/preview_test.go @@ -104,7 +104,7 @@ func TestPipelinePreview(t *testing.T) { }, ) - result, collectorErrorLogs, err := SimulatePipelinesProcessing( + result, collectorWarnAndErrorLogs, err := SimulatePipelinesProcessing( context.Background(), testPipelines, []model.SignozLog{ @@ -114,7 +114,7 @@ func TestPipelinePreview(t *testing.T) { ) require.Nil(err) - require.Equal(0, len(collectorErrorLogs)) + require.Equal(0, len(collectorWarnAndErrorLogs)) require.Equal(2, len(result)) // matching log should have been modified as expected. @@ -190,7 +190,7 @@ func TestGrokParsingPreview(t *testing.T) { "method": "GET", }, ) - result, collectorErrorLogs, err := SimulatePipelinesProcessing( + result, collectorWarnAndErrorLogs, err := SimulatePipelinesProcessing( context.Background(), testPipelines, []model.SignozLog{ @@ -199,7 +199,7 @@ func TestGrokParsingPreview(t *testing.T) { ) require.Nil(err) - require.Equal(0, len(collectorErrorLogs)) + require.Equal(0, len(collectorWarnAndErrorLogs)) require.Equal(1, len(result)) processed := result[0] @@ -280,7 +280,7 @@ func TestTraceParsingPreview(t *testing.T) { TraceFlags: 0, } - result, collectorErrorLogs, err := SimulatePipelinesProcessing( + result, collectorWarnAndErrorLogs, err := SimulatePipelinesProcessing( context.Background(), testPipelines, []model.SignozLog{ @@ -289,7 +289,7 @@ func TestTraceParsingPreview(t *testing.T) { ) require.Nil(err) require.Equal(1, len(result)) - require.Equal(0, len(collectorErrorLogs)) + require.Equal(0, len(collectorWarnAndErrorLogs)) processed := result[0] require.Equal(testTraceId, processed.TraceID) @@ -301,7 +301,7 @@ func TestTraceParsingPreview(t *testing.T) { // trace parser should work even if parse_from value is empty testPipelines[0].Config[0].SpanId.ParseFrom = "" - result, collectorErrorLogs, err = SimulatePipelinesProcessing( + result, collectorWarnAndErrorLogs, err = SimulatePipelinesProcessing( context.Background(), testPipelines, []model.SignozLog{ @@ -310,7 +310,7 @@ func TestTraceParsingPreview(t *testing.T) { ) require.Nil(err) require.Equal(1, len(result)) - require.Equal(0, len(collectorErrorLogs)) + require.Equal(0, len(collectorWarnAndErrorLogs)) require.Equal("", result[0].SpanID) } diff --git a/pkg/query-service/app/parser/metrics.go b/pkg/query-service/app/parser/metrics.go index b13ff6d534..3391be78c7 100644 --- a/pkg/query-service/app/parser/metrics.go +++ b/pkg/query-service/app/parser/metrics.go @@ -91,7 +91,6 @@ func ParseMetricAutocompleteTagParams(r *http.Request) (*model.MetricAutocomplet } tagsStr := r.URL.Query().Get("tags") - // fmt.Println(tagsStr) // parsing tags var tags map[string]string diff --git a/pkg/query-service/app/server.go b/pkg/query-service/app/server.go index f7e6e43d2c..7af82b072a 100644 --- a/pkg/query-service/app/server.go +++ b/pkg/query-service/app/server.go @@ -152,6 +152,9 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) { } fluxInterval, err := time.ParseDuration(serverOptions.FluxInterval) + if err != nil { + return nil, err + } // ingestion pipelines manager logParsingPipelineController, err := logparsingpipeline.NewLogParsingPipelinesController(localDB, "sqlite") if err != nil { @@ -371,7 +374,10 @@ func extractQueryRangeV3Data(path string, r *http.Request) (map[string]interface telemetry.GetInstance().AddActiveLogsUser() } data["dataSources"] = dataSources - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_QUERY_RANGE_V3, data, true) + userEmail, err := auth.GetEmailFromJwt(r.Context()) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_QUERY_RANGE_V3, data, userEmail, true) + } } return data, true } @@ -392,6 +398,8 @@ func getActiveLogs(path string, r *http.Request) { func (s *Server) analyticsMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := auth.AttachJwtToContext(r.Context(), r) + r = r.WithContext(ctx) route := mux.CurrentRoute(r) path, _ := route.GetPathTemplate() @@ -410,7 +418,10 @@ func (s *Server) analyticsMiddleware(next http.Handler) http.Handler { // if telemetry.GetInstance().IsSampled() { if _, ok := telemetry.IgnoredPaths()[path]; !ok { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data) + userEmail, err := auth.GetEmailFromJwt(r.Context()) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail) + } } // } diff --git a/pkg/query-service/auth/auth.go b/pkg/query-service/auth/auth.go index 6190e87826..6ce7c425c9 100644 --- a/pkg/query-service/auth/auth.go +++ b/pkg/query-service/auth/auth.go @@ -49,8 +49,8 @@ func Invite(ctx context.Context, req *model.InviteRequest) (*model.InviteRespons return nil, errors.Wrap(err, "invalid invite request") } - jwtAdmin, err := ExtractJwtFromContext(ctx) - if err != nil { + jwtAdmin, ok := ExtractJwtFromContext(ctx) + if !ok { return nil, errors.Wrap(err, "failed to extract admin jwt token") } diff --git a/pkg/query-service/auth/jwt.go b/pkg/query-service/auth/jwt.go index df70064c51..90e2f7008d 100644 --- a/pkg/query-service/auth/jwt.go +++ b/pkg/query-service/auth/jwt.go @@ -11,7 +11,6 @@ import ( "github.com/pkg/errors" "go.signoz.io/signoz/pkg/query-service/model" "go.uber.org/zap" - "google.golang.org/grpc/metadata" ) var ( @@ -65,29 +64,12 @@ func AttachJwtToContext(ctx context.Context, r *http.Request) context.Context { return ctx } - if len(token) > 0 { - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - md = metadata.New(nil) - } - - md.Append("accessJwt", token) - ctx = metadata.NewIncomingContext(ctx, md) - } - return ctx + return context.WithValue(ctx, "accessJwt", token) } -func ExtractJwtFromContext(ctx context.Context) (string, error) { - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return "", errors.New("No JWT metadata token found") - } - accessJwt := md.Get("accessJwt") - if len(accessJwt) == 0 { - return "", errors.New("No JWT token found") - } - - return accessJwt[0], nil +func ExtractJwtFromContext(ctx context.Context) (string, bool) { + jwtToken, ok := ctx.Value("accessJwt").(string) + return jwtToken, ok } func ExtractJwtFromRequest(r *http.Request) (string, error) { @@ -96,9 +78,9 @@ func ExtractJwtFromRequest(r *http.Request) (string, error) { func ExtractUserIdFromContext(ctx context.Context) (string, error) { userId := "" - jwt, err := ExtractJwtFromContext(ctx) - if err != nil { - return "", model.InternalError(fmt.Errorf("failed to extract jwt from context %v", err)) + jwt, ok := ExtractJwtFromContext(ctx) + if !ok { + return "", model.InternalError(fmt.Errorf("failed to extract jwt from context")) } claims, err := ParseJWT(jwt) @@ -111,3 +93,17 @@ func ExtractUserIdFromContext(ctx context.Context) (string, error) { } return userId, nil } + +func GetEmailFromJwt(ctx context.Context) (string, error) { + jwt, ok := ExtractJwtFromContext(ctx) + if !ok { + return "", model.InternalError(fmt.Errorf("failed to extract jwt from context")) + } + + claims, err := ParseJWT(jwt) + if err != nil { + return "", model.InternalError(fmt.Errorf("failed get claims from jwt %v", err)) + } + + return claims["email"].(string), nil +} diff --git a/pkg/query-service/collectorsimulator/collectorsimulator.go b/pkg/query-service/collectorsimulator/collectorsimulator.go index 96a2d9fdf7..4a8236b483 100644 --- a/pkg/query-service/collectorsimulator/collectorsimulator.go +++ b/pkg/query-service/collectorsimulator/collectorsimulator.go @@ -181,14 +181,14 @@ func (l *CollectorSimulator) Shutdown(ctx context.Context) ( simulationErrs = append(simulationErrs, reportedErr.Error()) } - collectorErrorLogs, err := os.ReadFile(l.collectorLogsOutputFilePath) + collectorWarnAndErrorLogs, err := os.ReadFile(l.collectorLogsOutputFilePath) if err != nil { return nil, model.InternalError(fmt.Errorf( "could not read collector logs from tmp file: %w", err, )) } - if len(collectorErrorLogs) > 0 { - errorLines := strings.Split(string(collectorErrorLogs), "\n") + if len(collectorWarnAndErrorLogs) > 0 { + errorLines := strings.Split(string(collectorWarnAndErrorLogs), "\n") simulationErrs = append(simulationErrs, errorLines...) } @@ -219,7 +219,7 @@ func generateSimulationConfig( metrics: level: none logs: - level: error + level: warn output_paths: ["%s"] `, receiverId, exporterId, collectorLogsOutputPath) diff --git a/pkg/query-service/dao/sqlite/apdex.go b/pkg/query-service/dao/sqlite/apdex.go index 65c1eae350..63280d8fb6 100644 --- a/pkg/query-service/dao/sqlite/apdex.go +++ b/pkg/query-service/dao/sqlite/apdex.go @@ -2,7 +2,6 @@ package sqlite import ( "context" - "fmt" "github.com/jmoiron/sqlx" "go.signoz.io/signoz/pkg/query-service/model" @@ -51,7 +50,6 @@ func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, services []stri func (mds *ModelDaoSqlite) SetApdexSettings(ctx context.Context, apdexSettings *model.ApdexSettings) *model.ApiError { - fmt.Println("apdexSettings:", apdexSettings) _, err := mds.db.NamedExec(` INSERT OR REPLACE INTO apdex_settings ( service_name, diff --git a/pkg/query-service/dao/sqlite/rbac.go b/pkg/query-service/dao/sqlite/rbac.go index 07e870fdff..c28a2b675c 100644 --- a/pkg/query-service/dao/sqlite/rbac.go +++ b/pkg/query-service/dao/sqlite/rbac.go @@ -203,7 +203,7 @@ func (mds *ModelDaoSqlite) CreateUser(ctx context.Context, } telemetry.GetInstance().IdentifyUser(user) - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER, data) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER, data, user.Email) return user, nil } diff --git a/pkg/query-service/queryBuilderToExpr/queryBuilderToExpr.go b/pkg/query-service/queryBuilderToExpr/queryBuilderToExpr.go index f71470ff6e..ed68feb071 100644 --- a/pkg/query-service/queryBuilderToExpr/queryBuilderToExpr.go +++ b/pkg/query-service/queryBuilderToExpr/queryBuilderToExpr.go @@ -30,9 +30,9 @@ var logOperatorsToExpr = map[v3.FilterOperator]string{ func getName(v v3.AttributeKey) string { if v.Type == v3.AttributeKeyTypeTag { - return "attributes." + v.Key + return "attributes?." + v.Key } else if v.Type == v3.AttributeKeyTypeResource { - return "resources." + v.Key + return "resource?." + v.Key } return v.Key } @@ -41,7 +41,7 @@ func getTypeName(v v3.AttributeKeyType) string { if v == v3.AttributeKeyTypeTag { return "attributes" } else if v == v3.AttributeKeyTypeResource { - return "resources" + return "resource" } return "" } diff --git a/pkg/query-service/rules/alerting.go b/pkg/query-service/rules/alerting.go index 60cb4c9384..ad82470e83 100644 --- a/pkg/query-service/rules/alerting.go +++ b/pkg/query-service/rules/alerting.go @@ -144,6 +144,7 @@ type RuleCondition struct { Target *float64 `yaml:"target,omitempty" json:"target,omitempty"` MatchType `json:"matchType,omitempty"` TargetUnit string `json:"targetUnit,omitempty"` + SelectedQuery string `json:"selectedQueryName,omitempty"` } func (rc *RuleCondition) IsValid() bool { diff --git a/pkg/query-service/rules/manager.go b/pkg/query-service/rules/manager.go index 30c643b031..a91ff88101 100644 --- a/pkg/query-service/rules/manager.go +++ b/pkg/query-service/rules/manager.go @@ -714,6 +714,18 @@ func (m *Manager) GetRule(ctx context.Context, id string) (*GettableRule, error) return nil, err } r.Id = fmt.Sprintf("%d", s.Id) + // fetch state of rule from memory + if rm, ok := m.rules[r.Id]; !ok { + r.State = StateDisabled.String() + r.Disabled = true + } else { + r.State = rm.State().String() + } + r.CreatedAt = s.CreatedAt + r.CreatedBy = s.CreatedBy + r.UpdatedAt = s.UpdatedAt + r.UpdatedBy = s.UpdatedBy + return r, nil } diff --git a/pkg/query-service/rules/promRule.go b/pkg/query-service/rules/promRule.go index 25209b21c8..6d0cafa930 100644 --- a/pkg/query-service/rules/promRule.go +++ b/pkg/query-service/rules/promRule.go @@ -297,11 +297,28 @@ func (r *PromRule) SendAlerts(ctx context.Context, ts time.Time, resendDelay tim notifyFunc(ctx, "", alerts...) } +func (r *PromRule) GetSelectedQuery() string { + if r.ruleCondition != nil { + // If the user has explicitly set the selected query, we return that. + if r.ruleCondition.SelectedQuery != "" { + return r.ruleCondition.SelectedQuery + } + // Historically, we used to have only one query in the alerts for promql. + // So, if there is only one query, we return that. + // This is to maintain backward compatibility. + // For new rules, we will have to explicitly set the selected query. + return "A" + } + // This should never happen. + return "" +} + func (r *PromRule) getPqlQuery() (string, error) { if r.ruleCondition.CompositeQuery.QueryType == v3.QueryTypePromQL { if len(r.ruleCondition.CompositeQuery.PromQueries) > 0 { - if promQuery, ok := r.ruleCondition.CompositeQuery.PromQueries["A"]; ok { + selectedQuery := r.GetSelectedQuery() + if promQuery, ok := r.ruleCondition.CompositeQuery.PromQueries[selectedQuery]; ok { query := promQuery.Query if query == "" { return query, fmt.Errorf("a promquery needs to be set for this rule to function") diff --git a/pkg/query-service/rules/thresholdRule.go b/pkg/query-service/rules/thresholdRule.go index c4d6fdbf12..62b8b97635 100644 --- a/pkg/query-service/rules/thresholdRule.go +++ b/pkg/query-service/rules/thresholdRule.go @@ -135,13 +135,6 @@ func (r *ThresholdRule) PreferredChannels() []string { return r.preferredChannels } -func (r *ThresholdRule) target() *float64 { - if r.ruleCondition == nil { - return nil - } - return r.ruleCondition.Target -} - func (r *ThresholdRule) targetVal() float64 { if r.ruleCondition == nil || r.ruleCondition.Target == nil { return 0 @@ -217,26 +210,6 @@ func (r *ThresholdRule) Annotations() labels.BaseLabels { return r.annotations } -func (r *ThresholdRule) sample(alert *Alert, ts time.Time) Sample { - lb := labels.NewBuilder(r.labels) - alertLabels := alert.Labels.(labels.Labels) - for _, l := range alertLabels { - lb.Set(l.Name, l.Value) - } - - lb.Set(labels.MetricNameLabel, alertMetricName) - lb.Set(labels.AlertNameLabel, r.name) - lb.Set(labels.AlertRuleIdLabel, r.ID()) - lb.Set(labels.AlertStateLabel, alert.State.String()) - - s := Sample{ - Metric: lb.Labels(), - Point: Point{T: timestamp.FromTime(ts), V: 1}, - } - - return s -} - // GetEvaluationDuration returns the time in seconds it took to evaluate the alerting rule. func (r *ThresholdRule) GetEvaluationDuration() time.Duration { r.mtx.Lock() @@ -682,6 +655,54 @@ func (r *ThresholdRule) prepareClickhouseQueries(ts time.Time) (map[string]strin return queries, nil } +func (r *ThresholdRule) GetSelectedQuery() string { + + // The acutal query string is not relevant here + // we just need to know the selected query + + var queries map[string]string + var err error + + if r.ruleCondition.QueryType() == v3.QueryTypeBuilder { + queries, err = r.prepareBuilderQueries(time.Now()) + if err != nil { + zap.S().Errorf("ruleid:", r.ID(), "\t msg: failed to prepare metric queries", zap.Error(err)) + return "" + } + } else if r.ruleCondition.QueryType() == v3.QueryTypeClickHouseSQL { + queries, err = r.prepareClickhouseQueries(time.Now()) + if err != nil { + zap.S().Errorf("ruleid:", r.ID(), "\t msg: failed to prepare clickhouse queries", zap.Error(err)) + return "" + } + } + + if r.ruleCondition != nil { + if r.ruleCondition.SelectedQuery != "" { + return r.ruleCondition.SelectedQuery + } + + // The following logic exists for backward compatibility + // If there is no selected query, then + // - check if F1 is present, if yes, return F1 + // - else return the query with max ascii value + // this logic is not really correct. we should be considering + // whether the query is enabled or not. but this is a temporary + // fix to support backward compatibility + if _, ok := queries["F1"]; ok { + return "F1" + } + keys := make([]string, 0, len(queries)) + for k := range queries { + keys = append(keys, k) + } + sort.Strings(keys) + return keys[len(keys)-1] + } + // This should never happen + return "" +} + // query looks if alert condition is being // satisfied and returns the signals func (r *ThresholdRule) buildAndRunQuery(ctx context.Context, ts time.Time, ch clickhouse.Conn) (Vector, error) { @@ -691,7 +712,7 @@ func (r *ThresholdRule) buildAndRunQuery(ctx context.Context, ts time.Time, ch c } // var to hold target query to be executed - queries := make(map[string]string) + var queries map[string]string var err error // fetch the target query based on query type @@ -723,22 +744,7 @@ func (r *ThresholdRule) buildAndRunQuery(ctx context.Context, ts time.Time, ch c zap.S().Debugf("ruleid:", r.ID(), "\t runQueries:", queries) - // find target query label - if query, ok := queries["F1"]; ok { - // found a formula query, run with it - return r.runChQuery(ctx, ch, query) - } - - // no formula in rule condition, now look for - // query label with max ascii val - keys := make([]string, 0, len(queries)) - for k := range queries { - keys = append(keys, k) - } - sort.Strings(keys) - - queryLabel := keys[len(keys)-1] - + queryLabel := r.GetSelectedQuery() zap.S().Debugf("ruleId: ", r.ID(), "\t result query label:", queryLabel) if queryString, ok := queries[queryLabel]; ok { diff --git a/pkg/query-service/telemetry/telemetry.go b/pkg/query-service/telemetry/telemetry.go index 9aa88a6376..10ce722af5 100644 --- a/pkg/query-service/telemetry/telemetry.go +++ b/pkg/query-service/telemetry/telemetry.go @@ -40,6 +40,7 @@ const ( TELEMETRY_EVENT_QUERY_RANGE_V3 = "Query Range V3 Metadata" TELEMETRY_EVENT_ACTIVE_USER = "Active User" TELEMETRY_EVENT_ACTIVE_USER_PH = "Active User V2" + DEFAULT_CLOUD_EMAIL = "admin@signoz.cloud" ) const api_key = "4Gmoa4ixJAUHx2BpJxsjwA1bEfnwEeRz" @@ -165,7 +166,7 @@ func createTelemetry() { data := map[string]interface{}{} telemetry.SetTelemetryEnabled(constants.IsTelemetryEnabled()) - telemetry.SendEvent(TELEMETRY_EVENT_HEART_BEAT, data) + telemetry.SendEvent(TELEMETRY_EVENT_HEART_BEAT, data, "") ticker := time.NewTicker(HEART_BEAT_DURATION) activeUserTicker := time.NewTicker(ACTIVE_USER_DURATION) @@ -187,7 +188,7 @@ func createTelemetry() { if (telemetry.activeUser["traces"] != 0) || (telemetry.activeUser["metrics"] != 0) || (telemetry.activeUser["logs"] != 0) { telemetry.activeUser["any"] = 1 } - telemetry.SendEvent(TELEMETRY_EVENT_ACTIVE_USER, map[string]interface{}{"traces": telemetry.activeUser["traces"], "metrics": telemetry.activeUser["metrics"], "logs": telemetry.activeUser["logs"], "any": telemetry.activeUser["any"]}) + telemetry.SendEvent(TELEMETRY_EVENT_ACTIVE_USER, map[string]interface{}{"traces": telemetry.activeUser["traces"], "metrics": telemetry.activeUser["metrics"], "logs": telemetry.activeUser["logs"], "any": telemetry.activeUser["any"]}, "") telemetry.activeUser = map[string]int8{"traces": 0, "metrics": 0, "logs": 0, "any": 0} case <-ticker.C: @@ -195,11 +196,11 @@ func createTelemetry() { tagsInfo, _ := telemetry.reader.GetTagsInfoInLastHeartBeatInterval(context.Background()) if len(tagsInfo.Env) != 0 { - telemetry.SendEvent(TELEMETRY_EVENT_ENVIRONMENT, map[string]interface{}{"value": tagsInfo.Env}) + telemetry.SendEvent(TELEMETRY_EVENT_ENVIRONMENT, map[string]interface{}{"value": tagsInfo.Env}, "") } for language, _ := range tagsInfo.Languages { - telemetry.SendEvent(TELEMETRY_EVENT_LANGUAGE, map[string]interface{}{"language": language}) + telemetry.SendEvent(TELEMETRY_EVENT_LANGUAGE, map[string]interface{}{"language": language}, "") } totalSpans, _ := telemetry.reader.GetTotalSpans(context.Background()) @@ -226,10 +227,10 @@ func createTelemetry() { for key, value := range tsInfo { data[key] = value } - telemetry.SendEvent(TELEMETRY_EVENT_HEART_BEAT, data) + telemetry.SendEvent(TELEMETRY_EVENT_HEART_BEAT, data, "") getDistributedInfoInLastHeartBeatInterval, _ := telemetry.reader.GetDistributedInfoInLastHeartBeatInterval(context.Background()) - telemetry.SendEvent(TELEMETRY_EVENT_DISTRIBUTED, getDistributedInfoInLastHeartBeatInterval) + telemetry.SendEvent(TELEMETRY_EVENT_DISTRIBUTED, getDistributedInfoInLastHeartBeatInterval, "") } } @@ -259,7 +260,7 @@ func getOutboundIP() string { } func (a *Telemetry) IdentifyUser(user *model.User) { - if user.Email == "admin@admin.com" || user.Email == "admin@signoz.cloud" { + if user.Email == DEFAULT_CLOUD_EMAIL { return } a.SetCompanyDomain(user.Email) @@ -334,8 +335,16 @@ func (a *Telemetry) checkEvents(event string) bool { return sendEvent } -func (a *Telemetry) SendEvent(event string, data map[string]interface{}, opts ...bool) { +func (a *Telemetry) SendEvent(event string, data map[string]interface{}, userEmail string, opts ...bool) { + // ignore telemetry for default user + if userEmail == DEFAULT_CLOUD_EMAIL { + return + } + + if userEmail != "" { + a.SetUserEmail(userEmail) + } rateLimitFlag := true if len(opts) > 0 { rateLimitFlag = opts[0] @@ -388,6 +397,11 @@ func (a *Telemetry) SendEvent(event string, data map[string]interface{}, opts .. Event: event, UserId: a.GetUserEmail(), Properties: properties, + Context: &analytics.Context{ + Extra: map[string]interface{}{ + "groupId": a.getCompanyDomain(), + }, + }, }) } diff --git a/pkg/query-service/tests/integration/logparsingpipeline_test.go b/pkg/query-service/tests/integration/logparsingpipeline_test.go index 1b370e7804..0b4d22973c 100644 --- a/pkg/query-service/tests/integration/logparsingpipeline_test.go +++ b/pkg/query-service/tests/integration/logparsingpipeline_test.go @@ -436,6 +436,8 @@ func (tb *LogPipelinesTestBed) PostPipelinesToQSExpectingStatusCode( } respWriter := httptest.NewRecorder() + ctx := auth.AttachJwtToContext(req.Context(), req) + req = req.WithContext(ctx) tb.apiHandler.CreateLogsPipeline(respWriter, req) response := respWriter.Result() diff --git a/pkg/query-service/tests/test-deploy/docker-compose.yaml b/pkg/query-service/tests/test-deploy/docker-compose.yaml index 368f22bb4b..7b19265b2b 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.yaml @@ -192,7 +192,7 @@ services: <<: *db-depend otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.79.13} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.0} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -205,7 +205,7 @@ services: # condition: service_healthy otel-collector: - image: signoz/signoz-otel-collector:0.79.13 + image: signoz/signoz-otel-collector:0.88.0 container_name: signoz-otel-collector command: [ @@ -245,7 +245,7 @@ services: condition: service_healthy otel-collector-metrics: - image: signoz/signoz-otel-collector:0.79.13 + image: signoz/signoz-otel-collector:0.88.0 container_name: signoz-otel-collector-metrics command: [