Compare commits

...

3 Commits

Author SHA1 Message Date
nityanandagohain
9499228398 feat: module and store for span mapper 2026-04-28 16:08:44 +05:30
Prakhar Dewan
d1c9864f52 chore: remove unused files (#11033)
Some checks failed
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
build-staging / staging (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
* chore: remove unused files

* chore: add knip.json file
2026-04-27 19:23:12 +00:00
Manika Malhotra
f80b650390 chore(onboarding): add open-source tooling to interest in signoz option (#11083) 2026-04-27 14:55:09 +00:00
31 changed files with 574 additions and 410 deletions

View File

@@ -4500,6 +4500,7 @@ components:
enum:
- attribute
- resource
nullable: true
type: string
SpantypesGettableSpanMapperGroups:
properties:
@@ -4527,8 +4528,6 @@ components:
type: object
SpantypesPostableSpanMapperGroup:
properties:
category:
$ref: '#/components/schemas/SpantypesSpanMapperGroupCategory'
condition:
$ref: '#/components/schemas/SpantypesSpanMapperGroupCondition'
enabled:
@@ -4537,7 +4536,6 @@ components:
type: string
required:
- name
- category
- condition
type: object
SpantypesSpanMapper:
@@ -4573,6 +4571,7 @@ components:
- enabled
type: object
SpantypesSpanMapperConfig:
nullable: true
properties:
sources:
items:
@@ -4584,8 +4583,6 @@ components:
type: object
SpantypesSpanMapperGroup:
properties:
category:
$ref: '#/components/schemas/SpantypesSpanMapperGroupCategory'
condition:
$ref: '#/components/schemas/SpantypesSpanMapperGroupCondition'
createdAt:
@@ -4610,13 +4607,11 @@ components:
- id
- orgId
- name
- category
- condition
- enabled
type: object
SpantypesSpanMapperGroupCategory:
type: object
SpantypesSpanMapperGroupCondition:
nullable: true
properties:
attributes:
items:
@@ -9423,12 +9418,6 @@ paths:
org.
operationId: ListSpanMapperGroups
parameters:
- explode: true
in: query
name: category
schema:
$ref: '#/components/schemas/SpantypesSpanMapperGroupCategory'
style: deepObject
- in: query
name: enabled
schema:

5
frontend/knip.json Normal file
View File

@@ -0,0 +1,5 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"project": ["src/**/*.ts", "src/**/*.tsx"],
"ignore": ["src/api/generated/**/*.ts"]
}

View File

@@ -5472,9 +5472,13 @@ export interface Sigv4SigV4ConfigDTO {
[key: string]: unknown;
}
/**
* @nullable
*/
export enum SpantypesFieldContextDTO {
attribute = 'attribute',
resource = 'resource',
null = null,
}
export interface SpantypesGettableSpanMapperGroupsDTO {
/**
@@ -5497,7 +5501,6 @@ export interface SpantypesPostableSpanMapperDTO {
}
export interface SpantypesPostableSpanMapperGroupDTO {
category: SpantypesSpanMapperGroupCategoryDTO;
condition: SpantypesSpanMapperGroupConditionDTO;
/**
* @type boolean
@@ -5548,16 +5551,18 @@ export interface SpantypesSpanMapperDTO {
updatedBy?: string;
}
export interface SpantypesSpanMapperConfigDTO {
/**
* @nullable
*/
export type SpantypesSpanMapperConfigDTO = {
/**
* @type array
* @nullable true
*/
sources: SpantypesSpanMapperSourceDTO[] | null;
}
} | null;
export interface SpantypesSpanMapperGroupDTO {
category: SpantypesSpanMapperGroupCategoryDTO;
condition: SpantypesSpanMapperGroupConditionDTO;
/**
* @type string
@@ -5595,11 +5600,10 @@ export interface SpantypesSpanMapperGroupDTO {
updatedBy?: string;
}
export interface SpantypesSpanMapperGroupCategoryDTO {
[key: string]: unknown;
}
export interface SpantypesSpanMapperGroupConditionDTO {
/**
* @nullable
*/
export type SpantypesSpanMapperGroupConditionDTO = {
/**
* @type array
* @nullable true
@@ -5610,7 +5614,7 @@ export interface SpantypesSpanMapperGroupConditionDTO {
* @nullable true
*/
resource: string[] | null;
}
} | null;
export enum SpantypesSpanMapperOperationDTO {
move = 'move',
@@ -7104,10 +7108,6 @@ export type GetMyServiceAccount200 = {
};
export type ListSpanMapperGroupsParams = {
/**
* @description undefined
*/
category?: SpantypesSpanMapperGroupCategoryDTO;
/**
* @type boolean
* @nullable true

View File

@@ -27,6 +27,7 @@ const interestedInOptions: Record<string, string> = {
singleTool:
'Single Tool (logs, metrics & traces) to reduce operational overhead',
correlateSignals: 'Correlate signals for faster troubleshooting',
openSourceTooling: 'Prefer open-source tooling',
};
export function AboutSigNozQuestions({

View File

@@ -1,22 +0,0 @@
import ROUTES from 'constants/routes';
import CreateAlertChannels from 'container/CreateAlertChannels';
import { ChannelType } from 'container/CreateAlertChannels/config';
import GeneralSettings from 'container/GeneralSettings';
import { t } from 'i18next';
export const alertsRoutesConfig = [
{
Component: GeneralSettings,
name: t('routes.general'),
route: ROUTES.SETTINGS,
key: ROUTES.SETTINGS,
},
{
Component: (): JSX.Element => (
<CreateAlertChannels preType={ChannelType.Slack} />
),
name: t('routes.alert_channels'),
route: ROUTES.CHANNELS_NEW,
key: ROUTES.CHANNELS_NEW,
},
];

View File

@@ -1,19 +0,0 @@
import { useLocation } from 'react-router-dom';
import RouteTab from 'components/RouteTab';
import history from 'lib/history';
import { alertsRoutesConfig } from './config';
function SettingsPage(): JSX.Element {
const { pathname } = useLocation();
return (
<RouteTab
history={history}
routes={alertsRoutesConfig}
activeKey={pathname}
/>
);
}
export default SettingsPage;

View File

@@ -1,54 +0,0 @@
import { useMemo } from 'react';
import { Color } from '@signozhq/design-tokens';
import { CircleCheck, Siren } from 'lucide-react';
import { getDurationFromNow } from 'utils/timeUtils';
import { AlertStatusProps, StatusConfig } from './types';
import './AlertStatus.styles.scss';
export default function AlertStatus({
status,
timestamp,
}: AlertStatusProps): JSX.Element {
const statusConfig: StatusConfig = useMemo(
() => ({
firing: {
icon: <Siren size={14} color={Color.TEXT_VANILLA_400} />,
text: 'Firing since',
extraInfo: timestamp ? (
<>
<div></div>
<div className="time">{getDurationFromNow(timestamp)}</div>
</>
) : null,
className: 'alert-status-info--firing',
},
resolved: {
icon: (
<CircleCheck
size={14}
fill={Color.BG_VANILLA_400}
color={Color.BG_INK_400}
/>
),
text: 'Resolved',
extraInfo: null,
className: 'alert-status-info--resolved',
},
}),
[timestamp],
);
const currentStatus = statusConfig[status];
return (
<div className={`alert-status-info ${currentStatus.className}`}>
<div className="alert-status-info__icon">{currentStatus.icon}</div>
<div className="alert-status-info__details">
<div className="text">{currentStatus.text}</div>
{currentStatus.extraInfo}
</div>
</div>
);
}

View File

@@ -1,18 +0,0 @@
export type AlertStatusProps =
| { status: 'firing'; timestamp: number }
| { status: 'resolved'; timestamp?: number };
export type StatusConfig = {
firing: {
icon: JSX.Element;
text: string;
extraInfo: JSX.Element | null;
className: string;
};
resolved: {
icon: JSX.Element;
text: string;
extraInfo: JSX.Element | null;
className: string;
};
};

View File

@@ -1,3 +0,0 @@
import AlertHistory from 'container/AlertHistory';
export default AlertHistory;

View File

@@ -1,86 +0,0 @@
import {
FiltersType,
IQuickFiltersConfig,
} from 'components/QuickFilters/types';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder';
export const ExceptionsQuickFiltersConfig: IQuickFiltersConfig[] = [
{
type: FiltersType.CHECKBOX,
title: 'Environment',
dataSource: DataSource.TRACES,
attributeKey: {
key: 'deployment.environment',
dataType: DataTypes.String,
type: 'resource',
},
defaultOpen: true,
},
{
type: FiltersType.CHECKBOX,
title: 'Service Name',
dataSource: DataSource.TRACES,
attributeKey: {
key: 'service.name',
dataType: DataTypes.String,
type: 'resource',
},
defaultOpen: false,
},
{
type: FiltersType.CHECKBOX,
title: 'Hostname',
dataSource: DataSource.TRACES,
attributeKey: {
key: 'host.name',
dataType: DataTypes.String,
type: 'resource',
},
defaultOpen: false,
},
{
type: FiltersType.CHECKBOX,
title: 'K8s Cluster Name',
dataSource: DataSource.TRACES,
attributeKey: {
key: 'k8s.cluster.name',
dataType: DataTypes.String,
type: 'resource',
},
defaultOpen: false,
},
{
type: FiltersType.CHECKBOX,
title: 'K8s Deployment Name',
dataSource: DataSource.TRACES,
attributeKey: {
key: 'k8s.deployment.name',
dataType: DataTypes.String,
type: 'resource',
},
defaultOpen: false,
},
{
type: FiltersType.CHECKBOX,
title: 'K8s Namespace Name',
dataSource: DataSource.TRACES,
attributeKey: {
key: 'k8s.namespace.name',
dataType: DataTypes.String,
type: 'resource',
},
defaultOpen: false,
},
{
type: FiltersType.CHECKBOX,
title: 'K8s Pod Name',
dataSource: DataSource.TRACES,
attributeKey: {
key: 'k8s.pod.name',
dataType: DataTypes.String,
type: 'resource',
},
defaultOpen: false,
},
];

View File

@@ -1,13 +0,0 @@
import BillingContainer from 'container/BillingContainer/BillingContainer';
import './BillingPage.styles.scss';
function BillingPage(): JSX.Element {
return (
<div className="billingPageContainer">
<BillingContainer />
</div>
);
}
export default BillingPage;

View File

@@ -1,3 +0,0 @@
import BillingPage from './BillingPage';
export default BillingPage;

View File

@@ -1,18 +0,0 @@
import { Typography } from 'antd';
import styled from 'styled-components';
export const Title = styled(Typography)`
&&& {
margin-top: 1rem;
margin-bottom: 1rem;
}
`;
export const ButtonContainer = styled.div`
&&& {
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 1rem;
}
`;

View File

@@ -1,3 +0,0 @@
import HomePage from './HomePage';
export default HomePage;

View File

@@ -1,14 +0,0 @@
import { Col } from 'antd';
import { themeColors } from 'constants/theme';
import styled from 'styled-components';
export const WrapperStyled = styled.div`
display: flex;
flex-direction: column;
flex: 1;
color: ${themeColors.lightWhite};
`;
export const ButtonWrapperStyled = styled(Col)`
margin-left: auto;
`;

View File

@@ -1,7 +0,0 @@
export const removeSourcePageFromPath = (path: string): string => {
const lastSlashIndex = path.lastIndexOf('/');
if (lastSlashIndex !== -1) {
return path.substring(0, lastSlashIndex);
}
return path;
};

View File

@@ -1,6 +0,0 @@
import MySettingsContainer from 'container/MySettings';
function MySettings(): JSX.Element {
return <MySettingsContainer />;
}
export default MySettings;

View File

@@ -1,25 +0,0 @@
import { Button, Typography } from 'antd';
import SomethingWentWrongAsset from 'assets/SomethingWentWrong';
import { Container } from 'components/NotFound/styles';
import ROUTES from 'constants/routes';
import history from 'lib/history';
function SomethingWentWrong(): JSX.Element {
return (
<Container>
<SomethingWentWrongAsset />
<Typography.Title level={3}>Oops! Something went wrong</Typography.Title>
<Button
type="primary"
onClick={(): void => {
history.push(ROUTES.HOME);
}}
className="periscope-btn primary"
>
Return to Home
</Button>
</Container>
);
}
export default SomethingWentWrong;

View File

@@ -1,10 +0,0 @@
import styled from 'styled-components';
export const Container = styled.div`
margin: 1rem 0;
`;
export const ActionsWrapper = styled.div`
display: flex;
justify-content: flex-end;
`;

View File

@@ -72,10 +72,9 @@ func (h *handler) CreateGroup(rw http.ResponseWriter, r *http.Request) {
return
}
group := spantypes.NewSpanMapperGroupFromPostable(req)
group := spantypes.NewSpanMapperGroup(orgID, claims.Email, req)
err = h.module.CreateGroup(ctx, orgID, claims.Email, group)
if err != nil {
if err := h.module.CreateGroup(ctx, orgID, group); err != nil {
render.Error(rw, err)
return
}
@@ -107,11 +106,17 @@ func (h *handler) UpdateGroup(rw http.ResponseWriter, r *http.Request) {
return
}
err = h.module.UpdateGroup(ctx, orgID, id, claims.Email, spantypes.NewSpanMapperGroupFromUpdatable(req))
group, err := h.module.GetGroup(ctx, orgID, id)
if err != nil {
render.Error(rw, err)
return
}
group.Update(req, claims.Email)
if err := h.module.UpdateGroup(ctx, orgID, id, group); err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusNoContent, nil)
}
@@ -195,10 +200,9 @@ func (h *handler) CreateMapper(rw http.ResponseWriter, r *http.Request) {
render.Error(rw, err)
return
}
mapper := spantypes.NewSpanMapperFromPostable(req)
mapper := spantypes.NewSpanMapper(groupID, claims.Email, req)
err = h.module.CreateMapper(ctx, orgID, groupID, claims.Email, mapper)
if err != nil {
if err := h.module.CreateMapper(ctx, orgID, groupID, mapper); err != nil {
render.Error(rw, err)
return
}
@@ -237,11 +241,17 @@ func (h *handler) UpdateMapper(rw http.ResponseWriter, r *http.Request) {
return
}
err = h.module.UpdateMapper(ctx, orgID, groupID, mapperID, claims.Email, spantypes.NewSpanMapperFromUpdatable(req))
mapper, err := h.module.GetMapper(ctx, orgID, groupID, mapperID)
if err != nil {
render.Error(rw, err)
return
}
mapper.Update(req, claims.Email)
if err := h.module.UpdateMapper(ctx, orgID, groupID, mapperID, mapper); err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusNoContent, nil)
}

View File

@@ -0,0 +1,64 @@
package implspanmapper
import (
"context"
"github.com/SigNoz/signoz/pkg/modules/spanmapper"
"github.com/SigNoz/signoz/pkg/types/spantypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
type module struct {
store spantypes.Store
}
func NewModule(store spantypes.Store) spanmapper.Module {
return &module{store: store}
}
func (module *module) ListGroups(ctx context.Context, orgID valuer.UUID, q *spantypes.ListSpanMapperGroupsQuery) ([]*spantypes.SpanMapperGroup, error) {
return module.store.ListSpanMapperGroups(ctx, orgID, q)
}
func (module *module) GetGroup(ctx context.Context, orgID, id valuer.UUID) (*spantypes.SpanMapperGroup, error) {
return module.store.GetSpanMapperGroup(ctx, orgID, id)
}
func (module *module) CreateGroup(ctx context.Context, orgID valuer.UUID, group *spantypes.SpanMapperGroup) error {
return module.store.CreateSpanMapperGroup(ctx, group)
}
func (module *module) UpdateGroup(ctx context.Context, orgID, id valuer.UUID, group *spantypes.SpanMapperGroup) error {
return module.store.UpdateSpanMapperGroup(ctx, group)
}
func (module *module) DeleteGroup(ctx context.Context, orgID, id valuer.UUID) error {
return module.store.DeleteSpanMapperGroup(ctx, orgID, id)
}
func (module *module) ListMappers(ctx context.Context, orgID, groupID valuer.UUID) ([]*spantypes.SpanMapper, error) {
return module.store.ListSpanMappers(ctx, orgID, groupID)
}
func (module *module) GetMapper(ctx context.Context, orgID, groupID, id valuer.UUID) (*spantypes.SpanMapper, error) {
return module.store.GetSpanMapper(ctx, orgID, groupID, id)
}
func (module *module) CreateMapper(ctx context.Context, orgID, groupID valuer.UUID, mapper *spantypes.SpanMapper) error {
// Ensure the group belongs to the org before inserting the child row.
if _, err := module.store.GetSpanMapperGroup(ctx, orgID, groupID); err != nil {
return err
}
return module.store.CreateSpanMapper(ctx, mapper)
}
func (module *module) UpdateMapper(ctx context.Context, orgID, groupID, id valuer.UUID, mapper *spantypes.SpanMapper) error {
if _, err := module.store.GetSpanMapperGroup(ctx, orgID, groupID); err != nil {
return err
}
return module.store.UpdateSpanMapper(ctx, mapper)
}
func (module *module) DeleteMapper(ctx context.Context, orgID, groupID, id valuer.UUID) error {
return module.store.DeleteSpanMapper(ctx, orgID, groupID, id)
}

View File

@@ -0,0 +1,231 @@
package implspanmapper
import (
"context"
"database/sql"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types/spantypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
type store struct {
sqlstore sqlstore.SQLStore
}
func NewStore(sqlstore sqlstore.SQLStore) spantypes.Store {
return &store{sqlstore: sqlstore}
}
func (s *store) CreateSpanMapperGroup(ctx context.Context, group *spantypes.SpanMapperGroup) error {
storable := spantypes.NewStorableSpanMapperGroupFromGroup(group)
_, err := s.sqlstore.
BunDBCtx(ctx).
NewInsert().
Model(storable).
Exec(ctx)
if err != nil {
return s.sqlstore.WrapAlreadyExistsErrf(err, spantypes.ErrCodeMappingGroupAlreadyExists, "span mapper group %q already exists", group.Name)
}
return nil
}
func (s *store) GetSpanMapperGroup(ctx context.Context, orgID, id valuer.UUID) (*spantypes.SpanMapperGroup, error) {
storable := new(spantypes.StorableSpanMapperGroup)
err := s.sqlstore.
BunDB().
NewSelect().
Model(storable).
Where("org_id = ?", orgID).
Where("id = ?", id).
Scan(ctx)
if err != nil {
return nil, s.sqlstore.WrapNotFoundErrf(err, spantypes.ErrCodeMappingGroupNotFound, "span mapper group %s not found", id)
}
return spantypes.NewSpanMapperGroupFromStorable(storable), nil
}
func (s *store) ListSpanMapperGroups(ctx context.Context, orgID valuer.UUID, q *spantypes.ListSpanMapperGroupsQuery) ([]*spantypes.SpanMapperGroup, error) {
storables := make([]*spantypes.StorableSpanMapperGroup, 0)
sel := s.sqlstore.
BunDB().
NewSelect().
Model(&storables).
Where("org_id = ?", orgID)
if q != nil {
if q.Enabled != nil {
sel = sel.Where("enabled = ?", *q.Enabled)
}
}
if err := sel.Order("created_at DESC").Scan(ctx); err != nil {
return nil, err
}
return spantypes.NewSpanMapperGroupsFromStorableGroups(storables), nil
}
func (s *store) UpdateSpanMapperGroup(ctx context.Context, group *spantypes.SpanMapperGroup) error {
storable := spantypes.NewStorableSpanMapperGroupFromGroup(group)
res, err := s.sqlstore.
BunDBCtx(ctx).
NewUpdate().
Model(storable).
Where("org_id = ?", storable.OrgID).
Where("id = ?", storable.ID).
Exec(ctx)
if err != nil {
return err
}
rowsAffected, err := res.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return errors.Newf(errors.TypeNotFound, spantypes.ErrCodeMappingGroupNotFound, "span mapper group %s not found", group.ID)
}
return nil
}
func (s *store) DeleteSpanMapperGroup(ctx context.Context, orgID, id valuer.UUID) error {
tx, err := s.sqlstore.BunDBCtx(ctx).BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() { _ = tx.Rollback() }()
// Cascade: remove mappers belonging to this group first.
if _, err := tx.NewDelete().
Model((*spantypes.StorableSpanMapper)(nil)).
Where("group_id = ?", id).
Exec(ctx); err != nil {
return err
}
res, err := tx.NewDelete().
Model((*spantypes.StorableSpanMapperGroup)(nil)).
Where("org_id = ?", orgID).
Where("id = ?", id).
Exec(ctx)
if err != nil {
return err
}
rowsAffected, err := res.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return errors.Newf(errors.TypeNotFound, spantypes.ErrCodeMappingGroupNotFound, "span mapper group %s not found", id)
}
return tx.Commit()
}
func (s *store) CreateSpanMapper(ctx context.Context, mapper *spantypes.SpanMapper) error {
storable := spantypes.NewStorableSpanMapperFromMapper(mapper)
_, err := s.sqlstore.
BunDBCtx(ctx).
NewInsert().
Model(storable).
Exec(ctx)
if err != nil {
return s.sqlstore.WrapAlreadyExistsErrf(err, spantypes.ErrCodeMapperAlreadyExists, "span mapper %q already exists", mapper.Name)
}
return nil
}
func (s *store) GetSpanMapper(ctx context.Context, orgID, groupID, id valuer.UUID) (*spantypes.SpanMapper, error) {
// Ensure the group belongs to the org.
if _, err := s.GetSpanMapperGroup(ctx, orgID, groupID); err != nil {
return nil, err
}
storable := new(spantypes.StorableSpanMapper)
err := s.sqlstore.
BunDB().
NewSelect().
Model(storable).
Where("group_id = ?", groupID).
Where("id = ?", id).
Scan(ctx)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, s.sqlstore.WrapNotFoundErrf(err, spantypes.ErrCodeMapperNotFound, "span mapper %s not found", id)
}
return nil, err
}
return spantypes.NewSpanMapperFromStorable(storable), nil
}
func (s *store) ListSpanMappers(ctx context.Context, orgID, groupID valuer.UUID) ([]*spantypes.SpanMapper, error) {
// Scope by org via the parent group's org_id.
if _, err := s.GetSpanMapperGroup(ctx, orgID, groupID); err != nil {
return nil, err
}
storables := make([]*spantypes.StorableSpanMapper, 0)
if err := s.sqlstore.
BunDB().
NewSelect().
Model(&storables).
Where("group_id = ?", groupID).
Order("created_at DESC").
Scan(ctx); err != nil {
return nil, err
}
return spantypes.NewSpanMappersFromStorableSpanMappers(storables), nil
}
func (s *store) UpdateSpanMapper(ctx context.Context, mapper *spantypes.SpanMapper) error {
storable := spantypes.NewStorableSpanMapperFromMapper(mapper)
res, err := s.sqlstore.
BunDBCtx(ctx).
NewUpdate().
Model(storable).
Where("group_id = ?", storable.GroupID).
Where("id = ?", storable.ID).
Exec(ctx)
if err != nil {
return err
}
rowsAffected, err := res.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return errors.Newf(errors.TypeNotFound, spantypes.ErrCodeMapperNotFound, "span mapper %s not found", mapper.ID)
}
return nil
}
func (s *store) DeleteSpanMapper(ctx context.Context, orgID, groupID, id valuer.UUID) error {
if _, err := s.GetSpanMapperGroup(ctx, orgID, groupID); err != nil {
return err
}
res, err := s.sqlstore.
BunDBCtx(ctx).
NewDelete().
Model((*spantypes.StorableSpanMapper)(nil)).
Where("group_id = ?", groupID).
Where("id = ?", id).
Exec(ctx)
if err != nil {
return err
}
rowsAffected, err := res.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return errors.Newf(errors.TypeNotFound, spantypes.ErrCodeMapperNotFound, "span mapper %s not found", id)
}
return nil
}

View File

@@ -12,17 +12,17 @@ import (
type Module interface {
// Group operations
ListGroups(ctx context.Context, orgID valuer.UUID, q *spantypes.ListSpanMapperGroupsQuery) ([]*spantypes.SpanMapperGroup, error)
GetGroup(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*spantypes.SpanMapperGroup, error)
CreateGroup(ctx context.Context, orgID valuer.UUID, createdBy string, group *spantypes.SpanMapperGroup) error
UpdateGroup(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, group *spantypes.SpanMapperGroup) error
DeleteGroup(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error
GetGroup(ctx context.Context, orgID, id valuer.UUID) (*spantypes.SpanMapperGroup, error)
CreateGroup(ctx context.Context, orgID valuer.UUID, group *spantypes.SpanMapperGroup) error
UpdateGroup(ctx context.Context, orgID, id valuer.UUID, group *spantypes.SpanMapperGroup) error
DeleteGroup(ctx context.Context, orgID, id valuer.UUID) error
// Mapper operations
ListMappers(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID) ([]*spantypes.SpanMapper, error)
GetMapper(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID, id valuer.UUID) (*spantypes.SpanMapper, error)
CreateMapper(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID, createdBy string, mapper *spantypes.SpanMapper) error
UpdateMapper(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID, id valuer.UUID, updatedBy string, mapper *spantypes.SpanMapper) error
DeleteMapper(ctx context.Context, orgID valuer.UUID, groupID valuer.UUID, id valuer.UUID) error
ListMappers(ctx context.Context, orgID, groupID valuer.UUID) ([]*spantypes.SpanMapper, error)
GetMapper(ctx context.Context, orgID, groupID, id valuer.UUID) (*spantypes.SpanMapper, error)
CreateMapper(ctx context.Context, orgID, groupID valuer.UUID, mapper *spantypes.SpanMapper) error
UpdateMapper(ctx context.Context, orgID, groupID, id valuer.UUID, mapper *spantypes.SpanMapper) error
DeleteMapper(ctx context.Context, orgID, groupID, id valuer.UUID) error
}
// Handler defines the HTTP handler interface for mapping group and mapper endpoints.

View File

@@ -117,7 +117,7 @@ func NewHandlers(
RegistryHandler: registryHandler,
RuleStateHistory: implrulestatehistory.NewHandler(modules.RuleStateHistory),
CloudIntegrationHandler: implcloudintegration.NewHandler(modules.CloudIntegration),
SpanMapperHandler: implspanmapper.NewHandler(nil, providerSettings), // todo(nitya): will update this in future PR
SpanMapperHandler: implspanmapper.NewHandler(modules.SpanMapper, providerSettings),
AlertmanagerHandler: signozalertmanager.NewHandler(alertmanagerService),
TraceDetail: impltracedetail.NewHandler(modules.TraceDetail),
RulerHandler: signozruler.NewHandler(rulerService),

View File

@@ -37,6 +37,8 @@ import (
"github.com/SigNoz/signoz/pkg/modules/services/implservices"
"github.com/SigNoz/signoz/pkg/modules/session"
"github.com/SigNoz/signoz/pkg/modules/session/implsession"
"github.com/SigNoz/signoz/pkg/modules/spanmapper"
"github.com/SigNoz/signoz/pkg/modules/spanmapper/implspanmapper"
"github.com/SigNoz/signoz/pkg/modules/spanpercentile"
"github.com/SigNoz/signoz/pkg/modules/spanpercentile/implspanpercentile"
"github.com/SigNoz/signoz/pkg/modules/tracedetail"
@@ -79,6 +81,7 @@ type Modules struct {
CloudIntegration cloudintegration.Module
RuleStateHistory rulestatehistory.Module
TraceDetail tracedetail.Module
SpanMapper spanmapper.Module
}
func NewModules(
@@ -131,5 +134,6 @@ func NewModules(
RuleStateHistory: implrulestatehistory.NewModule(implrulestatehistory.NewStore(telemetryStore, telemetryMetadataStore, providerSettings.Logger)),
CloudIntegration: cloudIntegrationModule,
TraceDetail: impltracedetail.NewModule(impltracedetail.NewTraceStore(telemetryStore), providerSettings, config.TraceDetail),
SpanMapper: implspanmapper.NewModule(implspanmapper.NewStore(sqlstore)),
}
}

View File

@@ -195,6 +195,7 @@ func NewSQLMigrationProviderFactories(
sqlmigration.NewServiceAccountAuthzactory(sqlstore),
sqlmigration.NewDropUserDeletedAtFactory(sqlstore, sqlschema),
sqlmigration.NewMigrateAWSAllRegionsFactory(sqlstore),
sqlmigration.NewAddSpanMapperFactory(sqlstore, sqlschema),
)
}

View File

@@ -0,0 +1,118 @@
package sqlmigration
import (
"context"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/sqlschema"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
)
type addSpanMapper struct {
sqlschema sqlschema.SQLSchema
sqlstore sqlstore.SQLStore
}
func NewAddSpanMapperFactory(sqlstore sqlstore.SQLStore, sqlschema sqlschema.SQLSchema) factory.ProviderFactory[SQLMigration, Config] {
return factory.NewProviderFactory(factory.MustNewName("add_span_mapper"), func(_ context.Context, _ factory.ProviderSettings, _ Config) (SQLMigration, error) {
return &addSpanMapper{sqlschema: sqlschema, sqlstore: sqlstore}, nil
})
}
func (migration *addSpanMapper) Register(migrations *migrate.Migrations) error {
return migrations.Register(migration.Up, migration.Down)
}
func (migration *addSpanMapper) Up(ctx context.Context, db *bun.DB) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() {
_ = tx.Rollback()
}()
sqls := [][]byte{}
groupSQLs := migration.sqlschema.Operator().CreateTable(&sqlschema.Table{
Name: "span_mapper_group",
Columns: []*sqlschema.Column{
{Name: "id", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "created_at", DataType: sqlschema.DataTypeTimestamp, Nullable: false},
{Name: "updated_at", DataType: sqlschema.DataTypeTimestamp, Nullable: false},
{Name: "created_by", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "updated_by", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "org_id", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "name", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "condition", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "enabled", DataType: sqlschema.DataTypeBoolean, Nullable: false, Default: "true"},
},
PrimaryKeyConstraint: &sqlschema.PrimaryKeyConstraint{
ColumnNames: []sqlschema.ColumnName{"id"},
},
ForeignKeyConstraints: []*sqlschema.ForeignKeyConstraint{
{
ReferencingColumnName: sqlschema.ColumnName("org_id"),
ReferencedTableName: sqlschema.TableName("organizations"),
ReferencedColumnName: sqlschema.ColumnName("id"),
},
},
})
sqls = append(sqls, groupSQLs...)
groupIdxSQLs := migration.sqlschema.Operator().CreateIndex(
&sqlschema.UniqueIndex{
TableName: "span_mapper_group",
ColumnNames: []sqlschema.ColumnName{"org_id", "name"},
})
sqls = append(sqls, groupIdxSQLs...)
mapperSQLs := migration.sqlschema.Operator().CreateTable(&sqlschema.Table{
Name: "span_mapper",
Columns: []*sqlschema.Column{
{Name: "id", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "created_at", DataType: sqlschema.DataTypeTimestamp, Nullable: false},
{Name: "updated_at", DataType: sqlschema.DataTypeTimestamp, Nullable: false},
{Name: "created_by", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "updated_by", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "group_id", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "name", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "field_context", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "config", DataType: sqlschema.DataTypeText, Nullable: false},
{Name: "enabled", DataType: sqlschema.DataTypeBoolean, Nullable: false, Default: "true"},
},
PrimaryKeyConstraint: &sqlschema.PrimaryKeyConstraint{
ColumnNames: []sqlschema.ColumnName{"id"},
},
ForeignKeyConstraints: []*sqlschema.ForeignKeyConstraint{
{
ReferencingColumnName: sqlschema.ColumnName("group_id"),
ReferencedTableName: sqlschema.TableName("span_mapper_group"),
ReferencedColumnName: sqlschema.ColumnName("id"),
},
},
})
sqls = append(sqls, mapperSQLs...)
mapperIdxSQLs := migration.sqlschema.Operator().CreateIndex(
&sqlschema.UniqueIndex{
TableName: "span_mapper",
ColumnNames: []sqlschema.ColumnName{"group_id", "name"},
})
sqls = append(sqls, mapperIdxSQLs...)
for _, sql := range sqls {
if _, err := tx.ExecContext(ctx, string(sql)); err != nil {
return err
}
}
return tx.Commit()
}
func (migration *addSpanMapper) Down(context.Context, *bun.DB) error {
return nil
}

View File

@@ -1,6 +1,8 @@
package spantypes
import (
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
@@ -69,9 +71,9 @@ type PostableSpanMapper struct {
// UpdatableSpanMapper is the HTTP request body for updating a span mapper.
// All fields are optional; only non-nil fields are applied.
type UpdatableSpanMapper struct {
FieldContext FieldContext `json:"field_context,omitempty"`
Config *SpanMapperConfig `json:"config,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
FieldContext *FieldContext `json:"field_context" nullable:"true"`
Config *SpanMapperConfig `json:"config" nullable:"true"`
Enabled *bool `json:"enabled" nullable:"true"`
}
type GettableSpanMapper = SpanMapper
@@ -88,6 +90,53 @@ func (SpanMapperOperation) Enum() []any {
return []any{SpanMapperOperationMove, SpanMapperOperationCopy}
}
func NewSpanMapper(groupID valuer.UUID, createdBy string, p *PostableSpanMapper) *SpanMapper {
now := time.Now()
return &SpanMapper{
ID: valuer.GenerateUUID(),
GroupID: groupID,
Name: p.Name,
FieldContext: p.FieldContext,
Config: p.Config,
Enabled: p.Enabled,
TimeAuditable: types.TimeAuditable{
CreatedAt: now,
UpdatedAt: now,
},
UserAuditable: types.UserAuditable{
CreatedBy: createdBy,
UpdatedBy: createdBy,
},
}
}
func (m *SpanMapper) Update(u *UpdatableSpanMapper, updatedBy string) {
if u.FieldContext != nil {
m.FieldContext = *u.FieldContext
}
if u.Config != nil {
m.Config = *u.Config
}
if u.Enabled != nil {
m.Enabled = *u.Enabled
}
m.UpdatedAt = time.Now()
m.UpdatedBy = updatedBy
}
func NewStorableSpanMapperFromMapper(m *SpanMapper) *StorableSpanMapper {
return &StorableSpanMapper{
Identifiable: types.Identifiable{ID: m.ID},
TimeAuditable: m.TimeAuditable,
UserAuditable: m.UserAuditable,
GroupID: m.GroupID,
Name: m.Name,
FieldContext: m.FieldContext,
Config: m.Config,
Enabled: m.Enabled,
}
}
func NewSpanMapperFromStorable(s *StorableSpanMapper) *SpanMapper {
return &SpanMapper{
TimeAuditable: s.TimeAuditable,
@@ -101,29 +150,6 @@ func NewSpanMapperFromStorable(s *StorableSpanMapper) *SpanMapper {
}
}
func NewSpanMapperFromPostable(req *PostableSpanMapper) *SpanMapper {
return &SpanMapper{
Name: req.Name,
FieldContext: req.FieldContext,
Config: req.Config,
Enabled: req.Enabled,
}
}
func NewSpanMapperFromUpdatable(req *UpdatableSpanMapper) *SpanMapper {
m := &SpanMapper{}
if req.FieldContext != (FieldContext{}) {
m.FieldContext = req.FieldContext
}
if req.Config != nil {
m.Config = *req.Config
}
if req.Enabled != nil {
m.Enabled = *req.Enabled
}
return m
}
func NewSpanMappersFromStorableSpanMappers(ss []*StorableSpanMapper) []*SpanMapper {
mappers := make([]*SpanMapper, len(ss))
for i, s := range ss {

View File

@@ -1,6 +1,8 @@
package spantypes
import (
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
@@ -11,9 +13,6 @@ var (
ErrCodeMappingGroupAlreadyExists = errors.MustNewCode("span_attribute_mapping_group_already_exists")
)
// SpanMapperGroupCategory defaults will be llm, tool, agent but user can configure more as they want.
type SpanMapperGroupCategory valuer.String
// A group runs when any of the listed attribute/resource key patterns match.
type SpanMapperGroupCondition struct {
Attributes []string `json:"attributes" required:"true" nullable:"true"`
@@ -28,7 +27,6 @@ type SpanMapperGroup struct {
ID valuer.UUID `json:"id" required:"true"`
OrgID valuer.UUID `json:"orgId" required:"true"`
Name string `json:"name" required:"true"`
Category SpanMapperGroupCategory `json:"category" required:"true"`
Condition SpanMapperGroupCondition `json:"condition" required:"true"`
Enabled bool `json:"enabled" required:"true"`
}
@@ -38,7 +36,6 @@ type GettableSpanMapperGroup = SpanMapperGroup
type PostableSpanMapperGroup struct {
Name string `json:"name" required:"true"`
Category SpanMapperGroupCategory `json:"category" required:"true"`
Condition SpanMapperGroupCondition `json:"condition" required:"true"`
Enabled bool `json:"enabled"`
}
@@ -46,44 +43,39 @@ type PostableSpanMapperGroup struct {
// UpdatableSpanMapperGroup is the HTTP request body for updating a mapping group.
// All fields are optional; only non-nil fields are applied.
type UpdatableSpanMapperGroup struct {
Name *string `json:"name,omitempty"`
Condition *SpanMapperGroupCondition `json:"condition,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
Name *string `json:"name" nullable:"true"`
Condition *SpanMapperGroupCondition `json:"condition" nullable:"true"`
Enabled *bool `json:"enabled" nullable:"true"`
}
type ListSpanMapperGroupsQuery struct {
Category *SpanMapperGroupCategory `query:"category"`
Enabled *bool `query:"enabled"`
Enabled *bool `query:"enabled"`
}
type GettableSpanMapperGroups struct {
Items []*GettableSpanMapperGroup `json:"items" required:"true" nullable:"false"`
}
func NewSpanMapperGroupFromStorable(s *StorableSpanMapperGroup) *SpanMapperGroup {
return &SpanMapperGroup{
TimeAuditable: s.TimeAuditable,
UserAuditable: s.UserAuditable,
ID: s.ID,
OrgID: s.OrgID,
Name: s.Name,
Category: s.Category,
Condition: s.Condition,
Enabled: s.Enabled,
}
}
func NewSpanMapperGroupFromPostable(p *PostableSpanMapperGroup) *SpanMapperGroup {
func NewSpanMapperGroup(orgID valuer.UUID, createdBy string, p *PostableSpanMapperGroup) *SpanMapperGroup {
now := time.Now()
return &SpanMapperGroup{
ID: valuer.GenerateUUID(),
OrgID: orgID,
Name: p.Name,
Category: p.Category,
Condition: p.Condition,
Enabled: p.Enabled,
TimeAuditable: types.TimeAuditable{
CreatedAt: now,
UpdatedAt: now,
},
UserAuditable: types.UserAuditable{
CreatedBy: createdBy,
UpdatedBy: createdBy,
},
}
}
func NewSpanMapperGroupFromUpdatable(u *UpdatableSpanMapperGroup) *SpanMapperGroup {
g := &SpanMapperGroup{}
func (g *SpanMapperGroup) Update(u *UpdatableSpanMapperGroup, updatedBy string) {
if u.Name != nil {
g.Name = *u.Name
}
@@ -93,7 +85,32 @@ func NewSpanMapperGroupFromUpdatable(u *UpdatableSpanMapperGroup) *SpanMapperGro
if u.Enabled != nil {
g.Enabled = *u.Enabled
}
return g
g.UpdatedAt = time.Now()
g.UpdatedBy = updatedBy
}
func NewStorableSpanMapperGroupFromGroup(g *SpanMapperGroup) *StorableSpanMapperGroup {
return &StorableSpanMapperGroup{
Identifiable: types.Identifiable{ID: g.ID},
TimeAuditable: g.TimeAuditable,
UserAuditable: g.UserAuditable,
OrgID: g.OrgID,
Name: g.Name,
Condition: g.Condition,
Enabled: g.Enabled,
}
}
func NewSpanMapperGroupFromStorable(s *StorableSpanMapperGroup) *SpanMapperGroup {
return &SpanMapperGroup{
TimeAuditable: s.TimeAuditable,
UserAuditable: s.UserAuditable,
ID: s.ID,
OrgID: s.OrgID,
Name: s.Name,
Condition: s.Condition,
Enabled: s.Enabled,
}
}
func NewSpanMapperGroupsFromStorableGroups(ss []*StorableSpanMapperGroup) []*SpanMapperGroup {

View File

@@ -19,7 +19,6 @@ type StorableSpanMapperGroup struct {
OrgID valuer.UUID `bun:"org_id,type:text,notnull"`
Name string `bun:"name,type:text,notnull"`
Category SpanMapperGroupCategory `bun:"category,type:text,notnull"`
Condition SpanMapperGroupCondition `bun:"condition,type:jsonb,notnull"`
Enabled bool `bun:"enabled,notnull,default:true"`
}

View File

@@ -8,16 +8,16 @@ import (
type Store interface {
// Group operations
ListSpanMapperGroups(ctx context.Context, orgID valuer.UUID, q *ListSpanMapperGroupsQuery) ([]*StorableSpanMapperGroup, error)
GetSpanMapperGroup(ctx context.Context, orgID, id valuer.UUID) (*StorableSpanMapperGroup, error)
CreateSpanMapperGroup(ctx context.Context, group *StorableSpanMapperGroup) error
UpdateSpanMapperGroup(ctx context.Context, group *StorableSpanMapperGroup) error
ListSpanMapperGroups(ctx context.Context, orgID valuer.UUID, q *ListSpanMapperGroupsQuery) ([]*SpanMapperGroup, error)
GetSpanMapperGroup(ctx context.Context, orgID, id valuer.UUID) (*SpanMapperGroup, error)
CreateSpanMapperGroup(ctx context.Context, group *SpanMapperGroup) error
UpdateSpanMapperGroup(ctx context.Context, group *SpanMapperGroup) error
DeleteSpanMapperGroup(ctx context.Context, orgID, id valuer.UUID) error
// Mapper operations
ListSpanMappers(ctx context.Context, orgID, groupID valuer.UUID) ([]*StorableSpanMapper, error)
GetSpanMapper(ctx context.Context, orgID, groupID, id valuer.UUID) (*StorableSpanMapper, error)
CreateSpanMapper(ctx context.Context, mapper *StorableSpanMapper) error
UpdateSpanMapper(ctx context.Context, mapper *StorableSpanMapper) error
ListSpanMappers(ctx context.Context, orgID, groupID valuer.UUID) ([]*SpanMapper, error)
GetSpanMapper(ctx context.Context, orgID, groupID, id valuer.UUID) (*SpanMapper, error)
CreateSpanMapper(ctx context.Context, mapper *SpanMapper) error
UpdateSpanMapper(ctx context.Context, mapper *SpanMapper) error
DeleteSpanMapper(ctx context.Context, orgID, groupID, id valuer.UUID) error
}