Compare commits

...

5 Commits

Author SHA1 Message Date
primus-bot[bot]
0d281e694d chore(release): bump to v0.72.0 (#7096)
#### Summary
 - Release SigNoz v0.72.0
 - Bump SigNoz OTel Collector to v0.111.27

 Created by [Primus-Bot](https://github.com/apps/primus-bot)

Co-authored-by: primus-bot[bot] <171087277+primus-bot[bot]@users.noreply.github.com>
2025-02-12 15:52:55 +05:30
SagarRajput-7
b772a0fb56 feat: changed visible Panel name for Value Panel to Number (#7092)
* feat: changed visible Panel name for Value Panel to Number

* feat: added display values to enum
2025-02-12 11:29:07 +05:30
Nityananda Gohain
962e75c6d4 fix: move migrations to bun (#7055)
* fix: move migrations to bun

* fix: use anonymous structs and move modes to types package

* fix: minor changes after tests

* fix: remove bun relations and add foreign keys

* fix: minor changes

* Update pkg/types/agent.go

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* fix: address minor comments

* fix: use bun create index

* fix: remove extra comma

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-02-12 09:58:49 +05:30
SagarRajput-7
42fad23cb0 feat: added feat to add new panel in a section (#6999)
* feat: added common util and took possible space available in last row in account

* feat: added different test cases

* feat: remove console.log

* feat: added default value to widgetWidth

* feat: added feat to add new panel in a section

* feat: added different test cases
2025-02-11 22:55:42 +05:30
SagarRajput-7
d22ecb9f7c feat: allowed user to edit panel while the API is loading (#7007)
* feat: allowed user to edit panel while the API is loading

* feat: added test case
2025-02-11 18:32:13 +05:30
41 changed files with 1344 additions and 429 deletions

View File

@@ -181,7 +181,7 @@ services:
- query-service
query-service:
!!merge <<: *db-depend
image: signoz/query-service:0.71.0
image: signoz/query-service:0.72.0
command:
- --config=/root/config/prometheus.yml
- --use-logs-new-schema=true
@@ -214,7 +214,7 @@ services:
retries: 3
frontend:
!!merge <<: *common
image: signoz/frontend:0.71.0
image: signoz/frontend:0.72.0
depends_on:
- alertmanager
- query-service
@@ -224,7 +224,7 @@ services:
- ../common/signoz/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector:
!!merge <<: *db-depend
image: signoz/signoz-otel-collector:0.111.26
image: signoz/signoz-otel-collector:0.111.27
command:
- --config=/etc/otel-collector-config.yaml
- --manager-config=/etc/manager-config.yaml

View File

@@ -117,7 +117,7 @@ services:
- query-service
query-service:
!!merge <<: *db-depend
image: signoz/query-service:0.71.0
image: signoz/query-service:0.72.0
command:
- --config=/root/config/prometheus.yml
- --use-logs-new-schema=true
@@ -150,7 +150,7 @@ services:
retries: 3
frontend:
!!merge <<: *common
image: signoz/frontend:0.71.0
image: signoz/frontend:0.72.0
depends_on:
- alertmanager
- query-service
@@ -160,7 +160,7 @@ services:
- ../common/signoz/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector:
!!merge <<: *db-depend
image: signoz/signoz-otel-collector:0.111.26
image: signoz/signoz-otel-collector:0.111.27
command:
- --config=/etc/otel-collector-config.yaml
- --manager-config=/etc/manager-config.yaml

View File

@@ -188,7 +188,7 @@ services:
condition: service_healthy
query-service:
!!merge <<: *db-depend
image: signoz/query-service:${DOCKER_TAG:-0.71.0}
image: signoz/query-service:${DOCKER_TAG:-0.72.0}
container_name: signoz-query-service
command:
- --config=/root/config/prometheus.yml
@@ -222,7 +222,7 @@ services:
retries: 3
frontend:
!!merge <<: *common
image: signoz/frontend:${DOCKER_TAG:-0.71.0}
image: signoz/frontend:${DOCKER_TAG:-0.72.0}
container_name: signoz-frontend
depends_on:
- alertmanager
@@ -234,7 +234,7 @@ services:
# TODO: support otel-collector multiple replicas. Nginx/Traefik for loadbalancing?
otel-collector:
!!merge <<: *db-depend
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.26}
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.27}
container_name: signoz-otel-collector
command:
- --config=/etc/otel-collector-config.yaml

View File

@@ -121,7 +121,7 @@ services:
condition: service_healthy
query-service:
!!merge <<: *db-depend
image: signoz/query-service:${DOCKER_TAG:-0.71.0}
image: signoz/query-service:${DOCKER_TAG:-0.72.0}
container_name: signoz-query-service
command:
- --config=/root/config/prometheus.yml
@@ -157,7 +157,7 @@ services:
retries: 3
frontend:
!!merge <<: *common
image: signoz/frontend:${DOCKER_TAG:-0.71.0}
image: signoz/frontend:${DOCKER_TAG:-0.72.0}
container_name: signoz-frontend
depends_on:
- alertmanager
@@ -168,7 +168,7 @@ services:
- ../common/signoz/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector:
!!merge <<: *db-depend
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.26}
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.27}
container_name: signoz-otel-collector
command:
- --config=/etc/otel-collector-config.yaml

View File

@@ -121,7 +121,7 @@ services:
condition: service_healthy
query-service:
!!merge <<: *db-depend
image: signoz/query-service:${DOCKER_TAG:-0.71.0}
image: signoz/query-service:${DOCKER_TAG:-0.72.0}
container_name: signoz-query-service
command:
- --config=/root/config/prometheus.yml
@@ -155,7 +155,7 @@ services:
retries: 3
frontend:
!!merge <<: *common
image: signoz/frontend:${DOCKER_TAG:-0.71.0}
image: signoz/frontend:${DOCKER_TAG:-0.72.0}
container_name: signoz-frontend
depends_on:
- alertmanager
@@ -166,7 +166,7 @@ services:
- ../common/signoz/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector:
!!merge <<: *db-depend
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.26}
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.27}
container_name: signoz-otel-collector
command:
- --config=/etc/otel-collector-config.yaml

View File

@@ -407,3 +407,13 @@ export const HAVING_OPERATORS: string[] = [
OPERATORS['<='],
OPERATORS['<'],
];
export enum PanelDisplay {
TIME_SERIES = 'Time Series',
VALUE = 'Number',
TABLE = 'Table',
LIST = 'List',
BAR = 'Bar',
PIE = 'Pie',
HISTOGRAM = 'Histogram',
}

View File

