mirror of
https://github.com/SigNoz/signoz.git
synced 2026-09-28 06:10:41 +01:00
Compare commits
7 Commits
hotfix/pat
...
nv/dashboa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1331010f8 | ||
|
|
f05666a704 | ||
|
|
72ad7e5d5d | ||
|
|
2d48967690 | ||
|
|
c29b7b492f | ||
|
|
d49919053f | ||
|
|
67878a01b2 |
2
.github/workflows/integrationci.yaml
vendored
2
.github/workflows/integrationci.yaml
vendored
@@ -38,6 +38,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
suite:
|
||||
- alerts
|
||||
- alertmanager
|
||||
- alertmanagerrotation
|
||||
- basepath
|
||||
@@ -63,7 +64,6 @@ jobs:
|
||||
- querierauthz
|
||||
- role
|
||||
- rootuser
|
||||
- ruler
|
||||
- savedview
|
||||
- semconvfamilies
|
||||
- serviceaccount
|
||||
|
||||
@@ -3321,6 +3321,69 @@ components:
|
||||
repeatVariable:
|
||||
type: string
|
||||
type: object
|
||||
DashboardtypesAIBuilderQuerySpec:
|
||||
properties:
|
||||
aggregations:
|
||||
items:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5TraceAggregation'
|
||||
nullable: true
|
||||
type: array
|
||||
bucketOptions:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5BucketOptions'
|
||||
cursor:
|
||||
type: string
|
||||
disabled:
|
||||
type: boolean
|
||||
filter:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5Filter'
|
||||
functions:
|
||||
items:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5Function'
|
||||
nullable: true
|
||||
type: array
|
||||
groupBy:
|
||||
items:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5GroupByKey'
|
||||
nullable: true
|
||||
type: array
|
||||
having:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5Having'
|
||||
legend:
|
||||
type: string
|
||||
limit:
|
||||
type: integer
|
||||
limitBy:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5LimitBy'
|
||||
name:
|
||||
type: string
|
||||
offset:
|
||||
type: integer
|
||||
order:
|
||||
items:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5OrderBy'
|
||||
nullable: true
|
||||
type: array
|
||||
secondaryAggregations:
|
||||
items:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5SecondaryAggregation'
|
||||
nullable: true
|
||||
type: array
|
||||
selectFields:
|
||||
items:
|
||||
$ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey'
|
||||
nullable: true
|
||||
type: array
|
||||
signal:
|
||||
enum:
|
||||
- traces
|
||||
type: string
|
||||
source:
|
||||
$ref: '#/components/schemas/TelemetrytypesSource'
|
||||
stepInterval:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5Step'
|
||||
required:
|
||||
- signal
|
||||
type: object
|
||||
DashboardtypesAreaChartAppearance:
|
||||
properties:
|
||||
fillMode:
|
||||
@@ -4281,6 +4344,7 @@ components:
|
||||
DashboardtypesQueryPlugin:
|
||||
discriminator:
|
||||
mapping:
|
||||
signoz/AIBuilderQuery: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpec'
|
||||
signoz/BuilderQuery: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpec'
|
||||
signoz/ClickHouseSQL: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5ClickHouseQuery'
|
||||
signoz/CompositeQuery: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5CompositeQuery'
|
||||
@@ -4290,6 +4354,7 @@ components:
|
||||
propertyName: kind
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpec'
|
||||
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpec'
|
||||
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5CompositeQuery'
|
||||
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5QueryBuilderFormula'
|
||||
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5PromQuery'
|
||||
@@ -4299,12 +4364,25 @@ components:
|
||||
DashboardtypesQueryPluginKind:
|
||||
enum:
|
||||
- signoz/BuilderQuery
|
||||
- signoz/AIBuilderQuery
|
||||
- signoz/CompositeQuery
|
||||
- signoz/Formula
|
||||
- signoz/PromQLQuery
|
||||
- signoz/ClickHouseSQL
|
||||
- signoz/TraceOperator
|
||||
type: string
|
||||
DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpec:
|
||||
properties:
|
||||
kind:
|
||||
enum:
|
||||
- signoz/AIBuilderQuery
|
||||
type: string
|
||||
spec:
|
||||
$ref: '#/components/schemas/DashboardtypesAIBuilderQuerySpec'
|
||||
required:
|
||||
- kind
|
||||
- spec
|
||||
type: object
|
||||
DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpec:
|
||||
properties:
|
||||
kind:
|
||||
|
||||
@@ -47,5 +47,4 @@ export enum LOCALSTORAGE {
|
||||
DASHBOARDS_LIST_VIEWS = 'DASHBOARDS_LIST_VIEWS',
|
||||
DASHBOARD_V2_PANEL_COLUMN_WIDTHS = 'DASHBOARD_V2_PANEL_COLUMN_WIDTHS',
|
||||
LLM_ATTRIBUTE_MAPPING_TEST_SPAN = 'LLM_ATTRIBUTE_MAPPING_TEST_SPAN',
|
||||
SAVED_VIEW_ENABLED = 'SAVED_VIEW_ENABLED',
|
||||
}
|
||||
|
||||
@@ -53,10 +53,6 @@
|
||||
z-index: 0;
|
||||
background: var(--l1-background);
|
||||
|
||||
// Column so the bottom strip sits under the scrolling content, not inside it.
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.full-screen-content {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -74,9 +70,7 @@
|
||||
|
||||
.chat-support-gateway {
|
||||
position: fixed;
|
||||
// Lifted above the bottom strip. Don't extend this pattern — new fixed-bottom
|
||||
// UI belongs in the bounded layout, not in another offset here.
|
||||
bottom: calc(20px + var(--bottom-strip-height, 0px));
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
z-index: 1000;
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ import { USER_PREFERENCES } from 'constants/userPreferences';
|
||||
import AIAssistantModal from 'container/AIAssistant/AIAssistantModal';
|
||||
import AIAssistantPanel from 'container/AIAssistant/AIAssistantPanel';
|
||||
import { useAIAssistantStore } from 'container/AIAssistant/store/useAIAssistantStore';
|
||||
import BottomStrip from 'container/BottomStrip';
|
||||
import SideNav from 'container/SideNav';
|
||||
import TopNav from 'container/TopNav';
|
||||
import dayjs from 'dayjs';
|
||||
@@ -52,7 +51,6 @@ import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||
import { useIsAIAssistantEnabled } from 'hooks/useIsAIAssistantEnabled';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { useSavedViewEnabled } from 'hooks/useSavedViewEnabled';
|
||||
import useTabVisibility from 'hooks/useTabFocus';
|
||||
import history from 'lib/history';
|
||||
import { isNull } from 'lodash-es';
|
||||
@@ -404,7 +402,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
}, [pathname]);
|
||||
|
||||
const isToDisplayLayout = isLoggedIn;
|
||||
const isSavedViewEnabled = useSavedViewEnabled();
|
||||
|
||||
const routeKey = useMemo(() => getRouteKey(pathname), [pathname]);
|
||||
const pageTitle = t(routeKey);
|
||||
@@ -871,10 +868,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
</OverlayScrollbar>
|
||||
</LayoutContent>
|
||||
</Sentry.ErrorBoundary>
|
||||
|
||||
{isSavedViewEnabled && isToDisplayLayout && !renderFullScreen && (
|
||||
<BottomStrip />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isLoggedIn && isAIAssistantEnabled && (
|
||||
|
||||
@@ -12,12 +12,8 @@ export const Layout = styled(LayoutComponent)`
|
||||
}
|
||||
`;
|
||||
|
||||
// Takes the height left in `.app-content` after the bottom strip.
|
||||
// `min-height: 0` is not needed right now, overlayscrollbars already sets
|
||||
// `overflow: auto` here. Kept so this does not break if that goes away.
|
||||
export const LayoutContent = styled(LayoutComponent.Content)`
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
&::-webkit-scrollbar {
|
||||
width: 0.1rem;
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
.strip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--spacing-6);
|
||||
|
||||
flex-shrink: 0;
|
||||
height: var(--bottom-strip-height);
|
||||
padding: 0 var(--spacing-6);
|
||||
|
||||
background: var(--l2-background);
|
||||
border-top: 1px solid var(--l2-border);
|
||||
|
||||
font-family: var(--font-family-sf-mono, monospace);
|
||||
|
||||
// Above page content, below the body-portalled overlays that are meant to
|
||||
// cover the strip.
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.left,
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-6);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
// Temporary placeholder for the left slot. Replaced later.
|
||||
.version {
|
||||
color: var(--l2-foreground);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { render } from 'tests/test-utils';
|
||||
|
||||
import BottomStrip, {
|
||||
BOTTOM_STRIP_HEIGHT,
|
||||
BOTTOM_STRIP_HEIGHT_VAR,
|
||||
BOTTOM_STRIP_ON_CLASS,
|
||||
} from '..';
|
||||
|
||||
describe('BottomStrip', () => {
|
||||
it('publishes the body class and height property while mounted', () => {
|
||||
const { unmount } = render(<BottomStrip />);
|
||||
|
||||
expect(document.body.classList.contains(BOTTOM_STRIP_ON_CLASS)).toBe(true);
|
||||
expect(document.body.style.getPropertyValue(BOTTOM_STRIP_HEIGHT_VAR)).toBe(
|
||||
`${BOTTOM_STRIP_HEIGHT}px`,
|
||||
);
|
||||
|
||||
unmount();
|
||||
|
||||
expect(document.body.classList.contains(BOTTOM_STRIP_ON_CLASS)).toBe(false);
|
||||
expect(document.body.style.getPropertyValue(BOTTOM_STRIP_HEIGHT_VAR)).toBe(
|
||||
'',
|
||||
);
|
||||
});
|
||||
|
||||
// The string is whatever the Go build injected, so it is rendered untouched —
|
||||
// same as SideNav. Release tags carry the "v", local builds do not.
|
||||
it.each([['v0.134.67'], ['main-64f1c2a']])(
|
||||
'renders the build version %p exactly as given',
|
||||
(version) => {
|
||||
const { getByTestId } = render(<BottomStrip />, undefined, {
|
||||
appContextOverrides: {
|
||||
versionData: { version, ee: 'Y', setupCompleted: true },
|
||||
},
|
||||
});
|
||||
|
||||
expect(getByTestId('bottom-strip-version')).toHaveTextContent(version);
|
||||
},
|
||||
);
|
||||
|
||||
it('renders the strip without a version when none is available', () => {
|
||||
const { getByTestId, queryByTestId } = render(<BottomStrip />, undefined, {
|
||||
appContextOverrides: { versionData: null },
|
||||
});
|
||||
|
||||
expect(getByTestId('bottom-strip')).toBeInTheDocument();
|
||||
expect(queryByTestId('bottom-strip-version')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
import { useLayoutEffect } from 'react';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
|
||||
import styles from './BottomStrip.module.scss';
|
||||
|
||||
export const BOTTOM_STRIP_HEIGHT = 24;
|
||||
|
||||
export const BOTTOM_STRIP_ON_CLASS = 'bottom-strip-on';
|
||||
export const BOTTOM_STRIP_HEIGHT_VAR = '--bottom-strip-height';
|
||||
|
||||
function BottomStrip(): JSX.Element {
|
||||
const { versionData } = useAppContext();
|
||||
const version = versionData?.version?.trim();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
document.body.classList.add(BOTTOM_STRIP_ON_CLASS);
|
||||
document.body.style.setProperty(
|
||||
BOTTOM_STRIP_HEIGHT_VAR,
|
||||
`${BOTTOM_STRIP_HEIGHT}px`,
|
||||
);
|
||||
|
||||
return (): void => {
|
||||
document.body.classList.remove(BOTTOM_STRIP_ON_CLASS);
|
||||
document.body.style.removeProperty(BOTTOM_STRIP_HEIGHT_VAR);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.strip} data-testid="bottom-strip">
|
||||
<div className={styles.left}>
|
||||
{version && (
|
||||
<span className={styles.version} data-testid="bottom-strip-version">
|
||||
{version}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.right} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BottomStrip;
|
||||
@@ -1,8 +1,6 @@
|
||||
.create-alert-v2-footer {
|
||||
position: fixed;
|
||||
// Lifted above the bottom strip. Don't extend this pattern — new fixed-bottom
|
||||
// UI belongs in the bounded layout, not in another offset here.
|
||||
bottom: var(--bottom-strip-height, 0px);
|
||||
bottom: 0;
|
||||
left: 63px;
|
||||
right: 0;
|
||||
background-color: var(--l1-background);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
.explorer-options-container {
|
||||
position: fixed;
|
||||
// Lifted above the bottom strip. Don't extend this pattern — new fixed-bottom
|
||||
// UI belongs in the bounded layout, not in another offset here.
|
||||
bottom: var(--bottom-strip-height, 0px);
|
||||
bottom: 0px;
|
||||
left: calc(50% + 240px);
|
||||
transform: translate(calc(-50% - 120px), 0);
|
||||
transition: left 0.2s linear;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
.explorer-option-droppable-container {
|
||||
position: fixed;
|
||||
// Lifted above the bottom strip. Don't extend this pattern — new fixed-bottom
|
||||
// UI belongs in the bounded layout, not in another offset here.
|
||||
bottom: var(--bottom-strip-height, 0px);
|
||||
bottom: 0;
|
||||
width: -webkit-fill-available;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.home-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
.licenses-page {
|
||||
max-height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
.licenses-page-header {
|
||||
border-bottom: 1px solid var(--l1-border);
|
||||
background: var(--l1-background);
|
||||
@@ -29,6 +32,7 @@
|
||||
|
||||
.licenses-page-content {
|
||||
flex: 1;
|
||||
height: calc(100vh - 48px);
|
||||
background: var(--l1-background);
|
||||
padding: 10px 8px;
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
flex: 1;
|
||||
height: calc(100vh - 62px);
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
|
||||
@@ -181,9 +181,7 @@
|
||||
|
||||
.ant-pagination {
|
||||
position: fixed;
|
||||
// Lifted above the bottom strip. Don't extend this pattern — new
|
||||
// fixed-bottom UI belongs in the bounded layout, not in another offset here.
|
||||
bottom: var(--bottom-strip-height, 0px);
|
||||
bottom: 0;
|
||||
width: calc(100% - 54px);
|
||||
background: var(--l1-background);
|
||||
padding: 16px;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
flex: 1;
|
||||
height: calc(100vh - 62px);
|
||||
min-height: 400px;
|
||||
padding-top: var(--spacing-8);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
.version-container {
|
||||
max-height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
.version-page-header {
|
||||
border-bottom: 1px solid var(--l1-border);
|
||||
background: var(--l1-background);
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { useState } from 'react';
|
||||
|
||||
export function useSavedViewEnabled(): boolean {
|
||||
const [isEnabled] = useState(
|
||||
() => getLocalStorageKey(LOCALSTORAGE.SAVED_VIEW_ENABLED) === 'true',
|
||||
);
|
||||
|
||||
return isEnabled;
|
||||
}
|
||||
@@ -1,29 +1,4 @@
|
||||
.alerts-container {
|
||||
// Hands the page height down to the active tab so its content can bound itself
|
||||
// instead of guessing with 100vh. Child combinators only, nested Tabs
|
||||
// (Configuration) must not be caught.
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
|
||||
> .ant-tabs-content-holder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> .ant-tabs-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> .ant-tabs-tabpane-active {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.top-level-tab.periscope-tab {
|
||||
padding: 2px 0;
|
||||
}
|
||||
@@ -65,9 +40,5 @@
|
||||
|
||||
.alert-rules-container {
|
||||
margin-top: 10px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
// Lifted above the bottom strip. Don't extend this pattern — new fixed-bottom
|
||||
// UI belongs in the bounded layout, not in another offset here.
|
||||
bottom: var(--bottom-strip-height, 0px);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
.support-page-container {
|
||||
max-height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
.support-page-header {
|
||||
border-bottom: 1px solid var(--l1-border);
|
||||
background: var(--l1-background);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
.root {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
height: calc(100vh);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
.traces-funnel-details {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
// 45px -> height of the tab bar
|
||||
height: calc(100vh - 45px);
|
||||
|
||||
&__steps-config {
|
||||
flex-shrink: 0;
|
||||
width: 600px;
|
||||
border-right: 1px solid var(--l1-border);
|
||||
// Positioning context for the absolute .steps-footer.
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
// Scoped here so the modal usage of FunnelConfiguration on trace details
|
||||
// stays in normal flow.
|
||||
.funnel-configuration {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
&__steps-results {
|
||||
width: 100%;
|
||||
|
||||
@@ -4,17 +4,14 @@
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
&.funnel-details-page {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
// .steps-footer is absolute against the config column, so its 64px is
|
||||
// reserved rather than laid out.
|
||||
margin-bottom: 64px;
|
||||
height: calc(
|
||||
100vh - 170px
|
||||
); // 64px bottom bar + 61px configuration header + 45px page navbar
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -157,6 +157,7 @@ func (d *DashboardSpec) validateQuery(qi int, q Query, panelKind PanelPluginKind
|
||||
func validateQueryAllowedForPanel(plugin QueryPlugin, allowed []QueryPluginKind, panelKind PanelPluginKind, path string) error {
|
||||
compositeSubQueryTypeToPluginKind := map[qb.QueryType]QueryPluginKind{
|
||||
qb.QueryTypeBuilder: QueryKindBuilder,
|
||||
qb.QueryTypeBuilderAI: QueryKindAIBuilder,
|
||||
qb.QueryTypeFormula: QueryKindFormula,
|
||||
qb.QueryTypeTraceOperator: QueryKindTraceOperator,
|
||||
qb.QueryTypePromQL: QueryKindPromQL,
|
||||
|
||||
@@ -117,6 +117,22 @@ func TestNewStatsFromStorableDashboardsCountsCompositeSubQueries(t *testing.T) {
|
||||
assert.Equal(t, int64(1), stats[statKeyPanelLogsCount])
|
||||
}
|
||||
|
||||
// An AI builder query is always a traces query, so it counts towards traces.
|
||||
func TestNewStatsFromStorableDashboardsCountsAIBuilderQueries(t *testing.T) {
|
||||
aiBuilder := `{
|
||||
"kind": "time_series",
|
||||
"spec": {"plugin": {"kind": "signoz/AIBuilderQuery", "spec": {"name": "A", "aggregations": [{"expression": "count()"}]}}}
|
||||
}`
|
||||
dashboard := newStatsStorableV2(t, `"p1": `+statsPanel(aiBuilder))
|
||||
|
||||
stats := NewStatsFromStorableDashboards([]*StorableDashboard{dashboard})
|
||||
|
||||
assert.Equal(t, int64(1), stats[statKeyPanelCount])
|
||||
assert.Equal(t, int64(1), stats[statKeyPanelTracesCount])
|
||||
assert.Equal(t, int64(0), stats[statKeyPanelMetricsCount])
|
||||
assert.Equal(t, int64(0), stats[statKeyPanelLogsCount])
|
||||
}
|
||||
|
||||
// promql and clickhouse queries carry no signal, so they land in the panel total
|
||||
// and nowhere else.
|
||||
func TestNewStatsFromStorableDashboardsIgnoresSignallessQueries(t *testing.T) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/perses/spec/go/dashboard"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -1876,6 +1877,43 @@ func TestStorageRoundTrip(t *testing.T) {
|
||||
assert.Contains(t, responseStr, `"operator":"above"`, "expected operator:above after storage round-trip")
|
||||
}
|
||||
|
||||
// An AI builder query carries no signal of its own: the plugin kind implies
|
||||
// gen_ai, which only reads traces, so decode pins the signal and marshal emits it.
|
||||
func TestAIBuilderQueryStorageRoundTrip(t *testing.T) {
|
||||
input := []byte(`{
|
||||
"variables": [],
|
||||
"panels": {"p1": {"kind": "Panel", "spec": {
|
||||
"links": [],
|
||||
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
|
||||
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/AIBuilderQuery", "spec": {
|
||||
"name": "A", "aggregations": [{"expression": "count()"}]
|
||||
}}}}]
|
||||
}}},
|
||||
"links": [],
|
||||
"layouts": []
|
||||
}`)
|
||||
|
||||
d, err := unmarshalDashboard(input)
|
||||
require.NoError(t, err)
|
||||
|
||||
plugin := d.Panels["p1"].Spec.Queries[0].Spec.Plugin
|
||||
assert.Equal(t, QueryKindAIBuilder, plugin.Kind)
|
||||
|
||||
aiSpec, ok := plugin.Spec.(*AIBuilderQuerySpec)
|
||||
require.True(t, ok, "expected *AIBuilderQuerySpec, got %T", plugin.Spec)
|
||||
assert.Equal(t, "A", aiSpec.Name)
|
||||
assert.Equal(t, telemetrytypes.SignalTraces, aiSpec.Signal)
|
||||
|
||||
stored, err := json.Marshal(plugin)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, string(stored), `"kind":"signoz/AIBuilderQuery"`)
|
||||
assert.Contains(t, string(stored), `"signal":"traces"`)
|
||||
|
||||
var loaded QueryPlugin
|
||||
require.NoError(t, json.Unmarshal(stored, &loaded))
|
||||
assert.Equal(t, plugin, loaded)
|
||||
}
|
||||
|
||||
func TestPostableDashboardV2GenerateNameFlag(t *testing.T) {
|
||||
const validSpec = `"spec": {"variables": [], "panels": {}, "layouts": [], "links": []}`
|
||||
|
||||
@@ -2088,6 +2126,8 @@ func TestPanelTypeQueryTypeCompatibility(t *testing.T) {
|
||||
{"TimeSeries+PromQL", mkQuery("signoz/TimeSeriesPanel", "signoz/PromQLQuery", `{"name":"A","query":"up"}`), false},
|
||||
{"Table+ClickHouse", mkQuery("signoz/TablePanel", "signoz/ClickHouseSQL", `{"name":"A","query":"SELECT 1"}`), false},
|
||||
{"List+Builder", mkQuery("signoz/ListPanel", "signoz/BuilderQuery", `{"name":"A","signal":"logs"}`), false},
|
||||
{"TimeSeries+AIBuilder", mkQuery("signoz/TimeSeriesPanel", "signoz/AIBuilderQuery", `{"name":"A","aggregations":[{"expression":"count()"}]}`), false},
|
||||
{"List+AIBuilder", mkQuery("signoz/ListPanel", "signoz/AIBuilderQuery", `{"name":"A"}`), false},
|
||||
// Top-level: rejected
|
||||
{"Table+PromQL", mkQuery("signoz/TablePanel", "signoz/PromQLQuery", `{"name":"A","query":"up"}`), true},
|
||||
{"List+ClickHouse", mkQuery("signoz/ListPanel", "signoz/ClickHouseSQL", `{"name":"A","query":"SELECT 1"}`), true},
|
||||
@@ -2097,6 +2137,7 @@ func TestPanelTypeQueryTypeCompatibility(t *testing.T) {
|
||||
// Composite sub-queries
|
||||
{"Table+Composite(promql)", mkComposite("signoz/TablePanel", "promql", `{"name":"A","query":"up"}`), true},
|
||||
{"Table+Composite(clickhouse)", mkComposite("signoz/TablePanel", "clickhouse_sql", `{"name":"A","query":"SELECT 1"}`), false},
|
||||
{"Table+Composite(builder_ai)", mkComposite("signoz/TablePanel", "builder_ai_query", `{"name":"A","aggregations":[{"expression":"count()"}]}`), false},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
|
||||
@@ -93,6 +93,7 @@ type QueryPlugin struct {
|
||||
func (QueryPlugin) PrepareJSONSchema(s *jsonschema.Schema) error {
|
||||
return markDiscriminator(s, "kind", map[string]string{
|
||||
string(QueryKindBuilder): schemaRef("DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpec"),
|
||||
string(QueryKindAIBuilder): schemaRef("DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpec"),
|
||||
string(QueryKindComposite): schemaRef("DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5CompositeQuery"),
|
||||
string(QueryKindFormula): schemaRef("DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5QueryBuilderFormula"),
|
||||
string(QueryKindPromQL): schemaRef("DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5PromQuery"),
|
||||
@@ -122,6 +123,7 @@ func (p *QueryPlugin) UnmarshalJSON(data []byte) error {
|
||||
func (QueryPlugin) JSONSchemaOneOf() []any {
|
||||
return []any{
|
||||
QueryPluginVariant[BuilderQuerySpec]{Kind: string(QueryKindBuilder)},
|
||||
QueryPluginVariant[AIBuilderQuerySpec]{Kind: string(QueryKindAIBuilder)},
|
||||
QueryPluginVariant[CompositeQuerySpec]{Kind: string(QueryKindComposite)},
|
||||
QueryPluginVariant[FormulaSpec]{Kind: string(QueryKindFormula)},
|
||||
QueryPluginVariant[PromQLQuerySpec]{Kind: string(QueryKindPromQL)},
|
||||
@@ -142,6 +144,11 @@ func (plugin QueryPlugin) buildV5CompositeQueryFromPlugin() (qb.CompositeQuery,
|
||||
return qb.CompositeQuery{}, errors.Newf(errors.TypeInvalidInput, ErrCodeDashboardInvalidWidgetQuery, "builder query is empty")
|
||||
}
|
||||
return wrapEnvelope(qb.QueryTypeBuilder, spec.Spec), nil
|
||||
case *AIBuilderQuerySpec:
|
||||
if spec == nil {
|
||||
return qb.CompositeQuery{}, errors.Newf(errors.TypeInvalidInput, ErrCodeDashboardInvalidWidgetQuery, "AI builder query is empty")
|
||||
}
|
||||
return wrapEnvelope(qb.QueryTypeBuilderAI, qb.QueryBuilderQuery[qb.TraceAggregation](*spec)), nil
|
||||
case *qb.PromQuery:
|
||||
return wrapEnvelope(qb.QueryTypePromQL, *spec), nil
|
||||
case *qb.ClickHouseQuery:
|
||||
@@ -237,6 +244,7 @@ var (
|
||||
}
|
||||
queryPluginSpecs = map[QueryPluginKind]func() any{
|
||||
QueryKindBuilder: func() any { return new(BuilderQuerySpec) },
|
||||
QueryKindAIBuilder: func() any { return new(AIBuilderQuerySpec) },
|
||||
QueryKindComposite: func() any { return new(CompositeQuerySpec) },
|
||||
QueryKindFormula: func() any { return new(FormulaSpec) },
|
||||
QueryKindPromQL: func() any { return new(PromQLQuerySpec) },
|
||||
@@ -249,14 +257,14 @@ var (
|
||||
VariableKindCustom: func() any { return new(CustomVariableSpec) },
|
||||
}
|
||||
allowedQueryKinds = map[PanelPluginKind][]QueryPluginKind{
|
||||
PanelKindTimeSeries: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindBarChart: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindAreaChart: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindNumber: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindHistogram: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindPieChart: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindClickHouseSQL},
|
||||
PanelKindTable: {QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindClickHouseSQL},
|
||||
PanelKindList: {QueryKindBuilder},
|
||||
PanelKindTimeSeries: {QueryKindBuilder, QueryKindAIBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindBarChart: {QueryKindBuilder, QueryKindAIBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindAreaChart: {QueryKindBuilder, QueryKindAIBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindNumber: {QueryKindBuilder, QueryKindAIBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindHistogram: {QueryKindBuilder, QueryKindAIBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindPromQL, QueryKindClickHouseSQL},
|
||||
PanelKindPieChart: {QueryKindBuilder, QueryKindAIBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindClickHouseSQL},
|
||||
PanelKindTable: {QueryKindBuilder, QueryKindAIBuilder, QueryKindComposite, QueryKindFormula, QueryKindTraceOperator, QueryKindClickHouseSQL},
|
||||
PanelKindList: {QueryKindBuilder, QueryKindAIBuilder},
|
||||
PanelKindText: {},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -106,6 +106,13 @@ func redactQuery(spec any) any {
|
||||
return spec
|
||||
}
|
||||
return &BuilderQuerySpec{Spec: redactLeafQuery(s.Spec)}
|
||||
case *AIBuilderQuerySpec:
|
||||
if s == nil {
|
||||
return spec
|
||||
}
|
||||
redacted := redactLeafQuery(qb.QueryBuilderQuery[qb.TraceAggregation](*s)).(qb.QueryBuilderQuery[qb.TraceAggregation])
|
||||
out := AIBuilderQuerySpec(redacted)
|
||||
return &out
|
||||
case *qb.PromQuery:
|
||||
return redactQueryPtr(s)
|
||||
case *qb.ClickHouseQuery:
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
qb "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -159,6 +160,11 @@ func TestDashboardV2GetPanelQuery(t *testing.T) {
|
||||
plugin QueryPlugin
|
||||
expectedType qb.QueryType
|
||||
}{
|
||||
{
|
||||
description: "AI builder query",
|
||||
plugin: QueryPlugin{Kind: QueryKindAIBuilder, Spec: &AIBuilderQuerySpec{Name: "A"}},
|
||||
expectedType: qb.QueryTypeBuilderAI,
|
||||
},
|
||||
{
|
||||
description: "promql",
|
||||
plugin: QueryPlugin{Kind: QueryKindPromQL, Spec: &qb.PromQuery{Name: "A", Query: "up"}},
|
||||
@@ -209,6 +215,42 @@ func TestDashboardV2GetPanelQuery(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
// The gen_ai statement builder only reads traces, so an AI builder query
|
||||
// carries no signal of its own and unwraps to a traces builder query.
|
||||
t.Run("unwraps an AI builder query to a traces builder query", func(t *testing.T) {
|
||||
dashboard := &DashboardV2{
|
||||
Spec: DashboardSpec{
|
||||
Panels: map[string]*Panel{
|
||||
"panel-1": {
|
||||
Spec: PanelSpec{
|
||||
Plugin: PanelPlugin{Kind: PanelKindTimeSeries},
|
||||
Queries: []Query{
|
||||
{
|
||||
Kind: qb.RequestTypeTimeSeries,
|
||||
Spec: QuerySpec{
|
||||
Plugin: QueryPlugin{
|
||||
Kind: QueryKindAIBuilder,
|
||||
Spec: &AIBuilderQuerySpec{Name: "A", Signal: telemetrytypes.SignalTraces},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
req, err := dashboard.GetPanelQuery(1, 2, "panel-1")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, req.CompositeQuery.Queries, 1)
|
||||
|
||||
spec, ok := req.CompositeQuery.Queries[0].Spec.(qb.QueryBuilderQuery[qb.TraceAggregation])
|
||||
require.True(t, ok, "expected traces builder query, got %T", req.CompositeQuery.Queries[0].Spec)
|
||||
assert.Equal(t, "A", spec.Name)
|
||||
assert.Equal(t, telemetrytypes.SignalTraces, spec.Signal)
|
||||
})
|
||||
|
||||
t.Run("sets FormatTableResultForUI only for table panels", func(t *testing.T) {
|
||||
dashboard := &DashboardV2{
|
||||
Spec: DashboardSpec{
|
||||
|
||||
@@ -133,6 +133,19 @@ func TestRedactQueryPluginWrappers(t *testing.T) {
|
||||
assert.Equal(t, "A", builder.Name)
|
||||
})
|
||||
|
||||
t.Run("AI builder plugin pointer is redacted and stays a pointer", func(t *testing.T) {
|
||||
plugin := &AIBuilderQuerySpec{
|
||||
Name: "A",
|
||||
Filter: &qb.Filter{Expression: "body contains 'secret'"},
|
||||
}
|
||||
|
||||
result, ok := redactQuery(plugin).(*AIBuilderQuerySpec)
|
||||
require.True(t, ok)
|
||||
|
||||
assert.Nil(t, result.Filter)
|
||||
assert.Equal(t, "A", result.Name)
|
||||
})
|
||||
|
||||
t.Run("composite plugin redacts every sub-query envelope", func(t *testing.T) {
|
||||
composite := &qb.CompositeQuery{Queries: []qb.QueryEnvelope{
|
||||
{Type: qb.QueryTypeBuilder, Spec: qb.QueryBuilderQuery[qb.MetricAggregation]{Name: "A", Filter: &qb.Filter{Expression: "x = 1"}}},
|
||||
|
||||
@@ -93,6 +93,7 @@ type QueryPluginKind string
|
||||
|
||||
const (
|
||||
QueryKindBuilder QueryPluginKind = "signoz/BuilderQuery"
|
||||
QueryKindAIBuilder QueryPluginKind = "signoz/AIBuilderQuery"
|
||||
QueryKindComposite QueryPluginKind = "signoz/CompositeQuery"
|
||||
QueryKindFormula QueryPluginKind = "signoz/Formula"
|
||||
QueryKindPromQL QueryPluginKind = "signoz/PromQLQuery"
|
||||
@@ -101,7 +102,7 @@ const (
|
||||
)
|
||||
|
||||
func (QueryPluginKind) Enum() []any {
|
||||
return []any{QueryKindBuilder, QueryKindComposite, QueryKindFormula, QueryKindPromQL, QueryKindClickHouseSQL, QueryKindTraceOperator}
|
||||
return []any{QueryKindBuilder, QueryKindAIBuilder, QueryKindComposite, QueryKindFormula, QueryKindPromQL, QueryKindClickHouseSQL, QueryKindTraceOperator}
|
||||
}
|
||||
|
||||
type (
|
||||
@@ -159,6 +160,26 @@ func (BuilderQuerySpec) JSONSchemaOneOf() []any {
|
||||
}
|
||||
}
|
||||
|
||||
// AIBuilderQuerySpec is the spec of a signoz/AIBuilderQuery plugin: a gen_ai-scoped
|
||||
// (AI observability) traces builder query, executed as qb.QueryTypeBuilderAI. The
|
||||
// signal is implied by the kind and pinned to traces, mirroring the builder_ai_query
|
||||
// QueryEnvelope decode.
|
||||
type AIBuilderQuerySpec qb.QueryBuilderQuery[qb.TraceAggregation]
|
||||
|
||||
func (b *AIBuilderQuerySpec) UnmarshalJSON(data []byte) error {
|
||||
var spec qb.QueryBuilderQuery[qb.TraceAggregation]
|
||||
if err := json.Unmarshal(data, &spec); err != nil {
|
||||
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "invalid AI builder query spec")
|
||||
}
|
||||
spec.Signal = telemetrytypes.SignalTraces
|
||||
*b = AIBuilderQuerySpec(spec)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (AIBuilderQuerySpec) PrepareJSONSchema(s *jsonschema.Schema) error {
|
||||
return (qb.QueryBuilderQuery[qb.TraceAggregation]{}).PrepareJSONSchema(s)
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// SigNoz panel plugin specs
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
@@ -208,35 +208,6 @@ func NewGettableUnmappedModels(items []*UnmappedModel) *GettableUnmappedModels {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UpdatableLLMPricingRule) UnmarshalJSON(data []byte) error {
|
||||
type Alias UpdatableLLMPricingRule
|
||||
|
||||
var temp Alias
|
||||
if err := json.Unmarshal(data, &temp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*u = UpdatableLLMPricingRule(temp)
|
||||
return u.Validate()
|
||||
}
|
||||
|
||||
// Validate mirrors the collector's pattern check: at least one pattern, none
|
||||
// empty, all valid path.Match globs.
|
||||
func (u *UpdatableLLMPricingRule) Validate() error {
|
||||
if len(u.ModelPattern) == 0 {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodePricingRuleInvalidInput, "model %q: modelPattern must contain at least one pattern", u.Model)
|
||||
}
|
||||
for _, p := range u.ModelPattern {
|
||||
if p == "" {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodePricingRuleInvalidInput, "model %q: modelPattern must not contain an empty pattern", u.Model)
|
||||
}
|
||||
if _, err := path.Match(p, ""); err != nil {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodePricingRuleInvalidInput, "model %q: modelPattern %q is not a valid glob", u.Model, p)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewLLMPricingRuleFromUpdatable(u *UpdatableLLMPricingRule, orgID valuer.UUID, userEmail string, now time.Time) *LLMPricingRule {
|
||||
id := valuer.GenerateUUID()
|
||||
if u.ID != nil {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package llmpricingruletypes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -127,34 +126,3 @@ func TestGenerateCollectorConfig_EmptyInputPassthrough(t *testing.T) {
|
||||
assert.Equal(t, in, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatableLLMPricingRuleUnmarshalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pattern string
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "valid", pattern: `["gpt-4o*", "gpt-4o"]`},
|
||||
{name: "missing", pattern: ``, wantErr: true},
|
||||
{name: "null", pattern: `null`, wantErr: true},
|
||||
{name: "empty_list", pattern: `[]`, wantErr: true},
|
||||
{name: "empty_entry", pattern: `["gpt-4o*", ""]`, wantErr: true},
|
||||
{name: "bad_glob", pattern: `["gpt-["]`, wantErr: true},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
body := `{"modelName": "gpt-4o"}`
|
||||
if tc.pattern != "" {
|
||||
body = `{"modelName": "gpt-4o", "modelPattern": ` + tc.pattern + `}`
|
||||
}
|
||||
var req UpdatableLLMPricingRules
|
||||
err := json.Unmarshal([]byte(`{"rules": [`+body+`]}`), &req)
|
||||
if tc.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
15
tests/fixtures/alerts.py
vendored
15
tests/fixtures/alerts.py
vendored
@@ -108,23 +108,14 @@ def delete_all_rules(signoz: types.SigNoz, token: str) -> None:
|
||||
def seed_alert_rules(
|
||||
signoz: types.SigNoz,
|
||||
get_token: Callable[[str, str], str],
|
||||
notification_channel: types.TestContainerDocker,
|
||||
create_notification_channel: Callable[[dict], str],
|
||||
create_alert_rule: Callable[[dict], str],
|
||||
) -> Callable[[str, list[dict]], None]:
|
||||
) -> Callable[[dict, list[dict]], None]:
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
# create_notification_channel rather than create_webhook_notification_channel:
|
||||
# only the former deletes on teardown, and callers reuse one channel name
|
||||
# across tests, so a leaked channel fails the next create as a duplicate.
|
||||
def _seed_alert_rules(channel_name: str, rules: list[dict]) -> None:
|
||||
def _seed_alert_rules(channel_config: dict, rules: list[dict]) -> None:
|
||||
delete_all_rules(signoz, admin_token)
|
||||
create_notification_channel(
|
||||
{
|
||||
"name": channel_name,
|
||||
"webhook_configs": [{"url": notification_channel.container_configs["8080"].get(f"/alert/{channel_name}"), "send_resolved": False}],
|
||||
}
|
||||
)
|
||||
create_notification_channel(channel_config)
|
||||
for rule in rules:
|
||||
create_alert_rule(rule)
|
||||
|
||||
|
||||
@@ -31,11 +31,11 @@ logger = setup_logger(__name__)
|
||||
NOTIFIERS_TEST = [
|
||||
types.AlertManagerNotificationTestCase(
|
||||
name="slack_notifier_default_templating",
|
||||
rule_path="ruler/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
channel_config=slack_default_config,
|
||||
@@ -64,11 +64,11 @@ NOTIFIERS_TEST = [
|
||||
),
|
||||
types.AlertManagerNotificationTestCase(
|
||||
name="msteams_notifier_default_templating",
|
||||
rule_path="ruler/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
channel_config=msteams_default_config,
|
||||
@@ -149,11 +149,11 @@ NOTIFIERS_TEST = [
|
||||
),
|
||||
types.AlertManagerNotificationTestCase(
|
||||
name="pagerduty_notifier_default_templating",
|
||||
rule_path="ruler/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
channel_config=pagerduty_default_config,
|
||||
@@ -194,11 +194,11 @@ NOTIFIERS_TEST = [
|
||||
),
|
||||
types.AlertManagerNotificationTestCase(
|
||||
name="opsgenie_notifier_default_templating",
|
||||
rule_path="ruler/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
channel_config=opsgenie_default_config,
|
||||
@@ -226,11 +226,11 @@ NOTIFIERS_TEST = [
|
||||
),
|
||||
types.AlertManagerNotificationTestCase(
|
||||
name="webhook_notifier_default_templating",
|
||||
rule_path="ruler/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
channel_config=webhook_default_config,
|
||||
@@ -275,11 +275,11 @@ NOTIFIERS_TEST = [
|
||||
),
|
||||
types.AlertManagerNotificationTestCase(
|
||||
name="email_notifier_default_templating",
|
||||
rule_path="ruler/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
channel_config=email_default_config,
|
||||
|
||||
@@ -15,10 +15,10 @@ logger = setup_logger(__name__)
|
||||
|
||||
def test_webhook_notification_channel(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
notification_channel: types.TestContainerDocker,
|
||||
make_http_mocks: Callable[[types.TestContainerDocker, list[Mapping]], None],
|
||||
create_webhook_notification_channel: Callable[[str, str, dict, bool], str],
|
||||
) -> None:
|
||||
logger.info("Setting up notification channel")
|
||||
|
||||
@@ -45,6 +45,14 @@ def test_webhook_notification_channel(
|
||||
],
|
||||
)
|
||||
|
||||
# Create an alert channel using the given route
|
||||
create_webhook_notification_channel(
|
||||
channel_name=notification_channel_name,
|
||||
webhook_url=webhook_endpoint,
|
||||
http_config={},
|
||||
send_resolved=True,
|
||||
)
|
||||
|
||||
# TODO: @abhishekhugetech # pylint: disable=W0511
|
||||
# Time required for newly created Org to be registered in the alertmanager is 5 seconds in signoz.py
|
||||
# this will be fixed after [https://github.com/SigNoz/engineering-pod/issues/3800]
|
||||
@@ -21,12 +21,12 @@ from fixtures.logger import setup_logger
|
||||
TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_above_at_least_once",
|
||||
rule_path="ruler/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_above_at_least_once/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
# active requests dummy data
|
||||
data_path="ruler/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_above_at_least_once/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -44,11 +44,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_above_all_the_time",
|
||||
rule_path="ruler/test_scenarios/threshold_above_all_the_time/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_above_all_the_time/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_above_all_the_time/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_above_all_the_time/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -66,11 +66,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_above_in_total",
|
||||
rule_path="ruler/test_scenarios/threshold_above_in_total/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_above_in_total/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_above_in_total/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_above_in_total/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -96,11 +96,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_above_average",
|
||||
rule_path="ruler/test_scenarios/threshold_above_average/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_above_average/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="traces",
|
||||
data_path="ruler/test_scenarios/threshold_above_average/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_above_average/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -118,11 +118,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_above_last",
|
||||
rule_path="ruler/test_scenarios/threshold_above_last/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_above_last/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_above_last/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_above_last/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -140,11 +140,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_below_at_least_once",
|
||||
rule_path="ruler/test_scenarios/threshold_below_at_least_once/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_below_at_least_once/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="logs",
|
||||
data_path="ruler/test_scenarios/threshold_below_at_least_once/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_below_at_least_once/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -162,11 +162,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_below_all_the_time",
|
||||
rule_path="ruler/test_scenarios/threshold_below_all_the_time/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_below_all_the_time/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="logs",
|
||||
data_path="ruler/test_scenarios/threshold_below_all_the_time/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_below_all_the_time/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -184,12 +184,12 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_below_in_total",
|
||||
rule_path="ruler/test_scenarios/threshold_below_in_total/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_below_in_total/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
# one rate ~5 + rest 0.01 so it remains in total below 10
|
||||
data_path="ruler/test_scenarios/threshold_below_in_total/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_below_in_total/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -207,11 +207,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_below_average",
|
||||
rule_path="ruler/test_scenarios/threshold_below_average/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_below_average/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_below_average/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_below_average/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -229,11 +229,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_below_last",
|
||||
rule_path="ruler/test_scenarios/threshold_below_last/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_below_last/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_below_last/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_below_last/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -251,11 +251,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_equal_to_at_least_once",
|
||||
rule_path="ruler/test_scenarios/threshold_equal_to_at_least_once/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_equal_to_at_least_once/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_equal_to_at_least_once/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_equal_to_at_least_once/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -273,11 +273,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_equal_to_all_the_time",
|
||||
rule_path="ruler/test_scenarios/threshold_equal_to_all_the_time/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_equal_to_all_the_time/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_equal_to_all_the_time/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_equal_to_all_the_time/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -295,11 +295,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_equal_to_in_total",
|
||||
rule_path="ruler/test_scenarios/threshold_equal_to_in_total/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_equal_to_in_total/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_equal_to_in_total/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_equal_to_in_total/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -317,11 +317,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_equal_to_average",
|
||||
rule_path="ruler/test_scenarios/threshold_equal_to_average/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_equal_to_average/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_equal_to_average/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_equal_to_average/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -339,11 +339,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_equal_to_last",
|
||||
rule_path="ruler/test_scenarios/threshold_equal_to_last/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_equal_to_last/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_equal_to_last/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_equal_to_last/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -361,11 +361,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_not_equal_to_at_least_once",
|
||||
rule_path="ruler/test_scenarios/threshold_not_equal_to_at_least_once/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_not_equal_to_at_least_once/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_not_equal_to_at_least_once/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_not_equal_to_at_least_once/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -383,11 +383,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_not_equal_to_all_the_time",
|
||||
rule_path="ruler/test_scenarios/threshold_not_equal_to_all_the_time/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_not_equal_to_all_the_time/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_not_equal_to_all_the_time/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_not_equal_to_all_the_time/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -405,11 +405,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_not_equal_to_in_total",
|
||||
rule_path="ruler/test_scenarios/threshold_not_equal_to_in_total/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_not_equal_to_in_total/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_not_equal_to_in_total/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_not_equal_to_in_total/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -427,11 +427,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_not_equal_to_average",
|
||||
rule_path="ruler/test_scenarios/threshold_not_equal_to_average/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_not_equal_to_average/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_not_equal_to_average/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_not_equal_to_average/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -449,11 +449,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_threshold_not_equal_to_last",
|
||||
rule_path="ruler/test_scenarios/threshold_not_equal_to_last/rule.json",
|
||||
rule_path="alerts/test_scenarios/threshold_not_equal_to_last/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/threshold_not_equal_to_last/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/threshold_not_equal_to_last/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -475,11 +475,11 @@ TEST_RULES_MATCH_TYPE_AND_COMPARE_OPERATORS = [
|
||||
TEST_RULES_UNIT_CONVERSION = [
|
||||
types.AlertTestCase(
|
||||
name="test_unit_conversion_bytes_to_mb",
|
||||
rule_path="ruler/test_scenarios/unit_conversion_bytes_to_mb/rule.json",
|
||||
rule_path="alerts/test_scenarios/unit_conversion_bytes_to_mb/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/unit_conversion_bytes_to_mb/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/unit_conversion_bytes_to_mb/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -497,11 +497,11 @@ TEST_RULES_UNIT_CONVERSION = [
|
||||
),
|
||||
types.AlertTestCase(
|
||||
name="test_unit_conversion_ms_to_second",
|
||||
rule_path="ruler/test_scenarios/unit_conversion_ms_to_second/rule.json",
|
||||
rule_path="alerts/test_scenarios/unit_conversion_ms_to_second/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/unit_conversion_ms_to_second/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/unit_conversion_ms_to_second/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -523,11 +523,11 @@ TEST_RULES_UNIT_CONVERSION = [
|
||||
TEST_RULES_MISCELLANEOUS = [
|
||||
types.AlertTestCase(
|
||||
name="test_no_data_rule_test",
|
||||
rule_path="ruler/test_scenarios/no_data_rule_test/rule.json",
|
||||
rule_path="alerts/test_scenarios/no_data_rule_test/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/no_data_rule_test/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/no_data_rule_test/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -547,11 +547,11 @@ TEST_RULES_MISCELLANEOUS = [
|
||||
# after the [issue](https://github.com/SigNoz/engineering-pod/issues/3934) with alertManager is resolved
|
||||
# types.AlertTestCase(
|
||||
# name="test_multi_threshold_rule_test",
|
||||
# rule_path="ruler/test_scenarios/multi_threshold_rule_test/rule.json",
|
||||
# rule_path="alerts/test_scenarios/multi_threshold_rule_test/rule.json",
|
||||
# alert_data=[
|
||||
# types.AlertData(
|
||||
# type="metrics",
|
||||
# data_path="ruler/test_scenarios/multi_threshold_rule_test/alert_data.jsonl",
|
||||
# data_path="alerts/test_scenarios/multi_threshold_rule_test/alert_data.jsonl",
|
||||
# ),
|
||||
# ],
|
||||
# alert_expectation=types.AlertExpectation(
|
||||
@@ -29,10 +29,10 @@ def test_logs_rule_history_related_links(
|
||||
query_start_ms = int((datetime.now(tz=UTC) - timedelta(minutes=30)).timestamp() * 1000)
|
||||
|
||||
insert_alert_data(
|
||||
[types.AlertData(type="logs", data_path="ruler/test_scenarios/rule_state_history_logs/alert_data.jsonl")],
|
||||
[types.AlertData(type="logs", data_path="alerts/test_scenarios/rule_state_history_logs/alert_data.jsonl")],
|
||||
base_time=datetime.now(tz=UTC) - timedelta(minutes=5),
|
||||
)
|
||||
rule_id = create_alert_rule_with_channel("ruler/test_scenarios/rule_state_history_logs/rule.json")
|
||||
rule_id = create_alert_rule_with_channel("alerts/test_scenarios/rule_state_history_logs/rule.json")
|
||||
|
||||
(item, query_end_ms) = wait_for_firing_timeline_entry(signoz, token, rule_id, query_start_ms)
|
||||
|
||||
@@ -73,10 +73,10 @@ def test_traces_rule_history_related_links(
|
||||
query_start_ms = int((datetime.now(tz=UTC) - timedelta(minutes=30)).timestamp() * 1000)
|
||||
|
||||
insert_alert_data(
|
||||
[types.AlertData(type="traces", data_path="ruler/test_scenarios/rule_state_history_traces/alert_data.jsonl")],
|
||||
[types.AlertData(type="traces", data_path="alerts/test_scenarios/rule_state_history_traces/alert_data.jsonl")],
|
||||
base_time=datetime.now(tz=UTC) - timedelta(minutes=5),
|
||||
)
|
||||
rule_id = create_alert_rule_with_channel("ruler/test_scenarios/rule_state_history_traces/rule.json")
|
||||
rule_id = create_alert_rule_with_channel("alerts/test_scenarios/rule_state_history_traces/rule.json")
|
||||
|
||||
(item, query_end_ms) = wait_for_firing_timeline_entry(signoz, token, rule_id, query_start_ms)
|
||||
|
||||
@@ -117,10 +117,10 @@ def test_ai_traces_rule_history_related_links(
|
||||
query_start_ms = int((datetime.now(tz=UTC) - timedelta(minutes=30)).timestamp() * 1000)
|
||||
|
||||
insert_alert_data(
|
||||
[types.AlertData(type="traces", data_path="ruler/test_scenarios/rule_state_history_ai_traces/alert_data.jsonl")],
|
||||
[types.AlertData(type="traces", data_path="alerts/test_scenarios/rule_state_history_ai_traces/alert_data.jsonl")],
|
||||
base_time=datetime.now(tz=UTC) - timedelta(minutes=5),
|
||||
)
|
||||
rule_id = create_alert_rule_with_channel("ruler/test_scenarios/rule_state_history_ai_traces/rule.json")
|
||||
rule_id = create_alert_rule_with_channel("alerts/test_scenarios/rule_state_history_ai_traces/rule.json")
|
||||
|
||||
(item, query_end_ms) = wait_for_firing_timeline_entry(signoz, token, rule_id, query_start_ms)
|
||||
|
||||
@@ -14,11 +14,11 @@ from fixtures.fs import get_testdata_file_path
|
||||
|
||||
TEST_CASE = types.AlertTestCase(
|
||||
name="promql_subquery_no_step",
|
||||
rule_path="ruler/test_scenarios/promql_subquery_no_step/rule.json",
|
||||
rule_path="alerts/test_scenarios/promql_subquery_no_step/rule.json",
|
||||
alert_data=[
|
||||
types.AlertData(
|
||||
type="metrics",
|
||||
data_path="ruler/test_scenarios/promql_subquery_no_step/alert_data.jsonl",
|
||||
data_path="alerts/test_scenarios/promql_subquery_no_step/alert_data.jsonl",
|
||||
),
|
||||
],
|
||||
alert_expectation=types.AlertExpectation(
|
||||
@@ -40,7 +40,7 @@ def test_disabled_rule_does_not_evaluate_or_notify(
|
||||
A rule created with disabled: true must not be evaluated: its state must
|
||||
stay "disabled" and it must not send any notification, even though the
|
||||
inserted data would fire the rule if it were evaluated. The companion
|
||||
scenario threshold_above_at_least_once in 01_basic_alert_conditions.py
|
||||
scenario threshold_above_at_least_once in 02_basic_alert_conditions.py
|
||||
uses the same data shape and fires when the rule is enabled.
|
||||
"""
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
@@ -78,12 +78,12 @@ def test_disabled_rule_does_not_evaluate_or_notify(
|
||||
|
||||
# Insert alert data that would fire the rule if it were evaluated
|
||||
insert_alert_data(
|
||||
[types.AlertData(type="metrics", data_path="ruler/test_scenarios/disabled_rule/alert_data.jsonl")],
|
||||
[types.AlertData(type="metrics", data_path="alerts/test_scenarios/disabled_rule/alert_data.jsonl")],
|
||||
base_time=datetime.now(tz=UTC) - timedelta(minutes=5),
|
||||
)
|
||||
|
||||
# Create the disabled alert rule
|
||||
rule_path = get_testdata_file_path("ruler/test_scenarios/disabled_rule/rule.json")
|
||||
rule_path = get_testdata_file_path("alerts/test_scenarios/disabled_rule/rule.json")
|
||||
with open(rule_path, encoding="utf-8") as f:
|
||||
rule_data = json.loads(f.read())
|
||||
update_rule_channel_name(rule_data, notification_channel_name)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user