mirror of
https://github.com/SigNoz/signoz.git
synced 2026-09-16 16:30:41 +01:00
Compare commits
4 Commits
test/expli
...
v0.142.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57268e50b4 | ||
|
|
4933a2740c | ||
|
|
2481e3d5f4 | ||
|
|
099e81202d |
@@ -103,6 +103,7 @@ func runGenerateAuthz(_ context.Context) error {
|
||||
coretypes.NewResourceRef(coretypes.ResourceTelemetryResourceTraces).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceTelemetryResourceMetrics).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceTelemetryResourceMeterMetrics).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceMetaResourceQuickFilter).String(): true,
|
||||
}
|
||||
|
||||
allowedTypes := map[string]bool{}
|
||||
|
||||
@@ -197,6 +197,7 @@ components:
|
||||
title:
|
||||
type: string
|
||||
webhookUrl:
|
||||
format: password
|
||||
type: string
|
||||
required:
|
||||
- webhookUrl
|
||||
@@ -215,6 +216,7 @@ components:
|
||||
title:
|
||||
type: string
|
||||
token:
|
||||
format: password
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
@@ -225,6 +227,7 @@ components:
|
||||
AlertmanagertypesChannelJSMOpsConfig:
|
||||
properties:
|
||||
apiKey:
|
||||
format: password
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
@@ -243,6 +246,7 @@ components:
|
||||
AlertmanagertypesChannelJiraConfig:
|
||||
properties:
|
||||
apiToken:
|
||||
format: password
|
||||
type: string
|
||||
customFields:
|
||||
additionalProperties: {}
|
||||
@@ -317,6 +321,7 @@ components:
|
||||
title:
|
||||
type: string
|
||||
webhookUrl:
|
||||
format: password
|
||||
type: string
|
||||
required:
|
||||
- webhookUrl
|
||||
@@ -324,6 +329,7 @@ components:
|
||||
AlertmanagertypesChannelOpsgenieConfig:
|
||||
properties:
|
||||
apiKey:
|
||||
format: password
|
||||
type: string
|
||||
apiUrl:
|
||||
type: string
|
||||
@@ -364,6 +370,7 @@ components:
|
||||
group:
|
||||
type: string
|
||||
routingKey:
|
||||
format: password
|
||||
type: string
|
||||
sendResolved:
|
||||
nullable: true
|
||||
@@ -380,6 +387,7 @@ components:
|
||||
AlertmanagertypesChannelSlackConfig:
|
||||
properties:
|
||||
apiUrl:
|
||||
format: password
|
||||
type: string
|
||||
channel:
|
||||
type: string
|
||||
@@ -396,13 +404,16 @@ components:
|
||||
AlertmanagertypesChannelWebhookConfig:
|
||||
properties:
|
||||
bearerToken:
|
||||
format: password
|
||||
type: string
|
||||
password:
|
||||
format: password
|
||||
type: string
|
||||
sendResolved:
|
||||
nullable: true
|
||||
type: boolean
|
||||
url:
|
||||
format: password
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
|
||||
@@ -28,8 +28,10 @@ export default defineConfig({
|
||||
clean: true,
|
||||
override: {
|
||||
query: {
|
||||
useQuery: true,
|
||||
useMutation: true,
|
||||
// Leave useQuery/useMutation unset. Orval's verb tiebreak
|
||||
// (`if (verb === GET && isMutation) isQuery = false`) inverts every
|
||||
// operation when both are forced to true: GET becomes a mutation and
|
||||
// the write verbs become queries.
|
||||
useInvalidate: true,
|
||||
signal: true,
|
||||
useOperationIdAsQueryKey: false,
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
"crypto-js": "4.2.0",
|
||||
"d3-hierarchy": "3.1.2",
|
||||
"dayjs": "^1.10.7",
|
||||
"dompurify": "3.4.12",
|
||||
"dompurify": "3.4.15",
|
||||
"event-source-polyfill": "1.0.31",
|
||||
"eventemitter3": "5.0.1",
|
||||
"history": "4.10.1",
|
||||
@@ -118,7 +118,7 @@
|
||||
"react-redux": "^7.2.2",
|
||||
"react-rnd": "^10.5.3",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-router-dom-v5-compat": "6.30.3",
|
||||
"react-router-dom-v5-compat": "6.30.6",
|
||||
"react-syntax-highlighter": "15.5.0",
|
||||
"react-use": "^17.3.2",
|
||||
"react-virtuoso": "4.0.3",
|
||||
@@ -200,7 +200,7 @@
|
||||
"json-schema-to-typescript": "^15.0.4",
|
||||
"lint-staged": "^17.0.4",
|
||||
"msw": "1.3.2",
|
||||
"orval": "8.9.1",
|
||||
"orval": "8.22.0",
|
||||
"oxfmt": "0.54.0",
|
||||
"oxlint": "1.69.0",
|
||||
"oxlint-tsgolint": "0.23.0",
|
||||
@@ -209,10 +209,10 @@
|
||||
"react-resizable": "3.0.4",
|
||||
"redux-mock-store": "1.5.4",
|
||||
"sass": "1.97.3",
|
||||
"sharp": "0.35.0",
|
||||
"sharp": "0.35.4",
|
||||
"storybook": "10.5.9",
|
||||
"stylelint": "17.7.0",
|
||||
"svgo": "4.0.2",
|
||||
"stylelint": "17.15.0",
|
||||
"svgo": "4.1.0",
|
||||
"ts-jest": "29.4.9",
|
||||
"typescript-plugin-css-modules": "5.2.0",
|
||||
"use-sync-external-store": "1.6.0",
|
||||
@@ -232,26 +232,5 @@
|
||||
"*.(scss|css)": [
|
||||
"stylelint"
|
||||
]
|
||||
},
|
||||
"overrides": {
|
||||
"@babel/core@<=7.29.0": ">=7.29.6 <8",
|
||||
"@istanbuljs/load-nyc-config>js-yaml": ">=4.3.1 <5",
|
||||
"cookie@<0.7.0": ">=0.7.1 <1",
|
||||
"dompurify@<=3.4.10": ">=3.4.11 <4",
|
||||
"esbuild@>=0.27.3 <0.28.1": ">=0.28.1 <0.29.0",
|
||||
"js-cookie@<=3.0.5": ">=3.0.7 <4",
|
||||
"js-yaml@>=4.0.0 <=4.1.1": ">=4.2.0 <5",
|
||||
"prismjs@<1.30.0": ">=1.30.0 <2",
|
||||
"react-router@>=6.7.0 <6.30.4": ">=6.30.4 <7",
|
||||
"tmp@<0.2.6": ">=0.2.6 <0.3.0",
|
||||
"yaml@>=1.0.0 <1.10.3": ">=1.10.3 <2",
|
||||
"brace-expansion@<1.1.18": ">=1.1.18 <2",
|
||||
"brace-expansion@>=2.0.0 <2.1.4": ">=2.1.4 <3",
|
||||
"brace-expansion@>=5.0.0 <5.0.9": ">=5.0.9 <6",
|
||||
"fast-uri@<3.1.5": ">=3.1.5 <4",
|
||||
"immutable@<5.1.8": ">=5.1.8 <6",
|
||||
"js-yaml@>=4.0.0 <4.3.1": ">=4.3.1 <5",
|
||||
"less@<4.5.0": ">=4.5.0 <5",
|
||||
"nanoid@<3.3.18": ">=3.3.18 <4"
|
||||
}
|
||||
}
|
||||
|
||||
1047
frontend/pnpm-lock.yaml
generated
1047
frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,11 @@ overrides:
|
||||
'@babel/core@<=7.29.0': '>=7.29.6 <8'
|
||||
# via: jest > babel-plugin-istanbul > @istanbuljs/load-nyc-config@1.1.0 (js-yaml ^3.13.1)
|
||||
# remove: blocked — 1.1.0 is latest and still depends on js-yaml 3.x
|
||||
'@istanbuljs/load-nyc-config>js-yaml': '>=4.3.1 <5'
|
||||
'@istanbuljs/load-nyc-config>js-yaml': '>=4.3.2 <5'
|
||||
# via: msw@1.3.2 (devDep) > @mswjs/interceptors@0.17.10 (@xmldom/xmldom ^0.8.3)
|
||||
# remove: upgrade msw to >=2 (drops the 0.8.x interceptor). Do NOT open the cap:
|
||||
# 0.9.x is ESM-only and changes the DOMParser error contract
|
||||
'@xmldom/xmldom@<0.8.15': '>=0.8.15 <0.9'
|
||||
# via: babel-plugin-istanbul > test-exclude@6 > minimatch@3.1.5 (^1.1.7); also glob@7
|
||||
# remove: blocked — babel-plugin-istanbul pins test-exclude@6, which pins minimatch@3
|
||||
brace-expansion@<1.1.18: '>=1.1.18 <2'
|
||||
@@ -23,32 +27,39 @@ overrides:
|
||||
# via: eslint-plugin-sonarjs@4.0.2 (minimatch ^10.2.4) > minimatch@10.2.5 (^5.0.5)
|
||||
# remove: blocked — minimatch@10.2.6 (latest) only widens to ^5.0.8, still vulnerable
|
||||
'brace-expansion@>=5.0.0 <5.0.9': '>=5.0.9 <6'
|
||||
# via: direct devDep @babel/core > @babel/helper-compilation-targets (browserslist ^4.24.0)
|
||||
# remove: blocked, the range is open so the floor is what pulls the fix.
|
||||
# Also lifts baseline-browser-mapping, which browserslist@4.28.7 floors at ^2.11.20
|
||||
browserslist@<4.28.7: '>=4.28.7 <5'
|
||||
# via: msw@1.3.2 (devDep) > cookie ^0.4.2
|
||||
# remove: upgrade msw to >=2 (ships cookie ^1). Do NOT open the cap: cookie >=1 is
|
||||
# ESM-only and breaks msw under jest's CJS sandbox (kills every test suite)
|
||||
cookie@<0.7.0: '>=0.7.1 <1'
|
||||
# via: direct dep dompurify 3.4.0; @grafana/data@11.6.15 (3.4.0/3.2.4 exact);
|
||||
# via: @grafana/data@11.6.15 (3.4.0/3.2.4 exact);
|
||||
# @monaco-editor/react > monaco-editor@0.55.1 (3.2.7 exact)
|
||||
# remove: bump direct dep to 3.4.11; @grafana/data (latest 13.1.0) and
|
||||
# monaco-editor (latest 0.55.1) still pin vulnerable versions — blocked
|
||||
dompurify@<=3.4.10: '>=3.4.11 <4'
|
||||
# via: rolldown-vite@7.3.1 (esbuild ^0.27.0); orval@8.9.1 (^0.27.4); ts-jest@29.4.9 (~0.27.4)
|
||||
# remove: blocked. The direct dep is already on 3.4.15, but @grafana/data
|
||||
# (latest 13.1.0) and monaco-editor (latest 0.55.1) still pin vulnerable versions
|
||||
dompurify@<=3.4.12: '>=3.4.13 <4'
|
||||
# via: rolldown-vite@7.3.1 (esbuild ^0.27.0); ts-jest@29.4.9 (~0.27.4)
|
||||
# remove: blocked on rolldown-vite (7.3.1 is latest, still ^0.27.0);
|
||||
# orval >=8.20.0 and ts-jest >=29.4.11 already fixed on their side
|
||||
esbuild@>=0.27.3 <0.28.1: '>=0.28.1 <0.29.0'
|
||||
# via: @commitlint/cli > @commitlint/config-validator > ajv@8 (fast-uri ^3.0.1)
|
||||
# remove: blocked — ajv@8 caps fast-uri at ^3, and only 3.1.5 carries the fix
|
||||
fast-uri@<3.1.5: '>=3.1.5 <4'
|
||||
# remove: blocked — ajv@8 caps fast-uri at ^3, and only 3.1.6 carries the fix
|
||||
fast-uri@<3.1.6: '>=3.1.6 <4'
|
||||
# via: direct dep posthog-js@1.298.0 (fflate ^0.4.8)
|
||||
# remove: blocked, posthog-js 1.430.2 (latest) is still on ^0.4.8
|
||||
fflate@<0.4.9: '>=0.4.9 <0.5'
|
||||
# via: direct devDep sass@1.97.3 (immutable ^5.0.2)
|
||||
# remove: blocked — plain sass bumps stay within ^5, so the floor is what pulls 5.1.8
|
||||
immutable@<5.1.8: '>=5.1.8 <6'
|
||||
# via: react-use@17.5.1 (direct, js-cookie ^2.2.1); @grafana/data > react-use@17.6.0
|
||||
# remove: bump react-use to >=17.6.1 (js-cookie ^3); @grafana/data side blocked
|
||||
js-cookie@<=3.0.5: '>=3.0.7 <4'
|
||||
# via: @orval/core@8.9.1 (devDep, js-yaml 4.1.1 EXACT pin — not deletable);
|
||||
# json-schema-to-typescript@15 > @apidevtools/json-schema-ref-parser (^4.1.0)
|
||||
# remove: upgrade orval to >=8.20.0 (drops js-yaml dependency entirely)
|
||||
'js-yaml@>=4.0.0 <4.3.1': '>=4.3.1 <5'
|
||||
# via: json-schema-to-typescript@15 > @apidevtools/json-schema-ref-parser (^4.1.0);
|
||||
# @commitlint/cli > @commitlint/load > cosmiconfig@9 (^4.1.0)
|
||||
# remove: blocked, both consumers cap js-yaml at ^4 and only 4.3.2 carries the fix
|
||||
'js-yaml@>=4.0.0 <4.3.2': '>=4.3.2 <5'
|
||||
# via: typescript-plugin-css-modules@5.2.0 (less ^4.2.0) > less@4.4.0 (image-size ~0.5.0)
|
||||
# remove: bump typescript-plugin-css-modules once it floors less itself; image-size has
|
||||
# no patched release at all, so dropping the dep is the only fix — less@4.5.0 did
|
||||
@@ -59,10 +70,15 @@ overrides:
|
||||
# via: react-syntax-highlighter@15.5.0 (prismjs ^1.27.0 + refractor@3 ~1.27.0 tilde-pinned)
|
||||
# remove: bump react-syntax-highlighter to >=16.1.1 (prismjs ^1.30.0, refractor@5)
|
||||
prismjs@<1.30.0: '>=1.30.0 <2'
|
||||
# via: direct dep react-router-dom-v5-compat@6.30.3 (react-router 6.30.3 exact)
|
||||
# remove: bump react-router-dom-v5-compat to 6.30.4. Do NOT open the cap:
|
||||
# react-router >=7 requires React 19 and breaks the app-wide CompatRouter
|
||||
react-router@>=6.7.0 <6.30.4: '>=6.30.4 <7'
|
||||
# via: typescript-plugin-css-modules@5.2.0 > postcss-modules-local-by-default@4.2.0
|
||||
# (postcss-selector-parser ^7.0.0)
|
||||
# remove: blocked, 4.2.0 is latest and the range is open, so the floor is what pulls the fix
|
||||
postcss-selector-parser@>=7.1.0 <7.1.3: '>=7.1.3 <8'
|
||||
# via: @signozhq/ui > nuqs@2 (react-router ^6.4.0 || ^7)
|
||||
# remove: blocked. GHSA-wrjc-x8rr-h8h6 and the deserializeErrors advisory are patched
|
||||
# only in 7.18.0. Do NOT open the cap: react-router >=7 requires React 19 and breaks
|
||||
# the app-wide CompatRouter, so those two moderates stay until the app moves to React 19
|
||||
react-router@>=6.7.0 <6.30.6: '>=6.30.6 <7'
|
||||
# via: msw@1.3.2 (devDep) > inquirer@8 > external-editor@3.1.0 (tmp ^0.0.33)
|
||||
# remove: upgrade msw to >=2 (drops the inquirer/external-editor chain)
|
||||
tmp@<0.2.6: '>=0.2.6 <0.3.0'
|
||||
|
||||
@@ -14,15 +14,6 @@ done
|
||||
|
||||
echo "\n✅ Tag files renamed to index.ts"
|
||||
|
||||
# Format generated files
|
||||
echo "\n\n---\nRunning prettier...\n"
|
||||
if ! pnpm prettify src/api/generated; then
|
||||
echo "Formatting failed!"
|
||||
exit 1
|
||||
fi
|
||||
echo "\n✅ Formatting successful"
|
||||
|
||||
|
||||
# Fix linting issues
|
||||
echo "\n\n---\nRunning lint...\n"
|
||||
if ! pnpm lint:generated; then
|
||||
@@ -32,6 +23,15 @@ fi
|
||||
echo "\n✅ Lint check successful"
|
||||
|
||||
|
||||
# Format generated files (must run after lint: oxlint --fix writes unformatted autofixes)
|
||||
echo "\n\n---\nRunning prettier...\n"
|
||||
if ! pnpm prettify src/api/generated; then
|
||||
echo "Formatting failed!"
|
||||
exit 1
|
||||
fi
|
||||
echo "\n✅ Formatting successful"
|
||||
|
||||
|
||||
# Check for type errors
|
||||
echo "\n\n---\nChecking for type errors...\n"
|
||||
if ! tsc --noEmit; then
|
||||
|
||||
@@ -25,6 +25,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns the field keys the AI observability explorer can filter on, including the computed per-trace aggregates
|
||||
* @summary Get AI observability field keys
|
||||
@@ -111,7 +131,7 @@ export function useGetAIObservabilityFieldsKeys<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,7 +236,7 @@ export function useGetAIObservabilityFieldsValues<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,26 @@ import type { GetAlerts200, RenderErrorResponseDTO } from '../sigNoz.schemas';
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns alerts for the organization
|
||||
* @summary Get alerts
|
||||
@@ -77,7 +97,7 @@ export function useGetAlerts<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,6 +32,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint lists all auth domains
|
||||
* @summary List all auth domains
|
||||
@@ -98,7 +118,7 @@ export function useListAuthDomains<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,7 +343,7 @@ export const getGetAuthDomainQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getAuthDomain>>,
|
||||
@@ -360,7 +380,7 @@ export function useGetAuthDomain<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,6 +43,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint lists all notification channels for the organization
|
||||
* @summary List notification channels
|
||||
@@ -109,7 +129,7 @@ export function useListChannels<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,7 +354,7 @@ export const getGetChannelByIDQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getChannelByID>>,
|
||||
@@ -371,7 +391,7 @@ export function useGetChannelByID<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -738,7 +758,7 @@ export function useListNotificationChannels<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -967,7 +987,7 @@ export const getGetNotificationChannelQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getNotificationChannel>>,
|
||||
@@ -1005,7 +1025,7 @@ export function useGetNotificationChannel<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,6 +51,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* [Deprecated] This endpoint is called by the deployed agent to check in
|
||||
* @deprecated
|
||||
@@ -199,7 +219,7 @@ export const getListAccountsQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!cloudProvider,
|
||||
enabled: cloudProvider !== null && cloudProvider !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listAccounts>>,
|
||||
@@ -236,7 +256,7 @@ export function useListAccounts<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -480,7 +500,11 @@ export const getGetAccountQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!(cloudProvider && id),
|
||||
enabled:
|
||||
cloudProvider !== null &&
|
||||
cloudProvider !== undefined &&
|
||||
id !== null &&
|
||||
id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<Awaited<ReturnType<typeof getAccount>>, TError, TData> & {
|
||||
queryKey: QueryKey;
|
||||
@@ -515,7 +539,7 @@ export function useGetAccount<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -683,7 +707,11 @@ export const getListAccountServicesMetadataQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!(cloudProvider && id),
|
||||
enabled:
|
||||
cloudProvider !== null &&
|
||||
cloudProvider !== undefined &&
|
||||
id !== null &&
|
||||
id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listAccountServicesMetadata>>,
|
||||
@@ -724,7 +752,7 @@ export function useListAccountServicesMetadata<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -795,7 +823,13 @@ export const getGetAccountServiceQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!(cloudProvider && id && serviceId),
|
||||
enabled:
|
||||
cloudProvider !== null &&
|
||||
cloudProvider !== undefined &&
|
||||
id !== null &&
|
||||
id !== undefined &&
|
||||
serviceId !== null &&
|
||||
serviceId !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getAccountService>>,
|
||||
@@ -835,7 +869,7 @@ export function useGetAccountService<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1099,7 +1133,7 @@ export const getGetConnectionCredentialsQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!cloudProvider,
|
||||
enabled: cloudProvider !== null && cloudProvider !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getConnectionCredentials>>,
|
||||
@@ -1140,7 +1174,7 @@ export function useGetConnectionCredentials<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1205,7 +1239,7 @@ export const getListServicesMetadataQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!cloudProvider,
|
||||
enabled: cloudProvider !== null && cloudProvider !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listServicesMetadata>>,
|
||||
@@ -1245,7 +1279,7 @@ export function useListServicesMetadata<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1313,7 +1347,11 @@ export const getGetServiceQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!(cloudProvider && serviceId),
|
||||
enabled:
|
||||
cloudProvider !== null &&
|
||||
cloudProvider !== undefined &&
|
||||
serviceId !== null &&
|
||||
serviceId !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<Awaited<ReturnType<typeof getService>>, TError, TData> & {
|
||||
queryKey: QueryKey;
|
||||
@@ -1351,7 +1389,7 @@ export function useGetService<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,6 +72,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint deletes the public sharing config and disables the public sharing of a dashboard
|
||||
* @summary Delete public dashboard
|
||||
@@ -198,7 +218,7 @@ export const getGetPublicDashboardQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getPublicDashboard>>,
|
||||
@@ -235,7 +255,7 @@ export function useGetPublicDashboard<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,7 +520,7 @@ export const getGetPublicDashboardDataQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getPublicDashboardData>>,
|
||||
@@ -538,7 +558,7 @@ export function useGetPublicDashboardData<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -605,7 +625,7 @@ export const getGetPublicDashboardWidgetQueryRangeQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!(id && idx),
|
||||
enabled: id !== null && id !== undefined && idx !== null && idx !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getPublicDashboardWidgetQueryRange>>,
|
||||
@@ -646,7 +666,7 @@ export function useGetPublicDashboardWidgetQueryRange<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -731,7 +751,7 @@ export function useListDashboardViews<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1091,7 +1111,7 @@ export function useListDashboardsV2<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1317,7 +1337,7 @@ export const getGetDashboardV2QueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getDashboardV2>>,
|
||||
@@ -1354,7 +1374,7 @@ export function useGetDashboardV2<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1933,7 +1953,7 @@ export const getGetSystemDashboardQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!name,
|
||||
enabled: name !== null && name !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSystemDashboard>>,
|
||||
@@ -1970,7 +1990,7 @@ export function useGetSystemDashboard<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2035,7 +2055,7 @@ export const getGetPublicDashboardDataV2QueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getPublicDashboardDataV2>>,
|
||||
@@ -2073,7 +2093,7 @@ export function useGetPublicDashboardDataV2<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2147,7 +2167,7 @@ export const getGetPublicDashboardPanelQueryRangeV2QueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!(id && key),
|
||||
enabled: id !== null && id !== undefined && key !== null && key !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getPublicDashboardPanelQueryRangeV2>>,
|
||||
@@ -2190,7 +2210,7 @@ export function useGetPublicDashboardPanelQueryRangeV2<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2295,7 +2315,7 @@ export function useListDashboardsForUserV2<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,6 +32,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint lists all planned maintenance / downtime schedules
|
||||
* @summary List downtime schedules
|
||||
@@ -111,7 +131,7 @@ export function useListDowntimeSchedules<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,7 +360,7 @@ export const getGetDowntimeScheduleByIDQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getDowntimeScheduleByID>>,
|
||||
@@ -378,7 +398,7 @@ export function useGetDowntimeScheduleByID<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,26 @@ import type { GetFeatures200, RenderErrorResponseDTO } from '../sigNoz.schemas';
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns the supported features and their details
|
||||
* @summary Get features
|
||||
@@ -85,7 +105,7 @@ export function useGetFeatures<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns field keys
|
||||
* @summary Get field keys
|
||||
@@ -101,7 +121,7 @@ export function useGetFieldsKeys<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,7 +216,7 @@ export function useGetFieldsValues<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,6 +48,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns the ingestion keys for a workspace
|
||||
* @summary Get ingestion keys for workspace
|
||||
@@ -129,7 +149,7 @@ export function useGetIngestionKeys<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,7 +376,7 @@ export const getGetIngestionKeyQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!keyId,
|
||||
enabled: keyId !== null && keyId !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getIngestionKey>>,
|
||||
@@ -393,7 +413,7 @@ export function useGetIngestionKey<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -557,7 +577,7 @@ export const getGetIngestionKeyLimitsQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!keyId,
|
||||
enabled: keyId !== null && keyId !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getIngestionKeyLimits>>,
|
||||
@@ -594,7 +614,7 @@ export function useGetIngestionKeyLimits<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -981,7 +1001,7 @@ export function useSearchIngestionKeys<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1210,7 +1230,7 @@ export const getGetIngestionLimitQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!limitId,
|
||||
enabled: limitId !== null && limitId !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getIngestionLimit>>,
|
||||
@@ -1247,7 +1267,7 @@ export function useGetIngestionLimit<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns global config
|
||||
* @summary Get global config
|
||||
@@ -88,7 +108,7 @@ export function useGetGlobalConfig<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Health check
|
||||
*/
|
||||
@@ -83,7 +103,7 @@ export function useHealthz<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,7 +176,7 @@ export function useLivez<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +246,7 @@ export function useReadyz<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,6 +48,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether the metrics and attributes required to power the infra-monitoring section selected by the 'type' query parameter (hosts, processes, pods, nodes, deployments, daemonsets, statefulsets, jobs, namespaces, clusters, volumes) are being received. For each collector receiver or processor that contributes required metrics or attributes, lists what is present and what is missing, with a prebuilt user-facing message and a docs link per missing component. Default-enabled metrics are those expected as soon as the receiver is configured; optional metrics require 'enabled: true' in receiver config. 'ready' is true only when every missing list is empty.
|
||||
* @summary Run Infra Monitoring Setup Checks
|
||||
@@ -116,7 +136,7 @@ export function useGetChecks<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,6 +32,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint validates the license key with the upstream server and activates the license for the organization.
|
||||
* @deprecated
|
||||
@@ -261,7 +281,7 @@ export function useListLicenses<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -484,7 +504,7 @@ export const getGetLicenseQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<Awaited<ReturnType<typeof getLicense>>, TError, TData> & {
|
||||
queryKey: QueryKey;
|
||||
@@ -519,7 +539,7 @@ export function useGetLicense<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -683,7 +703,7 @@ export function useGetActiveLicense<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,6 +31,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns all LLM pricing rules for the authenticated org, with pagination.
|
||||
* @summary List pricing rules
|
||||
@@ -110,7 +130,7 @@ export function useListLLMPricingRules<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,7 +359,7 @@ export const getGetLLMPricingRuleQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getLLMPricingRule>>,
|
||||
@@ -376,7 +396,7 @@ export function useGetLLMPricingRule<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -461,7 +481,7 @@ export function useListUnmappedLLMModels<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoints allows complex query exporting raw data for traces and logs
|
||||
* @summary Export raw data
|
||||
@@ -197,7 +217,7 @@ export function useListPromotedAndIndexedPaths<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,6 +60,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns active metric volume-control (label reduction) rules.
|
||||
* @summary List metric reduction rules
|
||||
@@ -143,7 +163,7 @@ export function useListMetricReductionRules<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,7 +392,7 @@ export const getGetMetricReductionRuleByIDQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getMetricReductionRuleByID>>,
|
||||
@@ -413,7 +433,7 @@ export function useGetMetricReductionRuleByID<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -684,7 +704,7 @@ export function useGetMetricReductionRuleStats<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -770,7 +790,7 @@ export function useGetMetricReductionRuleTimeseries<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -864,7 +884,7 @@ export function useListMetrics<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -959,7 +979,7 @@ export function useGetMetricAlerts<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1057,7 +1077,7 @@ export function useGetMetricAttributes<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1155,7 +1175,7 @@ export function useGetMetricDashboards<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1253,7 +1273,7 @@ export function useGetMetricHighlights<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1434,7 +1454,7 @@ export function useGetMetricMetadata<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1605,7 +1625,7 @@ export function useGetMetricsOnboardingStatus<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1868,7 +1888,7 @@ export function useGetMetricDashboardsV2<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns the organization I belong to
|
||||
* @summary Get my organization
|
||||
@@ -92,7 +112,7 @@ export function useGetMyOrganization<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint lists all org preferences
|
||||
* @summary List org preferences
|
||||
@@ -99,7 +119,7 @@ export function useListOrgPreferences<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,7 +183,7 @@ export const getGetOrgPreferenceQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!name,
|
||||
enabled: name !== null && name !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getOrgPreference>>,
|
||||
@@ -200,7 +220,7 @@ export function useGetOrgPreference<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,7 +405,7 @@ export function useListUserPreferences<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -449,7 +469,7 @@ export const getGetUserPreferenceQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!name,
|
||||
enabled: name !== null && name !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getUserPreference>>,
|
||||
@@ -486,7 +506,7 @@ export function useGetUserPreference<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prometheus-compatible endpoint: the request and response contract is the upstream Prometheus HTTP API (https://prometheus.io/docs/prometheus/latest/querying/api/). Parameters are accepted as URL query parameters or a form-encoded body, on GET and POST alike.
|
||||
* @summary Prometheus instant query
|
||||
@@ -108,7 +128,7 @@ export function usePrometheusQuery<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,7 +313,7 @@ export function usePrometheusQueryRange<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the org's quick filters for every source, each filter as a telemetry field key.
|
||||
* @summary List quick filters
|
||||
@@ -95,7 +115,7 @@ export function useListQuickFilters<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,7 +179,7 @@ export const getGetQuickFiltersQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!source,
|
||||
enabled: source !== null && source !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getQuickFilters>>,
|
||||
@@ -196,7 +216,7 @@ export function useGetQuickFilters<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,6 +32,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint lists all roles
|
||||
* @summary List roles
|
||||
@@ -90,7 +110,7 @@ export function useListRoles<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,7 +329,7 @@ export const getGetRoleQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<Awaited<ReturnType<typeof getRole>>, TError, TData> & {
|
||||
queryKey: QueryKey;
|
||||
@@ -340,7 +360,7 @@ export function useGetRole<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,6 +32,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint lists all route policies for the organization
|
||||
* @summary List route policies
|
||||
@@ -98,7 +118,7 @@ export function useGetAllRoutePolicies<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -325,7 +345,7 @@ export const getGetRoutePolicyByIDQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRoutePolicyByID>>,
|
||||
@@ -362,7 +382,7 @@ export function useGetRoutePolicyByID<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,6 +52,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint lists all alert rules with their current evaluation state
|
||||
* @summary List alert rules
|
||||
@@ -110,7 +130,7 @@ export function useListRules<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,7 +353,7 @@ export const getGetRuleByIDQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleByID>>,
|
||||
@@ -370,7 +390,7 @@ export function useGetRuleByID<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -640,7 +660,7 @@ export const getGetRuleHistoryFilterKeysQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
|
||||
@@ -683,7 +703,7 @@ export function useGetRuleHistoryFilterKeys<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -757,7 +777,7 @@ export const getGetRuleHistoryFilterValuesQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
|
||||
@@ -800,7 +820,7 @@ export function useGetRuleHistoryFilterValues<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -874,7 +894,7 @@ export const getGetRuleHistoryOverallStatusQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
|
||||
@@ -917,7 +937,7 @@ export function useGetRuleHistoryOverallStatus<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -990,7 +1010,7 @@ export const getGetRuleHistoryStatsQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryStats>>,
|
||||
@@ -1032,7 +1052,7 @@ export function useGetRuleHistoryStats<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1105,7 +1125,7 @@ export const getGetRuleHistoryTimelineQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
|
||||
@@ -1148,7 +1168,7 @@ export function useGetRuleHistoryTimeline<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1222,7 +1242,7 @@ export const getGetRuleHistoryTopContributorsQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
|
||||
@@ -1265,7 +1285,7 @@ export function useGetRuleHistoryTopContributors<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns saved views, optionally filtered by source and name.
|
||||
* @summary List saved views
|
||||
@@ -109,7 +129,7 @@ export function useListSavedViews<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,7 +353,7 @@ export const getGetSavedViewQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSavedView>>,
|
||||
@@ -370,7 +390,7 @@ export function useGetSavedView<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,6 +47,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint assigns a role to a service account
|
||||
* @summary Create service account role
|
||||
@@ -257,7 +277,7 @@ export const getGetServiceAccountRoleQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getServiceAccountRole>>,
|
||||
@@ -294,7 +314,7 @@ export function useGetServiceAccountRole<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -379,7 +399,7 @@ export function useListServiceAccounts<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -607,7 +627,7 @@ export const getGetServiceAccountQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getServiceAccount>>,
|
||||
@@ -644,7 +664,7 @@ export function useGetServiceAccount<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -809,7 +829,7 @@ export const getListServiceAccountKeysQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listServiceAccountKeys>>,
|
||||
@@ -847,7 +867,7 @@ export function useListServiceAccountKeys<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1192,7 +1212,7 @@ export const getGetServiceAccountRolesQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getServiceAccountRoles>>,
|
||||
@@ -1230,7 +1250,7 @@ export function useGetServiceAccountRoles<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1315,7 +1335,7 @@ export function useGetMyServiceAccount<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,6 +34,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint creates a session for a user using google callback
|
||||
* @summary Create session by google callback
|
||||
@@ -103,7 +123,7 @@ export function useCreateSessionByGoogleCallback<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,7 +210,7 @@ export function useCreateSessionByOIDCCallback<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -464,7 +484,7 @@ export function useGetSessionContext<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,6 +43,7 @@ export enum AlertmanagertypesChannelConfigVariantGithubComSigNozSignozPkgTypesAl
|
||||
export interface AlertmanagertypesChannelSlackConfigDTO {
|
||||
/**
|
||||
* @type string
|
||||
* @format password
|
||||
*/
|
||||
apiUrl: string;
|
||||
/**
|
||||
@@ -113,10 +114,12 @@ export enum AlertmanagertypesChannelConfigVariantGithubComSigNozSignozPkgTypesAl
|
||||
export interface AlertmanagertypesChannelWebhookConfigDTO {
|
||||
/**
|
||||
* @type string
|
||||
* @format password
|
||||
*/
|
||||
bearerToken?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @format password
|
||||
*/
|
||||
password?: string;
|
||||
/**
|
||||
@@ -125,6 +128,7 @@ export interface AlertmanagertypesChannelWebhookConfigDTO {
|
||||
sendResolved?: boolean | null;
|
||||
/**
|
||||
* @type string
|
||||
* @format password
|
||||
*/
|
||||
url: string;
|
||||
/**
|
||||
@@ -180,6 +184,7 @@ export interface AlertmanagertypesChannelPagerdutyConfigDTO {
|
||||
group?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @format password
|
||||
*/
|
||||
routingKey: string;
|
||||
/**
|
||||
@@ -219,6 +224,7 @@ export type AlertmanagertypesChannelOpsgenieConfigDTODetails = {
|
||||
export interface AlertmanagertypesChannelOpsgenieConfigDTO {
|
||||
/**
|
||||
* @type string
|
||||
* @format password
|
||||
*/
|
||||
apiKey: string;
|
||||
/**
|
||||
@@ -278,6 +284,7 @@ export interface AlertmanagertypesChannelMSTeamsConfigDTO {
|
||||
title?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @format password
|
||||
*/
|
||||
webhookUrl: string;
|
||||
}
|
||||
@@ -309,6 +316,7 @@ export interface AlertmanagertypesChannelGoogleChatConfigDTO {
|
||||
title?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @format password
|
||||
*/
|
||||
webhookUrl: string;
|
||||
}
|
||||
@@ -332,6 +340,7 @@ export type AlertmanagertypesChannelJiraConfigDTOCustomFields = {
|
||||
export interface AlertmanagertypesChannelJiraConfigDTO {
|
||||
/**
|
||||
* @type string
|
||||
* @format password
|
||||
*/
|
||||
apiToken: string;
|
||||
/**
|
||||
@@ -407,6 +416,7 @@ export enum AlertmanagertypesChannelConfigVariantGithubComSigNozSignozPkgTypesAl
|
||||
export interface AlertmanagertypesChannelJSMOpsConfigDTO {
|
||||
/**
|
||||
* @type string
|
||||
* @format password
|
||||
*/
|
||||
apiKey: string;
|
||||
/**
|
||||
@@ -466,6 +476,7 @@ export interface AlertmanagertypesChannelIncidentIOConfigDTO {
|
||||
title?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @format password
|
||||
*/
|
||||
token: string;
|
||||
/**
|
||||
@@ -3220,33 +3231,30 @@ export interface CloudintegrationtypesAWSServiceConfigDTO {
|
||||
metrics?: CloudintegrationtypesAWSServiceMetricsConfigDTO;
|
||||
}
|
||||
|
||||
export type CloudintegrationtypesAgentReportDTOAnyOfDataAnyOf = {
|
||||
export type CloudintegrationtypesAgentReportDTODataAnyOf = {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesAgentReportDTOAnyOfData =
|
||||
CloudintegrationtypesAgentReportDTOAnyOfDataAnyOf | null;
|
||||
export type CloudintegrationtypesAgentReportDTOData =
|
||||
CloudintegrationtypesAgentReportDTODataAnyOf | null;
|
||||
|
||||
export type CloudintegrationtypesAgentReportDTOAnyOf = {
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesAgentReportDTO = {
|
||||
/**
|
||||
* @type object,null
|
||||
*/
|
||||
data: CloudintegrationtypesAgentReportDTOAnyOfData;
|
||||
data: CloudintegrationtypesAgentReportDTOData;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
timestampMillis: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesAgentReportDTO =
|
||||
CloudintegrationtypesAgentReportDTOAnyOf | null;
|
||||
} | null;
|
||||
|
||||
export interface CloudintegrationtypesAzureAccountConfigDTO {
|
||||
/**
|
||||
@@ -3445,7 +3453,10 @@ export enum CloudintegrationtypesServiceIDDTO {
|
||||
cloudstorage = 'cloudstorage',
|
||||
cloudsql_mysql = 'cloudsql_mysql',
|
||||
}
|
||||
export type CloudintegrationtypesCloudIntegrationServiceDTOAnyOf = {
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesCloudIntegrationServiceDTO = {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -3466,13 +3477,7 @@ export type CloudintegrationtypesCloudIntegrationServiceDTOAnyOf = {
|
||||
* @format date-time
|
||||
*/
|
||||
updatedAt?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesCloudIntegrationServiceDTO =
|
||||
CloudintegrationtypesCloudIntegrationServiceDTOAnyOf | null;
|
||||
} | null;
|
||||
|
||||
export interface CloudintegrationtypesCollectedLogAttributeDTO {
|
||||
/**
|
||||
@@ -3626,19 +3631,16 @@ export interface CloudintegrationtypesOldAWSCollectionStrategyDTO {
|
||||
s3_buckets?: CloudintegrationtypesOldAWSCollectionStrategyDTOS3Buckets;
|
||||
}
|
||||
|
||||
export type CloudintegrationtypesIntegrationConfigDTOAnyOf = {
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesIntegrationConfigDTO = {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
enabled_regions: string[];
|
||||
telemetry: CloudintegrationtypesOldAWSCollectionStrategyDTO;
|
||||
};
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesIntegrationConfigDTO =
|
||||
CloudintegrationtypesIntegrationConfigDTOAnyOf | null;
|
||||
} | null;
|
||||
|
||||
export interface CloudintegrationtypesProviderIntegrationConfigDTO {
|
||||
aws?: CloudintegrationtypesAWSIntegrationConfigDTO;
|
||||
@@ -9459,7 +9461,10 @@ export interface TelemetrystoretypesMergeTreeReadDTO {
|
||||
table: string;
|
||||
}
|
||||
|
||||
export type TelemetrystoretypesGranulesDTOAnyOf = {
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type TelemetrystoretypesGranulesDTO = {
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
@@ -9479,13 +9484,7 @@ export type TelemetrystoretypesGranulesDTOAnyOf = {
|
||||
* @format int64
|
||||
*/
|
||||
skipped: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type TelemetrystoretypesGranulesDTO =
|
||||
TelemetrystoretypesGranulesDTOAnyOf | null;
|
||||
} | null;
|
||||
|
||||
export interface Querybuildertypesv5PreviewStatementDTO {
|
||||
/**
|
||||
@@ -10683,7 +10682,10 @@ export interface SpantypesGettableFlamegraphTraceDTO {
|
||||
startTimestampMillis: number;
|
||||
}
|
||||
|
||||
export type SpantypesSpanMapperGroupConditionDTOAnyOf = {
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type SpantypesSpanMapperGroupConditionDTO = {
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
@@ -10692,13 +10694,7 @@ export type SpantypesSpanMapperGroupConditionDTOAnyOf = {
|
||||
* @type array,null
|
||||
*/
|
||||
resource: string[] | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type SpantypesSpanMapperGroupConditionDTO =
|
||||
SpantypesSpanMapperGroupConditionDTOAnyOf | null;
|
||||
} | null;
|
||||
|
||||
export interface SpantypesSpanMapperGroupDTO {
|
||||
condition: SpantypesSpanMapperGroupConditionDTO | null;
|
||||
|
||||
@@ -41,6 +41,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns all span attribute mapping groups for the authenticated org.
|
||||
* @summary List span attribute mapping groups
|
||||
@@ -120,7 +140,7 @@ export function useListSpanMapperGroups<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -449,7 +469,7 @@ export const getListSpanMappersQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!groupId,
|
||||
enabled: groupId !== null && groupId !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listSpanMappers>>,
|
||||
@@ -486,7 +506,7 @@ export function useListSpanMappers<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,26 @@ import type { GetStats200, RenderErrorResponseDTO } from '../sigNoz.schemas';
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns the collected stats for the organization
|
||||
* @summary Get stats
|
||||
@@ -77,7 +97,7 @@ export function useGetStats<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint gets the organization's subscription along with its usage and billing details.
|
||||
* @summary Get the subscription.
|
||||
@@ -94,7 +114,7 @@ export function useGetSubscription<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,6 +50,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint is deprecated and always fails. Use GET /api/v2/users/me instead.
|
||||
* @deprecated
|
||||
@@ -118,7 +138,7 @@ export function useGetMyUserDeprecated<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -432,7 +452,7 @@ export const getGetUsersByRoleIDQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getUsersByRoleID>>,
|
||||
@@ -469,7 +489,7 @@ export function useGetUsersByRoleID<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -693,7 +713,7 @@ export const getGetUserRoleQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getUserRole>>,
|
||||
@@ -730,7 +750,7 @@ export function useGetUserRole<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -807,7 +827,7 @@ export function useListUsers<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1026,7 +1046,7 @@ export const getGetUserQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<Awaited<ReturnType<typeof getUser>>, TError, TData> & {
|
||||
queryKey: QueryKey;
|
||||
@@ -1057,7 +1077,7 @@ export function useGetUser<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1221,7 +1241,7 @@ export const getGetResetPasswordTokenQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordToken>>,
|
||||
@@ -1258,7 +1278,7 @@ export function useGetResetPasswordToken<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1402,7 +1422,7 @@ export const getGetRolesByUserIDQueryOptions = <
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
enabled: id !== null && id !== undefined,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRolesByUserID>>,
|
||||
@@ -1439,7 +1459,7 @@ export function useGetRolesByUserID<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1516,7 +1536,7 @@ export function useGetMyUser<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,26 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
const withQueryKey = <T extends object, K>(
|
||||
query: T,
|
||||
queryKey: K,
|
||||
): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint gets the host info from zeus.
|
||||
* @summary Get host info from Zeus.
|
||||
@@ -85,7 +105,7 @@ export function useGetHosts<
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,6 @@ export const Logout = async (): Promise<void> => {
|
||||
deleteLocalStorageKey(LOCALSTORAGE.LOGGED_IN_USER_NAME);
|
||||
deleteLocalStorageKey(LOCALSTORAGE.CHAT_SUPPORT);
|
||||
deleteLocalStorageKey(LOCALSTORAGE.USER_ID);
|
||||
deleteLocalStorageKey(LOCALSTORAGE.QUICK_FILTERS_SETTINGS_ANNOUNCEMENT);
|
||||
window.dispatchEvent(new CustomEvent('LOGOUT'));
|
||||
history.push(ROUTES.LOGIN);
|
||||
};
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
--button-variant-link-color: var(--l1-foreground);
|
||||
|
||||
.divider-filter {
|
||||
width: 1px;
|
||||
|
||||
@@ -15,28 +15,30 @@ import {
|
||||
ComboboxTrigger,
|
||||
} from '@signozhq/ui/combobox';
|
||||
import { Skeleton, Tooltip } from 'antd';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Switch } from '@signozhq/ui/switch';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||
import setLocalStorageKey from 'api/browser/localstorage/set';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import classNames from 'classnames';
|
||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { useApiMonitoringParams } from 'container/ApiMonitoring/queryParams';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { isFunction, isNull } from 'lodash-es';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { AuthZGuardContent } from 'lib/authz/components/AuthZGuard/AuthZGuardContent';
|
||||
import AuthZButton from 'lib/authz/components/AuthZButton/AuthZButton';
|
||||
import { useAuthZ } from 'lib/authz/hooks/useAuthZ/useAuthZ';
|
||||
import {
|
||||
QuickFilterManagePermissions,
|
||||
QuickFilterReadPermission,
|
||||
} from 'lib/authz/hooks/useAuthZ/permissions/quick-filter.permissions';
|
||||
import { isFunction } from 'lodash-es';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
|
||||
import Checkbox from './FilterRenderers/Checkbox/Checkbox';
|
||||
import CheckboxV2 from './FilterRenderers/Checkbox/v2/CheckboxFilterV2';
|
||||
import Duration from './FilterRenderers/Duration/Duration';
|
||||
import Slider from './FilterRenderers/Slider/Slider';
|
||||
import useFilterConfig from './hooks/useFilterConfig';
|
||||
import AnnouncementTooltip from './QuickFiltersSettings/AnnouncementTooltip';
|
||||
import QuickFiltersSettings from './QuickFiltersSettings/QuickFiltersSettings';
|
||||
import { FiltersType, IQuickFiltersProps, QuickFiltersSource } from './types';
|
||||
|
||||
@@ -55,9 +57,7 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
showQueryName = true,
|
||||
useFieldApis,
|
||||
} = props;
|
||||
const { user } = useAppContext();
|
||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
||||
const isAdmin = user.role === USER_ROLES.ADMIN;
|
||||
const [params, setParams] = useApiMonitoringParams();
|
||||
const showIP = params.showIP ?? true;
|
||||
|
||||
@@ -68,6 +68,12 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
refetchCustomFilters,
|
||||
isCustomFiltersLoading,
|
||||
} = useFilterConfig({ signal, config });
|
||||
const {
|
||||
deniedPermissions: deniedSettingsPermissions,
|
||||
isLoading: isSettingsChecking,
|
||||
} = useAuthZ(QuickFilterManagePermissions, { enabled: isDynamicFilters });
|
||||
const isSettingsDisabled =
|
||||
isSettingsChecking || deniedSettingsPermissions.length > 0;
|
||||
|
||||
const {
|
||||
currentQuery,
|
||||
@@ -107,16 +113,6 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
const shouldShowDropdownInListView =
|
||||
isListView && source === QuickFiltersSource.TRACES_EXPLORER;
|
||||
|
||||
const showAnnouncementTooltip = useMemo(() => {
|
||||
const localStorageValue = getLocalStorageKey(
|
||||
LOCALSTORAGE.QUICK_FILTERS_SETTINGS_ANNOUNCEMENT,
|
||||
);
|
||||
if (!isNull(localStorageValue)) {
|
||||
return !(localStorageValue === 'false');
|
||||
}
|
||||
return true;
|
||||
}, []);
|
||||
|
||||
const activeQueryIndex = useMemo(() => {
|
||||
if (isListView) {
|
||||
return source === QuickFiltersSource.TRACES_EXPLORER
|
||||
@@ -232,49 +228,49 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
const renderRightActions = (): JSX.Element => (
|
||||
<section className="right-actions">
|
||||
<Tooltip title="Reset All">
|
||||
<div className="right-action-icon-container">
|
||||
<RefreshCw className="sync-icon" size="md" onClick={handleReset} />
|
||||
</div>
|
||||
<Button
|
||||
variant="link"
|
||||
color="secondary"
|
||||
aria-label="Reset All"
|
||||
className="right-action-icon-container"
|
||||
onClick={handleReset}
|
||||
prefix={<RefreshCw className="sync-icon" size="md" />}
|
||||
/>
|
||||
</Tooltip>
|
||||
{showFilterCollapse && (
|
||||
<Tooltip title="Collapse Filters">
|
||||
<div className="right-action-icon-container">
|
||||
<ArrowUpToLine
|
||||
style={{ rotate: '270deg', cursor: 'pointer' }}
|
||||
size="md"
|
||||
onClick={handleFilterVisibilityChange}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant="link"
|
||||
color="secondary"
|
||||
aria-label="Collapse Filters"
|
||||
className="right-action-icon-container"
|
||||
onClick={handleFilterVisibilityChange}
|
||||
prefix={<ArrowUpToLine style={{ rotate: '270deg' }} size="md" />}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isDynamicFilters && isAdmin && (
|
||||
<Tooltip title="Settings">
|
||||
<div
|
||||
className={classNames('right-action-icon-container', {
|
||||
active: isSettingsOpen,
|
||||
})}
|
||||
>
|
||||
<SettingsIcon
|
||||
className="settings-icon"
|
||||
data-testid="settings-icon"
|
||||
width={14}
|
||||
height={14}
|
||||
onClick={(): void => setIsSettingsOpen(true)}
|
||||
/>
|
||||
<AnnouncementTooltip
|
||||
show={showAnnouncementTooltip}
|
||||
position={{ top: -5, left: 15 }}
|
||||
title="Edit your quick filters"
|
||||
message="You can now customize and re-arrange your quick filters panel. Select the quick filters you’d need and hide away the rest for faster exploration."
|
||||
onClose={(): void => {
|
||||
setLocalStorageKey(
|
||||
LOCALSTORAGE.QUICK_FILTERS_SETTINGS_ANNOUNCEMENT,
|
||||
'false',
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
{isDynamicFilters && (
|
||||
<AuthZButton
|
||||
checks={QuickFilterManagePermissions}
|
||||
variant="link"
|
||||
color="secondary"
|
||||
aria-label="Settings"
|
||||
className={classNames('right-action-icon-container', {
|
||||
active: isSettingsOpen,
|
||||
})}
|
||||
onClick={(): void => setIsSettingsOpen(true)}
|
||||
testId="settings-icon-container"
|
||||
prefix={
|
||||
<Tooltip title="Settings" open={isSettingsDisabled ? false : undefined}>
|
||||
<SettingsIcon
|
||||
className="settings-icon"
|
||||
data-testid="settings-icon"
|
||||
width={14}
|
||||
height={14}
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
@@ -361,6 +357,21 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
</>
|
||||
);
|
||||
|
||||
const filtersSkeleton = (
|
||||
<div className="quick-filters-skeleton">
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<Skeleton.Input active size="small" key={index} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
const filtersContent = isCustomFiltersLoading ? (
|
||||
filtersSkeleton
|
||||
) : (
|
||||
<OverlayScrollbar>{renderContent()}</OverlayScrollbar>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="quick-filters-container">
|
||||
<div className="quick-filters">
|
||||
@@ -371,15 +382,15 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
</section>
|
||||
)}
|
||||
|
||||
{isCustomFiltersLoading ? (
|
||||
<div className="quick-filters-skeleton">
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<Skeleton.Input active size="small" key={index} />
|
||||
))}
|
||||
</div>
|
||||
{signal ? (
|
||||
<AuthZGuardContent
|
||||
checks={[QuickFilterReadPermission]}
|
||||
fallbackOnLoading={filtersSkeleton}
|
||||
>
|
||||
{filtersContent}
|
||||
</AuthZGuardContent>
|
||||
) : (
|
||||
<OverlayScrollbar>{renderContent()}</OverlayScrollbar>
|
||||
filtersContent
|
||||
)}
|
||||
</div>
|
||||
<div className="quick-filters-settings-container">
|
||||
|
||||
@@ -35,10 +35,7 @@ const useFilterConfig = ({
|
||||
[data],
|
||||
);
|
||||
|
||||
const isDynamicFilters = useMemo(
|
||||
() => customFilters.length > 0,
|
||||
[customFilters],
|
||||
);
|
||||
const isDynamicFilters = !!signal;
|
||||
|
||||
const filterConfig = useMemo(
|
||||
() => getFilterConfig(signal, customFilters, config),
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
import { ENVIRONMENT } from 'constants/env';
|
||||
import {
|
||||
ApiMonitoringParams,
|
||||
useApiMonitoringParams,
|
||||
} from 'container/ApiMonitoring/queryParams';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import {
|
||||
QuickFilterReadPermission,
|
||||
QuickFilterUpdatePermission,
|
||||
} from 'lib/authz/hooks/useAuthZ/permissions/quick-filter.permissions';
|
||||
import {
|
||||
AUTHZ_CHECK_URL,
|
||||
setupAuthzAdmin,
|
||||
setupAuthzDeny,
|
||||
setupAuthzDenyAll,
|
||||
} from 'lib/authz/utils/authz-test-utils';
|
||||
import {
|
||||
otherFiltersResponse,
|
||||
quickFiltersAttributeValuesResponse,
|
||||
quickFiltersListResponse,
|
||||
} from 'mocks-server/__mockdata__/customQuickFilters';
|
||||
import { server } from 'mocks-server/server';
|
||||
import { rest } from 'msw';
|
||||
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
||||
|
||||
import QuickFilters from '../QuickFilters';
|
||||
import { QuickFiltersSource, SignalType } from '../types';
|
||||
import { QuickFiltersConfig } from './constants';
|
||||
|
||||
jest.mock('hooks/queryBuilder/useQueryBuilder', () => ({
|
||||
useQueryBuilder: jest.fn(),
|
||||
}));
|
||||
jest.mock('container/ApiMonitoring/queryParams');
|
||||
|
||||
const mockUseApiMonitoringParams = jest.mocked(useApiMonitoringParams);
|
||||
|
||||
const BASE_URL = ENVIRONMENT.baseURL;
|
||||
const SIGNAL = SignalType.LOGS;
|
||||
const quickFiltersListURL = `${BASE_URL}/api/v2/quick_filters/${SIGNAL}`;
|
||||
const fieldsKeysURL = `${BASE_URL}/api/v1/fields/keys`;
|
||||
const attributeValuesURL = `${BASE_URL}/api/v3/autocomplete/attribute_values`;
|
||||
const fieldsValuesURL = `${BASE_URL}/api/v1/fields/values`;
|
||||
|
||||
const NOT_AUTHORIZED_TEXT = /is not authorized to perform/i;
|
||||
const FILTER_SERVICE_NAME = 'Service Name';
|
||||
const SETTINGS_CONTAINER_TEST_ID = 'settings-icon-container';
|
||||
|
||||
beforeEach(() => {
|
||||
(useQueryBuilder as jest.Mock).mockReturnValue({
|
||||
currentQuery: {
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
queryName: 'Test Query',
|
||||
filters: { items: [] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
lastUsedQuery: 0,
|
||||
redirectWithQueryBuilderData: jest.fn(),
|
||||
});
|
||||
mockUseApiMonitoringParams.mockReturnValue([
|
||||
{ showIP: true } as ApiMonitoringParams,
|
||||
jest.fn(),
|
||||
]);
|
||||
server.use(
|
||||
rest.get(quickFiltersListURL, (_req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(quickFiltersListResponse)),
|
||||
),
|
||||
rest.get(fieldsKeysURL, (_req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(otherFiltersResponse)),
|
||||
),
|
||||
rest.get(attributeValuesURL, (_req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(quickFiltersAttributeValuesResponse)),
|
||||
),
|
||||
rest.get(fieldsValuesURL, (_req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(quickFiltersAttributeValuesResponse)),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
server.resetHandlers();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
function renderWithSignal(): void {
|
||||
render(
|
||||
<QuickFilters
|
||||
source={QuickFiltersSource.LOGS_EXPLORER}
|
||||
signal={SIGNAL}
|
||||
handleFilterVisibilityChange={jest.fn()}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
function renderStaticConfig(): void {
|
||||
render(
|
||||
<QuickFilters
|
||||
source={QuickFiltersSource.EXCEPTIONS}
|
||||
config={QuickFiltersConfig}
|
||||
handleFilterVisibilityChange={jest.fn()}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
describe('QuickFilters - AuthZ', () => {
|
||||
describe('read denied', () => {
|
||||
it('shows the inline denial instead of the filters, header stays', async () => {
|
||||
server.use(setupAuthzDeny(QuickFilterReadPermission));
|
||||
|
||||
renderWithSignal();
|
||||
|
||||
await expect(
|
||||
screen.findByText(NOT_AUTHORIZED_TEXT),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByText(FILTER_SERVICE_NAME)).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Filters for')).toBeInTheDocument();
|
||||
|
||||
const settingsTrigger = await screen.findByTestId(
|
||||
SETTINGS_CONTAINER_TEST_ID,
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(settingsTrigger).toHaveAttribute(
|
||||
'data-denied-permissions',
|
||||
expect.stringContaining('read'),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('update denied', () => {
|
||||
it('renders the filters but disables the settings trigger', async () => {
|
||||
server.use(setupAuthzDeny(QuickFilterUpdatePermission));
|
||||
|
||||
renderWithSignal();
|
||||
|
||||
await expect(
|
||||
screen.findByText(FILTER_SERVICE_NAME),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
const settingsTrigger = await screen.findByTestId(
|
||||
SETTINGS_CONTAINER_TEST_ID,
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(settingsTrigger).toHaveAttribute(
|
||||
'data-denied-permissions',
|
||||
expect.stringContaining('update'),
|
||||
),
|
||||
);
|
||||
|
||||
await userEvent.click(settingsTrigger);
|
||||
expect(screen.queryByText(/ADDED FILTERS/i)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('all permissions granted', () => {
|
||||
it('renders the filters and opens settings from the trigger', async () => {
|
||||
server.use(setupAuthzAdmin());
|
||||
|
||||
renderWithSignal();
|
||||
|
||||
await expect(
|
||||
screen.findByText(FILTER_SERVICE_NAME),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
const settingsTrigger = await screen.findByTestId(
|
||||
SETTINGS_CONTAINER_TEST_ID,
|
||||
);
|
||||
expect(settingsTrigger).not.toHaveAttribute('data-denied-permissions');
|
||||
|
||||
await userEvent.click(settingsTrigger);
|
||||
await expect(
|
||||
screen.findByText(/ADDED FILTERS/i),
|
||||
).resolves.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('static config pages (no signal)', () => {
|
||||
it('is not gated even when every permission is denied', async () => {
|
||||
server.use(setupAuthzDenyAll());
|
||||
|
||||
renderStaticConfig();
|
||||
|
||||
await expect(
|
||||
screen.findByText(FILTER_SERVICE_NAME),
|
||||
).resolves.toBeInTheDocument();
|
||||
expect(screen.queryByText(NOT_AUTHORIZED_TEXT)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('permission check loading', () => {
|
||||
it('shows the skeleton, not the filters or a denial', async () => {
|
||||
server.use(
|
||||
rest.post(AUTHZ_CHECK_URL, (_req, res, ctx) => res(ctx.delay('infinite'))),
|
||||
);
|
||||
|
||||
renderWithSignal();
|
||||
|
||||
await waitFor(() =>
|
||||
// eslint-disable-next-line testing-library/no-node-access
|
||||
expect(
|
||||
document.querySelector('.quick-filters-skeleton'),
|
||||
).toBeInTheDocument(),
|
||||
);
|
||||
expect(screen.queryByText(NOT_AUTHORIZED_TEXT)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(FILTER_SERVICE_NAME)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -54,6 +54,7 @@ export interface IQuickFiltersProps {
|
||||
source: QuickFiltersSource;
|
||||
onFilterChange?: (query: Query) => void;
|
||||
onQuickFilterChange?: (data: QuickFilterChangeEventData) => void;
|
||||
/** Pass to fetch quick filters for this signal; omit to use `config` as-is */
|
||||
signal?: SignalType;
|
||||
className?: string;
|
||||
showFilterCollapse?: boolean;
|
||||
|
||||
@@ -31,7 +31,6 @@ export enum LOCALSTORAGE {
|
||||
DONT_SHOW_SLOW_API_WARNING = 'DONT_SHOW_SLOW_API_WARNING',
|
||||
METRICS_LIST_OPTIONS = 'METRICS_LIST_OPTIONS',
|
||||
SHOW_EXCEPTIONS_QUICK_FILTERS = 'SHOW_EXCEPTIONS_QUICK_FILTERS',
|
||||
QUICK_FILTERS_SETTINGS_ANNOUNCEMENT = 'QUICK_FILTERS_SETTINGS_ANNOUNCEMENT',
|
||||
FUNNEL_STEPS = 'FUNNEL_STEPS',
|
||||
SPAN_DETAILS_PINNED_ATTRIBUTES = 'SPAN_DETAILS_PINNED_ATTRIBUTES',
|
||||
LAST_USED_CUSTOM_TIME_RANGES = 'LAST_USED_CUSTOM_TIME_RANGES',
|
||||
|
||||
@@ -131,7 +131,7 @@ export function AboutSigNozQuestions({
|
||||
|
||||
<AntdInput.TextArea
|
||||
className="discover-signoz-input"
|
||||
placeholder={`e.g., googling "datadog alternative", a post on r/devops, from a friend/colleague, a LinkedIn post, ChatGPT, etc.`}
|
||||
placeholder="e.g., I asked ChatGPT for Datadog alternatives, searched Google for “OpenTelemetry tools,” saw a Reddit or LinkedIn post, or heard about it from a colleague."
|
||||
value={discoverSignoz}
|
||||
autoFocus
|
||||
rows={4}
|
||||
|
||||
@@ -204,10 +204,12 @@ describe('OnboardingQuestionaire Component', () => {
|
||||
await user.click(screen.getByRole('button', { name: /next/i }));
|
||||
|
||||
await expect(
|
||||
screen.findByPlaceholderText(/e\.g\., googling/i, {}),
|
||||
screen.findByPlaceholderText(/e\.g\., I asked ChatGPT/i, {}),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
const discoverInput = screen.getByPlaceholderText(/e\.g\., googling/i);
|
||||
const discoverInput = screen.getByPlaceholderText(
|
||||
/e\.g\., I asked ChatGPT/i,
|
||||
);
|
||||
await user.type(discoverInput, 'Found via Google search');
|
||||
|
||||
const interestCheckbox = screen.getByLabelText(
|
||||
@@ -256,11 +258,11 @@ describe('OnboardingQuestionaire Component', () => {
|
||||
await user.click(screen.getByRole('button', { name: /next/i }));
|
||||
|
||||
await expect(
|
||||
screen.findByPlaceholderText(/e\.g\., googling/i, {}),
|
||||
screen.findByPlaceholderText(/e\.g\., I asked ChatGPT/i, {}),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
await user.type(
|
||||
screen.getByPlaceholderText(/e\.g\., googling/i),
|
||||
screen.getByPlaceholderText(/e\.g\., I asked ChatGPT/i),
|
||||
'Found via Google',
|
||||
);
|
||||
await user.click(screen.getByLabelText(/lowering observability costs/i));
|
||||
@@ -297,7 +299,7 @@ describe('OnboardingQuestionaire Component', () => {
|
||||
await user.click(screen.getByRole('button', { name: /next/i }));
|
||||
|
||||
await user.type(
|
||||
await screen.findByPlaceholderText(/e\.g\., googling/i),
|
||||
await screen.findByPlaceholderText(/e\.g\., I asked ChatGPT/i),
|
||||
'Found via Google',
|
||||
);
|
||||
await user.click(screen.getByLabelText(/lowering observability costs/i));
|
||||
@@ -329,11 +331,11 @@ describe('OnboardingQuestionaire Component', () => {
|
||||
await user.click(screen.getByRole('button', { name: /next/i }));
|
||||
|
||||
await expect(
|
||||
screen.findByPlaceholderText(/e\.g\., googling/i, {}),
|
||||
screen.findByPlaceholderText(/e\.g\., I asked ChatGPT/i, {}),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
await user.type(
|
||||
screen.getByPlaceholderText(/e\.g\., googling/i),
|
||||
screen.getByPlaceholderText(/e\.g\., I asked ChatGPT/i),
|
||||
'Found via Google',
|
||||
);
|
||||
await user.click(screen.getByLabelText(/lowering observability costs/i));
|
||||
|
||||
@@ -329,7 +329,7 @@ describe('transformTransactionGroupsToResourcePermissions', () => {
|
||||
it('returns all resources from RESOURCE_ORDER even with empty transaction groups', () => {
|
||||
const result = transformTransactionGroupsToResourcePermissions([]);
|
||||
|
||||
expect(result).toHaveLength(10);
|
||||
expect(result).toHaveLength(11);
|
||||
expect(result.map((r) => r.resourceKind)).toStrictEqual([
|
||||
'factor-api-key',
|
||||
'dashboard',
|
||||
@@ -337,6 +337,7 @@ describe('transformTransactionGroupsToResourcePermissions', () => {
|
||||
'logs',
|
||||
'meter-metrics',
|
||||
'metrics',
|
||||
'quick-filter',
|
||||
'role',
|
||||
'serviceaccount',
|
||||
'subscription',
|
||||
@@ -421,7 +422,7 @@ describe('createEmptyRolePermissions', () => {
|
||||
it('creates permissions for all resources in RESOURCE_ORDER', () => {
|
||||
const result = createEmptyRolePermissions();
|
||||
|
||||
expect(result).toHaveLength(10);
|
||||
expect(result).toHaveLength(11);
|
||||
expect(result.map((r) => r.resourceKind)).toStrictEqual([
|
||||
'factor-api-key',
|
||||
'dashboard',
|
||||
@@ -429,6 +430,7 @@ describe('createEmptyRolePermissions', () => {
|
||||
'logs',
|
||||
'meter-metrics',
|
||||
'metrics',
|
||||
'quick-filter',
|
||||
'role',
|
||||
'serviceaccount',
|
||||
'subscription',
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Gauge,
|
||||
Grid3X3,
|
||||
Key,
|
||||
ListFilter,
|
||||
Logs,
|
||||
Receipt,
|
||||
Shield,
|
||||
@@ -86,6 +87,14 @@ export const RESOURCE_PANELS: Record<AuthZResource, ResourcePanelConfig> = {
|
||||
selectorPlaceholder: 'Type * to cover the workspace subscription',
|
||||
docsAnchor: 'subscription',
|
||||
},
|
||||
'quick-filter': {
|
||||
label: 'Quick Filters',
|
||||
description: 'Quick filters shown in the logs, traces, and other explorers.',
|
||||
icon: ListFilter,
|
||||
selectorPlaceholder:
|
||||
'Type quick filter ID, separate multiple with comma or space',
|
||||
docsAnchor: 'quick-filter',
|
||||
},
|
||||
logs: {
|
||||
label: 'Logs',
|
||||
description: 'Log data collected across the workspace.',
|
||||
|
||||
@@ -18,6 +18,11 @@ export default {
|
||||
type: 'metaresource',
|
||||
allowedVerbs: ['create', 'delete', 'list', 'read', 'update'],
|
||||
},
|
||||
{
|
||||
kind: 'quick-filter',
|
||||
type: 'metaresource',
|
||||
allowedVerbs: ['list', 'read', 'update'],
|
||||
},
|
||||
{
|
||||
kind: 'subscription',
|
||||
type: 'metaresource',
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { buildPermission } from '../utils';
|
||||
|
||||
export const QuickFilterReadPermission = buildPermission(
|
||||
'read',
|
||||
'quick-filter:*',
|
||||
);
|
||||
export const QuickFilterUpdatePermission = buildPermission(
|
||||
'update',
|
||||
'quick-filter:*',
|
||||
);
|
||||
|
||||
// Editing quick filters needs read as well as update
|
||||
export const QuickFilterManagePermissions = [
|
||||
QuickFilterReadPermission,
|
||||
QuickFilterUpdatePermission,
|
||||
];
|
||||
@@ -207,7 +207,7 @@ type ChannelSpec interface {
|
||||
|
||||
type ChannelSlackConfig struct {
|
||||
SendResolved *bool `json:"sendResolved,omitempty"`
|
||||
APIURL string `json:"apiUrl" required:"true"`
|
||||
APIURL string `json:"apiUrl" required:"true" format:"password"`
|
||||
Channel string `json:"channel"`
|
||||
Title valuer.UnsetOrNonEmptyString `json:"title"`
|
||||
Text valuer.UnsetOrNonEmptyString `json:"text"`
|
||||
@@ -312,10 +312,10 @@ func newChannelEmailConfigFromReceiver(_ string, receiver *Receiver) (ChannelSpe
|
||||
// was really a bearer token.
|
||||
type ChannelWebhookConfig struct {
|
||||
SendResolved *bool `json:"sendResolved,omitempty"`
|
||||
URL string `json:"url" required:"true"`
|
||||
URL string `json:"url" required:"true" format:"password"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
BearerToken string `json:"bearerToken"`
|
||||
Password string `json:"password" format:"password"`
|
||||
BearerToken string `json:"bearerToken" format:"password"`
|
||||
}
|
||||
|
||||
func (c ChannelWebhookConfig) Validate() error {
|
||||
@@ -403,7 +403,7 @@ func newChannelWebhookConfigFromReceiver(name string, receiver *Receiver) (Chann
|
||||
|
||||
type ChannelPagerdutyConfig struct {
|
||||
SendResolved *bool `json:"sendResolved,omitempty"`
|
||||
RoutingKey string `json:"routingKey" required:"true"`
|
||||
RoutingKey string `json:"routingKey" required:"true" format:"password"`
|
||||
URL string `json:"url"`
|
||||
Source valuer.UnsetOrNonEmptyString `json:"source"`
|
||||
Client valuer.UnsetOrNonEmptyString `json:"client"`
|
||||
@@ -488,7 +488,7 @@ func newChannelPagerdutyConfigFromReceiver(name string, receiver *Receiver) (Cha
|
||||
|
||||
type ChannelOpsgenieConfig struct {
|
||||
SendResolved *bool `json:"sendResolved,omitempty"`
|
||||
APIKey string `json:"apiKey" required:"true"`
|
||||
APIKey string `json:"apiKey" required:"true" format:"password"`
|
||||
APIURL string `json:"apiUrl"`
|
||||
Message valuer.UnsetOrNonEmptyString `json:"message"`
|
||||
Description valuer.UnsetOrNonEmptyString `json:"description"`
|
||||
@@ -552,7 +552,7 @@ func newChannelOpsgenieConfigFromReceiver(name string, receiver *Receiver) (Chan
|
||||
|
||||
type ChannelMSTeamsConfig struct {
|
||||
SendResolved *bool `json:"sendResolved,omitempty"`
|
||||
WebhookURL string `json:"webhookUrl" required:"true"`
|
||||
WebhookURL string `json:"webhookUrl" required:"true" format:"password"`
|
||||
Title valuer.UnsetOrNonEmptyString `json:"title"`
|
||||
Text valuer.UnsetOrNonEmptyString `json:"text"`
|
||||
}
|
||||
@@ -600,7 +600,7 @@ func newChannelMSTeamsConfigFromReceiver(name string, receiver *Receiver) (Chann
|
||||
|
||||
type ChannelGoogleChatConfig struct {
|
||||
SendResolved *bool `json:"sendResolved,omitempty"`
|
||||
WebhookURL string `json:"webhookUrl" required:"true"`
|
||||
WebhookURL string `json:"webhookUrl" required:"true" format:"password"`
|
||||
Title valuer.UnsetOrNonEmptyString `json:"title"`
|
||||
Text valuer.UnsetOrNonEmptyString `json:"text"`
|
||||
}
|
||||
@@ -664,7 +664,7 @@ type ChannelJiraConfig struct {
|
||||
CustomFields map[string]any `json:"customFields,omitempty"`
|
||||
|
||||
Email string `json:"email" required:"true"`
|
||||
APIToken string `json:"apiToken" required:"true"`
|
||||
APIToken string `json:"apiToken" required:"true" format:"password"`
|
||||
}
|
||||
|
||||
func (c ChannelJiraConfig) Validate() error {
|
||||
@@ -785,7 +785,7 @@ func newChannelJiraConfigFromReceiver(name string, receiver *Receiver) (ChannelS
|
||||
// keyed by the integration API key, which the notifier pins itself.
|
||||
type ChannelJSMOpsConfig struct {
|
||||
SendResolved *bool `json:"sendResolved,omitempty"`
|
||||
APIKey string `json:"apiKey" required:"true"`
|
||||
APIKey string `json:"apiKey" required:"true" format:"password"`
|
||||
Message valuer.UnsetOrNonEmptyString `json:"message"`
|
||||
Description valuer.UnsetOrNonEmptyString `json:"description"`
|
||||
Priority string `json:"priority"`
|
||||
@@ -836,7 +836,7 @@ func newChannelJSMOpsConfigFromReceiver(name string, receiver *Receiver) (Channe
|
||||
type ChannelIncidentIOConfig struct {
|
||||
SendResolved *bool `json:"sendResolved,omitempty"`
|
||||
URL string `json:"url" required:"true"`
|
||||
Token string `json:"token" required:"true"`
|
||||
Token string `json:"token" required:"true" format:"password"`
|
||||
Title valuer.UnsetOrNonEmptyString `json:"title"`
|
||||
Description valuer.UnsetOrNonEmptyString `json:"description"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
|
||||
103
tests/fixtures/queriercommon.py
vendored
103
tests/fixtures/queriercommon.py
vendored
@@ -1,4 +1,4 @@
|
||||
"""Seed data for the queriercommon keyless-semantics and explicit-context tests.
|
||||
"""Seed data for the queriercommon keyless-semantics tests.
|
||||
|
||||
Three identities exist in every signal. GOLD and SILVER carry the test keys.
|
||||
NONE carries no key at all. The tests assert which identities a filter
|
||||
@@ -8,7 +8,6 @@ The attribute names are outside every semantic-convention family, so the
|
||||
seeded data pins base behavior with any semconv overlay state.
|
||||
"""
|
||||
|
||||
import json
|
||||
from collections.abc import Callable, Generator
|
||||
from datetime import UTC, datetime, timedelta
|
||||
|
||||
@@ -123,103 +122,3 @@ def keyless_series(insert_metrics: Callable[[list[Metrics]], None]) -> Generator
|
||||
]
|
||||
)
|
||||
yield start, start + points * 60
|
||||
|
||||
|
||||
EXPLICIT_PREFIX = "explicit-ctx"
|
||||
# String attribute that identifies the row. It has one context only. Each
|
||||
# assertion reads it back.
|
||||
IDENTITY_KEY = "probe.id"
|
||||
# Attribute with no column of the same name. It tests a key under the
|
||||
# signal's own context that metadata does not know. On logs, the rows without
|
||||
# the attribute have the value nested in the body JSON.
|
||||
ATTRIBUTE_ONLY_KEY = "route.tag"
|
||||
CONTESTED_VALUE = "checkout"
|
||||
|
||||
# Row identities. Each row shows where the contested value is:
|
||||
# - COLUMN_ONLY: in the column (`name` on spans, `severity_text` on logs).
|
||||
# - ATTRIBUTE_ONLY: in the string attribute with the same name.
|
||||
# - BOTH: in the column and in the string attribute.
|
||||
# - NEITHER: in none of them.
|
||||
# - NUMBER_ATTRIBUTE: in a number attribute with the same name. Its data
|
||||
# type is different from the column.
|
||||
COLUMN_ONLY = f"{EXPLICIT_PREFIX}-column"
|
||||
ATTRIBUTE_ONLY = f"{EXPLICIT_PREFIX}-attribute"
|
||||
BOTH = f"{EXPLICIT_PREFIX}-both"
|
||||
NEITHER = f"{EXPLICIT_PREFIX}-neither"
|
||||
NUMBER_ATTRIBUTE = f"{EXPLICIT_PREFIX}-number"
|
||||
NUMBER_VALUE = 42
|
||||
|
||||
# (identity, value in the column, value in the string attribute, value in
|
||||
# the number attribute, resource service.name, attribute service.name,
|
||||
# has route.tag, insert offset in seconds)
|
||||
ROWS = [
|
||||
(COLUMN_ONLY, True, False, False, "svc-a", None, True, 1),
|
||||
(ATTRIBUTE_ONLY, False, True, False, "svc-b", "svc-a", False, 2),
|
||||
(BOTH, True, True, False, "svc-a", "svc-a", True, 3),
|
||||
(NEITHER, False, False, False, "svc-b", "svc-b", False, 4),
|
||||
(NUMBER_ATTRIBUTE, False, False, True, "svc-b", None, False, 5),
|
||||
]
|
||||
|
||||
# Logs only. The scope name is a declared path. A scope attribute also has
|
||||
# the name `name`. A second scope attribute has a plain name.
|
||||
SCOPE_NAME = "scope-a"
|
||||
SCOPE_ATTRIBUTE_KEY = "env"
|
||||
SCOPE_ATTRIBUTE_VALUE = "prod"
|
||||
|
||||
|
||||
@pytest.fixture(name="ambiguous_rows", scope="function")
|
||||
def ambiguous_rows(
|
||||
insert_logs: Callable[[list[Logs]], None],
|
||||
insert_traces: Callable[[list[Traces]], None],
|
||||
) -> Generator[datetime]:
|
||||
"""Inserts one span and one log for each identity. Every row has a
|
||||
resource `service.name`. Some rows also have a span or log attribute
|
||||
`service.name` with a different value. On logs, the rows without the
|
||||
`route.tag` attribute have the value in the body JSON. Logs with the
|
||||
column value have the scope name. Logs with the attribute value have the
|
||||
scope attributes. Yields the base timestamp."""
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0) - timedelta(minutes=1)
|
||||
|
||||
insert_traces(
|
||||
[
|
||||
Traces(
|
||||
timestamp=now - timedelta(seconds=offset),
|
||||
duration=timedelta(milliseconds=10),
|
||||
trace_id=TraceIdGenerator.trace_id(),
|
||||
span_id=TraceIdGenerator.span_id(),
|
||||
name=CONTESTED_VALUE if column else "other",
|
||||
kind=TracesKind.SPAN_KIND_SERVER,
|
||||
status_code=TracesStatusCode.STATUS_CODE_OK,
|
||||
resources={"service.name": resource_service},
|
||||
attributes={
|
||||
IDENTITY_KEY: identity,
|
||||
**({"name": CONTESTED_VALUE} if attribute else {}),
|
||||
**({"name": NUMBER_VALUE} if number else {}),
|
||||
**({"service.name": attribute_service} if attribute_service else {}),
|
||||
**({ATTRIBUTE_ONLY_KEY: CONTESTED_VALUE} if tagged else {}),
|
||||
},
|
||||
)
|
||||
for identity, column, attribute, number, resource_service, attribute_service, tagged, offset in ROWS
|
||||
]
|
||||
)
|
||||
insert_logs(
|
||||
[
|
||||
Logs(
|
||||
timestamp=now - timedelta(seconds=offset),
|
||||
body=json.dumps({} if tagged else {"route": {"tag": CONTESTED_VALUE}}),
|
||||
severity_text="ERROR" if column else "INFO",
|
||||
scope_name=SCOPE_NAME if column else "",
|
||||
scope_attributes={"name": CONTESTED_VALUE, SCOPE_ATTRIBUTE_KEY: SCOPE_ATTRIBUTE_VALUE} if attribute else {},
|
||||
resources={"service.name": resource_service},
|
||||
attributes={
|
||||
IDENTITY_KEY: identity,
|
||||
**({"severity_text": "ERROR"} if attribute else {}),
|
||||
**({"severity_text": NUMBER_VALUE} if number else {}),
|
||||
**({"service.name": attribute_service} if attribute_service else {}),
|
||||
**({ATTRIBUTE_ONLY_KEY: CONTESTED_VALUE} if tagged else {}),
|
||||
},
|
||||
)
|
||||
for identity, column, attribute, number, resource_service, attribute_service, tagged, offset in ROWS
|
||||
]
|
||||
)
|
||||
yield now
|
||||
|
||||
@@ -1,400 +0,0 @@
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime, timedelta
|
||||
from http import HTTPStatus
|
||||
|
||||
import pytest
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.auth import USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD
|
||||
from fixtures.querier import (
|
||||
RequestType,
|
||||
assert_scalar_value,
|
||||
build_aggregation,
|
||||
build_group_by_field,
|
||||
build_order_by,
|
||||
build_raw_query,
|
||||
build_scalar_query,
|
||||
get_all_warnings,
|
||||
get_column_data_from_response,
|
||||
get_scalar_table_data,
|
||||
make_query_request,
|
||||
)
|
||||
from fixtures.queriercommon import (
|
||||
ATTRIBUTE_ONLY,
|
||||
BOTH,
|
||||
COLUMN_ONLY,
|
||||
EXPLICIT_PREFIX,
|
||||
IDENTITY_KEY,
|
||||
NEITHER,
|
||||
NUMBER_ATTRIBUTE,
|
||||
)
|
||||
|
||||
# One name can exist in more than one place. `name` is a span column and a
|
||||
# span attribute. `severity_text` is a log column and a log attribute.
|
||||
# `service.name` is a resource attribute and a span or log attribute.
|
||||
#
|
||||
# Rules for a filter:
|
||||
# - A key with an explicit context reads that context only.
|
||||
# - A bare key that is a column and an attribute reads both. The query
|
||||
# returns an ambiguity warning.
|
||||
# - A bare key that is a resource attribute and an attribute reads the
|
||||
# resource attribute. The query returns an ambiguity warning.
|
||||
# - An `attribute.` key returns the warning when the attribute has two data
|
||||
# types.
|
||||
# - A string operand matches a number attribute through a text cast.
|
||||
# - A key under the signal's own context (`span.`, `log.`) that exists only
|
||||
# as an attribute reads the attribute. On logs it also reads the body JSON
|
||||
# path.
|
||||
FILTER_MATRIX = [
|
||||
pytest.param("{contested} = '{value}'", {COLUMN_ONLY, ATTRIBUTE_ONLY, BOTH}, True, id="bare_column_and_attribute"),
|
||||
pytest.param("{own}.{contested} = '{value}'", {COLUMN_ONLY, BOTH}, False, id="own_context_column_only"),
|
||||
pytest.param("attribute.{contested} = '{value}'", {ATTRIBUTE_ONLY, BOTH}, True, id="attribute_context_warns_about_two_types"),
|
||||
pytest.param("{contested} != '{value}'", {NEITHER, NUMBER_ATTRIBUTE}, True, id="bare_negative_excludes_every_carrier"),
|
||||
pytest.param("{contested} EXISTS", {COLUMN_ONLY, ATTRIBUTE_ONLY, BOTH, NEITHER, NUMBER_ATTRIBUTE}, True, id="bare_exists_is_the_column"),
|
||||
pytest.param("{contested} NOT EXISTS", set(), True, id="bare_not_exists_is_never"),
|
||||
pytest.param("attribute.{contested} EXISTS", {ATTRIBUTE_ONLY, BOTH, NUMBER_ATTRIBUTE}, True, id="attribute_exists_spans_both_types"),
|
||||
pytest.param("attribute.{contested} NOT EXISTS", {COLUMN_ONLY, NEITHER}, True, id="attribute_not_exists"),
|
||||
pytest.param("{contested} = '42'", {NUMBER_ATTRIBUTE}, True, id="bare_string_operand_reaches_the_number_attribute"),
|
||||
pytest.param("attribute.{contested}:string = '{value}'", {ATTRIBUTE_ONLY, BOTH}, False, id="type_suffix_selects_the_string_attribute"),
|
||||
pytest.param("attribute.{contested}:float64 = 42", {NUMBER_ATTRIBUTE}, False, id="type_suffix_selects_the_number_attribute"),
|
||||
pytest.param("service.name = 'svc-a'", {COLUMN_ONLY, BOTH}, True, id="bare_resource_wins_with_warning"),
|
||||
pytest.param("service.name != 'svc-a'", {ATTRIBUTE_ONLY, NEITHER, NUMBER_ATTRIBUTE}, True, id="bare_resource_negative"),
|
||||
pytest.param("resource.service.name = 'svc-a'", {COLUMN_ONLY, BOTH}, False, id="resource_context_no_warning"),
|
||||
pytest.param("resource.service.name != 'svc-a'", {ATTRIBUTE_ONLY, NEITHER, NUMBER_ATTRIBUTE}, False, id="resource_context_negative"),
|
||||
pytest.param("attribute.service.name = 'svc-a'", {ATTRIBUTE_ONLY, BOTH}, False, id="attribute_context_no_warning"),
|
||||
pytest.param(
|
||||
"{own}.route.tag = 'checkout'",
|
||||
{"traces": {COLUMN_ONLY, BOTH}, "logs": {COLUMN_ONLY, ATTRIBUTE_ONLY, BOTH, NEITHER, NUMBER_ATTRIBUTE}},
|
||||
False,
|
||||
id="own_context_miss_corrects_to_attribute_and_on_logs_to_body",
|
||||
),
|
||||
pytest.param("route.tag = 'checkout'", {COLUMN_ONLY, BOTH}, False, id="bare_attribute_only_key"),
|
||||
]
|
||||
|
||||
# Traces run under both physical attribute layouts. `insert_traces` writes the
|
||||
# legacy maps and the `attributes` JSON column; the backend decides which one
|
||||
# the query builder reads. Logs have the maps only.
|
||||
SIGNALS = [
|
||||
pytest.param("traces", "map", "span", "name", "checkout", "other", id="traces_map"),
|
||||
pytest.param("traces", "json", "span", "name", "checkout", "other", id="traces_json"),
|
||||
pytest.param("logs", "map", "log", "severity_text", "ERROR", "INFO", id="logs"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expression_template,expected,expects_ambiguity_warning", FILTER_MATRIX)
|
||||
@pytest.mark.parametrize("signal,attribute_backend,own_context,contested,value,other_value", SIGNALS)
|
||||
def test_filter_resolution(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
ambiguous_rows: datetime,
|
||||
use_attribute_backend: Callable[[str], None],
|
||||
signal: str,
|
||||
attribute_backend: str,
|
||||
own_context: str,
|
||||
contested: str,
|
||||
value: str,
|
||||
other_value: str, # pylint: disable=unused-argument
|
||||
expression_template: str,
|
||||
expected: set[str] | dict[str, set[str]],
|
||||
expects_ambiguity_warning: bool,
|
||||
) -> None:
|
||||
use_attribute_backend(attribute_backend)
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
expression = expression_template.format(own=own_context, contested=contested, value=value)
|
||||
|
||||
response = make_query_request(
|
||||
signoz,
|
||||
token,
|
||||
start_ms=int((ambiguous_rows - timedelta(minutes=2)).timestamp() * 1000),
|
||||
end_ms=int((ambiguous_rows + timedelta(minutes=1)).timestamp() * 1000),
|
||||
request_type=RequestType.RAW,
|
||||
queries=[
|
||||
build_raw_query(
|
||||
"A",
|
||||
signal,
|
||||
limit=100,
|
||||
filter_expression=expression,
|
||||
order=[build_order_by("timestamp", "asc")],
|
||||
select_fields=[{"name": IDENTITY_KEY}],
|
||||
)
|
||||
],
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
|
||||
matched = {row for row in get_column_data_from_response(response.json(), IDENTITY_KEY) if row.startswith(EXPLICIT_PREFIX)}
|
||||
assert matched == (expected[f"{signal}_{attribute_backend}" if f"{signal}_{attribute_backend}" in expected else signal] if isinstance(expected, dict) else expected), expression
|
||||
|
||||
warnings = [w["message"] for w in get_all_warnings(response.json())]
|
||||
assert any("ambiguous" in w for w in warnings) == expects_ambiguity_warning, warnings
|
||||
|
||||
|
||||
# Rules for a group by:
|
||||
# - A bare key that is a column and an attribute groups by the column only.
|
||||
# - A key with an explicit context groups by that context only.
|
||||
GROUP_BY_MATRIX = [
|
||||
pytest.param(None, {"{value}": 2, "{other}": 3}, id="bare_groups_by_the_column"),
|
||||
pytest.param("own", {"{value}": 2, "{other}": 3}, id="own_context_groups_by_the_column"),
|
||||
pytest.param("attribute", {"{value}": 2}, id="attribute_context_groups_by_the_attribute"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("context,expected_template", GROUP_BY_MATRIX)
|
||||
@pytest.mark.parametrize("signal,attribute_backend,own_context,contested,value,other_value", SIGNALS)
|
||||
def test_group_by_resolution(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
ambiguous_rows: datetime,
|
||||
use_attribute_backend: Callable[[str], None],
|
||||
signal: str,
|
||||
attribute_backend: str,
|
||||
own_context: str,
|
||||
contested: str,
|
||||
value: str,
|
||||
other_value: str,
|
||||
context: str | None,
|
||||
expected_template: dict[str, int],
|
||||
) -> None:
|
||||
use_attribute_backend(attribute_backend)
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
field_context = own_context if context == "own" else context
|
||||
|
||||
response = make_query_request(
|
||||
signoz,
|
||||
token,
|
||||
start_ms=int((ambiguous_rows - timedelta(minutes=2)).timestamp() * 1000),
|
||||
end_ms=int((ambiguous_rows + timedelta(minutes=1)).timestamp() * 1000),
|
||||
request_type=RequestType.SCALAR,
|
||||
queries=[
|
||||
build_scalar_query(
|
||||
"A",
|
||||
signal,
|
||||
[build_aggregation("count()", "rows")],
|
||||
group_by=[build_group_by_field(contested, "string", field_context) if field_context else {"name": contested}],
|
||||
filter_expression=f"{IDENTITY_KEY} LIKE '{EXPLICIT_PREFIX}%'",
|
||||
)
|
||||
],
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
|
||||
expected = {key.format(value=value, other=other_value): count for key, count in expected_template.items()}
|
||||
groups = {row[0]: row[1] for row in get_scalar_table_data(response.json()) if row[0] in expected}
|
||||
assert groups == expected, get_scalar_table_data(response.json())
|
||||
|
||||
|
||||
# Rule for a raw select of a bare key that is a resource attribute and an
|
||||
# attribute: each row shows the resource value. This is also true for a row
|
||||
# where the attribute has a different value.
|
||||
@pytest.mark.parametrize("signal,attribute_backend", [("traces", "map"), ("traces", "json"), ("logs", "map")], ids=["traces_map", "traces_json", "logs"])
|
||||
def test_select_of_ambiguous_name(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
ambiguous_rows: datetime,
|
||||
use_attribute_backend: Callable[[str], None],
|
||||
signal: str,
|
||||
attribute_backend: str,
|
||||
) -> None:
|
||||
use_attribute_backend(attribute_backend)
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
response = make_query_request(
|
||||
signoz,
|
||||
token,
|
||||
start_ms=int((ambiguous_rows - timedelta(minutes=2)).timestamp() * 1000),
|
||||
end_ms=int((ambiguous_rows + timedelta(minutes=1)).timestamp() * 1000),
|
||||
request_type=RequestType.RAW,
|
||||
queries=[
|
||||
build_raw_query(
|
||||
"A",
|
||||
signal,
|
||||
limit=100,
|
||||
filter_expression=f"{IDENTITY_KEY} LIKE '{EXPLICIT_PREFIX}%'",
|
||||
order=[build_order_by("timestamp", "asc")],
|
||||
select_fields=[{"name": IDENTITY_KEY}, {"name": "service.name"}],
|
||||
)
|
||||
],
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
|
||||
rows = response.json()["data"]["data"]["results"][0]["rows"] or []
|
||||
by_identity = {row["data"][IDENTITY_KEY]: row["data"]["service.name"] for row in rows if row["data"].get(IDENTITY_KEY, "").startswith(EXPLICIT_PREFIX)}
|
||||
assert by_identity == {
|
||||
COLUMN_ONLY: "svc-a",
|
||||
ATTRIBUTE_ONLY: "svc-b",
|
||||
BOTH: "svc-a",
|
||||
NEITHER: "svc-b",
|
||||
NUMBER_ATTRIBUTE: "svc-b",
|
||||
}
|
||||
|
||||
|
||||
# Rules for an order by, descending, with the timestamp descending as the
|
||||
# second key:
|
||||
# - A bare key or a key under the signal's own context sorts by the column
|
||||
# only.
|
||||
# - An `attribute.` key sorts by the attribute on traces. The number
|
||||
# attribute sorts as text. Rows without the attribute come last.
|
||||
# - An `attribute.` key sorts by the column on logs.
|
||||
BY_COLUMN = [ATTRIBUTE_ONLY, NEITHER, NUMBER_ATTRIBUTE, COLUMN_ONLY, BOTH]
|
||||
ORDER_BY_MATRIX = [
|
||||
pytest.param(None, {"traces": BY_COLUMN, "logs": BY_COLUMN}, id="bare_orders_by_the_column"),
|
||||
pytest.param("own", {"traces": BY_COLUMN, "logs": BY_COLUMN}, id="own_context_orders_by_the_column"),
|
||||
pytest.param(
|
||||
"attribute",
|
||||
{"traces": [ATTRIBUTE_ONLY, BOTH, NUMBER_ATTRIBUTE, COLUMN_ONLY, NEITHER], "logs": BY_COLUMN},
|
||||
id="attribute_context_orders_by_the_attribute_on_traces_only",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("context,expected", ORDER_BY_MATRIX)
|
||||
@pytest.mark.parametrize("signal,attribute_backend,own_context,contested,value,other_value", SIGNALS)
|
||||
def test_order_by_resolution(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
ambiguous_rows: datetime,
|
||||
use_attribute_backend: Callable[[str], None],
|
||||
signal: str,
|
||||
attribute_backend: str,
|
||||
own_context: str,
|
||||
contested: str,
|
||||
value: str, # pylint: disable=unused-argument
|
||||
other_value: str, # pylint: disable=unused-argument
|
||||
context: str | None,
|
||||
expected: dict[str, list[str]],
|
||||
) -> None:
|
||||
use_attribute_backend(attribute_backend)
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
prefix = f"{own_context}." if context == "own" else f"{context}." if context else ""
|
||||
|
||||
response = make_query_request(
|
||||
signoz,
|
||||
token,
|
||||
start_ms=int((ambiguous_rows - timedelta(minutes=2)).timestamp() * 1000),
|
||||
end_ms=int((ambiguous_rows + timedelta(minutes=1)).timestamp() * 1000),
|
||||
request_type=RequestType.RAW,
|
||||
queries=[
|
||||
build_raw_query(
|
||||
"A",
|
||||
signal,
|
||||
limit=100,
|
||||
filter_expression=f"{IDENTITY_KEY} LIKE '{EXPLICIT_PREFIX}%'",
|
||||
order=[build_order_by(f"{prefix}{contested}", "desc"), build_order_by("timestamp", "desc")],
|
||||
select_fields=[{"name": IDENTITY_KEY}],
|
||||
)
|
||||
],
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
|
||||
ordered = [row for row in get_column_data_from_response(response.json(), IDENTITY_KEY) if row.startswith(EXPLICIT_PREFIX)]
|
||||
assert ordered == expected.get(f"{signal}_{attribute_backend}", expected[signal])
|
||||
|
||||
|
||||
# Rules for an aggregation argument:
|
||||
# - A bare key counts the values of the column only.
|
||||
# - An `attribute.` key counts the attribute in both data types. The number
|
||||
# attribute adds one distinct value.
|
||||
AGGREGATION_MATRIX = [
|
||||
pytest.param(None, 2, id="bare_counts_the_column"),
|
||||
pytest.param("own", 2, id="own_context_counts_the_column"),
|
||||
pytest.param("attribute", 2, id="attribute_context_counts_both_attribute_types"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("context,expected", AGGREGATION_MATRIX)
|
||||
@pytest.mark.parametrize("signal,attribute_backend,own_context,contested,value,other_value", SIGNALS)
|
||||
def test_aggregation_argument_resolution(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
ambiguous_rows: datetime,
|
||||
use_attribute_backend: Callable[[str], None],
|
||||
signal: str,
|
||||
attribute_backend: str,
|
||||
own_context: str,
|
||||
contested: str,
|
||||
value: str, # pylint: disable=unused-argument
|
||||
other_value: str, # pylint: disable=unused-argument
|
||||
context: str | None,
|
||||
expected: int,
|
||||
) -> None:
|
||||
use_attribute_backend(attribute_backend)
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
prefix = f"{own_context}." if context == "own" else f"{context}." if context else ""
|
||||
|
||||
response = make_query_request(
|
||||
signoz,
|
||||
token,
|
||||
start_ms=int((ambiguous_rows - timedelta(minutes=2)).timestamp() * 1000),
|
||||
end_ms=int((ambiguous_rows + timedelta(minutes=1)).timestamp() * 1000),
|
||||
request_type=RequestType.SCALAR,
|
||||
queries=[
|
||||
build_scalar_query(
|
||||
"A",
|
||||
signal,
|
||||
[build_aggregation(f"count_distinct({prefix}{contested})", "distinct")],
|
||||
filter_expression=f"{IDENTITY_KEY} LIKE '{EXPLICIT_PREFIX}%'",
|
||||
)
|
||||
],
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
assert_scalar_value(response, "A", expected)
|
||||
|
||||
|
||||
# Rules for logs only:
|
||||
# - A `body.` key reads the body JSON path. It does not read the attribute
|
||||
# with the same name.
|
||||
# - A `log.` key reads the attribute and the body JSON path together. This
|
||||
# is also true when metadata reports the attribute.
|
||||
# - A `scope.` key resolves through metadata only. When metadata does not
|
||||
# report the key, the query fails with "key not found". This is also true
|
||||
# for the declared path `scope.name` and for rows that have the scope
|
||||
# data.
|
||||
LOGS_ONLY_MATRIX = [
|
||||
pytest.param("body.route.tag = 'checkout'", {ATTRIBUTE_ONLY, NEITHER, NUMBER_ATTRIBUTE}, id="body_context_reads_the_body_json"),
|
||||
pytest.param("log.route.tag = 'checkout'", {COLUMN_ONLY, ATTRIBUTE_ONLY, BOTH, NEITHER, NUMBER_ATTRIBUTE}, id="log_context_reads_attribute_and_body"),
|
||||
pytest.param("scope.name = 'scope-a'", "key `name` not found", id="scope_name_needs_metadata"),
|
||||
pytest.param("scope.env = 'prod'", "key `env` not found", id="scope_attribute_needs_metadata"),
|
||||
pytest.param("scope.env EXISTS", "key `env` not found", id="scope_attribute_exists_needs_metadata"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expression,expected", LOGS_ONLY_MATRIX)
|
||||
def test_logs_body_and_scope_contexts(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
ambiguous_rows: datetime,
|
||||
expression: str,
|
||||
expected: set[str] | str,
|
||||
) -> None:
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
response = make_query_request(
|
||||
signoz,
|
||||
token,
|
||||
start_ms=int((ambiguous_rows - timedelta(minutes=2)).timestamp() * 1000),
|
||||
end_ms=int((ambiguous_rows + timedelta(minutes=1)).timestamp() * 1000),
|
||||
request_type=RequestType.RAW,
|
||||
queries=[
|
||||
build_raw_query(
|
||||
"A",
|
||||
"logs",
|
||||
limit=100,
|
||||
filter_expression=expression,
|
||||
order=[build_order_by("timestamp", "asc")],
|
||||
select_fields=[{"name": IDENTITY_KEY}],
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
if isinstance(expected, str):
|
||||
assert response.status_code == HTTPStatus.BAD_REQUEST, response.text
|
||||
assert expected in response.text, response.text
|
||||
return
|
||||
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
matched = {row for row in get_column_data_from_response(response.json(), IDENTITY_KEY) if row.startswith(EXPLICIT_PREFIX)}
|
||||
assert matched == expected, expression
|
||||
Reference in New Issue
Block a user