Compare commits

...

53 Commits

Author SHA1 Message Date
Yunus M
e021dd8bb3 refactor(metrics-explorer): style the metric search empty state by class instead of .ant-empty 2026-11-12 15:49:38 +05:30
Yunus M
6d24b35a1f refactor(workspace-locked): style the benefits list by class instead of .ant-list 2026-11-12 15:49:29 +05:30
Yunus M
cb787b342c refactor(trace-funnels): style the latency pointer trigger by class instead of .ant-space 2026-11-12 15:49:20 +05:30
Yunus M
7b08b6f28a refactor(query-builder): style formula rows and items by class instead of .ant-row/.ant-col 2026-11-12 15:49:12 +05:30
Yunus M
2b507ee0a5 refactor(logs): style the raw log row by class instead of .ant-row 2026-11-12 15:49:02 +05:30
Yunus M
66d63c97d9 refactor: use periscope List instead of antd's 2026-11-12 15:24:59 +05:30
Yunus M
eeef9f2ddf refactor: use periscope Empty instead of antd's 2026-11-12 15:24:34 +05:30
Yunus M
16731f3ac7 refactor: use periscope Layout instead of antd's 2026-11-12 15:22:29 +05:30
Yunus M
20c3c9ec35 refactor: use periscope Space instead of antd's 2026-11-12 15:22:04 +05:30
Yunus M
4dce57ca7a refactor: use periscope Col instead of antd's 2026-11-12 15:14:54 +05:30
Yunus M
caec5c4c4e refactor: use periscope Row instead of antd's 2026-11-12 15:13:07 +05:30
Yunus M
7bbde78006 refactor: use periscope Flex instead of antd's 2026-11-12 15:10:36 +05:30
Yunus M
4f6890b617 refactor(periscope): remove the theme CSS variable publisher 2026-11-12 13:15:50 +05:30
Yunus M
98d33b6924 refactor(periscope): style List with design-system tokens 2026-11-12 13:15:44 +05:30
Yunus M
c7ffaa11cd refactor(periscope): style Empty with design-system tokens 2026-11-12 13:15:38 +05:30
Yunus M
fcec9d17fa refactor(periscope): style Layout with design-system tokens 2026-11-12 13:15:32 +05:30
Yunus M
0473cc43b4 refactor(periscope): style Space with design-system tokens 2026-11-12 13:15:25 +05:30
Yunus M
da9c05b1d0 refactor(periscope): style Col with design-system tokens 2026-11-12 13:15:19 +05:30
Yunus M
50bb486138 refactor(periscope): style Row with design-system tokens 2026-11-12 13:15:12 +05:30
Yunus M
59a6c376bb refactor(periscope): style Flex with design-system tokens 2026-11-12 13:15:05 +05:30
Yunus M
7bac8db242 refactor(periscope): remove the shared antd reset mixins 2026-11-12 12:39:18 +05:30
Yunus M
96cef83257 refactor(periscope): write List's font and link rules with theme variables 2026-11-12 12:39:12 +05:30
Yunus M
f34bf60300 refactor(periscope): write Empty's font and link rules with theme variables 2026-11-12 12:39:05 +05:30
Yunus M
e55eda557b refactor(periscope): write Layout's font and link rules with theme variables 2026-11-12 12:38:58 +05:30
Yunus M
2d775bf9f9 refactor(periscope): write Space's link rules with theme variables 2026-11-12 12:38:52 +05:30
Yunus M
b9d45141b2 refactor(periscope): write Col's font and link rules with theme variables 2026-11-12 12:38:40 +05:30
Yunus M
69068f1c43 refactor(periscope): write Row's font and link rules with theme variables 2026-11-12 12:38:32 +05:30
Yunus M
332db70f2f refactor(periscope): write Flex's font and link rules with theme variables 2026-11-12 12:38:22 +05:30
Yunus M
4b12daf823 refactor(periscope): rename theme CSS variables to --theme-* 2026-11-12 12:18:01 +05:30
Yunus M
c5dd885df5 refactor(periscope): read List's theme colours from CSS variables
Line heights stay hard-coded; no line-height token is published.
2026-11-12 11:09:47 +05:30
Yunus M
cbef97fa3f refactor(periscope): read Layout's theme colours from CSS variables
The header keeps #001529, antd's fixed default rather than a theme value.
2026-11-12 11:09:44 +05:30
Yunus M
4b7b657a83 refactor(periscope): read Empty's theme colours from CSS variables
The simple image takes its colours from CSS classes rather than SVG
attributes, so its markup no longer varies by theme.
2026-11-12 11:09:37 +05:30
Yunus M
3ae7afa83d refactor(periscope): read antd reset values from theme CSS variables
AppShell publishes the antd tokens periscope components need as
--antd-* variables, so they follow theme changes as antd does.
2026-11-12 10:50:35 +05:30
Yunus M
437358ae26 fix(periscope): copy antd's font and link resets onto List
The font reset sits only on elements antd scopes it to, so a font set
on the List reaches the header and footer but not the items.
2026-11-12 09:02:45 +05:30
Yunus M
79e4fc17fe fix(periscope): copy antd's font and link resets onto Empty 2026-11-12 09:02:40 +05:30
Yunus M
ba9f1a61dc fix(periscope): copy antd's font and link resets onto Layout 2026-11-12 09:02:35 +05:30
Yunus M
c3330b40d4 fix(periscope): copy antd's link reset onto Space
antd skips its font reset for Space, so only the link reset applies.
2026-11-12 09:02:30 +05:30
Yunus M
c1bc2cda99 fix(periscope): copy antd's font and link resets onto Col 2026-11-12 09:02:25 +05:30
Yunus M
3fdd1dc894 fix(periscope): copy antd's font and link resets onto Row 2026-11-12 09:02:19 +05:30
Yunus M
f961c1942c fix(periscope): copy antd's font and link resets onto Flex
Adds shared mixins for antd 5.11's font and link resets, at antd's
specificity, so text and links render as they do inside antd.
2026-11-12 09:02:13 +05:30
Yunus M
3cc18ece1c feat(periscope): add List mirroring antd List
Pagination, grid, loadMore and loading are left out; nothing uses them.
2026-11-11 23:55:55 +05:30
Yunus M
18ab532d4d feat(periscope): add Empty mirroring antd Empty 2026-11-11 23:55:50 +05:30
Yunus M
7d06152d5e feat(periscope): add Layout mirroring antd Layout
Sider is left out; nothing uses it.
2026-11-11 23:55:46 +05:30
Yunus M
761d2dd076 feat(periscope): add Space mirroring antd Space
Space.Compact copies only its container; restyling antd children is
left out.
2026-11-11 23:55:41 +05:30
Yunus M
3b13919d8e feat(periscope): add Col mirroring antd Col 2026-11-11 23:55:37 +05:30
Yunus M
1fcdeb92bd feat(periscope): add Row mirroring antd Row
RowContext stays a React Context, as in antd: it carries each Row's
gutter to its Cols, which a global store cannot.
2026-11-11 23:55:32 +05:30
Yunus M
801031a91a feat(periscope): add Flex mirroring antd Flex
Copies antd 5.11 Flex's own styles, box-sizing included; antd's
font and link resets are left out.
2026-11-11 23:20:28 +05:30
Nikhil Soni
88e9ad73e0 fix(quick-filters): reject high-cardinality fields as quick filters (#13385)
#### Description

- `PUT /api/v2/quick_filters/{source}` (and the legacy v1 update) now
returns 400 for fields whose values are unique per span/log, so a quick
filter on them has nothing to pick from: trace/span/parent-span IDs
(incl. legacy `traceID`/`spanID`/`parentSpanID`), `timestamp`, log
`body`, `gen_ai.tool.call.id`, free-text payloads
(gen_ai/OpenInference/Langfuse/Traceloop messages,
`exception.message`/`stacktrace`, `db.statement`/`db.query.text`), token
counts (`gen_ai.usage.*`, `llm.token_count.*`, `ai.usage.*`, …) and the
per-span `signoz.gen_ai.*.cost` keys. The list is hard-coded in
`quickfiltertypes`.
- `trace_id` is removed from the default traces quick filters, including
the seeds in migrations `030`/`031` (separate commit), so `147` is a
no-op on fresh installs.
- Migration `147` strips these fields from every stored quick filter;
other entries are kept byte-for-byte.

#### Issues closed by this PR

Closes #13157

#### Additional Information

- Follows the approach agreed in SigNoz/platform-pod#3282, replacing the
frontend-only blocklist from #13182.
- Changelog: `trace_id`, `span_id` and other high-cardinality fields
(token counts, costs, message payloads) are no longer supported as quick
filters and are removed from existing quick filter settings; filter by
them from the search bar instead.
- The quick filter settings panel still lists these fields as
suggestions; adding one now surfaces the API error. Hiding them is out
of scope.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 12:10:49 +00:00
Ashwin Bhatkal
7f5bd4d1cf fix(alerts): match the rule state filter to the created-by filter (#13460)
#### Description

- The alert rules State filter now uses the same select as the Created
by filter: checkbox options, one pill plus "+N", and the same background
and width. The placeholder reads "State" instead of "Filter by state".
- The Created by select moves into a shared `MultiSelectFilter` in
`components/ListFilter`, and both filters render through it. Created by
looks and behaves as before on the dashboards and alert rules lists.
- The State filter moves from `@signozhq/ui` `ComboboxSimple` to antd
`Select`, to match the antd Created by and Updated filters beside it.
`ComboboxSimple` can't run the query once on close or show checkbox
options yet (https://github.com/SigNoz/components/issues/483).
- New story on the alert rules page: the State dropdown open.

#### Issues closed by this PR

Closes https://github.com/SigNoz/pulse-pod/issues/720

#### Screenshots / Screen Recordings

Visual diff in dark and light:
https://claude.ai/artifact/UwA1RZA6VTDMEEbGpyVsvg

- 10 changed, all inside the State filter box on the alert rules list.
- 2 new: the State dropdown open.
- 8 pixel-identical: the dashboards list, where Created by is unchanged.
2026-11-11 10:44:29 +00:00
Nityananda Gohain
86e0e4840b feat(authz): enable FGA for llm pricing rule routes (#13260)
Some checks failed
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
build-staging / staging (push) Has been cancelled
cacheci / tests (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
<!--A few plain bullets saying what changed and why, for a reviewer
skimming it - not a wall of text, not a restatement of the diff, not
generated boilerplate.-->
#### Description
- Get and delete check the path id. Bulk upsert checks each rule id and
falls back to the wildcard for creates and sync rows.
- Managed roles keep the same access as before: admin writes, editor and
viewer read. A migration backfills the tuples for existing orgs.
- Integration tests cover managed roles plus wildcard and rule-scoped
custom roles.

<!--Reference issues using `Closes #issue-number` to enable automatic
closure on merge. -->
#### Issues closed by this PR
Closes https://github.com/SigNoz/nerve-pod/issues/463
2026-11-11 09:57:38 +00:00
Naman Verma
9ee433424d feat: migrate all rules to v2 schema (#13158)
#### Description

https://app.notion.com/p/signoz/Alert-Schema-Crosswalk-v1-v2alpha1-3cefcc6bcd198115acf3daf3fcedb409?source=copy_link

<!--Reference issues using `Closes #issue-number` to enable automatic
closure on merge. -->
#### Issues closed by this PR

Closes https://github.com/SigNoz/pulse-pod/issues/225
Closes https://github.com/SigNoz/pulse-pod/issues/588
Closes https://github.com/SigNoz/pulse-pod/issues/362
Closes https://github.com/SigNoz/pulse-pod/issues/233
Closes https://github.com/SigNoz/uforge-pod/issues/5698
Closes https://github.com/SigNoz/pulse-pod/issues/696

---------

Co-authored-by: Ashwin Bhatkal <ashwin96@gmail.com>
2026-11-11 07:37:41 +00:00
Swapnil Nakade
da8d66b5b9 feat(audit): emit audit logs per signoz semantic conventions (#13477)
Some checks failed
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
build-staging / staging (push) Has been cancelled
cacheci / tests (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
<!--A few plain bullets saying what changed and why, for a reviewer
skimming it - not a wall of text, not a restatement of the diff, not
generated boilerplate.-->
#### Description
- using semantic conventions described in `signoz-semanatic-conventions`
repository for audit

<!--Reference issues using `Closes #issue-number` to enable automatic
closure on merge. -->
#### Issues closed by this PR
contributes to https://github.com/SigNoz/keystone-pod/issues/188
2026-11-10 21:26:00 +00:00
Swapnil Nakade
f1d6c988c9 feat(audit): adding kafka audit exporter (#13357)
Some checks failed
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
build-staging / staging (push) Has been cancelled
cacheci / tests (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
<!--A few plain bullets saying what changed and why, for a reviewer
skimming it - not a wall of text, not a restatement of the diff, not
generated boilerplate.-->
#### Description
- add `kafka` auditor provider that produces audit logs to a
kafka/redpanda topic as `otlp_proto`
- uses franz-go v1.21.7.
- no delivery timeout or retry cap: on franz-go < v1.22.2 these can ack
a record that was never written (twmb/franz-go#1484).
- added unit tests

<!--Reference issues using `Closes #issue-number` to enable automatic
closure on merge. -->
#### Issues closed by this PR
closes https://github.com/SigNoz/keystone-pod/issues/2

<!--Please delete paragraphs that you did not use before submitting.-->
2026-11-10 13:38:39 +00:00
428 changed files with 10879 additions and 7705 deletions

4
.github/CODEOWNERS vendored
View File

@@ -194,8 +194,8 @@ go.mod @therealpandey
/frontend/src/pages/EditRules/ @SigNoz/pulse-frontend
/frontend/src/container/AlertHistory/ @SigNoz/pulse-frontend
/frontend/src/container/CreateAlertRule/ @SigNoz/pulse-frontend
/frontend/src/container/CreateAlertV2/ @SigNoz/pulse-frontend
/frontend/src/container/EditAlertV2/ @SigNoz/pulse-frontend
/frontend/src/container/CreateAlert/ @SigNoz/pulse-frontend
/frontend/src/container/EditAlert/ @SigNoz/pulse-frontend
/frontend/src/container/FormAlertRules/ @SigNoz/pulse-frontend
/frontend/src/container/ListAlertRules/ @SigNoz/pulse-frontend
/frontend/src/container/TriggeredAlerts/ @SigNoz/pulse-frontend

View File

@@ -9,6 +9,7 @@ import (
"github.com/SigNoz/signoz/cmd"
"github.com/SigNoz/signoz/ee/auditor/fileauditor"
"github.com/SigNoz/signoz/ee/auditor/kafkaauditor"
"github.com/SigNoz/signoz/ee/auditor/otlphttpauditor"
"github.com/SigNoz/signoz/ee/authn/callbackauthn/oidccallbackauthn"
"github.com/SigNoz/signoz/ee/authn/callbackauthn/samlcallbackauthn"
@@ -156,6 +157,9 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
if err := factories.Add(fileauditor.NewFactory(licensing, version.Info)); err != nil {
panic(err)
}
if err := factories.Add(kafkaauditor.NewFactory(licensing, version.Info)); err != nil {
panic(err)
}
return factories
},
func(ctx context.Context, providerSettings factory.ProviderSettings, flagger pkgflagger.Flagger, licensing licensing.Licensing, telemetryStore telemetrystore.TelemetryStore, retentionGetter retention.Getter, orgGetter organization.Getter, zeus zeus.Zeus) (factory.NamedMap[factory.ProviderFactory[meterreporter.Reporter, meterreporter.Config]], string) {

View File

@@ -436,6 +436,7 @@ auditor:
# Specifies the auditor provider to use.
# noop: discards all audit events (community default).
# otlphttp: exports audit events via OTLP HTTP (enterprise).
# kafka: exports audit events to a Kafka topic as OTLP protobuf (enterprise).
provider: noop
# The async channel capacity for audit events. Events are dropped when full (fail-open).
buffer_size: 1000
@@ -461,6 +462,18 @@ auditor:
max_interval: 30s
# The total maximum time spent retrying.
max_elapsed_time: 60s
kafka:
# The seed broker addresses (host:port) used to discover the cluster.
brokers:
- localhost:9092
# The topic audit events are written to. It must already exist.
topic: ""
# The client id the producer reports to the brokers.
client_id: signoz
# The compression codec for produced batches: none, gzip, snappy, lz4 or zstd.
compression: lz4
# The maximum time to drain buffered events on shutdown. Undelivered events are dropped after it.
shutdown_timeout: 30s
##################### Cloud Integration #####################
cloudintegration:

View File

@@ -3970,6 +3970,7 @@ components:
- logs-field
- traces-field
- metrics-field
- llm-pricing-rule
type: string
CoretypesObject:
properties:
@@ -11745,20 +11746,10 @@ components:
description: Whether the rule is paused. A disabled rule is neither evaluated
nor notifies. Defaults to false.
type: boolean
evalWindow:
description: The lookback window as a Go duration such as 5m. Only read
when schemaVersion is v1, defaulting to 5m; v2alpha1 rules set it in evaluation.spec
instead.
type: string
evaluation:
allOf:
- $ref: '#/components/schemas/RuletypesEvaluationEnvelope'
description: The evaluation window, as a kind and spec.
frequency:
description: How often the rule is evaluated, as a Go duration such as 1m.
Only read when schemaVersion is v1, defaulting to 1m; v2alpha1 rules set
it in evaluation.spec instead.
type: string
labels:
additionalProperties:
type: string
@@ -11770,13 +11761,6 @@ components:
allOf:
- $ref: '#/components/schemas/RuletypesNotificationSettings'
description: Grouping, re-notification and routing behaviour.
preferredChannels:
description: The displayName of each notification channel to notify, for
schemaVersion v1 rules. Ignored for v2alpha1, where each threshold carries
its own channels.
items:
type: string
type: array
ruleType:
allOf:
- $ref: '#/components/schemas/RuletypesRuleType'
@@ -11886,20 +11870,10 @@ components:
description: Whether the rule is paused. A disabled rule is neither evaluated
nor notifies. Defaults to false.
type: boolean
evalWindow:
description: The lookback window as a Go duration such as 5m. Only read
when schemaVersion is v1, defaulting to 5m; v2alpha1 rules set it in evaluation.spec
instead.
type: string
evaluation:
allOf:
- $ref: '#/components/schemas/RuletypesEvaluationEnvelope'
description: The evaluation window, as a kind and spec.
frequency:
description: How often the rule is evaluated, as a Go duration such as 1m.
Only read when schemaVersion is v1, defaulting to 1m; v2alpha1 rules set
it in evaluation.spec instead.
type: string
id:
description: The unique identifier of the rule.
type: string
@@ -11914,13 +11888,6 @@ components:
allOf:
- $ref: '#/components/schemas/RuletypesNotificationSettings'
description: Grouping, re-notification and routing behaviour.
preferredChannels:
description: The displayName of each notification channel to notify, for
schemaVersion v1 rules. Ignored for v2alpha1, where each threshold carries
its own channels.
items:
type: string
type: array
ruleType:
allOf:
- $ref: '#/components/schemas/RuletypesRuleType'
@@ -11974,28 +11941,10 @@ components:
description: Whether to fire a nodata alert when the query returns no data.
Defaults to false.
type: boolean
algorithm:
description: 'anomaly_rule only: the anomaly detection algorithm. The supported
value is standard.'
type: string
compositeQuery:
allOf:
- $ref: '#/components/schemas/RuletypesAlertCompositeQuery'
description: The queries whose result is compared against the thresholds.
matchType:
allOf:
- $ref: '#/components/schemas/RuletypesMatchType'
description: 'schemaVersion v1 only: which points in the window must satisfy
op. Accepted as at_least_once, all_the_times, on_average, in_total or
last, their aliases avg and sum, or the legacy codes as strings, ''1''
to ''5''. Required for v1; v2alpha1 sets matchType on each threshold.'
op:
allOf:
- $ref: '#/components/schemas/RuletypesCompareOperator'
description: 'schemaVersion v1 only: how the value is compared with target.
Accepted as above, below, equal, not_equal, above_or_equal, below_or_equal
or outside_bounds, their symbols, or the legacy codes as strings, ''1''
to ''7''. Required for v1; v2alpha1 sets op on each threshold.'
requireMinPoints:
description: Whether a series is skipped until it has at least requiredNumPoints
points in the window. Defaults to false.
@@ -12013,17 +11962,6 @@ components:
description: The name of the query whose result is evaluated, such as A
or F1. Must match a query in compositeQuery.
type: string
target:
description: 'schemaVersion v1 only: the value compared against, in targetUnit.
Required for v1; v2alpha1 sets target on each threshold.'
format: double
nullable: true
type: number
targetUnit:
description: 'schemaVersion v1 only: the unit of target, such as percent
or ms. Converted to compositeQuery.unit before comparison when the two
differ.'
type: string
thresholds:
allOf:
- $ref: '#/components/schemas/RuletypesRuleThresholdData'
@@ -16150,9 +16088,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- VIEWER
- llm-pricing-rule:list
- tokenizer:
- VIEWER
- llm-pricing-rule:list
summary: List pricing rules
tags:
- llmpricingrules
@@ -16197,9 +16135,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- ADMIN
- llm-pricing-rule:update
- tokenizer:
- ADMIN
- llm-pricing-rule:update
summary: Create or update pricing rules
tags:
- llmpricingrules
@@ -16245,9 +16183,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- ADMIN
- llm-pricing-rule:delete
- tokenizer:
- ADMIN
- llm-pricing-rule:delete
summary: Delete a pricing rule
tags:
- llmpricingrules
@@ -16303,9 +16241,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- VIEWER
- llm-pricing-rule:read
- tokenizer:
- VIEWER
- llm-pricing-rule:read
summary: Get a pricing rule
tags:
- llmpricingrules
@@ -16358,9 +16296,11 @@ paths:
description: Internal Server Error
security:
- api_key:
- VIEWER
- llm-pricing-rule:list
- traces:read
- tokenizer:
- VIEWER
- llm-pricing-rule:list
- traces:read
summary: List unmapped models
tags:
- llmpricingrules
@@ -25530,56 +25470,6 @@ paths:
- rules
x-signoz-stability: alpha
/api/v2/rules:
get:
deprecated: true
description: 'This endpoint lists all alert rules with their current evaluation
state. Deprecated: use ListRulesV3, which supports filtering, sorting and
pagination.'
operationId: ListRules
responses:
"200":
content:
application/json:
schema:
properties:
data:
items:
$ref: '#/components/schemas/RuletypesRule'
type: array
status:
type: string
required:
- status
- data
type: object
description: OK
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- VIEWER
- tokenizer:
- VIEWER
summary: List alert rules
tags:
- rules
x-signoz-stability: alpha
post:
deprecated: false
description: This endpoint creates a new alert rule

View File

@@ -0,0 +1,31 @@
package kafkaauditor
import (
"context"
"log/slog"
"github.com/twmb/franz-go/pkg/kgo"
"github.com/SigNoz/signoz/pkg/auditor"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types/audittypes"
)
// Produces the whole batch as a single record so the collector's otlp_proto unmarshaler reads it as one ExportLogsServiceRequest.
func (provider *provider) export(ctx context.Context, events []audittypes.AuditEvent) error {
logs := audittypes.NewPLogsFromAuditEvents(events, "signoz", provider.build.Version(), "signoz.audit")
value, err := provider.marshaler.MarshalLogs(logs)
if err != nil {
return errors.Wrapf(err, errors.TypeInternal, auditor.ErrCodeAuditExportFailed, "failed to marshal audit logs")
}
record := &kgo.Record{Topic: provider.config.Kafka.Topic, Value: value}
if err := provider.client.ProduceSync(ctx, record).FirstErr(); err != nil {
provider.settings.Logger().ErrorContext(ctx, "audit export failed", errors.Attr(err), slog.Int("dropped_log_records", len(events)))
return errors.Wrapf(err, errors.TypeInternal, auditor.ErrCodeAuditExportFailed, "failed to produce audit logs to topic %q", provider.config.Kafka.Topic)
}
return nil
}

View File

@@ -0,0 +1,132 @@
package kafkaauditor
import (
"context"
"log/slog"
"github.com/twmb/franz-go/pkg/kgo"
"github.com/twmb/franz-go/plugin/kslog"
"go.opentelemetry.io/collector/pdata/plog"
"github.com/SigNoz/signoz/pkg/auditor"
"github.com/SigNoz/signoz/pkg/auditor/auditorserver"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/licensing"
"github.com/SigNoz/signoz/pkg/types/audittypes"
"github.com/SigNoz/signoz/pkg/version"
)
var _ auditor.Auditor = (*provider)(nil)
type provider struct {
settings factory.ScopedProviderSettings
config auditor.Config
licensing licensing.Licensing
build version.Build
server *auditorserver.Server
marshaler plog.ProtoMarshaler
client *kgo.Client
}
func NewFactory(licensing licensing.Licensing, build version.Build) factory.ProviderFactory[auditor.Auditor, auditor.Config] {
return factory.NewProviderFactory(factory.MustNewName("kafka"), func(ctx context.Context, providerSettings factory.ProviderSettings, config auditor.Config) (auditor.Auditor, error) {
return newProvider(ctx, providerSettings, config, licensing, build)
})
}
func newProvider(_ context.Context, providerSettings factory.ProviderSettings, config auditor.Config, licensing licensing.Licensing, build version.Build) (auditor.Auditor, error) {
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/ee/auditor/kafkaauditor")
// Acks from all in-sync replicas, idempotent writes and unbounded record retries are franz-go defaults and are kept as is.
// A delivery timeout or retry limit lets franz-go < v1.22.2 ack a record that was never written (twmb/franz-go#1484).
client, err := kgo.NewClient(
kgo.SeedBrokers(config.Kafka.Brokers...),
kgo.ClientID(config.Kafka.ClientID),
kgo.ProducerBatchCompression(compressionCodec(config.Kafka.Compression)),
kgo.ProducerLinger(0),
kgo.WithLogger(kslog.New(settings.Logger())),
)
if err != nil {
return nil, err
}
provider := &provider{
settings: settings,
config: config,
licensing: licensing,
build: build,
marshaler: plog.ProtoMarshaler{},
client: client,
}
server, err := auditorserver.New(settings,
auditorserver.Config{
BufferSize: config.BufferSize,
BatchSize: config.BatchSize,
FlushInterval: config.FlushInterval,
},
provider.export,
)
if err != nil {
client.Close()
return nil, err
}
provider.server = server
return provider, nil
}
func (provider *provider) Start(ctx context.Context) error {
return provider.server.Start(ctx)
}
func (provider *provider) Audit(ctx context.Context, event audittypes.AuditEvent) {
if event.PrincipalAttributes.PrincipalOrgID.IsZero() {
provider.settings.Logger().WarnContext(ctx, "audit event dropped as org_id is zero")
return
}
if _, err := provider.licensing.GetActive(ctx, event.PrincipalAttributes.PrincipalOrgID); err != nil {
return
}
provider.server.Add(ctx, event)
}
func (provider *provider) Healthy() <-chan struct{} {
return provider.server.Healthy()
}
func (provider *provider) Stop(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, provider.config.Kafka.ShutdownTimeout)
defer cancel()
stoppedC := make(chan error, 1)
go func() { stoppedC <- provider.server.Stop(ctx) }()
select {
case err := <-stoppedC:
provider.client.Close()
return err
case <-ctx.Done():
provider.settings.Logger().WarnContext(ctx, "audit drain timed out, closing kafka client", slog.Duration("shutdown_timeout", provider.config.Kafka.ShutdownTimeout))
// Closing fails buffered and in-flight records, so the blocked export and the rest of the drain return immediately.
provider.client.Close()
return <-stoppedC
}
}
func compressionCodec(name string) kgo.CompressionCodec {
switch name {
case "gzip":
return kgo.GzipCompression()
case "snappy":
return kgo.SnappyCompression()
case "lz4":
return kgo.Lz4Compression()
case "zstd":
return kgo.ZstdCompression()
default:
return kgo.NoCompression()
}
}

View File

@@ -0,0 +1,227 @@
package kafkaauditor
import (
"context"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/twmb/franz-go/pkg/kerr"
"github.com/twmb/franz-go/pkg/kfake"
"github.com/twmb/franz-go/pkg/kgo"
"go.opentelemetry.io/collector/pdata/plog"
"github.com/SigNoz/signoz/pkg/auditor"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/types/audittypes"
"github.com/SigNoz/signoz/pkg/types/coretypes"
"github.com/SigNoz/signoz/pkg/version"
)
func newTestCluster(t *testing.T, topics ...string) *kfake.Cluster {
t.Helper()
cluster, err := kfake.NewCluster(kfake.NumBrokers(1), kfake.SeedTopics(1, topics...))
require.NoError(t, err)
t.Cleanup(cluster.Close)
return cluster
}
func newTestProvider(t *testing.T, brokers []string, topic string, compression string) *provider {
t.Helper()
config := auditor.Config{
Provider: "kafka",
BufferSize: 10,
BatchSize: 5,
FlushInterval: 50 * time.Millisecond,
Kafka: auditor.KafkaConfig{
Brokers: brokers,
Topic: topic,
ClientID: "signoz-test",
Compression: compression,
ShutdownTimeout: 30 * time.Second,
},
}
auditor, err := newProvider(context.Background(), instrumentationtest.New().ToProviderSettings(), config, nil, version.Info)
require.NoError(t, err)
return auditor.(*provider)
}
func newTestEvent(resource coretypes.Resource, action coretypes.Verb, body string) audittypes.AuditEvent {
return audittypes.AuditEvent{
Timestamp: time.Now(),
Body: body,
EventName: audittypes.NewEventName(action),
AuditAttributes: audittypes.AuditAttributes{
Action: action,
Outcome: audittypes.OutcomeSuccess,
},
ResourceAttributes: audittypes.ResourceAttributes{
Resource: resource,
},
}
}
// Polls the topic until the consumed records hold wantLogRecords audit log records in total.
func consumeRecords(t *testing.T, brokers []string, topic string, wantLogRecords int) []*kgo.Record {
t.Helper()
client, err := kgo.NewClient(
kgo.SeedBrokers(brokers...),
kgo.ConsumeTopics(topic),
kgo.ConsumeResetOffset(kgo.NewOffset().AtStart()),
)
require.NoError(t, err)
defer client.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var records []*kgo.Record
gotLogRecords := 0
for gotLogRecords < wantLogRecords {
fetches := client.PollFetches(ctx)
require.NoError(t, ctx.Err(), "timed out waiting for %d log records, got %d", wantLogRecords, gotLogRecords)
require.Empty(t, fetches.Errors())
for _, record := range fetches.Records() {
logs, err := (&plog.ProtoUnmarshaler{}).UnmarshalLogs(record.Value)
require.NoError(t, err)
gotLogRecords += logs.LogRecordCount()
records = append(records, record)
}
}
return records
}
func TestExport(t *testing.T) {
testCases := []struct {
name string
compression string
compressionType uint8
}{
{name: "CompressionNone", compression: "none", compressionType: 0},
{name: "CompressionGzip", compression: "gzip", compressionType: 1},
{name: "CompressionSnappy", compression: "snappy", compressionType: 2},
{name: "CompressionLz4", compression: "lz4", compressionType: 3},
{name: "CompressionZstd", compression: "zstd", compressionType: 4},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
cluster := newTestCluster(t, "tenant_audit")
provider := newTestProvider(t, cluster.ListenAddrs(), "tenant_audit", testCase.compression)
defer provider.client.Close()
events := []audittypes.AuditEvent{
newTestEvent(coretypes.ResourceUser, coretypes.VerbUpdate, "user updated"),
newTestEvent(coretypes.ResourceMetaResourceDashboard, coretypes.VerbCreate, "dashboard created"),
newTestEvent(coretypes.ResourceMetaResourceDashboard, coretypes.VerbDelete, "dashboard deleted"),
}
require.NoError(t, provider.export(context.Background(), events))
records := consumeRecords(t, cluster.ListenAddrs(), "tenant_audit", len(events))
require.Len(t, records, 1)
assert.Equal(t, "tenant_audit", records[0].Topic)
assert.Equal(t, testCase.compressionType, records[0].Attrs.CompressionType())
logs, err := (&plog.ProtoUnmarshaler{}).UnmarshalLogs(records[0].Value)
require.NoError(t, err)
assert.Equal(t, len(events), logs.LogRecordCount())
serviceName, ok := logs.ResourceLogs().At(0).Resource().Attributes().Get("service.name")
require.True(t, ok)
assert.Equal(t, "signoz", serviceName.Str())
})
}
}
func TestExport_RecordTooLarge(t *testing.T) {
cluster := newTestCluster(t, "large_audit")
provider := newTestProvider(t, cluster.ListenAddrs(), "large_audit", "none")
defer provider.client.Close()
event := newTestEvent(coretypes.ResourceUser, coretypes.VerbCreate, strings.Repeat("a", 2<<20))
err := provider.export(context.Background(), []audittypes.AuditEvent{event})
require.Error(t, err)
assert.True(t, errors.Is(err, kerr.MessageTooLarge))
}
func TestExport_UnknownTopic(t *testing.T) {
cluster := newTestCluster(t)
provider := newTestProvider(t, cluster.ListenAddrs(), "missing_audit", "lz4")
defer provider.client.Close()
err := provider.export(context.Background(), []audittypes.AuditEvent{newTestEvent(coretypes.ResourceUser, coretypes.VerbCreate, "user created")})
require.Error(t, err)
assert.True(t, errors.Is(err, kerr.UnknownTopicOrPartition))
}
func TestExport_BrokerDown_RetriesUntilContextDone(t *testing.T) {
cluster := newTestCluster(t, "down_audit")
brokers := cluster.ListenAddrs()
cluster.Close()
provider := newTestProvider(t, brokers, "down_audit", "lz4")
defer provider.client.Close()
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
start := time.Now()
err := provider.export(ctx, []audittypes.AuditEvent{newTestEvent(coretypes.ResourceUser, coretypes.VerbCreate, "user created")})
require.Error(t, err)
assert.True(t, errors.Is(err, context.DeadlineExceeded))
assert.GreaterOrEqual(t, time.Since(start), 500*time.Millisecond)
}
func TestStop_DrainsQueuedEvents(t *testing.T) {
cluster := newTestCluster(t, "drain_audit")
provider := newTestProvider(t, cluster.ListenAddrs(), "drain_audit", "lz4")
done := make(chan error, 1)
go func() { done <- provider.Start(context.Background()) }()
<-provider.Healthy()
for range 7 {
provider.server.Add(context.Background(), newTestEvent(coretypes.ResourceUser, coretypes.VerbUpdate, "user updated"))
}
require.NoError(t, provider.Stop(context.Background()))
require.NoError(t, <-done)
records := consumeRecords(t, cluster.ListenAddrs(), "drain_audit", 7)
assert.NotEmpty(t, records)
}
func TestStop_BrokerDown_ClosesAfterShutdownTimeout(t *testing.T) {
cluster := newTestCluster(t, "stuck_audit")
brokers := cluster.ListenAddrs()
cluster.Close()
provider := newTestProvider(t, brokers, "stuck_audit", "lz4")
provider.config.Kafka.ShutdownTimeout = 500 * time.Millisecond
done := make(chan error, 1)
go func() { done <- provider.Start(context.Background()) }()
<-provider.Healthy()
for range 7 {
provider.server.Add(context.Background(), newTestEvent(coretypes.ResourceUser, coretypes.VerbUpdate, "user updated"))
}
start := time.Now()
require.NoError(t, provider.Stop(context.Background()))
require.NoError(t, <-done)
assert.GreaterOrEqual(t, time.Since(start), 500*time.Millisecond)
assert.Less(t, time.Since(start), 5*time.Second)
}

View File

@@ -34,8 +34,7 @@ type AnomalyRule struct {
provider anomaly.Provider
version string
logger *slog.Logger
logger *slog.Logger
seasonality anomaly.Seasonality
}
@@ -63,7 +62,6 @@ func NewAnomalyRule(
r := AnomalyRule{
BaseRule: baseRule,
querier: querier,
version: p.Version,
logger: logger.With(slog.String("rule.id", id)),
}
@@ -105,15 +103,15 @@ func (r *AnomalyRule) Type() ruletypes.RuleType {
}
func (r *AnomalyRule) prepareQueryRange(ctx context.Context, ts time.Time) *qbtypes.QueryRangeRequest {
r.logger.InfoContext(
ctx, "prepare query range request", slog.Int64("ts", ts.UnixMilli()),
slog.Int64("eval.window_ms", r.EvalWindow().Milliseconds()),
slog.Int64("eval.delay_ms", r.EvalDelay().Milliseconds()),
)
startTs, endTs := r.Timestamps(ts)
start, end := startTs.UnixMilli(), endTs.UnixMilli()
r.logger.InfoContext(
ctx, "prepare query range request", slog.Int64("ts", ts.UnixMilli()),
slog.Int64("eval.window_ms", end-start),
slog.Int64("eval.delay_ms", r.EvalDelay().Milliseconds()),
)
req := &qbtypes.QueryRangeRequest{
Start: uint64(start),
End: uint64(end),
@@ -420,12 +418,10 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (int, error) {
func (r *AnomalyRule) String() string {
ar := ruletypes.PostableRule{
AlertName: valuer.NewDisplayName(r.Name()),
RuleCondition: r.Condition(),
EvalWindow: r.EvalWindow(),
Labels: r.Labels().Map(),
Annotations: r.Annotations().Map(),
PreferredChannels: r.PreferredChannels(),
AlertName: valuer.NewDisplayName(r.Name()),
RuleCondition: r.Condition(),
Labels: r.Labels().Map(),
Annotations: r.Annotations().Map(),
}
byt, err := json.Marshal(ar)

View File

@@ -54,9 +54,6 @@ func TestAnomalyRule_NoData_AlertOnAbsent(t *testing.T) {
Frequency: valuer.MustParseTextDuration("1m"),
}},
RuleCondition: &ruletypes.RuleCondition{
CompareOperator: ruletypes.ValueIsAbove,
MatchType: ruletypes.AtleastOnce,
Target: &target,
CompositeQuery: &ruletypes.AlertCompositeQuery{
QueryType: ruletypes.QueryTypeBuilder,
Queries: []qbtypes.QueryEnvelope{{
@@ -157,10 +154,7 @@ func TestAnomalyRule_NoData_AbsentFor(t *testing.T) {
Frequency: valuer.MustParseTextDuration("1m"),
}},
RuleCondition: &ruletypes.RuleCondition{
CompareOperator: ruletypes.ValueIsAbove,
MatchType: ruletypes.AtleastOnce,
AlertOnAbsent: true,
Target: &target,
AlertOnAbsent: true,
CompositeQuery: &ruletypes.AlertCompositeQuery{
QueryType: ruletypes.QueryTypeBuilder,
Queries: []qbtypes.QueryEnvelope{{

View File

@@ -45,7 +45,6 @@ import type {
GetRuleHistoryTopContributorsParams,
GetRuleHistoryTopContributorsPathParameters,
ListRuleViews200,
ListRules200,
ListRulesV3200,
ListRulesV3Params,
PatchRuleByID200,
@@ -427,85 +426,6 @@ export const useUpdateRuleView = <
> => {
return useMutation(getUpdateRuleViewMutationOptions(options));
};
/**
* This endpoint lists all alert rules with their current evaluation state. Deprecated: use ListRulesV3, which supports filtering, sorting and pagination.
* @deprecated
* @summary List alert rules
*/
export const listRules = (signal?: AbortSignal) => {
return GeneratedAPIInstance<ListRules200>({
url: `/api/v2/rules`,
method: 'GET',
signal,
});
};
export const getListRulesQueryKey = () => {
return [`/api/v2/rules`] as const;
};
export const getListRulesQueryOptions = <
TData = Awaited<ReturnType<typeof listRules>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(options?: {
query?: UseQueryOptions<Awaited<ReturnType<typeof listRules>>, TError, TData>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getListRulesQueryKey();
const queryFn: QueryFunction<Awaited<ReturnType<typeof listRules>>> = ({
signal,
}) => listRules(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof listRules>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type ListRulesQueryResult = NonNullable<
Awaited<ReturnType<typeof listRules>>
>;
export type ListRulesQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @deprecated
* @summary List alert rules
*/
export function useListRules<
TData = Awaited<ReturnType<typeof listRules>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(options?: {
query?: UseQueryOptions<Awaited<ReturnType<typeof listRules>>, TError, TData>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getListRulesQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
return withQueryKey(query, queryOptions.queryKey);
}
/**
* @deprecated
* @summary List alert rules
*/
export const invalidateListRules = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getListRulesQueryKey() },
options,
);
return queryClient;
};
/**
* This endpoint creates a new alert rule
* @summary Create alert rule

View File

@@ -3611,6 +3611,7 @@ export enum CoretypesKindDTO {
'logs-field' = 'logs-field',
'traces-field' = 'traces-field',
'metrics-field' = 'metrics-field',
'llm-pricing-rule' = 'llm-pricing-rule',
}
export enum CoretypesTypeDTO {
user = 'user',
@@ -12966,23 +12967,10 @@ export interface RuletypesRuleConditionDTO {
* @description Whether to fire a nodata alert when the query returns no data. Defaults to false.
*/
alertOnAbsent?: boolean;
/**
* @type string
* @description anomaly_rule only: the anomaly detection algorithm. The supported value is standard.
*/
algorithm?: string;
/**
* @description The queries whose result is compared against the thresholds.
*/
compositeQuery: RuletypesAlertCompositeQueryDTO;
/**
* @description schemaVersion v1 only: which points in the window must satisfy op. Accepted as at_least_once, all_the_times, on_average, in_total or last, their aliases avg and sum, or the legacy codes as strings, '1' to '5'. Required for v1; v2alpha1 sets matchType on each threshold.
*/
matchType?: RuletypesMatchTypeDTO;
/**
* @description schemaVersion v1 only: how the value is compared with target. Accepted as above, below, equal, not_equal, above_or_equal, below_or_equal or outside_bounds, their symbols, or the legacy codes as strings, '1' to '7'. Required for v1; v2alpha1 sets op on each threshold.
*/
op?: RuletypesCompareOperatorDTO;
/**
* @type boolean
* @description Whether a series is skipped until it has at least requiredNumPoints points in the window. Defaults to false.
@@ -13002,17 +12990,6 @@ export interface RuletypesRuleConditionDTO {
* @description The name of the query whose result is evaluated, such as A or F1. Must match a query in compositeQuery.
*/
selectedQueryName: string;
/**
* @type number,null
* @format double
* @description schemaVersion v1 only: the value compared against, in targetUnit. Required for v1; v2alpha1 sets target on each threshold.
*/
target?: number | null;
/**
* @type string
* @description schemaVersion v1 only: the unit of target, such as percent or ms. Converted to compositeQuery.unit before comparison when the two differ.
*/
targetUnit?: string;
/**
* @description The thresholds to evaluate, as a kind and spec.
*/
@@ -13048,20 +13025,10 @@ export interface RuletypesPostableRuleDTO {
* @description Whether the rule is paused. A disabled rule is neither evaluated nor notifies. Defaults to false.
*/
disabled?: boolean;
/**
* @type string
* @description The lookback window as a Go duration such as 5m. Only read when schemaVersion is v1, defaulting to 5m; v2alpha1 rules set it in evaluation.spec instead.
*/
evalWindow?: string;
/**
* @description The evaluation window, as a kind and spec.
*/
evaluation: RuletypesEvaluationEnvelopeDTO;
/**
* @type string
* @description How often the rule is evaluated, as a Go duration such as 1m. Only read when schemaVersion is v1, defaulting to 1m; v2alpha1 rules set it in evaluation.spec instead.
*/
frequency?: string;
/**
* @type object
* @description Labels attached to every alert the rule fires. Keys match [a-zA-Z_.][a-zA-Z0-9_.]*; values may use Go templates such as {{$labels.service_name}}. severity is the conventional routing label.
@@ -13071,11 +13038,6 @@ export interface RuletypesPostableRuleDTO {
* @description Grouping, re-notification and routing behaviour.
*/
notificationSettings: RuletypesNotificationSettingsDTO;
/**
* @type array
* @description The displayName of each notification channel to notify, for schemaVersion v1 rules. Ignored for v2alpha1, where each threshold carries its own channels.
*/
preferredChannels?: string[];
/**
* @description The evaluation strategy: threshold_rule compares against thresholds, promql_rule evaluates a PromQL expression, anomaly_rule scores deviation from seasonal history. Defaults to threshold_rule for builder and clickhouse_sql queries and is forced to promql_rule for promql queries.
*/
@@ -13160,20 +13122,10 @@ export interface RuletypesRuleDTO {
* @description Whether the rule is paused. A disabled rule is neither evaluated nor notifies. Defaults to false.
*/
disabled?: boolean;
/**
* @type string
* @description The lookback window as a Go duration such as 5m. Only read when schemaVersion is v1, defaulting to 5m; v2alpha1 rules set it in evaluation.spec instead.
*/
evalWindow?: string;
/**
* @description The evaluation window, as a kind and spec.
*/
evaluation: RuletypesEvaluationEnvelopeDTO;
/**
* @type string
* @description How often the rule is evaluated, as a Go duration such as 1m. Only read when schemaVersion is v1, defaulting to 1m; v2alpha1 rules set it in evaluation.spec instead.
*/
frequency?: string;
/**
* @type string
* @description The unique identifier of the rule.
@@ -13188,11 +13140,6 @@ export interface RuletypesRuleDTO {
* @description Grouping, re-notification and routing behaviour.
*/
notificationSettings: RuletypesNotificationSettingsDTO;
/**
* @type array
* @description The displayName of each notification channel to notify, for schemaVersion v1 rules. Ignored for v2alpha1, where each threshold carries its own channels.
*/
preferredChannels?: string[];
/**
* @description The evaluation strategy: threshold_rule compares against thresholds, promql_rule evaluates a PromQL expression, anomaly_rule scores deviation from seasonal history. Defaults to threshold_rule for builder and clickhouse_sql queries and is forced to promql_rule for promql queries.
*/
@@ -16783,17 +16730,6 @@ export type UpdateRuleView200 = {
status: string;
};
export type ListRules200 = {
/**
* @type array
*/
data: RuletypesRuleDTO[];
/**
* @type string
*/
status: string;
};
export type CreateRule201 = {
data: RuletypesRuleDTO;
/**

View File

@@ -1,6 +1,7 @@
import React, { Dispatch, SetStateAction, useState } from 'react';
import { Check, Plus, X } from '@signozhq/icons';
import { Button, Flex } from 'antd';
import { Button } from 'antd';
import Flex from 'periscope/components/Flex/Flex';
import { Pill } from '@signozhq/ui/pill';
import Input from 'components/Input';

View File

@@ -1,5 +1,6 @@
import { useCallback, useMemo, useState } from 'react';
import { Row, Select, Spin } from 'antd';
import { Select, Spin } from 'antd';
import Row from 'periscope/components/Row/Row';
import { navigate } from 'lib/router/navigation';
import { useAppLocation } from 'lib/router/useAppLocation';
import {

View File

@@ -6,15 +6,15 @@ import { Loader, Search } from '@signozhq/icons';
import { Color } from '@signozhq/design-tokens';
import {
Button,
Flex,
Input,
InputRef,
Space,
Spin,
TableColumnsType,
TableColumnType,
Tooltip,
} from 'antd';
import Space from 'periscope/components/Space/Space';
import Flex from 'periscope/components/Flex/Flex';
import { Progress } from '@signozhq/ui/progress';
import { Typography } from '@signozhq/ui/typography';
import type { FilterDropdownProps } from 'antd/lib/table/interface';

View File

@@ -1,7 +1,8 @@
import { useCallback, useMemo, useState } from 'react';
// eslint-disable-next-line no-restricted-imports
import { useDispatch, useSelector } from 'react-redux';
import { Col, Row } from 'antd';
import Col from 'periscope/components/Col/Col';
import Row from 'periscope/components/Row/Row';
import { navigate } from 'lib/router/navigation';
import { useAppLocation } from 'lib/router/useAppLocation';
import logEvent from 'api/common/logEvent';

View File

@@ -1,5 +1,6 @@
import { Dispatch, SetStateAction, useMemo } from 'react';
import { Col, Row } from 'antd';
import Col from 'periscope/components/Col/Col';
import Row from 'periscope/components/Row/Row';
import logEvent from 'api/common/logEvent';
import { QueryParams } from 'constants/query';
import useUrlQuery from 'hooks/useUrlQuery';

View File

@@ -1,7 +1,8 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { useMutation } from 'react-query';
import { Check, ChevronsDown, ScrollText, X } from '@signozhq/icons';
import { Button, Flex, Modal } from 'antd';
import { Button, Modal } from 'antd';
import Flex from 'periscope/components/Flex/Flex';
import updateUserPreference from 'api/v1/user/preferences/name/update';
import cx from 'classnames';
import { USER_PREFERENCES } from 'constants/userPreferences';

View File

@@ -1,18 +1,13 @@
import { ConfigProvider, Select } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Select } from 'antd';
import cx from 'classnames';
import type { UpdatedWindow } from '../dsl/types';
import type { CreatorOption } from '../types';
import { UPDATED_OPTIONS } from './constants';
import MultiSelectFilter from './MultiSelectFilter';
import styles from './FilterZone.module.scss';
// The option checkbox already marks selection, so selected rows stay regular weight.
const CREATOR_SELECT_THEME = {
components: { Select: { optionSelectedFontWeight: 400 } },
};
interface Props {
createdBy: string[];
updated: UpdatedWindow;
@@ -52,36 +47,16 @@ function FilterChips({
return (
<div className={styles.chips}>
<ConfigProvider theme={CREATOR_SELECT_THEME}>
<Select
mode="multiple"
showSearch
allowClear
className={cx(styles.select, styles.selectWide)}
placeholder="Created by"
value={createdBy}
options={creatorOptionsData}
optionFilterProp="label"
maxTagCount={1}
menuItemSelectedIcon={null}
data-testid={`${testIdPrefix}-filter-created-by`}
disabled={disabled}
onClear={onClearCreatedBy}
optionRender={(option): JSX.Element => (
<div className={styles.creatorOption}>
<div className={styles.creatorCheck}>
<Checkbox
color="primary"
value={createdBy.includes(option.value as string)}
/>
</div>
<span className={styles.creatorLabel}>{option.label}</span>
</div>
)}
onChange={(value): void => onCreatedByChange(value as string[])}
onDropdownVisibleChange={runOnClose}
/>
</ConfigProvider>
<MultiSelectFilter
value={createdBy}
options={creatorOptionsData}
placeholder="Created by"
testId={`${testIdPrefix}-filter-created-by`}
disabled={disabled}
onClear={onClearCreatedBy}
onChange={onCreatedByChange}
onDropdownVisibleChange={runOnClose}
/>
<Select
showSearch
className={cx(styles.select, styles.selectNarrow)}

View File

@@ -86,21 +86,21 @@
}
}
// Created-by option: a presentational checkbox + label (antd handles the toggle).
.creatorOption {
// Multi-select option: a presentational checkbox + label (antd handles the toggle).
.checkOption {
display: flex;
align-items: center;
gap: 8px;
}
.creatorCheck {
.checkOptionBox {
// Flex, not block: a line box would shift with the check mark's baseline.
display: flex;
flex-shrink: 0;
pointer-events: none;
}
.creatorLabel {
.checkOptionLabel {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

View File

@@ -0,0 +1,72 @@
import { ConfigProvider, Select } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import cx from 'classnames';
import styles from './FilterZone.module.scss';
// The option checkbox already marks selection, so selected rows stay regular weight.
const CHECKBOX_SELECT_THEME = {
components: { Select: { optionSelectedFontWeight: 400 } },
};
export interface MultiSelectFilterOption {
value: string;
label: string;
}
interface Props {
value: string[];
options: MultiSelectFilterOption[];
placeholder: string;
testId: string;
onChange: (values: string[]) => void;
onClear?: () => void;
onDropdownVisibleChange?: (open: boolean) => void;
disabled?: boolean;
}
function MultiSelectFilter({
value,
options,
placeholder,
testId,
onChange,
onClear,
onDropdownVisibleChange,
disabled = false,
}: Props): JSX.Element {
return (
<ConfigProvider theme={CHECKBOX_SELECT_THEME}>
<Select
mode="multiple"
showSearch
allowClear
className={cx(styles.select, styles.selectWide)}
placeholder={placeholder}
value={value}
options={options}
optionFilterProp="label"
maxTagCount={1}
menuItemSelectedIcon={null}
data-testid={testId}
disabled={disabled}
onClear={onClear}
optionRender={(option): JSX.Element => (
<div className={styles.checkOption}>
<div className={styles.checkOptionBox}>
<Checkbox
color="primary"
value={value.includes(option.value as string)}
/>
</div>
<span className={styles.checkOptionLabel}>{option.label}</span>
</div>
)}
onChange={(next): void => onChange(next as string[])}
onDropdownVisibleChange={onDropdownVisibleChange}
/>
</ConfigProvider>
);
}
export default MultiSelectFilter;

View File

@@ -157,6 +157,7 @@ function RawLogView({
return (
<RawLogViewContainer
className="raw-log-row"
data-testid={`logs-raw-row-${data.id}`}
onClick={handleClickExpand}
wrap={false}

View File

@@ -1,6 +1,8 @@
import { blue } from '@ant-design/colors';
import { Color } from '@signozhq/design-tokens';
import { Col, Row, Space } from 'antd';
import Space from 'periscope/components/Space/Space';
import Col from 'periscope/components/Col/Col';
import Row from 'periscope/components/Row/Row';
import { FontSize } from 'container/OptionsMenu/types';
import { Info } from '@signozhq/icons';
import styled from 'styled-components';

View File

@@ -215,7 +215,8 @@
padding-left: 8px;
.qb-formula {
.ant-row {
.formula-row,
.formula-container {
row-gap: 8px !important;
}
@@ -229,7 +230,7 @@
padding: 8px;
margin-left: 74px;
.ant-col {
.formula-item {
&::before {
content: '';
position: absolute;

View File

@@ -4,7 +4,8 @@ import type {
TableColumnsType as ColumnsType,
TableColumnType as ColumnType,
} from 'antd';
import { Button, Flex } from 'antd';
import { Button } from 'antd';
import Flex from 'periscope/components/Flex/Flex';
import { Dropdown, type DropdownItemType } from '@signozhq/ui/dropdown';
import { Switch } from '@signozhq/ui/switch';
import logEvent from 'api/common/logEvent';

View File

@@ -1,14 +1,11 @@
import { HTMLAttributes } from 'react';
import {
Button,
ButtonProps,
Col,
ColProps,
Row,
RowProps,
Space,
SpaceProps,
} from 'antd';
import { Button, ButtonProps } from 'antd';
import Space from 'periscope/components/Space/Space';
import type { SpaceProps } from 'periscope/components/Space/types';
import Col from 'periscope/components/Col/Col';
import type { ColProps } from 'periscope/components/Col/types';
import Row from 'periscope/components/Row/Row';
import type { RowProps } from 'periscope/components/Row/types';
import {
Typography,
TypographyTextProps,

View File

@@ -53,7 +53,6 @@ export enum QueryParams {
variables = 'variables',
version = 'version',
source = 'source',
showClassicCreateAlertsPage = 'showClassicCreateAlertsPage',
isTestAlert = 'isTestAlert',
yAxisUnit = 'yAxisUnit',
ruleName = 'ruleName',

View File

@@ -26,7 +26,6 @@ export const REACT_QUERY_KEY = {
REMOVE_ALERT_RULE: 'REMOVE_ALERT_RULE',
CLONE_ALERT_RULE: 'CLONE_ALERT_RULE',
GET_HOST_LIST: 'GET_HOST_LIST',
UPDATE_ALERT_RULE: 'UPDATE_ALERT_RULE',
GET_TRACE_V2_WATERFALL: 'GET_TRACE_V2_WATERFALL',
GET_TRACE_V4_WATERFALL: 'GET_TRACE_V4_WATERFALL',
GET_TRACE_AGGREGATIONS: 'GET_TRACE_AGGREGATIONS',

View File

@@ -6,7 +6,9 @@ import { useQueries } from 'react-query';
import { useSelector } from 'react-redux';
import { AppLink } from 'lib/router/AppLink';
import { useAppLocation } from 'lib/router/useAppLocation';
import { Button, Card, Input, Space, TableProps, Tooltip, Flex } from 'antd';
import { Button, Card, Input, TableProps, Tooltip } from 'antd';
import Space from 'periscope/components/Space/Space';
import Flex from 'periscope/components/Flex/Flex';
import { Search } from '@signozhq/icons';
import type { ColumnType, TablePaginationConfig } from 'antd/es/table';
import type { FilterValue, SorterResult } from 'antd/es/table/interface';

View File

@@ -15,7 +15,7 @@ import { useAppLocation } from 'lib/router/useAppLocation';
import * as Sentry from '@sentry/react';
import { Toaster } from '@signozhq/ui/sonner';
import { TooltipProvider } from '@signozhq/ui/tooltip';
import { Flex } from 'antd';
import Flex from 'periscope/components/Flex/Flex';
import getLocalStorageApi from 'api/browser/localstorage/get';
import setLocalStorageApi from 'api/browser/localstorage/set';
import getChangelogByVersion from 'api/changelog/getChangelogByVersion';

View File

@@ -1,4 +1,4 @@
import { Layout as LayoutComponent } from 'antd';
import LayoutComponent from 'periscope/components/Layout/Layout';
import styled from 'styled-components';
export const Layout = styled(LayoutComponent)`

View File

@@ -5,15 +5,10 @@ import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useMutation } from 'react-query';
import { CircleCheck, Landmark, MonitorDown } from '@signozhq/icons';
import {
Card,
Col,
Flex,
Row,
Skeleton,
Table,
TableColumnsType as ColumnsType,
} from 'antd';
import { Card, Skeleton, Table, TableColumnsType as ColumnsType } from 'antd';
import Col from 'periscope/components/Col/Col';
import Row from 'periscope/components/Row/Row';
import Flex from 'periscope/components/Flex/Flex';
import { Badge } from '@signozhq/ui/badge';
import logEvent from 'api/common/logEvent';
import type {

View File

@@ -1,5 +1,6 @@
import { useCallback, useMemo, useRef } from 'react';
import { Card, Flex } from 'antd';
import { Card } from 'antd';
import Flex from 'periscope/components/Flex/Flex';
import { Typography } from '@signozhq/ui/typography';
import BarChart from 'lib/visualization/charts/BarChart/BarChart';
import { useIsDarkMode } from 'hooks/useDarkMode';

View File

@@ -111,8 +111,8 @@ jest.mock('constants/formats/alertFormatCategories', () => ({
]),
}));
jest.mock('container/CreateAlertV2/utils', () => ({
...jest.requireActual('container/CreateAlertV2/utils'),
jest.mock('container/CreateAlert/utils', () => ({
...jest.requireActual('container/CreateAlert/utils'),
}));
const TEST_STRINGS = {

View File

@@ -1,4 +1,4 @@
import { createMockAlertContextState } from 'container/CreateAlertV2/EvaluationSettings/__tests__/testUtils';
import { createMockAlertContextState } from 'container/CreateAlert/EvaluationSettings/__tests__/testUtils';
import { render, screen, userEvent, within } from 'tests/test-utils';
import { ChannelOption } from 'hooks/notificationChannels/useChannelOptions';

View File

@@ -1,6 +1,6 @@
import { fireEvent, render, screen } from '@testing-library/react';
import type { DefaultOptionType } from 'antd/es/select';
import { createMockAlertContextState } from 'container/CreateAlertV2/EvaluationSettings/__tests__/testUtils';
import { createMockAlertContextState } from 'container/CreateAlert/EvaluationSettings/__tests__/testUtils';
import { getAppContextMockState } from 'container/RoutingPolicies/__tests__/testUtils';
import * as appHooks from 'providers/App/App';
import { ChannelOption } from 'hooks/notificationChannels/useChannelOptions';

View File

@@ -1,4 +1,5 @@
import { Button, Flex, SelectProps } from 'antd';
import { Button, SelectProps } from 'antd';
import Flex from 'periscope/components/Flex/Flex';
import { Switch } from '@signozhq/ui/switch';
import { Typography } from '@signozhq/ui/typography';
import type { BaseOptionType, DefaultOptionType } from 'antd/es/select';
@@ -9,7 +10,7 @@ import ROUTES from 'constants/routes';
import {
AlertThresholdMatchType,
AlertThresholdOperator,
} from 'container/CreateAlertV2/context/types';
} from 'container/CreateAlert/context/types';
import { getSelectedQueryOptions } from 'container/FormAlertRules/utils';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import { ArrowRight } from '@signozhq/icons';

View File

@@ -9,12 +9,12 @@ import CreateAlertHeader from './CreateAlertHeader';
import Footer from './Footer';
import NotificationSettings from './NotificationSettings';
import QuerySection from './QuerySection';
import { CreateAlertV2Props } from './types';
import { CreateAlertProps } from './types';
import { Spinner } from './utils';
import './CreateAlertV2.styles.scss';
import './CreateAlert.styles.scss';
function CreateAlertV2({ alertType }: CreateAlertV2Props): JSX.Element {
function CreateAlert({ alertType }: CreateAlertProps): JSX.Element {
const queryToRedirect = buildInitialAlertDef(alertType);
const currentQueryToRedirect = mapQueryDataFromApi(
queryToRedirect.condition.compositeQuery,
@@ -36,8 +36,8 @@ function CreateAlertV2({ alertType }: CreateAlertV2Props): JSX.Element {
);
}
CreateAlertV2.defaultProps = {
CreateAlert.defaultProps = {
initialQuery: initialQueriesMap.metrics,
};
export default CreateAlertV2;
export default CreateAlert;

View File

@@ -1,14 +1,7 @@
import { useCallback, useMemo } from 'react';
import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import logEvent from 'api/common/logEvent';
import classNames from 'classnames';
import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import useUrlQuery from 'hooks/useUrlQuery';
import { RotateCcw } from '@signozhq/icons';
import { useAlertRuleOptional } from 'providers/Alert';
import { Labels } from 'types/api/alerts/def';
@@ -22,8 +15,6 @@ function CreateAlertHeader(): JSX.Element {
const alertRuleContext = useAlertRuleOptional();
const { currentQuery } = useQueryBuilder();
const { safeNavigate } = useSafeNavigate();
const urlQuery = useUrlQuery();
const groupByLabels = useMemo(() => {
const labels = new Array<string>();
@@ -46,14 +37,6 @@ function CreateAlertHeader(): JSX.Element {
[groupByLabels],
);
const handleSwitchToClassicExperience = useCallback(() => {
logEvent('Alert: Switch to classic experience button clicked', {});
urlQuery.set(QueryParams.showClassicCreateAlertsPage, 'true');
const url = `${ROUTES.ALERTS_NEW}?${urlQuery.toString()}`;
safeNavigate(url, { replace: true });
}, [safeNavigate, urlQuery]);
return (
<div
className={classNames('alert-header', { 'edit-alert-header': isEditMode })}
@@ -61,15 +44,6 @@ function CreateAlertHeader(): JSX.Element {
{!isEditMode && (
<div className="alert-header__tab-bar">
<div className="alert-header__tab">New Alert Rule</div>
<Button
prefix={<RotateCcw size={12} />}
onClick={handleSwitchToClassicExperience}
variant="solid"
color="secondary"
size="sm"
>
Switch to Classic Experience
</Button>
</div>
)}
<div className="alert-header__content">

View File

@@ -1,9 +1,6 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes';
import { defaultPostableAlertRuleV2 } from 'container/CreateAlertV2/constants';
import { getCreateAlertLocalStateFromAlertDef } from 'container/CreateAlertV2/utils';
import * as useSafeNavigateHook from 'hooks/useSafeNavigate';
import { defaultPostableAlertRuleV2 } from 'container/CreateAlert/constants';
import { getCreateAlertLocalStateFromAlertDef } from 'container/CreateAlert/utils';
import { mockAppLocation } from 'tests/router-mocks';
import { AlertTypes } from 'types/api/alerts/alertTypes';
@@ -11,11 +8,6 @@ import * as rulesHook from '../../../../api/generated/services/rules';
import { CreateAlertProvider } from '../../context';
import CreateAlertHeader from '../CreateAlertHeader';
const mockSafeNavigate = jest.fn();
jest.spyOn(useSafeNavigateHook, 'useSafeNavigate').mockReturnValue({
safeNavigate: mockSafeNavigate,
});
jest.spyOn(rulesHook, 'useCreateRule').mockReturnValue({
mutate: jest.fn(),
isLoading: false,
@@ -101,37 +93,4 @@ describe('CreateAlertHeader', () => {
screen.getByPlaceholderText(ENTER_ALERT_RULE_NAME_PLACEHOLDER),
).toHaveValue('TEST_ALERT');
});
it('should navigate to classic experience when button is clicked', () => {
renderCreateAlertHeader();
const switchToClassicExperienceButton = screen.getByText(
'Switch to Classic Experience',
);
expect(switchToClassicExperienceButton).toBeInTheDocument();
fireEvent.click(switchToClassicExperienceButton);
const params = new URLSearchParams();
params.set(QueryParams.showClassicCreateAlertsPage, 'true');
expect(mockSafeNavigate).toHaveBeenCalledWith(
`${ROUTES.ALERTS_NEW}?${params.toString()}`,
{ replace: true },
);
});
it('should not render "switch to classic experience" button when isEditMode is true', () => {
render(
<CreateAlertProvider
isEditMode
initialAlertType={AlertTypes.METRICS_BASED_ALERT}
initialAlertState={getCreateAlertLocalStateFromAlertDef(
defaultPostableAlertRuleV2,
)}
>
<CreateAlertHeader />
</CreateAlertProvider>,
);
expect(
screen.queryByText('Switch to Classic Experience'),
).not.toBeInTheDocument();
});
});

View File

@@ -1,9 +1,9 @@
import { useMemo } from 'react';
import { Button } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { useCreateAlertState } from 'container/CreateAlertV2/context';
import { INITIAL_ADVANCED_OPTIONS_STATE } from 'container/CreateAlertV2/context/constants';
import { IEditCustomScheduleProps } from 'container/CreateAlertV2/EvaluationSettings/types';
import { useCreateAlertState } from 'container/CreateAlert/context';
import { INITIAL_ADVANCED_OPTIONS_STATE } from 'container/CreateAlert/context/constants';
import { IEditCustomScheduleProps } from 'container/CreateAlert/EvaluationSettings/types';
import { Calendar1, Pencil, Trash } from '@signozhq/icons';
function EditCustomSchedule({

View File

@@ -2,8 +2,8 @@ import { useEffect, useMemo, useState } from 'react';
import { Button, DatePicker, Input, Select } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import classNames from 'classnames';
import { useCreateAlertState } from 'container/CreateAlertV2/context';
import { AdvancedOptionsState } from 'container/CreateAlertV2/context/types';
import { useCreateAlertState } from 'container/CreateAlert/context';
import { AdvancedOptionsState } from 'container/CreateAlert/context/types';
import dayjs from 'dayjs';
import { Code, PencilLine } from '@signozhq/icons';

View File

@@ -1,5 +1,5 @@
import { fireEvent, render, screen } from '@testing-library/react';
import * as alertState from 'container/CreateAlertV2/context';
import * as alertState from 'container/CreateAlert/context';
import AdvancedOptions from '../AdvancedOptions';
import { createMockAlertContextState } from './testUtils';

View File

@@ -1,6 +1,6 @@
import { fireEvent, render, screen } from '@testing-library/react';
import * as alertState from 'container/CreateAlertV2/context';
import { INITIAL_ADVANCED_OPTIONS_STATE } from 'container/CreateAlertV2/context/constants';
import * as alertState from 'container/CreateAlert/context';
import { INITIAL_ADVANCED_OPTIONS_STATE } from 'container/CreateAlert/context/constants';
import { TIMEZONE_DATA } from '../constants';
import EditCustomSchedule from '../EvaluationCadence/EditCustomSchedule';

View File

@@ -1,6 +1,6 @@
import { fireEvent, render, screen } from '@testing-library/react';
import * as alertState from 'container/CreateAlertV2/context';
import { INITIAL_ADVANCED_OPTIONS_STATE } from 'container/CreateAlertV2/context/constants';
import * as alertState from 'container/CreateAlert/context';
import { INITIAL_ADVANCED_OPTIONS_STATE } from 'container/CreateAlert/context/constants';
import { TIMEZONE_DATA } from '../constants';
import EvaluationCadence from '../EvaluationCadence';

View File

@@ -1,7 +1,7 @@
import { fireEvent, render, screen } from '@testing-library/react';
import * as alertState from 'container/CreateAlertV2/context';
import { INITIAL_ADVANCED_OPTIONS_STATE } from 'container/CreateAlertV2/context/constants';
import { AdvancedOptionsState } from 'container/CreateAlertV2/context/types';
import * as alertState from 'container/CreateAlert/context';
import { INITIAL_ADVANCED_OPTIONS_STATE } from 'container/CreateAlert/context/constants';
import { AdvancedOptionsState } from 'container/CreateAlert/context/types';
import EvaluationCadenceDetails from '../EvaluationCadence/EvaluationCadenceDetails';
import { createMockAlertContextState } from './testUtils';

View File

@@ -1,6 +1,6 @@
import { render, screen } from '@testing-library/react';
import * as alertState from 'container/CreateAlertV2/context';
import { INITIAL_ADVANCED_OPTIONS_STATE } from 'container/CreateAlertV2/context/constants';
import * as alertState from 'container/CreateAlert/context';
import { INITIAL_ADVANCED_OPTIONS_STATE } from 'container/CreateAlert/context/constants';
import { TIMEZONE_DATA } from '../constants';
import EvaluationCadencePreview, {

View File

@@ -1,11 +1,11 @@
import { render, screen } from '@testing-library/react';
import * as alertState from 'container/CreateAlertV2/context';
import * as alertState from 'container/CreateAlert/context';
import EvaluationSettings from '../EvaluationSettings';
import { createMockAlertContextState } from './testUtils';
jest.mock('container/CreateAlertV2/utils', () => ({
...jest.requireActual('container/CreateAlertV2/utils'),
jest.mock('container/CreateAlert/utils', () => ({
...jest.requireActual('container/CreateAlert/utils'),
}));
const mockSetEvaluationWindow = jest.fn();

View File

@@ -1,5 +1,5 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { EvaluationWindowState } from 'container/CreateAlertV2/context/types';
import { EvaluationWindowState } from 'container/CreateAlert/context/types';
import {
EVALUATION_WINDOW_TIMEFRAME,

View File

@@ -4,11 +4,11 @@ import {
INITIAL_ALERT_THRESHOLD_STATE,
INITIAL_EVALUATION_WINDOW_STATE,
INITIAL_NOTIFICATION_SETTINGS_STATE,
} from 'container/CreateAlertV2/context/constants';
} from 'container/CreateAlert/context/constants';
import {
EvaluationWindowState,
ICreateAlertContextProps,
} from 'container/CreateAlertV2/context/types';
} from 'container/CreateAlert/context/types';
import { AlertTypes } from 'types/api/alerts/alertTypes';
export const createMockAlertContextState = (

View File

@@ -20,7 +20,7 @@ jest.mock('dayjs', () => {
});
import { UniversalYAxisUnit } from 'components/YAxisUnitSelector/types';
import { EvaluationWindowState } from 'container/CreateAlertV2/context/types';
import { EvaluationWindowState } from 'container/CreateAlert/context/types';
import dayjs, { Dayjs } from 'dayjs';
import { rrulestr } from 'rrule';

View File

@@ -2,8 +2,8 @@ import { fireEvent, render, screen } from '@testing-library/react';
import {
AlertThresholdMatchType,
AlertThresholdOperator,
} from 'container/CreateAlertV2/context/types';
import { createMockAlertContextState } from 'container/CreateAlertV2/EvaluationSettings/__tests__/testUtils';
} from 'container/CreateAlert/context/types';
import { createMockAlertContextState } from 'container/CreateAlert/EvaluationSettings/__tests__/testUtils';
import * as createAlertState from '../../context';
import Footer from '../Footer';

View File

@@ -6,15 +6,15 @@ import {
INITIAL_ALERT_THRESHOLD_STATE,
INITIAL_EVALUATION_WINDOW_STATE,
INITIAL_NOTIFICATION_SETTINGS_STATE,
} from 'container/CreateAlertV2/context/constants';
} from 'container/CreateAlert/context/constants';
import {
AdvancedOptionsState,
AlertThresholdOperator,
EvaluationWindowState,
NotificationSettingsState,
Seasonality,
} from 'container/CreateAlertV2/context/types';
import { createMockAlertContextState } from 'container/CreateAlertV2/EvaluationSettings/__tests__/testUtils';
} from 'container/CreateAlert/context/types';
import { createMockAlertContextState } from 'container/CreateAlert/EvaluationSettings/__tests__/testUtils';
import { AlertTypes } from 'types/api/alerts/alertTypes';
import { EQueryType } from 'types/common/dashboard';
@@ -556,8 +556,6 @@ describe('Footer utils', () => {
},
ruleType: 'threshold_rule',
schemaVersion: 'v2alpha1',
source: 'http://localhost/',
version: 'v5',
});
});

View File

@@ -1,12 +1,12 @@
import { RuletypesRuleTypeDTO } from 'api/generated/services/sigNoz.schemas';
import { UniversalYAxisUnit } from 'components/YAxisUnitSelector/types';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { AlertDetectionTypes } from 'container/FormAlertRules';
import { mapQueryDataToApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi';
import { AlertTypes } from 'types/api/alerts/alertTypes';
import {
AlertTypes,
BasicThreshold,
PostableAlertRuleV2,
} from 'types/api/alerts/alertTypesV2';
} from 'types/api/alerts/alertTypes';
import { EQueryType } from 'types/common/dashboard';
import { compositeQueryToQueryEnvelope } from 'utils/compositeQueryToQueryEnvelope';
@@ -266,7 +266,7 @@ export function buildCreateThresholdAlertRulePayload(
// Evaluation
const evaluationProps = getEvaluationProps(evaluationWindow, advancedOptions);
let ruleType: string = AlertDetectionTypes.THRESHOLD_ALERT;
let ruleType: string = RuletypesRuleTypeDTO.threshold_rule;
if (query.queryType === EQueryType.PROM) {
ruleType = 'promql_rule';
}
@@ -292,9 +292,7 @@ export function buildCreateThresholdAlertRulePayload(
summary: notificationSettings.description,
},
notificationSettings: notificationSettingsProps,
version: 'v5',
schemaVersion: 'v2alpha1',
source: window?.location.toString(),
};
}
@@ -321,7 +319,7 @@ export function buildCreateAnomalyAlertRulePayload(
return {
...payload,
ruleType: AlertDetectionTypes.ANOMALY_DETECTION_ALERT,
ruleType: RuletypesRuleTypeDTO.anomaly_rule,
alertType: AlertTypes.METRICS_BASED_ALERT,
condition: {
...payload.condition,

Some files were not shown because too many files have changed in this diff Show More