Compare commits
5 Commits
issue_6584
...
v0.72.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d281e694d | ||
|
|
b772a0fb56 | ||
|
|
962e75c6d4 | ||
|
|
42fad23cb0 | ||
|
|
d22ecb9f7c |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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={{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
]);
|
||||
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -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>>;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
49
pkg/types/agent.go
Normal 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
71
pkg/types/dashboard.go
Normal 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"`
|
||||
}
|
||||
15
pkg/types/datamigration.go
Normal file
15
pkg/types/datamigration.go
Normal 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
37
pkg/types/integration.go
Normal 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
46
pkg/types/license.go
Normal 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
78
pkg/types/organization.go
Normal 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"`
|
||||
}
|
||||
32
pkg/types/personal_access_token.go
Normal file
32
pkg/types/personal_access_token.go
Normal 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
22
pkg/types/pipeline.go
Normal 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
21
pkg/types/preference.go
Normal 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
23
pkg/types/savedview.go
Normal 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"`
|
||||
}
|
||||
Reference in New Issue
Block a user