diff --git a/ee/query-service/app/api/api.go b/ee/query-service/app/api/api.go index 6defd85201..418cd00cf9 100644 --- a/ee/query-service/app/api/api.go +++ b/ee/query-service/app/api/api.go @@ -152,7 +152,6 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router, am *baseapp.AuthMiddlew router.HandleFunc("/api/v1/register", am.OpenAccess(ah.registerUser)).Methods(http.MethodPost) router.HandleFunc("/api/v1/login", am.OpenAccess(ah.loginUser)).Methods(http.MethodPost) router.HandleFunc("/api/v1/traces/{traceId}", am.ViewAccess(ah.searchTraces)).Methods(http.MethodGet) - router.HandleFunc("/api/v2/metrics/query_range", am.ViewAccess(ah.queryRangeMetricsV2)).Methods(http.MethodPost) // PAT APIs router.HandleFunc("/api/v1/pats", am.AdminAccess(ah.createPAT)).Methods(http.MethodPost) diff --git a/ee/query-service/app/api/metrics.go b/ee/query-service/app/api/metrics.go deleted file mode 100644 index 7c0e320f45..0000000000 --- a/ee/query-service/app/api/metrics.go +++ /dev/null @@ -1,236 +0,0 @@ -package api - -import ( - "bytes" - "fmt" - "net/http" - "sync" - "text/template" - "time" - - "go.signoz.io/signoz/pkg/query-service/app/metrics" - "go.signoz.io/signoz/pkg/query-service/app/parser" - "go.signoz.io/signoz/pkg/query-service/constants" - basemodel "go.signoz.io/signoz/pkg/query-service/model" - querytemplate "go.signoz.io/signoz/pkg/query-service/utils/queryTemplate" - "go.uber.org/zap" -) - -func (ah *APIHandler) queryRangeMetricsV2(w http.ResponseWriter, r *http.Request) { - if !ah.CheckFeature(basemodel.CustomMetricsFunction) { - zap.L().Info("CustomMetricsFunction feature is not enabled in this plan") - ah.APIHandler.QueryRangeMetricsV2(w, r) - return - } - metricsQueryRangeParams, apiErrorObj := parser.ParseMetricQueryRangeParams(r) - - if apiErrorObj != nil { - zap.L().Error("Error in parsing metric query params", zap.Error(apiErrorObj.Err)) - RespondError(w, apiErrorObj, nil) - return - } - - // prometheus instant query needs same timestamp - if metricsQueryRangeParams.CompositeMetricQuery.PanelType == basemodel.QUERY_VALUE && - metricsQueryRangeParams.CompositeMetricQuery.QueryType == basemodel.PROM { - metricsQueryRangeParams.Start = metricsQueryRangeParams.End - } - - // round up the end to nearest multiple - if metricsQueryRangeParams.CompositeMetricQuery.QueryType == basemodel.QUERY_BUILDER { - end := (metricsQueryRangeParams.End) / 1000 - step := metricsQueryRangeParams.Step - metricsQueryRangeParams.End = (end / step * step) * 1000 - } - - type channelResult struct { - Series []*basemodel.Series - TableName string - Err error - Name string - Query string - } - - execClickHouseQueries := func(queries map[string]string) ([]*basemodel.Series, []string, error, map[string]string) { - var seriesList []*basemodel.Series - var tableName []string - ch := make(chan channelResult, len(queries)) - var wg sync.WaitGroup - - for name, query := range queries { - wg.Add(1) - go func(name, query string) { - defer wg.Done() - seriesList, tableName, err := ah.opts.DataConnector.GetMetricResultEE(r.Context(), query) - for _, series := range seriesList { - series.QueryName = name - } - - if err != nil { - ch <- channelResult{Err: fmt.Errorf("error in query-%s: %v", name, err), Name: name, Query: query} - return - } - ch <- channelResult{Series: seriesList, TableName: tableName} - }(name, query) - } - - wg.Wait() - close(ch) - - var errs []error - errQuriesByName := make(map[string]string) - // read values from the channel - for r := range ch { - if r.Err != nil { - errs = append(errs, r.Err) - errQuriesByName[r.Name] = r.Query - continue - } - seriesList = append(seriesList, r.Series...) - tableName = append(tableName, r.TableName) - } - if len(errs) != 0 { - return nil, nil, fmt.Errorf("encountered multiple errors: %s", metrics.FormatErrs(errs, "\n")), errQuriesByName - } - return seriesList, tableName, nil, nil - } - - execPromQueries := func(metricsQueryRangeParams *basemodel.QueryRangeParamsV2) ([]*basemodel.Series, error, map[string]string) { - var seriesList []*basemodel.Series - ch := make(chan channelResult, len(metricsQueryRangeParams.CompositeMetricQuery.PromQueries)) - var wg sync.WaitGroup - - for name, query := range metricsQueryRangeParams.CompositeMetricQuery.PromQueries { - if query.Disabled { - continue - } - wg.Add(1) - go func(name string, query *basemodel.PromQuery) { - var seriesList []*basemodel.Series - defer wg.Done() - tmpl := template.New("promql-query") - tmpl, tmplErr := tmpl.Parse(query.Query) - if tmplErr != nil { - ch <- channelResult{Err: fmt.Errorf("error in parsing query-%s: %v", name, tmplErr), Name: name, Query: query.Query} - return - } - var queryBuf bytes.Buffer - tmplErr = tmpl.Execute(&queryBuf, metricsQueryRangeParams.Variables) - if tmplErr != nil { - ch <- channelResult{Err: fmt.Errorf("error in parsing query-%s: %v", name, tmplErr), Name: name, Query: query.Query} - return - } - query.Query = queryBuf.String() - queryModel := basemodel.QueryRangeParams{ - Start: time.UnixMilli(metricsQueryRangeParams.Start), - End: time.UnixMilli(metricsQueryRangeParams.End), - Step: time.Duration(metricsQueryRangeParams.Step * int64(time.Second)), - Query: query.Query, - } - promResult, _, err := ah.opts.DataConnector.GetQueryRangeResult(r.Context(), &queryModel) - if err != nil { - ch <- channelResult{Err: fmt.Errorf("error in query-%s: %v", name, err), Name: name, Query: query.Query} - return - } - matrix, _ := promResult.Matrix() - for _, v := range matrix { - var s basemodel.Series - s.QueryName = name - s.Labels = v.Metric.Copy().Map() - for _, p := range v.Floats { - s.Points = append(s.Points, basemodel.MetricPoint{Timestamp: p.T, Value: p.F}) - } - seriesList = append(seriesList, &s) - } - ch <- channelResult{Series: seriesList} - }(name, query) - } - - wg.Wait() - close(ch) - - var errs []error - errQuriesByName := make(map[string]string) - // read values from the channel - for r := range ch { - if r.Err != nil { - errs = append(errs, r.Err) - errQuriesByName[r.Name] = r.Query - continue - } - seriesList = append(seriesList, r.Series...) - } - if len(errs) != 0 { - return nil, fmt.Errorf("encountered multiple errors: %s", metrics.FormatErrs(errs, "\n")), errQuriesByName - } - return seriesList, nil, nil - } - - var seriesList []*basemodel.Series - var tableName []string - var err error - var errQuriesByName map[string]string - switch metricsQueryRangeParams.CompositeMetricQuery.QueryType { - case basemodel.QUERY_BUILDER: - runQueries := metrics.PrepareBuilderMetricQueries(metricsQueryRangeParams, constants.SIGNOZ_TIMESERIES_TABLENAME) - if runQueries.Err != nil { - RespondError(w, &basemodel.ApiError{Typ: basemodel.ErrorBadData, Err: runQueries.Err}, nil) - return - } - seriesList, tableName, err, errQuriesByName = execClickHouseQueries(runQueries.Queries) - - case basemodel.CLICKHOUSE: - queries := make(map[string]string) - - for name, chQuery := range metricsQueryRangeParams.CompositeMetricQuery.ClickHouseQueries { - if chQuery.Disabled { - continue - } - tmpl := template.New("clickhouse-query") - tmpl, err := tmpl.Parse(chQuery.Query) - if err != nil { - RespondError(w, &basemodel.ApiError{Typ: basemodel.ErrorBadData, Err: err}, nil) - return - } - var query bytes.Buffer - - // replace go template variables - querytemplate.AssignReservedVars(metricsQueryRangeParams) - - err = tmpl.Execute(&query, metricsQueryRangeParams.Variables) - if err != nil { - RespondError(w, &basemodel.ApiError{Typ: basemodel.ErrorBadData, Err: err}, nil) - return - } - queries[name] = query.String() - } - seriesList, tableName, err, errQuriesByName = execClickHouseQueries(queries) - case basemodel.PROM: - seriesList, err, errQuriesByName = execPromQueries(metricsQueryRangeParams) - default: - err = fmt.Errorf("invalid query type") - RespondError(w, &basemodel.ApiError{Typ: basemodel.ErrorBadData, Err: err}, errQuriesByName) - return - } - - if err != nil { - apiErrObj := &basemodel.ApiError{Typ: basemodel.ErrorBadData, Err: err} - RespondError(w, apiErrObj, errQuriesByName) - return - } - if metricsQueryRangeParams.CompositeMetricQuery.PanelType == basemodel.QUERY_VALUE && - len(seriesList) > 1 && - (metricsQueryRangeParams.CompositeMetricQuery.QueryType == basemodel.QUERY_BUILDER || - metricsQueryRangeParams.CompositeMetricQuery.QueryType == basemodel.CLICKHOUSE) { - RespondError(w, &basemodel.ApiError{Typ: basemodel.ErrorBadData, Err: fmt.Errorf("invalid: query resulted in more than one series for value type")}, nil) - return - } - - type ResponseFormat struct { - ResultType string `json:"resultType"` - Result []*basemodel.Series `json:"result"` - TableName []string `json:"tableName"` - } - resp := ResponseFormat{ResultType: "matrix", Result: seriesList, TableName: tableName} - ah.Respond(w, resp) -} diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go index dcb5568c04..53b9a27314 100644 --- a/ee/query-service/app/server.go +++ b/ee/query-service/app/server.go @@ -329,7 +329,6 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler) (*http.Server, e r.Use(loggingMiddleware) apiHandler.RegisterRoutes(r, am) - apiHandler.RegisterMetricsRoutes(r, am) apiHandler.RegisterLogsRoutes(r, am) apiHandler.RegisterIntegrationRoutes(r, am) apiHandler.RegisterQueryRangeV3Routes(r, am) diff --git a/frontend/package.json b/frontend/package.json index 011ced7569..6040b2e3f0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -44,6 +44,9 @@ "@sentry/webpack-plugin": "2.16.0", "@signozhq/design-tokens": "0.0.8", "@uiw/react-md-editor": "3.23.5", + "@visx/group": "3.3.0", + "@visx/shape": "3.5.0", + "@visx/tooltip": "3.3.0", "@xstate/react": "^3.0.0", "ansi-to-html": "0.7.2", "antd": "5.11.0", diff --git a/frontend/public/locales/en-GB/channels.json b/frontend/public/locales/en-GB/channels.json index 027501f69d..807b7a6e3b 100644 --- a/frontend/public/locales/en-GB/channels.json +++ b/frontend/public/locales/en-GB/channels.json @@ -15,6 +15,7 @@ "button_test_channel": "Test", "button_return": "Back", "field_channel_name": "Name", + "field_send_resolved": "Send resolved alerts", "field_channel_type": "Type", "field_webhook_url": "Webhook URL", "field_slack_recipient": "Recipient", diff --git a/frontend/public/locales/en/channels.json b/frontend/public/locales/en/channels.json index 9ab31d697c..0d9387d329 100644 --- a/frontend/public/locales/en/channels.json +++ b/frontend/public/locales/en/channels.json @@ -15,6 +15,7 @@ "button_test_channel": "Test", "button_return": "Back", "field_channel_name": "Name", + "field_send_resolved": "Send resolved alerts", "field_channel_type": "Type", "field_webhook_url": "Webhook URL", "field_slack_recipient": "Recipient", diff --git a/frontend/src/api/channels/createEmail.ts b/frontend/src/api/channels/createEmail.ts index cde74b9c6d..7d0910d40f 100644 --- a/frontend/src/api/channels/createEmail.ts +++ b/frontend/src/api/channels/createEmail.ts @@ -12,7 +12,7 @@ const create = async ( name: props.name, email_configs: [ { - send_resolved: true, + send_resolved: props.send_resolved, to: props.to, html: props.html, headers: props.headers, diff --git a/frontend/src/api/channels/createMsTeams.ts b/frontend/src/api/channels/createMsTeams.ts index 9e06e275a0..ef9d309a97 100644 --- a/frontend/src/api/channels/createMsTeams.ts +++ b/frontend/src/api/channels/createMsTeams.ts @@ -12,7 +12,7 @@ const create = async ( name: props.name, msteams_configs: [ { - send_resolved: true, + send_resolved: props.send_resolved, webhook_url: props.webhook_url, title: props.title, text: props.text, diff --git a/frontend/src/api/channels/createPager.ts b/frontend/src/api/channels/createPager.ts index 2747768cf1..682874f7b4 100644 --- a/frontend/src/api/channels/createPager.ts +++ b/frontend/src/api/channels/createPager.ts @@ -12,7 +12,7 @@ const create = async ( name: props.name, pagerduty_configs: [ { - send_resolved: true, + send_resolved: props.send_resolved, routing_key: props.routing_key, client: props.client, client_url: props.client_url, diff --git a/frontend/src/api/channels/createSlack.ts b/frontend/src/api/channels/createSlack.ts index f9e430fbc9..d68beddc9b 100644 --- a/frontend/src/api/channels/createSlack.ts +++ b/frontend/src/api/channels/createSlack.ts @@ -12,7 +12,7 @@ const create = async ( name: props.name, slack_configs: [ { - send_resolved: true, + send_resolved: props.send_resolved, api_url: props.api_url, channel: props.channel, title: props.title, diff --git a/frontend/src/api/channels/createWebhook.ts b/frontend/src/api/channels/createWebhook.ts index 9c3c52c943..67a0de7a7b 100644 --- a/frontend/src/api/channels/createWebhook.ts +++ b/frontend/src/api/channels/createWebhook.ts @@ -30,7 +30,7 @@ const create = async ( name: props.name, webhook_configs: [ { - send_resolved: true, + send_resolved: props.send_resolved, url: props.api_url, http_config: httpConfig, }, diff --git a/frontend/src/api/channels/editEmail.ts b/frontend/src/api/channels/editEmail.ts index f20e5eb8f9..b80fe687a9 100644 --- a/frontend/src/api/channels/editEmail.ts +++ b/frontend/src/api/channels/editEmail.ts @@ -12,7 +12,7 @@ const editEmail = async ( name: props.name, email_configs: [ { - send_resolved: true, + send_resolved: props.send_resolved, to: props.to, html: props.html, headers: props.headers, diff --git a/frontend/src/api/channels/editMsTeams.ts b/frontend/src/api/channels/editMsTeams.ts index ee6bd309c1..293688f6c2 100644 --- a/frontend/src/api/channels/editMsTeams.ts +++ b/frontend/src/api/channels/editMsTeams.ts @@ -12,7 +12,7 @@ const editMsTeams = async ( name: props.name, msteams_configs: [ { - send_resolved: true, + send_resolved: props.send_resolved, webhook_url: props.webhook_url, title: props.title, text: props.text, diff --git a/frontend/src/api/channels/editOpsgenie.ts b/frontend/src/api/channels/editOpsgenie.ts index 71f830f9f8..1eb65c7add 100644 --- a/frontend/src/api/channels/editOpsgenie.ts +++ b/frontend/src/api/channels/editOpsgenie.ts @@ -12,7 +12,7 @@ const editOpsgenie = async ( name: props.name, opsgenie_configs: [ { - send_resolved: true, + send_resolved: props.send_resolved, api_key: props.api_key, description: props.description, priority: props.priority, diff --git a/frontend/src/api/channels/editPager.ts b/frontend/src/api/channels/editPager.ts index a31d73dcdb..091d42b640 100644 --- a/frontend/src/api/channels/editPager.ts +++ b/frontend/src/api/channels/editPager.ts @@ -12,7 +12,7 @@ const editPager = async ( name: props.name, pagerduty_configs: [ { - send_resolved: true, + send_resolved: props.send_resolved, routing_key: props.routing_key, client: props.client, client_url: props.client_url, diff --git a/frontend/src/api/channels/editSlack.ts b/frontend/src/api/channels/editSlack.ts index 9a34f41318..639646452c 100644 --- a/frontend/src/api/channels/editSlack.ts +++ b/frontend/src/api/channels/editSlack.ts @@ -12,7 +12,7 @@ const editSlack = async ( name: props.name, slack_configs: [ { - send_resolved: true, + send_resolved: props.send_resolved, api_url: props.api_url, channel: props.channel, title: props.title, diff --git a/frontend/src/api/channels/editWebhook.ts b/frontend/src/api/channels/editWebhook.ts index a574633e4e..a96850c2db 100644 --- a/frontend/src/api/channels/editWebhook.ts +++ b/frontend/src/api/channels/editWebhook.ts @@ -29,7 +29,7 @@ const editWebhook = async ( name: props.name, webhook_configs: [ { - send_resolved: true, + send_resolved: props.send_resolved, url: props.api_url, http_config: httpConfig, }, diff --git a/frontend/src/api/metrics/ApDex/getMetricMeta.ts b/frontend/src/api/metrics/ApDex/getMetricMeta.ts index e3045730a7..90baa61cd8 100644 --- a/frontend/src/api/metrics/ApDex/getMetricMeta.ts +++ b/frontend/src/api/metrics/ApDex/getMetricMeta.ts @@ -1,4 +1,4 @@ -import axios from 'api'; +import { ApiV4Instance } from 'api'; import { AxiosResponse } from 'axios'; import { MetricMetaProps } from 'types/api/metrics/getApDex'; @@ -6,4 +6,6 @@ export const getMetricMeta = ( metricName: string, servicename: string, ): Promise> => - axios.get(`/metric_meta?metricName=${metricName}&serviceName=${servicename}`); + ApiV4Instance.get( + `/metric/metric_metadata?metricName=${metricName}&serviceName=${servicename}`, + ); diff --git a/frontend/src/api/metrics/getMetricName.ts b/frontend/src/api/metrics/getMetricName.ts deleted file mode 100644 index f3bff5a921..0000000000 --- a/frontend/src/api/metrics/getMetricName.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ApiV2Instance as axios } from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; -import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; -import { - MetricNameProps, - MetricNamesPayloadProps, -} from 'types/api/metrics/getMetricName'; - -export const getMetricName = async ( - props: MetricNameProps, -): Promise | ErrorResponse> => { - try { - const response = await axios.get( - `/metrics/autocomplete/list?match=${props || ''}`, - ); - - return { - statusCode: 200, - error: null, - message: response.data.status, - payload: response.data, - }; - } catch (error) { - return ErrorResponseHandler(error as AxiosError); - } -}; diff --git a/frontend/src/api/metrics/getResourceAttributes.ts b/frontend/src/api/metrics/getResourceAttributes.ts index 66524bf8f7..c482f863ae 100644 --- a/frontend/src/api/metrics/getResourceAttributes.ts +++ b/frontend/src/api/metrics/getResourceAttributes.ts @@ -1,6 +1,7 @@ -import { ApiV2Instance as axios } from 'api'; +import { ApiV3Instance as axios } from 'api'; import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; import { AxiosError } from 'axios'; +import createQueryParams from 'lib/createQueryParams'; import { ErrorResponse, SuccessResponse } from 'types/api'; import { TagKeyProps, @@ -8,15 +9,19 @@ import { TagValueProps, TagValuesPayloadProps, } from 'types/api/metrics/getResourceAttributes'; +import { DataSource, MetricAggregateOperator } from 'types/common/queryBuilder'; export const getResourceAttributesTagKeys = async ( props: TagKeyProps, ): Promise | ErrorResponse> => { try { const response = await axios.get( - `/metrics/autocomplete/tagKey?metricName=${props.metricName}${ - props.match ? `&match=${props.match}` : '' - }`, + `/autocomplete/attribute_keys?${createQueryParams({ + aggregateOperator: MetricAggregateOperator.RATE, + searchText: props.match, + dataSource: DataSource.METRICS, + aggregateAttribute: props.metricName, + })}`, ); return { @@ -35,7 +40,13 @@ export const getResourceAttributesTagValues = async ( ): Promise | ErrorResponse> => { try { const response = await axios.get( - `/metrics/autocomplete/tagValue?metricName=${props.metricName}&tagKey=${props.tagKey}`, + `/autocomplete/attribute_values?${createQueryParams({ + aggregateOperator: MetricAggregateOperator.RATE, + dataSource: DataSource.METRICS, + aggregateAttribute: props.metricName, + attributeKey: props.tagKey, + searchText: '', + })}`, ); return { diff --git a/frontend/src/constants/panelTypes.ts b/frontend/src/constants/panelTypes.ts index c6db5db2da..c76380fff1 100644 --- a/frontend/src/constants/panelTypes.ts +++ b/frontend/src/constants/panelTypes.ts @@ -29,6 +29,7 @@ export const getComponentForPanelType = ( [PANEL_TYPES.LIST]: dataSource === DataSource.LOGS ? LogsPanelComponent : TracesTableComponent, [PANEL_TYPES.BAR]: Uplot, + [PANEL_TYPES.PIE]: null, [PANEL_TYPES.EMPTY_WIDGET]: null, }; diff --git a/frontend/src/constants/queryBuilder.ts b/frontend/src/constants/queryBuilder.ts index 0999b634ba..c1603e02e5 100644 --- a/frontend/src/constants/queryBuilder.ts +++ b/frontend/src/constants/queryBuilder.ts @@ -285,6 +285,7 @@ export enum PANEL_TYPES { LIST = 'list', TRACE = 'trace', BAR = 'bar', + PIE = 'pie', EMPTY_WIDGET = 'EMPTY_WIDGET', } diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 7806ecfc13..901e2b36d8 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -315,7 +315,7 @@ function AppLayout(props: AppLayoutProps): JSX.Element { className={cx( 'app-layout', isDarkMode ? 'darkMode' : 'lightMode', - !collapsed ? 'docked' : '', + !collapsed && !renderFullScreen ? 'docked' : '', )} > {isToDisplayLayout && !renderFullScreen && ( diff --git a/frontend/src/container/CreateAlertChannels/index.tsx b/frontend/src/container/CreateAlertChannels/index.tsx index 51a0b6214e..c0eec3ecdd 100644 --- a/frontend/src/container/CreateAlertChannels/index.tsx +++ b/frontend/src/container/CreateAlertChannels/index.tsx @@ -53,6 +53,7 @@ function CreateAlertChannels({ EmailChannel > >({ + send_resolved: true, text: `{{ range .Alerts -}} *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} @@ -119,7 +120,7 @@ function CreateAlertChannels({ api_url: selectedConfig?.api_url || '', channel: selectedConfig?.channel || '', name: selectedConfig?.name || '', - send_resolved: true, + send_resolved: selectedConfig?.send_resolved || false, text: selectedConfig?.text || '', title: selectedConfig?.title || '', }), @@ -158,7 +159,7 @@ function CreateAlertChannels({ let request: WebhookChannel = { api_url: selectedConfig?.api_url || '', name: selectedConfig?.name || '', - send_resolved: true, + send_resolved: selectedConfig?.send_resolved || false, }; if (selectedConfig?.username !== '' || selectedConfig?.password !== '') { @@ -226,7 +227,7 @@ function CreateAlertChannels({ return { name: selectedConfig?.name || '', - send_resolved: true, + send_resolved: selectedConfig?.send_resolved || false, routing_key: selectedConfig?.routing_key || '', client: selectedConfig?.client || '', client_url: selectedConfig?.client_url || '', @@ -274,7 +275,7 @@ function CreateAlertChannels({ () => ({ api_key: selectedConfig?.api_key || '', name: selectedConfig?.name || '', - send_resolved: true, + send_resolved: selectedConfig?.send_resolved || false, description: selectedConfig?.description || '', message: selectedConfig?.message || '', priority: selectedConfig?.priority || '', @@ -312,7 +313,7 @@ function CreateAlertChannels({ const prepareEmailRequest = useCallback( () => ({ name: selectedConfig?.name || '', - send_resolved: true, + send_resolved: selectedConfig?.send_resolved || false, to: selectedConfig?.to || '', html: selectedConfig?.html || '', headers: selectedConfig?.headers || {}, @@ -350,7 +351,7 @@ function CreateAlertChannels({ () => ({ webhook_url: selectedConfig?.webhook_url || '', name: selectedConfig?.name || '', - send_resolved: true, + send_resolved: selectedConfig?.send_resolved || false, text: selectedConfig?.text || '', title: selectedConfig?.title || '', }), diff --git a/frontend/src/container/EditAlertChannels/index.tsx b/frontend/src/container/EditAlertChannels/index.tsx index 29d7816d90..3c2e956f14 100644 --- a/frontend/src/container/EditAlertChannels/index.tsx +++ b/frontend/src/container/EditAlertChannels/index.tsx @@ -72,7 +72,7 @@ function EditAlertChannels({ api_url: selectedConfig?.api_url || '', channel: selectedConfig?.channel || '', name: selectedConfig?.name || '', - send_resolved: true, + send_resolved: selectedConfig?.send_resolved || false, text: selectedConfig?.text || '', title: selectedConfig?.title || '', id, @@ -115,7 +115,7 @@ function EditAlertChannels({ return { api_url: selectedConfig?.api_url || '', name: name || '', - send_resolved: true, + send_resolved: selectedConfig?.send_resolved || false, username, password, id, @@ -284,7 +284,7 @@ function EditAlertChannels({ () => ({ webhook_url: selectedConfig?.webhook_url || '', name: selectedConfig?.name || '', - send_resolved: true, + send_resolved: selectedConfig?.send_resolved || false, text: selectedConfig?.text || '', title: selectedConfig?.title || '', id, diff --git a/frontend/src/container/FormAlertChannels/index.tsx b/frontend/src/container/FormAlertChannels/index.tsx index 844e86236f..1d772ca7a8 100644 --- a/frontend/src/container/FormAlertChannels/index.tsx +++ b/frontend/src/container/FormAlertChannels/index.tsx @@ -1,4 +1,4 @@ -import { Form, FormInstance, Input, Select, Typography } from 'antd'; +import { Form, FormInstance, Input, Select, Switch, Typography } from 'antd'; import { Store } from 'antd/lib/form/interface'; import UpgradePrompt from 'components/Upgrade/UpgradePrompt'; import { FeatureKeys } from 'constants/features'; @@ -95,6 +95,22 @@ function FormAlertChannels({ /> + + { + setSelectedConfig((state) => ({ + ...state, + send_resolved: value, + })); + }} + /> + +