mirror of
https://github.com/SigNoz/signoz.git
synced 2026-04-12 15:10:20 +01:00
Compare commits
2 Commits
issue_4361
...
fix/metric
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
012eed1576 | ||
|
|
5a7660069a |
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/cmd"
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
"github.com/SigNoz/signoz/pkg/auditor"
|
||||
"github.com/SigNoz/signoz/pkg/authn"
|
||||
"github.com/SigNoz/signoz/pkg/authz"
|
||||
"github.com/SigNoz/signoz/pkg/authz/openfgaauthz"
|
||||
@@ -94,9 +93,6 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
func(_ licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config] {
|
||||
return noopgateway.NewProviderFactory()
|
||||
},
|
||||
func(_ licensing.Licensing) factory.NamedMap[factory.ProviderFactory[auditor.Auditor, auditor.Config]] {
|
||||
return signoz.NewAuditorProviderFactories()
|
||||
},
|
||||
func(ps factory.ProviderSettings, q querier.Querier, a analytics.Analytics) querier.Handler {
|
||||
return querier.NewHandler(ps, q, a)
|
||||
},
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/SigNoz/signoz/cmd"
|
||||
"github.com/SigNoz/signoz/ee/auditor/otlphttpauditor"
|
||||
"github.com/SigNoz/signoz/ee/authn/callbackauthn/oidccallbackauthn"
|
||||
"github.com/SigNoz/signoz/ee/authn/callbackauthn/samlcallbackauthn"
|
||||
"github.com/SigNoz/signoz/ee/authz/openfgaauthz"
|
||||
@@ -25,7 +24,6 @@ import (
|
||||
enterprisezeus "github.com/SigNoz/signoz/ee/zeus"
|
||||
"github.com/SigNoz/signoz/ee/zeus/httpzeus"
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
"github.com/SigNoz/signoz/pkg/auditor"
|
||||
"github.com/SigNoz/signoz/pkg/authn"
|
||||
"github.com/SigNoz/signoz/pkg/authz"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
@@ -135,13 +133,6 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
func(licensing licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config] {
|
||||
return httpgateway.NewProviderFactory(licensing)
|
||||
},
|
||||
func(licensing licensing.Licensing) factory.NamedMap[factory.ProviderFactory[auditor.Auditor, auditor.Config]] {
|
||||
factories := signoz.NewAuditorProviderFactories()
|
||||
if err := factories.Add(otlphttpauditor.NewFactory(licensing, version.Info)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return factories
|
||||
},
|
||||
func(ps factory.ProviderSettings, q querier.Querier, a analytics.Analytics) querier.Handler {
|
||||
communityHandler := querier.NewHandler(ps, q, a)
|
||||
return eequerier.NewHandler(ps, q, communityHandler)
|
||||
|
||||
@@ -364,34 +364,3 @@ serviceaccount:
|
||||
analytics:
|
||||
# toggle service account analytics
|
||||
enabled: true
|
||||
|
||||
##################### Auditor #####################
|
||||
auditor:
|
||||
# Specifies the auditor provider to use.
|
||||
# noop: discards all audit events (community default).
|
||||
# otlphttp: exports audit events via OTLP HTTP (enterprise).
|
||||
provider: noop
|
||||
# The async channel capacity for audit events. Events are dropped when full (fail-open).
|
||||
buffer_size: 1000
|
||||
# The maximum number of events per export batch.
|
||||
batch_size: 100
|
||||
# The maximum time between export flushes.
|
||||
flush_interval: 1s
|
||||
otlphttp:
|
||||
# The target scheme://host:port/path of the OTLP HTTP endpoint.
|
||||
endpoint: http://localhost:4318/v1/logs
|
||||
# Whether to use HTTP instead of HTTPS.
|
||||
insecure: false
|
||||
# The maximum duration for an export attempt.
|
||||
timeout: 10s
|
||||
# Additional HTTP headers sent with every export request.
|
||||
headers: {}
|
||||
retry:
|
||||
# Whether to retry on transient failures.
|
||||
enabled: true
|
||||
# The initial wait time before the first retry.
|
||||
initial_interval: 5s
|
||||
# The upper bound on backoff interval.
|
||||
max_interval: 30s
|
||||
# The total maximum time spent retrying.
|
||||
max_elapsed_time: 60s
|
||||
|
||||
@@ -3309,7 +3309,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CloudintegrationtypesPostableAccount'
|
||||
responses:
|
||||
"201":
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -3322,7 +3322,7 @@ paths:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: Created
|
||||
description: OK
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
@@ -3683,11 +3683,6 @@ paths:
|
||||
provider
|
||||
operationId: ListServicesMetadata
|
||||
parameters:
|
||||
- in: query
|
||||
name: cloud_integration_id
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- in: path
|
||||
name: cloud_provider
|
||||
required: true
|
||||
@@ -3740,11 +3735,6 @@ paths:
|
||||
description: This endpoint gets a service for the specified cloud provider
|
||||
operationId: GetService
|
||||
parameters:
|
||||
- in: query
|
||||
name: cloud_integration_id
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- in: path
|
||||
name: cloud_provider
|
||||
required: true
|
||||
|
||||
@@ -227,7 +227,7 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
|
||||
s.config.APIServer.Timeout.Default,
|
||||
s.config.APIServer.Timeout.Max,
|
||||
).Wrap)
|
||||
r.Use(middleware.NewAudit(s.signoz.Instrumentation.Logger(), s.config.APIServer.Logging.ExcludedRoutes, s.signoz.Auditor).Wrap)
|
||||
r.Use(middleware.NewAudit(s.signoz.Instrumentation.Logger(), s.config.APIServer.Logging.ExcludedRoutes, nil).Wrap)
|
||||
r.Use(middleware.NewComment().Wrap)
|
||||
|
||||
apiHandler.RegisterRoutes(r, am)
|
||||
|
||||
@@ -28,7 +28,7 @@ import type {
|
||||
CloudintegrationtypesPostableAgentCheckInDTO,
|
||||
CloudintegrationtypesUpdatableAccountDTO,
|
||||
CloudintegrationtypesUpdatableServiceDTO,
|
||||
CreateAccount201,
|
||||
CreateAccount200,
|
||||
CreateAccountPathParameters,
|
||||
DisconnectAccountPathParameters,
|
||||
GetAccount200,
|
||||
@@ -36,12 +36,10 @@ import type {
|
||||
GetConnectionCredentials200,
|
||||
GetConnectionCredentialsPathParameters,
|
||||
GetService200,
|
||||
GetServiceParams,
|
||||
GetServicePathParameters,
|
||||
ListAccounts200,
|
||||
ListAccountsPathParameters,
|
||||
ListServicesMetadata200,
|
||||
ListServicesMetadataParams,
|
||||
ListServicesMetadataPathParameters,
|
||||
RenderErrorResponseDTO,
|
||||
UpdateAccountPathParameters,
|
||||
@@ -262,7 +260,7 @@ export const createAccount = (
|
||||
cloudintegrationtypesPostableAccountDTO: BodyType<CloudintegrationtypesPostableAccountDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateAccount201>({
|
||||
return GeneratedAPIInstance<CreateAccount200>({
|
||||
url: `/api/v1/cloud_integrations/${cloudProvider}/accounts`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -942,25 +940,19 @@ export const invalidateGetConnectionCredentials = async (
|
||||
*/
|
||||
export const listServicesMetadata = (
|
||||
{ cloudProvider }: ListServicesMetadataPathParameters,
|
||||
params?: ListServicesMetadataParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<ListServicesMetadata200>({
|
||||
url: `/api/v1/cloud_integrations/${cloudProvider}/services`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getListServicesMetadataQueryKey = (
|
||||
{ cloudProvider }: ListServicesMetadataPathParameters,
|
||||
params?: ListServicesMetadataParams,
|
||||
) => {
|
||||
return [
|
||||
`/api/v1/cloud_integrations/${cloudProvider}/services`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
export const getListServicesMetadataQueryKey = ({
|
||||
cloudProvider,
|
||||
}: ListServicesMetadataPathParameters) => {
|
||||
return [`/api/v1/cloud_integrations/${cloudProvider}/services`] as const;
|
||||
};
|
||||
|
||||
export const getListServicesMetadataQueryOptions = <
|
||||
@@ -968,7 +960,6 @@ export const getListServicesMetadataQueryOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ cloudProvider }: ListServicesMetadataPathParameters,
|
||||
params?: ListServicesMetadataParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listServicesMetadata>>,
|
||||
@@ -980,12 +971,11 @@ export const getListServicesMetadataQueryOptions = <
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ??
|
||||
getListServicesMetadataQueryKey({ cloudProvider }, params);
|
||||
queryOptions?.queryKey ?? getListServicesMetadataQueryKey({ cloudProvider });
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof listServicesMetadata>>
|
||||
> = ({ signal }) => listServicesMetadata({ cloudProvider }, params, signal);
|
||||
> = ({ signal }) => listServicesMetadata({ cloudProvider }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
@@ -1013,7 +1003,6 @@ export function useListServicesMetadata<
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ cloudProvider }: ListServicesMetadataPathParameters,
|
||||
params?: ListServicesMetadataParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listServicesMetadata>>,
|
||||
@@ -1024,7 +1013,6 @@ export function useListServicesMetadata<
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getListServicesMetadataQueryOptions(
|
||||
{ cloudProvider },
|
||||
params,
|
||||
options,
|
||||
);
|
||||
|
||||
@@ -1043,11 +1031,10 @@ export function useListServicesMetadata<
|
||||
export const invalidateListServicesMetadata = async (
|
||||
queryClient: QueryClient,
|
||||
{ cloudProvider }: ListServicesMetadataPathParameters,
|
||||
params?: ListServicesMetadataParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getListServicesMetadataQueryKey({ cloudProvider }, params) },
|
||||
{ queryKey: getListServicesMetadataQueryKey({ cloudProvider }) },
|
||||
options,
|
||||
);
|
||||
|
||||
@@ -1060,24 +1047,21 @@ export const invalidateListServicesMetadata = async (
|
||||
*/
|
||||
export const getService = (
|
||||
{ cloudProvider, serviceId }: GetServicePathParameters,
|
||||
params?: GetServiceParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetService200>({
|
||||
url: `/api/v1/cloud_integrations/${cloudProvider}/services/${serviceId}`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetServiceQueryKey = (
|
||||
{ cloudProvider, serviceId }: GetServicePathParameters,
|
||||
params?: GetServiceParams,
|
||||
) => {
|
||||
export const getGetServiceQueryKey = ({
|
||||
cloudProvider,
|
||||
serviceId,
|
||||
}: GetServicePathParameters) => {
|
||||
return [
|
||||
`/api/v1/cloud_integrations/${cloudProvider}/services/${serviceId}`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
};
|
||||
|
||||
@@ -1086,7 +1070,6 @@ export const getGetServiceQueryOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ cloudProvider, serviceId }: GetServicePathParameters,
|
||||
params?: GetServiceParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getService>>,
|
||||
@@ -1098,12 +1081,11 @@ export const getGetServiceQueryOptions = <
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ??
|
||||
getGetServiceQueryKey({ cloudProvider, serviceId }, params);
|
||||
queryOptions?.queryKey ?? getGetServiceQueryKey({ cloudProvider, serviceId });
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof getService>>> = ({
|
||||
signal,
|
||||
}) => getService({ cloudProvider, serviceId }, params, signal);
|
||||
}) => getService({ cloudProvider, serviceId }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
@@ -1129,7 +1111,6 @@ export function useGetService<
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ cloudProvider, serviceId }: GetServicePathParameters,
|
||||
params?: GetServiceParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getService>>,
|
||||
@@ -1140,7 +1121,6 @@ export function useGetService<
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetServiceQueryOptions(
|
||||
{ cloudProvider, serviceId },
|
||||
params,
|
||||
options,
|
||||
);
|
||||
|
||||
@@ -1159,11 +1139,10 @@ export function useGetService<
|
||||
export const invalidateGetService = async (
|
||||
queryClient: QueryClient,
|
||||
{ cloudProvider, serviceId }: GetServicePathParameters,
|
||||
params?: GetServiceParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetServiceQueryKey({ cloudProvider, serviceId }, params) },
|
||||
{ queryKey: getGetServiceQueryKey({ cloudProvider, serviceId }) },
|
||||
options,
|
||||
);
|
||||
|
||||
|
||||
@@ -3589,7 +3589,7 @@ export type ListAccounts200 = {
|
||||
export type CreateAccountPathParameters = {
|
||||
cloudProvider: string;
|
||||
};
|
||||
export type CreateAccount201 = {
|
||||
export type CreateAccount200 = {
|
||||
data: CloudintegrationtypesGettableAccountWithConnectionArtifactDTO;
|
||||
/**
|
||||
* @type string
|
||||
@@ -3647,14 +3647,6 @@ export type GetConnectionCredentials200 = {
|
||||
export type ListServicesMetadataPathParameters = {
|
||||
cloudProvider: string;
|
||||
};
|
||||
export type ListServicesMetadataParams = {
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
cloud_integration_id?: string;
|
||||
};
|
||||
|
||||
export type ListServicesMetadata200 = {
|
||||
data: CloudintegrationtypesGettableServicesMetadataDTO;
|
||||
/**
|
||||
@@ -3667,14 +3659,6 @@ export type GetServicePathParameters = {
|
||||
cloudProvider: string;
|
||||
serviceId: string;
|
||||
};
|
||||
export type GetServiceParams = {
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
cloud_integration_id?: string;
|
||||
};
|
||||
|
||||
export type GetService200 = {
|
||||
data: CloudintegrationtypesServiceDTO;
|
||||
/**
|
||||
|
||||
@@ -25,11 +25,12 @@ export const REACT_QUERY_KEY = {
|
||||
ALERT_RULE_TIMELINE_GRAPH: 'ALERT_RULE_TIMELINE_GRAPH',
|
||||
GET_CONSUMER_LAG_DETAILS: 'GET_CONSUMER_LAG_DETAILS',
|
||||
TOGGLE_ALERT_STATE: 'TOGGLE_ALERT_STATE',
|
||||
GET_ALL_ALLERTS: 'GET_ALL_ALLERTS',
|
||||
GET_ALL_ALERTS: 'GET_ALL_ALERTS',
|
||||
REMOVE_ALERT_RULE: 'REMOVE_ALERT_RULE',
|
||||
DUPLICATE_ALERT_RULE: 'DUPLICATE_ALERT_RULE',
|
||||
GET_HOST_LIST: 'GET_HOST_LIST',
|
||||
UPDATE_ALERT_RULE: 'UPDATE_ALERT_RULE',
|
||||
ALERT_RULES_CHART_PREVIEW: 'ALERT_RULES_CHART_PREVIEW',
|
||||
GET_ACTIVE_LICENSE_V3: 'GET_ACTIVE_LICENSE_V3',
|
||||
GET_TRACE_V2_WATERFALL: 'GET_TRACE_V2_WATERFALL',
|
||||
GET_TRACE_V2_FLAMEGRAPH: 'GET_TRACE_V2_FLAMEGRAPH',
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useIsFetching, useQueryClient } from 'react-query';
|
||||
import { Button } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { YAxisSource } from 'components/YAxisUnitSelector/types';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import QuerySectionComponent from 'container/FormAlertRules/QuerySection';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { getMetricNameFromQueryData } from 'hooks/useGetYAxisUnit';
|
||||
@@ -62,6 +64,13 @@ function QuerySection(): JSX.Element {
|
||||
return currentQueryKey !== stagedQueryKey;
|
||||
}, [currentQuery, alertType, thresholdState, stagedQuery]);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const isLoadingQueries =
|
||||
useIsFetching([REACT_QUERY_KEY.ALERT_RULES_CHART_PREVIEW]) > 0;
|
||||
const handleCancelQuery = useCallback(() => {
|
||||
queryClient.cancelQueries([REACT_QUERY_KEY.ALERT_RULES_CHART_PREVIEW]);
|
||||
}, [queryClient]);
|
||||
|
||||
const runQueryHandler = useCallback(() => {
|
||||
// Reset the source param when the query is changed
|
||||
// Then manually run the query
|
||||
@@ -130,6 +139,8 @@ function QuerySection(): JSX.Element {
|
||||
setQueryCategory={onQueryCategoryChange}
|
||||
alertType={alertType}
|
||||
runQuery={runQueryHandler}
|
||||
isLoadingQueries={isLoadingQueries}
|
||||
handleCancelQuery={handleCancelQuery}
|
||||
alertDef={alertDef}
|
||||
panelType={PANEL_TYPES.TIME_SERIES}
|
||||
key={currentQuery.queryType}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ENTITY_VERSION_V5 } from 'constants/app';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import AnomalyAlertEvaluationView from 'container/AnomalyAlertEvaluationView';
|
||||
import { INITIAL_CRITICAL_THRESHOLD } from 'container/CreateAlertV2/context/constants';
|
||||
import { Threshold } from 'container/CreateAlertV2/context/types';
|
||||
@@ -185,7 +186,7 @@ function ChartPreview({
|
||||
ENTITY_VERSION_V5,
|
||||
{
|
||||
queryKey: [
|
||||
'chartPreview',
|
||||
REACT_QUERY_KEY.ALERT_RULES_CHART_PREVIEW,
|
||||
userQueryKey || JSON.stringify(query),
|
||||
selectedInterval,
|
||||
minTime,
|
||||
|
||||
@@ -29,6 +29,8 @@ function QuerySection({
|
||||
setQueryCategory,
|
||||
alertType,
|
||||
runQuery,
|
||||
isLoadingQueries,
|
||||
handleCancelQuery,
|
||||
alertDef,
|
||||
panelType,
|
||||
ruleId,
|
||||
@@ -176,6 +178,8 @@ function QuerySection({
|
||||
queryType: queryCategory,
|
||||
});
|
||||
}}
|
||||
handleCancelQuery={handleCancelQuery}
|
||||
isLoadingQueries={isLoadingQueries}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
@@ -195,7 +199,11 @@ function QuerySection({
|
||||
onChange={handleQueryCategoryChange}
|
||||
tabBarExtraContent={
|
||||
<span style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
||||
<RunQueryBtn onStageRunQuery={runQuery} />
|
||||
<RunQueryBtn
|
||||
onStageRunQuery={runQuery}
|
||||
handleCancelQuery={handleCancelQuery}
|
||||
isLoadingQueries={isLoadingQueries}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
items={items}
|
||||
@@ -237,6 +245,8 @@ interface QuerySectionProps {
|
||||
setQueryCategory: (n: EQueryType) => void;
|
||||
alertType: AlertTypes;
|
||||
runQuery: VoidFunction;
|
||||
isLoadingQueries: boolean;
|
||||
handleCancelQuery: () => void;
|
||||
alertDef: AlertDef;
|
||||
panelType: PANEL_TYPES;
|
||||
ruleId: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import { useIsFetching, useQueryClient } from 'react-query';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
@@ -127,6 +127,11 @@ function FormAlertRules({
|
||||
|
||||
// use query client
|
||||
const ruleCache = useQueryClient();
|
||||
const isLoadingAlertQuery =
|
||||
useIsFetching([REACT_QUERY_KEY.ALERT_RULES_CHART_PREVIEW]) > 0;
|
||||
const handleCancelAlertQuery = useCallback(() => {
|
||||
ruleCache.cancelQueries([REACT_QUERY_KEY.ALERT_RULES_CHART_PREVIEW]);
|
||||
}, [ruleCache]);
|
||||
|
||||
const isNewRule = !ruleId || isEmpty(ruleId);
|
||||
|
||||
@@ -914,6 +919,8 @@ function FormAlertRules({
|
||||
setQueryCategory={onQueryCategoryChange}
|
||||
alertType={alertType || AlertTypes.METRICS_BASED_ALERT}
|
||||
runQuery={(): void => handleRunQuery()}
|
||||
isLoadingQueries={isLoadingAlertQuery}
|
||||
handleCancelQuery={handleCancelAlertQuery}
|
||||
alertDef={alertDef}
|
||||
panelType={panelType || PANEL_TYPES.TIME_SERIES}
|
||||
key={currentQuery.queryType}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { Button } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { QueryBuilder } from 'container/QueryBuilder';
|
||||
import { ButtonWrapper } from 'container/TracesExplorer/QuerySection/styles';
|
||||
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import { MeterExplorerEventKeys, MeterExplorerEvents } from '../events';
|
||||
|
||||
function QuerySection(): JSX.Element {
|
||||
const { handleRunQuery } = useQueryBuilder();
|
||||
|
||||
const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.TIME_SERIES);
|
||||
|
||||
return (
|
||||
<div className="query-section">
|
||||
<QueryBuilder
|
||||
panelType={panelTypes}
|
||||
config={{ initialDataSource: DataSource.METRICS, queryVariant: 'static' }}
|
||||
version="v4"
|
||||
actions={
|
||||
<ButtonWrapper>
|
||||
<Button
|
||||
onClick={(): void => {
|
||||
handleRunQuery();
|
||||
logEvent(MeterExplorerEvents.QueryBuilderQueryChanged, {
|
||||
[MeterExplorerEventKeys.Tab]: 'explorer',
|
||||
});
|
||||
}}
|
||||
type="primary"
|
||||
>
|
||||
Run Query
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default QuerySection;
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Button } from 'antd';
|
||||
import { useCallback } from 'react';
|
||||
import { useIsFetching, useQueryClient } from 'react-query';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { QueryBuilder } from 'container/QueryBuilder';
|
||||
import RunQueryBtn from 'container/QueryBuilder/components/RunQueryBtn/RunQueryBtn';
|
||||
import { ButtonWrapper } from 'container/TracesExplorer/QuerySection/styles';
|
||||
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
@@ -11,9 +14,16 @@ import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
|
||||
|
||||
function QuerySection(): JSX.Element {
|
||||
const { handleRunQuery } = useQueryBuilder();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.TIME_SERIES);
|
||||
|
||||
const isLoadingQueries = useIsFetching([REACT_QUERY_KEY.GET_QUERY_RANGE]) > 0;
|
||||
|
||||
const handleCancelQuery = useCallback(() => {
|
||||
queryClient.cancelQueries([REACT_QUERY_KEY.GET_QUERY_RANGE]);
|
||||
}, [queryClient]);
|
||||
|
||||
return (
|
||||
<div className="query-section">
|
||||
<QueryBuilder
|
||||
@@ -22,17 +32,16 @@ function QuerySection(): JSX.Element {
|
||||
version="v4"
|
||||
actions={
|
||||
<ButtonWrapper>
|
||||
<Button
|
||||
onClick={(): void => {
|
||||
<RunQueryBtn
|
||||
onStageRunQuery={(): void => {
|
||||
handleRunQuery();
|
||||
logEvent(MetricsExplorerEvents.QueryBuilderQueryChanged, {
|
||||
[MetricsExplorerEventKeys.Tab]: 'explorer',
|
||||
});
|
||||
}}
|
||||
type="primary"
|
||||
>
|
||||
Run Query
|
||||
</Button>
|
||||
isLoadingQueries={isLoadingQueries}
|
||||
handleCancelQuery={handleCancelQuery}
|
||||
/>
|
||||
</ButtonWrapper>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Drawer, Empty, Skeleton, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { useGetMetricMetadata } from 'api/generated/services/metrics';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
@@ -109,6 +111,11 @@ function Inspect({
|
||||
reset,
|
||||
} = useInspectMetrics(appliedMetricName);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const handleCancelInspectQuery = useCallback(() => {
|
||||
queryClient.cancelQueries(REACT_QUERY_KEY.GET_INSPECT_METRICS_DETAILS);
|
||||
}, [queryClient]);
|
||||
|
||||
const handleDispatchMetricInspectionOptions = useCallback(
|
||||
(action: MetricInspectionAction): void => {
|
||||
dispatchMetricInspectionOptions(action);
|
||||
@@ -234,6 +241,8 @@ function Inspect({
|
||||
inspectMetricsTimeSeries={inspectMetricsTimeSeries}
|
||||
currentQuery={currentQueryData}
|
||||
setCurrentQuery={setCurrentQueryData}
|
||||
isLoadingQueries={isInspectMetricsLoading}
|
||||
handleCancelQuery={handleCancelInspectQuery}
|
||||
/>
|
||||
</div>
|
||||
<div className="inspect-metrics-content-second-col">
|
||||
|
||||
@@ -20,6 +20,8 @@ function QueryBuilder({
|
||||
inspectMetricsTimeSeries,
|
||||
currentQuery,
|
||||
setCurrentQuery,
|
||||
isLoadingQueries,
|
||||
handleCancelQuery,
|
||||
}: QueryBuilderProps): JSX.Element {
|
||||
const applyInspectionOptions = useCallback(() => {
|
||||
setAppliedMetricName(currentMetricName ?? '');
|
||||
@@ -39,7 +41,11 @@ function QueryBuilder({
|
||||
>
|
||||
Query Builder
|
||||
</Button>
|
||||
<RunQueryBtn onStageRunQuery={applyInspectionOptions} />
|
||||
<RunQueryBtn
|
||||
onStageRunQuery={applyInspectionOptions}
|
||||
handleCancelQuery={handleCancelQuery}
|
||||
isLoadingQueries={isLoadingQueries}
|
||||
/>
|
||||
</div>
|
||||
<Card className="inspect-metrics-query-builder-content">
|
||||
<MetricNameSearch
|
||||
|
||||
@@ -103,6 +103,8 @@ describe('QueryBuilder', () => {
|
||||
filterExpression: '',
|
||||
} as any,
|
||||
setCurrentQuery: jest.fn(),
|
||||
isLoadingQueries: false,
|
||||
handleCancelQuery: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -65,6 +65,8 @@ export interface QueryBuilderProps {
|
||||
inspectMetricsTimeSeries: InspectMetricsSeries[];
|
||||
currentQuery: IBuilderQuery;
|
||||
setCurrentQuery: (query: IBuilderQuery) => void;
|
||||
isLoadingQueries: boolean;
|
||||
handleCancelQuery: () => void;
|
||||
}
|
||||
|
||||
export interface MetricNameSearchProps {
|
||||
|
||||
@@ -12,6 +12,8 @@ function MetricsSearch({
|
||||
currentQueryFilterExpression,
|
||||
setCurrentQueryFilterExpression,
|
||||
isLoading,
|
||||
handleCancelQuery,
|
||||
onRunQuery,
|
||||
}: MetricsSearchProps): JSX.Element {
|
||||
const handleOnChange = useCallback(
|
||||
(expression: string): void => {
|
||||
@@ -22,7 +24,8 @@ function MetricsSearch({
|
||||
|
||||
const handleStageAndRunQuery = useCallback(() => {
|
||||
onChange(currentQueryFilterExpression);
|
||||
}, [currentQueryFilterExpression, onChange]);
|
||||
onRunQuery?.();
|
||||
}, [currentQueryFilterExpression, onChange, onRunQuery]);
|
||||
|
||||
const handleRunQuery = useCallback(
|
||||
(expression: string): void => {
|
||||
@@ -53,6 +56,7 @@ function MetricsSearch({
|
||||
<RunQueryBtn
|
||||
onStageRunQuery={handleStageAndRunQuery}
|
||||
isLoadingQueries={isLoading}
|
||||
handleCancelQuery={handleCancelQuery}
|
||||
/>
|
||||
<div className="metrics-search-options">
|
||||
<DateTimeSelectionV2
|
||||
|
||||
@@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux'; // old code, TODO: fix this correctly
|
||||
import { useSearchParams } from 'react-router-dom-v5-compat';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import {
|
||||
@@ -104,6 +105,8 @@ function Summary(): JSX.Element {
|
||||
setCurrentQueryFilterExpression,
|
||||
] = useState<string>(appliedFilterExpression);
|
||||
|
||||
const [isCancelled, setIsCancelled] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentQueryFilterExpression(appliedFilterExpression);
|
||||
}, [appliedFilterExpression]);
|
||||
@@ -164,6 +167,7 @@ function Summary(): JSX.Element {
|
||||
isLoading: isGetMetricsStatsLoading,
|
||||
isError: isGetMetricsStatsError,
|
||||
error: metricsStatsError,
|
||||
reset: resetMetricsStats,
|
||||
} = useGetMetricsStats();
|
||||
|
||||
const {
|
||||
@@ -172,6 +176,7 @@ function Summary(): JSX.Element {
|
||||
isLoading: isGetMetricsTreemapLoading,
|
||||
isError: isGetMetricsTreemapError,
|
||||
error: metricsTreemapError,
|
||||
reset: resetMetricsTreemap,
|
||||
} = useGetMetricsTreemap();
|
||||
|
||||
const metricsStatsApiError = useMemo(
|
||||
@@ -196,6 +201,40 @@ function Summary(): JSX.Element {
|
||||
});
|
||||
}, [metricsTreemapQuery, getMetricsTreemap]);
|
||||
|
||||
const handleCancelQuery = useCallback(() => {
|
||||
resetMetricsStats();
|
||||
resetMetricsTreemap();
|
||||
setCurrentQueryFilterExpression(appliedFilterExpression);
|
||||
setIsCancelled(true);
|
||||
}, [
|
||||
resetMetricsStats,
|
||||
resetMetricsTreemap,
|
||||
setCurrentQueryFilterExpression,
|
||||
appliedFilterExpression,
|
||||
]);
|
||||
|
||||
const handleRunQuery = useCallback(() => {
|
||||
setIsCancelled(false);
|
||||
getMetricsStats({
|
||||
data: {
|
||||
...metricsListQuery,
|
||||
filter: { expression: currentQueryFilterExpression },
|
||||
},
|
||||
});
|
||||
getMetricsTreemap({
|
||||
data: {
|
||||
...metricsTreemapQuery,
|
||||
filter: { expression: currentQueryFilterExpression },
|
||||
},
|
||||
});
|
||||
}, [
|
||||
getMetricsStats,
|
||||
getMetricsTreemap,
|
||||
metricsListQuery,
|
||||
metricsTreemapQuery,
|
||||
currentQueryFilterExpression,
|
||||
]);
|
||||
|
||||
const handleFilterChange = useCallback(
|
||||
(expression: string) => {
|
||||
const newFilters: TagFilter = {
|
||||
@@ -330,11 +369,19 @@ function Summary(): JSX.Element {
|
||||
!isGetMetricsTreemapLoading &&
|
||||
!isGetMetricsTreemapError;
|
||||
|
||||
const isLoadingQueries =
|
||||
isGetMetricsStatsLoading || isGetMetricsTreemapLoading;
|
||||
|
||||
const showFullScreenLoading =
|
||||
(isGetMetricsStatsLoading || isGetMetricsTreemapLoading) &&
|
||||
isLoadingQueries &&
|
||||
formattedMetricsData.length === 0 &&
|
||||
!treeMapData?.data[heatmapView]?.length;
|
||||
|
||||
const showNoMetrics =
|
||||
isMetricsListDataEmpty &&
|
||||
isMetricsTreeMapDataEmpty &&
|
||||
!appliedFilterExpression;
|
||||
|
||||
return (
|
||||
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
|
||||
<div className="metrics-explorer-summary-tab">
|
||||
@@ -343,13 +390,30 @@ function Summary(): JSX.Element {
|
||||
onChange={handleFilterChange}
|
||||
currentQueryFilterExpression={currentQueryFilterExpression}
|
||||
setCurrentQueryFilterExpression={setCurrentQueryFilterExpression}
|
||||
isLoading={isGetMetricsStatsLoading || isGetMetricsTreemapLoading}
|
||||
isLoading={isLoadingQueries}
|
||||
handleCancelQuery={handleCancelQuery}
|
||||
onRunQuery={handleRunQuery}
|
||||
/>
|
||||
{showFullScreenLoading ? (
|
||||
<MetricsLoading />
|
||||
) : isMetricsListDataEmpty &&
|
||||
isMetricsTreeMapDataEmpty &&
|
||||
!appliedFilterExpression ? (
|
||||
) : isCancelled ? (
|
||||
<div className="no-logs-container">
|
||||
<div className="no-logs-container-content">
|
||||
<img
|
||||
className="eyes-emoji"
|
||||
src="/Images/eyesEmoji.svg"
|
||||
alt="eyes emoji"
|
||||
/>
|
||||
<Typography className="no-logs-text">
|
||||
Query cancelled.
|
||||
<span className="sub-text">
|
||||
{' '}
|
||||
Click "Run Query" to load metrics.
|
||||
</span>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
) : showNoMetrics ? (
|
||||
<NoLogs dataSource={DataSource.METRICS} />
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -33,6 +33,8 @@ export interface MetricsSearchProps {
|
||||
currentQueryFilterExpression: string;
|
||||
setCurrentQueryFilterExpression: (expression: string) => void;
|
||||
isLoading: boolean;
|
||||
handleCancelQuery: () => void;
|
||||
onRunQuery: () => void;
|
||||
}
|
||||
|
||||
export interface MetricsTreemapProps {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { QueryKey } from 'react-query';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Tabs, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
@@ -25,8 +24,8 @@ import PromQLQueryContainer from './QueryBuilder/promQL';
|
||||
import './QuerySection.styles.scss';
|
||||
function QuerySection({
|
||||
selectedGraph,
|
||||
queryRangeKey,
|
||||
isLoadingQueries,
|
||||
handleCancelQuery,
|
||||
selectedWidget,
|
||||
dashboardVersion,
|
||||
dashboardId,
|
||||
@@ -179,7 +178,7 @@ function QuerySection({
|
||||
label="Stage & Run Query"
|
||||
onStageRunQuery={handleRunQuery}
|
||||
isLoadingQueries={isLoadingQueries}
|
||||
queryRangeKey={queryRangeKey}
|
||||
handleCancelQuery={handleCancelQuery}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
@@ -191,8 +190,8 @@ function QuerySection({
|
||||
|
||||
interface QueryProps {
|
||||
selectedGraph: PANEL_TYPES;
|
||||
queryRangeKey?: QueryKey;
|
||||
isLoadingQueries?: boolean;
|
||||
isLoadingQueries: boolean;
|
||||
handleCancelQuery: () => void;
|
||||
selectedWidget: Widgets;
|
||||
dashboardVersion?: string;
|
||||
dashboardId?: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { memo, useEffect } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo } from 'react';
|
||||
import { useQueryClient } from 'react-query';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { useSelector } from 'react-redux';
|
||||
import { ENTITY_VERSION_V5 } from 'constants/app';
|
||||
@@ -34,6 +34,7 @@ function LeftContainer({
|
||||
isNewPanel = false,
|
||||
}: WidgetGraphProps): JSX.Element {
|
||||
const { stagedQuery } = useQueryBuilder();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { selectedTime: globalSelectedInterval, minTime, maxTime } = useSelector<
|
||||
AppState,
|
||||
@@ -49,6 +50,10 @@ function LeftContainer({
|
||||
],
|
||||
[globalSelectedInterval, requestData, minTime, maxTime],
|
||||
);
|
||||
const handleCancelQuery = useCallback(() => {
|
||||
queryClient.cancelQueries(queryRangeKey);
|
||||
}, [queryClient, queryRangeKey]);
|
||||
|
||||
const queryResponse = useGetQueryRange(requestData, ENTITY_VERSION_V5, {
|
||||
enabled: !!stagedQuery,
|
||||
queryKey: queryRangeKey,
|
||||
@@ -75,8 +80,8 @@ function LeftContainer({
|
||||
<QueryContainer className="query-section-left-container">
|
||||
<QuerySection
|
||||
selectedGraph={selectedGraph}
|
||||
queryRangeKey={queryRangeKey}
|
||||
isLoadingQueries={queryResponse.isFetching}
|
||||
handleCancelQuery={handleCancelQuery}
|
||||
selectedWidget={selectedWidget}
|
||||
dashboardVersion={ENTITY_VERSION_V5}
|
||||
dashboardId={selectedDashboard?.id}
|
||||
|
||||
@@ -677,18 +677,6 @@ function NewWidget({
|
||||
queryType: currentQuery.queryType,
|
||||
isNewPanel,
|
||||
dataSource: currentQuery?.builder?.queryData?.[0]?.dataSource,
|
||||
...(currentQuery.queryType === EQueryType.CLICKHOUSE && {
|
||||
clickhouseQueryCount: currentQuery.clickhouse_sql.length,
|
||||
clickhouseQueries: currentQuery.clickhouse_sql.map((q) => ({
|
||||
name: q.name,
|
||||
query: (q.query ?? '')
|
||||
.replace(/--[^\n]*/g, '') // strip line comments
|
||||
.replace(/\/\*[\s\S]*?\*\//g, '') // strip block comments
|
||||
.replace(/'(?:[^'\\]|\\.|'')*'/g, "'?'") // replace single-quoted strings (handles \' and '' escapes)
|
||||
.replace(/\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b/g, '?'), // replace numeric literals (int, float, scientific)
|
||||
disabled: q.disabled,
|
||||
})),
|
||||
}),
|
||||
});
|
||||
setSaveModal(true);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useCallback } from 'react';
|
||||
import { QueryKey, useIsFetching, useQueryClient } from 'react-query';
|
||||
import { Button } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import {
|
||||
@@ -12,14 +10,23 @@ import {
|
||||
import { getUserOperatingSystem, UserOperatingSystem } from 'utils/getUserOS';
|
||||
|
||||
import './RunQueryBtn.scss';
|
||||
interface RunQueryBtnProps {
|
||||
|
||||
type RunQueryBtnProps = {
|
||||
className?: string;
|
||||
label?: string;
|
||||
isLoadingQueries?: boolean;
|
||||
handleCancelQuery?: () => void;
|
||||
onStageRunQuery?: () => void;
|
||||
queryRangeKey?: QueryKey;
|
||||
}
|
||||
disabled?: boolean;
|
||||
} & (
|
||||
| {
|
||||
onStageRunQuery: () => void;
|
||||
handleCancelQuery: () => void;
|
||||
isLoadingQueries: boolean;
|
||||
}
|
||||
| {
|
||||
onStageRunQuery?: never;
|
||||
handleCancelQuery?: never;
|
||||
isLoadingQueries?: never;
|
||||
}
|
||||
);
|
||||
|
||||
function RunQueryBtn({
|
||||
className,
|
||||
@@ -27,33 +34,17 @@ function RunQueryBtn({
|
||||
isLoadingQueries,
|
||||
handleCancelQuery,
|
||||
onStageRunQuery,
|
||||
queryRangeKey,
|
||||
disabled,
|
||||
}: RunQueryBtnProps): JSX.Element {
|
||||
const isMac = getUserOperatingSystem() === UserOperatingSystem.MACOS;
|
||||
const queryClient = useQueryClient();
|
||||
const isKeyFetchingCount = useIsFetching(
|
||||
queryRangeKey as QueryKey | undefined,
|
||||
);
|
||||
const isLoading =
|
||||
typeof isLoadingQueries === 'boolean'
|
||||
? isLoadingQueries
|
||||
: isKeyFetchingCount > 0;
|
||||
|
||||
const onCancel = useCallback(() => {
|
||||
if (handleCancelQuery) {
|
||||
return handleCancelQuery();
|
||||
}
|
||||
if (queryRangeKey) {
|
||||
queryClient.cancelQueries(queryRangeKey);
|
||||
}
|
||||
}, [handleCancelQuery, queryClient, queryRangeKey]);
|
||||
const isLoading = isLoadingQueries ?? false;
|
||||
|
||||
return isLoading ? (
|
||||
<Button
|
||||
type="default"
|
||||
icon={<Loader2 size={14} className="loading-icon animate-spin" />}
|
||||
className={cx('cancel-query-btn periscope-btn danger', className)}
|
||||
onClick={onCancel}
|
||||
onClick={handleCancelQuery}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
@@ -61,7 +52,7 @@ function RunQueryBtn({
|
||||
<Button
|
||||
type="primary"
|
||||
className={cx('run-query-btn periscope-btn primary', className)}
|
||||
disabled={isLoading || !onStageRunQuery}
|
||||
disabled={disabled}
|
||||
onClick={onStageRunQuery}
|
||||
icon={<Play size={14} />}
|
||||
>
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
// frontend/src/container/QueryBuilder/components/RunQueryBtn/__tests__/RunQueryBtn.test.tsx
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import RunQueryBtn from '../RunQueryBtn';
|
||||
|
||||
jest.mock('react-query', () => {
|
||||
const actual = jest.requireActual('react-query');
|
||||
return {
|
||||
...actual,
|
||||
useIsFetching: jest.fn(),
|
||||
useQueryClient: jest.fn(),
|
||||
};
|
||||
});
|
||||
import { useIsFetching, useQueryClient } from 'react-query';
|
||||
|
||||
// Mock OS util
|
||||
jest.mock('utils/getUserOS', () => ({
|
||||
getUserOperatingSystem: jest.fn(),
|
||||
@@ -26,79 +17,76 @@ describe('RunQueryBtn', () => {
|
||||
(getUserOperatingSystem as jest.Mock).mockReturnValue(
|
||||
UserOperatingSystem.MACOS,
|
||||
);
|
||||
(useIsFetching as jest.Mock).mockReturnValue(0);
|
||||
(useQueryClient as jest.Mock).mockReturnValue({
|
||||
cancelQueries: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
test('uses isLoadingQueries prop over useIsFetching', () => {
|
||||
// Simulate fetching but prop forces not loading
|
||||
(useIsFetching as jest.Mock).mockReturnValue(1);
|
||||
test('shows run button and hides cancel when isLoadingQueries is false', () => {
|
||||
render(
|
||||
<RunQueryBtn
|
||||
onStageRunQuery={jest.fn()}
|
||||
handleCancelQuery={jest.fn()}
|
||||
isLoadingQueries={false}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByRole('button', { name: /run query/i })).toBeEnabled();
|
||||
expect(
|
||||
screen.queryByRole('button', { name: /cancel/i }),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('shows cancel button and hides run button when isLoadingQueries is true', () => {
|
||||
render(
|
||||
<RunQueryBtn
|
||||
onStageRunQuery={jest.fn()}
|
||||
handleCancelQuery={jest.fn()}
|
||||
isLoadingQueries
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole('button', { name: /run query/i }),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('calls onStageRunQuery when run button is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onRun = jest.fn();
|
||||
render(<RunQueryBtn onStageRunQuery={onRun} isLoadingQueries={false} />);
|
||||
// Should show "Run Query" (not cancel)
|
||||
const runBtn = screen.getByRole('button', { name: /run query/i });
|
||||
expect(runBtn).toBeInTheDocument();
|
||||
expect(runBtn).toBeEnabled();
|
||||
});
|
||||
|
||||
test('fallback cancel: uses handleCancelQuery when no key provided', () => {
|
||||
(useIsFetching as jest.Mock).mockReturnValue(0);
|
||||
const cancelQueries = jest.fn();
|
||||
(useQueryClient as jest.Mock).mockReturnValue({ cancelQueries });
|
||||
|
||||
const onCancel = jest.fn();
|
||||
render(<RunQueryBtn isLoadingQueries handleCancelQuery={onCancel} />);
|
||||
|
||||
const cancelBtn = screen.getByRole('button', { name: /cancel/i });
|
||||
fireEvent.click(cancelBtn);
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
expect(cancelQueries).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('renders run state and triggers on click', () => {
|
||||
const onRun = jest.fn();
|
||||
render(<RunQueryBtn onStageRunQuery={onRun} />);
|
||||
const btn = screen.getByRole('button', { name: /run query/i });
|
||||
expect(btn).toBeEnabled();
|
||||
fireEvent.click(btn);
|
||||
render(
|
||||
<RunQueryBtn
|
||||
onStageRunQuery={onRun}
|
||||
handleCancelQuery={jest.fn()}
|
||||
isLoadingQueries={false}
|
||||
/>,
|
||||
);
|
||||
await user.click(screen.getByRole('button', { name: /run query/i }));
|
||||
expect(onRun).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('disabled when onStageRunQuery is undefined', () => {
|
||||
test('calls handleCancelQuery when cancel button is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onCancel = jest.fn();
|
||||
render(
|
||||
<RunQueryBtn
|
||||
onStageRunQuery={jest.fn()}
|
||||
isLoadingQueries
|
||||
handleCancelQuery={onCancel}
|
||||
/>,
|
||||
);
|
||||
await user.click(screen.getByRole('button', { name: /cancel/i }));
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('is disabled when no props provided', () => {
|
||||
render(<RunQueryBtn />);
|
||||
expect(screen.getByRole('button', { name: /run query/i })).toBeDisabled();
|
||||
});
|
||||
|
||||
test('shows cancel state and calls handleCancelQuery', () => {
|
||||
const onCancel = jest.fn();
|
||||
render(<RunQueryBtn isLoadingQueries handleCancelQuery={onCancel} />);
|
||||
const cancel = screen.getByRole('button', { name: /cancel/i });
|
||||
fireEvent.click(cancel);
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('derives loading from queryKey via useIsFetching and cancels via queryClient', () => {
|
||||
(useIsFetching as jest.Mock).mockReturnValue(1);
|
||||
const cancelQueries = jest.fn();
|
||||
(useQueryClient as jest.Mock).mockReturnValue({ cancelQueries });
|
||||
|
||||
const queryKey = ['GET_QUERY_RANGE', '1h', { some: 'req' }, 1, 2];
|
||||
render(<RunQueryBtn queryRangeKey={queryKey} />);
|
||||
|
||||
// Button switches to cancel state
|
||||
const cancelBtn = screen.getByRole('button', { name: /cancel/i });
|
||||
expect(cancelBtn).toBeInTheDocument();
|
||||
|
||||
// Clicking cancel calls cancelQueries with the key
|
||||
fireEvent.click(cancelBtn);
|
||||
expect(cancelQueries).toHaveBeenCalledWith(queryKey);
|
||||
});
|
||||
|
||||
test('shows Command + CornerDownLeft on mac', () => {
|
||||
const { container } = render(
|
||||
<RunQueryBtn onStageRunQuery={(): void => {}} />,
|
||||
<RunQueryBtn
|
||||
onStageRunQuery={jest.fn()}
|
||||
handleCancelQuery={jest.fn()}
|
||||
isLoadingQueries={false}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelector('.lucide-command')).toBeInTheDocument();
|
||||
expect(
|
||||
@@ -111,7 +99,11 @@ describe('RunQueryBtn', () => {
|
||||
UserOperatingSystem.WINDOWS,
|
||||
);
|
||||
const { container } = render(
|
||||
<RunQueryBtn onStageRunQuery={(): void => {}} />,
|
||||
<RunQueryBtn
|
||||
onStageRunQuery={jest.fn()}
|
||||
handleCancelQuery={jest.fn()}
|
||||
isLoadingQueries={false}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelector('.lucide-chevron-up')).toBeInTheDocument();
|
||||
expect(container.querySelector('.lucide-command')).not.toBeInTheDocument();
|
||||
@@ -120,11 +112,20 @@ describe('RunQueryBtn', () => {
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders custom label when provided', () => {
|
||||
test('renders custom label and calls onStageRunQuery on click', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onRun = jest.fn();
|
||||
render(<RunQueryBtn onStageRunQuery={onRun} label="Stage & Run Query" />);
|
||||
expect(
|
||||
screen.getByRole('button', { name: /stage & run query/i }),
|
||||
).toBeInTheDocument();
|
||||
render(
|
||||
<RunQueryBtn
|
||||
onStageRunQuery={onRun}
|
||||
handleCancelQuery={jest.fn()}
|
||||
isLoadingQueries={false}
|
||||
label="Stage & Run Query"
|
||||
/>,
|
||||
);
|
||||
const btn = screen.getByRole('button', { name: /stage & run query/i });
|
||||
expect(btn).toBeInTheDocument();
|
||||
await user.click(btn);
|
||||
expect(onRun).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function RightToolbarActions({
|
||||
if (showLiveLogs) {
|
||||
return (
|
||||
<div className="right-toolbar-actions-container">
|
||||
<RunQueryBtn />
|
||||
<RunQueryBtn disabled />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -59,7 +59,7 @@ export default function RightToolbarActions({
|
||||
return (
|
||||
<div className="right-toolbar-actions-container">
|
||||
<RunQueryBtn
|
||||
isLoadingQueries={isLoadingQueries}
|
||||
isLoadingQueries={!!isLoadingQueries}
|
||||
handleCancelQuery={handleCancelQuery}
|
||||
onStageRunQuery={onStageRunQuery}
|
||||
/>
|
||||
|
||||
@@ -431,7 +431,7 @@ export const useAlertRuleDuplicate = ({
|
||||
|
||||
const params = useUrlQuery();
|
||||
|
||||
const { refetch } = useQuery(REACT_QUERY_KEY.GET_ALL_ALLERTS, {
|
||||
const { refetch } = useQuery(REACT_QUERY_KEY.GET_ALL_ALERTS, {
|
||||
queryFn: getAll,
|
||||
cacheTime: 0,
|
||||
});
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
package signozapiserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/http/handler"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/aio11ymappingtypes"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (provider *provider) addAIO11yMappingRoutes(router *mux.Router) error {
|
||||
// --- Mapping Groups ---
|
||||
|
||||
if err := router.Handle("/api/v1/ai-o11y/mapping/groups", handler.New(
|
||||
provider.authZ.ViewAccess(provider.aio11yMappingHandler.ListGroups),
|
||||
handler.OpenAPIDef{
|
||||
ID: "ListMappingGroups",
|
||||
Tags: []string{"ai-o11y"},
|
||||
Summary: "List mapping groups",
|
||||
Description: "Returns all span attribute mapping groups for the authenticated org.",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
RequestQuery: new(aio11ymappingtypes.ListMappingGroupsQuery),
|
||||
Response: new(aio11ymappingtypes.ListMappingGroupsResponse),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
|
||||
},
|
||||
)).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/ai-o11y/mapping/groups", handler.New(
|
||||
provider.authZ.AdminAccess(provider.aio11yMappingHandler.CreateGroup),
|
||||
handler.OpenAPIDef{
|
||||
ID: "CreateMappingGroup",
|
||||
Tags: []string{"ai-o11y"},
|
||||
Summary: "Create a mapping group",
|
||||
Description: "Creates a new span attribute mapping group for the org.",
|
||||
Request: new(aio11ymappingtypes.PostableMappingGroup),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(aio11ymappingtypes.GettableMappingGroup),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/ai-o11y/mapping/groups/{id}", handler.New(
|
||||
provider.authZ.AdminAccess(provider.aio11yMappingHandler.UpdateGroup),
|
||||
handler.OpenAPIDef{
|
||||
ID: "UpdateMappingGroup",
|
||||
Tags: []string{"ai-o11y"},
|
||||
Summary: "Update a mapping group",
|
||||
Description: "Partially updates an existing mapping group's name, condition, or enabled state.",
|
||||
Request: new(aio11ymappingtypes.UpdatableMappingGroup),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(aio11ymappingtypes.GettableMappingGroup),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodPut).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/ai-o11y/mapping/groups/{id}", handler.New(
|
||||
provider.authZ.AdminAccess(provider.aio11yMappingHandler.DeleteGroup),
|
||||
handler.OpenAPIDef{
|
||||
ID: "DeleteMappingGroup",
|
||||
Tags: []string{"ai-o11y"},
|
||||
Summary: "Delete a mapping group",
|
||||
Description: "Hard-deletes a mapping group and cascades to all its mappers.",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: nil,
|
||||
ResponseContentType: "",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodDelete).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// --- Mappers ---
|
||||
|
||||
if err := router.Handle("/api/v1/ai-o11y/mapping/groups/{id}/mappers", handler.New(
|
||||
provider.authZ.ViewAccess(provider.aio11yMappingHandler.ListMappers),
|
||||
handler.OpenAPIDef{
|
||||
ID: "ListMappers",
|
||||
Tags: []string{"ai-o11y"},
|
||||
Summary: "List mappers for a group",
|
||||
Description: "Returns all attribute mappers belonging to a mapping group.",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
RequestQuery: new(aio11ymappingtypes.ListMappersQuery),
|
||||
Response: new(aio11ymappingtypes.ListMappersResponse),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
|
||||
},
|
||||
)).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/ai-o11y/mapping/groups/{id}/mappers", handler.New(
|
||||
provider.authZ.AdminAccess(provider.aio11yMappingHandler.CreateMapper),
|
||||
handler.OpenAPIDef{
|
||||
ID: "CreateMapper",
|
||||
Tags: []string{"ai-o11y"},
|
||||
Summary: "Create a mapper",
|
||||
Description: "Adds a new attribute mapper to the specified mapping group.",
|
||||
Request: new(aio11ymappingtypes.PostableMapper),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(aio11ymappingtypes.GettableMapper),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound, http.StatusConflict},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/ai-o11y/mapping/groups/{groupId}/mappers/{mapperId}", handler.New(
|
||||
provider.authZ.AdminAccess(provider.aio11yMappingHandler.UpdateMapper),
|
||||
handler.OpenAPIDef{
|
||||
ID: "UpdateMapper",
|
||||
Tags: []string{"ai-o11y"},
|
||||
Summary: "Update a mapper",
|
||||
Description: "Partially updates an existing mapper's field context, config, or enabled state.",
|
||||
Request: new(aio11ymappingtypes.UpdatableMapper),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(aio11ymappingtypes.GettableMapper),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodPut).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/ai-o11y/mapping/groups/{groupId}/mappers/{mapperId}", handler.New(
|
||||
provider.authZ.AdminAccess(provider.aio11yMappingHandler.DeleteMapper),
|
||||
handler.OpenAPIDef{
|
||||
ID: "DeleteMapper",
|
||||
Tags: []string{"ai-o11y"},
|
||||
Summary: "Delete a mapper",
|
||||
Description: "Hard-deletes a mapper from a mapping group.",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: nil,
|
||||
ResponseContentType: "",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodDelete).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -41,7 +41,7 @@ func (provider *provider) addCloudIntegrationRoutes(router *mux.Router) error {
|
||||
RequestContentType: "application/json",
|
||||
Response: new(citypes.GettableAccountWithConnectionArtifact),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
@@ -138,7 +138,6 @@ func (provider *provider) addCloudIntegrationRoutes(router *mux.Router) error {
|
||||
Summary: "List services metadata",
|
||||
Description: "This endpoint lists the services metadata for the specified cloud provider",
|
||||
Request: nil,
|
||||
RequestQuery: new(citypes.ListServicesMetadataParams),
|
||||
RequestContentType: "",
|
||||
Response: new(citypes.GettableServicesMetadata),
|
||||
ResponseContentType: "application/json",
|
||||
@@ -159,7 +158,6 @@ func (provider *provider) addCloudIntegrationRoutes(router *mux.Router) error {
|
||||
Summary: "Get service",
|
||||
Description: "This endpoint gets a service for the specified cloud provider",
|
||||
Request: nil,
|
||||
RequestQuery: new(citypes.GetServiceParams),
|
||||
RequestContentType: "",
|
||||
Response: new(citypes.Service),
|
||||
ResponseContentType: "application/json",
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/global"
|
||||
"github.com/SigNoz/signoz/pkg/http/handler"
|
||||
"github.com/SigNoz/signoz/pkg/http/middleware"
|
||||
"github.com/SigNoz/signoz/pkg/modules/aio11ymapping"
|
||||
"github.com/SigNoz/signoz/pkg/modules/authdomain"
|
||||
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
||||
@@ -58,7 +57,6 @@ type provider struct {
|
||||
factoryHandler factory.Handler
|
||||
cloudIntegrationHandler cloudintegration.Handler
|
||||
ruleStateHistoryHandler rulestatehistory.Handler
|
||||
aio11yMappingHandler aio11ymapping.Handler
|
||||
}
|
||||
|
||||
func NewFactory(
|
||||
@@ -85,7 +83,6 @@ func NewFactory(
|
||||
factoryHandler factory.Handler,
|
||||
cloudIntegrationHandler cloudintegration.Handler,
|
||||
ruleStateHistoryHandler rulestatehistory.Handler,
|
||||
aio11yMappingHandler aio11ymapping.Handler,
|
||||
) factory.ProviderFactory[apiserver.APIServer, apiserver.Config] {
|
||||
return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, providerSettings factory.ProviderSettings, config apiserver.Config) (apiserver.APIServer, error) {
|
||||
return newProvider(
|
||||
@@ -115,7 +112,6 @@ func NewFactory(
|
||||
factoryHandler,
|
||||
cloudIntegrationHandler,
|
||||
ruleStateHistoryHandler,
|
||||
aio11yMappingHandler,
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -147,7 +143,6 @@ func newProvider(
|
||||
factoryHandler factory.Handler,
|
||||
cloudIntegrationHandler cloudintegration.Handler,
|
||||
ruleStateHistoryHandler rulestatehistory.Handler,
|
||||
aio11yMappingHandler aio11ymapping.Handler,
|
||||
) (apiserver.APIServer, error) {
|
||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/apiserver/signozapiserver")
|
||||
router := mux.NewRouter().UseEncodedPath()
|
||||
@@ -177,7 +172,6 @@ func newProvider(
|
||||
factoryHandler: factoryHandler,
|
||||
cloudIntegrationHandler: cloudIntegrationHandler,
|
||||
ruleStateHistoryHandler: ruleStateHistoryHandler,
|
||||
aio11yMappingHandler: aio11yMappingHandler,
|
||||
}
|
||||
|
||||
provider.authZ = middleware.NewAuthZ(settings.Logger(), orgGetter, authz)
|
||||
@@ -278,10 +272,6 @@ func (provider *provider) AddToRouter(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := provider.addAIO11yMappingRoutes(router); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ type RetryConfig struct {
|
||||
|
||||
func newConfig() factory.Config {
|
||||
return Config{
|
||||
Provider: "noop",
|
||||
BufferSize: 1000,
|
||||
BatchSize: 100,
|
||||
FlushInterval: time.Second,
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
package aio11ymapping
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/aio11ymappingtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
// Module defines the business logic for span attribute mapping groups and mappers.
|
||||
type Module interface {
|
||||
// Group operations
|
||||
|
||||
ListGroups(ctx context.Context, orgID valuer.UUID, q *aio11ymappingtypes.ListMappingGroupsQuery) ([]*aio11ymappingtypes.MappingGroup, error)
|
||||
GetGroup(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*aio11ymappingtypes.MappingGroup, error)
|
||||
CreateGroup(ctx context.Context, orgID valuer.UUID, createdBy string, req *aio11ymappingtypes.PostableMappingGroup) (*aio11ymappingtypes.MappingGroup, error)
|
||||
UpdateGroup(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, req *aio11ymappingtypes.UpdatableMappingGroup) (*aio11ymappingtypes.MappingGroup, error)
|
||||
DeleteGroup(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error
|
||||
|
||||
// Mapper operations
|
||||
|
||||
ListMappers(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID, q *aio11ymappingtypes.ListMappersQuery) ([]*aio11ymappingtypes.Mapper, error)
|
||||
GetMapper(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID, id valuer.UUID) (*aio11ymappingtypes.Mapper, error)
|
||||
CreateMapper(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID, createdBy string, req *aio11ymappingtypes.PostableMapper) (*aio11ymappingtypes.Mapper, error)
|
||||
UpdateMapper(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID, id valuer.UUID, updatedBy string, req *aio11ymappingtypes.UpdatableMapper) (*aio11ymappingtypes.Mapper, error)
|
||||
DeleteMapper(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID, id valuer.UUID) error
|
||||
}
|
||||
|
||||
// Handler defines the HTTP handler interface for mapping group and mapper endpoints.
|
||||
type Handler interface {
|
||||
// Group handlers
|
||||
ListGroups(rw http.ResponseWriter, r *http.Request)
|
||||
CreateGroup(rw http.ResponseWriter, r *http.Request)
|
||||
UpdateGroup(rw http.ResponseWriter, r *http.Request)
|
||||
DeleteGroup(rw http.ResponseWriter, r *http.Request)
|
||||
|
||||
// Mapper handlers
|
||||
ListMappers(rw http.ResponseWriter, r *http.Request)
|
||||
CreateMapper(rw http.ResponseWriter, r *http.Request)
|
||||
UpdateMapper(rw http.ResponseWriter, r *http.Request)
|
||||
DeleteMapper(rw http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
@@ -1,356 +0,0 @@
|
||||
package impiaio11ymapping
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/http/binding"
|
||||
"github.com/SigNoz/signoz/pkg/http/render"
|
||||
"github.com/SigNoz/signoz/pkg/modules/aio11ymapping"
|
||||
"github.com/SigNoz/signoz/pkg/types/aio11ymappingtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
module aio11ymapping.Module
|
||||
providerSettings factory.ProviderSettings
|
||||
}
|
||||
|
||||
func NewHandler(module aio11ymapping.Module, providerSettings factory.ProviderSettings) aio11ymapping.Handler {
|
||||
return &handler{module: module, providerSettings: providerSettings}
|
||||
}
|
||||
|
||||
// ListGroups handles GET /api/v1/ai-o11y/mapping/groups.
|
||||
func (h *handler) ListGroups(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, err)
|
||||
return
|
||||
}
|
||||
|
||||
var q aio11ymappingtypes.ListMappingGroupsQuery
|
||||
if err := binding.Query.BindQuery(r.URL.Query(), &q); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
groups, err := h.module.ListGroups(ctx, orgID, &q)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
items := make([]*aio11ymappingtypes.GettableMappingGroup, len(groups))
|
||||
for i, g := range groups {
|
||||
items[i] = aio11ymappingtypes.NewGettableMappingGroup(g)
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusOK, &aio11ymappingtypes.ListMappingGroupsResponse{Items: items})
|
||||
}
|
||||
|
||||
// CreateGroup handles POST /api/v1/ai-o11y/mapping/groups.
|
||||
func (h *handler) CreateGroup(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, err)
|
||||
return
|
||||
}
|
||||
|
||||
req := new(aio11ymappingtypes.PostableMappingGroup)
|
||||
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
group, err := h.module.CreateGroup(ctx, orgID, claims.Email, req)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusCreated, aio11ymappingtypes.NewGettableMappingGroup(group))
|
||||
}
|
||||
|
||||
// UpdateGroup handles PUT /api/v1/ai-o11y/mapping/groups/{id}.
|
||||
func (h *handler) UpdateGroup(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, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := groupIDFromPath(r)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
req := new(aio11ymappingtypes.UpdatableMappingGroup)
|
||||
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
group, err := h.module.UpdateGroup(ctx, orgID, id, claims.Email, req)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusOK, aio11ymappingtypes.NewGettableMappingGroup(group))
|
||||
}
|
||||
|
||||
// DeleteGroup handles DELETE /api/v1/ai-o11y/mapping/groups/{id}.
|
||||
func (h *handler) DeleteGroup(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, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := groupIDFromPath(r)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.module.DeleteGroup(ctx, orgID, id); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
// ListMappers handles GET /api/v1/ai-o11y/mapping/groups/{id}/mappers.
|
||||
func (h *handler) ListMappers(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, err)
|
||||
return
|
||||
}
|
||||
|
||||
groupID, err := groupIDFromPath(r)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
var q aio11ymappingtypes.ListMappersQuery
|
||||
if err := binding.Query.BindQuery(r.URL.Query(), &q); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
mappers, err := h.module.ListMappers(ctx, orgID, groupID, &q)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
items := make([]*aio11ymappingtypes.GettableMapper, len(mappers))
|
||||
for i, m := range mappers {
|
||||
items[i] = aio11ymappingtypes.NewGettableMapper(m)
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusOK, &aio11ymappingtypes.ListMappersResponse{Items: items})
|
||||
}
|
||||
|
||||
// CreateMapper handles POST /api/v1/ai-o11y/mapping/groups/{id}/mappers.
|
||||
func (h *handler) CreateMapper(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, err)
|
||||
return
|
||||
}
|
||||
|
||||
groupID, err := groupIDFromPath(r)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
req := new(aio11ymappingtypes.PostableMapper)
|
||||
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
mapper, err := h.module.CreateMapper(ctx, orgID, groupID, claims.Email, req)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusCreated, aio11ymappingtypes.NewGettableMapper(mapper))
|
||||
}
|
||||
|
||||
// UpdateMapper handles PUT /api/v1/ai-o11y/mapping/groups/{groupId}/mappers/{mapperId}.
|
||||
func (h *handler) UpdateMapper(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, err)
|
||||
return
|
||||
}
|
||||
|
||||
groupID, err := groupIDFromPath(r)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
mapperID, err := mapperIDFromPath(r)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
req := new(aio11ymappingtypes.UpdatableMapper)
|
||||
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
mapper, err := h.module.UpdateMapper(ctx, orgID, groupID, mapperID, claims.Email, req)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusOK, aio11ymappingtypes.NewGettableMapper(mapper))
|
||||
}
|
||||
|
||||
// DeleteMapper handles DELETE /api/v1/ai-o11y/mapping/groups/{groupId}/mappers/{mapperId}.
|
||||
func (h *handler) DeleteMapper(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, err)
|
||||
return
|
||||
}
|
||||
|
||||
groupID, err := groupIDFromPath(r)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
mapperID, err := mapperIDFromPath(r)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.module.DeleteMapper(ctx, orgID, groupID, mapperID); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
// groupIDFromPath extracts and validates the {id} or {groupId} path variable.
|
||||
func groupIDFromPath(r *http.Request) (valuer.UUID, error) {
|
||||
vars := mux.Vars(r)
|
||||
raw := vars["groupId"]
|
||||
if raw == "" {
|
||||
raw = vars["id"]
|
||||
}
|
||||
if raw == "" {
|
||||
return valuer.UUID{}, errors.Newf(errors.TypeInvalidInput, aio11ymappingtypes.ErrCodeMappingInvalidInput, "group id is missing from the path")
|
||||
}
|
||||
id, err := valuer.NewUUID(raw)
|
||||
if err != nil {
|
||||
return valuer.UUID{}, errors.Wrapf(err, errors.TypeInvalidInput, aio11ymappingtypes.ErrCodeMappingInvalidInput, "group id is not a valid uuid")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// mapperIDFromPath extracts and validates the {mapperId} path variable.
|
||||
func mapperIDFromPath(r *http.Request) (valuer.UUID, error) {
|
||||
raw := mux.Vars(r)["mapperId"]
|
||||
if raw == "" {
|
||||
return valuer.UUID{}, errors.Newf(errors.TypeInvalidInput, aio11ymappingtypes.ErrCodeMappingInvalidInput, "mapper id is missing from the path")
|
||||
}
|
||||
id, err := valuer.NewUUID(raw)
|
||||
if err != nil {
|
||||
return valuer.UUID{}, errors.Wrapf(err, errors.TypeInvalidInput, aio11ymappingtypes.ErrCodeMappingInvalidInput, "mapper id is not a valid uuid")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
@@ -208,7 +208,7 @@ func (s *Server) createPublicServer(api *APIHandler, web web.Web) (*http.Server,
|
||||
s.config.APIServer.Timeout.Default,
|
||||
s.config.APIServer.Timeout.Max,
|
||||
).Wrap)
|
||||
r.Use(middleware.NewAudit(s.signoz.Instrumentation.Logger(), s.config.APIServer.Logging.ExcludedRoutes, s.signoz.Auditor).Wrap)
|
||||
r.Use(middleware.NewAudit(s.signoz.Instrumentation.Logger(), s.config.APIServer.Logging.ExcludedRoutes, nil).Wrap)
|
||||
r.Use(middleware.NewComment().Wrap)
|
||||
|
||||
am := middleware.NewAuthZ(s.signoz.Instrumentation.Logger(), s.signoz.Modules.OrgGetter, s.signoz.Authz)
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
"github.com/SigNoz/signoz/pkg/apiserver"
|
||||
"github.com/SigNoz/signoz/pkg/auditor"
|
||||
"github.com/SigNoz/signoz/pkg/cache"
|
||||
"github.com/SigNoz/signoz/pkg/config"
|
||||
"github.com/SigNoz/signoz/pkg/emailing"
|
||||
@@ -124,9 +123,6 @@ type Config struct {
|
||||
|
||||
// ServiceAccount config
|
||||
ServiceAccount serviceaccount.Config `mapstructure:"serviceaccount"`
|
||||
|
||||
// Auditor config
|
||||
Auditor auditor.Config `mapstructure:"auditor"`
|
||||
}
|
||||
|
||||
func NewConfig(ctx context.Context, logger *slog.Logger, resolverConfig config.ResolverConfig) (Config, error) {
|
||||
@@ -157,7 +153,6 @@ func NewConfig(ctx context.Context, logger *slog.Logger, resolverConfig config.R
|
||||
user.NewConfigFactory(),
|
||||
identn.NewConfigFactory(),
|
||||
serviceaccount.NewConfigFactory(),
|
||||
auditor.NewConfigFactory(),
|
||||
}
|
||||
|
||||
conf, err := config.New(ctx, resolverConfig, configFactories)
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/global"
|
||||
"github.com/SigNoz/signoz/pkg/global/signozglobal"
|
||||
"github.com/SigNoz/signoz/pkg/licensing"
|
||||
"github.com/SigNoz/signoz/pkg/modules/aio11ymapping"
|
||||
"github.com/SigNoz/signoz/pkg/modules/apdex"
|
||||
"github.com/SigNoz/signoz/pkg/modules/apdex/implapdex"
|
||||
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||
@@ -63,7 +62,6 @@ type Handlers struct {
|
||||
RegistryHandler factory.Handler
|
||||
CloudIntegrationHandler cloudintegration.Handler
|
||||
RuleStateHistory rulestatehistory.Handler
|
||||
AIO11yMappingHandler aio11ymapping.Handler
|
||||
}
|
||||
|
||||
func NewHandlers(
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/global"
|
||||
"github.com/SigNoz/signoz/pkg/http/handler"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/modules/aio11ymapping"
|
||||
"github.com/SigNoz/signoz/pkg/modules/authdomain"
|
||||
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
||||
@@ -70,7 +69,6 @@ func NewOpenAPI(ctx context.Context, instrumentation instrumentation.Instrumenta
|
||||
struct{ factory.Handler }{},
|
||||
struct{ cloudintegration.Handler }{},
|
||||
struct{ rulestatehistory.Handler }{},
|
||||
struct{ aio11ymapping.Handler }{},
|
||||
).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -3,8 +3,6 @@ package signoz
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager"
|
||||
"github.com/SigNoz/signoz/pkg/auditor"
|
||||
"github.com/SigNoz/signoz/pkg/auditor/noopauditor"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager/rulebasednotification"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
@@ -288,7 +286,6 @@ func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.Au
|
||||
handlers.RegistryHandler,
|
||||
handlers.CloudIntegrationHandler,
|
||||
handlers.RuleStateHistory,
|
||||
handlers.AIO11yMappingHandler,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -315,12 +312,6 @@ func NewGlobalProviderFactories(identNConfig identn.Config) factory.NamedMap[fac
|
||||
)
|
||||
}
|
||||
|
||||
func NewAuditorProviderFactories() factory.NamedMap[factory.ProviderFactory[auditor.Auditor, auditor.Config]] {
|
||||
return factory.MustNewNamedMap(
|
||||
noopauditor.NewFactory(),
|
||||
)
|
||||
}
|
||||
|
||||
func NewFlaggerProviderFactories(registry featuretypes.Registry) factory.NamedMap[factory.ProviderFactory[flagger.FlaggerProvider, flagger.Config]] {
|
||||
return factory.MustNewNamedMap(
|
||||
configflagger.NewFactory(registry),
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager"
|
||||
"github.com/SigNoz/signoz/pkg/auditor"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager/nfroutingstore/sqlroutingstore"
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
"github.com/SigNoz/signoz/pkg/apiserver"
|
||||
@@ -76,7 +75,6 @@ type SigNoz struct {
|
||||
QueryParser queryparser.QueryParser
|
||||
Flagger flagger.Flagger
|
||||
Gateway gateway.Gateway
|
||||
Auditor auditor.Auditor
|
||||
}
|
||||
|
||||
func New(
|
||||
@@ -96,7 +94,6 @@ func New(
|
||||
authzCallback func(context.Context, sqlstore.SQLStore, licensing.Licensing, dashboard.Module) (factory.ProviderFactory[authz.AuthZ, authz.Config], error),
|
||||
dashboardModuleCallback func(sqlstore.SQLStore, factory.ProviderSettings, analytics.Analytics, organization.Getter, queryparser.QueryParser, querier.Querier, licensing.Licensing) dashboard.Module,
|
||||
gatewayProviderFactory func(licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config],
|
||||
auditorProviderFactories func(licensing.Licensing) factory.NamedMap[factory.ProviderFactory[auditor.Auditor, auditor.Config]],
|
||||
querierHandlerCallback func(factory.ProviderSettings, querier.Querier, analytics.Analytics) querier.Handler,
|
||||
) (*SigNoz, error) {
|
||||
// Initialize instrumentation
|
||||
@@ -374,12 +371,6 @@ func New(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize auditor from the variant-specific provider factories
|
||||
auditor, err := factory.NewProviderFromNamedMap(ctx, providerSettings, config.Auditor, auditorProviderFactories(licensing), config.Auditor.Provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize authns
|
||||
store := sqlauthnstore.NewStore(sqlstore)
|
||||
authNs, err := authNsCallback(ctx, providerSettings, store, licensing)
|
||||
@@ -479,7 +470,6 @@ func New(
|
||||
factory.NewNamedService(factory.MustNewName("tokenizer"), tokenizer),
|
||||
factory.NewNamedService(factory.MustNewName("authz"), authz),
|
||||
factory.NewNamedService(factory.MustNewName("user"), userService, factory.MustNewName("authz")),
|
||||
factory.NewNamedService(factory.MustNewName("auditor"), auditor),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -526,6 +516,5 @@ func New(
|
||||
QueryParser: queryParser,
|
||||
Flagger: flagger,
|
||||
Gateway: gateway,
|
||||
Auditor: auditor,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package aio11ymappingtypes
|
||||
|
||||
import "github.com/SigNoz/signoz/pkg/errors"
|
||||
|
||||
var (
|
||||
ErrCodeMappingGroupNotFound = errors.MustNewCode("mapping_group_not_found")
|
||||
ErrCodeMappingGroupAlreadyExists = errors.MustNewCode("mapping_group_already_exists")
|
||||
ErrCodeMapperNotFound = errors.MustNewCode("mapper_not_found")
|
||||
ErrCodeMapperAlreadyExists = errors.MustNewCode("mapper_already_exists")
|
||||
ErrCodeMappingInvalidInput = errors.MustNewCode("mapping_invalid_input")
|
||||
)
|
||||
@@ -1,147 +0,0 @@
|
||||
package aio11ymappingtypes
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type GroupCategory string
|
||||
|
||||
const (
|
||||
GroupCategoryLLM GroupCategory = "llm"
|
||||
GroupCategoryTool GroupCategory = "tool"
|
||||
GroupCategoryAgent GroupCategory = "agent"
|
||||
)
|
||||
|
||||
func (GroupCategory) Enum() []any {
|
||||
return []any{GroupCategoryLLM, GroupCategoryTool, GroupCategoryAgent}
|
||||
}
|
||||
|
||||
// Condition is the trigger condition for a mapping group.
|
||||
// A group runs when any of the listed attribute/resource key patterns match.
|
||||
// It implements driver.Valuer and sql.Scanner for JSON text column storage.
|
||||
type Condition struct {
|
||||
Attributes []string `json:"attributes"`
|
||||
Resource []string `json:"resource"`
|
||||
}
|
||||
|
||||
func (c Condition) Value() (driver.Value, error) {
|
||||
b, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func (c *Condition) Scan(src any) error {
|
||||
var raw []byte
|
||||
switch v := src.(type) {
|
||||
case string:
|
||||
raw = []byte(v)
|
||||
case []byte:
|
||||
raw = v
|
||||
case nil:
|
||||
*c = Condition{}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("aio11ymappingtypes: cannot scan %T into Condition", src)
|
||||
}
|
||||
return json.Unmarshal(raw, c)
|
||||
}
|
||||
|
||||
// MappingGroup is the domain model for a span attribute mapping group.
|
||||
// It has no serialisation concerns — use GettableMappingGroup for HTTP responses.
|
||||
type MappingGroup struct {
|
||||
types.TimeAuditable
|
||||
types.UserAuditable
|
||||
|
||||
ID string
|
||||
OrgID valuer.UUID
|
||||
Name string
|
||||
Category GroupCategory
|
||||
Condition Condition
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
// NewMappingGroupFromStorable converts a StorableMappingGroup to a MappingGroup.
|
||||
func NewMappingGroupFromStorable(s *StorableMappingGroup) *MappingGroup {
|
||||
return &MappingGroup{
|
||||
TimeAuditable: s.TimeAuditable,
|
||||
UserAuditable: s.UserAuditable,
|
||||
ID: s.ID.StringValue(),
|
||||
OrgID: s.OrgID,
|
||||
Name: s.Name,
|
||||
Category: s.Category,
|
||||
Condition: s.Condition,
|
||||
Enabled: s.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMappingGroupsFromStorable converts a slice of StorableMappingGroup to a slice of MappingGroup.
|
||||
func NewMappingGroupsFromStorable(ss []*StorableMappingGroup) []*MappingGroup {
|
||||
groups := make([]*MappingGroup, len(ss))
|
||||
for i, s := range ss {
|
||||
groups[i] = NewMappingGroupFromStorable(s)
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
// GettableMappingGroup is the HTTP response representation of a mapping group.
|
||||
type GettableMappingGroup struct {
|
||||
ID string `json:"id" required:"true"`
|
||||
Name string `json:"name" required:"true"`
|
||||
Category GroupCategory `json:"category" required:"true"`
|
||||
Condition Condition `json:"condition" required:"true"`
|
||||
Enabled bool `json:"enabled" required:"true"`
|
||||
CreatedAt time.Time `json:"created_at" required:"true"`
|
||||
UpdatedAt time.Time `json:"updated_at" required:"true"`
|
||||
CreatedBy string `json:"created_by" required:"true"`
|
||||
UpdatedBy string `json:"updated_by" required:"true"`
|
||||
}
|
||||
|
||||
// NewGettableMappingGroup converts a domain MappingGroup to a GettableMappingGroup.
|
||||
func NewGettableMappingGroup(g *MappingGroup) *GettableMappingGroup {
|
||||
return &GettableMappingGroup{
|
||||
ID: g.ID,
|
||||
Name: g.Name,
|
||||
Category: g.Category,
|
||||
Condition: g.Condition,
|
||||
Enabled: g.Enabled,
|
||||
CreatedAt: g.CreatedAt,
|
||||
UpdatedAt: g.UpdatedAt,
|
||||
CreatedBy: g.CreatedBy,
|
||||
UpdatedBy: g.UpdatedBy,
|
||||
}
|
||||
}
|
||||
|
||||
// PostableMappingGroup is the HTTP request body for creating a mapping group.
|
||||
type PostableMappingGroup struct {
|
||||
Name string `json:"name" required:"true"`
|
||||
Category GroupCategory `json:"category" required:"true"`
|
||||
Condition Condition `json:"condition" required:"true"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// UpdatableMappingGroup is the HTTP request body for updating a mapping group.
|
||||
// All fields are optional; only non-nil fields are applied.
|
||||
type UpdatableMappingGroup struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
Condition *Condition `json:"condition,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
}
|
||||
|
||||
// ListMappingGroupsQuery holds optional filter parameters for listing mapping groups.
|
||||
type ListMappingGroupsQuery struct {
|
||||
Category *GroupCategory `query:"category"`
|
||||
Enabled *bool `query:"enabled"`
|
||||
}
|
||||
|
||||
// ListMappingGroupsResponse is the response for listing mapping groups.
|
||||
type ListMappingGroupsResponse struct {
|
||||
Items []*GettableMappingGroup `json:"items" required:"true" nullable:"true"`
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
package aio11ymappingtypes
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
// FieldContext is where the target attribute is written.
|
||||
type FieldContext string
|
||||
|
||||
const (
|
||||
FieldContextSpanAttribute FieldContext = "span_attribute"
|
||||
FieldContextResource FieldContext = "resource"
|
||||
)
|
||||
|
||||
func (FieldContext) Enum() []any {
|
||||
return []any{FieldContextSpanAttribute, FieldContextResource}
|
||||
}
|
||||
|
||||
// MapperOperation determines whether the source attribute is moved (deleted) or copied.
|
||||
type MapperOperation string
|
||||
|
||||
const (
|
||||
MapperOperationMove MapperOperation = "move"
|
||||
MapperOperationCopy MapperOperation = "copy"
|
||||
)
|
||||
|
||||
func (MapperOperation) Enum() []any {
|
||||
return []any{MapperOperationMove, MapperOperationCopy}
|
||||
}
|
||||
|
||||
// SourceContext indicates whether the source key is read from span attributes or resource attributes.
|
||||
type SourceContext string
|
||||
|
||||
const (
|
||||
SourceContextAttribute SourceContext = "attribute"
|
||||
SourceContextResource SourceContext = "resource"
|
||||
)
|
||||
|
||||
func (SourceContext) Enum() []any {
|
||||
return []any{SourceContextAttribute, SourceContextResource}
|
||||
}
|
||||
|
||||
// MapperSource describes one candidate source for a target attribute.
|
||||
type MapperSource struct {
|
||||
// Key is the span/resource attribute key to read from.
|
||||
Key string `json:"key"`
|
||||
// Context indicates whether to read from span attributes or resource attributes.
|
||||
Context SourceContext `json:"context"`
|
||||
// Operation determines whether to move or copy the source value.
|
||||
Operation MapperOperation `json:"operation"`
|
||||
// Priority controls the evaluation order; lower value = higher priority.
|
||||
Priority int `json:"priority"`
|
||||
}
|
||||
|
||||
// MapperConfig holds the mapping logic for a single target attribute.
|
||||
// It implements driver.Valuer and sql.Scanner for JSON text column storage.
|
||||
type MapperConfig struct {
|
||||
Sources []MapperSource `json:"sources"`
|
||||
}
|
||||
|
||||
func (m MapperConfig) Value() (driver.Value, error) {
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func (m *MapperConfig) Scan(src any) error {
|
||||
var raw []byte
|
||||
switch v := src.(type) {
|
||||
case string:
|
||||
raw = []byte(v)
|
||||
case []byte:
|
||||
raw = v
|
||||
case nil:
|
||||
*m = MapperConfig{}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("aio11ymappingtypes: cannot scan %T into MapperConfig", src)
|
||||
}
|
||||
return json.Unmarshal(raw, m)
|
||||
}
|
||||
|
||||
// Mapper is the domain model for a span attribute mapper.
|
||||
type Mapper struct {
|
||||
types.TimeAuditable
|
||||
types.UserAuditable
|
||||
|
||||
ID string
|
||||
OrgID valuer.UUID
|
||||
GroupID valuer.UUID
|
||||
Name string
|
||||
FieldContext FieldContext
|
||||
Config MapperConfig
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
// NewMapperFromStorable converts a StorableMapper to a Mapper.
|
||||
func NewMapperFromStorable(s *StorableMapper) *Mapper {
|
||||
return &Mapper{
|
||||
TimeAuditable: s.TimeAuditable,
|
||||
UserAuditable: s.UserAuditable,
|
||||
ID: s.ID.StringValue(),
|
||||
OrgID: s.OrgID,
|
||||
GroupID: s.GroupID,
|
||||
Name: s.Name,
|
||||
FieldContext: s.FieldContext,
|
||||
Config: s.Config,
|
||||
Enabled: s.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMappersFromStorable converts a slice of StorableMapper to a slice of Mapper.
|
||||
func NewMappersFromStorable(ss []*StorableMapper) []*Mapper {
|
||||
mappers := make([]*Mapper, len(ss))
|
||||
for i, s := range ss {
|
||||
mappers[i] = NewMapperFromStorable(s)
|
||||
}
|
||||
return mappers
|
||||
}
|
||||
|
||||
// GettableMapper is the HTTP response representation of a mapper.
|
||||
type GettableMapper struct {
|
||||
ID string `json:"id" required:"true"`
|
||||
GroupID string `json:"group_id" required:"true"`
|
||||
Name string `json:"name" required:"true"`
|
||||
FieldContext FieldContext `json:"field_context" required:"true"`
|
||||
Config MapperConfig `json:"config" required:"true"`
|
||||
Enabled bool `json:"enabled" required:"true"`
|
||||
CreatedAt time.Time `json:"created_at" required:"true"`
|
||||
UpdatedAt time.Time `json:"updated_at" required:"true"`
|
||||
CreatedBy string `json:"created_by" required:"true"`
|
||||
UpdatedBy string `json:"updated_by" required:"true"`
|
||||
}
|
||||
|
||||
// NewGettableMapper converts a domain Mapper to a GettableMapper.
|
||||
func NewGettableMapper(m *Mapper) *GettableMapper {
|
||||
return &GettableMapper{
|
||||
ID: m.ID,
|
||||
GroupID: m.GroupID.StringValue(),
|
||||
Name: m.Name,
|
||||
FieldContext: m.FieldContext,
|
||||
Config: m.Config,
|
||||
Enabled: m.Enabled,
|
||||
CreatedAt: m.CreatedAt,
|
||||
UpdatedAt: m.UpdatedAt,
|
||||
CreatedBy: m.CreatedBy,
|
||||
UpdatedBy: m.UpdatedBy,
|
||||
}
|
||||
}
|
||||
|
||||
// PostableMapper is the HTTP request body for creating a mapper.
|
||||
type PostableMapper struct {
|
||||
Name string `json:"name" required:"true"`
|
||||
FieldContext FieldContext `json:"field_context" required:"true"`
|
||||
Config MapperConfig `json:"config" required:"true"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// UpdatableMapper is the HTTP request body for updating a mapper.
|
||||
// All fields are optional; only non-nil fields are applied.
|
||||
type UpdatableMapper struct {
|
||||
FieldContext *FieldContext `json:"field_context,omitempty"`
|
||||
Config *MapperConfig `json:"config,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
}
|
||||
|
||||
// ListMappersQuery holds optional filter parameters for listing mappers in a group.
|
||||
type ListMappersQuery struct {
|
||||
Enabled *bool `query:"enabled"`
|
||||
}
|
||||
|
||||
// ListMappersResponse is the response for listing mappers within a group.
|
||||
type ListMappersResponse struct {
|
||||
Items []*GettableMapper `json:"items" required:"true" nullable:"true"`
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package aio11ymappingtypes
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
// StorableMappingGroup is the bun/DB representation of a span attribute mapping group.
|
||||
type StorableMappingGroup struct {
|
||||
bun.BaseModel `bun:"table:span_attribute_mapping_group,alias:span_attribute_mapping_group"`
|
||||
|
||||
types.Identifiable
|
||||
types.TimeAuditable
|
||||
types.UserAuditable
|
||||
|
||||
OrgID valuer.UUID `bun:"org_id,type:text,notnull"`
|
||||
Name string `bun:"name,type:text,notnull"`
|
||||
Category GroupCategory `bun:"category,type:text,notnull"`
|
||||
Condition Condition `bun:"condition,type:text,notnull"`
|
||||
Enabled bool `bun:"enabled,notnull,default:true"`
|
||||
}
|
||||
|
||||
// StorableMapper is the bun/DB representation of a span attribute mapper.
|
||||
type StorableMapper struct {
|
||||
bun.BaseModel `bun:"table:span_mapping_attribute,alias:span_mapping_attribute"`
|
||||
|
||||
types.Identifiable
|
||||
types.TimeAuditable
|
||||
types.UserAuditable
|
||||
|
||||
OrgID valuer.UUID `bun:"org_id,type:text,notnull"`
|
||||
GroupID valuer.UUID `bun:"group_id,type:text,notnull"`
|
||||
Name string `bun:"name,type:text,notnull"`
|
||||
FieldContext FieldContext `bun:"field_context,type:text,notnull"`
|
||||
Config MapperConfig `bun:"config,type:text,notnull"`
|
||||
Enabled bool `bun:"enabled,notnull,default:true"`
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package aio11ymappingtypes
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type Store interface {
|
||||
// Group operations
|
||||
ListGroups(ctx context.Context, orgID valuer.UUID, q *ListMappingGroupsQuery) ([]*StorableMappingGroup, error)
|
||||
GetGroup(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*StorableMappingGroup, error)
|
||||
CreateGroup(ctx context.Context, group *StorableMappingGroup) error
|
||||
UpdateGroup(ctx context.Context, group *StorableMappingGroup) error
|
||||
DeleteGroup(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error
|
||||
|
||||
// Mapper operations
|
||||
ListMappers(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID, q *ListMappersQuery) ([]*StorableMapper, error)
|
||||
GetMapper(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID, id valuer.UUID) (*StorableMapper, error)
|
||||
CreateMapper(ctx context.Context, mapper *StorableMapper) error
|
||||
UpdateMapper(ctx context.Context, mapper *StorableMapper) error
|
||||
DeleteMapper(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID, id valuer.UUID) error
|
||||
}
|
||||
@@ -62,10 +62,6 @@ type GettableServicesMetadata struct {
|
||||
Services []*ServiceMetadata `json:"services" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type ListServicesMetadataParams struct {
|
||||
CloudIntegrationID valuer.UUID `query:"cloud_integration_id" required:"false"`
|
||||
}
|
||||
|
||||
// Service represents a cloud integration service with its definition,
|
||||
// cloud integration service is non nil only when the service entry exists in DB with ANY config (enabled or disabled).
|
||||
type Service struct {
|
||||
@@ -73,10 +69,6 @@ type Service struct {
|
||||
CloudIntegrationService *CloudIntegrationService `json:"cloudIntegrationService" required:"true" nullable:"true"`
|
||||
}
|
||||
|
||||
type GetServiceParams struct {
|
||||
CloudIntegrationID valuer.UUID `query:"cloud_integration_id" required:"false"`
|
||||
}
|
||||
|
||||
type UpdatableService struct {
|
||||
Config *ServiceConfig `json:"config" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user