mirror of
https://github.com/SigNoz/signoz.git
synced 2026-08-08 06:00:42 +01:00
Compare commits
25 Commits
feat/semco
...
issue_5601
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a59a9ef2d | ||
|
|
f0bac9f6be | ||
|
|
2d22cb6374 | ||
|
|
5f3d869058 | ||
|
|
b2c5af428e | ||
|
|
093f9b41c4 | ||
|
|
596e128005 | ||
|
|
2e1da92367 | ||
|
|
1e8d72e8fa | ||
|
|
cce080e1ae | ||
|
|
deca060a4d | ||
|
|
03c7e524e7 | ||
|
|
815dc7d88b | ||
|
|
f50d9199fe | ||
|
|
97c49c870b | ||
|
|
31efe177a4 | ||
|
|
d502d12ac3 | ||
|
|
bd9f15a716 | ||
|
|
813ef988c9 | ||
|
|
40e6799285 | ||
|
|
1caa60a3cd | ||
|
|
3f781f0083 | ||
|
|
6aec05cf7a | ||
|
|
683a52f35a | ||
|
|
e924fa1e62 |
@@ -1,11 +0,0 @@
|
||||
# Comments
|
||||
|
||||
Applies to everything in the repo — code, config, workflows.
|
||||
|
||||
- **No unnecessary comments.** Do not comment where the code is self-explanatory; never restate what the code already says.
|
||||
- **Document only** non-obvious behavior, constraints, formats, and edge cases.
|
||||
- **Rationale goes in prose, not source.** Why a version is pinned, why a job exists, how a subsystem fits together — that belongs in the README or the PR.
|
||||
- **Never remove pre-existing comments** when editing code. The bar above applies to comments you write, not comments already there.
|
||||
- **Never talk to the reviewer.** No comments about where a change came from, what was changed, or why the change is correct — that belongs in the PR description and is noise the moment it merges.
|
||||
|
||||
Language rules build on this one: [`go-comments`](go-comments.md), [`py-comments`](py-comments.md).
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.go"
|
||||
---
|
||||
|
||||
# Go comments
|
||||
|
||||
The bar is the [`comments`](comments.md) rule: nothing where the code is self-explanatory.
|
||||
|
||||
- **Names carry the meaning.** Make function, type, and variable names self-explanatory so the comment is unnecessary in the first place. If a comment is needed to explain what a function does, fix the name, not the comment.
|
||||
- **Godoc**: Skip comments that merely restate the identifier. Document only non-obvious behavior, constraints, formats, and edge cases.
|
||||
- **Generated code**: If the comment is emitted by an external codegen tool, leave it as-is — do not add or trim comments in generated files.
|
||||
@@ -1,7 +0,0 @@
|
||||
# Pull requests
|
||||
|
||||
- **Follow the template** (`.github/pull_request_template.md`): fill in its headings (Description / Issues closed by this PR / Screenshots / Additional Information). Don't add sections the template doesn't have.
|
||||
- **Keep only the headings that apply.** Delete every heading that has nothing under it, along with its `<!--...-->` placeholder comment. The body must never contain an empty heading — if only Description applies, the body has exactly that one heading.
|
||||
- **Keep the description concise and human-readable.** A few plain bullets saying what changed and why, for a reviewer skimming it — not a wall of text, not a restatement of the diff, not generated boilerplate.
|
||||
- **Reference issues with `Closes #issue-number`** under "Issues closed by this PR" so they auto-close on merge. This goes in the PR description only — never in commit messages.
|
||||
- **AI assistance in commits may optionally be disclosed with an `Assisted-by:` trailer** naming the model (e.g. `Assisted-by: Claude Opus 4.5`) — do NOT use a `Co-authored-by:` trailer for this.
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.py"
|
||||
---
|
||||
|
||||
# Python comments
|
||||
|
||||
The bar is the [`comments`](comments.md) rule: nothing where the code is self-explanatory.
|
||||
|
||||
- **Names carry the meaning.** Make function and variable names self-explanatory so the comment or docstring is unnecessary in the first place. If a docstring is needed to explain what a function does, fix the name, not the docstring.
|
||||
- **No file-level docstring.** The filename says what the module is for — `tool_bin.py` gets the tool binary. A module docstring restating that is noise, and a paragraph of design prose at the top of a file goes stale where nobody is looking. A constraint belongs next to the code it constrains, not in a preamble.
|
||||
- **Docstrings**: only when they say something the name and signature don't — drop them otherwise. Keep them short. A contract that genuinely needs a few lines (interacting flags, retry semantics, an edge case) is fine; a narrative is not.
|
||||
- **No song and dance.** Comment the constraint or the edge case. Not the narrative, not the rationale, not what the next line does.
|
||||
88
.github/pull_request_template.md
vendored
88
.github/pull_request_template.md
vendored
@@ -1,13 +1,85 @@
|
||||
<!--A few plain bullets saying what changed and why, for a reviewer skimming it - not a wall of text, not a restatement of the diff, not generated boilerplate.-->
|
||||
#### Description
|
||||
## Pull Request
|
||||
|
||||
---
|
||||
|
||||
### 📄 Summary
|
||||
> Why does this change exist?
|
||||
> What problem does it solve, and why is this the right approach?
|
||||
|
||||
|
||||
|
||||
#### Screenshots / Screen Recordings (if applicable)
|
||||
> Include screenshots or screen recordings that clearly show the behavior before the change and the result after the change. This helps reviewers quickly understand the impact and verify the update.
|
||||
|
||||
|
||||
<!--Reference issues using `Closes #issue-number` to enable automatic closure on merge. -->
|
||||
#### Issues closed by this PR
|
||||
> Reference issues using `Closes #issue-number` to enable automatic closure on merge.
|
||||
|
||||
<!--If applicable, include screenshots or screen recordings that clearly show the behavior before the change and the result after the change. -->
|
||||
#### Screenshots / Screen Recordings
|
||||
---
|
||||
|
||||
<!--Anything reviewers should keep in mind while reviewing -->
|
||||
#### Additional Information
|
||||
### ✅ Change Type
|
||||
_Select all that apply_
|
||||
|
||||
<!--Please delete paragraphs that you did not use before submitting.-->
|
||||
- [ ] ✨ Feature
|
||||
- [ ] 🐛 Bug fix
|
||||
- [ ] ♻️ Refactor
|
||||
- [ ] 🛠️ Infra / Tooling
|
||||
- [ ] 🧪 Test-only
|
||||
|
||||
---
|
||||
|
||||
### 🐛 Bug Context
|
||||
> Required if this PR fixes a bug
|
||||
|
||||
#### Root Cause
|
||||
> What caused the issue?
|
||||
> Regression, faulty assumption, edge case, refactor, etc.
|
||||
|
||||
#### Fix Strategy
|
||||
> How does this PR address the root cause?
|
||||
|
||||
---
|
||||
|
||||
### 🧪 Testing Strategy
|
||||
> How was this change validated?
|
||||
|
||||
- Tests added/updated:
|
||||
- Manual verification:
|
||||
- Edge cases covered:
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ Risk & Impact Assessment
|
||||
> What could break? How do we recover?
|
||||
|
||||
- Blast radius:
|
||||
- Potential regressions:
|
||||
- Rollback plan:
|
||||
|
||||
---
|
||||
|
||||
### 📝 Changelog
|
||||
> Fill only if this affects users, APIs, UI, or documented behavior
|
||||
> Use **N/A** for internal or non-user-facing changes
|
||||
|
||||
| Field | Value |
|
||||
|------|-------|
|
||||
| Deployment Type | Cloud / OSS / Enterprise |
|
||||
| Change Type | Feature / Bug Fix / Maintenance |
|
||||
| Description | User-facing summary |
|
||||
|
||||
---
|
||||
|
||||
### 📋 Checklist
|
||||
- [ ] Tests added or explicitly not required
|
||||
- [ ] Manually tested
|
||||
- [ ] Breaking changes documented
|
||||
- [ ] Backward compatibility considered
|
||||
|
||||
---
|
||||
|
||||
## 👀 Notes for Reviewers
|
||||
|
||||
<!-- Anything reviewers should keep in mind while reviewing -->
|
||||
|
||||
---
|
||||
|
||||
83
.github/workflows/docs.yml
vendored
Normal file
83
.github/workflows/docs.yml
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
name: "Update PR labels and Block PR until related docs are shipped for the feature"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
types: [opened, edited, labeled, unlabeled]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
docs_label_check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check PR Title and Manage Labels
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const prTitle = context.payload.pull_request.title;
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
|
||||
// Fetch the current PR details to get labels
|
||||
const pr = await github.rest.pulls.get({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: prNumber
|
||||
});
|
||||
|
||||
const labels = pr.data.labels.map(label => label.name);
|
||||
|
||||
if (prTitle.startsWith('feat:')) {
|
||||
const hasDocsRequired = labels.includes('docs required');
|
||||
const hasDocsShipped = labels.includes('docs shipped');
|
||||
const hasDocsNotRequired = labels.includes('docs not required');
|
||||
|
||||
// If "docs not required" is present, skip the checks
|
||||
if (hasDocsNotRequired && !hasDocsRequired) {
|
||||
console.log("Skipping checks due to 'docs not required' label.");
|
||||
return; // Exit the script early
|
||||
}
|
||||
|
||||
// If "docs shipped" is present, remove "docs required" if it exists
|
||||
if (hasDocsShipped && hasDocsRequired) {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
name: 'docs required'
|
||||
});
|
||||
console.log("Removed 'docs required' label.");
|
||||
}
|
||||
|
||||
// Add "docs required" label if neither "docs shipped" nor "docs required" are present
|
||||
if (!hasDocsRequired && !hasDocsShipped) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
labels: ['docs required']
|
||||
});
|
||||
console.log("Added 'docs required' label.");
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the updated labels after any changes
|
||||
const updatedPr = await github.rest.pulls.get({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: prNumber
|
||||
});
|
||||
|
||||
const updatedLabels = updatedPr.data.labels.map(label => label.name);
|
||||
const updatedHasDocsRequired = updatedLabels.includes('docs required');
|
||||
const updatedHasDocsShipped = updatedLabels.includes('docs shipped');
|
||||
|
||||
// Block PR if "docs required" is still present and "docs shipped" is missing
|
||||
if (updatedHasDocsRequired && !updatedHasDocsShipped) {
|
||||
core.setFailed("This PR requires documentation. Please remove the 'docs required' label and add the 'docs shipped' label to proceed.");
|
||||
}
|
||||
15
.github/workflows/goci.yaml
vendored
15
.github/workflows/goci.yaml
vendored
@@ -53,21 +53,6 @@ jobs:
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.24
|
||||
semconv-generated:
|
||||
if: |
|
||||
github.event_name == 'merge_group' ||
|
||||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
|
||||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: self-checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: go-install
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
- name: check-semconv-generated-files-and-product-literals
|
||||
run: make semconv-check
|
||||
build:
|
||||
if: |
|
||||
github.event_name == 'merge_group' ||
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -90,6 +90,8 @@ queries.active
|
||||
.devenv/**/tmp/**
|
||||
.qodo
|
||||
|
||||
.dev
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
@@ -229,6 +231,4 @@ cython_debug/
|
||||
# LSP config files
|
||||
pyrightconfig.json
|
||||
|
||||
# dev
|
||||
.dev/
|
||||
.claude/worktrees/
|
||||
|
||||
|
||||
27
Makefile
27
Makefile
@@ -220,25 +220,6 @@ py-test-teardown: ## Tear down the shared SigNoz backend
|
||||
py-test: ## Runs integration tests
|
||||
@cd tests && uv run pytest --basetemp=./tmp/ -vv --capture=no integration/tests/
|
||||
|
||||
.PHONY: py-test-semconv-phase1
|
||||
py-test-semconv-phase1: py-test-setup ## Rebuild the shared stack and run the semantic-convention Phase 1 matrix
|
||||
@cd tests && uv run pytest --basetemp=./tmp/ -vv --reuse --capture=no integration/tests/queriertraces/13_semconv_evolution.py
|
||||
|
||||
.PHONY: py-test-semconv-phase2
|
||||
py-test-semconv-phase2: py-test-setup ## Rebuild the shared stack and run the Phase 1-2 cross-signal matrices
|
||||
@cd tests && uv run pytest --basetemp=./tmp/ -vv --reuse --capture=no integration/tests/queriertraces/13_semconv_evolution.py integration/tests/queriersemconv/02_cross_signal.py
|
||||
|
||||
.PHONY: py-test-semconv-phase3
|
||||
py-test-semconv-phase3: py-test-setup ## Rebuild the shared stack and run the Phase 1-3 compatibility and migration-report matrices
|
||||
@cd tests && uv run pytest --basetemp=./tmp/ -vv --reuse --capture=no integration/tests/queriertraces/13_semconv_evolution.py integration/tests/queriersemconv/02_cross_signal.py
|
||||
|
||||
.PHONY: py-test-semconv-phase4
|
||||
py-test-semconv-phase4: py-test-setup ## Rebuild the shared stack and run all semantic-convention compatibility matrices
|
||||
@cd tests && uv run pytest --basetemp=./tmp/ -vv --reuse --capture=no integration/tests/queriertraces/13_semconv_evolution.py integration/tests/queriersemconv/02_cross_signal.py integration/tests/queriersemconv/04_phase4_families.py
|
||||
|
||||
.PHONY: py-test-semconv
|
||||
py-test-semconv: py-test-semconv-phase4 ## Run the complete semantic-convention evolution closure gate
|
||||
|
||||
.PHONY: py-clean
|
||||
py-clean: ## Clear all pycache and pytest cache from tests directory recursively
|
||||
@echo ">> cleaning python cache files from tests directory"
|
||||
@@ -252,14 +233,6 @@ py-clean: ## Clear all pycache and pytest cache from tests directory recursively
|
||||
##############################################################
|
||||
# generate commands
|
||||
##############################################################
|
||||
.PHONY: semconv-generate
|
||||
semconv-generate: ## Regenerate semantic-convention families for Go and TypeScript
|
||||
@go run ./scripts/semconv
|
||||
|
||||
.PHONY: semconv-check
|
||||
semconv-check: ## Verify generated semantic-convention files and reject old-name product literals
|
||||
@go run ./scripts/semconv -check -lint
|
||||
|
||||
.PHONY: gen-mocks
|
||||
gen-mocks:
|
||||
@echo ">> Generating mocks"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -176,7 +176,7 @@ func (m *module) Create(ctx context.Context, orgID valuer.UUID, userEmail string
|
||||
if err := m.checkAccess(ctx, orgID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := metricreductionrule.ValidatePostableReductionRule(req); err != nil {
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.validateMetricForReduction(ctx, orgID, req.MetricName); err != nil {
|
||||
@@ -218,7 +218,7 @@ func (m *module) UpdateByID(ctx context.Context, orgID valuer.UUID, userEmail st
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := metricreductionrule.ValidateUpdatableReductionRule(req); err != nil {
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -543,7 +543,7 @@ func resolveDroppedKept(matchType metricreductionruletypes.MatchType, ruleLabels
|
||||
}
|
||||
|
||||
for _, k := range keys {
|
||||
if metricreductionrule.IsProtectedLabel(k) {
|
||||
if metricreductionruletypes.IsProtectedLabel(k) {
|
||||
kept = append(kept, k)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ import type {
|
||||
GetFieldsKeysParams,
|
||||
GetFieldsValues200,
|
||||
GetFieldsValuesParams,
|
||||
GetSemconvMigrationReport200,
|
||||
GetSemconvMigrationReportParams,
|
||||
RenderErrorResponseDTO,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
@@ -122,108 +120,6 @@ export const invalidateGetFieldsKeys = async (
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns services that still emit old semantic-convention names without the current family name
|
||||
* @summary Get semantic-convention migration report
|
||||
*/
|
||||
export const getSemconvMigrationReport = (
|
||||
params?: GetSemconvMigrationReportParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetSemconvMigrationReport200>({
|
||||
url: `/api/v1/fields/semconv-migration`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetSemconvMigrationReportQueryKey = (
|
||||
params?: GetSemconvMigrationReportParams,
|
||||
) => {
|
||||
return [
|
||||
`/api/v1/fields/semconv-migration`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getGetSemconvMigrationReportQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getSemconvMigrationReport>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
params?: GetSemconvMigrationReportParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSemconvMigrationReport>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetSemconvMigrationReportQueryKey(params);
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getSemconvMigrationReport>>
|
||||
> = ({ signal }) => getSemconvMigrationReport(params, signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSemconvMigrationReport>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetSemconvMigrationReportQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getSemconvMigrationReport>>
|
||||
>;
|
||||
export type GetSemconvMigrationReportQueryError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get semantic-convention migration report
|
||||
*/
|
||||
|
||||
export function useGetSemconvMigrationReport<
|
||||
TData = Awaited<ReturnType<typeof getSemconvMigrationReport>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
params?: GetSemconvMigrationReportParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSemconvMigrationReport>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetSemconvMigrationReportQueryOptions(params, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get semantic-convention migration report
|
||||
*/
|
||||
export const invalidateGetSemconvMigrationReport = async (
|
||||
queryClient: QueryClient,
|
||||
params?: GetSemconvMigrationReportParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetSemconvMigrationReportQueryKey(params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns field values
|
||||
* @summary Get field values
|
||||
|
||||
@@ -1,490 +0,0 @@
|
||||
/**
|
||||
* ! Do not edit manually
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
InvalidateOptions,
|
||||
MutationFunction,
|
||||
QueryClient,
|
||||
QueryFunction,
|
||||
QueryKey,
|
||||
UseMutationOptions,
|
||||
UseMutationResult,
|
||||
UseQueryOptions,
|
||||
UseQueryResult,
|
||||
} from 'react-query';
|
||||
|
||||
import type {
|
||||
CreateSavedView201,
|
||||
DeleteSavedViewPathParameters,
|
||||
GetSavedView200,
|
||||
GetSavedViewPathParameters,
|
||||
ListSavedViews200,
|
||||
ListSavedViewsParams,
|
||||
RenderErrorResponseDTO,
|
||||
SavedviewtypesPostableSavedViewDTO,
|
||||
SavedviewtypesUpdatableSavedViewDTO,
|
||||
UpdateSavedViewPathParameters,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
/**
|
||||
* Returns saved views, optionally filtered by source and name.
|
||||
* @summary List saved views
|
||||
*/
|
||||
export const listSavedViews = (
|
||||
params?: ListSavedViewsParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<ListSavedViews200>({
|
||||
url: `/api/v2/saved_views`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getListSavedViewsQueryKey = (params?: ListSavedViewsParams) => {
|
||||
return [`/api/v2/saved_views`, ...(params ? [params] : [])] as const;
|
||||
};
|
||||
|
||||
export const getListSavedViewsQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof listSavedViews>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
params?: ListSavedViewsParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listSavedViews>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getListSavedViewsQueryKey(params);
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof listSavedViews>>> = ({
|
||||
signal,
|
||||
}) => listSavedViews(params, signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listSavedViews>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type ListSavedViewsQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof listSavedViews>>
|
||||
>;
|
||||
export type ListSavedViewsQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary List saved views
|
||||
*/
|
||||
|
||||
export function useListSavedViews<
|
||||
TData = Awaited<ReturnType<typeof listSavedViews>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
params?: ListSavedViewsParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listSavedViews>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getListSavedViewsQueryOptions(params, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary List saved views
|
||||
*/
|
||||
export const invalidateListSavedViews = async (
|
||||
queryClient: QueryClient,
|
||||
params?: ListSavedViewsParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getListSavedViewsQueryKey(params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Persists a saved view for the explore page. Returns the id of the created view.
|
||||
* @summary Create saved view
|
||||
*/
|
||||
export const createSavedView = (
|
||||
savedviewtypesPostableSavedViewDTO?: BodyType<SavedviewtypesPostableSavedViewDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateSavedView201>({
|
||||
url: `/api/v2/saved_views`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: savedviewtypesPostableSavedViewDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getCreateSavedViewMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createSavedView>>,
|
||||
TError,
|
||||
{ data?: BodyType<SavedviewtypesPostableSavedViewDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createSavedView>>,
|
||||
TError,
|
||||
{ data?: BodyType<SavedviewtypesPostableSavedViewDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['createSavedView'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof createSavedView>>,
|
||||
{ data?: BodyType<SavedviewtypesPostableSavedViewDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return createSavedView(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type CreateSavedViewMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createSavedView>>
|
||||
>;
|
||||
export type CreateSavedViewMutationBody =
|
||||
| BodyType<SavedviewtypesPostableSavedViewDTO>
|
||||
| undefined;
|
||||
export type CreateSavedViewMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Create saved view
|
||||
*/
|
||||
export const useCreateSavedView = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createSavedView>>,
|
||||
TError,
|
||||
{ data?: BodyType<SavedviewtypesPostableSavedViewDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof createSavedView>>,
|
||||
TError,
|
||||
{ data?: BodyType<SavedviewtypesPostableSavedViewDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getCreateSavedViewMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Deletes a saved view by id.
|
||||
* @summary Delete saved view
|
||||
*/
|
||||
export const deleteSavedView = (
|
||||
{ id }: DeleteSavedViewPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/saved_views/${id}`,
|
||||
method: 'DELETE',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getDeleteSavedViewMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteSavedView>>,
|
||||
TError,
|
||||
{ pathParams: DeleteSavedViewPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteSavedView>>,
|
||||
TError,
|
||||
{ pathParams: DeleteSavedViewPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['deleteSavedView'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof deleteSavedView>>,
|
||||
{ pathParams: DeleteSavedViewPathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return deleteSavedView(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type DeleteSavedViewMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof deleteSavedView>>
|
||||
>;
|
||||
|
||||
export type DeleteSavedViewMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Delete saved view
|
||||
*/
|
||||
export const useDeleteSavedView = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteSavedView>>,
|
||||
TError,
|
||||
{ pathParams: DeleteSavedViewPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof deleteSavedView>>,
|
||||
TError,
|
||||
{ pathParams: DeleteSavedViewPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getDeleteSavedViewMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns a saved view by id.
|
||||
* @summary Get saved view
|
||||
*/
|
||||
export const getSavedView = (
|
||||
{ id }: GetSavedViewPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetSavedView200>({
|
||||
url: `/api/v2/saved_views/${id}`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetSavedViewQueryKey = ({ id }: GetSavedViewPathParameters) => {
|
||||
return [`/api/v2/saved_views/${id}`] as const;
|
||||
};
|
||||
|
||||
export const getGetSavedViewQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getSavedView>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ id }: GetSavedViewPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSavedView>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getGetSavedViewQueryKey({ id });
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof getSavedView>>> = ({
|
||||
signal,
|
||||
}) => getSavedView({ id }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSavedView>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetSavedViewQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getSavedView>>
|
||||
>;
|
||||
export type GetSavedViewQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get saved view
|
||||
*/
|
||||
|
||||
export function useGetSavedView<
|
||||
TData = Awaited<ReturnType<typeof getSavedView>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ id }: GetSavedViewPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSavedView>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetSavedViewQueryOptions({ id }, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get saved view
|
||||
*/
|
||||
export const invalidateGetSavedView = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetSavedViewPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetSavedViewQueryKey({ id }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Replaces a saved view's name and query.
|
||||
* @summary Update saved view
|
||||
*/
|
||||
export const updateSavedView = (
|
||||
{ id }: UpdateSavedViewPathParameters,
|
||||
savedviewtypesUpdatableSavedViewDTO?: BodyType<SavedviewtypesUpdatableSavedViewDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/saved_views/${id}`,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: savedviewtypesUpdatableSavedViewDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUpdateSavedViewMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateSavedView>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateSavedViewPathParameters;
|
||||
data?: BodyType<SavedviewtypesUpdatableSavedViewDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateSavedView>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateSavedViewPathParameters;
|
||||
data?: BodyType<SavedviewtypesUpdatableSavedViewDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['updateSavedView'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof updateSavedView>>,
|
||||
{
|
||||
pathParams: UpdateSavedViewPathParameters;
|
||||
data?: BodyType<SavedviewtypesUpdatableSavedViewDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return updateSavedView(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UpdateSavedViewMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof updateSavedView>>
|
||||
>;
|
||||
export type UpdateSavedViewMutationBody =
|
||||
| BodyType<SavedviewtypesUpdatableSavedViewDTO>
|
||||
| undefined;
|
||||
export type UpdateSavedViewMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Update saved view
|
||||
*/
|
||||
export const useUpdateSavedView = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateSavedView>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateSavedViewPathParameters;
|
||||
data?: BodyType<SavedviewtypesUpdatableSavedViewDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof updateSavedView>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateSavedViewPathParameters;
|
||||
data?: BodyType<SavedviewtypesUpdatableSavedViewDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getUpdateSavedViewMutationOptions(options));
|
||||
};
|
||||
@@ -3482,10 +3482,6 @@ export enum TelemetrytypesFieldDataTypeDTO {
|
||||
number = 'number',
|
||||
'' = '',
|
||||
}
|
||||
export enum TelemetrytypesFieldResolutionDTO {
|
||||
exact = 'exact',
|
||||
'' = '',
|
||||
}
|
||||
export enum TelemetrytypesSignalDTO {
|
||||
traces = 'traces',
|
||||
logs = 'logs',
|
||||
@@ -3499,7 +3495,6 @@ export interface Querybuildertypesv5GroupByKeyDTO {
|
||||
description?: string;
|
||||
fieldContext?: TelemetrytypesFieldContextDTO;
|
||||
fieldDataType?: TelemetrytypesFieldDataTypeDTO;
|
||||
fieldResolution?: TelemetrytypesFieldResolutionDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -3540,7 +3535,6 @@ export interface Querybuildertypesv5OrderByKeyDTO {
|
||||
description?: string;
|
||||
fieldContext?: TelemetrytypesFieldContextDTO;
|
||||
fieldDataType?: TelemetrytypesFieldDataTypeDTO;
|
||||
fieldResolution?: TelemetrytypesFieldResolutionDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -3594,7 +3588,6 @@ export interface TelemetrytypesTelemetryFieldKeyDTO {
|
||||
description?: string;
|
||||
fieldContext?: TelemetrytypesFieldContextDTO;
|
||||
fieldDataType?: TelemetrytypesFieldDataTypeDTO;
|
||||
fieldResolution?: TelemetrytypesFieldResolutionDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -7943,7 +7936,6 @@ export interface Querybuildertypesv5ColumnDescriptorDTO {
|
||||
description?: string;
|
||||
fieldContext?: TelemetrytypesFieldContextDTO;
|
||||
fieldDataType?: TelemetrytypesFieldDataTypeDTO;
|
||||
fieldResolution?: TelemetrytypesFieldResolutionDTO;
|
||||
/**
|
||||
* @type object
|
||||
*/
|
||||
@@ -7967,25 +7959,6 @@ export type Querybuildertypesv5ExecStatsDTOStepIntervals = {
|
||||
[key: string]: number;
|
||||
};
|
||||
|
||||
export interface Querybuildertypesv5SemconvResolutionDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
current?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
kind?: string;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
members?: string[] | null;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
requested?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution statistics for the query, including rows scanned, bytes scanned, and duration.
|
||||
*/
|
||||
@@ -8005,10 +7978,6 @@ export interface Querybuildertypesv5ExecStatsDTO {
|
||||
* @minimum 0
|
||||
*/
|
||||
rowsScanned?: number;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
semconvResolutions?: Querybuildertypesv5SemconvResolutionDTO[];
|
||||
/**
|
||||
* @type object
|
||||
*/
|
||||
@@ -8889,112 +8858,6 @@ export interface RuletypesRuleDTO {
|
||||
export enum RuletypesThresholdKindDTO {
|
||||
basic = 'basic',
|
||||
}
|
||||
export interface SavedviewtypesDisplayDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
color?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
fontSize?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
format?: string;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
maxLines?: number;
|
||||
}
|
||||
|
||||
export enum SavedviewtypesPanelTypeDTO {
|
||||
value = 'value',
|
||||
graph = 'graph',
|
||||
table = 'table',
|
||||
list = 'list',
|
||||
trace = 'trace',
|
||||
}
|
||||
export interface SavedviewtypesSavedViewSpecDTO {
|
||||
display: SavedviewtypesDisplayDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
displayName: string;
|
||||
panelType: SavedviewtypesPanelTypeDTO;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
queries: Querybuildertypesv5QueryEnvelopeDTO[];
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
selectedFields: TelemetrytypesTelemetryFieldKeyDTO[];
|
||||
}
|
||||
|
||||
export interface SavedviewtypesSavedViewDataDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
schemaVersion: string;
|
||||
spec: SavedviewtypesSavedViewSpecDTO;
|
||||
}
|
||||
|
||||
export enum SavedviewtypesSourceDTO {
|
||||
traces = 'traces',
|
||||
logs = 'logs',
|
||||
metrics = 'metrics',
|
||||
meter = 'meter',
|
||||
}
|
||||
export interface SavedviewtypesPostableSavedViewDTO {
|
||||
data: SavedviewtypesSavedViewDataDTO;
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
generateName?: boolean;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name?: string;
|
||||
source: SavedviewtypesSourceDTO;
|
||||
}
|
||||
|
||||
export interface SavedviewtypesSavedViewDTO {
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
createdAt?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
createdBy?: string;
|
||||
data?: SavedviewtypesSavedViewDataDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name?: string;
|
||||
source?: SavedviewtypesSourceDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
updatedAt?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
updatedBy?: string;
|
||||
}
|
||||
|
||||
export interface SavedviewtypesUpdatableSavedViewDTO {
|
||||
data: SavedviewtypesSavedViewDataDTO;
|
||||
source: SavedviewtypesSourceDTO;
|
||||
}
|
||||
|
||||
export interface ServiceaccounttypesDeprecatedPostableServiceAccountRoleDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -9847,52 +9710,6 @@ export interface TelemetrytypesGettableFieldValuesDTO {
|
||||
values: TelemetrytypesTelemetryFieldValuesDTO;
|
||||
}
|
||||
|
||||
export interface TelemetrytypesSemconvMigrationReportEntryDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
current?: string;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
lastSeenUnixMilli?: number;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
old?: string;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
resourceSets?: number;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
services?: string[] | null;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
signal?: string;
|
||||
}
|
||||
|
||||
export interface TelemetrytypesGettableSemconvMigrationReportDTO {
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
endUnixMilli?: number;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
entries: TelemetrytypesSemconvMigrationReportEntryDTO[] | null;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
startUnixMilli?: number;
|
||||
}
|
||||
|
||||
export interface TypesChangePasswordRequestDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -10659,29 +10476,6 @@ export type GetFieldsKeys200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetSemconvMigrationReportParams = {
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
startUnixMilli?: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
endUnixMilli?: number;
|
||||
};
|
||||
|
||||
export type GetSemconvMigrationReport200 = {
|
||||
data: TelemetrytypesGettableSemconvMigrationReportDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetFieldsValuesParams = {
|
||||
/**
|
||||
* @description undefined
|
||||
@@ -12262,54 +12056,6 @@ export type TestRule200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ListSavedViewsParams = {
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
source?: SavedviewtypesSourceDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
name?: string;
|
||||
};
|
||||
|
||||
export type ListSavedViews200 = {
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
data: SavedviewtypesSavedViewDTO[] | null;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type CreateSavedView201 = {
|
||||
data: TypesIdentifiableDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type DeleteSavedViewPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetSavedViewPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetSavedView200 = {
|
||||
data: SavedviewtypesSavedViewDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type UpdateSavedViewPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetSessionContext200 = {
|
||||
data: AuthtypesSessionContextDTO;
|
||||
/**
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import axios from 'api';
|
||||
import { SemconvMigrationReport } from 'types/api/semconvMigration';
|
||||
|
||||
async function getSemconvMigrationReport(): Promise<SemconvMigrationReport> {
|
||||
const response = await axios.get('/fields/semconv-migration');
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export default getSemconvMigrationReport;
|
||||
@@ -64,12 +64,12 @@ export const COMMON_FILTERS = {
|
||||
SERVER_SPANS: "kind_string = 'Server'",
|
||||
CLIENT_SPANS: "kind_string = 'Client'",
|
||||
INTERNAL_SPANS: "kind_string = 'Internal'",
|
||||
ERROR_SPANS: 'http.response.status_code >= 400',
|
||||
SUCCESS_SPANS: 'http.response.status_code < 400',
|
||||
ERROR_SPANS: 'http.status_code >= 400',
|
||||
SUCCESS_SPANS: 'http.status_code < 400',
|
||||
|
||||
// Common service filters
|
||||
EXCLUDE_HEALTH_CHECKS: "http.route != '/health' AND http.route != '/ping'",
|
||||
HTTP_REQUESTS: "http.request.method != ''",
|
||||
HTTP_REQUESTS: "http.method != ''",
|
||||
|
||||
// Log filters
|
||||
ERROR_LOGS: "severity_text = 'ERROR'",
|
||||
@@ -87,7 +87,7 @@ export const COMMON_GROUP_BY_FIELDS = {
|
||||
fieldContext: 'resource' as const,
|
||||
},
|
||||
HTTP_METHOD: {
|
||||
name: 'http.request.method',
|
||||
name: 'http.method',
|
||||
fieldDataType: 'string' as const,
|
||||
fieldContext: 'attribute' as const,
|
||||
},
|
||||
@@ -97,7 +97,7 @@ export const COMMON_GROUP_BY_FIELDS = {
|
||||
fieldContext: 'attribute' as const,
|
||||
},
|
||||
HTTP_STATUS_CODE: {
|
||||
name: 'http.response.status_code',
|
||||
name: 'http.status_code',
|
||||
fieldDataType: 'int64' as const,
|
||||
fieldContext: 'attribute' as const,
|
||||
},
|
||||
|
||||
@@ -145,7 +145,7 @@ function CeleryOverviewConfigOptions(): JSX.Element {
|
||||
{
|
||||
placeholder: 'Destination',
|
||||
queryParam: QueryParams.destination,
|
||||
filterType: ['messaging.destination.name'],
|
||||
filterType: ['messaging.destination.name', 'messaging.destination'],
|
||||
},
|
||||
{
|
||||
placeholder: 'Kind',
|
||||
|
||||
@@ -1746,7 +1746,7 @@ QuerySearch.defaultProps = {
|
||||
signalSource: '',
|
||||
hardcodedAttributeKeys: undefined,
|
||||
placeholder:
|
||||
"Enter your filter query (e.g., http.response.status_code >= 500 AND service.name = 'frontend')",
|
||||
"Enter your filter query (e.g., http.status_code >= 500 AND service.name = 'frontend')",
|
||||
showFilterSuggestionsWithoutMetric: false,
|
||||
initialExpression: undefined,
|
||||
};
|
||||
|
||||
@@ -152,18 +152,15 @@ describe('QuerySearch (Integration with Real CodeMirror)', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
// Wait for this mount's debounced call. A debounce from the preceding
|
||||
// real-CodeMirror test can finish after mockClear(), so do not assume the
|
||||
// first or last recorded call belongs to this render.
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(
|
||||
mockedGetKeysOnMount.mock.calls.some(
|
||||
([args]) => args.signal === DataSource.LOGS && args.searchText === '',
|
||||
),
|
||||
).toBe(true),
|
||||
{ timeout: 2000 },
|
||||
);
|
||||
// Wait for debounced API call (300ms debounce + some buffer)
|
||||
await waitFor(() => expect(mockedGetKeysOnMount).toHaveBeenCalled(), {
|
||||
timeout: 2000,
|
||||
});
|
||||
|
||||
const lastArgs = mockedGetKeysOnMount.mock.calls[
|
||||
mockedGetKeysOnMount.mock.calls.length - 1
|
||||
]?.[0] as { signal: unknown; searchText: string };
|
||||
expect(lastArgs).toMatchObject({ signal: DataSource.LOGS, searchText: '' });
|
||||
});
|
||||
|
||||
it('calls provided onRun on Mod-Enter', async () => {
|
||||
|
||||
@@ -995,16 +995,6 @@ describe('removeKeysFromExpression', () => {
|
||||
expect(result).toBe("status = 'success'");
|
||||
});
|
||||
|
||||
it('should remove a comparison that uses the exact field wrapper', () => {
|
||||
const expression =
|
||||
"exact(resource.deployment.environment) EXISTS AND service.name = 'api-gateway'";
|
||||
const result = removeKeysFromExpression(expression, [
|
||||
'resource.deployment.environment',
|
||||
]);
|
||||
|
||||
expect(result).toBe("service.name = 'api-gateway'");
|
||||
});
|
||||
|
||||
it('should remove multiple keys from expression', () => {
|
||||
const expression =
|
||||
"service.name = 'api-gateway' AND status = 'success' AND region = 'us-east-1'";
|
||||
|
||||
@@ -652,16 +652,7 @@ export const removeKeysFromExpression = (
|
||||
}
|
||||
|
||||
function visitComparison(ctx: ComparisonContext): string | null {
|
||||
const field = ctx.field();
|
||||
// The runtime returns null for the inactive field alternative even though
|
||||
// the generated TypeScript signature is non-nullable.
|
||||
const exactCall = field.exactCall() as unknown as ReturnType<
|
||||
typeof field.exactCall
|
||||
> | null;
|
||||
const keyText = (exactCall ? exactCall.key() : field.key())
|
||||
.getText()
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const keyText = ctx.key().getText().trim().toLowerCase();
|
||||
|
||||
if (!keysSet.has(keyText)) {
|
||||
return src(ctx);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ArrowUpRight } from '@signozhq/icons';
|
||||
|
||||
const QUICK_FILTER_DOC_PATHS: Record<string, string> = {
|
||||
severity_text: 'severity-text',
|
||||
'deployment.environment.name': 'environment',
|
||||
'deployment.environment': 'environment',
|
||||
'service.name': 'service-name',
|
||||
'host.name': 'hostname',
|
||||
'k8s.cluster.name': 'k8s-cluster-name',
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Alert } from 'antd';
|
||||
import { findOldSemconvNames } from 'utils/semconv';
|
||||
|
||||
interface SemconvEditorWarningProps {
|
||||
value: unknown;
|
||||
editor: string;
|
||||
}
|
||||
|
||||
function SemconvEditorWarning({
|
||||
value,
|
||||
editor,
|
||||
}: SemconvEditorWarningProps): JSX.Element | null {
|
||||
const text = typeof value === 'string' ? value : JSON.stringify(value ?? '');
|
||||
const renames = findOldSemconvNames(text);
|
||||
if (renames.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
data-testid="semconv-editor-warning"
|
||||
message={`${editor} contains renamed OpenTelemetry fields`}
|
||||
description={renames
|
||||
.map(({ old, current }) => `${old} → ${current}`)
|
||||
.join(', ')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default SemconvEditorWarning;
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { getSemconvRename } from 'utils/semconv';
|
||||
|
||||
interface SemconvOldNameBadgeProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
function SemconvOldNameBadge({
|
||||
name,
|
||||
}: SemconvOldNameBadgeProps): JSX.Element | null {
|
||||
const rename = getSemconvRename(name);
|
||||
if (!rename || rename.family.kind !== 'attribute') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge color="amber" variant="outline" data-testid="semconv-old-name-badge">
|
||||
old name, renamed to {rename.current}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
export default SemconvOldNameBadge;
|
||||
@@ -1,39 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import SemconvEditorWarning from '../SemconvEditorWarning';
|
||||
import SemconvOldNameBadge from '../SemconvOldNameBadge';
|
||||
|
||||
describe('semantic convention product hints', () => {
|
||||
it('badges an old raw attribute with its current name', () => {
|
||||
render(<SemconvOldNameBadge name="deployment.environment" />);
|
||||
|
||||
expect(screen.getByTestId('semconv-old-name-badge')).toHaveTextContent(
|
||||
'old name, renamed to deployment.environment.name',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not badge a current raw attribute', () => {
|
||||
render(<SemconvOldNameBadge name="deployment.environment.name" />);
|
||||
|
||||
expect(
|
||||
screen.queryByTestId('semconv-old-name-badge'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows an informational editor warning without disabling the editor', () => {
|
||||
render(
|
||||
<>
|
||||
<input aria-label="query" defaultValue="db.system = 'postgresql'" />
|
||||
<SemconvEditorWarning
|
||||
value="db.system = 'postgresql'"
|
||||
editor="ClickHouse SQL"
|
||||
/>
|
||||
</>,
|
||||
);
|
||||
|
||||
expect(screen.getByLabelText('query')).not.toBeDisabled();
|
||||
expect(screen.getByTestId('semconv-editor-warning')).toHaveTextContent(
|
||||
'db.system → db.system.name',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,2 +0,0 @@
|
||||
export { default as SemconvEditorWarning } from './SemconvEditorWarning';
|
||||
export { default as SemconvOldNameBadge } from './SemconvOldNameBadge';
|
||||
@@ -1,235 +0,0 @@
|
||||
// Code generated by scripts/semconv. DO NOT EDIT.
|
||||
|
||||
export type SemconvFamily = {
|
||||
readonly current: string;
|
||||
readonly old: readonly string[];
|
||||
readonly kind: 'attribute' | 'metric';
|
||||
readonly contexts: readonly string[];
|
||||
readonly signals: readonly string[];
|
||||
readonly applyToMetrics: readonly string[];
|
||||
readonly valueMap: Readonly<Record<string, string>>;
|
||||
};
|
||||
|
||||
export const SEMCONV_FAMILIES: readonly SemconvFamily[] = [
|
||||
{
|
||||
current: 'code.file.path',
|
||||
old: ['code.filepath'],
|
||||
kind: 'attribute',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'code.function.name',
|
||||
old: ['code.function'],
|
||||
kind: 'attribute',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'code.line.number',
|
||||
old: ['code.lineno'],
|
||||
kind: 'attribute',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'container.cpu.usage',
|
||||
old: ['container.cpu.utilization'],
|
||||
kind: 'metric',
|
||||
contexts: ['metric'],
|
||||
signals: ['metrics'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'container.runtime.name',
|
||||
old: ['container.runtime'],
|
||||
kind: 'attribute',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'db.namespace',
|
||||
old: [
|
||||
'db.elasticsearch.cluster.name',
|
||||
'db.name',
|
||||
'db.cassandra.keyspace',
|
||||
'db.hbase.namespace',
|
||||
],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'db.operation.name',
|
||||
old: ['db.operation'],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'db.query.text',
|
||||
old: ['db.statement'],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'db.system.name',
|
||||
old: ['db.system'],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute', 'resource'],
|
||||
signals: ['logs', 'metrics', 'traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'deployment.environment.name',
|
||||
old: ['deployment.environment'],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute', 'resource'],
|
||||
signals: ['logs', 'metrics', 'traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'http.request.method',
|
||||
old: ['http.method'],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute'],
|
||||
signals: ['logs', 'traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'http.response.status_code',
|
||||
old: ['http.status_code'],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute'],
|
||||
signals: ['logs', 'traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'k8s.node.cpu.usage',
|
||||
old: ['k8s.node.cpu.utilization'],
|
||||
kind: 'metric',
|
||||
contexts: ['metric'],
|
||||
signals: ['metrics'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'k8s.pod.cpu.usage',
|
||||
old: ['k8s.pod.cpu.utilization'],
|
||||
kind: 'metric',
|
||||
contexts: ['metric'],
|
||||
signals: ['metrics'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'messaging.client.id',
|
||||
old: [
|
||||
'messaging.client_id',
|
||||
'messaging.kafka.client_id',
|
||||
'messaging.rocketmq.client_id',
|
||||
],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute'],
|
||||
signals: ['metrics', 'traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'messaging.consumer.group.name',
|
||||
old: [
|
||||
'messaging.eventhubs.consumer.group',
|
||||
'messaging.kafka.consumer.group',
|
||||
'messaging.rocketmq.client_group',
|
||||
'messaging.kafka.consumer_group',
|
||||
],
|
||||
kind: 'attribute',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'messaging.destination.name',
|
||||
old: ['messaging.destination'],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'messaging.operation.type',
|
||||
old: ['messaging.operation'],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'rpc.system.name',
|
||||
old: ['rpc.system'],
|
||||
kind: 'attribute',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'service.peer.name',
|
||||
old: ['peer.service'],
|
||||
kind: 'attribute',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'url.full',
|
||||
old: ['http.url'],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute'],
|
||||
signals: ['logs', 'traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'url.scheme',
|
||||
old: ['http.scheme'],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute'],
|
||||
signals: ['logs', 'traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'user_agent.original',
|
||||
old: ['browser.user_agent', 'http.user_agent'],
|
||||
kind: 'attribute',
|
||||
contexts: ['attribute'],
|
||||
signals: ['logs', 'traces'],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
] as const;
|
||||
@@ -155,7 +155,7 @@ function DomainList(): JSX.Element {
|
||||
dataSource={DataSource.TRACES}
|
||||
queryData={query}
|
||||
onChange={handleSearchChange}
|
||||
placeholder="Enter your filter query (e.g., deployment.environment.name = 'otel-demo' AND service.name = 'frontend')"
|
||||
placeholder="Enter your filter query (e.g., deployment.environment = 'otel-demo' AND service.name = 'frontend')"
|
||||
hardcodedAttributeKeys={ApiMonitoringHardcodedAttributeKeys}
|
||||
/>
|
||||
</div>
|
||||
@@ -180,8 +180,9 @@ function DomainList(): JSX.Element {
|
||||
</div>
|
||||
<div className="no-domain-subtitle">
|
||||
Ensure all HTTP client spans are being sent with kind as{' '}
|
||||
<span className="attribute">Client</span> and the URL set in the{' '}
|
||||
<span className="attribute">url.full</span> attribute.
|
||||
<span className="attribute">Client</span> and url set in{' '}
|
||||
<span className="attribute">url.full</span> or{' '}
|
||||
<span className="attribute">http.url</span> attribute.
|
||||
</div>
|
||||
<a
|
||||
href={DOCLINKS.EXTERNAL_API_MONITORING}
|
||||
|
||||
@@ -6,9 +6,9 @@ import { SPAN_ATTRIBUTES } from './Explorer/Domains/DomainDetails/constants';
|
||||
export const ApiMonitoringHardcodedAttributeKeys: QueryKeyDataSuggestionsProps[] =
|
||||
[
|
||||
{
|
||||
label: 'deployment.environment.name',
|
||||
label: 'deployment.environment',
|
||||
type: 'resource',
|
||||
name: 'deployment.environment.name',
|
||||
name: 'deployment.environment',
|
||||
signal: 'traces',
|
||||
fieldDataType: QUERY_BUILDER_KEY_TYPES.STRING,
|
||||
},
|
||||
|
||||
@@ -87,7 +87,7 @@ export const ApiMonitoringQuickFiltersConfig: IQuickFiltersConfig[] = [
|
||||
title: 'Environment',
|
||||
|
||||
attributeKey: {
|
||||
key: 'deployment.environment.name',
|
||||
key: 'deployment.environment',
|
||||
dataType: DataTypes.String,
|
||||
type: 'resource',
|
||||
},
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { FormatTimezoneAdjustedTimestamp } from 'hooks/useTimezoneFormatter/useTimezoneFormatter';
|
||||
import { getSemconvMembers } from 'utils/semconv';
|
||||
import styles from './traceListColumns.module.scss';
|
||||
|
||||
const keyToLabelMap: Record<string, string> = {
|
||||
@@ -28,12 +27,7 @@ const keyToLabelMap: Record<string, string> = {
|
||||
const keyAliases: Record<string, string[]> = {
|
||||
serviceName: ['serviceName', 'service.name', 'service_name'],
|
||||
durationNano: ['durationNano', 'duration.nano', 'duration_nano'],
|
||||
httpMethod: [
|
||||
'httpMethod',
|
||||
...getSemconvMembers('http.request.method'),
|
||||
'http_request_method',
|
||||
'http_method',
|
||||
],
|
||||
httpMethod: ['httpMethod', 'http.method', 'http_method'],
|
||||
responseStatusCode: [
|
||||
'response_status_code',
|
||||
'response.status.code',
|
||||
|
||||
@@ -112,7 +112,7 @@ export const INFRA_MONITORING_ATTR_KEYS = {
|
||||
K8S_OBJECT_NAME: 'k8s.object.name',
|
||||
|
||||
// Environment
|
||||
DEPLOYMENT_ENVIRONMENT: 'deployment.environment.name',
|
||||
DEPLOYMENT_ENVIRONMENT: 'deployment.environment',
|
||||
|
||||
// Host System
|
||||
OS_TYPE: 'os.type',
|
||||
@@ -733,7 +733,7 @@ export const ENTITY_FILTER_PLACEHOLDERS: Record<InfraMonitoringEntity, string> =
|
||||
[InfraMonitoringEntity.NAMESPACES]:
|
||||
"Enter your filter query (e.g., k8s.namespace.name = 'production' AND k8s.cluster.name = 'prod-cluster')",
|
||||
[InfraMonitoringEntity.CLUSTERS]:
|
||||
"Enter your filter query (e.g., k8s.cluster.name = 'prod-cluster' AND deployment.environment.name = 'production')",
|
||||
"Enter your filter query (e.g., k8s.cluster.name = 'prod-cluster' AND deployment.environment = 'production')",
|
||||
[InfraMonitoringEntity.DEPLOYMENTS]:
|
||||
"Enter your filter query (e.g., k8s.deployment.name = 'api-server' AND k8s.namespace.name = 'production')",
|
||||
[InfraMonitoringEntity.STATEFULSETS]:
|
||||
|
||||
@@ -2,17 +2,6 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
.semconv-migration-report {
|
||||
margin-top: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
.ant-table-wrapper {
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.ingestion-key-container {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
|
||||
@@ -5,8 +5,6 @@ import getIngestionData from 'api/settings/getIngestionData';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { IngestionDataType } from 'types/api/settings/ingestion';
|
||||
|
||||
import SemconvMigrationReport from './SemconvMigrationReport';
|
||||
|
||||
import './IngestionSettings.styles.scss';
|
||||
|
||||
export default function IngestionSettings(): JSX.Element {
|
||||
@@ -86,7 +84,6 @@ export default function IngestionSettings(): JSX.Element {
|
||||
dataSource={data}
|
||||
bordered
|
||||
/>
|
||||
<SemconvMigrationReport />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -83,8 +83,6 @@ import { MeterAggregateOperator } from 'types/common/queryBuilder';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
import { getDaysUntilExpiry } from 'utils/timeUtils';
|
||||
|
||||
import SemconvMigrationReport from './SemconvMigrationReport';
|
||||
|
||||
import './IngestionSettings.styles.scss';
|
||||
|
||||
const { Option } = Select;
|
||||
@@ -1707,7 +1705,6 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
}}
|
||||
className="ingestion-keys-table"
|
||||
/>
|
||||
<SemconvMigrationReport />
|
||||
</div>
|
||||
|
||||
{/* Delete Key Modal */}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import { useQuery } from 'react-query';
|
||||
import { Alert, Table, TableColumnsType } from 'antd';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import getSemconvMigrationReport from 'api/semconv/getMigrationReport';
|
||||
import dayjs from 'dayjs';
|
||||
import { SemconvMigrationReportEntry } from 'types/api/semconvMigration';
|
||||
|
||||
function SemconvMigrationReport(): JSX.Element {
|
||||
const { data, isLoading, isError } = useQuery({
|
||||
queryKey: ['semconv-migration-report'],
|
||||
queryFn: getSemconvMigrationReport,
|
||||
});
|
||||
|
||||
const columns: TableColumnsType<SemconvMigrationReportEntry> = [
|
||||
{
|
||||
title: 'Old name',
|
||||
dataIndex: 'old',
|
||||
key: 'old',
|
||||
},
|
||||
{
|
||||
title: 'Current name',
|
||||
dataIndex: 'current',
|
||||
key: 'current',
|
||||
},
|
||||
{
|
||||
title: 'Signal',
|
||||
dataIndex: 'signal',
|
||||
key: 'signal',
|
||||
},
|
||||
{
|
||||
title: 'Services still sending only the old name',
|
||||
dataIndex: 'services',
|
||||
key: 'services',
|
||||
render: (services: string[]): string => services.join(', '),
|
||||
},
|
||||
{
|
||||
title: 'Last seen',
|
||||
dataIndex: 'lastSeenUnixMilli',
|
||||
key: 'lastSeenUnixMilli',
|
||||
render: (value: number): string =>
|
||||
dayjs(value).format('YYYY-MM-DD HH:mm:ss'),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="semconv-migration-report">
|
||||
<Typography.Title level={4}>Semantic convention migration</Typography.Title>
|
||||
<Typography.Text>
|
||||
Services in this report sent an old OpenTelemetry field during the last 24
|
||||
hours without sending its current replacement. Update their SDK or
|
||||
instrumentation when practical; SigNoz queries remain backward compatible.
|
||||
</Typography.Text>
|
||||
{isError && (
|
||||
<Alert
|
||||
type="error"
|
||||
showIcon
|
||||
message="Could not load the semantic convention migration report"
|
||||
/>
|
||||
)}
|
||||
<Table
|
||||
loading={isLoading}
|
||||
columns={columns}
|
||||
dataSource={data?.entries ?? []}
|
||||
rowKey={(entry): string => `${entry.current}-${entry.old}-${entry.signal}`}
|
||||
pagination={false}
|
||||
locale={{ emptyText: 'No old-only services found in the last 24 hours' }}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default SemconvMigrationReport;
|
||||
@@ -15,7 +15,7 @@ export const SAMPLE_SPAN_JSON = `{
|
||||
},
|
||||
"resource": {
|
||||
"service.name": "llm-gateway",
|
||||
"deployment.environment.name": "production"
|
||||
"deployment.environment": "production"
|
||||
}
|
||||
}`;
|
||||
|
||||
|
||||
@@ -1120,7 +1120,7 @@
|
||||
"plugin": {
|
||||
"kind": "signoz/QueryVariable",
|
||||
"spec": {
|
||||
"queryValue": "SELECT DISTINCT resources_string['deployment.environment.name'] AS environment FROM signoz_traces.distributed_signoz_index_v3 WHERE mapContains(resources_string, 'deployment.environment.name') AND timestamp >= now() - INTERVAL 1 DAY"
|
||||
"queryValue": "SELECT DISTINCT resources_string['deployment.environment'] AS environment FROM signoz_traces.distributed_signoz_index_v3 WHERE mapContains(resources_string, 'deployment.environment') AND timestamp >= now() - INTERVAL 1 DAY"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Divider } from '@signozhq/ui/divider';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { SemconvOldNameBadge } from 'components/Semconv';
|
||||
|
||||
import { TagContainer, TagLabel, TagValue } from './FieldRenderer.styles';
|
||||
import { FieldRendererProps } from './LogDetailedView.types';
|
||||
@@ -29,7 +28,6 @@ function FieldRenderer({ field }: FieldRendererProps): JSX.Element {
|
||||
<Typography.Text truncate={1} className="label">
|
||||
{newField}{' '}
|
||||
</Typography.Text>
|
||||
<SemconvOldNameBadge name={newField} />
|
||||
</TooltipSimple>
|
||||
|
||||
<div className="tags">
|
||||
@@ -49,10 +47,7 @@ function FieldRenderer({ field }: FieldRendererProps): JSX.Element {
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="label">{field}</span>
|
||||
<SemconvOldNameBadge name={field} />
|
||||
</>
|
||||
<span className="label">{field}</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -164,9 +164,7 @@ describe('useInitialQuery - Priority-Based Resource Filtering', () => {
|
||||
value: 'frontend-service',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
key: expect.objectContaining({
|
||||
key: 'deployment.environment.name',
|
||||
}),
|
||||
key: expect.objectContaining({ key: 'deployment.environment' }),
|
||||
value: 'production',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
@@ -288,9 +286,7 @@ describe('useInitialQuery - Priority-Based Resource Filtering', () => {
|
||||
value: 'legacy-app',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
key: expect.objectContaining({
|
||||
key: 'deployment.environment.name',
|
||||
}),
|
||||
key: expect.objectContaining({ key: 'deployment.environment' }),
|
||||
value: 'production',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -6,14 +6,13 @@ import {
|
||||
TagFilterItem,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { getSemconvRename } from 'utils/semconv';
|
||||
|
||||
const FALLBACK_STARTS_WITH_REGEX = /^(k8s|cloud|host|deployment)/; // regex to filter out resources that start with the specified keywords
|
||||
const FALLBACK_CONTAINS_REGEX = /(env|service|file|container|tenant)/; // regex to filter out resources that contains the specified keywords
|
||||
|
||||
// Priority categories for filter selection
|
||||
// Strategy:
|
||||
// - Always include: service.name, deployment.environment.name, env, environment
|
||||
// - Always include: service.name, deployment.environment, env, environment
|
||||
// - Select ONE category only: stops at the first category with a matching attribute
|
||||
// - Within category: picks the first available attribute by order
|
||||
// - Order (highest to lowest priority): Kubernetes > Cloud > Host > Container
|
||||
@@ -27,36 +26,27 @@ const PRIORITY_CATEGORIES = [
|
||||
|
||||
const SERVICE_AND_ENVIRONMENT_KEYS = [
|
||||
'service.name',
|
||||
'deployment.environment.name',
|
||||
'deployment.environment',
|
||||
'env',
|
||||
'environment',
|
||||
];
|
||||
|
||||
export const getFiltersFromResources = (
|
||||
resources: ILog['resources_string'],
|
||||
): TagFilterItem[] => {
|
||||
const items = new Map<string, TagFilterItem>();
|
||||
Object.keys(resources).forEach((key: string) => {
|
||||
const currentKey = getSemconvRename(key)?.current ?? key;
|
||||
): TagFilterItem[] =>
|
||||
Object.keys(resources).map((key: string) => {
|
||||
const resourceValue = resources[key] as string;
|
||||
const item = {
|
||||
return {
|
||||
id: uuid(),
|
||||
key: {
|
||||
key: currentKey,
|
||||
key,
|
||||
dataType: DataTypes.String,
|
||||
type: 'resource',
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: resourceValue,
|
||||
};
|
||||
// If raw data contains both names, retain the current value just like the
|
||||
// backend's current-first resolver.
|
||||
if (!items.has(currentKey) || key === currentKey) {
|
||||
items.set(currentKey, item);
|
||||
}
|
||||
});
|
||||
return Array.from(items.values());
|
||||
};
|
||||
|
||||
export const isServiceOrEnvironmentAttribute = (key: string): boolean =>
|
||||
SERVICE_AND_ENVIRONMENT_KEYS.includes(key);
|
||||
|
||||
@@ -94,7 +94,7 @@ function DBCall(): JSX.Element {
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const legend = dotMetricsEnabled ? '{{db.system.name}}' : '{{db_system_name}}';
|
||||
const legend = dotMetricsEnabled ? '{{db.system}}' : '{{db_system}}';
|
||||
|
||||
const databaseCallsRPSWidget = useMemo(
|
||||
() =>
|
||||
|
||||
@@ -28,7 +28,7 @@ import { v4 as uuid } from 'uuid';
|
||||
|
||||
export const dbSystemTags: Tags[] = [
|
||||
{
|
||||
Key: 'db.system.name.(string)',
|
||||
Key: 'db.system.(string)',
|
||||
StringValues: [''],
|
||||
NumberValues: [],
|
||||
BoolValues: [],
|
||||
|
||||
@@ -103,7 +103,7 @@ export enum WidgetKeys {
|
||||
SignozExternalCallLatencySum = 'signoz_external_call_latency_sum',
|
||||
Signoz_latency_bucket_norm = 'signoz_latency_bucket',
|
||||
Signoz_latency_bucket = 'signoz_latency.bucket',
|
||||
Db_system = 'db.system.name',
|
||||
Db_system = 'db.system',
|
||||
Db_system_norm = 'db_system',
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ChangeEvent, useCallback } from 'react';
|
||||
import MEditor, { Monaco } from '@monaco-editor/react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Input } from 'antd';
|
||||
import { SemconvEditorWarning } from 'components/Semconv';
|
||||
import { LEGEND } from 'constants/global';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
@@ -119,7 +118,6 @@ function ClickHouseQueryBuilder({
|
||||
theme={isDarkMode ? 'my-theme' : 'light'}
|
||||
beforeMount={setEditorTheme}
|
||||
/>
|
||||
<SemconvEditorWarning value={queryData?.query} editor="ClickHouse SQL" />
|
||||
<Input
|
||||
onChange={handleUpdateInput}
|
||||
name="legend"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ChangeEvent, useCallback } from 'react';
|
||||
import { Input } from 'antd';
|
||||
import { LEGEND } from 'constants/global';
|
||||
import { SemconvEditorWarning } from 'components/Semconv';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { IPromQLQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
@@ -67,7 +66,6 @@ function PromQLQueryBuilder({
|
||||
style={{ marginBottom: '0.5rem' }}
|
||||
data-testid="promql-query-input"
|
||||
/>
|
||||
<SemconvEditorWarning value={queryData?.query} editor="PromQL" />
|
||||
|
||||
<Input
|
||||
onChange={handleUpdateQuery}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Form } from 'antd';
|
||||
import { initialQueryBuilderFormValuesMap } from 'constants/queryBuilder';
|
||||
import { SemconvEditorWarning } from 'components/Semconv';
|
||||
import QueryBuilderSearchV2 from 'container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||
@@ -56,7 +55,6 @@ function TagFilterInputWithLogsResultPreview({
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<SemconvEditorWarning value={value} editor="Pipeline filter" />
|
||||
<div className="pipeline-filter-input-preview-container">
|
||||
<LogsFilterPreview filter={value} />
|
||||
</div>
|
||||
|
||||
@@ -424,7 +424,7 @@ describe('ResourceProvider', () => {
|
||||
await waitFor(() => {
|
||||
expect(result.current.queries).toHaveLength(1);
|
||||
expect(result.current.queries[0]).toMatchObject({
|
||||
tagKey: 'resource_deployment_environment_name',
|
||||
tagKey: 'resource_deployment_environment',
|
||||
operator: 'IN',
|
||||
tagValue: ['production'],
|
||||
});
|
||||
@@ -435,7 +435,7 @@ describe('ResourceProvider', () => {
|
||||
const seeded = [
|
||||
{
|
||||
id: 'env',
|
||||
tagKey: 'resource_deployment_environment_name',
|
||||
tagKey: 'resource_deployment_environment',
|
||||
operator: 'IN',
|
||||
tagValue: ['production'],
|
||||
},
|
||||
@@ -459,7 +459,7 @@ describe('ResourceProvider', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
const tagKeys = result.current.queries.map((q) => q.tagKey);
|
||||
expect(tagKeys).not.toContain('resource_deployment_environment_name');
|
||||
expect(tagKeys).not.toContain('resource_deployment_environment');
|
||||
expect(tagKeys).toContain('resource_service_name');
|
||||
});
|
||||
});
|
||||
@@ -468,7 +468,7 @@ describe('ResourceProvider', () => {
|
||||
const seeded = [
|
||||
{
|
||||
id: 'env',
|
||||
tagKey: 'resource_deployment_environment_name',
|
||||
tagKey: 'resource_deployment_environment',
|
||||
operator: 'IN',
|
||||
tagValue: ['production'],
|
||||
},
|
||||
@@ -486,7 +486,7 @@ describe('ResourceProvider', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
const envQueries = result.current.queries.filter(
|
||||
(q) => q.tagKey === 'resource_deployment_environment_name',
|
||||
(q) => q.tagKey === 'resource_deployment_environment',
|
||||
);
|
||||
expect(envQueries).toHaveLength(1);
|
||||
expect(envQueries[0].tagValue).toStrictEqual(['staging']);
|
||||
@@ -518,7 +518,7 @@ describe('ResourceProvider', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.queries[0].tagKey).toBe(
|
||||
'resource_deployment.environment.name',
|
||||
'resource_deployment.environment',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,13 +6,13 @@ import { mappingWithRoutesAndKeys } from '../utils';
|
||||
describe('useResourceAttribute config', () => {
|
||||
describe('whilelistedKeys', () => {
|
||||
it('should include underscore-notation keys (DOT_METRICS_ENABLED=false)', () => {
|
||||
expect(whilelistedKeys).toContain('resource_deployment_environment_name');
|
||||
expect(whilelistedKeys).toContain('resource_deployment_environment');
|
||||
expect(whilelistedKeys).toContain('resource_k8s_cluster_name');
|
||||
expect(whilelistedKeys).toContain('resource_k8s_cluster_namespace');
|
||||
});
|
||||
|
||||
it('should include dot-notation keys (DOT_METRICS_ENABLED=true)', () => {
|
||||
expect(whilelistedKeys).toContain('resource_deployment.environment.name');
|
||||
expect(whilelistedKeys).toContain('resource_deployment.environment');
|
||||
expect(whilelistedKeys).toContain('resource_k8s.cluster.name');
|
||||
expect(whilelistedKeys).toContain('resource_k8s.cluster.namespace');
|
||||
});
|
||||
@@ -21,8 +21,8 @@ describe('useResourceAttribute config', () => {
|
||||
describe('mappingWithRoutesAndKeys', () => {
|
||||
const dotNotationFilters = [
|
||||
{
|
||||
label: 'deployment.environment.name',
|
||||
value: 'resource_deployment.environment.name',
|
||||
label: 'deployment.environment',
|
||||
value: 'resource_deployment.environment',
|
||||
},
|
||||
{ label: 'k8s.cluster.name', value: 'resource_k8s.cluster.name' },
|
||||
{ label: 'k8s.cluster.namespace', value: 'resource_k8s.cluster.namespace' },
|
||||
@@ -30,8 +30,8 @@ describe('useResourceAttribute config', () => {
|
||||
|
||||
const underscoreNotationFilters = [
|
||||
{
|
||||
label: 'deployment.environment.name',
|
||||
value: 'resource_deployment_environment_name',
|
||||
label: 'deployment.environment',
|
||||
value: 'resource_deployment_environment',
|
||||
},
|
||||
{ label: 'k8s.cluster.name', value: 'resource_k8s_cluster_name' },
|
||||
{ label: 'k8s.cluster.namespace', value: 'resource_k8s_cluster_namespace' },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const whilelistedKeys = [
|
||||
'resource_deployment_environment_name',
|
||||
'resource_deployment.environment.name',
|
||||
'resource_deployment_environment',
|
||||
'resource_deployment.environment',
|
||||
'resource_k8s_cluster_name',
|
||||
'resource_k8s.cluster.name',
|
||||
'resource_k8s_cluster_namespace',
|
||||
|
||||
@@ -148,9 +148,9 @@ export const getResourceDeploymentKeys = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): string => {
|
||||
if (dotMetricsEnabled) {
|
||||
return 'resource_deployment.environment.name';
|
||||
return 'resource_deployment.environment';
|
||||
}
|
||||
return 'resource_deployment_environment_name';
|
||||
return 'resource_deployment_environment';
|
||||
};
|
||||
|
||||
export const GetTagKeys = async (
|
||||
|
||||
@@ -35,7 +35,13 @@ export default function CeleryOverviewDetails({
|
||||
? undefined
|
||||
: getFiltersFromKeyValue('messaging.system', value, 'tag');
|
||||
case 'destination':
|
||||
return getFiltersFromKeyValue('messaging.destination.name', value, 'tag');
|
||||
return getFiltersFromKeyValue(
|
||||
details.messaging_system === 'celery'
|
||||
? 'messaging.destination'
|
||||
: 'messaging.destination.name',
|
||||
value,
|
||||
'tag',
|
||||
);
|
||||
case 'kind_string':
|
||||
return getFiltersFromKeyValue('kind_string', value, '');
|
||||
default:
|
||||
|
||||
@@ -233,7 +233,7 @@ describe('Logs Explorer Tests', () => {
|
||||
);
|
||||
|
||||
const queries = queryAllByText(
|
||||
"Enter your filter query (e.g., http.response.status_code >= 500 AND service.name = 'frontend')",
|
||||
"Enter your filter query (e.g., http.status_code >= 500 AND service.name = 'frontend')",
|
||||
);
|
||||
expect(queries).toHaveLength(1);
|
||||
});
|
||||
|
||||
@@ -40,7 +40,7 @@ export const LogsQuickFiltersConfig: IQuickFiltersConfig[] = [
|
||||
type: FiltersType.CHECKBOX,
|
||||
title: 'Environment',
|
||||
attributeKey: {
|
||||
key: 'deployment.environment.name',
|
||||
key: 'deployment.environment',
|
||||
dataType: DataTypes.String,
|
||||
type: 'resource',
|
||||
},
|
||||
|
||||
@@ -11,7 +11,6 @@ import { Skeleton } from 'antd';
|
||||
import { DetailsHeader, DetailsPanelDrawer } from 'components/DetailsPanel';
|
||||
import { HeaderAction } from 'components/DetailsPanel/DetailsHeader/DetailsHeader';
|
||||
import { DetailsPanelState } from 'components/DetailsPanel/types';
|
||||
import { SemconvOldNameBadge } from 'components/Semconv';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import {
|
||||
initialQueryBuilderFormValuesMap,
|
||||
@@ -109,12 +108,6 @@ function SpanDetailsContent({
|
||||
() => getSpanDisplayData(selectedSpan),
|
||||
[selectedSpan],
|
||||
);
|
||||
const semconvLabelSuffix = useCallback(
|
||||
(fieldKey: string): React.ReactNode => (
|
||||
<SemconvOldNameBadge name={fieldKey} />
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Map span attribute actions to PrettyView actions format.
|
||||
// Use the last key in fieldKeyPath (the actual attribute key), not the full display path.
|
||||
@@ -336,7 +329,6 @@ function SpanDetailsContent({
|
||||
visibleActions: VISIBLE_ACTIONS,
|
||||
pinnedFieldsValue,
|
||||
onPinnedFieldsChange,
|
||||
labelSuffixRenderer: semconvLabelSuffix,
|
||||
}}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
@@ -29,7 +29,7 @@ export const KEY_ATTRIBUTE_KEYS: Record<string, string[]> = {
|
||||
traces: [
|
||||
'service.name',
|
||||
'service.namespace',
|
||||
'deployment.environment.name',
|
||||
'deployment.environment',
|
||||
'timestamp',
|
||||
'duration_nano',
|
||||
'kind_string',
|
||||
|
||||
@@ -359,7 +359,7 @@ function Filters({
|
||||
onChange={handleExpressionChange}
|
||||
onRun={handleRunQuery}
|
||||
dataSource={DataSource.TRACES}
|
||||
placeholder="Enter your filter query (e.g., http.response.status_code >= 500 AND service.name = 'frontend')"
|
||||
placeholder="Enter your filter query (e.g., http.status_code >= 500 AND service.name = 'frontend')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,8 +22,8 @@ export const SPAN_CATEGORIES: readonly SpanCategory[] = [
|
||||
|
||||
// Map each category to the attribute key it filters on
|
||||
const CATEGORY_KEYS: Record<Exclude<SpanCategory, 'All'>, string> = {
|
||||
Database: 'db.system.name',
|
||||
HTTP: 'http.request.method',
|
||||
Database: 'db.system',
|
||||
HTTP: 'http.method',
|
||||
Functions: 'kind_string',
|
||||
Jobs: 'messaging.system',
|
||||
LLM: 'gen_ai.request.model',
|
||||
@@ -34,8 +34,8 @@ const ALL_CATEGORY_KEYS = Object.values(CATEGORY_KEYS);
|
||||
|
||||
// The expression clause to add for each category
|
||||
const CATEGORY_EXPRESSIONS: Record<Exclude<SpanCategory, 'All'>, string> = {
|
||||
Database: 'db.system.name exists',
|
||||
HTTP: 'http.request.method exists',
|
||||
Database: 'db.system exists',
|
||||
HTTP: 'http.method exists',
|
||||
Functions: "kind_string = 'Internal'",
|
||||
Jobs: 'messaging.system exists',
|
||||
LLM: 'gen_ai.request.model exists',
|
||||
|
||||
@@ -38,7 +38,7 @@ export function Section(props: SectionProps): JSX.Element {
|
||||
'hasError',
|
||||
'durationNano',
|
||||
'serviceName',
|
||||
'deployment.environment.name',
|
||||
'deployment.environment',
|
||||
]),
|
||||
),
|
||||
[selectedFilters],
|
||||
|
||||
@@ -14,7 +14,7 @@ export const AllTraceFilterKeyValue: Record<string, string> = {
|
||||
durationNano: 'Duration',
|
||||
duration_nano: 'Duration',
|
||||
durationNanoMax: 'Duration',
|
||||
'deployment.environment.name': 'Environment',
|
||||
'deployment.environment': 'Environment',
|
||||
hasError: 'Status',
|
||||
has_error: 'Status',
|
||||
serviceName: 'Service Name',
|
||||
@@ -208,11 +208,11 @@ export const traceFilterKeys: Record<AllTraceFilterKeys, BaseAutocompleteData> =
|
||||
id: 'serviceName--string--tag--true',
|
||||
},
|
||||
|
||||
'deployment.environment.name': {
|
||||
key: 'deployment.environment.name',
|
||||
'deployment.environment': {
|
||||
key: 'deployment.environment',
|
||||
dataType: DataTypes.String,
|
||||
type: 'resource',
|
||||
id: 'deployment.environment.name--string--resource--false',
|
||||
id: 'deployment.environment--string--resource--false',
|
||||
},
|
||||
name: {
|
||||
key: 'name',
|
||||
|
||||
@@ -74,8 +74,7 @@ export function tracesRunQueryAction(
|
||||
properties: {
|
||||
key: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Attribute key, e.g. service.name, http.response.status_code',
|
||||
description: 'Attribute key, e.g. service.name, http.status_code',
|
||||
},
|
||||
op: {
|
||||
type: 'string',
|
||||
@@ -144,7 +143,7 @@ export function tracesAddFilterAction(
|
||||
properties: {
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'Attribute key, e.g. service.name, http.response.status_code',
|
||||
description: 'Attribute key, e.g. service.name, http.status_code',
|
||||
},
|
||||
op: {
|
||||
type: 'string',
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -24,14 +24,12 @@ HASTOKEN=23
|
||||
HAS=24
|
||||
HASANY=25
|
||||
HASALL=26
|
||||
SEARCH=27
|
||||
EXACT=28
|
||||
BOOL=29
|
||||
NUMBER=30
|
||||
QUOTED_TEXT=31
|
||||
KEY=32
|
||||
WS=33
|
||||
FREETEXT=34
|
||||
BOOL=27
|
||||
NUMBER=28
|
||||
QUOTED_TEXT=29
|
||||
KEY=30
|
||||
WS=31
|
||||
FREETEXT=32
|
||||
'('=1
|
||||
')'=2
|
||||
'['=3
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -24,14 +24,12 @@ HASTOKEN=23
|
||||
HAS=24
|
||||
HASANY=25
|
||||
HASALL=26
|
||||
SEARCH=27
|
||||
EXACT=28
|
||||
BOOL=29
|
||||
NUMBER=30
|
||||
QUOTED_TEXT=31
|
||||
KEY=32
|
||||
WS=33
|
||||
FREETEXT=34
|
||||
BOOL=27
|
||||
NUMBER=28
|
||||
QUOTED_TEXT=29
|
||||
KEY=30
|
||||
WS=31
|
||||
FREETEXT=32
|
||||
'('=1
|
||||
')'=2
|
||||
'['=3
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated from grammar/FilterQuery.g4 by ANTLR 4.13.2
|
||||
// Generated from FilterQuery.g4 by ANTLR 4.13.1
|
||||
// noinspection ES6UnusedImports,JSUnusedGlobalSymbols,JSUnusedLocalSymbols
|
||||
import {
|
||||
ATN,
|
||||
@@ -38,14 +38,12 @@ export default class FilterQueryLexer extends Lexer {
|
||||
public static readonly HAS = 24;
|
||||
public static readonly HASANY = 25;
|
||||
public static readonly HASALL = 26;
|
||||
public static readonly SEARCH = 27;
|
||||
public static readonly EXACT = 28;
|
||||
public static readonly BOOL = 29;
|
||||
public static readonly NUMBER = 30;
|
||||
public static readonly QUOTED_TEXT = 31;
|
||||
public static readonly KEY = 32;
|
||||
public static readonly WS = 33;
|
||||
public static readonly FREETEXT = 34;
|
||||
public static readonly BOOL = 27;
|
||||
public static readonly NUMBER = 28;
|
||||
public static readonly QUOTED_TEXT = 29;
|
||||
public static readonly KEY = 30;
|
||||
public static readonly WS = 31;
|
||||
public static readonly FREETEXT = 32;
|
||||
public static readonly EOF = Token.EOF;
|
||||
|
||||
public static readonly channelNames: string[] = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" ];
|
||||
@@ -70,8 +68,7 @@ export default class FilterQueryLexer extends Lexer {
|
||||
"AND", "OR",
|
||||
"HASTOKEN",
|
||||
"HAS", "HASANY",
|
||||
"HASALL", "SEARCH",
|
||||
"EXACT", "BOOL",
|
||||
"HASALL", "BOOL",
|
||||
"NUMBER", "QUOTED_TEXT",
|
||||
"KEY", "WS",
|
||||
"FREETEXT" ];
|
||||
@@ -81,8 +78,8 @@ export default class FilterQueryLexer extends Lexer {
|
||||
"LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS",
|
||||
"NEQ", "LT", "LE", "GT", "GE", "LIKE", "ILIKE", "BETWEEN", "EXISTS", "REGEXP",
|
||||
"CONTAINS", "IN", "NOT", "AND", "OR", "HASTOKEN", "HAS", "HASANY", "HASALL",
|
||||
"SEARCH", "EXACT", "BOOL", "SIGN", "NUMBER", "QUOTED_TEXT", "SEGMENT",
|
||||
"EMPTY_BRACKS", "OLD_JSON_BRACKS", "KEY", "WS", "DIGIT", "FREETEXT",
|
||||
"BOOL", "SIGN", "NUMBER", "QUOTED_TEXT", "SEGMENT", "EMPTY_BRACKS", "OLD_JSON_BRACKS",
|
||||
"KEY", "WS", "DIGIT", "FREETEXT",
|
||||
];
|
||||
|
||||
|
||||
@@ -103,124 +100,119 @@ export default class FilterQueryLexer extends Lexer {
|
||||
|
||||
public get modeNames(): string[] { return FilterQueryLexer.modeNames; }
|
||||
|
||||
public static readonly _serializedATN: number[] = [4,0,34,337,6,-1,2,0,
|
||||
public static readonly _serializedATN: number[] = [4,0,32,320,6,-1,2,0,
|
||||
7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,
|
||||
7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,
|
||||
16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,
|
||||
2,24,7,24,2,25,7,25,2,26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,
|
||||
31,7,31,2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,
|
||||
7,38,1,0,1,0,1,1,1,1,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,5,3,5,93,8,5,1,6,
|
||||
1,6,1,6,1,7,1,7,1,7,1,8,1,8,1,9,1,9,1,9,1,10,1,10,1,11,1,11,1,11,1,12,1,
|
||||
12,1,12,1,12,1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,
|
||||
1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,3,15,136,8,15,1,16,1,16,1,
|
||||
16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,3,17,153,
|
||||
8,17,1,18,1,18,1,18,1,19,1,19,1,19,1,19,1,20,1,20,1,20,1,20,1,21,1,21,1,
|
||||
21,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,23,1,23,1,23,1,23,1,24,
|
||||
1,24,1,24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,
|
||||
26,1,26,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,27,1,27,1,28,1,28,1,28,
|
||||
1,28,1,28,1,28,1,28,1,28,1,28,3,28,218,8,28,1,29,1,29,1,30,3,30,223,8,30,
|
||||
1,30,4,30,226,8,30,11,30,12,30,227,1,30,1,30,5,30,232,8,30,10,30,12,30,
|
||||
235,9,30,3,30,237,8,30,1,30,1,30,3,30,241,8,30,1,30,4,30,244,8,30,11,30,
|
||||
12,30,245,3,30,248,8,30,1,30,3,30,251,8,30,1,30,1,30,4,30,255,8,30,11,30,
|
||||
12,30,256,1,30,1,30,3,30,261,8,30,1,30,4,30,264,8,30,11,30,12,30,265,3,
|
||||
30,268,8,30,3,30,270,8,30,1,31,1,31,1,31,1,31,5,31,276,8,31,10,31,12,31,
|
||||
279,9,31,1,31,1,31,1,31,1,31,1,31,5,31,286,8,31,10,31,12,31,289,9,31,1,
|
||||
31,3,31,292,8,31,1,32,1,32,5,32,296,8,32,10,32,12,32,299,9,32,1,33,1,33,
|
||||
1,33,1,34,1,34,1,34,1,34,1,35,1,35,1,35,1,35,1,35,1,35,1,35,4,35,315,8,
|
||||
35,11,35,12,35,316,5,35,319,8,35,10,35,12,35,322,9,35,1,36,4,36,325,8,36,
|
||||
11,36,12,36,326,1,36,1,36,1,37,1,37,1,38,4,38,334,8,38,11,38,12,38,335,
|
||||
0,0,39,1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,
|
||||
27,14,29,15,31,16,33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,49,25,
|
||||
51,26,53,27,55,28,57,29,59,0,61,30,63,31,65,0,67,0,69,0,71,32,73,33,75,
|
||||
0,77,34,1,0,29,2,0,76,76,108,108,2,0,73,73,105,105,2,0,75,75,107,107,2,
|
||||
0,69,69,101,101,2,0,66,66,98,98,2,0,84,84,116,116,2,0,87,87,119,119,2,0,
|
||||
78,78,110,110,2,0,88,88,120,120,2,0,83,83,115,115,2,0,82,82,114,114,2,0,
|
||||
71,71,103,103,2,0,80,80,112,112,2,0,67,67,99,99,2,0,79,79,111,111,2,0,65,
|
||||
65,97,97,2,0,68,68,100,100,2,0,72,72,104,104,2,0,89,89,121,121,2,0,85,85,
|
||||
117,117,2,0,70,70,102,102,2,0,43,43,45,45,2,0,34,34,92,92,2,0,39,39,92,
|
||||
92,4,0,35,36,64,90,95,95,97,123,7,0,35,36,45,45,47,58,64,90,95,95,97,123,
|
||||
125,125,3,0,9,10,13,13,32,32,1,0,48,57,8,0,9,10,13,13,32,34,39,41,44,44,
|
||||
60,62,91,91,93,93,361,0,1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,
|
||||
9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,
|
||||
0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,
|
||||
31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,
|
||||
0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,
|
||||
53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,71,1,0,
|
||||
0,0,0,73,1,0,0,0,0,77,1,0,0,0,1,79,1,0,0,0,3,81,1,0,0,0,5,83,1,0,0,0,7,
|
||||
85,1,0,0,0,9,87,1,0,0,0,11,92,1,0,0,0,13,94,1,0,0,0,15,97,1,0,0,0,17,100,
|
||||
1,0,0,0,19,102,1,0,0,0,21,105,1,0,0,0,23,107,1,0,0,0,25,110,1,0,0,0,27,
|
||||
115,1,0,0,0,29,121,1,0,0,0,31,129,1,0,0,0,33,137,1,0,0,0,35,144,1,0,0,0,
|
||||
37,154,1,0,0,0,39,157,1,0,0,0,41,161,1,0,0,0,43,165,1,0,0,0,45,168,1,0,
|
||||
0,0,47,177,1,0,0,0,49,181,1,0,0,0,51,188,1,0,0,0,53,195,1,0,0,0,55,202,
|
||||
1,0,0,0,57,217,1,0,0,0,59,219,1,0,0,0,61,269,1,0,0,0,63,291,1,0,0,0,65,
|
||||
293,1,0,0,0,67,300,1,0,0,0,69,303,1,0,0,0,71,307,1,0,0,0,73,324,1,0,0,0,
|
||||
75,330,1,0,0,0,77,333,1,0,0,0,79,80,5,40,0,0,80,2,1,0,0,0,81,82,5,41,0,
|
||||
0,82,4,1,0,0,0,83,84,5,91,0,0,84,6,1,0,0,0,85,86,5,93,0,0,86,8,1,0,0,0,
|
||||
87,88,5,44,0,0,88,10,1,0,0,0,89,93,5,61,0,0,90,91,5,61,0,0,91,93,5,61,0,
|
||||
0,92,89,1,0,0,0,92,90,1,0,0,0,93,12,1,0,0,0,94,95,5,33,0,0,95,96,5,61,0,
|
||||
0,96,14,1,0,0,0,97,98,5,60,0,0,98,99,5,62,0,0,99,16,1,0,0,0,100,101,5,60,
|
||||
0,0,101,18,1,0,0,0,102,103,5,60,0,0,103,104,5,61,0,0,104,20,1,0,0,0,105,
|
||||
106,5,62,0,0,106,22,1,0,0,0,107,108,5,62,0,0,108,109,5,61,0,0,109,24,1,
|
||||
0,0,0,110,111,7,0,0,0,111,112,7,1,0,0,112,113,7,2,0,0,113,114,7,3,0,0,114,
|
||||
26,1,0,0,0,115,116,7,1,0,0,116,117,7,0,0,0,117,118,7,1,0,0,118,119,7,2,
|
||||
0,0,119,120,7,3,0,0,120,28,1,0,0,0,121,122,7,4,0,0,122,123,7,3,0,0,123,
|
||||
124,7,5,0,0,124,125,7,6,0,0,125,126,7,3,0,0,126,127,7,3,0,0,127,128,7,7,
|
||||
0,0,128,30,1,0,0,0,129,130,7,3,0,0,130,131,7,8,0,0,131,132,7,1,0,0,132,
|
||||
133,7,9,0,0,133,135,7,5,0,0,134,136,7,9,0,0,135,134,1,0,0,0,135,136,1,0,
|
||||
0,0,136,32,1,0,0,0,137,138,7,10,0,0,138,139,7,3,0,0,139,140,7,11,0,0,140,
|
||||
141,7,3,0,0,141,142,7,8,0,0,142,143,7,12,0,0,143,34,1,0,0,0,144,145,7,13,
|
||||
0,0,145,146,7,14,0,0,146,147,7,7,0,0,147,148,7,5,0,0,148,149,7,15,0,0,149,
|
||||
150,7,1,0,0,150,152,7,7,0,0,151,153,7,9,0,0,152,151,1,0,0,0,152,153,1,0,
|
||||
0,0,153,36,1,0,0,0,154,155,7,1,0,0,155,156,7,7,0,0,156,38,1,0,0,0,157,158,
|
||||
7,7,0,0,158,159,7,14,0,0,159,160,7,5,0,0,160,40,1,0,0,0,161,162,7,15,0,
|
||||
0,162,163,7,7,0,0,163,164,7,16,0,0,164,42,1,0,0,0,165,166,7,14,0,0,166,
|
||||
167,7,10,0,0,167,44,1,0,0,0,168,169,7,17,0,0,169,170,7,15,0,0,170,171,7,
|
||||
9,0,0,171,172,7,5,0,0,172,173,7,14,0,0,173,174,7,2,0,0,174,175,7,3,0,0,
|
||||
175,176,7,7,0,0,176,46,1,0,0,0,177,178,7,17,0,0,178,179,7,15,0,0,179,180,
|
||||
7,9,0,0,180,48,1,0,0,0,181,182,7,17,0,0,182,183,7,15,0,0,183,184,7,9,0,
|
||||
0,184,185,7,15,0,0,185,186,7,7,0,0,186,187,7,18,0,0,187,50,1,0,0,0,188,
|
||||
189,7,17,0,0,189,190,7,15,0,0,190,191,7,9,0,0,191,192,7,15,0,0,192,193,
|
||||
7,0,0,0,193,194,7,0,0,0,194,52,1,0,0,0,195,196,7,9,0,0,196,197,7,3,0,0,
|
||||
197,198,7,15,0,0,198,199,7,10,0,0,199,200,7,13,0,0,200,201,7,17,0,0,201,
|
||||
54,1,0,0,0,202,203,7,3,0,0,203,204,7,8,0,0,204,205,7,15,0,0,205,206,7,13,
|
||||
0,0,206,207,7,5,0,0,207,56,1,0,0,0,208,209,7,5,0,0,209,210,7,10,0,0,210,
|
||||
211,7,19,0,0,211,218,7,3,0,0,212,213,7,20,0,0,213,214,7,15,0,0,214,215,
|
||||
7,0,0,0,215,216,7,9,0,0,216,218,7,3,0,0,217,208,1,0,0,0,217,212,1,0,0,0,
|
||||
218,58,1,0,0,0,219,220,7,21,0,0,220,60,1,0,0,0,221,223,3,59,29,0,222,221,
|
||||
1,0,0,0,222,223,1,0,0,0,223,225,1,0,0,0,224,226,3,75,37,0,225,224,1,0,0,
|
||||
0,226,227,1,0,0,0,227,225,1,0,0,0,227,228,1,0,0,0,228,236,1,0,0,0,229,233,
|
||||
5,46,0,0,230,232,3,75,37,0,231,230,1,0,0,0,232,235,1,0,0,0,233,231,1,0,
|
||||
0,0,233,234,1,0,0,0,234,237,1,0,0,0,235,233,1,0,0,0,236,229,1,0,0,0,236,
|
||||
237,1,0,0,0,237,247,1,0,0,0,238,240,7,3,0,0,239,241,3,59,29,0,240,239,1,
|
||||
0,0,0,240,241,1,0,0,0,241,243,1,0,0,0,242,244,3,75,37,0,243,242,1,0,0,0,
|
||||
244,245,1,0,0,0,245,243,1,0,0,0,245,246,1,0,0,0,246,248,1,0,0,0,247,238,
|
||||
1,0,0,0,247,248,1,0,0,0,248,270,1,0,0,0,249,251,3,59,29,0,250,249,1,0,0,
|
||||
0,250,251,1,0,0,0,251,252,1,0,0,0,252,254,5,46,0,0,253,255,3,75,37,0,254,
|
||||
253,1,0,0,0,255,256,1,0,0,0,256,254,1,0,0,0,256,257,1,0,0,0,257,267,1,0,
|
||||
0,0,258,260,7,3,0,0,259,261,3,59,29,0,260,259,1,0,0,0,260,261,1,0,0,0,261,
|
||||
263,1,0,0,0,262,264,3,75,37,0,263,262,1,0,0,0,264,265,1,0,0,0,265,263,1,
|
||||
0,0,0,265,266,1,0,0,0,266,268,1,0,0,0,267,258,1,0,0,0,267,268,1,0,0,0,268,
|
||||
270,1,0,0,0,269,222,1,0,0,0,269,250,1,0,0,0,270,62,1,0,0,0,271,277,5,34,
|
||||
0,0,272,276,8,22,0,0,273,274,5,92,0,0,274,276,9,0,0,0,275,272,1,0,0,0,275,
|
||||
273,1,0,0,0,276,279,1,0,0,0,277,275,1,0,0,0,277,278,1,0,0,0,278,280,1,0,
|
||||
0,0,279,277,1,0,0,0,280,292,5,34,0,0,281,287,5,39,0,0,282,286,8,23,0,0,
|
||||
283,284,5,92,0,0,284,286,9,0,0,0,285,282,1,0,0,0,285,283,1,0,0,0,286,289,
|
||||
1,0,0,0,287,285,1,0,0,0,287,288,1,0,0,0,288,290,1,0,0,0,289,287,1,0,0,0,
|
||||
290,292,5,39,0,0,291,271,1,0,0,0,291,281,1,0,0,0,292,64,1,0,0,0,293,297,
|
||||
7,24,0,0,294,296,7,25,0,0,295,294,1,0,0,0,296,299,1,0,0,0,297,295,1,0,0,
|
||||
0,297,298,1,0,0,0,298,66,1,0,0,0,299,297,1,0,0,0,300,301,5,91,0,0,301,302,
|
||||
5,93,0,0,302,68,1,0,0,0,303,304,5,91,0,0,304,305,5,42,0,0,305,306,5,93,
|
||||
0,0,306,70,1,0,0,0,307,320,3,65,32,0,308,309,5,46,0,0,309,319,3,65,32,0,
|
||||
310,319,3,67,33,0,311,319,3,69,34,0,312,314,5,46,0,0,313,315,3,75,37,0,
|
||||
314,313,1,0,0,0,315,316,1,0,0,0,316,314,1,0,0,0,316,317,1,0,0,0,317,319,
|
||||
1,0,0,0,318,308,1,0,0,0,318,310,1,0,0,0,318,311,1,0,0,0,318,312,1,0,0,0,
|
||||
319,322,1,0,0,0,320,318,1,0,0,0,320,321,1,0,0,0,321,72,1,0,0,0,322,320,
|
||||
1,0,0,0,323,325,7,26,0,0,324,323,1,0,0,0,325,326,1,0,0,0,326,324,1,0,0,
|
||||
0,326,327,1,0,0,0,327,328,1,0,0,0,328,329,6,36,0,0,329,74,1,0,0,0,330,331,
|
||||
7,27,0,0,331,76,1,0,0,0,332,334,8,28,0,0,333,332,1,0,0,0,334,335,1,0,0,
|
||||
0,335,333,1,0,0,0,335,336,1,0,0,0,336,78,1,0,0,0,29,0,92,135,152,217,222,
|
||||
227,233,236,240,245,247,250,256,260,265,267,269,275,277,285,287,291,297,
|
||||
316,318,320,326,335,1,6,0,0];
|
||||
31,7,31,2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,1,0,1,0,1,1,1,
|
||||
1,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,5,3,5,89,8,5,1,6,1,6,1,6,1,7,1,7,1,
|
||||
7,1,8,1,8,1,9,1,9,1,9,1,10,1,10,1,11,1,11,1,11,1,12,1,12,1,12,1,12,1,12,
|
||||
1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,
|
||||
15,1,15,1,15,1,15,1,15,1,15,3,15,132,8,15,1,16,1,16,1,16,1,16,1,16,1,16,
|
||||
1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,3,17,149,8,17,1,18,1,18,1,
|
||||
18,1,19,1,19,1,19,1,19,1,20,1,20,1,20,1,20,1,21,1,21,1,21,1,22,1,22,1,22,
|
||||
1,22,1,22,1,22,1,22,1,22,1,22,1,23,1,23,1,23,1,23,1,24,1,24,1,24,1,24,1,
|
||||
24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,26,1,26,1,26,1,26,
|
||||
1,26,1,26,1,26,1,26,3,26,201,8,26,1,27,1,27,1,28,3,28,206,8,28,1,28,4,28,
|
||||
209,8,28,11,28,12,28,210,1,28,1,28,5,28,215,8,28,10,28,12,28,218,9,28,3,
|
||||
28,220,8,28,1,28,1,28,3,28,224,8,28,1,28,4,28,227,8,28,11,28,12,28,228,
|
||||
3,28,231,8,28,1,28,3,28,234,8,28,1,28,1,28,4,28,238,8,28,11,28,12,28,239,
|
||||
1,28,1,28,3,28,244,8,28,1,28,4,28,247,8,28,11,28,12,28,248,3,28,251,8,28,
|
||||
3,28,253,8,28,1,29,1,29,1,29,1,29,5,29,259,8,29,10,29,12,29,262,9,29,1,
|
||||
29,1,29,1,29,1,29,1,29,5,29,269,8,29,10,29,12,29,272,9,29,1,29,3,29,275,
|
||||
8,29,1,30,1,30,5,30,279,8,30,10,30,12,30,282,9,30,1,31,1,31,1,31,1,32,1,
|
||||
32,1,32,1,32,1,33,1,33,1,33,1,33,1,33,1,33,1,33,4,33,298,8,33,11,33,12,
|
||||
33,299,5,33,302,8,33,10,33,12,33,305,9,33,1,34,4,34,308,8,34,11,34,12,34,
|
||||
309,1,34,1,34,1,35,1,35,1,36,4,36,317,8,36,11,36,12,36,318,0,0,37,1,1,3,
|
||||
2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,27,14,29,15,31,
|
||||
16,33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,49,25,51,26,53,27,55,
|
||||
0,57,28,59,29,61,0,63,0,65,0,67,30,69,31,71,0,73,32,1,0,29,2,0,76,76,108,
|
||||
108,2,0,73,73,105,105,2,0,75,75,107,107,2,0,69,69,101,101,2,0,66,66,98,
|
||||
98,2,0,84,84,116,116,2,0,87,87,119,119,2,0,78,78,110,110,2,0,88,88,120,
|
||||
120,2,0,83,83,115,115,2,0,82,82,114,114,2,0,71,71,103,103,2,0,80,80,112,
|
||||
112,2,0,67,67,99,99,2,0,79,79,111,111,2,0,65,65,97,97,2,0,68,68,100,100,
|
||||
2,0,72,72,104,104,2,0,89,89,121,121,2,0,85,85,117,117,2,0,70,70,102,102,
|
||||
2,0,43,43,45,45,2,0,34,34,92,92,2,0,39,39,92,92,4,0,35,36,64,90,95,95,97,
|
||||
123,7,0,35,36,45,45,47,58,64,90,95,95,97,123,125,125,3,0,9,10,13,13,32,
|
||||
32,1,0,48,57,8,0,9,10,13,13,32,34,39,41,44,44,60,62,91,91,93,93,344,0,1,
|
||||
1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,
|
||||
13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,
|
||||
0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,
|
||||
35,1,0,0,0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,
|
||||
0,0,0,47,1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,57,1,0,0,0,0,
|
||||
59,1,0,0,0,0,67,1,0,0,0,0,69,1,0,0,0,0,73,1,0,0,0,1,75,1,0,0,0,3,77,1,0,
|
||||
0,0,5,79,1,0,0,0,7,81,1,0,0,0,9,83,1,0,0,0,11,88,1,0,0,0,13,90,1,0,0,0,
|
||||
15,93,1,0,0,0,17,96,1,0,0,0,19,98,1,0,0,0,21,101,1,0,0,0,23,103,1,0,0,0,
|
||||
25,106,1,0,0,0,27,111,1,0,0,0,29,117,1,0,0,0,31,125,1,0,0,0,33,133,1,0,
|
||||
0,0,35,140,1,0,0,0,37,150,1,0,0,0,39,153,1,0,0,0,41,157,1,0,0,0,43,161,
|
||||
1,0,0,0,45,164,1,0,0,0,47,173,1,0,0,0,49,177,1,0,0,0,51,184,1,0,0,0,53,
|
||||
200,1,0,0,0,55,202,1,0,0,0,57,252,1,0,0,0,59,274,1,0,0,0,61,276,1,0,0,0,
|
||||
63,283,1,0,0,0,65,286,1,0,0,0,67,290,1,0,0,0,69,307,1,0,0,0,71,313,1,0,
|
||||
0,0,73,316,1,0,0,0,75,76,5,40,0,0,76,2,1,0,0,0,77,78,5,41,0,0,78,4,1,0,
|
||||
0,0,79,80,5,91,0,0,80,6,1,0,0,0,81,82,5,93,0,0,82,8,1,0,0,0,83,84,5,44,
|
||||
0,0,84,10,1,0,0,0,85,89,5,61,0,0,86,87,5,61,0,0,87,89,5,61,0,0,88,85,1,
|
||||
0,0,0,88,86,1,0,0,0,89,12,1,0,0,0,90,91,5,33,0,0,91,92,5,61,0,0,92,14,1,
|
||||
0,0,0,93,94,5,60,0,0,94,95,5,62,0,0,95,16,1,0,0,0,96,97,5,60,0,0,97,18,
|
||||
1,0,0,0,98,99,5,60,0,0,99,100,5,61,0,0,100,20,1,0,0,0,101,102,5,62,0,0,
|
||||
102,22,1,0,0,0,103,104,5,62,0,0,104,105,5,61,0,0,105,24,1,0,0,0,106,107,
|
||||
7,0,0,0,107,108,7,1,0,0,108,109,7,2,0,0,109,110,7,3,0,0,110,26,1,0,0,0,
|
||||
111,112,7,1,0,0,112,113,7,0,0,0,113,114,7,1,0,0,114,115,7,2,0,0,115,116,
|
||||
7,3,0,0,116,28,1,0,0,0,117,118,7,4,0,0,118,119,7,3,0,0,119,120,7,5,0,0,
|
||||
120,121,7,6,0,0,121,122,7,3,0,0,122,123,7,3,0,0,123,124,7,7,0,0,124,30,
|
||||
1,0,0,0,125,126,7,3,0,0,126,127,7,8,0,0,127,128,7,1,0,0,128,129,7,9,0,0,
|
||||
129,131,7,5,0,0,130,132,7,9,0,0,131,130,1,0,0,0,131,132,1,0,0,0,132,32,
|
||||
1,0,0,0,133,134,7,10,0,0,134,135,7,3,0,0,135,136,7,11,0,0,136,137,7,3,0,
|
||||
0,137,138,7,8,0,0,138,139,7,12,0,0,139,34,1,0,0,0,140,141,7,13,0,0,141,
|
||||
142,7,14,0,0,142,143,7,7,0,0,143,144,7,5,0,0,144,145,7,15,0,0,145,146,7,
|
||||
1,0,0,146,148,7,7,0,0,147,149,7,9,0,0,148,147,1,0,0,0,148,149,1,0,0,0,149,
|
||||
36,1,0,0,0,150,151,7,1,0,0,151,152,7,7,0,0,152,38,1,0,0,0,153,154,7,7,0,
|
||||
0,154,155,7,14,0,0,155,156,7,5,0,0,156,40,1,0,0,0,157,158,7,15,0,0,158,
|
||||
159,7,7,0,0,159,160,7,16,0,0,160,42,1,0,0,0,161,162,7,14,0,0,162,163,7,
|
||||
10,0,0,163,44,1,0,0,0,164,165,7,17,0,0,165,166,7,15,0,0,166,167,7,9,0,0,
|
||||
167,168,7,5,0,0,168,169,7,14,0,0,169,170,7,2,0,0,170,171,7,3,0,0,171,172,
|
||||
7,7,0,0,172,46,1,0,0,0,173,174,7,17,0,0,174,175,7,15,0,0,175,176,7,9,0,
|
||||
0,176,48,1,0,0,0,177,178,7,17,0,0,178,179,7,15,0,0,179,180,7,9,0,0,180,
|
||||
181,7,15,0,0,181,182,7,7,0,0,182,183,7,18,0,0,183,50,1,0,0,0,184,185,7,
|
||||
17,0,0,185,186,7,15,0,0,186,187,7,9,0,0,187,188,7,15,0,0,188,189,7,0,0,
|
||||
0,189,190,7,0,0,0,190,52,1,0,0,0,191,192,7,5,0,0,192,193,7,10,0,0,193,194,
|
||||
7,19,0,0,194,201,7,3,0,0,195,196,7,20,0,0,196,197,7,15,0,0,197,198,7,0,
|
||||
0,0,198,199,7,9,0,0,199,201,7,3,0,0,200,191,1,0,0,0,200,195,1,0,0,0,201,
|
||||
54,1,0,0,0,202,203,7,21,0,0,203,56,1,0,0,0,204,206,3,55,27,0,205,204,1,
|
||||
0,0,0,205,206,1,0,0,0,206,208,1,0,0,0,207,209,3,71,35,0,208,207,1,0,0,0,
|
||||
209,210,1,0,0,0,210,208,1,0,0,0,210,211,1,0,0,0,211,219,1,0,0,0,212,216,
|
||||
5,46,0,0,213,215,3,71,35,0,214,213,1,0,0,0,215,218,1,0,0,0,216,214,1,0,
|
||||
0,0,216,217,1,0,0,0,217,220,1,0,0,0,218,216,1,0,0,0,219,212,1,0,0,0,219,
|
||||
220,1,0,0,0,220,230,1,0,0,0,221,223,7,3,0,0,222,224,3,55,27,0,223,222,1,
|
||||
0,0,0,223,224,1,0,0,0,224,226,1,0,0,0,225,227,3,71,35,0,226,225,1,0,0,0,
|
||||
227,228,1,0,0,0,228,226,1,0,0,0,228,229,1,0,0,0,229,231,1,0,0,0,230,221,
|
||||
1,0,0,0,230,231,1,0,0,0,231,253,1,0,0,0,232,234,3,55,27,0,233,232,1,0,0,
|
||||
0,233,234,1,0,0,0,234,235,1,0,0,0,235,237,5,46,0,0,236,238,3,71,35,0,237,
|
||||
236,1,0,0,0,238,239,1,0,0,0,239,237,1,0,0,0,239,240,1,0,0,0,240,250,1,0,
|
||||
0,0,241,243,7,3,0,0,242,244,3,55,27,0,243,242,1,0,0,0,243,244,1,0,0,0,244,
|
||||
246,1,0,0,0,245,247,3,71,35,0,246,245,1,0,0,0,247,248,1,0,0,0,248,246,1,
|
||||
0,0,0,248,249,1,0,0,0,249,251,1,0,0,0,250,241,1,0,0,0,250,251,1,0,0,0,251,
|
||||
253,1,0,0,0,252,205,1,0,0,0,252,233,1,0,0,0,253,58,1,0,0,0,254,260,5,34,
|
||||
0,0,255,259,8,22,0,0,256,257,5,92,0,0,257,259,9,0,0,0,258,255,1,0,0,0,258,
|
||||
256,1,0,0,0,259,262,1,0,0,0,260,258,1,0,0,0,260,261,1,0,0,0,261,263,1,0,
|
||||
0,0,262,260,1,0,0,0,263,275,5,34,0,0,264,270,5,39,0,0,265,269,8,23,0,0,
|
||||
266,267,5,92,0,0,267,269,9,0,0,0,268,265,1,0,0,0,268,266,1,0,0,0,269,272,
|
||||
1,0,0,0,270,268,1,0,0,0,270,271,1,0,0,0,271,273,1,0,0,0,272,270,1,0,0,0,
|
||||
273,275,5,39,0,0,274,254,1,0,0,0,274,264,1,0,0,0,275,60,1,0,0,0,276,280,
|
||||
7,24,0,0,277,279,7,25,0,0,278,277,1,0,0,0,279,282,1,0,0,0,280,278,1,0,0,
|
||||
0,280,281,1,0,0,0,281,62,1,0,0,0,282,280,1,0,0,0,283,284,5,91,0,0,284,285,
|
||||
5,93,0,0,285,64,1,0,0,0,286,287,5,91,0,0,287,288,5,42,0,0,288,289,5,93,
|
||||
0,0,289,66,1,0,0,0,290,303,3,61,30,0,291,292,5,46,0,0,292,302,3,61,30,0,
|
||||
293,302,3,63,31,0,294,302,3,65,32,0,295,297,5,46,0,0,296,298,3,71,35,0,
|
||||
297,296,1,0,0,0,298,299,1,0,0,0,299,297,1,0,0,0,299,300,1,0,0,0,300,302,
|
||||
1,0,0,0,301,291,1,0,0,0,301,293,1,0,0,0,301,294,1,0,0,0,301,295,1,0,0,0,
|
||||
302,305,1,0,0,0,303,301,1,0,0,0,303,304,1,0,0,0,304,68,1,0,0,0,305,303,
|
||||
1,0,0,0,306,308,7,26,0,0,307,306,1,0,0,0,308,309,1,0,0,0,309,307,1,0,0,
|
||||
0,309,310,1,0,0,0,310,311,1,0,0,0,311,312,6,34,0,0,312,70,1,0,0,0,313,314,
|
||||
7,27,0,0,314,72,1,0,0,0,315,317,8,28,0,0,316,315,1,0,0,0,317,318,1,0,0,
|
||||
0,318,316,1,0,0,0,318,319,1,0,0,0,319,74,1,0,0,0,29,0,88,131,148,200,205,
|
||||
210,216,219,223,228,230,233,239,243,248,250,252,258,260,268,270,274,280,
|
||||
299,301,303,309,318,1,6,0,0];
|
||||
|
||||
private static __ATN: ATN;
|
||||
public static get _ATN(): ATN {
|
||||
@@ -233,4 +225,4 @@ export default class FilterQueryLexer extends Lexer {
|
||||
|
||||
|
||||
static DecisionsToDFA = FilterQueryLexer._ATN.decisionToState.map( (ds: DecisionState, index: number) => new DFA(ds, index) );
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,25 @@
|
||||
// Generated from grammar/FilterQuery.g4 by ANTLR 4.13.2
|
||||
// Generated from FilterQuery.g4 by ANTLR 4.13.1
|
||||
|
||||
import {ParseTreeListener} from "antlr4";
|
||||
|
||||
|
||||
import { QueryContext } from "./FilterQueryParser.js";
|
||||
import { ExpressionContext } from "./FilterQueryParser.js";
|
||||
import { OrExpressionContext } from "./FilterQueryParser.js";
|
||||
import { AndExpressionContext } from "./FilterQueryParser.js";
|
||||
import { UnaryExpressionContext } from "./FilterQueryParser.js";
|
||||
import { PrimaryContext } from "./FilterQueryParser.js";
|
||||
import { ComparisonContext } from "./FilterQueryParser.js";
|
||||
import { InClauseContext } from "./FilterQueryParser.js";
|
||||
import { NotInClauseContext } from "./FilterQueryParser.js";
|
||||
import { ValueListContext } from "./FilterQueryParser.js";
|
||||
import { FullTextContext } from "./FilterQueryParser.js";
|
||||
import { FunctionCallContext } from "./FilterQueryParser.js";
|
||||
import { SearchCallContext } from "./FilterQueryParser.js";
|
||||
import { FunctionParamListContext } from "./FilterQueryParser.js";
|
||||
import { FunctionParamContext } from "./FilterQueryParser.js";
|
||||
import { ArrayContext } from "./FilterQueryParser.js";
|
||||
import { ValueContext } from "./FilterQueryParser.js";
|
||||
import { KeyContext } from "./FilterQueryParser.js";
|
||||
import { FieldContext } from "./FilterQueryParser.js";
|
||||
import { ExactCallContext } from "./FilterQueryParser.js";
|
||||
import { QueryContext } from "./FilterQueryParser";
|
||||
import { ExpressionContext } from "./FilterQueryParser";
|
||||
import { OrExpressionContext } from "./FilterQueryParser";
|
||||
import { AndExpressionContext } from "./FilterQueryParser";
|
||||
import { UnaryExpressionContext } from "./FilterQueryParser";
|
||||
import { PrimaryContext } from "./FilterQueryParser";
|
||||
import { ComparisonContext } from "./FilterQueryParser";
|
||||
import { InClauseContext } from "./FilterQueryParser";
|
||||
import { NotInClauseContext } from "./FilterQueryParser";
|
||||
import { ValueListContext } from "./FilterQueryParser";
|
||||
import { FullTextContext } from "./FilterQueryParser";
|
||||
import { FunctionCallContext } from "./FilterQueryParser";
|
||||
import { FunctionParamListContext } from "./FilterQueryParser";
|
||||
import { FunctionParamContext } from "./FilterQueryParser";
|
||||
import { ArrayContext } from "./FilterQueryParser";
|
||||
import { ValueContext } from "./FilterQueryParser";
|
||||
import { KeyContext } from "./FilterQueryParser";
|
||||
|
||||
|
||||
/**
|
||||
@@ -150,16 +147,6 @@ export default class FilterQueryListener extends ParseTreeListener {
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitFunctionCall?: (ctx: FunctionCallContext) => void;
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.searchCall`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterSearchCall?: (ctx: SearchCallContext) => void;
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.searchCall`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitSearchCall?: (ctx: SearchCallContext) => void;
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.functionParamList`.
|
||||
* @param ctx the parse tree
|
||||
@@ -210,25 +197,5 @@ export default class FilterQueryListener extends ParseTreeListener {
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitKey?: (ctx: KeyContext) => void;
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.field`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterField?: (ctx: FieldContext) => void;
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.field`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitField?: (ctx: FieldContext) => void;
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.exactCall`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterExactCall?: (ctx: ExactCallContext) => void;
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.exactCall`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitExactCall?: (ctx: ExactCallContext) => void;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,28 +1,25 @@
|
||||
// Generated from grammar/FilterQuery.g4 by ANTLR 4.13.2
|
||||
// Generated from FilterQuery.g4 by ANTLR 4.13.1
|
||||
|
||||
import {ParseTreeVisitor} from 'antlr4';
|
||||
|
||||
|
||||
import { QueryContext } from "./FilterQueryParser.js";
|
||||
import { ExpressionContext } from "./FilterQueryParser.js";
|
||||
import { OrExpressionContext } from "./FilterQueryParser.js";
|
||||
import { AndExpressionContext } from "./FilterQueryParser.js";
|
||||
import { UnaryExpressionContext } from "./FilterQueryParser.js";
|
||||
import { PrimaryContext } from "./FilterQueryParser.js";
|
||||
import { ComparisonContext } from "./FilterQueryParser.js";
|
||||
import { InClauseContext } from "./FilterQueryParser.js";
|
||||
import { NotInClauseContext } from "./FilterQueryParser.js";
|
||||
import { ValueListContext } from "./FilterQueryParser.js";
|
||||
import { FullTextContext } from "./FilterQueryParser.js";
|
||||
import { FunctionCallContext } from "./FilterQueryParser.js";
|
||||
import { SearchCallContext } from "./FilterQueryParser.js";
|
||||
import { FunctionParamListContext } from "./FilterQueryParser.js";
|
||||
import { FunctionParamContext } from "./FilterQueryParser.js";
|
||||
import { ArrayContext } from "./FilterQueryParser.js";
|
||||
import { ValueContext } from "./FilterQueryParser.js";
|
||||
import { KeyContext } from "./FilterQueryParser.js";
|
||||
import { FieldContext } from "./FilterQueryParser.js";
|
||||
import { ExactCallContext } from "./FilterQueryParser.js";
|
||||
import { QueryContext } from "./FilterQueryParser";
|
||||
import { ExpressionContext } from "./FilterQueryParser";
|
||||
import { OrExpressionContext } from "./FilterQueryParser";
|
||||
import { AndExpressionContext } from "./FilterQueryParser";
|
||||
import { UnaryExpressionContext } from "./FilterQueryParser";
|
||||
import { PrimaryContext } from "./FilterQueryParser";
|
||||
import { ComparisonContext } from "./FilterQueryParser";
|
||||
import { InClauseContext } from "./FilterQueryParser";
|
||||
import { NotInClauseContext } from "./FilterQueryParser";
|
||||
import { ValueListContext } from "./FilterQueryParser";
|
||||
import { FullTextContext } from "./FilterQueryParser";
|
||||
import { FunctionCallContext } from "./FilterQueryParser";
|
||||
import { FunctionParamListContext } from "./FilterQueryParser";
|
||||
import { FunctionParamContext } from "./FilterQueryParser";
|
||||
import { ArrayContext } from "./FilterQueryParser";
|
||||
import { ValueContext } from "./FilterQueryParser";
|
||||
import { KeyContext } from "./FilterQueryParser";
|
||||
|
||||
|
||||
/**
|
||||
@@ -105,12 +102,6 @@ export default class FilterQueryVisitor<Result> extends ParseTreeVisitor<Result>
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitFunctionCall?: (ctx: FunctionCallContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.searchCall`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitSearchCall?: (ctx: SearchCallContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.functionParamList`.
|
||||
* @param ctx the parse tree
|
||||
@@ -141,17 +132,5 @@ export default class FilterQueryVisitor<Result> extends ParseTreeVisitor<Result>
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitKey?: (ctx: KeyContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.field`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitField?: (ctx: FieldContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.exactCall`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitExactCall?: (ctx: ExactCallContext) => Result;
|
||||
}
|
||||
|
||||
|
||||
@@ -223,12 +223,6 @@
|
||||
padding-left: 6px !important;
|
||||
}
|
||||
|
||||
&__label {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
&__pinned-icon {
|
||||
flex-shrink: 0;
|
||||
color: var(--text-robin-400);
|
||||
|
||||
@@ -67,7 +67,6 @@ export interface PrettyViewProps {
|
||||
*/
|
||||
pinnedFieldsValue?: string[];
|
||||
onPinnedFieldsChange?: (next: string[]) => void;
|
||||
labelSuffixRenderer?: (fieldKey: string) => React.ReactNode;
|
||||
}
|
||||
|
||||
function PrettyView({
|
||||
@@ -79,7 +78,6 @@ function PrettyView({
|
||||
drawerKey = 'default',
|
||||
pinnedFieldsValue,
|
||||
onPinnedFieldsChange,
|
||||
labelSuffixRenderer,
|
||||
}: PrettyViewProps): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const [, setCopy] = useCopyToClipboard();
|
||||
@@ -307,24 +305,10 @@ function PrettyView({
|
||||
}}
|
||||
/>
|
||||
<span>{displayKey}</span>
|
||||
{labelSuffixRenderer?.(displayKey)}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
[togglePin, pinnedEntries, labelSuffixRenderer],
|
||||
);
|
||||
|
||||
const labelRenderer = useCallback(
|
||||
(keyPath: KeyPath): React.ReactNode => {
|
||||
const displayKey = String(keyPath[0]);
|
||||
return (
|
||||
<span className="pretty-view__label">
|
||||
<span>{displayKey}</span>
|
||||
{labelSuffixRenderer?.(displayKey)}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
[labelSuffixRenderer],
|
||||
[togglePin, pinnedEntries],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -367,7 +351,6 @@ function PrettyView({
|
||||
shouldExpandNodeInitially={shouldExpandNodeInitially}
|
||||
valueRenderer={valueRenderer}
|
||||
getItemString={getItemString}
|
||||
labelRenderer={labelRenderer}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
export interface SemconvMigrationReportEntry {
|
||||
current: string;
|
||||
old: string;
|
||||
signal: string;
|
||||
services: string[];
|
||||
resourceSets: number;
|
||||
lastSeenUnixMilli: number;
|
||||
}
|
||||
|
||||
export interface SemconvMigrationReport {
|
||||
startUnixMilli: number;
|
||||
endUnixMilli: number;
|
||||
entries: SemconvMigrationReportEntry[];
|
||||
}
|
||||
@@ -117,8 +117,6 @@ export type SpaceAggregation =
|
||||
|
||||
export type ColumnType = 'group' | 'aggregation';
|
||||
|
||||
export type FieldResolution = 'exact';
|
||||
|
||||
// ===================== Variable Types =====================
|
||||
|
||||
export type VariableType = 'query' | 'dynamic' | 'custom' | 'text';
|
||||
@@ -138,7 +136,6 @@ export interface TelemetryFieldKey {
|
||||
signal?: SignalType;
|
||||
fieldContext?: FieldContext;
|
||||
fieldDataType?: FieldDataType;
|
||||
fieldResolution?: FieldResolution;
|
||||
materialized?: boolean;
|
||||
isIndexed?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import {
|
||||
findOldSemconvNames,
|
||||
getSemconvMembers,
|
||||
getSemconvRename,
|
||||
} from 'utils/semconv';
|
||||
|
||||
describe('semantic convention helpers', () => {
|
||||
it('returns the current name for an old attribute', () => {
|
||||
expect(getSemconvRename('deployment.environment')).toMatchObject({
|
||||
old: 'deployment.environment',
|
||||
current: 'deployment.environment.name',
|
||||
});
|
||||
});
|
||||
|
||||
it('finds old names in editor text without matching larger custom names', () => {
|
||||
expect(
|
||||
findOldSemconvNames(
|
||||
"deployment.environment = 'prod' AND custom.db.system.value = 'x'",
|
||||
),
|
||||
).toStrictEqual([
|
||||
expect.objectContaining({
|
||||
old: 'deployment.environment',
|
||||
current: 'deployment.environment.name',
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not warn for current names', () => {
|
||||
expect(
|
||||
findOldSemconvNames('deployment.environment.name = prod'),
|
||||
).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('returns current-first members for compatibility readers', () => {
|
||||
expect(getSemconvMembers('http.request.method')).toStrictEqual([
|
||||
'http.request.method',
|
||||
'http.method',
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,61 +0,0 @@
|
||||
import {
|
||||
SEMCONV_FAMILIES,
|
||||
SemconvFamily,
|
||||
} from 'constants/generated/semconvFamilies.gen';
|
||||
|
||||
export type SemconvRename = {
|
||||
old: string;
|
||||
current: string;
|
||||
family: SemconvFamily;
|
||||
};
|
||||
|
||||
const OLD_NAMES = SEMCONV_FAMILIES.flatMap((family) =>
|
||||
family.old.map((old) => ({ old, current: family.current, family })),
|
||||
);
|
||||
|
||||
const OLD_NAME_INDEX = new Map(OLD_NAMES.map((rename) => [rename.old, rename]));
|
||||
const FAMILY_BY_NAME = new Map(
|
||||
SEMCONV_FAMILIES.flatMap((family) =>
|
||||
[family.current, ...family.old].map((name) => [name, family] as const),
|
||||
),
|
||||
);
|
||||
|
||||
export function getSemconvRename(name: string): SemconvRename | undefined {
|
||||
return OLD_NAME_INDEX.get(name);
|
||||
}
|
||||
|
||||
/** Returns the current name first, followed by every historical spelling. */
|
||||
export function getSemconvMembers(name: string): readonly string[] {
|
||||
const family = FAMILY_BY_NAME.get(name);
|
||||
return family ? [family.current, ...family.old] : [name];
|
||||
}
|
||||
|
||||
export function findOldSemconvNames(text: string): SemconvRename[] {
|
||||
if (!text) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return OLD_NAMES.filter(({ old }) => containsSemconvName(text, old));
|
||||
}
|
||||
|
||||
function containsSemconvName(text: string, name: string): boolean {
|
||||
let offset = 0;
|
||||
while (offset < text.length) {
|
||||
const index = text.indexOf(name, offset);
|
||||
if (index === -1) {
|
||||
return false;
|
||||
}
|
||||
const before = index === 0 ? '' : text[index - 1];
|
||||
const afterIndex = index + name.length;
|
||||
const after = afterIndex === text.length ? '' : text[afterIndex];
|
||||
if (!isSemconvNameCharacter(before) && !isSemconvNameCharacter(after)) {
|
||||
return true;
|
||||
}
|
||||
offset = index + 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isSemconvNameCharacter(value: string): boolean {
|
||||
return /[A-Za-z0-9_.-]/.test(value);
|
||||
}
|
||||
2
go.mod
2
go.mod
@@ -4,7 +4,7 @@ go 1.25.7
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.2
|
||||
github.com/AfterShip/clickhouse-sql-parser v0.5.5
|
||||
github.com/AfterShip/clickhouse-sql-parser v0.5.4
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.44.0
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
github.com/SigNoz/clickhouse-go-mock v0.14.0
|
||||
|
||||
4
go.sum
4
go.sum
@@ -66,8 +66,8 @@ dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
github.com/AfterShip/clickhouse-sql-parser v0.5.5 h1:LCA23yAA4GgF73PoYXb67yzCdC4sXsj4geQz1Oij3U8=
|
||||
github.com/AfterShip/clickhouse-sql-parser v0.5.5/go.mod h1:Qi3qvPTfZb/aFwI5V4WFOahgjsLJa4MzVijIAfwOhDw=
|
||||
github.com/AfterShip/clickhouse-sql-parser v0.5.4 h1:yiCQaMq8EO+dpKdnpP9YYd/ne6MSuOXgsMsNL33NiTI=
|
||||
github.com/AfterShip/clickhouse-sql-parser v0.5.4/go.mod h1:Qi3qvPTfZb/aFwI5V4WFOahgjsLJa4MzVijIAfwOhDw=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=
|
||||
|
||||
@@ -50,30 +50,30 @@ primary
|
||||
* [NOT] BETWEEN, [NOT] IN, [NOT] EXISTS, [NOT] REGEXP, [NOT] CONTAINS, etc.
|
||||
*/
|
||||
comparison
|
||||
: field EQUALS value
|
||||
| field (NOT_EQUALS | NEQ) value
|
||||
| field LT value
|
||||
| field LE value
|
||||
| field GT value
|
||||
| field GE value
|
||||
: key EQUALS value
|
||||
| key (NOT_EQUALS | NEQ) value
|
||||
| key LT value
|
||||
| key LE value
|
||||
| key GT value
|
||||
| key GE value
|
||||
|
||||
| field (LIKE | ILIKE) value
|
||||
| field NOT (LIKE | ILIKE) value
|
||||
| key (LIKE | ILIKE) value
|
||||
| key NOT (LIKE | ILIKE) value
|
||||
|
||||
| field BETWEEN value AND value
|
||||
| field NOT BETWEEN value AND value
|
||||
| key BETWEEN value AND value
|
||||
| key NOT BETWEEN value AND value
|
||||
|
||||
| field inClause
|
||||
| field notInClause
|
||||
| key inClause
|
||||
| key notInClause
|
||||
|
||||
| field EXISTS
|
||||
| field NOT EXISTS
|
||||
| key EXISTS
|
||||
| key NOT EXISTS
|
||||
|
||||
| field REGEXP value
|
||||
| field NOT REGEXP value
|
||||
| key REGEXP value
|
||||
| key NOT REGEXP value
|
||||
|
||||
| field CONTAINS value
|
||||
| field NOT CONTAINS value
|
||||
| key CONTAINS value
|
||||
| key NOT CONTAINS value
|
||||
;
|
||||
|
||||
// in(...) or in[...]
|
||||
@@ -126,7 +126,7 @@ functionParamList
|
||||
;
|
||||
|
||||
functionParam
|
||||
: field
|
||||
: key
|
||||
| value
|
||||
| array
|
||||
;
|
||||
@@ -155,17 +155,6 @@ key
|
||||
: KEY
|
||||
;
|
||||
|
||||
// exact(key) disables semantic-convention family resolution for this field.
|
||||
// It is deliberately a field wrapper rather than a general function.
|
||||
field
|
||||
: key
|
||||
| exactCall
|
||||
;
|
||||
|
||||
exactCall
|
||||
: EXACT LPAREN key RPAREN
|
||||
;
|
||||
|
||||
|
||||
/*
|
||||
* Lexer Rules
|
||||
@@ -206,7 +195,6 @@ HAS : [Hh][Aa][Ss] ;
|
||||
HASANY : [Hh][Aa][Ss][Aa][Nn][Yy] ;
|
||||
HASALL : [Hh][Aa][Ss][Aa][Ll][Ll] ;
|
||||
SEARCH : [Ss][Ee][Aa][Rr][Cc][Hh] ;
|
||||
EXACT : [Ee][Xx][Aa][Cc][Tt] ;
|
||||
|
||||
// Potential boolean constants
|
||||
BOOL
|
||||
|
||||
@@ -46,23 +46,5 @@ func (provider *provider) addFieldsRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/fields/semconv-migration", handler.New(provider.authzMiddleware.ViewAccess(provider.fieldsHandler.GetSemconvMigrationReport), handler.OpenAPIDef{
|
||||
ID: "GetSemconvMigrationReport",
|
||||
Tags: []string{"fields"},
|
||||
Summary: "Get semantic-convention migration report",
|
||||
Description: "Returns services that still emit old semantic-convention names without the current family name",
|
||||
Request: nil,
|
||||
RequestQuery: new(telemetrytypes.PostableSemconvMigrationReportParams),
|
||||
RequestContentType: "",
|
||||
Response: new(telemetrytypes.GettableSemconvMigrationReport),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/modules/promote"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rawdataexport"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
||||
"github.com/SigNoz/signoz/pkg/modules/serviceaccount"
|
||||
"github.com/SigNoz/signoz/pkg/modules/session"
|
||||
"github.com/SigNoz/signoz/pkg/modules/spanmapper"
|
||||
@@ -76,7 +75,6 @@ type provider struct {
|
||||
rulerHandler ruler.Handler
|
||||
llmPricingRuleHandler llmpricingrule.Handler
|
||||
statsHandler statsreporter.Handler
|
||||
savedViewHandler savedview.Handler
|
||||
}
|
||||
|
||||
func NewFactory(
|
||||
@@ -112,7 +110,6 @@ func NewFactory(
|
||||
traceDetailHandler tracedetail.Handler,
|
||||
rulerHandler ruler.Handler,
|
||||
statsHandler statsreporter.Handler,
|
||||
savedViewHandler savedview.Handler,
|
||||
) factory.ProviderFactory[apiserver.APIServer, apiserver.Config] {
|
||||
return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, providerSettings factory.ProviderSettings, config apiserver.Config) (apiserver.APIServer, error) {
|
||||
return newProvider(
|
||||
@@ -151,7 +148,6 @@ func NewFactory(
|
||||
traceDetailHandler,
|
||||
rulerHandler,
|
||||
statsHandler,
|
||||
savedViewHandler,
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -192,7 +188,6 @@ func newProvider(
|
||||
traceDetailHandler tracedetail.Handler,
|
||||
rulerHandler ruler.Handler,
|
||||
statsHandler statsreporter.Handler,
|
||||
savedViewHandler savedview.Handler,
|
||||
) (apiserver.APIServer, error) {
|
||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/apiserver/signozapiserver")
|
||||
router := mux.NewRouter().UseEncodedPath()
|
||||
@@ -232,7 +227,6 @@ func newProvider(
|
||||
rulerHandler: rulerHandler,
|
||||
llmPricingRuleHandler: llmPricingRuleHandler,
|
||||
statsHandler: statsHandler,
|
||||
savedViewHandler: savedViewHandler,
|
||||
}
|
||||
|
||||
provider.authzMiddleware = middleware.NewAuthZ(settings.Logger(), orgGetter, authzService)
|
||||
@@ -365,10 +359,6 @@ func (provider *provider) AddToRouter(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := provider.addSavedViewRoutes(router); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -51,9 +51,9 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"filter": map[string]any{"expression": "k8s.deployment.name = 'api-service'"},
|
||||
"groupBy": []any{
|
||||
map[string]any{"name": "k8s.pod.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
},
|
||||
"legend": "{{k8s.pod.name}} ({{deployment.environment.name}})",
|
||||
"legend": "{{k8s.pod.name}} ({{deployment.environment}})",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -74,12 +74,12 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
},
|
||||
"evaluation": rolling("15m", "1m"),
|
||||
"notificationSettings": map[string]any{
|
||||
"groupBy": []any{"k8s.pod.name", "deployment.environment.name"},
|
||||
"groupBy": []any{"k8s.pod.name", "deployment.environment"},
|
||||
"renotify": renotify("4h", "firing"),
|
||||
},
|
||||
"labels": map[string]any{"severity": "critical", "team": "platform"},
|
||||
"annotations": map[string]any{
|
||||
"description": "Pod {{$k8s.pod.name}} CPU is at {{$value}} of request in {{$deployment.environment.name}}.",
|
||||
"description": "Pod {{$k8s.pod.name}} CPU is at {{$value}} of request in {{$deployment.environment}}.",
|
||||
"summary": "Pod CPU above {{$threshold}} of request",
|
||||
},
|
||||
},
|
||||
@@ -170,7 +170,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
{
|
||||
Name: "metric_promql",
|
||||
Summary: "Metric threshold PromQL rule",
|
||||
Description: "PromQL expression instead of the builder. Dotted OTEL resource attributes are quoted (\"deployment.environment.name\"). Useful for queries that combine series with group_right or other Prom operators.",
|
||||
Description: "PromQL expression instead of the builder. Dotted OTEL resource attributes are quoted (\"deployment.environment\"). Useful for queries that combine series with group_right or other Prom operators.",
|
||||
Value: map[string]any{
|
||||
"alert": "Kafka consumer group lag above 1000",
|
||||
"alertType": "METRIC_BASED_ALERT",
|
||||
@@ -187,7 +187,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"type": "promql",
|
||||
"spec": map[string]any{
|
||||
"name": "A",
|
||||
"query": "(max by(topic, partition, \"deployment.environment.name\")(kafka_log_end_offset) - on(topic, partition, \"deployment.environment.name\") group_right max by(group, topic, partition, \"deployment.environment.name\")(kafka_consumer_committed_offset)) > 0",
|
||||
"query": "(max by(topic, partition, \"deployment.environment\")(kafka_log_end_offset) - on(topic, partition, \"deployment.environment\") group_right max by(group, topic, partition, \"deployment.environment\")(kafka_consumer_committed_offset)) > 0",
|
||||
"legend": "{{topic}}/{{partition}} ({{group}})",
|
||||
},
|
||||
},
|
||||
@@ -299,9 +299,9 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"filter": map[string]any{"expression": "service.name = 'payments-api' AND severity_text = 'ERROR' AND body CONTAINS 'panic'"},
|
||||
"groupBy": []any{
|
||||
map[string]any{"name": "k8s.pod.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
},
|
||||
"legend": "{{k8s.pod.name}} ({{deployment.environment.name}})",
|
||||
"legend": "{{k8s.pod.name}} ({{deployment.environment}})",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -322,12 +322,12 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
},
|
||||
"evaluation": rolling("5m", "1m"),
|
||||
"notificationSettings": map[string]any{
|
||||
"groupBy": []any{"k8s.pod.name", "deployment.environment.name"},
|
||||
"groupBy": []any{"k8s.pod.name", "deployment.environment"},
|
||||
"renotify": renotify("15m", "firing"),
|
||||
},
|
||||
"labels": map[string]any{"severity": "critical", "team": "payments"},
|
||||
"annotations": map[string]any{
|
||||
"description": "{{$k8s.pod.name}} emitted {{$value}} panic log(s) in {{$deployment.environment.name}}.",
|
||||
"description": "{{$k8s.pod.name}} emitted {{$value}} panic log(s) in {{$deployment.environment}}.",
|
||||
"summary": "Payments service panic",
|
||||
},
|
||||
},
|
||||
@@ -358,7 +358,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"disabled": true,
|
||||
"aggregations": []any{map[string]any{"expression": "count()"}},
|
||||
"filter": map[string]any{"expression": "service.name = 'payments-api' AND severity_text IN ['ERROR', 'FATAL']"},
|
||||
"groupBy": []any{map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"}},
|
||||
"groupBy": []any{map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"}},
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
@@ -370,7 +370,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"disabled": true,
|
||||
"aggregations": []any{map[string]any{"expression": "count()"}},
|
||||
"filter": map[string]any{"expression": "service.name = 'payments-api'"},
|
||||
"groupBy": []any{map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"}},
|
||||
"groupBy": []any{map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"}},
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
@@ -378,7 +378,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"spec": map[string]any{
|
||||
"name": "F1",
|
||||
"expression": "(A / B) * 100",
|
||||
"legend": "{{deployment.environment.name}}",
|
||||
"legend": "{{deployment.environment}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -399,12 +399,12 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
},
|
||||
"evaluation": rolling("5m", "1m"),
|
||||
"notificationSettings": map[string]any{
|
||||
"groupBy": []any{"deployment.environment.name"},
|
||||
"groupBy": []any{"deployment.environment"},
|
||||
"renotify": renotify("30m", "firing"),
|
||||
},
|
||||
"labels": map[string]any{"severity": "critical", "team": "payments"},
|
||||
"annotations": map[string]any{
|
||||
"description": "Error log rate in {{$deployment.environment.name}} is {{$value}}%",
|
||||
"description": "Error log rate in {{$deployment.environment}} is {{$value}}%",
|
||||
"summary": "Payments-api error rate above {{$threshold}}%",
|
||||
},
|
||||
},
|
||||
@@ -669,10 +669,10 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"stepInterval": 60,
|
||||
"disabled": true,
|
||||
"aggregations": []any{map[string]any{"expression": "count()"}},
|
||||
"filter": map[string]any{"expression": "service.name CONTAINS 'api' AND http.response.status_code >= 500"},
|
||||
"filter": map[string]any{"expression": "service.name CONTAINS 'api' AND http.status_code >= 500"},
|
||||
"groupBy": []any{
|
||||
map[string]any{"name": "service.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -687,7 +687,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"filter": map[string]any{"expression": "service.name CONTAINS 'api'"},
|
||||
"groupBy": []any{
|
||||
map[string]any{"name": "service.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -696,7 +696,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"spec": map[string]any{
|
||||
"name": "F1",
|
||||
"expression": "(A / B) * 100",
|
||||
"legend": "{{service.name}} ({{deployment.environment.name}})",
|
||||
"legend": "{{service.name}} ({{deployment.environment}})",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -717,14 +717,14 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
},
|
||||
"evaluation": rolling("5m", "1m"),
|
||||
"notificationSettings": map[string]any{
|
||||
"groupBy": []any{"service.name", "deployment.environment.name"},
|
||||
"groupBy": []any{"service.name", "deployment.environment"},
|
||||
"newGroupEvalDelay": "2m",
|
||||
"usePolicy": false,
|
||||
"renotify": renotify("30m", "firing", "nodata"),
|
||||
},
|
||||
"labels": map[string]any{"team": "platform"},
|
||||
"annotations": map[string]any{
|
||||
"description": "{{$service.name}} 5xx rate in {{$deployment.environment.name}} is {{$value}}%.",
|
||||
"description": "{{$service.name}} 5xx rate in {{$deployment.environment}} is {{$value}}%.",
|
||||
"summary": "API service error rate elevated",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
package signozapiserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/http/handler"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/coretypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/savedviewtypes"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (provider *provider) addSavedViewRoutes(router *mux.Router) error {
|
||||
if err := router.Handle("/api/v2/saved_views", handler.New(
|
||||
provider.authzMiddleware.CheckResources(provider.savedViewHandler.ListV2, authtypes.SigNozAdminRoleName, authtypes.SigNozEditorRoleName, authtypes.SigNozViewerRoleName),
|
||||
handler.OpenAPIDef{
|
||||
ID: "ListSavedViews",
|
||||
Tags: []string{"saved_view"},
|
||||
Summary: "List saved views",
|
||||
Description: "Returns saved views, optionally filtered by source and name.",
|
||||
Request: nil,
|
||||
RequestQuery: new(savedviewtypes.ListSavedViewsParams),
|
||||
RequestContentType: "",
|
||||
Response: new([]*savedviewtypes.SavedView),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceSavedView.Scope(coretypes.VerbList)}),
|
||||
},
|
||||
handler.WithResourceDefs(handler.BasicResourceDef{
|
||||
Resource: coretypes.ResourceMetaResourceSavedView,
|
||||
Verb: coretypes.VerbList,
|
||||
Category: coretypes.ActionCategoryDataAccess,
|
||||
Selector: coretypes.WildcardSelector,
|
||||
}),
|
||||
)).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/saved_views", handler.New(
|
||||
provider.authzMiddleware.CheckResources(provider.savedViewHandler.CreateV2, authtypes.SigNozAdminRoleName, authtypes.SigNozEditorRoleName),
|
||||
handler.OpenAPIDef{
|
||||
ID: "CreateSavedView",
|
||||
Tags: []string{"saved_view"},
|
||||
Summary: "Create saved view",
|
||||
Description: "Persists a saved view for the explore page. Returns the id of the created view.",
|
||||
Request: new(savedviewtypes.PostableSavedView),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(types.Identifiable),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceSavedView.Scope(coretypes.VerbCreate)}),
|
||||
},
|
||||
handler.WithResourceDefs(handler.BasicResourceDef{
|
||||
Resource: coretypes.ResourceMetaResourceSavedView,
|
||||
Verb: coretypes.VerbCreate,
|
||||
Category: coretypes.ActionCategoryDataAccess,
|
||||
ID: coretypes.ResponseJSONPath("data.id"),
|
||||
Selector: coretypes.WildcardSelector,
|
||||
}),
|
||||
)).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/saved_views/{id}", handler.New(
|
||||
provider.authzMiddleware.CheckResources(provider.savedViewHandler.GetV2, authtypes.SigNozAdminRoleName, authtypes.SigNozEditorRoleName, authtypes.SigNozViewerRoleName),
|
||||
handler.OpenAPIDef{
|
||||
ID: "GetSavedView",
|
||||
Tags: []string{"saved_view"},
|
||||
Summary: "Get saved view",
|
||||
Description: "Returns a saved view by id.",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(savedviewtypes.SavedView),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceSavedView.Scope(coretypes.VerbRead)}),
|
||||
},
|
||||
handler.WithResourceDefs(handler.BasicResourceDef{
|
||||
Resource: coretypes.ResourceMetaResourceSavedView,
|
||||
Verb: coretypes.VerbRead,
|
||||
Category: coretypes.ActionCategoryDataAccess,
|
||||
ID: coretypes.PathParam("id"),
|
||||
Selector: coretypes.IDSelector,
|
||||
}),
|
||||
)).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/saved_views/{id}", handler.New(
|
||||
provider.authzMiddleware.CheckResources(provider.savedViewHandler.UpdateV2, authtypes.SigNozAdminRoleName, authtypes.SigNozEditorRoleName),
|
||||
handler.OpenAPIDef{
|
||||
ID: "UpdateSavedView",
|
||||
Tags: []string{"saved_view"},
|
||||
Summary: "Update saved view",
|
||||
Description: "Replaces a saved view's name and query.",
|
||||
Request: new(savedviewtypes.UpdatableSavedView),
|
||||
RequestContentType: "application/json",
|
||||
Response: nil,
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceSavedView.Scope(coretypes.VerbUpdate)}),
|
||||
},
|
||||
handler.WithResourceDefs(handler.BasicResourceDef{
|
||||
Resource: coretypes.ResourceMetaResourceSavedView,
|
||||
Verb: coretypes.VerbUpdate,
|
||||
Category: coretypes.ActionCategoryDataAccess,
|
||||
ID: coretypes.PathParam("id"),
|
||||
Selector: coretypes.IDSelector,
|
||||
}),
|
||||
)).Methods(http.MethodPut).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/saved_views/{id}", handler.New(
|
||||
provider.authzMiddleware.CheckResources(provider.savedViewHandler.Delete, authtypes.SigNozAdminRoleName, authtypes.SigNozEditorRoleName),
|
||||
handler.OpenAPIDef{
|
||||
ID: "DeleteSavedView",
|
||||
Tags: []string{"saved_view"},
|
||||
Summary: "Delete saved view",
|
||||
Description: "Deletes a saved view by id.",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: nil,
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceSavedView.Scope(coretypes.VerbDelete)}),
|
||||
},
|
||||
handler.WithResourceDefs(handler.BasicResourceDef{
|
||||
Resource: coretypes.ResourceMetaResourceSavedView,
|
||||
Verb: coretypes.VerbDelete,
|
||||
Category: coretypes.ActionCategoryDataAccess,
|
||||
ID: coretypes.PathParam("id"),
|
||||
Selector: coretypes.IDSelector,
|
||||
}),
|
||||
)).Methods(http.MethodDelete).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"strings"
|
||||
|
||||
parser "github.com/SigNoz/signoz/pkg/parser/filterquery/grammar"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/antlr4-go/antlr/v4"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
@@ -188,35 +187,25 @@ func (r *WhereClauseRewriter) VisitPrimary(ctx *parser.PrimaryContext) any {
|
||||
|
||||
// VisitComparison visits comparison expressions.
|
||||
func (r *WhereClauseRewriter) VisitComparison(ctx *parser.ComparisonContext) any {
|
||||
if ctx.Field() == nil {
|
||||
if ctx.Key() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
field := ctx.Field().GetText()
|
||||
key := field
|
||||
if exactCall := ctx.Field().ExactCall(); exactCall != nil {
|
||||
key = exactCall.Key().GetText()
|
||||
}
|
||||
key := ctx.Key().GetText()
|
||||
r.keysSeen[key] = struct{}{}
|
||||
parsedKey := telemetrytypes.GetFieldKeyFromKeyText(key)
|
||||
r.keysSeen[parsedKey.Name] = struct{}{}
|
||||
labelKey := key
|
||||
if _, exists := r.labels[labelKey]; !exists {
|
||||
labelKey = parsedKey.Name
|
||||
}
|
||||
|
||||
// Check if this key is in the labels and was part of group by
|
||||
if value, exists := r.labels[labelKey]; exists {
|
||||
if _, partOfGroup := r.groupBySet[labelKey]; partOfGroup {
|
||||
if value, exists := r.labels[key]; exists {
|
||||
if _, partOfGroup := r.groupBySet[key]; partOfGroup {
|
||||
// Case 1: Replace with actual value
|
||||
escapedValue := escapeValueIfNeeded(value)
|
||||
fmt.Fprintf(&r.rewritten, "%s=%s", field, escapedValue)
|
||||
fmt.Fprintf(&r.rewritten, "%s=%s", key, escapedValue)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, keep the original comparison
|
||||
r.rewritten.WriteString(field)
|
||||
r.rewritten.WriteString(key)
|
||||
|
||||
if ctx.EQUALS() != nil {
|
||||
r.rewritten.WriteString("=")
|
||||
@@ -419,8 +408,8 @@ func (r *WhereClauseRewriter) VisitFunctionParamList(ctx *parser.FunctionParamLi
|
||||
|
||||
// VisitFunctionParam visits function parameters.
|
||||
func (r *WhereClauseRewriter) VisitFunctionParam(ctx *parser.FunctionParamContext) any {
|
||||
if ctx.Field() != nil {
|
||||
r.rewritten.WriteString(ctx.Field().GetText())
|
||||
if ctx.Key() != nil {
|
||||
ctx.Key().Accept(r)
|
||||
} else if ctx.Value() != nil {
|
||||
ctx.Value().Accept(r)
|
||||
} else if ctx.Array() != nil {
|
||||
|
||||
@@ -234,18 +234,6 @@ func TestPrepareFiltersV5(t *testing.T) {
|
||||
expected: "(error_details EXISTS) AND service.name='serviceA'",
|
||||
description: "Should preserve EXISTS operator",
|
||||
},
|
||||
{
|
||||
name: "exact_field_label_replacement",
|
||||
labels: map[string]string{
|
||||
"deployment.environment": "production",
|
||||
},
|
||||
whereClause: "exact(resource.deployment.environment) = 'staging'",
|
||||
groupByItems: []qbtypes.GroupByKey{
|
||||
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "deployment.environment"}},
|
||||
},
|
||||
expected: "exact(resource.deployment.environment)='production'",
|
||||
description: "Should keep the exact wrapper when replacing a grouped label",
|
||||
},
|
||||
|
||||
{
|
||||
name: "empty_where_clause_with_labels",
|
||||
|
||||
@@ -23,13 +23,13 @@ func TestSource(t *testing.T) {
|
||||
err := json.Unmarshal(buf.Bytes(), &m)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, m, "code.file.path")
|
||||
assert.Contains(t, m, "code.function.name")
|
||||
assert.Contains(t, m, "code.line.number")
|
||||
assert.Contains(t, m, "code.filepath")
|
||||
assert.Contains(t, m, "code.function")
|
||||
assert.Contains(t, m, "code.lineno")
|
||||
|
||||
assert.Contains(t, m["code.file.path"], "source_test.go")
|
||||
assert.Contains(t, m["code.function.name"], "TestSource")
|
||||
assert.NotZero(t, m["code.line.number"])
|
||||
assert.Contains(t, m["code.filepath"], "source_test.go")
|
||||
assert.Contains(t, m["code.function"], "TestSource")
|
||||
assert.NotZero(t, m["code.lineno"])
|
||||
|
||||
// Ensure the nested "source" key is not present.
|
||||
assert.NotContains(t, m, "source")
|
||||
|
||||
@@ -136,12 +136,7 @@ func (v *visitor) VisitPrimary(ctx *grammar.PrimaryContext) any {
|
||||
// predicate; any other identifier is treated as a tag key — the operator
|
||||
// applies to the tag's value, with a case-insensitive match on the tag's key.
|
||||
func (v *visitor) VisitComparison(ctx *grammar.ComparisonContext) any {
|
||||
field := ctx.Field()
|
||||
keyText := field.GetText()
|
||||
if exactCall := field.ExactCall(); exactCall != nil {
|
||||
keyText = exactCall.Key().GetText()
|
||||
}
|
||||
key := strings.ToLower(strings.TrimSpace(keyText))
|
||||
key := strings.ToLower(strings.TrimSpace(ctx.Key().GetText()))
|
||||
|
||||
operation, ok := v.extractOperation(ctx)
|
||||
if !ok {
|
||||
@@ -432,7 +427,7 @@ func (v *visitor) buildFreeTextTerm(value string) string {
|
||||
}
|
||||
|
||||
// buildFreeTextContains emits a case-insensitive contains as
|
||||
// LOWER(COALESCE(col, ”)) LIKE LOWER(?), identical on SQLite and Postgres.
|
||||
// LOWER(COALESCE(col, '')) LIKE LOWER(?), identical on SQLite and Postgres.
|
||||
// COALESCE keeps a NULL column (an absent description) false rather than NULL —
|
||||
// otherwise `NOT (…)` goes NULL and drops every description-less dashboard. The
|
||||
// value's % and _ are escaped, and ESCAPE pins backslash as the escape char.
|
||||
|
||||
@@ -8,7 +8,4 @@ type Handler interface {
|
||||
|
||||
// Gets the fields values for the given field value selector
|
||||
GetFieldsValues(http.ResponseWriter, *http.Request)
|
||||
|
||||
// Gets services that still emit only historical semantic-convention names.
|
||||
GetSemconvMigrationReport(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package implfields
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/http/binding"
|
||||
@@ -17,43 +16,6 @@ type handler struct {
|
||||
telemetryMetadataStore telemetrytypes.MetadataStore
|
||||
}
|
||||
|
||||
func (handler *handler) GetSemconvMigrationReport(rw http.ResponseWriter, req *http.Request) {
|
||||
ctx := req.Context()
|
||||
|
||||
var params telemetrytypes.PostableSemconvMigrationReportParams
|
||||
if err := binding.Query.BindQuery(req.URL.Query(), ¶ms); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
if params.EndUnixMilli == 0 {
|
||||
params.EndUnixMilli = now.UnixMilli()
|
||||
}
|
||||
if params.StartUnixMilli == 0 {
|
||||
params.StartUnixMilli = now.Add(-24 * time.Hour).UnixMilli()
|
||||
}
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
report, err := handler.telemetryMetadataStore.GetSemconvMigrationReport(
|
||||
ctx,
|
||||
valuer.MustNewUUID(claims.OrgID),
|
||||
params.StartUnixMilli,
|
||||
params.EndUnixMilli,
|
||||
)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusOK, report)
|
||||
}
|
||||
|
||||
func NewHandler(settings factory.ProviderSettings, telemetryMetadataStore telemetrytypes.MetadataStore) fields.Handler {
|
||||
return &handler{
|
||||
telemetryMetadataStore: telemetryMetadataStore,
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package metricreductionrule
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/semconv"
|
||||
"github.com/SigNoz/signoz/pkg/types/metricreductionruletypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
var protectedLabels = buildProtectedLabels()
|
||||
|
||||
// ValidatePostableReductionRule validates request structure and protected-label
|
||||
// policy before a rule reaches storage.
|
||||
func ValidatePostableReductionRule(req *metricreductionruletypes.PostableReductionRule) error {
|
||||
if err := req.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
return validateProtectedLabels(req.MatchType, req.Labels)
|
||||
}
|
||||
|
||||
// ValidateUpdatableReductionRule validates request structure and
|
||||
// protected-label policy before a rule reaches storage.
|
||||
func ValidateUpdatableReductionRule(req *metricreductionruletypes.UpdatableReductionRule) error {
|
||||
if err := req.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
return validateProtectedLabels(req.MatchType, req.Labels)
|
||||
}
|
||||
|
||||
// IsProtectedLabel reports whether metric reduction must always retain label.
|
||||
func IsProtectedLabel(label string) bool {
|
||||
_, ok := protectedLabels[label]
|
||||
return ok
|
||||
}
|
||||
|
||||
func buildProtectedLabels() map[string]struct{} {
|
||||
labels := map[string]struct{}{
|
||||
"le": {},
|
||||
"quantile": {},
|
||||
"__name__": {},
|
||||
"__temporality__": {},
|
||||
}
|
||||
selector := telemetrytypes.FieldKeySelector{
|
||||
Name: "deployment.environment.name",
|
||||
Signal: telemetrytypes.SignalMetrics,
|
||||
FieldContext: telemetrytypes.FieldContextResource,
|
||||
}
|
||||
for _, member := range semconv.Members(semconv.KindAttribute, selector) {
|
||||
labels[member] = struct{}{}
|
||||
}
|
||||
return labels
|
||||
}
|
||||
|
||||
func validateProtectedLabels(matchType metricreductionruletypes.MatchType, labels []string) error {
|
||||
if matchType != metricreductionruletypes.MatchTypeDrop {
|
||||
return nil
|
||||
}
|
||||
for _, label := range labels {
|
||||
if IsProtectedLabel(label) {
|
||||
return errors.Newf(errors.TypeInvalidInput, metricreductionruletypes.ErrCodeMetricReductionRuleProtectedLabel,
|
||||
"label %q is protected and cannot be dropped", label)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package metricreductionrule_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/modules/metricreductionrule"
|
||||
"github.com/SigNoz/signoz/pkg/types/metricreductionruletypes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDropRuleRejectsBuiltInProtectedLabel(t *testing.T) {
|
||||
req := &metricreductionruletypes.UpdatableReductionRule{
|
||||
MatchType: metricreductionruletypes.MatchTypeDrop,
|
||||
Labels: []string{"le"},
|
||||
}
|
||||
|
||||
err := metricreductionrule.ValidateUpdatableReductionRule(req)
|
||||
|
||||
assert.Error(t, err, "histogram boundary label must remain protected")
|
||||
}
|
||||
|
||||
func TestDropRuleRejectsCurrentEnvironmentLabel(t *testing.T) {
|
||||
req := &metricreductionruletypes.UpdatableReductionRule{
|
||||
MatchType: metricreductionruletypes.MatchTypeDrop,
|
||||
Labels: []string{"deployment.environment.name"},
|
||||
}
|
||||
|
||||
err := metricreductionrule.ValidateUpdatableReductionRule(req)
|
||||
|
||||
assert.Error(t, err, "current deployment environment label must remain protected")
|
||||
}
|
||||
|
||||
func TestDropRuleRejectsHistoricalEnvironmentLabel(t *testing.T) {
|
||||
req := &metricreductionruletypes.UpdatableReductionRule{
|
||||
MatchType: metricreductionruletypes.MatchTypeDrop,
|
||||
Labels: []string{"deployment.environment"},
|
||||
}
|
||||
|
||||
err := metricreductionrule.ValidateUpdatableReductionRule(req)
|
||||
|
||||
assert.Error(t, err, "historical deployment environment label must remain protected")
|
||||
}
|
||||
|
||||
func TestKeepRuleAllowsProtectedLabel(t *testing.T) {
|
||||
req := &metricreductionruletypes.UpdatableReductionRule{
|
||||
MatchType: metricreductionruletypes.MatchTypeKeep,
|
||||
Labels: []string{"le"},
|
||||
}
|
||||
|
||||
err := metricreductionrule.ValidateUpdatableReductionRule(req)
|
||||
|
||||
assert.NoError(t, err, "a keep rule may retain a protected label")
|
||||
}
|
||||
|
||||
func TestDropRuleAllowsUnprotectedLabel(t *testing.T) {
|
||||
req := &metricreductionruletypes.UpdatableReductionRule{
|
||||
MatchType: metricreductionruletypes.MatchTypeDrop,
|
||||
Labels: []string{"host.name"},
|
||||
}
|
||||
|
||||
err := metricreductionrule.ValidateUpdatableReductionRule(req)
|
||||
|
||||
assert.NoError(t, err, "an ordinary label may be dropped")
|
||||
}
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/savedviewtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
@@ -25,116 +23,6 @@ func NewHandler(module savedview.Module) savedview.Handler {
|
||||
return &handler{module: module}
|
||||
}
|
||||
|
||||
// legacyExtraData mirrors the frontend's extraData JSON shape so /api/v1
|
||||
// responses can synthesize the same shape back for the legacy frontend.
|
||||
type legacyExtraData struct {
|
||||
Color string `json:"color,omitempty"`
|
||||
SelectColumns []telemetrytypes.TelemetryFieldKey `json:"selectColumns,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
MaxLines int `json:"maxLines,omitempty"`
|
||||
FontSize string `json:"fontSize,omitempty"`
|
||||
}
|
||||
|
||||
// newPostableSavedViewFromLegacyView builds a create payload for a v1 request.
|
||||
func newPostableSavedViewFromLegacyView(v *v3.SavedView) savedviewtypes.PostableSavedView {
|
||||
var legacy legacyExtraData
|
||||
if v.ExtraData != "" {
|
||||
// Best-effort: malformed/older extraData shapes never fail the request
|
||||
_ = json.Unmarshal([]byte(v.ExtraData), &legacy)
|
||||
}
|
||||
|
||||
return savedviewtypes.PostableSavedView{
|
||||
GenerateName: true,
|
||||
Source: savedviewtypes.Source{String: valuer.NewString(v.SourcePage)},
|
||||
Data: savedviewtypes.SavedViewData{
|
||||
SchemaVersion: savedviewtypes.SavedViewSchemaVersion,
|
||||
Spec: savedviewtypes.SavedViewSpec{
|
||||
DisplayName: v.Name,
|
||||
PanelType: savedviewtypes.PanelType{String: valuer.NewString(string(v.CompositeQuery.PanelType))},
|
||||
Queries: v.CompositeQuery.Queries,
|
||||
SelectedFields: legacy.SelectColumns,
|
||||
Display: savedviewtypes.Display{
|
||||
MaxLines: legacy.MaxLines,
|
||||
FontSize: legacy.FontSize,
|
||||
Format: legacy.Format,
|
||||
Color: legacy.Color,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// newUpdatableSavedViewFromLegacyView builds an update payload for a v1 request.
|
||||
func newUpdatableSavedViewFromLegacyView(v *v3.SavedView) savedviewtypes.UpdatableSavedView {
|
||||
var legacy legacyExtraData
|
||||
if v.ExtraData != "" {
|
||||
// Best-effort: malformed/older extraData shapes never fail the request
|
||||
_ = json.Unmarshal([]byte(v.ExtraData), &legacy)
|
||||
}
|
||||
|
||||
return savedviewtypes.UpdatableSavedView{
|
||||
Source: savedviewtypes.Source{String: valuer.NewString(v.SourcePage)},
|
||||
Data: savedviewtypes.SavedViewData{
|
||||
SchemaVersion: savedviewtypes.SavedViewSchemaVersion,
|
||||
Spec: savedviewtypes.SavedViewSpec{
|
||||
DisplayName: v.Name,
|
||||
PanelType: savedviewtypes.PanelType{String: valuer.NewString(string(v.CompositeQuery.PanelType))},
|
||||
Queries: v.CompositeQuery.Queries,
|
||||
SelectedFields: legacy.SelectColumns,
|
||||
Display: savedviewtypes.Display{
|
||||
MaxLines: legacy.MaxLines,
|
||||
FontSize: legacy.FontSize,
|
||||
Format: legacy.Format,
|
||||
Color: legacy.Color,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// newLegacyViewFromSavedView renders a v2 SavedView back into the v1 shape.
|
||||
func newLegacyViewFromSavedView(v *savedviewtypes.SavedView) (*v3.SavedView, error) {
|
||||
extraData, err := json.Marshal(legacyExtraData{
|
||||
Color: v.Data.Spec.Display.Color,
|
||||
SelectColumns: v.Data.Spec.SelectedFields,
|
||||
Format: v.Data.Spec.Display.Format,
|
||||
MaxLines: v.Data.Spec.Display.MaxLines,
|
||||
FontSize: v.Data.Spec.Display.FontSize,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "error in marshalling extra data")
|
||||
}
|
||||
|
||||
return &v3.SavedView{
|
||||
ID: v.ID,
|
||||
Name: v.Data.Spec.DisplayName,
|
||||
CreatedAt: v.CreatedAt,
|
||||
CreatedBy: v.CreatedBy,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
UpdatedBy: v.UpdatedBy,
|
||||
SourcePage: v.Source.StringValue(),
|
||||
CompositeQuery: &v3.CompositeQuery{
|
||||
PanelType: v3.PanelType(v.Data.Spec.PanelType.StringValue()),
|
||||
// Saved views are only ever created from the explorer's builder mode.
|
||||
QueryType: v3.QueryTypeBuilder,
|
||||
Queries: v.Data.Spec.Queries,
|
||||
},
|
||||
ExtraData: string(extraData),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newLegacyViewsFromSavedViews(views []*savedviewtypes.SavedView) ([]*v3.SavedView, error) {
|
||||
out := make([]*v3.SavedView, 0, len(views))
|
||||
for _, view := range views {
|
||||
legacyView, err := newLegacyViewFromSavedView(view)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, legacyView)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (handler *handler) Create(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
@@ -156,7 +44,7 @@ func (handler *handler) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
uuid, err := handler.module.CreateView(ctx, claims.OrgID, newPostableSavedViewFromLegacyView(&view))
|
||||
uuid, err := handler.module.CreateView(ctx, claims.OrgID, view)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -175,7 +63,7 @@ func (handler *handler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
viewID := mux.Vars(r)["id"]
|
||||
viewID := mux.Vars(r)["viewId"]
|
||||
viewUUID, err := valuer.NewUUID(viewID)
|
||||
if err != nil {
|
||||
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to parse view id"))
|
||||
@@ -188,13 +76,7 @@ func (handler *handler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
legacyView, err := newLegacyViewFromSavedView(view)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, legacyView)
|
||||
render.Success(w, http.StatusOK, view)
|
||||
}
|
||||
|
||||
func (handler *handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -207,7 +89,7 @@ func (handler *handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
viewID := mux.Vars(r)["id"]
|
||||
viewID := mux.Vars(r)["viewId"]
|
||||
viewUUID, err := valuer.NewUUID(viewID)
|
||||
if err != nil {
|
||||
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to parse view id"))
|
||||
@@ -224,7 +106,7 @@ func (handler *handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = handler.module.UpdateView(ctx, claims.OrgID, viewUUID, newUpdatableSavedViewFromLegacyView(&view))
|
||||
err = handler.module.UpdateView(ctx, claims.OrgID, viewUUID, view)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -243,7 +125,7 @@ func (handler *handler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
viewID := mux.Vars(r)["id"]
|
||||
viewID := mux.Vars(r)["viewId"]
|
||||
viewUUID, err := valuer.NewUUID(viewID)
|
||||
if err != nil {
|
||||
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to parse view id"))
|
||||
@@ -256,7 +138,7 @@ func (handler *handler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
render.Success(w, http.StatusOK, nil)
|
||||
}
|
||||
|
||||
func (handler *handler) List(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -271,18 +153,13 @@ func (handler *handler) List(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
sourcePage := r.URL.Query().Get("sourcePage")
|
||||
name := r.URL.Query().Get("name")
|
||||
category := r.URL.Query().Get("category")
|
||||
|
||||
views, err := handler.module.GetViewsForFilters(r.Context(), claims.OrgID, savedviewtypes.Source{String: valuer.NewString(sourcePage)}, name)
|
||||
queries, err := handler.module.GetViewsForFilters(r.Context(), claims.OrgID, sourcePage, name, category)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
legacyViews, err := newLegacyViewsFromSavedViews(views)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, legacyViews)
|
||||
render.Success(w, http.StatusOK, queries)
|
||||
}
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
package implsavedview
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/savedviewtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testQueries() []qbtypes.QueryEnvelope {
|
||||
return []qbtypes.QueryEnvelope{
|
||||
{
|
||||
Type: qbtypes.QueryTypeBuilder,
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Aggregations: []qbtypes.LogAggregation{{Expression: "count()"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPostableSavedViewFromLegacyView(t *testing.T) {
|
||||
t.Run("all fields carried over", func(t *testing.T) {
|
||||
legacy := &v3.SavedView{
|
||||
Name: "my view",
|
||||
SourcePage: "logs",
|
||||
CompositeQuery: &v3.CompositeQuery{
|
||||
PanelType: v3.PanelTypeGraph,
|
||||
Queries: testQueries(),
|
||||
},
|
||||
ExtraData: `{"color":"blue","selectColumns":[{"name":"service.name"}],"format":"table","maxLines":10,"fontSize":"large"}`,
|
||||
}
|
||||
|
||||
postable := newPostableSavedViewFromLegacyView(legacy)
|
||||
|
||||
assert.Empty(t, postable.Name, "v1 has no slug concept -- name must always be generated")
|
||||
assert.True(t, postable.GenerateName, "v1 has no slug concept -- name must always be generated")
|
||||
assert.Equal(t, "my view", postable.Data.Spec.DisplayName)
|
||||
assert.Equal(t, savedviewtypes.SourceLogs, postable.Source)
|
||||
assert.Equal(t, savedviewtypes.SavedViewSchemaVersion, postable.Data.SchemaVersion)
|
||||
assert.Equal(t, savedviewtypes.PanelTypeGraph, postable.Data.Spec.PanelType)
|
||||
assert.Equal(t, legacy.CompositeQuery.Queries, postable.Data.Spec.Queries)
|
||||
assert.Equal(t, []telemetrytypes.TelemetryFieldKey{{Name: "service.name"}}, postable.Data.Spec.SelectedFields)
|
||||
assert.Equal(t, savedviewtypes.Display{MaxLines: 10, FontSize: "large", Format: "table", Color: "blue"}, postable.Data.Spec.Display)
|
||||
})
|
||||
|
||||
t.Run("empty extra data leaves display and selected fields zero-valued", func(t *testing.T) {
|
||||
legacy := &v3.SavedView{
|
||||
Name: "no extra data",
|
||||
SourcePage: "traces",
|
||||
CompositeQuery: &v3.CompositeQuery{
|
||||
PanelType: v3.PanelTypeTable,
|
||||
Queries: testQueries(),
|
||||
},
|
||||
ExtraData: "",
|
||||
}
|
||||
|
||||
postable := newPostableSavedViewFromLegacyView(legacy)
|
||||
|
||||
assert.Equal(t, savedviewtypes.Display{}, postable.Data.Spec.Display)
|
||||
assert.Nil(t, postable.Data.Spec.SelectedFields)
|
||||
})
|
||||
|
||||
t.Run("malformed extra data is ignored, not an error", func(t *testing.T) {
|
||||
legacy := &v3.SavedView{
|
||||
Name: "malformed extra data",
|
||||
SourcePage: "metrics",
|
||||
CompositeQuery: &v3.CompositeQuery{
|
||||
PanelType: v3.PanelTypeList,
|
||||
Queries: testQueries(),
|
||||
},
|
||||
ExtraData: `{not valid json`,
|
||||
}
|
||||
|
||||
postable := newPostableSavedViewFromLegacyView(legacy)
|
||||
|
||||
assert.Equal(t, "malformed extra data", postable.Data.Spec.DisplayName)
|
||||
assert.Equal(t, savedviewtypes.Display{}, postable.Data.Spec.Display)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewUpdatableSavedViewFromLegacyView(t *testing.T) {
|
||||
legacy := &v3.SavedView{
|
||||
Name: "renamed view",
|
||||
SourcePage: "traces",
|
||||
CompositeQuery: &v3.CompositeQuery{
|
||||
PanelType: v3.PanelTypeTable,
|
||||
Queries: testQueries(),
|
||||
},
|
||||
ExtraData: `{"color":"red"}`,
|
||||
}
|
||||
|
||||
updatable := newUpdatableSavedViewFromLegacyView(legacy)
|
||||
|
||||
assert.Equal(t, "renamed view", updatable.Data.Spec.DisplayName)
|
||||
assert.Equal(t, savedviewtypes.SourceTraces, updatable.Source)
|
||||
}
|
||||
|
||||
func TestNewLegacyViewFromSavedView(t *testing.T) {
|
||||
now := time.Now()
|
||||
savedView := &savedviewtypes.SavedView{
|
||||
Name: "my-view-abc123ef",
|
||||
Source: savedviewtypes.SourceLogs,
|
||||
Data: savedviewtypes.SavedViewData{
|
||||
SchemaVersion: savedviewtypes.SavedViewSchemaVersion,
|
||||
Spec: savedviewtypes.SavedViewSpec{
|
||||
DisplayName: "my view",
|
||||
PanelType: savedviewtypes.PanelTypeGraph,
|
||||
Queries: testQueries(),
|
||||
SelectedFields: []telemetrytypes.TelemetryFieldKey{{Name: "service.name"}},
|
||||
Display: savedviewtypes.Display{MaxLines: 10, FontSize: "large", Format: "table", Color: "blue"},
|
||||
},
|
||||
},
|
||||
}
|
||||
savedView.ID = valuer.GenerateUUID()
|
||||
savedView.CreatedAt = now
|
||||
savedView.UpdatedAt = now
|
||||
savedView.CreatedBy = "creator@signoz.io"
|
||||
savedView.UpdatedBy = "updater@signoz.io"
|
||||
|
||||
legacy, err := newLegacyViewFromSavedView(savedView)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, savedView.ID, legacy.ID)
|
||||
assert.Equal(t, savedView.Data.Spec.DisplayName, legacy.Name)
|
||||
assert.Equal(t, savedView.CreatedAt, legacy.CreatedAt)
|
||||
assert.Equal(t, savedView.CreatedBy, legacy.CreatedBy)
|
||||
assert.Equal(t, savedView.UpdatedAt, legacy.UpdatedAt)
|
||||
assert.Equal(t, savedView.UpdatedBy, legacy.UpdatedBy)
|
||||
assert.Equal(t, "logs", legacy.SourcePage)
|
||||
assert.Equal(t, v3.PanelTypeGraph, legacy.CompositeQuery.PanelType)
|
||||
assert.Equal(t, v3.QueryTypeBuilder, legacy.CompositeQuery.QueryType)
|
||||
assert.Equal(t, savedView.Data.Spec.Queries, legacy.CompositeQuery.Queries)
|
||||
|
||||
var extra legacyExtraData
|
||||
require.NoError(t, json.Unmarshal([]byte(legacy.ExtraData), &extra))
|
||||
assert.Equal(t, "blue", extra.Color)
|
||||
assert.Equal(t, savedView.Data.Spec.SelectedFields, extra.SelectColumns)
|
||||
assert.Equal(t, "table", extra.Format)
|
||||
assert.Equal(t, 10, extra.MaxLines)
|
||||
assert.Equal(t, "large", extra.FontSize)
|
||||
}
|
||||
|
||||
func TestNewLegacyViewsFromSavedViews(t *testing.T) {
|
||||
a := &savedviewtypes.SavedView{Name: "a-slug", Source: savedviewtypes.SourceLogs, Data: savedviewtypes.SavedViewData{Spec: savedviewtypes.SavedViewSpec{DisplayName: "a", PanelType: savedviewtypes.PanelTypeGraph, Queries: testQueries()}}}
|
||||
b := &savedviewtypes.SavedView{Name: "b-slug", Source: savedviewtypes.SourceTraces, Data: savedviewtypes.SavedViewData{Spec: savedviewtypes.SavedViewSpec{DisplayName: "b", PanelType: savedviewtypes.PanelTypeTable, Queries: testQueries()}}}
|
||||
|
||||
legacyViews, err := newLegacyViewsFromSavedViews([]*savedviewtypes.SavedView{a, b})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, legacyViews, 2)
|
||||
assert.Equal(t, "a", legacyViews[0].Name)
|
||||
assert.Equal(t, "b", legacyViews[1].Name)
|
||||
}
|
||||
|
||||
// TestLegacyViewRoundTrip guards the whole v1<->v2 bridge: converting a
|
||||
// SavedView to its legacy shape and back must recover the fields the legacy
|
||||
// frontend round-trips through (displayName, source, panelType, queries,
|
||||
// selectedFields, display) -- these two functions are each other's inverse
|
||||
// on the API surface, so a regression in either should fail this. The internal
|
||||
// slug (Name) is deliberately NOT part of this contract -- v1 never sees it.
|
||||
func TestLegacyViewRoundTrip(t *testing.T) {
|
||||
original := &savedviewtypes.SavedView{
|
||||
Name: "round-trip-abc123ef",
|
||||
Source: savedviewtypes.SourceMetrics,
|
||||
Data: savedviewtypes.SavedViewData{
|
||||
SchemaVersion: savedviewtypes.SavedViewSchemaVersion,
|
||||
Spec: savedviewtypes.SavedViewSpec{
|
||||
DisplayName: "round trip",
|
||||
PanelType: savedviewtypes.PanelTypeTable,
|
||||
Queries: testQueries(),
|
||||
SelectedFields: []telemetrytypes.TelemetryFieldKey{{Name: "service.name"}},
|
||||
Display: savedviewtypes.Display{MaxLines: 5, FontSize: "small", Format: "list", Color: "red"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
legacy, err := newLegacyViewFromSavedView(original)
|
||||
require.NoError(t, err)
|
||||
|
||||
roundTripped := newPostableSavedViewFromLegacyView(legacy)
|
||||
|
||||
assert.Empty(t, roundTripped.Name)
|
||||
assert.True(t, roundTripped.GenerateName)
|
||||
assert.Equal(t, original.Data.Spec.DisplayName, roundTripped.Data.Spec.DisplayName)
|
||||
assert.Equal(t, original.Source, roundTripped.Source)
|
||||
assert.Equal(t, original.Data.Spec.PanelType, roundTripped.Data.Spec.PanelType)
|
||||
assert.Equal(t, original.Data.Spec.Queries, roundTripped.Data.Spec.Queries)
|
||||
assert.Equal(t, original.Data.Spec.SelectedFields, roundTripped.Data.Spec.SelectedFields)
|
||||
assert.Equal(t, original.Data.Spec.Display, roundTripped.Data.Spec.Display)
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
package implsavedview
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/http/binding"
|
||||
"github.com/SigNoz/signoz/pkg/http/render"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/savedviewtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (handler *handler) CreateV2(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var view savedviewtypes.PostableSavedView
|
||||
if err := binding.JSON.BindBody(r.Body, &view, binding.WithDisallowUnknownFields(true)); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
if err := view.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
uuid, err := handler.module.CreateView(ctx, claims.OrgID, view)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusCreated, types.Identifiable{ID: uuid})
|
||||
}
|
||||
|
||||
func (handler *handler) GetV2(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
viewID := mux.Vars(r)["id"]
|
||||
viewUUID, err := valuer.NewUUID(viewID)
|
||||
if err != nil {
|
||||
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to parse view id"))
|
||||
return
|
||||
}
|
||||
|
||||
view, err := handler.module.GetView(ctx, claims.OrgID, viewUUID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, view)
|
||||
}
|
||||
|
||||
func (handler *handler) UpdateV2(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
viewID := mux.Vars(r)["id"]
|
||||
viewUUID, err := valuer.NewUUID(viewID)
|
||||
if err != nil {
|
||||
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to parse view id"))
|
||||
return
|
||||
}
|
||||
var view savedviewtypes.UpdatableSavedView
|
||||
if err := binding.JSON.BindBody(r.Body, &view, binding.WithDisallowUnknownFields(true)); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
if err := view.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = handler.module.UpdateView(ctx, claims.OrgID, viewUUID, view)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (handler *handler) ListV2(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
params := new(savedviewtypes.ListSavedViewsParams)
|
||||
if err := binding.Query.BindQuery(r.URL.Query(), params); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
if err := params.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
queries, err := handler.module.GetViewsForFilters(r.Context(), claims.OrgID, params.Source, params.Name)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, queries)
|
||||
}
|
||||
@@ -2,59 +2,185 @@ package implsavedview
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/savedviewtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type module struct {
|
||||
store savedviewtypes.Store
|
||||
sqlstore sqlstore.SQLStore
|
||||
}
|
||||
|
||||
func NewModule(store savedviewtypes.Store) savedview.Module {
|
||||
return &module{store: store}
|
||||
func NewModule(sqlstore sqlstore.SQLStore) savedview.Module {
|
||||
return &module{sqlstore: sqlstore}
|
||||
}
|
||||
|
||||
func (module *module) GetViewsForFilters(ctx context.Context, orgID string, source savedviewtypes.Source, name string) ([]*savedviewtypes.SavedView, error) {
|
||||
return module.store.List(ctx, orgID, source, name)
|
||||
}
|
||||
|
||||
func (module *module) CreateView(ctx context.Context, orgID string, view savedviewtypes.PostableSavedView) (valuer.UUID, error) {
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
func (module *module) GetViewsForFilters(ctx context.Context, orgID string, sourcePage string, name string, category string) ([]*v3.SavedView, error) {
|
||||
var views []savedviewtypes.SavedView
|
||||
var err error
|
||||
if len(category) == 0 {
|
||||
err = module.sqlstore.BunDB().NewSelect().Model(&views).Where("org_id = ? AND source_page = ? AND name LIKE ?", orgID, sourcePage, "%"+name+"%").Scan(ctx)
|
||||
} else {
|
||||
err = module.sqlstore.BunDB().NewSelect().Model(&views).Where("org_id = ? AND source_page = ? AND category LIKE ? AND name LIKE ?", orgID, sourcePage, "%"+category+"%", "%"+name+"%").Scan(ctx)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "error in getting saved views")
|
||||
}
|
||||
|
||||
var savedViews []*v3.SavedView
|
||||
for _, view := range views {
|
||||
var compositeQuery v3.CompositeQuery
|
||||
err = json.Unmarshal([]byte(view.Data), &compositeQuery)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "error in unmarshalling explorer query data: %s", err.Error())
|
||||
}
|
||||
savedViews = append(savedViews, &v3.SavedView{
|
||||
ID: view.ID,
|
||||
Name: view.Name,
|
||||
CreatedAt: view.CreatedAt,
|
||||
CreatedBy: view.CreatedBy,
|
||||
UpdatedAt: view.UpdatedAt,
|
||||
UpdatedBy: view.UpdatedBy,
|
||||
Tags: strings.Split(view.Tags, ","),
|
||||
SourcePage: view.SourcePage,
|
||||
CompositeQuery: &compositeQuery,
|
||||
ExtraData: view.ExtraData,
|
||||
})
|
||||
}
|
||||
return savedViews, nil
|
||||
}
|
||||
|
||||
func (module *module) CreateView(ctx context.Context, orgID string, view v3.SavedView) (valuer.UUID, error) {
|
||||
data, err := json.Marshal(view.CompositeQuery)
|
||||
if err != nil {
|
||||
return valuer.UUID{}, errors.WrapInternalf(err, errors.CodeInternal, "error in marshalling explorer query data")
|
||||
}
|
||||
|
||||
uuid := valuer.GenerateUUID()
|
||||
createdAt := time.Now()
|
||||
updatedAt := time.Now()
|
||||
|
||||
claims, errv2 := authtypes.ClaimsFromContext(ctx)
|
||||
if errv2 != nil {
|
||||
return valuer.UUID{}, errors.NewInternalf(errors.CodeInternal, "error in getting email from context")
|
||||
}
|
||||
|
||||
dbView := view.ToSavedView(orgID, claims.Email)
|
||||
createBy := claims.Email
|
||||
updatedBy := claims.Email
|
||||
|
||||
if err := module.store.Create(ctx, dbView); err != nil {
|
||||
return valuer.UUID{}, err
|
||||
dbView := savedviewtypes.SavedView{
|
||||
TimeAuditable: types.TimeAuditable{
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
},
|
||||
UserAuditable: types.UserAuditable{
|
||||
CreatedBy: createBy,
|
||||
UpdatedBy: updatedBy,
|
||||
},
|
||||
OrgID: orgID,
|
||||
Identifiable: types.Identifiable{
|
||||
ID: uuid,
|
||||
},
|
||||
Name: view.Name,
|
||||
Category: view.Category,
|
||||
SourcePage: view.SourcePage,
|
||||
Tags: strings.Join(view.Tags, ","),
|
||||
Data: string(data),
|
||||
ExtraData: view.ExtraData,
|
||||
}
|
||||
return dbView.ID, nil
|
||||
}
|
||||
|
||||
func (module *module) GetView(ctx context.Context, orgID string, uuid valuer.UUID) (*savedviewtypes.SavedView, error) {
|
||||
return module.store.Get(ctx, orgID, uuid)
|
||||
}
|
||||
|
||||
func (module *module) UpdateView(ctx context.Context, orgID string, uuid valuer.UUID, view savedviewtypes.UpdatableSavedView) error {
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
_, err = module.sqlstore.BunDB().NewInsert().Model(&dbView).Exec(ctx)
|
||||
if err != nil {
|
||||
return valuer.UUID{}, errors.WrapInternalf(err, errors.CodeInternal, "error in creating saved view")
|
||||
}
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
func (module *module) GetView(ctx context.Context, orgID string, uuid valuer.UUID) (*v3.SavedView, error) {
|
||||
var view savedviewtypes.SavedView
|
||||
err := module.sqlstore.BunDB().NewSelect().Model(&view).Where("org_id = ? AND id = ?", orgID, uuid.StringValue()).Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "error in getting saved view")
|
||||
}
|
||||
|
||||
var compositeQuery v3.CompositeQuery
|
||||
err = json.Unmarshal([]byte(view.Data), &compositeQuery)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "error in unmarshalling explorer query data")
|
||||
}
|
||||
return &v3.SavedView{
|
||||
ID: view.ID,
|
||||
Name: view.Name,
|
||||
Category: view.Category,
|
||||
CreatedAt: view.CreatedAt,
|
||||
CreatedBy: view.CreatedBy,
|
||||
UpdatedAt: view.UpdatedAt,
|
||||
UpdatedBy: view.UpdatedBy,
|
||||
SourcePage: view.SourcePage,
|
||||
Tags: strings.Split(view.Tags, ","),
|
||||
CompositeQuery: &compositeQuery,
|
||||
ExtraData: view.ExtraData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (module *module) UpdateView(ctx context.Context, orgID string, uuid valuer.UUID, view v3.SavedView) error {
|
||||
data, err := json.Marshal(view.CompositeQuery)
|
||||
if err != nil {
|
||||
return errors.WrapInternalf(err, errors.CodeInternal, "error in marshalling explorer query data")
|
||||
}
|
||||
|
||||
claims, errv2 := authtypes.ClaimsFromContext(ctx)
|
||||
if errv2 != nil {
|
||||
return errors.NewInternalf(errors.CodeInternal, "error in getting email from context")
|
||||
}
|
||||
|
||||
return module.store.Update(ctx, view.ToSavedView(uuid, orgID, claims.Email))
|
||||
updatedAt := time.Now()
|
||||
updatedBy := claims.Email
|
||||
|
||||
_, err = module.sqlstore.BunDB().NewUpdate().
|
||||
Model(&savedviewtypes.SavedView{}).
|
||||
Set("updated_at = ?, updated_by = ?, name = ?, category = ?, source_page = ?, tags = ?, data = ?, extra_data = ?",
|
||||
updatedAt, updatedBy, view.Name, view.Category, view.SourcePage, strings.Join(view.Tags, ","), data, view.ExtraData).
|
||||
Where("id = ?", uuid.StringValue()).
|
||||
Where("org_id = ?", orgID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.WrapInternalf(err, errors.CodeInternal, "error in updating saved view")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (module *module) DeleteView(ctx context.Context, orgID string, uuid valuer.UUID) error {
|
||||
return module.store.Delete(ctx, orgID, uuid)
|
||||
_, err := module.sqlstore.BunDB().NewDelete().
|
||||
Model(&savedviewtypes.SavedView{}).
|
||||
Where("id = ?", uuid.StringValue()).
|
||||
Where("org_id = ?", orgID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.WrapInternalf(err, errors.CodeInternal, "error in deleting explorer query")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (module *module) Collect(ctx context.Context, orgID valuer.UUID) (map[string]any, error) {
|
||||
savedViews, err := module.store.List(ctx, orgID.StringValue(), savedviewtypes.Source{}, "")
|
||||
savedViews := []*savedviewtypes.SavedView{}
|
||||
|
||||
err := module.
|
||||
sqlstore.
|
||||
BunDB().
|
||||
NewSelect().
|
||||
Model(&savedViews).
|
||||
Where("org_id = ?", orgID).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,299 +0,0 @@
|
||||
package implsavedview_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
||||
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore/sqlstoretest"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/savedviewtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/savedviewtypes/savedviewtypestest"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func newTestStore() (savedview.Module, *savedviewtypestest.StoreTest) {
|
||||
sqlStore := sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherRegexp)
|
||||
store := implsavedview.NewStore(sqlStore)
|
||||
return implsavedview.NewModule(store), savedviewtypestest.New(store, sqlStore.Mock())
|
||||
}
|
||||
|
||||
func testPostableSavedView(name string, source savedviewtypes.Source) savedviewtypes.PostableSavedView {
|
||||
return savedviewtypes.PostableSavedView{
|
||||
Name: name,
|
||||
Source: source,
|
||||
Data: savedviewtypes.SavedViewData{
|
||||
SchemaVersion: savedviewtypes.SavedViewSchemaVersion,
|
||||
Spec: savedviewtypes.SavedViewSpec{
|
||||
DisplayName: name,
|
||||
PanelType: savedviewtypes.PanelTypeGraph,
|
||||
Queries: []qbtypes.QueryEnvelope{
|
||||
{
|
||||
Type: qbtypes.QueryTypeBuilder,
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Aggregations: []qbtypes.LogAggregation{{Expression: "count()"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
SelectedFields: []telemetrytypes.TelemetryFieldKey{},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testUpdatableSavedView(displayName string, source savedviewtypes.Source) savedviewtypes.UpdatableSavedView {
|
||||
postable := testPostableSavedView(displayName, source)
|
||||
return savedviewtypes.UpdatableSavedView{
|
||||
Source: postable.Source,
|
||||
Data: postable.Data,
|
||||
}
|
||||
}
|
||||
|
||||
func testSavedView(orgID string, id valuer.UUID, updatedBy string, view savedviewtypes.PostableSavedView) *savedviewtypes.SavedView {
|
||||
savedView := view.ToSavedView(orgID, "creator@signoz.io")
|
||||
savedView.ID = id
|
||||
savedView.UpdatedBy = updatedBy
|
||||
return savedView
|
||||
}
|
||||
|
||||
func contextWithClaims(orgID, email string) context.Context {
|
||||
return authtypes.NewContextWithClaims(context.Background(), authtypes.Claims{
|
||||
OrgID: orgID,
|
||||
Email: email,
|
||||
})
|
||||
}
|
||||
|
||||
func TestModule_CreateAndGetView(t *testing.T) {
|
||||
m, st := newTestStore()
|
||||
|
||||
orgID := valuer.GenerateUUID().StringValue()
|
||||
ctx := contextWithClaims(orgID, "creator@signoz.io")
|
||||
view := testPostableSavedView("my view", savedviewtypes.SourceLogs)
|
||||
|
||||
st.ExpectCreate()
|
||||
id, err := m.CreateView(ctx, orgID, view)
|
||||
require.NoError(t, err)
|
||||
require.False(t, id.IsZero())
|
||||
|
||||
stored := testSavedView(orgID, id, "creator@signoz.io", view)
|
||||
st.ExpectGet(orgID, id, stored)
|
||||
got, err := m.GetView(ctx, orgID, id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, id, got.ID)
|
||||
assert.Equal(t, "my view", got.Name)
|
||||
assert.Equal(t, savedviewtypes.SourceLogs, got.Source)
|
||||
assert.Equal(t, "creator@signoz.io", got.CreatedBy)
|
||||
assert.Equal(t, "creator@signoz.io", got.UpdatedBy)
|
||||
assert.Equal(t, savedviewtypes.PanelTypeGraph, got.Data.Spec.PanelType)
|
||||
|
||||
require.NoError(t, st.AssertExpectations())
|
||||
}
|
||||
|
||||
func TestModule_GetView_NotFound(t *testing.T) {
|
||||
m, st := newTestStore()
|
||||
|
||||
orgID := valuer.GenerateUUID().StringValue()
|
||||
id := valuer.GenerateUUID()
|
||||
|
||||
st.ExpectGet(orgID, id, nil)
|
||||
_, err := m.GetView(contextWithClaims(orgID, "someone@signoz.io"), orgID, id)
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Ast(err, errors.TypeNotFound), "expected a not-found error, got %v", err)
|
||||
|
||||
require.NoError(t, st.AssertExpectations())
|
||||
}
|
||||
|
||||
func TestModule_GetView_ScopedToOrg(t *testing.T) {
|
||||
m, st := newTestStore()
|
||||
|
||||
orgB := valuer.GenerateUUID().StringValue()
|
||||
id := valuer.GenerateUUID()
|
||||
|
||||
// The mock only has an expectation for orgB's WHERE clause; a lookup
|
||||
// scoped to org A's real id must not accidentally match it.
|
||||
st.ExpectGet(orgB, id, nil)
|
||||
_, err := m.GetView(contextWithClaims(orgB, "b@signoz.io"), orgB, id)
|
||||
require.Error(t, err, "a view created under org A must not be visible to org B")
|
||||
assert.True(t, errors.Ast(err, errors.TypeNotFound), "expected a not-found error, got %v", err)
|
||||
|
||||
require.NoError(t, st.AssertExpectations())
|
||||
}
|
||||
|
||||
func TestModule_UpdateView(t *testing.T) {
|
||||
m, st := newTestStore()
|
||||
|
||||
orgID := valuer.GenerateUUID().StringValue()
|
||||
id := valuer.GenerateUUID()
|
||||
|
||||
existing := testSavedView(orgID, id, "creator@signoz.io", testPostableSavedView("my-view", savedviewtypes.SourceLogs))
|
||||
existingName := existing.Name
|
||||
|
||||
updated := testUpdatableSavedView("renamed", savedviewtypes.SourceTraces)
|
||||
updated.Data.Spec.PanelType = savedviewtypes.PanelTypeTable
|
||||
|
||||
st.ExpectUpdate(orgID, id, 1)
|
||||
require.NoError(t, m.UpdateView(contextWithClaims(orgID, "updater@signoz.io"), orgID, id, updated))
|
||||
|
||||
stored := testSavedView(orgID, id, "updater@signoz.io", testPostableSavedView("renamed", savedviewtypes.SourceTraces))
|
||||
stored.Name = existingName
|
||||
stored.Data.Spec.PanelType = savedviewtypes.PanelTypeTable
|
||||
st.ExpectGet(orgID, id, stored)
|
||||
got, err := m.GetView(contextWithClaims(orgID, "creator@signoz.io"), orgID, id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, existingName, got.Name, "name must not change on update")
|
||||
assert.Equal(t, "renamed", got.Data.Spec.DisplayName)
|
||||
assert.Equal(t, savedviewtypes.SourceTraces, got.Source)
|
||||
assert.Equal(t, savedviewtypes.PanelTypeTable, got.Data.Spec.PanelType)
|
||||
assert.Equal(t, "updater@signoz.io", got.UpdatedBy)
|
||||
|
||||
require.NoError(t, st.AssertExpectations())
|
||||
}
|
||||
|
||||
func TestModule_UpdateView_NotFound(t *testing.T) {
|
||||
m, st := newTestStore()
|
||||
|
||||
orgID := valuer.GenerateUUID().StringValue()
|
||||
ctx := contextWithClaims(orgID, "someone@signoz.io")
|
||||
id := valuer.GenerateUUID()
|
||||
|
||||
st.ExpectUpdate(orgID, id, 0)
|
||||
err := m.UpdateView(ctx, orgID, id, testUpdatableSavedView("does-not-exist", savedviewtypes.SourceLogs))
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Ast(err, errors.TypeNotFound), "expected a not-found error, got %v", err)
|
||||
|
||||
require.NoError(t, st.AssertExpectations())
|
||||
}
|
||||
|
||||
func TestModule_UpdateView_ScopedToOrg(t *testing.T) {
|
||||
m, st := newTestStore()
|
||||
|
||||
orgB := valuer.GenerateUUID().StringValue()
|
||||
id := valuer.GenerateUUID()
|
||||
|
||||
// Only an Update scoped to orgB's WHERE clause is registered; updating org
|
||||
// A's view while authenticated as org B must not match it.
|
||||
st.ExpectUpdate(orgB, id, 0)
|
||||
err := m.UpdateView(contextWithClaims(orgB, "b@signoz.io"), orgB, id, testUpdatableSavedView("hijacked", savedviewtypes.SourceLogs))
|
||||
require.Error(t, err, "org B must not be able to update org A's view")
|
||||
assert.True(t, errors.Ast(err, errors.TypeNotFound))
|
||||
|
||||
require.NoError(t, st.AssertExpectations())
|
||||
}
|
||||
|
||||
func TestModule_DeleteView(t *testing.T) {
|
||||
m, st := newTestStore()
|
||||
|
||||
orgID := valuer.GenerateUUID().StringValue()
|
||||
ctx := contextWithClaims(orgID, "creator@signoz.io")
|
||||
id := valuer.GenerateUUID()
|
||||
|
||||
st.ExpectDelete(orgID, id, 1)
|
||||
require.NoError(t, m.DeleteView(ctx, orgID, id))
|
||||
|
||||
require.NoError(t, st.AssertExpectations())
|
||||
}
|
||||
|
||||
func TestModule_DeleteView_NotFound(t *testing.T) {
|
||||
m, st := newTestStore()
|
||||
|
||||
orgID := valuer.GenerateUUID().StringValue()
|
||||
ctx := contextWithClaims(orgID, "someone@signoz.io")
|
||||
id := valuer.GenerateUUID()
|
||||
|
||||
st.ExpectDelete(orgID, id, 0)
|
||||
err := m.DeleteView(ctx, orgID, id)
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Ast(err, errors.TypeNotFound), "expected a not-found error, got %v", err)
|
||||
|
||||
require.NoError(t, st.AssertExpectations())
|
||||
}
|
||||
|
||||
func TestModule_DeleteView_ScopedToOrg(t *testing.T) {
|
||||
m, st := newTestStore()
|
||||
|
||||
orgB := valuer.GenerateUUID().StringValue()
|
||||
id := valuer.GenerateUUID()
|
||||
|
||||
st.ExpectDelete(orgB, id, 0)
|
||||
err := m.DeleteView(contextWithClaims(orgB, "b@signoz.io"), orgB, id)
|
||||
require.Error(t, err, "org B must not be able to delete org A's view")
|
||||
assert.True(t, errors.Ast(err, errors.TypeNotFound))
|
||||
|
||||
require.NoError(t, st.AssertExpectations())
|
||||
}
|
||||
|
||||
func TestModule_GetViewsForFilters(t *testing.T) {
|
||||
m, st := newTestStore()
|
||||
|
||||
orgID := valuer.GenerateUUID().StringValue()
|
||||
ctx := contextWithClaims(orgID, "creator@signoz.io")
|
||||
|
||||
logsOverview := testSavedView(orgID, valuer.GenerateUUID(), "creator@signoz.io", testPostableSavedView("logs overview", savedviewtypes.SourceLogs))
|
||||
logsErrors := testSavedView(orgID, valuer.GenerateUUID(), "creator@signoz.io", testPostableSavedView("logs errors", savedviewtypes.SourceLogs))
|
||||
tracesOverview := testSavedView(orgID, valuer.GenerateUUID(), "creator@signoz.io", testPostableSavedView("traces overview", savedviewtypes.SourceTraces))
|
||||
|
||||
t.Run("filters by source page", func(t *testing.T) {
|
||||
st.ExpectList(orgID, []*savedviewtypes.SavedView{logsOverview, logsErrors})
|
||||
views, err := m.GetViewsForFilters(ctx, orgID, savedviewtypes.SourceLogs, "")
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, views, 2)
|
||||
})
|
||||
|
||||
t.Run("filters by name substring", func(t *testing.T) {
|
||||
st.ExpectList(orgID, []*savedviewtypes.SavedView{logsErrors})
|
||||
views, err := m.GetViewsForFilters(ctx, orgID, savedviewtypes.SourceLogs, "errors")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, views, 1)
|
||||
assert.Equal(t, "logs errors", views[0].Name)
|
||||
})
|
||||
|
||||
t.Run("omitted source page returns everything, not nothing", func(t *testing.T) {
|
||||
// Fixes a bug: source used to be an unconditional exact-match
|
||||
// clause, so a zero-value source matched zero rows -- even though
|
||||
// ListSavedViewsParams.Validate() treats a zero Source as valid
|
||||
// ("no filter"). Store.List now only applies the source clause
|
||||
// when it's non-zero.
|
||||
st.ExpectList(orgID, []*savedviewtypes.SavedView{logsOverview, logsErrors, tracesOverview})
|
||||
views, err := m.GetViewsForFilters(ctx, orgID, savedviewtypes.Source{}, "")
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, views, 3)
|
||||
})
|
||||
|
||||
t.Run("scoped to org", func(t *testing.T) {
|
||||
otherOrgID := valuer.GenerateUUID().StringValue()
|
||||
st.ExpectList(otherOrgID, nil)
|
||||
views, err := m.GetViewsForFilters(ctx, otherOrgID, savedviewtypes.SourceLogs, "")
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, views)
|
||||
})
|
||||
|
||||
require.NoError(t, st.AssertExpectations())
|
||||
}
|
||||
|
||||
func TestModule_Collect(t *testing.T) {
|
||||
m, st := newTestStore()
|
||||
|
||||
orgID := valuer.GenerateUUID()
|
||||
|
||||
logsA := testSavedView(orgID.StringValue(), valuer.GenerateUUID(), "creator@signoz.io", testPostableSavedView("logs a", savedviewtypes.SourceLogs))
|
||||
logsB := testSavedView(orgID.StringValue(), valuer.GenerateUUID(), "creator@signoz.io", testPostableSavedView("logs b", savedviewtypes.SourceLogs))
|
||||
tracesA := testSavedView(orgID.StringValue(), valuer.GenerateUUID(), "creator@signoz.io", testPostableSavedView("traces a", savedviewtypes.SourceTraces))
|
||||
|
||||
st.ExpectList(orgID.StringValue(), []*savedviewtypes.SavedView{logsA, logsB, tracesA})
|
||||
stats, err := m.Collect(context.Background(), orgID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(3), stats["savedview.count"])
|
||||
assert.Equal(t, int64(2), stats["savedview.source.logs.count"])
|
||||
assert.Equal(t, int64(1), stats["savedview.source.traces.count"])
|
||||
|
||||
require.NoError(t, st.AssertExpectations())
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package implsavedview
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/types/savedviewtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
sqlstore sqlstore.SQLStore
|
||||
}
|
||||
|
||||
func NewStore(sqlstore sqlstore.SQLStore) savedviewtypes.Store {
|
||||
return &store{sqlstore: sqlstore}
|
||||
}
|
||||
|
||||
func (store *store) Create(ctx context.Context, view *savedviewtypes.SavedView) error {
|
||||
_, err := store.sqlstore.BunDB().NewInsert().Model(view).Exec(ctx)
|
||||
if err != nil {
|
||||
return store.sqlstore.WrapAlreadyExistsErrf(err, errors.CodeAlreadyExists, "saved view with name %s already exists", view.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *store) Get(ctx context.Context, orgID string, id valuer.UUID) (*savedviewtypes.SavedView, error) {
|
||||
var view savedviewtypes.SavedView
|
||||
err := store.sqlstore.BunDB().NewSelect().Model(&view).Where("org_id = ? AND id = ?", orgID, id.StringValue()).Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, store.sqlstore.WrapNotFoundErrf(err, savedviewtypes.ErrCodeSavedViewNotFound, "saved view %s not found", id.StringValue())
|
||||
}
|
||||
|
||||
normalizeSelectedFields(&view)
|
||||
return &view, nil
|
||||
}
|
||||
|
||||
func (store *store) Update(ctx context.Context, view *savedviewtypes.SavedView) error {
|
||||
res, err := store.sqlstore.BunDB().NewUpdate().
|
||||
Model(&savedviewtypes.SavedView{}).
|
||||
Set("updated_at = ?, updated_by = ?, source = ?, data = ?",
|
||||
view.UpdatedAt, view.UpdatedBy, view.Source, view.Data).
|
||||
Where("id = ?", view.ID.StringValue()).
|
||||
Where("org_id = ?", view.OrgID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.WrapInternalf(err, errors.CodeInternal, "error in updating saved view")
|
||||
}
|
||||
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return errors.WrapInternalf(err, errors.CodeInternal, "error in verifying the updated saved view")
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return errors.NewNotFoundf(savedviewtypes.ErrCodeSavedViewNotFound, "saved view %s not found", view.ID.StringValue())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *store) Delete(ctx context.Context, orgID string, id valuer.UUID) error {
|
||||
res, err := store.sqlstore.BunDB().NewDelete().
|
||||
Model(&savedviewtypes.SavedView{}).
|
||||
Where("id = ?", id.StringValue()).
|
||||
Where("org_id = ?", orgID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.WrapInternalf(err, errors.CodeInternal, "error in deleting saved view")
|
||||
}
|
||||
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return errors.WrapInternalf(err, errors.CodeInternal, "error in verifying the deleted saved view")
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return errors.NewNotFoundf(savedviewtypes.ErrCodeSavedViewNotFound, "saved view %s not found", id.StringValue())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *store) List(ctx context.Context, orgID string, source savedviewtypes.Source, name string) ([]*savedviewtypes.SavedView, error) {
|
||||
var views []*savedviewtypes.SavedView
|
||||
q := store.sqlstore.BunDB().NewSelect().Model(&views).
|
||||
Where("org_id = ?", orgID).
|
||||
Where("name LIKE ?", "%"+name+"%")
|
||||
if !source.IsZero() {
|
||||
q = q.Where("source = ?", source)
|
||||
}
|
||||
|
||||
if err := q.Scan(ctx); err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "error in getting saved views")
|
||||
}
|
||||
|
||||
for _, view := range views {
|
||||
normalizeSelectedFields(view)
|
||||
}
|
||||
|
||||
return views, nil
|
||||
}
|
||||
|
||||
// normalizeSelectedFields fixes up a scanned row's nil SelectedFields.
|
||||
func normalizeSelectedFields(view *savedviewtypes.SavedView) {
|
||||
if view.Data.Spec.SelectedFields == nil {
|
||||
view.Data.Spec.SelectedFields = []telemetrytypes.TelemetryFieldKey{}
|
||||
}
|
||||
}
|
||||
@@ -4,19 +4,19 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/statsreporter"
|
||||
"github.com/SigNoz/signoz/pkg/types/savedviewtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type Module interface {
|
||||
GetViewsForFilters(ctx context.Context, orgID string, source savedviewtypes.Source, name string) ([]*savedviewtypes.SavedView, error)
|
||||
GetViewsForFilters(ctx context.Context, orgID string, sourcePage string, name string, category string) ([]*v3.SavedView, error)
|
||||
|
||||
CreateView(ctx context.Context, orgID string, view savedviewtypes.PostableSavedView) (valuer.UUID, error)
|
||||
CreateView(ctx context.Context, orgID string, view v3.SavedView) (valuer.UUID, error)
|
||||
|
||||
GetView(ctx context.Context, orgID string, uuid valuer.UUID) (*savedviewtypes.SavedView, error)
|
||||
GetView(ctx context.Context, orgID string, uuid valuer.UUID) (*v3.SavedView, error)
|
||||
|
||||
UpdateView(ctx context.Context, orgID string, uuid valuer.UUID, view savedviewtypes.UpdatableSavedView) error
|
||||
UpdateView(ctx context.Context, orgID string, uuid valuer.UUID, view v3.SavedView) error
|
||||
|
||||
DeleteView(ctx context.Context, orgID string, uuid valuer.UUID) error
|
||||
|
||||
@@ -33,22 +33,9 @@ type Handler interface {
|
||||
// Updates the saved view
|
||||
Update(http.ResponseWriter, *http.Request)
|
||||
|
||||
// Deletes the saved view. Shared by both API generations -- delete has no
|
||||
// request/response body to reshape.
|
||||
// Deletes the saved view
|
||||
Delete(http.ResponseWriter, *http.Request)
|
||||
|
||||
// Lists the saved views
|
||||
List(http.ResponseWriter, *http.Request)
|
||||
|
||||
// CreateV2 is the /api/v2/saved_views typed-spec variant of Create.
|
||||
CreateV2(http.ResponseWriter, *http.Request)
|
||||
|
||||
// GetV2 is the /api/v2/saved_views typed-spec variant of Get.
|
||||
GetV2(http.ResponseWriter, *http.Request)
|
||||
|
||||
// UpdateV2 is the /api/v2/saved_views typed-spec variant of Update.
|
||||
UpdateV2(http.ResponseWriter, *http.Request)
|
||||
|
||||
// ListV2 is the /api/v2/saved_views typed-spec variant of List.
|
||||
ListV2(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25,13 +25,12 @@ HAS=24
|
||||
HASANY=25
|
||||
HASALL=26
|
||||
SEARCH=27
|
||||
EXACT=28
|
||||
BOOL=29
|
||||
NUMBER=30
|
||||
QUOTED_TEXT=31
|
||||
KEY=32
|
||||
WS=33
|
||||
FREETEXT=34
|
||||
BOOL=28
|
||||
NUMBER=29
|
||||
QUOTED_TEXT=30
|
||||
KEY=31
|
||||
WS=32
|
||||
FREETEXT=33
|
||||
'('=1
|
||||
')'=2
|
||||
'['=3
|
||||
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user