mirror of
https://github.com/SigNoz/signoz.git
synced 2026-04-20 10:40:29 +01:00
Compare commits
36 Commits
feat/cloud
...
base-path-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b61376b904 | ||
|
|
18429b3b91 | ||
|
|
c7adb22c61 | ||
|
|
b936e63658 | ||
|
|
727b105b6c | ||
|
|
96d816bd1a | ||
|
|
f52d89c338 | ||
|
|
691e919a41 | ||
|
|
f3269318b7 | ||
|
|
5088bd7499 | ||
|
|
7648d4f3d3 | ||
|
|
5729a4584a | ||
|
|
825d06249d | ||
|
|
9034471587 | ||
|
|
4cc23ead6b | ||
|
|
867e27d45f | ||
|
|
be37e588f8 | ||
|
|
057dcbe6e4 | ||
|
|
3a28d741a3 | ||
|
|
223e83154f | ||
|
|
50ae51cdaa | ||
|
|
c8ae8476c3 | ||
|
|
daaa66e1fc | ||
|
|
b0717d6a69 | ||
|
|
4aefe44313 | ||
|
|
4dc6f6fe7b | ||
|
|
d3e0c46ba2 | ||
|
|
0fed17e11a | ||
|
|
a2264b4960 | ||
|
|
2740964106 | ||
|
|
0ca22dd7fe | ||
|
|
a3b6bddac8 | ||
|
|
d908ce321a | ||
|
|
c221a44f3d | ||
|
|
22fb4daaf9 | ||
|
|
1bdc059d76 |
@@ -668,8 +668,8 @@ components:
|
||||
properties:
|
||||
aws:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSAccountConfig'
|
||||
azure:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAzureAccountConfig'
|
||||
required:
|
||||
- aws
|
||||
type: object
|
||||
CloudintegrationtypesAgentReport:
|
||||
nullable: true
|
||||
@@ -693,87 +693,6 @@ components:
|
||||
nullable: true
|
||||
type: array
|
||||
type: object
|
||||
CloudintegrationtypesAzureAccountConfig:
|
||||
properties:
|
||||
deploymentRegion:
|
||||
type: string
|
||||
resourceGroups:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- deploymentRegion
|
||||
- resourceGroups
|
||||
type: object
|
||||
CloudintegrationtypesAzureConnectionArtifact:
|
||||
properties:
|
||||
cliCommand:
|
||||
type: string
|
||||
cloudShellCommand:
|
||||
type: string
|
||||
required:
|
||||
- cliCommand
|
||||
- cloudShellCommand
|
||||
type: object
|
||||
CloudintegrationtypesAzureIntegrationConfig:
|
||||
properties:
|
||||
deploymentRegion:
|
||||
type: string
|
||||
resourceGroups:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
telemetryCollectionStrategy:
|
||||
items:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAzureTelemetryCollectionStrategy'
|
||||
type: array
|
||||
required:
|
||||
- deploymentRegion
|
||||
- resourceGroups
|
||||
- telemetryCollectionStrategy
|
||||
type: object
|
||||
CloudintegrationtypesAzureLogsCollectionStrategy:
|
||||
properties:
|
||||
categoryGroups:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- categoryGroups
|
||||
type: object
|
||||
CloudintegrationtypesAzureMetricsCollectionStrategy:
|
||||
type: object
|
||||
CloudintegrationtypesAzureServiceConfig:
|
||||
properties:
|
||||
logs:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAzureServiceLogsConfig'
|
||||
metrics:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAzureServiceMetricsConfig'
|
||||
required:
|
||||
- logs
|
||||
- metrics
|
||||
type: object
|
||||
CloudintegrationtypesAzureServiceLogsConfig:
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
type: object
|
||||
CloudintegrationtypesAzureServiceMetricsConfig:
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
type: object
|
||||
CloudintegrationtypesAzureTelemetryCollectionStrategy:
|
||||
properties:
|
||||
logs:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAzureLogsCollectionStrategy'
|
||||
metrics:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAzureMetricsCollectionStrategy'
|
||||
resourceType:
|
||||
type: string
|
||||
required:
|
||||
- resourceType
|
||||
type: object
|
||||
CloudintegrationtypesCloudIntegrationService:
|
||||
nullable: true
|
||||
properties:
|
||||
@@ -818,8 +737,8 @@ components:
|
||||
properties:
|
||||
aws:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSConnectionArtifact'
|
||||
azure:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAzureConnectionArtifact'
|
||||
required:
|
||||
- aws
|
||||
type: object
|
||||
CloudintegrationtypesCredentials:
|
||||
properties:
|
||||
@@ -991,8 +910,8 @@ components:
|
||||
properties:
|
||||
aws:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSPostableAccountConfig'
|
||||
azure:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAzureAccountConfig'
|
||||
required:
|
||||
- aws
|
||||
type: object
|
||||
CloudintegrationtypesPostableAgentCheckIn:
|
||||
properties:
|
||||
@@ -1015,8 +934,8 @@ components:
|
||||
properties:
|
||||
aws:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSIntegrationConfig'
|
||||
azure:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAzureIntegrationConfig'
|
||||
required:
|
||||
- aws
|
||||
type: object
|
||||
CloudintegrationtypesService:
|
||||
properties:
|
||||
@@ -1053,8 +972,8 @@ components:
|
||||
properties:
|
||||
aws:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSServiceConfig'
|
||||
azure:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAzureServiceConfig'
|
||||
required:
|
||||
- aws
|
||||
type: object
|
||||
CloudintegrationtypesServiceID:
|
||||
enum:
|
||||
@@ -1071,8 +990,6 @@ components:
|
||||
- s3sync
|
||||
- sns
|
||||
- sqs
|
||||
- blobstorage
|
||||
- frontdoor
|
||||
type: string
|
||||
CloudintegrationtypesServiceMetadata:
|
||||
properties:
|
||||
@@ -1101,32 +1018,16 @@ components:
|
||||
properties:
|
||||
aws:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSTelemetryCollectionStrategy'
|
||||
azure:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAzureTelemetryCollectionStrategy'
|
||||
required:
|
||||
- aws
|
||||
type: object
|
||||
CloudintegrationtypesUpdatableAccount:
|
||||
properties:
|
||||
config:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesUpdatableAccountConfig'
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAccountConfig'
|
||||
required:
|
||||
- config
|
||||
type: object
|
||||
CloudintegrationtypesUpdatableAccountConfig:
|
||||
properties:
|
||||
aws:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesAWSAccountConfig'
|
||||
azure:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesUpdatableAzureAccountConfig'
|
||||
type: object
|
||||
CloudintegrationtypesUpdatableAzureAccountConfig:
|
||||
properties:
|
||||
resourceGroups:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- resourceGroups
|
||||
type: object
|
||||
CloudintegrationtypesUpdatableService:
|
||||
properties:
|
||||
config:
|
||||
|
||||
@@ -20,3 +20,4 @@ We **recommend** (almost enforce) reviewing these guides before contributing to
|
||||
- [Packages](packages.md) - Naming, layout, and conventions for `pkg/` packages
|
||||
- [Service](service.md) - Managed service lifecycle with `factory.Service`
|
||||
- [SQL](sql.md) - Database and SQL patterns
|
||||
- [Types](types.md) - Domain types, request/response bodies, and storage rows in `pkg/types/`
|
||||
|
||||
152
docs/contributing/go/types.md
Normal file
152
docs/contributing/go/types.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Types
|
||||
|
||||
Domain types in `pkg/types/<domain>/` live on three serialization boundaries — inbound HTTP, outbound HTTP, and SQL — on top of an in-memory domain representation. SigNoz's convention is **core-type-first**: every domain defines a single canonical type `X`, and specialized flavors (`PostableX`, `GettableX`, `UpdatableX`, `StorableX`) are introduced **only when they actually differ from `X`**. This guide spells out when each flavor is warranted and how they relate to each other.
|
||||
|
||||
Before reading, make sure you have read [abstractions.md](abstractions.md) — the rules here build on its guidance that every new type must earn its place.
|
||||
|
||||
## The core type is required
|
||||
|
||||
Every domain package in `pkg/types/<domain>/` defines exactly one core type `X`: `AuthDomain`, `Channel`, `Rule`, `Dashboard`, `Role`, `PlannedMaintenance`. This is the canonical in-memory representation of the domain object. Domain methods, validation invariants, and business logic hang off `X` — not off the flavor types.
|
||||
|
||||
Two rules shape how the core type behaves:
|
||||
|
||||
- **Conversions can be either `New<Output>From<Input>` or a receiver-style `(x *X) ToY()` method.** Either form is fine; pick whichever reads best at the call site:
|
||||
|
||||
```go
|
||||
// Constructor form
|
||||
func NewGettableAuthDomainFromAuthDomain(d *AuthDomain, info *AuthNProviderInfo) *GettableAuthDomain
|
||||
|
||||
// Receiver form
|
||||
func (m *PlannedMaintenanceWithRules) ToPlannedMaintenance() *PlannedMaintenance
|
||||
```
|
||||
- **`X` can double as the storage row** when the DB shape would be identical. `Channel` embeds `bun.BaseModel` directly, and there is no `StorableChannel`. This is the preferred shape when it works.
|
||||
|
||||
Domain packages under `pkg/types/` must not import from other `pkg/` packages. Keep the core type's methods lightweight and push orchestration out to the module layer.
|
||||
|
||||
## Add a flavor only when it differs
|
||||
|
||||
For each of the four flavors, create it only if its shape diverges from `X`. If a flavor would have the same fields and tags as `X`, reuse `X` directly, or declare a type alias. Every flavor must earn its place per [abstractions.md](abstractions.md) rule 6 ("Wrappers must add semantics, not just rename").
|
||||
|
||||
| Flavor | Create it when it differs in… |
|
||||
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `PostableX` | JSON shape differs from `X` — typically no `Id`, no audit fields, no server-computed fields. Often owns input validation via `Validate()` or a custom `UnmarshalJSON`. |
|
||||
| `GettableX` | Response shape adds server-computed fields that are not persisted — e.g., `GettableAuthDomain` adds `AuthNProviderInfo`, which is resolved at read time. |
|
||||
| `UpdatableX` | Only a strict subset of `PostableX` is replaceable on PUT. If the updatable shape equals `PostableX`, reuse `PostableX`. |
|
||||
| `StorableX` | DB row shape differs from `X` — usually `X` carries nested typed config while `StorableX` carries a flat `Data string` JSON column, plus bun tags, audit mixins, and an `OrgID`. If `X` already has those, skip the flavor. |
|
||||
|
||||
The failure mode this rule exists to prevent: minting all four flavors on reflex for every new resource, even when two or three are structurally identical. Each unnecessary flavor is another type contributors must understand and another conversion that can drift.
|
||||
|
||||
## Worked examples
|
||||
|
||||
### Channel — core type only
|
||||
|
||||
```go
|
||||
type Channels = []*Channel
|
||||
type GettableChannels = []*Channel
|
||||
|
||||
type Channel struct {
|
||||
bun.BaseModel `bun:"table:notification_channel"`
|
||||
types.Identifiable
|
||||
types.TimeAuditable
|
||||
Name string `json:"name" required:"true" bun:"name"`
|
||||
Type string `json:"type" required:"true" bun:"type"`
|
||||
Data string `json:"data" required:"true" bun:"data"`
|
||||
OrgID string `json:"orgId" required:"true" bun:"org_id"`
|
||||
}
|
||||
```
|
||||
|
||||
`Channel` is both the domain type and the bun row. `GettableChannels` is a **type alias** because `*Channel` already serializes correctly as a response. There is no `StorableChannel`, `PostableChannel`, or `UpdatableChannel` — those would be identical to `Channel` and so do not exist. Prefer this shape when it works.
|
||||
|
||||
### AuthDomain — all four flavors
|
||||
|
||||
```go
|
||||
type AuthDomain struct {
|
||||
storableAuthDomain *StorableAuthDomain
|
||||
authDomainConfig *AuthDomainConfig
|
||||
}
|
||||
|
||||
type StorableAuthDomain struct {
|
||||
bun.BaseModel `bun:"table:auth_domain"`
|
||||
types.Identifiable
|
||||
Name string `bun:"name"`
|
||||
Data string `bun:"data"` // AuthDomainConfig serialized as JSON
|
||||
OrgID valuer.UUID `bun:"org_id"`
|
||||
types.TimeAuditable
|
||||
}
|
||||
|
||||
type PostableAuthDomain struct {
|
||||
Config AuthDomainConfig `json:"config"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type UpdateableAuthDomain struct {
|
||||
Config AuthDomainConfig `json:"config"` // Name intentionally absent
|
||||
}
|
||||
|
||||
type GettableAuthDomain struct {
|
||||
*StorableAuthDomain
|
||||
*AuthDomainConfig
|
||||
AuthNProviderInfo *AuthNProviderInfo `json:"authNProviderInfo"`
|
||||
}
|
||||
```
|
||||
|
||||
Each flavor exists for a concrete reason:
|
||||
|
||||
- `StorableAuthDomain` stores the typed config as an opaque `Data string` column, so the schema does not need to migrate every time a config field is added.
|
||||
- `PostableAuthDomain` carries the config as a structured object (not a string) for the request.
|
||||
- `UpdateableAuthDomain` excludes `Name` because a domain's name cannot change after creation.
|
||||
- `GettableAuthDomain` adds `AuthNProviderInfo`, which is derived at read time and never persisted.
|
||||
|
||||
The core `AuthDomain` holds the two live halves — `storableAuthDomain` and `authDomainConfig` — and owns business methods such as `Update(config)`. Conversions use the `New<Output>From<Input>` form: `NewAuthDomainFromConfig`, `NewAuthDomainFromStorableAuthDomain`, `NewGettableAuthDomainFromAuthDomain`.
|
||||
|
||||
## Conventions that tie the flavors together
|
||||
|
||||
- **Conversions** use either a `New<Output>From<Input>` constructor — e.g. `NewChannelFromReceiver`, `NewGettableAuthDomainFromAuthDomain` — or a receiver-style `ToY()` method. Both forms coexist in the codebase; use whichever fits the call site.
|
||||
- **Validation belongs on the core type `X`.** Putting it on `X` means every write path — HTTP create, HTTP update, in-process migration, replay — runs the same checks. `Validate()` on `PostableX` is reserved for checks that are specific to the request shape and do not apply to `X`. `UnmarshalJSON` on `PostableX` is a separate tool that lives there because decoding only happens at the HTTP boundary — `PostableAuthDomain.UnmarshalJSON` rejecting a malformed domain name at decode time is the canonical example.
|
||||
|
||||
```go
|
||||
// Domain invariants: every write path re-runs these.
|
||||
func (x *X) Validate() error { ... }
|
||||
|
||||
// Request-shape-only: checks that do not apply once the value is persisted.
|
||||
func (p *PostableX) Validate() error { ... }
|
||||
```
|
||||
- **Type aliases, not wrappers**, when two shapes are identical. `type GettableChannels = []*Channel` is correct because it adds no semantics beyond the underlying type.
|
||||
- **Serialization tags** follow [handler.md](handler.md): `required:"true"` means the JSON key must be present, `nullable:"true"` is required on any slice or map that may serialize as `null`, and types with a fixed value set must implement `Enum() []any`.
|
||||
|
||||
## A note on `UpdatableX` and `PatchableX`
|
||||
|
||||
- `UpdatableX` — the body for PUT (full replace) when the shape is a strict subset of `PostableX`. If the updatable shape equals `PostableX`, reuse `PostableX`.
|
||||
- `PatchableX` — the body for PATCH (partial update); only the fields a client is allowed to patch. For example, `PatchableRole` carries a single `Description` field even though `Role` has many — clients may patch the description but not anything else.
|
||||
|
||||
```go
|
||||
type PatchableRole struct {
|
||||
Description string `json:"description"`
|
||||
}
|
||||
```
|
||||
|
||||
Both are optional. Do not introduce them if `PostableX` already covers the case.
|
||||
|
||||
## What to avoid
|
||||
|
||||
- **Do not mint a flavor that mirrors the core type.** If `StorableX` would have the same fields as `X`, use `X` directly with `bun.BaseModel` embedded. `Channel` is the canonical example.
|
||||
- **Do not bolt domain methods onto `StorableX`.** Storage types are data carriers. Domain methods live on `X`.
|
||||
- **Do not invent new suffixes** (`Creatable`, `Fetchable`, `Savable`). The core type plus `Postable` / `Gettable` / `Updatable` / `Patchable` / `Storable` covers every case that exists today.
|
||||
- **Spelling — `Updatable`, not `Updateable`.** `Updateable` is a common typo. Prefer the shorter form when introducing new types, and rename any stragglers you come across.
|
||||
- **Spelling — `Storable`, not `Storeable`.** `Storeable` is a common typo. Prefer the shorter form when introducing new types, and rename any stragglers you come across.
|
||||
|
||||
## What should I remember?
|
||||
|
||||
- Every domain package defines the core type `X`. Only `X` is mandatory.
|
||||
- Add `PostableX` / `GettableX` / `UpdatableX` / `StorableX` one at a time, only when the shape actually diverges from `X`.
|
||||
- Domain logic lives on `X`, not on the flavor types.
|
||||
- Conversions can be a `New<Output>From<Input>` constructor or a receiver-style `ToY()` method — pick whichever reads best at the call site.
|
||||
- Use a type alias when two shapes are truly identical.
|
||||
- `pkg/types/<domain>/` must not import from other `pkg/` packages.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [abstractions.md](abstractions.md) — when to introduce a new type at all.
|
||||
- [handler.md](handler.md) — struct tag rules at the HTTP boundary.
|
||||
- [packages.md](packages.md) — where types live under `pkg/types/`.
|
||||
- [sql.md](sql.md) — star-schema requirements for `StorableX`.
|
||||
@@ -19,11 +19,11 @@ func NewAWSCloudProvider(defStore cloudintegrationtypes.ServiceDefinitionStore)
|
||||
}
|
||||
|
||||
func (provider *awscloudprovider) GetConnectionArtifact(ctx context.Context, account *cloudintegrationtypes.Account, req *cloudintegrationtypes.GetConnectionArtifactRequest) (*cloudintegrationtypes.ConnectionArtifact, error) {
|
||||
baseURL := fmt.Sprintf(cloudintegrationtypes.CloudFormationQuickCreateBaseURL.StringValue(), req.Config.AWS.DeploymentRegion)
|
||||
baseURL := fmt.Sprintf(cloudintegrationtypes.CloudFormationQuickCreateBaseURL.StringValue(), req.Config.Aws.DeploymentRegion)
|
||||
u, _ := url.Parse(baseURL)
|
||||
|
||||
q := u.Query()
|
||||
q.Set("region", req.Config.AWS.DeploymentRegion)
|
||||
q.Set("region", req.Config.Aws.DeploymentRegion)
|
||||
u.Fragment = "/stacks/quickcreate"
|
||||
|
||||
u.RawQuery = q.Encode()
|
||||
@@ -39,7 +39,9 @@ func (provider *awscloudprovider) GetConnectionArtifact(ctx context.Context, acc
|
||||
q.Set("param_IngestionKey", req.Credentials.IngestionKey)
|
||||
|
||||
return &cloudintegrationtypes.ConnectionArtifact{
|
||||
AWS: cloudintegrationtypes.NewAWSConnectionArtifact(u.String() + "?&" + q.Encode()), // this format is required by AWS
|
||||
Aws: &cloudintegrationtypes.AWSConnectionArtifact{
|
||||
ConnectionURL: u.String() + "?&" + q.Encode(), // this format is required by AWS
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -122,6 +124,9 @@ func (provider *awscloudprovider) BuildIntegrationConfig(
|
||||
}
|
||||
|
||||
return &cloudintegrationtypes.ProviderIntegrationConfig{
|
||||
AWS: cloudintegrationtypes.NewAWSIntegrationConfig(account.Config.AWS.Regions, collectionStrategy),
|
||||
AWS: &cloudintegrationtypes.AWSIntegrationConfig{
|
||||
EnabledRegions: account.Config.AWS.Regions,
|
||||
TelemetryCollectionStrategy: collectionStrategy,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) {
|
||||
}
|
||||
|
||||
// start the usagemanager
|
||||
usageManager, err := usage.New(signoz.Licensing, signoz.TelemetryStore.ClickhouseDB(), signoz.Zeus, signoz.Modules.OrgGetter)
|
||||
usageManager, err := usage.New(signoz.Licensing, signoz.TelemetryStore.ClickhouseDB(), signoz.Zeus, signoz.Modules.OrgGetter, signoz.Flagger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/ee/query-service/model"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/flagger"
|
||||
"github.com/SigNoz/signoz/pkg/licensing"
|
||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/utils/encryption"
|
||||
"github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||
"github.com/SigNoz/signoz/pkg/zeus"
|
||||
)
|
||||
|
||||
@@ -43,15 +45,18 @@ type Manager struct {
|
||||
zeus zeus.Zeus
|
||||
|
||||
orgGetter organization.Getter
|
||||
|
||||
flagger flagger.Flagger
|
||||
}
|
||||
|
||||
func New(licenseService licensing.Licensing, clickhouseConn clickhouse.Conn, zeus zeus.Zeus, orgGetter organization.Getter) (*Manager, error) {
|
||||
func New(licenseService licensing.Licensing, clickhouseConn clickhouse.Conn, zeus zeus.Zeus, orgGetter organization.Getter, flagger flagger.Flagger) (*Manager, error) {
|
||||
m := &Manager{
|
||||
clickhouseConn: clickhouseConn,
|
||||
licenseService: licenseService,
|
||||
scheduler: gocron.NewScheduler(time.UTC).Every(1).Day().At("00:00"), // send usage every at 00:00 UTC
|
||||
zeus: zeus,
|
||||
orgGetter: orgGetter,
|
||||
flagger: flagger,
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
@@ -168,7 +173,14 @@ func (lm *Manager) UploadUsage(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
errv2 = lm.zeus.PutMeters(ctx, payload.LicenseKey.String(), body)
|
||||
evalCtx := featuretypes.NewFlaggerEvaluationContext(organization.ID)
|
||||
useZeus := lm.flagger.BooleanOrEmpty(ctx, flagger.FeaturePutMetersInZeus, evalCtx)
|
||||
|
||||
if useZeus {
|
||||
errv2 = lm.zeus.PutMetersV2(ctx, payload.LicenseKey.String(), body)
|
||||
} else {
|
||||
errv2 = lm.zeus.PutMeters(ctx, payload.LicenseKey.String(), body)
|
||||
}
|
||||
if errv2 != nil {
|
||||
slog.ErrorContext(ctx, "failed to upload usage", errors.Attr(errv2))
|
||||
// not returning error here since it is captured in the failed count
|
||||
|
||||
@@ -136,6 +136,18 @@ func (provider *Provider) PutMeters(ctx context.Context, key string, data []byte
|
||||
return err
|
||||
}
|
||||
|
||||
func (provider *Provider) PutMetersV2(ctx context.Context, key string, data []byte) error {
|
||||
_, err := provider.do(
|
||||
ctx,
|
||||
provider.config.URL.JoinPath("/v1/meters"),
|
||||
http.MethodPost,
|
||||
key,
|
||||
data,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (provider *Provider) PutProfile(ctx context.Context, key string, profile *zeustypes.PostableProfile) error {
|
||||
body, err := json.Marshal(profile)
|
||||
if err != nil {
|
||||
|
||||
@@ -66,6 +66,8 @@ module.exports = {
|
||||
rules: {
|
||||
// Asset migration — base-path safety
|
||||
'rulesdir/no-unsupported-asset-pattern': 'error',
|
||||
// Base-path safety — window.open and origin-concat patterns; upgrade to error coming PR
|
||||
'rulesdir/no-raw-absolute-path': 'warn',
|
||||
|
||||
// Code quality rules
|
||||
'prefer-const': 'error', // Enforces const for variables never reassigned
|
||||
|
||||
153
frontend/eslint-rules/no-raw-absolute-path.js
Normal file
153
frontend/eslint-rules/no-raw-absolute-path.js
Normal file
@@ -0,0 +1,153 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* ESLint rule: no-raw-absolute-path
|
||||
*
|
||||
* Catches patterns that break at runtime when the app is served from a
|
||||
* sub-path (e.g. /signoz/):
|
||||
*
|
||||
* 1. window.open(path, '_blank')
|
||||
* → use openInNewTab(path) which calls withBasePath internally
|
||||
*
|
||||
* 2. window.location.origin + path / `${window.location.origin}${path}`
|
||||
* → use getAbsoluteUrl(path)
|
||||
*
|
||||
* 3. frontendBaseUrl: window.location.origin (bare origin usage)
|
||||
* → use getBaseUrl() to include the base path
|
||||
*
|
||||
* 4. window.location.href = path
|
||||
* → use withBasePath(path) or navigate() for internal navigation
|
||||
*
|
||||
* External URLs (first arg starts with "http") are explicitly allowed.
|
||||
*/
|
||||
|
||||
function isOriginAccess(node) {
|
||||
return (
|
||||
node.type === 'MemberExpression' &&
|
||||
!node.computed &&
|
||||
node.property.name === 'origin' &&
|
||||
node.object.type === 'MemberExpression' &&
|
||||
!node.object.computed &&
|
||||
node.object.property.name === 'location' &&
|
||||
node.object.object.type === 'Identifier' &&
|
||||
node.object.object.name === 'window'
|
||||
);
|
||||
}
|
||||
|
||||
function isHrefAccess(node) {
|
||||
return (
|
||||
node.type === 'MemberExpression' &&
|
||||
!node.computed &&
|
||||
node.property.name === 'href' &&
|
||||
node.object.type === 'MemberExpression' &&
|
||||
!node.object.computed &&
|
||||
node.object.property.name === 'location' &&
|
||||
node.object.object.type === 'Identifier' &&
|
||||
node.object.object.name === 'window'
|
||||
);
|
||||
}
|
||||
|
||||
function isExternalUrl(node) {
|
||||
if (node.type === 'Literal' && typeof node.value === 'string') {
|
||||
return node.value.startsWith('http://') || node.value.startsWith('https://');
|
||||
}
|
||||
if (node.type === 'TemplateLiteral' && node.quasis.length > 0) {
|
||||
const raw = node.quasis[0].value.raw;
|
||||
return raw.startsWith('http://') || raw.startsWith('https://');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// window.open(withBasePath(x)) and window.open(getAbsoluteUrl(x)) are already safe.
|
||||
function isSafeHelperCall(node) {
|
||||
return (
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.type === 'Identifier' &&
|
||||
(node.callee.name === 'withBasePath' || node.callee.name === 'getAbsoluteUrl')
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description:
|
||||
'Disallow raw window.open and origin-concatenation patterns that miss the runtime base path',
|
||||
category: 'Base Path Safety',
|
||||
},
|
||||
schema: [],
|
||||
messages: {
|
||||
windowOpen:
|
||||
'Use openInNewTab(path) instead of window.open(path, "_blank") — openInNewTab prepends the base path automatically.',
|
||||
originConcat:
|
||||
'Use getAbsoluteUrl(path) instead of window.location.origin + path — getAbsoluteUrl prepends the base path automatically.',
|
||||
originDirect:
|
||||
'Use getBaseUrl() instead of window.location.origin — getBaseUrl includes the base path.',
|
||||
hrefAssign:
|
||||
'Use withBasePath(path) or navigate() instead of window.location.href = path — ensures the base path is included.',
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
return {
|
||||
// window.open(path, ...) — allow only external first-arg URLs
|
||||
CallExpression(node) {
|
||||
const { callee, arguments: args } = node;
|
||||
if (
|
||||
callee.type !== 'MemberExpression' ||
|
||||
callee.object.type !== 'Identifier' ||
|
||||
callee.object.name !== 'window' ||
|
||||
callee.property.name !== 'open'
|
||||
)
|
||||
return;
|
||||
if (args.length < 1) return;
|
||||
if (isExternalUrl(args[0])) return;
|
||||
if (isSafeHelperCall(args[0])) return;
|
||||
|
||||
context.report({ node, messageId: 'windowOpen' });
|
||||
},
|
||||
|
||||
// window.location.origin + path
|
||||
BinaryExpression(node) {
|
||||
if (node.operator !== '+') return;
|
||||
if (isOriginAccess(node.left) || isOriginAccess(node.right)) {
|
||||
context.report({ node, messageId: 'originConcat' });
|
||||
}
|
||||
},
|
||||
|
||||
// `${window.location.origin}${path}`
|
||||
TemplateLiteral(node) {
|
||||
if (node.expressions.some(isOriginAccess)) {
|
||||
context.report({ node, messageId: 'originConcat' });
|
||||
}
|
||||
},
|
||||
|
||||
// window.location.origin used directly (not in concatenation)
|
||||
// Catches: frontendBaseUrl: window.location.origin
|
||||
MemberExpression(node) {
|
||||
if (!isOriginAccess(node)) return;
|
||||
|
||||
const parent = node.parent;
|
||||
// Skip if parent is BinaryExpression with + (handled by BinaryExpression visitor)
|
||||
if (parent.type === 'BinaryExpression' && parent.operator === '+') return;
|
||||
// Skip if inside TemplateLiteral (handled by TemplateLiteral visitor)
|
||||
if (parent.type === 'TemplateLiteral') return;
|
||||
|
||||
context.report({ node, messageId: 'originDirect' });
|
||||
},
|
||||
|
||||
// window.location.href = path
|
||||
AssignmentExpression(node) {
|
||||
if (node.operator !== '=') return;
|
||||
if (!isHrefAccess(node.left)) return;
|
||||
|
||||
// Allow external URLs
|
||||
if (isExternalUrl(node.right)) return;
|
||||
// Allow safe helper calls
|
||||
if (isSafeHelperCall(node.right)) return;
|
||||
|
||||
context.report({ node, messageId: 'hrefAssign' });
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<base href="[[.BaseHref]]" />
|
||||
<meta
|
||||
http-equiv="Cache-Control"
|
||||
content="no-cache, no-store, must-revalidate, max-age: 0"
|
||||
@@ -59,7 +60,7 @@
|
||||
<meta data-react-helmet="true" name="docusaurus_locale" content="en" />
|
||||
<meta data-react-helmet="true" name="docusaurus_tag" content="default" />
|
||||
<meta name="robots" content="noindex" />
|
||||
<link data-react-helmet="true" rel="shortcut icon" href="/favicon.ico" />
|
||||
<link data-react-helmet="true" rel="shortcut icon" href="favicon.ico" />
|
||||
</head>
|
||||
<body data-theme="default">
|
||||
<script>
|
||||
@@ -136,7 +137,7 @@
|
||||
})(document, 'script');
|
||||
}
|
||||
</script>
|
||||
<link rel="stylesheet" href="/css/uPlot.min.css" />
|
||||
<link rel="stylesheet" href="css/uPlot.min.css" />
|
||||
<script type="module" src="./src/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { initReactI18next } from 'react-i18next';
|
||||
import i18n from 'i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import Backend from 'i18next-http-backend';
|
||||
import { getBasePath } from 'utils/basePath';
|
||||
|
||||
import cacheBursting from '../../i18n-translations-hash.json';
|
||||
|
||||
@@ -24,7 +25,7 @@ i18n
|
||||
const ns = namespace[0];
|
||||
const pathkey = `/${language}/${ns}`;
|
||||
const hash = cacheBursting[pathkey as keyof typeof cacheBursting] || '';
|
||||
return `/locales/${language}/${namespace}.json?h=${hash}`;
|
||||
return `${getBasePath()}locales/${language}/${namespace}.json?h=${hash}`;
|
||||
},
|
||||
},
|
||||
react: {
|
||||
|
||||
@@ -795,8 +795,7 @@ export interface CloudintegrationtypesAccountDTO {
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAccountConfigDTO {
|
||||
aws?: CloudintegrationtypesAWSAccountConfigDTO;
|
||||
azure?: CloudintegrationtypesAzureAccountConfigDTO;
|
||||
aws: CloudintegrationtypesAWSAccountConfigDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -830,82 +829,6 @@ export interface CloudintegrationtypesAssetsDTO {
|
||||
dashboards?: CloudintegrationtypesDashboardDTO[] | null;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAzureAccountConfigDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
deploymentRegion: string;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
resourceGroups: string[];
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAzureConnectionArtifactDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
cliCommand: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
cloudShellCommand: string;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAzureIntegrationConfigDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
deploymentRegion: string;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
resourceGroups: string[];
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
telemetryCollectionStrategy: CloudintegrationtypesAzureTelemetryCollectionStrategyDTO[];
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAzureLogsCollectionStrategyDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
categoryGroups: string[];
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAzureMetricsCollectionStrategyDTO {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAzureServiceConfigDTO {
|
||||
logs: CloudintegrationtypesAzureServiceLogsConfigDTO;
|
||||
metrics: CloudintegrationtypesAzureServiceMetricsConfigDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAzureServiceLogsConfigDTO {
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAzureServiceMetricsConfigDTO {
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAzureTelemetryCollectionStrategyDTO {
|
||||
logs?: CloudintegrationtypesAzureLogsCollectionStrategyDTO;
|
||||
metrics?: CloudintegrationtypesAzureMetricsCollectionStrategyDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
resourceType: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
@@ -967,8 +890,7 @@ export interface CloudintegrationtypesCollectedMetricDTO {
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesConnectionArtifactDTO {
|
||||
aws?: CloudintegrationtypesAWSConnectionArtifactDTO;
|
||||
azure?: CloudintegrationtypesAzureConnectionArtifactDTO;
|
||||
aws: CloudintegrationtypesAWSConnectionArtifactDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesCredentialsDTO {
|
||||
@@ -1150,8 +1072,7 @@ export interface CloudintegrationtypesPostableAccountDTO {
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesPostableAccountConfigDTO {
|
||||
aws?: CloudintegrationtypesAWSPostableAccountConfigDTO;
|
||||
azure?: CloudintegrationtypesAzureAccountConfigDTO;
|
||||
aws: CloudintegrationtypesAWSPostableAccountConfigDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1186,8 +1107,7 @@ export interface CloudintegrationtypesPostableAgentCheckInDTO {
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesProviderIntegrationConfigDTO {
|
||||
aws?: CloudintegrationtypesAWSIntegrationConfigDTO;
|
||||
azure?: CloudintegrationtypesAzureIntegrationConfigDTO;
|
||||
aws: CloudintegrationtypesAWSIntegrationConfigDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesServiceDTO {
|
||||
@@ -1215,8 +1135,7 @@ export interface CloudintegrationtypesServiceDTO {
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesServiceConfigDTO {
|
||||
aws?: CloudintegrationtypesAWSServiceConfigDTO;
|
||||
azure?: CloudintegrationtypesAzureServiceConfigDTO;
|
||||
aws: CloudintegrationtypesAWSServiceConfigDTO;
|
||||
}
|
||||
|
||||
export enum CloudintegrationtypesServiceIDDTO {
|
||||
@@ -1233,8 +1152,6 @@ export enum CloudintegrationtypesServiceIDDTO {
|
||||
s3sync = 's3sync',
|
||||
sns = 'sns',
|
||||
sqs = 'sqs',
|
||||
blobstorage = 'blobstorage',
|
||||
frontdoor = 'frontdoor',
|
||||
}
|
||||
export interface CloudintegrationtypesServiceMetadataDTO {
|
||||
/**
|
||||
@@ -1267,24 +1184,11 @@ export interface CloudintegrationtypesSupportedSignalsDTO {
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesTelemetryCollectionStrategyDTO {
|
||||
aws?: CloudintegrationtypesAWSTelemetryCollectionStrategyDTO;
|
||||
azure?: CloudintegrationtypesAzureTelemetryCollectionStrategyDTO;
|
||||
aws: CloudintegrationtypesAWSTelemetryCollectionStrategyDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesUpdatableAccountDTO {
|
||||
config: CloudintegrationtypesUpdatableAccountConfigDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesUpdatableAccountConfigDTO {
|
||||
aws?: CloudintegrationtypesAWSAccountConfigDTO;
|
||||
azure?: CloudintegrationtypesUpdatableAzureAccountConfigDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesUpdatableAzureAccountConfigDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
resourceGroups: string[];
|
||||
config: CloudintegrationtypesAccountConfigDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesUpdatableServiceDTO {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
interceptorRejected,
|
||||
interceptorsRequestBasePath,
|
||||
interceptorsRequestResponse,
|
||||
interceptorsResponse,
|
||||
} from 'api';
|
||||
@@ -17,6 +18,7 @@ export const GeneratedAPIInstance = <T>(
|
||||
return generatedAPIAxiosInstance({ ...config }).then(({ data }) => data);
|
||||
};
|
||||
|
||||
generatedAPIAxiosInstance.interceptors.request.use(interceptorsRequestBasePath);
|
||||
generatedAPIAxiosInstance.interceptors.request.use(interceptorsRequestResponse);
|
||||
generatedAPIAxiosInstance.interceptors.response.use(
|
||||
interceptorsResponse,
|
||||
|
||||
@@ -11,6 +11,7 @@ import axios, {
|
||||
import { ENVIRONMENT } from 'constants/env';
|
||||
import { Events } from 'constants/events';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { getBasePath } from 'utils/basePath';
|
||||
import { eventEmitter } from 'utils/getEventEmitter';
|
||||
|
||||
import apiV1, { apiAlertManager, apiV2, apiV3, apiV4, apiV5 } from './apiV1';
|
||||
@@ -67,6 +68,39 @@ export const interceptorsRequestResponse = (
|
||||
return value;
|
||||
};
|
||||
|
||||
// Strips the leading '/' from path and joins with base — idempotent if already prefixed.
|
||||
// e.g. prependBase('/signoz/', '/api/v1/') → '/signoz/api/v1/'
|
||||
function prependBase(base: string, path: string): string {
|
||||
return path.startsWith(base) ? path : base + path.slice(1);
|
||||
}
|
||||
|
||||
// Prepends the runtime base path to outgoing requests so API calls work under
|
||||
// a URL prefix (e.g. /signoz/api/v1/…). No-op for root deployments and dev
|
||||
// (dev baseURL is a full http:// URL, not an absolute path).
|
||||
export const interceptorsRequestBasePath = (
|
||||
value: InternalAxiosRequestConfig,
|
||||
): InternalAxiosRequestConfig => {
|
||||
const basePath = getBasePath();
|
||||
if (basePath === '/') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value.baseURL?.startsWith('/')) {
|
||||
// Production relative baseURL: '/api/v1/' → '/signoz/api/v1/'
|
||||
value.baseURL = prependBase(basePath, value.baseURL);
|
||||
} else if (value.baseURL?.startsWith('http')) {
|
||||
// Dev absolute baseURL (VITE_FRONTEND_API_ENDPOINT): 'https://host/api/v1/' → 'https://host/signoz/api/v1/'
|
||||
const url = new URL(value.baseURL);
|
||||
url.pathname = prependBase(basePath, url.pathname);
|
||||
value.baseURL = url.toString();
|
||||
} else if (!value.baseURL && value.url?.startsWith('/')) {
|
||||
// Orval-generated client (empty baseURL, path in url): '/api/signoz/v1/rules' → '/signoz/api/signoz/v1/rules'
|
||||
value.url = prependBase(basePath, value.url);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export const interceptorRejected = async (
|
||||
value: AxiosResponse<any>,
|
||||
): Promise<AxiosResponse<any>> => {
|
||||
@@ -133,6 +167,7 @@ const instance = axios.create({
|
||||
});
|
||||
|
||||
instance.interceptors.request.use(interceptorsRequestResponse);
|
||||
instance.interceptors.request.use(interceptorsRequestBasePath);
|
||||
instance.interceptors.response.use(interceptorsResponse, interceptorRejected);
|
||||
|
||||
export const AxiosAlertManagerInstance = axios.create({
|
||||
@@ -147,6 +182,7 @@ ApiV2Instance.interceptors.response.use(
|
||||
interceptorRejected,
|
||||
);
|
||||
ApiV2Instance.interceptors.request.use(interceptorsRequestResponse);
|
||||
ApiV2Instance.interceptors.request.use(interceptorsRequestBasePath);
|
||||
|
||||
// axios V3
|
||||
export const ApiV3Instance = axios.create({
|
||||
@@ -158,6 +194,7 @@ ApiV3Instance.interceptors.response.use(
|
||||
interceptorRejected,
|
||||
);
|
||||
ApiV3Instance.interceptors.request.use(interceptorsRequestResponse);
|
||||
ApiV3Instance.interceptors.request.use(interceptorsRequestBasePath);
|
||||
//
|
||||
|
||||
// axios V4
|
||||
@@ -170,6 +207,7 @@ ApiV4Instance.interceptors.response.use(
|
||||
interceptorRejected,
|
||||
);
|
||||
ApiV4Instance.interceptors.request.use(interceptorsRequestResponse);
|
||||
ApiV4Instance.interceptors.request.use(interceptorsRequestBasePath);
|
||||
//
|
||||
|
||||
// axios V5
|
||||
@@ -182,6 +220,7 @@ ApiV5Instance.interceptors.response.use(
|
||||
interceptorRejected,
|
||||
);
|
||||
ApiV5Instance.interceptors.request.use(interceptorsRequestResponse);
|
||||
ApiV5Instance.interceptors.request.use(interceptorsRequestBasePath);
|
||||
//
|
||||
|
||||
// axios Base
|
||||
@@ -194,6 +233,7 @@ LogEventAxiosInstance.interceptors.response.use(
|
||||
interceptorRejectedBase,
|
||||
);
|
||||
LogEventAxiosInstance.interceptors.request.use(interceptorsRequestResponse);
|
||||
LogEventAxiosInstance.interceptors.request.use(interceptorsRequestBasePath);
|
||||
//
|
||||
|
||||
AxiosAlertManagerInstance.interceptors.response.use(
|
||||
@@ -201,6 +241,7 @@ AxiosAlertManagerInstance.interceptors.response.use(
|
||||
interceptorRejected,
|
||||
);
|
||||
AxiosAlertManagerInstance.interceptors.request.use(interceptorsRequestResponse);
|
||||
AxiosAlertManagerInstance.interceptors.request.use(interceptorsRequestBasePath);
|
||||
|
||||
export { apiV1 };
|
||||
export default instance;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { AppState } from 'store/reducers';
|
||||
import { Query, TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource, MetricAggregateOperator } from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { withBasePath } from 'utils/basePath';
|
||||
|
||||
export interface NavigateToExplorerProps {
|
||||
filters: TagFilterItem[];
|
||||
@@ -133,7 +134,7 @@ export function useNavigateToExplorer(): (
|
||||
QueryParams.compositeQuery
|
||||
}=${JSONCompositeQuery}`;
|
||||
|
||||
window.open(newExplorerPath, sameTab ? '_self' : '_blank');
|
||||
window.open(withBasePath(newExplorerPath), sameTab ? '_self' : '_blank');
|
||||
},
|
||||
[
|
||||
prepareQuery,
|
||||
|
||||
@@ -13,6 +13,7 @@ import GetMinMax from 'lib/getMinMax';
|
||||
import { Check, Info, Link2 } from 'lucide-react';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { getAbsoluteUrl } from 'utils/basePath';
|
||||
|
||||
const routesToBeSharedWithTime = [
|
||||
ROUTES.LOGS_EXPLORER,
|
||||
@@ -80,17 +81,13 @@ function ShareURLModal(): JSX.Element {
|
||||
|
||||
urlQuery.delete(QueryParams.relativeTime);
|
||||
|
||||
currentUrl = `${window.location.origin}${
|
||||
location.pathname
|
||||
}?${urlQuery.toString()}`;
|
||||
currentUrl = getAbsoluteUrl(`${location.pathname}?${urlQuery.toString()}`);
|
||||
} else {
|
||||
urlQuery.delete(QueryParams.startTime);
|
||||
urlQuery.delete(QueryParams.endTime);
|
||||
|
||||
urlQuery.set(QueryParams.relativeTime, selectedTime);
|
||||
currentUrl = `${window.location.origin}${
|
||||
location.pathname
|
||||
}?${urlQuery.toString()}`;
|
||||
currentUrl = getAbsoluteUrl(`${location.pathname}?${urlQuery.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
TracesAggregatorOperator,
|
||||
} from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { VIEW_TYPES, VIEWS } from './constants';
|
||||
@@ -330,10 +331,7 @@ function HostMetricsDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`);
|
||||
} else if (selectedView === VIEW_TYPES.TRACES) {
|
||||
const compositeQuery = {
|
||||
...initialQueryState,
|
||||
@@ -352,10 +350,7 @@ function HostMetricsDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from 'container/ApiMonitoring/utils';
|
||||
import { UnfoldVertical } from 'lucide-react';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
|
||||
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
|
||||
|
||||
@@ -94,20 +95,14 @@ function DependentServices({
|
||||
}}
|
||||
onRow={(record): { onClick: () => void; className: string } => ({
|
||||
onClick: (): void => {
|
||||
const url = new URL(
|
||||
`/services/${
|
||||
record.serviceData.serviceName &&
|
||||
record.serviceData.serviceName !== '-'
|
||||
? record.serviceData.serviceName
|
||||
: ''
|
||||
}`,
|
||||
window.location.origin,
|
||||
);
|
||||
const serviceName =
|
||||
record.serviceData.serviceName && record.serviceData.serviceName !== '-'
|
||||
? record.serviceData.serviceName
|
||||
: '';
|
||||
const urlQuery = new URLSearchParams();
|
||||
urlQuery.set(QueryParams.startTime, timeRange.startTime.toString());
|
||||
urlQuery.set(QueryParams.endTime, timeRange.endTime.toString());
|
||||
url.search = urlQuery.toString();
|
||||
window.open(url.toString(), '_blank');
|
||||
openInNewTab(`/services/${serviceName}?${urlQuery.toString()}`);
|
||||
},
|
||||
className: 'clickable-row',
|
||||
})}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { IUser } from 'providers/App/types';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
|
||||
import { ROUTING_POLICIES_ROUTE } from './constants';
|
||||
import { RoutingPolicyBannerProps } from './types';
|
||||
@@ -387,7 +388,7 @@ export function NotificationChannelsNotFoundContent({
|
||||
style={{ padding: '0 4px' }}
|
||||
type="link"
|
||||
onClick={(): void => {
|
||||
window.open(ROUTES.CHANNELS_NEW, '_blank');
|
||||
openInNewTab(ROUTES.CHANNELS_NEW);
|
||||
}}
|
||||
>
|
||||
here.
|
||||
|
||||
@@ -15,6 +15,7 @@ import { AlertDef, Labels } from 'types/api/alerts/def';
|
||||
import { Channels } from 'types/api/channels/getAll';
|
||||
import APIError from 'types/api/error';
|
||||
import { requireErrorMessage } from 'utils/form/requireErrorMessage';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import { popupContainer } from 'utils/selectPopupContainer';
|
||||
|
||||
import ChannelSelect from './ChannelSelect';
|
||||
@@ -87,7 +88,7 @@ function BasicInfo({
|
||||
dataSource: ALERTS_DATA_SOURCE_MAP[alertDef?.alertType as AlertTypes],
|
||||
ruleId: isNewRule ? 0 : alertDef?.id,
|
||||
});
|
||||
window.open(ROUTES.CHANNELS_NEW, '_blank');
|
||||
openInNewTab(ROUTES.CHANNELS_NEW);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
const hasLoggedEvent = useRef(false);
|
||||
|
||||
@@ -49,6 +49,7 @@ import {
|
||||
TracesAggregatorOperator,
|
||||
} from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import ClusterEvents from '../../EntityDetailsUtils/EntityEvents';
|
||||
@@ -414,10 +415,7 @@ function ClusterDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`);
|
||||
} else if (selectedView === VIEW_TYPES.TRACES) {
|
||||
const compositeQuery = {
|
||||
...initialQueryState,
|
||||
@@ -436,10 +434,7 @@ function ClusterDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ import {
|
||||
TracesAggregatorOperator,
|
||||
} from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import DaemonSetEvents from '../../EntityDetailsUtils/EntityEvents';
|
||||
@@ -429,10 +430,7 @@ function DaemonSetDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`);
|
||||
} else if (selectedView === VIEW_TYPES.TRACES) {
|
||||
const compositeQuery = {
|
||||
...initialQueryState,
|
||||
@@ -451,10 +449,7 @@ function DaemonSetDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
TracesAggregatorOperator,
|
||||
} from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import DeploymentEvents from '../../EntityDetailsUtils/EntityEvents';
|
||||
@@ -433,10 +434,7 @@ function DeploymentDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`);
|
||||
} else if (selectedView === VIEW_TYPES.TRACES) {
|
||||
const compositeQuery = {
|
||||
...initialQueryState,
|
||||
@@ -455,10 +453,7 @@ function DeploymentDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ import {
|
||||
TracesAggregatorOperator,
|
||||
} from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import JobEvents from '../../EntityDetailsUtils/EntityEvents';
|
||||
@@ -427,10 +428,7 @@ function JobDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`);
|
||||
} else if (selectedView === VIEW_TYPES.TRACES) {
|
||||
const compositeQuery = {
|
||||
...initialQueryState,
|
||||
@@ -449,10 +447,7 @@ function JobDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
TracesAggregatorOperator,
|
||||
} from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import NamespaceEvents from '../../EntityDetailsUtils/EntityEvents';
|
||||
@@ -419,10 +420,7 @@ function NamespaceDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`);
|
||||
} else if (selectedView === VIEW_TYPES.TRACES) {
|
||||
const compositeQuery = {
|
||||
...initialQueryState,
|
||||
@@ -441,10 +439,7 @@ function NamespaceDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
TracesAggregatorOperator,
|
||||
} from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import NodeLogs from '../../EntityDetailsUtils/EntityLogs';
|
||||
@@ -416,10 +417,7 @@ function NodeDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`);
|
||||
} else if (selectedView === VIEW_TYPES.TRACES) {
|
||||
const compositeQuery = {
|
||||
...initialQueryState,
|
||||
@@ -438,10 +436,7 @@ function NodeDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
TracesAggregatorOperator,
|
||||
} from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import PodEvents from '../../EntityDetailsUtils/EntityEvents';
|
||||
@@ -435,10 +436,7 @@ function PodDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`);
|
||||
} else if (selectedView === VIEW_TYPES.TRACES) {
|
||||
const compositeQuery = {
|
||||
...initialQueryState,
|
||||
@@ -457,10 +455,7 @@ function PodDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ import {
|
||||
TracesAggregatorOperator,
|
||||
} from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import {
|
||||
@@ -431,10 +432,7 @@ function StatefulSetDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`);
|
||||
} else if (selectedView === VIEW_TYPES.TRACES) {
|
||||
const compositeQuery = {
|
||||
...initialQueryState,
|
||||
@@ -453,10 +451,7 @@ function StatefulSetDetails({
|
||||
|
||||
urlQuery.set('compositeQuery', JSON.stringify(compositeQuery));
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`,
|
||||
'_blank',
|
||||
);
|
||||
openInNewTab(`${ROUTES.TRACES_EXPLORER}?${urlQuery.toString()}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -83,6 +83,8 @@ import {
|
||||
} from 'types/api/dashboard/getAll';
|
||||
import APIError from 'types/api/error';
|
||||
import { isModifierKeyPressed } from 'utils/app';
|
||||
import { getAbsoluteUrl } from 'utils/basePath';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
|
||||
import awwSnapUrl from '@/assets/Icons/awwSnap.svg';
|
||||
import dashboardsUrl from '@/assets/Icons/dashboards.svg';
|
||||
@@ -457,7 +459,7 @@ function DashboardsList(): JSX.Element {
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
window.open(getLink(), '_blank');
|
||||
openInNewTab(getLink());
|
||||
}}
|
||||
>
|
||||
Open in New Tab
|
||||
@@ -469,7 +471,7 @@ function DashboardsList(): JSX.Element {
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
setCopy(`${window.location.origin}${getLink()}`);
|
||||
setCopy(getAbsoluteUrl(getLink()));
|
||||
}}
|
||||
>
|
||||
Copy Link
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { LockFilled } from '@ant-design/icons';
|
||||
import ROUTES from 'constants/routes';
|
||||
import history from 'lib/history';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
|
||||
import { Data } from '../DashboardsList';
|
||||
import { TableLinkText } from './styles';
|
||||
@@ -12,7 +13,7 @@ function Name(name: Data['name'], data: Data): JSX.Element {
|
||||
|
||||
const onClickHandler = (event: React.MouseEvent<HTMLElement>): void => {
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
window.open(getLink(), '_blank');
|
||||
openInNewTab(getLink());
|
||||
} else {
|
||||
history.push(getLink());
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource, StringOperators } from 'types/common/queryBuilder';
|
||||
import { withBasePath } from 'utils/basePath';
|
||||
|
||||
import { useContextLogData } from './useContextLogData';
|
||||
|
||||
@@ -116,7 +117,7 @@ function ContextLogRenderer({
|
||||
);
|
||||
|
||||
const link = `${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`;
|
||||
window.open(link, '_blank', 'noopener,noreferrer');
|
||||
window.open(withBasePath(link), '_blank', 'noopener,noreferrer');
|
||||
},
|
||||
[query, urlQuery],
|
||||
);
|
||||
|
||||
@@ -34,6 +34,7 @@ import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
|
||||
import { IField } from 'types/api/logs/fields';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
|
||||
import { ActionItemProps } from './ActionItem';
|
||||
import FieldRenderer from './FieldRenderer';
|
||||
@@ -191,7 +192,7 @@ function TableView({
|
||||
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
// open the trace in new tab
|
||||
window.open(route, '_blank');
|
||||
openInNewTab(route);
|
||||
} else {
|
||||
history.push(route);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import ROUTES from 'constants/routes';
|
||||
import { useActiveLog } from 'hooks/logs/useActiveLog';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import useDragColumns from 'hooks/useDragColumns';
|
||||
import { getAbsoluteUrl } from 'utils/basePath';
|
||||
|
||||
import { infinityDefaultStyles } from '../InfinityTableView/config';
|
||||
import { TanStackTableStyled } from '../InfinityTableView/styles';
|
||||
@@ -239,7 +240,7 @@ const TanStackTableView = forwardRef<TableVirtuosoHandle, InfinityTableProps>(
|
||||
urlQuery.delete(QueryParams.activeLogId);
|
||||
urlQuery.delete(QueryParams.relativeTime);
|
||||
urlQuery.set(QueryParams.activeLogId, `"${logId}"`);
|
||||
const link = `${window.location.origin}${pathname}?${urlQuery.toString()}`;
|
||||
const link = getAbsoluteUrl(`${pathname}?${urlQuery.toString()}`);
|
||||
|
||||
setCopy(link);
|
||||
toast.success('Copied to clipboard', { position: 'top-right' });
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { QueryParams } from 'constants/query';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { withBasePath } from 'utils/basePath';
|
||||
|
||||
import { TopOperationList } from './TopOperationsTable';
|
||||
import { NavigateToTraceProps } from './types';
|
||||
@@ -37,7 +38,7 @@ export const navigateToTrace = ({
|
||||
}=${JSONCompositeQuery}`;
|
||||
|
||||
if (openInNewTab) {
|
||||
window.open(newTraceExplorerPath, '_blank');
|
||||
window.open(withBasePath(newTraceExplorerPath), '_blank');
|
||||
} else {
|
||||
safeNavigate(newTraceExplorerPath);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import ContextMenu from 'periscope/components/ContextMenu';
|
||||
import { useDashboardStore } from 'providers/Dashboard/store/useDashboardStore';
|
||||
import { ContextLinksData } from 'types/api/dashboard/getAll';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
|
||||
import { ContextMenuItem } from './contextConfig';
|
||||
import { getDataLinks } from './dataLinksUtils';
|
||||
@@ -115,7 +116,7 @@ const useBaseAggregateOptions = ({
|
||||
key={id}
|
||||
icon={<LinkOutlined />}
|
||||
onClick={(): void => {
|
||||
window.open(url, '_blank');
|
||||
openInNewTab(url);
|
||||
onClose?.();
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ModalTitle } from 'container/PipelinePage/PipelineListsView/styles';
|
||||
import { Check, Loader, X } from 'lucide-react';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
|
||||
import { INITIAL_ROUTING_POLICY_DETAILS_FORM_STATE } from './constants';
|
||||
import {
|
||||
@@ -76,7 +77,7 @@ function RoutingPolicyDetails({
|
||||
style={{ padding: '0 4px' }}
|
||||
type="link"
|
||||
onClick={(): void => {
|
||||
window.open(ROUTES.CHANNELS_NEW, '_blank');
|
||||
openInNewTab(ROUTES.CHANNELS_NEW);
|
||||
}}
|
||||
>
|
||||
here.
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import noDataUrl from '@/assets/Icons/no-data.svg';
|
||||
@@ -143,7 +144,7 @@ function SpanLogs({
|
||||
|
||||
const url = `${ROUTES.LOGS_EXPLORER}?${createQueryParams(queryParams)}`;
|
||||
|
||||
window.open(url, '_blank');
|
||||
openInNewTab(url);
|
||||
},
|
||||
[
|
||||
isLogSpanRelated,
|
||||
|
||||
@@ -17,6 +17,7 @@ import { BarChart2, Compass, X } from 'lucide-react';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { Span } from 'types/api/trace/getTraceV2';
|
||||
import { DataSource, LogsAggregatorOperator } from 'types/common/queryBuilder';
|
||||
import { getAbsoluteUrl } from 'utils/basePath';
|
||||
|
||||
import { RelatedSignalsViews } from '../constants';
|
||||
import SpanLogs from '../SpanLogs/SpanLogs';
|
||||
@@ -158,9 +159,7 @@ function SpanRelatedSignals({
|
||||
searchParams.set(QueryParams.endTime, endTimeMs.toString());
|
||||
|
||||
window.open(
|
||||
`${window.location.origin}${
|
||||
ROUTES.LOGS_EXPLORER
|
||||
}?${searchParams.toString()}`,
|
||||
getAbsoluteUrl(`${ROUTES.LOGS_EXPLORER}?${searchParams.toString()}`),
|
||||
'_blank',
|
||||
'noopener,noreferrer',
|
||||
);
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
UPDATE_SPANS_AGGREGATE_PAGE_SIZE,
|
||||
} from 'types/actions/trace';
|
||||
import { TraceReducer } from 'types/reducer/trace';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
dayjs.extend(duration);
|
||||
@@ -214,7 +215,7 @@ function TraceTable(): JSX.Element {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
window.open(getLink(record), '_blank');
|
||||
openInNewTab(getLink(record));
|
||||
} else {
|
||||
history.push(getLink(record));
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { useTimezone } from 'providers/Timezone';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
|
||||
import './TracesTableComponent.styles.scss';
|
||||
|
||||
@@ -86,7 +87,7 @@ function TracesTableComponent({
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
window.open(getTraceLink(record), '_blank');
|
||||
openInNewTab(getTraceLink(record));
|
||||
} else {
|
||||
history.push(getTraceLink(record));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { getAbsoluteUrl } from 'utils/basePath';
|
||||
|
||||
import { HIGHLIGHTED_DELAY } from './configs';
|
||||
import { UseCopyLogLink } from './types';
|
||||
@@ -60,7 +61,7 @@ export const useCopyLogLink = (logId?: string): UseCopyLogLink => {
|
||||
urlQuery.set(QueryParams.startTime, minTime?.toString() || '');
|
||||
urlQuery.set(QueryParams.endTime, maxTime?.toString() || '');
|
||||
|
||||
const link = `${window.location.origin}${pathname}?${urlQuery.toString()}`;
|
||||
const link = getAbsoluteUrl(`${pathname}?${urlQuery.toString()}`);
|
||||
|
||||
setCopy(link);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useCopyToClipboard } from 'react-use';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { Span } from 'types/api/trace/getTraceV2';
|
||||
import { getAbsoluteUrl } from 'utils/basePath';
|
||||
|
||||
export const useCopySpanLink = (
|
||||
span?: Span,
|
||||
@@ -28,7 +29,7 @@ export const useCopySpanLink = (
|
||||
urlQuery.set('spanId', span?.spanId);
|
||||
}
|
||||
|
||||
const link = `${window.location.origin}${pathname}?${urlQuery.toString()}`;
|
||||
const link = getAbsoluteUrl(`${pathname}?${urlQuery.toString()}`);
|
||||
|
||||
setCopy(link);
|
||||
notifications.success({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom-v5-compat';
|
||||
import { cloneDeep, isEqual } from 'lodash-es';
|
||||
import { withBasePath } from 'utils/basePath';
|
||||
|
||||
interface NavigateOptions {
|
||||
replace?: boolean;
|
||||
@@ -130,7 +131,7 @@ export const useSafeNavigate = (
|
||||
typeof to === 'string'
|
||||
? to
|
||||
: `${to.pathname || location.pathname}${to.search || ''}`;
|
||||
window.open(targetPath, '_blank');
|
||||
window.open(withBasePath(targetPath), '_blank');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createBrowserHistory } from 'history';
|
||||
import { getBasePath } from 'utils/basePath';
|
||||
|
||||
export default createBrowserHistory();
|
||||
export default createBrowserHistory({ basename: getBasePath() });
|
||||
|
||||
@@ -4,6 +4,7 @@ import ROUTES from 'constants/routes';
|
||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||
import { Home, LifeBuoy } from 'lucide-react';
|
||||
import { handleContactSupport } from 'pages/Integrations/utils';
|
||||
import { withBasePath } from 'utils/basePath';
|
||||
|
||||
import cloudUrl from '@/assets/Images/cloud.svg';
|
||||
|
||||
@@ -11,8 +12,8 @@ import './ErrorBoundaryFallback.styles.scss';
|
||||
|
||||
function ErrorBoundaryFallback(): JSX.Element {
|
||||
const handleReload = (): void => {
|
||||
// Go to home page
|
||||
window.location.href = ROUTES.HOME;
|
||||
// Hard reload resets Sentry.ErrorBoundary state; withBasePath preserves any /signoz/ prefix.
|
||||
window.location.href = withBasePath(ROUTES.HOME);
|
||||
};
|
||||
|
||||
const { isCloudUser: isCloudUserVal } = useGetTenantLicense();
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
} from 'pages/MessagingQueues/MessagingQueuesUtils';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
|
||||
import {
|
||||
convertToMilliseconds,
|
||||
@@ -93,7 +94,7 @@ export function getColumns(
|
||||
key={item}
|
||||
className="traceid-text"
|
||||
onClick={(): void => {
|
||||
window.open(`${ROUTES.TRACE}/${item}`, '_blank');
|
||||
openInNewTab(`${ROUTES.TRACE}/${item}`);
|
||||
logEvent(`MQ Kafka: Drop Rate - traceid navigation`, {
|
||||
item,
|
||||
});
|
||||
@@ -123,7 +124,7 @@ export function getColumns(
|
||||
onClick={(e): void => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
window.open(`/services/${encodeURIComponent(text)}`, '_blank');
|
||||
openInNewTab(`/services/${encodeURIComponent(text)}`);
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
|
||||
118
frontend/src/utils/__tests__/basePath.test.ts
Normal file
118
frontend/src/utils/__tests__/basePath.test.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* basePath is memoized at module init, so each describe block isolates the
|
||||
* module with a fresh DOM state using jest.isolateModules + require.
|
||||
*/
|
||||
|
||||
type BasePath = typeof import('../basePath');
|
||||
|
||||
function loadModule(href?: string): BasePath {
|
||||
if (href !== undefined) {
|
||||
const base = document.createElement('base');
|
||||
base.setAttribute('href', href);
|
||||
document.head.appendChild(base);
|
||||
}
|
||||
|
||||
let mod!: BasePath;
|
||||
jest.isolateModules(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
|
||||
mod = require('../basePath');
|
||||
});
|
||||
return mod;
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
document.head.querySelectorAll('base').forEach((el) => el.remove());
|
||||
});
|
||||
|
||||
describe('at basePath="/"', () => {
|
||||
let m: BasePath;
|
||||
beforeEach(() => {
|
||||
m = loadModule('/');
|
||||
});
|
||||
|
||||
it('getBasePath returns "/"', () => {
|
||||
expect(m.getBasePath()).toBe('/');
|
||||
});
|
||||
|
||||
it('withBasePath is a no-op for any internal path', () => {
|
||||
expect(m.withBasePath('/logs')).toBe('/logs');
|
||||
expect(m.withBasePath('/logs/explorer')).toBe('/logs/explorer');
|
||||
});
|
||||
|
||||
it('withBasePath passes through external URLs', () => {
|
||||
expect(m.withBasePath('https://example.com/foo')).toBe(
|
||||
'https://example.com/foo',
|
||||
);
|
||||
});
|
||||
|
||||
it('getAbsoluteUrl returns origin + path', () => {
|
||||
expect(m.getAbsoluteUrl('/logs')).toBe(`${window.location.origin}/logs`);
|
||||
});
|
||||
|
||||
it('getBaseUrl returns bare origin', () => {
|
||||
expect(m.getBaseUrl()).toBe(window.location.origin);
|
||||
});
|
||||
});
|
||||
|
||||
describe('at basePath="/signoz/"', () => {
|
||||
let m: BasePath;
|
||||
beforeEach(() => {
|
||||
m = loadModule('/signoz/');
|
||||
});
|
||||
|
||||
it('getBasePath returns "/signoz/"', () => {
|
||||
expect(m.getBasePath()).toBe('/signoz/');
|
||||
});
|
||||
|
||||
it('withBasePath prepends the prefix', () => {
|
||||
expect(m.withBasePath('/logs')).toBe('/signoz/logs');
|
||||
expect(m.withBasePath('/logs/explorer')).toBe('/signoz/logs/explorer');
|
||||
});
|
||||
|
||||
it('withBasePath is idempotent — safe to call twice', () => {
|
||||
expect(m.withBasePath('/signoz/logs')).toBe('/signoz/logs');
|
||||
});
|
||||
|
||||
it('withBasePath is idempotent when path equals the prefix without trailing slash', () => {
|
||||
expect(m.withBasePath('/signoz')).toBe('/signoz');
|
||||
});
|
||||
|
||||
it('withBasePath passes through external URLs', () => {
|
||||
expect(m.withBasePath('https://example.com/foo')).toBe(
|
||||
'https://example.com/foo',
|
||||
);
|
||||
});
|
||||
|
||||
it('getAbsoluteUrl returns origin + prefixed path', () => {
|
||||
expect(m.getAbsoluteUrl('/logs')).toBe(
|
||||
`${window.location.origin}/signoz/logs`,
|
||||
);
|
||||
});
|
||||
|
||||
it('getBaseUrl returns origin + prefix without trailing slash', () => {
|
||||
expect(m.getBaseUrl()).toBe(`${window.location.origin}/signoz`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('no <base> tag', () => {
|
||||
it('getBasePath falls back to "/"', () => {
|
||||
const m = loadModule();
|
||||
expect(m.getBasePath()).toBe('/');
|
||||
});
|
||||
});
|
||||
|
||||
describe('href without trailing slash', () => {
|
||||
it('normalises to trailing slash', () => {
|
||||
const m = loadModule('/signoz');
|
||||
expect(m.getBasePath()).toBe('/signoz/');
|
||||
expect(m.withBasePath('/logs')).toBe('/signoz/logs');
|
||||
});
|
||||
});
|
||||
|
||||
describe('nested prefix "/a/b/prefix/"', () => {
|
||||
it('withBasePath handles arbitrary depth', () => {
|
||||
const m = loadModule('/a/b/prefix/');
|
||||
expect(m.withBasePath('/logs')).toBe('/a/b/prefix/logs');
|
||||
expect(m.withBasePath('/a/b/prefix/logs')).toBe('/a/b/prefix/logs');
|
||||
});
|
||||
});
|
||||
@@ -1,15 +1,27 @@
|
||||
import { isModifierKeyPressed } from '../app';
|
||||
import { openInNewTab } from '../navigation';
|
||||
|
||||
type NavigationModule = typeof import('../navigation');
|
||||
|
||||
function loadNavigationModule(href?: string): NavigationModule {
|
||||
if (href !== undefined) {
|
||||
const base = document.createElement('base');
|
||||
base.setAttribute('href', href);
|
||||
document.head.appendChild(base);
|
||||
}
|
||||
let mod!: NavigationModule;
|
||||
jest.isolateModules(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
|
||||
mod = require('../navigation');
|
||||
});
|
||||
return mod;
|
||||
}
|
||||
|
||||
describe('navigation utilities', () => {
|
||||
const originalWindowOpen = window.open;
|
||||
|
||||
beforeEach(() => {
|
||||
window.open = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.open = originalWindowOpen;
|
||||
document.head.querySelectorAll('base').forEach((el) => el.remove());
|
||||
});
|
||||
|
||||
describe('isModifierKeyPressed', () => {
|
||||
@@ -56,25 +68,59 @@ describe('navigation utilities', () => {
|
||||
});
|
||||
|
||||
describe('openInNewTab', () => {
|
||||
it('calls window.open with the given path and _blank target', () => {
|
||||
openInNewTab('/dashboard');
|
||||
expect(window.open).toHaveBeenCalledWith('/dashboard', '_blank');
|
||||
describe('at basePath="/"', () => {
|
||||
let m: NavigationModule;
|
||||
beforeEach(() => {
|
||||
window.open = jest.fn();
|
||||
m = loadNavigationModule('/');
|
||||
});
|
||||
|
||||
it('passes internal path through unchanged', () => {
|
||||
m.openInNewTab('/dashboard');
|
||||
expect(window.open).toHaveBeenCalledWith('/dashboard', '_blank');
|
||||
});
|
||||
|
||||
it('passes through external URLs unchanged', () => {
|
||||
m.openInNewTab('https://example.com/page');
|
||||
expect(window.open).toHaveBeenCalledWith(
|
||||
'https://example.com/page',
|
||||
'_blank',
|
||||
);
|
||||
});
|
||||
|
||||
it('handles paths with query strings', () => {
|
||||
m.openInNewTab('/alerts?tab=AlertRules&relativeTime=30m');
|
||||
expect(window.open).toHaveBeenCalledWith(
|
||||
'/alerts?tab=AlertRules&relativeTime=30m',
|
||||
'_blank',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('handles full URLs', () => {
|
||||
openInNewTab('https://example.com/page');
|
||||
expect(window.open).toHaveBeenCalledWith(
|
||||
'https://example.com/page',
|
||||
'_blank',
|
||||
);
|
||||
});
|
||||
describe('at basePath="/signoz/"', () => {
|
||||
let m: NavigationModule;
|
||||
beforeEach(() => {
|
||||
window.open = jest.fn();
|
||||
m = loadNavigationModule('/signoz/');
|
||||
});
|
||||
|
||||
it('handles paths with query strings', () => {
|
||||
openInNewTab('/alerts?tab=AlertRules&relativeTime=30m');
|
||||
expect(window.open).toHaveBeenCalledWith(
|
||||
'/alerts?tab=AlertRules&relativeTime=30m',
|
||||
'_blank',
|
||||
);
|
||||
it('prepends base path to internal paths', () => {
|
||||
m.openInNewTab('/dashboard');
|
||||
expect(window.open).toHaveBeenCalledWith('/signoz/dashboard', '_blank');
|
||||
});
|
||||
|
||||
it('passes through external URLs unchanged', () => {
|
||||
m.openInNewTab('https://example.com/page');
|
||||
expect(window.open).toHaveBeenCalledWith(
|
||||
'https://example.com/page',
|
||||
'_blank',
|
||||
);
|
||||
});
|
||||
|
||||
it('is idempotent — does not double-prefix an already-prefixed path', () => {
|
||||
m.openInNewTab('/signoz/dashboard');
|
||||
expect(window.open).toHaveBeenCalledWith('/signoz/dashboard', '_blank');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
50
frontend/src/utils/basePath.ts
Normal file
50
frontend/src/utils/basePath.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
// Read once at module init — avoids a DOM query on every axios request.
|
||||
const _basePath: string = ((): string => {
|
||||
const href = document.querySelector('base')?.getAttribute('href') ?? '/';
|
||||
return href.endsWith('/') ? href : `${href}/`;
|
||||
})();
|
||||
|
||||
/** Returns the runtime base path — always trailing-slashed. e.g. "/" or "/signoz/" */
|
||||
export function getBasePath(): string {
|
||||
return _basePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends the base path to an internal absolute path.
|
||||
* Idempotent and safe to call on any value.
|
||||
*
|
||||
* withBasePath('/logs') → '/signoz/logs'
|
||||
* withBasePath('/signoz/logs') → '/signoz/logs' (already prefixed)
|
||||
* withBasePath('https://x.com') → 'https://x.com' (external, passthrough)
|
||||
*/
|
||||
export function withBasePath(path: string): string {
|
||||
if (!path.startsWith('/')) {
|
||||
return path;
|
||||
}
|
||||
if (_basePath === '/') {
|
||||
return path;
|
||||
}
|
||||
if (path.startsWith(_basePath) || path === _basePath.slice(0, -1)) {
|
||||
return path;
|
||||
}
|
||||
return _basePath + path.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full absolute URL — for copy-to-clipboard and window.open calls.
|
||||
* getAbsoluteUrl(ROUTES.LOGS_EXPLORER) → 'https://host/signoz/logs/logs-explorer'
|
||||
*/
|
||||
export function getAbsoluteUrl(path: string): string {
|
||||
return window.location.origin + withBasePath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Origin + base path without trailing slash — for sending to the backend
|
||||
* as frontendBaseUrl in invite / password-reset email flows.
|
||||
* getBaseUrl() → 'https://host/signoz'
|
||||
*/
|
||||
export function getBaseUrl(): string {
|
||||
return (
|
||||
window.location.origin + (_basePath === '/' ? '' : _basePath.slice(0, -1))
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
/**
|
||||
* Opens the given path in a new browser tab.
|
||||
*/
|
||||
import { withBasePath } from 'utils/basePath';
|
||||
|
||||
export const openInNewTab = (path: string): void => {
|
||||
window.open(path, '_blank');
|
||||
window.open(withBasePath(path), '_blank');
|
||||
};
|
||||
|
||||
@@ -10,6 +10,18 @@ import { createHtmlPlugin } from 'vite-plugin-html';
|
||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
|
||||
// In dev the Go backend is not involved, so replace the [[.BaseHref]] placeholder
|
||||
// with "/" so relative assets resolve correctly from the Vite dev server.
|
||||
function devBasePathPlugin(): Plugin {
|
||||
return {
|
||||
name: 'dev-base-path',
|
||||
apply: 'serve',
|
||||
transformIndexHtml(html): string {
|
||||
return html.replaceAll('[[.BaseHref]]', '/');
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function rawMarkdownPlugin(): Plugin {
|
||||
return {
|
||||
name: 'raw-markdown',
|
||||
@@ -32,6 +44,7 @@ export default defineConfig(
|
||||
const plugins = [
|
||||
tsconfigPaths(),
|
||||
rawMarkdownPlugin(),
|
||||
devBasePathPlugin(),
|
||||
react(),
|
||||
createHtmlPlugin({
|
||||
inject: {
|
||||
@@ -124,6 +137,7 @@ export default defineConfig(
|
||||
'process.env.TUNNEL_DOMAIN': JSON.stringify(env.VITE_TUNNEL_DOMAIN),
|
||||
'process.env.DOCS_BASE_URL': JSON.stringify(env.VITE_DOCS_BASE_URL),
|
||||
},
|
||||
base: './',
|
||||
build: {
|
||||
sourcemap: true,
|
||||
outDir: 'build',
|
||||
|
||||
@@ -7,6 +7,7 @@ var (
|
||||
FeatureKafkaSpanEval = featuretypes.MustNewName("kafka_span_eval")
|
||||
FeatureHideRootUser = featuretypes.MustNewName("hide_root_user")
|
||||
FeatureGetMetersFromZeus = featuretypes.MustNewName("get_meters_from_zeus")
|
||||
FeaturePutMetersInZeus = featuretypes.MustNewName("put_meters_in_zeus")
|
||||
)
|
||||
|
||||
func MustNewRegistry() featuretypes.Registry {
|
||||
@@ -43,6 +44,14 @@ func MustNewRegistry() featuretypes.Registry {
|
||||
DefaultVariant: featuretypes.MustNewName("disabled"),
|
||||
Variants: featuretypes.NewBooleanVariants(),
|
||||
},
|
||||
&featuretypes.Feature{
|
||||
Name: FeaturePutMetersInZeus,
|
||||
Kind: featuretypes.KindBoolean,
|
||||
Stage: featuretypes.StageExperimental,
|
||||
Description: "Controls whether usage meters are sent to Zeus instead of the legacy subscriptions service",
|
||||
DefaultVariant: featuretypes.MustNewName("disabled"),
|
||||
Variants: featuretypes.NewBooleanVariants(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -373,13 +373,7 @@ func (handler *handler) UpdateService(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// update or create service
|
||||
if svc.CloudIntegrationService == nil {
|
||||
var cloudIntegrationService *cloudintegrationtypes.CloudIntegrationService
|
||||
cloudIntegrationService, err = cloudintegrationtypes.NewCloudIntegrationService(serviceID, cloudIntegrationID, provider, req.Config)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
cloudIntegrationService := cloudintegrationtypes.NewCloudIntegrationService(serviceID, cloudIntegrationID, req.Config)
|
||||
err = handler.module.CreateService(ctx, orgID, cloudIntegrationService, provider)
|
||||
} else {
|
||||
err = svc.CloudIntegrationService.Update(provider, serviceID, req.Config)
|
||||
|
||||
@@ -826,7 +826,7 @@ func TestThresholdRuleTracesLink(t *testing.T) {
|
||||
queryString := "SELECT any"
|
||||
telemetryStore.Mock().
|
||||
ExpectQuery(queryString).
|
||||
WithArgs(nil, nil, nil, nil, nil, nil, nil, nil, nil).
|
||||
WithArgs(nil, nil, nil, nil, nil, nil, nil).
|
||||
WillReturnRows(rows)
|
||||
|
||||
querier := prepareQuerierForTraces(telemetryStore, keysMap)
|
||||
|
||||
@@ -218,6 +218,10 @@ func (v *exprVisitor) VisitFunctionExpr(fn *chparser.FunctionExpr) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// not possible for whereClause to be nil here but still adding a check.
|
||||
if whereClause == nil {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "invalid predicate argument for %q: %q", name, origPred)
|
||||
}
|
||||
|
||||
newPred, chArgs := whereClause.WhereClause.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
newPred = strings.TrimPrefix(newPred, "WHERE")
|
||||
|
||||
@@ -166,11 +166,10 @@ func PrepareWhereClause(query string, opts FilterExprVisitorOpts) (*PreparedWher
|
||||
return nil, combinedErrors.WithAdditional(visitor.errors...).WithUrl(url)
|
||||
}
|
||||
|
||||
// The visitor returns exactly SkipConditionLiteral (never a substring) when
|
||||
// there are no evaluable conditions; replace it with the no-op SQL literal.
|
||||
// TODO(nitya): In this case we can choose to ignore resource_filter_cte
|
||||
// Return nil so callers can skip the
|
||||
// entire CTE/subquery rather than emitting WHERE clause that select all the rows
|
||||
if cond == "" || cond == SkipConditionLiteral {
|
||||
cond = TrueConditionLiteral
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
whereClause := sqlbuilder.NewWhereClause().AddWhereExpr(visitor.builder.Args, cond)
|
||||
|
||||
@@ -702,7 +702,7 @@ func TestVisitKey(t *testing.T) {
|
||||
// → AND returns SkipConditionLiteral which propagates upward
|
||||
// • In OR: short-circuits the entire OR immediately (returns SkipConditionLiteral)
|
||||
// • NOT(SkipConditionLiteral) → SkipConditionLiteral (guard in VisitUnaryExpression)
|
||||
// • PrepareWhereClause converts a top-level SkipConditionLiteral to TrueConditionLiteral ("WHERE true")
|
||||
// • PrepareWhereClause converts a top-level SkipConditionLiteral to nil
|
||||
//
|
||||
// Test cases with wantErrSB=true use PrepareWhereClause directly to verify
|
||||
// that SB returns an error (instead of calling buildSQLOpts which fatalf's).
|
||||
@@ -819,27 +819,27 @@ func TestVisitComparison_AND(t *testing.T) {
|
||||
{
|
||||
name: "single attribute key",
|
||||
expr: "a = 'v'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE a_cond",
|
||||
},
|
||||
{
|
||||
name: "single resource key",
|
||||
expr: "x = 'x'",
|
||||
wantRSB: "WHERE x_cond",
|
||||
wantSB: "WHERE true",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
// RSB: both attribute keys → true; AND propagates TrueConditionLiteral.
|
||||
name: "two attribute keys AND",
|
||||
expr: "a = 'a' AND b = 'b'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (a_cond AND b_cond)",
|
||||
},
|
||||
{
|
||||
name: "two resource keys AND",
|
||||
expr: "x = 'x' AND y = 'y'",
|
||||
wantRSB: "WHERE (x_cond AND y_cond)",
|
||||
wantSB: "WHERE true",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
// RSB: attribute → true stripped by AND; resource key survives.
|
||||
@@ -868,13 +868,19 @@ func TestVisitComparison_AND(t *testing.T) {
|
||||
result, err := PrepareWhereClause(tt.expr, rsbOpts)
|
||||
assert.Equal(t, tt.wantErrRSB, err != nil, "resourceConditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantRSB, expr, "resourceConditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantRSB, expr)
|
||||
}
|
||||
result, err = PrepareWhereClause(tt.expr, sbOpts)
|
||||
assert.Equal(t, tt.wantErrSB, err != nil, "conditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantSB, expr, "conditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantSB, expr)
|
||||
}
|
||||
})
|
||||
@@ -892,14 +898,14 @@ func TestVisitComparison_NOT(t *testing.T) {
|
||||
// Unary NOT on an attribute key: NOT(SkipConditionLiteral) → SkipConditionLiteral (guard).
|
||||
name: "NOT attribute key",
|
||||
expr: "NOT a = 'a'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE NOT (a_cond)",
|
||||
},
|
||||
{
|
||||
name: "NOT resource key",
|
||||
expr: "NOT x = 'x'",
|
||||
wantRSB: "WHERE NOT (x_cond)",
|
||||
wantSB: "WHERE true",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
// RSB: NOT(SkipConditionLiteral) → SkipConditionLiteral; stripped from AND; x_cond survives.
|
||||
@@ -912,14 +918,14 @@ func TestVisitComparison_NOT(t *testing.T) {
|
||||
// NOT inside comparison (op=NotLike): conditionBuilder ignores it → same as LIKE.
|
||||
name: "NOT inside LIKE comparison",
|
||||
expr: "a NOT LIKE '%a%'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE a_cond",
|
||||
},
|
||||
{
|
||||
// Unary NOT wrapping LIKE: structural NOT emitted around a_cond.
|
||||
name: "NOT at unary level wrapping LIKE",
|
||||
expr: "NOT a LIKE '%a%'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE NOT (a_cond)",
|
||||
},
|
||||
{
|
||||
@@ -947,7 +953,7 @@ func TestVisitComparison_NOT(t *testing.T) {
|
||||
// The inner NOT is an operator token; the outer NOT is structural.
|
||||
name: "unary NOT wrapping comparison NOT LIKE",
|
||||
expr: "NOT (a NOT LIKE '%a%')",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE NOT ((a_cond))",
|
||||
},
|
||||
}
|
||||
@@ -956,13 +962,19 @@ func TestVisitComparison_NOT(t *testing.T) {
|
||||
result, err := PrepareWhereClause(tt.expr, rsbOpts)
|
||||
assert.Equal(t, tt.wantErrRSB, err != nil, "resourceConditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantRSB, expr, "resourceConditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantRSB, expr)
|
||||
}
|
||||
result, err = PrepareWhereClause(tt.expr, sbOpts)
|
||||
assert.Equal(t, tt.wantErrSB, err != nil, "conditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantSB, expr, "conditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantSB, expr)
|
||||
}
|
||||
})
|
||||
@@ -991,7 +1003,7 @@ func TestVisitComparison_OR(t *testing.T) {
|
||||
// RSB: attribute → TrueConditionLiteral short-circuits OR.
|
||||
name: "attribute OR resource",
|
||||
expr: "a = 'a' OR x = 'x'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (a_cond OR x_cond)",
|
||||
},
|
||||
{
|
||||
@@ -1029,21 +1041,21 @@ func TestVisitComparison_OR(t *testing.T) {
|
||||
// RSB: NOT(a→SkipConditionLiteral) → SkipConditionLiteral → OR short-circuits.
|
||||
name: "NOT attr OR resource with OR override",
|
||||
expr: "NOT a = 'a' OR x = 'x'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (NOT (a_cond) OR x_cond)",
|
||||
},
|
||||
{
|
||||
// RSB: a → TrueConditionLiteral → OR short-circuits.
|
||||
name: "all attribute keys OR",
|
||||
expr: "a = 'a' OR b = 'b' OR c = 'c'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (a_cond OR b_cond OR c_cond)",
|
||||
},
|
||||
{
|
||||
// RSB: a→SkipConditionLiteral → OR short-circuits; paren passes through; NOT(SkipConditionLiteral) → SkipConditionLiteral.
|
||||
name: "NOT of three-way OR",
|
||||
expr: "NOT (a = 'a' OR b = 'b' OR x = 'x')",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE NOT (((a_cond OR b_cond OR x_cond)))",
|
||||
},
|
||||
}
|
||||
@@ -1052,13 +1064,19 @@ func TestVisitComparison_OR(t *testing.T) {
|
||||
result, err := PrepareWhereClause(tt.expr, rsbOpts)
|
||||
assert.Equal(t, tt.wantErrRSB, err != nil, "resourceConditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantRSB, expr, "resourceConditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantRSB, expr)
|
||||
}
|
||||
result, err = PrepareWhereClause(tt.expr, sbOpts)
|
||||
assert.Equal(t, tt.wantErrSB, err != nil, "conditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantSB, expr, "conditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantSB, expr)
|
||||
}
|
||||
})
|
||||
@@ -1074,28 +1092,28 @@ func TestVisitComparison_Precedence(t *testing.T) {
|
||||
// a→true short-circuits OR.
|
||||
name: "attr OR attr OR resource",
|
||||
expr: "a = 'a' OR b = 'b' OR x = 'x'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (a_cond OR b_cond OR x_cond)",
|
||||
},
|
||||
{
|
||||
// AND before OR: (a AND b)→true short-circuits OR.
|
||||
name: "attr AND attr OR resource",
|
||||
expr: "a = 'a' AND b = 'b' OR x = 'x'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE ((a_cond AND b_cond) OR x_cond)",
|
||||
},
|
||||
{
|
||||
// AND tighter: a as own OR branch; (b AND x) as second.
|
||||
name: "attr OR attr AND resource",
|
||||
expr: "a = 'a' OR b = 'b' AND x = 'x'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (a_cond OR (b_cond AND x_cond))",
|
||||
},
|
||||
{
|
||||
// Left AND group (a,b)→true short-circuits OR.
|
||||
name: "two AND groups OR",
|
||||
expr: "a = 'a' AND b = 'b' OR x = 'x' AND y = 'y'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE ((a_cond AND b_cond) OR (x_cond AND y_cond))",
|
||||
},
|
||||
{
|
||||
@@ -1109,7 +1127,7 @@ func TestVisitComparison_Precedence(t *testing.T) {
|
||||
// RSB: NOT(a→SkipConditionLiteral)→SkipConditionLiteral → OR short-circuits.
|
||||
name: "NOT attr OR NOT resource",
|
||||
expr: "NOT a = 'a' OR NOT x = 'x'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (NOT (a_cond) OR NOT (x_cond))",
|
||||
},
|
||||
{
|
||||
@@ -1118,7 +1136,7 @@ func TestVisitComparison_Precedence(t *testing.T) {
|
||||
// SkipConditionLiteral OR x_cond → SkipConditionLiteral short-circuits OR.
|
||||
name: "complex NOT OR AND",
|
||||
expr: "NOT a = 'a' OR b = 'b' AND x = 'x'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (NOT (a_cond) OR (b_cond AND x_cond))",
|
||||
},
|
||||
}
|
||||
@@ -1127,13 +1145,19 @@ func TestVisitComparison_Precedence(t *testing.T) {
|
||||
result, err := PrepareWhereClause(tt.expr, rsbOpts)
|
||||
assert.Equal(t, tt.wantErrRSB, err != nil, "resourceConditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantRSB, expr, "resourceConditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantRSB, expr)
|
||||
}
|
||||
result, err = PrepareWhereClause(tt.expr, sbOpts)
|
||||
assert.Equal(t, tt.wantErrSB, err != nil, "conditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantSB, expr, "conditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantSB, expr)
|
||||
}
|
||||
})
|
||||
@@ -1150,7 +1174,7 @@ func TestVisitComparison_Parens(t *testing.T) {
|
||||
// RSB: SkipConditionLiteral passes through unwrapped. SB: VisitPrimary wraps in parens.
|
||||
name: "single attribute key in parens",
|
||||
expr: "(a = 'a')",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (a_cond)",
|
||||
},
|
||||
{
|
||||
@@ -1177,7 +1201,7 @@ func TestVisitComparison_Parens(t *testing.T) {
|
||||
// RSB: left (a OR b)→true → OR short-circuits.
|
||||
name: "two paren-OR groups ORed",
|
||||
expr: "(a = 'a' OR b = 'b') OR (x = 'x' OR y = 'y')",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (((a_cond OR b_cond)) OR ((x_cond OR y_cond)))",
|
||||
},
|
||||
{
|
||||
@@ -1192,7 +1216,7 @@ func TestVisitComparison_Parens(t *testing.T) {
|
||||
name: "deeply nested parentheses",
|
||||
expr: "(((x = 'x')))",
|
||||
wantRSB: "WHERE (((x_cond)))",
|
||||
wantSB: "WHERE true",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
// RSB: inner NOT(a→SkipConditionLiteral)→SkipConditionLiteral; paren passes through;
|
||||
@@ -1200,7 +1224,7 @@ func TestVisitComparison_Parens(t *testing.T) {
|
||||
// SB: structural parens accumulate around each NOT.
|
||||
name: "double NOT via parens",
|
||||
expr: "NOT (NOT a = 'a')",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE NOT ((NOT (a_cond)))",
|
||||
},
|
||||
{
|
||||
@@ -1208,14 +1232,14 @@ func TestVisitComparison_Parens(t *testing.T) {
|
||||
// paren passes through; NOT(SkipConditionLiteral) → SkipConditionLiteral.
|
||||
name: "NOT of parenthesized all-attribute AND",
|
||||
expr: "NOT (a = 'a' AND b = 'b')",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE NOT (((a_cond AND b_cond)))",
|
||||
},
|
||||
{
|
||||
// RSB: a→SkipConditionLiteral short-circuits OR; paren passes through; NOT(SkipConditionLiteral)→SkipConditionLiteral.
|
||||
name: "NOT of parenthesized mixed OR attr short-circuits",
|
||||
expr: "NOT (a = 'a' OR x = 'x')",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE NOT (((a_cond OR x_cond)))",
|
||||
},
|
||||
}
|
||||
@@ -1224,13 +1248,19 @@ func TestVisitComparison_Parens(t *testing.T) {
|
||||
result, err := PrepareWhereClause(tt.expr, rsbOpts)
|
||||
assert.Equal(t, tt.wantErrRSB, err != nil, "resourceConditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantRSB, expr, "resourceConditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantRSB, expr)
|
||||
}
|
||||
result, err = PrepareWhereClause(tt.expr, sbOpts)
|
||||
assert.Equal(t, tt.wantErrSB, err != nil, "conditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantSB, expr, "conditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantSB, expr)
|
||||
}
|
||||
})
|
||||
@@ -1246,14 +1276,14 @@ func TestVisitComparison_FullText(t *testing.T) {
|
||||
{
|
||||
name: "standalone full-text term",
|
||||
expr: "'hello'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE body_cond",
|
||||
},
|
||||
{
|
||||
// RSB: FT→true, a→true; AND propagates true.
|
||||
name: "full-text AND attribute",
|
||||
expr: "'hello' AND a = 'a'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (body_cond AND a_cond)",
|
||||
},
|
||||
{
|
||||
@@ -1267,56 +1297,56 @@ func TestVisitComparison_FullText(t *testing.T) {
|
||||
// RSB: NOT(FT→SkipConditionLiteral)→SkipConditionLiteral. SB: structural NOT applied.
|
||||
name: "NOT full-text term",
|
||||
expr: "NOT 'hello'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE NOT (body_cond)",
|
||||
},
|
||||
{
|
||||
// RSB: FT→true short-circuits OR.
|
||||
name: "full-text OR resource",
|
||||
expr: "'hello' OR x = 'x'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (body_cond OR x_cond)",
|
||||
},
|
||||
{
|
||||
name: "full-text OR attribute",
|
||||
expr: "'hello' OR a = 'a'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (body_cond OR a_cond)",
|
||||
},
|
||||
{
|
||||
name: "two full-text terms ANDed",
|
||||
expr: "'hello' AND 'world'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (body_cond AND body_cond)",
|
||||
},
|
||||
{
|
||||
name: "two full-text terms ORed",
|
||||
expr: "'hello' OR 'world'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (body_cond OR body_cond)",
|
||||
},
|
||||
{
|
||||
name: "full-text in parentheses",
|
||||
expr: "('hello')",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (body_cond)",
|
||||
},
|
||||
{
|
||||
name: "two full-text AND attribute",
|
||||
expr: "'hello' AND 'world' AND a = 'a'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (body_cond AND body_cond AND a_cond)",
|
||||
},
|
||||
{
|
||||
name: "full-text OR attr OR resource all types",
|
||||
expr: "'hello' OR a = 'a' OR x = 'x'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (body_cond OR a_cond OR x_cond)",
|
||||
},
|
||||
{
|
||||
name: "NOT of paren full-text AND attr",
|
||||
expr: "NOT ('hello' AND a = 'a')",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE NOT (((body_cond AND a_cond)))",
|
||||
},
|
||||
{
|
||||
@@ -1329,7 +1359,7 @@ func TestVisitComparison_FullText(t *testing.T) {
|
||||
{
|
||||
name: "NOT full-text OR resource",
|
||||
expr: "NOT 'hello' OR x = 'x'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (NOT (body_cond) OR x_cond)",
|
||||
},
|
||||
{
|
||||
@@ -1350,21 +1380,21 @@ func TestVisitComparison_FullText(t *testing.T) {
|
||||
// SB: allVariable→TrueConditionLiteral stripped; body_cond survives.
|
||||
name: "full-text AND allVariable",
|
||||
expr: "'hello' AND x IN $service",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE body_cond",
|
||||
},
|
||||
{
|
||||
// SB: body_cond added first; then allVariable→TrueConditionLiteral short-circuits OR.
|
||||
name: "full-text OR allVariable",
|
||||
expr: "'hello' OR x IN $service",
|
||||
wantRSB: "WHERE true",
|
||||
wantSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
// SB: body_cond
|
||||
name: "full-text with sentinel value",
|
||||
expr: SkipConditionLiteral,
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE body_cond",
|
||||
},
|
||||
}
|
||||
@@ -1373,13 +1403,19 @@ func TestVisitComparison_FullText(t *testing.T) {
|
||||
result, err := PrepareWhereClause(tt.expr, rsbOpts)
|
||||
assert.Equal(t, tt.wantErrRSB, err != nil, "resourceConditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantRSB, expr, "resourceConditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantRSB, expr)
|
||||
}
|
||||
result, err = PrepareWhereClause(tt.expr, sbOpts)
|
||||
assert.Equal(t, tt.wantErrSB, err != nil, "conditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantSB, expr, "conditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantSB, expr)
|
||||
}
|
||||
})
|
||||
@@ -1397,22 +1433,22 @@ func TestVisitComparison_AllVariable(t *testing.T) {
|
||||
{
|
||||
name: "IN allVariable alone",
|
||||
expr: "x IN $service",
|
||||
wantRSB: "WHERE true",
|
||||
wantSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
// TrueConditionLiteral stripped from AND; y_cond remains.
|
||||
name: "IN allVariable AND resource",
|
||||
expr: "x IN $service AND y = 'y'",
|
||||
wantRSB: "WHERE y_cond",
|
||||
wantSB: "WHERE true",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
// TrueConditionLiteral short-circuits OR.
|
||||
name: "IN allVariable OR resource",
|
||||
expr: "x IN $service OR y = 'y'",
|
||||
wantRSB: "WHERE true",
|
||||
wantSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
// RSB: a IN __all__→true stripped; x_cond remains.
|
||||
@@ -1420,47 +1456,47 @@ func TestVisitComparison_AllVariable(t *testing.T) {
|
||||
name: "attr IN allVariable AND resource",
|
||||
expr: "a IN $service AND x = 'x'",
|
||||
wantRSB: "WHERE x_cond",
|
||||
wantSB: "WHERE true",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
// NOT IN also resolves __all__ to TrueConditionLiteral.
|
||||
name: "NOT IN allVariable alone",
|
||||
expr: "x NOT IN $service",
|
||||
wantRSB: "WHERE true",
|
||||
wantSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
name: "NOT IN allVariable AND resource",
|
||||
expr: "x NOT IN $service AND y = 'y'",
|
||||
wantRSB: "WHERE y_cond",
|
||||
wantSB: "WHERE true",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
// NOT (x IN $service): __all__ → SkipConditionLiteral; VisitPrimary passes through;
|
||||
// NOT(SkipConditionLiteral) → SkipConditionLiteral.
|
||||
name: "NOT of allVariable IN",
|
||||
expr: "NOT (x IN $service)",
|
||||
wantRSB: "WHERE true",
|
||||
wantSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
name: "allVariable IN AND allVariable IN",
|
||||
expr: "x IN $service AND y IN $service",
|
||||
wantRSB: "WHERE true",
|
||||
wantSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "",
|
||||
},
|
||||
{
|
||||
// SB: allVariable→TrueConditionLiteral stripped; body_cond survives.
|
||||
name: "allVariable IN AND full-text",
|
||||
expr: "x IN $service AND 'hello'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE body_cond",
|
||||
},
|
||||
{
|
||||
// Equality does not trigger __all__ short-circuit; ConditionFor called normally.
|
||||
name: "equality with __all__ variable no shortcircuit",
|
||||
expr: "a = $service",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE a_cond",
|
||||
},
|
||||
{
|
||||
@@ -1468,7 +1504,7 @@ func TestVisitComparison_AllVariable(t *testing.T) {
|
||||
name: "NOT of paren with __all__ AND resource",
|
||||
expr: "NOT (x IN $service AND y = 'y')",
|
||||
wantRSB: "WHERE NOT ((y_cond))",
|
||||
wantSB: "WHERE true",
|
||||
wantSB: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -1476,13 +1512,19 @@ func TestVisitComparison_AllVariable(t *testing.T) {
|
||||
result, err := PrepareWhereClause(tt.expr, rsbOpts)
|
||||
assert.Equal(t, tt.wantErrRSB, err != nil, "resourceConditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantRSB, expr, "resourceConditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantRSB, expr)
|
||||
}
|
||||
result, err = PrepareWhereClause(tt.expr, sbOpts)
|
||||
assert.Equal(t, tt.wantErrSB, err != nil, "conditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantSB, expr, "conditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantSB, expr)
|
||||
}
|
||||
})
|
||||
@@ -1499,13 +1541,13 @@ func TestVisitComparison_FunctionCalls(t *testing.T) {
|
||||
{
|
||||
name: "has on attribute key",
|
||||
expr: "has(a, 'hello')",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantErrSB: true,
|
||||
},
|
||||
{
|
||||
name: "has on resource key",
|
||||
expr: "has(x, 'hello')",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantErrSB: true,
|
||||
},
|
||||
{
|
||||
@@ -1519,13 +1561,13 @@ func TestVisitComparison_FunctionCalls(t *testing.T) {
|
||||
// RSB: TrueConditionLiteral short-circuits OR.
|
||||
name: "has OR resource key",
|
||||
expr: "has(a, 'hello') OR x = 'x'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantErrSB: true,
|
||||
},
|
||||
{
|
||||
name: "NOT of has",
|
||||
expr: "NOT has(a, 'hello')",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantErrSB: true,
|
||||
},
|
||||
{
|
||||
@@ -1549,13 +1591,19 @@ func TestVisitComparison_FunctionCalls(t *testing.T) {
|
||||
result, err := PrepareWhereClause(tt.expr, rsbOpts)
|
||||
assert.Equal(t, tt.wantErrRSB, err != nil, "resourceConditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantRSB, expr, "resourceConditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantRSB, expr)
|
||||
}
|
||||
result, err = PrepareWhereClause(tt.expr, sbOpts)
|
||||
assert.Equal(t, tt.wantErrSB, err != nil, "conditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantSB, expr, "conditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantSB, expr)
|
||||
}
|
||||
})
|
||||
@@ -1581,21 +1629,21 @@ func TestVisitComparison_UnknownKeys(t *testing.T) {
|
||||
// SkipConditionLiteral short-circuits OR → x_cond never evaluated → WHERE true.
|
||||
name: "unknown key OR resource",
|
||||
expr: "unknown_key = 'val' OR x = 'x'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantErrSB: true,
|
||||
},
|
||||
{
|
||||
// RSB: unknown_key → SkipConditionLiteral short-circuits OR → WHERE true (a=a never evaluated).
|
||||
name: "unknown key OR attribute",
|
||||
expr: "unknown_key = 'val' OR a = 'a'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantErrSB: true,
|
||||
},
|
||||
{
|
||||
// RSB: both → SkipConditionLiteral; all stripped from AND → AND returns SkipConditionLiteral → WHERE true.
|
||||
name: "all unknown keys in AND",
|
||||
expr: "unk1 = 'v' AND unk2 = 'v'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantErrSB: true,
|
||||
},
|
||||
{
|
||||
@@ -1603,7 +1651,7 @@ func TestVisitComparison_UnknownKeys(t *testing.T) {
|
||||
// PrepareWhereClause converts to WHERE true.
|
||||
name: "NOT of unknown key",
|
||||
expr: "NOT unknown_key = 'val'",
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantErrSB: true,
|
||||
},
|
||||
}
|
||||
@@ -1612,13 +1660,19 @@ func TestVisitComparison_UnknownKeys(t *testing.T) {
|
||||
result, err := PrepareWhereClause(tt.expr, rsbOpts)
|
||||
assert.Equal(t, tt.wantErrRSB, err != nil, "resourceConditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantRSB, expr, "resourceConditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantRSB, expr)
|
||||
}
|
||||
result, err = PrepareWhereClause(tt.expr, sbOpts)
|
||||
assert.Equal(t, tt.wantErrSB, err != nil, "conditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantSB, expr, "conditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantSB, expr)
|
||||
}
|
||||
})
|
||||
@@ -1636,26 +1690,26 @@ func TestVisitComparison_SkippableLiteralValues(t *testing.T) {
|
||||
// sbOpts: conditionBuilder ignores value → WHERE a_cond.
|
||||
name: "value equals TrueConditionLiteral",
|
||||
expr: fmt.Sprintf("a = '%s'", TrueConditionLiteral),
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE a_cond",
|
||||
},
|
||||
{
|
||||
name: "value equals SkipConditionLiteral",
|
||||
expr: fmt.Sprintf("a = '%s'", SkipConditionLiteral),
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE a_cond",
|
||||
},
|
||||
{
|
||||
name: "value equals ErrorConditionLiteral",
|
||||
expr: fmt.Sprintf("a = '%s'", ErrorConditionLiteral),
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE a_cond",
|
||||
},
|
||||
{
|
||||
// IN list whose members are all sentinel literals.
|
||||
name: "IN list containing all sentinel literals",
|
||||
expr: fmt.Sprintf("a IN ('%s', '%s', '%s')", TrueConditionLiteral, SkipConditionLiteral, ErrorConditionLiteral),
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE a_cond",
|
||||
},
|
||||
{
|
||||
@@ -1663,7 +1717,7 @@ func TestVisitComparison_SkippableLiteralValues(t *testing.T) {
|
||||
// sbOpts → two real conditions joined by AND.
|
||||
name: "AND with sentinel value on one branch",
|
||||
expr: fmt.Sprintf("a = '%s' AND b = 'real_value'", SkipConditionLiteral),
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE (a_cond AND b_cond)",
|
||||
},
|
||||
{
|
||||
@@ -1671,7 +1725,7 @@ func TestVisitComparison_SkippableLiteralValues(t *testing.T) {
|
||||
// sbOpts: NOT wraps the real condition.
|
||||
name: "NOT with sentinel value",
|
||||
expr: fmt.Sprintf("NOT a = '%s'", TrueConditionLiteral),
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE NOT (a_cond)",
|
||||
},
|
||||
{
|
||||
@@ -1680,7 +1734,7 @@ func TestVisitComparison_SkippableLiteralValues(t *testing.T) {
|
||||
// sbOpts: full-text search on body column → WHERE body_cond.
|
||||
name: "full text search with SkipConditionLiteral",
|
||||
expr: SkipConditionLiteral,
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE body_cond",
|
||||
},
|
||||
{
|
||||
@@ -1689,7 +1743,7 @@ func TestVisitComparison_SkippableLiteralValues(t *testing.T) {
|
||||
// sbOpts: full-text search on body column → WHERE body_cond.
|
||||
name: "full text search with TrueConditionLiteral",
|
||||
expr: TrueConditionLiteral,
|
||||
wantRSB: "WHERE true",
|
||||
wantRSB: "",
|
||||
wantSB: "WHERE body_cond",
|
||||
},
|
||||
}
|
||||
@@ -1698,13 +1752,19 @@ func TestVisitComparison_SkippableLiteralValues(t *testing.T) {
|
||||
result, err := PrepareWhereClause(tt.expr, rsbOpts)
|
||||
assert.Equal(t, tt.wantErrRSB, err != nil, "resourceConditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantRSB, expr, "resourceConditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantRSB, expr)
|
||||
}
|
||||
result, err = PrepareWhereClause(tt.expr, sbOpts)
|
||||
assert.Equal(t, tt.wantErrSB, err != nil, "conditionBuilder: error expectation mismatch")
|
||||
if err == nil {
|
||||
expr, _ := result.WhereClause.Build()
|
||||
var expr string
|
||||
if result != nil {
|
||||
expr, _ = result.WhereClause.Build()
|
||||
}
|
||||
assert.Equal(t, tt.wantSB, expr, "conditionBuilder SQL mismatch:\n want: %s\n got: %s", tt.wantSB, expr)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -606,6 +606,10 @@ func (b *auditQueryStatementBuilder) maybeAttachResourceFilter(
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if stmt == nil {
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
sb.Where("resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter)")
|
||||
|
||||
return fmt.Sprintf("__resource_filter AS (%s)", stmt.Query), stmt.Args, nil
|
||||
|
||||
@@ -89,8 +89,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
Limit: 100,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (`attribute_string_signoz$$audit$$principal$$id` = ? AND `attribute_string_signoz$$audit$$principal$$id_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "019a-1234-abcd-5678", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 100},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE (`attribute_string_signoz$$audit$$principal$$id` = ? AND `attribute_string_signoz$$audit$$principal$$id_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"019a-1234-abcd-5678", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 100},
|
||||
},
|
||||
},
|
||||
// List: all failed actions (materialized outcome filter)
|
||||
@@ -106,8 +106,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
Limit: 100,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (`attribute_string_signoz$$audit$$outcome` = ? AND `attribute_string_signoz$$audit$$outcome_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "failure", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 100},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE (`attribute_string_signoz$$audit$$outcome` = ? AND `attribute_string_signoz$$audit$$outcome_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"failure", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 100},
|
||||
},
|
||||
},
|
||||
// List: change history of a specific dashboard (two materialized column AND)
|
||||
@@ -123,7 +123,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
Limit: 100,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE ((simpleJSONExtractString(labels, 'signoz.audit.resource.kind') = ? AND labels LIKE ? AND labels LIKE ?) AND (simpleJSONExtractString(labels, 'signoz.audit.resource.id') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE ((simpleJSONExtractString(labels, 'signoz.audit.resource.kind') = ? AND labels LIKE ? AND labels LIKE ?) AND (simpleJSONExtractString(labels, 'signoz.audit.resource.id') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"dashboard", "%signoz.audit.resource.kind%", "%signoz.audit.resource.kind\":\"dashboard%", "019b-5678-efgh-9012", "%signoz.audit.resource.id%", "%signoz.audit.resource.id\":\"019b-5678-efgh-9012%", uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 100},
|
||||
},
|
||||
},
|
||||
@@ -157,8 +157,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
Limit: 100,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (`attribute_string_signoz$$audit$$principal$$type` = ? AND `attribute_string_signoz$$audit$$principal$$type_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "service_account", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 100},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, event_name, attributes_string, attributes_number, attributes_bool, resource, scope_string FROM signoz_audit.distributed_logs WHERE (`attribute_string_signoz$$audit$$principal$$type` = ? AND `attribute_string_signoz$$audit$$principal$$type_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"service_account", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 100},
|
||||
},
|
||||
},
|
||||
// Scalar: alert — count forbidden errors (outcome + action AND)
|
||||
@@ -177,8 +177,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT count() AS __result_0 FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((`attribute_string_signoz$$audit$$outcome` = ? AND `attribute_string_signoz$$audit$$outcome_exists` = ?) AND (`attribute_string_signoz$$audit$$action` = ? AND `attribute_string_signoz$$audit$$action_exists` = ?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? ORDER BY __result_0 DESC",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "failure", true, "update", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448)},
|
||||
Query: "SELECT count() AS __result_0 FROM signoz_audit.distributed_logs WHERE ((`attribute_string_signoz$$audit$$outcome` = ? AND `attribute_string_signoz$$audit$$outcome_exists` = ?) AND (`attribute_string_signoz$$audit$$action` = ? AND `attribute_string_signoz$$audit$$action_exists` = ?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? ORDER BY __result_0 DESC",
|
||||
Args: []any{"failure", true, "update", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448)},
|
||||
},
|
||||
},
|
||||
// TimeSeries: failures grouped by principal email with top-N limit
|
||||
@@ -201,8 +201,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
Limit: 5,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_audit.distributed_logs_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(`attribute_string_signoz$$audit$$principal$$email_exists` = ?, `attribute_string_signoz$$audit$$principal$$email`, NULL)) AS `signoz.audit.principal.email`, count() AS __result_0 FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (`attribute_string_signoz$$audit$$outcome` = ? AND `attribute_string_signoz$$audit$$outcome_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `signoz.audit.principal.email` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toString(multiIf(`attribute_string_signoz$$audit$$principal$$email_exists` = ?, `attribute_string_signoz$$audit$$principal$$email`, NULL)) AS `signoz.audit.principal.email`, count() AS __result_0 FROM signoz_audit.distributed_logs WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (`attribute_string_signoz$$audit$$outcome` = ? AND `attribute_string_signoz$$audit$$outcome_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`signoz.audit.principal.email`) GLOBAL IN (SELECT `signoz.audit.principal.email` FROM __limit_cte) GROUP BY ts, `signoz.audit.principal.email`",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), true, "failure", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 5, true, "failure", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(`attribute_string_signoz$$audit$$principal$$email_exists` = ?, `attribute_string_signoz$$audit$$principal$$email`, NULL)) AS `signoz.audit.principal.email`, count() AS __result_0 FROM signoz_audit.distributed_logs WHERE (`attribute_string_signoz$$audit$$outcome` = ? AND `attribute_string_signoz$$audit$$outcome_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `signoz.audit.principal.email` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toString(multiIf(`attribute_string_signoz$$audit$$principal$$email_exists` = ?, `attribute_string_signoz$$audit$$principal$$email`, NULL)) AS `signoz.audit.principal.email`, count() AS __result_0 FROM signoz_audit.distributed_logs WHERE (`attribute_string_signoz$$audit$$outcome` = ? AND `attribute_string_signoz$$audit$$outcome_exists` = ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`signoz.audit.principal.email`) GLOBAL IN (SELECT `signoz.audit.principal.email` FROM __limit_cte) GROUP BY ts, `signoz.audit.principal.email`",
|
||||
Args: []any{true, "failure", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 5, true, "failure", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448)},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
testJSONQueryPrefix = "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body_v2 as body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND"
|
||||
testJSONQueryPrefix = "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body_v2 as body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE"
|
||||
testJSONQuerySuffix = "AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?"
|
||||
)
|
||||
|
||||
@@ -65,8 +65,8 @@ func TestJSONStmtBuilder_TimeSeries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf((dynamicElement(body_v2.`user.age`, 'Int64') IS NOT NULL), toString(dynamicElement(body_v2.`user.age`, 'Int64')), (dynamicElement(body_v2.`user.age`, 'String') IS NOT NULL), dynamicElement(body_v2.`user.age`, 'String'), NULL)) AS `user.age`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `user.age` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, toString(multiIf((dynamicElement(body_v2.`user.age`, 'Int64') IS NOT NULL), toString(dynamicElement(body_v2.`user.age`, 'Int64')), (dynamicElement(body_v2.`user.age`, 'String') IS NOT NULL), dynamicElement(body_v2.`user.age`, 'String'), NULL)) AS `user.age`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`user.age`) GLOBAL IN (SELECT `user.age` FROM __limit_cte) GROUP BY ts, `user.age`",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf((dynamicElement(body_v2.`user.age`, 'Int64') IS NOT NULL), toString(dynamicElement(body_v2.`user.age`, 'Int64')), (dynamicElement(body_v2.`user.age`, 'String') IS NOT NULL), dynamicElement(body_v2.`user.age`, 'String'), NULL)) AS `user.age`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `user.age` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, toString(multiIf((dynamicElement(body_v2.`user.age`, 'Int64') IS NOT NULL), toString(dynamicElement(body_v2.`user.age`, 'Int64')), (dynamicElement(body_v2.`user.age`, 'String') IS NOT NULL), dynamicElement(body_v2.`user.age`, 'String'), NULL)) AS `user.age`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`user.age`) GLOBAL IN (SELECT `user.age` FROM __limit_cte) GROUP BY ts, `user.age`",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448)},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -153,7 +153,7 @@ func TestStmtBuilderTimeSeriesBodyGroupByPromoted(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448)},
|
||||
Args: []any{ "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448)},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -193,7 +193,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "message Exists",
|
||||
expected: TestExpected{
|
||||
WhereClause: "body_v2.message <> ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -201,7 +201,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "message Contains 'Iron Award'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "LOWER(body_v2.message) LIKE LOWER(?)",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%Iron Award%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%Iron Award%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -211,7 +211,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name = 'x'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((dynamicElement(body_v2.`user.name`, 'String') = ?) AND has(JSONAllPaths(body_v2), 'user.name'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "x", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"x", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -219,7 +219,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.age Contains 25",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((LOWER(toString(dynamicElement(body_v2.`user.age`, 'Int64'))) LIKE LOWER(?)) AND has(JSONAllPaths(body_v2), 'user.age')) OR ((LOWER(dynamicElement(body_v2.`user.age`, 'String')) LIKE LOWER(?)) AND has(JSONAllPaths(body_v2), 'user.age')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%25%", "%25%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%25%", "%25%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{"Key `user.age` is ambiguous, found 2 different combinations of field context / data type: [name=user.age,context=body,datatype=int64,jsondatatype=Int64 name=user.age,context=body,datatype=string,jsondatatype=String]."},
|
||||
},
|
||||
},
|
||||
@@ -228,7 +228,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.height Contains 5.8",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((LOWER(toString(dynamicElement(body_v2.`user.height`, 'Float64'))) LIKE LOWER(?)) AND has(JSONAllPaths(body_v2), 'user.height'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%5.8%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%5.8%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -236,7 +236,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.height > 5.8",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((toFloat64(dynamicElement(body_v2.`user.height`, 'Float64')) > ?) AND has(JSONAllPaths(body_v2), 'user.height'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), 5.8, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{5.8, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -244,7 +244,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name LIKE '%ali%'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((dynamicElement(body_v2.`user.name`, 'String') LIKE ?) AND has(JSONAllPaths(body_v2), 'user.name'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%ali%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%ali%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -252,7 +252,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name REGEXP 'al.*'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((match(dynamicElement(body_v2.`user.name`, 'String'), ?)) AND has(JSONAllPaths(body_v2), 'user.name'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "al.*", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"al.*", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -260,7 +260,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.active != true",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(assumeNotNull(dynamicElement(body_v2.`user.active`, 'Bool')) <> ?)",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -268,7 +268,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.active NOT EXISTS",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(dynamicElement(body_v2.`user.active`, 'Bool') IS NULL)",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -276,7 +276,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name NOT CONTAINS 'IIT'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(LOWER(assumeNotNull(dynamicElement(body_v2.`user.name`, 'String'))) NOT LIKE LOWER(?))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -284,7 +284,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name NOT LIKE '%ali%'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(assumeNotNull(dynamicElement(body_v2.`user.name`, 'String')) NOT LIKE ?)",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%ali%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%ali%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -292,7 +292,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name NOT REGEXP 'al.*'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT match(assumeNotNull(dynamicElement(body_v2.`user.name`, 'String')), ?))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "al.*", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"al.*", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -300,7 +300,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name NOT IN ['alice', 'bob']",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(assumeNotNull(dynamicElement(body_v2.`user.name`, 'String')) NOT IN (?, ?))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "alice", "bob", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"alice", "bob", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -310,7 +310,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.address.zip NOT BETWEEN 100000 AND 999999",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(toFloat64(assumeNotNull(dynamicElement(body_v2.`user.address.zip`, 'Int64'))) NOT BETWEEN ? AND ?)",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), float64(100000), float64(999999), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(100000), float64(999999), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -361,7 +361,7 @@ func TestStatementBuilderListQueryBodyPromoted(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: TestExpected{
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "production", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{ "production", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -374,7 +374,7 @@ func TestStatementBuilderListQueryBodyPromoted(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: TestExpected{
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{ "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -387,7 +387,7 @@ func TestStatementBuilderListQueryBodyPromoted(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: TestExpected{
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{ "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -400,7 +400,7 @@ func TestStatementBuilderListQueryBodyPromoted(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: TestExpected{
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "Iron Award", "Iron Award", "Iron Award", "Iron Award", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{ "Iron Award", "Iron Award", "Iron Award", "Iron Award", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -413,7 +413,7 @@ func TestStatementBuilderListQueryBodyPromoted(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: TestExpected{
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%1.65%", 1.65, "%1.65%", 1.65, "%1.65%", 1.65, "%1.65%", 1.65, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{ "%1.65%", 1.65, "%1.65%", 1.65, "%1.65%", 1.65, "%1.65%", 1.65, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64,materialized=true,jsondatatype=Array(Nullable(Float64)) name=education[].parameters,context=body,datatype=[]dynamic,materialized=true,jsondatatype=Array(Dynamic)]."},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -427,7 +427,7 @@ func TestStatementBuilderListQueryBodyPromoted(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: TestExpected{
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%IIT%", "%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{ "%IIT%", "%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -440,7 +440,7 @@ func TestStatementBuilderListQueryBodyPromoted(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: TestExpected{
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%true%", true, "%true%", true, "%true%", true, "%true%", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{ "%true%", true, "%true%", true, "%true%", true, "%true%", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64,materialized=true,jsondatatype=Array(Nullable(Float64)) name=education[].parameters,context=body,datatype=[]dynamic,materialized=true,jsondatatype=Array(Dynamic)]."},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -454,7 +454,7 @@ func TestStatementBuilderListQueryBodyPromoted(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: TestExpected{
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%passed%", "passed", "%passed%", "passed", "%passed%", "passed", "%passed%", "passed", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{ "%passed%", "passed", "%passed%", "passed", "%passed%", "passed", "%passed%", "passed", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64,materialized=true,jsondatatype=Array(Nullable(Float64)) name=education[].parameters,context=body,datatype=[]dynamic,materialized=true,jsondatatype=Array(Dynamic)]."},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -468,7 +468,7 @@ func TestStatementBuilderListQueryBodyPromoted(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: TestExpected{
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "sports", "sports", "sports", "sports", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{ "sports", "sports", "sports", "sports", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -481,7 +481,7 @@ func TestStatementBuilderListQueryBodyPromoted(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: TestExpected{
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%Civil%", "%Civil%", "%Civil%", "%Civil%", "%Civil%", "%Civil%", "%Civil%", "%Civil%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{ "%Civil%", "%Civil%", "%Civil%", "%Civil%", "%Civil%", "%Civil%", "%Civil%", "%Civil%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -524,7 +524,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].name Exists",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(arrayExists(`body_v2.education`-> dynamicElement(`body_v2.education`.`name`, 'String') IS NOT NULL, dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -532,7 +532,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].name Contains 'IIT'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> LOWER(dynamicElement(`body_v2.education`.`name`, 'String')) LIKE LOWER(?), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -540,7 +540,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].year Contains 2020",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> LOWER(toString(dynamicElement(`body_v2.education`.`year`, 'Int64'))) LIKE LOWER(?), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%2020%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%2020%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -548,7 +548,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].type != '10001'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((NOT arrayExists(`body_v2.education`-> toFloat64OrNull(dynamicElement(`body_v2.education`.`type`, 'String')) = ?, dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND (NOT arrayExists(`body_v2.education`-> dynamicElement(`body_v2.education`.`type`, 'Int64') = ?, dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), int64(10001), int64(10001), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{int64(10001), int64(10001), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{"Key `education[].type` is ambiguous, found 2 different combinations of field context / data type: [name=education[].type,context=body,datatype=string,jsondatatype=String name=education[].type,context=body,datatype=int64,jsondatatype=Int64]."},
|
||||
},
|
||||
},
|
||||
@@ -557,7 +557,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].name NOT EXISTS",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.education`-> dynamicElement(`body_v2.education`.`name`, 'String') IS NOT NULL, dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -565,7 +565,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].name NOT CONTAINS 'IIT'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.education`-> LOWER(dynamicElement(`body_v2.education`.`name`, 'String')) LIKE LOWER(?), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -575,7 +575,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].parameters Contains 1.65",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((arrayExists(`body_v2.education`-> (arrayExists(x -> LOWER(toString(x)) LIKE LOWER(?), dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))')) OR arrayExists(x -> toFloat64(x) = ?, dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))'))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')) OR ((arrayExists(`body_v2.education`-> (arrayExists(x -> LOWER(toString(x)) LIKE LOWER(?), arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)'))) OR arrayExists(x -> accurateCastOrNull(x, 'Float64') = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)')))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%1.65%", 1.65, "%1.65%", 1.65, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%1.65%", 1.65, "%1.65%", 1.65, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64,jsondatatype=Array(Nullable(Float64)) name=education[].parameters,context=body,datatype=[]dynamic,jsondatatype=Array(Dynamic)]."},
|
||||
},
|
||||
},
|
||||
@@ -584,7 +584,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].parameters = 'passed'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((arrayExists(`body_v2.education`-> arrayExists(x -> toString(x) = ?, dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))')), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')) OR ((arrayExists(`body_v2.education`-> arrayExists(x -> toString(x) = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)'))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "passed", "passed", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"passed", "passed", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64,jsondatatype=Array(Nullable(Float64)) name=education[].parameters,context=body,datatype=[]dynamic,jsondatatype=Array(Dynamic)]."},
|
||||
},
|
||||
},
|
||||
@@ -593,7 +593,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].parameters Contains 'passed'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((arrayExists(`body_v2.education`-> (arrayExists(x -> LOWER(toString(x)) LIKE LOWER(?), dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))')) OR arrayExists(x -> toString(x) = ?, dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))'))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')) OR ((arrayExists(`body_v2.education`-> (arrayExists(x -> LOWER(toString(x)) LIKE LOWER(?), arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)'))) OR arrayExists(x -> toString(x) = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)')))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%passed%", "passed", "%passed%", "passed", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%passed%", "passed", "%passed%", "passed", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64,jsondatatype=Array(Nullable(Float64)) name=education[].parameters,context=body,datatype=[]dynamic,jsondatatype=Array(Dynamic)]."},
|
||||
},
|
||||
},
|
||||
@@ -602,7 +602,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].parameters IN [1.65, 1.99]",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((arrayExists(`body_v2.education`-> arrayExists(x -> toFloat64(x) IN (?, ?), dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))')), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')) OR ((arrayExists(`body_v2.education`-> arrayExists(x -> toString(x) IN (?, ?), arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)'))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), 1.65, 1.99, "1.65", "1.99", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{1.65, 1.99, "1.65", "1.99", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64,jsondatatype=Array(Nullable(Float64)) name=education[].parameters,context=body,datatype=[]dynamic,jsondatatype=Array(Dynamic)]."},
|
||||
},
|
||||
},
|
||||
@@ -611,7 +611,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].parameters NOT CONTAINS 1.65",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((NOT arrayExists(`body_v2.education`-> (arrayExists(x -> LOWER(toString(x)) LIKE LOWER(?), dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))')) OR arrayExists(x -> toFloat64(x) = ?, dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))'))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND (NOT arrayExists(`body_v2.education`-> (arrayExists(x -> LOWER(toString(x)) LIKE LOWER(?), arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)'))) OR arrayExists(x -> accurateCastOrNull(x, 'Float64') = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)')))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%1.65%", float64(1.65), "%1.65%", float64(1.65), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%1.65%", float64(1.65), "%1.65%", float64(1.65), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64,jsondatatype=Array(Nullable(Float64)) name=education[].parameters,context=body,datatype=[]dynamic,jsondatatype=Array(Dynamic)]."},
|
||||
},
|
||||
},
|
||||
@@ -620,7 +620,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "has(body.education[].parameters, 1.65)",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(has(arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))'), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))), ?) OR has(arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)'), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))), ?))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), 1.65, 1.65, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{1.65, 1.65, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{
|
||||
"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64,jsondatatype=Array(Nullable(Float64)) name=education[].parameters,context=body,datatype=[]dynamic,jsondatatype=Array(Dynamic)].",
|
||||
},
|
||||
@@ -631,7 +631,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "hasAll(body.user.permissions, ['read', 'write'])",
|
||||
expected: TestExpected{
|
||||
WhereClause: "hasAll(dynamicElement(body_v2.`user.permissions`, 'Array(Nullable(String))'), ?)",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), []any{"read", "write"}, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{[]any{"read", "write"}, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -641,7 +641,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.http-events[].request-info.host != 'example.com'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.http-events`-> dynamicElement(`body_v2.http-events`.`request-info.host`, 'String') = ?, dynamicElement(body_v2.`http-events`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "example.com", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"example.com", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -651,7 +651,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].awards[].name Exists",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`name`, 'String') IS NOT NULL, dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`name`, 'String') IS NOT NULL, arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -659,7 +659,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].awards[].name = 'Iron Award'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`name`, 'String') = ?, dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`name`, 'String') = ?, arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "Iron Award", "Iron Award", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"Iron Award", "Iron Award", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -667,7 +667,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "education[].awards[].semester BETWEEN 2 AND 4",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> toFloat64(dynamicElement(`body_v2.education[].awards`.`semester`, 'Int64')) BETWEEN ? AND ?, dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> toFloat64(dynamicElement(`body_v2.education[].awards`.`semester`, 'Int64')) BETWEEN ? AND ?, arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), float64(2), float64(4), float64(2), float64(4), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(2), float64(4), float64(2), float64(4), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -675,7 +675,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "education[].awards[].semester IN [2, 4]",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> toFloat64(dynamicElement(`body_v2.education[].awards`.`semester`, 'Int64')) IN (?, ?), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> toFloat64(dynamicElement(`body_v2.education[].awards`.`semester`, 'Int64')) IN (?, ?), arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), float64(2), float64(4), float64(2), float64(4), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(2), float64(4), float64(2), float64(4), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -683,7 +683,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].awards[].type != 'sports'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`type`, 'String') = ?, dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`type`, 'String') = ?, arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "sports", "sports", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"sports", "sports", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -691,7 +691,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].awards[].name NOT EXISTS",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`name`, 'String') IS NOT NULL, dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`name`, 'String') IS NOT NULL, arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -701,7 +701,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.interests[].entities[].reviews[].entries[].metadata[].positions[].ratings Contains 4",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((arrayExists(`body_v2.interests`-> arrayExists(`body_v2.interests[].entities`-> arrayExists(`body_v2.interests[].entities[].reviews`-> arrayExists(`body_v2.interests[].entities[].reviews[].entries`-> arrayExists(`body_v2.interests[].entities[].reviews[].entries[].metadata`-> arrayExists(`body_v2.interests[].entities[].reviews[].entries[].metadata[].positions`-> (arrayExists(x -> LOWER(toString(x)) LIKE LOWER(?), dynamicElement(`body_v2.interests[].entities[].reviews[].entries[].metadata[].positions`.`ratings`, 'Array(Nullable(Int64))')) OR arrayExists(x -> toFloat64(x) = ?, dynamicElement(`body_v2.interests[].entities[].reviews[].entries[].metadata[].positions`.`ratings`, 'Array(Nullable(Int64))'))), dynamicElement(`body_v2.interests[].entities[].reviews[].entries[].metadata`.`positions`, 'Array(JSON(max_dynamic_types=0, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests[].entities[].reviews[].entries`.`metadata`, 'Array(JSON(max_dynamic_types=1, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests[].entities[].reviews`.`entries`, 'Array(JSON(max_dynamic_types=2, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests[].entities`.`reviews`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests`.`entities`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), dynamicElement(body_v2.`interests`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'interests')) OR ((arrayExists(`body_v2.interests`-> arrayExists(`body_v2.interests[].entities`-> arrayExists(`body_v2.interests[].entities[].reviews`-> arrayExists(`body_v2.interests[].entities[].reviews[].entries`-> arrayExists(`body_v2.interests[].entities[].reviews[].entries[].metadata`-> arrayExists(`body_v2.interests[].entities[].reviews[].entries[].metadata[].positions`-> (arrayExists(x -> LOWER(x) LIKE LOWER(?), dynamicElement(`body_v2.interests[].entities[].reviews[].entries[].metadata[].positions`.`ratings`, 'Array(Nullable(String))')) OR arrayExists(x -> toFloat64OrNull(x) = ?, dynamicElement(`body_v2.interests[].entities[].reviews[].entries[].metadata[].positions`.`ratings`, 'Array(Nullable(String))'))), dynamicElement(`body_v2.interests[].entities[].reviews[].entries[].metadata`.`positions`, 'Array(JSON(max_dynamic_types=0, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests[].entities[].reviews[].entries`.`metadata`, 'Array(JSON(max_dynamic_types=1, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests[].entities[].reviews`.`entries`, 'Array(JSON(max_dynamic_types=2, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests[].entities`.`reviews`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests`.`entities`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), dynamicElement(body_v2.`interests`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'interests')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%4%", float64(4), "%4%", float64(4), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%4%", float64(4), "%4%", float64(4), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{"Key `interests[].entities[].reviews[].entries[].metadata[].positions[].ratings` is ambiguous, found 2 different combinations of field context / data type: [name=interests[].entities[].reviews[].entries[].metadata[].positions[].ratings,context=body,datatype=[]int64,jsondatatype=Array(Nullable(Int64)) name=interests[].entities[].reviews[].entries[].metadata[].positions[].ratings,context=body,datatype=[]string,jsondatatype=Array(Nullable(String))]."},
|
||||
},
|
||||
},
|
||||
@@ -710,7 +710,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].awards[].participated[].team[].branch Contains 'Civil'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> (arrayExists(`body_v2.education[].awards[].participated`-> arrayExists(`body_v2.education[].awards[].participated[].team`-> LOWER(dynamicElement(`body_v2.education[].awards[].participated[].team`.`branch`, 'String')) LIKE LOWER(?), dynamicElement(`body_v2.education[].awards[].participated`.`team`, 'Array(JSON(max_dynamic_types=2, max_dynamic_paths=0))')), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards[].participated`-> arrayExists(`body_v2.education[].awards[].participated[].team`-> LOWER(dynamicElement(`body_v2.education[].awards[].participated[].team`.`branch`, 'String')) LIKE LOWER(?), dynamicElement(`body_v2.education[].awards[].participated`.`team`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')), arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> (arrayExists(`body_v2.education[].awards[].participated`-> arrayExists(`body_v2.education[].awards[].participated[].team`-> LOWER(dynamicElement(`body_v2.education[].awards[].participated[].team`.`branch`, 'String')) LIKE LOWER(?), dynamicElement(`body_v2.education[].awards[].participated`.`team`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=64))')), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')) OR arrayExists(`body_v2.education[].awards[].participated`-> arrayExists(`body_v2.education[].awards[].participated[].team`-> LOWER(dynamicElement(`body_v2.education[].awards[].participated[].team`.`branch`, 'String')) LIKE LOWER(?), dynamicElement(`body_v2.education[].awards[].participated`.`team`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')), arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%Civil%", "%Civil%", "%Civil%", "%Civil%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%Civil%", "%Civil%", "%Civil%", "%Civil%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -719,7 +719,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].scores IN [90, 95]",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> arrayExists(x -> toFloat64(x) IN (?, ?), dynamicElement(`body_v2.education`.`scores`, 'Array(Nullable(Int64))')), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), float64(90), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(90), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -729,7 +729,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].scores NOT IN [90, 95]",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.education`-> arrayExists(x -> toFloat64(x) IN (?, ?), dynamicElement(`body_v2.education`.`scores`, 'Array(Nullable(Int64))')), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), float64(90), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(90), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -739,7 +739,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].scores BETWEEN 80 AND 95",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> arrayExists(x -> toFloat64(x) BETWEEN ? AND ?, dynamicElement(`body_v2.education`.`scores`, 'Array(Nullable(Int64))')), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), float64(80), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(80), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -749,7 +749,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].scores NOT BETWEEN 80 AND 95",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.education`-> arrayExists(x -> toFloat64(x) BETWEEN ? AND ?, dynamicElement(`body_v2.education`.`scores`, 'Array(Nullable(Int64))')), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), float64(80), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(80), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -757,7 +757,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "hasAny(education[].awards[].participated[].members, ['Piyush', 'Tushar'])",
|
||||
expected: TestExpected{
|
||||
WhereClause: "hasAny(arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->arrayConcat(arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))), ?)",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), []any{"Piyush", "Tushar"}, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{[]any{"Piyush", "Tushar"}, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -765,7 +765,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "ids Contains 1",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((arrayExists(x -> LOWER(toString(x)) LIKE LOWER(?), arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(body_v2.`ids`, 'Array(Dynamic)'))) OR arrayExists(x -> accurateCastOrNull(x, 'Float64') = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(body_v2.`ids`, 'Array(Dynamic)'))))) AND has(JSONAllPaths(body_v2), 'ids'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%1%", float64(1), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%1%", float64(1), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -773,7 +773,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "ids != '1'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(x -> accurateCastOrNull(x, 'Float64') = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(body_v2.`ids`, 'Array(Dynamic)'))))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), int64(1), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{int64(1), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -781,7 +781,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "ids = true",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(x -> accurateCastOrNull(x, 'Bool') = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(body_v2.`ids`, 'Array(Dynamic)')))) AND has(JSONAllPaths(body_v2), 'ids'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -829,7 +829,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "((assumeNotNull(dynamicElement(body_v2.`user.name`, 'String')) = ?) AND has(JSONAllPaths(body_v2), 'user.name'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "alice", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"alice", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -841,7 +841,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "((toFloat64(assumeNotNull(dynamicElement(body_v2.`user.address.zip`, 'Int64'))) = ?) AND has(JSONAllPaths(body_v2), 'user.address.zip'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), float64(110001), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(110001), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
// ── indexed exists: emits assumeNotNull != nil AND dynamicElement IS NOT NULL ─
|
||||
@@ -854,7 +854,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "((assumeNotNull(dynamicElement(body_v2.`user.name`, 'String')) <> ? AND dynamicElement(body_v2.`user.name`, 'String') IS NOT NULL))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
// ── indexed not-equal: assumeNotNull wrapping + no path index ─────────
|
||||
@@ -867,7 +867,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "(assumeNotNull(dynamicElement(body_v2.`user.name`, 'String')) <> ?)",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "alice", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"alice", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
// ── indexed not-exists: assumeNotNull = "" AND assumeNotNull IS NOT NULL ─
|
||||
@@ -883,7 +883,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "((assumeNotNull(dynamicElement(body_v2.`user.name`, 'String')) = ? AND dynamicElement(body_v2.`user.name`, 'String') IS NULL))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -897,7 +897,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((toFloat64(assumeNotNull(dynamicElement(body_v2.`http-status`, 'Int64'))) = ?) AND has(JSONAllPaths(body_v2), 'http-status')) OR ((toFloat64OrNull(assumeNotNull(dynamicElement(body_v2.`http-status`, 'String'))) = ?) AND has(JSONAllPaths(body_v2), 'http-status')))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), float64(200), float64(200), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(200), float64(200), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -909,7 +909,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "((toFloat64(assumeNotNull(dynamicElement(body_v2.`response.time-taken`, 'Float64'))) > ?) AND has(JSONAllPaths(body_v2), 'response.time-taken'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), float64(1.5), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(1.5), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
// ── Bool is NOT IndexSupported — index branch must be skipped ─────────
|
||||
@@ -923,7 +923,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "((dynamicElement(body_v2.`user.active`, 'Bool') = ?) AND has(JSONAllPaths(body_v2), 'user.active'))",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -701,6 +701,9 @@ func (b *logQueryStatementBuilder) maybeAttachResourceFilter(
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if stmt == nil {
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
sb.Where("resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter)")
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ func TestStatementBuilderTimeSeries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(resource.`service.name`::String IS NOT NULL, resource.`service.name`::String, NULL)) AS `service.name`, countDistinct(multiIf(resource.`service.name`::String IS NOT NULL, resource.`service.name`::String, NULL)) AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, toString(multiIf(resource.`service.name`::String IS NOT NULL, resource.`service.name`::String, NULL)) AS `service.name`, countDistinct(multiIf(resource.`service.name`::String IS NOT NULL, resource.`service.name`::String, NULL)) AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(resource.`service.name`::String IS NOT NULL, resource.`service.name`::String, NULL)) AS `service.name`, countDistinct(multiIf(resource.`service.name`::String IS NOT NULL, resource.`service.name`::String, NULL)) AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, toString(multiIf(resource.`service.name`::String IS NOT NULL, resource.`service.name`::String, NULL)) AS `service.name`, countDistinct(multiIf(resource.`service.name`::String IS NOT NULL, resource.`service.name`::String, NULL)) AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1705397400), uint64(1705485600), "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), 10, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -86,8 +86,8 @@ func TestStatementBuilderTimeSeries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, countDistinct(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL) OR (attributes_string['http.method'] = ? AND mapContains(attributes_string, 'http.method') = ?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, countDistinct(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL) OR (attributes_string['http.method'] = ? AND mapContains(attributes_string, 'http.method') = ?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{uint64(1705224600), uint64(1705485600), "redis-manual", "GET", true, "1705226400000000000", uint64(1705224600), "1705485600000000000", uint64(1705485600), 10, "redis-manual", "GET", true, "1705226400000000000", uint64(1705224600), "1705485600000000000", uint64(1705485600)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, countDistinct(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE ((multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL) OR (attributes_string['http.method'] = ? AND mapContains(attributes_string, 'http.method') = ?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, countDistinct(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE ((multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL) OR (attributes_string['http.method'] = ? AND mapContains(attributes_string, 'http.method') = ?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "GET", true, "1705226400000000000", uint64(1705224600), "1705485600000000000", uint64(1705485600), 10, "redis-manual", "GET", true, "1705226400000000000", uint64(1705224600), "1705485600000000000", uint64(1705485600)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -127,7 +127,7 @@ func TestStatementBuilderTimeSeries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(resource.`service.name`::String IS NOT NULL, resource.`service.name`::String, NULL)) AS `service.name`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY `service.name` desc LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, toString(multiIf(resource.`service.name`::String IS NOT NULL, resource.`service.name`::String, NULL)) AS `service.name`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name` ORDER BY `service.name` desc, ts desc",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(resource.`service.name`::String IS NOT NULL, resource.`service.name`::String, NULL)) AS `service.name`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY `service.name` desc LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, toString(multiIf(resource.`service.name`::String IS NOT NULL, resource.`service.name`::String, NULL)) AS `service.name`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name` ORDER BY `service.name` desc, ts desc",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1705397400), uint64(1705485600), "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), 10, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -160,7 +160,7 @@ func TestStatementBuilderTimeSeries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(`attribute_string_materialized$$key$$name_exists` = ?, `attribute_string_materialized$$key$$name`, NULL)) AS `materialized.key.name`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `materialized.key.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, toString(multiIf(`attribute_string_materialized$$key$$name_exists` = ?, `attribute_string_materialized$$key$$name`, NULL)) AS `materialized.key.name`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`materialized.key.name`) GLOBAL IN (SELECT `materialized.key.name` FROM __limit_cte) GROUP BY ts, `materialized.key.name`",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(`attribute_string_materialized$$key$$name_exists` = ?, `attribute_string_materialized$$key$$name`, NULL)) AS `materialized.key.name`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY `materialized.key.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, toString(multiIf(`attribute_string_materialized$$key$$name_exists` = ?, `attribute_string_materialized$$key$$name`, NULL)) AS `materialized.key.name`, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? AND (`materialized.key.name`) GLOBAL IN (SELECT `materialized.key.name` FROM __limit_cte) GROUP BY ts, `materialized.key.name`",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1705397400), uint64(1705485600), true, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), 10, true, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600)},
|
||||
},
|
||||
},
|
||||
@@ -183,8 +183,8 @@ func TestStatementBuilderTimeSeries(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((match(`attribute_string_materialized$$key$$name`, ?) AND `attribute_string_materialized$$key$$name_exists` = ?) OR (`attribute_string_materialized$$key$$name` = ? AND `attribute_string_materialized$$key$$name_exists` = ?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY ts",
|
||||
Args: []any{uint64(1705397400), uint64(1705485600), "redis.*", true, "memcached", true, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600)},
|
||||
Query: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 SECOND) AS ts, count() AS __result_0 FROM signoz_logs.distributed_logs_v2 WHERE ((match(`attribute_string_materialized$$key$$name`, ?) AND `attribute_string_materialized$$key$$name_exists` = ?) OR (`attribute_string_materialized$$key$$name` = ? AND `attribute_string_materialized$$key$$name_exists` = ?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? GROUP BY ts",
|
||||
Args: []any{"redis.*", true, "memcached", true, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -249,7 +249,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -277,7 +277,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? ORDER BY `attribute_string_materialized$$key$$name` AS `materialized.key.name` desc LIMIT ?",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? ORDER BY `attribute_string_materialized$$key$$name` AS `materialized.key.name` desc LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -305,8 +305,8 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((match(`attribute_string_materialized$$key$$name`, ?) AND `attribute_string_materialized$$key$$name_exists` = ?) OR (`attribute_string_materialized$$key$$name` = ? AND `attribute_string_materialized$$key$$name_exists` = ?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? ORDER BY `attribute_string_materialized$$key$$name` AS `materialized.key.name` desc LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "redis.*", true, "memcached", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE ((match(`attribute_string_materialized$$key$$name`, ?) AND `attribute_string_materialized$$key$$name_exists` = ?) OR (`attribute_string_materialized$$key$$name` = ? AND `attribute_string_materialized$$key$$name_exists` = ?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? ORDER BY `attribute_string_materialized$$key$$name` AS `materialized.key.name` desc LIMIT ?",
|
||||
Args: []any{"redis.*", true, "memcached", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -370,8 +370,8 @@ func TestStatementBuilderListQueryResourceTests(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND match(LOWER(body), LOWER(?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "hello", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE match(LOWER(body), LOWER(?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"hello", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -414,8 +414,8 @@ func TestStatementBuilderListQueryResourceTests(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (JSON_VALUE(body, '$.\"status\"') = ? AND JSON_EXISTS(body, '$.\"status\"')) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "success", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE (JSON_VALUE(body, '$.\"status\"') = ? AND JSON_EXISTS(body, '$.\"status\"')) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"success", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -430,8 +430,8 @@ func TestStatementBuilderListQueryResourceTests(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((JSONExtract(JSON_QUERY(body, '$.\"user_names\"[*]'), 'Array(String)') = ?) AND JSON_EXISTS(body, '$.\"user_names\"[*]')) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "john_doe", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE ((JSONExtract(JSON_QUERY(body, '$.\"user_names\"[*]'), 'Array(String)') = ?) AND JSON_EXISTS(body, '$.\"user_names\"[*]')) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"john_doe", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -446,8 +446,8 @@ func TestStatementBuilderListQueryResourceTests(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND has(JSONExtract(JSON_QUERY(body, '$.\"user_names\"[*]'), 'Array(String)'), ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "john_doe", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE has(JSONExtract(JSON_QUERY(body, '$.\"user_names\"[*]'), 'Array(String)'), ?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"john_doe", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -900,8 +900,8 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
},
|
||||
enableBodyJSONQuery: true,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body_v2 as body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND body_v2.message <> ? AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body_v2 as body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE body_v2.message <> ? AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{bodySearchDefaultWarning},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -916,8 +916,8 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
},
|
||||
enableBodyJSONQuery: false,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND body <> ? AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE body <> ? AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -931,8 +931,8 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
},
|
||||
enableBodyJSONQuery: true,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body_v2 as body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND body_v2.message = ? AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body_v2 as body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE body_v2.message = ? AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{bodySearchDefaultWarning},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -947,8 +947,8 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
},
|
||||
enableBodyJSONQuery: false,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND body = ? AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE body = ? AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -962,8 +962,8 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
},
|
||||
enableBodyJSONQuery: true,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body_v2 as body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND LOWER(body_v2.message) LIKE LOWER(?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%error%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body_v2 as body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE LOWER(body_v2.message) LIKE LOWER(?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"%error%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Warnings: []string{bodySearchDefaultWarning},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -978,8 +978,8 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
},
|
||||
enableBodyJSONQuery: false,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND LOWER(body) LIKE LOWER(?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "%error%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE LOWER(body) LIKE LOWER(?) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"%error%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -1051,8 +1051,8 @@ func TestStmtBuilderBodyFullTextSearch(t *testing.T) {
|
||||
},
|
||||
enableBodyJSONQuery: true,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body_v2 as body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND match(LOWER(body_v2.message), LOWER(?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "error", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body_v2 as body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE match(LOWER(body_v2.message), LOWER(?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"error", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -1066,8 +1066,8 @@ func TestStmtBuilderBodyFullTextSearch(t *testing.T) {
|
||||
},
|
||||
enableBodyJSONQuery: false,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND match(LOWER(body), LOWER(?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "error", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string FROM signoz_logs.distributed_logs_v2 WHERE match(LOWER(body), LOWER(?)) AND timestamp >= ? AND ts_bucket_start >= ? AND timestamp < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"error", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
|
||||
@@ -1330,11 +1330,12 @@ func (t *telemetryMetaStore) getRelatedValues(ctx context.Context, fieldValueSel
|
||||
ConditionBuilder: t.conditionBuilder,
|
||||
FieldKeys: keys,
|
||||
})
|
||||
if err == nil {
|
||||
sb.AddWhereClause(whereClause.WhereClause)
|
||||
} else {
|
||||
if err != nil {
|
||||
t.logger.WarnContext(ctx, "error parsing existing query for related values", errors.Attr(err))
|
||||
}
|
||||
if whereClause != nil {
|
||||
sb.AddWhereClause(whereClause.WhereClause)
|
||||
}
|
||||
}
|
||||
|
||||
if fieldValueSelector.StartUnixMilli != 0 {
|
||||
|
||||
@@ -102,9 +102,13 @@ func (b *resourceFilterStatementBuilder[T]) Build(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := b.addConditions(ctx, q, start, end, query, keys, variables); err != nil {
|
||||
isNoOp, err := b.addConditions(ctx, q, start, end, query, keys, variables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isNoOp {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
stmt, args := q.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
return &qbtypes.Statement{
|
||||
@@ -114,6 +118,8 @@ func (b *resourceFilterStatementBuilder[T]) Build(
|
||||
}
|
||||
|
||||
// addConditions adds both filter and time conditions to the query.
|
||||
// Returns true (isNoOp) when the filter expression evaluated to no resource conditions,
|
||||
// meaning the CTE would select all fingerprints and should be skipped entirely.
|
||||
func (b *resourceFilterStatementBuilder[T]) addConditions(
|
||||
ctx context.Context,
|
||||
sb *sqlbuilder.SelectBuilder,
|
||||
@@ -121,7 +127,7 @@ func (b *resourceFilterStatementBuilder[T]) addConditions(
|
||||
query qbtypes.QueryBuilderQuery[T],
|
||||
keys map[string][]*telemetrytypes.TelemetryFieldKey,
|
||||
variables map[string]qbtypes.VariableItem,
|
||||
) error {
|
||||
) (bool, error) {
|
||||
// Add filter condition if present
|
||||
if query.Filter != nil && query.Filter.Expression != "" {
|
||||
|
||||
@@ -144,16 +150,22 @@ func (b *resourceFilterStatementBuilder[T]) addConditions(
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
if filterWhereClause != nil {
|
||||
sb.AddWhereClause(filterWhereClause.WhereClause)
|
||||
if filterWhereClause == nil {
|
||||
// this means all conditions evaluated to no-op (non-resource fields, unknown keys, skipped full-text/functions)
|
||||
// the CTE would select all fingerprints, so skip it entirely
|
||||
return true, nil
|
||||
}
|
||||
sb.AddWhereClause(filterWhereClause.WhereClause)
|
||||
} else {
|
||||
// No filter expression means we would select all fingerprints — skip the CTE.
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Add time filter
|
||||
b.addTimeFilter(sb, start, end)
|
||||
return nil
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// addTimeFilter adds time-based filtering conditions.
|
||||
|
||||
@@ -104,7 +104,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
query qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]
|
||||
start uint64
|
||||
end uint64
|
||||
expected qbtypes.Statement
|
||||
expected *qbtypes.Statement
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
@@ -117,7 +117,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -132,7 +132,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE ((simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND (simpleJSONExtractString(labels, 'k8s.namespace.name') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", "production", "%k8s.namespace.name%", "%k8s.namespace.name\":\"production%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -145,12 +145,9 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
Expression: "service.name = 'redis-manual' OR http.request.method = 'GET'",
|
||||
},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil, // OR with non-resource field: entire CTE is skipped
|
||||
},
|
||||
{
|
||||
name: "resource filter with empty filter expression",
|
||||
@@ -160,12 +157,9 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
Expression: "",
|
||||
},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil, // no filter: CTE is skipped
|
||||
},
|
||||
{
|
||||
name: "resource filter with nil filter",
|
||||
@@ -173,12 +167,9 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
Signal: telemetrytypes.SignalTraces,
|
||||
Filter: nil,
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil, // no filter: CTE is skipped
|
||||
},
|
||||
{
|
||||
name: "resource filter with LIKE operator",
|
||||
@@ -190,7 +181,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (LOWER(simpleJSONExtractString(labels, 'service.name')) LIKE LOWER(?) AND labels LIKE ? AND LOWER(labels) LIKE LOWER(?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"redis%", "%service.name%", "%service.name%redis%%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -205,7 +196,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONHas(labels, 'service.name') = ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{true, "%service.name%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -220,7 +211,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONHas(labels, 'service.name') <> ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{true, expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -235,7 +226,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE ((simpleJSONExtractString(labels, 'service.name') = ? OR simpleJSONExtractString(labels, 'service.name') = ?) AND labels LIKE ? AND (labels LIKE ? OR labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"redis", "postgres", "%service.name%", "%service.name\":\"redis%", "%service.name\":\"postgres%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -250,7 +241,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE ((simpleJSONExtractString(labels, 'service.name') <> ? AND simpleJSONExtractString(labels, 'service.name') <> ?) AND (labels NOT LIKE ? AND labels NOT LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"redis", "postgres", "%service.name\":\"redis%", "%service.name\":\"postgres%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -265,7 +256,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (LOWER(simpleJSONExtractString(labels, 'service.name')) LIKE LOWER(?) AND labels LIKE ? AND LOWER(labels) LIKE LOWER(?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"%redis%", "%service.name%", "%service.name%redis%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -280,7 +271,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (match(simpleJSONExtractString(labels, 'service.name'), ?) AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"redis.*", "%service.name%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -295,25 +286,22 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') <> ? AND labels NOT LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"redis", "%service.name\":\"redis%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "resource filter with attribute-only filter (should return true)",
|
||||
name: "resource filter with attribute-only filter",
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
||||
Signal: telemetrytypes.SignalTraces,
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: "http.request.method = 'POST'",
|
||||
},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil, // only non-resource fields: CTE is skipped
|
||||
},
|
||||
{
|
||||
name: "resource filter with zero end time",
|
||||
@@ -325,7 +313,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: 0,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ?",
|
||||
Args: []any{"redis", "%service.name%", "%service.name\":\"redis%", expectedBucketStart},
|
||||
},
|
||||
@@ -340,7 +328,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE NOT (((simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?))) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"redis", "%service.name%", "%service.name\":\"redis%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -350,20 +338,14 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
||||
Signal: telemetrytypes.SignalTraces,
|
||||
Filter: &qbtypes.Filter{
|
||||
// http.request.method is an attribute field, not a resource field
|
||||
// so the condition returns "true", and NOT should also return "true" (not "NOT (true)")
|
||||
// In this system, SkipConditionLiteral means "this condition is not evaluable here"
|
||||
// and the negation of "not evaluable" is also "not evaluable",
|
||||
// so true is the right no-op. Returning false would incorrectly exclude all rows.
|
||||
// http.request.method is an attribute field, not a resource field.
|
||||
// NOT of a non-evaluable condition is also non-evaluable: CTE is skipped.
|
||||
Expression: "NOT (http.request.method = 'GET')",
|
||||
},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil, // only non-resource fields: CTE is skipped
|
||||
},
|
||||
}
|
||||
|
||||
@@ -390,8 +372,13 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
|
||||
require.Contains(t, err.Error(), c.expectedErr.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c.expected.Query, stmt.Query)
|
||||
require.Equal(t, c.expected.Args, stmt.Args)
|
||||
if c.expected == nil {
|
||||
require.Nil(t, stmt)
|
||||
} else {
|
||||
require.NotNil(t, stmt)
|
||||
require.Equal(t, c.expected.Query, stmt.Query)
|
||||
require.Equal(t, c.expected.Args, stmt.Args)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -403,7 +390,7 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
|
||||
query qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]
|
||||
start uint64
|
||||
end uint64
|
||||
expected qbtypes.Statement
|
||||
expected *qbtypes.Statement
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
@@ -416,7 +403,7 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -427,12 +414,9 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Filter: nil,
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil, // no filter: CTE is skipped
|
||||
},
|
||||
{
|
||||
name: "resource filter with empty filter expression for logs",
|
||||
@@ -442,12 +426,9 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
|
||||
Expression: "",
|
||||
},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil, // no filter: CTE is skipped
|
||||
},
|
||||
{
|
||||
name: "resource filter with multiple conditions for logs",
|
||||
@@ -459,7 +440,7 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE ((simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND (simpleJSONExtractString(labels, 'k8s.namespace.name') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"redis", "%service.name%", "%service.name\":\"redis%", "default", "%k8s.namespace.name%", "%k8s.namespace.name\":\"default%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -470,14 +451,14 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Filter: &qbtypes.Filter{
|
||||
// env and k8s.deployment.name are resource fields
|
||||
// severity_text is an attribute field (returns true)
|
||||
// Multiple grouped conditions with attribute fields
|
||||
// severity_text is an attribute field (skipped)
|
||||
// Mixed AND: resource conditions are kept, attribute conditions are dropped
|
||||
Expression: "env = 'prod' AND k8s.deployment.name = 'prod-deployment' AND severity_text = 'ERROR' AND severity_text = 'WARN' AND (severity_text = 'INFO' AND severity_text = 'DEBUG') AND (severity_text = 'TRACE' AND severity_text = 'FATAL') AND (severity_text = 'a' AND severity_text = 'b') AND (severity_text = 'c' AND severity_text = 'd') AND (severity_text = 'e' AND severity_text = 'f')",
|
||||
},
|
||||
},
|
||||
start: uint64(1769976178000000000), // These will give bucket start 1769974378 and end 1770062578
|
||||
end: uint64(1770062578000000000),
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE ((simpleJSONExtractString(labels, 'env') = ? AND labels LIKE ? AND labels LIKE ?) AND (simpleJSONExtractString(labels, 'k8s.deployment.name') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"prod", "%env%", "%env\":\"prod%", "prod-deployment", "%k8s.deployment.name%", "%k8s.deployment.name\":\"prod-deployment%", uint64(1769974378), uint64(1770062578)},
|
||||
},
|
||||
@@ -487,101 +468,77 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Filter: &qbtypes.Filter{
|
||||
// using not with full text search
|
||||
Expression: "NOT 'error'",
|
||||
},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "NOT with unknown key should not generate not()",
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Filter: &qbtypes.Filter{
|
||||
// unknown.key is not in the metadata store, so with IgnoreNotFoundKeys=true
|
||||
// the condition returns empty, and NOT should also return empty (not "not()")
|
||||
// unknown.key not in metadata store; with IgnoreNotFoundKeys=true → no-op
|
||||
Expression: "NOT (unknown.key = 'value')",
|
||||
},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil, // unknown key ignored: CTE is skipped
|
||||
},
|
||||
{
|
||||
name: "NOT EQUAL with unknown key should not generate not()",
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Filter: &qbtypes.Filter{
|
||||
// unknown.key is not in the metadata store, so with IgnoreNotFoundKeys=true
|
||||
// the condition returns empty, and NOT should also return empty (not "not()")
|
||||
// unknown.key not in metadata store; with IgnoreNotFoundKeys=true → no-op
|
||||
Expression: "not(unknown.key = 'value1' and unknown.key = 'value2')",
|
||||
},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil, // unknown key ignored: CTE is skipped
|
||||
},
|
||||
{
|
||||
name: "NOT with attribute field should not generate NOT (true)",
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Filter: &qbtypes.Filter{
|
||||
// http.request.method is an attribute field, not a resource field
|
||||
// so the condition returns "true", and NOT should also return "true" (not "NOT (true)")
|
||||
// http.request.method is an attribute field: CTE is skipped
|
||||
Expression: "not(http.request.method = 'POST')",
|
||||
},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil, // only non-resource fields: CTE is skipped
|
||||
},
|
||||
{
|
||||
name: "NOT with multiple attribute fields should not generate NOT (true and true)",
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Filter: &qbtypes.Filter{
|
||||
// http.request.method is an attribute field, not a resource field
|
||||
// so the condition returns "true", and NOT should also return "true" (not "NOT (true)")
|
||||
// all attribute fields: CTE is skipped
|
||||
Expression: "not(http.request.method = 'POST' and module.name = 'abc')",
|
||||
},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil, // only non-resource fields: CTE is skipped
|
||||
},
|
||||
{
|
||||
name: "NOT with multiple attribute fields and values should not generate NOT (true and true)",
|
||||
query: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Filter: &qbtypes.Filter{
|
||||
// http.request.method is an attribute field, not a resource field
|
||||
// so the condition returns "true", and NOT should also return "true" (not "NOT (true)")
|
||||
// all attribute fields: CTE is skipped
|
||||
Expression: "not(http.request.method = 'POST' and (not 'error' and http.request.method = 'GET'))",
|
||||
},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: nil, // only non-resource fields: CTE is skipped
|
||||
},
|
||||
}
|
||||
|
||||
@@ -608,8 +565,13 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
|
||||
require.Contains(t, err.Error(), c.expectedErr.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c.expected.Query, stmt.Query)
|
||||
require.Equal(t, c.expected.Args, stmt.Args)
|
||||
if c.expected == nil {
|
||||
require.Nil(t, stmt)
|
||||
} else {
|
||||
require.NotNil(t, stmt)
|
||||
require.Equal(t, c.expected.Query, stmt.Query)
|
||||
require.Equal(t, c.expected.Args, stmt.Args)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -622,7 +584,7 @@ func TestResourceFilterStatementBuilder_Variables(t *testing.T) {
|
||||
variables map[string]qbtypes.VariableItem
|
||||
start uint64
|
||||
end uint64
|
||||
expected qbtypes.Statement
|
||||
expected *qbtypes.Statement
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
@@ -640,7 +602,7 @@ func TestResourceFilterStatementBuilder_Variables(t *testing.T) {
|
||||
},
|
||||
start: testStartNs,
|
||||
end: testEndNs,
|
||||
expected: qbtypes.Statement{
|
||||
expected: &qbtypes.Statement{
|
||||
Query: "SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", expectedBucketStart, expectedBucketEnd},
|
||||
},
|
||||
@@ -670,8 +632,13 @@ func TestResourceFilterStatementBuilder_Variables(t *testing.T) {
|
||||
require.Contains(t, err.Error(), c.expectedErr.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c.expected.Query, stmt.Query)
|
||||
require.Equal(t, c.expected.Args, stmt.Args)
|
||||
if c.expected == nil {
|
||||
require.Nil(t, stmt)
|
||||
} else {
|
||||
require.NotNil(t, stmt)
|
||||
require.Equal(t, c.expected.Query, stmt.Query)
|
||||
require.Equal(t, c.expected.Args, stmt.Args)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -816,6 +816,9 @@ func (b *traceQueryStatementBuilder) maybeAttachResourceFilter(
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if stmt == nil {
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
sb.Where("resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter)")
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -75,8 +75,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL) OR (attributes_string['http.request.method'] = ? AND mapContains(attributes_string, 'http.request.method') = ?)) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL) OR (attributes_string['http.request.method'] = ? AND mapContains(attributes_string, 'http.request.method') = ?)) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "redis-manual", "GET", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "redis-manual", "GET", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE ((multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL) OR (attributes_string['http.request.method'] = ? AND mapContains(attributes_string, 'http.request.method') = ?)) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE ((multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL) OR (attributes_string['http.request.method'] = ? AND mapContains(attributes_string, 'http.request.method') = ?)) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "GET", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "redis-manual", "GET", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -104,8 +104,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((match(`attribute_string_materialized$$key$$name`, ?) AND `attribute_string_materialized$$key$$name_exists` = ?) OR (multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL)) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((match(`attribute_string_materialized$$key$$name`, ?) AND `attribute_string_materialized$$key$$name_exists` = ?) OR (multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL)) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "redis-manual", true, "redis-manual", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "redis-manual", true, "redis-manual", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE ((match(`attribute_string_materialized$$key$$name`, ?) AND `attribute_string_materialized$$key$$name_exists` = ?) OR (multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL)) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE ((match(`attribute_string_materialized$$key$$name`, ?) AND `attribute_string_materialized$$key$$name_exists` = ?) OR (multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL)) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", true, "redis-manual", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "redis-manual", true, "redis-manual", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -135,7 +135,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(attribute_string_http$$route <> ?, attribute_string_http$$route, NULL)) AS `httpRoute`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `httpRoute` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(attribute_string_http$$route <> ?, attribute_string_http$$route, NULL)) AS `httpRoute`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`httpRoute`) GLOBAL IN (SELECT `httpRoute` FROM __limit_cte) GROUP BY ts, `httpRoute`",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(attribute_string_http$$route <> ?, attribute_string_http$$route, NULL)) AS `httpRoute`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `httpRoute` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(attribute_string_http$$route <> ?, attribute_string_http$$route, NULL)) AS `httpRoute`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`httpRoute`) GLOBAL IN (SELECT `httpRoute` FROM __limit_cte) GROUP BY ts, `httpRoute`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -173,8 +173,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(attribute_string_http$$route <> ?, attribute_string_http$$route, NULL)) AS `httpRoute`, toString(multiIf(http_method <> ?, http_method, NULL)) AS `httpMethod`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((resource_string_service$$name = ? AND resource_string_service$$name <> ?) AND http_method <> ? AND kind_string = ?) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `httpRoute`, `httpMethod` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(attribute_string_http$$route <> ?, attribute_string_http$$route, NULL)) AS `httpRoute`, toString(multiIf(http_method <> ?, http_method, NULL)) AS `httpMethod`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((resource_string_service$$name = ? AND resource_string_service$$name <> ?) AND http_method <> ? AND kind_string = ?) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`httpRoute`, `httpMethod`) GLOBAL IN (SELECT `httpRoute`, `httpMethod` FROM __limit_cte) GROUP BY ts, `httpRoute`, `httpMethod`",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "", "", "redis-manual", "", "", "Server", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", "", "redis-manual", "", "", "Server", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(attribute_string_http$$route <> ?, attribute_string_http$$route, NULL)) AS `httpRoute`, toString(multiIf(http_method <> ?, http_method, NULL)) AS `httpMethod`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE ((resource_string_service$$name = ? AND resource_string_service$$name <> ?) AND http_method <> ? AND kind_string = ?) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `httpRoute`, `httpMethod` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(attribute_string_http$$route <> ?, attribute_string_http$$route, NULL)) AS `httpRoute`, toString(multiIf(http_method <> ?, http_method, NULL)) AS `httpMethod`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE ((resource_string_service$$name = ? AND resource_string_service$$name <> ?) AND http_method <> ? AND kind_string = ?) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`httpRoute`, `httpMethod`) GLOBAL IN (SELECT `httpRoute`, `httpMethod` FROM __limit_cte) GROUP BY ts, `httpRoute`, `httpMethod`",
|
||||
Args: []any{"", "", "redis-manual", "", "", "Server", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", "", "redis-manual", "", "", "Server", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -214,7 +214,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, sum(multiIf(mapContains(attributes_number, 'metric.max_count') = ?, toFloat64(attributes_number['metric.max_count']), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 desc LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, sum(multiIf(mapContains(attributes_number, 'metric.max_count') = ?, toFloat64(attributes_number['metric.max_count']), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name` ORDER BY ts desc",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, sum(multiIf(mapContains(attributes_number, 'metric.max_count') = ?, toFloat64(attributes_number['metric.max_count']), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 desc LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, sum(multiIf(mapContains(attributes_number, 'metric.max_count') = ?, toFloat64(attributes_number['metric.max_count']), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name` ORDER BY ts desc",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -243,7 +243,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, sum(multiIf(`attribute_number_cart$$items_count_exists` = ?, toFloat64(`attribute_number_cart$$items_count`), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, sum(multiIf(`attribute_number_cart$$items_count_exists` = ?, toFloat64(`attribute_number_cart$$items_count`), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, sum(multiIf(`attribute_number_cart$$items_count_exists` = ?, toFloat64(`attribute_number_cart$$items_count`), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, sum(multiIf(`attribute_number_cart$$items_count_exists` = ?, toFloat64(`attribute_number_cart$$items_count`), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -282,7 +282,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, sum(multiIf(`attribute_number_cart$$items_count_exists` = ?, toFloat64(`attribute_number_cart$$items_count`), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY `service.name` desc LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, sum(multiIf(`attribute_number_cart$$items_count_exists` = ?, toFloat64(`attribute_number_cart$$items_count`), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name` ORDER BY `service.name` desc, ts desc",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, sum(multiIf(`attribute_number_cart$$items_count_exists` = ?, toFloat64(`attribute_number_cart$$items_count`), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY `service.name` desc LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, sum(multiIf(`attribute_number_cart$$items_count_exists` = ?, toFloat64(`attribute_number_cart$$items_count`), NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name` ORDER BY `service.name` desc, ts desc",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -313,7 +313,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(response_status_code <> ?, response_status_code, NULL)) AS `responseStatusCode`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `responseStatusCode` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(response_status_code <> ?, response_status_code, NULL)) AS `responseStatusCode`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`responseStatusCode`) GLOBAL IN (SELECT `responseStatusCode` FROM __limit_cte) GROUP BY ts, `responseStatusCode`",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(response_status_code <> ?, response_status_code, NULL)) AS `responseStatusCode`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `responseStatusCode` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(response_status_code <> ?, response_status_code, NULL)) AS `responseStatusCode`, count() AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`responseStatusCode`) GLOBAL IN (SELECT `responseStatusCode` FROM __limit_cte) GROUP BY ts, `responseStatusCode`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -344,7 +344,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(response_status_code <> ?, response_status_code, NULL)) AS `responseStatusCode`, quantile(0.90)(multiIf(duration_nano <> ?, duration_nano, NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `responseStatusCode` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(response_status_code <> ?, response_status_code, NULL)) AS `responseStatusCode`, quantile(0.90)(multiIf(duration_nano <> ?, duration_nano, NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`responseStatusCode`) GLOBAL IN (SELECT `responseStatusCode` FROM __limit_cte) GROUP BY ts, `responseStatusCode`",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __limit_cte AS (SELECT toString(multiIf(response_status_code <> ?, response_status_code, NULL)) AS `responseStatusCode`, quantile(0.90)(multiIf(duration_nano <> ?, duration_nano, NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `responseStatusCode` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(timestamp, INTERVAL 30 SECOND) AS ts, toString(multiIf(response_status_code <> ?, response_status_code, NULL)) AS `responseStatusCode`, quantile(0.90)(multiIf(duration_nano <> ?, duration_nano, NULL)) AS __result_0 FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`responseStatusCode`) GLOBAL IN (SELECT `responseStatusCode` FROM __limit_cte) GROUP BY ts, `responseStatusCode`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "", 0, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", 0, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -436,7 +436,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT name AS `name`, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) AS `service.name`, duration_nano AS `duration_nano`, `attribute_number_cart$$items_count` AS `cart.items_count`, timestamp AS `timestamp`, span_id AS `span_id`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT name AS `name`, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) AS `service.name`, duration_nano AS `duration_nano`, `attribute_number_cart$$items_count` AS `cart.items_count`, timestamp AS `timestamp`, span_id AS `span_id`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -465,7 +465,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT duration_nano AS `duration_nano`, name AS `name`, response_status_code AS `response_status_code`, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) AS `service.name`, span_id AS `span_id`, timestamp AS `timestamp`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY attributes_string['user.id'] AS `user.id` desc LIMIT ?",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT duration_nano AS `duration_nano`, name AS `name`, response_status_code AS `response_status_code`, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) AS `service.name`, span_id AS `span_id`, timestamp AS `timestamp`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY attributes_string['user.id'] AS `user.id` desc LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -509,7 +509,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT name AS `name`, resource_string_service$$name AS `serviceName`, duration_nano AS `durationNano`, http_method AS `httpMethod`, response_status_code AS `responseStatusCode`, timestamp AS `timestamp`, span_id AS `span_id`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT name AS `name`, resource_string_service$$name AS `serviceName`, duration_nano AS `durationNano`, http_method AS `httpMethod`, response_status_code AS `responseStatusCode`, timestamp AS `timestamp`, span_id AS `span_id`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -553,7 +553,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT name AS `name`, resource_string_service$$name AS `serviceName`, duration_nano AS `durationNano`, http_method AS `httpMethod`, multiIf(toString(`attribute_string_mixed$$materialization$$key`) != '', toString(`attribute_string_mixed$$materialization$$key`), toString(multiIf(resource.`mixed.materialization.key` IS NOT NULL, resource.`mixed.materialization.key`::String, mapContains(resources_string, 'mixed.materialization.key'), resources_string['mixed.materialization.key'], NULL)) != '', toString(multiIf(resource.`mixed.materialization.key` IS NOT NULL, resource.`mixed.materialization.key`::String, mapContains(resources_string, 'mixed.materialization.key'), resources_string['mixed.materialization.key'], NULL)), NULL) AS `mixed.materialization.key`, timestamp AS `timestamp`, span_id AS `span_id`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT name AS `name`, resource_string_service$$name AS `serviceName`, duration_nano AS `durationNano`, http_method AS `httpMethod`, multiIf(toString(`attribute_string_mixed$$materialization$$key`) != '', toString(`attribute_string_mixed$$materialization$$key`), toString(multiIf(resource.`mixed.materialization.key` IS NOT NULL, resource.`mixed.materialization.key`::String, mapContains(resources_string, 'mixed.materialization.key'), resources_string['mixed.materialization.key'], NULL)) != '', toString(multiIf(resource.`mixed.materialization.key` IS NOT NULL, resource.`mixed.materialization.key`::String, mapContains(resources_string, 'mixed.materialization.key'), resources_string['mixed.materialization.key'], NULL)), NULL) AS `mixed.materialization.key`, timestamp AS `timestamp`, span_id AS `span_id`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -598,7 +598,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT name AS `name`, resource_string_service$$name AS `serviceName`, duration_nano AS `durationNano`, http_method AS `httpMethod`, `attribute_string_mixed$$materialization$$key` AS `mixed.materialization.key`, timestamp AS `timestamp`, span_id AS `span_id`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT name AS `name`, resource_string_service$$name AS `serviceName`, duration_nano AS `durationNano`, http_method AS `httpMethod`, `attribute_string_mixed$$materialization$$key` AS `mixed.materialization.key`, timestamp AS `timestamp`, span_id AS `span_id`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -706,8 +706,8 @@ func TestStatementBuilderListQueryWithCorruptData(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT duration_nano AS `duration_nano`, name AS `name`, response_status_code AS `response_status_code`, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) AS `service.name`, span_id AS `span_id`, timestamp AS `timestamp`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "SELECT duration_nano AS `duration_nano`, name AS `name`, response_status_code AS `response_status_code`, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) AS `service.name`, span_id AS `span_id`, timestamp AS `timestamp`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -739,8 +739,8 @@ func TestStatementBuilderListQueryWithCorruptData(t *testing.T) {
|
||||
}},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?) SELECT duration_nano AS `duration_nano`, name AS `name`, response_status_code AS `response_status_code`, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) AS `service.name`, span_id AS `span_id`, timestamp AS `timestamp`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY timestamp AS `timestamp` asc LIMIT ?",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "SELECT duration_nano AS `duration_nano`, name AS `name`, response_status_code AS `response_status_code`, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) AS `service.name`, span_id AS `span_id`, timestamp AS `timestamp`, trace_id AS `trace_id` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY timestamp AS `timestamp` asc LIMIT ?",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -800,7 +800,7 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND true AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND trace_id GLOBAL IN __toe AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND trace_id GLOBAL IN __toe AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -816,8 +816,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (`attribute_string_materialized$$key$$name` = ? AND `attribute_string_materialized$$key$$name_exists` = ?) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND trace_id GLOBAL IN __toe AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "redis-manual", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE (`attribute_string_materialized$$key$$name` = ? AND `attribute_string_materialized$$key$$name_exists` = ?) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND trace_id GLOBAL IN __toe AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"redis-manual", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -832,8 +832,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((match(`attribute_string_materialized$$key$$name`, ?) AND `attribute_string_materialized$$key$$name_exists` = ?) OR (multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL)) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND trace_id GLOBAL IN __toe AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "redis-manual", true, "redis-manual", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE ((match(`attribute_string_materialized$$key$$name`, ?) AND `attribute_string_materialized$$key$$name_exists` = ?) OR (multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) = ? AND multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL)) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND trace_id GLOBAL IN __toe AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"redis-manual", true, "redis-manual", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -846,8 +846,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -862,8 +862,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND ((name, resource_string_service$$name) GLOBAL IN (SELECT DISTINCT name, serviceName from signoz_traces.distributed_top_level_operations WHERE time >= toDateTime(1747947419))) AND parent_span_id != '' AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND trace_id GLOBAL IN __toe AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE ((name, resource_string_service$$name) GLOBAL IN (SELECT DISTINCT name, serviceName from signoz_traces.distributed_top_level_operations WHERE time >= toDateTime(1747947419))) AND parent_span_id != '' AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND trace_id GLOBAL IN __toe AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -878,8 +878,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (((name, resource_string_service$$name) GLOBAL IN (SELECT DISTINCT name, serviceName from signoz_traces.distributed_top_level_operations WHERE time >= toDateTime(1747947419))) AND parent_span_id != '' OR (`attribute_string_materialized$$key$$name` = ? AND `attribute_string_materialized$$key$$name_exists` = ?)) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND trace_id GLOBAL IN __toe AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), "redis-manual", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE (((name, resource_string_service$$name) GLOBAL IN (SELECT DISTINCT name, serviceName from signoz_traces.distributed_top_level_operations WHERE time >= toDateTime(1747947419))) AND parent_span_id != '' OR (`attribute_string_materialized$$key$$name` = ? AND `attribute_string_materialized$$key$$name_exists` = ?)) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND trace_id GLOBAL IN __toe AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"redis-manual", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -894,8 +894,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (toFloat64(kind) = ? OR kind_string = ?) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND trace_id GLOBAL IN __toe AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{uint64(1747945619), uint64(1747983448), float64(2), "Server", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM signoz_traces.distributed_signoz_index_v3 WHERE (toFloat64(kind) = ? OR kind_string = ?) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration_nano, resource_string_service$$name as `service.name`, name FROM signoz_traces.distributed_signoz_index_v3 WHERE parent_span_id = '' AND trace_id GLOBAL IN __toe AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{float64(2), "Server", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
|
||||
@@ -66,7 +66,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent_span_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) AS `service.name` FROM A_DIR_DESC_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent_span_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) AS `service.name` FROM A_DIR_DESC_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -103,7 +103,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent_span_id, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent_span_id, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent_span_id WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id FROM A_INDIR_DESC_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent_span_id, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent_span_id, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent_span_id WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id FROM A_INDIR_DESC_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "gateway", "%service.name%", "%service.name\":\"gateway%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 5},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -140,7 +140,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), A_AND_B AS (SELECT l.* FROM A AS l INNER JOIN B AS r ON l.trace_id = r.trace_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id FROM A_AND_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_AND_B AS (SELECT l.* FROM A AS l INNER JOIN B AS r ON l.trace_id = r.trace_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id FROM A_AND_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 15},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -177,7 +177,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), A_OR_B AS (SELECT * FROM A UNION DISTINCT SELECT * FROM B) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id FROM A_OR_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_OR_B AS (SELECT * FROM A UNION DISTINCT SELECT * FROM B) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id FROM A_OR_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 20},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -214,7 +214,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), A_not_B AS (SELECT l.* FROM A AS l WHERE l.trace_id GLOBAL NOT IN (SELECT DISTINCT trace_id FROM B)) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id FROM A_not_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_not_B AS (SELECT l.* FROM A AS l WHERE l.trace_id GLOBAL NOT IN (SELECT DISTINCT trace_id FROM B)) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id FROM A_not_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -263,7 +263,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent_span_id) SELECT toStartOfInterval(timestamp, INTERVAL 60 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM A_DIR_DESC_B GROUP BY ts, `service.name` ORDER BY ts desc SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent_span_id) SELECT toStartOfInterval(timestamp, INTERVAL 60 SECOND) AS ts, toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, count() AS __result_0 FROM A_DIR_DESC_B GROUP BY ts, `service.name` ORDER BY ts desc SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -322,8 +322,8 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE true AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND toFloat64(response_status_code) < ?), A_AND_B AS (SELECT l.* FROM A AS l INNER JOIN B AS r ON l.trace_id = r.trace_id) SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, avg(multiIf(duration_nano <> ?, duration_nano, NULL)) AS __result_0 FROM A_AND_B GROUP BY `service.name` ORDER BY __result_0 desc SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), float64(400), 0},
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND toFloat64(response_status_code) < ?), A_AND_B AS (SELECT l.* FROM A AS l INNER JOIN B AS r ON l.trace_id = r.trace_id) SELECT toString(multiIf(multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL) IS NOT NULL, multiIf(resource.`service.name` IS NOT NULL, resource.`service.name`::String, mapContains(resources_string, 'service.name'), resources_string['service.name'], NULL), NULL)) AS `service.name`, avg(multiIf(duration_nano <> ?, duration_nano, NULL)) AS __result_0 FROM A_AND_B GROUP BY `service.name` ORDER BY __result_0 desc SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), float64(400), 0},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -379,7 +379,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent_span_id), __resource_filter_C AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), C AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_C) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), __resource_filter_D AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), D AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_D) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND true), C_DIR_DESC_D AS (SELECT p.* FROM C AS p INNER JOIN D AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent_span_id), A_DIR_DESC_B_AND_C_DIR_DESC_D AS (SELECT l.* FROM A_DIR_DESC_B AS l INNER JOIN C_DIR_DESC_D AS r ON l.trace_id = r.trace_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id FROM A_DIR_DESC_B_AND_C_DIR_DESC_D ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent_span_id), __resource_filter_C AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), C AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_C) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_D AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ?), D AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_D) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), C_DIR_DESC_D AS (SELECT p.* FROM C AS p INNER JOIN D AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent_span_id), A_DIR_DESC_B_AND_C_DIR_DESC_D AS (SELECT l.* FROM A_DIR_DESC_B AS l INNER JOIN C_DIR_DESC_D AS r ON l.trace_id = r.trace_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id FROM A_DIR_DESC_B_AND_C_DIR_DESC_D ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "auth", "%service.name%", "%service.name\":\"auth%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 5},
|
||||
},
|
||||
expectedErr: nil,
|
||||
|
||||
@@ -29,13 +29,12 @@ type AgentReport struct {
|
||||
}
|
||||
|
||||
type AccountConfig struct {
|
||||
AWS *AWSAccountConfig `json:"aws,omitempty" required:"false" nullable:"false"`
|
||||
Azure *AzureAccountConfig `json:"azure,omitempty" required:"false" nullable:"false"`
|
||||
// required till new providers are added
|
||||
AWS *AWSAccountConfig `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type UpdatableAccountConfig struct {
|
||||
AWS *UpdatableAWSAccountConfig `json:"aws,omitempty" required:"false" nullable:"false"`
|
||||
Azure *UpdatableAzureAccountConfig `json:"azure,omitempty" required:"false" nullable:"false"`
|
||||
type AWSAccountConfig struct {
|
||||
Regions []string `json:"regions" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type PostableAccount struct {
|
||||
@@ -46,8 +45,7 @@ type PostableAccount struct {
|
||||
type PostableAccountConfig struct {
|
||||
// as agent version is common for all providers, we can keep it at top level of this struct
|
||||
AgentVersion string
|
||||
AWS *AWSPostableAccountConfig `json:"aws,omitempty" required:"false" nullable:"false"`
|
||||
Azure *AzurePostableAccountConfig `json:"azure,omitempty" required:"false" nullable:"false"`
|
||||
Aws *AWSPostableAccountConfig `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type Credentials struct {
|
||||
@@ -57,6 +55,11 @@ type Credentials struct {
|
||||
IngestionKey string `json:"ingestionKey" required:"true"`
|
||||
}
|
||||
|
||||
type AWSPostableAccountConfig struct {
|
||||
DeploymentRegion string `json:"deploymentRegion" required:"true"`
|
||||
Regions []string `json:"regions" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type GettableAccountWithConnectionArtifact struct {
|
||||
ID valuer.UUID `json:"id" required:"true"`
|
||||
ConnectionArtifact *ConnectionArtifact `json:"connectionArtifact" required:"true"`
|
||||
@@ -64,8 +67,11 @@ type GettableAccountWithConnectionArtifact struct {
|
||||
|
||||
type ConnectionArtifact struct {
|
||||
// required till new providers are added
|
||||
AWS *AWSConnectionArtifact `json:"aws,omitempty" required:"false" nullable:"false"`
|
||||
Azure *AzureConnectionArtifact `json:"azure,omitempty" required:"false" nullable:"false"`
|
||||
Aws *AWSConnectionArtifact `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSConnectionArtifact struct {
|
||||
ConnectionURL string `json:"connectionUrl" required:"true"`
|
||||
}
|
||||
|
||||
type GetConnectionArtifactRequest = PostableAccount
|
||||
@@ -75,7 +81,7 @@ type GettableAccounts struct {
|
||||
}
|
||||
|
||||
type UpdatableAccount struct {
|
||||
Config *UpdatableAccountConfig `json:"config" required:"true" nullable:"false"`
|
||||
Config *AccountConfig `json:"config" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
func NewAccount(orgID valuer.UUID, provider CloudProviderType, config *AccountConfig) *Account {
|
||||
@@ -126,13 +132,6 @@ func NewAccountFromStorable(storableAccount *StorableCloudIntegration) (*Account
|
||||
return nil, err
|
||||
}
|
||||
account.Config.AWS = awsConfig
|
||||
case CloudProviderTypeAzure:
|
||||
azureConfig := new(AzureAccountConfig)
|
||||
err := json.Unmarshal([]byte(storableAccount.Config), azureConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
account.Config.Azure = azureConfig
|
||||
}
|
||||
|
||||
if storableAccount.LastAgentReport != nil {
|
||||
@@ -174,43 +173,25 @@ func NewGettableAccounts(accounts []*Account) *GettableAccounts {
|
||||
func NewAccountConfigFromPostable(provider CloudProviderType, config *PostableAccountConfig) (*AccountConfig, error) {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
if config.AWS == nil {
|
||||
if config.Aws == nil {
|
||||
return nil, errors.NewInvalidInputf(ErrCodeInvalidInput, "AWS config can not be nil for AWS provider")
|
||||
}
|
||||
|
||||
if err := validateAWSRegion(config.AWS.DeploymentRegion); err != nil {
|
||||
if err := validateAWSRegion(config.Aws.DeploymentRegion); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(config.AWS.Regions) == 0 {
|
||||
if len(config.Aws.Regions) == 0 {
|
||||
return nil, errors.NewInvalidInputf(ErrCodeInvalidInput, "at least one region is required")
|
||||
}
|
||||
|
||||
for _, region := range config.AWS.Regions {
|
||||
for _, region := range config.Aws.Regions {
|
||||
if err := validateAWSRegion(region); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &AccountConfig{AWS: &AWSAccountConfig{Regions: config.AWS.Regions}}, nil
|
||||
case CloudProviderTypeAzure:
|
||||
if config.Azure == nil {
|
||||
return nil, errors.NewInvalidInputf(ErrCodeInvalidInput, "Azure config can not be nil for Azure provider")
|
||||
}
|
||||
|
||||
if config.Azure.DeploymentRegion == "" {
|
||||
return nil, errors.NewInvalidInputf(ErrCodeInvalidInput, "deployment region is required for Azure provider")
|
||||
}
|
||||
|
||||
if err := validateAzureRegion(config.Azure.DeploymentRegion); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(config.Azure.ResourceGroups) == 0 {
|
||||
return nil, errors.NewInvalidInputf(ErrCodeInvalidInput, "at least one resource group is required for Azure provider")
|
||||
}
|
||||
|
||||
return &AccountConfig{Azure: &AzureAccountConfig{DeploymentRegion: config.Azure.DeploymentRegion, ResourceGroups: config.Azure.ResourceGroups}}, nil
|
||||
return &AccountConfig{AWS: &AWSAccountConfig{Regions: config.Aws.Regions}}, nil
|
||||
default:
|
||||
return nil, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider.StringValue())
|
||||
}
|
||||
@@ -320,13 +301,5 @@ func (config *AccountConfig) ToJSON() ([]byte, error) {
|
||||
return json.Marshal(config.AWS)
|
||||
}
|
||||
|
||||
if config.Azure != nil {
|
||||
return json.Marshal(config.Azure)
|
||||
}
|
||||
|
||||
return nil, errors.NewInternalf(errors.CodeInternal, "no provider account config found")
|
||||
}
|
||||
|
||||
func NewIngestionKeyName(provider CloudProviderType) string {
|
||||
return fmt.Sprintf("%s-integration", provider.StringValue())
|
||||
}
|
||||
|
||||
@@ -48,8 +48,12 @@ type IntegrationConfig struct {
|
||||
}
|
||||
|
||||
type ProviderIntegrationConfig struct {
|
||||
AWS *AWSIntegrationConfig `json:"aws,omitempty" required:"false" nullable:"false"`
|
||||
Azure *AzureIntegrationConfig `json:"azure,omitempty" required:"false" nullable:"false"`
|
||||
AWS *AWSIntegrationConfig `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSIntegrationConfig struct {
|
||||
EnabledRegions []string `json:"enabledRegions" required:"true" nullable:"false"`
|
||||
TelemetryCollectionStrategy *AWSTelemetryCollectionStrategy `json:"telemetryCollectionStrategy" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
// NewGettableAgentCheckIn constructs a backward-compatible response from an AgentCheckInResponse.
|
||||
|
||||
@@ -61,8 +61,7 @@ type StorableCloudIntegrationService struct {
|
||||
// Following Service config types are only internally used to store service config in DB and use JSON snake case keys for backward compatibility.
|
||||
|
||||
type StorableServiceConfig struct {
|
||||
AWS *StorableAWSServiceConfig
|
||||
Azure *StorableAzureServiceConfig
|
||||
AWS *StorableAWSServiceConfig
|
||||
}
|
||||
|
||||
type StorableAWSServiceConfig struct {
|
||||
@@ -79,19 +78,6 @@ type StorableAWSMetricsServiceConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type StorableAzureServiceConfig struct {
|
||||
Logs *StorableAzureLogsServiceConfig `json:"logs,omitempty"`
|
||||
Metrics *StorableAzureMetricsServiceConfig `json:"metrics,omitempty"`
|
||||
}
|
||||
|
||||
type StorableAzureLogsServiceConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type StorableAzureMetricsServiceConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// Scan scans value from DB.
|
||||
func (r *StorableAgentReport) Scan(src any) error {
|
||||
var data []byte
|
||||
@@ -201,30 +187,6 @@ func newStorableServiceConfig(provider CloudProviderType, serviceID ServiceID, s
|
||||
}
|
||||
|
||||
return &StorableServiceConfig{AWS: storableAWSServiceConfig}, nil
|
||||
case CloudProviderTypeAzure:
|
||||
storableAzureServiceConfig := new(StorableAzureServiceConfig)
|
||||
|
||||
if supportedSignals.Logs {
|
||||
if serviceConfig.Azure.Logs == nil {
|
||||
return nil, errors.NewInvalidInputf(ErrCodeCloudIntegrationInvalidConfig, "logs config is required for Azure service: %s", serviceID.StringValue())
|
||||
}
|
||||
|
||||
storableAzureServiceConfig.Logs = &StorableAzureLogsServiceConfig{
|
||||
Enabled: serviceConfig.Azure.Logs.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
if supportedSignals.Metrics {
|
||||
if serviceConfig.Azure.Metrics == nil {
|
||||
return nil, errors.NewInvalidInputf(ErrCodeCloudIntegrationInvalidConfig, "metrics config is required for Azure service: %s", serviceID.StringValue())
|
||||
}
|
||||
|
||||
storableAzureServiceConfig.Metrics = &StorableAzureMetricsServiceConfig{
|
||||
Enabled: serviceConfig.Azure.Metrics.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
return &StorableServiceConfig{Azure: storableAzureServiceConfig}, nil
|
||||
default:
|
||||
return nil, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider.StringValue())
|
||||
}
|
||||
@@ -239,13 +201,6 @@ func newStorableServiceConfigFromJSON(provider CloudProviderType, jsonStr string
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't parse AWS service config JSON")
|
||||
}
|
||||
return &StorableServiceConfig{AWS: awsConfig}, nil
|
||||
case CloudProviderTypeAzure:
|
||||
azureConfig := new(StorableAzureServiceConfig)
|
||||
err := json.Unmarshal([]byte(jsonStr), azureConfig)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't parse Azure service config JSON")
|
||||
}
|
||||
return &StorableServiceConfig{Azure: azureConfig}, nil
|
||||
default:
|
||||
return nil, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider.StringValue())
|
||||
}
|
||||
@@ -259,13 +214,6 @@ func (config *StorableServiceConfig) toJSON(provider CloudProviderType) ([]byte,
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't serialize AWS service config to JSON")
|
||||
}
|
||||
|
||||
return jsonBytes, nil
|
||||
case CloudProviderTypeAzure:
|
||||
jsonBytes, err := json.Marshal(config.Azure)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't serialize Azure service config to JSON")
|
||||
}
|
||||
|
||||
return jsonBytes, nil
|
||||
default:
|
||||
return nil, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider.StringValue())
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package cloudintegrationtypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
// CloudProviderType type alias.
|
||||
type CloudProviderType struct{ valuer.String }
|
||||
|
||||
var (
|
||||
@@ -12,9 +15,16 @@ var (
|
||||
CloudProviderTypeAWS = CloudProviderType{valuer.NewString("aws")}
|
||||
CloudProviderTypeAzure = CloudProviderType{valuer.NewString("azure")}
|
||||
|
||||
// errors.
|
||||
ErrCodeCloudProviderInvalidInput = errors.MustNewCode("cloud_integration_invalid_cloud_provider")
|
||||
|
||||
CloudFormationQuickCreateBaseURL = valuer.NewString("https://%s.console.aws.amazon.com/cloudformation/home")
|
||||
AgentCloudFormationTemplateS3Path = valuer.NewString("https://signoz-integrations.s3.us-east-1.amazonaws.com/aws-quickcreate-template-%s.json")
|
||||
AgentCloudFormationBaseStackName = valuer.NewString("signoz-integration")
|
||||
)
|
||||
|
||||
// NewCloudProvider returns a new CloudProviderType from a string.
|
||||
// It validates the input and returns an error if the input is not valid cloud provider.
|
||||
func NewCloudProvider(provider string) (CloudProviderType, error) {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS.StringValue():
|
||||
@@ -25,3 +35,7 @@ func NewCloudProvider(provider string) (CloudProviderType, error) {
|
||||
return CloudProviderType{}, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider)
|
||||
}
|
||||
}
|
||||
|
||||
func NewIngestionKeyName(provider CloudProviderType) string {
|
||||
return fmt.Sprintf("%s-integration", provider.StringValue())
|
||||
}
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
package cloudintegrationtypes
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
var (
|
||||
CloudFormationQuickCreateBaseURL = valuer.NewString("https://%s.console.aws.amazon.com/cloudformation/home")
|
||||
AgentCloudFormationTemplateS3Path = valuer.NewString("https://signoz-integrations.s3.us-east-1.amazonaws.com/aws-quickcreate-template-%s.json")
|
||||
AgentCloudFormationBaseStackName = valuer.NewString("signoz-integration")
|
||||
)
|
||||
|
||||
type AWSPostableAccountConfig struct {
|
||||
DeploymentRegion string `json:"deploymentRegion" required:"true"`
|
||||
Regions []string `json:"regions" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSConnectionArtifact struct {
|
||||
ConnectionURL string `json:"connectionUrl" required:"true"`
|
||||
}
|
||||
|
||||
type AWSAccountConfig struct {
|
||||
Regions []string `json:"regions" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type UpdatableAWSAccountConfig = AWSAccountConfig
|
||||
|
||||
// OldAWSCollectionStrategy is the backward-compatible snake_case form of AWSCollectionStrategy,
|
||||
// used in the legacy integration_config response field for older agents.
|
||||
type OldAWSCollectionStrategy struct {
|
||||
Provider string `json:"provider"`
|
||||
Metrics *OldAWSMetricsStrategy `json:"aws_metrics,omitempty"`
|
||||
Logs *OldAWSLogsStrategy `json:"aws_logs,omitempty"`
|
||||
S3Buckets map[string][]string `json:"s3_buckets,omitempty"`
|
||||
}
|
||||
|
||||
// OldAWSMetricsStrategy is the snake_case form of AWSMetricsStrategy for older agents.
|
||||
type OldAWSMetricsStrategy struct {
|
||||
StreamFilters []struct {
|
||||
Namespace string `json:"Namespace"`
|
||||
MetricNames []string `json:"MetricNames,omitempty"`
|
||||
} `json:"cloudwatch_metric_stream_filters"`
|
||||
}
|
||||
|
||||
// OldAWSLogsStrategy is the snake_case form of AWSLogsStrategy for older agents.
|
||||
type OldAWSLogsStrategy struct {
|
||||
Subscriptions []struct {
|
||||
LogGroupNamePrefix string `json:"log_group_name_prefix"`
|
||||
FilterPattern string `json:"filter_pattern"`
|
||||
} `json:"cloudwatch_logs_subscriptions"`
|
||||
}
|
||||
|
||||
type AWSIntegrationConfig struct {
|
||||
EnabledRegions []string `json:"enabledRegions" required:"true" nullable:"false"`
|
||||
TelemetryCollectionStrategy *AWSTelemetryCollectionStrategy `json:"telemetryCollectionStrategy" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
// AWSTelemetryCollectionStrategy represents signal collection strategy for AWS services.
|
||||
type AWSTelemetryCollectionStrategy struct {
|
||||
Metrics *AWSMetricsCollectionStrategy `json:"metrics,omitempty" required:"false" nullable:"false"`
|
||||
Logs *AWSLogsCollectionStrategy `json:"logs,omitempty" required:"false" nullable:"false"`
|
||||
S3Buckets map[string][]string `json:"s3Buckets,omitempty" required:"false"` // Only available in S3 Sync Service Type in AWS
|
||||
}
|
||||
|
||||
// AWSMetricsCollectionStrategy represents metrics collection strategy for AWS services.
|
||||
type AWSMetricsCollectionStrategy struct {
|
||||
// to be used as https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudwatch-metricstream.html#cfn-cloudwatch-metricstream-includefilters
|
||||
StreamFilters []*AWSCloudWatchMetricStreamFilter `json:"streamFilters" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSCloudWatchMetricStreamFilter struct {
|
||||
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudwatch-metricstream-metricstreamfilter.html
|
||||
Namespace string `json:"namespace" required:"true"`
|
||||
MetricNames []string `json:"metricNames,omitempty" required:"false" nullable:"false"`
|
||||
}
|
||||
|
||||
// AWSLogsCollectionStrategy represents logs collection strategy for AWS services.
|
||||
type AWSLogsCollectionStrategy struct {
|
||||
Subscriptions []*AWSCloudWatchLogsSubscription `json:"subscriptions" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSCloudWatchLogsSubscription struct {
|
||||
// subscribe to all logs groups with specified prefix.
|
||||
// eg: `/aws/rds/`
|
||||
LogGroupNamePrefix string `json:"logGroupNamePrefix" required:"true"`
|
||||
|
||||
// https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html
|
||||
// "" implies no filtering is required
|
||||
FilterPattern string `json:"filterPattern" required:"true"`
|
||||
}
|
||||
|
||||
type AWSServiceConfig struct {
|
||||
Logs *AWSServiceLogsConfig `json:"logs"`
|
||||
Metrics *AWSServiceMetricsConfig `json:"metrics"`
|
||||
}
|
||||
|
||||
// AWSServiceLogsConfig is AWS specific logs config for a service
|
||||
// NOTE: the JSON keys are snake case for backward compatibility with existing agents.
|
||||
type AWSServiceLogsConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
S3Buckets map[string][]string `json:"s3Buckets,omitempty"`
|
||||
}
|
||||
|
||||
type AWSServiceMetricsConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
func NewAWSConnectionArtifact(connectionURL string) *AWSConnectionArtifact {
|
||||
return &AWSConnectionArtifact{
|
||||
ConnectionURL: connectionURL,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAWSIntegrationConfig(enabledRegions []string, telemetryCollectionStrategy *AWSTelemetryCollectionStrategy) *AWSIntegrationConfig {
|
||||
return &AWSIntegrationConfig{
|
||||
EnabledRegions: enabledRegions,
|
||||
TelemetryCollectionStrategy: telemetryCollectionStrategy,
|
||||
}
|
||||
}
|
||||
|
||||
// awsOlderIntegrationConfig converts a ProviderIntegrationConfig into the legacy snake_case
|
||||
// IntegrationConfig format consumed by older AWS agents. Returns nil if AWS config is absent.
|
||||
func awsOlderIntegrationConfig(cfg *ProviderIntegrationConfig) *IntegrationConfig {
|
||||
if cfg == nil || cfg.AWS == nil {
|
||||
return nil
|
||||
}
|
||||
awsCfg := cfg.AWS
|
||||
|
||||
older := &IntegrationConfig{
|
||||
EnabledRegions: awsCfg.EnabledRegions,
|
||||
}
|
||||
|
||||
if awsCfg.TelemetryCollectionStrategy == nil {
|
||||
return older
|
||||
}
|
||||
|
||||
// Older agents expect a "provider" field and fully snake_case keys inside telemetry.
|
||||
oldTelemetry := &OldAWSCollectionStrategy{
|
||||
Provider: CloudProviderTypeAWS.StringValue(),
|
||||
S3Buckets: awsCfg.TelemetryCollectionStrategy.S3Buckets,
|
||||
}
|
||||
|
||||
if awsCfg.TelemetryCollectionStrategy.Metrics != nil {
|
||||
// Convert camelCase cloudwatchMetricStreamFilters → snake_case cloudwatch_metric_stream_filters
|
||||
oldMetrics := &OldAWSMetricsStrategy{}
|
||||
for _, f := range awsCfg.TelemetryCollectionStrategy.Metrics.StreamFilters {
|
||||
oldMetrics.StreamFilters = append(oldMetrics.StreamFilters, struct {
|
||||
Namespace string `json:"Namespace"`
|
||||
MetricNames []string `json:"MetricNames,omitempty"`
|
||||
}{Namespace: f.Namespace, MetricNames: f.MetricNames})
|
||||
}
|
||||
oldTelemetry.Metrics = oldMetrics
|
||||
}
|
||||
|
||||
if awsCfg.TelemetryCollectionStrategy.Logs != nil {
|
||||
// Convert camelCase cloudwatchLogsSubscriptions → snake_case cloudwatch_logs_subscriptions
|
||||
oldLogs := &OldAWSLogsStrategy{}
|
||||
for _, s := range awsCfg.TelemetryCollectionStrategy.Logs.Subscriptions {
|
||||
oldLogs.Subscriptions = append(oldLogs.Subscriptions, struct {
|
||||
LogGroupNamePrefix string `json:"log_group_name_prefix"`
|
||||
FilterPattern string `json:"filter_pattern"`
|
||||
}{LogGroupNamePrefix: s.LogGroupNamePrefix, FilterPattern: s.FilterPattern})
|
||||
}
|
||||
oldTelemetry.Logs = oldLogs
|
||||
}
|
||||
|
||||
older.Telemetry = oldTelemetry
|
||||
return older
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package cloudintegrationtypes
|
||||
|
||||
type AzureAccountConfig struct {
|
||||
DeploymentRegion string `json:"deploymentRegion" required:"true"`
|
||||
ResourceGroups []string `json:"resourceGroups" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type UpdatableAzureAccountConfig struct {
|
||||
ResourceGroups []string `json:"resourceGroups" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AzurePostableAccountConfig = AzureAccountConfig
|
||||
|
||||
type AzureConnectionArtifact struct {
|
||||
CLICommand string `json:"cliCommand" required:"true"`
|
||||
CloudShellCommand string `json:"cloudShellCommand" required:"true"`
|
||||
}
|
||||
|
||||
type AzureServiceConfig struct {
|
||||
Logs *AzureServiceLogsConfig `json:"logs" required:"true"`
|
||||
Metrics *AzureServiceMetricsConfig `json:"metrics" required:"true"`
|
||||
}
|
||||
|
||||
type AzureServiceLogsConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type AzureServiceMetricsConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type AzureTelemetryCollectionStrategy struct {
|
||||
ResourceType string `json:"resourceType" required:"true"`
|
||||
Metrics *AzureMetricsCollectionStrategy `json:"metrics,omitempty" required:"false" nullable:"false"`
|
||||
Logs *AzureLogsCollectionStrategy `json:"logs,omitempty" required:"false" nullable:"false"`
|
||||
}
|
||||
|
||||
// AzureMetricsCollectionStrategy no additional config required for metrics, will be added in future as required.
|
||||
type AzureMetricsCollectionStrategy struct{}
|
||||
|
||||
type AzureLogsCollectionStrategy struct {
|
||||
// List of categories to enable for diagnostic settings, to start with it will have 'allLogs' and no filtering.
|
||||
CategoryGroups []string `json:"categoryGroups" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AzureIntegrationConfig struct {
|
||||
DeploymentRegion string `json:"deploymentRegion" required:"true"`
|
||||
ResourceGroups []string `json:"resourceGroups" required:"true" nullable:"false"`
|
||||
TelemetryCollectionStrategy []*AzureTelemetryCollectionStrategy `json:"telemetryCollectionStrategy" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
func NewAzureIntegrationConfig(
|
||||
deploymentRegion string,
|
||||
resourceGroups []string,
|
||||
strategies []*AzureTelemetryCollectionStrategy,
|
||||
) *AzureIntegrationConfig {
|
||||
return &AzureIntegrationConfig{
|
||||
DeploymentRegion: deploymentRegion,
|
||||
ResourceGroups: resourceGroups,
|
||||
TelemetryCollectionStrategy: strategies,
|
||||
}
|
||||
}
|
||||
@@ -2,171 +2,107 @@ package cloudintegrationtypes
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type CloudProviderRegion struct{ valuer.String }
|
||||
|
||||
var ErrCodeInvalidCloudRegion = errors.MustNewCode("invalid_cloud_region")
|
||||
var (
|
||||
// AWS regions.
|
||||
AWSRegionAFSouth1 = CloudProviderRegion{valuer.NewString("af-south-1")} // Africa (Cape Town).
|
||||
AWSRegionAPEast1 = CloudProviderRegion{valuer.NewString("ap-east-1")} // Asia Pacific (Hong Kong).
|
||||
AWSRegionAPNortheast1 = CloudProviderRegion{valuer.NewString("ap-northeast-1")} // Asia Pacific (Tokyo).
|
||||
AWSRegionAPNortheast2 = CloudProviderRegion{valuer.NewString("ap-northeast-2")} // Asia Pacific (Seoul).
|
||||
AWSRegionAPNortheast3 = CloudProviderRegion{valuer.NewString("ap-northeast-3")} // Asia Pacific (Osaka).
|
||||
AWSRegionAPSouth1 = CloudProviderRegion{valuer.NewString("ap-south-1")} // Asia Pacific (Mumbai).
|
||||
AWSRegionAPSouth2 = CloudProviderRegion{valuer.NewString("ap-south-2")} // Asia Pacific (Hyderabad).
|
||||
AWSRegionAPSoutheast1 = CloudProviderRegion{valuer.NewString("ap-southeast-1")} // Asia Pacific (Singapore).
|
||||
AWSRegionAPSoutheast2 = CloudProviderRegion{valuer.NewString("ap-southeast-2")} // Asia Pacific (Sydney).
|
||||
AWSRegionAPSoutheast3 = CloudProviderRegion{valuer.NewString("ap-southeast-3")} // Asia Pacific (Jakarta).
|
||||
AWSRegionAPSoutheast4 = CloudProviderRegion{valuer.NewString("ap-southeast-4")} // Asia Pacific (Melbourne).
|
||||
AWSRegionCACentral1 = CloudProviderRegion{valuer.NewString("ca-central-1")} // Canada (Central).
|
||||
AWSRegionCAWest1 = CloudProviderRegion{valuer.NewString("ca-west-1")} // Canada West (Calgary).
|
||||
AWSRegionEUCentral1 = CloudProviderRegion{valuer.NewString("eu-central-1")} // Europe (Frankfurt).
|
||||
AWSRegionEUCentral2 = CloudProviderRegion{valuer.NewString("eu-central-2")} // Europe (Zurich).
|
||||
AWSRegionEUNorth1 = CloudProviderRegion{valuer.NewString("eu-north-1")} // Europe (Stockholm).
|
||||
AWSRegionEUSouth1 = CloudProviderRegion{valuer.NewString("eu-south-1")} // Europe (Milan).
|
||||
AWSRegionEUSouth2 = CloudProviderRegion{valuer.NewString("eu-south-2")} // Europe (Spain).
|
||||
AWSRegionEUWest1 = CloudProviderRegion{valuer.NewString("eu-west-1")} // Europe (Ireland).
|
||||
AWSRegionEUWest2 = CloudProviderRegion{valuer.NewString("eu-west-2")} // Europe (London).
|
||||
AWSRegionEUWest3 = CloudProviderRegion{valuer.NewString("eu-west-3")} // Europe (Paris).
|
||||
AWSRegionILCentral1 = CloudProviderRegion{valuer.NewString("il-central-1")} // Israel (Tel Aviv).
|
||||
AWSRegionMECentral1 = CloudProviderRegion{valuer.NewString("me-central-1")} // Middle East (UAE).
|
||||
AWSRegionMESouth1 = CloudProviderRegion{valuer.NewString("me-south-1")} // Middle East (Bahrain).
|
||||
AWSRegionSAEast1 = CloudProviderRegion{valuer.NewString("sa-east-1")} // South America (Sao Paulo).
|
||||
AWSRegionUSEast1 = CloudProviderRegion{valuer.NewString("us-east-1")} // US East (N. Virginia).
|
||||
AWSRegionUSEast2 = CloudProviderRegion{valuer.NewString("us-east-2")} // US East (Ohio).
|
||||
AWSRegionUSWest1 = CloudProviderRegion{valuer.NewString("us-west-1")} // US West (N. California).
|
||||
AWSRegionUSWest2 = CloudProviderRegion{valuer.NewString("us-west-2")} // US West (Oregon).
|
||||
|
||||
// Azure regions.
|
||||
AzureRegionAustraliaCentral = CloudProviderRegion{valuer.NewString("australiacentral")} // Australia Central.
|
||||
AzureRegionAustraliaCentral2 = CloudProviderRegion{valuer.NewString("australiacentral2")} // Australia Central 2.
|
||||
AzureRegionAustraliaEast = CloudProviderRegion{valuer.NewString("australiaeast")} // Australia East.
|
||||
AzureRegionAustraliaSoutheast = CloudProviderRegion{valuer.NewString("australiasoutheast")} // Australia Southeast.
|
||||
AzureRegionAustriaEast = CloudProviderRegion{valuer.NewString("austriaeast")} // Austria East.
|
||||
AzureRegionBelgiumCentral = CloudProviderRegion{valuer.NewString("belgiumcentral")} // Belgium Central.
|
||||
AzureRegionBrazilSouth = CloudProviderRegion{valuer.NewString("brazilsouth")} // Brazil South.
|
||||
AzureRegionBrazilSoutheast = CloudProviderRegion{valuer.NewString("brazilsoutheast")} // Brazil Southeast.
|
||||
AzureRegionCanadaCentral = CloudProviderRegion{valuer.NewString("canadacentral")} // Canada Central.
|
||||
AzureRegionCanadaEast = CloudProviderRegion{valuer.NewString("canadaeast")} // Canada East.
|
||||
AzureRegionCentralIndia = CloudProviderRegion{valuer.NewString("centralindia")} // Central India.
|
||||
AzureRegionCentralUS = CloudProviderRegion{valuer.NewString("centralus")} // Central US.
|
||||
AzureRegionChileCentral = CloudProviderRegion{valuer.NewString("chilecentral")} // Chile Central.
|
||||
AzureRegionDenmarkEast = CloudProviderRegion{valuer.NewString("denmarkeast")} // Denmark East.
|
||||
AzureRegionEastAsia = CloudProviderRegion{valuer.NewString("eastasia")} // East Asia.
|
||||
AzureRegionEastUS = CloudProviderRegion{valuer.NewString("eastus")} // East US.
|
||||
AzureRegionEastUS2 = CloudProviderRegion{valuer.NewString("eastus2")} // East US 2.
|
||||
AzureRegionFranceCentral = CloudProviderRegion{valuer.NewString("francecentral")} // France Central.
|
||||
AzureRegionFranceSouth = CloudProviderRegion{valuer.NewString("francesouth")} // France South.
|
||||
AzureRegionGermanyNorth = CloudProviderRegion{valuer.NewString("germanynorth")} // Germany North.
|
||||
AzureRegionGermanyWestCentral = CloudProviderRegion{valuer.NewString("germanywestcentral")} // Germany West Central.
|
||||
AzureRegionIndonesiaCentral = CloudProviderRegion{valuer.NewString("indonesiacentral")} // Indonesia Central.
|
||||
AzureRegionIsraelCentral = CloudProviderRegion{valuer.NewString("israelcentral")} // Israel Central.
|
||||
AzureRegionItalyNorth = CloudProviderRegion{valuer.NewString("italynorth")} // Italy North.
|
||||
AzureRegionJapanEast = CloudProviderRegion{valuer.NewString("japaneast")} // Japan East.
|
||||
AzureRegionJapanWest = CloudProviderRegion{valuer.NewString("japanwest")} // Japan West.
|
||||
AzureRegionKoreaCentral = CloudProviderRegion{valuer.NewString("koreacentral")} // Korea Central.
|
||||
AzureRegionKoreaSouth = CloudProviderRegion{valuer.NewString("koreasouth")} // Korea South.
|
||||
AzureRegionMalaysiaWest = CloudProviderRegion{valuer.NewString("malaysiawest")} // Malaysia West.
|
||||
AzureRegionMexicoCentral = CloudProviderRegion{valuer.NewString("mexicocentral")} // Mexico Central.
|
||||
AzureRegionNewZealandNorth = CloudProviderRegion{valuer.NewString("newzealandnorth")} // New Zealand North.
|
||||
AzureRegionNorthCentralUS = CloudProviderRegion{valuer.NewString("northcentralus")} // North Central US.
|
||||
AzureRegionNorthEurope = CloudProviderRegion{valuer.NewString("northeurope")} // North Europe.
|
||||
AzureRegionNorwayEast = CloudProviderRegion{valuer.NewString("norwayeast")} // Norway East.
|
||||
AzureRegionNorwayWest = CloudProviderRegion{valuer.NewString("norwaywest")} // Norway West.
|
||||
AzureRegionPolandCentral = CloudProviderRegion{valuer.NewString("polandcentral")} // Poland Central.
|
||||
AzureRegionQatarCentral = CloudProviderRegion{valuer.NewString("qatarcentral")} // Qatar Central.
|
||||
AzureRegionSouthAfricaNorth = CloudProviderRegion{valuer.NewString("southafricanorth")} // South Africa North.
|
||||
AzureRegionSouthAfricaWest = CloudProviderRegion{valuer.NewString("southafricawest")} // South Africa West.
|
||||
AzureRegionSouthCentralUS = CloudProviderRegion{valuer.NewString("southcentralus")} // South Central US.
|
||||
AzureRegionSouthIndia = CloudProviderRegion{valuer.NewString("southindia")} // South India.
|
||||
AzureRegionSoutheastAsia = CloudProviderRegion{valuer.NewString("southeastasia")} // Southeast Asia.
|
||||
AzureRegionSpainCentral = CloudProviderRegion{valuer.NewString("spaincentral")} // Spain Central.
|
||||
AzureRegionSwedenCentral = CloudProviderRegion{valuer.NewString("swedencentral")} // Sweden Central.
|
||||
AzureRegionSwitzerlandNorth = CloudProviderRegion{valuer.NewString("switzerlandnorth")} // Switzerland North.
|
||||
AzureRegionSwitzerlandWest = CloudProviderRegion{valuer.NewString("switzerlandwest")} // Switzerland West.
|
||||
AzureRegionUAECentral = CloudProviderRegion{valuer.NewString("uaecentral")} // UAE Central.
|
||||
AzureRegionUAENorth = CloudProviderRegion{valuer.NewString("uaenorth")} // UAE North.
|
||||
AzureRegionUKSouth = CloudProviderRegion{valuer.NewString("uksouth")} // UK South.
|
||||
AzureRegionUKWest = CloudProviderRegion{valuer.NewString("ukwest")} // UK West.
|
||||
AzureRegionWestCentralUS = CloudProviderRegion{valuer.NewString("westcentralus")} // West Central US.
|
||||
AzureRegionWestEurope = CloudProviderRegion{valuer.NewString("westeurope")} // West Europe.
|
||||
AzureRegionWestIndia = CloudProviderRegion{valuer.NewString("westindia")} // West India.
|
||||
AzureRegionWestUS = CloudProviderRegion{valuer.NewString("westus")} // West US.
|
||||
AzureRegionWestUS2 = CloudProviderRegion{valuer.NewString("westus2")} // West US 2.
|
||||
AzureRegionWestUS3 = CloudProviderRegion{valuer.NewString("westus3")} // West US 3.
|
||||
)
|
||||
|
||||
func Enum() []any {
|
||||
return []any{
|
||||
// AWS regions.
|
||||
AWSRegionAFSouth1, AWSRegionAPEast1, AWSRegionAPNortheast1, AWSRegionAPNortheast2, AWSRegionAPNortheast3,
|
||||
AWSRegionAPSouth1, AWSRegionAPSouth2, AWSRegionAPSoutheast1, AWSRegionAPSoutheast2, AWSRegionAPSoutheast3,
|
||||
AWSRegionAPSoutheast4, AWSRegionCACentral1, AWSRegionCAWest1, AWSRegionEUCentral1, AWSRegionEUCentral2, AWSRegionEUNorth1,
|
||||
AWSRegionEUSouth1, AWSRegionEUSouth2, AWSRegionEUWest1, AWSRegionEUWest2, AWSRegionEUWest3,
|
||||
AWSRegionILCentral1, AWSRegionMECentral1, AWSRegionMESouth1, AWSRegionSAEast1, AWSRegionUSEast1, AWSRegionUSEast2,
|
||||
AWSRegionUSWest1, AWSRegionUSWest2,
|
||||
// Azure regions.
|
||||
AzureRegionAustraliaCentral, AzureRegionAustraliaCentral2, AzureRegionAustraliaEast, AzureRegionAustraliaSoutheast,
|
||||
AzureRegionAustriaEast, AzureRegionBelgiumCentral, AzureRegionBrazilSouth, AzureRegionBrazilSoutheast,
|
||||
AzureRegionCanadaCentral, AzureRegionCanadaEast, AzureRegionCentralIndia, AzureRegionCentralUS,
|
||||
AzureRegionChileCentral, AzureRegionDenmarkEast, AzureRegionEastAsia, AzureRegionEastUS, AzureRegionEastUS2,
|
||||
AzureRegionFranceCentral, AzureRegionFranceSouth, AzureRegionGermanyNorth, AzureRegionGermanyWestCentral,
|
||||
AzureRegionIndonesiaCentral, AzureRegionIsraelCentral, AzureRegionItalyNorth, AzureRegionJapanEast, AzureRegionJapanWest,
|
||||
AzureRegionKoreaCentral, AzureRegionKoreaSouth, AzureRegionMalaysiaWest, AzureRegionMexicoCentral,
|
||||
AzureRegionNewZealandNorth, AzureRegionNorthCentralUS, AzureRegionNorthEurope, AzureRegionNorwayEast, AzureRegionNorwayWest,
|
||||
AzureRegionPolandCentral, AzureRegionQatarCentral, AzureRegionSouthAfricaNorth, AzureRegionSouthAfricaWest,
|
||||
AzureRegionSouthCentralUS, AzureRegionSouthIndia, AzureRegionSoutheastAsia, AzureRegionSpainCentral,
|
||||
AzureRegionSwedenCentral, AzureRegionSwitzerlandNorth, AzureRegionSwitzerlandWest,
|
||||
AzureRegionUAECentral, AzureRegionUAENorth, AzureRegionUKSouth, AzureRegionUKWest,
|
||||
AzureRegionWestCentralUS, AzureRegionWestEurope, AzureRegionWestIndia, AzureRegionWestUS, AzureRegionWestUS2, AzureRegionWestUS3,
|
||||
}
|
||||
// List of all valid cloud regions on Amazon Web Services.
|
||||
var ValidAWSRegions = map[string]struct{}{
|
||||
"af-south-1": {}, // Africa (Cape Town).
|
||||
"ap-east-1": {}, // Asia Pacific (Hong Kong).
|
||||
"ap-northeast-1": {}, // Asia Pacific (Tokyo).
|
||||
"ap-northeast-2": {}, // Asia Pacific (Seoul).
|
||||
"ap-northeast-3": {}, // Asia Pacific (Osaka).
|
||||
"ap-south-1": {}, // Asia Pacific (Mumbai).
|
||||
"ap-south-2": {}, // Asia Pacific (Hyderabad).
|
||||
"ap-southeast-1": {}, // Asia Pacific (Singapore).
|
||||
"ap-southeast-2": {}, // Asia Pacific (Sydney).
|
||||
"ap-southeast-3": {}, // Asia Pacific (Jakarta).
|
||||
"ap-southeast-4": {}, // Asia Pacific (Melbourne).
|
||||
"ca-central-1": {}, // Canada (Central).
|
||||
"ca-west-1": {}, // Canada West (Calgary).
|
||||
"eu-central-1": {}, // Europe (Frankfurt).
|
||||
"eu-central-2": {}, // Europe (Zurich).
|
||||
"eu-north-1": {}, // Europe (Stockholm).
|
||||
"eu-south-1": {}, // Europe (Milan).
|
||||
"eu-south-2": {}, // Europe (Spain).
|
||||
"eu-west-1": {}, // Europe (Ireland).
|
||||
"eu-west-2": {}, // Europe (London).
|
||||
"eu-west-3": {}, // Europe (Paris).
|
||||
"il-central-1": {}, // Israel (Tel Aviv).
|
||||
"me-central-1": {}, // Middle East (UAE).
|
||||
"me-south-1": {}, // Middle East (Bahrain).
|
||||
"sa-east-1": {}, // South America (Sao Paulo).
|
||||
"us-east-1": {}, // US East (N. Virginia).
|
||||
"us-east-2": {}, // US East (Ohio).
|
||||
"us-west-1": {}, // US West (N. California).
|
||||
"us-west-2": {}, // US West (Oregon).
|
||||
}
|
||||
|
||||
var SupportedRegions = map[CloudProviderType][]CloudProviderRegion{
|
||||
CloudProviderTypeAWS: {
|
||||
AWSRegionAFSouth1, AWSRegionAPEast1, AWSRegionAPNortheast1, AWSRegionAPNortheast2, AWSRegionAPNortheast3,
|
||||
AWSRegionAPSouth1, AWSRegionAPSouth2, AWSRegionAPSoutheast1, AWSRegionAPSoutheast2, AWSRegionAPSoutheast3,
|
||||
AWSRegionAPSoutheast4, AWSRegionCACentral1, AWSRegionCAWest1, AWSRegionEUCentral1, AWSRegionEUCentral2, AWSRegionEUNorth1,
|
||||
AWSRegionEUSouth1, AWSRegionEUSouth2, AWSRegionEUWest1, AWSRegionEUWest2, AWSRegionEUWest3,
|
||||
AWSRegionILCentral1, AWSRegionMECentral1, AWSRegionMESouth1, AWSRegionSAEast1, AWSRegionUSEast1, AWSRegionUSEast2,
|
||||
AWSRegionUSWest1, AWSRegionUSWest2,
|
||||
},
|
||||
CloudProviderTypeAzure: {
|
||||
AzureRegionAustraliaCentral, AzureRegionAustraliaCentral2, AzureRegionAustraliaEast, AzureRegionAustraliaSoutheast,
|
||||
AzureRegionAustriaEast, AzureRegionBelgiumCentral, AzureRegionBrazilSouth, AzureRegionBrazilSoutheast,
|
||||
AzureRegionCanadaCentral, AzureRegionCanadaEast, AzureRegionCentralIndia, AzureRegionCentralUS,
|
||||
AzureRegionChileCentral, AzureRegionDenmarkEast, AzureRegionEastAsia, AzureRegionEastUS, AzureRegionEastUS2,
|
||||
AzureRegionFranceCentral, AzureRegionFranceSouth, AzureRegionGermanyNorth, AzureRegionGermanyWestCentral,
|
||||
AzureRegionIndonesiaCentral, AzureRegionIsraelCentral, AzureRegionItalyNorth, AzureRegionJapanEast, AzureRegionJapanWest,
|
||||
AzureRegionKoreaCentral, AzureRegionKoreaSouth, AzureRegionMalaysiaWest, AzureRegionMexicoCentral,
|
||||
AzureRegionNewZealandNorth, AzureRegionNorthCentralUS, AzureRegionNorthEurope, AzureRegionNorwayEast, AzureRegionNorwayWest,
|
||||
AzureRegionPolandCentral, AzureRegionQatarCentral, AzureRegionSouthAfricaNorth, AzureRegionSouthAfricaWest,
|
||||
AzureRegionSouthCentralUS, AzureRegionSouthIndia, AzureRegionSoutheastAsia, AzureRegionSpainCentral,
|
||||
AzureRegionSwedenCentral, AzureRegionSwitzerlandNorth, AzureRegionSwitzerlandWest,
|
||||
AzureRegionUAECentral, AzureRegionUAENorth, AzureRegionUKSouth, AzureRegionUKWest,
|
||||
AzureRegionWestCentralUS, AzureRegionWestEurope, AzureRegionWestIndia, AzureRegionWestUS, AzureRegionWestUS2, AzureRegionWestUS3,
|
||||
},
|
||||
// List of all valid cloud regions for Microsoft Azure.
|
||||
var ValidAzureRegions = map[string]struct{}{
|
||||
"australiacentral": {}, // Australia Central
|
||||
"australiacentral2": {}, // Australia Central 2
|
||||
"australiaeast": {}, // Australia East
|
||||
"australiasoutheast": {}, // Australia Southeast
|
||||
"austriaeast": {}, // Austria East
|
||||
"belgiumcentral": {}, // Belgium Central
|
||||
"brazilsouth": {}, // Brazil South
|
||||
"brazilsoutheast": {}, // Brazil Southeast
|
||||
"canadacentral": {}, // Canada Central
|
||||
"canadaeast": {}, // Canada East
|
||||
"centralindia": {}, // Central India
|
||||
"centralus": {}, // Central US
|
||||
"chilecentral": {}, // Chile Central
|
||||
"denmarkeast": {}, // Denmark East
|
||||
"eastasia": {}, // East Asia
|
||||
"eastus": {}, // East US
|
||||
"eastus2": {}, // East US 2
|
||||
"francecentral": {}, // France Central
|
||||
"francesouth": {}, // France South
|
||||
"germanynorth": {}, // Germany North
|
||||
"germanywestcentral": {}, // Germany West Central
|
||||
"indonesiacentral": {}, // Indonesia Central
|
||||
"israelcentral": {}, // Israel Central
|
||||
"italynorth": {}, // Italy North
|
||||
"japaneast": {}, // Japan East
|
||||
"japanwest": {}, // Japan West
|
||||
"koreacentral": {}, // Korea Central
|
||||
"koreasouth": {}, // Korea South
|
||||
"malaysiawest": {}, // Malaysia West
|
||||
"mexicocentral": {}, // Mexico Central
|
||||
"newzealandnorth": {}, // New Zealand North
|
||||
"northcentralus": {}, // North Central US
|
||||
"northeurope": {}, // North Europe
|
||||
"norwayeast": {}, // Norway East
|
||||
"norwaywest": {}, // Norway West
|
||||
"polandcentral": {}, // Poland Central
|
||||
"qatarcentral": {}, // Qatar Central
|
||||
"southafricanorth": {}, // South Africa North
|
||||
"southafricawest": {}, // South Africa West
|
||||
"southcentralus": {}, // South Central US
|
||||
"southindia": {}, // South India
|
||||
"southeastasia": {}, // Southeast Asia
|
||||
"spaincentral": {}, // Spain Central
|
||||
"swedencentral": {}, // Sweden Central
|
||||
"switzerlandnorth": {}, // Switzerland North
|
||||
"switzerlandwest": {}, // Switzerland West
|
||||
"uaecentral": {}, // UAE Central
|
||||
"uaenorth": {}, // UAE North
|
||||
"uksouth": {}, // UK South
|
||||
"ukwest": {}, // UK West
|
||||
"westcentralus": {}, // West Central US
|
||||
"westeurope": {}, // West Europe
|
||||
"westindia": {}, // West India
|
||||
"westus": {}, // West US
|
||||
"westus2": {}, // West US 2
|
||||
"westus3": {}, // West US 3
|
||||
}
|
||||
|
||||
func validateAWSRegion(region string) error {
|
||||
for _, r := range SupportedRegions[CloudProviderTypeAWS] {
|
||||
if r.StringValue() == region {
|
||||
return nil
|
||||
}
|
||||
_, ok := ValidAWSRegions[region]
|
||||
if !ok {
|
||||
return errors.NewInvalidInputf(ErrCodeInvalidCloudRegion, "invalid AWS region: %s", region)
|
||||
}
|
||||
|
||||
return errors.NewInvalidInputf(ErrCodeInvalidCloudRegion, "invalid AWS region: %s", region)
|
||||
}
|
||||
|
||||
func validateAzureRegion(region string) error {
|
||||
for _, r := range SupportedRegions[CloudProviderTypeAzure] {
|
||||
if r.StringValue() == region {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.NewInvalidInputf(ErrCodeInvalidCloudRegion, "invalid Azure region: %s", region)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
var ErrCodeInvalidServiceID = errors.MustNewCode("invalid_service_id")
|
||||
|
||||
type CloudIntegrationService struct {
|
||||
types.Identifiable
|
||||
types.TimeAuditable
|
||||
@@ -21,8 +23,24 @@ type CloudIntegrationService struct {
|
||||
}
|
||||
|
||||
type ServiceConfig struct {
|
||||
AWS *AWSServiceConfig `json:"aws,omitempty" required:"false" nullable:"false"`
|
||||
Azure *AzureServiceConfig `json:"azure,omitempty" required:"false" nullable:"false"`
|
||||
// required till new providers are added
|
||||
AWS *AWSServiceConfig `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSServiceConfig struct {
|
||||
Logs *AWSServiceLogsConfig `json:"logs"`
|
||||
Metrics *AWSServiceMetricsConfig `json:"metrics"`
|
||||
}
|
||||
|
||||
// AWSServiceLogsConfig is AWS specific logs config for a service
|
||||
// NOTE: the JSON keys are snake case for backward compatibility with existing agents.
|
||||
type AWSServiceLogsConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
S3Buckets map[string][]string `json:"s3Buckets,omitempty"`
|
||||
}
|
||||
|
||||
type AWSServiceMetricsConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// ServiceMetadata helps to quickly list available services and whether it is enabled or not.
|
||||
@@ -88,8 +106,7 @@ type DataCollected struct {
|
||||
// TelemetryCollectionStrategy is cloud provider specific configuration for signal collection,
|
||||
// this is used by agent to understand the nitty-gritty for collecting telemetry for the cloud provider.
|
||||
type TelemetryCollectionStrategy struct {
|
||||
AWS *AWSTelemetryCollectionStrategy `json:"aws,omitempty" required:"false" nullable:"false"`
|
||||
Azure *AzureTelemetryCollectionStrategy `json:"azure,omitempty" required:"false" nullable:"false"`
|
||||
AWS *AWSTelemetryCollectionStrategy `json:"aws" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
// Assets represents the collection of dashboards.
|
||||
@@ -113,6 +130,65 @@ type CollectedMetric struct {
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// OldAWSCollectionStrategy is the backward-compatible snake_case form of AWSCollectionStrategy,
|
||||
// used in the legacy integration_config response field for older agents.
|
||||
type OldAWSCollectionStrategy struct {
|
||||
Provider string `json:"provider"`
|
||||
Metrics *OldAWSMetricsStrategy `json:"aws_metrics,omitempty"`
|
||||
Logs *OldAWSLogsStrategy `json:"aws_logs,omitempty"`
|
||||
S3Buckets map[string][]string `json:"s3_buckets,omitempty"`
|
||||
}
|
||||
|
||||
// OldAWSMetricsStrategy is the snake_case form of AWSMetricsStrategy for older agents.
|
||||
type OldAWSMetricsStrategy struct {
|
||||
StreamFilters []struct {
|
||||
Namespace string `json:"Namespace"`
|
||||
MetricNames []string `json:"MetricNames,omitempty"`
|
||||
} `json:"cloudwatch_metric_stream_filters"`
|
||||
}
|
||||
|
||||
// OldAWSLogsStrategy is the snake_case form of AWSLogsStrategy for older agents.
|
||||
type OldAWSLogsStrategy struct {
|
||||
Subscriptions []struct {
|
||||
LogGroupNamePrefix string `json:"log_group_name_prefix"`
|
||||
FilterPattern string `json:"filter_pattern"`
|
||||
} `json:"cloudwatch_logs_subscriptions"`
|
||||
}
|
||||
|
||||
// AWSTelemetryCollectionStrategy represents signal collection strategy for AWS services.
|
||||
type AWSTelemetryCollectionStrategy struct {
|
||||
Metrics *AWSMetricsCollectionStrategy `json:"metrics,omitempty" required:"false" nullable:"false"`
|
||||
Logs *AWSLogsCollectionStrategy `json:"logs,omitempty" required:"false" nullable:"false"`
|
||||
S3Buckets map[string][]string `json:"s3Buckets,omitempty" required:"false"` // Only available in S3 Sync Service Type in AWS
|
||||
}
|
||||
|
||||
// AWSMetricsCollectionStrategy represents metrics collection strategy for AWS services.
|
||||
type AWSMetricsCollectionStrategy struct {
|
||||
// to be used as https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudwatch-metricstream.html#cfn-cloudwatch-metricstream-includefilters
|
||||
StreamFilters []*AWSCloudWatchMetricStreamFilter `json:"streamFilters" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSCloudWatchMetricStreamFilter struct {
|
||||
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudwatch-metricstream-metricstreamfilter.html
|
||||
Namespace string `json:"namespace" required:"true"`
|
||||
MetricNames []string `json:"metricNames,omitempty" required:"false" nullable:"false"`
|
||||
}
|
||||
|
||||
// AWSLogsCollectionStrategy represents logs collection strategy for AWS services.
|
||||
type AWSLogsCollectionStrategy struct {
|
||||
Subscriptions []*AWSCloudWatchLogsSubscription `json:"subscriptions" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type AWSCloudWatchLogsSubscription struct {
|
||||
// subscribe to all logs groups with specified prefix.
|
||||
// eg: `/aws/rds/`
|
||||
LogGroupNamePrefix string `json:"logGroupNamePrefix" required:"true"`
|
||||
|
||||
// https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html
|
||||
// "" implies no filtering is required
|
||||
FilterPattern string `json:"filterPattern" required:"true"`
|
||||
}
|
||||
|
||||
// Dashboard represents a dashboard definition for cloud integration.
|
||||
// This is used to show available pre-made dashboards for a service,
|
||||
// hence has additional fields like id, title and description.
|
||||
@@ -123,18 +199,7 @@ type Dashboard struct {
|
||||
Definition dashboardtypes.StorableDashboardData `json:"definition,omitempty"`
|
||||
}
|
||||
|
||||
func NewCloudIntegrationService(serviceID ServiceID, cloudIntegrationID valuer.UUID, provider CloudProviderType, config *ServiceConfig) (*CloudIntegrationService, error) {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
if config.AWS == nil {
|
||||
return nil, errors.NewInvalidInputf(ErrCodeInvalidInput, "AWS config is required for AWS service")
|
||||
}
|
||||
case CloudProviderTypeAzure:
|
||||
if config.Azure == nil {
|
||||
return nil, errors.NewInvalidInputf(ErrCodeInvalidInput, "Azure config is required for Azure service")
|
||||
}
|
||||
}
|
||||
|
||||
func NewCloudIntegrationService(serviceID ServiceID, cloudIntegrationID valuer.UUID, config *ServiceConfig) *CloudIntegrationService {
|
||||
return &CloudIntegrationService{
|
||||
Identifiable: types.Identifiable{
|
||||
ID: valuer.GenerateUUID(),
|
||||
@@ -146,7 +211,7 @@ func NewCloudIntegrationService(serviceID ServiceID, cloudIntegrationID valuer.U
|
||||
Type: serviceID,
|
||||
Config: config,
|
||||
CloudIntegrationID: cloudIntegrationID,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewCloudIntegrationServiceFromStorable(stored *StorableCloudIntegrationService, config *ServiceConfig) *CloudIntegrationService {
|
||||
@@ -203,22 +268,6 @@ func NewServiceConfigFromJSON(provider CloudProviderType, jsonString string) (*S
|
||||
}
|
||||
|
||||
return &ServiceConfig{AWS: awsServiceConfig}, nil
|
||||
case CloudProviderTypeAzure:
|
||||
azureServiceConfig := new(AzureServiceConfig)
|
||||
|
||||
if storableServiceConfig.Azure.Logs != nil {
|
||||
azureServiceConfig.Logs = &AzureServiceLogsConfig{
|
||||
Enabled: storableServiceConfig.Azure.Logs.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
if storableServiceConfig.Azure.Metrics != nil {
|
||||
azureServiceConfig.Metrics = &AzureServiceMetricsConfig{
|
||||
Enabled: storableServiceConfig.Azure.Metrics.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
return &ServiceConfig{Azure: azureServiceConfig}, nil
|
||||
default:
|
||||
return nil, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider.StringValue())
|
||||
}
|
||||
@@ -256,10 +305,6 @@ func (config *ServiceConfig) IsServiceEnabled(provider CloudProviderType) bool {
|
||||
logsEnabled := config.AWS.Logs != nil && config.AWS.Logs.Enabled
|
||||
metricsEnabled := config.AWS.Metrics != nil && config.AWS.Metrics.Enabled
|
||||
return logsEnabled || metricsEnabled
|
||||
case CloudProviderTypeAzure:
|
||||
logsEnabled := config.Azure.Logs != nil && config.Azure.Logs.Enabled
|
||||
metricsEnabled := config.Azure.Metrics != nil && config.Azure.Metrics.Enabled
|
||||
return logsEnabled || metricsEnabled
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -271,8 +316,6 @@ func (config *ServiceConfig) IsMetricsEnabled(provider CloudProviderType) bool {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
return config.AWS.Metrics != nil && config.AWS.Metrics.Enabled
|
||||
case CloudProviderTypeAzure:
|
||||
return config.Azure.Metrics != nil && config.Azure.Metrics.Enabled
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -283,8 +326,6 @@ func (config *ServiceConfig) IsLogsEnabled(provider CloudProviderType) bool {
|
||||
switch provider {
|
||||
case CloudProviderTypeAWS:
|
||||
return config.AWS.Logs != nil && config.AWS.Logs.Enabled
|
||||
case CloudProviderTypeAzure:
|
||||
return config.Azure.Logs != nil && config.Azure.Logs.Enabled
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -367,3 +408,53 @@ func GetDashboardsFromAssets(
|
||||
|
||||
return dashboards
|
||||
}
|
||||
|
||||
// awsOlderIntegrationConfig converts a ProviderIntegrationConfig into the legacy snake_case
|
||||
// IntegrationConfig format consumed by older AWS agents. Returns nil if AWS config is absent.
|
||||
func awsOlderIntegrationConfig(cfg *ProviderIntegrationConfig) *IntegrationConfig {
|
||||
if cfg == nil || cfg.AWS == nil {
|
||||
return nil
|
||||
}
|
||||
awsCfg := cfg.AWS
|
||||
|
||||
older := &IntegrationConfig{
|
||||
EnabledRegions: awsCfg.EnabledRegions,
|
||||
}
|
||||
|
||||
if awsCfg.TelemetryCollectionStrategy == nil {
|
||||
return older
|
||||
}
|
||||
|
||||
// Older agents expect a "provider" field and fully snake_case keys inside telemetry.
|
||||
oldTelemetry := &OldAWSCollectionStrategy{
|
||||
Provider: CloudProviderTypeAWS.StringValue(),
|
||||
S3Buckets: awsCfg.TelemetryCollectionStrategy.S3Buckets,
|
||||
}
|
||||
|
||||
if awsCfg.TelemetryCollectionStrategy.Metrics != nil {
|
||||
// Convert camelCase cloudwatchMetricStreamFilters → snake_case cloudwatch_metric_stream_filters
|
||||
oldMetrics := &OldAWSMetricsStrategy{}
|
||||
for _, f := range awsCfg.TelemetryCollectionStrategy.Metrics.StreamFilters {
|
||||
oldMetrics.StreamFilters = append(oldMetrics.StreamFilters, struct {
|
||||
Namespace string `json:"Namespace"`
|
||||
MetricNames []string `json:"MetricNames,omitempty"`
|
||||
}{Namespace: f.Namespace, MetricNames: f.MetricNames})
|
||||
}
|
||||
oldTelemetry.Metrics = oldMetrics
|
||||
}
|
||||
|
||||
if awsCfg.TelemetryCollectionStrategy.Logs != nil {
|
||||
// Convert camelCase cloudwatchLogsSubscriptions → snake_case cloudwatch_logs_subscriptions
|
||||
oldLogs := &OldAWSLogsStrategy{}
|
||||
for _, s := range awsCfg.TelemetryCollectionStrategy.Logs.Subscriptions {
|
||||
oldLogs.Subscriptions = append(oldLogs.Subscriptions, struct {
|
||||
LogGroupNamePrefix string `json:"log_group_name_prefix"`
|
||||
FilterPattern string `json:"filter_pattern"`
|
||||
}{LogGroupNamePrefix: s.LogGroupNamePrefix, FilterPattern: s.FilterPattern})
|
||||
}
|
||||
oldTelemetry.Logs = oldLogs
|
||||
}
|
||||
|
||||
older.Telemetry = oldTelemetry
|
||||
return older
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
|
||||
type ServiceID struct{ valuer.String }
|
||||
|
||||
var ErrCodeInvalidServiceID = errors.MustNewCode("invalid_service_id")
|
||||
|
||||
var (
|
||||
AWSServiceALB = ServiceID{valuer.NewString("alb")}
|
||||
AWSServiceAPIGateway = ServiceID{valuer.NewString("api-gateway")}
|
||||
@@ -23,10 +21,6 @@ var (
|
||||
AWSServiceS3Sync = ServiceID{valuer.NewString("s3sync")}
|
||||
AWSServiceSNS = ServiceID{valuer.NewString("sns")}
|
||||
AWSServiceSQS = ServiceID{valuer.NewString("sqs")}
|
||||
|
||||
// Azure services.
|
||||
AzureServiceBlobStorage = ServiceID{valuer.NewString("blobstorage")}
|
||||
AzureServiceFrontDoor = ServiceID{valuer.NewString("frontdoor")}
|
||||
)
|
||||
|
||||
func (ServiceID) Enum() []any {
|
||||
@@ -44,8 +38,6 @@ func (ServiceID) Enum() []any {
|
||||
AWSServiceS3Sync,
|
||||
AWSServiceSNS,
|
||||
AWSServiceSQS,
|
||||
AzureServiceBlobStorage,
|
||||
AzureServiceFrontDoor,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,17 +58,18 @@ var SupportedServices = map[CloudProviderType][]ServiceID{
|
||||
AWSServiceSNS,
|
||||
AWSServiceSQS,
|
||||
},
|
||||
CloudProviderTypeAzure: {
|
||||
AzureServiceBlobStorage,
|
||||
AzureServiceFrontDoor,
|
||||
},
|
||||
}
|
||||
|
||||
// NewServiceID returns a new ServiceID from a string, validated against the supported services for the given cloud provider.
|
||||
func NewServiceID(provider CloudProviderType, service string) (ServiceID, error) {
|
||||
for _, s := range SupportedServices[provider] {
|
||||
services, ok := SupportedServices[provider]
|
||||
if !ok {
|
||||
return ServiceID{}, errors.NewInvalidInputf(ErrCodeInvalidServiceID, "no services defined for cloud provider: %s", provider)
|
||||
}
|
||||
for _, s := range services {
|
||||
if s.StringValue() == service {
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
return ServiceID{}, errors.NewInvalidInputf(ErrCodeInvalidServiceID, "invalid service id %q for %s cloud provider", service, provider.StringValue())
|
||||
return ServiceID{}, errors.NewInvalidInputf(ErrCodeInvalidServiceID, "invalid service id %q for cloud provider %s", service, provider)
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ func (provider *provider) PutMeters(_ context.Context, _ string, _ []byte) error
|
||||
return errors.New(errors.TypeUnsupported, zeus.ErrCodeUnsupported, "putting meters is not supported")
|
||||
}
|
||||
|
||||
func (provider *provider) PutMetersV2(_ context.Context, _ string, _ []byte) error {
|
||||
return errors.New(errors.TypeUnsupported, zeus.ErrCodeUnsupported, "putting meters v2 is not supported")
|
||||
}
|
||||
|
||||
func (provider *provider) PutProfile(_ context.Context, _ string, _ *zeustypes.PostableProfile) error {
|
||||
return errors.New(errors.TypeUnsupported, zeus.ErrCodeUnsupported, "putting profile is not supported")
|
||||
}
|
||||
|
||||
@@ -29,9 +29,12 @@ type Zeus interface {
|
||||
// Returns the billing details for the given license key.
|
||||
GetMeters(context.Context, string) ([]byte, error)
|
||||
|
||||
// Puts the meters for the given license key.
|
||||
// Puts the meters for the given license key using the legacy subscriptions service.
|
||||
PutMeters(context.Context, string, []byte) error
|
||||
|
||||
// Puts the meters for the given license key using Zeus.
|
||||
PutMetersV2(context.Context, string, []byte) error
|
||||
|
||||
// Put profile for the given license key.
|
||||
PutProfile(context.Context, string, *zeustypes.PostableProfile) error
|
||||
|
||||
|
||||
Reference in New Issue
Block a user