mirror of
https://github.com/SigNoz/signoz.git
synced 2026-03-03 20:42:02 +00:00
Compare commits
18 Commits
issue_4071
...
cursor/rol
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c98823b67 | ||
|
|
ec6b6480c3 | ||
|
|
0670f07dba | ||
|
|
914d125517 | ||
|
|
5aa87af3b2 | ||
|
|
948c1dad04 | ||
|
|
207760128c | ||
|
|
a2115105be | ||
|
|
f114435060 | ||
|
|
f7cab9e61e | ||
|
|
d3893f706d | ||
|
|
a67d585f51 | ||
|
|
cb0a8ba042 | ||
|
|
26bc7c4640 | ||
|
|
320c1aaf6f | ||
|
|
be749fd002 | ||
|
|
c3ebee7169 | ||
|
|
7c42805e6b |
@@ -5,11 +5,9 @@ import (
|
||||
"log/slog"
|
||||
"math"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/querier"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
)
|
||||
|
||||
@@ -69,10 +67,6 @@ func (p *BaseSeasonalProvider) toTSResults(ctx context.Context, resp *qbtypes.Qu
|
||||
}
|
||||
|
||||
func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID, params *anomalyQueryParams) (*anomalyQueryResults, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "anomaly",
|
||||
instrumentation.CodeFunctionName: "getResults",
|
||||
})
|
||||
// TODO(srikanthccv): parallelize this?
|
||||
p.logger.InfoContext(ctx, "fetching results for current period", "anomaly_current_period_query", params.CurrentPeriodQuery)
|
||||
currentPeriodResults, err := p.querier.QueryRange(ctx, orgID, ¶ms.CurrentPeriodQuery)
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/licensing"
|
||||
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
||||
pkgimpldashboard "github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/queryparser"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
||||
@@ -107,10 +105,6 @@ func (module *module) GetPublicDashboardSelectorsAndOrg(ctx context.Context, id
|
||||
}
|
||||
|
||||
func (module *module) GetPublicWidgetQueryRange(ctx context.Context, id valuer.UUID, widgetIdx, startTime, endTime uint64) (*querybuildertypesv5.QueryRangeResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "dashboard",
|
||||
instrumentation.CodeFunctionName: "GetPublicWidgetQueryRange",
|
||||
})
|
||||
dashboard, err := module.GetDashboardByPublicID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -6,12 +6,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/cache"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/utils/labels"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -63,10 +61,6 @@ func (p *BaseSeasonalProvider) getQueryParams(req *GetAnomaliesRequest) *anomaly
|
||||
}
|
||||
|
||||
func (p *BaseSeasonalProvider) getResults(ctx context.Context, orgID valuer.UUID, params *anomalyQueryParams) (*anomalyQueryResults, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "anomaly",
|
||||
instrumentation.CodeFunctionName: "getResults",
|
||||
})
|
||||
zap.L().Info("fetching results for current period", zap.Any("currentPeriodQuery", params.CurrentPeriodQuery))
|
||||
currentPeriodResults, _, err := p.querierV2.QueryRange(ctx, orgID, params.CurrentPeriodQuery)
|
||||
if err != nil {
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"@signozhq/sonner": "0.1.0",
|
||||
"@signozhq/switch": "0.0.2",
|
||||
"@signozhq/table": "0.3.7",
|
||||
"@signozhq/toggle-group": "0.0.1",
|
||||
"@signozhq/tooltip": "0.0.2",
|
||||
"@tanstack/react-table": "8.20.6",
|
||||
"@tanstack/react-virtual": "3.11.2",
|
||||
|
||||
@@ -13,5 +13,6 @@
|
||||
"pipelines": "Pipelines",
|
||||
"archives": "Archives",
|
||||
"logs_to_metrics": "Logs To Metrics",
|
||||
"roles": "Roles"
|
||||
"roles": "Roles",
|
||||
"role_details": "Role Details"
|
||||
}
|
||||
|
||||
@@ -13,5 +13,6 @@
|
||||
"pipelines": "Pipelines",
|
||||
"archives": "Archives",
|
||||
"logs_to_metrics": "Logs To Metrics",
|
||||
"roles": "Roles"
|
||||
"roles": "Roles",
|
||||
"role_details": "Role Details"
|
||||
}
|
||||
|
||||
1
frontend/src/auto-import-registry.d.ts
vendored
1
frontend/src/auto-import-registry.d.ts
vendored
@@ -26,4 +26,5 @@ import '@signozhq/resizable';
|
||||
import '@signozhq/sonner';
|
||||
import '@signozhq/switch';
|
||||
import '@signozhq/table';
|
||||
import '@signozhq/toggle-group';
|
||||
import '@signozhq/tooltip';
|
||||
|
||||
@@ -56,6 +56,7 @@ const ROUTES = {
|
||||
TRACE_EXPLORER: '/trace-explorer',
|
||||
BILLING: '/settings/billing',
|
||||
ROLES_SETTINGS: '/settings/roles',
|
||||
ROLE_DETAILS: '/settings/roles/:roleId',
|
||||
SUPPORT: '/support',
|
||||
LOGS_SAVE_VIEWS: '/logs/saved-views',
|
||||
TRACES_SAVE_VIEWS: '/traces/saved-views',
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
.permission-side-panel-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 100;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.permission-side-panel {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 101;
|
||||
width: 720px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--l2-background);
|
||||
border-left: 1px solid var(--l2-border);
|
||||
box-shadow: -4px 10px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
flex-shrink: 0;
|
||||
height: 48px;
|
||||
padding: 0 16px;
|
||||
background: var(--l2-background);
|
||||
border-bottom: 1px solid var(--l2-border);
|
||||
}
|
||||
|
||||
&__close {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
color: var(--foreground);
|
||||
flex-shrink: 0;
|
||||
|
||||
&:hover {
|
||||
color: var(--text-base-white);
|
||||
}
|
||||
}
|
||||
|
||||
&__header-divider {
|
||||
display: block;
|
||||
width: 1px;
|
||||
height: 16px;
|
||||
background: var(--l2-border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
&__content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 12px 15px;
|
||||
}
|
||||
|
||||
&__resource-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid var(--l2-border);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-shrink: 0;
|
||||
height: 56px;
|
||||
padding: 0 16px;
|
||||
gap: 12px;
|
||||
background: var(--l2-background);
|
||||
border-top: 1px solid var(--l2-border);
|
||||
}
|
||||
|
||||
&__unsaved {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
&__unsaved-dot {
|
||||
display: block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50px;
|
||||
background: var(--primary);
|
||||
box-shadow: 0px 0px 6px 0px rgba(78, 116, 248, 0.4);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__unsaved-text {
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.07px;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
&__footer-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.psp-resource {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-bottom: 1px solid var(--l2-border);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease;
|
||||
|
||||
&--expanded {
|
||||
background: rgba(171, 189, 255, 0.04);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(171, 189, 255, 0.03);
|
||||
}
|
||||
}
|
||||
|
||||
&__left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
&__chevron {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: var(--foreground);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
color: var(--text-base-white);
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
&__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: 8px 0 8px 44px;
|
||||
background: rgba(171, 189, 255, 0.04);
|
||||
}
|
||||
|
||||
&__radio-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
&__radio-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 0;
|
||||
|
||||
label {
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
color: var(--text-base-white);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&__select-wrapper {
|
||||
padding: 6px 16px 4px 24px;
|
||||
}
|
||||
|
||||
&__select {
|
||||
width: 100%;
|
||||
|
||||
// todo: https://github.com/SigNoz/components/issues/116
|
||||
.ant-select-selector {
|
||||
background: var(--l2-background) !important;
|
||||
border: 1px solid var(--border) !important;
|
||||
border-radius: 2px !important;
|
||||
padding: 4px 6px !important;
|
||||
min-height: 32px !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
border-color: var(--input) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-select-selection-placeholder {
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: var(--foreground);
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.ant-select-selection-item {
|
||||
background: var(--input) !important;
|
||||
border: none !important;
|
||||
border-radius: 2px !important;
|
||||
padding: 0 6px !important;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
color: var(--text-base-white) !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.ant-select-selection-item-remove {
|
||||
color: var(--foreground) !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ant-select-arrow {
|
||||
color: var(--foreground);
|
||||
}
|
||||
}
|
||||
|
||||
&__select-popup {
|
||||
.ant-select-item {
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: var(--foreground);
|
||||
background: var(--l2-background);
|
||||
|
||||
&-option-selected {
|
||||
background: var(--border) !important;
|
||||
color: var(--text-base-white) !important;
|
||||
}
|
||||
|
||||
&-option-active {
|
||||
background: var(--l2-background-hover) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-select-dropdown {
|
||||
background: var(--l2-background);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 2px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.permission-side-panel {
|
||||
&__close {
|
||||
&:hover {
|
||||
color: var(--text-base-black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.psp-resource {
|
||||
&__label {
|
||||
color: var(--text-base-black);
|
||||
}
|
||||
|
||||
&__radio-item label {
|
||||
color: var(--text-base-black);
|
||||
}
|
||||
|
||||
&__select .ant-select-selection-item {
|
||||
color: var(--text-base-black) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.psp-resource__select-popup {
|
||||
.ant-select-item {
|
||||
&-option-selected {
|
||||
color: var(--text-base-black) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Button } from '@signozhq/button';
|
||||
import { ChevronDown, ChevronRight, X } from '@signozhq/icons';
|
||||
import {
|
||||
RadioGroup,
|
||||
RadioGroupItem,
|
||||
RadioGroupLabel,
|
||||
} from '@signozhq/radio-group';
|
||||
import { Select, Skeleton } from 'antd';
|
||||
|
||||
import {
|
||||
buildConfig,
|
||||
configsEqual,
|
||||
DEFAULT_RESOURCE_CONFIG,
|
||||
isResourceConfigEqual,
|
||||
} from '../utils';
|
||||
import type {
|
||||
PermissionConfig,
|
||||
PermissionSidePanelProps,
|
||||
ResourceConfig,
|
||||
ResourceDefinition,
|
||||
ScopeType,
|
||||
} from './PermissionSidePanel.types';
|
||||
import { PermissionScope } from './PermissionSidePanel.types';
|
||||
|
||||
import './PermissionSidePanel.styles.scss';
|
||||
|
||||
interface ResourceRowProps {
|
||||
resource: ResourceDefinition;
|
||||
config: ResourceConfig;
|
||||
isExpanded: boolean;
|
||||
onToggleExpand: (id: string) => void;
|
||||
onScopeChange: (id: string, scope: ScopeType) => void;
|
||||
onSelectedIdsChange: (id: string, ids: string[]) => void;
|
||||
}
|
||||
|
||||
function ResourceRow({
|
||||
resource,
|
||||
config,
|
||||
isExpanded,
|
||||
onToggleExpand,
|
||||
onScopeChange,
|
||||
onSelectedIdsChange,
|
||||
}: ResourceRowProps): JSX.Element {
|
||||
return (
|
||||
<div className="psp-resource">
|
||||
<div
|
||||
className={`psp-resource__row${
|
||||
isExpanded ? ' psp-resource__row--expanded' : ''
|
||||
}`}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={(): void => onToggleExpand(resource.id)}
|
||||
onKeyDown={(e): void => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
onToggleExpand(resource.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="psp-resource__left">
|
||||
<span className="psp-resource__chevron">
|
||||
{isExpanded ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
|
||||
</span>
|
||||
<span className="psp-resource__label">{resource.label}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isExpanded && (
|
||||
<div className="psp-resource__body">
|
||||
<RadioGroup
|
||||
value={config.scope}
|
||||
onValueChange={(val): void =>
|
||||
onScopeChange(resource.id, val as ScopeType)
|
||||
}
|
||||
className="psp-resource__radio-group"
|
||||
>
|
||||
<div className="psp-resource__radio-item">
|
||||
<RadioGroupItem
|
||||
value={PermissionScope.ALL}
|
||||
id={`${resource.id}-all`}
|
||||
color="robin"
|
||||
/>
|
||||
<RadioGroupLabel htmlFor={`${resource.id}-all`}>All</RadioGroupLabel>
|
||||
</div>
|
||||
|
||||
<div className="psp-resource__radio-item">
|
||||
<RadioGroupItem
|
||||
value={PermissionScope.ONLY_SELECTED}
|
||||
id={`${resource.id}-only-selected`}
|
||||
color="robin"
|
||||
/>
|
||||
<RadioGroupLabel htmlFor={`${resource.id}-only-selected`}>
|
||||
Only selected
|
||||
</RadioGroupLabel>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
|
||||
{config.scope === PermissionScope.ONLY_SELECTED && (
|
||||
<div className="psp-resource__select-wrapper">
|
||||
{/* TODO: right now made to only accept user input, we need to give it proper resource based value fetching from APIs */}
|
||||
<Select
|
||||
mode="tags"
|
||||
value={config.selectedIds}
|
||||
onChange={(vals: string[]): void =>
|
||||
onSelectedIdsChange(resource.id, vals)
|
||||
}
|
||||
options={resource.options ?? []}
|
||||
placeholder="Select resources..."
|
||||
className="psp-resource__select"
|
||||
popupClassName="psp-resource__select-popup"
|
||||
showSearch
|
||||
filterOption={(input, option): boolean =>
|
||||
String(option?.label ?? '')
|
||||
.toLowerCase()
|
||||
.includes(input.toLowerCase())
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PermissionSidePanel({
|
||||
open,
|
||||
onClose,
|
||||
permissionLabel,
|
||||
resources,
|
||||
initialConfig,
|
||||
isLoading = false,
|
||||
isSaving = false,
|
||||
onSave,
|
||||
}: PermissionSidePanelProps): JSX.Element | null {
|
||||
const [config, setConfig] = useState<PermissionConfig>(() =>
|
||||
buildConfig(resources, initialConfig),
|
||||
);
|
||||
const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setConfig(buildConfig(resources, initialConfig));
|
||||
setExpandedIds(new Set());
|
||||
}
|
||||
}, [open, resources, initialConfig]);
|
||||
|
||||
const savedConfig = useMemo(() => buildConfig(resources, initialConfig), [
|
||||
resources,
|
||||
initialConfig,
|
||||
]);
|
||||
|
||||
const unsavedCount = useMemo(() => {
|
||||
if (configsEqual(config, savedConfig)) {
|
||||
return 0;
|
||||
}
|
||||
return Object.keys(config).filter(
|
||||
(id) => !isResourceConfigEqual(config[id], savedConfig[id]),
|
||||
).length;
|
||||
}, [config, savedConfig]);
|
||||
|
||||
const updateResource = useCallback(
|
||||
(id: string, patch: Partial<ResourceConfig>): void => {
|
||||
setConfig((prev) => ({
|
||||
...prev,
|
||||
[id]: { ...prev[id], ...patch },
|
||||
}));
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const handleToggleExpand = useCallback((id: string): void => {
|
||||
setExpandedIds((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(id)) {
|
||||
next.delete(id);
|
||||
} else {
|
||||
next.add(id);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleScopeChange = useCallback(
|
||||
(id: string, scope: ScopeType): void => {
|
||||
updateResource(id, { scope, selectedIds: [] });
|
||||
},
|
||||
[updateResource],
|
||||
);
|
||||
|
||||
const handleSelectedIdsChange = useCallback(
|
||||
(id: string, ids: string[]): void => {
|
||||
updateResource(id, { selectedIds: ids });
|
||||
},
|
||||
[updateResource],
|
||||
);
|
||||
|
||||
const handleSave = useCallback((): void => {
|
||||
onSave(config);
|
||||
}, [config, onSave]);
|
||||
|
||||
const handleDiscard = useCallback((): void => {
|
||||
setConfig(buildConfig(resources, initialConfig));
|
||||
setExpandedIds(new Set());
|
||||
}, [resources, initialConfig]);
|
||||
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="permission-side-panel-backdrop"
|
||||
role="presentation"
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
<div className="permission-side-panel">
|
||||
<div className="permission-side-panel__header">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="permission-side-panel__close"
|
||||
onClick={onClose}
|
||||
aria-label="Close panel"
|
||||
>
|
||||
<X size={16} />
|
||||
</Button>
|
||||
<span className="permission-side-panel__header-divider" />
|
||||
<span className="permission-side-panel__title">
|
||||
Edit {permissionLabel} Permissions
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="permission-side-panel__content">
|
||||
{isLoading ? (
|
||||
<Skeleton active paragraph={{ rows: 6 }} />
|
||||
) : (
|
||||
<div className="permission-side-panel__resource-list">
|
||||
{resources.map((resource) => (
|
||||
<ResourceRow
|
||||
key={resource.id}
|
||||
resource={resource}
|
||||
config={config[resource.id] ?? DEFAULT_RESOURCE_CONFIG}
|
||||
isExpanded={expandedIds.has(resource.id)}
|
||||
onToggleExpand={handleToggleExpand}
|
||||
onScopeChange={handleScopeChange}
|
||||
onSelectedIdsChange={handleSelectedIdsChange}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="permission-side-panel__footer">
|
||||
{unsavedCount > 0 && (
|
||||
<div className="permission-side-panel__unsaved">
|
||||
<span className="permission-side-panel__unsaved-dot" />
|
||||
<span className="permission-side-panel__unsaved-text">
|
||||
{unsavedCount} unsaved change{unsavedCount !== 1 ? 's' : ''}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="permission-side-panel__footer-actions">
|
||||
<Button
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
prefixIcon={<X size={14} />}
|
||||
onClick={unsavedCount > 0 ? handleDiscard : onClose}
|
||||
size="sm"
|
||||
disabled={isSaving}
|
||||
>
|
||||
{unsavedCount > 0 ? 'Discard' : 'Cancel'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
loading={isSaving}
|
||||
disabled={isLoading || unsavedCount === 0}
|
||||
>
|
||||
Save Changes
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default PermissionSidePanel;
|
||||
@@ -0,0 +1,35 @@
|
||||
export interface ResourceOption {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface ResourceDefinition {
|
||||
id: string;
|
||||
label: string;
|
||||
options?: ResourceOption[];
|
||||
}
|
||||
|
||||
export enum PermissionScope {
|
||||
ALL = 'all',
|
||||
ONLY_SELECTED = 'only_selected',
|
||||
}
|
||||
|
||||
export type ScopeType = PermissionScope;
|
||||
|
||||
export interface ResourceConfig {
|
||||
scope: ScopeType;
|
||||
selectedIds: string[];
|
||||
}
|
||||
|
||||
export type PermissionConfig = Record<string, ResourceConfig>;
|
||||
|
||||
export interface PermissionSidePanelProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
permissionLabel: string;
|
||||
resources: ResourceDefinition[];
|
||||
initialConfig?: PermissionConfig;
|
||||
isLoading?: boolean;
|
||||
isSaving?: boolean;
|
||||
onSave: (config: PermissionConfig) => void;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export { default } from './PermissionSidePanel';
|
||||
export type {
|
||||
PermissionConfig,
|
||||
PermissionSidePanelProps,
|
||||
ResourceConfig,
|
||||
ResourceDefinition,
|
||||
ResourceOption,
|
||||
ScopeType,
|
||||
} from './PermissionSidePanel.types';
|
||||
export { PermissionScope } from './PermissionSidePanel.types';
|
||||
@@ -0,0 +1,417 @@
|
||||
.role-details-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
max-width: 60vw;
|
||||
margin: 0 auto;
|
||||
|
||||
.role-details-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.role-details-title {
|
||||
color: var(--text-base-white);
|
||||
font-family: Inter;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
line-height: 28px;
|
||||
letter-spacing: -0.09px;
|
||||
}
|
||||
|
||||
.role-details-permission-item--readonly {
|
||||
cursor: default !important;
|
||||
pointer-events: none;
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.role-details-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.role-details-tab {
|
||||
gap: 4px;
|
||||
padding: 0 16px;
|
||||
height: 32px;
|
||||
border-radius: 0;
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
letter-spacing: -0.06px;
|
||||
|
||||
&[data-state='on'] {
|
||||
border-radius: 2px 0 0 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.role-details-tab-count {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 20px;
|
||||
padding: 0 6px;
|
||||
border-radius: 50px;
|
||||
background: var(--secondary);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
color: var(--foreground);
|
||||
letter-spacing: -0.06px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.role-details-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.role-details-overview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.role-details-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.role-details-section-label {
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.48px;
|
||||
text-transform: uppercase;
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
.role-details-description-text {
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
.role-details-info-row {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.role-details-info-col {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.role-details-info-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.role-details-info-name {
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
.role-details-permissions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.role-details-permissions-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.role-details-permissions-divider {
|
||||
flex: 1;
|
||||
border: none;
|
||||
border-top: 2px dotted var(--border);
|
||||
border-bottom: 2px dotted var(--border);
|
||||
height: 7px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.role-details-permission-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.role-details-permission-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 44px;
|
||||
padding: 0 12px;
|
||||
background: rgba(171, 189, 255, 0.08);
|
||||
border: 1px solid var(--secondary);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
background: rgba(171, 189, 255, 0.12);
|
||||
}
|
||||
}
|
||||
|
||||
.role-details-permission-item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.role-details-permission-item-label {
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
color: var(--text-base-white);
|
||||
}
|
||||
|
||||
.role-details-members {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.role-details-members-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
height: 32px;
|
||||
padding: 6px 6px 6px 8px;
|
||||
background: var(--l2-background);
|
||||
border: 1px solid var(--secondary);
|
||||
border-radius: 2px;
|
||||
|
||||
.role-details-members-search-icon {
|
||||
flex-shrink: 0;
|
||||
color: var(--foreground);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.role-details-members-search-input {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
letter-spacing: -0.07px;
|
||||
color: var(--foreground);
|
||||
|
||||
&::placeholder {
|
||||
color: var(--foreground);
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.role-details-members-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 420px;
|
||||
border: 1px dashed var(--secondary);
|
||||
border-radius: 3px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.role-details-members-empty-state {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 48px 0;
|
||||
flex-grow: 1;
|
||||
|
||||
.role-details-members-empty-emoji {
|
||||
font-size: 32px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.role-details-members-empty-text {
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
letter-spacing: -0.07px;
|
||||
|
||||
&--bold {
|
||||
font-weight: 500;
|
||||
color: var(--text-base-white);
|
||||
}
|
||||
|
||||
&--muted {
|
||||
font-weight: 400;
|
||||
color: var(--foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.role-details-skeleton {
|
||||
padding: 16px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.role-details-delete-action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
min-width: 32px;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
background: transparent;
|
||||
color: var(--destructive);
|
||||
opacity: 0.6;
|
||||
padding: 0;
|
||||
transition: background-color 0.2s, opacity 0.2s;
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
background: rgba(229, 72, 77, 0.1);
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.role-details-delete-modal {
|
||||
width: calc(100% - 30px) !important;
|
||||
max-width: 384px;
|
||||
|
||||
.ant-modal-content {
|
||||
padding: 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--secondary);
|
||||
background: var(--l2-background);
|
||||
box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
|
||||
.ant-modal-header {
|
||||
padding: 16px;
|
||||
background: var(--l2-background);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
padding: 0 16px 28px 16px;
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px;
|
||||
margin: 0;
|
||||
|
||||
.cancel-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
color: var(--text-base-white);
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.065px;
|
||||
}
|
||||
|
||||
.delete-text {
|
||||
color: var(--foreground);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
|
||||
strong {
|
||||
font-weight: 600;
|
||||
color: var(--text-base-white);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.role-details-delete-modal {
|
||||
.ant-modal-content {
|
||||
.ant-modal-header {
|
||||
.title {
|
||||
color: var(--text-base-black);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
.delete-text {
|
||||
strong {
|
||||
color: var(--text-base-black);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.role-details-page {
|
||||
.role-details-title {
|
||||
color: var(--text-base-black);
|
||||
}
|
||||
|
||||
.role-details-members-empty-state {
|
||||
.role-details-members-empty-text--bold {
|
||||
color: var(--text-base-black);
|
||||
}
|
||||
}
|
||||
|
||||
.role-details-permission-item {
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.07);
|
||||
}
|
||||
}
|
||||
|
||||
.role-details-permission-item-label {
|
||||
color: var(--text-base-black);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { Button } from '@signozhq/button';
|
||||
import { Table2, Trash2, Users } from '@signozhq/icons';
|
||||
import { toast } from '@signozhq/sonner';
|
||||
import { ToggleGroup, ToggleGroupItem } from '@signozhq/toggle-group';
|
||||
import { Skeleton } from 'antd';
|
||||
import { useAuthzResources } from 'api/generated/services/authz';
|
||||
import {
|
||||
getGetObjectsQueryKey,
|
||||
useDeleteRole,
|
||||
useGetObjects,
|
||||
useGetRole,
|
||||
usePatchObjects,
|
||||
} from 'api/generated/services/role';
|
||||
import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { capitalize } from 'lodash-es';
|
||||
import { useErrorModal } from 'providers/ErrorModalProvider';
|
||||
import { RoleType } from 'types/roles';
|
||||
import { handleApiError, toAPIError } from 'utils/errorUtils';
|
||||
|
||||
import { IS_ROLE_DETAILS_AND_CRUD_ENABLED } from '../config';
|
||||
import type { PermissionConfig } from '../PermissionSidePanel';
|
||||
import PermissionSidePanel from '../PermissionSidePanel';
|
||||
import CreateRoleModal from '../RolesComponents/CreateRoleModal';
|
||||
import DeleteRoleModal from '../RolesComponents/DeleteRoleModal';
|
||||
import {
|
||||
buildPatchPayload,
|
||||
derivePermissionTypes,
|
||||
deriveResourcesForRelation,
|
||||
objectsToPermissionConfig,
|
||||
} from '../utils';
|
||||
import MembersTab from './components/MembersTab';
|
||||
import OverviewTab from './components/OverviewTab';
|
||||
import { ROLE_ID_REGEX } from './constants';
|
||||
|
||||
import './RoleDetailsPage.styles.scss';
|
||||
|
||||
type TabKey = 'overview' | 'members';
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
function RoleDetailsPage(): JSX.Element {
|
||||
const { pathname } = useLocation();
|
||||
const history = useHistory();
|
||||
|
||||
useEffect(() => {
|
||||
if (!IS_ROLE_DETAILS_AND_CRUD_ENABLED) {
|
||||
history.push(ROUTES.ROLES_SETTINGS);
|
||||
}
|
||||
}, [history]);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const { showErrorModal } = useErrorModal();
|
||||
|
||||
const { data: authzResourcesResponse } = useAuthzResources({
|
||||
query: { enabled: true },
|
||||
});
|
||||
const authzResources = authzResourcesResponse?.data ?? null;
|
||||
|
||||
// Extract channelId from URL pathname since useParams doesn't work in nested routing
|
||||
const roleIdMatch = pathname.match(ROLE_ID_REGEX);
|
||||
const roleId = roleIdMatch ? roleIdMatch[1] : '';
|
||||
|
||||
const [activeTab, setActiveTab] = useState<TabKey>('overview');
|
||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [activePermission, setActivePermission] = useState<string | null>(null);
|
||||
|
||||
const { data, isLoading, isFetching, isError, error } = useGetRole(
|
||||
{ id: roleId },
|
||||
{ query: { enabled: !!roleId } },
|
||||
);
|
||||
const role = data?.data;
|
||||
const isTransitioning = isFetching && role?.id !== roleId;
|
||||
const isManaged = role?.type === RoleType.MANAGED;
|
||||
|
||||
const permissionTypes = useMemo(
|
||||
() => derivePermissionTypes(authzResources?.relations ?? null),
|
||||
[authzResources],
|
||||
);
|
||||
|
||||
const resourcesForActivePermission = useMemo(
|
||||
() =>
|
||||
activePermission
|
||||
? deriveResourcesForRelation(authzResources ?? null, activePermission)
|
||||
: [],
|
||||
[authzResources, activePermission],
|
||||
);
|
||||
|
||||
const { data: objectsData, isLoading: isLoadingObjects } = useGetObjects(
|
||||
{ id: roleId, relation: activePermission ?? '' },
|
||||
{ query: { enabled: !!activePermission && !!roleId && !isManaged } },
|
||||
);
|
||||
|
||||
const initialConfig = useMemo(() => {
|
||||
if (!objectsData?.data || !activePermission) {
|
||||
return undefined;
|
||||
}
|
||||
return objectsToPermissionConfig(
|
||||
objectsData.data,
|
||||
resourcesForActivePermission,
|
||||
);
|
||||
}, [objectsData, activePermission, resourcesForActivePermission]);
|
||||
|
||||
const handleSaveSuccess = (): void => {
|
||||
toast.success('Permissions saved successfully');
|
||||
if (activePermission) {
|
||||
queryClient.removeQueries(
|
||||
getGetObjectsQueryKey({ id: roleId, relation: activePermission }),
|
||||
);
|
||||
}
|
||||
setActivePermission(null);
|
||||
};
|
||||
|
||||
const { mutate: patchObjects, isLoading: isSaving } = usePatchObjects({
|
||||
mutation: {
|
||||
onSuccess: handleSaveSuccess,
|
||||
onError: (err) => handleApiError(err, showErrorModal),
|
||||
},
|
||||
});
|
||||
|
||||
const { mutate: deleteRole, isLoading: isDeleting } = useDeleteRole({
|
||||
mutation: {
|
||||
onSuccess: (): void => {
|
||||
toast.success('Role deleted successfully');
|
||||
history.push(ROUTES.ROLES_SETTINGS);
|
||||
},
|
||||
onError: (err) => handleApiError(err, showErrorModal),
|
||||
},
|
||||
});
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="role-details-page">
|
||||
<ErrorInPlace
|
||||
error={toAPIError(
|
||||
error,
|
||||
'An unexpected error occurred while fetching role details.',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!IS_ROLE_DETAILS_AND_CRUD_ENABLED ||
|
||||
isLoading ||
|
||||
isTransitioning ||
|
||||
!role
|
||||
) {
|
||||
return (
|
||||
<div className="role-details-page">
|
||||
<Skeleton
|
||||
active
|
||||
paragraph={{ rows: 8 }}
|
||||
className="role-details-skeleton"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const handleSave = (config: PermissionConfig): void => {
|
||||
if (!activePermission || !authzResources) {
|
||||
return;
|
||||
}
|
||||
patchObjects({
|
||||
pathParams: { id: roleId, relation: activePermission },
|
||||
data: buildPatchPayload({
|
||||
newConfig: config,
|
||||
initialConfig: initialConfig ?? {},
|
||||
resources: resourcesForActivePermission,
|
||||
authzRes: authzResources,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="role-details-page">
|
||||
<div className="role-details-header">
|
||||
<h2 className="role-details-title">Role — {role.name}</h2>
|
||||
</div>
|
||||
|
||||
<div className="role-details-nav">
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
value={activeTab}
|
||||
onValueChange={(val): void => {
|
||||
if (val) {
|
||||
setActiveTab(val as TabKey);
|
||||
}
|
||||
}}
|
||||
className="role-details-tabs"
|
||||
>
|
||||
<ToggleGroupItem value="overview" className="role-details-tab">
|
||||
<Table2 size={14} />
|
||||
Overview
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="members" className="role-details-tab">
|
||||
<Users size={14} />
|
||||
Members
|
||||
<span className="role-details-tab-count">0</span>
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
|
||||
{!isManaged && (
|
||||
<div className="role-details-actions">
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="destructive"
|
||||
className="role-details-delete-action-btn"
|
||||
onClick={(): void => setIsDeleteModalOpen(true)}
|
||||
aria-label="Delete role"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
onClick={(): void => setIsEditModalOpen(true)}
|
||||
>
|
||||
Edit Role Details
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{activeTab === 'overview' && (
|
||||
<OverviewTab
|
||||
role={role || null}
|
||||
isManaged={isManaged}
|
||||
permissionTypes={permissionTypes}
|
||||
onPermissionClick={(key): void => setActivePermission(key)}
|
||||
/>
|
||||
)}
|
||||
{activeTab === 'members' && <MembersTab />}
|
||||
|
||||
{!isManaged && (
|
||||
<>
|
||||
<PermissionSidePanel
|
||||
open={activePermission !== null}
|
||||
onClose={(): void => setActivePermission(null)}
|
||||
permissionLabel={activePermission ? capitalize(activePermission) : ''}
|
||||
resources={resourcesForActivePermission}
|
||||
initialConfig={initialConfig}
|
||||
isLoading={isLoadingObjects}
|
||||
isSaving={isSaving}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
|
||||
<CreateRoleModal
|
||||
isOpen={isEditModalOpen}
|
||||
onClose={(): void => setIsEditModalOpen(false)}
|
||||
initialData={{
|
||||
id: roleId,
|
||||
name: role.name || '',
|
||||
description: role.description || '',
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<DeleteRoleModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
roleName={role.name || ''}
|
||||
isDeleting={isDeleting}
|
||||
onCancel={(): void => setIsDeleteModalOpen(false)}
|
||||
onConfirm={(): void => deleteRole({ pathParams: { id: roleId } })}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default RoleDetailsPage;
|
||||
@@ -0,0 +1,438 @@
|
||||
// Ungate feature flag for all tests in this file
|
||||
jest.mock('../../config', () => ({ IS_ROLE_DETAILS_AND_CRUD_ENABLED: true }));
|
||||
|
||||
import {
|
||||
customRoleResponse,
|
||||
managedRoleResponse,
|
||||
} from 'mocks-server/__mockdata__/roles';
|
||||
import { server } from 'mocks-server/server';
|
||||
import { rest } from 'msw';
|
||||
import { render, screen, userEvent, waitFor, within } from 'tests/test-utils';
|
||||
|
||||
import RoleDetailsPage from '../RoleDetailsPage';
|
||||
|
||||
const CUSTOM_ROLE_ID = '019c24aa-3333-0001-aaaa-111111111111';
|
||||
const MANAGED_ROLE_ID = '019c24aa-2248-756f-9833-984f1ab63819';
|
||||
|
||||
const rolesApiBase = 'http://localhost/api/v1/roles';
|
||||
const authzResourcesUrl = 'http://localhost/api/v1/authz/resources';
|
||||
|
||||
const authzResourcesResponse = {
|
||||
status: 'success',
|
||||
data: {
|
||||
relations: { create: ['dashboard'], read: ['dashboard'] },
|
||||
resources: [{ name: 'dashboard', type: 'dashboard' }],
|
||||
},
|
||||
};
|
||||
|
||||
const emptyObjectsResponse = { status: 'success', data: [] };
|
||||
|
||||
const allScopeObjectsResponse = {
|
||||
status: 'success',
|
||||
data: [
|
||||
{
|
||||
resource: { name: 'dashboard', type: 'dashboard' },
|
||||
selectors: ['*'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function setupDefaultHandlers(roleId = CUSTOM_ROLE_ID): void {
|
||||
const roleResponse =
|
||||
roleId === MANAGED_ROLE_ID ? managedRoleResponse : customRoleResponse;
|
||||
|
||||
server.use(
|
||||
rest.get(`${rolesApiBase}/:id`, (_req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(roleResponse)),
|
||||
),
|
||||
rest.get(authzResourcesUrl, (_req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(authzResourcesResponse)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
server.resetHandlers();
|
||||
});
|
||||
|
||||
describe('RoleDetailsPage', () => {
|
||||
it('renders custom role header, tabs, description, permissions, and action buttons', async () => {
|
||||
setupDefaultHandlers();
|
||||
|
||||
render(<RoleDetailsPage />, undefined, {
|
||||
initialRoute: `/settings/roles/${CUSTOM_ROLE_ID}`,
|
||||
});
|
||||
|
||||
expect(await screen.findByText('Role — billing-manager')).toBeInTheDocument();
|
||||
|
||||
// Tab navigation
|
||||
expect(screen.getByText('Overview')).toBeInTheDocument();
|
||||
expect(screen.getByText('Members')).toBeInTheDocument();
|
||||
|
||||
// Role description (OverviewTab)
|
||||
expect(
|
||||
screen.getByText('Custom role for managing billing and invoices.'),
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Permission items derived from mocked authz relations
|
||||
expect(screen.getByText('Create')).toBeInTheDocument();
|
||||
expect(screen.getByText('Read')).toBeInTheDocument();
|
||||
|
||||
// Action buttons present for custom role
|
||||
expect(
|
||||
screen.getByRole('button', { name: /edit role details/i }),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole('button', { name: /delete role/i }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows managed-role warning callout and hides edit/delete buttons', async () => {
|
||||
setupDefaultHandlers(MANAGED_ROLE_ID);
|
||||
|
||||
render(<RoleDetailsPage />, undefined, {
|
||||
initialRoute: `/settings/roles/${MANAGED_ROLE_ID}`,
|
||||
});
|
||||
|
||||
expect(await screen.findByText(/Role — signoz-admin/)).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
'This is a managed role. Permissions and settings are view-only and cannot be modified.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Action buttons absent for managed role
|
||||
expect(screen.queryByText('Edit Role Details')).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole('button', { name: /delete role/i }),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('edit flow: modal opens pre-filled and calls PATCH on save and verify', async () => {
|
||||
const patchSpy = jest.fn();
|
||||
let description = customRoleResponse.data.description;
|
||||
server.use(
|
||||
rest.get(`${rolesApiBase}/:id`, (_req, res, ctx) =>
|
||||
res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
...customRoleResponse,
|
||||
data: { ...customRoleResponse.data, description },
|
||||
}),
|
||||
),
|
||||
),
|
||||
rest.patch(`${rolesApiBase}/:id`, async (req, res, ctx) => {
|
||||
const body = await req.json();
|
||||
patchSpy(body);
|
||||
description = body.description;
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
...customRoleResponse,
|
||||
data: { ...customRoleResponse.data, description },
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
render(<RoleDetailsPage />, undefined, {
|
||||
initialRoute: `/settings/roles/${CUSTOM_ROLE_ID}`,
|
||||
});
|
||||
|
||||
await screen.findByText('Role — billing-manager');
|
||||
|
||||
// Open the edit modal
|
||||
await user.click(screen.getByRole('button', { name: /edit role details/i }));
|
||||
expect(
|
||||
await screen.findByText('Edit Role Details', {
|
||||
selector: '.ant-modal-title',
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Name field is disabled in edit mode (role rename is not allowed)
|
||||
const nameInput = screen.getByPlaceholderText(
|
||||
'Enter role name e.g. : Service Owner',
|
||||
);
|
||||
expect(nameInput).toBeDisabled();
|
||||
|
||||
// Update description and save
|
||||
const descField = screen.getByPlaceholderText(
|
||||
'A helpful description of the role',
|
||||
);
|
||||
await user.clear(descField);
|
||||
await user.type(descField, 'Updated description');
|
||||
await user.click(screen.getByRole('button', { name: /save changes/i }));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(patchSpy).toHaveBeenCalledWith({
|
||||
description: 'Updated description',
|
||||
}),
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
screen.queryByText('Edit Role Details', {
|
||||
selector: '.ant-modal-title',
|
||||
}),
|
||||
).not.toBeInTheDocument(),
|
||||
);
|
||||
|
||||
expect(await screen.findByText('Updated description')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('delete flow: modal shows role name, DELETE called on confirm', async () => {
|
||||
const deleteSpy = jest.fn();
|
||||
|
||||
setupDefaultHandlers();
|
||||
server.use(
|
||||
rest.delete(`${rolesApiBase}/:id`, (_req, res, ctx) => {
|
||||
deleteSpy();
|
||||
return res(ctx.status(200), ctx.json({ status: 'success' }));
|
||||
}),
|
||||
);
|
||||
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
render(<RoleDetailsPage />, undefined, {
|
||||
initialRoute: `/settings/roles/${CUSTOM_ROLE_ID}`,
|
||||
});
|
||||
|
||||
await screen.findByText('Role — billing-manager');
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /delete role/i }));
|
||||
|
||||
expect(
|
||||
await screen.findByText(/Are you sure you want to delete the role/),
|
||||
).toBeInTheDocument();
|
||||
|
||||
const dialog = await screen.findByRole('dialog');
|
||||
await user.click(
|
||||
within(dialog).getByRole('button', { name: /delete role/i }),
|
||||
);
|
||||
|
||||
await waitFor(() => expect(deleteSpy).toHaveBeenCalled());
|
||||
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
screen.queryByText(/Are you sure you want to delete the role/),
|
||||
).not.toBeInTheDocument(),
|
||||
);
|
||||
});
|
||||
|
||||
describe('permission side panel', () => {
|
||||
async function openCreatePanel(
|
||||
user: ReturnType<typeof userEvent.setup>,
|
||||
): Promise<void> {
|
||||
await screen.findByText('Role — billing-manager');
|
||||
await user.click(screen.getByText('Create'));
|
||||
await screen.findByText('Edit Create Permissions');
|
||||
await screen.findByRole('button', { name: /dashboard/i });
|
||||
}
|
||||
|
||||
it('Save Changes is disabled until a resource scope is changed', async () => {
|
||||
setupDefaultHandlers();
|
||||
server.use(
|
||||
rest.get(
|
||||
`${rolesApiBase}/:id/relation/:relation/objects`,
|
||||
(_req, res, ctx) => res(ctx.status(200), ctx.json(emptyObjectsResponse)),
|
||||
),
|
||||
);
|
||||
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
render(<RoleDetailsPage />, undefined, {
|
||||
initialRoute: `/settings/roles/${CUSTOM_ROLE_ID}`,
|
||||
});
|
||||
|
||||
await openCreatePanel(user);
|
||||
|
||||
// No change yet — config matches initial, unsavedCount = 0
|
||||
expect(screen.getByRole('button', { name: /save changes/i })).toBeDisabled();
|
||||
|
||||
// Expand Dashboard and flip to All — now Save is enabled
|
||||
await user.click(screen.getByRole('button', { name: /dashboard/i }));
|
||||
await user.click(screen.getByText('All'));
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: /save changes/i }),
|
||||
).not.toBeDisabled();
|
||||
|
||||
// check for what shown now - unsavedCount = 1
|
||||
expect(screen.getByText('1 unsaved change')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('set scope to All → patchObjects additions: ["*"], deletions: null', async () => {
|
||||
const patchSpy = jest.fn();
|
||||
|
||||
setupDefaultHandlers();
|
||||
server.use(
|
||||
rest.get(
|
||||
`${rolesApiBase}/:id/relation/:relation/objects`,
|
||||
(_req, res, ctx) => res(ctx.status(200), ctx.json(emptyObjectsResponse)),
|
||||
),
|
||||
rest.patch(
|
||||
`${rolesApiBase}/:id/relation/:relation/objects`,
|
||||
async (req, res, ctx) => {
|
||||
patchSpy(await req.json());
|
||||
return res(ctx.status(200), ctx.json({ status: 'success', data: null }));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
render(<RoleDetailsPage />, undefined, {
|
||||
initialRoute: `/settings/roles/${CUSTOM_ROLE_ID}`,
|
||||
});
|
||||
|
||||
await openCreatePanel(user);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /dashboard/i }));
|
||||
await user.click(screen.getByText('All'));
|
||||
await user.click(screen.getByRole('button', { name: /save changes/i }));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(patchSpy).toHaveBeenCalledWith({
|
||||
additions: [
|
||||
{
|
||||
resource: { name: 'dashboard', type: 'dashboard' },
|
||||
selectors: ['*'],
|
||||
},
|
||||
],
|
||||
deletions: null,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('set scope to Only selected with IDs → patchObjects additions contain those IDs', async () => {
|
||||
const patchSpy = jest.fn();
|
||||
|
||||
setupDefaultHandlers();
|
||||
server.use(
|
||||
rest.get(
|
||||
`${rolesApiBase}/:id/relation/:relation/objects`,
|
||||
(_req, res, ctx) => res(ctx.status(200), ctx.json(emptyObjectsResponse)),
|
||||
),
|
||||
rest.patch(
|
||||
`${rolesApiBase}/:id/relation/:relation/objects`,
|
||||
async (req, res, ctx) => {
|
||||
patchSpy(await req.json());
|
||||
return res(ctx.status(200), ctx.json({ status: 'success', data: null }));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
render(<RoleDetailsPage />, undefined, {
|
||||
initialRoute: `/settings/roles/${CUSTOM_ROLE_ID}`,
|
||||
});
|
||||
|
||||
await openCreatePanel(user);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /dashboard/i }));
|
||||
|
||||
const combobox = screen.getByRole('combobox');
|
||||
await user.click(combobox);
|
||||
await user.type(combobox, 'dash-1');
|
||||
await user.keyboard('{Enter}');
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /save changes/i }));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(patchSpy).toHaveBeenCalledWith({
|
||||
additions: [
|
||||
{
|
||||
resource: { name: 'dashboard', type: 'dashboard' },
|
||||
selectors: ['dash-1'],
|
||||
},
|
||||
],
|
||||
deletions: null,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('existing All scope changed to Only selected (empty) → patchObjects deletions: ["*"], additions: null', async () => {
|
||||
const patchSpy = jest.fn();
|
||||
|
||||
setupDefaultHandlers();
|
||||
server.use(
|
||||
rest.get(
|
||||
`${rolesApiBase}/:id/relation/:relation/objects`,
|
||||
(_req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(allScopeObjectsResponse)),
|
||||
),
|
||||
rest.patch(
|
||||
`${rolesApiBase}/:id/relation/:relation/objects`,
|
||||
async (req, res, ctx) => {
|
||||
patchSpy(await req.json());
|
||||
return res(ctx.status(200), ctx.json({ status: 'success', data: null }));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
render(<RoleDetailsPage />, undefined, {
|
||||
initialRoute: `/settings/roles/${CUSTOM_ROLE_ID}`,
|
||||
});
|
||||
|
||||
await openCreatePanel(user);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /dashboard/i }));
|
||||
|
||||
await user.click(screen.getByText('Only selected'));
|
||||
await user.click(screen.getByRole('button', { name: /save changes/i }));
|
||||
|
||||
// Should delete the '*' selector and add nothing
|
||||
await waitFor(() =>
|
||||
expect(patchSpy).toHaveBeenCalledWith({
|
||||
additions: null,
|
||||
deletions: [
|
||||
{
|
||||
resource: { name: 'dashboard', type: 'dashboard' },
|
||||
selectors: ['*'],
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('unsaved changes counter shown on scope change, Discard resets it', async () => {
|
||||
setupDefaultHandlers();
|
||||
server.use(
|
||||
rest.get(
|
||||
`${rolesApiBase}/:id/relation/:relation/objects`,
|
||||
(_req, res, ctx) => res(ctx.status(200), ctx.json(emptyObjectsResponse)),
|
||||
),
|
||||
);
|
||||
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
render(<RoleDetailsPage />, undefined, {
|
||||
initialRoute: `/settings/roles/${CUSTOM_ROLE_ID}`,
|
||||
});
|
||||
|
||||
await openCreatePanel(user);
|
||||
|
||||
// No unsaved changes indicator yet
|
||||
expect(screen.queryByText(/unsaved change/)).not.toBeInTheDocument();
|
||||
|
||||
// Change dashboard scope to "All"
|
||||
await user.click(screen.getByRole('button', { name: /dashboard/i }));
|
||||
await user.click(screen.getByText('All'));
|
||||
|
||||
expect(screen.getByText('1 unsaved change')).toBeInTheDocument();
|
||||
|
||||
// Discard reverts to initial config — counter disappears, Save re-disabled
|
||||
await user.click(screen.getByRole('button', { name: /discard/i }));
|
||||
|
||||
expect(screen.queryByText(/unsaved change/)).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /save changes/i })).toBeDisabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import { useState } from 'react';
|
||||
import { Search } from '@signozhq/icons';
|
||||
|
||||
function MembersTab(): JSX.Element {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
return (
|
||||
<div className="role-details-members">
|
||||
<div className="role-details-members-search">
|
||||
<Search size={12} className="role-details-members-search-icon" />
|
||||
<input
|
||||
type="text"
|
||||
className="role-details-members-search-input"
|
||||
placeholder="Search and add members..."
|
||||
value={searchQuery}
|
||||
onChange={(e): void => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Todo: Right now we are only adding the empty state in this cut */}
|
||||
<div className="role-details-members-content">
|
||||
<div className="role-details-members-empty-state">
|
||||
<span
|
||||
className="role-details-members-empty-emoji"
|
||||
role="img"
|
||||
aria-label="monocle face"
|
||||
>
|
||||
🧐
|
||||
</span>
|
||||
<p className="role-details-members-empty-text">
|
||||
<span className="role-details-members-empty-text--bold">
|
||||
No members added.
|
||||
</span>{' '}
|
||||
<span className="role-details-members-empty-text--muted">
|
||||
Start adding members to this role.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MembersTab;
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Callout } from '@signozhq/callout';
|
||||
|
||||
import { PermissionType, TimestampBadge } from '../../utils';
|
||||
import PermissionItem from './PermissionItem';
|
||||
|
||||
interface OverviewTabProps {
|
||||
role: {
|
||||
description?: string;
|
||||
createdAt?: Date | string;
|
||||
updatedAt?: Date | string;
|
||||
} | null;
|
||||
isManaged: boolean;
|
||||
permissionTypes: PermissionType[];
|
||||
onPermissionClick: (relationKey: string) => void;
|
||||
}
|
||||
|
||||
function OverviewTab({
|
||||
role,
|
||||
isManaged,
|
||||
permissionTypes,
|
||||
onPermissionClick,
|
||||
}: OverviewTabProps): JSX.Element {
|
||||
return (
|
||||
<div className="role-details-overview">
|
||||
{isManaged && (
|
||||
<Callout
|
||||
type="warning"
|
||||
showIcon
|
||||
message="This is a managed role. Permissions and settings are view-only and cannot be modified."
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="role-details-meta">
|
||||
<div>
|
||||
<p className="role-details-section-label">Description</p>
|
||||
<p className="role-details-description-text">{role?.description || '—'}</p>
|
||||
</div>
|
||||
|
||||
<div className="role-details-info-row">
|
||||
<div className="role-details-info-col">
|
||||
<p className="role-details-section-label">Created At</p>
|
||||
<div className="role-details-info-value">
|
||||
<TimestampBadge date={role?.createdAt} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="role-details-info-col">
|
||||
<p className="role-details-section-label">Last Modified At</p>
|
||||
<div className="role-details-info-value">
|
||||
<TimestampBadge date={role?.updatedAt} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="role-details-permissions">
|
||||
<div className="role-details-permissions-header">
|
||||
<span className="role-details-section-label">Permissions</span>
|
||||
<hr className="role-details-permissions-divider" />
|
||||
</div>
|
||||
|
||||
<div className="role-details-permission-list">
|
||||
{permissionTypes.map((permissionType) => (
|
||||
<PermissionItem
|
||||
key={permissionType.key}
|
||||
permissionType={permissionType}
|
||||
isManaged={isManaged}
|
||||
onPermissionClick={onPermissionClick}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default OverviewTab;
|
||||
@@ -0,0 +1,54 @@
|
||||
import { ChevronRight } from '@signozhq/icons';
|
||||
|
||||
import { PermissionType } from '../../utils';
|
||||
|
||||
interface PermissionItemProps {
|
||||
permissionType: PermissionType;
|
||||
isManaged: boolean;
|
||||
onPermissionClick: (key: string) => void;
|
||||
}
|
||||
|
||||
function PermissionItem({
|
||||
permissionType,
|
||||
isManaged,
|
||||
onPermissionClick,
|
||||
}: PermissionItemProps): JSX.Element {
|
||||
const { key, label, icon } = permissionType;
|
||||
|
||||
if (isManaged) {
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
className="role-details-permission-item role-details-permission-item--readonly"
|
||||
>
|
||||
<div className="role-details-permission-item-left">
|
||||
{icon}
|
||||
<span className="role-details-permission-item-label">{label}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
className="role-details-permission-item"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={(): void => onPermissionClick(key)}
|
||||
onKeyDown={(e): void => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
onPermissionClick(key);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="role-details-permission-item-left">
|
||||
{icon}
|
||||
<span className="role-details-permission-item-label">{label}</span>
|
||||
</div>
|
||||
<ChevronRight size={14} color="var(--foreground)" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PermissionItem;
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
BadgePlus,
|
||||
Eye,
|
||||
LayoutList,
|
||||
PencilRuler,
|
||||
Settings,
|
||||
Trash2,
|
||||
} from '@signozhq/icons';
|
||||
|
||||
export const ROLE_ID_REGEX = /\/settings\/roles\/([^/]+)/;
|
||||
|
||||
export type IconComponent = React.ComponentType<any>;
|
||||
|
||||
export const PERMISSION_ICON_MAP: Record<string, IconComponent> = {
|
||||
create: BadgePlus,
|
||||
list: LayoutList,
|
||||
read: Eye,
|
||||
update: PencilRuler,
|
||||
delete: Trash2,
|
||||
};
|
||||
|
||||
export const FALLBACK_PERMISSION_ICON: IconComponent = Settings;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './RoleDetailsPage';
|
||||
@@ -0,0 +1,184 @@
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import { generatePath, useHistory } from 'react-router-dom';
|
||||
import { Button } from '@signozhq/button';
|
||||
import { X } from '@signozhq/icons';
|
||||
import { Input, inputVariants } from '@signozhq/input';
|
||||
import { toast } from '@signozhq/sonner';
|
||||
import { Form, Modal } from 'antd';
|
||||
import {
|
||||
invalidateGetRole,
|
||||
invalidateListRoles,
|
||||
useCreateRole,
|
||||
usePatchRole,
|
||||
} from 'api/generated/services/role';
|
||||
import type { RoletypesPostableRoleDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { useErrorModal } from 'providers/ErrorModalProvider';
|
||||
import { handleApiError } from 'utils/errorUtils';
|
||||
|
||||
import '../RolesSettings.styles.scss';
|
||||
|
||||
export interface CreateRoleModalInitialData {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface CreateRoleModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
initialData?: CreateRoleModalInitialData;
|
||||
}
|
||||
|
||||
interface CreateRoleFormValues {
|
||||
name: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
function CreateRoleModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
initialData,
|
||||
}: CreateRoleModalProps): JSX.Element {
|
||||
const [form] = Form.useForm<CreateRoleFormValues>();
|
||||
const queryClient = useQueryClient();
|
||||
const history = useHistory();
|
||||
const { showErrorModal } = useErrorModal();
|
||||
|
||||
const isEditMode = !!initialData?.id;
|
||||
const prevIsOpen = useRef(isOpen);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && !prevIsOpen.current) {
|
||||
if (isEditMode && initialData) {
|
||||
form.setFieldsValue({
|
||||
name: initialData.name,
|
||||
description: initialData.description || '',
|
||||
});
|
||||
} else {
|
||||
form.resetFields();
|
||||
}
|
||||
}
|
||||
prevIsOpen.current = isOpen;
|
||||
}, [isOpen, isEditMode, initialData, form]);
|
||||
|
||||
const handleSuccess = async (
|
||||
message: string,
|
||||
redirectPath?: string,
|
||||
): Promise<void> => {
|
||||
await invalidateListRoles(queryClient);
|
||||
if (isEditMode && initialData?.id) {
|
||||
await invalidateGetRole(queryClient, { id: initialData.id });
|
||||
}
|
||||
toast.success(message);
|
||||
form.resetFields();
|
||||
onClose();
|
||||
if (redirectPath) {
|
||||
history.push(redirectPath);
|
||||
}
|
||||
};
|
||||
|
||||
const handleError = (error: unknown): void => {
|
||||
handleApiError(error as any, showErrorModal);
|
||||
};
|
||||
|
||||
const { mutate: createRole, isLoading: isCreating } = useCreateRole({
|
||||
mutation: {
|
||||
onSuccess: (res) =>
|
||||
handleSuccess(
|
||||
'Role created successfully',
|
||||
generatePath(ROUTES.ROLE_DETAILS, { roleId: res.data.id }),
|
||||
),
|
||||
onError: handleError,
|
||||
},
|
||||
});
|
||||
|
||||
const { mutate: patchRole, isLoading: isPatching } = usePatchRole({
|
||||
mutation: {
|
||||
onSuccess: () => handleSuccess('Role updated successfully'),
|
||||
onError: handleError,
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = useCallback(async (): Promise<void> => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
if (isEditMode && initialData?.id) {
|
||||
patchRole({
|
||||
pathParams: { id: initialData.id },
|
||||
data: { description: values.description || '' },
|
||||
});
|
||||
} else {
|
||||
const data: RoletypesPostableRoleDTO = {
|
||||
name: values.name,
|
||||
...(values.description ? { description: values.description } : {}),
|
||||
};
|
||||
createRole({ data });
|
||||
}
|
||||
} catch {
|
||||
// form validation failed; antd handles inline error display
|
||||
}
|
||||
}, [form, createRole, patchRole, isEditMode, initialData]);
|
||||
|
||||
const onCancel = useCallback((): void => {
|
||||
form.resetFields();
|
||||
onClose();
|
||||
}, [form, onClose]);
|
||||
|
||||
const isLoading = isCreating || isPatching;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={isOpen}
|
||||
onCancel={onCancel}
|
||||
title={isEditMode ? 'Edit Role Details' : 'Create a New Role'}
|
||||
footer={[
|
||||
<Button
|
||||
key="cancel"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
onClick={onCancel}
|
||||
size="sm"
|
||||
>
|
||||
<X size={14} />
|
||||
Cancel
|
||||
</Button>,
|
||||
<Button
|
||||
key="submit"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
onClick={onSubmit}
|
||||
loading={isLoading}
|
||||
size="sm"
|
||||
>
|
||||
{isEditMode ? 'Save Changes' : 'Create Role'}
|
||||
</Button>,
|
||||
]}
|
||||
destroyOnClose
|
||||
className="create-role-modal"
|
||||
width={530}
|
||||
>
|
||||
<Form form={form} layout="vertical" className="create-role-form">
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="Name"
|
||||
rules={[{ required: true, message: 'Role name is required' }]}
|
||||
>
|
||||
<Input
|
||||
disabled={isEditMode}
|
||||
placeholder="Enter role name e.g. : Service Owner"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label="Description">
|
||||
<textarea
|
||||
className={inputVariants()}
|
||||
placeholder="A helpful description of the role"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreateRoleModal;
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Button } from '@signozhq/button';
|
||||
import { Trash2, X } from '@signozhq/icons';
|
||||
import { Modal } from 'antd';
|
||||
|
||||
interface DeleteRoleModalProps {
|
||||
isOpen: boolean;
|
||||
roleName: string;
|
||||
isDeleting: boolean;
|
||||
onCancel: () => void;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
function DeleteRoleModal({
|
||||
isOpen,
|
||||
roleName,
|
||||
isDeleting,
|
||||
onCancel,
|
||||
onConfirm,
|
||||
}: DeleteRoleModalProps): JSX.Element {
|
||||
return (
|
||||
<Modal
|
||||
open={isOpen}
|
||||
onCancel={onCancel}
|
||||
title={<span className="title">Delete Role</span>}
|
||||
closable
|
||||
footer={[
|
||||
<Button
|
||||
key="cancel"
|
||||
className="cancel-btn"
|
||||
prefixIcon={<X size={16} />}
|
||||
onClick={onCancel}
|
||||
size="sm"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
>
|
||||
Cancel
|
||||
</Button>,
|
||||
<Button
|
||||
key="delete"
|
||||
className="delete-btn"
|
||||
prefixIcon={<Trash2 size={16} />}
|
||||
onClick={onConfirm}
|
||||
loading={isDeleting}
|
||||
size="sm"
|
||||
variant="solid"
|
||||
color="destructive"
|
||||
>
|
||||
Delete Role
|
||||
</Button>,
|
||||
]}
|
||||
destroyOnClose
|
||||
className="role-details-delete-modal"
|
||||
>
|
||||
<p className="delete-text">
|
||||
Are you sure you want to delete the role <strong>{roleName}</strong>? This
|
||||
action cannot be undone.
|
||||
</p>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default DeleteRoleModal;
|
||||
@@ -5,11 +5,15 @@ import { useListRoles } from 'api/generated/services/role';
|
||||
import { RoletypesRoleDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import ROUTES from 'constants/routes';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import LineClampedText from 'periscope/components/LineClampedText/LineClampedText';
|
||||
import { useTimezone } from 'providers/Timezone';
|
||||
import { RoleType } from 'types/roles';
|
||||
import { toAPIError } from 'utils/errorUtils';
|
||||
|
||||
import { IS_ROLE_DETAILS_AND_CRUD_ENABLED } from '../config';
|
||||
|
||||
import '../RolesSettings.styles.scss';
|
||||
|
||||
const PAGE_SIZE = 20;
|
||||
@@ -68,11 +72,15 @@ function RolesListingTable({
|
||||
}, [roles, searchQuery]);
|
||||
|
||||
const managedRoles = useMemo(
|
||||
() => filteredRoles.filter((role) => role.type?.toLowerCase() === 'managed'),
|
||||
() =>
|
||||
filteredRoles.filter(
|
||||
(role) => role.type?.toLowerCase() === RoleType.MANAGED,
|
||||
),
|
||||
[filteredRoles],
|
||||
);
|
||||
const customRoles = useMemo(
|
||||
() => filteredRoles.filter((role) => role.type?.toLowerCase() === 'custom'),
|
||||
() =>
|
||||
filteredRoles.filter((role) => role.type?.toLowerCase() === RoleType.CUSTOM),
|
||||
[filteredRoles],
|
||||
);
|
||||
|
||||
@@ -174,9 +182,34 @@ function RolesListingTable({
|
||||
);
|
||||
}
|
||||
|
||||
const navigateToRole = (roleId: string): void => {
|
||||
history.push(ROUTES.ROLE_DETAILS.replace(':roleId', roleId));
|
||||
};
|
||||
|
||||
// todo: use table from periscope when its available for consumption
|
||||
const renderRow = (role: RoletypesRoleDTO): JSX.Element => (
|
||||
<div key={role.id} className="roles-table-row">
|
||||
<div
|
||||
key={role.id}
|
||||
className={`roles-table-row ${
|
||||
IS_ROLE_DETAILS_AND_CRUD_ENABLED ? 'roles-table-row--clickable' : ''
|
||||
}`}
|
||||
role="button"
|
||||
tabIndex={IS_ROLE_DETAILS_AND_CRUD_ENABLED ? 0 : -1}
|
||||
onClick={(): void => {
|
||||
if (IS_ROLE_DETAILS_AND_CRUD_ENABLED && role.id) {
|
||||
navigateToRole(role.id);
|
||||
}
|
||||
}}
|
||||
onKeyDown={(e): void => {
|
||||
if (
|
||||
IS_ROLE_DETAILS_AND_CRUD_ENABLED &&
|
||||
(e.key === 'Enter' || e.key === ' ') &&
|
||||
role.id
|
||||
) {
|
||||
navigateToRole(role.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="roles-table-cell roles-table-cell--name">
|
||||
{role.name ?? '—'}
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
.roles-settings-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
@@ -31,8 +32,28 @@
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.roles-settings-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.role-settings-toolbar-button {
|
||||
display: flex;
|
||||
width: 156px;
|
||||
height: 32px;
|
||||
padding: 6px 12px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: https://github.com/SigNoz/components/issues/116
|
||||
.roles-search-wrapper {
|
||||
flex: 1;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
background: var(--l3-background);
|
||||
@@ -153,6 +174,14 @@
|
||||
background: rgba(171, 189, 255, 0.02);
|
||||
border-bottom: 1px solid var(--secondary);
|
||||
gap: 24px;
|
||||
|
||||
&--clickable {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: rgba(171, 189, 255, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.roles-table-cell {
|
||||
@@ -235,4 +264,136 @@
|
||||
.roles-table-row {
|
||||
background: rgba(0, 0, 0, 0.01);
|
||||
}
|
||||
|
||||
.create-role-modal {
|
||||
.ant-modal-title {
|
||||
color: var(--text-base-black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.create-role-modal {
|
||||
.ant-modal-content {
|
||||
padding: 0;
|
||||
background: var(--l2-background);
|
||||
border: 1px solid var(--secondary);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ant-modal-header {
|
||||
background: var(--l2-background);
|
||||
border-bottom: 1px solid var(--secondary);
|
||||
padding: 16px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ant-modal-close {
|
||||
top: 14px;
|
||||
inset-inline-end: 16px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: var(--foreground);
|
||||
|
||||
.ant-modal-close-x {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-title {
|
||||
color: var(--text-base-white);
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.065px;
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.create-role-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
.ant-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ant-form-item-label {
|
||||
padding-bottom: 8px;
|
||||
|
||||
label {
|
||||
color: var(--foreground);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: https://github.com/SigNoz/components/issues/116
|
||||
input,
|
||||
textarea {
|
||||
width: 100%;
|
||||
background: var(--l3-background);
|
||||
border: 1px solid var(--l3-border);
|
||||
border-radius: 2px;
|
||||
padding: 6px 8px;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
letter-spacing: -0.07px;
|
||||
color: var(--l1-foreground);
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--l3-foreground);
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
border-color: var(--input);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--l3-border);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 100px;
|
||||
resize: vertical;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin: 0;
|
||||
padding: 0 16px;
|
||||
height: 56px;
|
||||
border-top: 1px solid var(--secondary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@signozhq/button';
|
||||
import { Plus } from '@signozhq/icons';
|
||||
import { Input } from '@signozhq/input';
|
||||
|
||||
import { IS_ROLE_DETAILS_AND_CRUD_ENABLED } from './config';
|
||||
import CreateRoleModal from './RolesComponents/CreateRoleModal';
|
||||
import RolesListingTable from './RolesComponents/RolesListingTable';
|
||||
|
||||
import './RolesSettings.styles.scss';
|
||||
|
||||
function RolesSettings(): JSX.Element {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="roles-settings" data-testid="roles-settings">
|
||||
@@ -17,16 +22,33 @@ function RolesSettings(): JSX.Element {
|
||||
</p>
|
||||
</div>
|
||||
<div className="roles-settings-content">
|
||||
<div className="roles-search-wrapper">
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="Search for roles..."
|
||||
value={searchQuery}
|
||||
onChange={(e): void => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
<div className="roles-settings-toolbar">
|
||||
<div className="roles-search-wrapper">
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="Search for roles..."
|
||||
value={searchQuery}
|
||||
onChange={(e): void => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{IS_ROLE_DETAILS_AND_CRUD_ENABLED && (
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
className="role-settings-toolbar-button"
|
||||
onClick={(): void => setIsCreateModalOpen(true)}
|
||||
>
|
||||
<Plus size={14} />
|
||||
Custom role
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<RolesListingTable searchQuery={searchQuery} />
|
||||
</div>
|
||||
<CreateRoleModal
|
||||
isOpen={isCreateModalOpen}
|
||||
onClose={(): void => setIsCreateModalOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
384
frontend/src/container/RolesSettings/__tests__/utils.test.ts
Normal file
384
frontend/src/container/RolesSettings/__tests__/utils.test.ts
Normal file
@@ -0,0 +1,384 @@
|
||||
import type {
|
||||
AuthtypesGettableObjectsDTO,
|
||||
AuthtypesGettableResourcesDTO,
|
||||
} from 'api/generated/services/sigNoz.schemas';
|
||||
|
||||
import type {
|
||||
PermissionConfig,
|
||||
ResourceDefinition,
|
||||
} from '../PermissionSidePanel/PermissionSidePanel.types';
|
||||
import { PermissionScope } from '../PermissionSidePanel/PermissionSidePanel.types';
|
||||
import {
|
||||
buildConfig,
|
||||
buildPatchPayload,
|
||||
configsEqual,
|
||||
DEFAULT_RESOURCE_CONFIG,
|
||||
derivePermissionTypes,
|
||||
deriveResourcesForRelation,
|
||||
objectsToPermissionConfig,
|
||||
} from '../utils';
|
||||
|
||||
jest.mock('../RoleDetails/constants', () => {
|
||||
const MockIcon = (): null => null;
|
||||
return {
|
||||
PERMISSION_ICON_MAP: {
|
||||
create: MockIcon,
|
||||
list: MockIcon,
|
||||
read: MockIcon,
|
||||
update: MockIcon,
|
||||
delete: MockIcon,
|
||||
},
|
||||
FALLBACK_PERMISSION_ICON: MockIcon,
|
||||
ROLE_ID_REGEX: /\/settings\/roles\/([^/]+)/,
|
||||
};
|
||||
});
|
||||
|
||||
const dashboardResource: AuthtypesGettableResourcesDTO['resources'][number] = {
|
||||
name: 'dashboard',
|
||||
type: 'metaresource',
|
||||
};
|
||||
|
||||
const alertResource: AuthtypesGettableResourcesDTO['resources'][number] = {
|
||||
name: 'alert',
|
||||
type: 'metaresource',
|
||||
};
|
||||
|
||||
const baseAuthzResources: AuthtypesGettableResourcesDTO = {
|
||||
resources: [dashboardResource, alertResource],
|
||||
relations: {
|
||||
create: ['metaresource'],
|
||||
read: ['metaresource'],
|
||||
},
|
||||
};
|
||||
|
||||
const resourceDefs: ResourceDefinition[] = [
|
||||
{ id: 'dashboard', label: 'Dashboard' },
|
||||
{ id: 'alert', label: 'Alert' },
|
||||
];
|
||||
|
||||
const ID_A = 'aaaaaaaa-0000-0000-0000-000000000001';
|
||||
const ID_B = 'bbbbbbbb-0000-0000-0000-000000000002';
|
||||
const ID_C = 'cccccccc-0000-0000-0000-000000000003';
|
||||
|
||||
describe('buildPatchPayload', () => {
|
||||
it('sends only the added selector as an addition', () => {
|
||||
const initial: PermissionConfig = {
|
||||
dashboard: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [ID_A] },
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [] },
|
||||
};
|
||||
const newConfig: PermissionConfig = {
|
||||
dashboard: {
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [ID_A, ID_B],
|
||||
},
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [] },
|
||||
};
|
||||
|
||||
const result = buildPatchPayload({
|
||||
newConfig,
|
||||
initialConfig: initial,
|
||||
resources: resourceDefs,
|
||||
authzRes: baseAuthzResources,
|
||||
});
|
||||
|
||||
expect(result.additions).toEqual([
|
||||
{ resource: dashboardResource, selectors: [ID_B] },
|
||||
]);
|
||||
expect(result.deletions).toBeNull();
|
||||
});
|
||||
|
||||
it('sends only the removed selector as a deletion', () => {
|
||||
const initial: PermissionConfig = {
|
||||
dashboard: {
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [ID_A, ID_B, ID_C],
|
||||
},
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [] },
|
||||
};
|
||||
const newConfig: PermissionConfig = {
|
||||
dashboard: {
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [ID_A, ID_C],
|
||||
},
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [] },
|
||||
};
|
||||
|
||||
const result = buildPatchPayload({
|
||||
newConfig,
|
||||
initialConfig: initial,
|
||||
resources: resourceDefs,
|
||||
authzRes: baseAuthzResources,
|
||||
});
|
||||
|
||||
expect(result.deletions).toEqual([
|
||||
{ resource: dashboardResource, selectors: [ID_B] },
|
||||
]);
|
||||
expect(result.additions).toBeNull();
|
||||
});
|
||||
|
||||
it('treats selector order as irrelevant — produces no payload when IDs are identical', () => {
|
||||
const initial: PermissionConfig = {
|
||||
dashboard: {
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [ID_A, ID_B],
|
||||
},
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [] },
|
||||
};
|
||||
const newConfig: PermissionConfig = {
|
||||
dashboard: {
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [ID_B, ID_A],
|
||||
},
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [] },
|
||||
};
|
||||
|
||||
const result = buildPatchPayload({
|
||||
newConfig,
|
||||
initialConfig: initial,
|
||||
resources: resourceDefs,
|
||||
authzRes: baseAuthzResources,
|
||||
});
|
||||
|
||||
expect(result.additions).toBeNull();
|
||||
expect(result.deletions).toBeNull();
|
||||
});
|
||||
|
||||
it('replaces wildcard with specific IDs when switching all → only_selected', () => {
|
||||
const initial: PermissionConfig = {
|
||||
dashboard: { scope: PermissionScope.ALL, selectedIds: [] },
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [] },
|
||||
};
|
||||
const newConfig: PermissionConfig = {
|
||||
dashboard: {
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [ID_A, ID_B],
|
||||
},
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [] },
|
||||
};
|
||||
|
||||
const result = buildPatchPayload({
|
||||
newConfig,
|
||||
initialConfig: initial,
|
||||
resources: resourceDefs,
|
||||
authzRes: baseAuthzResources,
|
||||
});
|
||||
|
||||
expect(result.deletions).toEqual([
|
||||
{ resource: dashboardResource, selectors: ['*'] },
|
||||
]);
|
||||
expect(result.additions).toEqual([
|
||||
{ resource: dashboardResource, selectors: [ID_A, ID_B] },
|
||||
]);
|
||||
});
|
||||
|
||||
it('only deletes wildcard when switching all → only_selected with empty selector list', () => {
|
||||
const initial: PermissionConfig = {
|
||||
dashboard: { scope: PermissionScope.ALL, selectedIds: [] },
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [] },
|
||||
};
|
||||
const newConfig: PermissionConfig = {
|
||||
dashboard: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [] },
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [] },
|
||||
};
|
||||
|
||||
const result = buildPatchPayload({
|
||||
newConfig,
|
||||
initialConfig: initial,
|
||||
resources: resourceDefs,
|
||||
authzRes: baseAuthzResources,
|
||||
});
|
||||
|
||||
expect(result.deletions).toEqual([
|
||||
{ resource: dashboardResource, selectors: ['*'] },
|
||||
]);
|
||||
expect(result.additions).toBeNull();
|
||||
});
|
||||
|
||||
it('only includes resources that actually changed', () => {
|
||||
const initial: PermissionConfig = {
|
||||
dashboard: { scope: PermissionScope.ALL, selectedIds: [] },
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [ID_A] },
|
||||
};
|
||||
const newConfig: PermissionConfig = {
|
||||
dashboard: { scope: PermissionScope.ALL, selectedIds: [] }, // unchanged
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [ID_A, ID_B] }, // added ID_B
|
||||
};
|
||||
|
||||
const result = buildPatchPayload({
|
||||
newConfig,
|
||||
initialConfig: initial,
|
||||
resources: resourceDefs,
|
||||
authzRes: baseAuthzResources,
|
||||
});
|
||||
|
||||
expect(result.additions).toEqual([
|
||||
{ resource: alertResource, selectors: [ID_B] },
|
||||
]);
|
||||
expect(result.deletions).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('objectsToPermissionConfig', () => {
|
||||
it('maps a wildcard selector to ALL scope', () => {
|
||||
const objects: AuthtypesGettableObjectsDTO[] = [
|
||||
{ resource: dashboardResource, selectors: ['*'] },
|
||||
];
|
||||
|
||||
const result = objectsToPermissionConfig(objects, resourceDefs);
|
||||
|
||||
expect(result.dashboard).toEqual({
|
||||
scope: PermissionScope.ALL,
|
||||
selectedIds: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('maps specific selectors to ONLY_SELECTED scope with the IDs', () => {
|
||||
const objects: AuthtypesGettableObjectsDTO[] = [
|
||||
{ resource: dashboardResource, selectors: [ID_A, ID_B] },
|
||||
];
|
||||
|
||||
const result = objectsToPermissionConfig(objects, resourceDefs);
|
||||
|
||||
expect(result.dashboard).toEqual({
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [ID_A, ID_B],
|
||||
});
|
||||
});
|
||||
|
||||
it('defaults to ONLY_SELECTED with empty selectedIds when resource is absent from API response', () => {
|
||||
const result = objectsToPermissionConfig([], resourceDefs);
|
||||
|
||||
expect(result.dashboard).toEqual({
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [],
|
||||
});
|
||||
expect(result.alert).toEqual({
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('configsEqual', () => {
|
||||
it('returns true for identical configs', () => {
|
||||
const config: PermissionConfig = {
|
||||
dashboard: { scope: PermissionScope.ALL, selectedIds: [] },
|
||||
alert: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [ID_A] },
|
||||
};
|
||||
|
||||
expect(configsEqual(config, { ...config })).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false when configs differ', () => {
|
||||
const a: PermissionConfig = {
|
||||
dashboard: { scope: PermissionScope.ALL, selectedIds: [] },
|
||||
};
|
||||
const b: PermissionConfig = {
|
||||
dashboard: { scope: PermissionScope.ONLY_SELECTED, selectedIds: [] },
|
||||
};
|
||||
|
||||
expect(configsEqual(a, b)).toBe(false);
|
||||
|
||||
const c: PermissionConfig = {
|
||||
dashboard: {
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [ID_C, ID_B],
|
||||
},
|
||||
};
|
||||
const d: PermissionConfig = {
|
||||
dashboard: {
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [ID_A, ID_B],
|
||||
},
|
||||
};
|
||||
|
||||
expect(configsEqual(c, d)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true when selectedIds are the same but in different order', () => {
|
||||
const a: PermissionConfig = {
|
||||
dashboard: {
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [ID_A, ID_B],
|
||||
},
|
||||
};
|
||||
const b: PermissionConfig = {
|
||||
dashboard: {
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [ID_B, ID_A],
|
||||
},
|
||||
};
|
||||
|
||||
expect(configsEqual(a, b)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildConfig', () => {
|
||||
it('uses initial values when provided and defaults for resources not in initial', () => {
|
||||
const initial: PermissionConfig = {
|
||||
dashboard: { scope: PermissionScope.ALL, selectedIds: [] },
|
||||
};
|
||||
|
||||
const result = buildConfig(resourceDefs, initial);
|
||||
|
||||
expect(result.dashboard).toEqual({
|
||||
scope: PermissionScope.ALL,
|
||||
selectedIds: [],
|
||||
});
|
||||
expect(result.alert).toEqual(DEFAULT_RESOURCE_CONFIG);
|
||||
});
|
||||
|
||||
it('applies DEFAULT_RESOURCE_CONFIG to all resources when no initial is provided', () => {
|
||||
const result = buildConfig(resourceDefs);
|
||||
|
||||
expect(result.dashboard).toEqual(DEFAULT_RESOURCE_CONFIG);
|
||||
expect(result.alert).toEqual(DEFAULT_RESOURCE_CONFIG);
|
||||
});
|
||||
});
|
||||
|
||||
describe('derivePermissionTypes', () => {
|
||||
it('derives one PermissionType per relation key with correct key and capitalised label', () => {
|
||||
const relations: AuthtypesGettableResourcesDTO['relations'] = {
|
||||
create: ['metaresource'],
|
||||
read: ['metaresource'],
|
||||
delete: ['metaresource'],
|
||||
};
|
||||
|
||||
const result = derivePermissionTypes(relations);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result.map((p) => p.key)).toEqual(['create', 'read', 'delete']);
|
||||
expect(result[0].label).toBe('Create');
|
||||
});
|
||||
|
||||
it('falls back to the default set of permission types when relations is null', () => {
|
||||
const result = derivePermissionTypes(null);
|
||||
|
||||
expect(result.map((p) => p.key)).toEqual([
|
||||
'create',
|
||||
'list',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deriveResourcesForRelation', () => {
|
||||
it('returns resources whose type matches the relation', () => {
|
||||
const result = deriveResourcesForRelation(baseAuthzResources, 'create');
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result.map((r) => r.id)).toEqual(['dashboard', 'alert']);
|
||||
});
|
||||
|
||||
it('returns an empty array when authzResources is null', () => {
|
||||
expect(deriveResourcesForRelation(null, 'create')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('returns an empty array when the relation is not defined in the map', () => {
|
||||
expect(
|
||||
deriveResourcesForRelation(baseAuthzResources, 'nonexistent'),
|
||||
).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
1
frontend/src/container/RolesSettings/config.ts
Normal file
1
frontend/src/container/RolesSettings/config.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const IS_ROLE_DETAILS_AND_CRUD_ENABLED = false;
|
||||
226
frontend/src/container/RolesSettings/utils.tsx
Normal file
226
frontend/src/container/RolesSettings/utils.tsx
Normal file
@@ -0,0 +1,226 @@
|
||||
import React from 'react';
|
||||
import { Badge } from '@signozhq/badge';
|
||||
import type {
|
||||
AuthtypesGettableObjectsDTO,
|
||||
AuthtypesGettableResourcesDTO,
|
||||
} from 'api/generated/services/sigNoz.schemas';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { capitalize } from 'lodash-es';
|
||||
import { useTimezone } from 'providers/Timezone';
|
||||
|
||||
import type {
|
||||
PermissionConfig,
|
||||
ResourceConfig,
|
||||
ResourceDefinition,
|
||||
} from './PermissionSidePanel/PermissionSidePanel.types';
|
||||
import { PermissionScope } from './PermissionSidePanel/PermissionSidePanel.types';
|
||||
import {
|
||||
FALLBACK_PERMISSION_ICON,
|
||||
PERMISSION_ICON_MAP,
|
||||
} from './RoleDetails/constants';
|
||||
|
||||
export interface PermissionType {
|
||||
key: string;
|
||||
label: string;
|
||||
icon: JSX.Element;
|
||||
}
|
||||
|
||||
export interface PatchPayloadOptions {
|
||||
newConfig: PermissionConfig;
|
||||
initialConfig: PermissionConfig;
|
||||
resources: ResourceDefinition[];
|
||||
authzRes: AuthtypesGettableResourcesDTO;
|
||||
}
|
||||
|
||||
export function derivePermissionTypes(
|
||||
relations: AuthtypesGettableResourcesDTO['relations'] | null,
|
||||
): PermissionType[] {
|
||||
const iconSize = { size: 14 };
|
||||
|
||||
if (!relations) {
|
||||
return Object.entries(PERMISSION_ICON_MAP).map(([key, IconComp]) => ({
|
||||
key,
|
||||
label: capitalize(key),
|
||||
icon: React.createElement(IconComp, iconSize),
|
||||
}));
|
||||
}
|
||||
return Object.keys(relations).map((key) => {
|
||||
const IconComp = PERMISSION_ICON_MAP[key] ?? FALLBACK_PERMISSION_ICON;
|
||||
return {
|
||||
key,
|
||||
label: capitalize(key),
|
||||
icon: React.createElement(IconComp, iconSize),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function deriveResourcesForRelation(
|
||||
authzResources: AuthtypesGettableResourcesDTO | null,
|
||||
relation: string,
|
||||
): ResourceDefinition[] {
|
||||
if (!authzResources?.relations) {
|
||||
return [];
|
||||
}
|
||||
const supportedTypes = authzResources.relations[relation] ?? [];
|
||||
return authzResources.resources
|
||||
.filter((r) => supportedTypes.includes(r.type))
|
||||
.map((r) => ({
|
||||
id: r.name,
|
||||
label: capitalize(r.name).replace(/_/g, ' '),
|
||||
options: [],
|
||||
}));
|
||||
}
|
||||
|
||||
export function objectsToPermissionConfig(
|
||||
objects: AuthtypesGettableObjectsDTO[],
|
||||
resources: ResourceDefinition[],
|
||||
): PermissionConfig {
|
||||
const config: PermissionConfig = {};
|
||||
for (const res of resources) {
|
||||
const obj = objects.find((o) => o.resource.name === res.id);
|
||||
if (!obj) {
|
||||
config[res.id] = {
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [],
|
||||
};
|
||||
} else {
|
||||
const isAll = obj.selectors.includes('*');
|
||||
config[res.id] = {
|
||||
scope: isAll ? PermissionScope.ALL : PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: isAll ? [] : obj.selectors,
|
||||
};
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
export function buildPatchPayload({
|
||||
newConfig,
|
||||
initialConfig,
|
||||
resources,
|
||||
authzRes,
|
||||
}: PatchPayloadOptions): {
|
||||
additions: AuthtypesGettableObjectsDTO[] | null;
|
||||
deletions: AuthtypesGettableObjectsDTO[] | null;
|
||||
} {
|
||||
if (!authzRes) {
|
||||
return { additions: null, deletions: null };
|
||||
}
|
||||
const additions: AuthtypesGettableObjectsDTO[] = [];
|
||||
const deletions: AuthtypesGettableObjectsDTO[] = [];
|
||||
|
||||
for (const res of resources) {
|
||||
const initial = initialConfig[res.id];
|
||||
const current = newConfig[res.id];
|
||||
const resourceDef = authzRes.resources.find((r) => r.name === res.id);
|
||||
if (!resourceDef) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const initialScope = initial?.scope ?? PermissionScope.ONLY_SELECTED;
|
||||
const currentScope = current?.scope ?? PermissionScope.ONLY_SELECTED;
|
||||
|
||||
if (initialScope === currentScope) {
|
||||
// Same scope — only diff individual selectors when both are ONLY_SELECTED
|
||||
if (initialScope === PermissionScope.ONLY_SELECTED) {
|
||||
const initialIds = new Set(initial?.selectedIds ?? []);
|
||||
const currentIds = new Set(current?.selectedIds ?? []);
|
||||
const removed = [...initialIds].filter((id) => !currentIds.has(id));
|
||||
const added = [...currentIds].filter((id) => !initialIds.has(id));
|
||||
if (removed.length > 0) {
|
||||
deletions.push({ resource: resourceDef, selectors: removed });
|
||||
}
|
||||
if (added.length > 0) {
|
||||
additions.push({ resource: resourceDef, selectors: added });
|
||||
}
|
||||
}
|
||||
// Both ALL → no change, skip
|
||||
} else {
|
||||
// Scope changed (ALL ↔ ONLY_SELECTED) — replace old with new
|
||||
const initialSelectors =
|
||||
initialScope === PermissionScope.ALL ? ['*'] : initial?.selectedIds ?? [];
|
||||
if (initialSelectors.length > 0) {
|
||||
deletions.push({ resource: resourceDef, selectors: initialSelectors });
|
||||
}
|
||||
const currentSelectors =
|
||||
currentScope === PermissionScope.ALL ? ['*'] : current?.selectedIds ?? [];
|
||||
if (currentSelectors.length > 0) {
|
||||
additions.push({ resource: resourceDef, selectors: currentSelectors });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
additions: additions.length > 0 ? additions : null,
|
||||
deletions: deletions.length > 0 ? deletions : null,
|
||||
};
|
||||
}
|
||||
|
||||
interface TimestampBadgeProps {
|
||||
date?: Date | string;
|
||||
}
|
||||
|
||||
export function TimestampBadge({ date }: TimestampBadgeProps): JSX.Element {
|
||||
const { formatTimezoneAdjustedTimestamp } = useTimezone();
|
||||
|
||||
if (!date) {
|
||||
return <Badge color="vanilla">—</Badge>;
|
||||
}
|
||||
|
||||
const d = new Date(date);
|
||||
if (Number.isNaN(d.getTime())) {
|
||||
return <Badge color="vanilla">—</Badge>;
|
||||
}
|
||||
|
||||
const formatted = formatTimezoneAdjustedTimestamp(
|
||||
date,
|
||||
DATE_TIME_FORMATS.DASH_DATETIME,
|
||||
);
|
||||
|
||||
return <Badge color="vanilla">{formatted}</Badge>;
|
||||
}
|
||||
|
||||
export const DEFAULT_RESOURCE_CONFIG: ResourceConfig = {
|
||||
scope: PermissionScope.ONLY_SELECTED,
|
||||
selectedIds: [],
|
||||
};
|
||||
|
||||
export function buildConfig(
|
||||
resources: ResourceDefinition[],
|
||||
initial?: PermissionConfig,
|
||||
): PermissionConfig {
|
||||
const config: PermissionConfig = {};
|
||||
resources.forEach((r) => {
|
||||
config[r.id] = initial?.[r.id] ?? { ...DEFAULT_RESOURCE_CONFIG };
|
||||
});
|
||||
return config;
|
||||
}
|
||||
|
||||
export function isResourceConfigEqual(
|
||||
ac: ResourceConfig,
|
||||
bc?: ResourceConfig,
|
||||
): boolean {
|
||||
if (!bc) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
ac.scope === bc.scope &&
|
||||
JSON.stringify([...ac.selectedIds].sort()) ===
|
||||
JSON.stringify([...bc.selectedIds].sort())
|
||||
);
|
||||
}
|
||||
|
||||
export function configsEqual(
|
||||
a: PermissionConfig,
|
||||
b: PermissionConfig,
|
||||
): boolean {
|
||||
const keysA = Object.keys(a);
|
||||
const keysB = Object.keys(b);
|
||||
|
||||
if (keysA.length !== keysB.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return keysA.every((id) => isResourceConfigEqual(a[id], b[id]));
|
||||
}
|
||||
@@ -160,6 +160,7 @@ export const routesToSkip = [
|
||||
ROUTES.LOGS_PIPELINES,
|
||||
ROUTES.BILLING,
|
||||
ROUTES.ROLES_SETTINGS,
|
||||
ROUTES.ROLE_DETAILS,
|
||||
ROUTES.SUPPORT,
|
||||
ROUTES.WORKSPACE_LOCKED,
|
||||
ROUTES.WORKSPACE_SUSPENDED,
|
||||
|
||||
@@ -62,3 +62,6 @@ export const listRolesSuccessResponse = {
|
||||
status: 'success',
|
||||
data: allRoles,
|
||||
};
|
||||
|
||||
export const customRoleResponse = { status: 'success', data: customRoles[0] };
|
||||
export const managedRoleResponse = { status: 'success', data: managedRoles[0] };
|
||||
|
||||
@@ -109,6 +109,7 @@ function SettingsPage(): JSX.Element {
|
||||
isEnabled:
|
||||
item.key === ROUTES.BILLING ||
|
||||
item.key === ROUTES.ROLES_SETTINGS ||
|
||||
item.key === ROUTES.ROLE_DETAILS ||
|
||||
item.key === ROUTES.INTEGRATIONS ||
|
||||
item.key === ROUTES.API_KEYS ||
|
||||
item.key === ROUTES.ORG_SETTINGS ||
|
||||
@@ -137,7 +138,8 @@ function SettingsPage(): JSX.Element {
|
||||
isEnabled:
|
||||
item.key === ROUTES.API_KEYS ||
|
||||
item.key === ROUTES.ORG_SETTINGS ||
|
||||
item.key === ROUTES.ROLES_SETTINGS
|
||||
item.key === ROUTES.ROLES_SETTINGS ||
|
||||
item.key === ROUTES.ROLE_DETAILS
|
||||
? true
|
||||
: item.isEnabled,
|
||||
}));
|
||||
@@ -229,6 +231,13 @@ function SettingsPage(): JSX.Element {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
pathname.startsWith(ROUTES.ROLES_SETTINGS) &&
|
||||
key === ROUTES.ROLES_SETTINGS
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return pathname === key;
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import MultiIngestionSettings from 'container/IngestionSettings/MultiIngestionSe
|
||||
import MySettings from 'container/MySettings';
|
||||
import OrganizationSettings from 'container/OrganizationSettings';
|
||||
import RolesSettings from 'container/RolesSettings';
|
||||
import RoleDetailsPage from 'container/RolesSettings/RoleDetails';
|
||||
import { TFunction } from 'i18next';
|
||||
import {
|
||||
Backpack,
|
||||
@@ -163,6 +164,19 @@ export const rolesSettings = (t: TFunction): RouteTabProps['routes'] => [
|
||||
},
|
||||
];
|
||||
|
||||
export const roleDetails = (t: TFunction): RouteTabProps['routes'] => [
|
||||
{
|
||||
Component: RoleDetailsPage,
|
||||
name: (
|
||||
<div className="periscope-tab">
|
||||
<Shield size={16} /> {t('routes:role_details').toString()}
|
||||
</div>
|
||||
),
|
||||
route: ROUTES.ROLE_DETAILS,
|
||||
key: ROUTES.ROLE_DETAILS,
|
||||
},
|
||||
];
|
||||
|
||||
export const keyboardShortcuts = (t: TFunction): RouteTabProps['routes'] => [
|
||||
{
|
||||
Component: Shortcuts,
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
multiIngestionSettings,
|
||||
mySettings,
|
||||
organizationSettings,
|
||||
roleDetails,
|
||||
rolesSettings,
|
||||
} from './config';
|
||||
|
||||
@@ -68,7 +69,7 @@ export const getRoutes = (
|
||||
}
|
||||
|
||||
if (isAdmin) {
|
||||
settings.push(...rolesSettings(t));
|
||||
settings.push(...rolesSettings(t), ...roleDetails(t));
|
||||
}
|
||||
|
||||
settings.push(
|
||||
|
||||
@@ -245,6 +245,7 @@ export function getAppContextMock(
|
||||
ee: 'Y',
|
||||
setupCompleted: true,
|
||||
},
|
||||
|
||||
...appContextOverrides,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,3 +11,8 @@ export const USER_ROLES = {
|
||||
EDITOR: 'EDITOR',
|
||||
AUTHOR: 'AUTHOR',
|
||||
};
|
||||
|
||||
export enum RoleType {
|
||||
MANAGED = 'managed',
|
||||
CUSTOM = 'custom',
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp } from 'types/api';
|
||||
import { ErrorResponseHandlerForGeneratedAPIs } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import { RenderErrorResponseDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import { ErrorType } from 'api/generatedAPIInstance';
|
||||
import APIError from 'types/api/error';
|
||||
|
||||
/**
|
||||
@@ -35,11 +35,11 @@ export const isRetryableError = (error: any): boolean => {
|
||||
};
|
||||
|
||||
export function toAPIError(
|
||||
error: unknown,
|
||||
error: ErrorType<RenderErrorResponseDTO>,
|
||||
defaultMessage = 'An unexpected error occurred.',
|
||||
): APIError {
|
||||
try {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
ErrorResponseHandlerForGeneratedAPIs(error);
|
||||
} catch (apiError) {
|
||||
if (apiError instanceof APIError) {
|
||||
return apiError;
|
||||
@@ -55,3 +55,14 @@ export function toAPIError(
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function handleApiError(
|
||||
err: ErrorType<RenderErrorResponseDTO>,
|
||||
showErrorFunction: (error: APIError) => void,
|
||||
): void {
|
||||
try {
|
||||
ErrorResponseHandlerForGeneratedAPIs(err);
|
||||
} catch (apiError) {
|
||||
showErrorFunction(apiError as APIError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ export const routePermission: Record<keyof typeof ROUTES, ROLES[]> = {
|
||||
WORKSPACE_LOCKED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
WORKSPACE_SUSPENDED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
ROLES_SETTINGS: ['ADMIN'],
|
||||
ROLE_DETAILS: ['ADMIN'],
|
||||
BILLING: ['ADMIN'],
|
||||
SUPPORT: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
SOMETHING_WENT_WRONG: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
|
||||
@@ -4804,6 +4804,28 @@
|
||||
"@radix-ui/react-roving-focus" "1.0.4"
|
||||
"@radix-ui/react-use-controllable-state" "1.0.1"
|
||||
|
||||
"@radix-ui/react-toggle-group@^1.1.7":
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.11.tgz#e513d6ffdb07509b400ab5b26f2523747c0d51c1"
|
||||
integrity sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==
|
||||
dependencies:
|
||||
"@radix-ui/primitive" "1.1.3"
|
||||
"@radix-ui/react-context" "1.1.2"
|
||||
"@radix-ui/react-direction" "1.1.1"
|
||||
"@radix-ui/react-primitive" "2.1.3"
|
||||
"@radix-ui/react-roving-focus" "1.1.11"
|
||||
"@radix-ui/react-toggle" "1.1.10"
|
||||
"@radix-ui/react-use-controllable-state" "1.2.2"
|
||||
|
||||
"@radix-ui/react-toggle@1.1.10", "@radix-ui/react-toggle@^1.1.6":
|
||||
version "1.1.10"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz#b04ba0f9609599df666fce5b2f38109a197f08cf"
|
||||
integrity sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==
|
||||
dependencies:
|
||||
"@radix-ui/primitive" "1.1.3"
|
||||
"@radix-ui/react-primitive" "2.1.3"
|
||||
"@radix-ui/react-use-controllable-state" "1.2.2"
|
||||
|
||||
"@radix-ui/react-tooltip@1.0.7":
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz#8f55070f852e7e7450cc1d9210b793d2e5a7686e"
|
||||
@@ -5595,6 +5617,21 @@
|
||||
tailwind-merge "^2.5.2"
|
||||
tailwindcss-animate "^1.0.7"
|
||||
|
||||
"@signozhq/toggle-group@0.0.1":
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@signozhq/toggle-group/-/toggle-group-0.0.1.tgz#c82ff1da34e77b24da53c2d595ad6b4a0d1b1de4"
|
||||
integrity sha512-871bQayL5MaqsuNOFHKexidu9W2Hlg1y4xmH8C5mGmlfZ4bd0ovJ9OweQrM6Puys3jeMwi69xmJuesYCfKQc1g==
|
||||
dependencies:
|
||||
"@radix-ui/react-icons" "^1.3.0"
|
||||
"@radix-ui/react-slot" "^1.1.0"
|
||||
"@radix-ui/react-toggle" "^1.1.6"
|
||||
"@radix-ui/react-toggle-group" "^1.1.7"
|
||||
class-variance-authority "^0.7.0"
|
||||
clsx "^2.1.1"
|
||||
lucide-react "^0.445.0"
|
||||
tailwind-merge "^2.5.2"
|
||||
tailwindcss-animate "^1.0.7"
|
||||
|
||||
"@signozhq/tooltip@0.0.2":
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@signozhq/tooltip/-/tooltip-0.0.2.tgz#bb4e2681868fa2e06db78eff5872ffb2a78b93b6"
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package instrumentation
|
||||
|
||||
import "time"
|
||||
|
||||
// DurationBucket returns a human-readable bucket label for the duration between fromMS and toMS.
|
||||
// fromMS and toMS are Unix timestamps (same unit as used by time.Unix).
|
||||
// Returns labels like "<1h", "<6h", "<24h", "<3D", "<1W", "<2W", "<1M", or ">=1M".
|
||||
func DurationBucket(fromMS, toMS uint64) string {
|
||||
diff := time.Unix(0, int64(toMS)).Sub(time.Unix(0, int64(fromMS)))
|
||||
|
||||
buckets := []struct {
|
||||
d time.Duration
|
||||
l string
|
||||
}{
|
||||
{1 * time.Hour, "<1h"},
|
||||
{6 * time.Hour, "<6h"},
|
||||
{24 * time.Hour, "<24h"},
|
||||
{3 * 24 * time.Hour, "<3D"},
|
||||
{7 * 24 * time.Hour, "<1W"},
|
||||
{14 * 24 * time.Hour, "<2W"},
|
||||
{30 * 24 * time.Hour, "<1M"},
|
||||
}
|
||||
|
||||
for _, b := range buckets {
|
||||
if diff < b.d {
|
||||
return b.l
|
||||
}
|
||||
}
|
||||
|
||||
return ">=1M"
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package instrumentation
|
||||
|
||||
import semconv "go.opentelemetry.io/collector/semconv/v1.6.1"
|
||||
|
||||
// Log comment / context keys for query observability.
|
||||
// Names align with OpenTelemetry semantic conventions where applicable
|
||||
// (https://pkg.go.dev/go.opentelemetry.io/otel/semconv); custom keys are namespaced.
|
||||
const (
|
||||
// CodeFunctionName is the fully-qualified function or method name (OTel code.function.name).
|
||||
CodeFunctionName = semconv.AttributeCodeFunction
|
||||
// CodeNamespace is the logical module or component name (e.g. "dashboard", "anomaly").
|
||||
CodeNamespace = semconv.AttributeCodeNamespace
|
||||
// TelemetrySignal is the telemetry signal type: "traces", "logs", or "metrics".
|
||||
TelemetrySignal = "telemetry.signal"
|
||||
// QueryDuration is the query time-range bucket label (e.g. "<1h", "<24h").
|
||||
QueryDuration = "query.duration"
|
||||
// PanelType is the panel type: "timeseries", "list", "value"
|
||||
PanelType = "panel.type"
|
||||
// QueryType is the query type: "promql", "clickhouse_sql", "builder_query".
|
||||
QueryType = "query.type"
|
||||
)
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/cache"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
||||
"github.com/SigNoz/signoz/pkg/modules/metricsexplorer"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
@@ -60,8 +59,6 @@ func NewModule(ts telemetrystore.TelemetryStore, telemetryMetadataStore telemetr
|
||||
|
||||
// TODO(srikanthccv): use metadata store to fetch metric metadata
|
||||
func (m *module) ListMetrics(ctx context.Context, orgID valuer.UUID, params *metricsexplorertypes.ListMetricsParams) (*metricsexplorertypes.ListMetricsResponse, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "ListMetrics")
|
||||
|
||||
if err := params.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -291,7 +288,6 @@ func (m *module) GetTreemap(ctx context.Context, orgID valuer.UUID, req *metrics
|
||||
}
|
||||
|
||||
func (m *module) GetMetricMetadataMulti(ctx context.Context, orgID valuer.UUID, metricNames []string) (map[string]*metricsexplorertypes.MetricMetadata, error) {
|
||||
|
||||
if len(metricNames) == 0 {
|
||||
return map[string]*metricsexplorertypes.MetricMetadata{}, nil
|
||||
}
|
||||
@@ -480,8 +476,6 @@ func (m *module) GetMetricAttributes(ctx context.Context, orgID valuer.UUID, req
|
||||
}
|
||||
|
||||
func (m *module) CheckMetricExists(ctx context.Context, orgID valuer.UUID, metricName string) (bool, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "CheckMetricExists")
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("count(*) > 0 as metricExists")
|
||||
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.AttributesMetadataTableName))
|
||||
@@ -518,8 +512,6 @@ func (m *module) fetchMetadataFromCache(ctx context.Context, orgID valuer.UUID,
|
||||
}
|
||||
|
||||
func (m *module) fetchUpdatedMetadata(ctx context.Context, orgID valuer.UUID, metricNames []string) (map[string]*metricsexplorertypes.MetricMetadata, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "fetchUpdatedMetadata")
|
||||
|
||||
if len(metricNames) == 0 {
|
||||
return map[string]*metricsexplorertypes.MetricMetadata{}, nil
|
||||
}
|
||||
@@ -578,8 +570,6 @@ func (m *module) fetchUpdatedMetadata(ctx context.Context, orgID valuer.UUID, me
|
||||
}
|
||||
|
||||
func (m *module) fetchTimeseriesMetadata(ctx context.Context, orgID valuer.UUID, metricNames []string) (map[string]*metricsexplorertypes.MetricMetadata, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "fetchTimeseriesMetadata")
|
||||
|
||||
if len(metricNames) == 0 {
|
||||
return map[string]*metricsexplorertypes.MetricMetadata{}, nil
|
||||
}
|
||||
@@ -708,8 +698,6 @@ func (m *module) validateMetricLabels(ctx context.Context, req *metricsexplorert
|
||||
}
|
||||
|
||||
func (m *module) checkForLabelInMetric(ctx context.Context, metricName string, label string) (bool, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "checkForLabelInMetric")
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("count(*) > 0 AS has_label")
|
||||
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.AttributesMetadataTableName))
|
||||
@@ -731,7 +719,6 @@ func (m *module) checkForLabelInMetric(ctx context.Context, metricName string, l
|
||||
}
|
||||
|
||||
func (m *module) insertMetricsMetadata(ctx context.Context, orgID valuer.UUID, req *metricsexplorertypes.UpdateMetricMetadataRequest) error {
|
||||
ctx = withMetricsExplorerQuery(ctx, "insertMetricsMetadata")
|
||||
createdAt := time.Now().UnixMilli()
|
||||
|
||||
ib := sqlbuilder.NewInsertBuilder()
|
||||
@@ -825,7 +812,6 @@ func (m *module) fetchMetricsStatsWithSamples(
|
||||
normalized bool,
|
||||
orderBy *qbtypes.OrderBy,
|
||||
) ([]metricsexplorertypes.Stat, uint64, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "fetchMetricsStatsWithSamples")
|
||||
|
||||
start, end, distributedTsTable, localTsTable := telemetrymetrics.WhichTSTableToUse(uint64(req.Start), uint64(req.End), nil)
|
||||
samplesTable := telemetrymetrics.WhichSamplesTableToUse(uint64(req.Start), uint64(req.End), metrictypes.UnspecifiedType, metrictypes.TimeAggregationUnspecified, nil)
|
||||
@@ -933,8 +919,6 @@ func (m *module) fetchMetricsStatsWithSamples(
|
||||
}
|
||||
|
||||
func (m *module) computeTimeseriesTreemap(ctx context.Context, req *metricsexplorertypes.TreemapRequest, filterWhereClause *sqlbuilder.WhereClause) ([]metricsexplorertypes.TreemapEntry, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "computeTimeseriesTreemap")
|
||||
|
||||
start, end, distributedTsTable, _ := telemetrymetrics.WhichTSTableToUse(uint64(req.Start), uint64(req.End), nil)
|
||||
|
||||
totalTSBuilder := sqlbuilder.NewSelectBuilder()
|
||||
@@ -999,8 +983,6 @@ func (m *module) computeTimeseriesTreemap(ctx context.Context, req *metricsexplo
|
||||
}
|
||||
|
||||
func (m *module) computeSamplesTreemap(ctx context.Context, req *metricsexplorertypes.TreemapRequest, filterWhereClause *sqlbuilder.WhereClause) ([]metricsexplorertypes.TreemapEntry, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "computeSamplesTreemap")
|
||||
|
||||
start, end, distributedTsTable, localTsTable := telemetrymetrics.WhichTSTableToUse(uint64(req.Start), uint64(req.End), nil)
|
||||
samplesTable := telemetrymetrics.WhichSamplesTableToUse(uint64(req.Start), uint64(req.End), metrictypes.UnspecifiedType, metrictypes.TimeAggregationUnspecified, nil)
|
||||
countExp := telemetrymetrics.CountExpressionForSamplesTable(samplesTable)
|
||||
@@ -1102,8 +1084,6 @@ func (m *module) computeSamplesTreemap(ctx context.Context, req *metricsexplorer
|
||||
|
||||
// getMetricDataPoints returns the total number of data points (samples) for a metric.
|
||||
func (m *module) getMetricDataPoints(ctx context.Context, metricName string) (uint64, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "getMetricDataPoints")
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("sum(count) AS data_points")
|
||||
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.SamplesV4Agg30mTableName))
|
||||
@@ -1124,8 +1104,6 @@ func (m *module) getMetricDataPoints(ctx context.Context, metricName string) (ui
|
||||
|
||||
// getMetricLastReceived returns the last received timestamp for a metric.
|
||||
func (m *module) getMetricLastReceived(ctx context.Context, metricName string) (uint64, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "getMetricLastReceived")
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("MAX(last_reported_unix_milli) AS last_received_time")
|
||||
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.AttributesMetadataTableName))
|
||||
@@ -1149,8 +1127,6 @@ func (m *module) getMetricLastReceived(ctx context.Context, metricName string) (
|
||||
|
||||
// getTotalTimeSeriesForMetricName returns the total number of unique time series for a metric.
|
||||
func (m *module) getTotalTimeSeriesForMetricName(ctx context.Context, metricName string) (uint64, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "getTotalTimeSeriesForMetricName")
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("uniq(fingerprint) AS time_series_count")
|
||||
sb.From(fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.TimeseriesV41weekTableName))
|
||||
@@ -1171,8 +1147,6 @@ func (m *module) getTotalTimeSeriesForMetricName(ctx context.Context, metricName
|
||||
|
||||
// getActiveTimeSeriesForMetricName returns the number of active time series for a metric within the given duration.
|
||||
func (m *module) getActiveTimeSeriesForMetricName(ctx context.Context, metricName string, duration time.Duration) (uint64, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "getActiveTimeSeriesForMetricName")
|
||||
|
||||
milli := time.Now().Add(-duration).UnixMilli()
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
@@ -1194,8 +1168,6 @@ func (m *module) getActiveTimeSeriesForMetricName(ctx context.Context, metricNam
|
||||
}
|
||||
|
||||
func (m *module) fetchMetricAttributes(ctx context.Context, metricName string, start, end *int64) ([]metricsexplorertypes.MetricAttribute, error) {
|
||||
ctx = withMetricsExplorerQuery(ctx, "fetchMetricAttributes")
|
||||
|
||||
// Build query using sqlbuilder
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select(
|
||||
@@ -1244,12 +1216,3 @@ func (m *module) fetchMetricAttributes(ctx context.Context, metricName string, s
|
||||
|
||||
return attributes, nil
|
||||
}
|
||||
|
||||
func withMetricsExplorerQuery(ctx context.Context, functionName string) context.Context {
|
||||
comments := map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "metrics-explorer",
|
||||
instrumentation.CodeFunctionName: functionName,
|
||||
}
|
||||
return ctxtypes.AddCommentsToContext(ctx, comments)
|
||||
}
|
||||
|
||||
@@ -8,11 +8,9 @@ import (
|
||||
|
||||
schemamigrator "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/modules/promote"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrylogs"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/promotetypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
@@ -107,11 +105,6 @@ func (m *module) PromotePaths(ctx context.Context, paths []string) error {
|
||||
|
||||
// createIndexes creates string ngram + token filter indexes on JSON path subcolumns for LIKE queries.
|
||||
func (m *module) createIndexes(ctx context.Context, indexes []schemamigrator.Index) error {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "promote",
|
||||
instrumentation.CodeFunctionName: "createIndexes",
|
||||
})
|
||||
if len(indexes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rawdataexport"
|
||||
"github.com/SigNoz/signoz/pkg/querier"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
@@ -23,10 +22,6 @@ func NewModule(querier querier.Querier) rawdataexport.Module {
|
||||
}
|
||||
|
||||
func (m *Module) ExportRawData(ctx context.Context, orgID valuer.UUID, rangeRequest *qbtypes.QueryRangeRequest, doneChan chan any) (chan *qbtypes.RawRow, chan error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "rawdataexport",
|
||||
instrumentation.CodeFunctionName: "ExportRawData",
|
||||
})
|
||||
|
||||
spec := rangeRequest.CompositeQuery.Queries[0].Spec.(qbtypes.QueryBuilderQuery[qbtypes.LogAggregation])
|
||||
rowCountLimit := spec.Limit
|
||||
|
||||
@@ -9,12 +9,10 @@ import (
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/modules/services"
|
||||
"github.com/SigNoz/signoz/pkg/querier"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrytraces"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/servicetypes/servicetypesv1"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
@@ -36,12 +34,6 @@ func NewModule(q querier.Querier, ts telemetrystore.TelemetryStore) services.Mod
|
||||
|
||||
// FetchTopLevelOperations returns top-level operations per service using db query
|
||||
func (m *module) FetchTopLevelOperations(ctx context.Context, start time.Time, services []string) (map[string][]string, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "services",
|
||||
instrumentation.CodeFunctionName: "FetchTopLevelOperations",
|
||||
})
|
||||
|
||||
db := m.TelemetryStore.ClickhouseDB()
|
||||
query := fmt.Sprintf("SELECT name, serviceName, max(time) as ts FROM %s.%s WHERE time >= @start", telemetrytraces.DBName, telemetrytraces.TopLevelOperationsTableName)
|
||||
args := []any{clickhouse.Named("start", start)}
|
||||
@@ -78,10 +70,6 @@ func (m *module) FetchTopLevelOperations(ctx context.Context, start time.Time, s
|
||||
// Get implements services.Module
|
||||
// Builds a QBv5 traces aggregation grouped by service.name and maps results to ResponseItem.
|
||||
func (m *module) Get(ctx context.Context, orgUUID valuer.UUID, req *servicetypesv1.Request) ([]*servicetypesv1.ResponseItem, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "services",
|
||||
instrumentation.CodeFunctionName: "Get",
|
||||
})
|
||||
if req == nil {
|
||||
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "request is nil")
|
||||
}
|
||||
@@ -116,10 +104,6 @@ func (m *module) Get(ctx context.Context, orgUUID valuer.UUID, req *servicetypes
|
||||
|
||||
// GetTopOperations implements services.Module for QBV5 based top ops
|
||||
func (m *module) GetTopOperations(ctx context.Context, orgUUID valuer.UUID, req *servicetypesv1.OperationsRequest) ([]servicetypesv1.OperationItem, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "services",
|
||||
instrumentation.CodeFunctionName: "GetTopOperations",
|
||||
})
|
||||
if req == nil {
|
||||
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "request is nil")
|
||||
}
|
||||
@@ -140,10 +124,6 @@ func (m *module) GetTopOperations(ctx context.Context, orgUUID valuer.UUID, req
|
||||
|
||||
// GetEntryPointOperations implements services.Module for QBV5 based entry point ops
|
||||
func (m *module) GetEntryPointOperations(ctx context.Context, orgUUID valuer.UUID, req *servicetypesv1.OperationsRequest) ([]servicetypesv1.OperationItem, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "services",
|
||||
instrumentation.CodeFunctionName: "GetEntryPointOperations",
|
||||
})
|
||||
if req == nil {
|
||||
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "request is nil")
|
||||
}
|
||||
|
||||
@@ -6,10 +6,8 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/modules/spanpercentile"
|
||||
"github.com/SigNoz/signoz/pkg/querier"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/spanpercentiletypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
@@ -29,10 +27,6 @@ func NewModule(
|
||||
}
|
||||
|
||||
func (m *module) GetSpanPercentile(ctx context.Context, orgID valuer.UUID, userID valuer.UUID, req *spanpercentiletypes.SpanPercentileRequest) (*spanpercentiletypes.SpanPercentileResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "spanpercentile",
|
||||
instrumentation.CodeFunctionName: "GetSpanPercentile",
|
||||
})
|
||||
queryRangeRequest, err := buildSpanPercentileQuery(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -9,11 +9,8 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
promValue "github.com/prometheus/prometheus/model/value"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
@@ -140,11 +137,6 @@ func (client *client) queryToClickhouseQuery(_ context.Context, query *prompb.Qu
|
||||
}
|
||||
|
||||
func (client *client) getFingerprintsFromClickhouseQuery(ctx context.Context, query string, args []any) (map[uint64][]prompb.Label, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-prometheus",
|
||||
"function_name": "getFingerprintsFromClickhouseQuery",
|
||||
})
|
||||
rows, err := client.telemetryStore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -176,11 +168,6 @@ func (client *client) getFingerprintsFromClickhouseQuery(ctx context.Context, qu
|
||||
}
|
||||
|
||||
func (client *client) querySamples(ctx context.Context, start int64, end int64, fingerprints map[uint64][]prompb.Label, metricName string, subQuery string, args []any) ([]*prompb.TimeSeries, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-prometheus",
|
||||
instrumentation.CodeFunctionName: "querySamples",
|
||||
})
|
||||
argCount := len(args)
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
@@ -257,12 +244,6 @@ func (client *client) querySamples(ctx context.Context, start int64, end int64,
|
||||
}
|
||||
|
||||
func (client *client) queryRaw(ctx context.Context, query string, ts int64) (*prompb.QueryResult, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": telemetrytypes.SignalMetrics.StringValue(),
|
||||
"module_name": "clickhouse-prometheus",
|
||||
"function_name": "queryRaw",
|
||||
})
|
||||
|
||||
rows, err := client.telemetryStore.ClickhouseDB().Query(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/http/render"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
@@ -34,10 +33,6 @@ func NewHandler(set factory.ProviderSettings, querier Querier, analytics analyti
|
||||
|
||||
func (handler *handler) QueryRange(rw http.ResponseWriter, req *http.Request) {
|
||||
ctx := req.Context()
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "querier",
|
||||
instrumentation.CodeFunctionName: "QueryRange",
|
||||
})
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -10,10 +10,8 @@ import (
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrylogs"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/bytedance/sonic"
|
||||
@@ -214,11 +212,6 @@ func (q *builderQuery[T]) Execute(ctx context.Context) (*qbtypes.Result, error)
|
||||
|
||||
// executeWithContext executes the query with query window and step context for partial value detection
|
||||
func (q *builderQuery[T]) executeWithContext(ctx context.Context, query string, args []any) (*qbtypes.Result, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"signal": q.spec.Signal.StringValue(),
|
||||
instrumentation.QueryDuration: instrumentation.DurationBucket(q.fromMS, q.toMS),
|
||||
})
|
||||
|
||||
totalRows := uint64(0)
|
||||
totalBytes := uint64(0)
|
||||
elapsed := time.Duration(0)
|
||||
|
||||
@@ -12,10 +12,8 @@ import (
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
)
|
||||
|
||||
@@ -100,10 +98,6 @@ func (q *chSQLQuery) renderVars(query string, vars map[string]qbtypes.VariableIt
|
||||
}
|
||||
|
||||
func (q *chSQLQuery) Execute(ctx context.Context) (*qbtypes.Result, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
"module_name": "clickhouse-query",
|
||||
instrumentation.QueryDuration: instrumentation.DurationBucket(q.fromMS, q.toMS),
|
||||
})
|
||||
|
||||
totalRows := uint64(0)
|
||||
totalBytes := uint64(0)
|
||||
|
||||
@@ -12,10 +12,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbv5 "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
@@ -189,11 +187,6 @@ func (q *promqlQuery) renderVars(query string, vars map[string]qbv5.VariableItem
|
||||
|
||||
func (q *promqlQuery) Execute(ctx context.Context) (*qbv5.Result, error) {
|
||||
|
||||
comment := ctxtypes.CommentFromContext(ctx)
|
||||
comment.Set("signal", telemetrytypes.SignalMetrics.StringValue())
|
||||
comment.Set("duration", instrumentation.DurationBucket(q.tr.From, q.tr.To))
|
||||
ctx = ctxtypes.NewContextWithComment(ctx, comment)
|
||||
|
||||
start := int64(querybuilder.ToNanoSecs(q.tr.From))
|
||||
end := int64(querybuilder.ToNanoSecs(q.tr.To))
|
||||
|
||||
|
||||
@@ -12,12 +12,10 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/utils"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/metrictypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"golang.org/x/exp/maps"
|
||||
@@ -528,11 +526,6 @@ func (q *querier) run(
|
||||
steps map[string]qbtypes.Step,
|
||||
qbEvent *qbtypes.QBEvent,
|
||||
) (*qbtypes.QueryRangeResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.PanelType: qbEvent.PanelType,
|
||||
instrumentation.QueryType: qbEvent.QueryType,
|
||||
})
|
||||
|
||||
results := make(map[string]any)
|
||||
warnings := make([]string, 0)
|
||||
warningsDocURL := ""
|
||||
|
||||
@@ -5,11 +5,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
type traceOperatorQuery struct {
|
||||
@@ -55,11 +52,6 @@ func (q *traceOperatorQuery) Execute(ctx context.Context) (*qbtypes.Result, erro
|
||||
}
|
||||
|
||||
func (q *traceOperatorQuery) executeWithContext(ctx context.Context, query string, args []any) (*qbtypes.Result, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.QueryDuration: instrumentation.DurationBucket(q.fromMS, q.toMS),
|
||||
})
|
||||
|
||||
totalRows := uint64(0)
|
||||
totalBytes := uint64(0)
|
||||
elapsed := time.Duration(0)
|
||||
|
||||
@@ -15,14 +15,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model/metrics_explorer"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/uptrace/bun"
|
||||
|
||||
@@ -272,12 +269,6 @@ func (r *ClickHouseReader) GetQueryRangeResult(ctx context.Context, query *model
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetServicesList(ctx context.Context) (*[]string, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetServicesList",
|
||||
})
|
||||
|
||||
services := []string{}
|
||||
rows, err := r.db.Query(ctx, fmt.Sprintf(`SELECT DISTINCT resource_string_service$$name FROM %s.%s WHERE ts_bucket_start > (toUnixTimestamp(now() - INTERVAL 1 DAY) - 1800) AND toDate(timestamp) > now() - INTERVAL 1 DAY`, r.TraceDB, r.traceTableName))
|
||||
if err != nil {
|
||||
@@ -297,12 +288,6 @@ func (r *ClickHouseReader) GetServicesList(ctx context.Context) (*[]string, erro
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetTopLevelOperations(ctx context.Context, start, end time.Time, services []string) (*map[string][]string, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetTopLevelOperations",
|
||||
})
|
||||
|
||||
start = start.In(time.UTC)
|
||||
|
||||
// The `top_level_operations` that have `time` >= start
|
||||
@@ -398,12 +383,6 @@ func (r *ClickHouseReader) buildResourceSubQuery(tags []model.TagQueryParam, svc
|
||||
|
||||
func (r *ClickHouseReader) GetServices(ctx context.Context, queryParams *model.GetServicesParams) (*[]model.ServiceItem, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetServices",
|
||||
})
|
||||
|
||||
if r.indexTable == "" {
|
||||
return nil, &model.ApiError{Typ: model.ErrorExec, Err: ErrNoIndexTable}
|
||||
}
|
||||
@@ -760,11 +739,6 @@ func (r *ClickHouseReader) GetEntryPointOperations(ctx context.Context, queryPar
|
||||
|
||||
func (r *ClickHouseReader) GetTopOperations(ctx context.Context, queryParams *model.GetTopOperationsParams) (*[]model.TopOperationsItem, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetTopOperations",
|
||||
})
|
||||
namedArgs := []interface{}{
|
||||
clickhouse.Named("start", strconv.FormatInt(queryParams.Start.UnixNano(), 10)),
|
||||
clickhouse.Named("end", strconv.FormatInt(queryParams.End.UnixNano(), 10)),
|
||||
@@ -820,11 +794,6 @@ func (r *ClickHouseReader) GetTopOperations(ctx context.Context, queryParams *mo
|
||||
|
||||
func (r *ClickHouseReader) GetUsage(ctx context.Context, queryParams *model.GetUsageParams) (*[]model.UsageItem, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetUsage",
|
||||
})
|
||||
var usageItems []model.UsageItem
|
||||
namedArgs := []interface{}{
|
||||
clickhouse.Named("interval", queryParams.StepHour),
|
||||
@@ -860,13 +829,6 @@ func (r *ClickHouseReader) GetUsage(ctx context.Context, queryParams *model.GetU
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetSpansForTrace(ctx context.Context, traceID string, traceDetailsQuery string) ([]model.SpanItemV2, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetSpansForTrace",
|
||||
})
|
||||
|
||||
var traceSummary model.TraceSummary
|
||||
summaryQuery := fmt.Sprintf("SELECT trace_id, min(start) AS start, max(end) AS end, sum(num_spans) AS num_spans FROM %s.%s WHERE trace_id=$1 GROUP BY trace_id", r.TraceDB, r.traceSummaryTable)
|
||||
err := r.db.QueryRow(ctx, summaryQuery, traceID).Scan(&traceSummary.TraceID, &traceSummary.Start, &traceSummary.End, &traceSummary.NumSpans)
|
||||
@@ -1265,11 +1227,6 @@ func (r *ClickHouseReader) GetFlamegraphSpansForTrace(ctx context.Context, orgID
|
||||
|
||||
func (r *ClickHouseReader) GetDependencyGraph(ctx context.Context, queryParams *model.GetServicesParams) (*[]model.ServiceMapDependencyResponseItem, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetDependencyGraph",
|
||||
})
|
||||
response := []model.ServiceMapDependencyResponseItem{}
|
||||
|
||||
args := []interface{}{}
|
||||
@@ -1324,11 +1281,6 @@ func getLocalTableName(tableName string) string {
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) setTTLLogs(ctx context.Context, orgID string, params *model.TTLParams) (*model.SetTTLResponseItem, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "setTTLLogs",
|
||||
})
|
||||
hasCustomRetention, err := r.hasCustomRetentionColumn(ctx)
|
||||
if hasCustomRetention {
|
||||
return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("SetTTLV2 only supported")}
|
||||
@@ -1492,11 +1444,6 @@ func (r *ClickHouseReader) setTTLLogs(ctx context.Context, orgID string, params
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) setTTLTraces(ctx context.Context, orgID string, params *model.TTLParams) (*model.SetTTLResponseItem, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "setTTLTraces",
|
||||
})
|
||||
// uuid is used as transaction id
|
||||
uuidWithHyphen := uuid.New()
|
||||
uuid := strings.Replace(uuidWithHyphen.String(), "-", "", -1)
|
||||
@@ -1642,12 +1589,6 @@ func (r *ClickHouseReader) setTTLTraces(ctx context.Context, orgID string, param
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) hasCustomRetentionColumn(ctx context.Context) (bool, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "hasCustomRetentionColumn",
|
||||
})
|
||||
|
||||
// Directly query for the _retention_days column existence
|
||||
query := fmt.Sprintf("SELECT 1 FROM system.columns WHERE database = '%s' AND table = '%s' AND name = '_retention_days' LIMIT 1", r.logsDB, r.logsLocalTableV2)
|
||||
|
||||
@@ -1669,11 +1610,6 @@ func (r *ClickHouseReader) hasCustomRetentionColumn(ctx context.Context) (bool,
|
||||
|
||||
func (r *ClickHouseReader) SetTTLV2(ctx context.Context, orgID string, params *model.CustomRetentionTTLParams) (*model.CustomRetentionTTLResponse, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "SetTTLV2",
|
||||
})
|
||||
hasCustomRetention, err := r.hasCustomRetentionColumn(ctx)
|
||||
if err != nil {
|
||||
return nil, errorsV2.Wrapf(err, errorsV2.TypeInternal, errorsV2.CodeInternal, "custom retention not supported")
|
||||
@@ -2063,10 +1999,6 @@ func (r *ClickHouseReader) updateCustomRetentionTTLStatus(ctx context.Context, o
|
||||
|
||||
// Enhanced validation function with duplicate detection and efficient key validation
|
||||
func (r *ClickHouseReader) validateTTLConditions(ctx context.Context, ttlConditions []model.CustomRetentionRule) error {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "validateTTLConditions",
|
||||
})
|
||||
if len(ttlConditions) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -2184,11 +2116,6 @@ func (r *ClickHouseReader) SetTTL(ctx context.Context, orgID string, params *mod
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) setTTLMetrics(ctx context.Context, orgID string, params *model.TTLParams) (*model.SetTTLResponseItem, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "setTTLMetrics",
|
||||
})
|
||||
// uuid is used as transaction id
|
||||
uuidWithHyphen := uuid.New()
|
||||
uuid := strings.Replace(uuidWithHyphen.String(), "-", "", -1)
|
||||
@@ -2397,10 +2324,6 @@ func (r *ClickHouseReader) getTTLQueryStatus(ctx context.Context, orgID string,
|
||||
|
||||
func (r *ClickHouseReader) setColdStorage(ctx context.Context, tableName string, coldStorageVolume string) *model.ApiError {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "setColdStorage",
|
||||
})
|
||||
// Set the storage policy for the required table. If it is already set, then setting it again
|
||||
// will not a problem.
|
||||
if len(coldStorageVolume) > 0 {
|
||||
@@ -2417,10 +2340,6 @@ func (r *ClickHouseReader) setColdStorage(ctx context.Context, tableName string,
|
||||
|
||||
// GetDisks returns a list of disks {name, type} configured in clickhouse DB.
|
||||
func (r *ClickHouseReader) GetDisks(ctx context.Context) (*[]model.DiskItem, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetDisks",
|
||||
})
|
||||
diskItems := []model.DiskItem{}
|
||||
|
||||
query := "SELECT name,type FROM system.disks"
|
||||
@@ -2444,10 +2363,6 @@ func getLocalTableNameArray(tableNames []string) []string {
|
||||
// GetTTL returns current ttl, expected ttl and past setTTL status for metrics/traces.
|
||||
func (r *ClickHouseReader) GetTTL(ctx context.Context, orgID string, ttlParams *model.GetTTLParams) (*model.GetTTLResponseItem, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetTTL",
|
||||
})
|
||||
parseTTL := func(queryResp string) (int, int) {
|
||||
|
||||
zap.L().Info("Parsing TTL from: ", zap.String("queryResp", queryResp))
|
||||
@@ -2617,11 +2532,6 @@ func (r *ClickHouseReader) GetTTL(ctx context.Context, orgID string, ttlParams *
|
||||
|
||||
func (r *ClickHouseReader) ListErrors(ctx context.Context, queryParams *model.ListErrorsParams) (*[]model.Error, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "ListErrors",
|
||||
})
|
||||
var getErrorResponses []model.Error
|
||||
|
||||
query := "SELECT any(exceptionMessage) as exceptionMessage, count() AS exceptionCount, min(timestamp) as firstSeen, max(timestamp) as lastSeen, groupID"
|
||||
@@ -2694,12 +2604,6 @@ func (r *ClickHouseReader) ListErrors(ctx context.Context, queryParams *model.Li
|
||||
|
||||
func (r *ClickHouseReader) CountErrors(ctx context.Context, queryParams *model.CountErrorsParams) (uint64, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "CountErrors",
|
||||
})
|
||||
|
||||
var errorCount uint64
|
||||
|
||||
query := fmt.Sprintf("SELECT count(distinct(groupID)) FROM %s.%s WHERE timestamp >= @timestampL AND timestamp <= @timestampU", r.TraceDB, r.errorTable)
|
||||
@@ -2737,11 +2641,6 @@ func (r *ClickHouseReader) CountErrors(ctx context.Context, queryParams *model.C
|
||||
|
||||
func (r *ClickHouseReader) GetErrorFromErrorID(ctx context.Context, queryParams *model.GetErrorParams) (*model.ErrorWithSpan, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetErrorFromErrorID",
|
||||
})
|
||||
if queryParams.ErrorID == "" {
|
||||
zap.L().Error("errorId missing from params")
|
||||
return nil, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("ErrorID missing from params")}
|
||||
@@ -2769,11 +2668,6 @@ func (r *ClickHouseReader) GetErrorFromErrorID(ctx context.Context, queryParams
|
||||
|
||||
func (r *ClickHouseReader) GetErrorFromGroupID(ctx context.Context, queryParams *model.GetErrorParams) (*model.ErrorWithSpan, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetErrorFromGroupID",
|
||||
})
|
||||
var getErrorWithSpanReponse []model.ErrorWithSpan
|
||||
|
||||
query := fmt.Sprintf("SELECT errorID, exceptionType, exceptionStacktrace, exceptionEscaped, exceptionMessage, timestamp, spanID, traceID, serviceName, groupID FROM %s.%s WHERE timestamp = @timestamp AND groupID = @groupID LIMIT 1", r.TraceDB, r.errorTable)
|
||||
@@ -2822,11 +2716,6 @@ func (r *ClickHouseReader) GetNextPrevErrorIDs(ctx context.Context, queryParams
|
||||
|
||||
func (r *ClickHouseReader) getNextErrorID(ctx context.Context, queryParams *model.GetErrorParams) (string, time.Time, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "getNextErrorID",
|
||||
})
|
||||
var getNextErrorIDReponse []model.NextPrevErrorIDsDBResponse
|
||||
|
||||
query := fmt.Sprintf("SELECT errorID as nextErrorID, timestamp as nextTimestamp FROM %s.%s WHERE groupID = @groupID AND timestamp >= @timestamp AND errorID != @errorID ORDER BY timestamp ASC LIMIT 2", r.TraceDB, r.errorTable)
|
||||
@@ -2896,11 +2785,6 @@ func (r *ClickHouseReader) getNextErrorID(ctx context.Context, queryParams *mode
|
||||
|
||||
func (r *ClickHouseReader) getPrevErrorID(ctx context.Context, queryParams *model.GetErrorParams) (string, time.Time, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "getPrevErrorID",
|
||||
})
|
||||
var getPrevErrorIDReponse []model.NextPrevErrorIDsDBResponse
|
||||
|
||||
query := fmt.Sprintf("SELECT errorID as prevErrorID, timestamp as prevTimestamp FROM %s.%s WHERE groupID = @groupID AND timestamp <= @timestamp AND errorID != @errorID ORDER BY timestamp DESC LIMIT 2", r.TraceDB, r.errorTable)
|
||||
@@ -2992,11 +2876,6 @@ func (r *ClickHouseReader) FetchTemporality(ctx context.Context, orgID valuer.UU
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetLogFields(ctx context.Context) (*model.GetFieldsResponse, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetLogFields",
|
||||
})
|
||||
// response will contain top level fields from the otel log model
|
||||
response := model.GetFieldsResponse{
|
||||
Selected: constants.StaticSelectedLogFields,
|
||||
@@ -3033,11 +2912,6 @@ func (r *ClickHouseReader) GetLogFields(ctx context.Context) (*model.GetFieldsRe
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetLogFieldsFromNames(ctx context.Context, fieldNames []string) (*model.GetFieldsResponse, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetLogFieldsFromNames",
|
||||
})
|
||||
// response will contain top level fields from the otel log model
|
||||
response := model.GetFieldsResponse{
|
||||
Selected: constants.StaticSelectedLogFields,
|
||||
@@ -3088,10 +2962,6 @@ func (r *ClickHouseReader) extractSelectedAndInterestingFields(tableStatement st
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) UpdateLogField(ctx context.Context, field *model.UpdateField) *model.ApiError {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "UpdateLogField",
|
||||
})
|
||||
if !field.Selected {
|
||||
return model.ForbiddenError(errors.New("removing a selected field is not allowed, please reach out to support."))
|
||||
}
|
||||
@@ -3158,10 +3028,6 @@ func (r *ClickHouseReader) UpdateLogField(ctx context.Context, field *model.Upda
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetTraceFields(ctx context.Context) (*model.GetFieldsResponse, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetTraceFields",
|
||||
})
|
||||
// response will contain top level fields from the otel trace model
|
||||
response := model.GetFieldsResponse{
|
||||
Selected: []model.Field{},
|
||||
@@ -3217,11 +3083,6 @@ func (r *ClickHouseReader) GetTraceFields(ctx context.Context) (*model.GetFields
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) UpdateTraceField(ctx context.Context, field *model.UpdateField) *model.ApiError {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "UpdateTraceField",
|
||||
})
|
||||
if !field.Selected {
|
||||
return model.ForbiddenError(errors.New("removing a selected field is not allowed, please reach out to support."))
|
||||
}
|
||||
@@ -3313,10 +3174,6 @@ func (r *ClickHouseReader) UpdateTraceField(ctx context.Context, field *model.Up
|
||||
return nil
|
||||
}
|
||||
func (r *ClickHouseReader) QueryDashboardVars(ctx context.Context, query string) (*model.DashboardVar, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "QueryDashboardVars",
|
||||
})
|
||||
var result = model.DashboardVar{VariableValues: make([]interface{}, 0)}
|
||||
rows, err := r.db.Query(ctx, query)
|
||||
|
||||
@@ -3353,11 +3210,6 @@ func (r *ClickHouseReader) QueryDashboardVars(ctx context.Context, query string)
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMetricAggregateAttributes(ctx context.Context, orgID valuer.UUID, req *v3.AggregateAttributeRequest, skipSignozMetrics bool) (*v3.AggregateAttributeResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMetricAggregateAttributes",
|
||||
})
|
||||
var response v3.AggregateAttributeResponse
|
||||
normalized := true
|
||||
if constants.IsDotMetricsEnabled {
|
||||
@@ -3436,11 +3288,6 @@ func (r *ClickHouseReader) GetMetricAggregateAttributes(ctx context.Context, org
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMeterAggregateAttributes(ctx context.Context, orgID valuer.UUID, req *v3.AggregateAttributeRequest) (*v3.AggregateAttributeResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMeterAggregateAttributes",
|
||||
})
|
||||
var response v3.AggregateAttributeResponse
|
||||
// Query all relevant metric names from time_series_v4, but leave metadata retrieval to cache/db
|
||||
query := fmt.Sprintf(
|
||||
@@ -3489,11 +3336,6 @@ func (r *ClickHouseReader) GetMeterAggregateAttributes(ctx context.Context, orgI
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMetricAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMetricAttributeKeys",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -3534,11 +3376,6 @@ func (r *ClickHouseReader) GetMetricAttributeKeys(ctx context.Context, req *v3.F
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMeterAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMeterAttributeKeys",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -3575,11 +3412,6 @@ func (r *ClickHouseReader) GetMeterAttributeKeys(ctx context.Context, req *v3.Fi
|
||||
|
||||
func (r *ClickHouseReader) GetMetricAttributeValues(ctx context.Context, req *v3.FilterAttributeValueRequest) (*v3.FilterAttributeValueResponse, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMetricAttributeValues",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -3620,11 +3452,6 @@ func (r *ClickHouseReader) GetMetricAttributeValues(ctx context.Context, req *v3
|
||||
|
||||
func (r *ClickHouseReader) GetMetricMetadata(ctx context.Context, orgID valuer.UUID, metricName, serviceName string) (*v3.MetricMetadataResponse, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMetricMetadata",
|
||||
})
|
||||
unixMilli := common.PastDayRoundOff()
|
||||
|
||||
// 1. Fetch metadata from cache/db using unified function
|
||||
@@ -3706,10 +3533,6 @@ func (r *ClickHouseReader) GetMetricMetadata(ctx context.Context, orgID valuer.U
|
||||
// GetCountOfThings returns the count of things in the query
|
||||
// This is a generic function that can be used to check if any data exists for a given query
|
||||
func (r *ClickHouseReader) GetCountOfThings(ctx context.Context, query string) (uint64, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetCountOfThings",
|
||||
})
|
||||
var count uint64
|
||||
err := r.db.QueryRow(ctx, query).Scan(&count)
|
||||
if err != nil {
|
||||
@@ -3760,11 +3583,6 @@ func (r *ClickHouseReader) GetActiveHostsFromMetricMetadata(ctx context.Context,
|
||||
func (r *ClickHouseReader) GetLatestReceivedMetric(
|
||||
ctx context.Context, metricNames []string, labelValues map[string]string,
|
||||
) (*model.MetricStatus, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetLatestReceivedMetric",
|
||||
})
|
||||
// at least 1 metric name must be specified.
|
||||
// this query can be too slow otherwise.
|
||||
if len(metricNames) < 1 {
|
||||
@@ -3849,11 +3667,6 @@ func isColumn(tableStatement, attrType, field, datType string) bool {
|
||||
|
||||
func (r *ClickHouseReader) GetLogAggregateAttributes(ctx context.Context, req *v3.AggregateAttributeRequest) (*v3.AggregateAttributeResponse, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetLogAggregateAttributes",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -3938,11 +3751,6 @@ func (r *ClickHouseReader) GetLogAggregateAttributes(ctx context.Context, req *v
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetLogAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetLogAttributeKeys",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -4009,10 +3817,6 @@ func (r *ClickHouseReader) GetLogAttributeKeys(ctx context.Context, req *v3.Filt
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) FetchRelatedValues(ctx context.Context, req *v3.FilterAttributeValueRequest) ([]string, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "FetchRelatedValues",
|
||||
})
|
||||
var andConditions []string
|
||||
|
||||
andConditions = append(andConditions, fmt.Sprintf("unix_milli >= %d", req.StartTimeMillis))
|
||||
@@ -4104,11 +3908,6 @@ func (r *ClickHouseReader) FetchRelatedValues(ctx context.Context, req *v3.Filte
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetLogAttributeValues(ctx context.Context, req *v3.FilterAttributeValueRequest) (*v3.FilterAttributeValueResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetLogAttributeValues",
|
||||
})
|
||||
var err error
|
||||
var filterValueColumn string
|
||||
var rows driver.Rows
|
||||
@@ -4426,10 +4225,6 @@ func readRowsForTimeSeriesResult(rows driver.Rows, vars []interface{}, columnNam
|
||||
|
||||
// GetTimeSeriesResultV3 runs the query and returns list of time series
|
||||
func (r *ClickHouseReader) GetTimeSeriesResultV3(ctx context.Context, query string) ([]*v3.Series, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetTimeSeriesResultV3",
|
||||
})
|
||||
// Hook up query progress reporting if requested.
|
||||
queryId := ctx.Value("queryId")
|
||||
if queryId != nil {
|
||||
@@ -4493,10 +4288,6 @@ func (r *ClickHouseReader) GetTimeSeriesResultV3(ctx context.Context, query stri
|
||||
|
||||
// GetListResultV3 runs the query and returns list of rows
|
||||
func (r *ClickHouseReader) GetListResultV3(ctx context.Context, query string) ([]*v3.Row, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetListResultV3",
|
||||
})
|
||||
rows, err := r.db.Query(ctx, query)
|
||||
if err != nil {
|
||||
zap.L().Error("error while reading time series result", zap.Error(err))
|
||||
@@ -4559,11 +4350,6 @@ func (r *ClickHouseReader) GetListResultV3(ctx context.Context, query string) ([
|
||||
// GetHostMetricsExistenceAndEarliestTime returns (count, minFirstReportedUnixMilli, error) for the given host metric names
|
||||
// from distributed_metadata. When count is 0, minFirstReportedUnixMilli is 0.
|
||||
func (r *ClickHouseReader) GetMetricsExistenceAndEarliestTime(ctx context.Context, metricNames []string) (uint64, uint64, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMetricsExistenceAndEarliestTime",
|
||||
})
|
||||
if len(metricNames) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
@@ -4599,10 +4385,6 @@ func getPersonalisedError(err error) error {
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) CheckClickHouse(ctx context.Context) error {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "CheckClickHouse",
|
||||
})
|
||||
rows, err := r.db.Query(ctx, "SELECT 1")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -4613,11 +4395,6 @@ func (r *ClickHouseReader) CheckClickHouse(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetTraceAggregateAttributes(ctx context.Context, req *v3.AggregateAttributeRequest) (*v3.AggregateAttributeResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetTraceAggregateAttributes",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -4711,11 +4488,6 @@ func (r *ClickHouseReader) GetTraceAggregateAttributes(ctx context.Context, req
|
||||
|
||||
func (r *ClickHouseReader) GetTraceAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetTraceAttributeKeys",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -4784,11 +4556,6 @@ func (r *ClickHouseReader) GetTraceAttributeKeys(ctx context.Context, req *v3.Fi
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetTraceAttributeValues(ctx context.Context, req *v3.FilterAttributeValueRequest) (*v3.FilterAttributeValueResponse, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetTraceAttributeValues",
|
||||
})
|
||||
var query string
|
||||
var filterValueColumn string
|
||||
var err error
|
||||
@@ -4882,11 +4649,6 @@ func (r *ClickHouseReader) GetTraceAttributeValues(ctx context.Context, req *v3.
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetSpanAttributeKeysByNames(ctx context.Context, names []string) (map[string]v3.AttributeKey, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetSpanAttributeKeysByNames",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -4935,10 +4697,6 @@ func (r *ClickHouseReader) GetSpanAttributeKeysByNames(ctx context.Context, name
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) AddRuleStateHistory(ctx context.Context, ruleStateHistory []model.RuleStateHistory) error {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "AddRuleStateHistory",
|
||||
})
|
||||
var statement driver.Batch
|
||||
var err error
|
||||
|
||||
@@ -4970,10 +4728,6 @@ func (r *ClickHouseReader) AddRuleStateHistory(ctx context.Context, ruleStateHis
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetLastSavedRuleStateHistory(ctx context.Context, ruleID string) ([]model.RuleStateHistory, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetLastSavedRuleStateHistory",
|
||||
})
|
||||
query := fmt.Sprintf("SELECT * FROM %s.%s WHERE rule_id = '%s' AND state_changed = true ORDER BY unix_milli DESC LIMIT 1 BY fingerprint",
|
||||
signozHistoryDBName, ruleStateHistoryTableName, ruleID)
|
||||
|
||||
@@ -4988,10 +4742,6 @@ func (r *ClickHouseReader) GetLastSavedRuleStateHistory(ctx context.Context, rul
|
||||
func (r *ClickHouseReader) ReadRuleStateHistoryByRuleID(
|
||||
ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) (*model.RuleStateTimeline, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "ReadRuleStateHistoryByRuleID",
|
||||
})
|
||||
var conditions []string
|
||||
|
||||
conditions = append(conditions, fmt.Sprintf("rule_id = '%s'", ruleID))
|
||||
@@ -5106,10 +4856,6 @@ func (r *ClickHouseReader) ReadRuleStateHistoryByRuleID(
|
||||
|
||||
func (r *ClickHouseReader) ReadRuleStateHistoryTopContributorsByRuleID(
|
||||
ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) ([]model.RuleStateHistoryContributor, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "ReadRuleStateHistoryTopContributorsByRuleID",
|
||||
})
|
||||
query := fmt.Sprintf(`SELECT
|
||||
fingerprint,
|
||||
any(labels) as labels,
|
||||
@@ -5134,10 +4880,6 @@ func (r *ClickHouseReader) ReadRuleStateHistoryTopContributorsByRuleID(
|
||||
|
||||
func (r *ClickHouseReader) GetOverallStateTransitions(ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) ([]model.ReleStateItem, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetOverallStateTransitions",
|
||||
})
|
||||
tmpl := `WITH firing_events AS (
|
||||
SELECT
|
||||
rule_id,
|
||||
@@ -5265,10 +5007,6 @@ ORDER BY firing_time ASC;`
|
||||
|
||||
func (r *ClickHouseReader) GetAvgResolutionTime(ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) (float64, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetAvgResolutionTime",
|
||||
})
|
||||
tmpl := `
|
||||
WITH firing_events AS (
|
||||
SELECT
|
||||
@@ -5380,10 +5118,6 @@ ORDER BY ts ASC;`
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetTotalTriggers(ctx context.Context, ruleID string, params *model.QueryRuleStateHistory) (uint64, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetTotalTriggers",
|
||||
})
|
||||
query := fmt.Sprintf("SELECT count(*) FROM %s.%s WHERE rule_id = '%s' AND (state_changed = true) AND (state = '%s') AND unix_milli >= %d AND unix_milli <= %d",
|
||||
signozHistoryDBName, ruleStateHistoryTableName, ruleID, model.StateFiring.String(), params.Start, params.End)
|
||||
|
||||
@@ -5412,11 +5146,6 @@ func (r *ClickHouseReader) GetTriggersByInterval(ctx context.Context, ruleID str
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMinAndMaxTimestampForTraceID(ctx context.Context, traceID []string) (int64, int64, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMinAndMaxTimestampForTraceID",
|
||||
})
|
||||
var minTime, maxTime time.Time
|
||||
|
||||
query := fmt.Sprintf("SELECT min(timestamp), max(timestamp) FROM %s.%s WHERE traceID IN ('%s')",
|
||||
@@ -5454,11 +5183,6 @@ func (r *ClickHouseReader) SubscribeToQueryProgress(
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetAllMetricFilterAttributeKeys(ctx context.Context, req *metrics_explorer.FilterKeyRequest) (*[]v3.AttributeKey, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetAllMetricFilterAttributeKeys",
|
||||
})
|
||||
var rows driver.Rows
|
||||
var response []v3.AttributeKey
|
||||
normalized := true
|
||||
@@ -5496,11 +5220,6 @@ func (r *ClickHouseReader) GetAllMetricFilterAttributeKeys(ctx context.Context,
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetAllMetricFilterAttributeValues(ctx context.Context, req *metrics_explorer.FilterValueRequest) ([]string, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetAllMetricFilterAttributeValues",
|
||||
})
|
||||
var query string
|
||||
var err error
|
||||
var rows driver.Rows
|
||||
@@ -5537,11 +5256,6 @@ func (r *ClickHouseReader) GetAllMetricFilterAttributeValues(ctx context.Context
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetAllMetricFilterUnits(ctx context.Context, req *metrics_explorer.FilterValueRequest) ([]string, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetAllMetricFilterUnits",
|
||||
})
|
||||
var rows driver.Rows
|
||||
var response []string
|
||||
query := fmt.Sprintf("SELECT DISTINCT unit FROM %s.%s WHERE unit ILIKE $1 AND unit IS NOT NULL ORDER BY unit", signozMetricDBName, signozTSTableNameV41Day)
|
||||
@@ -5569,11 +5283,6 @@ func (r *ClickHouseReader) GetAllMetricFilterUnits(ctx context.Context, req *met
|
||||
return response, nil
|
||||
}
|
||||
func (r *ClickHouseReader) GetAllMetricFilterTypes(ctx context.Context, req *metrics_explorer.FilterValueRequest) ([]string, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetAllMetricFilterTypes",
|
||||
})
|
||||
var rows driver.Rows
|
||||
var response []string
|
||||
query := fmt.Sprintf("SELECT DISTINCT type FROM %s.%s WHERE type ILIKE $1 AND type IS NOT NULL ORDER BY type", signozMetricDBName, signozTSTableNameV41Day)
|
||||
@@ -5601,11 +5310,6 @@ func (r *ClickHouseReader) GetAllMetricFilterTypes(ctx context.Context, req *met
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMetricsDataPoints(ctx context.Context, metricName string) (uint64, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMetricsDataPoints",
|
||||
})
|
||||
query := fmt.Sprintf(`SELECT
|
||||
sum(count) as data_points
|
||||
FROM %s.%s
|
||||
@@ -5621,11 +5325,6 @@ WHERE metric_name = ?
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMetricsLastReceived(ctx context.Context, metricName string) (int64, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMetricsLastReceived",
|
||||
})
|
||||
query := fmt.Sprintf(`SELECT
|
||||
MAX(unix_milli) AS last_received_time
|
||||
FROM %s.%s
|
||||
@@ -5651,11 +5350,6 @@ WHERE metric_name = ? and unix_milli > ?
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetTotalTimeSeriesForMetricName(ctx context.Context, metricName string) (uint64, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetTotalTimeSeriesForMetricName",
|
||||
})
|
||||
query := fmt.Sprintf(`SELECT
|
||||
uniq(fingerprint) AS timeSeriesCount
|
||||
FROM %s.%s
|
||||
@@ -5670,11 +5364,6 @@ WHERE metric_name = ?;`, signozMetricDBName, signozTSTableNameV41Week)
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetAttributesForMetricName(ctx context.Context, metricName string, start, end *int64, filters *v3.FilterSet) (*[]metrics_explorer.Attribute, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetAttributesForMetricName",
|
||||
})
|
||||
whereClause := ""
|
||||
if filters != nil {
|
||||
conditions, _ := utils.BuildFilterConditions(filters, "t")
|
||||
@@ -5742,11 +5431,6 @@ WHERE metric_name = ? AND __normalized=? %s`
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetActiveTimeSeriesForMetricName(ctx context.Context, metricName string, duration time.Duration) (uint64, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetActiveTimeSeriesForMetricName",
|
||||
})
|
||||
milli := time.Now().Add(-duration).UnixMilli()
|
||||
query := fmt.Sprintf("SELECT uniq(fingerprint) FROM %s.%s WHERE metric_name = '%s' and unix_milli >= ?", signozMetricDBName, signozTSTableNameV4, metricName)
|
||||
var timeSeries uint64
|
||||
@@ -5760,11 +5444,6 @@ func (r *ClickHouseReader) GetActiveTimeSeriesForMetricName(ctx context.Context,
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) ListSummaryMetrics(ctx context.Context, orgID valuer.UUID, req *metrics_explorer.SummaryListMetricsRequest) (*metrics_explorer.SummaryListMetricsResponse, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "ListSummaryMetrics",
|
||||
})
|
||||
var args []interface{}
|
||||
|
||||
// Build filter conditions (if any)
|
||||
@@ -5983,11 +5662,6 @@ func (r *ClickHouseReader) ListSummaryMetrics(ctx context.Context, orgID valuer.
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMetricsTimeSeriesPercentage(ctx context.Context, req *metrics_explorer.TreeMapMetricsRequest) (*[]metrics_explorer.TreeMapResponseItem, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMetricsTimeSeriesPercentage",
|
||||
})
|
||||
var args []interface{}
|
||||
|
||||
normalized := true
|
||||
@@ -6068,11 +5742,6 @@ func (r *ClickHouseReader) GetMetricsTimeSeriesPercentage(ctx context.Context, r
|
||||
|
||||
func (r *ClickHouseReader) GetMetricsSamplesPercentage(ctx context.Context, req *metrics_explorer.TreeMapMetricsRequest) (*[]metrics_explorer.TreeMapResponseItem, *model.ApiError) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMetricsSamplesPercentage",
|
||||
})
|
||||
conditions, _ := utils.BuildFilterConditions(&req.Filters, "ts")
|
||||
whereClause := ""
|
||||
if conditions != nil {
|
||||
@@ -6232,11 +5901,6 @@ func (r *ClickHouseReader) GetMetricsSamplesPercentage(ctx context.Context, req
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetNameSimilarity(ctx context.Context, req *metrics_explorer.RelatedMetricsRequest) (map[string]metrics_explorer.RelatedMetricsScore, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetNameSimilarity",
|
||||
})
|
||||
start, end, tsTable, _ := utils.WhichTSTableToUse(req.Start, req.End)
|
||||
|
||||
normalized := true
|
||||
@@ -6290,11 +5954,6 @@ func (r *ClickHouseReader) GetNameSimilarity(ctx context.Context, req *metrics_e
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetAttributeSimilarity(ctx context.Context, req *metrics_explorer.RelatedMetricsRequest) (map[string]metrics_explorer.RelatedMetricsScore, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetAttributeSimilarity",
|
||||
})
|
||||
start, end, tsTable, _ := utils.WhichTSTableToUse(req.Start, req.End)
|
||||
|
||||
normalized := true
|
||||
@@ -6453,11 +6112,6 @@ func (r *ClickHouseReader) GetAttributeSimilarity(ctx context.Context, req *metr
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetMetricsAllResourceAttributes(ctx context.Context, start int64, end int64) (map[string]uint64, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetMetricsAllResourceAttributes",
|
||||
})
|
||||
start, end, attTable, _ := utils.WhichAttributesTableToUse(start, end)
|
||||
query := fmt.Sprintf(`SELECT
|
||||
key,
|
||||
@@ -6494,11 +6148,6 @@ ORDER BY distinct_value_count DESC;`, signozMetadataDbName, attTable)
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetInspectMetrics(ctx context.Context, req *metrics_explorer.InspectMetricsRequest, fingerprints []string) (*metrics_explorer.InspectMetricsResponse, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetInspectMetrics",
|
||||
})
|
||||
start, end, _, localTsTable := utils.WhichTSTableToUse(req.Start, req.End)
|
||||
fingerprintsString := strings.Join(fingerprints, ",")
|
||||
query := fmt.Sprintf(`SELECT
|
||||
@@ -6593,11 +6242,6 @@ func (r *ClickHouseReader) GetInspectMetrics(ctx context.Context, req *metrics_e
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetInspectMetricsFingerprints(ctx context.Context, attributes []string, req *metrics_explorer.InspectMetricsRequest) ([]string, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetInspectMetricsFingerprints",
|
||||
})
|
||||
// Build dynamic key selections and JSON extracts
|
||||
var jsonExtracts []string
|
||||
var groupBys []string
|
||||
@@ -6679,11 +6323,6 @@ LIMIT 40`, // added rand to get diff value every time we run this query
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) UpdateMetricsMetadata(ctx context.Context, orgID valuer.UUID, req *model.UpdateMetricsMetadata) *model.ApiError {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "UpdateMetricsMetadata",
|
||||
})
|
||||
if req.MetricType == v3.MetricTypeHistogram {
|
||||
labels := []string{"le"}
|
||||
hasLabels, apiError := r.CheckForLabelsInMetric(ctx, req.MetricName, labels)
|
||||
@@ -6728,11 +6367,6 @@ VALUES ( ?, ?, ?, ?, ?, ?, ?);`, signozMetricDBName, signozUpdatedMetricsMetadat
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) CheckForLabelsInMetric(ctx context.Context, metricName string, labels []string) (bool, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "CheckForLabelsInMetric",
|
||||
})
|
||||
if len(labels) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
@@ -6767,11 +6401,6 @@ func (r *ClickHouseReader) CheckForLabelsInMetric(ctx context.Context, metricNam
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetUpdatedMetricsMetadata(ctx context.Context, orgID valuer.UUID, metricNames ...string) (map[string]*model.UpdateMetricsMetadata, *model.ApiError) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetUpdatedMetricsMetadata",
|
||||
})
|
||||
cachedMetadata := make(map[string]*model.UpdateMetricsMetadata)
|
||||
var missingMetrics []string
|
||||
|
||||
@@ -6881,11 +6510,6 @@ func (r *ClickHouseReader) GetUpdatedMetricsMetadata(ctx context.Context, orgID
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) SearchTraces(ctx context.Context, params *model.SearchTracesParams) (*[]model.SearchSpansResult, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "SearchTraces",
|
||||
})
|
||||
searchSpansResult := []model.SearchSpansResult{
|
||||
{
|
||||
Columns: []string{"__time", "SpanId", "TraceId", "ServiceName", "Name", "Kind", "DurationNano", "TagsKeys", "TagsValues", "References", "Events", "HasError", "StatusMessage", "StatusCodeString", "SpanKind"},
|
||||
@@ -6997,11 +6621,6 @@ func (r *ClickHouseReader) GetNormalizedStatus(
|
||||
metricNames []string,
|
||||
) (map[string]bool, error) {
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "clickhouse-reader",
|
||||
instrumentation.CodeFunctionName: "GetNormalizedStatus",
|
||||
})
|
||||
if len(metricNames) == 0 {
|
||||
return map[string]bool{}, nil
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/flagger"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/modules/thirdpartyapi"
|
||||
"github.com/SigNoz/signoz/pkg/queryparser"
|
||||
|
||||
@@ -63,7 +62,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
||||
@@ -2414,12 +2412,7 @@ func (aH *APIHandler) onboardKafka(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "onboardKafka",
|
||||
})
|
||||
|
||||
results, errQueriesByName, err := aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
|
||||
results, errQueriesByName, err := aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
@@ -2531,12 +2524,7 @@ func (aH *APIHandler) getNetworkData(w http.ResponseWriter, r *http.Request) {
|
||||
var result []*v3.Result
|
||||
var errQueriesByName map[string]error
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "getNetworkData",
|
||||
})
|
||||
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
@@ -2572,7 +2560,7 @@ func (aH *APIHandler) getNetworkData(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
resultFetchLatency, errQueriesByNameFetchLatency, err := aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
|
||||
resultFetchLatency, errQueriesByNameFetchLatency, err := aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByNameFetchLatency)
|
||||
@@ -2628,11 +2616,7 @@ func (aH *APIHandler) getProducerData(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
evalCtx := featuretypes.NewFlaggerEvaluationContext(orgID)
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "getProducerData",
|
||||
})
|
||||
kafkaSpanEval := aH.Signoz.Flagger.BooleanOrEmpty(ctx, flagger.FeatureKafkaSpanEval, evalCtx)
|
||||
kafkaSpanEval := aH.Signoz.Flagger.BooleanOrEmpty(r.Context(), flagger.FeatureKafkaSpanEval, evalCtx)
|
||||
|
||||
queryRangeParams, err := kafka.BuildQueryRangeParams(messagingQueue, "producer", kafkaSpanEval)
|
||||
if err != nil {
|
||||
@@ -2650,7 +2634,7 @@ func (aH *APIHandler) getProducerData(w http.ResponseWriter, r *http.Request) {
|
||||
var result []*v3.Result
|
||||
var errQueriesByName map[string]error
|
||||
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
@@ -2703,11 +2687,7 @@ func (aH *APIHandler) getConsumerData(w http.ResponseWriter, r *http.Request) {
|
||||
var result []*v3.Result
|
||||
var errQueriesByName map[string]error
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "getConsumerData",
|
||||
})
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
@@ -2761,11 +2741,7 @@ func (aH *APIHandler) getPartitionOverviewLatencyData(w http.ResponseWriter, r *
|
||||
var result []*v3.Result
|
||||
var errQueriesByName map[string]error
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "getPartitionOverviewLatencyData",
|
||||
})
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
@@ -2819,11 +2795,7 @@ func (aH *APIHandler) getConsumerPartitionLatencyData(w http.ResponseWriter, r *
|
||||
var result []*v3.Result
|
||||
var errQueriesByName map[string]error
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "getConsumerPartitionLatencyData",
|
||||
})
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
@@ -2880,11 +2852,7 @@ func (aH *APIHandler) getProducerThroughputOverview(w http.ResponseWriter, r *ht
|
||||
var result []*v3.Result
|
||||
var errQueriesByName map[string]error
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "getProducerThroughputOverview",
|
||||
})
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, producerQueryRangeParams)
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, producerQueryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
@@ -2918,7 +2886,7 @@ func (aH *APIHandler) getProducerThroughputOverview(w http.ResponseWriter, r *ht
|
||||
return
|
||||
}
|
||||
|
||||
resultFetchLatency, errQueriesByNameFetchLatency, err := aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
|
||||
resultFetchLatency, errQueriesByNameFetchLatency, err := aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByNameFetchLatency)
|
||||
@@ -2995,11 +2963,7 @@ func (aH *APIHandler) getProducerThroughputDetails(w http.ResponseWriter, r *htt
|
||||
var result []*v3.Result
|
||||
var errQueriesByName map[string]error
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "getProducerThroughputDetails",
|
||||
})
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
@@ -3053,11 +3017,7 @@ func (aH *APIHandler) getConsumerThroughputOverview(w http.ResponseWriter, r *ht
|
||||
var result []*v3.Result
|
||||
var errQueriesByName map[string]error
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "getConsumerThroughputOverview",
|
||||
})
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
@@ -3111,11 +3071,7 @@ func (aH *APIHandler) getConsumerThroughputDetails(w http.ResponseWriter, r *htt
|
||||
var result []*v3.Result
|
||||
var errQueriesByName map[string]error
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "getConsumerThroughputDetails",
|
||||
})
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
@@ -3175,11 +3131,7 @@ func (aH *APIHandler) getProducerConsumerEval(w http.ResponseWriter, r *http.Req
|
||||
var result []*v3.Result
|
||||
var errQueriesByName map[string]error
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "getProducerConsumerEval",
|
||||
})
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(ctx, orgID, queryRangeParams)
|
||||
result, errQueriesByName, err = aH.querierV2.QueryRange(r.Context(), orgID, queryRangeParams)
|
||||
if err != nil {
|
||||
apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
@@ -3440,10 +3392,6 @@ func (aH *APIHandler) calculateLogsConnectionStatus(ctx context.Context, orgID v
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "calculateLogsConnectionStatus",
|
||||
})
|
||||
queryRes, _, err := aH.querier.QueryRange(ctx, orgID, qrParams)
|
||||
if err != nil {
|
||||
return nil, model.InternalError(fmt.Errorf(
|
||||
@@ -3996,10 +3944,6 @@ func (aH *APIHandler) calculateAWSIntegrationSvcLogsConnectionStatus(
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "calculateLogsConnectionStatus",
|
||||
})
|
||||
queryRes, _, err := aH.querier.QueryRange(
|
||||
ctx, orgID, qrParams,
|
||||
)
|
||||
@@ -4548,10 +4492,6 @@ func (aH *APIHandler) queryRangeV3(ctx context.Context, queryRangeParams *v3.Que
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "QueryRange",
|
||||
})
|
||||
result, errQueriesByName, err = aH.querier.QueryRange(ctx, orgID, queryRangeParams)
|
||||
|
||||
if err != nil {
|
||||
@@ -4946,11 +4886,7 @@ func (aH *APIHandler) QueryRangeV4(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "QueryRangeV4",
|
||||
})
|
||||
aH.queryRangeV4(ctx, queryRangeParams, w, r)
|
||||
aH.queryRangeV4(r.Context(), queryRangeParams, w, r)
|
||||
}
|
||||
|
||||
func (aH *APIHandler) traceFields(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -5043,12 +4979,8 @@ func (aH *APIHandler) getDomainList(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "getDomainList",
|
||||
})
|
||||
// Execute the query using the v5 querier
|
||||
result, err := aH.Signoz.Querier.QueryRange(ctx, orgID, queryRangeRequest)
|
||||
result, err := aH.Signoz.Querier.QueryRange(r.Context(), orgID, queryRangeRequest)
|
||||
if err != nil {
|
||||
zap.L().Error("Query execution failed", zap.Error(err))
|
||||
apiErrObj := errorsV2.New(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error())
|
||||
@@ -5103,12 +5035,8 @@ func (aH *APIHandler) getDomainInfo(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx := ctxtypes.AddCommentsToContext(r.Context(), map[string]string{
|
||||
instrumentation.CodeNamespace: "app",
|
||||
instrumentation.CodeFunctionName: "getDomainInfo",
|
||||
})
|
||||
// Execute the query using the v5 querier
|
||||
result, err := aH.Signoz.Querier.QueryRange(ctx, orgID, queryRangeRequest)
|
||||
result, err := aH.Signoz.Querier.QueryRange(r.Context(), orgID, queryRangeRequest)
|
||||
if err != nil {
|
||||
zap.L().Error("Query execution failed", zap.Error(err))
|
||||
apiErrObj := errorsV2.New(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, err.Error())
|
||||
|
||||
@@ -5,14 +5,12 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4/helpers"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@@ -165,10 +163,6 @@ func (p *ClustersRepo) getTopClusterGroups(ctx context.Context, orgID valuer.UUI
|
||||
topClusterGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "getTopClusterGroups",
|
||||
})
|
||||
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, topClusterGroupsQueryRangeParams)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -284,10 +278,6 @@ func (p *ClustersRepo) GetClusterList(ctx context.Context, orgID valuer.UUID, re
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "GetClusterList",
|
||||
})
|
||||
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, query)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
||||
@@ -5,14 +5,12 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4/helpers"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@@ -232,10 +230,6 @@ func (d *DaemonSetsRepo) getTopDaemonSetGroups(ctx context.Context, orgID valuer
|
||||
topDaemonSetGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "getTopDaemonSetGroups",
|
||||
})
|
||||
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, topDaemonSetGroupsQueryRangeParams)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -361,10 +355,6 @@ func (d *DaemonSetsRepo) GetDaemonSetList(ctx context.Context, orgID valuer.UUID
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "GetDaemonSetList",
|
||||
})
|
||||
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, query)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
||||
@@ -5,14 +5,12 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4/helpers"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@@ -232,10 +230,6 @@ func (d *DeploymentsRepo) getTopDeploymentGroups(ctx context.Context, orgID valu
|
||||
topDeploymentGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "getTopDeploymentGroups",
|
||||
})
|
||||
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, topDeploymentGroupsQueryRangeParams)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4/helpers"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||
@@ -17,7 +16,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/exp/maps"
|
||||
@@ -274,10 +272,6 @@ func (h *HostsRepo) getTopHostGroups(ctx context.Context, orgID valuer.UUID, req
|
||||
topHostGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "getTopHostGroups",
|
||||
})
|
||||
queryResponse, _, err := h.querierV2.QueryRange(ctx, orgID, topHostGroupsQueryRangeParams)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -488,10 +482,6 @@ func (h *HostsRepo) GetHostList(ctx context.Context, orgID valuer.UUID, req mode
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "GetHostList",
|
||||
})
|
||||
queryResponse, _, err := h.querierV2.QueryRange(ctx, orgID, query)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
||||
@@ -5,14 +5,12 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4/helpers"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@@ -276,10 +274,6 @@ func (d *JobsRepo) getTopJobGroups(ctx context.Context, orgID valuer.UUID, req m
|
||||
topJobGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "getTopJobGroups",
|
||||
})
|
||||
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, topJobGroupsQueryRangeParams)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -405,10 +399,6 @@ func (d *JobsRepo) GetJobList(ctx context.Context, orgID valuer.UUID, req model.
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "GetJobList",
|
||||
})
|
||||
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, query)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
||||
@@ -5,14 +5,12 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4/helpers"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@@ -159,10 +157,6 @@ func (p *NamespacesRepo) getTopNamespaceGroups(ctx context.Context, orgID valuer
|
||||
topNamespaceGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "getTopNamespaceGroups",
|
||||
})
|
||||
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, topNamespaceGroupsQueryRangeParams)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -283,10 +277,6 @@ func (p *NamespacesRepo) GetNamespaceList(ctx context.Context, orgID valuer.UUID
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "GetNamespaceList",
|
||||
})
|
||||
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, query)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4/helpers"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||
@@ -15,7 +14,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@@ -189,10 +187,6 @@ func (p *NodesRepo) getTopNodeGroups(ctx context.Context, orgID valuer.UUID, req
|
||||
topNodeGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "getTopNodeGroups",
|
||||
})
|
||||
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, topNodeGroupsQueryRangeParams)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -308,10 +302,6 @@ func (p *NodesRepo) GetNodeList(ctx context.Context, orgID valuer.UUID, req mode
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "GetNodeList",
|
||||
})
|
||||
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, query)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4/helpers"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||
@@ -15,7 +14,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@@ -334,10 +332,6 @@ func (p *PodsRepo) getTopPodGroups(ctx context.Context, orgID valuer.UUID, req m
|
||||
topPodGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "getTopPodGroups",
|
||||
})
|
||||
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, topPodGroupsQueryRangeParams)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -453,10 +447,6 @@ func (p *PodsRepo) GetPodList(ctx context.Context, orgID valuer.UUID, req model.
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "GetPodList",
|
||||
})
|
||||
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, query)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
||||
@@ -5,14 +5,12 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4/helpers"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@@ -173,10 +171,6 @@ func (p *ProcessesRepo) getTopProcessGroups(ctx context.Context, orgID valuer.UU
|
||||
topProcessGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "getTopProcessGroups",
|
||||
})
|
||||
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, topProcessGroupsQueryRangeParams)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -290,10 +284,6 @@ func (p *ProcessesRepo) GetProcessList(ctx context.Context, orgID valuer.UUID, r
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "GetProcessList",
|
||||
})
|
||||
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, query)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
||||
@@ -5,14 +5,12 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4/helpers"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@@ -192,10 +190,6 @@ func (p *PvcsRepo) getTopVolumeGroups(ctx context.Context, orgID valuer.UUID, re
|
||||
topVolumeGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "getTopVolumeGroups",
|
||||
})
|
||||
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, topVolumeGroupsQueryRangeParams)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -311,10 +305,6 @@ func (p *PvcsRepo) GetPvcList(ctx context.Context, orgID valuer.UUID, req model.
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "GetPvcList",
|
||||
})
|
||||
queryResponse, _, err := p.querierV2.QueryRange(ctx, orgID, query)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
||||
@@ -5,14 +5,12 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4/helpers"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@@ -232,10 +230,6 @@ func (d *StatefulSetsRepo) getTopStatefulSetGroups(ctx context.Context, orgID va
|
||||
topStatefulSetGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "getTopStatefulSetGroups",
|
||||
})
|
||||
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, topStatefulSetGroupsQueryRangeParams)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -361,10 +355,6 @@ func (d *StatefulSetsRepo) GetStatefulSetList(ctx context.Context, orgID valuer.
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "inframetrics",
|
||||
instrumentation.CodeFunctionName: "GetStatefulSetList",
|
||||
})
|
||||
queryResponse, _, err := d.querierV2.QueryRange(ctx, orgID, query)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
||||
@@ -13,12 +13,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/contextlinks"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/transition"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/ruletypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
@@ -435,10 +433,7 @@ func (r *ThresholdRule) buildAndRunQuery(ctx context.Context, orgID valuer.UUID,
|
||||
|
||||
var results []*v3.Result
|
||||
var queryErrors map[string]error
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "rules",
|
||||
instrumentation.CodeFunctionName: "buildAndRunQuery",
|
||||
})
|
||||
|
||||
if r.version == "v4" {
|
||||
results, queryErrors, err = r.querierV2.QueryRange(ctx, orgID, params)
|
||||
} else {
|
||||
|
||||
@@ -176,10 +176,9 @@ func NewSQLMigrationProviderFactories(
|
||||
func NewTelemetryStoreProviderFactories() factory.NamedMap[factory.ProviderFactory[telemetrystore.TelemetryStore, telemetrystore.Config]] {
|
||||
return factory.MustNewNamedMap(
|
||||
clickhousetelemetrystore.NewFactory(
|
||||
telemetrystorehook.NewLoggingFactory(),
|
||||
// adding instrumentation factory before settings as we are starting the query span here
|
||||
telemetrystorehook.NewInstrumentationFactory(),
|
||||
telemetrystorehook.NewSettingsFactory(),
|
||||
telemetrystorehook.NewLoggingFactory(),
|
||||
telemetrystorehook.NewInstrumentationFactory(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,14 +8,12 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/analytics"
|
||||
"github.com/SigNoz/signoz/pkg/analytics/segmentanalytics"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||
"github.com/SigNoz/signoz/pkg/modules/user"
|
||||
"github.com/SigNoz/signoz/pkg/statsreporter"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/tokenizer"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/SigNoz/signoz/pkg/version"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
@@ -203,10 +201,6 @@ func (provider *provider) Stop(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (provider *provider) collectOrg(ctx context.Context, orgID valuer.UUID) map[string]any {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.CodeNamespace: "statsreporter",
|
||||
instrumentation.CodeFunctionName: "collectOrg",
|
||||
})
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(provider.collectors))
|
||||
|
||||
|
||||
@@ -11,10 +11,8 @@ import (
|
||||
schemamigrator "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator"
|
||||
"github.com/SigNoz/signoz-otel-collector/constants"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrylogs"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
@@ -49,11 +47,6 @@ var (
|
||||
// searchOperator: LIKE for pattern matching, EQUAL for exact match
|
||||
func (t *telemetryMetaStore) fetchBodyJSONPaths(ctx context.Context,
|
||||
fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, []string, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "fetchBodyJSONPaths",
|
||||
})
|
||||
query, args, limit := buildGetBodyJSONPathsQuery(fieldKeySelectors)
|
||||
rows, err := t.telemetrystore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
@@ -274,7 +267,6 @@ func buildListLogsJSONIndexesQuery(cluster string, filters ...string) (string, [
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) ListLogsJSONIndexes(ctx context.Context, filters ...string) (map[string][]schemamigrator.Index, error) {
|
||||
ctx = withTelemetryContext(ctx, "ListLogsJSONIndexes")
|
||||
query, args := buildListLogsJSONIndexesQuery(t.telemetrystore.Cluster(), filters...)
|
||||
rows, err := t.telemetrystore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
@@ -304,7 +296,6 @@ func (t *telemetryMetaStore) ListLogsJSONIndexes(ctx context.Context, filters ..
|
||||
|
||||
// TODO(Piyush): Remove this if not used in future
|
||||
func (t *telemetryMetaStore) ListJSONValues(ctx context.Context, path string, limit int) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
ctx = withTelemetryContext(ctx, "ListJSONValues")
|
||||
path = CleanPathPrefixes(path)
|
||||
|
||||
if strings.Contains(path, telemetrytypes.ArraySep) || strings.Contains(path, telemetrytypes.ArrayAnyIndex) {
|
||||
@@ -467,7 +458,6 @@ func derefValue(v any) any {
|
||||
|
||||
// IsPathPromoted checks if a specific path is promoted (Column Evolution table: field_name for logs body).
|
||||
func (t *telemetryMetaStore) IsPathPromoted(ctx context.Context, path string) (bool, error) {
|
||||
ctx = withTelemetryContext(ctx, "IsPathPromoted")
|
||||
split := strings.Split(path, telemetrytypes.ArraySep)
|
||||
pathSegment := split[0]
|
||||
query := fmt.Sprintf("SELECT 1 FROM %s.%s WHERE signal = ? AND column_name = ? AND field_context = ? AND field_name = ? LIMIT 1", DBName, PromotedPathsTableName)
|
||||
@@ -482,7 +472,6 @@ func (t *telemetryMetaStore) IsPathPromoted(ctx context.Context, path string) (b
|
||||
|
||||
// GetPromotedPaths returns promoted paths from the Column Evolution table (field_name for logs body).
|
||||
func (t *telemetryMetaStore) GetPromotedPaths(ctx context.Context, paths ...string) (map[string]bool, error) {
|
||||
ctx = withTelemetryContext(ctx, "GetPromotedPaths")
|
||||
sb := sqlbuilder.Select("field_name").From(fmt.Sprintf("%s.%s", DBName, PromotedPathsTableName))
|
||||
conditions := []string{
|
||||
sb.Equal("signal", telemetrytypes.SignalLogs),
|
||||
@@ -529,7 +518,6 @@ func CleanPathPrefixes(path string) string {
|
||||
|
||||
// PromotePaths inserts promoted paths into the Column Evolution table (same schema as signoz-otel-collector metadata_migrations).
|
||||
func (t *telemetryMetaStore) PromotePaths(ctx context.Context, paths ...string) error {
|
||||
ctx = withTelemetryContext(ctx, "PromotePaths")
|
||||
batch, err := t.telemetrystore.ClickhouseDB().PrepareBatch(ctx,
|
||||
fmt.Sprintf("INSERT INTO %s.%s (signal, column_name, column_type, field_context, field_name, version, release_time) VALUES", DBName,
|
||||
PromotedPathsTableName))
|
||||
@@ -554,11 +542,3 @@ func (t *telemetryMetaStore) PromotePaths(ctx context.Context, paths ...string)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func withTelemetryContext(ctx context.Context, functionName string) context.Context {
|
||||
return ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: functionName,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,13 +8,11 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrylogs"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymetrics"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrytraces"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/metrictypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
@@ -120,11 +118,6 @@ func NewTelemetryMetaStore(
|
||||
|
||||
// tracesTblStatementToFieldKeys returns materialised attribute/resource/scope keys from the traces table
|
||||
func (t *telemetryMetaStore) tracesTblStatementToFieldKeys(ctx context.Context) ([]*telemetrytypes.TelemetryFieldKey, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "tracesTblStatementToFieldKeys",
|
||||
})
|
||||
query := fmt.Sprintf("SHOW CREATE TABLE %s.%s", t.tracesDBName, t.indexV3TblName)
|
||||
statements := []telemetrytypes.ShowCreateTableStatement{}
|
||||
err := t.telemetrystore.ClickhouseDB().Select(ctx, &statements, query)
|
||||
@@ -146,12 +139,6 @@ func (t *telemetryMetaStore) tracesTblStatementToFieldKeys(ctx context.Context)
|
||||
|
||||
// getTracesKeys returns the keys from the spans that match the field selection criteria
|
||||
func (t *telemetryMetaStore) getTracesKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "getTracesKeys",
|
||||
})
|
||||
|
||||
if len(fieldKeySelectors) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
@@ -326,11 +313,6 @@ func (t *telemetryMetaStore) getTracesKeys(ctx context.Context, fieldKeySelector
|
||||
|
||||
// logsTblStatementToFieldKeys returns materialised attribute/resource/scope keys from the logs table
|
||||
func (t *telemetryMetaStore) logsTblStatementToFieldKeys(ctx context.Context) ([]*telemetrytypes.TelemetryFieldKey, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "logsTblStatementToFieldKeys",
|
||||
})
|
||||
query := fmt.Sprintf("SHOW CREATE TABLE %s.%s", t.logsDBName, t.logsV2TblName)
|
||||
statements := []telemetrytypes.ShowCreateTableStatement{}
|
||||
err := t.telemetrystore.ClickhouseDB().Select(ctx, &statements, query)
|
||||
@@ -352,12 +334,6 @@ func (t *telemetryMetaStore) logsTblStatementToFieldKeys(ctx context.Context) ([
|
||||
|
||||
// getLogsKeys returns the keys from the spans that match the field selection criteria
|
||||
func (t *telemetryMetaStore) getLogsKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "getLogsKeys",
|
||||
})
|
||||
|
||||
if len(fieldKeySelectors) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
@@ -607,11 +583,6 @@ func getPriorityForContext(ctx telemetrytypes.FieldContext) int {
|
||||
|
||||
// getMetricsKeys returns the keys from the metrics that match the field selection criteria
|
||||
func (t *telemetryMetaStore) getMetricsKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "getMetricsKeys",
|
||||
})
|
||||
if len(fieldKeySelectors) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
@@ -714,12 +685,6 @@ func (t *telemetryMetaStore) getMetricsKeys(ctx context.Context, fieldKeySelecto
|
||||
|
||||
// getMeterKeys returns the keys from the meter metrics that match the field selection criteria
|
||||
func (t *telemetryMetaStore) getMeterSourceMetricKeys(ctx context.Context, fieldKeySelectors []*telemetrytypes.FieldKeySelector) ([]*telemetrytypes.TelemetryFieldKey, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "getMeterSourceMetricKeys",
|
||||
})
|
||||
|
||||
if len(fieldKeySelectors) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
@@ -1008,11 +973,6 @@ func (t *telemetryMetaStore) GetKey(ctx context.Context, fieldKeySelector *telem
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) getRelatedValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) ([]string, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: fieldValueSelector.Signal.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "getRelatedValues",
|
||||
})
|
||||
|
||||
// nothing to return as "related" value if there is nothing to filter on
|
||||
if fieldValueSelector.ExistingQuery == "" {
|
||||
@@ -1158,11 +1118,6 @@ func (t *telemetryMetaStore) GetRelatedValues(ctx context.Context, fieldValueSel
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) getSpanFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "getSpanFieldValues",
|
||||
})
|
||||
// build the query to get the keys from the spans that match the field selection criteria
|
||||
limit := fieldValueSelector.Limit
|
||||
if limit == 0 {
|
||||
@@ -1247,11 +1202,6 @@ func (t *telemetryMetaStore) getSpanFieldValues(ctx context.Context, fieldValueS
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) getLogFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalLogs.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "getLogFieldValues",
|
||||
})
|
||||
// build the query to get the keys from the spans that match the field selection criteria
|
||||
limit := fieldValueSelector.Limit
|
||||
if limit == 0 {
|
||||
@@ -1335,11 +1285,6 @@ func (t *telemetryMetaStore) getLogFieldValues(ctx context.Context, fieldValueSe
|
||||
|
||||
// getMetricFieldValues returns field values and whether the result is complete
|
||||
func (t *telemetryMetaStore) getMetricFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "getMetricFieldValues",
|
||||
})
|
||||
limit := fieldValueSelector.Limit
|
||||
if limit == 0 {
|
||||
limit = 50
|
||||
@@ -1428,11 +1373,6 @@ func (t *telemetryMetaStore) getMetricFieldValues(ctx context.Context, fieldValu
|
||||
|
||||
// getIntrinsicMetricFieldValues returns values, isSearchComplete, error
|
||||
func (t *telemetryMetaStore) getIntrinsicMetricFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector, limit int) (*telemetrytypes.TelemetryFieldValues, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "getIntrinsicMetricFieldValues",
|
||||
})
|
||||
key, ok := telemetrymetrics.IntrinsicMetricFieldDefinitions[fieldValueSelector.Name]
|
||||
if !ok {
|
||||
return &telemetrytypes.TelemetryFieldValues{}, nil
|
||||
@@ -1506,11 +1446,6 @@ func (t *telemetryMetaStore) getIntrinsicMetricFieldValues(ctx context.Context,
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) getMeterSourceMetricFieldValues(ctx context.Context, fieldValueSelector *telemetrytypes.FieldValueSelector) (*telemetrytypes.TelemetryFieldValues, bool, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "getMeterSourceMetricFieldValues",
|
||||
})
|
||||
sb := sqlbuilder.Select("DISTINCT arrayJoin(JSONExtractKeysAndValues(labels, 'String')) AS attr").
|
||||
From(t.meterDBName + "." + t.meterFieldsTblName)
|
||||
|
||||
@@ -1727,11 +1662,6 @@ func (t *telemetryMetaStore) FetchTemporalityAndTypeMulti(ctx context.Context, q
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) fetchMetricsTemporalityAndType(ctx context.Context, queryTimeRangeStartTs, queryTimeRangeEndTs uint64, metricNames ...string) (map[string][]metrictypes.Temporality, map[string]metrictypes.Type, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "fetchMetricsTemporalityAndType",
|
||||
})
|
||||
temporalities := make(map[string][]metrictypes.Temporality)
|
||||
types := make(map[string]metrictypes.Type)
|
||||
|
||||
@@ -1793,11 +1723,6 @@ func (t *telemetryMetaStore) fetchMetricsTemporalityAndType(ctx context.Context,
|
||||
}
|
||||
|
||||
func (t *telemetryMetaStore) fetchMeterSourceMetricsTemporalityAndType(ctx context.Context, metricNames ...string) (map[string]metrictypes.Temporality, map[string]metrictypes.Type, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "fetchMeterSourceMetricsTemporalityAndType",
|
||||
})
|
||||
temporalities := make(map[string]metrictypes.Temporality)
|
||||
types := make(map[string]metrictypes.Type)
|
||||
|
||||
@@ -1864,11 +1789,6 @@ const chunkSizeFirstSeenMetricMetadata = 1600
|
||||
// for each metric-attribute-value combination.
|
||||
// Returns a map where key is `telemetrytypes.MetricMetadataLookupKey` and value is first_seen in milliseconds.
|
||||
func (t *telemetryMetaStore) GetFirstSeenFromMetricMetadata(ctx context.Context, lookupKeys []telemetrytypes.MetricMetadataLookupKey) (map[telemetrytypes.MetricMetadataLookupKey]int64, error) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentation.CodeNamespace: "metadata",
|
||||
instrumentation.CodeFunctionName: "GetFirstSeenFromMetricMetadata",
|
||||
})
|
||||
result := make(map[telemetrytypes.MetricMetadataLookupKey]int64)
|
||||
|
||||
for i := 0; i < len(lookupKeys); i += chunkSizeFirstSeenMetricMetadata {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
@@ -36,14 +35,7 @@ func NewInstrumentation(ctx context.Context, providerSettings factory.ProviderSe
|
||||
}
|
||||
|
||||
func (hook *instrumentation) BeforeQuery(ctx context.Context, event *telemetrystore.QueryEvent) context.Context {
|
||||
ctx, span := hook.tracer.Start(ctx, "", trace.WithSpanKind(trace.SpanKindClient))
|
||||
|
||||
// add trace_id and span_id to the log_comment
|
||||
comment := ctxtypes.CommentFromContext(ctx)
|
||||
comment.Set("trace_id", span.SpanContext().TraceID().String())
|
||||
comment.Set("span_id", span.SpanContext().SpanID().String())
|
||||
ctx = ctxtypes.NewContextWithComment(ctx, comment)
|
||||
|
||||
ctx, _ = hook.tracer.Start(ctx, "", trace.WithSpanKind(trace.SpanKindClient))
|
||||
return ctx
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
type TraceTimeRangeFinder struct {
|
||||
@@ -27,11 +24,6 @@ func (f *TraceTimeRangeFinder) GetTraceTimeRange(ctx context.Context, traceID st
|
||||
}
|
||||
|
||||
func (f *TraceTimeRangeFinder) GetTraceTimeRangeMulti(ctx context.Context, traceIDs []string) (startNano, endNano int64, ok bool) {
|
||||
ctx = ctxtypes.AddCommentsToContext(ctx, map[string]string{
|
||||
instrumentation.TelemetrySignal: telemetrytypes.SignalTraces.StringValue(),
|
||||
instrumentation.CodeNamespace: "trace-time-range",
|
||||
instrumentation.CodeFunctionName: "GetTraceTimeRangeMulti",
|
||||
})
|
||||
if len(traceIDs) == 0 {
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
@@ -104,21 +104,6 @@ func CommentFromHTTPRequest(req *http.Request) map[string]string {
|
||||
return comments
|
||||
}
|
||||
|
||||
// AddCommentsToContext returns a new context with all key-value pairs from comments merged into the Comment in the context.
|
||||
func AddCommentsToContext(ctx context.Context, comments map[string]string) context.Context {
|
||||
if len(comments) == 0 {
|
||||
return ctx
|
||||
}
|
||||
comment := CommentFromContext(ctx)
|
||||
if comment == nil {
|
||||
comment = NewComment()
|
||||
}
|
||||
for k, v := range comments {
|
||||
comment.Set(k, v)
|
||||
}
|
||||
return NewContextWithComment(ctx, comment)
|
||||
}
|
||||
|
||||
// NewComment creates a new Comment with an empty map. It is safe to use concurrently.
|
||||
func NewComment() *Comment {
|
||||
return &Comment{vals: map[string]string{}}
|
||||
|
||||
Reference in New Issue
Block a user