@@ -17,6 +17,7 @@ export default function DashboardEmptyState(): JSX.Element {
selectedDashboard,
isDashboardLocked,
handleToggleDashboardSlider,
setSelectedRowWidgetId,
} = useDashboard();
const { user } = useAppContext();
@@ -34,6 +35,7 @@ export default function DashboardEmptyState(): JSX.Element {
const [addPanelPermission] = useComponentPermission(permissions, userRole);
const onEmptyWidgetHandler = useCallback(() => {
setSelectedRowWidgetId(null);
handleToggleDashboardSlider(true);
logEvent('Dashboard Detail: Add new panel clicked', {
dashboardId: selectedDashboard?.uuid,

View File

@@ -0,0 +1,233 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { PANEL_TYPES } from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import { AppProvider } from 'providers/App/App';
import MockQueryClientProvider from 'providers/test/MockQueryClientProvider';
import { Provider } from 'react-redux';
import store from 'store';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { EQueryType } from 'types/common/dashboard';
import { DataSource } from 'types/common/queryBuilder';
import {
MENUITEM_KEYS_VS_LABELS,
MenuItemKeys,
} from '../WidgetHeader/contants';
import { WidgetGraphComponentProps } from './types';
import WidgetGraphComponent from './WidgetGraphComponent';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: (): { pathname: string } => ({
pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.DASHBOARD}/624652db-6097-42f5-bbca-e9012901db00`,
}),
}));
jest.mock('uplot', () => {
const paths = {
spline: jest.fn(),
bars: jest.fn(),
};
const uplotMock = jest.fn(() => ({
paths,
}));
return {
paths,
default: uplotMock,
};
});
// Mock data
const mockProps: WidgetGraphComponentProps = {
widget: {
bucketCount: 30,
bucketWidth: 0,
columnUnits: {},
description: '',
fillSpans: false,
id: '17f905f6-d355-46bd-a78e-cbc87e6f58cc',
isStacked: false,
mergeAllActiveQueries: false,
nullZeroValues: 'zero',
opacity: '1',
panelTypes: PANEL_TYPES.VALUE,
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.String,
id: 'span_id--string----true',
isColumn: true,
isJSON: false,
key: 'span_id',
type: '',
},
aggregateOperator: 'count_distinct',
dataSource: DataSource.TRACES,
disabled: false,
expression: 'A',
filters: {
items: [],
op: 'AND',
},
functions: [],
groupBy: [],
having: [],
legend: '',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'last',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'count_distinct',
},
],
queryFormulas: [],
},
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
id: '47449208-2c76-4465-9c62-a37fb4f5f11f',
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: EQueryType.QUERY_BUILDER,
},
selectedLogFields: [
{
dataType: 'string',
name: 'body',
type: '',
},
{
dataType: 'string',
name: 'timestamp',
type: '',
},
],
selectedTracesFields: [],
softMax: 0,
softMin: 0,
stackedBarChart: false,
thresholds: [],
timePreferance: 'GLOBAL_TIME',
title: 'Test Dashboard',
yAxisUnit: 'none',
},
queryResponse: {
status: 'loading',
isLoading: true,
isSuccess: false,
isError: false,
isIdle: false,
dataUpdatedAt: 0,
error: null,
errorUpdatedAt: 0,
failureCount: 0,
errorUpdateCount: 0,
isFetched: false,
isFetchedAfterMount: false,
isFetching: true,
isRefetching: false,
isLoadingError: false,
isPlaceholderData: false,
isPreviousData: false,
isRefetchError: false,
isStale: true,
data: undefined,
refetch: jest.fn(),
remove: jest.fn(),
},
errorMessage: '',
version: 'v4',
headerMenuList: [
MenuItemKeys.View,
MenuItemKeys.Clone,
MenuItemKeys.Delete,
MenuItemKeys.Edit,
MenuItemKeys.CreateAlerts,
],
isWarning: false,
isFetchingResponse: false,
setRequestData: jest.fn(),
onClickHandler: jest.fn(),
onDragSelect: jest.fn(),
openTracesButton: false,
onOpenTraceBtnClick: jest.fn(),
};
// Mock useDashabord hook
jest.mock('providers/Dashboard/Dashboard', () => ({
useDashboard: (): any => ({
selectedDashboard: {
data: {
variables: [],
},
},
}),
}));
describe('WidgetGraphComponent', () => {
it('should show correct menu items when hovering over more options while loading', async () => {
const { getByTestId, findByRole, getByText, container } = render(
<MockQueryClientProvider>
<Provider store={store}>
<AppProvider>
<WidgetGraphComponent
widget={mockProps.widget}
queryResponse={mockProps.queryResponse}
errorMessage={mockProps.errorMessage}
version={mockProps.version}
headerMenuList={mockProps.headerMenuList}
isWarning={mockProps.isWarning}
isFetchingResponse={mockProps.isFetchingResponse}
setRequestData={mockProps.setRequestData}
onClickHandler={mockProps.onClickHandler}
onDragSelect={mockProps.onDragSelect}
openTracesButton={mockProps.openTracesButton}
onOpenTraceBtnClick={mockProps.onOpenTraceBtnClick}
/>
</AppProvider>
</Provider>
</MockQueryClientProvider>,
);
expect(getByText('Test Dashboard')).toBeInTheDocument();
// check if skeleton is rendered
const skeleton = container.querySelector('.ant-skeleton');
expect(skeleton).toBeInTheDocument();
const moreOptionsButton = getByTestId('widget-header-options');
fireEvent.mouseEnter(moreOptionsButton);
const menu = await findByRole('menu');
expect(menu).toBeInTheDocument();
// Check if all menu items are present
const expectedMenuItems = [
MENUITEM_KEYS_VS_LABELS[MenuItemKeys.View],
MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Clone],
MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Delete],
MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Edit],
MENUITEM_KEYS_VS_LABELS[MenuItemKeys.CreateAlerts],
];
// check that menu is visible
expectedMenuItems.forEach((item) => {
expect(screen.getByText(item)).toBeInTheDocument();
});
});
});

View File

@@ -229,21 +229,6 @@ function WidgetGraphComponent({
const [searchTerm, setSearchTerm] = useState<string>('');
const loadingState =
(queryResponse.isLoading || queryResponse.status === 'idle') &&
widget.panelTypes !== PANEL_TYPES.LIST;
if (loadingState) {
return (
<Skeleton
style={{
height: '100%',
padding: '16px',
}}
/>
);
}
return (
<div
style={{

View File

@@ -133,7 +133,8 @@
.menu-content {
.section-1 {
.rename-btn {
.rename-btn,
.new-panel-btn {
display: flex;
align-items: center;
gap: 6px;
@@ -150,6 +151,10 @@
margin-inline-end: 0px;
}
}
.rename-btn {
padding-bottom: 10px;
}
}
.section-2 {

View File

@@ -65,6 +65,7 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
isDashboardLocked,
dashboardQueryRangeCalled,
setDashboardQueryRangeCalled,
setSelectedRowWidgetId,
} = useDashboard();
const { data } = selectedDashboard || {};
const { pathname } = useLocation();
@@ -174,6 +175,7 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
updateDashboardMutation.mutate(updatedDashboard, {
onSuccess: (updatedDashboard) => {
setSelectedRowWidgetId(null);
if (updatedDashboard.payload) {
if (updatedDashboard.payload.data.layout)
setLayouts(sortLayout(updatedDashboard.payload.data.layout));

View File

@@ -1,7 +1,12 @@
import { Button, Popover } from 'antd';
import { EllipsisIcon, PenLine, X } from 'lucide-react';
import useComponentPermission from 'hooks/useComponentPermission';
import { EllipsisIcon, PenLine, Plus, X } from 'lucide-react';
import { useAppContext } from 'providers/App/App';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useState } from 'react';
import { Layout } from 'react-grid-layout';
import { ROLES, USER_ROLES } from 'types/roles';
import { ComponentTypes } from 'utils/permission';
interface WidgetRowHeaderProps {
rowWidgetProperties: {
@@ -27,6 +32,23 @@ export function WidgetRowHeader(props: WidgetRowHeaderProps): JSX.Element {
id,
} = props;
const [isRowSettingsOpen, setIsRowSettingsOpen] = useState<boolean>(false);
const {
handleToggleDashboardSlider,
selectedDashboard,
isDashboardLocked,
setSelectedRowWidgetId,
} = useDashboard();
const permissions: ComponentTypes[] = ['add_panel'];
const { user } = useAppContext();
const userRole: ROLES | null =
selectedDashboard?.created_by === user?.email
? (USER_ROLES.AUTHOR as ROLES)
: user.role;
const [addPanelPermission] = useComponentPermission(permissions, userRole);
return (
<Popover
open={isRowSettingsOpen}
@@ -52,6 +74,20 @@ export function WidgetRowHeader(props: WidgetRowHeaderProps): JSX.Element {
Rename
</Button>
</section>
<section className="section-1">
<Button
className="new-panel-btn"
type="text"
disabled={!editWidget && addPanelPermission && !isDashboardLocked}
icon={<Plus size={14} />}
onClick={(): void => {
setSelectedRowWidgetId(id);
handleToggleDashboardSlider(true);
}}
>
New Panel
</Button>
</section>
{!rowWidgetProperties.collapsed && (
<section className="section-2">
<Button

View File

@@ -1,5 +1,5 @@
import { Color } from '@signozhq/design-tokens';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { PANEL_TYPES, PanelDisplay } from 'constants/queryBuilder';
import {
BarChart3,
LineChart,
@@ -13,37 +13,37 @@ const Items: ItemsProps[] = [
{
name: PANEL_TYPES.TIME_SERIES,
icon: <LineChart size={16} color={Color.BG_ROBIN_400} />,
display: 'Time Series',
display: PanelDisplay.TIME_SERIES,
},
{
name: PANEL_TYPES.VALUE,
icon: <SigmaSquare size={16} color={Color.BG_ROBIN_400} />,
display: 'Value',
display: PanelDisplay.VALUE,
},
{
name: PANEL_TYPES.TABLE,
icon: <Table size={16} color={Color.BG_ROBIN_400} />,
display: 'Table',
display: PanelDisplay.TABLE,
},
{
name: PANEL_TYPES.LIST,
icon: <List size={16} color={Color.BG_ROBIN_400} />,
display: 'List',
display: PanelDisplay.LIST,
},
{
name: PANEL_TYPES.BAR,
icon: <BarChart3 size={16} color={Color.BG_ROBIN_400} />,
display: 'Bar',
display: PanelDisplay.BAR,
},
{
name: PANEL_TYPES.PIE,
icon: <PieChart size={16} color={Color.BG_ROBIN_400} />,
display: 'Pie',
display: PanelDisplay.PIE,
},
{
name: PANEL_TYPES.HISTOGRAM,
icon: <BarChart3 size={16} color={Color.BG_ROBIN_400} />,
display: 'Histogram',
display: PanelDisplay.HISTOGRAM,
},
];

View File

@@ -100,6 +100,7 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
listSortOrder,
setSelectedDashboard,
handleToggleDashboardSlider,
setSelectedRowWidgetId,
handleDashboardLockToggle,
} = useDashboard();
@@ -157,6 +158,7 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
const [addPanelPermission] = useComponentPermission(permissions, userRole);
const onEmptyWidgetHandler = useCallback(() => {
setSelectedRowWidgetId(null);
handleToggleDashboardSlider(true);
logEvent('Dashboard Detail: Add new panel clicked', {
dashboardId: selectedDashboard?.uuid,

View File

@@ -4,7 +4,7 @@ import './RightContainer.styles.scss';
import { Input, InputNumber, Select, Space, Switch, Typography } from 'antd';
import TimePreference from 'components/TimePreferenceDropDown';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { PANEL_TYPES, PanelDisplay } from 'constants/queryBuilder';
import GraphTypes, {
ItemsProps,
} from 'container/NewDashboard/ComponentsSlider/menuItems';
@@ -212,7 +212,8 @@ function RightContainer({
defaultValue={yAxisUnit}
onSelect={setYAxisUnit}
fieldLabel={
selectedGraphType === 'Value' || selectedGraphType === 'Pie'
selectedGraphType === PanelDisplay.VALUE ||
selectedGraphType === PanelDisplay.PIE
? 'Unit'
: 'Y Axis Unit'
}

View File

@@ -6,7 +6,7 @@
// - Handling multiple rows correctly
// - Handling widgets with different heights
import { placeWidgetAtBottom } from '../utils';
import { placeWidgetAtBottom, placeWidgetBetweenRows } from '../utils';
describe('placeWidgetAtBottom', () => {
it('should place widget at (0,0) when layout is empty', () => {
@@ -90,3 +90,129 @@ describe('placeWidgetAtBottom', () => {
});
});
});
describe('placeWidgetBetweenRows', () => {
it('should return single widget layout when layout is empty', () => {
const result = placeWidgetBetweenRows('widget1', [], 'currentRow');
expect(result).toEqual([
{
i: 'widget1',
x: 0,
y: 0,
w: 6,
h: 6,
},
]);
});
it('should place widget at the end of the layout when no nextRowId is provided', () => {
const existingLayout = [
{ i: 'widget1', x: 0, y: 0, w: 6, h: 6 },
{ i: 'widget2', x: 6, y: 0, w: 6, h: 6 },
];
const result = placeWidgetBetweenRows('widget3', existingLayout, 'widget2');
expect(result).toEqual([
{ i: 'widget1', x: 0, y: 0, w: 6, h: 6 },
{ i: 'widget2', x: 6, y: 0, w: 6, h: 6 },
{ i: 'widget3', x: 0, y: 6, w: 6, h: 6 },
]);
});
it('should place widget between current and next row', () => {
const existingLayout = [
{
h: 1,
i: "'widget1'",
maxH: 1,
minH: 1,
minW: 12,
moved: false,
static: false,
w: 12,
x: 0,
y: 0,
},
{ i: 'widget2', x: 6, y: 0, w: 6, h: 6 },
{
h: 1,
i: 'widget3',
maxH: 1,
minH: 1,
minW: 12,
moved: false,
static: false,
w: 12,
x: 0,
y: 7,
},
];
const result = placeWidgetBetweenRows(
'widget4',
existingLayout,
'widget1',
'widget3',
);
expect(result).toEqual([
{
h: 1,
i: "'widget1'",
maxH: 1,
minH: 1,
minW: 12,
moved: false,
static: false,
w: 12,
x: 0,
y: 0,
},
{
h: 6,
i: 'widget2',
w: 6,
x: 6,
y: 0,
},
{
h: 6,
i: 'widget4',
w: 6,
x: 0,
y: 6,
},
{
h: 1,
i: 'widget3',
maxH: 1,
minH: 1,
minW: 12,
moved: false,
static: false,
w: 12,
x: 0,
y: 7,
},
]);
});
it('should respect custom widget dimensions', () => {
const existingLayout = [{ i: 'widget1', x: 0, y: 0, w: 12, h: 4 }];
const result = placeWidgetBetweenRows(
'widget2',
existingLayout,
'widget1',
null,
8,
3,
);
expect(result).toEqual([
{ i: 'widget1', x: 0, y: 0, w: 12, h: 4 },
{ i: 'widget2', x: 0, y: 4, w: 8, h: 3 },
]);
});
});

View File

@@ -7,7 +7,11 @@ import logEvent from 'api/common/logEvent';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { FeatureKeys } from 'constants/features';
import { QueryParams } from 'constants/query';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import {
initialQueriesMap,
PANEL_GROUP_TYPES,
PANEL_TYPES,
} from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import { DashboardShortcuts } from 'constants/shortcuts/DashboardShortcuts';
import { DEFAULT_BUCKET_COUNT } from 'container/PanelWrapper/constants';
@@ -20,7 +24,7 @@ import useUrlQuery from 'hooks/useUrlQuery';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import history from 'lib/history';
import { defaultTo, isUndefined } from 'lodash-es';
import { defaultTo, isEmpty, isUndefined } from 'lodash-es';
import { Check, X } from 'lucide-react';
import { DashboardWidgetPageParams } from 'pages/DashboardWidget';
import { useAppContext } from 'providers/App/App';
@@ -59,6 +63,7 @@ import {
getIsQueryModified,
handleQueryChange,
placeWidgetAtBottom,
placeWidgetBetweenRows,
} from './utils';
function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
@@ -66,6 +71,8 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
selectedDashboard,
setSelectedDashboard,
setToScrollWidgetId,
selectedRowWidgetId,
setSelectedRowWidgetId,
} = useDashboard();
const { t } = useTranslation(['dashboard']);
@@ -367,11 +374,33 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
const widgetId = query.get('widgetId') || '';
let updatedLayout = selectedDashboard.data.layout || [];
if (isNewDashboard) {
if (isNewDashboard && isEmpty(selectedRowWidgetId)) {
const newLayoutItem = placeWidgetAtBottom(widgetId, updatedLayout);
updatedLayout = [...updatedLayout, newLayoutItem];
}
if (isNewDashboard && selectedRowWidgetId) {
// Find the next row by looking through remaining layout items
const currentIndex = updatedLayout.findIndex(
(e) => e.i === selectedRowWidgetId,
);
const nextRowIndex = updatedLayout.findIndex(
(item, index) =>
index > currentIndex &&
widgets?.find((w) => w.id === item.i)?.panelTypes ===
PANEL_GROUP_TYPES.ROW,
);
const nextRowId = nextRowIndex !== -1 ? updatedLayout[nextRowIndex].i : null;
const newLayoutItem = placeWidgetBetweenRows(
widgetId,
updatedLayout,
selectedRowWidgetId,
nextRowId,
);
updatedLayout = newLayoutItem;
}
const dashboard: Dashboard = {
...selectedDashboard,
uuid: selectedDashboard.uuid,
@@ -437,6 +466,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
updateDashboardMutation.mutateAsync(dashboard, {
onSuccess: () => {
setSelectedRowWidgetId(null);
setSelectedDashboard(dashboard);
setToScrollWidgetId(selectedWidget?.id || '');
history.push({
@@ -449,16 +479,19 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
selectedDashboard,
query,
isNewDashboard,
preWidgets,
selectedRowWidgetId,
afterWidgets,
selectedWidget,
selectedTime.enum,
graphType,
currentQuery,
afterWidgets,
preWidgets,
updateDashboardMutation,
handleError,
widgets,
setSelectedDashboard,
setToScrollWidgetId,
setSelectedRowWidgetId,
dashboardId,
]);

View File

@@ -631,3 +631,43 @@ export const placeWidgetAtBottom = (
h: widgetHeight || 6,
};
};
export const placeWidgetBetweenRows = (
widgetId: string,
layout: Layout[],
_currentRowId: string,
nextRowId?: string | null,
widgetWidth?: number,
widgetHeight?: number,
): Layout[] => {
if (layout.length === 0) {
return [
{
i: widgetId,
x: 0,
y: 0,
w: widgetWidth || 6,
h: widgetHeight || 6,
},
];
}
const nextRowIndex = nextRowId
? layout.findIndex((item) => item.i === nextRowId)
: -1;
// slice the layout from current row to next row
const sectionWidgets =
nextRowIndex === -1 ? layout : layout.slice(0, nextRowIndex);
const newWidgetLayout = placeWidgetAtBottom(
widgetId,
sectionWidgets,
widgetWidth,
widgetHeight,
);
const remainingWidgets = nextRowIndex === -1 ? [] : layout.slice(nextRowIndex);
// add new layout in between the sectionWidgets and the rest of the layout
return [...sectionWidgets, newWidgetLayout, ...remainingWidgets];
};

View File

@@ -71,6 +71,8 @@ const DashboardContext = createContext<IDashboardContext>({
setVariablesToGetUpdated: () => {},
dashboardQueryRangeCalled: false,
setDashboardQueryRangeCalled: () => {},
selectedRowWidgetId: '',
setSelectedRowWidgetId: () => {},
});
interface Props {
@@ -87,6 +89,10 @@ export function DashboardProvider({
const [isDashboardLocked, setIsDashboardLocked] = useState<boolean>(false);
const [selectedRowWidgetId, setSelectedRowWidgetId] = useState<string | null>(
null,
);
const [
dashboardQueryRangeCalled,
setDashboardQueryRangeCalled,
@@ -416,6 +422,8 @@ export function DashboardProvider({
setVariablesToGetUpdated,
dashboardQueryRangeCalled,
setDashboardQueryRangeCalled,
selectedRowWidgetId,
setSelectedRowWidgetId,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[
@@ -435,6 +443,8 @@ export function DashboardProvider({
setVariablesToGetUpdated,
dashboardQueryRangeCalled,
setDashboardQueryRangeCalled,
selectedRowWidgetId,
setSelectedRowWidgetId,
],
);

View File

@@ -45,4 +45,6 @@ export interface IDashboardContext {
setVariablesToGetUpdated: React.Dispatch<React.SetStateAction<string[]>>;
dashboardQueryRangeCalled: boolean;
setDashboardQueryRangeCalled: (value: boolean) => void;
selectedRowWidgetId: string | null;
setSelectedRowWidgetId: React.Dispatch<React.SetStateAction<string | null>>;
}

View File

@@ -6706,6 +6706,11 @@ core-js@^2.4.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
core-js@^3.38.1:
version "3.40.0"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.40.0.tgz#2773f6b06877d8eda102fc42f828176437062476"
integrity sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==
core-util-is@~1.0.0:
version "1.0.3"
resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz"
@@ -13118,14 +13123,15 @@ postcss@8.4.38, postcss@^8.0.0, postcss@^8.1.1, postcss@^8.3.7, postcss@^8.4.21,
picocolors "^1.0.0"
source-map-js "^1.2.0"
posthog-js@1.160.3:
version "1.160.3"
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.160.3.tgz#17c8af4c9ffa2d795d925ca1e7146e61cd5ccabd"
integrity sha512-mGvxOIlWPtdPx8EI0MQ81wNKlnH2K0n4RqwQOl044b34BCKiFVzZ7Hc7geMuZNaRAvCi5/5zyGeWHcAYZQxiMQ==
posthog-js@1.215.5:
version "1.215.5"
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.215.5.tgz#0512cfdb919da960b809c5f686ca147f9c2922ba"
integrity sha512-42lPur+xvkp51pHz2FQ7Y+KHdZ4eQSNIhUO03EECvc2UsmnM0FiVTrF1bcLwHZMaWfR26gOeuOAAjTUV9tinJg==
dependencies:
core-js "^3.38.1"
fflate "^0.4.8"
preact "^10.19.3"
web-vitals "^4.0.1"
web-vitals "^4.2.0"
preact@^10.19.3:
version "10.22.0"
@@ -16465,10 +16471,10 @@ web-vitals@^0.2.4:
resolved "https://registry.npmjs.org/web-vitals/-/web-vitals-0.2.4.tgz"
integrity sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg==
web-vitals@^4.0.1:
version "4.2.0"
resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.0.tgz#008949ab79717a68ccaaa3c4371cbc7bbbd78a92"
integrity sha512-ohj72kbtVWCpKYMxcbJ+xaOBV3En76hW47j52dG+tEGG36LZQgfFw5yHl9xyjmosy3XUMn8d/GBUAy4YPM839w==
web-vitals@^4.2.0:
version "4.2.4"
resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.4.tgz#1d20bc8590a37769bd0902b289550936069184b7"
integrity sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==
web-worker@^1.2.0:
version "1.2.0"

View File

@@ -2,6 +2,7 @@ package sqlmigration
import (
"context"
"time"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
@@ -27,12 +28,16 @@ func (migration *addDataMigrations) Register(migrations *migrate.Migrations) err
func (migration *addDataMigrations) Up(ctx context.Context, db *bun.DB) error {
// table:data_migrations
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS data_migrations (
id SERIAL PRIMARY KEY,
version VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
succeeded BOOLEAN NOT NULL DEFAULT FALSE
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:data_migrations"`
ID int `bun:"id,pk,autoincrement"`
Version string `bun:"version,unique,notnull,type:VARCHAR(255)"`
CreatedAt time.Time `bun:"created_at,notnull,default:current_timestamp"`
Succeeded bool `bun:"succeeded,notnull,default:false"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package sqlmigration
import (
"context"
"time"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
@@ -27,92 +28,125 @@ func (migration *addOrganization) Register(migrations *migrate.Migrations) error
}
func (migration *addOrganization) Up(ctx context.Context, db *bun.DB) error {
// table:invites
if _, err := db.NewRaw(`CREATE TABLE IF NOT EXISTS invites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
token TEXT NOT NULL,
created_at INTEGER NOT NULL,
role TEXT NOT NULL,
org_id TEXT NOT NULL,
FOREIGN KEY(org_id) REFERENCES organizations(id)
)`).Exec(ctx); err != nil {
return err
}
// table:organizations
if _, err := db.NewRaw(`CREATE TABLE IF NOT EXISTS organizations (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
created_at INTEGER NOT NULL,
is_anonymous INTEGER NOT NULL DEFAULT 0 CHECK(is_anonymous IN (0,1)),
has_opted_updates INTEGER NOT NULL DEFAULT 1 CHECK(has_opted_updates IN (0,1))
)`).Exec(ctx); err != nil {
return err
}
// table:users
if _, err := db.NewRaw(`CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
created_at INTEGER NOT NULL,
profile_picture_url TEXT,
group_id TEXT NOT NULL,
org_id TEXT NOT NULL,
FOREIGN KEY(group_id) REFERENCES groups(id),
FOREIGN KEY(org_id) REFERENCES organizations(id)
)`).Exec(ctx); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:organizations"`
ID string `bun:"id,pk,type:text"`
Name string `bun:"name,type:text,notnull"`
CreatedAt int `bun:"created_at,notnull"`
IsAnonymous int `bun:"is_anonymous,notnull,default:0,CHECK(is_anonymous IN (0,1))"`
HasOptedUpdates int `bun:"has_opted_updates,notnull,default:1,CHECK(has_opted_updates IN (0,1))"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
// table:groups
if _, err := db.NewRaw(`CREATE TABLE IF NOT EXISTS groups (
id TEXT PRIMARY KEY,
name TEXT NOT NULL UNIQUE
)`).Exec(ctx); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:groups"`
ID string `bun:"id,pk,type:text"`
Name string `bun:"name,type:text,notnull,unique"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
// table:users
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:users"`
ID string `bun:"id,pk,type:text"`
Name string `bun:"name,type:text,notnull"`
Email string `bun:"email,type:text,notnull,unique"`
Password string `bun:"password,type:text,notnull"`
CreatedAt int `bun:"created_at,notnull"`
ProfilePictureURL string `bun:"profile_picture_url,type:text"`
GroupID string `bun:"group_id,type:text,notnull"`
OrgID string `bun:"org_id,type:text,notnull"`
}{}).
ForeignKey(`("org_id") REFERENCES "organizations" ("id")`).
ForeignKey(`("group_id") REFERENCES "groups" ("id")`).
IfNotExists().
Exec(ctx); err != nil {
return err
}
// table:invites
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:invites"`
ID int `bun:"id,pk,autoincrement"`
Name string `bun:"name,type:text,notnull"`
Email string `bun:"email,type:text,notnull,unique"`
Token string `bun:"token,type:text,notnull"`
CreatedAt int `bun:"created_at,notnull"`
Role string `bun:"role,type:text,notnull"`
OrgID string `bun:"org_id,type:text,notnull"`
}{}).
ForeignKey(`("org_id") REFERENCES "organizations" ("id")`).
IfNotExists().
Exec(ctx); err != nil {
return err
}
// table:reset_password_request
if _, err := db.NewRaw(`CREATE TABLE IF NOT EXISTS reset_password_request (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL,
token TEXT NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id)
)`).Exec(ctx); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:reset_password_request"`
ID int `bun:"id,pk,autoincrement"`
Token string `bun:"token,type:text,notnull"`
UserID string `bun:"user_id,type:text,notnull"`
}{}).
ForeignKey(`("user_id") REFERENCES "users" ("id")`).
IfNotExists().
Exec(ctx); err != nil {
return err
}
// table:user_flags
if _, err := db.NewRaw(`CREATE TABLE IF NOT EXISTS user_flags (
user_id TEXT PRIMARY KEY,
flags TEXT,
FOREIGN KEY(user_id) REFERENCES users(id)
)`).Exec(ctx); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:user_flags"`
UserID string `bun:"user_id,pk,type:text,notnull"`
Flags string `bun:"flags,type:text"`
}{}).
ForeignKey(`("user_id") REFERENCES "users" ("id")`).
IfNotExists().
Exec(ctx); err != nil {
return err
}
// table:apdex_settings
if _, err := db.NewRaw(`CREATE TABLE IF NOT EXISTS apdex_settings (
service_name TEXT PRIMARY KEY,
threshold FLOAT NOT NULL,
exclude_status_codes TEXT NOT NULL
)`).Exec(ctx); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:apdex_settings"`
ServiceName string `bun:"service_name,pk,type:text"`
Threshold float64 `bun:"threshold,type:float,notnull"`
ExcludeStatusCodes string `bun:"exclude_status_codes,type:text,notnull"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
// table:ingestion_keys
if _, err := db.NewRaw(`CREATE TABLE IF NOT EXISTS ingestion_keys (
key_id TEXT PRIMARY KEY,
name TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ingestion_key TEXT NOT NULL,
ingestion_url TEXT NOT NULL,
data_region TEXT NOT NULL
)`).Exec(ctx); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:ingestion_keys"`
KeyId string `bun:"key_id,pk,type:text"`
Name string `bun:"name,type:text"`
CreatedAt time.Time `bun:"created_at,default:current_timestamp"`
IngestionKey string `bun:"ingestion_key,type:text,notnull"`
IngestionURL string `bun:"ingestion_url,type:text,notnull"`
DataRegion string `bun:"data_region,type:text,notnull"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}

View File

@@ -28,24 +28,30 @@ func (migration *addPreferences) Register(migrations *migrate.Migrations) error
func (migration *addPreferences) Up(ctx context.Context, db *bun.DB) error {
// table:user_preference
if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS user_preference (
preference_id TEXT NOT NULL,
preference_value TEXT,
user_id TEXT NOT NULL,
PRIMARY KEY (preference_id,user_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE
)`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:user_preference"`
PreferenceID string `bun:"preference_id,type:text,pk"`
PreferenceValue string `bun:"preference_value,type:text"`
UserID string `bun:"user_id,type:text,pk"`
}{}).
IfNotExists().
ForeignKey(`("user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE`).
Exec(ctx); err != nil {
return err
}
// table:org_preference
if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS org_preference (
preference_id TEXT NOT NULL,
preference_value TEXT,
org_id TEXT NOT NULL,
PRIMARY KEY (preference_id,org_id),
FOREIGN KEY (org_id) REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:org_preference"`
PreferenceID string `bun:"preference_id,pk,type:text,notnull"`
PreferenceValue string `bun:"preference_value,type:text,notnull"`
OrgID string `bun:"org_id,pk,type:text,notnull"`
}{}).
ForeignKey(`("org_id") REFERENCES "organizations" ("id") ON DELETE CASCADE ON UPDATE CASCADE`).
IfNotExists().
Exec(ctx); err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package sqlmigration
import (
"context"
"time"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
@@ -28,125 +29,91 @@ func (migration *addDashboards) Register(migrations *migrate.Migrations) error {
func (migration *addDashboards) Up(ctx context.Context, db *bun.DB) error {
// table:dashboards
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS dashboards (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT NOT NULL UNIQUE,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
data TEXT NOT NULL
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:dashboards"`
ID int `bun:"id,pk,autoincrement"`
UUID string `bun:"uuid,type:text,notnull,unique"`
CreatedAt time.Time `bun:"created_at,type:datetime,notnull"`
CreatedBy string `bun:"created_by,type:text,notnull"`
UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"`
UpdatedBy string `bun:"updated_by,type:text,notnull"`
Data string `bun:"data,type:text,notnull"`
Locked int `bun:"locked,notnull,default:0"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
// table:rules
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS rules (
id INTEGER PRIMARY KEY AUTOINCREMENT,
updated_at datetime NOT NULL,
deleted INTEGER DEFAULT 0,
data TEXT NOT NULL
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:rules"`
ID int `bun:"id,pk,autoincrement"`
CreatedAt time.Time `bun:"created_at,type:datetime,notnull"`
CreatedBy string `bun:"created_by,type:text,notnull"`
UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"`
UpdatedBy string `bun:"updated_by,type:text,notnull"`
Deleted int `bun:"deleted,notnull,default:0"`
Data string `bun:"data,type:text,notnull"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
// table:notification_channels
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS notification_channels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
name TEXT NOT NULL UNIQUE,
type TEXT NOT NULL,
deleted INTEGER DEFAULT 0,
data TEXT NOT NULL
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:notification_channels"`
ID int `bun:"id,pk,autoincrement"`
CreatedAt time.Time `bun:"created_at,type:datetime,notnull"`
UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"`
Name string `bun:"name,type:text,notnull,unique"`
Type string `bun:"type,type:text,notnull"`
Deleted int `bun:"deleted,notnull,default:0"`
Data string `bun:"data,type:text,notnull"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
// table:planned_maintenance
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS planned_maintenance (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
alert_ids TEXT,
schedule TEXT NOT NULL,
created_at datetime NOT NULL,
created_by TEXT NOT NULL,
updated_at datetime NOT NULL,
updated_by TEXT NOT NULL
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:planned_maintenance"`
ID int `bun:"id,pk,autoincrement"`
Name string `bun:"name,type:text,notnull"`
Description string `bun:"description,type:text"`
AlertIDs string `bun:"alert_ids,type:text"`
Schedule string `bun:"schedule,type:text,notnull"`
CreatedAt time.Time `bun:"created_at,type:datetime,notnull"`
CreatedBy string `bun:"created_by,type:text,notnull"`
UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"`
UpdatedBy string `bun:"updated_by,type:text,notnull"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
// table:ttl_status
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS ttl_status (
id INTEGER PRIMARY KEY AUTOINCREMENT,
transaction_id TEXT NOT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
table_name TEXT NOT NULL,
ttl INTEGER DEFAULT 0,
cold_storage_ttl INTEGER DEFAULT 0,
status TEXT NOT NULL
);`); err != nil {
return err
}
// table:rules op:add column created_at
if _, err := db.
NewAddColumn().
Table("rules").
ColumnExpr("created_at datetime").
Apply(WrapIfNotExists(ctx, db, "rules", "created_at")).
Exec(ctx); err != nil && err != ErrNoExecute {
return err
}
// table:rules op:add column created_by
if _, err := db.
NewAddColumn().
Table("rules").
ColumnExpr("created_by TEXT").
Apply(WrapIfNotExists(ctx, db, "rules", "created_by")).
Exec(ctx); err != nil && err != ErrNoExecute {
return err
}
// table:rules op:add column updated_by
if _, err := db.
NewAddColumn().
Table("rules").
ColumnExpr("updated_by TEXT").
Apply(WrapIfNotExists(ctx, db, "rules", "updated_by")).
Exec(ctx); err != nil && err != ErrNoExecute {
return err
}
// table:dashboards op:add column created_by
if _, err := db.
NewAddColumn().
Table("dashboards").
ColumnExpr("created_by TEXT").
Apply(WrapIfNotExists(ctx, db, "dashboards", "created_by")).
Exec(ctx); err != nil && err != ErrNoExecute {
return err
}
// table:dashboards op:add column updated_by
if _, err := db.
NewAddColumn().
Table("dashboards").
ColumnExpr("updated_by TEXT").
Apply(WrapIfNotExists(ctx, db, "dashboards", "updated_by")).
Exec(ctx); err != nil && err != ErrNoExecute {
return err
}
// table:dashboards op:add column locked
if _, err := db.
NewAddColumn().
Table("dashboards").
ColumnExpr("locked INTEGER DEFAULT 0").
Apply(WrapIfNotExists(ctx, db, "dashboards", "locked")).
Exec(ctx); err != nil && err != ErrNoExecute {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:ttl_status"`
ID int `bun:"id,pk,autoincrement"`
TransactionID string `bun:"transaction_id,type:text,notnull"`
CreatedAt time.Time `bun:"created_at,type:datetime,notnull"`
UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"`
TableName string `bun:"table_name,type:text,notnull"`
TTL int `bun:"ttl,notnull,default:0"`
ColdStorageTTL int `bun:"cold_storage_ttl,notnull,default:0"`
Status string `bun:"status,type:text,notnull"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package sqlmigration
import (
"context"
"time"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
@@ -28,19 +29,23 @@ func (migration *addSavedViews) Register(migrations *migrate.Migrations) error {
func (migration *addSavedViews) Up(ctx context.Context, db *bun.DB) error {
// table:saved_views op:create
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS saved_views (
uuid TEXT PRIMARY KEY,
name TEXT NOT NULL,
category TEXT NOT NULL,
created_at datetime NOT NULL,
created_by TEXT,
updated_at datetime NOT NULL,
updated_by TEXT,
source_page TEXT NOT NULL,
tags TEXT,
data TEXT NOT NULL,
extra_data TEXT
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:saved_views"`
UUID string `bun:"uuid,pk,type:text"`
Name string `bun:"name,type:text,notnull"`
Category string `bun:"category,type:text,notnull"`
CreatedAt time.Time `bun:"created_at,type:datetime,notnull"`
CreatedBy string `bun:"created_by,type:text"`
UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"`
UpdatedBy string `bun:"updated_by,type:text"`
SourcePage string `bun:"source_page,type:text,notnull"`
Tags string `bun:"tags,type:text"`
Data string `bun:"data,type:text,notnull"`
ExtraData string `bun:"extra_data,type:text"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package sqlmigration
import (
"context"
"time"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
@@ -27,59 +28,69 @@ func (migration *addAgents) Register(migrations *migrate.Migrations) error {
}
func (migration *addAgents) Up(ctx context.Context, db *bun.DB) error {
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS agents (
agent_id TEXT PRIMARY KEY UNIQUE,
started_at datetime NOT NULL,
terminated_at datetime,
current_status TEXT NOT NULL,
effective_config TEXT NOT NULL
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:agents"`
AgentID string `bun:"agent_id,pk,type:text,unique"`
StartedAt time.Time `bun:"started_at,type:datetime,notnull"`
TerminatedAt time.Time `bun:"terminated_at,type:datetime"`
CurrentStatus string `bun:"current_status,type:text,notnull"`
EffectiveConfig string `bun:"effective_config,type:text,notnull"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS agent_config_versions(
id TEXT PRIMARY KEY,
created_by TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_by TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
version INTEGER DEFAULT 1,
active int,
is_valid int,
disabled int,
element_type VARCHAR(120) NOT NULL,
deploy_status VARCHAR(80) NOT NULL DEFAULT 'DIRTY',
deploy_sequence INTEGER,
deploy_result TEXT,
last_hash TEXT,
last_config TEXT,
UNIQUE(element_type, version)
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:agent_config_versions"`
ID string `bun:"id,pk,type:text"`
CreatedBy string `bun:"created_by,type:text"`
CreatedAt time.Time `bun:"created_at,default:CURRENT_TIMESTAMP"`
UpdatedBy string `bun:"updated_by,type:text"`
UpdatedAt time.Time `bun:"updated_at,default:CURRENT_TIMESTAMP"`
Version int `bun:"version,default:1,unique:element_version_idx"`
Active int `bun:"active"`
IsValid int `bun:"is_valid"`
Disabled int `bun:"disabled"`
ElementType string `bun:"element_type,notnull,type:varchar(120),unique:element_version_idx"`
DeployStatus string `bun:"deploy_status,notnull,type:varchar(80),default:'DIRTY'"`
DeploySequence int `bun:"deploy_sequence"`
DeployResult string `bun:"deploy_result,type:text"`
LastHash string `bun:"last_hash,type:text"`
LastConfig string `bun:"last_config,type:text"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
if _, err := db.ExecContext(ctx, `CREATE UNIQUE INDEX IF NOT EXISTS agent_config_versions_u1 ON agent_config_versions(element_type, version);`); err != nil {
// add an index on the last_hash column
if _, err := db.NewCreateIndex().
Table("agent_config_versions").
Column("last_hash").
Index("idx_agent_config_versions_last_hash").
IfNotExists().
Exec(ctx); err != nil {
return err
}
if _, err := db.ExecContext(ctx, `CREATE INDEX IF NOT EXISTS agent_config_versions_nu1 ON agent_config_versions(last_hash);`); err != nil {
return err
}
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:agent_config_elements"`
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS agent_config_elements(
id TEXT PRIMARY KEY,
created_by TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_by TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
element_id TEXT NOT NULL,
element_type VARCHAR(120) NOT NULL,
version_id TEXT NOT NULL
);`); err != nil {
return err
}
if _, err := db.ExecContext(ctx, `CREATE UNIQUE INDEX IF NOT EXISTS agent_config_elements_u1 ON agent_config_elements(version_id, element_id, element_type);`); err != nil {
ID string `bun:"id,pk,type:text"`
CreatedBy string `bun:"created_by,type:text"`
CreatedAt time.Time `bun:"created_at,default:CURRENT_TIMESTAMP"`
UpdatedBy string `bun:"updated_by,type:text"`
UpdatedAt time.Time `bun:"updated_at,default:CURRENT_TIMESTAMP"`
ElementID string `bun:"element_id,type:text,notnull,unique:agent_config_elements_u1"`
ElementType string `bun:"element_type,type:varchar(120),notnull,unique:agent_config_elements_u1"`
VersionID string `bun:"version_id,type:text,notnull,unique:agent_config_elements_u1"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package sqlmigration
import (
"context"
"time"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
@@ -27,18 +28,22 @@ func (migration *addPipelines) Register(migrations *migrate.Migrations) error {
}
func (migration *addPipelines) Up(ctx context.Context, db *bun.DB) error {
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS pipelines(
id TEXT PRIMARY KEY,
order_id INTEGER,
enabled BOOLEAN,
created_by TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
name VARCHAR(400) NOT NULL,
alias VARCHAR(20) NOT NULL,
description TEXT,
filter TEXT NOT NULL,
config_json TEXT
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:pipelines"`
ID string `bun:"id,pk,type:text"`
OrderID int `bun:"order_id"`
Enabled bool `bun:"enabled"`
CreatedBy string `bun:"created_by,type:text"`
CreatedAt time.Time `bun:"created_at,default:current_timestamp"`
Name string `bun:"name,type:varchar(400),notnull"`
Alias string `bun:"alias,type:varchar(20),notnull"`
Description string `bun:"description,type:text"`
Filter string `bun:"filter,type:text,notnull"`
ConfigJSON string `bun:"config_json,type:text"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package sqlmigration
import (
"context"
"time"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
@@ -27,35 +28,46 @@ func (migration *addIntegrations) Register(migrations *migrate.Migrations) error
}
func (migration *addIntegrations) Up(ctx context.Context, db *bun.DB) error {
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS integrations_installed(
integration_id TEXT PRIMARY KEY,
config_json TEXT,
installed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:integrations_installed"`
IntegrationID string `bun:"integration_id,pk,type:text"`
ConfigJSON string `bun:"config_json,type:text"`
InstalledAt time.Time `bun:"installed_at,default:current_timestamp"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS cloud_integrations_accounts(
cloud_provider TEXT NOT NULL,
id TEXT NOT NULL,
config_json TEXT,
cloud_account_id TEXT,
last_agent_report_json TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
removed_at TIMESTAMP,
UNIQUE(cloud_provider, id)
)`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:cloud_integrations_accounts"`
CloudProvider string `bun:"cloud_provider,type:text,unique:cloud_provider_id"`
ID string `bun:"id,type:text,notnull,unique:cloud_provider_id"`
ConfigJSON string `bun:"config_json,type:text"`
CloudAccountID string `bun:"cloud_account_id,type:text"`
LastAgentReportJSON string `bun:"last_agent_report_json,type:text"`
CreatedAt time.Time `bun:"created_at,notnull,default:current_timestamp"`
RemovedAt time.Time `bun:"removed_at,type:timestamp"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS cloud_integrations_service_configs(
cloud_provider TEXT NOT NULL,
cloud_account_id TEXT NOT NULL,
service_id TEXT NOT NULL,
config_json TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
UNIQUE(cloud_provider, cloud_account_id, service_id)
)`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:cloud_integrations_service_configs"`
CloudProvider string `bun:"cloud_provider,type:text,notnull,unique:service_cloud_provider_account"`
CloudAccountID string `bun:"cloud_account_id,type:text,notnull,unique:service_cloud_provider_account"`
ServiceID string `bun:"service_id,type:text,notnull,unique:service_cloud_provider_account"`
ConfigJSON string `bun:"config_json,type:text"`
CreatedAt time.Time `bun:"created_at,default:current_timestamp"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package sqlmigration
import (
"context"
"time"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
@@ -27,42 +28,59 @@ func (migration *addLicenses) Register(migrations *migrate.Migrations) error {
}
func (migration *addLicenses) Up(ctx context.Context, db *bun.DB) error {
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS licenses(
key TEXT PRIMARY KEY,
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
planDetails TEXT,
activationId TEXT,
validationMessage TEXT,
lastValidated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:licenses"`
Key string `bun:"key,pk,type:text"`
CreatedAt time.Time `bun:"createdAt,default:current_timestamp"`
UpdatedAt time.Time `bun:"updatedAt,default:current_timestamp"`
PlanDetails string `bun:"planDetails,type:text"`
ActivationID string `bun:"activationId,type:text"`
ValidationMessage string `bun:"validationMessage,type:text"`
LastValidated time.Time `bun:"lastValidated,default:current_timestamp"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS sites(
uuid TEXT PRIMARY KEY,
alias VARCHAR(180) DEFAULT 'PROD',
url VARCHAR(300),
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:sites"`
UUID string `bun:"uuid,pk,type:text"`
Alias string `bun:"alias,type:varchar(180),default:'PROD'"`
URL string `bun:"url,type:varchar(300)"`
CreatedAt time.Time `bun:"createdAt,default:current_timestamp"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS feature_status (
name TEXT PRIMARY KEY,
active bool,
usage INTEGER DEFAULT 0,
usage_limit INTEGER DEFAULT 0,
route TEXT
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:feature_status"`
Name string `bun:"name,pk,type:text"`
Active bool `bun:"active"`
Usage int `bun:"usage,default:0"`
UsageLimit int `bun:"usage_limit,default:0"`
Route string `bun:"route,type:text"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS licenses_v3 (
id TEXT PRIMARY KEY,
key TEXT NOT NULL UNIQUE,
data TEXT
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:licenses_v3"`
ID string `bun:"id,pk,type:text"`
Key string `bun:"key,type:text,notnull,unique"`
Data string `bun:"data,type:text"`
}{}).
IfNotExists().
Exec(ctx); err != nil {
return err
}

View File

@@ -27,77 +27,42 @@ func (migration *addPats) Register(migrations *migrate.Migrations) error {
}
func (migration *addPats) Up(ctx context.Context, db *bun.DB) error {
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS org_domains(
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
name VARCHAR(50) NOT NULL UNIQUE,
created_at INTEGER NOT NULL,
updated_at INTEGER,
data TEXT NOT NULL,
FOREIGN KEY(org_id) REFERENCES organizations(id)
);`); err != nil {
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:org_domains"`
ID string `bun:"id,pk,type:text"`
OrgID string `bun:"org_id,type:text,notnull"`
Name string `bun:"name,type:varchar(50),notnull,unique"`
CreatedAt int `bun:"created_at,notnull"`
UpdatedAt int `bun:"updated_at,type:timestamp"`
Data string `bun:"data,type:text,notnull"`
}{}).
ForeignKey(`("org_id") REFERENCES "organizations" ("id")`).
IfNotExists().
Exec(ctx); err != nil {
return err
}
if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS personal_access_tokens (
id INTEGER PRIMARY KEY AUTOINCREMENT,
role TEXT NOT NULL,
user_id TEXT NOT NULL,
token TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
created_at INTEGER NOT NULL,
expires_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
last_used INTEGER NOT NULL,
revoked BOOLEAN NOT NULL,
updated_by_user_id TEXT NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id)
);`); err != nil {
return err
}
if _, err := db.NewCreateTable().
Model(&struct {
bun.BaseModel `bun:"table:personal_access_tokens"`
if _, err := db.
NewAddColumn().
Table("personal_access_tokens").
ColumnExpr("role TEXT NOT NULL DEFAULT 'ADMIN'").
Apply(WrapIfNotExists(ctx, db, "personal_access_tokens", "role")).
Exec(ctx); err != nil && err != ErrNoExecute {
return err
}
if _, err := db.
NewAddColumn().
Table("personal_access_tokens").
ColumnExpr("updated_at INTEGER NOT NULL DEFAULT 0").
Apply(WrapIfNotExists(ctx, db, "personal_access_tokens", "updated_at")).
Exec(ctx); err != nil && err != ErrNoExecute {
return err
}
if _, err := db.
NewAddColumn().
Table("personal_access_tokens").
ColumnExpr("last_used INTEGER NOT NULL DEFAULT 0").
Apply(WrapIfNotExists(ctx, db, "personal_access_tokens", "last_used")).
Exec(ctx); err != nil && err != ErrNoExecute {
return err
}
if _, err := db.
NewAddColumn().
Table("personal_access_tokens").
ColumnExpr("revoked BOOLEAN NOT NULL DEFAULT FALSE").
Apply(WrapIfNotExists(ctx, db, "personal_access_tokens", "revoked")).
Exec(ctx); err != nil && err != ErrNoExecute {
return err
}
if _, err := db.
NewAddColumn().
Table("personal_access_tokens").
ColumnExpr("updated_by_user_id TEXT NOT NULL DEFAULT ''").
Apply(WrapIfNotExists(ctx, db, "personal_access_tokens", "updated_by_user_id")).
Exec(ctx); err != nil && err != ErrNoExecute {
ID int `bun:"id,pk,autoincrement"`
Role string `bun:"role,type:text,notnull,default:'ADMIN'"`
UserID string `bun:"user_id,type:text,notnull"`
Token string `bun:"token,type:text,notnull,unique"`
Name string `bun:"name,type:text,notnull"`
CreatedAt int `bun:"created_at,notnull,default:0"`
ExpiresAt int `bun:"expires_at,notnull,default:0"`
UpdatedAt int `bun:"updated_at,notnull,default:0"`
LastUsed int `bun:"last_used,notnull,default:0"`
Revoked bool `bun:"revoked,notnull,default:false"`
UpdatedByUserID string `bun:"updated_by_user_id,type:text,notnull,default:''"`
}{}).
ForeignKey(`("user_id") REFERENCES "users" ("id")`).
IfNotExists().
Exec(ctx); err != nil {
return err
}

49
pkg/types/agent.go Normal file
View File

@@ -0,0 +1,49 @@
package types
import (
"time"
"github.com/uptrace/bun"
)
type Agent struct {
bun.BaseModel `bun:"table:agents"`
AgentID string `bun:"agent_id,pk,type:text"`
StartedAt time.Time `bun:"started_at,type:datetime,notnull"`
TerminatedAt time.Time `bun:"terminated_at,type:datetime"`
CurrentStatus string `bun:"current_status,type:text,notnull"`
EffectiveConfig string `bun:"effective_config,type:text,notnull"`
}
type AgentConfigVersion struct {
bun.BaseModel `bun:"table:agent_config_versions"`
ID string `bun:"id,pk,type:text"`
CreatedBy string `bun:"created_by,type:text"`
CreatedAt time.Time `bun:"created_at,default:CURRENT_TIMESTAMP"`
UpdatedBy string `bun:"updated_by,type:text"`
UpdatedAt time.Time `bun:"updated_at,default:CURRENT_TIMESTAMP"`
Version int `bun:"version,default:1,unique:element_version_idx"`
Active int `bun:"active"`
IsValid int `bun:"is_valid"`
Disabled int `bun:"disabled"`
ElementType string `bun:"element_type,notnull,type:varchar(120),unique:element_version_idx"`
DeployStatus string `bun:"deploy_status,notnull,type:varchar(80),default:'DIRTY'"`
DeploySequence int `bun:"deploy_sequence"`
DeployResult string `bun:"deploy_result,type:text"`
LastHash string `bun:"last_hash,type:text"`
LastConfig string `bun:"last_config,type:text"`
}
type AgentConfigElement struct {
bun.BaseModel `bun:"table:agent_config_elements"`
ID string `bun:"id,pk,type:text"`
CreatedBy string `bun:"created_by,type:text"`
CreatedAt time.Time `bun:"created_at,default:CURRENT_TIMESTAMP"`
UpdatedBy string `bun:"updated_by,type:text"`
UpdatedAt time.Time `bun:"updated_at,default:CURRENT_TIMESTAMP"`
ElementID string `bun:"element_id,type:text,notnull,unique:agent_config_elements_u1"`
ElementType string `bun:"element_type,type:varchar(120),notnull,unique:agent_config_elements_u1"`
VersionID string `bun:"version_id,type:text,notnull,unique:agent_config_elements_u1"`
}

71
pkg/types/dashboard.go Normal file
View File

@@ -0,0 +1,71 @@
package types
import (
"time"
"github.com/uptrace/bun"
)
type Dashboard struct {
bun.BaseModel `bun:"table:dashboards"`
ID int `bun:"id,pk,autoincrement"`
UUID string `bun:"uuid,type:text,notnull,unique"`
CreatedAt time.Time `bun:"created_at,type:datetime,notnull"`
CreatedBy string `bun:"created_by,type:text,notnull"`
UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"`
UpdatedBy string `bun:"updated_by,type:text,notnull"`
Data string `bun:"data,type:text,notnull"`
Locked int `bun:"locked,notnull,default:0"`
}
type Rule struct {
bun.BaseModel `bun:"table:rules"`
ID int `bun:"id,pk,autoincrement"`
CreatedAt time.Time `bun:"created_at,type:datetime,notnull"`
CreatedBy string `bun:"created_by,type:text,notnull"`
UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"`
UpdatedBy string `bun:"updated_by,type:text,notnull"`
Deleted int `bun:"deleted,notnull,default:0"`
Data string `bun:"data,type:text,notnull"`
}
type NotificationChannel struct {
bun.BaseModel `bun:"table:notification_channels"`
ID int `bun:"id,pk,autoincrement"`
CreatedAt time.Time `bun:"created_at,type:datetime,notnull"`
UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"`
Name string `bun:"name,type:text,notnull,unique"`
Type string `bun:"type,type:text,notnull"`
Deleted int `bun:"deleted,notnull,default:0"`
Data string `bun:"data,type:text,notnull"`
}
type PlannedMaintenance struct {
bun.BaseModel `bun:"table:planned_maintenance"`
ID int `bun:"id,pk,autoincrement"`
Name string `bun:"name,type:text,notnull"`
Description string `bun:"description,type:text"`
AlertIDs string `bun:"alert_ids,type:text"`
Schedule string `bun:"schedule,type:text,notnull"`
CreatedAt time.Time `bun:"created_at,type:datetime,notnull"`
CreatedBy string `bun:"created_by,type:text,notnull"`
UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"`
UpdatedBy string `bun:"updated_by,type:text,notnull"`
}
type TTLStatus struct {
bun.BaseModel `bun:"table:ttl_status"`
ID int `bun:"id,pk,autoincrement"`
TransactionID string `bun:"transaction_id,type:text,notnull"`
CreatedAt time.Time `bun:"created_at,type:datetime,notnull"`
UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"`
TableName string `bun:"table_name,type:text,notnull"`
TTL int `bun:"ttl,notnull,default:0"`
ColdStorageTTL int `bun:"cold_storage_ttl,notnull,default:0"`
Status string `bun:"status,type:text,notnull"`
}

View File

@@ -0,0 +1,15 @@
package types
import (
"time"
"github.com/uptrace/bun"
)
type DataMigration struct {
bun.BaseModel `bun:"table:data_migrations"`
ID int `bun:"id,pk,autoincrement"`
Version string `bun:"version,unique,notnull,type:VARCHAR(255)"`
CreatedAt time.Time `bun:"created_at,notnull,default:current_timestamp"`
Succeeded bool `bun:"succeeded,notnull,default:false"`
}

37
pkg/types/integration.go Normal file
View File

@@ -0,0 +1,37 @@
package types
import (
"time"
"github.com/uptrace/bun"
)
type Integration struct {
bun.BaseModel `bun:"table:integrations_installed"`
IntegrationID string `bun:"integration_id,pk,type:text"`
ConfigJSON string `bun:"config_json,type:text"`
InstalledAt time.Time `bun:"installed_at,default:current_timestamp"`
}
type CloudIntegrationAccount struct {
bun.BaseModel `bun:"table:cloud_integrations_accounts"`
CloudProvider string `bun:"cloud_provider,type:text,unique:cloud_provider_id"`
ID string `bun:"id,type:text,notnull,unique:cloud_provider_id"`
ConfigJSON string `bun:"config_json,type:text"`
CloudAccountID string `bun:"cloud_account_id,type:text"`
LastAgentReportJSON string `bun:"last_agent_report_json,type:text"`
CreatedAt time.Time `bun:"created_at,notnull,default:current_timestamp"`
RemovedAt time.Time `bun:"removed_at,type:timestamp"`
}
type CloudIntegrationServiceConfig struct {
bun.BaseModel `bun:"table:cloud_integrations_service_configs"`
CloudProvider string `bun:"cloud_provider,type:text,notnull,unique:service_cloud_provider_account"`
CloudAccountID string `bun:"cloud_account_id,type:text,notnull,unique:service_cloud_provider_account"`
ServiceID string `bun:"service_id,type:text,notnull,unique:service_cloud_provider_account"`
ConfigJSON string `bun:"config_json,type:text"`
CreatedAt time.Time `bun:"created_at,default:current_timestamp"`
}

46
pkg/types/license.go Normal file
View File

@@ -0,0 +1,46 @@
package types
import (
"time"
"github.com/uptrace/bun"
)
type License struct {
bun.BaseModel `bun:"table:licenses"`
Key string `bun:"key,pk,type:text"`
CreatedAt time.Time `bun:"createdAt,default:current_timestamp"`
UpdatedAt time.Time `bun:"updatedAt,default:current_timestamp"`
PlanDetails string `bun:"planDetails,type:text"`
ActivationID string `bun:"activationId,type:text"`
ValidationMessage string `bun:"validationMessage,type:text"`
LastValidated time.Time `bun:"lastValidated,default:current_timestamp"`
}
type Site struct {
bun.BaseModel `bun:"table:sites"`
UUID string `bun:"uuid,pk,type:text"`
Alias string `bun:"alias,type:varchar(180),default:'PROD'"`
URL string `bun:"url,type:varchar(300)"`
CreatedAt time.Time `bun:"createdAt,default:current_timestamp"`
}
type FeatureStatus struct {
bun.BaseModel `bun:"table:feature_status"`
Name string `bun:"name,pk,type:text"`
Active bool `bun:"active"`
Usage int `bun:"usage,default:0"`
UsageLimit int `bun:"usage_limit,default:0"`
Route string `bun:"route,type:text"`
}
type LicenseV3 struct {
bun.BaseModel `bun:"table:licenses_v3"`
ID string `bun:"id,pk,type:text"`
Key string `bun:"key,type:text,notnull,unique"`
Data string `bun:"data,type:text"`
}

78
pkg/types/organization.go Normal file
View File

@@ -0,0 +1,78 @@
package types
import (
"time"
"github.com/uptrace/bun"
)
// TODO: check constraints are not working
type Organization struct {
bun.BaseModel `bun:"table:organizations"`
ID string `bun:"id,pk,type:text"`
Name string `bun:"name,type:text,notnull"`
CreatedAt int `bun:"created_at,notnull"`
IsAnonymous int `bun:"is_anonymous,notnull,default:0,CHECK(is_anonymous IN (0,1))"`
HasOptedUpdates int `bun:"has_opted_updates,notnull,default:1,CHECK(has_opted_updates IN (0,1))"`
}
type Invite struct {
bun.BaseModel `bun:"table:invites"`
ID int `bun:"id,pk,autoincrement"`
Name string `bun:"name,type:text,notnull"`
Email string `bun:"email,type:text,notnull,unique"`
Token string `bun:"token,type:text,notnull"`
CreatedAt int `bun:"created_at,notnull"`
Role string `bun:"role,type:text,notnull"`
OrgID string `bun:"org_id,type:text,notnull"`
}
type Group struct {
bun.BaseModel `bun:"table:groups"`
ID string `bun:"id,pk,type:text"`
Name string `bun:"name,type:text,notnull,unique"`
}
type User struct {
bun.BaseModel `bun:"table:users"`
ID string `bun:"id,pk,type:text"`
Name string `bun:"name,type:text,notnull"`
Email string `bun:"email,type:text,notnull,unique"`
Password string `bun:"password,type:text,notnull"`
CreatedAt int `bun:"created_at,notnull"`
ProfilePictureURL string `bun:"profile_picture_url,type:text"`
GroupID string `bun:"group_id,type:text,notnull"`
OrgID string `bun:"org_id,type:text,notnull"`
}
type ResetPasswordRequest struct {
bun.BaseModel `bun:"table:reset_password_request"`
ID int `bun:"id,pk,autoincrement"`
Token string `bun:"token,type:text,notnull"`
UserID string `bun:"user_id,type:text,notnull"`
}
type UserFlags struct {
bun.BaseModel `bun:"table:user_flags"`
UserID string `bun:"user_id,pk,type:text,notnull"`
Flags string `bun:"flags,type:text"`
}
type ApdexSettings struct {
bun.BaseModel `bun:"table:apdex_settings"`
ServiceName string `bun:"service_name,pk,type:text"`
Threshold float64 `bun:"threshold,type:float,notnull"`
ExcludeStatusCodes string `bun:"exclude_status_codes,type:text,notnull"`
}
type IngestionKey struct {
bun.BaseModel `bun:"table:ingestion_keys"`
KeyId string `bun:"key_id,pk,type:text"`
Name string `bun:"name,type:text"`
CreatedAt time.Time `bun:"created_at,default:current_timestamp"`
IngestionKey string `bun:"ingestion_key,type:text,notnull"`
IngestionURL string `bun:"ingestion_url,type:text,notnull"`
DataRegion string `bun:"data_region,type:text,notnull"`
}

View File

@@ -0,0 +1,32 @@
package types
import (
"github.com/uptrace/bun"
)
type PersonalAccessToken struct {
bun.BaseModel `bun:"table:personal_access_tokens"`
ID int `bun:"id,pk,autoincrement"`
Role string `bun:"role,type:text,notnull,default:'ADMIN'"`
UserID string `bun:"user_id,type:text,notnull"`
Token string `bun:"token,type:text,notnull,unique"`
Name string `bun:"name,type:text,notnull"`
CreatedAt int `bun:"created_at,notnull,default:0"`
ExpiresAt int `bun:"expires_at,notnull,default:0"`
UpdatedAt int `bun:"updated_at,notnull,default:0"`
LastUsed int `bun:"last_used,notnull,default:0"`
Revoked bool `bun:"revoked,notnull,default:false"`
UpdatedByUserID string `bun:"updated_by_user_id,type:text,notnull,default:''"`
}
type OrgDomain struct {
bun.BaseModel `bun:"table:org_domains"`
ID string `bun:"id,pk,type:text"`
OrgID string `bun:"org_id,type:text,notnull"`
Name string `bun:"name,type:varchar(50),notnull,unique"`
CreatedAt int `bun:"created_at,notnull"`
UpdatedAt int `bun:"updated_at,type:timestamp"`
Data string `bun:"data,type:text,notnull"`
}

22
pkg/types/pipeline.go Normal file
View File

@@ -0,0 +1,22 @@
package types
import (
"time"
"github.com/uptrace/bun"
)
type Pipeline struct {
bun.BaseModel `bun:"table:pipelines"`
ID string `bun:"id,pk,type:text"`
OrderID int `bun:"order_id"`
Enabled bool `bun:"enabled"`
CreatedBy string `bun:"created_by,type:text"`
CreatedAt time.Time `bun:"created_at,default:current_timestamp"`
Name string `bun:"name,type:varchar(400),notnull"`
Alias string `bun:"alias,type:varchar(20),notnull"`
Description string `bun:"description,type:text"`
Filter string `bun:"filter,type:text,notnull"`
ConfigJSON string `bun:"config_json,type:text"`
}

21
pkg/types/preference.go Normal file
View File

@@ -0,0 +1,21 @@
package types
import "github.com/uptrace/bun"
// on_delete:CASCADE,on_update:CASCADE not working
type UserPreference struct {
bun.BaseModel `bun:"table:user_preference"`
PreferenceID string `bun:"preference_id,type:text,pk"`
PreferenceValue string `bun:"preference_value,type:text"`
UserID string `bun:"user_id,type:text,pk"`
}
// on_delete:CASCADE,on_update:CASCADE not working
type OrgPreference struct {
bun.BaseModel `bun:"table:org_preference"`
PreferenceID string `bun:"preference_id,pk,type:text,notnull"`
PreferenceValue string `bun:"preference_value,type:text,notnull"`
OrgID string `bun:"org_id,pk,type:text,notnull"`
}

23
pkg/types/savedview.go Normal file
View File

@@ -0,0 +1,23 @@
package types
import (
"time"
"github.com/uptrace/bun"
)
type SavedView struct {
bun.BaseModel `bun:"table:saved_views"`
UUID string `bun:"uuid,pk,type:text"`
Name string `bun:"name,type:text,notnull"`
Category string `bun:"category,type:text,notnull"`
CreatedAt time.Time `bun:"created_at,type:datetime,notnull"`
CreatedBy string `bun:"created_by,type:text"`
UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"`
UpdatedBy string `bun:"updated_by,type:text"`
SourcePage string `bun:"source_page,type:text,notnull"`
Tags string `bun:"tags,type:text"`
Data string `bun:"data,type:text,notnull"`
ExtraData string `bun:"extra_data,type:text"`
}