mirror of
https://github.com/SigNoz/signoz.git
synced 2026-09-11 14:00:42 +01:00
Compare commits
1 Commits
promql-rem
...
feat/expos
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5885d2856 |
@@ -20,16 +20,6 @@ You are the Playwright Test Generator for the SigNoz frontend. You take a plan w
|
||||
await expect(page.getByRole('tab', { name: /alert rules/i })).toBeVisible();
|
||||
});
|
||||
```
|
||||
- **Extended fixtures:** For features needing complex setup (seeded data, API calls, cleanup), import from domain-specific fixtures that extend `auth`. See [docs/contributing/tests/e2e.md](../../docs/contributing/tests/e2e.md) for the full pattern.
|
||||
- `fixtures/alerts/alert-rules` — worker-scoped rule list + test-scoped rule factory
|
||||
- `fixtures/alerts/alert-history` — extends alert-rules, adds history fixtures (waits on ruler evaluation)
|
||||
```ts
|
||||
// Alert list tests - need rules, no history
|
||||
import { test, expect } from '../../../fixtures/alerts/alert-rules';
|
||||
|
||||
// Alert history tests - need evaluated history rows
|
||||
import { test, expect } from '../../../fixtures/alerts/alert-history';
|
||||
```
|
||||
- **Test titles:** `TC-NN <short description>` — matches the planner's IDs.
|
||||
- **Self-contained state.** The bootstrap creates a fresh stack with **zero** dashboards / alerts / etc. — never assume pre-existing data. Two cleanup shapes are valid; pick based on the spec size:
|
||||
- **Per-test `try / finally`** — small specs (~ <10 scenarios) where each test owns its data.
|
||||
|
||||
@@ -49,7 +49,6 @@ Don't try to start the stack yourself — it can take ~4 minutes on a cold build
|
||||
- **The list pages render zero-state when the workspace is empty.** Many locators (search input, sort button, `new-dashboard-cta` testid, "All Dashboards" header) are absent in zero-state. A 30s timeout on those usually means the workspace was empty — seed first via `createDashboardViaApi`.
|
||||
- **The "Enter dashboard name…" inline field is a `RequestDashboardBtn` (template-request feedback form), not a create flow.** Tests that try to use it to create a named dashboard will silently no-op. The only UI create paths are the "New dashboard" dropdown → "Create dashboard" (default name "Sample Title", see `DEFAULT_DASHBOARD_TITLE`) or "Import JSON".
|
||||
- **Auth.** `tests/e2e/fixtures/auth.ts` logs in once per worker and caches `storageState` (cookies + localStorage with `AUTH_TOKEN`). For API-driven seeding/cleanup, use `authToken(page)` from `helpers/dashboards.ts` and pass `Authorization: Bearer <token>`. Never re-implement login.
|
||||
- **Extended fixtures.** Domain-specific fixtures extend `auth` and add seeded data. Alerts uses `fixtures/alerts/alert-rules` (worker-scoped rule list, test-scoped factory) and `fixtures/alerts/alert-history` (extends alert-rules, waits on ruler evaluation). See [docs/contributing/tests/e2e.md](../../docs/contributing/tests/e2e.md) for the pattern. When a test fails on missing data, check if it imports the wrong fixture level.
|
||||
- **Ant Design popovers** (sort menu, action menu) are click-toggle. The trigger element is often an inline `<svg>` with a `data-testid` — clicking it opens the popover; clicking it again closes. After selecting an option, the popover auto-closes. If a test interacts with the popover twice, wait for the menu items to be visible explicitly between toggles.
|
||||
- **Artifacts.** Every failed test writes to `tests/e2e/artifacts/results/<test-slug>/` — the `error-context.md` accessibility snapshot is the fastest way to see what the page actually looked like when it failed.
|
||||
- **Type-check.** After edits, run `npx tsc --noEmit -p tests/e2e/tsconfig.json` if it succeeds, or rely on `npx playwright test --list` to validate the spec parses.
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"lsp": true,
|
||||
"experimental": {
|
||||
"disable_paste_summary": true
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,5 @@ Applies to everything in the repo — code, config, workflows.
|
||||
- **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.
|
||||
- **Less is more.** When writing something intended for human consumption, (comment, commit message, reply to prompt) use as few words as possible. Pick every word meticulously to reduce the volume to a strict minimum. Be down to the point. Less is more.
|
||||
|
||||
Language rules build on this one: [`go-comments`](go-comments.md), [`py-comments`](py-comments.md).
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.go"
|
||||
---
|
||||
|
||||
# Contribution guidelines
|
||||
|
||||
- When making Go changes, always ensure they follow the contributing guildelines in [`docs/contributing/go/`](../../docs/contributing/go/).
|
||||
- Look for existing patterns in the codebase for any change before implementing the changes.
|
||||
- If any API contract is modified, generate the OpenAPI specs with `make gen-openapi-specs`.
|
||||
- Always keep the OpenAPI spec generated in a separate commit, so the whole commit can be dropped in case of conflicts during merge. Do not try to resolve conflict in generated files, instead just generate them again.
|
||||
- Avoid breaking function calls unncessarily into multilines for couple of arguments.
|
||||
- Try to keep most computational only logic in types package itself related to a domain type, use modules as the orchestraction layer cordinating different layers and all db queries in store layer. Check the serviceaccount modules for inspiration when confused.
|
||||
- When defining types, keep the structure of file to have any constants and variables first, then exported types and exported methods and then finally the unexported types and methods.
|
||||
- Never import types or other modules in migration files, duplicate the required type or method to keep migration free from changes.
|
||||
- Always run the gofmt tool for formating beforing commiting any changes.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*_test.go"
|
||||
---
|
||||
|
||||
# Go tests
|
||||
|
||||
- **testify + table-driven.** Use `assert` / `require`; prefer table-driven cases. Tests live next to the source file.
|
||||
- **`require` vs `assert`.** `require` for anything the rest of the test cannot proceed without — setup, `require.NoError(t, err)`, nil/length checks before indexing or dereferencing. `assert` for the actual expectations, so one failed check still reports the rest.
|
||||
- **Mock with mockery.** When an interface needs mocking, list it in `.mockery.yml` and run `mockery`; never hand-write mocks. Generated mocks live in the source package's `<pkg>test` sibling (e.g. `resourcestest.NewMockAdapter(t)`).
|
||||
- **Table format.** Declare cases as `testCases := []struct{ name string; ... }` and iterate with `for _, testCase := range testCases { t.Run(testCase.name, ...) }` — the variables are named `testCases` / `testCase`. Case names are PascalCase segments joined by `_`, one segment per aspect (scenario, condition, expectation): `TimestampNotNullNoDefault`, `DropPrimaryKeyConstraint_AlterColumnNullable`, `ForeignKeyConstraint_DoesNotExist_SCreateAndDropConstraintTrue`.
|
||||
- **No hoisted test constants.** When goconst flags a repeated literal in a test, vary the fixture strings across cases instead of hoisting a constant — never introduce a shared const for test data.
|
||||
@@ -2,10 +2,6 @@
|
||||
|
||||
- **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 non repetitive 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 and not the user agent conversation details.
|
||||
- **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.
|
||||
- **Breaking changes can be added in additional information section** if any.
|
||||
- **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.
|
||||
- **Keep the commit body short and human readable** focused on decision made if any. Commit body must not re-iterate the changes done, skip if title is sufficient in conveying the change.
|
||||
- **Use convensional commit format** for commits and PR title.
|
||||
- **Do not amend the commits once pushed.** Always create a new commit once changes are pushed to remote.
|
||||
|
||||
32
.github/CODEOWNERS
vendored
32
.github/CODEOWNERS
vendored
@@ -15,10 +15,6 @@
|
||||
.github @therealpandey
|
||||
go.mod @therealpandey
|
||||
|
||||
# Security
|
||||
|
||||
/SECURITY.md @therealpandey
|
||||
|
||||
# Scaffold Owners
|
||||
|
||||
/pkg/config/ @therealpandey
|
||||
@@ -156,29 +152,39 @@ go.mod @therealpandey
|
||||
|
||||
## Dashboard Types
|
||||
|
||||
/frontend/src/types/api/dashboard/ @SigNoz/pulse-frontend
|
||||
/frontend/src/types/api/widgets/ @SigNoz/pulse-frontend
|
||||
/frontend/src/api/types/dashboard/ @SigNoz/pulse-frontend
|
||||
|
||||
## Widget Card
|
||||
## Dashboard List
|
||||
|
||||
/frontend/src/container/WidgetCard/ @SigNoz/pulse-frontend
|
||||
/frontend/src/pages/DashboardsListPage/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/ListOfDashboard/ @SigNoz/pulse-frontend
|
||||
|
||||
# Dashboard Widget Page
|
||||
|
||||
/frontend/src/pages/DashboardWidget/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/NewWidget/ @SigNoz/pulse-frontend
|
||||
|
||||
## Dashboard Page
|
||||
|
||||
/frontend/src/pages/DashboardPage/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/DashboardContainer/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/GridCardLayout/ @SigNoz/pulse-frontend
|
||||
|
||||
## Public Dashboard Page
|
||||
|
||||
/frontend/src/pages/PublicDashboard/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/PublicDashboardContainer/ @SigNoz/pulse-frontend
|
||||
|
||||
## Dashboard Libs + Components
|
||||
|
||||
/frontend/src/lib/uPlotV2/ @SigNoz/pulse-frontend
|
||||
/frontend/src/lib/visualization/ @SigNoz/pulse-frontend
|
||||
/frontend/src/lib/dashboard/ @SigNoz/pulse-frontend
|
||||
/frontend/src/lib/dashboardVariables/ @SigNoz/pulse-frontend
|
||||
/frontend/src/components/NewSelect/ @SigNoz/pulse-frontend
|
||||
|
||||
## Dashboard Pages
|
||||
|
||||
/frontend/src/pages/DashboardPage/ @SigNoz/pulse-frontend
|
||||
/frontend/src/pages/DashboardsListPage/ @SigNoz/pulse-frontend
|
||||
## Dashboard V2
|
||||
/frontend/src/pages/DashboardPageV2/ @SigNoz/pulse-frontend
|
||||
/frontend/src/pages/DashboardsListPageV2/ @SigNoz/pulse-frontend
|
||||
|
||||
## Infrastructure Monitoring
|
||||
/frontend/src/pages/InfrastructureMonitoring/ @SigNoz/pulse-frontend
|
||||
|
||||
1
.github/workflows/build-enterprise.yaml
vendored
1
.github/workflows/build-enterprise.yaml
vendored
@@ -103,6 +103,7 @@ jobs:
|
||||
-X github.com/SigNoz/signoz/pkg/version.branch=${{ needs.prepare.outputs.branch }}
|
||||
-X github.com/SigNoz/signoz/ee/zeus.url=https://api.signoz.cloud
|
||||
-X github.com/SigNoz/signoz/ee/zeus.deprecatedURL=https://license.signoz.io
|
||||
-X github.com/SigNoz/signoz/ee/query-service/constants.LicenseSignozIo=https://license.signoz.io/api/v1
|
||||
-X github.com/SigNoz/signoz/pkg/analytics.key=9kRrJ7oPCGPEJLF6QjMPLt5bljFhRQBr'
|
||||
DOCKER_BASE_IMAGES: '{"alpine": "alpine:3.20.3"}'
|
||||
DOCKER_DOCKERFILE_PATH: ./cmd/enterprise/Dockerfile.multi-arch
|
||||
|
||||
1
.github/workflows/build-staging.yaml
vendored
1
.github/workflows/build-staging.yaml
vendored
@@ -109,6 +109,7 @@ jobs:
|
||||
-X github.com/SigNoz/signoz/pkg/version.branch=${{ needs.prepare.outputs.branch }}
|
||||
-X github.com/SigNoz/signoz/ee/zeus.url=https://api.staging.signoz.cloud
|
||||
-X github.com/SigNoz/signoz/ee/zeus.deprecatedURL=https://license.staging.signoz.cloud
|
||||
-X github.com/SigNoz/signoz/ee/query-service/constants.LicenseSignozIo=https://license.staging.signoz.cloud/api/v1
|
||||
-X github.com/SigNoz/signoz/pkg/analytics.key=9kRrJ7oPCGPEJLF6QjMPLt5bljFhRQBr'
|
||||
DOCKER_BASE_IMAGES: '{"alpine": "alpine:3.20.3"}'
|
||||
DOCKER_DOCKERFILE_PATH: ./cmd/enterprise/Dockerfile.multi-arch
|
||||
|
||||
4
.github/workflows/integrationci.yaml
vendored
4
.github/workflows/integrationci.yaml
vendored
@@ -50,7 +50,6 @@ jobs:
|
||||
- logspipelines
|
||||
- passwordauthn
|
||||
- preference
|
||||
- quickfilter
|
||||
- querierlogs
|
||||
- queriertraces
|
||||
- queriermetrics
|
||||
@@ -59,12 +58,9 @@ jobs:
|
||||
- querierai
|
||||
- rawexportdata
|
||||
- promqlconformance
|
||||
- promapiconformance
|
||||
- querierauthz
|
||||
- role
|
||||
- rootuser
|
||||
- savedview
|
||||
- semconvfamilies
|
||||
- serviceaccount
|
||||
- spanmapper
|
||||
- querier_json_body
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -232,4 +232,3 @@ pyrightconfig.json
|
||||
# dev
|
||||
.dev/
|
||||
.claude/worktrees/
|
||||
.claude/settings.local.json
|
||||
|
||||
34
Makefile
34
Makefile
@@ -81,13 +81,10 @@ devenv-clickhouse-clean: ## Clean all ClickHouse data from filesystem
|
||||
##############################################################
|
||||
# go commands
|
||||
##############################################################
|
||||
SIGNOZ_SQLSTORE_SQLITE_PATH ?= signoz.db
|
||||
SIGNOZ_APISERVER_ADDRESS ?= 0.0.0.0:8080
|
||||
|
||||
.PHONY: go-run-enterprise
|
||||
go-run-enterprise: ## Runs the enterprise go backend server
|
||||
@SIGNOZ_INSTRUMENTATION_LOGS_LEVEL=debug \
|
||||
SIGNOZ_SQLSTORE_SQLITE_PATH=$(SIGNOZ_SQLSTORE_SQLITE_PATH) \
|
||||
SIGNOZ_SQLSTORE_SQLITE_PATH=signoz.db \
|
||||
SIGNOZ_WEB_ENABLED=false \
|
||||
SIGNOZ_TOKENIZER_JWT_SECRET=secret \
|
||||
SIGNOZ_ALERTMANAGER_PROVIDER=signoz \
|
||||
@@ -104,7 +101,7 @@ go-test: ## Runs go unit tests
|
||||
.PHONY: go-run-community
|
||||
go-run-community: ## Runs the community go backend server
|
||||
@SIGNOZ_INSTRUMENTATION_LOGS_LEVEL=debug \
|
||||
SIGNOZ_SQLSTORE_SQLITE_PATH=$(SIGNOZ_SQLSTORE_SQLITE_PATH) \
|
||||
SIGNOZ_SQLSTORE_SQLITE_PATH=signoz.db \
|
||||
SIGNOZ_WEB_ENABLED=false \
|
||||
SIGNOZ_TOKENIZER_JWT_SECRET=secret \
|
||||
SIGNOZ_ALERTMANAGER_PROVIDER=signoz \
|
||||
@@ -114,28 +111,6 @@ go-run-community: ## Runs the community go backend server
|
||||
go run -race \
|
||||
$(GO_BUILD_CONTEXT_COMMUNITY)/*.go server
|
||||
|
||||
.PHONY: go-stop
|
||||
go-stop: ## Stops the go backend server listening on SIGNOZ_APISERVER_ADDRESS, waiting for it to release every port it holds
|
||||
@PORT=$(lastword $(subst :, ,$(SIGNOZ_APISERVER_ADDRESS))); \
|
||||
PIDS=$$(lsof -ti tcp:$$PORT); \
|
||||
if [ -z "$$PIDS" ]; then \
|
||||
echo "No signoz server running on port $$PORT."; \
|
||||
echo "If it's running on a different port, rerun as: make go-stop SIGNOZ_APISERVER_ADDRESS=host:port"; \
|
||||
exit 0; \
|
||||
fi; \
|
||||
kill $$PIDS 2>/dev/null; \
|
||||
for i in $$(seq 1 10); do \
|
||||
alive=$$(for p in $$PIDS; do kill -0 $$p 2>/dev/null && echo $$p; done); \
|
||||
[ -z "$$alive" ] && break; \
|
||||
sleep 1; \
|
||||
done; \
|
||||
alive=$$(for p in $$PIDS; do kill -0 $$p 2>/dev/null && echo $$p; done); \
|
||||
if [ -n "$$alive" ]; then \
|
||||
echo "Graceful shutdown did not finish in 10s, sending SIGKILL to $$alive"; \
|
||||
kill -9 $$alive 2>/dev/null; \
|
||||
fi; \
|
||||
echo "Stopped signoz server on port $$PORT (pid $$PIDS)"
|
||||
|
||||
.PHONY: go-build-community $(GO_BUILD_ARCHS_COMMUNITY)
|
||||
go-build-community: ## Builds the go backend server for community
|
||||
go-build-community: $(GO_BUILD_ARCHS_COMMUNITY)
|
||||
@@ -266,8 +241,3 @@ semconv-generate: ## Regenerate semantic-convention families for Go and TypeScri
|
||||
gen-mocks:
|
||||
@echo ">> Generating mocks"
|
||||
@mockery --config .mockery.yml
|
||||
|
||||
.PHONY: gen-openapi-specs
|
||||
gen-openapi-specs:
|
||||
@go run cmd/enterprise/*.go generate openapi
|
||||
cd frontend && pnpm generate:api && cd -
|
||||
|
||||
17
SECURITY.md
17
SECURITY.md
@@ -1,26 +1,17 @@
|
||||
# Security Policy
|
||||
|
||||
SigNoz is looking forward to working with security researchers across the world to keep SigNoz and our users safe. If you have found an issue in our systems/applications, please report it to us privately.
|
||||
SigNoz is looking forward to working with security researchers across the world to keep SigNoz and our users safe. If you have found an issue in our systems/applications, please reach out to us.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We always recommend using the latest version of SigNoz to ensure you get all security updates.
|
||||
We always recommend using the latest version of SigNoz to ensure you get all security updates
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you believe you have found a security vulnerability within SigNoz, please let us know right away. We'll try and fix the problem as soon as possible.
|
||||
|
||||
**Do not report vulnerabilities using public GitHub issues, discussions, or pull requests.**
|
||||
**Do not report vulnerabilities using public GitHub issues**. Instead, email <security@signoz.io> with a detailed account of the issue. Please submit one issue per email, this helps us triage vulnerabilities.
|
||||
|
||||
Instead, report it privately through GitHub's private vulnerability reporting:
|
||||
|
||||
1. Go to the [**Security** tab](https://github.com/SigNoz/signoz/security) of this repository.
|
||||
2. Click **Report a vulnerability**, or use [this link](https://github.com/SigNoz/signoz/security/advisories/new).
|
||||
3. Describe the issue with as much detail as you can — affected version, impact, and steps to reproduce help us triage faster. Please submit one report per vulnerability.
|
||||
|
||||
This opens a private advisory visible only to you and the SigNoz maintainers. We'll respond there, keep you updated as we work on a fix, and coordinate disclosure. If the report is valid we'll credit you on the published advisory and request a CVE.
|
||||
|
||||
If you're unable to use GitHub's private reporting, you can email <security@signoz.io> instead.
|
||||
Once we've received your email we'll keep you updated as we fix the vulnerability.
|
||||
|
||||
## Thanks
|
||||
|
||||
|
||||
@@ -96,8 +96,6 @@ func runGenerateAuthz(_ context.Context) error {
|
||||
coretypes.NewResourceRef(coretypes.ResourceServiceAccount).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceRole).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceMetaResourceFactorAPIKey).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceMetaResourceLicense).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceMetaResourceSubscription).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceTelemetryResourceLogs).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceTelemetryResourceTraces).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceTelemetryResourceMetrics).String(): true,
|
||||
|
||||
@@ -44,11 +44,8 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/ruler/signozruler"
|
||||
"github.com/SigNoz/signoz/pkg/signoz"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/subscription"
|
||||
"github.com/SigNoz/signoz/pkg/subscription/noopsubscription"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/version"
|
||||
"github.com/SigNoz/signoz/pkg/zeus"
|
||||
@@ -89,9 +86,6 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
func(_ sqlstore.SQLStore, _ zeus.Zeus, _ organization.Getter, _ analytics.Analytics) factory.ProviderFactory[licensing.Licensing, licensing.Config] {
|
||||
return nooplicensing.NewFactory()
|
||||
},
|
||||
func(_ zeus.Zeus, _ licensing.Licensing) subscription.Subscription {
|
||||
return noopsubscription.New()
|
||||
},
|
||||
signoz.NewEmailingProviderFactories(),
|
||||
signoz.NewCacheProviderFactories(),
|
||||
signoz.NewWebProviderFactories(config.Global),
|
||||
@@ -109,8 +103,8 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
|
||||
return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx), openfgaDataStore, authtypes.NewRegistry()), nil
|
||||
},
|
||||
func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, queryParser queryparser.QueryParser, _ querier.Querier, _ licensing.Licensing, tagModule tag.Module, systemDashboardRegistry dashboardtypes.SystemDashboardRegistry) dashboard.Module {
|
||||
return impldashboard.NewModule(impldashboard.NewStore(store), settings, analytics, orgGetter, queryParser, tagModule, systemDashboardRegistry)
|
||||
func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, queryParser queryparser.QueryParser, _ querier.Querier, _ licensing.Licensing, tagModule tag.Module) dashboard.Module {
|
||||
return impldashboard.NewModule(impldashboard.NewStore(store), settings, analytics, orgGetter, queryParser, tagModule)
|
||||
},
|
||||
func(_ licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config] {
|
||||
return noopgateway.NewProviderFactory()
|
||||
|
||||
@@ -31,6 +31,7 @@ builds:
|
||||
- -X github.com/SigNoz/signoz/pkg/version.branch={{ .Branch }}
|
||||
- -X github.com/SigNoz/signoz/ee/zeus.url=https://api.signoz.cloud
|
||||
- -X github.com/SigNoz/signoz/ee/zeus.deprecatedURL=https://license.signoz.io
|
||||
- -X github.com/SigNoz/signoz/ee/query-service/constants.LicenseSignozIo=https://license.signoz.io/api/v1
|
||||
- -X github.com/SigNoz/signoz/pkg/analytics.key=9kRrJ7oPCGPEJLF6QjMPLt5bljFhRQBr
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
tags:
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
eequerier "github.com/SigNoz/signoz/ee/querier"
|
||||
enterpriseapp "github.com/SigNoz/signoz/ee/query-service/app"
|
||||
eerules "github.com/SigNoz/signoz/ee/query-service/rules"
|
||||
"github.com/SigNoz/signoz/ee/subscription/httpsubscription"
|
||||
enterprisezeus "github.com/SigNoz/signoz/ee/zeus"
|
||||
"github.com/SigNoz/signoz/ee/zeus/httpzeus"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||
@@ -61,11 +60,9 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/ruler/signozruler"
|
||||
"github.com/SigNoz/signoz/pkg/signoz"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/subscription"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/version"
|
||||
"github.com/SigNoz/signoz/pkg/zeus"
|
||||
@@ -105,9 +102,6 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
func(sqlstore sqlstore.SQLStore, zeus zeus.Zeus, orgGetter organization.Getter, analytics analytics.Analytics) factory.ProviderFactory[licensing.Licensing, licensing.Config] {
|
||||
return httplicensing.NewProviderFactory(sqlstore, zeus, orgGetter, analytics)
|
||||
},
|
||||
func(zeus zeus.Zeus, licensing licensing.Licensing) subscription.Subscription {
|
||||
return httpsubscription.New(zeus, licensing)
|
||||
},
|
||||
signoz.NewEmailingProviderFactories(),
|
||||
signoz.NewCacheProviderFactories(),
|
||||
signoz.NewWebProviderFactories(config.Global),
|
||||
@@ -142,8 +136,8 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
}
|
||||
return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx), openfgaDataStore, licensing, onBeforeRoleDelete, authtypes.NewRegistry()), nil
|
||||
},
|
||||
func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, queryParser queryparser.QueryParser, querier querier.Querier, licensing licensing.Licensing, tagModule tag.Module, systemDashboardRegistry dashboardtypes.SystemDashboardRegistry) dashboard.Module {
|
||||
return impldashboard.NewModule(pkgimpldashboard.NewStore(store), settings, analytics, orgGetter, queryParser, querier, licensing, tagModule, systemDashboardRegistry)
|
||||
func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, queryParser queryparser.QueryParser, querier querier.Querier, licensing licensing.Licensing, tagModule tag.Module) dashboard.Module {
|
||||
return impldashboard.NewModule(pkgimpldashboard.NewStore(store), settings, analytics, orgGetter, queryParser, querier, licensing, tagModule)
|
||||
},
|
||||
func(licensing licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config] {
|
||||
return httpgateway.NewProviderFactory(licensing)
|
||||
|
||||
@@ -138,18 +138,6 @@ sqlstore:
|
||||
|
||||
##################### APIServer #####################
|
||||
apiserver:
|
||||
# The TCP address the API server listens on, in the form "host:port".
|
||||
address: 0.0.0.0:8080
|
||||
# Maximum duration for reading an entire request, including the body.
|
||||
read_timeout: 60s
|
||||
# Keep at 0; any value cuts off streaming endpoints (livetail, SSE, export_raw_data).
|
||||
write_timeout: 0
|
||||
# tls:
|
||||
# enabled: true
|
||||
# cert_file: /path/to/server.crt
|
||||
# key_file: /path/to/server.key
|
||||
# # Minimum TLS version: "1.2" or "1.3". Defaults to "1.2".
|
||||
# min_version: "1.2"
|
||||
timeout:
|
||||
# Default request timeout.
|
||||
default: 60s
|
||||
@@ -409,6 +397,7 @@ identn:
|
||||
# headers to use for tokenizer identN resolver
|
||||
headers:
|
||||
- Authorization
|
||||
- Sec-WebSocket-Protocol
|
||||
apikey:
|
||||
# toggle apikey identN
|
||||
enabled: true
|
||||
|
||||
4127
docs/api/openapi.yml
4127
docs/api/openapi.yml
File diff suppressed because it is too large
Load Diff
@@ -83,13 +83,7 @@ This command:
|
||||
|
||||
You should see: `{"status":"ok"}`
|
||||
|
||||
3. Stop it when you're done:
|
||||
```bash
|
||||
make go-stop
|
||||
```
|
||||
|
||||
> 💡 **Tip**: The API server runs at `http://localhost:8080/` by default. You can configure this using `apiserver.address` configuration option. See
|
||||
> [running more than one instance](#how-do-i-run-more-than-one-instance) if you need that for agentic testing.
|
||||
> 💡 **Tip**: The API server runs at `http://localhost:8080/` by default
|
||||
|
||||
### 4. Setting up the Frontend
|
||||
|
||||
@@ -125,36 +119,6 @@ To verify everything is working correctly:
|
||||
3. **Check Backend**: `curl http://localhost:8080/api/v1/health` (should return `{"status":"ok"}`)
|
||||
4. **Check Frontend**: Open `http://localhost:3301` in your browser
|
||||
|
||||
## How do I run more than one instance?
|
||||
|
||||
Handy when you keep several branches checked out as separate git worktrees. Every port
|
||||
and path below is read from the environment, so set them on the `make` call:
|
||||
|
||||
```bash
|
||||
SIGNOZ_APISERVER_ADDRESS=0.0.0.0:8081 \
|
||||
SIGNOZ_SQLSTORE_SQLITE_PATH=/path/to/main/sqlite.db \
|
||||
SIGNOZ_INSTRUMENTATION_METRICS_READERS_PULL_EXPORTER_PROMETHEUS_PORT=9091 \
|
||||
make go-run-community
|
||||
```
|
||||
|
||||
| Variable | Default | Why you'd change it |
|
||||
| --- | --- | --- |
|
||||
| `SIGNOZ_APISERVER_ADDRESS` | `0.0.0.0:8080` | Address the API server listens on |
|
||||
| `SIGNOZ_SQLSTORE_SQLITE_PATH` | `signoz.db` in worktree | To reuse same database |
|
||||
| `SIGNOZ_INSTRUMENTATION_METRICS_READERS_PULL_EXPORTER_PROMETHEUS_PORT` | `9090` | Bound by the Prometheus metrics exporter on startup |
|
||||
|
||||
Point the frontend at whichever backend you want, in `frontend/.env`:
|
||||
|
||||
```env
|
||||
VITE_FRONTEND_API_ENDPOINT=http://localhost:8081
|
||||
```
|
||||
|
||||
Stop an instance using the address it was started on:
|
||||
|
||||
```bash
|
||||
make go-stop SIGNOZ_APISERVER_ADDRESS=0.0.0.0:8081
|
||||
```
|
||||
|
||||
## How to send test data?
|
||||
|
||||
You can now send telemetry data to your local SigNoz instance:
|
||||
|
||||
@@ -349,7 +349,7 @@ func (Step) JSONSchema() (jsonschema.Schema, error) {
|
||||
|
||||
### `oneOf` with a discriminator
|
||||
|
||||
For a sum type whose variants are keyed by a property (e.g. `kind`), expose the variants via `JSONSchemaOneOf()` and add a discriminator. Without it, code generators intersect the variants (`A & B & C`) instead of producing a clean discriminated union (`A | B | C`).
|
||||
For a sum type whose variants are keyed by a property (e.g. `kind`), expose the variants via `JSONSchemaOneOf()` and add a discriminator. Without it, code generators intersect the variants (`A & B & C`) instead of producing a clean discriminated union (`A | B | C`). How to model the sum type itself is covered in [types.md](types.md#sum-types-the-kindspec-envelope) — this section is only about its schema.
|
||||
|
||||
The parent keeps its `JSONSchemaOneOf()` (the `oneOf` itself) and *additionally* tags it via `PrepareJSONSchema` with the `x-signoz-discriminator` extension; `signoz.attachDiscriminators` then promotes that marker to a real OpenAPI 3 `discriminator` (and strips the duplicate parent properties) after reflection.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ For example, the [prometheus](/pkg/prometheus) provider delivers a prometheus en
|
||||
|
||||
- `pkg/prometheus/prometheus.go` - Interface definition
|
||||
- `pkg/prometheus/config.go` - Configuration
|
||||
- `pkg/prometheus/clickhouseprometheusv2/provider.go` - Clickhouse-powered implementation
|
||||
- `pkg/prometheus/clickhouseprometheus/provider.go` - Clickhouse-powered implementation
|
||||
- `pkg/prometheus/prometheustest/provider.go` - Mock implementation
|
||||
|
||||
## How to wire it up?
|
||||
|
||||
@@ -191,7 +191,7 @@ A standalone service only has the `factory.Service` lifecycle i.e it does not se
|
||||
// ... dependencies ...
|
||||
) user.Service {
|
||||
return &service{
|
||||
settings: factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/modules/user"),
|
||||
settings: factory.NewScopedProviderSettings(providerSettings, "go.signoz.io/pkg/modules/user"),
|
||||
// ... dependencies ...
|
||||
stopC: make(chan struct{}),
|
||||
}
|
||||
|
||||
@@ -61,37 +61,31 @@ type Channel struct {
|
||||
|
||||
```go
|
||||
type AuthDomain struct {
|
||||
storableAuthDomain *StorableAuthDomain
|
||||
storableAuthDomainConfig *StorableAuthDomainConfig
|
||||
storableAuthDomain *StorableAuthDomain
|
||||
authDomainConfig *AuthDomainConfig
|
||||
}
|
||||
|
||||
type StorableAuthDomain struct {
|
||||
bun.BaseModel `bun:"table:auth_domain"`
|
||||
types.Identifiable
|
||||
Name string `bun:"name"`
|
||||
Data string `bun:"data"` // StorableAuthDomainConfig serialized as JSON
|
||||
Data string `bun:"data"` // AuthDomainConfig serialized as JSON
|
||||
OrgID valuer.UUID `bun:"org_id"`
|
||||
types.TimeAuditable
|
||||
}
|
||||
|
||||
type PostableAuthDomain struct {
|
||||
Name string `json:"name" required:"true"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Config AuthDomainConfig `json:"config" required:"true"`
|
||||
RoleMapping *RoleMapping `json:"roleMapping"`
|
||||
Config AuthDomainConfig `json:"config"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type UpdatableAuthDomain struct {
|
||||
Enabled bool `json:"enabled"` // Name intentionally absent
|
||||
Config AuthDomainConfig `json:"config" required:"true"`
|
||||
RoleMapping *RoleMapping `json:"roleMapping"`
|
||||
type UpdateableAuthDomain struct {
|
||||
Config AuthDomainConfig `json:"config"` // Name intentionally absent
|
||||
}
|
||||
|
||||
type GettableAuthDomain struct {
|
||||
StorableAuthDomain
|
||||
Enabled bool `json:"enabled"`
|
||||
Config AuthDomainConfig `json:"config"`
|
||||
RoleMapping *RoleMapping `json:"roleMapping"`
|
||||
*StorableAuthDomain
|
||||
*AuthDomainConfig
|
||||
AuthNProviderInfo *AuthNProviderInfo `json:"authNProviderInfo"`
|
||||
}
|
||||
```
|
||||
@@ -99,11 +93,11 @@ type GettableAuthDomain struct {
|
||||
Each flavor exists for a concrete reason:
|
||||
|
||||
- `StorableAuthDomain` stores the typed config as an opaque `Data string` column, so the schema does not need to migrate every time a config field is added.
|
||||
- `PostableAuthDomain` carries the config as a structured object (not a string) for the request; `AuthDomainConfig` is a kind/spec envelope.
|
||||
- `UpdatableAuthDomain` excludes `Name` because a domain's name cannot change after creation.
|
||||
- `PostableAuthDomain` carries the config as a structured object (not a string) for the request.
|
||||
- `UpdateableAuthDomain` excludes `Name` because a domain's name cannot change after creation.
|
||||
- `GettableAuthDomain` adds `AuthNProviderInfo`, which is derived at read time and never persisted.
|
||||
|
||||
The core `AuthDomain` holds the two live halves — `storableAuthDomain` and `storableAuthDomainConfig` — and owns business methods such as `Update(updatable)` and `Patch(patchable)`. Conversions use the `New<Output>From<Input>` form: `NewAuthDomainFromPostableAuthDomain`, `NewAuthDomainFromStorableAuthDomain`, `NewGettableAuthDomainFromAuthDomain`.
|
||||
The core `AuthDomain` holds the two live halves — `storableAuthDomain` and `authDomainConfig` — and owns business methods such as `Update(config)`. Conversions use the `New<Output>From<Input>` form: `NewAuthDomainFromConfig`, `NewAuthDomainFromStorableAuthDomain`, `NewGettableAuthDomainFromAuthDomain`.
|
||||
|
||||
## Sum types: the kind/spec envelope
|
||||
|
||||
|
||||
@@ -9,16 +9,15 @@ change breaks an invariant, flag it and discuss it first.
|
||||
|
||||
---
|
||||
|
||||
## Why the provider looks like this
|
||||
## Why a second provider
|
||||
|
||||
The removed v1 provider served the promql engine through the remote-read
|
||||
protobuf adapter. It fetched every raw sample of a query's union window,
|
||||
serialized all of them, and gave them to the engine. The cost followed the
|
||||
ingested data, not the question. This is how a dashboard of PromQL panels
|
||||
could take an instance down. v2 replaced it after a byte-level parity
|
||||
rollout, and v1 was then deleted.
|
||||
The v1 provider (`pkg/prometheus/clickhouseprometheus`) serves the promql
|
||||
engine through the remote-read protobuf adapter. It fetches every raw sample
|
||||
of a query's union window. It serializes all of them and gives them to the
|
||||
engine. The cost follows the ingested data, not the question. This is how a
|
||||
dashboard of PromQL panels can take an instance down.
|
||||
|
||||
Each query runs in one of two ways. The classifier decides per query:
|
||||
In v2, each query runs in one of two ways. The classifier decides per query:
|
||||
|
||||
- **Transpiled**: ClickHouse evaluates the query. Only final (or near-final)
|
||||
per-group grid arrays come back. The statements use the
|
||||
@@ -31,7 +30,7 @@ Each query runs in one of two ways. The classifier decides per query:
|
||||
lost user. A construct that cannot reproduce engine semantics exactly falls
|
||||
back. It does not approximate.** The conformance suite
|
||||
(`tests/integration/tests/promqlconformance/`) replays Prometheus' own test
|
||||
corpus against the provider. It is the arbiter. The classification golden
|
||||
corpus against both providers. It is the arbiter. The classification golden
|
||||
(`testdata/classification_golden.json`) freezes the route of each corpus
|
||||
expression. The rest of this document is the PromQL-to-SQL story. That
|
||||
mapping is where correctness is won or lost.
|
||||
@@ -264,8 +263,7 @@ per-thread partials scaled memory with the thread count. The slide then
|
||||
combines each slot's at-most-W bucket partials by direct aggregation
|
||||
(`arraySum(arraySlice(...))`). Window sums are added the way the engine adds
|
||||
them. There is no prefix-sum differencing: its large-minus-large
|
||||
cancellation would drift past the conformance tolerance on counter-sized
|
||||
values.
|
||||
cancellation would drift past the shadow tolerance on counter-sized values.
|
||||
This is correct per slot because the bucket union is the exact window
|
||||
multiset, and avg/min/max/sum/count are order-insensitive on a multiset
|
||||
(sum/avg up to summation order; see the float caveat above). A slot with
|
||||
@@ -301,11 +299,8 @@ substituted. One subtlety makes it exact: we write stale markers at absent
|
||||
grid points. Without them, the engine's lookback would resurrect a point
|
||||
from up to `lookback` earlier. The marker encodes "absent here" the way the
|
||||
engine itself encodes it. Units evaluate concurrently. Each unit is one
|
||||
grid statement: the group-key join resolves the matchers, and the samples
|
||||
primary key takes the metric name straight from the selector. Only a
|
||||
selector without a static `__name__` runs the series lookup first, to learn
|
||||
the concrete metric names. A step of 0 is an instant query: a single
|
||||
evaluation at `end`.
|
||||
series lookup plus one grid statement. A step of 0 is an instant query: a
|
||||
single evaluation at `end`.
|
||||
|
||||
A note on the window sliver: when the window is narrower than the step, the
|
||||
grid windows cover only `window/step` of the timeline. A sample in a gap
|
||||
@@ -320,9 +315,8 @@ selectors and `last_over_time` transpile at window < step too.
|
||||
|
||||
## Series lookup
|
||||
|
||||
The engine path resolves matchers once per selector (`selectSeries`); the
|
||||
transpiled path builds the same conditions into its group-key join. Both
|
||||
read the same tables. The series tables hold one row per (fingerprint, bucket)
|
||||
Both paths resolve matchers the same way, once per selector
|
||||
(`selectSeries`). The series tables hold one row per (fingerprint, bucket)
|
||||
at 1h/6h/1d/1w granularities. The shared schema package
|
||||
(`pkg/telemetryschema/metricstelemetryschema`) picks the table whose bucket
|
||||
fits the window. It rounds the window start down to the bucket boundary, so
|
||||
@@ -335,7 +329,7 @@ can carry them.
|
||||
## The engine path
|
||||
|
||||
Queries that do not transpile run in the stock engine over this package's
|
||||
`storage.Querier`. Samples are fetched per
|
||||
`storage.Querier`. This is still not the v1 path. Samples are fetched per
|
||||
selector with the engine's per-selector hints, not the query-wide union
|
||||
window. So `foo / foo offset 1d` reads two narrow windows, not the widest
|
||||
one twice. Instant selectors of subquery-free queries fetch only the last
|
||||
@@ -369,9 +363,9 @@ same predicates as a shard-local semi-join, not a GLOBAL broadcast of the
|
||||
matched set. The temporality filter on every samples statement is a
|
||||
semantic no-op: the matched fingerprints already come from those
|
||||
temporalities. It engages the leading samples primary-key column.
|
||||
Delta-temporality series stay invisible to PromQL here, as they were before
|
||||
v2. To make Delta visible is its own change with its own semantics to
|
||||
design. A Delta stream fed to `rate()`
|
||||
Delta-temporality series stay invisible to PromQL here, exactly as in v1.
|
||||
The rollout gate is parity with v1. To make Delta visible is its own change
|
||||
with its own semantics to design. A Delta stream fed to `rate()`
|
||||
as-if-cumulative would be wrong, not just new.
|
||||
|
||||
## Observability
|
||||
|
||||
@@ -112,41 +112,6 @@ These two folders look similar but mean different things:
|
||||
|
||||
Rule of thumb: if it's a `test.extend` fixture, put it in `fixtures/`. If it's a function you call explicitly (or a constant the function uses), put it in `helpers/`. If it's a static file the helpers read, put it in `testdata/`.
|
||||
|
||||
### Extended fixtures
|
||||
|
||||
For features needing complex setup (API-seeded data, ruler evaluation waits, cleanup), create domain-specific fixtures that extend `auth`. Group them in `fixtures/<domain>/`.
|
||||
|
||||
**Fixture scopes:**
|
||||
- **test scope** — fresh data per test. Use for mutations (edit, delete, rename).
|
||||
- **worker scope** — shared across tests in one worker. Use for read-only data. Worker scope pays the setup cost once per worker instead of once per test.
|
||||
|
||||
**The alerts pattern** (`fixtures/alerts/`) demonstrates extending fixtures:
|
||||
|
||||
```
|
||||
fixtures/alerts/
|
||||
├── alert-rules.ts # extends auth — worker-scoped rule list + test-scoped factory
|
||||
└── alert-history.ts # extends alert-rules — adds history fixtures (waits on ruler)
|
||||
```
|
||||
|
||||
Specs import from the fixture they need:
|
||||
|
||||
```ts
|
||||
// List tests — just need rules, no history
|
||||
import { test, expect } from '../../../fixtures/alerts/alert-rules';
|
||||
|
||||
// History tests — need history rows from ruler evaluation
|
||||
import { test, expect } from '../../../fixtures/alerts/alert-history';
|
||||
```
|
||||
|
||||
**When creating new fixtures:**
|
||||
|
||||
1. **Identify scope** — Will tests mutate the data? If yes, test-scoped. If read-only, worker-scoped.
|
||||
2. **Group by domain** — Put fixtures in `fixtures/<domain>/`. Helpers in `helpers/<domain>/`.
|
||||
3. **Extend existing fixtures** — Chain from `auth` or another fixture to inherit its setup.
|
||||
4. **Handle timeouts** — Worker-scoped fixtures that wait on backend processing need explicit timeouts.
|
||||
5. **Clean up** — Always delete seeded data in the fixture teardown (after `use()`).
|
||||
6. **Extract logic into functions** — Keep the `test.extend()` block lean; move setup/teardown logic to named functions so the extend block reads as a manifest of "what fixtures exist."
|
||||
|
||||
Each spec follows these principles:
|
||||
|
||||
1. **Directory per feature**: `tests/e2e/tests/<feature>/*.spec.ts`. Cross-resource junction concerns (e.g. cascade-delete) go in their own file, not packed into one giant spec.
|
||||
@@ -267,14 +232,11 @@ cd tests/e2e
|
||||
# Single feature dir
|
||||
npx playwright test tests/alerts/ --project=chromium
|
||||
|
||||
# Single sub-area
|
||||
npx playwright test tests/alerts/history/ --project=chromium
|
||||
|
||||
# Single file
|
||||
npx playwright test tests/alerts/page.spec.ts --project=chromium
|
||||
npx playwright test tests/alerts/alerts.spec.ts --project=chromium
|
||||
|
||||
# Single test by title grep
|
||||
npx playwright test --project=chromium -g "AL-01"
|
||||
npx playwright test --project=chromium -g "TC-01"
|
||||
```
|
||||
|
||||
### Iterative modes
|
||||
@@ -308,14 +270,7 @@ yarn test:staging
|
||||
| `SIGNOZ_E2E_PASSWORD` | Admin password. Bootstrap writes the integration-test default. |
|
||||
| `SIGNOZ_E2E_SEEDER_URL` | Seeder HTTP base URL — hit by specs that need per-test telemetry. |
|
||||
|
||||
Precedence in `playwright.config.ts`, lowest to highest: `.env` (user-provided, staging) → `.env.local` (bootstrap-generated, local mode) → whatever is already in `process.env`. The config parses both files itself and only fills in keys the environment does not already define, so exporting a variable always wins:
|
||||
|
||||
```bash
|
||||
# runs against a locally served frontend, not whatever .env.local points at
|
||||
SIGNOZ_E2E_BASE_URL=http://127.0.0.1:3301 pnpm test tests/alerts
|
||||
```
|
||||
|
||||
This is deliberately not `dotenv.config({ override: true })`. That flag makes the *file* beat `process.env`, which silently discarded exported values — including the `SIGNOZ_E2E_BASE_URL` in `pnpm test:staging`, whenever a `.env.local` happened to exist.
|
||||
Loading order in `playwright.config.ts`: `.env` first (user-provided, staging), then `.env.local` with `override: true` (bootstrap-generated, local mode). Anything already set in `process.env` at yarn-test time wins because dotenv doesn't touch vars that are already present.
|
||||
|
||||
### Playwright options
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ func New(store authtypes.AuthNStore, licensing licensing.Licensing, providerSett
|
||||
}
|
||||
|
||||
func (a *AuthN) LoginURL(ctx context.Context, siteURL *url.URL, authDomain *authtypes.AuthDomain) (string, error) {
|
||||
if authDomain.AuthDomainConfig().AuthNProvider != authtypes.AuthNProviderOIDC {
|
||||
return "", errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthDomainMismatch, "domain type is not oidc")
|
||||
}
|
||||
|
||||
_, oauth2Config, err := a.oidcProviderAndoauth2Config(ctx, siteURL, authDomain)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -81,11 +85,6 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
|
||||
return nil, errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||
}
|
||||
|
||||
oidcConfig, err := authDomain.Config().OIDCConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oidcProvider, oauth2Config, err := a.oidcProviderAndoauth2Config(ctx, state.URL, authDomain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -107,14 +106,14 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if claims == nil && oidcConfig.GetUserInfo {
|
||||
if claims == nil && authDomain.AuthDomainConfig().OIDC.GetUserInfo {
|
||||
claims, err = a.claimsFromUserInfo(ctx, oidcProvider, token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
emailClaim, ok := claims[oidcConfig.ClaimMapping.Email].(string)
|
||||
emailClaim, ok := claims[authDomain.AuthDomainConfig().OIDC.ClaimMapping.Email].(string)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "oidc: missing email in claims")
|
||||
}
|
||||
@@ -124,7 +123,7 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
|
||||
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "oidc: failed to parse email").WithAdditional(err.Error())
|
||||
}
|
||||
|
||||
if !oidcConfig.InsecureSkipEmailVerified {
|
||||
if !authDomain.AuthDomainConfig().OIDC.InsecureSkipEmailVerified {
|
||||
emailVerifiedClaim, ok := claims["email_verified"].(bool)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "oidc: missing email_verified in claims")
|
||||
@@ -136,14 +135,14 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
|
||||
}
|
||||
|
||||
name := ""
|
||||
if nameClaim := oidcConfig.ClaimMapping.Name; nameClaim != "" {
|
||||
if nameClaim := authDomain.AuthDomainConfig().OIDC.ClaimMapping.Name; nameClaim != "" {
|
||||
if n, ok := claims[nameClaim].(string); ok {
|
||||
name = n
|
||||
}
|
||||
}
|
||||
|
||||
var groups []string
|
||||
if groupsClaim := oidcConfig.ClaimMapping.Groups; groupsClaim != "" {
|
||||
if groupsClaim := authDomain.AuthDomainConfig().OIDC.ClaimMapping.Groups; groupsClaim != "" {
|
||||
if claimValue, exists := claims[groupsClaim]; exists {
|
||||
switch g := claimValue.(type) {
|
||||
case []any:
|
||||
@@ -162,7 +161,7 @@ func (a *AuthN) HandleCallback(ctx context.Context, query url.Values) (*authtype
|
||||
}
|
||||
|
||||
role := ""
|
||||
if roleClaim := oidcConfig.ClaimMapping.Role; roleClaim != "" {
|
||||
if roleClaim := authDomain.AuthDomainConfig().OIDC.ClaimMapping.Role; roleClaim != "" {
|
||||
if r, ok := claims[roleClaim].(string); ok {
|
||||
role = r
|
||||
}
|
||||
@@ -178,16 +177,11 @@ func (a *AuthN) ProviderInfo(ctx context.Context, authDomain *authtypes.AuthDoma
|
||||
}
|
||||
|
||||
func (a *AuthN) oidcProviderAndoauth2Config(ctx context.Context, siteURL *url.URL, authDomain *authtypes.AuthDomain) (*oidc.Provider, *oauth2.Config, error) {
|
||||
oidcConfig, err := authDomain.Config().OIDCConfig()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
if authDomain.AuthDomainConfig().OIDC.IssuerAlias != "" {
|
||||
ctx = oidc.InsecureIssuerURLContext(ctx, authDomain.AuthDomainConfig().OIDC.IssuerAlias)
|
||||
}
|
||||
|
||||
if oidcConfig.IssuerAlias != "" {
|
||||
ctx = oidc.InsecureIssuerURLContext(ctx, oidcConfig.IssuerAlias)
|
||||
}
|
||||
|
||||
oidcProvider, err := oidc.NewProvider(ctx, oidcConfig.Issuer)
|
||||
oidcProvider, err := oidc.NewProvider(ctx, authDomain.AuthDomainConfig().OIDC.Issuer)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -195,13 +189,13 @@ func (a *AuthN) oidcProviderAndoauth2Config(ctx context.Context, siteURL *url.UR
|
||||
scopes := make([]string, len(defaultScopes))
|
||||
copy(scopes, defaultScopes)
|
||||
|
||||
if authDomain.RoleMapping() != nil && len(authDomain.RoleMapping().GroupMappings) > 0 {
|
||||
if authDomain.AuthDomainConfig().RoleMapping != nil && len(authDomain.AuthDomainConfig().RoleMapping.GroupMappings) > 0 {
|
||||
scopes = append(scopes, "groups")
|
||||
}
|
||||
|
||||
return oidcProvider, &oauth2.Config{
|
||||
ClientID: oidcConfig.ClientID,
|
||||
ClientSecret: oidcConfig.ClientSecret,
|
||||
ClientID: authDomain.AuthDomainConfig().OIDC.ClientID,
|
||||
ClientSecret: authDomain.AuthDomainConfig().OIDC.ClientSecret,
|
||||
Endpoint: oidcProvider.Endpoint(),
|
||||
Scopes: scopes,
|
||||
RedirectURL: (&url.URL{
|
||||
@@ -218,12 +212,7 @@ func (a *AuthN) claimsFromIDToken(ctx context.Context, authDomain *authtypes.Aut
|
||||
return nil, errors.New(errors.TypeNotFound, errors.CodeNotFound, "oidc: no id_token in token response")
|
||||
}
|
||||
|
||||
oidcConfig, err := authDomain.Config().OIDCConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
verifier := provider.Verifier(&oidc.Config{ClientID: oidcConfig.ClientID})
|
||||
verifier := provider.Verifier(&oidc.Config{ClientID: authDomain.AuthDomainConfig().OIDC.ClientID})
|
||||
idToken, err := verifier.Verify(ctx, rawIDToken)
|
||||
if err != nil {
|
||||
return nil, errors.Newf(errors.TypeForbidden, errors.CodeForbidden, "oidc: failed to verify token").WithAdditional(err.Error())
|
||||
|
||||
@@ -40,6 +40,10 @@ func New(ctx context.Context, store authtypes.AuthNStore, licensing licensing.Li
|
||||
}
|
||||
|
||||
func (a *AuthN) LoginURL(ctx context.Context, siteURL *url.URL, authDomain *authtypes.AuthDomain) (string, error) {
|
||||
if authDomain.AuthDomainConfig().AuthNProvider != authtypes.AuthNProviderSAML {
|
||||
return "", errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthDomainMismatch, "saml: domain type is not saml")
|
||||
}
|
||||
|
||||
sp, err := a.serviceProvider(siteURL, authDomain)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -69,11 +73,6 @@ func (a *AuthN) HandleCallback(ctx context.Context, formValues url.Values) (*aut
|
||||
return nil, errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||
}
|
||||
|
||||
samlConfig, err := authDomain.Config().SamlConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sp, err := a.serviceProvider(state.URL, authDomain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -102,19 +101,19 @@ func (a *AuthN) HandleCallback(ctx context.Context, formValues url.Values) (*aut
|
||||
}
|
||||
|
||||
name := ""
|
||||
if nameAttribute := samlConfig.AttributeMapping.Name; nameAttribute != "" {
|
||||
if nameAttribute := authDomain.AuthDomainConfig().SAML.AttributeMapping.Name; nameAttribute != "" {
|
||||
if val := assertionInfo.Values.Get(nameAttribute); val != "" {
|
||||
name = val
|
||||
}
|
||||
}
|
||||
|
||||
var groups []string
|
||||
if groupAttribute := samlConfig.AttributeMapping.Groups; groupAttribute != "" {
|
||||
if groupAttribute := authDomain.AuthDomainConfig().SAML.AttributeMapping.Groups; groupAttribute != "" {
|
||||
groups = assertionInfo.Values.GetAll(groupAttribute)
|
||||
}
|
||||
|
||||
role := ""
|
||||
if roleAttribute := samlConfig.AttributeMapping.Role; roleAttribute != "" {
|
||||
if roleAttribute := authDomain.AuthDomainConfig().SAML.AttributeMapping.Role; roleAttribute != "" {
|
||||
if val := assertionInfo.Values.Get(roleAttribute); val != "" {
|
||||
role = val
|
||||
}
|
||||
@@ -132,12 +131,7 @@ func (a *AuthN) ProviderInfo(ctx context.Context, authDomain *authtypes.AuthDoma
|
||||
}
|
||||
|
||||
func (a *AuthN) serviceProvider(siteURL *url.URL, authDomain *authtypes.AuthDomain) (*saml2.SAMLServiceProvider, error) {
|
||||
samlConfig, err := authDomain.Config().SamlConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certStore, err := a.getCertificateStore(samlConfig)
|
||||
certStore, err := a.getCertificateStore(authDomain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -148,32 +142,32 @@ func (a *AuthN) serviceProvider(siteURL *url.URL, authDomain *authtypes.AuthDoma
|
||||
// The ServiceProviderIssuer is the client id in case of keycloak. Since we set it to the host here, we need to set the client id == host in keycloak.
|
||||
// For AWSSSO, this is the value of Application SAML audience.
|
||||
return &saml2.SAMLServiceProvider{
|
||||
IdentityProviderSSOURL: samlConfig.Location,
|
||||
IdentityProviderIssuer: samlConfig.EntityID,
|
||||
IdentityProviderSSOURL: authDomain.AuthDomainConfig().SAML.SamlIdp,
|
||||
IdentityProviderIssuer: authDomain.AuthDomainConfig().SAML.SamlEntity,
|
||||
ServiceProviderIssuer: siteURL.Host,
|
||||
AssertionConsumerServiceURL: acsURL.String(),
|
||||
SignAuthnRequests: !samlConfig.InsecureSkipAuthNRequestsSigned,
|
||||
SignAuthnRequests: !authDomain.AuthDomainConfig().SAML.InsecureSkipAuthNRequestsSigned,
|
||||
AllowMissingAttributes: true,
|
||||
IDPCertificateStore: certStore,
|
||||
SPKeyStore: dsig.RandomKeyStoreForTest(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *AuthN) getCertificateStore(samlConfig authtypes.SamlConfig) (dsig.X509CertificateStore, error) {
|
||||
func (a *AuthN) getCertificateStore(authDomain *authtypes.AuthDomain) (dsig.X509CertificateStore, error) {
|
||||
certStore := &dsig.MemoryX509CertificateStore{
|
||||
Roots: []*x509.Certificate{},
|
||||
}
|
||||
|
||||
var certBytes []byte
|
||||
if strings.Contains(samlConfig.Certificate, "-----BEGIN CERTIFICATE-----") {
|
||||
block, _ := pem.Decode([]byte(samlConfig.Certificate))
|
||||
if strings.Contains(authDomain.AuthDomainConfig().SAML.SamlCert, "-----BEGIN CERTIFICATE-----") {
|
||||
block, _ := pem.Decode([]byte(authDomain.AuthDomainConfig().SAML.SamlCert))
|
||||
if block == nil {
|
||||
return certStore, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "no valid pem cert found")
|
||||
}
|
||||
|
||||
certBytes = block.Bytes
|
||||
} else {
|
||||
certData, err := base64.StdEncoding.DecodeString(samlConfig.Certificate)
|
||||
certData, err := base64.StdEncoding.DecodeString(authDomain.AuthDomainConfig().SAML.SamlCert)
|
||||
if err != nil {
|
||||
return certStore, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to read certificate: %s", err.Error())
|
||||
}
|
||||
|
||||
@@ -108,20 +108,6 @@ func (provider *Provider) SearchIngestionKeysByName(ctx context.Context, orgID v
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (provider *Provider) GetIngestionKey(ctx context.Context, orgID valuer.UUID, keyID string) (*gatewaytypes.IngestionKey, error) {
|
||||
responseBody, err := provider.do(ctx, orgID, http.MethodGet, "/v1/workspaces/me/keys/"+keyID, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ingestionKey gatewaytypes.IngestionKey
|
||||
if err := json.Unmarshal([]byte(gjson.GetBytes(responseBody, "data").String()), &ingestionKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ingestionKey, nil
|
||||
}
|
||||
|
||||
func (provider *Provider) CreateIngestionKey(ctx context.Context, orgID valuer.UUID, name string, tags []string, expiresAt time.Time) (*gatewaytypes.GettableCreatedIngestionKey, error) {
|
||||
requestBody := gatewaytypes.PostableIngestionKey{
|
||||
Name: name,
|
||||
@@ -175,7 +161,7 @@ func (provider *Provider) DeleteIngestionKey(ctx context.Context, orgID valuer.U
|
||||
}
|
||||
|
||||
func (provider *Provider) CreateIngestionKeyLimit(ctx context.Context, orgID valuer.UUID, keyID string, signal string, limitConfig gatewaytypes.LimitConfig, tags []string) (*gatewaytypes.GettableCreatedIngestionKeyLimit, error) {
|
||||
requestBody := gatewaytypes.DeprecatedPostableIngestionKeyLimit{
|
||||
requestBody := gatewaytypes.PostableIngestionKeyLimit{
|
||||
Signal: signal,
|
||||
Config: limitConfig,
|
||||
Tags: tags,
|
||||
@@ -198,34 +184,6 @@ func (provider *Provider) CreateIngestionKeyLimit(ctx context.Context, orgID val
|
||||
return &createdIngestionKeyLimitResponse, nil
|
||||
}
|
||||
|
||||
func (provider *Provider) GetIngestionKeyLimit(ctx context.Context, orgID valuer.UUID, limitID string) (*gatewaytypes.Limit, error) {
|
||||
responseBody, err := provider.do(ctx, orgID, http.MethodGet, "/v1/workspaces/me/limits/"+limitID, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var limit gatewaytypes.Limit
|
||||
if err := json.Unmarshal([]byte(gjson.GetBytes(responseBody, "data").String()), &limit); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &limit, nil
|
||||
}
|
||||
|
||||
func (provider *Provider) GetIngestionKeyLimits(ctx context.Context, orgID valuer.UUID, keyID string) ([]gatewaytypes.Limit, error) {
|
||||
responseBody, err := provider.do(ctx, orgID, http.MethodGet, "/v1/workspaces/me/keys/"+keyID+"/limits", nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var limits []gatewaytypes.Limit
|
||||
if err := json.Unmarshal([]byte(gjson.GetBytes(responseBody, "data").String()), &limits); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return limits, nil
|
||||
}
|
||||
|
||||
func (provider *Provider) UpdateIngestionKeyLimit(ctx context.Context, orgID valuer.UUID, limitID string, limitConfig gatewaytypes.LimitConfig, tags []string) error {
|
||||
requestBody := gatewaytypes.UpdatableIngestionKeyLimit{
|
||||
Config: limitConfig,
|
||||
|
||||
168
ee/licensing/httplicensing/api.go
Normal file
168
ee/licensing/httplicensing/api.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package httplicensing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/http/render"
|
||||
"github.com/SigNoz/signoz/pkg/licensing"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type licensingAPI struct {
|
||||
licensing licensing.Licensing
|
||||
}
|
||||
|
||||
func NewLicensingAPI(licensing licensing.Licensing) licensing.API {
|
||||
return &licensingAPI{licensing: licensing}
|
||||
}
|
||||
|
||||
func (api *licensingAPI) Activate(rw 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(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "orgId is invalid"))
|
||||
return
|
||||
}
|
||||
|
||||
req := new(licensetypes.PostableLicense)
|
||||
err = json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = api.licensing.Activate(r.Context(), orgID, req.Key)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusAccepted, nil)
|
||||
}
|
||||
|
||||
func (api *licensingAPI) GetActive(rw 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(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "orgId is invalid"))
|
||||
return
|
||||
}
|
||||
|
||||
license, err := api.licensing.GetActive(r.Context(), orgID)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
gettableLicense := licensetypes.NewGettableLicense(license.Data, license.Key)
|
||||
render.Success(rw, http.StatusOK, gettableLicense)
|
||||
}
|
||||
|
||||
func (api *licensingAPI) Refresh(rw 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(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "orgId is invalid"))
|
||||
return
|
||||
}
|
||||
|
||||
err = api.licensing.Refresh(r.Context(), orgID)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (api *licensingAPI) Checkout(rw 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(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "orgId is invalid"))
|
||||
return
|
||||
}
|
||||
|
||||
req := new(licensetypes.PostableSubscription)
|
||||
if err := json.NewDecoder(r.Body).Decode(req); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
gettableSubscription, err := api.licensing.Checkout(ctx, orgID, req)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusCreated, gettableSubscription)
|
||||
}
|
||||
|
||||
func (api *licensingAPI) Portal(rw 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(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "orgId is invalid"))
|
||||
return
|
||||
}
|
||||
|
||||
req := new(licensetypes.PostableSubscription)
|
||||
if err := json.NewDecoder(r.Body).Decode(req); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
gettableSubscription, err := api.licensing.Portal(ctx, orgID, req)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusCreated, gettableSubscription)
|
||||
}
|
||||
@@ -2,9 +2,12 @@ package httplicensing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/SigNoz/signoz/ee/licensing/licensingstore/sqllicensingstore"
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
@@ -92,65 +95,24 @@ func (provider *provider) Validate(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *provider) Activate(ctx context.Context, organizationID valuer.UUID, key string) (*licensetypes.License, error) {
|
||||
zeusLicense, err := provider.zeus.GetLicense(ctx, key)
|
||||
func (provider *provider) Activate(ctx context.Context, organizationID valuer.UUID, key string) error {
|
||||
data, err := provider.zeus.GetLicense(ctx, key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "unable to fetch license data with upstream server")
|
||||
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "unable to fetch license data with upstream server")
|
||||
}
|
||||
|
||||
license, err := licensetypes.NewLicense(zeusLicense, organizationID)
|
||||
license, err := licensetypes.NewLicense(data, organizationID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to create license entity")
|
||||
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to create license entity")
|
||||
}
|
||||
|
||||
storableLicense := licensetypes.NewStorableLicenseFromLicense(license)
|
||||
err = provider.store.Create(ctx, storableLicense)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return license, nil
|
||||
}
|
||||
|
||||
func (provider *provider) Get(ctx context.Context, organizationID valuer.UUID, licenseID valuer.UUID) (*licensetypes.License, error) {
|
||||
storableLicense, err := provider.store.Get(ctx, organizationID, licenseID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return licensetypes.NewLicenseFromStorableLicense(storableLicense)
|
||||
}
|
||||
|
||||
func (provider *provider) List(ctx context.Context, organizationID valuer.UUID) ([]*licensetypes.License, error) {
|
||||
storableLicenses, err := provider.store.GetAll(ctx, organizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
licenses := make([]*licensetypes.License, 0, len(storableLicenses))
|
||||
for _, storableLicense := range storableLicenses {
|
||||
license, err := licensetypes.NewLicenseFromStorableLicense(storableLicense)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
licenses = append(licenses, license)
|
||||
}
|
||||
|
||||
return licenses, nil
|
||||
}
|
||||
|
||||
func (provider *provider) Delete(ctx context.Context, organizationID valuer.UUID, licenseID valuer.UUID) error {
|
||||
license, err := provider.Get(ctx, organizationID, licenseID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := license.ErrIfCloud(); err != nil {
|
||||
return errors.WithAdditionalf(err, "license %s cannot be deleted", licenseID.StringValue())
|
||||
}
|
||||
|
||||
return provider.store.Delete(ctx, organizationID, licenseID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *provider) GetActive(ctx context.Context, organizationID valuer.UUID) (*licensetypes.License, error) {
|
||||
@@ -177,7 +139,7 @@ func (provider *provider) Refresh(ctx context.Context, organizationID valuer.UUI
|
||||
return err
|
||||
}
|
||||
|
||||
zeusLicense, err := provider.zeus.GetLicense(ctx, activeLicense.Key)
|
||||
data, err := provider.zeus.GetLicense(ctx, activeLicense.Key)
|
||||
if err != nil {
|
||||
if time.Since(activeLicense.LastValidatedAt) > time.Duration(provider.config.FailureThreshold)*provider.config.PollInterval {
|
||||
activeLicense.UpdateFeatures(licensetypes.BasicPlan)
|
||||
@@ -192,7 +154,7 @@ func (provider *provider) Refresh(ctx context.Context, organizationID valuer.UUI
|
||||
return err
|
||||
}
|
||||
|
||||
err = activeLicense.Update(zeusLicense)
|
||||
err = activeLicense.Update(data)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to create license entity from license data")
|
||||
}
|
||||
@@ -225,6 +187,47 @@ func (provider *provider) Refresh(ctx context.Context, organizationID valuer.UUI
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *provider) Checkout(ctx context.Context, organizationID valuer.UUID, postableSubscription *licensetypes.PostableSubscription) (*licensetypes.GettableSubscription, error) {
|
||||
activeLicense, err := provider.GetActive(ctx, organizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := json.Marshal(postableSubscription)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to marshal checkout payload")
|
||||
}
|
||||
|
||||
response, err := provider.zeus.GetCheckoutURL(ctx, activeLicense.Key, body)
|
||||
if err != nil {
|
||||
if errors.Ast(err, errors.TypeAlreadyExists) {
|
||||
return nil, errors.WithAdditionalf(err, "checkout has already been completed for this account. Please click 'Refresh Status' to sync your subscription")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &licensetypes.GettableSubscription{RedirectURL: gjson.GetBytes(response, "url").String()}, nil
|
||||
}
|
||||
|
||||
func (provider *provider) Portal(ctx context.Context, organizationID valuer.UUID, postableSubscription *licensetypes.PostableSubscription) (*licensetypes.GettableSubscription, error) {
|
||||
activeLicense, err := provider.GetActive(ctx, organizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := json.Marshal(postableSubscription)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to marshal portal payload")
|
||||
}
|
||||
|
||||
response, err := provider.zeus.GetPortalURL(ctx, activeLicense.Key, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &licensetypes.GettableSubscription{RedirectURL: gjson.GetBytes(response, "url").String()}, nil
|
||||
}
|
||||
|
||||
func (provider *provider) GetFeatureFlags(ctx context.Context, organizationID valuer.UUID) ([]*licensetypes.Feature, error) {
|
||||
license, err := provider.GetActive(ctx, organizationID)
|
||||
if err != nil {
|
||||
|
||||
@@ -64,22 +64,6 @@ func (store *store) GetAll(ctx context.Context, organizationID valuer.UUID) ([]*
|
||||
return storableLicenses, nil
|
||||
}
|
||||
|
||||
func (store *store) Delete(ctx context.Context, organizationID valuer.UUID, licenseID valuer.UUID) error {
|
||||
_, err := store.
|
||||
sqlstore.
|
||||
BunDB().
|
||||
NewDelete().
|
||||
Model(new(licensetypes.StorableLicense)).
|
||||
Where("org_id = ?", organizationID).
|
||||
Where("id = ?", licenseID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "unable to delete license with ID: %s", licenseID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *store) Update(ctx context.Context, organizationID valuer.UUID, storableLicense *licensetypes.StorableLicense) error {
|
||||
_, err := store.
|
||||
sqlstore.
|
||||
|
||||
@@ -32,9 +32,9 @@ type module struct {
|
||||
tagModule tag.Module
|
||||
}
|
||||
|
||||
func NewModule(store dashboardtypes.Store, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, queryParser queryparser.QueryParser, querier querier.Querier, licensing licensing.Licensing, tagModule tag.Module, systemDashboardRegistry dashboardtypes.SystemDashboardRegistry) dashboard.Module {
|
||||
func NewModule(store dashboardtypes.Store, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, queryParser queryparser.QueryParser, querier querier.Querier, licensing licensing.Licensing, tagModule tag.Module) dashboard.Module {
|
||||
scopedProviderSettings := factory.NewScopedProviderSettings(settings, "github.com/SigNoz/signoz/ee/modules/dashboard/impldashboard")
|
||||
pkgDashboardModule := pkgimpldashboard.NewModule(store, settings, analytics, orgGetter, queryParser, tagModule, systemDashboardRegistry)
|
||||
pkgDashboardModule := pkgimpldashboard.NewModule(store, settings, analytics, orgGetter, queryParser, tagModule)
|
||||
|
||||
return &module{
|
||||
pkgDashboardModule: pkgDashboardModule,
|
||||
@@ -361,14 +361,6 @@ func (module *module) LockUnlock(ctx context.Context, orgID valuer.UUID, id valu
|
||||
return module.pkgDashboardModule.LockUnlock(ctx, orgID, id, updatedBy, isAdmin, lock)
|
||||
}
|
||||
|
||||
func (module *module) ReconcileSystemDashboards(ctx context.Context, orgID valuer.UUID) error {
|
||||
return module.pkgDashboardModule.ReconcileSystemDashboards(ctx, orgID)
|
||||
}
|
||||
|
||||
func (module *module) GetSystemDashboard(ctx context.Context, orgID valuer.UUID, name string) (*dashboardtypes.DashboardV2, error) {
|
||||
return module.pkgDashboardModule.GetSystemDashboard(ctx, orgID, name)
|
||||
}
|
||||
|
||||
func (module *module) delete(ctx context.Context, orgID, id valuer.UUID) error {
|
||||
return module.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||
if err := module.store.DeletePublic(ctx, id.String()); err != nil && !errors.Ast(err, errors.TypeNotFound) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/ee/licensing/httplicensing"
|
||||
"github.com/SigNoz/signoz/ee/query-service/usage"
|
||||
"github.com/SigNoz/signoz/pkg/global"
|
||||
"github.com/SigNoz/signoz/pkg/http/middleware"
|
||||
@@ -41,6 +42,7 @@ func NewAPIHandler(opts APIHandlerOptions, signoz *signoz.SigNoz, config signoz.
|
||||
IntegrationsController: opts.IntegrationsController,
|
||||
LogsParsingPipelineController: opts.LogsParsingPipelineController,
|
||||
FluxInterval: opts.FluxInterval,
|
||||
LicensingAPI: httplicensing.NewLicensingAPI(signoz.Licensing),
|
||||
Signoz: signoz,
|
||||
QueryParserAPI: queryparser.NewAPI(signoz.Instrumentation.ToProviderSettings(), signoz.QueryParser),
|
||||
}, config)
|
||||
@@ -65,11 +67,20 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router, am *middleware.AuthZ) {
|
||||
// note: add ee override methods first
|
||||
|
||||
// routes available only in ee version
|
||||
router.HandleFunc("/api/v1/features", am.OpenAccess(ah.getFeatureFlags)).Methods(http.MethodGet)
|
||||
router.HandleFunc("/api/v1/features", am.ViewAccess(ah.getFeatureFlags)).Methods(http.MethodGet)
|
||||
|
||||
// base overrides
|
||||
router.HandleFunc("/api/v1/version", am.OpenAccess(ah.getVersion)).Methods(http.MethodGet)
|
||||
|
||||
router.HandleFunc("/api/v1/checkout", am.AdminAccess(ah.LicensingAPI.Checkout)).Methods(http.MethodPost)
|
||||
router.HandleFunc("/api/v1/billing", am.AdminAccess(ah.getBilling)).Methods(http.MethodGet)
|
||||
router.HandleFunc("/api/v1/portal", am.AdminAccess(ah.LicensingAPI.Portal)).Methods(http.MethodPost)
|
||||
|
||||
// v3
|
||||
router.HandleFunc("/api/v3/licenses", am.AdminAccess(ah.LicensingAPI.Activate)).Methods(http.MethodPost)
|
||||
router.HandleFunc("/api/v3/licenses", am.AdminAccess(ah.LicensingAPI.Refresh)).Methods(http.MethodPut)
|
||||
router.HandleFunc("/api/v3/licenses/active", am.ViewAccess(ah.LicensingAPI.GetActive)).Methods(http.MethodGet)
|
||||
|
||||
// v4
|
||||
router.HandleFunc("/api/v4/query_range", am.ViewAccess(ah.queryRangeV4)).Methods(http.MethodPost)
|
||||
|
||||
|
||||
@@ -98,6 +98,14 @@ func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
||||
Route: "",
|
||||
})
|
||||
|
||||
if constants.IsDotMetricsEnabled {
|
||||
for idx, feature := range featureSet {
|
||||
if feature.Name == licensetypes.DotMetricsEnabled {
|
||||
featureSet[idx].Active = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ah.Respond(w, featureSet)
|
||||
}
|
||||
|
||||
|
||||
122
ee/query-service/app/api/license.go
Normal file
122
ee/query-service/app/api/license.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/SigNoz/signoz/ee/query-service/constants"
|
||||
"github.com/SigNoz/signoz/ee/query-service/model"
|
||||
"github.com/SigNoz/signoz/pkg/flagger"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type DayWiseBreakdown struct {
|
||||
Type string `json:"type"`
|
||||
Breakdown []DayWiseData `json:"breakdown"`
|
||||
}
|
||||
|
||||
type DayWiseData struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Count float64 `json:"count"`
|
||||
Size float64 `json:"size"`
|
||||
UnitPrice float64 `json:"unitPrice"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
Total float64 `json:"total"`
|
||||
}
|
||||
|
||||
type tierBreakdown struct {
|
||||
UnitPrice float64 `json:"unitPrice"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
TierStart int64 `json:"tierStart"`
|
||||
TierEnd int64 `json:"tierEnd"`
|
||||
TierCost float64 `json:"tierCost"`
|
||||
}
|
||||
|
||||
type usageResponse struct {
|
||||
Type string `json:"type"`
|
||||
Unit string `json:"unit"`
|
||||
Tiers []tierBreakdown `json:"tiers"`
|
||||
DayWiseBreakdown DayWiseBreakdown `json:"dayWiseBreakdown"`
|
||||
}
|
||||
|
||||
type details struct {
|
||||
Total float64 `json:"total"`
|
||||
Breakdown []usageResponse `json:"breakdown"`
|
||||
BaseFee float64 `json:"baseFee"`
|
||||
BillTotal float64 `json:"billTotal"`
|
||||
}
|
||||
|
||||
type billingData struct {
|
||||
BillingPeriodStart int64 `json:"billingPeriodStart"`
|
||||
BillingPeriodEnd int64 `json:"billingPeriodEnd"`
|
||||
Details details `json:"details"`
|
||||
Discount float64 `json:"discount"`
|
||||
SubscriptionStatus string `json:"subscriptionStatus"`
|
||||
}
|
||||
|
||||
type billingDetails struct {
|
||||
Status string `json:"status"`
|
||||
Data billingData `json:"data"`
|
||||
}
|
||||
|
||||
func (ah *APIHandler) getBilling(w http.ResponseWriter, r *http.Request) {
|
||||
licenseKey := r.URL.Query().Get("licenseKey")
|
||||
|
||||
if licenseKey == "" {
|
||||
RespondError(w, model.BadRequest(fmt.Errorf("license key is required")), nil)
|
||||
return
|
||||
}
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(r.Context())
|
||||
if err != nil {
|
||||
RespondError(w, model.InternalError(err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
orgID := valuer.MustNewUUID(claims.OrgID)
|
||||
evalCtx := featuretypes.NewFlaggerEvaluationContext(orgID)
|
||||
useZeus := ah.Signoz.Flagger.BooleanOrEmpty(r.Context(), flagger.FeatureGetMetersFromZeus, evalCtx)
|
||||
|
||||
if useZeus {
|
||||
data, err := ah.Signoz.Zeus.GetMeters(r.Context(), licenseKey)
|
||||
if err != nil {
|
||||
RespondError(w, model.InternalError(err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
var billing billingData
|
||||
if err := json.Unmarshal(data, &billing); err != nil {
|
||||
RespondError(w, model.InternalError(err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
ah.Respond(w, billing)
|
||||
return
|
||||
}
|
||||
|
||||
billingURL := fmt.Sprintf("%s/usage?licenseKey=%s", constants.LicenseSignozIo, licenseKey)
|
||||
|
||||
hClient := &http.Client{}
|
||||
req, err := http.NewRequest("GET", billingURL, nil)
|
||||
if err != nil {
|
||||
RespondError(w, model.InternalError(err), nil)
|
||||
return
|
||||
}
|
||||
req.Header.Add("X-SigNoz-SecretKey", constants.LicenseAPIKey)
|
||||
billingResp, err := hClient.Do(req)
|
||||
if err != nil {
|
||||
RespondError(w, model.InternalError(err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
var billingResponse billingDetails
|
||||
if err := json.NewDecoder(billingResp.Body).Decode(&billingResponse); err != nil {
|
||||
RespondError(w, model.InternalError(err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
ah.Respond(w, billingResponse.Data)
|
||||
}
|
||||
@@ -3,29 +3,56 @@ package app
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"slices"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
|
||||
"github.com/rs/cors"
|
||||
"github.com/soheilhy/cmux"
|
||||
|
||||
"github.com/SigNoz/signoz/ee/query-service/app/api"
|
||||
"github.com/SigNoz/signoz/ee/query-service/usage"
|
||||
"github.com/SigNoz/signoz/pkg/http/middleware"
|
||||
"github.com/SigNoz/signoz/pkg/signoz"
|
||||
"github.com/SigNoz/signoz/pkg/web"
|
||||
|
||||
"log/slog"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/query-service/agentConf"
|
||||
baseapp "github.com/SigNoz/signoz/pkg/query-service/app"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/clickhouseReader"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/integrations"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/logparsingpipeline"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/opamp"
|
||||
opAmpModel "github.com/SigNoz/signoz/pkg/query-service/app/opamp/model"
|
||||
baseconst "github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/healthcheck"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/utils"
|
||||
)
|
||||
|
||||
// Server runs auxiliary servers (opamp) alongside the signoz apiserver
|
||||
// Server runs HTTP, Mux and a grpc server
|
||||
type Server struct {
|
||||
config signoz.Config
|
||||
signoz *signoz.SigNoz
|
||||
|
||||
// public http router
|
||||
httpConn net.Listener
|
||||
httpServer *http.Server
|
||||
httpHostPort string
|
||||
|
||||
opampServer *opamp.Server
|
||||
|
||||
// Usage manager
|
||||
usageManager *usage.Manager
|
||||
|
||||
unavailableChannel chan healthcheck.Status
|
||||
}
|
||||
|
||||
// NewServer creates and initializes Server
|
||||
@@ -100,25 +127,22 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Register the legacy query-service routes on the apiserver router. The
|
||||
// apiserver owns the HTTP server and applies the middleware chain at serve
|
||||
// time, so these routes get the same treatment as the apiserver routes.
|
||||
r := signoz.APIServer.Router()
|
||||
am := middleware.NewAuthZ(signoz.Instrumentation.Logger(), signoz.Modules.OrgGetter, signoz.Authz)
|
||||
|
||||
apiHandler.RegisterRoutes(r, am)
|
||||
apiHandler.RegisterLogsRoutes(r, am)
|
||||
apiHandler.RegisterIntegrationRoutes(r, am)
|
||||
apiHandler.RegisterQueryRangeV3Routes(r, am)
|
||||
apiHandler.RegisterQueryRangeV4Routes(r, am)
|
||||
apiHandler.RegisterMessagingQueuesRoutes(r, am)
|
||||
apiHandler.RegisterThirdPartyApiRoutes(r, am)
|
||||
apiHandler.RegisterTraceFunnelsRoutes(r, am)
|
||||
|
||||
s := &Server{
|
||||
usageManager: usageManager,
|
||||
config: config,
|
||||
signoz: signoz,
|
||||
httpHostPort: baseconst.HTTPHostPort,
|
||||
unavailableChannel: make(chan healthcheck.Status),
|
||||
usageManager: usageManager,
|
||||
}
|
||||
|
||||
httpServer, err := s.createPublicServer(apiHandler, signoz.Web)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.httpServer = httpServer
|
||||
|
||||
s.opampServer = opamp.InitializeServer(
|
||||
&opAmpModel.AllAgents, agentConfMgr, signoz.Instrumentation,
|
||||
)
|
||||
@@ -126,18 +150,147 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Start starts the opamp websocket server. The HTTP API server is started by
|
||||
// the signoz registry.
|
||||
func (s *Server) Start(ctx context.Context) error {
|
||||
slog.Info("Starting OpAmp Websocket server", "addr", baseconst.OpAmpWsEndpoint)
|
||||
if err := s.opampServer.Start(baseconst.OpAmpWsEndpoint); err != nil {
|
||||
// HealthCheckStatus returns health check status channel a client can subscribe to
|
||||
func (s Server) HealthCheckStatus() chan healthcheck.Status {
|
||||
return s.unavailableChannel
|
||||
}
|
||||
|
||||
func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*http.Server, error) {
|
||||
r := baseapp.NewRouter()
|
||||
am := middleware.NewAuthZ(s.signoz.Instrumentation.Logger(), s.signoz.Modules.OrgGetter, s.signoz.Authz)
|
||||
|
||||
r.Use(middleware.NewRecovery(s.signoz.Instrumentation.Logger()).Wrap)
|
||||
r.Use(otelmux.Middleware(
|
||||
"apiserver",
|
||||
otelmux.WithMeterProvider(s.signoz.Instrumentation.MeterProvider()),
|
||||
otelmux.WithTracerProvider(s.signoz.Instrumentation.TracerProvider()),
|
||||
otelmux.WithPropagators(propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{})),
|
||||
otelmux.WithFilter(func(r *http.Request) bool {
|
||||
return !slices.Contains([]string{"/api/v1/health"}, r.URL.Path)
|
||||
}),
|
||||
))
|
||||
r.Use(middleware.NewIdentN(s.signoz.IdentNResolver, s.signoz.Sharder, s.signoz.Instrumentation.Logger()).Wrap)
|
||||
r.Use(middleware.NewTimeout(s.signoz.Instrumentation.Logger(),
|
||||
s.config.APIServer.Timeout.ExcludedRoutes,
|
||||
s.config.APIServer.Timeout.Default,
|
||||
s.config.APIServer.Timeout.Max,
|
||||
).Wrap)
|
||||
r.Use(middleware.NewResource(s.signoz.Instrumentation.Logger()).Wrap)
|
||||
r.Use(middleware.NewAudit(s.signoz.Instrumentation.Logger(), s.config.APIServer.Logging.ExcludedRoutes, s.signoz.Auditor).Wrap)
|
||||
r.Use(middleware.NewComment().Wrap)
|
||||
|
||||
apiHandler.RegisterRoutes(r, am)
|
||||
apiHandler.RegisterLogsRoutes(r, am)
|
||||
apiHandler.RegisterIntegrationRoutes(r, am)
|
||||
apiHandler.RegisterQueryRangeV3Routes(r, am)
|
||||
apiHandler.RegisterInfraMetricsRoutes(r, am)
|
||||
apiHandler.RegisterQueryRangeV4Routes(r, am)
|
||||
apiHandler.RegisterWebSocketPaths(r, am)
|
||||
apiHandler.RegisterMessagingQueuesRoutes(r, am)
|
||||
apiHandler.RegisterThirdPartyApiRoutes(r, am)
|
||||
apiHandler.RegisterTraceFunnelsRoutes(r, am)
|
||||
|
||||
err := s.signoz.APIServer.AddToRouter(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
AllowedMethods: []string{"GET", "DELETE", "POST", "PUT", "PATCH", "OPTIONS"},
|
||||
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "cache-control", "X-SIGNOZ-QUERY-ID", "Sec-WebSocket-Protocol"},
|
||||
})
|
||||
|
||||
handler := c.Handler(r)
|
||||
|
||||
handler = handlers.CompressHandler(handler)
|
||||
|
||||
err = web.AddToRouter(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
routePrefix := s.config.Global.ExternalPath()
|
||||
if routePrefix != "" {
|
||||
prefixed := http.StripPrefix(routePrefix, handler)
|
||||
handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
switch req.URL.Path {
|
||||
case "/api/v1/health", "/api/v2/healthz", "/api/v2/readyz", "/api/v2/livez":
|
||||
r.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
prefixed.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
|
||||
return &http.Server{
|
||||
Handler: handler,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// initListeners initialises listeners of the server
|
||||
func (s *Server) initListeners() error {
|
||||
// listen on public port
|
||||
var err error
|
||||
publicHostPort := s.httpHostPort
|
||||
if publicHostPort == "" {
|
||||
return fmt.Errorf("baseconst.HTTPHostPort is required")
|
||||
}
|
||||
|
||||
s.httpConn, err = net.Listen("tcp", publicHostPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
slog.Info(fmt.Sprintf("Query server started listening on %s...", s.httpHostPort))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start listening on http and private http port concurrently
|
||||
func (s *Server) Start(ctx context.Context) error {
|
||||
err := s.initListeners()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var httpPort int
|
||||
if port, err := utils.GetPort(s.httpConn.Addr()); err == nil {
|
||||
httpPort = port
|
||||
}
|
||||
|
||||
go func() {
|
||||
slog.Info("Starting HTTP server", "port", httpPort, "addr", s.httpHostPort)
|
||||
|
||||
switch err := s.httpServer.Serve(s.httpConn); err {
|
||||
case nil, http.ErrServerClosed, cmux.ErrListenerClosed:
|
||||
// normal exit, nothing to do
|
||||
default:
|
||||
slog.Error("Could not start HTTP server", errors.Attr(err))
|
||||
}
|
||||
s.unavailableChannel <- healthcheck.Unavailable
|
||||
}()
|
||||
|
||||
go func() {
|
||||
slog.Info("Starting OpAmp Websocket server", "addr", baseconst.OpAmpWsEndpoint)
|
||||
err := s.opampServer.Start(baseconst.OpAmpWsEndpoint)
|
||||
if err != nil {
|
||||
slog.Error("opamp ws server failed to start", errors.Attr(err))
|
||||
s.unavailableChannel <- healthcheck.Unavailable
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Stop(ctx context.Context) error {
|
||||
if s.httpServer != nil {
|
||||
if err := s.httpServer.Shutdown(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.opampServer.Stop()
|
||||
|
||||
// stop usage manager
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
var LicenseSignozIo = "https://license.signoz.io/api/v1"
|
||||
var LicenseAPIKey = GetOrDefaultEnv("SIGNOZ_LICENSE_API_KEY", "")
|
||||
var SaasSegmentKey = GetOrDefaultEnv("SIGNOZ_SAAS_SEGMENT_KEY", "")
|
||||
var FetchFeatures = GetOrDefaultEnv("FETCH_FEATURES", "false")
|
||||
var ZeusFeaturesURL = GetOrDefaultEnv("ZEUS_FEATURES_URL", "ZeusFeaturesURL")
|
||||
@@ -15,3 +17,15 @@ func GetOrDefaultEnv(key string, fallback string) string {
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// constant functions that override env vars
|
||||
|
||||
const DotMetricsEnabled = "DOT_METRICS_ENABLED"
|
||||
|
||||
var IsDotMetricsEnabled = false
|
||||
|
||||
func init() {
|
||||
if GetOrDefaultEnv(DotMetricsEnabled, "true") == "true" {
|
||||
IsDotMetricsEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ func TestManager_TestNotification_SendUnmatched_PromRule(t *testing.T) {
|
||||
triggeredTestAlerts := []map[*alertmanagertypes.PostableAlert][]string{}
|
||||
|
||||
// Variable to store promProvider for cleanup
|
||||
var promProvider prometheus.Prometheus
|
||||
var promProvider *prometheustest.Provider
|
||||
|
||||
// Create manager using test factory with hooks
|
||||
mgr := rules.NewTestManager(t, &rules.TestManagerOptions{
|
||||
@@ -185,29 +185,76 @@ func TestManager_TestNotification_SendUnmatched_PromRule(t *testing.T) {
|
||||
TelemetryStoreHook: func(store telemetrystore.TelemetryStore) {
|
||||
mockStore := store.(*telemetrystoretest.Provider)
|
||||
|
||||
// Grid the TestNotification eval computes over (see
|
||||
// Timestamps on base_rule); nil args match any window.
|
||||
// Set up Prometheus-specific mock data
|
||||
// Fingerprint columns for Prometheus queries
|
||||
fingerprintCols := []cmock.ColumnType{
|
||||
{Name: "fingerprint", Type: "UInt64"},
|
||||
{Name: "any(labels)", Type: "String"},
|
||||
}
|
||||
|
||||
// Samples columns for Prometheus queries
|
||||
samplesCols := []cmock.ColumnType{
|
||||
{Name: "metric_name", Type: "String"},
|
||||
{Name: "fingerprint", Type: "UInt64"},
|
||||
{Name: "unix_milli", Type: "Int64"},
|
||||
{Name: "value", Type: "Float64"},
|
||||
{Name: "flags", Type: "UInt32"},
|
||||
}
|
||||
|
||||
// Calculate query time range similar to Prometheus rule tests
|
||||
// TestNotification uses time.Now().UTC() for evaluation
|
||||
// We calculate the query window based on current time to match what the actual evaluation will use
|
||||
evalTime := baseTime
|
||||
evalWindowMs := int64(5 * 60 * 1000) // 5 minutes in ms
|
||||
gridEnd := (evalTime.UnixMilli() / 60000) * 60000
|
||||
gridStart := gridEnd - evalWindowMs
|
||||
evalTimeMs := evalTime.UnixMilli()
|
||||
queryStart := ((evalTimeMs-2*evalWindowMs)/60000)*60000 + 1 // truncate to minute + 1ms
|
||||
queryEnd := (evalTimeMs / 60000) * 60000 // truncate to minute
|
||||
|
||||
tsList := make([]int64, 0, len(tc.Values))
|
||||
vList := make([]float64, 0, len(tc.Values))
|
||||
// Create fingerprint data
|
||||
fingerprint := uint64(12345)
|
||||
labelsJSON := `{"__name__":"test_metric"}`
|
||||
fingerprintData := [][]interface{}{
|
||||
{fingerprint, labelsJSON},
|
||||
}
|
||||
fingerprintRows := cmock.NewRows(fingerprintCols, fingerprintData)
|
||||
|
||||
// Create samples data from test case values, calculating timestamps relative to baseTime
|
||||
validSamplesData := make([][]interface{}, 0)
|
||||
for _, v := range tc.Values {
|
||||
// Skip NaN and Inf values in the samples data
|
||||
if math.IsNaN(v.Value) || math.IsInf(v.Value, 0) {
|
||||
continue
|
||||
}
|
||||
tsList = append(tsList, baseTime.Add(v.Offset).UnixMilli())
|
||||
vList = append(vList, v.Value)
|
||||
// Calculate timestamp relative to baseTime
|
||||
sampleTimestamp := baseTime.Add(v.Offset).UnixMilli()
|
||||
validSamplesData = append(validSamplesData, []interface{}{
|
||||
"test_metric",
|
||||
fingerprint,
|
||||
sampleTimestamp,
|
||||
v.Value,
|
||||
uint32(0), // flags - 0 means normal value
|
||||
})
|
||||
}
|
||||
grid := prometheustest.LastSampleGrid(tsList, vList, gridStart, gridEnd, 60_000, 300_000)
|
||||
samplesRows := cmock.NewRows(samplesCols, validSamplesData)
|
||||
|
||||
mock := mockStore.Mock()
|
||||
mock.ExpectQuery("SELECT gkey").
|
||||
WithArgs("test_metric", nil, nil, "test_metric", nil, nil).
|
||||
WillReturnRows(cmock.NewRows(prometheustest.GridCols, [][]any{{`[["__name__","test_metric"]]`, grid}}))
|
||||
|
||||
// Mock the fingerprint query (for Prometheus label matching)
|
||||
// args: $1=metric_name (the __name__ matcher maps onto the column)
|
||||
mock.ExpectQuery("SELECT fingerprint, any").
|
||||
WithArgs("test_metric").
|
||||
WillReturnRows(fingerprintRows)
|
||||
|
||||
// Mock the samples query (for Prometheus metric data)
|
||||
// args: metric_name IN (discovered names), subquery metric_name, start, end
|
||||
mock.ExpectQuery("SELECT metric_name, fingerprint, unix_milli").
|
||||
WithArgs(
|
||||
"test_metric",
|
||||
"test_metric",
|
||||
queryStart,
|
||||
queryEnd,
|
||||
).
|
||||
WillReturnRows(samplesRows)
|
||||
|
||||
// Create Prometheus provider for this test
|
||||
promProvider = prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{Timeout: 2 * time.Minute}, store)
|
||||
@@ -242,6 +289,7 @@ func TestManager_TestNotification_SendUnmatched_PromRule(t *testing.T) {
|
||||
assert.Empty(t, triggeredTestAlerts)
|
||||
}
|
||||
|
||||
promProvider.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
package httpsubscription
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/licensing"
|
||||
"github.com/SigNoz/signoz/pkg/subscription"
|
||||
"github.com/SigNoz/signoz/pkg/types/subscriptiontypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/SigNoz/signoz/pkg/zeus"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
const upstreamTimeout = 10 * time.Second
|
||||
|
||||
type provider struct {
|
||||
zeus zeus.Zeus
|
||||
licensing licensing.Licensing
|
||||
}
|
||||
|
||||
func New(zeus zeus.Zeus, licensing licensing.Licensing) subscription.Subscription {
|
||||
return &provider{
|
||||
zeus: zeus,
|
||||
licensing: licensing,
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *provider) Create(ctx context.Context, organizationID valuer.UUID, postableSubscription *subscriptiontypes.PostableSubscription) (*subscriptiontypes.GettableSubscription, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, upstreamTimeout)
|
||||
defer cancel()
|
||||
|
||||
license, err := provider.licensing.GetActive(ctx, organizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := json.Marshal(postableSubscription)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to marshal subscription payload")
|
||||
}
|
||||
|
||||
response, err := provider.zeus.GetCheckoutURL(ctx, license.Key, body)
|
||||
if err != nil {
|
||||
if errors.Ast(err, errors.TypeAlreadyExists) {
|
||||
return nil, errors.WithAdditionalf(err, "checkout has already been completed for this account. Please click 'Refresh Status' to sync your subscription")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &subscriptiontypes.GettableSubscription{RedirectURL: gjson.GetBytes(response, "url").String()}, nil
|
||||
}
|
||||
|
||||
func (provider *provider) Update(ctx context.Context, organizationID valuer.UUID, postableSubscription *subscriptiontypes.PostableSubscription) (*subscriptiontypes.GettableSubscription, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, upstreamTimeout)
|
||||
defer cancel()
|
||||
|
||||
license, err := provider.licensing.GetActive(ctx, organizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := json.Marshal(postableSubscription)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to marshal subscription payload")
|
||||
}
|
||||
|
||||
response, err := provider.zeus.GetPortalURL(ctx, license.Key, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &subscriptiontypes.GettableSubscription{RedirectURL: gjson.GetBytes(response, "url").String()}, nil
|
||||
}
|
||||
|
||||
func (provider *provider) Get(ctx context.Context, organizationID valuer.UUID) (*subscriptiontypes.GettableSubscriptionUsage, error) {
|
||||
license, err := provider.licensing.GetActive(ctx, organizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := provider.zeus.GetMeters(ctx, license.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
usage, err := subscriptiontypes.NewGettableSubscriptionUsage(data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInternal, zeus.ErrCodeResponseMalformed, "failed to unmarshal subscription usage")
|
||||
}
|
||||
|
||||
return usage, nil
|
||||
}
|
||||
@@ -51,7 +51,7 @@ func New(ctx context.Context, providerSettings factory.ProviderSettings, config
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (provider *Provider) GetLicense(ctx context.Context, key string) (*zeustypes.License, error) {
|
||||
func (provider *Provider) GetLicense(ctx context.Context, key string) ([]byte, error) {
|
||||
response, err := provider.do(
|
||||
ctx,
|
||||
provider.config.URL.JoinPath("/v2/licenses/me"),
|
||||
@@ -63,12 +63,7 @@ func (provider *Provider) GetLicense(ctx context.Context, key string) (*zeustype
|
||||
return nil, err
|
||||
}
|
||||
|
||||
license := new(zeustypes.License)
|
||||
if err := json.Unmarshal([]byte(gjson.GetBytes(response, "data").String()), license); err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInternal, zeus.ErrCodeResponseMalformed, "failed to unmarshal license data")
|
||||
}
|
||||
|
||||
return license, nil
|
||||
return []byte(gjson.GetBytes(response, "data").String()), nil
|
||||
}
|
||||
|
||||
func (provider *Provider) GetCheckoutURL(ctx context.Context, key string, body []byte) ([]byte, error) {
|
||||
|
||||
@@ -323,10 +323,9 @@
|
||||
"name": "react",
|
||||
"importNames": [
|
||||
"createContext",
|
||||
"useContext",
|
||||
"useSyncExternalStore"
|
||||
"useContext"
|
||||
],
|
||||
"message": "[State mgmt] React Context and hand-rolled external stores are deprecated. Migrate shared state to Zustand."
|
||||
"message": "[State mgmt] React Context is deprecated. Migrate shared state to Zustand."
|
||||
},
|
||||
{
|
||||
"name": "immer",
|
||||
@@ -488,11 +487,8 @@
|
||||
// Simplifies boolean returns
|
||||
"sonarjs/prefer-while": "error",
|
||||
// Suggests while loops over for loops
|
||||
"sonarjs/elseif-without-else": "off",
|
||||
"sonarjs/elseif-without-else": "off"
|
||||
// Requires final else in if-else-if chains (was disabled)
|
||||
"signoz/no-conditional-text-nodes-with-siblings": "warn",
|
||||
// Vendored from eslint-plugin-react-google-translate
|
||||
"signoz/no-return-text-nodes": "warn"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"src/parser/*.ts",
|
||||
@@ -566,12 +562,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
// Root dashboard pages own the fetch lifecycle; useDashboardFetchRequired wraps it.
|
||||
// Root V2 pages own the dashboard fetch lifecycle; useDashboardFetchRequired wraps it.
|
||||
// Everywhere else must use useDashboardFetchRequired().
|
||||
"files": [
|
||||
"src/pages/DashboardPage/DashboardPage.tsx",
|
||||
"src/pages/DashboardPage/PanelEditorPage/PanelEditorPage.tsx",
|
||||
"src/pages/DashboardPage/DashboardContainer/hooks/useDashboardFetchRequired.ts"
|
||||
"src/pages/DashboardPageV2/DashboardPageV2.tsx",
|
||||
"src/pages/DashboardPageV2/PanelEditorPage/PanelEditorPage.tsx",
|
||||
"src/pages/DashboardPageV2/DashboardContainer/hooks/useDashboardFetchRequired.ts"
|
||||
],
|
||||
"rules": {
|
||||
"signoz/no-dashboard-fetch-outside-root": "off"
|
||||
|
||||
@@ -56,10 +56,10 @@ const config: Config.InitialOptions = {
|
||||
transformIgnorePatterns: [
|
||||
// @chenglou/pretext is ESM-only; @signozhq/ui pulls it in via text-ellipsis.
|
||||
// Pattern 1: allow .pnpm virtual store through (handled by pattern 2), plus root-level ESM packages.
|
||||
'node_modules/(?!(\\.pnpm|react-json-tree|react-base16-styling|lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@chenglou/pretext|@signozhq/design-tokens|@signozhq|date-fns|d3-interpolate|d3-color|api|@codemirror|@lezer|@marijn|@grafana|nuqs|uuid|copy-text-to-clipboard|react-markdown|vfile|vfile-message|unist-util-stringify-position|unified|bail|is-plain-obj|trough|remark-parse|mdast-util-from-markdown|mdast-util-to-string|micromark|micromark-core-commonmark|micromark-extension-gfm|micromark-extension-gfm-autolink-literal|micromark-extension-gfm-footnote|micromark-extension-gfm-strikethrough|micromark-extension-gfm-table|micromark-extension-gfm-tagfilter|micromark-extension-gfm-task-list-item|micromark-factory-destination|micromark-factory-label|micromark-factory-space|micromark-factory-title|micromark-factory-whitespace|micromark-util-character|micromark-util-chunked|micromark-util-classify-character|micromark-util-combine-extensions|micromark-util-decode-numeric-character-reference|micromark-util-decode-string|micromark-util-encode|micromark-util-html-tag-name|micromark-util-normalize-identifier|micromark-util-resolve-all|micromark-util-sanitize-uri|micromark-util-subtokenize|micromark-util-symbol|micromark-util-types|decode-named-character-reference|remark-rehype|mdast-util-to-hast|unist-util-position|trim-lines|unist-util-visit|unist-util-visit-parents|unist-util-is|unist-util-generated|mdast-util-definitions|property-information|hast-util-whitespace|space-separated-tokens|comma-separated-tokens|rehype-raw|hast-util-raw|hast-util-from-parse5|devlop|hastscript|hast-util-parse-selector|vfile-location|web-namespaces|hast-util-to-parse5|zwitch|html-void-elements)/)',
|
||||
'node_modules/(?!(\\.pnpm|lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@chenglou/pretext|@signozhq/design-tokens|@signozhq|date-fns|d3-interpolate|d3-color|api|@codemirror|@lezer|@marijn|@grafana|nuqs|uuid|copy-text-to-clipboard|react-markdown|vfile|vfile-message|unist-util-stringify-position|unified|bail|is-plain-obj|trough|remark-parse|mdast-util-from-markdown|mdast-util-to-string|micromark|micromark-core-commonmark|micromark-extension-gfm|micromark-extension-gfm-autolink-literal|micromark-extension-gfm-footnote|micromark-extension-gfm-strikethrough|micromark-extension-gfm-table|micromark-extension-gfm-tagfilter|micromark-extension-gfm-task-list-item|micromark-factory-destination|micromark-factory-label|micromark-factory-space|micromark-factory-title|micromark-factory-whitespace|micromark-util-character|micromark-util-chunked|micromark-util-classify-character|micromark-util-combine-extensions|micromark-util-decode-numeric-character-reference|micromark-util-decode-string|micromark-util-encode|micromark-util-html-tag-name|micromark-util-normalize-identifier|micromark-util-resolve-all|micromark-util-sanitize-uri|micromark-util-subtokenize|micromark-util-symbol|micromark-util-types|decode-named-character-reference|remark-rehype|mdast-util-to-hast|unist-util-position|trim-lines|unist-util-visit|unist-util-visit-parents|unist-util-is|unist-util-generated|mdast-util-definitions|property-information|hast-util-whitespace|space-separated-tokens|comma-separated-tokens|rehype-raw|hast-util-raw|hast-util-from-parse5|devlop|hastscript|hast-util-parse-selector|vfile-location|web-namespaces|hast-util-to-parse5|zwitch|html-void-elements)/)',
|
||||
// Pattern 2: pnpm virtual store — ignore everything except ESM-only packages.
|
||||
// pnpm encodes scoped packages as @scope+name@version, so match on scope prefix.
|
||||
'node_modules/\\.pnpm/(?!(react-json-tree|react-base16-styling|lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@chenglou|@signozhq|date-fns|d3-interpolate|d3-color|api|@codemirror|@lezer|@marijn|@grafana|nuqs|uuid|copy-text-to-clipboard|react-markdown|vfile|vfile-message|unist-util-stringify-position|unified|bail|is-plain-obj|trough|remark-parse|mdast-util-from-markdown|mdast-util-to-string|micromark|decode-named-character-reference|remark-rehype|mdast-util-to-hast|unist-util-position|trim-lines|unist-util-visit|unist-util-visit-parents|unist-util-is|unist-util-generated|mdast-util-definitions|property-information|hast-util-whitespace|space-separated-tokens|comma-separated-tokens|rehype-raw|hast-util-raw|hast-util-from-parse5|devlop|hastscript|hast-util-parse-selector|vfile-location|web-namespaces|hast-util-to-parse5|zwitch|html-void-elements)[^/]*/node_modules)',
|
||||
'node_modules/\\.pnpm/(?!(lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@chenglou|@signozhq|date-fns|d3-interpolate|d3-color|api|@codemirror|@lezer|@marijn|@grafana|nuqs|uuid|copy-text-to-clipboard|react-markdown|vfile|vfile-message|unist-util-stringify-position|unified|bail|is-plain-obj|trough|remark-parse|mdast-util-from-markdown|mdast-util-to-string|micromark|decode-named-character-reference|remark-rehype|mdast-util-to-hast|unist-util-position|trim-lines|unist-util-visit|unist-util-visit-parents|unist-util-is|unist-util-generated|mdast-util-definitions|property-information|hast-util-whitespace|space-separated-tokens|comma-separated-tokens|rehype-raw|hast-util-raw|hast-util-from-parse5|devlop|hastscript|hast-util-parse-selector|vfile-location|web-namespaces|hast-util-to-parse5|zwitch|html-void-elements)[^/]*/node_modules)',
|
||||
],
|
||||
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
|
||||
testPathIgnorePatterns: ['/node_modules/', '/public/'],
|
||||
|
||||
@@ -62,40 +62,6 @@ if (typeof window.ResizeObserver === 'undefined') {
|
||||
(window as any).ResizeObserver = ResizeObserverMock;
|
||||
}
|
||||
|
||||
if (typeof globalThis.DOMRect === 'undefined') {
|
||||
(globalThis as any).DOMRect = class DOMRect {
|
||||
x = 0;
|
||||
y = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
top = 0;
|
||||
right = 0;
|
||||
bottom = 0;
|
||||
left = 0;
|
||||
constructor(x = 0, y = 0, width = 0, height = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.top = y;
|
||||
this.right = x + width;
|
||||
this.bottom = y + height;
|
||||
this.left = x;
|
||||
}
|
||||
toJSON(): any {
|
||||
return { x: this.x, y: this.y, width: this.width, height: this.height };
|
||||
}
|
||||
static fromRect(rect?: {
|
||||
x?: number;
|
||||
y?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}): DOMRect {
|
||||
return new DOMRect(rect?.x, rect?.y, rect?.width, rect?.height);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Patch getComputedStyle to handle CSS parsing errors from @signozhq/* packages.
|
||||
// These packages inject CSS at import time via style-inject / vite-plugin-css-injected-by-js.
|
||||
// jsdom's nwsapi cannot parse some of the injected selectors (e.g. Tailwind's :animate-in),
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"lint:generated": "oxlint ./src/api/generated --fix",
|
||||
"lint:fix": "oxlint ./src --fix",
|
||||
"lint:styles": "stylelint \"src/**/*.scss\"",
|
||||
"test:plugins": "node --test \"plugins/__tests__/*.test.mjs\"",
|
||||
"jest": "jest",
|
||||
"jest:coverage": "jest --coverage",
|
||||
"jest:watch": "jest --watch",
|
||||
@@ -48,9 +47,9 @@
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@sentry/react": "10.57.0",
|
||||
"@sentry/vite-plugin": "5.3.0",
|
||||
"@signozhq/design-tokens": "2.1.6",
|
||||
"@signozhq/design-tokens": "2.1.4",
|
||||
"@signozhq/icons": "0.4.0",
|
||||
"@signozhq/ui": "0.1.0",
|
||||
"@signozhq/ui": "0.0.23",
|
||||
"@tanstack/react-table": "8.21.3",
|
||||
"@tanstack/react-virtual": "3.13.22",
|
||||
"@uiw/codemirror-theme-copilot": "4.23.11",
|
||||
@@ -88,7 +87,6 @@
|
||||
"jest": "30.2.0",
|
||||
"js-base64": "^3.7.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"monaco-editor": "0.55.1",
|
||||
"motion": "12.4.13",
|
||||
"nuqs": "2.8.8",
|
||||
"overlayscrollbars": "^2.16.0",
|
||||
@@ -127,7 +125,6 @@
|
||||
"rrule": "2.8.1",
|
||||
"styled-components": "^5.3.11",
|
||||
"timestamp-nano": "^1.0.0",
|
||||
"translation-resilience": "^0.2.0",
|
||||
"typescript": "5.9.3",
|
||||
"uplot": "1.6.31",
|
||||
"uuid": "14.0.1",
|
||||
@@ -197,7 +194,7 @@
|
||||
"oxfmt": "0.54.0",
|
||||
"oxlint": "1.69.0",
|
||||
"oxlint-tsgolint": "0.23.0",
|
||||
"postcss": "8.5.26",
|
||||
"postcss": "8.5.14",
|
||||
"postcss-scss": "4.0.9",
|
||||
"react-resizable": "3.0.4",
|
||||
"redux-mock-store": "1.5.4",
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
# Plugin rule tests
|
||||
|
||||
Tests for the custom oxlint rules in `plugins/rules/`.
|
||||
|
||||
```bash
|
||||
pnpm test:plugins
|
||||
```
|
||||
|
||||
Runs on `node --test` rather than jest. The jest config is built for application
|
||||
code — jsdom, ts-jest ESM transforms, a large `transformIgnorePatterns` wall —
|
||||
and none of it applies to a suite whose only job is to shell out to the linter.
|
||||
|
||||
## Why it drives the real binary
|
||||
|
||||
Each case is written to a temp file and linted by the actual `oxlint` binary,
|
||||
with every builtin category switched off so the only diagnostics that can appear
|
||||
belong to the rule under test. Assertions therefore describe what CI enforces.
|
||||
|
||||
The alternative — walking the AST in-process — would need a stand-in for
|
||||
oxlint's JS plugin AST. That AST is ESTree-shaped but not ESTree, and it carries
|
||||
no type information, so a stand-in would drift from the runtime it claims to
|
||||
model and the tests would certify behaviour that never happens.
|
||||
|
||||
All cases in a suite share one `oxlint` invocation and are mapped back by
|
||||
filename. Per-case spawning costs roughly 80ms each; batching keeps both suites
|
||||
together at around 250ms.
|
||||
|
||||
## Adding a suite
|
||||
|
||||
```js
|
||||
import { ruleTester } from './rule-tester.mjs';
|
||||
|
||||
await ruleTester({
|
||||
rule: 'no-navigator-clipboard',
|
||||
valid: ['const x = 1;'],
|
||||
invalid: [
|
||||
{
|
||||
code: 'navigator.clipboard.writeText("x");',
|
||||
errors: [{ message: 'useCopyToClipboard', line: 1, column: 1 }],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
`ruleTester` must be awaited at the top level — it loads the plugin and runs
|
||||
`oxlint` before declaring the tests.
|
||||
|
||||
- `rule` — the key the plugin exports it under. `plugin` defaults to
|
||||
`plugins/signoz.mjs`; pass a path relative to `frontend/` for another plugin.
|
||||
- Cases are `.tsx` unless a `filename` gives another extension.
|
||||
- `errors` takes a count or an array. Each entry may assert `message` (substring
|
||||
or `RegExp`), `line` and `column`; omitted fields are not checked.
|
||||
- `name` labels the case in the output and defaults to its first line of code.
|
||||
- `output` asserts the source after suggestions are applied — see below.
|
||||
- `todo` marks a case as a known defect — see below.
|
||||
|
||||
## Suggestions
|
||||
|
||||
Both Google Translate rules attach their wrap as a *suggestion*, not a fix, so
|
||||
`--fix` leaves the code alone and `--fix-suggestions` applies it. The wrap is
|
||||
`<span className="translate-safe">`, and `.translate-safe` is `display: contents`
|
||||
in `src/styles.scss`: React owns an element that absorbs Translate's `<font>`
|
||||
swap, while the box tree stays as it was, so a flex or grid parent still sees one
|
||||
contiguous text run rather than a new item with its own `gap`.
|
||||
|
||||
It stays a suggestion because the element is still a DOM child even with no box:
|
||||
`> *`, `:nth-child` and sibling selectors still count it, and a component that
|
||||
inspects its children — `React.Children.map`, antd `Tooltip`, `Space` — sees an
|
||||
element where a string used to be. That is what oxlint means by "May change
|
||||
program behavior" in `--fix-suggestions --help`.
|
||||
|
||||
An invalid case carrying `output` is linted twice: once for diagnostics, and
|
||||
once with `--fix-suggestions` over an untouched copy of the same files. The
|
||||
second run costs one extra `oxlint` spawn per suite and only happens when at
|
||||
least one case asks for it.
|
||||
|
||||
```js
|
||||
{
|
||||
code: "export const A = () => <div>{f ? 'a' : 'b'}<b/></div>;",
|
||||
errors: 2,
|
||||
output:
|
||||
'export const A = () => <div>{f ? <span className="translate-safe">a</span> : <span className="translate-safe">b</span>}<b/></div>;',
|
||||
}
|
||||
```
|
||||
|
||||
Suggestions do not reformat, so a real run is `oxlint --fix-suggestions` then
|
||||
`oxfmt`.
|
||||
|
||||
## Known defects
|
||||
|
||||
A case carrying `todo` asserts what the rule *should* do. It still runs, but a
|
||||
failure is reported as a todo rather than failing the suite, so a bug can be
|
||||
pinned as an executable spec instead of prose. Fixing the rule turns the todo
|
||||
green; deleting the flag then makes it a regression guard.
|
||||
|
||||
Cases are prefixed `FP:` where the rule reports something it should not, `GAP:`
|
||||
where it misses something it should catch, and `TYPE-AWARE:` where the miss is
|
||||
only fixable once the linter can resolve types. Everything without a flag is a
|
||||
characterisation test recording current behaviour.
|
||||
|
||||
The current todos:
|
||||
|
||||
**Gaps — constructs the rules never visit.** `isProblematicConditional` requires
|
||||
a `JSXElement` parent, so a conditional inside a fragment is never inspected even
|
||||
though the failure does not care about the parent's kind. `no-return-text-nodes`
|
||||
listens only for `FunctionDeclaration` and reads the name off `node.id`, so
|
||||
arrow-function components and anonymous default exports are invisible — this
|
||||
codebase writes components as arrow functions, which is why that rule reports
|
||||
nothing across `src`.
|
||||
|
||||
Class components are left out deliberately rather than pinned as a gap: there
|
||||
are none in `src`.
|
||||
|
||||
**Type-aware gaps.** Upstream resolves branch types through
|
||||
`@typescript-eslint/utils` and reports anything typed `string` or `number`.
|
||||
oxlint's JS plugin runtime exposes no type information — `sourceCode.parserServices`
|
||||
is always `{}` — so those code paths were removed rather than left dormant. The
|
||||
`TYPE-AWARE:` todos record what they used to catch, and become the acceptance
|
||||
criteria if oxlint ever hands types to JS plugins.
|
||||
|
||||
## Not a defect
|
||||
|
||||
Without types, `no-conditional-text-nodes-with-siblings` falls back to a callee
|
||||
allowlist (`t`, `formatMessage`, `toString`, `toLocaleString`). Cases around
|
||||
that allowlist pin its edges; widening it is the supported way to catch more
|
||||
call expressions.
|
||||
|
||||
Both branches of a ternary are reported separately, so one fix can clear two
|
||||
diagnostics. That inflates the count but every reported node is genuinely a text
|
||||
node, so the cases assert both.
|
||||
@@ -1,302 +0,0 @@
|
||||
import { ruleTester } from './rule-tester.mjs';
|
||||
|
||||
const CONDITIONAL = 'Conditionally rendered text nodes with siblings';
|
||||
const PRECEDED = 'Text nodes which are preceded by a conditional expression';
|
||||
|
||||
await ruleTester({
|
||||
rule: 'no-conditional-text-nodes-with-siblings',
|
||||
valid: [
|
||||
{
|
||||
name: 'conditional text node without siblings',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? 'yes' : 'no'}\n\t</div>\n);",
|
||||
},
|
||||
{
|
||||
name: 'boolean branches are not text',
|
||||
code:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag ? true : false}\n\t\t<span>x</span>\n\t</div>\n);',
|
||||
},
|
||||
{
|
||||
name: 'null branches are not text',
|
||||
code:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag ? null : null}\n\t\t<span>x</span>\n\t</div>\n);',
|
||||
},
|
||||
{
|
||||
name: 'element branches are already wrapped',
|
||||
code:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag ? <b>y</b> : <i>n</i>}\n\t\t<span>x</span>\n\t</div>\n);',
|
||||
},
|
||||
{
|
||||
name: 'text node before the conditional is safe',
|
||||
code:
|
||||
'export const A = () => (\n\t<div>\n\t\tleading text\n\t\t{flag && <b>y</b>}\n\t</div>\n);',
|
||||
},
|
||||
{
|
||||
name: 'member expression on the condition side is not rendered',
|
||||
code:
|
||||
'export const A = () => (\n\t<div>\n\t\t{obj.name && <b>y</b>}\n\t\t<span>x</span>\n\t</div>\n);',
|
||||
},
|
||||
{
|
||||
name: 'binary comparison on the condition side is not rendered',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{obj.name === 'x' && <b>y</b>}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
},
|
||||
// An empty string renders no text node at all, so there is nothing for
|
||||
// Google Translate to wrap and nothing for React to lose. Reporting it used
|
||||
// to be the rule's most common false positive.
|
||||
{
|
||||
name: 'element branch with an empty-string fallback',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? <b>Free Trial</b> : ''}\n\t\t<span>s</span>\n\t</div>\n);",
|
||||
},
|
||||
{
|
||||
name: 'both branches empty',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? '' : ''}\n\t\t<span>s</span>\n\t</div>\n);",
|
||||
},
|
||||
{
|
||||
name: 'logical and with an empty-string right-hand side',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag && ''}\n\t\t<span>s</span>\n\t</div>\n);",
|
||||
},
|
||||
|
||||
// A template literal is checked the same way as the quoted form, so `{' '}`
|
||||
// and ``{` `}`` agree.
|
||||
{
|
||||
name: 'empty template literal branch',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? `` : ''}\n\t\t<span>s</span>\n\t</div>\n);",
|
||||
},
|
||||
{
|
||||
name: 'whitespace-only template literal branch is skipped',
|
||||
code:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag ? ` ` : <b>x</b>}\n\t\t<span>s</span>\n\t</div>\n);',
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
name: 'string literal branches with an element sibling',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? 'yes' : 'no'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 19 },
|
||||
],
|
||||
output:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag ? <span className="translate-safe">yes</span> : <span className="translate-safe">no</span>}\n\t\t<span>x</span>\n\t</div>\n);',
|
||||
},
|
||||
{
|
||||
name: 'logical and with a string right-hand side',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag && 'yes'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [{ message: CONDITIONAL, line: 3, column: 12 }],
|
||||
},
|
||||
{
|
||||
name: 'numeric literals render as text',
|
||||
code:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag ? 1 : 2}\n\t\t<span>x</span>\n\t</div>\n);',
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 15 },
|
||||
],
|
||||
output:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag ? <span className="translate-safe">{1}</span> : <span className="translate-safe">{2}</span>}\n\t\t<span>x</span>\n\t</div>\n);',
|
||||
},
|
||||
{
|
||||
name: 'template literal branch',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? `yes ${n}` : 'no'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 24 },
|
||||
],
|
||||
output:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag ? <span className="translate-safe">{`yes ${n}`}</span> : <span className="translate-safe">no</span>}\n\t\t<span>x</span>\n\t</div>\n);',
|
||||
},
|
||||
{
|
||||
name: 'member expression branch',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? obj.name : 'no'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 22 },
|
||||
],
|
||||
output:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag ? <span className="translate-safe">{obj.name}</span> : <span className="translate-safe">no</span>}\n\t\t<span>x</span>\n\t</div>\n);',
|
||||
},
|
||||
{
|
||||
name: 'a string needing escapes stays inside braces',
|
||||
code:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag ? "it\'s" : <b>x</b>}\n\t\t<span>s</span>\n\t</div>\n);',
|
||||
errors: [{ message: CONDITIONAL, line: 3, column: 11 }],
|
||||
output:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag ? <span className="translate-safe">{"it\'s"}</span> : <b>x</b>}\n\t\t<span>s</span>\n\t</div>\n);',
|
||||
},
|
||||
{
|
||||
name: 'optional chaining branch',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? obj?.deep?.name : 'no'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 29 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'nested ternary reports every text branch',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{a ? (b ? 'x' : 'y') : 'z'}\n\t\t<span>s</span>\n\t</div>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 13 },
|
||||
{ message: CONDITIONAL, line: 3, column: 19 },
|
||||
{ message: CONDITIONAL, line: 3, column: 26 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'static text following a conditional',
|
||||
code:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag && <b>y</b>}\n\t\ttrailing text\n\t</div>\n);',
|
||||
errors: [{ message: PRECEDED, line: 3, column: 21 }],
|
||||
// Only the visible run is wrapped; the surrounding newlines and tabs are
|
||||
// formatting and must stay outside the element.
|
||||
output:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag && <b>y</b>}\n\t\t<span className="translate-safe">trailing text</span>\n\t</div>\n);',
|
||||
},
|
||||
{
|
||||
name: 'conditional text plus trailing static text reports both kinds',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? 'a' : 'b'}\n\t\tliteral tail\n\t</div>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 17 },
|
||||
{ message: PRECEDED, line: 3, column: 21 },
|
||||
],
|
||||
},
|
||||
|
||||
// The callee allowlist below is the untyped fallback. Without type
|
||||
// information the rule can only recognise known string-returning helpers,
|
||||
// so `t()` and `formatMessage()` are reported while an arbitrary call is
|
||||
// not. These cases pin that boundary.
|
||||
{
|
||||
name: 't() branch is reported via the callee allowlist',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? t('key') : 'no'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 22 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'formatMessage() branch is reported via the callee allowlist',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? formatMessage({id:'k'}) : 'no'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 37 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'arbitrary call is not recognised, only the literal branch reports',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? getString() : 'no'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [{ message: CONDITIONAL, line: 3, column: 25 }],
|
||||
},
|
||||
{
|
||||
name: 'bare identifier is not recognised, only the literal branch reports',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? name : 'no'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [{ message: CONDITIONAL, line: 3, column: 18 }],
|
||||
},
|
||||
{
|
||||
name: 'toString() branch is reported',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? val.toString() : 'no'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 28 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'toLocaleString() branch is reported',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? val.toLocaleString() : 'no'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 34 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 't() in its own container following a conditional',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag && <b>y</b>}\n\t\t{t('key')}\n\t</div>\n);",
|
||||
errors: [{ message: PRECEDED, line: 4, column: 4 }],
|
||||
},
|
||||
{
|
||||
name: 'toString() in its own container following a conditional',
|
||||
code:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag && <b>y</b>}\n\t\t{val.toString()}\n\t</div>\n);',
|
||||
errors: [{ message: PRECEDED, line: 4, column: 4 }],
|
||||
// The whole container is replaced, so the result is not `{<span>{…}</span>}`.
|
||||
output:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag && <b>y</b>}\n\t\t<span className="translate-safe">{val.toString()}</span>\n\t</div>\n);',
|
||||
},
|
||||
{
|
||||
name: 'whitespace-only string branch is skipped, the other branch reports',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? ' ' : 'x'}\n\t\t<span>s</span>\n\t</div>\n);",
|
||||
errors: [{ message: CONDITIONAL, line: 3, column: 17 }],
|
||||
},
|
||||
{
|
||||
name: 'template literal holding an expression is not blank',
|
||||
code:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag ? `${n}` : <b>x</b>}\n\t\t<span>s</span>\n\t</div>\n);',
|
||||
errors: [{ message: CONDITIONAL, line: 3, column: 11 }],
|
||||
},
|
||||
|
||||
// Upstream resolves branch types through `@typescript-eslint/utils` and
|
||||
// reports anything typed `string` or `number`. oxlint's JS plugin runtime
|
||||
// exposes no type information, so those paths were dropped and only the
|
||||
// callee allowlist remains. Kept as todos: if oxlint ever hands types to JS
|
||||
// plugins these become the acceptance criteria.
|
||||
{
|
||||
todo: 'needs type information to know the call returns a string',
|
||||
name: 'TYPE-AWARE: call returning a string',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? getString() : 'no'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 25 },
|
||||
],
|
||||
},
|
||||
{
|
||||
todo: 'needs type information to know the identifier is a string',
|
||||
name: 'TYPE-AWARE: identifier holding a string',
|
||||
code:
|
||||
"export const A = () => (\n\t<div>\n\t\t{flag ? name : 'no'}\n\t\t<span>x</span>\n\t</div>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 18 },
|
||||
],
|
||||
},
|
||||
{
|
||||
todo: 'needs type information to know the identifier is a string',
|
||||
name: 'TYPE-AWARE: string identifier following a conditional',
|
||||
code:
|
||||
'export const A = () => (\n\t<div>\n\t\t{flag && <b>y</b>}\n\t\t{label}\n\t</div>\n);',
|
||||
errors: [{ message: PRECEDED, line: 4, column: 4 }],
|
||||
},
|
||||
|
||||
// `isChildOfJSXElement` matches only `JSXElement`, so a fragment parent is
|
||||
// never inspected. The Google Translate failure does not care whether the
|
||||
// parent is an element or a fragment.
|
||||
{
|
||||
todo: 'fragment parents are never inspected',
|
||||
name: 'GAP: conditional text with a sibling inside a fragment',
|
||||
code:
|
||||
"export const A = () => (\n\t<>\n\t\t{flag ? 'yes' : 'no'}\n\t\t<span>x</span>\n\t</>\n);",
|
||||
errors: [
|
||||
{ message: CONDITIONAL, line: 3, column: 11 },
|
||||
{ message: CONDITIONAL, line: 3, column: 19 },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -1,162 +0,0 @@
|
||||
import { ruleTester } from './rule-tester.mjs';
|
||||
|
||||
const RETURNS_TEXT = 'React components should avoid returning text nodes';
|
||||
|
||||
await ruleTester({
|
||||
rule: 'no-return-text-nodes',
|
||||
valid: [
|
||||
{
|
||||
name: 'lowercase function is not a component',
|
||||
code: "export function foo() {\n\treturn 'text';\n}",
|
||||
},
|
||||
{
|
||||
name: 'returning JSX',
|
||||
code: 'export function Foo() {\n\treturn <div>hi</div>;\n}',
|
||||
},
|
||||
{
|
||||
name: 'returning null',
|
||||
code: 'export function Foo() {\n\treturn null;\n}',
|
||||
},
|
||||
{
|
||||
name: 'returning boolean',
|
||||
code: 'export function Foo() {\n\treturn true;\n}',
|
||||
},
|
||||
{ name: 'bare return', code: 'export function Foo() {\n\treturn;\n}' },
|
||||
{
|
||||
name: 'returning a variable is not a literal',
|
||||
code: "export function Foo() {\n\tconst s = 'x';\n\treturn s;\n}",
|
||||
},
|
||||
{
|
||||
name: 'lowercase nested function inside a component',
|
||||
code:
|
||||
"export function Foo() {\n\tfunction helper() {\n\t\treturn 'x';\n\t}\n\treturn <div/>;\n}",
|
||||
},
|
||||
|
||||
{
|
||||
// The repo has no class components, so this is out of scope rather than
|
||||
// a gap worth closing.
|
||||
name: 'class method',
|
||||
code: "export class Foo {\n\trender() {\n\t\treturn 'text';\n\t}\n}",
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
name: 'string literal',
|
||||
code: "export function Foo() {\n\treturn 'text';\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 2, column: 2 }],
|
||||
output:
|
||||
'export function Foo() {\n\treturn <span className="translate-safe">{\'text\'}</span>;\n}',
|
||||
},
|
||||
{
|
||||
// JSX does not parse in a `.ts` file, so no suggestion is offered there.
|
||||
name: 'string literal in a non-JSX file',
|
||||
filename: 'case.ts',
|
||||
code: "export function Foo() {\n\treturn 'text';\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 2, column: 2 }],
|
||||
output: "export function Foo() {\n\treturn 'text';\n}",
|
||||
},
|
||||
{
|
||||
name: 'numeric literal',
|
||||
code: 'export function Foo() {\n\treturn 42;\n}',
|
||||
errors: [{ message: RETURNS_TEXT, line: 2, column: 2 }],
|
||||
},
|
||||
{
|
||||
name: 'template literal',
|
||||
code: 'export function Foo() {\n\treturn `text ${x}`;\n}',
|
||||
errors: [{ message: RETURNS_TEXT, line: 2, column: 2 }],
|
||||
},
|
||||
{
|
||||
name: 'inside an if consequent',
|
||||
code:
|
||||
"export function Foo() {\n\tif (a) {\n\t\treturn 'x';\n\t}\n\treturn <div/>;\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 3, column: 3 }],
|
||||
},
|
||||
{
|
||||
name: 'inside an else block',
|
||||
code:
|
||||
"export function Foo() {\n\tif (a) {\n\t\treturn <div/>;\n\t} else {\n\t\treturn 'x';\n\t}\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 5, column: 3 }],
|
||||
},
|
||||
{
|
||||
name: 'inside an else-if chain',
|
||||
code:
|
||||
"export function Foo() {\n\tif (a) {\n\t\treturn <div/>;\n\t} else if (b) {\n\t\treturn 'x';\n\t}\n\treturn null;\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 5, column: 3 }],
|
||||
},
|
||||
{
|
||||
name: 'inside a switch case',
|
||||
code:
|
||||
"export function Foo() {\n\tswitch (a) {\n\t\tcase 1:\n\t\t\treturn 'x';\n\t\tdefault:\n\t\t\treturn <div/>;\n\t}\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 4, column: 4 }],
|
||||
},
|
||||
{
|
||||
name: 'inside try, catch and finally',
|
||||
code:
|
||||
"export function Foo() {\n\ttry {\n\t\treturn 'a';\n\t} catch {\n\t\treturn 'b';\n\t} finally {\n\t\treturn 'c';\n\t}\n}",
|
||||
errors: [
|
||||
{ message: RETURNS_TEXT, line: 3, column: 3 },
|
||||
{ message: RETURNS_TEXT, line: 5, column: 3 },
|
||||
{ message: RETURNS_TEXT, line: 7, column: 3 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'inside a for loop',
|
||||
code:
|
||||
"export function Foo() {\n\tfor (let i = 0; i < 3; i++) {\n\t\treturn 'x';\n\t}\n\treturn <div/>;\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 3, column: 3 }],
|
||||
},
|
||||
{
|
||||
name: 'inside a for-of loop',
|
||||
code:
|
||||
"export function Foo() {\n\tfor (const i of list) {\n\t\treturn 'x';\n\t}\n\treturn <div/>;\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 3, column: 3 }],
|
||||
},
|
||||
{
|
||||
name: 'inside a for-in loop',
|
||||
code:
|
||||
"export function Foo() {\n\tfor (const k in obj) {\n\t\treturn 'x';\n\t}\n\treturn <div/>;\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 3, column: 3 }],
|
||||
},
|
||||
{
|
||||
name: 'inside a while loop',
|
||||
code:
|
||||
"export function Foo() {\n\twhile (a) {\n\t\treturn 'x';\n\t}\n\treturn <div/>;\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 3, column: 3 }],
|
||||
},
|
||||
{
|
||||
name: 'inside a do-while loop',
|
||||
code: "export function Foo() {\n\tdo {\n\t\treturn 'x';\n\t} while (a);\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 3, column: 3 }],
|
||||
},
|
||||
{
|
||||
name: 'capitalised nested function is treated as a component',
|
||||
code:
|
||||
"export function Foo() {\n\tfunction Helper() {\n\t\treturn 'x';\n\t}\n\treturn <div/>;\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 3, column: 3 }],
|
||||
},
|
||||
|
||||
// The rule listens only for `FunctionDeclaration` and reads the component
|
||||
// name off `node.id`. Everything below returns a text node from something
|
||||
// React renders as a component, and none of it is reported. This codebase
|
||||
// writes components as arrow functions, which is why the rule currently
|
||||
// finds nothing in `src`.
|
||||
{
|
||||
todo: 'arrow function components are never visited',
|
||||
name: 'GAP: arrow component with an expression body',
|
||||
code: "export const Foo = () => 'text';",
|
||||
errors: 1,
|
||||
},
|
||||
{
|
||||
todo: 'arrow function components are never visited',
|
||||
name: 'GAP: arrow component with a block body',
|
||||
code: "export const Foo = () => {\n\treturn 'text';\n};",
|
||||
errors: [{ message: RETURNS_TEXT, line: 2, column: 2 }],
|
||||
},
|
||||
{
|
||||
todo: 'anonymous declarations have no node.id to read a name from',
|
||||
name: 'GAP: anonymous default-exported component',
|
||||
code: "export default function () {\n\treturn 'text';\n}",
|
||||
errors: [{ message: RETURNS_TEXT, line: 2, column: 2 }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -1,257 +0,0 @@
|
||||
/**
|
||||
* Test harness for oxlint JS plugins.
|
||||
*
|
||||
* Rules are exercised through the real `oxlint` binary rather than a hand-rolled
|
||||
* AST walker, so what the tests assert is exactly what CI enforces. oxlint's JS
|
||||
* plugin AST is close to ESTree but not identical, and it exposes no type
|
||||
* information, so any in-process fake would drift from the real runtime.
|
||||
*
|
||||
* All cases in a suite are written to one temp directory and linted in a single
|
||||
* oxlint invocation, then mapped back by filename. Spawning per case costs ~80ms
|
||||
* each; batching keeps a full suite under a second.
|
||||
*/
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import {
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
|
||||
const FRONTEND_DIR = path.resolve(fileURLToPath(import.meta.url), '../../..');
|
||||
const OXLINT_BIN = path.join(FRONTEND_DIR, 'node_modules/.bin/oxlint');
|
||||
|
||||
// oxlint enables its default categories unless every one is switched off, and a
|
||||
// stray builtin diagnostic would be indistinguishable from the rule under test.
|
||||
const CATEGORIES_OFF = {
|
||||
correctness: 'off',
|
||||
suspicious: 'off',
|
||||
pedantic: 'off',
|
||||
perf: 'off',
|
||||
style: 'off',
|
||||
restriction: 'off',
|
||||
nursery: 'off',
|
||||
};
|
||||
|
||||
function normaliseCase(entry, index) {
|
||||
const testCase = typeof entry === 'string' ? { code: entry } : entry;
|
||||
const extension = testCase.filename
|
||||
? path.extname(testCase.filename).slice(1)
|
||||
: 'tsx';
|
||||
return {
|
||||
...testCase,
|
||||
index,
|
||||
basename: `case-${String(index).padStart(3, '0')}.${extension}`,
|
||||
};
|
||||
}
|
||||
|
||||
function diagnosticFilename(diagnostic) {
|
||||
const raw = diagnostic.filename ?? '';
|
||||
const asPath = raw.startsWith('file://') ? fileURLToPath(raw) : raw;
|
||||
return path.basename(asPath);
|
||||
}
|
||||
|
||||
function toError(diagnostic) {
|
||||
const span = diagnostic.labels?.[0]?.span;
|
||||
return {
|
||||
message: diagnostic.message,
|
||||
line: span?.line,
|
||||
column: span?.column,
|
||||
};
|
||||
}
|
||||
|
||||
function runOxlint(dir, configPath, extraArgs = []) {
|
||||
const args = ['--config', configPath, '--format', 'json', ...extraArgs, '.'];
|
||||
try {
|
||||
return execFileSync(OXLINT_BIN, args, {
|
||||
cwd: dir,
|
||||
encoding: 'utf8',
|
||||
// A rule that reports on every case can produce a lot of output.
|
||||
maxBuffer: 64 * 1024 * 1024,
|
||||
});
|
||||
} catch (error) {
|
||||
// oxlint exits non-zero whenever it reports a diagnostic, which is the
|
||||
// expected outcome for every `invalid` case.
|
||||
if (typeof error.stdout === 'string' && error.stdout.trim() !== '') {
|
||||
return error.stdout;
|
||||
}
|
||||
throw new Error(`oxlint failed to run:\n${error.stderr || error.message}`, {
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function writeSuite(dir, cases, { pluginPath, ruleId }) {
|
||||
for (const testCase of cases) {
|
||||
const target = path.join(dir, testCase.basename);
|
||||
mkdirSync(path.dirname(target), { recursive: true });
|
||||
writeFileSync(target, testCase.code);
|
||||
}
|
||||
|
||||
const configPath = path.join(dir, '.oxlintrc.json');
|
||||
writeFileSync(
|
||||
configPath,
|
||||
JSON.stringify({
|
||||
jsPlugins: [pluginPath],
|
||||
categories: CATEGORIES_OFF,
|
||||
rules: { [ruleId]: 'error' },
|
||||
}),
|
||||
);
|
||||
return configPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lints every case in one pass, and applies suggestions in a second pass over an
|
||||
* untouched copy when any case declares `output`.
|
||||
*
|
||||
* @returns {{errors: Map<string, object[]>, outputs: Map<string, string>}}
|
||||
*/
|
||||
function lintCases(cases, options) {
|
||||
const root = mkdtempSync(path.join(tmpdir(), 'oxlint-rule-tester-'));
|
||||
try {
|
||||
const lintDir = path.join(root, 'lint');
|
||||
mkdirSync(lintDir);
|
||||
const report = JSON.parse(
|
||||
runOxlint(lintDir, writeSuite(lintDir, cases, options)),
|
||||
);
|
||||
|
||||
const errors = new Map(cases.map((testCase) => [testCase.basename, []]));
|
||||
for (const diagnostic of report.diagnostics ?? []) {
|
||||
const bucket = errors.get(diagnosticFilename(diagnostic));
|
||||
// oxlint reports config-level problems without a filename; surfacing
|
||||
// them as a suite failure beats silently testing nothing.
|
||||
if (!bucket) {
|
||||
throw new Error(`Unexpected diagnostic: ${diagnostic.message}`);
|
||||
}
|
||||
bucket.push(toError(diagnostic));
|
||||
}
|
||||
|
||||
const outputs = new Map();
|
||||
if (cases.some((testCase) => testCase.output !== undefined)) {
|
||||
const fixDir = path.join(root, 'fix');
|
||||
mkdirSync(fixDir);
|
||||
runOxlint(fixDir, writeSuite(fixDir, cases, options), ['--fix-suggestions']);
|
||||
for (const testCase of cases) {
|
||||
outputs.set(
|
||||
testCase.basename,
|
||||
readFileSync(path.join(fixDir, testCase.basename), 'utf8'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return { errors, outputs };
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
function assertMessage(actual, expected, label) {
|
||||
if (expected instanceof RegExp) {
|
||||
assert.match(actual, expected, label);
|
||||
} else {
|
||||
assert.ok(
|
||||
actual.includes(expected),
|
||||
`${label}\n expected message to contain: ${expected}\n actual: ${actual}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function assertErrors(actual, expected, code) {
|
||||
const context = `\n--- code ---\n${code}\n--- reported ---\n${JSON.stringify(actual, null, 2)}`;
|
||||
|
||||
if (typeof expected === 'number') {
|
||||
assert.equal(actual.length, expected, `error count${context}`);
|
||||
return;
|
||||
}
|
||||
|
||||
assert.equal(actual.length, expected.length, `error count${context}`);
|
||||
expected.forEach((want, i) => {
|
||||
const got = actual[i];
|
||||
if (want.message !== undefined) {
|
||||
assertMessage(got.message, want.message, `error[${i}] message${context}`);
|
||||
}
|
||||
if (want.line !== undefined) {
|
||||
assert.equal(got.line, want.line, `error[${i}] line${context}`);
|
||||
}
|
||||
if (want.column !== undefined) {
|
||||
assert.equal(got.column, want.column, `error[${i}] column${context}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a suite for one rule.
|
||||
*
|
||||
* A case carrying `todo` asserts the behaviour the rule *should* have. It still
|
||||
* runs, but a failure is reported as a todo instead of failing the suite, so a
|
||||
* known bug can be pinned as an executable spec. Delete the flag once the rule
|
||||
* is fixed and the case starts guarding the fix.
|
||||
*
|
||||
* An invalid case carrying `output` also asserts the source after
|
||||
* `--fix-suggestions` has been applied.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {string} options.rule - rule name as exported by the plugin
|
||||
* @param {string} [options.plugin] - path to the plugin, relative to `frontend/`
|
||||
* @param {Array<string | {code: string, name?: string, filename?: string, todo?: string}>} options.valid
|
||||
* @param {Array<{code: string, name?: string, filename?: string, todo?: string, output?: string, errors: number | Array<{message?: string | RegExp, line?: number, column?: number}>}>} options.invalid
|
||||
*/
|
||||
export async function ruleTester({
|
||||
rule,
|
||||
plugin = 'plugins/signoz.mjs',
|
||||
valid = [],
|
||||
invalid = [],
|
||||
}) {
|
||||
const pluginPath = path.join(FRONTEND_DIR, plugin);
|
||||
const { default: pluginModule } = await import(pathToFileURL(pluginPath));
|
||||
|
||||
assert.ok(
|
||||
pluginModule.rules?.[rule],
|
||||
`plugin ${plugin} does not export a rule named "${rule}"`,
|
||||
);
|
||||
|
||||
const ruleId = `${pluginModule.meta.name}/${rule}`;
|
||||
const validCases = valid.map(normaliseCase);
|
||||
const invalidCases = invalid.map((entry, i) =>
|
||||
normaliseCase(entry, valid.length + i),
|
||||
);
|
||||
const { errors, outputs } = lintCases([...validCases, ...invalidCases], {
|
||||
pluginPath,
|
||||
ruleId,
|
||||
});
|
||||
|
||||
const declare = (t, testCase, expected) => {
|
||||
const label = testCase.name ?? testCase.code.trim().split('\n')[0];
|
||||
return t.test(label, { todo: testCase.todo }, () => {
|
||||
assertErrors(errors.get(testCase.basename), expected, testCase.code);
|
||||
if (testCase.output !== undefined) {
|
||||
assert.equal(
|
||||
outputs.get(testCase.basename),
|
||||
testCase.output,
|
||||
`suggestion output\n--- code ---\n${testCase.code}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
test(ruleId, async (t) => {
|
||||
await t.test('valid', async (t) => {
|
||||
for (const testCase of validCases) {
|
||||
await declare(t, testCase, 0);
|
||||
}
|
||||
});
|
||||
|
||||
await t.test('invalid', async (t) => {
|
||||
for (const testCase of invalidCases) {
|
||||
await declare(t, testCase, testCase.errors);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,313 +0,0 @@
|
||||
/**
|
||||
* Rule: no-conditional-text-nodes-with-siblings
|
||||
*
|
||||
* Conditionally rendered text nodes with siblings should be wrapped in an
|
||||
* element (for example a `<span>`), otherwise Google Translate causes a browser
|
||||
* error. Translate replaces the text node with a `<font>` wrapper, React still
|
||||
* holds a reference to the original node, and the next render throws on
|
||||
* `removeChild`.
|
||||
*
|
||||
* Adapted from https://github.com/getcouped/eslint-plugin-react-google-translate
|
||||
* (v1.0.4). The upstream rule resolves branch types through
|
||||
* `@typescript-eslint/utils`; oxlint's JS plugin runtime exposes no type
|
||||
* information, so those paths are dropped and call expressions are matched
|
||||
* against the allowlist below instead.
|
||||
*/
|
||||
|
||||
// Calls known to render as text. Without types this is the only way to
|
||||
// recognise a string-returning call; widen it to catch more helpers.
|
||||
const TEXT_RETURNING_CALLEES = new Set(['formatMessage', 't']);
|
||||
const STRINGIFY_METHODS = new Set(['toString', 'toLocaleString']);
|
||||
|
||||
function calleeName(node) {
|
||||
return node.type === 'Identifier' ? node.name : null;
|
||||
}
|
||||
|
||||
function isTextReturningCall(node) {
|
||||
const { callee } = node;
|
||||
|
||||
if (TEXT_RETURNING_CALLEES.has(calleeName(callee))) {
|
||||
return node.arguments.length > 0;
|
||||
}
|
||||
|
||||
if (callee.type === 'MemberExpression' && !callee.computed) {
|
||||
return STRINGIFY_METHODS.has(calleeName(callee.property));
|
||||
}
|
||||
|
||||
return STRINGIFY_METHODS.has(calleeName(callee));
|
||||
}
|
||||
|
||||
/**
|
||||
* True when the node renders no visible text. An empty or whitespace-only value
|
||||
* produces no DOM text node, so Google Translate has nothing to wrap and React
|
||||
* nothing to lose.
|
||||
*/
|
||||
function isBlankText(node) {
|
||||
if (node.type === 'Literal' || node.type === 'JSXText') {
|
||||
return typeof node.value === 'string' && node.value.trim() === '';
|
||||
}
|
||||
if (node.type === 'TemplateLiteral') {
|
||||
return (
|
||||
node.expressions.length === 0 &&
|
||||
node.quasis.every((quasi) => (quasi.value.cooked ?? '').trim() === '')
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isConditionallyRendered(node) {
|
||||
const parent = node.parent;
|
||||
return (
|
||||
parent?.type === 'ConditionalExpression' ||
|
||||
parent?.type === 'LogicalExpression'
|
||||
);
|
||||
}
|
||||
|
||||
function isRenderedConditional(node) {
|
||||
return (
|
||||
node.type === 'JSXExpressionContainer' &&
|
||||
(node.expression?.type === 'ConditionalExpression' ||
|
||||
node.expression?.type === 'LogicalExpression')
|
||||
);
|
||||
}
|
||||
|
||||
/** Children that produce output, i.e. everything but formatting whitespace. */
|
||||
function renderedChildren(node) {
|
||||
const children = node?.children;
|
||||
if (!children) {
|
||||
return null;
|
||||
}
|
||||
return children.filter((child) => !isBlankText(child));
|
||||
}
|
||||
|
||||
/** True when `node` is a JSX child rendered alongside at least one other child. */
|
||||
function hasSiblings(node) {
|
||||
if (!(node?.parent?.children?.length > 1)) {
|
||||
return false;
|
||||
}
|
||||
return renderedChildren(node.parent).some((child) => child !== node);
|
||||
}
|
||||
|
||||
function isPrecededByConditional(node) {
|
||||
const children = renderedChildren(node?.parent);
|
||||
if (!children) {
|
||||
return false;
|
||||
}
|
||||
return children.some(
|
||||
(child) => child.start < node.start && isRenderedConditional(child),
|
||||
);
|
||||
}
|
||||
|
||||
/** Walk out of nested conditionals so nested branches report against the outer container. */
|
||||
function getOutermostConditional(node) {
|
||||
let current = node;
|
||||
while (isConditionallyRendered(current)) {
|
||||
current = current.parent;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/** True when `node` is a conditional branch rendered directly beside other JSX children. */
|
||||
function isProblematicConditional(node) {
|
||||
if (!isConditionallyRendered(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const container = getOutermostConditional(node);
|
||||
return (
|
||||
container.parent?.type === 'JSXExpressionContainer' &&
|
||||
container.parent.parent?.type === 'JSXElement' &&
|
||||
hasSiblings(container.parent)
|
||||
);
|
||||
}
|
||||
|
||||
/** True when `node` renders after a sibling conditional, i.e. the DOM order Translate breaks. */
|
||||
function followsConditionalSibling(node) {
|
||||
return (
|
||||
node.parent?.parent?.type === 'JSXElement' &&
|
||||
hasSiblings(node.parent) &&
|
||||
isPrecededByConditional(node.parent)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* `A && B` and the test of a ternary are conditions, not rendered output.
|
||||
*/
|
||||
function isCondition(node) {
|
||||
let current = node;
|
||||
while (current.parent?.type === 'LogicalExpression') {
|
||||
if (current.parent.left === current) {
|
||||
return true;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
if (current.parent?.type === 'ConditionalExpression') {
|
||||
return current.parent.test === current;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isConditionOperand(node) {
|
||||
if (node.parent?.type === 'BinaryExpression') {
|
||||
return isCondition(node.parent);
|
||||
}
|
||||
return isCondition(node);
|
||||
}
|
||||
|
||||
// A string may only be inlined as JSX text when it needs no escaping and no
|
||||
// whitespace of its own: JSX collapses leading and trailing whitespace, and
|
||||
// these characters would either close the element or start an entity.
|
||||
const NEEDS_BRACES = /['"{}<>&\r\n]/;
|
||||
|
||||
// `display: contents`, declared in src/styles.scss. React owns the element so
|
||||
// Translate's `<font>` swap is absorbed, while the box tree stays as it was and
|
||||
// a flex or grid parent still sees one contiguous text run.
|
||||
const OPEN = '<span className="translate-safe">';
|
||||
const CLOSE = '</span>';
|
||||
|
||||
/** Wraps the reported expression so React owns an element Translate cannot replace. */
|
||||
function wrapExpression(fixer, sourceCode, node) {
|
||||
// A call reported on its own already sits in a container. Replacing the
|
||||
// container yields `<span …>{expr}</span>` rather than `{<span …>{expr}</span>}`.
|
||||
const target =
|
||||
node.parent?.type === 'JSXExpressionContainer' ? node.parent : node;
|
||||
|
||||
if (
|
||||
node.type === 'Literal' &&
|
||||
typeof node.value === 'string' &&
|
||||
!NEEDS_BRACES.test(node.value) &&
|
||||
node.value.trim() === node.value
|
||||
) {
|
||||
return fixer.replaceText(target, `${OPEN}${node.value}${CLOSE}`);
|
||||
}
|
||||
|
||||
return fixer.replaceText(
|
||||
target,
|
||||
`${OPEN}{${sourceCode.getText(node)}}${CLOSE}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps static JSX text. Only the visible run is wrapped: the node also spans
|
||||
* the formatting whitespace around it, which has to stay outside the element.
|
||||
*/
|
||||
function wrapJsxText(fixer, node) {
|
||||
const raw = node.value;
|
||||
const leading = raw.length - raw.trimStart().length;
|
||||
const trailing = raw.length - raw.trimEnd().length;
|
||||
return fixer.replaceTextRange(
|
||||
[node.start + leading, node.end - trailing],
|
||||
`${OPEN}${raw.trim()}${CLOSE}`,
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description:
|
||||
'Conditionally rendered text nodes should be wrapped in an element (for example a `<span>`), otherwise Google Translate can cause a browser error.',
|
||||
url: 'https://github.com/getcouped/eslint-plugin-react-google-translate#eslint-plugin-react-google-translate',
|
||||
},
|
||||
schema: [],
|
||||
// Wrapping adds a DOM element, which can turn into a flex/grid item or
|
||||
// break `> *` and `:nth-child` selectors, so it is offered as a suggestion
|
||||
// (`--fix-suggestions`) rather than applied by a bare `--fix`.
|
||||
hasSuggestions: true,
|
||||
messages: {
|
||||
'conditional-text-node':
|
||||
'Conditionally rendered text nodes with siblings, rendered as a direct child of a JSX element, must be wrapped so Google Translate cannot break React\'s DOM: `<span className="translate-safe">{value}</span>`. Translate replaces the bare text node with a `<font>` element and React then throws on `removeChild`. This also applies to values returned from functions, so `getString()` becomes `<span className="translate-safe">{getString()}</span>`.',
|
||||
'text-node-preceded-by-conditional':
|
||||
'Text nodes which are preceded by a conditional expression, rendered as a direct child of a JSX element, must be wrapped so Google Translate cannot break React\'s DOM: `<span className="translate-safe">text</span>`. Translate replaces the bare text node with a `<font>` element and React then throws on `removeChild`.',
|
||||
},
|
||||
},
|
||||
|
||||
createOnce(context) {
|
||||
const suggestWrap = (build) => [{ desc: 'Wrap in a <span>', fix: build }];
|
||||
|
||||
const wrap = (fixer, node) => wrapExpression(fixer, context.sourceCode, node);
|
||||
|
||||
const reportConditional = (node) => {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'conditional-text-node',
|
||||
suggest: suggestWrap((fixer) => wrap(fixer, node)),
|
||||
});
|
||||
};
|
||||
|
||||
const reportPreceded = (node, build) => {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'text-node-preceded-by-conditional',
|
||||
suggest: suggestWrap(build),
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
// String and numeric branches: `{flag ? 'yes' : 'no'}`
|
||||
Literal(node) {
|
||||
if (node.value === null || typeof node.value === 'boolean') {
|
||||
return;
|
||||
}
|
||||
if (isBlankText(node)) {
|
||||
return;
|
||||
}
|
||||
if (isProblematicConditional(node)) {
|
||||
reportConditional(node);
|
||||
}
|
||||
},
|
||||
|
||||
TemplateLiteral(node) {
|
||||
if (isBlankText(node)) {
|
||||
return;
|
||||
}
|
||||
if (isProblematicConditional(node)) {
|
||||
reportConditional(node);
|
||||
}
|
||||
},
|
||||
|
||||
// Static text rendered after a conditional: `{flag && <b/>}trailing`
|
||||
JSXText(node) {
|
||||
if (isBlankText(node)) {
|
||||
return;
|
||||
}
|
||||
if (hasSiblings(node) && isPrecededByConditional(node)) {
|
||||
reportPreceded(node, (fixer) => wrapJsxText(fixer, node));
|
||||
}
|
||||
},
|
||||
|
||||
CallExpression(node) {
|
||||
if (isCondition(node) || !isTextReturningCall(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isProblematicConditional(node)) {
|
||||
reportConditional(node);
|
||||
}
|
||||
if (followsConditionalSibling(node)) {
|
||||
reportPreceded(node, (fixer) => wrap(fixer, node));
|
||||
}
|
||||
},
|
||||
|
||||
// Values read off an object: `{flag ? user.name : 'anonymous'}`
|
||||
MemberExpression(node) {
|
||||
if (isConditionOperand(node)) {
|
||||
return;
|
||||
}
|
||||
if (isProblematicConditional(node)) {
|
||||
reportConditional(node);
|
||||
}
|
||||
},
|
||||
|
||||
// Optional chaining wraps the member expression: `{flag ? a?.b?.c : 'x'}`
|
||||
ChainExpression(node) {
|
||||
if (isConditionOperand(node)) {
|
||||
return;
|
||||
}
|
||||
if (isProblematicConditional(node)) {
|
||||
reportConditional(node);
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -1,115 +0,0 @@
|
||||
/**
|
||||
* Rule: no-return-text-nodes
|
||||
*
|
||||
* React components should not return a bare text node. Google Translate keeps
|
||||
* displaying the stale translated text after a state change and nothing throws,
|
||||
* which makes the bug very hard to track down. Numbers count too: JSX renders
|
||||
* them as text.
|
||||
*
|
||||
* Adapted from https://github.com/getcouped/eslint-plugin-react-google-translate
|
||||
* (v1.0.4). Upstream walks the function body statement by statement; this
|
||||
* version visits `ReturnStatement` directly and walks up to the enclosing
|
||||
* function, which covers the same constructs without enumerating them.
|
||||
*/
|
||||
|
||||
const FUNCTION_TYPES = new Set([
|
||||
'FunctionDeclaration',
|
||||
'FunctionExpression',
|
||||
'ArrowFunctionExpression',
|
||||
]);
|
||||
|
||||
function isTextNode(node) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
if (node.type === 'TemplateLiteral') {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
node.type === 'Literal' &&
|
||||
(typeof node.value === 'string' || typeof node.value === 'number')
|
||||
);
|
||||
}
|
||||
|
||||
function getEnclosingFunction(node) {
|
||||
let current = node.parent;
|
||||
while (current) {
|
||||
if (FUNCTION_TYPES.has(current.type)) {
|
||||
return current;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isComponentName(name) {
|
||||
return (
|
||||
typeof name === 'string' && name !== '' && name[0] === name[0].toUpperCase()
|
||||
);
|
||||
}
|
||||
|
||||
// The suggestion introduces JSX, which only parses in a JSX-enabled file.
|
||||
function allowsJsx(filename) {
|
||||
return filename.endsWith('.tsx') || filename.endsWith('.jsx');
|
||||
}
|
||||
|
||||
export default {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description:
|
||||
'React components should avoid returning text nodes directly (or numerical values which will be rendered as text). When a React component returns values other than JSX / null, Google Translate can continue to display stale values after state changes, without any error being thrown. Since this is very hard to debug it is better to avoid it altogether.',
|
||||
url: 'https://github.com/getcouped/eslint-plugin-react-google-translate#eslint-plugin-react-google-translate',
|
||||
},
|
||||
schema: [],
|
||||
// Wrapping changes what the component renders, so it is offered as a
|
||||
// suggestion (`--fix-suggestions`) rather than applied by a bare `--fix`.
|
||||
hasSuggestions: true,
|
||||
messages: {
|
||||
'return-value-is-text-node':
|
||||
'React components should avoid returning text nodes directly (or numerical values which will be rendered as text). When a React component returns values other than JSX / null, Google Translate can continue to display stale values after state changes, without any error being thrown. Since this is very hard to debug it is better to avoid it altogether.',
|
||||
},
|
||||
},
|
||||
|
||||
createOnce(context) {
|
||||
const buildSuggestion = (node) => {
|
||||
if (!allowsJsx(context.filename)) {
|
||||
return undefined;
|
||||
}
|
||||
return [
|
||||
{
|
||||
desc: 'Wrap in a <span>',
|
||||
fix: (fixer) =>
|
||||
fixer.replaceText(
|
||||
node.argument,
|
||||
`<span className="translate-safe">{${context.sourceCode.getText(node.argument)}}</span>`,
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
return {
|
||||
ReturnStatement(node) {
|
||||
if (!isTextNode(node.argument)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only named function declarations are recognised as components, so a
|
||||
// text return from a nested helper or a class method is left alone.
|
||||
const fn = getEnclosingFunction(node);
|
||||
if (fn?.type !== 'FunctionDeclaration') {
|
||||
return;
|
||||
}
|
||||
if (!isComponentName(fn.id?.name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'return-value-is-text-node',
|
||||
suggest: buildSuggestion(node),
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -13,8 +13,6 @@ import noAntdComponents from './rules/no-antd-components.mjs';
|
||||
import noSignozhqUiBarrel from './rules/no-signozhq-ui-barrel.mjs';
|
||||
import noCssModuleBracketAccess from './rules/no-css-module-bracket-access.mjs';
|
||||
import noDashboardFetchOutsideRoot from './rules/no-dashboard-fetch-outside-root.mjs';
|
||||
import noConditionalTextNodesWithSiblings from './rules/no-conditional-text-nodes-with-siblings.mjs';
|
||||
import noReturnTextNodes from './rules/no-return-text-nodes.mjs';
|
||||
|
||||
export default {
|
||||
meta: {
|
||||
@@ -29,7 +27,5 @@ export default {
|
||||
'no-signozhq-ui-barrel': noSignozhqUiBarrel,
|
||||
'no-css-module-bracket-access': noCssModuleBracketAccess,
|
||||
'no-dashboard-fetch-outside-root': noDashboardFetchOutsideRoot,
|
||||
'no-conditional-text-nodes-with-siblings': noConditionalTextNodesWithSiblings,
|
||||
'no-return-text-nodes': noReturnTextNodes,
|
||||
},
|
||||
};
|
||||
|
||||
1096
frontend/pnpm-lock.yaml
generated
1096
frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -13,16 +13,7 @@ overrides:
|
||||
'@babel/core@<=7.29.0': '>=7.29.6 <8'
|
||||
# via: jest > babel-plugin-istanbul > @istanbuljs/load-nyc-config@1.1.0 (js-yaml ^3.13.1)
|
||||
# remove: blocked — 1.1.0 is latest and still depends on js-yaml 3.x
|
||||
'@istanbuljs/load-nyc-config>js-yaml': '>=4.3.1 <5'
|
||||
# via: babel-plugin-istanbul > test-exclude@6 > minimatch@3.1.5 (^1.1.7); also glob@7
|
||||
# remove: blocked — babel-plugin-istanbul pins test-exclude@6, which pins minimatch@3
|
||||
brace-expansion@<1.1.18: '>=1.1.18 <2'
|
||||
# via: jest > glob@10 > minimatch@9.0.9 (^2.0.1); also minimatch@5.1.9 (^2.0.1)
|
||||
# remove: blocked — jest 30 resolves glob@10 > minimatch@9 internally
|
||||
'brace-expansion@>=2.0.0 <2.1.4': '>=2.1.4 <3'
|
||||
# via: eslint-plugin-sonarjs@4.0.2 (minimatch ^10.2.4) > minimatch@10.2.5 (^5.0.5)
|
||||
# remove: blocked — minimatch@10.2.6 (latest) only widens to ^5.0.8, still vulnerable
|
||||
'brace-expansion@>=5.0.0 <5.0.9': '>=5.0.9 <6'
|
||||
'@istanbuljs/load-nyc-config>js-yaml': '>=4.2.0 <5'
|
||||
# via: msw@1.3.2 (devDep) > cookie ^0.4.2
|
||||
# remove: upgrade msw to >=2 (ships cookie ^1). Do NOT open the cap: cookie >=1 is
|
||||
# ESM-only and breaks msw under jest's CJS sandbox (kills every test suite)
|
||||
@@ -36,26 +27,13 @@ overrides:
|
||||
# remove: blocked on rolldown-vite (7.3.1 is latest, still ^0.27.0);
|
||||
# orval >=8.20.0 and ts-jest >=29.4.11 already fixed on their side
|
||||
esbuild@>=0.27.3 <0.28.1: '>=0.28.1 <0.29.0'
|
||||
# via: @commitlint/cli > @commitlint/config-validator > ajv@8 (fast-uri ^3.0.1)
|
||||
# remove: blocked — ajv@8 caps fast-uri at ^3, and only 3.1.5 carries the fix
|
||||
fast-uri@<3.1.5: '>=3.1.5 <4'
|
||||
# via: direct devDep sass@1.97.3 (immutable ^5.0.2)
|
||||
# remove: blocked — plain sass bumps stay within ^5, so the floor is what pulls 5.1.8
|
||||
immutable@<5.1.8: '>=5.1.8 <6'
|
||||
# via: react-use@17.5.1 (direct, js-cookie ^2.2.1); @grafana/data > react-use@17.6.0
|
||||
# remove: bump react-use to >=17.6.1 (js-cookie ^3); @grafana/data side blocked
|
||||
js-cookie@<=3.0.5: '>=3.0.7 <4'
|
||||
# via: @orval/core@8.9.1 (devDep, js-yaml 4.1.1 EXACT pin — not deletable);
|
||||
# json-schema-to-typescript@15 > @apidevtools/json-schema-ref-parser (^4.1.0)
|
||||
# remove: upgrade orval to >=8.20.0 (drops js-yaml dependency entirely)
|
||||
'js-yaml@>=4.0.0 <4.3.1': '>=4.3.1 <5'
|
||||
# via: typescript-plugin-css-modules@5.2.0 (less ^4.2.0) > less@4.4.0 (image-size ~0.5.0)
|
||||
# remove: bump typescript-plugin-css-modules once it floors less itself; image-size has
|
||||
# no patched release at all, so dropping the dep is the only fix — less@4.5.0 did
|
||||
less@<4.5.0: '>=4.5.0 <5'
|
||||
# via: direct dep postcss (nanoid ^3.3.17)
|
||||
# remove: blocked — postcss 8.5.26 (latest) caps nanoid at ^3, fix landed in 3.3.18
|
||||
nanoid@<3.3.18: '>=3.3.18 <4'
|
||||
js-yaml@>=4.0.0 <=4.1.1: '>=4.2.0 <5'
|
||||
# via: react-syntax-highlighter@15.5.0 (prismjs ^1.27.0 + refractor@3 ~1.27.0 tilde-pinned)
|
||||
# remove: bump react-syntax-highlighter to >=16.1.1 (prismjs ^1.30.0, refractor@5)
|
||||
prismjs@<1.30.0: '>=1.30.0 <2'
|
||||
|
||||
@@ -26,75 +26,6 @@
|
||||
"tooltip_ms_teams_url": "The URL of the Microsoft Teams [webhook](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498) to send alerts to. Learn more about Microsoft Teams integration in the docs [here](https://signoz.io/docs/alerts-management/notification-channel/ms-teams/).",
|
||||
"tooltip_google_chat_url": "The URL of the Google Chat space [incoming webhook](https://developers.google.com/workspace/chat/quickstart/webhooks) to send alerts to. It must be an https URL on chat.googleapis.com.",
|
||||
"google_chat_webhook_url_invalid": "Webhook URL must be an https URL on chat.googleapis.com",
|
||||
"field_jira_site": "Site URL",
|
||||
"tooltip_jira_site": "Your Jira Cloud base URL, e.g. https://your-domain.atlassian.net. Only Jira Cloud is supported.",
|
||||
"jira_site_invalid": "Site URL must be an https URL on an atlassian.net domain",
|
||||
"jira_required_fields": "Site URL, email, API token, project and issue type are required",
|
||||
"jira_service_account_tip": "Recommended: use a Jira service account so alerts aren't reported under a personal name and the channel keeps working when someone leaves.",
|
||||
"jira_service_account_tip_link": "Learn how",
|
||||
"field_jira_email": "Email",
|
||||
"help_jira_email": "The Atlassian account email used for authentication.",
|
||||
"field_jira_api_token": "API token",
|
||||
"help_jira_api_token": "Create one at id.atlassian.com under Security → API tokens.",
|
||||
"field_jira_project": "Project key",
|
||||
"field_jira_issue_type": "Issue type",
|
||||
"help_jira_issue_type": "An issue type that exists in the project, e.g. Task, Bug or Incident.",
|
||||
"field_jira_summary": "Summary (issue title)",
|
||||
"help_jira_summary": "Template for the Jira issue title.",
|
||||
"field_jira_description": "Description",
|
||||
"help_jira_description": "Template for the issue description. Rendered as rich text with a status panel and links back to SigNoz.",
|
||||
"jira_advanced_section": "Advanced Options",
|
||||
"field_jira_priority": "Priority",
|
||||
"placeholder_jira_priority": "Leave empty to use the project default",
|
||||
"help_jira_priority": "Must match a priority in the project's scheme, e.g. High.",
|
||||
"field_jira_labels": "Labels",
|
||||
"placeholder_jira_labels": "Type a label and press Enter",
|
||||
"help_jira_labels": "signoz and a deduplication label are added automatically.",
|
||||
"field_jira_resolve_transition": "Resolve transition",
|
||||
"field_jira_reopen_transition": "Reopen transition",
|
||||
"help_jira_resolve_transition": "When the alert resolves, SigNoz moves the Jira issue to a \"Done\" status via a workflow transition. This is auto-detected — leave it empty unless your project has more than one \"Done\" transition (e.g. Done vs. Won't Do) and you want to force a specific one by name.",
|
||||
"help_jira_reopen_transition": "When a resolved alert fires again (within the reopen window), SigNoz moves the issue back out of \"Done\" to an active status via a workflow transition. This is auto-detected — leave it empty unless you want to force a specific one by name (e.g. To Do or Reopen).",
|
||||
"placeholder_jira_resolve_transition": "Auto-detected, e.g. Done",
|
||||
"placeholder_jira_reopen_transition": "Auto-detected, e.g. To Do",
|
||||
"field_jira_wont_fix_resolution": "Won't-fix resolution",
|
||||
"help_jira_wont_fix_resolution": "If the issue was resolved with this resolution (e.g. Won't Do), SigNoz won't reopen it when the alert fires again; a new issue is created instead. Leave empty to always reopen.",
|
||||
"placeholder_jira_wont_fix_resolution": "e.g. Won't Do",
|
||||
"field_jira_reopen_duration": "Reopen window",
|
||||
"placeholder_jira_reopen_duration": "e.g. 72h",
|
||||
"help_jira_reopen_duration": "If a resolved alert fires again within this window, the same ticket is reopened; after the window, a re-fire opens a new ticket instead. Default: 3d.",
|
||||
"tooltip_jira_reopen_duration": "Accepted units: m (minutes), h (hours), d (days), w (weeks), y (years) — e.g. 30m, 72h or 3d. Minimum 1m.",
|
||||
"jira_reopen_duration_invalid": "Reopen window must be a duration like 30m, 72h or 3d (minimum 1m)",
|
||||
"jsmops_tip": "Create an API integration on your JSM team's Operations page and paste its key below.",
|
||||
"jsmops_tip_link": "Learn how",
|
||||
"field_jsmops_api_key": "API key",
|
||||
"help_jsmops_api_key": "The JSM Ops integration API key, from your team's Operations → Integrations → API. Make sure the integration is turned on.",
|
||||
"field_jsmops_message": "Message (alert title)",
|
||||
"help_jsmops_message": "Template for the alert title. Truncated to 130 characters.",
|
||||
"field_jsmops_description": "Description",
|
||||
"help_jsmops_description": "Template for the alert description. Rendered as rich text; kept under 15,000 characters.",
|
||||
"jsmops_advanced_section": "Advanced Options",
|
||||
"field_jsmops_priority": "Priority",
|
||||
"help_jsmops_priority": "Template resolving to one of P1–P5. Leave as-is to map from alert severity.",
|
||||
"field_jsmops_tags": "Tags",
|
||||
"placeholder_jsmops_tags": "Type a tag and press Enter",
|
||||
"help_jsmops_tags": "Tags added to every alert.",
|
||||
"incidentio_tip": "Create an HTTP alert source in incident.io (On-call \u2192 Alert routing \u2192 Sources) and paste its URL and token below.",
|
||||
"incidentio_tip_link": "Learn how",
|
||||
"field_incidentio_url": "Alert source URL",
|
||||
"help_incidentio_url": "The alert events URL from the source's setup page, e.g. https://api.incident.io/v2/alert_events/http/<source_config_id>.",
|
||||
"field_incidentio_token": "Token",
|
||||
"help_incidentio_token": "The alert source's secret token, from the same setup page.",
|
||||
"field_incidentio_title": "Title",
|
||||
"help_incidentio_title": "Template for the alert title. Kept stable while the alert fires \u2014 incident.io ignores content updates on repeat events.",
|
||||
"field_incidentio_description": "Description",
|
||||
"help_incidentio_description": "Template for the alert description. Markdown, rendered natively by incident.io.",
|
||||
"incidentio_required_fields": "Alert source URL and token are required",
|
||||
"incidentio_url_invalid": "URL must be an incident.io alert events URL (https://api.incident.io/v2/alert_events/http/<source_config_id>)",
|
||||
"field_incidentio_metadata": "Additional metadata",
|
||||
"help_incidentio_metadata": "Key-value pairs added to every alert's metadata, on top of the alert's labels (these win on a key clash). Values may use templates, e.g. {{ .CommonLabels.severity }}.",
|
||||
"placeholder_incidentio_metadata_key": "Key",
|
||||
"placeholder_incidentio_metadata_value": "Value",
|
||||
"button_incidentio_add_metadata": "Add metadata",
|
||||
|
||||
"field_slack_recipient": "Recipient",
|
||||
"field_slack_title": "Title",
|
||||
|
||||
@@ -26,75 +26,6 @@
|
||||
"tooltip_ms_teams_url": "The URL of the Microsoft Teams [webhook](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498) to send alerts to. Learn more about Microsoft Teams integration in the docs [here](https://signoz.io/docs/alerts-management/notification-channel/ms-teams/).",
|
||||
"tooltip_google_chat_url": "The URL of the Google Chat space [incoming webhook](https://developers.google.com/workspace/chat/quickstart/webhooks) to send alerts to. It must be an https URL on chat.googleapis.com.",
|
||||
"google_chat_webhook_url_invalid": "Webhook URL must be an https URL on chat.googleapis.com",
|
||||
"field_jira_site": "Site URL",
|
||||
"tooltip_jira_site": "Your Jira Cloud base URL, e.g. https://your-domain.atlassian.net. Only Jira Cloud is supported.",
|
||||
"jira_site_invalid": "Site URL must be an https URL on an atlassian.net domain",
|
||||
"jira_required_fields": "Site URL, email, API token, project and issue type are required",
|
||||
"jira_service_account_tip": "Recommended: use a Jira service account so alerts aren't reported under a personal name and the channel keeps working when someone leaves.",
|
||||
"jira_service_account_tip_link": "Learn how",
|
||||
"field_jira_email": "Email",
|
||||
"help_jira_email": "The Atlassian account email used for authentication.",
|
||||
"field_jira_api_token": "API token",
|
||||
"help_jira_api_token": "Create one at id.atlassian.com under Security → API tokens.",
|
||||
"field_jira_project": "Project key",
|
||||
"field_jira_issue_type": "Issue type",
|
||||
"help_jira_issue_type": "An issue type that exists in the project, e.g. Task, Bug or Incident.",
|
||||
"field_jira_summary": "Summary (issue title)",
|
||||
"help_jira_summary": "Template for the Jira issue title.",
|
||||
"field_jira_description": "Description",
|
||||
"help_jira_description": "Template for the issue description. Rendered as rich text with a status panel and links back to SigNoz.",
|
||||
"jira_advanced_section": "Advanced Options",
|
||||
"field_jira_priority": "Priority",
|
||||
"placeholder_jira_priority": "Leave empty to use the project default",
|
||||
"help_jira_priority": "Must match a priority in the project's scheme, e.g. High.",
|
||||
"field_jira_labels": "Labels",
|
||||
"placeholder_jira_labels": "Type a label and press Enter",
|
||||
"help_jira_labels": "signoz and a deduplication label are added automatically.",
|
||||
"field_jira_resolve_transition": "Resolve transition",
|
||||
"field_jira_reopen_transition": "Reopen transition",
|
||||
"help_jira_resolve_transition": "When the alert resolves, SigNoz moves the Jira issue to a \"Done\" status via a workflow transition. This is auto-detected — leave it empty unless your project has more than one \"Done\" transition (e.g. Done vs. Won't Do) and you want to force a specific one by name.",
|
||||
"help_jira_reopen_transition": "When a resolved alert fires again (within the reopen window), SigNoz moves the issue back out of \"Done\" to an active status via a workflow transition. This is auto-detected — leave it empty unless you want to force a specific one by name (e.g. To Do or Reopen).",
|
||||
"placeholder_jira_resolve_transition": "Auto-detected, e.g. Done",
|
||||
"placeholder_jira_reopen_transition": "Auto-detected, e.g. To Do",
|
||||
"field_jira_wont_fix_resolution": "Won't-fix resolution",
|
||||
"help_jira_wont_fix_resolution": "If the issue was resolved with this resolution (e.g. Won't Do), SigNoz won't reopen it when the alert fires again; a new issue is created instead. Leave empty to always reopen.",
|
||||
"placeholder_jira_wont_fix_resolution": "e.g. Won't Do",
|
||||
"field_jira_reopen_duration": "Reopen window",
|
||||
"placeholder_jira_reopen_duration": "e.g. 72h",
|
||||
"help_jira_reopen_duration": "If a resolved alert fires again within this window, the same ticket is reopened; after the window, a re-fire opens a new ticket instead. Default: 3d.",
|
||||
"tooltip_jira_reopen_duration": "Accepted units: m (minutes), h (hours), d (days), w (weeks), y (years) — e.g. 30m, 72h or 3d. Minimum 1m.",
|
||||
"jira_reopen_duration_invalid": "Reopen window must be a duration like 30m, 72h or 3d (minimum 1m)",
|
||||
"jsmops_tip": "Create an API integration on your JSM team's Operations page and paste its key below.",
|
||||
"jsmops_tip_link": "Learn how",
|
||||
"field_jsmops_api_key": "API key",
|
||||
"help_jsmops_api_key": "The JSM Ops integration API key, from your team's Operations → Integrations → API. Make sure the integration is turned on.",
|
||||
"field_jsmops_message": "Message (alert title)",
|
||||
"help_jsmops_message": "Template for the alert title. Truncated to 130 characters.",
|
||||
"field_jsmops_description": "Description",
|
||||
"help_jsmops_description": "Template for the alert description. Rendered as rich text; kept under 15,000 characters.",
|
||||
"jsmops_advanced_section": "Advanced Options",
|
||||
"field_jsmops_priority": "Priority",
|
||||
"help_jsmops_priority": "Template resolving to one of P1–P5. Leave as-is to map from alert severity.",
|
||||
"field_jsmops_tags": "Tags",
|
||||
"placeholder_jsmops_tags": "Type a tag and press Enter",
|
||||
"help_jsmops_tags": "Tags added to every alert.",
|
||||
"incidentio_tip": "Create an HTTP alert source in incident.io (On-call \u2192 Alert routing \u2192 Sources) and paste its URL and token below.",
|
||||
"incidentio_tip_link": "Learn how",
|
||||
"field_incidentio_url": "Alert source URL",
|
||||
"help_incidentio_url": "The alert events URL from the source's setup page, e.g. https://api.incident.io/v2/alert_events/http/<source_config_id>.",
|
||||
"field_incidentio_token": "Token",
|
||||
"help_incidentio_token": "The alert source's secret token, from the same setup page.",
|
||||
"field_incidentio_title": "Title",
|
||||
"help_incidentio_title": "Template for the alert title. Kept stable while the alert fires \u2014 incident.io ignores content updates on repeat events.",
|
||||
"field_incidentio_description": "Description",
|
||||
"help_incidentio_description": "Template for the alert description. Markdown, rendered natively by incident.io.",
|
||||
"incidentio_required_fields": "Alert source URL and token are required",
|
||||
"incidentio_url_invalid": "URL must be an incident.io alert events URL (https://api.incident.io/v2/alert_events/http/<source_config_id>)",
|
||||
"field_incidentio_metadata": "Additional metadata",
|
||||
"help_incidentio_metadata": "Key-value pairs added to every alert's metadata, on top of the alert's labels (these win on a key clash). Values may use templates, e.g. {{ .CommonLabels.severity }}.",
|
||||
"placeholder_incidentio_metadata_key": "Key",
|
||||
"placeholder_incidentio_metadata_value": "Value",
|
||||
"button_incidentio_add_metadata": "Add metadata",
|
||||
"field_slack_recipient": "Recipient",
|
||||
"field_slack_title": "Title",
|
||||
"field_slack_description": "Description",
|
||||
|
||||
@@ -169,12 +169,12 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
// Check for workspace blocked (trial expired)
|
||||
if (!isFetchingActiveLicense && isCloudPlatform && trialInfo?.workSpaceBlock) {
|
||||
const isRouteEnabledForWorkspaceBlockedState =
|
||||
pathname === ROUTES.SETTINGS ||
|
||||
pathname === ROUTES.BILLING ||
|
||||
(isAdmin &&
|
||||
(pathname === ROUTES.ORG_SETTINGS ||
|
||||
pathname === ROUTES.MEMBERS_SETTINGS ||
|
||||
pathname === ROUTES.MY_SETTINGS));
|
||||
isAdmin &&
|
||||
(pathname === ROUTES.SETTINGS ||
|
||||
pathname === ROUTES.ORG_SETTINGS ||
|
||||
pathname === ROUTES.MEMBERS_SETTINGS ||
|
||||
pathname === ROUTES.BILLING ||
|
||||
pathname === ROUTES.MY_SETTINGS);
|
||||
|
||||
if (
|
||||
pathname !== ROUTES.WORKSPACE_LOCKED &&
|
||||
|
||||
@@ -103,30 +103,30 @@ function createMockLicense(
|
||||
overrides: Partial<LicenseResModel> = {},
|
||||
): LicenseResModel {
|
||||
return {
|
||||
id: 'test-license-id',
|
||||
eventQueue: {
|
||||
createdAt: '0',
|
||||
key: 'test-key',
|
||||
event_queue: {
|
||||
created_at: '0',
|
||||
event: LicenseEvent.NO_EVENT,
|
||||
scheduledAt: '0',
|
||||
scheduled_at: '0',
|
||||
status: '',
|
||||
updatedAt: '0',
|
||||
updated_at: '0',
|
||||
},
|
||||
state: LicenseState.ACTIVATED,
|
||||
status: LicenseStatus.VALID,
|
||||
platform: LicensePlatform.CLOUD,
|
||||
createdAt: '0',
|
||||
created_at: '0',
|
||||
plan: {
|
||||
id: '0',
|
||||
createdAt: '0',
|
||||
created_at: '0',
|
||||
description: '',
|
||||
isActive: true,
|
||||
is_active: true,
|
||||
name: '',
|
||||
updatedAt: '0',
|
||||
updated_at: '0',
|
||||
},
|
||||
freeUntil: '0',
|
||||
updatedAt: '0',
|
||||
validFrom: 0,
|
||||
validUntil: 0,
|
||||
plan_id: '0',
|
||||
free_until: '0',
|
||||
updated_at: '0',
|
||||
valid_from: 0,
|
||||
valid_until: 0,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
@@ -739,7 +739,7 @@ describe('PrivateRoute', () => {
|
||||
assertStaysOnRoute(ROUTES.MY_SETTINGS);
|
||||
});
|
||||
|
||||
it('should allow VIEWER to access /settings when workspace is blocked', () => {
|
||||
it('should redirect VIEWER to workspace locked even when trying to access settings', async () => {
|
||||
renderPrivateRoute({
|
||||
initialRoute: ROUTES.SETTINGS,
|
||||
appContext: {
|
||||
@@ -752,10 +752,10 @@ describe('PrivateRoute', () => {
|
||||
isCloudUser: true,
|
||||
});
|
||||
|
||||
assertStaysOnRoute(ROUTES.SETTINGS);
|
||||
await assertRedirectsTo(ROUTES.WORKSPACE_LOCKED);
|
||||
});
|
||||
|
||||
it('should allow VIEWER to access /settings/billing when workspace is blocked', () => {
|
||||
it('should redirect VIEWER to workspace locked when trying to access billing', async () => {
|
||||
renderPrivateRoute({
|
||||
initialRoute: ROUTES.BILLING,
|
||||
appContext: {
|
||||
@@ -768,7 +768,7 @@ describe('PrivateRoute', () => {
|
||||
isCloudUser: true,
|
||||
});
|
||||
|
||||
assertStaysOnRoute(ROUTES.BILLING);
|
||||
await assertRedirectsTo(ROUTES.WORKSPACE_LOCKED);
|
||||
});
|
||||
|
||||
it('should redirect VIEWER to workspace locked when trying to access org-settings', async () => {
|
||||
@@ -819,7 +819,7 @@ describe('PrivateRoute', () => {
|
||||
await assertRedirectsTo(ROUTES.WORKSPACE_LOCKED);
|
||||
});
|
||||
|
||||
it('should allow EDITOR to access /settings when workspace is blocked', () => {
|
||||
it('should redirect EDITOR to workspace locked when trying to access settings', async () => {
|
||||
renderPrivateRoute({
|
||||
initialRoute: ROUTES.SETTINGS,
|
||||
appContext: {
|
||||
@@ -832,7 +832,7 @@ describe('PrivateRoute', () => {
|
||||
isCloudUser: true,
|
||||
});
|
||||
|
||||
assertStaysOnRoute(ROUTES.SETTINGS);
|
||||
await assertRedirectsTo(ROUTES.WORKSPACE_LOCKED);
|
||||
});
|
||||
|
||||
it('should not redirect when already on workspace locked page', () => {
|
||||
@@ -850,22 +850,6 @@ describe('PrivateRoute', () => {
|
||||
assertStaysOnRoute(ROUTES.WORKSPACE_LOCKED);
|
||||
});
|
||||
|
||||
it('should keep a custom role (ANONYMOUS) on workspace locked instead of bouncing to unauthorized', () => {
|
||||
renderPrivateRoute({
|
||||
initialRoute: ROUTES.WORKSPACE_LOCKED,
|
||||
appContext: {
|
||||
isLoggedIn: true,
|
||||
isFetchingActiveLicense: false,
|
||||
activeLicense: createMockLicense({ platform: LicensePlatform.CLOUD }),
|
||||
trialInfo: createMockTrialInfo({ workSpaceBlock: true }),
|
||||
user: createMockUser({ role: USER_ROLES.ANONYMOUS as ROLES }),
|
||||
},
|
||||
isCloudUser: true,
|
||||
});
|
||||
|
||||
assertStaysOnRoute(ROUTES.WORKSPACE_LOCKED);
|
||||
});
|
||||
|
||||
it('should not redirect self-hosted users to workspace locked even when workSpaceBlock is true', () => {
|
||||
renderPrivateRoute({
|
||||
initialRoute: ROUTES.HOME,
|
||||
@@ -1040,24 +1024,6 @@ describe('PrivateRoute', () => {
|
||||
assertStaysOnRoute(ROUTES.WORKSPACE_SUSPENDED);
|
||||
});
|
||||
|
||||
it('should keep a custom role (ANONYMOUS) on workspace suspended instead of bouncing to unauthorized', () => {
|
||||
renderPrivateRoute({
|
||||
initialRoute: ROUTES.WORKSPACE_SUSPENDED,
|
||||
appContext: {
|
||||
isLoggedIn: true,
|
||||
isFetchingActiveLicense: false,
|
||||
activeLicense: createMockLicense({
|
||||
platform: LicensePlatform.CLOUD,
|
||||
state: LicenseState.DEFAULTED,
|
||||
}),
|
||||
user: createMockUser({ role: USER_ROLES.ANONYMOUS as ROLES }),
|
||||
},
|
||||
isCloudUser: true,
|
||||
});
|
||||
|
||||
assertStaysOnRoute(ROUTES.WORKSPACE_SUSPENDED);
|
||||
});
|
||||
|
||||
it('should not redirect self-hosted users to workspace suspended when license is defaulted', () => {
|
||||
renderPrivateRoute({
|
||||
initialRoute: ROUTES.HOME,
|
||||
@@ -1614,19 +1580,6 @@ describe('PrivateRoute', () => {
|
||||
path: ROUTES.SUPPORT,
|
||||
deniedRoles: [USER_ROLES.AUTHOR as ROLES],
|
||||
},
|
||||
WORKSPACE_LOCKED: {
|
||||
path: ROUTES.WORKSPACE_LOCKED,
|
||||
deniedRoles: DENIED_ROLES,
|
||||
},
|
||||
WORKSPACE_SUSPENDED: {
|
||||
path: ROUTES.WORKSPACE_SUSPENDED,
|
||||
deniedRoles: DENIED_ROLES,
|
||||
},
|
||||
WORKSPACE_ACCESS_RESTRICTED: {
|
||||
path: ROUTES.WORKSPACE_ACCESS_RESTRICTED,
|
||||
deniedRoles: DENIED_ROLES,
|
||||
},
|
||||
BILLING: { path: ROUTES.BILLING, deniedRoles: DENIED_ROLES },
|
||||
};
|
||||
|
||||
const authzRouteRolePairs: [string, string, ROLES][] = Object.entries(
|
||||
|
||||
@@ -376,23 +376,7 @@ function App(): JSX.Element {
|
||||
tracesSampleRate: 0, // Ref: https://github.com/SigNoz/platform-pod/issues/2393#issuecomment-4603658055
|
||||
replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
|
||||
replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
|
||||
beforeSend(event, hint) {
|
||||
const error = hint?.originalException as
|
||||
| { name?: string; code?: string | number }
|
||||
| undefined;
|
||||
|
||||
// Ignore benign aborted/cancelled requests (axios + fetch).
|
||||
if (error?.code === 'ERR_CANCELED' || error?.code === 'ECONNABORTED') {
|
||||
return null;
|
||||
}
|
||||
if (error?.name === 'AbortError') {
|
||||
return null;
|
||||
}
|
||||
// Ignore benign Monaco cancellation errors (name 'Canceled').
|
||||
if (error?.name === 'Canceled') {
|
||||
return null;
|
||||
}
|
||||
|
||||
beforeSend(event) {
|
||||
// Drop the event if its level is 'warning' or 'info'
|
||||
if (event.level === 'warning' || event.level === 'info') {
|
||||
return null;
|
||||
|
||||
@@ -102,10 +102,15 @@ export const DashboardPage = Loadable(
|
||||
() => import(/* webpackChunkName: "DashboardPage" */ 'pages/DashboardPage'),
|
||||
);
|
||||
|
||||
export const DashboardWidget = Loadable(
|
||||
() =>
|
||||
import(/* webpackChunkName: "DashboardWidgetPage" */ 'pages/DashboardWidget'),
|
||||
);
|
||||
|
||||
export const DashboardPanelEditorPage = Loadable(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "DashboardPanelEditorPage" */ 'pages/DashboardPage/PanelEditorPage/PanelEditorPage'
|
||||
/* webpackChunkName: "DashboardPanelEditorPage" */ 'pages/DashboardPageV2/PanelEditorPage/PanelEditorPage'
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
DashboardPage,
|
||||
DashboardPanelEditorPage,
|
||||
DashboardsListPage,
|
||||
DashboardWidget,
|
||||
EditRulesPage,
|
||||
ErrorDetails,
|
||||
ForgotPassword,
|
||||
@@ -182,6 +183,13 @@ const routes: AppRoutes[] = [
|
||||
isPrivate: false,
|
||||
key: 'PUBLIC_DASHBOARD',
|
||||
},
|
||||
{
|
||||
path: ROUTES.DASHBOARD_WIDGET,
|
||||
exact: true,
|
||||
component: DashboardWidget,
|
||||
isPrivate: true,
|
||||
key: 'DASHBOARD_WIDGET',
|
||||
},
|
||||
{
|
||||
path: ROUTES.DASHBOARD_PANEL_EDITOR,
|
||||
exact: true,
|
||||
|
||||
@@ -53,7 +53,7 @@ export function ErrorResponseHandler(error: AxiosError): ErrorResponse {
|
||||
};
|
||||
}
|
||||
// anything else
|
||||
console.error('ErrorResponseHandler: unclassified error');
|
||||
console.error('any');
|
||||
return {
|
||||
statusCode: 500,
|
||||
payload: null,
|
||||
|
||||
59
frontend/src/api/billing/getUsage.ts
Normal file
59
frontend/src/api/billing/getUsage.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
|
||||
export interface DayBreakdownEntry {
|
||||
timestamp: number;
|
||||
total: number;
|
||||
quantity: number;
|
||||
count: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export interface TierEntry {
|
||||
quantity: number;
|
||||
unitPrice: number;
|
||||
tierCost: number;
|
||||
}
|
||||
|
||||
export interface BreakdownEntry {
|
||||
type: string;
|
||||
unit: string;
|
||||
dayWiseBreakdown: {
|
||||
breakdown: DayBreakdownEntry[];
|
||||
};
|
||||
tiers?: TierEntry[];
|
||||
}
|
||||
|
||||
export interface UsageResponsePayloadProps {
|
||||
billingPeriodStart: number;
|
||||
billingPeriodEnd: number;
|
||||
details: {
|
||||
total: number;
|
||||
baseFee: number;
|
||||
breakdown: BreakdownEntry[];
|
||||
billTotal: number;
|
||||
};
|
||||
discount: number;
|
||||
subscriptionStatus?: string;
|
||||
}
|
||||
|
||||
const getUsage = async (
|
||||
licenseKey: string,
|
||||
): Promise<SuccessResponse<UsageResponsePayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.get(`/billing?licenseKey=${licenseKey}`);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: response.data.status,
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
export default getUsage;
|
||||
36
frontend/src/api/dashboard/public/createPublicDashboard.ts
Normal file
36
frontend/src/api/dashboard/public/createPublicDashboard.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constants';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { CreatePublicDashboardProps } from 'types/api/dashboard/public/create';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreatePublicDashboard` hook (or `createPublicDashboard` fetcher) from
|
||||
* `api/generated/services/dashboard` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const createPublicDashboard = async (
|
||||
props: CreatePublicDashboardProps,
|
||||
): Promise<SuccessResponseV2<CreatePublicDashboardProps>> => {
|
||||
|
||||
const { dashboardId, timeRangeEnabled = false, defaultTimeRange = DEFAULT_TIME_RANGE } = props;
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`/dashboards/${dashboardId}/public`,
|
||||
{ timeRangeEnabled, defaultTimeRange },
|
||||
);
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default createPublicDashboard;
|
||||
27
frontend/src/api/dashboard/public/getPublicDashboardData.ts
Normal file
27
frontend/src/api/dashboard/public/getPublicDashboardData.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { GetPublicDashboardDataProps, PayloadProps,PublicDashboardDataProps } from 'types/api/dashboard/public/get';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetPublicDashboardData` hook (or `getPublicDashboardData` fetcher) from
|
||||
* `api/generated/services/dashboard` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const getPublicDashboardData = async (props: GetPublicDashboardDataProps): Promise<SuccessResponseV2<PublicDashboardDataProps>> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>(`/public/dashboards/${props.id}`);
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default getPublicDashboardData;
|
||||
27
frontend/src/api/dashboard/public/getPublicDashboardMeta.ts
Normal file
27
frontend/src/api/dashboard/public/getPublicDashboardMeta.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { GetPublicDashboardMetaProps, PayloadProps,PublicDashboardMetaProps } from 'types/api/dashboard/public/getMeta';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetPublicDashboard` hook (or `getPublicDashboard` fetcher) from
|
||||
* `api/generated/services/dashboard` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const getPublicDashboardMeta = async (props: GetPublicDashboardMetaProps): Promise<SuccessResponseV2<PublicDashboardMetaProps>> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>(`/dashboards/${props.id}/public`);
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default getPublicDashboardMeta;
|
||||
@@ -0,0 +1,34 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { MetricRangePayloadV5 } from 'api/v5/v5';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { GetPublicDashboardWidgetDataProps } from 'types/api/dashboard/public/getWidgetData';
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useGetPublicDashboardWidgetQueryRange` hook (or `getPublicDashboardWidgetQueryRange` fetcher) from
|
||||
* `api/generated/services/dashboard` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const getPublicDashboardWidgetData = async (props: GetPublicDashboardWidgetDataProps): Promise<SuccessResponseV2<MetricRangePayloadV5>> => {
|
||||
try {
|
||||
const response = await axios.get(`/public/dashboards/${props.id}/widgets/${props.index}/query_range`, {
|
||||
params: {
|
||||
startTime: props.startTime,
|
||||
endTime: props.endTime,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default getPublicDashboardWidgetData;
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps,RevokePublicDashboardAccessProps } from 'types/api/dashboard/public/delete';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useDeletePublicDashboard` hook (or `deletePublicDashboard` fetcher) from
|
||||
* `api/generated/services/dashboard` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const revokePublicDashboardAccess = async (
|
||||
props: RevokePublicDashboardAccessProps,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
try {
|
||||
const response = await axios.delete<PayloadProps>(`/dashboards/${props.id}/public`);
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default revokePublicDashboardAccess;
|
||||
36
frontend/src/api/dashboard/public/updatePublicDashboard.ts
Normal file
36
frontend/src/api/dashboard/public/updatePublicDashboard.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { DEFAULT_TIME_RANGE } from 'container/TopNav/DateTimeSelectionV2/constants';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { UpdatePublicDashboardProps } from 'types/api/dashboard/public/update';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useUpdatePublicDashboard` hook (or `updatePublicDashboard` fetcher) from
|
||||
* `api/generated/services/dashboard` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const updatePublicDashboard = async (
|
||||
props: UpdatePublicDashboardProps,
|
||||
): Promise<SuccessResponseV2<UpdatePublicDashboardProps>> => {
|
||||
|
||||
const { dashboardId, timeRangeEnabled = false, defaultTimeRange = DEFAULT_TIME_RANGE } = props;
|
||||
|
||||
try {
|
||||
const response = await axios.put(
|
||||
`/dashboards/${dashboardId}/public`,
|
||||
{ timeRangeEnabled, defaultTimeRange },
|
||||
);
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default updatePublicDashboard;
|
||||
@@ -1,236 +0,0 @@
|
||||
/**
|
||||
* ! Do not edit manually
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
*/
|
||||
import { useQuery } from 'react-query';
|
||||
import type {
|
||||
InvalidateOptions,
|
||||
QueryClient,
|
||||
QueryFunction,
|
||||
QueryKey,
|
||||
UseQueryOptions,
|
||||
UseQueryResult,
|
||||
} from 'react-query';
|
||||
|
||||
import type {
|
||||
GetAIObservabilityFieldsKeys200,
|
||||
GetAIObservabilityFieldsKeysParams,
|
||||
GetAIObservabilityFieldsValues200,
|
||||
GetAIObservabilityFieldsValuesParams,
|
||||
RenderErrorResponseDTO,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType } from '../../../generatedAPIInstance';
|
||||
|
||||
/**
|
||||
* This endpoint returns the field keys the AI observability explorer can filter on, including the computed per-trace aggregates
|
||||
* @summary Get AI observability field keys
|
||||
*/
|
||||
export const getAIObservabilityFieldsKeys = (
|
||||
params?: GetAIObservabilityFieldsKeysParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetAIObservabilityFieldsKeys200>({
|
||||
url: `/api/v1/ai_observability/fields/keys`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetAIObservabilityFieldsKeysQueryKey = (
|
||||
params?: GetAIObservabilityFieldsKeysParams,
|
||||
) => {
|
||||
return [
|
||||
`/api/v1/ai_observability/fields/keys`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getGetAIObservabilityFieldsKeysQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getAIObservabilityFieldsKeys>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
params?: GetAIObservabilityFieldsKeysParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getAIObservabilityFieldsKeys>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetAIObservabilityFieldsKeysQueryKey(params);
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getAIObservabilityFieldsKeys>>
|
||||
> = ({ signal }) => getAIObservabilityFieldsKeys(params, signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getAIObservabilityFieldsKeys>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetAIObservabilityFieldsKeysQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getAIObservabilityFieldsKeys>>
|
||||
>;
|
||||
export type GetAIObservabilityFieldsKeysQueryError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get AI observability field keys
|
||||
*/
|
||||
|
||||
export function useGetAIObservabilityFieldsKeys<
|
||||
TData = Awaited<ReturnType<typeof getAIObservabilityFieldsKeys>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
params?: GetAIObservabilityFieldsKeysParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getAIObservabilityFieldsKeys>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetAIObservabilityFieldsKeysQueryOptions(
|
||||
params,
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get AI observability field keys
|
||||
*/
|
||||
export const invalidateGetAIObservabilityFieldsKeys = async (
|
||||
queryClient: QueryClient,
|
||||
params?: GetAIObservabilityFieldsKeysParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetAIObservabilityFieldsKeysQueryKey(params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns the values the AI observability explorer can filter a field key on
|
||||
* @summary Get AI observability field values
|
||||
*/
|
||||
export const getAIObservabilityFieldsValues = (
|
||||
params?: GetAIObservabilityFieldsValuesParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetAIObservabilityFieldsValues200>({
|
||||
url: `/api/v1/ai_observability/fields/values`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetAIObservabilityFieldsValuesQueryKey = (
|
||||
params?: GetAIObservabilityFieldsValuesParams,
|
||||
) => {
|
||||
return [
|
||||
`/api/v1/ai_observability/fields/values`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getGetAIObservabilityFieldsValuesQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getAIObservabilityFieldsValues>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
params?: GetAIObservabilityFieldsValuesParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getAIObservabilityFieldsValues>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetAIObservabilityFieldsValuesQueryKey(params);
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getAIObservabilityFieldsValues>>
|
||||
> = ({ signal }) => getAIObservabilityFieldsValues(params, signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getAIObservabilityFieldsValues>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetAIObservabilityFieldsValuesQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getAIObservabilityFieldsValues>>
|
||||
>;
|
||||
export type GetAIObservabilityFieldsValuesQueryError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get AI observability field values
|
||||
*/
|
||||
|
||||
export function useGetAIObservabilityFieldsValues<
|
||||
TData = Awaited<ReturnType<typeof getAIObservabilityFieldsValues>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
params?: GetAIObservabilityFieldsValuesParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getAIObservabilityFieldsValues>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetAIObservabilityFieldsValuesQueryOptions(
|
||||
params,
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get AI observability field values
|
||||
*/
|
||||
export const invalidateGetAIObservabilityFieldsValues = async (
|
||||
queryClient: QueryClient,
|
||||
params?: GetAIObservabilityFieldsValuesParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetAIObservabilityFieldsValuesQueryKey(params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
@@ -38,14 +38,14 @@ import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
*/
|
||||
export const listAuthDomains = (signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<ListAuthDomains200>({
|
||||
url: `/api/v2/auth_domains`,
|
||||
url: `/api/v1/domains`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getListAuthDomainsQueryKey = () => {
|
||||
return [`/api/v2/auth_domains`] as const;
|
||||
return [`/api/v1/domains`] as const;
|
||||
};
|
||||
|
||||
export const getListAuthDomainsQueryOptions = <
|
||||
@@ -125,7 +125,7 @@ export const createAuthDomain = (
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateAuthDomain201>({
|
||||
url: `/api/v2/auth_domains`,
|
||||
url: `/api/v1/domains`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: authtypesPostableAuthDomainDTO,
|
||||
@@ -208,7 +208,7 @@ export const deleteAuthDomain = (
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/auth_domains/${id}`,
|
||||
url: `/api/v1/domains/${id}`,
|
||||
method: 'DELETE',
|
||||
signal,
|
||||
});
|
||||
@@ -287,7 +287,7 @@ export const getAuthDomain = (
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetAuthDomain200>({
|
||||
url: `/api/v2/auth_domains/${id}`,
|
||||
url: `/api/v1/domains/${id}`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
@@ -296,7 +296,7 @@ export const getAuthDomain = (
|
||||
export const getGetAuthDomainQueryKey = ({
|
||||
id,
|
||||
}: GetAuthDomainPathParameters) => {
|
||||
return [`/api/v2/auth_domains/${id}`] as const;
|
||||
return [`/api/v1/domains/${id}`] as const;
|
||||
};
|
||||
|
||||
export const getGetAuthDomainQueryOptions = <
|
||||
@@ -389,7 +389,7 @@ export const updateAuthDomain = (
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/auth_domains/${id}`,
|
||||
url: `/api/v1/domains/${id}`,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: authtypesUpdatableAuthDomainDTO,
|
||||
|
||||
@@ -19,10 +19,8 @@ import type {
|
||||
|
||||
import type {
|
||||
AlertmanagertypesPostableChannelDTO,
|
||||
AlertmanagertypesPostableNotificationChannelDTO,
|
||||
AlertmanagertypesReceiverDTO,
|
||||
CreateChannel201,
|
||||
CreateNotificationChannel201,
|
||||
DeleteChannelByIDPathParameters,
|
||||
GetChannelByID200,
|
||||
GetChannelByIDPathParameters,
|
||||
@@ -649,87 +647,3 @@ export const useTestChannelDeprecated = <
|
||||
> => {
|
||||
return useMutation(getTestChannelDeprecatedMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint creates a notification channel
|
||||
* @summary Create notification channel
|
||||
*/
|
||||
export const createNotificationChannel = (
|
||||
alertmanagertypesPostableNotificationChannelDTO?: BodyType<AlertmanagertypesPostableNotificationChannelDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateNotificationChannel201>({
|
||||
url: `/api/v2/notification_channels`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: alertmanagertypesPostableNotificationChannelDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getCreateNotificationChannelMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createNotificationChannel>>,
|
||||
TError,
|
||||
{ data?: BodyType<AlertmanagertypesPostableNotificationChannelDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createNotificationChannel>>,
|
||||
TError,
|
||||
{ data?: BodyType<AlertmanagertypesPostableNotificationChannelDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['createNotificationChannel'];
|
||||
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 createNotificationChannel>>,
|
||||
{ data?: BodyType<AlertmanagertypesPostableNotificationChannelDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return createNotificationChannel(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type CreateNotificationChannelMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createNotificationChannel>>
|
||||
>;
|
||||
export type CreateNotificationChannelMutationBody =
|
||||
| BodyType<AlertmanagertypesPostableNotificationChannelDTO>
|
||||
| undefined;
|
||||
export type CreateNotificationChannelMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Create notification channel
|
||||
*/
|
||||
export const useCreateNotificationChannel = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createNotificationChannel>>,
|
||||
TError,
|
||||
{ data?: BodyType<AlertmanagertypesPostableNotificationChannelDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof createNotificationChannel>>,
|
||||
TError,
|
||||
{ data?: BodyType<AlertmanagertypesPostableNotificationChannelDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getCreateNotificationChannelMutationOptions(options));
|
||||
};
|
||||
|
||||
@@ -46,8 +46,6 @@ import type {
|
||||
GetPublicDashboardPathParameters,
|
||||
GetPublicDashboardWidgetQueryRange200,
|
||||
GetPublicDashboardWidgetQueryRangePathParameters,
|
||||
GetSystemDashboard200,
|
||||
GetSystemDashboardPathParameters,
|
||||
ListDashboardViews200,
|
||||
ListDashboardsForUserV2200,
|
||||
ListDashboardsForUserV2Params,
|
||||
@@ -1887,108 +1885,6 @@ export const useMigrateDashboardV2 = <
|
||||
> => {
|
||||
return useMutation(getMigrateDashboardV2MutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns a dashboard SigNoz ships and owns, addressed by its stable definition name (e.g. `ai-o11y-overview`) rather than its id. System dashboards are read-only and upgraded through releases. The dashboard's own `name` field carries a reserved prefix that the path segment must not include.
|
||||
* @summary Get system dashboard
|
||||
*/
|
||||
export const getSystemDashboard = (
|
||||
{ name }: GetSystemDashboardPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetSystemDashboard200>({
|
||||
url: `/api/v2/dashboards/system/${name}`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetSystemDashboardQueryKey = ({
|
||||
name,
|
||||
}: GetSystemDashboardPathParameters) => {
|
||||
return [`/api/v2/dashboards/system/${name}`] as const;
|
||||
};
|
||||
|
||||
export const getGetSystemDashboardQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getSystemDashboard>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ name }: GetSystemDashboardPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSystemDashboard>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetSystemDashboardQueryKey({ name });
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getSystemDashboard>>
|
||||
> = ({ signal }) => getSystemDashboard({ name }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!name,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSystemDashboard>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetSystemDashboardQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getSystemDashboard>>
|
||||
>;
|
||||
export type GetSystemDashboardQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get system dashboard
|
||||
*/
|
||||
|
||||
export function useGetSystemDashboard<
|
||||
TData = Awaited<ReturnType<typeof getSystemDashboard>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ name }: GetSystemDashboardPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSystemDashboard>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetSystemDashboardQueryOptions({ name }, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get system dashboard
|
||||
*/
|
||||
export const invalidateGetSystemDashboard = async (
|
||||
queryClient: QueryClient,
|
||||
{ name }: GetSystemDashboardPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetSystemDashboardQueryKey({ name }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns the sanitized v2-shape dashboard data for public access. Each panel query is reduced to a safe field subset, so filters and raw query strings are not exposed.
|
||||
* @summary Get public dashboard data (v2)
|
||||
|
||||
@@ -21,28 +21,18 @@ import type {
|
||||
CreateIngestionKey201,
|
||||
CreateIngestionKeyLimit201,
|
||||
CreateIngestionKeyLimitPathParameters,
|
||||
CreateIngestionLimit201,
|
||||
DeleteIngestionKeyLimitPathParameters,
|
||||
DeleteIngestionKeyPathParameters,
|
||||
DeleteIngestionLimitPathParameters,
|
||||
GatewaytypesDeprecatedPostableIngestionKeyLimitDTO,
|
||||
GatewaytypesPostableIngestionKeyDTO,
|
||||
GatewaytypesPostableIngestionKeyLimitDTO,
|
||||
GatewaytypesUpdatableIngestionKeyLimitDTO,
|
||||
GetIngestionKey200,
|
||||
GetIngestionKeyLimits200,
|
||||
GetIngestionKeyLimitsPathParameters,
|
||||
GetIngestionKeyPathParameters,
|
||||
GetIngestionKeys200,
|
||||
GetIngestionKeysParams,
|
||||
GetIngestionLimit200,
|
||||
GetIngestionLimitPathParameters,
|
||||
RenderErrorResponseDTO,
|
||||
SearchIngestionKeys200,
|
||||
SearchIngestionKeysParams,
|
||||
UpdateIngestionKeyLimitPathParameters,
|
||||
UpdateIngestionKeyPathParameters,
|
||||
UpdateIngestionLimitPathParameters,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
@@ -310,108 +300,6 @@ export const useDeleteIngestionKey = <
|
||||
> => {
|
||||
return useMutation(getDeleteIngestionKeyMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint returns an ingestion key for the workspace
|
||||
* @summary Get ingestion key for workspace
|
||||
*/
|
||||
export const getIngestionKey = (
|
||||
{ keyId }: GetIngestionKeyPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetIngestionKey200>({
|
||||
url: `/api/v2/gateway/ingestion_keys/${keyId}`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetIngestionKeyQueryKey = ({
|
||||
keyId,
|
||||
}: GetIngestionKeyPathParameters) => {
|
||||
return [`/api/v2/gateway/ingestion_keys/${keyId}`] as const;
|
||||
};
|
||||
|
||||
export const getGetIngestionKeyQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getIngestionKey>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ keyId }: GetIngestionKeyPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getIngestionKey>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetIngestionKeyQueryKey({ keyId });
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof getIngestionKey>>> = ({
|
||||
signal,
|
||||
}) => getIngestionKey({ keyId }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!keyId,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getIngestionKey>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetIngestionKeyQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getIngestionKey>>
|
||||
>;
|
||||
export type GetIngestionKeyQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get ingestion key for workspace
|
||||
*/
|
||||
|
||||
export function useGetIngestionKey<
|
||||
TData = Awaited<ReturnType<typeof getIngestionKey>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ keyId }: GetIngestionKeyPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getIngestionKey>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetIngestionKeyQueryOptions({ keyId }, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get ingestion key for workspace
|
||||
*/
|
||||
export const invalidateGetIngestionKey = async (
|
||||
queryClient: QueryClient,
|
||||
{ keyId }: GetIngestionKeyPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetIngestionKeyQueryKey({ keyId }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint updates an ingestion key for the workspace
|
||||
* @summary Update ingestion key for workspace
|
||||
@@ -512,122 +400,19 @@ export const useUpdateIngestionKey = <
|
||||
return useMutation(getUpdateIngestionKeyMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint returns the ingestion limits for an ingestion key
|
||||
* @summary Get limits for the ingestion key
|
||||
*/
|
||||
export const getIngestionKeyLimits = (
|
||||
{ keyId }: GetIngestionKeyLimitsPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetIngestionKeyLimits200>({
|
||||
url: `/api/v2/gateway/ingestion_keys/${keyId}/limits`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetIngestionKeyLimitsQueryKey = ({
|
||||
keyId,
|
||||
}: GetIngestionKeyLimitsPathParameters) => {
|
||||
return [`/api/v2/gateway/ingestion_keys/${keyId}/limits`] as const;
|
||||
};
|
||||
|
||||
export const getGetIngestionKeyLimitsQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getIngestionKeyLimits>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ keyId }: GetIngestionKeyLimitsPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getIngestionKeyLimits>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetIngestionKeyLimitsQueryKey({ keyId });
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getIngestionKeyLimits>>
|
||||
> = ({ signal }) => getIngestionKeyLimits({ keyId }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!keyId,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getIngestionKeyLimits>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetIngestionKeyLimitsQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getIngestionKeyLimits>>
|
||||
>;
|
||||
export type GetIngestionKeyLimitsQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get limits for the ingestion key
|
||||
*/
|
||||
|
||||
export function useGetIngestionKeyLimits<
|
||||
TData = Awaited<ReturnType<typeof getIngestionKeyLimits>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ keyId }: GetIngestionKeyLimitsPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getIngestionKeyLimits>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetIngestionKeyLimitsQueryOptions({ keyId }, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get limits for the ingestion key
|
||||
*/
|
||||
export const invalidateGetIngestionKeyLimits = async (
|
||||
queryClient: QueryClient,
|
||||
{ keyId }: GetIngestionKeyLimitsPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetIngestionKeyLimitsQueryKey({ keyId }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint creates an ingestion key limit.
|
||||
* @deprecated
|
||||
* This endpoint creates an ingestion key limit
|
||||
* @summary Create limit for the ingestion key
|
||||
*/
|
||||
export const createIngestionKeyLimit = (
|
||||
{ keyId }: CreateIngestionKeyLimitPathParameters,
|
||||
gatewaytypesDeprecatedPostableIngestionKeyLimitDTO?: BodyType<GatewaytypesDeprecatedPostableIngestionKeyLimitDTO>,
|
||||
gatewaytypesPostableIngestionKeyLimitDTO?: BodyType<GatewaytypesPostableIngestionKeyLimitDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateIngestionKeyLimit201>({
|
||||
url: `/api/v2/gateway/ingestion_keys/${keyId}/limits`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: gatewaytypesDeprecatedPostableIngestionKeyLimitDTO,
|
||||
data: gatewaytypesPostableIngestionKeyLimitDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
@@ -641,7 +426,7 @@ export const getCreateIngestionKeyLimitMutationOptions = <
|
||||
TError,
|
||||
{
|
||||
pathParams: CreateIngestionKeyLimitPathParameters;
|
||||
data?: BodyType<GatewaytypesDeprecatedPostableIngestionKeyLimitDTO>;
|
||||
data?: BodyType<GatewaytypesPostableIngestionKeyLimitDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
@@ -650,7 +435,7 @@ export const getCreateIngestionKeyLimitMutationOptions = <
|
||||
TError,
|
||||
{
|
||||
pathParams: CreateIngestionKeyLimitPathParameters;
|
||||
data?: BodyType<GatewaytypesDeprecatedPostableIngestionKeyLimitDTO>;
|
||||
data?: BodyType<GatewaytypesPostableIngestionKeyLimitDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
@@ -667,7 +452,7 @@ export const getCreateIngestionKeyLimitMutationOptions = <
|
||||
Awaited<ReturnType<typeof createIngestionKeyLimit>>,
|
||||
{
|
||||
pathParams: CreateIngestionKeyLimitPathParameters;
|
||||
data?: BodyType<GatewaytypesDeprecatedPostableIngestionKeyLimitDTO>;
|
||||
data?: BodyType<GatewaytypesPostableIngestionKeyLimitDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
@@ -682,13 +467,12 @@ export type CreateIngestionKeyLimitMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createIngestionKeyLimit>>
|
||||
>;
|
||||
export type CreateIngestionKeyLimitMutationBody =
|
||||
| BodyType<GatewaytypesDeprecatedPostableIngestionKeyLimitDTO>
|
||||
| BodyType<GatewaytypesPostableIngestionKeyLimitDTO>
|
||||
| undefined;
|
||||
export type CreateIngestionKeyLimitMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Create limit for the ingestion key
|
||||
*/
|
||||
export const useCreateIngestionKeyLimit = <
|
||||
@@ -700,7 +484,7 @@ export const useCreateIngestionKeyLimit = <
|
||||
TError,
|
||||
{
|
||||
pathParams: CreateIngestionKeyLimitPathParameters;
|
||||
data?: BodyType<GatewaytypesDeprecatedPostableIngestionKeyLimitDTO>;
|
||||
data?: BodyType<GatewaytypesPostableIngestionKeyLimitDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
@@ -709,7 +493,7 @@ export const useCreateIngestionKeyLimit = <
|
||||
TError,
|
||||
{
|
||||
pathParams: CreateIngestionKeyLimitPathParameters;
|
||||
data?: BodyType<GatewaytypesDeprecatedPostableIngestionKeyLimitDTO>;
|
||||
data?: BodyType<GatewaytypesPostableIngestionKeyLimitDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
@@ -717,7 +501,6 @@ export const useCreateIngestionKeyLimit = <
|
||||
};
|
||||
/**
|
||||
* This endpoint deletes an ingestion key limit
|
||||
* @deprecated
|
||||
* @summary Delete limit for the ingestion key
|
||||
*/
|
||||
export const deleteIngestionKeyLimit = (
|
||||
@@ -776,7 +559,6 @@ export type DeleteIngestionKeyLimitMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Delete limit for the ingestion key
|
||||
*/
|
||||
export const useDeleteIngestionKeyLimit = <
|
||||
@@ -798,8 +580,7 @@ export const useDeleteIngestionKeyLimit = <
|
||||
return useMutation(getDeleteIngestionKeyLimitMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint updates an ingestion key limit.
|
||||
* @deprecated
|
||||
* This endpoint updates an ingestion key limit
|
||||
* @summary Update limit for the ingestion key
|
||||
*/
|
||||
export const updateIngestionKeyLimit = (
|
||||
@@ -872,7 +653,6 @@ export type UpdateIngestionKeyLimitMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Update limit for the ingestion key
|
||||
*/
|
||||
export const useUpdateIngestionKeyLimit = <
|
||||
@@ -999,370 +779,3 @@ export const invalidateSearchIngestionKeys = async (
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint creates an ingestion limit for the ingestion key referenced by keyId
|
||||
* @summary Create ingestion limit
|
||||
*/
|
||||
export const createIngestionLimit = (
|
||||
gatewaytypesPostableIngestionKeyLimitDTO?: BodyType<GatewaytypesPostableIngestionKeyLimitDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateIngestionLimit201>({
|
||||
url: `/api/v2/gateway/ingestion_limits`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: gatewaytypesPostableIngestionKeyLimitDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getCreateIngestionLimitMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createIngestionLimit>>,
|
||||
TError,
|
||||
{ data?: BodyType<GatewaytypesPostableIngestionKeyLimitDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createIngestionLimit>>,
|
||||
TError,
|
||||
{ data?: BodyType<GatewaytypesPostableIngestionKeyLimitDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['createIngestionLimit'];
|
||||
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 createIngestionLimit>>,
|
||||
{ data?: BodyType<GatewaytypesPostableIngestionKeyLimitDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return createIngestionLimit(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type CreateIngestionLimitMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createIngestionLimit>>
|
||||
>;
|
||||
export type CreateIngestionLimitMutationBody =
|
||||
| BodyType<GatewaytypesPostableIngestionKeyLimitDTO>
|
||||
| undefined;
|
||||
export type CreateIngestionLimitMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Create ingestion limit
|
||||
*/
|
||||
export const useCreateIngestionLimit = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createIngestionLimit>>,
|
||||
TError,
|
||||
{ data?: BodyType<GatewaytypesPostableIngestionKeyLimitDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof createIngestionLimit>>,
|
||||
TError,
|
||||
{ data?: BodyType<GatewaytypesPostableIngestionKeyLimitDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getCreateIngestionLimitMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint deletes an ingestion limit
|
||||
* @summary Delete ingestion limit
|
||||
*/
|
||||
export const deleteIngestionLimit = (
|
||||
{ limitId }: DeleteIngestionLimitPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/gateway/ingestion_limits/${limitId}`,
|
||||
method: 'DELETE',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getDeleteIngestionLimitMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteIngestionLimit>>,
|
||||
TError,
|
||||
{ pathParams: DeleteIngestionLimitPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteIngestionLimit>>,
|
||||
TError,
|
||||
{ pathParams: DeleteIngestionLimitPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['deleteIngestionLimit'];
|
||||
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 deleteIngestionLimit>>,
|
||||
{ pathParams: DeleteIngestionLimitPathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return deleteIngestionLimit(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type DeleteIngestionLimitMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof deleteIngestionLimit>>
|
||||
>;
|
||||
|
||||
export type DeleteIngestionLimitMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Delete ingestion limit
|
||||
*/
|
||||
export const useDeleteIngestionLimit = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteIngestionLimit>>,
|
||||
TError,
|
||||
{ pathParams: DeleteIngestionLimitPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof deleteIngestionLimit>>,
|
||||
TError,
|
||||
{ pathParams: DeleteIngestionLimitPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getDeleteIngestionLimitMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint returns an ingestion limit
|
||||
* @summary Get ingestion limit
|
||||
*/
|
||||
export const getIngestionLimit = (
|
||||
{ limitId }: GetIngestionLimitPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetIngestionLimit200>({
|
||||
url: `/api/v2/gateway/ingestion_limits/${limitId}`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetIngestionLimitQueryKey = ({
|
||||
limitId,
|
||||
}: GetIngestionLimitPathParameters) => {
|
||||
return [`/api/v2/gateway/ingestion_limits/${limitId}`] as const;
|
||||
};
|
||||
|
||||
export const getGetIngestionLimitQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getIngestionLimit>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ limitId }: GetIngestionLimitPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getIngestionLimit>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetIngestionLimitQueryKey({ limitId });
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getIngestionLimit>>
|
||||
> = ({ signal }) => getIngestionLimit({ limitId }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!limitId,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getIngestionLimit>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetIngestionLimitQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getIngestionLimit>>
|
||||
>;
|
||||
export type GetIngestionLimitQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get ingestion limit
|
||||
*/
|
||||
|
||||
export function useGetIngestionLimit<
|
||||
TData = Awaited<ReturnType<typeof getIngestionLimit>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ limitId }: GetIngestionLimitPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getIngestionLimit>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetIngestionLimitQueryOptions({ limitId }, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get ingestion limit
|
||||
*/
|
||||
export const invalidateGetIngestionLimit = async (
|
||||
queryClient: QueryClient,
|
||||
{ limitId }: GetIngestionLimitPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetIngestionLimitQueryKey({ limitId }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint updates an ingestion limit
|
||||
* @summary Update ingestion limit
|
||||
*/
|
||||
export const updateIngestionLimit = (
|
||||
{ limitId }: UpdateIngestionLimitPathParameters,
|
||||
gatewaytypesUpdatableIngestionKeyLimitDTO?: BodyType<GatewaytypesUpdatableIngestionKeyLimitDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/gateway/ingestion_limits/${limitId}`,
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: gatewaytypesUpdatableIngestionKeyLimitDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUpdateIngestionLimitMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateIngestionLimit>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateIngestionLimitPathParameters;
|
||||
data?: BodyType<GatewaytypesUpdatableIngestionKeyLimitDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateIngestionLimit>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateIngestionLimitPathParameters;
|
||||
data?: BodyType<GatewaytypesUpdatableIngestionKeyLimitDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['updateIngestionLimit'];
|
||||
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 updateIngestionLimit>>,
|
||||
{
|
||||
pathParams: UpdateIngestionLimitPathParameters;
|
||||
data?: BodyType<GatewaytypesUpdatableIngestionKeyLimitDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return updateIngestionLimit(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UpdateIngestionLimitMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof updateIngestionLimit>>
|
||||
>;
|
||||
export type UpdateIngestionLimitMutationBody =
|
||||
| BodyType<GatewaytypesUpdatableIngestionKeyLimitDTO>
|
||||
| undefined;
|
||||
export type UpdateIngestionLimitMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Update ingestion limit
|
||||
*/
|
||||
export const useUpdateIngestionLimit = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateIngestionLimit>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateIngestionLimitPathParameters;
|
||||
data?: BodyType<GatewaytypesUpdatableIngestionKeyLimitDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof updateIngestionLimit>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateIngestionLimitPathParameters;
|
||||
data?: BodyType<GatewaytypesUpdatableIngestionKeyLimitDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getUpdateIngestionLimitMutationOptions(options));
|
||||
};
|
||||
|
||||
@@ -1,702 +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 {
|
||||
ActivateLicense201,
|
||||
DeleteLicensePathParameters,
|
||||
GetActiveLicense200,
|
||||
GetLicense200,
|
||||
GetLicensePathParameters,
|
||||
LicensetypesPostableLicenseDTO,
|
||||
ListLicenses200,
|
||||
RefreshLicensePathParameters,
|
||||
RenderErrorResponseDTO,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
/**
|
||||
* This endpoint validates the license key with the upstream server and activates the license for the organization.
|
||||
* @deprecated
|
||||
* @summary Activate a license.
|
||||
*/
|
||||
export const activateLicenseDeprecated = (
|
||||
licensetypesPostableLicenseDTO?: BodyType<LicensetypesPostableLicenseDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v3/licenses`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: licensetypesPostableLicenseDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getActivateLicenseDeprecatedMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof activateLicenseDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<LicensetypesPostableLicenseDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof activateLicenseDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<LicensetypesPostableLicenseDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['activateLicenseDeprecated'];
|
||||
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 activateLicenseDeprecated>>,
|
||||
{ data?: BodyType<LicensetypesPostableLicenseDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return activateLicenseDeprecated(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type ActivateLicenseDeprecatedMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof activateLicenseDeprecated>>
|
||||
>;
|
||||
export type ActivateLicenseDeprecatedMutationBody =
|
||||
| BodyType<LicensetypesPostableLicenseDTO>
|
||||
| undefined;
|
||||
export type ActivateLicenseDeprecatedMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Activate a license.
|
||||
*/
|
||||
export const useActivateLicenseDeprecated = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof activateLicenseDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<LicensetypesPostableLicenseDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof activateLicenseDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<LicensetypesPostableLicenseDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getActivateLicenseDeprecatedMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint refreshes the active license of the organization from the upstream server.
|
||||
* @deprecated
|
||||
* @summary Refresh a license.
|
||||
*/
|
||||
export const refreshLicenseDeprecated = (signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v3/licenses`,
|
||||
method: 'PUT',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getRefreshLicenseDeprecatedMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof refreshLicenseDeprecated>>,
|
||||
TError,
|
||||
void,
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof refreshLicenseDeprecated>>,
|
||||
TError,
|
||||
void,
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['refreshLicenseDeprecated'];
|
||||
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 refreshLicenseDeprecated>>,
|
||||
void
|
||||
> = () => {
|
||||
return refreshLicenseDeprecated();
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type RefreshLicenseDeprecatedMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof refreshLicenseDeprecated>>
|
||||
>;
|
||||
|
||||
export type RefreshLicenseDeprecatedMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Refresh a license.
|
||||
*/
|
||||
export const useRefreshLicenseDeprecated = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof refreshLicenseDeprecated>>,
|
||||
TError,
|
||||
void,
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof refreshLicenseDeprecated>>,
|
||||
TError,
|
||||
void,
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getRefreshLicenseDeprecatedMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint lists all the licenses of the organization.
|
||||
* @summary List licenses.
|
||||
*/
|
||||
export const listLicenses = (signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<ListLicenses200>({
|
||||
url: `/api/v4/licenses`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getListLicensesQueryKey = () => {
|
||||
return [`/api/v4/licenses`] as const;
|
||||
};
|
||||
|
||||
export const getListLicensesQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof listLicenses>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listLicenses>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getListLicensesQueryKey();
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof listLicenses>>> = ({
|
||||
signal,
|
||||
}) => listLicenses(signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listLicenses>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type ListLicensesQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof listLicenses>>
|
||||
>;
|
||||
export type ListLicensesQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary List licenses.
|
||||
*/
|
||||
|
||||
export function useListLicenses<
|
||||
TData = Awaited<ReturnType<typeof listLicenses>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listLicenses>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getListLicensesQueryOptions(options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary List licenses.
|
||||
*/
|
||||
export const invalidateListLicenses = async (
|
||||
queryClient: QueryClient,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getListLicensesQueryKey() },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint validates the license key with the upstream server and activates the license for the organization.
|
||||
* @summary Activate a license.
|
||||
*/
|
||||
export const activateLicense = (
|
||||
licensetypesPostableLicenseDTO?: BodyType<LicensetypesPostableLicenseDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<ActivateLicense201>({
|
||||
url: `/api/v4/licenses`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: licensetypesPostableLicenseDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getActivateLicenseMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof activateLicense>>,
|
||||
TError,
|
||||
{ data?: BodyType<LicensetypesPostableLicenseDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof activateLicense>>,
|
||||
TError,
|
||||
{ data?: BodyType<LicensetypesPostableLicenseDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['activateLicense'];
|
||||
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 activateLicense>>,
|
||||
{ data?: BodyType<LicensetypesPostableLicenseDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return activateLicense(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type ActivateLicenseMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof activateLicense>>
|
||||
>;
|
||||
export type ActivateLicenseMutationBody =
|
||||
| BodyType<LicensetypesPostableLicenseDTO>
|
||||
| undefined;
|
||||
export type ActivateLicenseMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Activate a license.
|
||||
*/
|
||||
export const useActivateLicense = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof activateLicense>>,
|
||||
TError,
|
||||
{ data?: BodyType<LicensetypesPostableLicenseDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof activateLicense>>,
|
||||
TError,
|
||||
{ data?: BodyType<LicensetypesPostableLicenseDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getActivateLicenseMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint deletes the license by id. Licenses managed by SigNoz Cloud cannot be deleted.
|
||||
* @summary Delete a license.
|
||||
*/
|
||||
export const deleteLicense = (
|
||||
{ id }: DeleteLicensePathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v4/licenses/${id}`,
|
||||
method: 'DELETE',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getDeleteLicenseMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteLicense>>,
|
||||
TError,
|
||||
{ pathParams: DeleteLicensePathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteLicense>>,
|
||||
TError,
|
||||
{ pathParams: DeleteLicensePathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['deleteLicense'];
|
||||
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 deleteLicense>>,
|
||||
{ pathParams: DeleteLicensePathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return deleteLicense(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type DeleteLicenseMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof deleteLicense>>
|
||||
>;
|
||||
|
||||
export type DeleteLicenseMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Delete a license.
|
||||
*/
|
||||
export const useDeleteLicense = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteLicense>>,
|
||||
TError,
|
||||
{ pathParams: DeleteLicensePathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof deleteLicense>>,
|
||||
TError,
|
||||
{ pathParams: DeleteLicensePathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getDeleteLicenseMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint gets the license by id.
|
||||
* @summary Get a license.
|
||||
*/
|
||||
export const getLicense = (
|
||||
{ id }: GetLicensePathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetLicense200>({
|
||||
url: `/api/v4/licenses/${id}`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetLicenseQueryKey = ({ id }: GetLicensePathParameters) => {
|
||||
return [`/api/v4/licenses/${id}`] as const;
|
||||
};
|
||||
|
||||
export const getGetLicenseQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getLicense>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ id }: GetLicensePathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getLicense>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getGetLicenseQueryKey({ id });
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof getLicense>>> = ({
|
||||
signal,
|
||||
}) => getLicense({ id }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<Awaited<ReturnType<typeof getLicense>>, TError, TData> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
};
|
||||
|
||||
export type GetLicenseQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getLicense>>
|
||||
>;
|
||||
export type GetLicenseQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get a license.
|
||||
*/
|
||||
|
||||
export function useGetLicense<
|
||||
TData = Awaited<ReturnType<typeof getLicense>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ id }: GetLicensePathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getLicense>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetLicenseQueryOptions({ id }, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get a license.
|
||||
*/
|
||||
export const invalidateGetLicense = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetLicensePathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetLicenseQueryKey({ id }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint refreshes the active license of the organization from the upstream server.
|
||||
* @summary Refresh a license.
|
||||
*/
|
||||
export const refreshLicense = (
|
||||
{ id }: RefreshLicensePathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v4/licenses/${id}`,
|
||||
method: 'PUT',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getRefreshLicenseMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof refreshLicense>>,
|
||||
TError,
|
||||
{ pathParams: RefreshLicensePathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof refreshLicense>>,
|
||||
TError,
|
||||
{ pathParams: RefreshLicensePathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['refreshLicense'];
|
||||
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 refreshLicense>>,
|
||||
{ pathParams: RefreshLicensePathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return refreshLicense(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type RefreshLicenseMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof refreshLicense>>
|
||||
>;
|
||||
|
||||
export type RefreshLicenseMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Refresh a license.
|
||||
*/
|
||||
export const useRefreshLicense = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof refreshLicense>>,
|
||||
TError,
|
||||
{ pathParams: RefreshLicensePathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof refreshLicense>>,
|
||||
TError,
|
||||
{ pathParams: RefreshLicensePathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getRefreshLicenseMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint gets the active license of the organization.
|
||||
* @summary Get the active license.
|
||||
*/
|
||||
export const getActiveLicense = (signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<GetActiveLicense200>({
|
||||
url: `/api/v4/licenses/active`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetActiveLicenseQueryKey = () => {
|
||||
return [`/api/v4/licenses/active`] as const;
|
||||
};
|
||||
|
||||
export const getGetActiveLicenseQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getActiveLicense>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getActiveLicense>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getGetActiveLicenseQueryKey();
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof getActiveLicense>>> = ({
|
||||
signal,
|
||||
}) => getActiveLicense(signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getActiveLicense>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetActiveLicenseQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getActiveLicense>>
|
||||
>;
|
||||
export type GetActiveLicenseQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get the active license.
|
||||
*/
|
||||
|
||||
export function useGetActiveLicense<
|
||||
TData = Awaited<ReturnType<typeof getActiveLicense>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getActiveLicense>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetActiveLicenseQueryOptions(options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get the active license.
|
||||
*/
|
||||
export const invalidateGetActiveLicense = async (
|
||||
queryClient: QueryClient,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetActiveLicenseQueryKey() },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
@@ -1,396 +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 {
|
||||
PrometheusErrorResponseSchemaDTO,
|
||||
PrometheusQueryParams,
|
||||
PrometheusQueryPostParams,
|
||||
PrometheusQueryRangeParams,
|
||||
PrometheusQueryRangePostParams,
|
||||
PrometheusSuccessResponseSchemaDTO,
|
||||
RenderErrorResponseDTO,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType } from '../../../generatedAPIInstance';
|
||||
|
||||
/**
|
||||
* Prometheus-compatible endpoint: the request and response contract is the upstream Prometheus HTTP API (https://prometheus.io/docs/prometheus/latest/querying/api/). Parameters are accepted as URL query parameters or a form-encoded body, on GET and POST alike.
|
||||
* @summary Prometheus instant query
|
||||
*/
|
||||
export const prometheusQuery = (
|
||||
params: PrometheusQueryParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<PrometheusSuccessResponseSchemaDTO>({
|
||||
url: `/prometheus/api/v1/query`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getPrometheusQueryQueryKey = (params?: PrometheusQueryParams) => {
|
||||
return [`/prometheus/api/v1/query`, ...(params ? [params] : [])] as const;
|
||||
};
|
||||
|
||||
export const getPrometheusQueryQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof prometheusQuery>>,
|
||||
TError = ErrorType<PrometheusErrorResponseSchemaDTO | RenderErrorResponseDTO>,
|
||||
>(
|
||||
params: PrometheusQueryParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof prometheusQuery>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getPrometheusQueryQueryKey(params);
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof prometheusQuery>>> = ({
|
||||
signal,
|
||||
}) => prometheusQuery(params, signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof prometheusQuery>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type PrometheusQueryQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof prometheusQuery>>
|
||||
>;
|
||||
export type PrometheusQueryQueryError = ErrorType<
|
||||
PrometheusErrorResponseSchemaDTO | RenderErrorResponseDTO
|
||||
>;
|
||||
|
||||
/**
|
||||
* @summary Prometheus instant query
|
||||
*/
|
||||
|
||||
export function usePrometheusQuery<
|
||||
TData = Awaited<ReturnType<typeof prometheusQuery>>,
|
||||
TError = ErrorType<PrometheusErrorResponseSchemaDTO | RenderErrorResponseDTO>,
|
||||
>(
|
||||
params: PrometheusQueryParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof prometheusQuery>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getPrometheusQueryQueryOptions(params, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Prometheus instant query
|
||||
*/
|
||||
export const invalidatePrometheusQuery = async (
|
||||
queryClient: QueryClient,
|
||||
params: PrometheusQueryParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getPrometheusQueryQueryKey(params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prometheus-compatible endpoint: the request and response contract is the upstream Prometheus HTTP API (https://prometheus.io/docs/prometheus/latest/querying/api/). Parameters are accepted as URL query parameters or a form-encoded body, on GET and POST alike.
|
||||
* @summary Prometheus instant query
|
||||
*/
|
||||
export const prometheusQueryPost = (
|
||||
params: PrometheusQueryPostParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<PrometheusSuccessResponseSchemaDTO>({
|
||||
url: `/prometheus/api/v1/query`,
|
||||
method: 'POST',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getPrometheusQueryPostMutationOptions = <
|
||||
TError = ErrorType<PrometheusErrorResponseSchemaDTO | RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof prometheusQueryPost>>,
|
||||
TError,
|
||||
{ params: PrometheusQueryPostParams },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof prometheusQueryPost>>,
|
||||
TError,
|
||||
{ params: PrometheusQueryPostParams },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['prometheusQueryPost'];
|
||||
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 prometheusQueryPost>>,
|
||||
{ params: PrometheusQueryPostParams }
|
||||
> = (props) => {
|
||||
const { params } = props ?? {};
|
||||
|
||||
return prometheusQueryPost(params);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type PrometheusQueryPostMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof prometheusQueryPost>>
|
||||
>;
|
||||
|
||||
export type PrometheusQueryPostMutationError = ErrorType<
|
||||
PrometheusErrorResponseSchemaDTO | RenderErrorResponseDTO
|
||||
>;
|
||||
|
||||
/**
|
||||
* @summary Prometheus instant query
|
||||
*/
|
||||
export const usePrometheusQueryPost = <
|
||||
TError = ErrorType<PrometheusErrorResponseSchemaDTO | RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof prometheusQueryPost>>,
|
||||
TError,
|
||||
{ params: PrometheusQueryPostParams },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof prometheusQueryPost>>,
|
||||
TError,
|
||||
{ params: PrometheusQueryPostParams },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getPrometheusQueryPostMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Prometheus-compatible endpoint: the request and response contract is the upstream Prometheus HTTP API (https://prometheus.io/docs/prometheus/latest/querying/api/). Parameters are accepted as URL query parameters or a form-encoded body, on GET and POST alike.
|
||||
* @summary Prometheus range query
|
||||
*/
|
||||
export const prometheusQueryRange = (
|
||||
params: PrometheusQueryRangeParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<PrometheusSuccessResponseSchemaDTO>({
|
||||
url: `/prometheus/api/v1/query_range`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getPrometheusQueryRangeQueryKey = (
|
||||
params?: PrometheusQueryRangeParams,
|
||||
) => {
|
||||
return [
|
||||
`/prometheus/api/v1/query_range`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getPrometheusQueryRangeQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof prometheusQueryRange>>,
|
||||
TError = ErrorType<PrometheusErrorResponseSchemaDTO | RenderErrorResponseDTO>,
|
||||
>(
|
||||
params: PrometheusQueryRangeParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof prometheusQueryRange>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getPrometheusQueryRangeQueryKey(params);
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof prometheusQueryRange>>
|
||||
> = ({ signal }) => prometheusQueryRange(params, signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof prometheusQueryRange>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type PrometheusQueryRangeQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof prometheusQueryRange>>
|
||||
>;
|
||||
export type PrometheusQueryRangeQueryError = ErrorType<
|
||||
PrometheusErrorResponseSchemaDTO | RenderErrorResponseDTO
|
||||
>;
|
||||
|
||||
/**
|
||||
* @summary Prometheus range query
|
||||
*/
|
||||
|
||||
export function usePrometheusQueryRange<
|
||||
TData = Awaited<ReturnType<typeof prometheusQueryRange>>,
|
||||
TError = ErrorType<PrometheusErrorResponseSchemaDTO | RenderErrorResponseDTO>,
|
||||
>(
|
||||
params: PrometheusQueryRangeParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof prometheusQueryRange>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getPrometheusQueryRangeQueryOptions(params, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Prometheus range query
|
||||
*/
|
||||
export const invalidatePrometheusQueryRange = async (
|
||||
queryClient: QueryClient,
|
||||
params: PrometheusQueryRangeParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getPrometheusQueryRangeQueryKey(params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prometheus-compatible endpoint: the request and response contract is the upstream Prometheus HTTP API (https://prometheus.io/docs/prometheus/latest/querying/api/). Parameters are accepted as URL query parameters or a form-encoded body, on GET and POST alike.
|
||||
* @summary Prometheus range query
|
||||
*/
|
||||
export const prometheusQueryRangePost = (
|
||||
params: PrometheusQueryRangePostParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<PrometheusSuccessResponseSchemaDTO>({
|
||||
url: `/prometheus/api/v1/query_range`,
|
||||
method: 'POST',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getPrometheusQueryRangePostMutationOptions = <
|
||||
TError = ErrorType<PrometheusErrorResponseSchemaDTO | RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof prometheusQueryRangePost>>,
|
||||
TError,
|
||||
{ params: PrometheusQueryRangePostParams },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof prometheusQueryRangePost>>,
|
||||
TError,
|
||||
{ params: PrometheusQueryRangePostParams },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['prometheusQueryRangePost'];
|
||||
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 prometheusQueryRangePost>>,
|
||||
{ params: PrometheusQueryRangePostParams }
|
||||
> = (props) => {
|
||||
const { params } = props ?? {};
|
||||
|
||||
return prometheusQueryRangePost(params);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type PrometheusQueryRangePostMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof prometheusQueryRangePost>>
|
||||
>;
|
||||
|
||||
export type PrometheusQueryRangePostMutationError = ErrorType<
|
||||
PrometheusErrorResponseSchemaDTO | RenderErrorResponseDTO
|
||||
>;
|
||||
|
||||
/**
|
||||
* @summary Prometheus range query
|
||||
*/
|
||||
export const usePrometheusQueryRangePost = <
|
||||
TError = ErrorType<PrometheusErrorResponseSchemaDTO | RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof prometheusQueryRangePost>>,
|
||||
TError,
|
||||
{ params: PrometheusQueryRangePostParams },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof prometheusQueryRangePost>>,
|
||||
TError,
|
||||
{ params: PrometheusQueryRangePostParams },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getPrometheusQueryRangePostMutationOptions(options));
|
||||
};
|
||||
@@ -1,316 +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 {
|
||||
GetQuickFilters200,
|
||||
GetQuickFiltersPathParameters,
|
||||
ListQuickFilters200,
|
||||
QuickfiltertypesUpdatableQuickFiltersDTO,
|
||||
RenderErrorResponseDTO,
|
||||
UpdateQuickFiltersPathParameters,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
/**
|
||||
* Returns the org's quick filters for every source, each filter as a telemetry field key.
|
||||
* @summary List quick filters
|
||||
*/
|
||||
export const listQuickFilters = (signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<ListQuickFilters200>({
|
||||
url: `/api/v2/quick_filters`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getListQuickFiltersQueryKey = () => {
|
||||
return [`/api/v2/quick_filters`] as const;
|
||||
};
|
||||
|
||||
export const getListQuickFiltersQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof listQuickFilters>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listQuickFilters>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getListQuickFiltersQueryKey();
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof listQuickFilters>>> = ({
|
||||
signal,
|
||||
}) => listQuickFilters(signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listQuickFilters>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type ListQuickFiltersQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof listQuickFilters>>
|
||||
>;
|
||||
export type ListQuickFiltersQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary List quick filters
|
||||
*/
|
||||
|
||||
export function useListQuickFilters<
|
||||
TData = Awaited<ReturnType<typeof listQuickFilters>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listQuickFilters>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getListQuickFiltersQueryOptions(options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary List quick filters
|
||||
*/
|
||||
export const invalidateListQuickFilters = async (
|
||||
queryClient: QueryClient,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getListQuickFiltersQueryKey() },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the org's quick filters for one source, each filter as a telemetry field key.
|
||||
* @summary Get a source's quick filters
|
||||
*/
|
||||
export const getQuickFilters = (
|
||||
{ source }: GetQuickFiltersPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetQuickFilters200>({
|
||||
url: `/api/v2/quick_filters/${source}`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetQuickFiltersQueryKey = ({
|
||||
source,
|
||||
}: GetQuickFiltersPathParameters) => {
|
||||
return [`/api/v2/quick_filters/${source}`] as const;
|
||||
};
|
||||
|
||||
export const getGetQuickFiltersQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getQuickFilters>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ source }: GetQuickFiltersPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getQuickFilters>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetQuickFiltersQueryKey({ source });
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof getQuickFilters>>> = ({
|
||||
signal,
|
||||
}) => getQuickFilters({ source }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!source,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getQuickFilters>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetQuickFiltersQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getQuickFilters>>
|
||||
>;
|
||||
export type GetQuickFiltersQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get a source's quick filters
|
||||
*/
|
||||
|
||||
export function useGetQuickFilters<
|
||||
TData = Awaited<ReturnType<typeof getQuickFilters>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ source }: GetQuickFiltersPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getQuickFilters>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetQuickFiltersQueryOptions({ source }, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get a source's quick filters
|
||||
*/
|
||||
export const invalidateGetQuickFilters = async (
|
||||
queryClient: QueryClient,
|
||||
{ source }: GetQuickFiltersPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetQuickFiltersQueryKey({ source }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Replaces the org's quick filters for the source named in the path.
|
||||
* @summary Update quick filters
|
||||
*/
|
||||
export const updateQuickFilters = (
|
||||
{ source }: UpdateQuickFiltersPathParameters,
|
||||
quickfiltertypesUpdatableQuickFiltersDTO?: BodyType<QuickfiltertypesUpdatableQuickFiltersDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/quick_filters/${source}`,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: quickfiltertypesUpdatableQuickFiltersDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUpdateQuickFiltersMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateQuickFilters>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateQuickFiltersPathParameters;
|
||||
data?: BodyType<QuickfiltertypesUpdatableQuickFiltersDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateQuickFilters>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateQuickFiltersPathParameters;
|
||||
data?: BodyType<QuickfiltertypesUpdatableQuickFiltersDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['updateQuickFilters'];
|
||||
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 updateQuickFilters>>,
|
||||
{
|
||||
pathParams: UpdateQuickFiltersPathParameters;
|
||||
data?: BodyType<QuickfiltertypesUpdatableQuickFiltersDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return updateQuickFilters(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UpdateQuickFiltersMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof updateQuickFilters>>
|
||||
>;
|
||||
export type UpdateQuickFiltersMutationBody =
|
||||
| BodyType<QuickfiltertypesUpdatableQuickFiltersDTO>
|
||||
| undefined;
|
||||
export type UpdateQuickFiltersMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Update quick filters
|
||||
*/
|
||||
export const useUpdateQuickFilters = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateQuickFilters>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateQuickFiltersPathParameters;
|
||||
data?: BodyType<QuickfiltertypesUpdatableQuickFiltersDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof updateQuickFilters>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateQuickFiltersPathParameters;
|
||||
data?: BodyType<QuickfiltertypesUpdatableQuickFiltersDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getUpdateQuickFiltersMutationOptions(options));
|
||||
};
|
||||
@@ -22,7 +22,10 @@ import type {
|
||||
CreateServiceAccountKey201,
|
||||
CreateServiceAccountKeyPathParameters,
|
||||
CreateServiceAccountRole201,
|
||||
CreateServiceAccountRoleDeprecated201,
|
||||
CreateServiceAccountRoleDeprecatedPathParameters,
|
||||
DeleteServiceAccountPathParameters,
|
||||
DeleteServiceAccountRoleDeprecatedPathParameters,
|
||||
DeleteServiceAccountRolePathParameters,
|
||||
GetMyServiceAccount200,
|
||||
GetServiceAccount200,
|
||||
@@ -36,6 +39,7 @@ import type {
|
||||
ListServiceAccounts200,
|
||||
RenderErrorResponseDTO,
|
||||
RevokeServiceAccountKeyPathParameters,
|
||||
ServiceaccounttypesDeprecatedPostableServiceAccountRoleDTO,
|
||||
ServiceaccounttypesPostableFactorAPIKeyDTO,
|
||||
ServiceaccounttypesPostableServiceAccountDTO,
|
||||
ServiceaccounttypesPostableServiceAccountRoleDTO,
|
||||
@@ -1249,6 +1253,194 @@ export const invalidateGetServiceAccountRoles = async (
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint assigns a role to a service account
|
||||
* @deprecated
|
||||
* @summary Create service account role
|
||||
*/
|
||||
export const createServiceAccountRoleDeprecated = (
|
||||
{ id }: CreateServiceAccountRoleDeprecatedPathParameters,
|
||||
serviceaccounttypesDeprecatedPostableServiceAccountRoleDTO?: BodyType<ServiceaccounttypesDeprecatedPostableServiceAccountRoleDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateServiceAccountRoleDeprecated201>({
|
||||
url: `/api/v1/service_accounts/${id}/roles`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: serviceaccounttypesDeprecatedPostableServiceAccountRoleDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getCreateServiceAccountRoleDeprecatedMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createServiceAccountRoleDeprecated>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: CreateServiceAccountRoleDeprecatedPathParameters;
|
||||
data?: BodyType<ServiceaccounttypesDeprecatedPostableServiceAccountRoleDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createServiceAccountRoleDeprecated>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: CreateServiceAccountRoleDeprecatedPathParameters;
|
||||
data?: BodyType<ServiceaccounttypesDeprecatedPostableServiceAccountRoleDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['createServiceAccountRoleDeprecated'];
|
||||
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 createServiceAccountRoleDeprecated>>,
|
||||
{
|
||||
pathParams: CreateServiceAccountRoleDeprecatedPathParameters;
|
||||
data?: BodyType<ServiceaccounttypesDeprecatedPostableServiceAccountRoleDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return createServiceAccountRoleDeprecated(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type CreateServiceAccountRoleDeprecatedMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createServiceAccountRoleDeprecated>>
|
||||
>;
|
||||
export type CreateServiceAccountRoleDeprecatedMutationBody =
|
||||
| BodyType<ServiceaccounttypesDeprecatedPostableServiceAccountRoleDTO>
|
||||
| undefined;
|
||||
export type CreateServiceAccountRoleDeprecatedMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Create service account role
|
||||
*/
|
||||
export const useCreateServiceAccountRoleDeprecated = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createServiceAccountRoleDeprecated>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: CreateServiceAccountRoleDeprecatedPathParameters;
|
||||
data?: BodyType<ServiceaccounttypesDeprecatedPostableServiceAccountRoleDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof createServiceAccountRoleDeprecated>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: CreateServiceAccountRoleDeprecatedPathParameters;
|
||||
data?: BodyType<ServiceaccounttypesDeprecatedPostableServiceAccountRoleDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(
|
||||
getCreateServiceAccountRoleDeprecatedMutationOptions(options),
|
||||
);
|
||||
};
|
||||
/**
|
||||
* This endpoint revokes a role from service account
|
||||
* @deprecated
|
||||
* @summary Delete service account role
|
||||
*/
|
||||
export const deleteServiceAccountRoleDeprecated = (
|
||||
{ id, rid }: DeleteServiceAccountRoleDeprecatedPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v1/service_accounts/${id}/roles/${rid}`,
|
||||
method: 'DELETE',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getDeleteServiceAccountRoleDeprecatedMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteServiceAccountRoleDeprecated>>,
|
||||
TError,
|
||||
{ pathParams: DeleteServiceAccountRoleDeprecatedPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteServiceAccountRoleDeprecated>>,
|
||||
TError,
|
||||
{ pathParams: DeleteServiceAccountRoleDeprecatedPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['deleteServiceAccountRoleDeprecated'];
|
||||
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 deleteServiceAccountRoleDeprecated>>,
|
||||
{ pathParams: DeleteServiceAccountRoleDeprecatedPathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return deleteServiceAccountRoleDeprecated(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type DeleteServiceAccountRoleDeprecatedMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof deleteServiceAccountRoleDeprecated>>
|
||||
>;
|
||||
|
||||
export type DeleteServiceAccountRoleDeprecatedMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Delete service account role
|
||||
*/
|
||||
export const useDeleteServiceAccountRoleDeprecated = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteServiceAccountRoleDeprecated>>,
|
||||
TError,
|
||||
{ pathParams: DeleteServiceAccountRoleDeprecatedPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof deleteServiceAccountRoleDeprecated>>,
|
||||
TError,
|
||||
{ pathParams: DeleteServiceAccountRoleDeprecatedPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(
|
||||
getDeleteServiceAccountRoleDeprecatedMutationOptions(options),
|
||||
);
|
||||
};
|
||||
/**
|
||||
* This endpoint gets my service account
|
||||
* @summary Gets my service account
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,280 +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 {
|
||||
CreateSubscription201,
|
||||
GetSubscription200,
|
||||
RenderErrorResponseDTO,
|
||||
SubscriptiontypesPostableSubscriptionDTO,
|
||||
UpdateSubscription200,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
/**
|
||||
* This endpoint gets the organization's subscription along with its usage and billing details.
|
||||
* @summary Get the subscription.
|
||||
*/
|
||||
export const getSubscription = (signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<GetSubscription200>({
|
||||
url: `/api/v1/subscriptions`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetSubscriptionQueryKey = () => {
|
||||
return [`/api/v1/subscriptions`] as const;
|
||||
};
|
||||
|
||||
export const getGetSubscriptionQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getSubscription>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSubscription>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getGetSubscriptionQueryKey();
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof getSubscription>>> = ({
|
||||
signal,
|
||||
}) => getSubscription(signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSubscription>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetSubscriptionQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getSubscription>>
|
||||
>;
|
||||
export type GetSubscriptionQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get the subscription.
|
||||
*/
|
||||
|
||||
export function useGetSubscription<
|
||||
TData = Awaited<ReturnType<typeof getSubscription>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getSubscription>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetSubscriptionQueryOptions(options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get the subscription.
|
||||
*/
|
||||
export const invalidateGetSubscription = async (
|
||||
queryClient: QueryClient,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetSubscriptionQueryKey() },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint creates a subscription for the organization.
|
||||
* @summary Create a subscription.
|
||||
*/
|
||||
export const createSubscription = (
|
||||
subscriptiontypesPostableSubscriptionDTO?: BodyType<SubscriptiontypesPostableSubscriptionDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateSubscription201>({
|
||||
url: `/api/v1/subscriptions`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: subscriptiontypesPostableSubscriptionDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getCreateSubscriptionMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createSubscription>>,
|
||||
TError,
|
||||
{ data?: BodyType<SubscriptiontypesPostableSubscriptionDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createSubscription>>,
|
||||
TError,
|
||||
{ data?: BodyType<SubscriptiontypesPostableSubscriptionDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['createSubscription'];
|
||||
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 createSubscription>>,
|
||||
{ data?: BodyType<SubscriptiontypesPostableSubscriptionDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return createSubscription(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type CreateSubscriptionMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createSubscription>>
|
||||
>;
|
||||
export type CreateSubscriptionMutationBody =
|
||||
| BodyType<SubscriptiontypesPostableSubscriptionDTO>
|
||||
| undefined;
|
||||
export type CreateSubscriptionMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Create a subscription.
|
||||
*/
|
||||
export const useCreateSubscription = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createSubscription>>,
|
||||
TError,
|
||||
{ data?: BodyType<SubscriptiontypesPostableSubscriptionDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof createSubscription>>,
|
||||
TError,
|
||||
{ data?: BodyType<SubscriptiontypesPostableSubscriptionDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getCreateSubscriptionMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint updates the organization's subscription.
|
||||
* @summary Update the subscription.
|
||||
*/
|
||||
export const updateSubscription = (
|
||||
subscriptiontypesPostableSubscriptionDTO?: BodyType<SubscriptiontypesPostableSubscriptionDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<UpdateSubscription200>({
|
||||
url: `/api/v1/subscriptions`,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: subscriptiontypesPostableSubscriptionDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUpdateSubscriptionMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateSubscription>>,
|
||||
TError,
|
||||
{ data?: BodyType<SubscriptiontypesPostableSubscriptionDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateSubscription>>,
|
||||
TError,
|
||||
{ data?: BodyType<SubscriptiontypesPostableSubscriptionDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['updateSubscription'];
|
||||
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 updateSubscription>>,
|
||||
{ data?: BodyType<SubscriptiontypesPostableSubscriptionDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return updateSubscription(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UpdateSubscriptionMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof updateSubscription>>
|
||||
>;
|
||||
export type UpdateSubscriptionMutationBody =
|
||||
| BodyType<SubscriptiontypesPostableSubscriptionDTO>
|
||||
| undefined;
|
||||
export type UpdateSubscriptionMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Update the subscription.
|
||||
*/
|
||||
export const useUpdateSubscription = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateSubscription>>,
|
||||
TError,
|
||||
{ data?: BodyType<SubscriptiontypesPostableSubscriptionDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof updateSubscription>>,
|
||||
TError,
|
||||
{ data?: BodyType<SubscriptiontypesPostableSubscriptionDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getUpdateSubscriptionMutationOptions(options));
|
||||
};
|
||||
@@ -20,6 +20,7 @@ import type {
|
||||
import type {
|
||||
AuthtypesPostableUserDTO,
|
||||
AuthtypesPostableUserRoleDTO,
|
||||
CreateInvite201,
|
||||
CreateResetPasswordToken201,
|
||||
CreateResetPasswordTokenPathParameters,
|
||||
CreateUser201,
|
||||
@@ -27,7 +28,10 @@ import type {
|
||||
DeleteUserPathParameters,
|
||||
DeleteUserRolePathParameters,
|
||||
GetMyUser200,
|
||||
GetMyUserDeprecated200,
|
||||
GetResetPasswordToken200,
|
||||
GetResetPasswordTokenDeprecated200,
|
||||
GetResetPasswordTokenDeprecatedPathParameters,
|
||||
GetResetPasswordTokenPathParameters,
|
||||
GetRolesByUserID200,
|
||||
GetRolesByUserIDPathParameters,
|
||||
@@ -38,10 +42,15 @@ import type {
|
||||
GetUsersByRoleID200,
|
||||
GetUsersByRoleIDPathParameters,
|
||||
ListUsers200,
|
||||
ListUsersDeprecated200,
|
||||
RemoveUserRoleByUserIDAndRoleIDPathParameters,
|
||||
RenderErrorResponseDTO,
|
||||
SetRoleByUserIDPathParameters,
|
||||
TypesChangePasswordRequestDTO,
|
||||
TypesPostableForgotPasswordDTO,
|
||||
TypesPostableInviteDTO,
|
||||
TypesPostableResetPasswordDTO,
|
||||
TypesPostableRoleDTO,
|
||||
TypesPostableVerifyResetPasswordTokenDTO,
|
||||
TypesUpdatableUserDTO,
|
||||
UpdateUserPathParameters,
|
||||
@@ -51,12 +60,379 @@ import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
/**
|
||||
* This endpoint is deprecated and always fails. Use GET /api/v2/users/me instead.
|
||||
* This endpoint returns the reset password token by id
|
||||
* @deprecated
|
||||
* @summary Get reset password token
|
||||
*/
|
||||
export const getResetPasswordTokenDeprecated = (
|
||||
{ id }: GetResetPasswordTokenDeprecatedPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetResetPasswordTokenDeprecated200>({
|
||||
url: `/api/v1/getResetPasswordToken/${id}`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetResetPasswordTokenDeprecatedQueryKey = ({
|
||||
id,
|
||||
}: GetResetPasswordTokenDeprecatedPathParameters) => {
|
||||
return [`/api/v1/getResetPasswordToken/${id}`] as const;
|
||||
};
|
||||
|
||||
export const getGetResetPasswordTokenDeprecatedQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ id }: GetResetPasswordTokenDeprecatedPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetResetPasswordTokenDeprecatedQueryKey({ id });
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>
|
||||
> = ({ signal }) => getResetPasswordTokenDeprecated({ id }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetResetPasswordTokenDeprecatedQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>
|
||||
>;
|
||||
export type GetResetPasswordTokenDeprecatedQueryError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Get reset password token
|
||||
*/
|
||||
|
||||
export function useGetResetPasswordTokenDeprecated<
|
||||
TData = Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ id }: GetResetPasswordTokenDeprecatedPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetResetPasswordTokenDeprecatedQueryOptions(
|
||||
{ id },
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Get reset password token
|
||||
*/
|
||||
export const invalidateGetResetPasswordTokenDeprecated = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetResetPasswordTokenDeprecatedPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetResetPasswordTokenDeprecatedQueryKey({ id }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint creates an invite for a user
|
||||
* @deprecated
|
||||
* @summary Create invite
|
||||
*/
|
||||
export const createInvite = (
|
||||
typesPostableInviteDTO?: BodyType<TypesPostableInviteDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateInvite201>({
|
||||
url: `/api/v1/invite`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: typesPostableInviteDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getCreateInviteMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createInvite>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableInviteDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createInvite>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableInviteDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['createInvite'];
|
||||
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 createInvite>>,
|
||||
{ data?: BodyType<TypesPostableInviteDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return createInvite(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type CreateInviteMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createInvite>>
|
||||
>;
|
||||
export type CreateInviteMutationBody =
|
||||
| BodyType<TypesPostableInviteDTO>
|
||||
| undefined;
|
||||
export type CreateInviteMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Create invite
|
||||
*/
|
||||
export const useCreateInvite = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createInvite>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableInviteDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof createInvite>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableInviteDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getCreateInviteMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint resets the password by token
|
||||
* @deprecated
|
||||
* @summary Reset password
|
||||
*/
|
||||
export const resetPasswordDeprecated = (
|
||||
typesPostableResetPasswordDTO?: BodyType<TypesPostableResetPasswordDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v1/resetPassword`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: typesPostableResetPasswordDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getResetPasswordDeprecatedMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof resetPasswordDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableResetPasswordDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof resetPasswordDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableResetPasswordDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['resetPasswordDeprecated'];
|
||||
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 resetPasswordDeprecated>>,
|
||||
{ data?: BodyType<TypesPostableResetPasswordDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return resetPasswordDeprecated(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type ResetPasswordDeprecatedMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof resetPasswordDeprecated>>
|
||||
>;
|
||||
export type ResetPasswordDeprecatedMutationBody =
|
||||
| BodyType<TypesPostableResetPasswordDTO>
|
||||
| undefined;
|
||||
export type ResetPasswordDeprecatedMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Reset password
|
||||
*/
|
||||
export const useResetPasswordDeprecated = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof resetPasswordDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableResetPasswordDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof resetPasswordDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableResetPasswordDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getResetPasswordDeprecatedMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint lists all users
|
||||
* @deprecated
|
||||
* @summary List users
|
||||
*/
|
||||
export const listUsersDeprecated = (signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<ListUsersDeprecated200>({
|
||||
url: `/api/v1/user`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getListUsersDeprecatedQueryKey = () => {
|
||||
return [`/api/v1/user`] as const;
|
||||
};
|
||||
|
||||
export const getListUsersDeprecatedQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof listUsersDeprecated>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listUsersDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getListUsersDeprecatedQueryKey();
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof listUsersDeprecated>>
|
||||
> = ({ signal }) => listUsersDeprecated(signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listUsersDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type ListUsersDeprecatedQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof listUsersDeprecated>>
|
||||
>;
|
||||
export type ListUsersDeprecatedQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary List users
|
||||
*/
|
||||
|
||||
export function useListUsersDeprecated<
|
||||
TData = Awaited<ReturnType<typeof listUsersDeprecated>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listUsersDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getListUsersDeprecatedQueryOptions(options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary List users
|
||||
*/
|
||||
export const invalidateListUsersDeprecated = async (
|
||||
queryClient: QueryClient,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getListUsersDeprecatedQueryKey() },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns the user I belong to
|
||||
* @deprecated
|
||||
* @summary Get my user
|
||||
*/
|
||||
export const getMyUserDeprecated = (signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
return GeneratedAPIInstance<GetMyUserDeprecated200>({
|
||||
url: `/api/v1/user/me`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
@@ -1458,6 +1834,189 @@ export const invalidateGetRolesByUserID = async (
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint assigns the role to the user roles by user id
|
||||
* @deprecated
|
||||
* @summary Set user roles
|
||||
*/
|
||||
export const setRoleByUserID = (
|
||||
{ id }: SetRoleByUserIDPathParameters,
|
||||
typesPostableRoleDTO?: BodyType<TypesPostableRoleDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/users/${id}/roles`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: typesPostableRoleDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getSetRoleByUserIDMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof setRoleByUserID>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: SetRoleByUserIDPathParameters;
|
||||
data?: BodyType<TypesPostableRoleDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof setRoleByUserID>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: SetRoleByUserIDPathParameters;
|
||||
data?: BodyType<TypesPostableRoleDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['setRoleByUserID'];
|
||||
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 setRoleByUserID>>,
|
||||
{
|
||||
pathParams: SetRoleByUserIDPathParameters;
|
||||
data?: BodyType<TypesPostableRoleDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return setRoleByUserID(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type SetRoleByUserIDMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof setRoleByUserID>>
|
||||
>;
|
||||
export type SetRoleByUserIDMutationBody =
|
||||
| BodyType<TypesPostableRoleDTO>
|
||||
| undefined;
|
||||
export type SetRoleByUserIDMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Set user roles
|
||||
*/
|
||||
export const useSetRoleByUserID = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof setRoleByUserID>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: SetRoleByUserIDPathParameters;
|
||||
data?: BodyType<TypesPostableRoleDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof setRoleByUserID>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: SetRoleByUserIDPathParameters;
|
||||
data?: BodyType<TypesPostableRoleDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getSetRoleByUserIDMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint removes a role from the user by user id and role id
|
||||
* @deprecated
|
||||
* @summary Remove a role from user
|
||||
*/
|
||||
export const removeUserRoleByUserIDAndRoleID = (
|
||||
{ id, roleId }: RemoveUserRoleByUserIDAndRoleIDPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/users/${id}/roles/${roleId}`,
|
||||
method: 'DELETE',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getRemoveUserRoleByUserIDAndRoleIDMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof removeUserRoleByUserIDAndRoleID>>,
|
||||
TError,
|
||||
{ pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof removeUserRoleByUserIDAndRoleID>>,
|
||||
TError,
|
||||
{ pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['removeUserRoleByUserIDAndRoleID'];
|
||||
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 removeUserRoleByUserIDAndRoleID>>,
|
||||
{ pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return removeUserRoleByUserIDAndRoleID(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type RemoveUserRoleByUserIDAndRoleIDMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof removeUserRoleByUserIDAndRoleID>>
|
||||
>;
|
||||
|
||||
export type RemoveUserRoleByUserIDAndRoleIDMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Remove a role from user
|
||||
*/
|
||||
export const useRemoveUserRoleByUserIDAndRoleID = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof removeUserRoleByUserIDAndRoleID>>,
|
||||
TError,
|
||||
{ pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof removeUserRoleByUserIDAndRoleID>>,
|
||||
TError,
|
||||
{ pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getRemoveUserRoleByUserIDAndRoleIDMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint returns the user I belong to
|
||||
* @summary Get my user v2
|
||||
|
||||
82
frontend/src/api/infraMonitoring/getHostLists.ts
Normal file
82
frontend/src/api/infraMonitoring/getHostLists.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export interface HostListPayload {
|
||||
filters: TagFilter;
|
||||
groupBy: BaseAutocompleteData[];
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
orderBy?: {
|
||||
columnName: string;
|
||||
order: 'asc' | 'desc';
|
||||
} | null;
|
||||
start?: number;
|
||||
end?: number;
|
||||
}
|
||||
|
||||
export interface TimeSeriesValue {
|
||||
timestamp: number;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface TimeSeries {
|
||||
labels: Record<string, string>;
|
||||
labelsArray: Array<Record<string, string>>;
|
||||
values: TimeSeriesValue[];
|
||||
}
|
||||
|
||||
export interface HostData {
|
||||
hostName: string;
|
||||
active: boolean;
|
||||
os: string;
|
||||
/** Present when the list API returns grouped rows or extra resource attributes. */
|
||||
meta?: Record<string, string>;
|
||||
cpu: number;
|
||||
cpuTimeSeries: TimeSeries;
|
||||
memory: number;
|
||||
memoryTimeSeries: TimeSeries;
|
||||
wait: number;
|
||||
waitTimeSeries: TimeSeries;
|
||||
load15: number;
|
||||
load15TimeSeries: TimeSeries;
|
||||
}
|
||||
|
||||
export interface HostListResponse {
|
||||
status: string;
|
||||
data: {
|
||||
type: string;
|
||||
records: HostData[];
|
||||
groups: null;
|
||||
total: number;
|
||||
sentAnyHostMetricsData: boolean;
|
||||
isSendingK8SAgentMetrics: boolean;
|
||||
endTimeBeforeRetention: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export const getHostLists = async (
|
||||
props: HostListPayload,
|
||||
signal?: AbortSignal,
|
||||
headers?: Record<string, string>,
|
||||
): Promise<SuccessResponse<HostListResponse> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post('/hosts/list', props, {
|
||||
signal,
|
||||
headers,
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data,
|
||||
params: props,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
25
frontend/src/api/quickFilters/getCustomFilters.ts
Normal file
25
frontend/src/api/quickFilters/getCustomFilters.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/quickFilters/getCustomFilters';
|
||||
|
||||
const getCustomFilters = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
const { signal } = props;
|
||||
try {
|
||||
const response = await axios.get(`/orgs/me/filters/${signal}`);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
export default getCustomFilters;
|
||||
13
frontend/src/api/quickFilters/updateCustomFilters.ts
Normal file
13
frontend/src/api/quickFilters/updateCustomFilters.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import axios from 'api';
|
||||
import { AxiosError } from 'axios';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { UpdateCustomFiltersProps } from 'types/api/quickFilters/updateCustomFilters';
|
||||
|
||||
const updateCustomFiltersAPI = async (
|
||||
props: UpdateCustomFiltersProps,
|
||||
): Promise<SuccessResponse<void> | AxiosError> =>
|
||||
axios.put(`/orgs/me/filters`, {
|
||||
...props.data,
|
||||
});
|
||||
|
||||
export default updateCustomFiltersAPI;
|
||||
@@ -24,3 +24,19 @@ export const Logout = async (): Promise<void> => {
|
||||
window.dispatchEvent(new CustomEvent('LOGOUT'));
|
||||
history.push(ROUTES.LOGIN);
|
||||
};
|
||||
|
||||
export const UnderscoreToDotMap: Record<string, string> = {
|
||||
k8s_cluster_name: 'k8s.cluster.name',
|
||||
k8s_cluster_uid: 'k8s.cluster.uid',
|
||||
k8s_namespace_name: 'k8s.namespace.name',
|
||||
k8s_node_name: 'k8s.node.name',
|
||||
k8s_node_uid: 'k8s.node.uid',
|
||||
k8s_pod_name: 'k8s.pod.name',
|
||||
k8s_pod_uid: 'k8s.pod.uid',
|
||||
k8s_deployment_name: 'k8s.deployment.name',
|
||||
k8s_daemonset_name: 'k8s.daemonset.name',
|
||||
k8s_statefulset_name: 'k8s.statefulset.name',
|
||||
k8s_cronjob_name: 'k8s.cronjob.name',
|
||||
k8s_job_name: 'k8s.job.name',
|
||||
k8s_persistentvolumeclaim_name: 'k8s.persistentvolumeclaim.name',
|
||||
};
|
||||
|
||||
28
frontend/src/api/v1/checkout/create.ts
Normal file
28
frontend/src/api/v1/checkout/create.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import {
|
||||
CheckoutRequestPayloadProps,
|
||||
CheckoutSuccessPayloadProps,
|
||||
PayloadProps,
|
||||
} from 'types/api/billing/checkout';
|
||||
|
||||
const updateCreditCardApi = async (
|
||||
props: CheckoutRequestPayloadProps,
|
||||
): Promise<SuccessResponseV2<CheckoutSuccessPayloadProps>> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/checkout', {
|
||||
url: props.url,
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default updateCreditCardApi;
|
||||
23
frontend/src/api/v1/dashboards/create.ts
Normal file
23
frontend/src/api/v1/dashboards/create.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/dashboard/create';
|
||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
|
||||
const create = async (props: Props): Promise<SuccessResponseV2<Dashboard>> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/dashboards', {
|
||||
...props,
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default create;
|
||||
19
frontend/src/api/v1/dashboards/getAll.ts
Normal file
19
frontend/src/api/v1/dashboards/getAll.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { Dashboard, PayloadProps } from 'types/api/dashboard/getAll';
|
||||
|
||||
const getAll = async (): Promise<SuccessResponseV2<Dashboard[]>> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>('/dashboards');
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default getAll;
|
||||
21
frontend/src/api/v1/dashboards/id/delete.ts
Normal file
21
frontend/src/api/v1/dashboards/id/delete.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/dashboard/delete';
|
||||
|
||||
const deleteDashboard = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<null>> => {
|
||||
try {
|
||||
const response = await axios.delete<PayloadProps>(`/dashboards/${props.id}`);
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: null,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default deleteDashboard;
|
||||
20
frontend/src/api/v1/dashboards/id/get.ts
Normal file
20
frontend/src/api/v1/dashboards/id/get.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/dashboard/get';
|
||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
|
||||
const get = async (props: Props): Promise<SuccessResponseV2<Dashboard>> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>(`/dashboards/${props.id}`);
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default get;
|
||||
23
frontend/src/api/v1/dashboards/id/lock.ts
Normal file
23
frontend/src/api/v1/dashboards/id/lock.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/dashboard/lockUnlock';
|
||||
|
||||
const lock = async (props: Props): Promise<SuccessResponseV2<null>> => {
|
||||
try {
|
||||
const response = await axios.put<PayloadProps>(
|
||||
`/dashboards/${props.id}/lock`,
|
||||
{ lock: props.lock },
|
||||
);
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default lock;
|
||||
23
frontend/src/api/v1/dashboards/id/update.ts
Normal file
23
frontend/src/api/v1/dashboards/id/update.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
import { PayloadProps, Props } from 'types/api/dashboard/update';
|
||||
|
||||
const update = async (props: Props): Promise<SuccessResponseV2<Dashboard>> => {
|
||||
try {
|
||||
const response = await axios.put<PayloadProps>(`/dashboards/${props.id}`, {
|
||||
...props.data,
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default update;
|
||||
31
frontend/src/api/v1/factor_password/resetPassword.ts
Normal file
31
frontend/src/api/v1/factor_password/resetPassword.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/user/resetPassword';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useResetPassword` hook (or `resetPassword` fetcher) from
|
||||
* `api/generated/services/users` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const resetPassword = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>(`/resetPassword`, {
|
||||
...props,
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default resetPassword;
|
||||
31
frontend/src/api/v1/invite/create.ts
Normal file
31
frontend/src/api/v1/invite/create.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/user/setInvite';
|
||||
|
||||
/**
|
||||
* @deprecated Use the generated `useCreateInvite` hook (or `createInvite` fetcher) from
|
||||
* `api/generated/services/users` instead. This hand-written client targets the
|
||||
* same endpoint and will be removed once call sites migrate.
|
||||
*
|
||||
* Part of https://github.com/SigNoz/engineering-pod/issues/5289, add a comment or update when removing this method.
|
||||
*/
|
||||
const sendInvite = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>(`/invite`, {
|
||||
...props,
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default sendInvite;
|
||||
28
frontend/src/api/v1/portal/create.ts
Normal file
28
frontend/src/api/v1/portal/create.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import {
|
||||
CheckoutRequestPayloadProps,
|
||||
CheckoutSuccessPayloadProps,
|
||||
PayloadProps,
|
||||
} from 'types/api/billing/checkout';
|
||||
|
||||
const manageCreditCardApi = async (
|
||||
props: CheckoutRequestPayloadProps,
|
||||
): Promise<SuccessResponseV2<CheckoutSuccessPayloadProps>> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/portal', {
|
||||
url: props.url,
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default manageCreditCardApi;
|
||||
25
frontend/src/api/v3/licenses/active/get.ts
Normal file
25
frontend/src/api/v3/licenses/active/get.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { ApiV3Instance as axios } from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import {
|
||||
LicenseEventQueueResModel,
|
||||
PayloadProps,
|
||||
} from 'types/api/licensesV3/getActive';
|
||||
|
||||
const getActive = async (): Promise<
|
||||
SuccessResponseV2<LicenseEventQueueResModel>
|
||||
> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>('/licenses/active');
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default getActive;
|
||||
24
frontend/src/api/v3/licenses/post.ts
Normal file
24
frontend/src/api/v3/licenses/post.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ApiV3Instance as axios } from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/licenses/apply';
|
||||
|
||||
const apply = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/licenses', {
|
||||
key: props.key,
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default apply;
|
||||
20
frontend/src/api/v3/licenses/put.ts
Normal file
20
frontend/src/api/v3/licenses/put.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { ApiV3Instance as axios } from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps } from 'types/api/licenses/apply';
|
||||
|
||||
const apply = async (): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
try {
|
||||
const response = await axios.put<PayloadProps>('/licenses');
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default apply;
|
||||
@@ -2,10 +2,8 @@ import { cloneDeep, isEmpty } from 'lodash-es';
|
||||
import { SuccessResponse, Warning } from 'types/api';
|
||||
import { MetricRangePayloadV3 } from 'types/api/metrics/getQueryRange';
|
||||
import {
|
||||
BuilderQuery,
|
||||
DistributionData,
|
||||
MetricRangePayloadV5,
|
||||
QueryEnvelope,
|
||||
QueryRangeRequestV5,
|
||||
RawData,
|
||||
ScalarData,
|
||||
@@ -13,11 +11,6 @@ import {
|
||||
} from 'types/api/v5/queryRange';
|
||||
import { QueryDataV3 } from 'types/api/widgets/getQuery';
|
||||
|
||||
const isBuilderQueryEnvelope = (
|
||||
envelope: QueryEnvelope,
|
||||
): envelope is QueryEnvelope & { spec: BuilderQuery } =>
|
||||
envelope.type === 'builder_query' || envelope.type === 'builder_ai_query';
|
||||
|
||||
function getColName(
|
||||
col: ScalarData['columns'][number],
|
||||
legendMap: Record<string, string>,
|
||||
@@ -416,19 +409,21 @@ export function convertV5ResponseToLegacy(
|
||||
const v5Data = payload?.data;
|
||||
|
||||
const aggregationPerQuery =
|
||||
params?.compositeQuery?.queries?.filter(isBuilderQueryEnvelope).reduce(
|
||||
(acc, query) => {
|
||||
if (
|
||||
isBuilderQueryEnvelope(query) &&
|
||||
'aggregations' in query.spec &&
|
||||
query.spec.name
|
||||
) {
|
||||
acc[query.spec.name] = query.spec.aggregations;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, any>,
|
||||
) || {};
|
||||
params?.compositeQuery?.queries
|
||||
?.filter((query) => query.type === 'builder_query')
|
||||
.reduce(
|
||||
(acc, query) => {
|
||||
if (
|
||||
query.type === 'builder_query' &&
|
||||
'aggregations' in query.spec &&
|
||||
query.spec.name
|
||||
) {
|
||||
acc[query.spec.name] = query.spec.aggregations;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, any>,
|
||||
) || {};
|
||||
|
||||
// clickhouse_sql queries have no aggregation metadata; their value columns
|
||||
// are named/keyed by the real SQL alias the response carries (see getColId).
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user