Compare commits

..

2 Commits

Author SHA1 Message Date
Yunus M
0b35f5b38f Merge branch 'main' into feat/json-logs-ui 2026-04-21 21:40:19 +05:30
Yunus M
02fddebeaa feat: enable JSON body query support and add group by functionality 2026-04-21 21:38:21 +05:30
60 changed files with 1468 additions and 487 deletions

View File

@@ -16,7 +16,6 @@ import (
"github.com/SigNoz/signoz/ee/query-service/constants"
"github.com/SigNoz/signoz/pkg/flagger"
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/querybuilder"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/featuretypes"
"github.com/SigNoz/signoz/pkg/types/licensetypes"
@@ -72,18 +71,6 @@ func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
Route: "",
})
bodyJSONQuery := ah.Signoz.Flagger.BooleanOrEmpty(ctx, flagger.FeatureBodyJSONQuery, evalCtx)
if querybuilder.BodyJSONQueryEnabled {
bodyJSONQuery = querybuilder.BodyJSONQueryEnabled
}
featureSet = append(featureSet, &licensetypes.Feature{
Name: valuer.NewString(flagger.FeatureBodyJSONQuery.String()),
Active: bodyJSONQuery,
Usage: 0,
UsageLimit: -1,
Route: "",
})
if constants.IsDotMetricsEnabled {
for idx, feature := range featureSet {
if feature.Name == licensetypes.DotMetricsEnabled {

View File

@@ -1,8 +1,14 @@
.download-popover {
.ant-popover-inner {
border-radius: 4px;
background-color: var(--l2-background);
border: 1px solid var(--l1-border);
background: linear-gradient(
139deg,
var(--l2-background) 0%,
var(--l3-background) 98.68%
);
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(20px);
padding: 0 8px 12px 8px;
margin: 6px 0;
}
@@ -13,7 +19,7 @@
.title {
display: flex;
color: var(--l1-foreground);
color: var(--l3-foreground);
font-family: Inter;
font-size: 11px;
font-style: normal;
@@ -32,7 +38,7 @@
flex-direction: column;
:global(.ant-radio-wrapper) {
color: var(--l1-foreground);
color: var(--foreground);
font-family: Inter;
font-size: 13px;
}

View File

@@ -3,13 +3,13 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useSelector } from 'react-redux'; // old code, TODO: fix this correctly
import { useCopyToClipboard, useLocation } from 'react-use';
import { Color, Spacing } from '@signozhq/design-tokens';
import { Button } from '@signozhq/ui';
import { Divider, Drawer, Radio, Tooltip, Typography } from 'antd';
import { Button, Divider, Drawer, Radio, Tooltip, Typography } from 'antd';
import type { RadioChangeEvent } from 'antd/lib';
import cx from 'classnames';
import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator';
import QuerySearch from 'components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch';
import { convertExpressionToFilters } from 'components/QueryBuilderV2/utils';
import { FeatureKeys } from 'constants/features';
import { LOCALSTORAGE } from 'constants/localStorage';
import { QueryParams } from 'constants/query';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
@@ -46,6 +46,7 @@ import {
TextSelect,
X,
} from 'lucide-react';
import { useAppContext } from 'providers/App/App';
import { AppState } from 'store/reducers';
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource, StringOperators } from 'types/common/queryBuilder';
@@ -79,6 +80,10 @@ function LogDetailInner({
const [selectedView, setSelectedView] = useState<VIEWS>(selectedTab);
const [isFilterVisible, setIsFilterVisible] = useState<boolean>(false);
const { featureFlags } = useAppContext();
const isBodyJsonQueryEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.ENABLE_BODY_JSON_QUERY)
?.active || false;
const [filters, setFilters] = useState<TagFilter | null>(null);
const [isEdit, setIsEdit] = useState<boolean>(false);
@@ -208,11 +213,29 @@ function LogDetailInner({
}
};
const logBody = useMemo(() => {
if (!isBodyJsonQueryEnabled) {
return log?.body || '';
}
try {
const json = JSON.parse(log?.body || '');
if (typeof json?.message === 'string' && json.message !== '') {
return json.message;
}
return log?.body || '';
} catch (error) {
return log?.body || '';
}
}, [isBodyJsonQueryEnabled, log?.body]);
const htmlBody = useMemo(
() => ({
__html: getSanitizedLogBody(log?.body || '', { shouldEscapeHtml: true }),
__html: getSanitizedLogBody(logBody || '', { shouldEscapeHtml: true }),
}),
[log?.body],
[logBody],
);
const handleJSONCopy = (): void => {
@@ -364,9 +387,7 @@ function LogDetailInner({
mouseLeaveDelay={0}
>
<Button
variant="outlined"
color="secondary"
prefix={<ChevronUp size={14} />}
icon={<ChevronUp size={14} />}
className="log-arrow-btn log-arrow-btn-up"
disabled={isPrevDisabled}
onClick={(): void => handleNavigateLog({ direction: 'previous' })}
@@ -378,9 +399,7 @@ function LogDetailInner({
mouseLeaveDelay={0}
>
<Button
variant="outlined"
color="secondary"
prefix={<ChevronDown size={14} />}
icon={<ChevronDown size={14} />}
className="log-arrow-btn log-arrow-btn-down"
disabled={isNextDisabled}
onClick={(): void => handleNavigateLog({ direction: 'next' })}
@@ -390,10 +409,8 @@ function LogDetailInner({
{showOpenInExplorerBtn && (
<div>
<Button
variant="outlined"
color="secondary"
prefix={<Compass size={16} />}
className="open-in-explorer-btn"
icon={<Compass size={16} />}
onClick={handleOpenInExplorer}
>
Open in Explorer
@@ -418,7 +435,7 @@ function LogDetailInner({
<div className="log-detail-drawer__log">
<Divider type="vertical" className={cx('log-type-indicator', logType)} />
<Tooltip
title={removeEscapeCharacters(log?.body)}
title={removeEscapeCharacters(logBody)}
placement="left"
mouseLeaveDelay={0}
>
@@ -489,10 +506,8 @@ function LogDetailInner({
mouseLeaveDelay={0}
>
<Button
variant="link"
color="secondary"
size="sm"
prefix={<Filter size={12} />}
className="action-btn"
icon={<Filter size={16} />}
onClick={handleFilterVisible}
/>
</Tooltip>
@@ -507,10 +522,8 @@ function LogDetailInner({
mouseLeaveDelay={0}
>
<Button
variant="link"
color="secondary"
size="sm"
prefix={<Copy size={12} />}
className="action-btn"
icon={<Copy size={16} />}
onClick={selectedView === VIEW_TYPES.JSON ? handleJSONCopy : onLogCopy}
/>
</Tooltip>

View File

@@ -1,5 +1,4 @@
import { CSSProperties } from 'react';
import { Color } from '@signozhq/design-tokens';
import { TableProps } from 'antd';
export function getDefaultCellStyle(isDarkMode?: boolean): CSSProperties {
@@ -8,7 +7,7 @@ export function getDefaultCellStyle(isDarkMode?: boolean): CSSProperties {
paddingBottom: 6,
paddingRight: 8,
paddingLeft: 8,
color: isDarkMode ? Color.BG_VANILLA_100 : Color.BG_INK_400,
color: isDarkMode ? 'var(--bg-vanilla-400)' : 'var(--bg-slate-400)',
fontSize: '14px',
fontStyle: 'normal',
fontWeight: 400,

View File

@@ -1,4 +1,3 @@
import { Color } from '@signozhq/design-tokens';
import { FontSize } from 'container/OptionsMenu/types';
import styled from 'styled-components';
@@ -11,7 +10,7 @@ interface TableBodyContentProps {
export const TableBodyContent = styled.div<TableBodyContentProps>`
margin-bottom: 0;
color: ${(props): string =>
props.isDarkMode ? Color.BG_VANILLA_100 : Color.BG_INK_400};
props.isDarkMode ? 'var(--bg-vanilla-400, #c0c1c3)' : 'var(--bg-slate-400)'};
font-size: 14px;
font-style: normal;
font-weight: 400;

View File

@@ -33,9 +33,8 @@
display: flex;
align-items: center;
.timestamp-text {
color: var(--l1-foreground);
margin: 0 !important;
p {
margin-bottom: 0;
}
}

View File

@@ -123,7 +123,7 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
return {
children: (
<div className="table-timestamp">
<p className={cx('timestamp-text text', fontSize)}>{date}</p>
<p className={cx('text', fontSize)}>{date}</p>
</div>
),
};

View File

@@ -35,7 +35,7 @@
}
.text {
color: var(--l1-foreground);
color: var(--muted-foreground);
font-family: Inter;
font-size: 11px;
font-style: normal;
@@ -93,7 +93,7 @@
gap: 12px;
.title {
color: var(--l1-foreground);
color: var(--muted-foreground);
font-family: Inter;
font-size: 11px;
font-style: normal;
@@ -139,8 +139,7 @@
line-height: 18px;
letter-spacing: 0.08em;
text-align: left;
color: var(--l1-foreground);
color: var(--muted-foreground);
}
.menu-items {
@@ -178,7 +177,7 @@
padding: 12px;
.title {
color: var(--l1-foreground);
color: var(--muted-foreground);
font-family: Inter;
font-size: 11px;
font-style: normal;
@@ -331,7 +330,7 @@
}
.title {
color: var(--l1-foreground);
color: var(--muted-foreground);
font-family: Inter;
font-size: 11px;
font-style: normal;
@@ -487,3 +486,169 @@
}
}
}
.lightMode {
.format-options-popover {
.ant-popover-inner {
border: 1px solid var(--l1-border);
background: linear-gradient(
139deg,
rgba(255, 255, 255, 0.8) 0%,
rgba(255, 255, 255, 0.9) 98.68%
);
box-shadow: 4px 10px 16px 2px rgba(255, 255, 255, 0.2);
.nested-menu-container {
.font-size-dropdown {
.back-btn {
.text {
color: var(--l2-background);
}
}
.content {
.option-btn {
.text {
color: var(--l2-background);
}
.text:hover {
color: var(--l3-background);
}
}
}
}
.add-new-column-container {
.add-new-column-header {
.title {
color: var(--l2-foreground);
}
}
.add-new-column-content {
.column-format-new-options {
.column-name {
color: var(--l2-background);
&.selected {
background-color: var(--l3-background);
}
}
}
.loading-container {
color: var(--l2-background);
}
}
}
.font-size-container {
.title {
color: var(--l2-foreground);
}
.value {
.font-value {
color: var(--l2-background);
}
}
}
.horizontal-line {
background: var(--l3-background);
}
.item-content {
.column-divider {
border-top: 2px solid var(--l1-border);
}
}
.max-lines-per-row {
.title {
color: var(--l2-foreground);
.lucide {
color: var(--l1-foreground);
}
}
.max-lines-per-row-input {
border: 1px solid var(--l1-border);
.periscope-btn {
background: var(--l3-background);
}
}
}
.menu-container {
.title {
color: var(--l2-foreground);
}
.item {
.item-label {
color: var(--l2-background);
}
}
}
.selected-item-content-container {
.title {
color: var(--l2-foreground);
.lucide {
color: var(--l1-foreground);
}
}
.horizontal-line {
background: var(--l3-background);
}
.item-content {
.max-lines-per-row-input {
border: 1px solid var(--l1-border);
.periscope-btn {
background: var(--l3-background);
}
}
.column-format,
.column-format-new-options {
.column-name {
color: var(--l1-foreground);
}
}
}
}
&.active {
.nested-menu-container {
backdrop-filter: blur(18px);
.item {
.item-label {
color: var(--l1-foreground);
}
}
}
.selected-item-content-container {
border: 1px solid var(--l1-border);
background: linear-gradient(
139deg,
rgba(255, 255, 255, 0.8) 0%,
rgba(255, 255, 255, 0.9) 98.68%
);
box-shadow: 4px 10px 16px 2px rgba(255, 255, 255, 0.2);
}
}
}
}
}
}

View File

@@ -50,8 +50,8 @@ const havingOperators = [
value: 'IN',
},
{
label: 'NOT IN',
value: 'NOT IN',
label: 'NOT_IN',
value: 'NOT_IN',
},
];
@@ -129,7 +129,7 @@ function HavingFilter({
const operator = havingOperators[j];
newOptions.push({
label: `${opt.func}(${opt.arg}) ${operator.label}`,
value: `${opt.func}(${opt.arg}) ${operator.value} `,
value: `${opt.func}(${opt.arg}) ${operator.label} `,
apply: (
view: EditorView,
completion: { label: string; value: string },

View File

@@ -11,8 +11,6 @@
&__title {
font-weight: 500;
font-size: 15px;
padding: 0;
margin: 0;
}
&__container {
@@ -21,7 +19,7 @@
background-color: var(--primary-background);
color: var(--primary-foreground);
border-radius: 8px;
padding: 12px;
padding: 16px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 1000;
}
@@ -45,23 +43,14 @@
&__footer {
display: flex;
justify-content: flex-end;
}
// TODO: Need to override the button styles for this component due to container styles.
// Fix - @aks07
&__button {
margin-top: 12px;
color: var(--base-black);
background-color: var(--base-white);
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
&:hover {
background-color: var(--base-white);
color: var(--bg-robin-500);
}
}
&__button {
background: var(--secondary-background);
color: var(--secondary-foreground);
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
}
}

View File

@@ -1,7 +1,7 @@
import { useState } from 'react';
import { Button } from '@signozhq/ui';
import { Button, Typography } from 'antd';
import classNames from 'classnames';
import { Check, X } from 'lucide-react';
import { X } from 'lucide-react';
import './AnnouncementTooltip.styles.scss';
@@ -46,12 +46,13 @@ function AnnouncementTooltip({
className={classNames('announcement-tooltip__container', className)}
style={{
top: position.top,
left: position.left + 20,
left: position.left + 30,
}}
>
<div className="announcement-tooltip__header">
<p className="announcement-tooltip__title">{title}</p>
<Typography.Text className="announcement-tooltip__title">
{title}
</Typography.Text>
<X
size={18}
onClick={closeTooltip}
@@ -60,13 +61,7 @@ function AnnouncementTooltip({
</div>
<p className="announcement-tooltip__message">{message}</p>
<div className="announcement-tooltip__footer">
<Button
variant="solid"
color="primary"
onClick={closeTooltip}
prefix={<Check size={16} />}
className="announcement-tooltip__footer__button"
>
<Button onClick={closeTooltip} className="announcement-tooltip__button">
Okay
</Button>
</div>

View File

@@ -9,4 +9,5 @@ export enum FeatureKeys {
ANOMALY_DETECTION = 'anomaly_detection',
ONBOARDING_V3 = 'onboarding_v3',
DOT_METRICS_ENABLED = 'dot_metrics_enabled',
ENABLE_BODY_JSON_QUERY = 'enable_body_json_query',
}

View File

@@ -22,7 +22,7 @@
.domain-detail-drawer {
border-left: 1px solid var(--l1-border);
background: var(--l2-background) !important;
background: var(--l2-background);
box-shadow: -4px 10px 16px 2px rgba(0, 0, 0, 0.2);
.ant-drawer-header {
@@ -309,12 +309,16 @@
}
.ant-table-tbody > tr:hover > td {
background-color: var(--l2-background);
background: color-mix(in srgb, var(--l1-foreground) 4%, transparent);
cursor: pointer;
}
.ant-table-tbody > tr:hover > td:has(.endpoint-name-value) {
background-color: var(--l2-background);
background: color-mix(
in srgb,
var(--l1-foreground) 4%,
color-mix(in srgb, var(--l3-background) 60%, transparent)
);
}
.ant-table-cell:first-child {
@@ -355,6 +359,10 @@
background: none;
}
.table-row-dark {
background: var(--l3-background);
}
.endpoint-name-value {
display: flex;
align-items: center;
@@ -528,7 +536,7 @@
}
.ant-table-tbody > tr:hover > td {
background: color-mix(in srgb, var(--l1-background) 4%, transparent);
background: color-mix(in srgb, var(--l1-foreground) 4%, transparent);
}
.ant-table-tbody > tr:hover > td:has(.status-code-value) {
@@ -578,7 +586,7 @@
}
.table-row-dark {
background: var(--l2-background);
background: var(--l3-background);
}
.endpoint-name-value {
@@ -760,7 +768,6 @@
.dependent-services-container {
border-radius: 3px;
border: 1px solid var(--l1-border);
.ant-table {
.ant-table-thead > tr > th {
padding: 12px;
@@ -790,7 +797,7 @@
}
.ant-table-cell {
padding: 0px;
padding: 12px;
font-size: 13px;
line-height: 20px;
color: var(--l1-foreground);
@@ -805,11 +812,13 @@
.ant-table-cell:has(.top-services-item-latency) {
text-align: center;
opacity: 0.8;
background: color-mix(in srgb, var(--bg-robin-200) 4%, transparent);
}
.ant-table-cell:has(.top-services-item-latency-title) {
text-align: center;
opacity: 0.8;
background: color-mix(in srgb, var(--bg-robin-200) 4%, transparent);
}
.ant-table-tbody > tr:hover > td {
@@ -844,7 +853,7 @@
}
.table-row-dark {
background: var(--l2-background);
background: var(--l3-background);
}
.ant-table-content {
@@ -902,6 +911,7 @@
}
.top-services-item-progress-bar {
background-color: var(--l1-border);
border-radius: 2px;
height: 100%;
position: absolute;
@@ -1017,3 +1027,213 @@
overflow-wrap: break-word;
}
}
.lightMode {
.domain-navigate-cta {
border: 1px solid var(--l1-border);
color: var(--l2-foreground);
}
.domain-detail-drawer {
.endpoint-details-card,
.status-code-table-container,
.endpoint-details-filters-container,
.endpoint-details-filters-container-dropdown,
.ant-radio-button-wrapper,
.views-tabs-container,
.ant-btn-default.tab,
.tab::before,
.endpoint-meta-data-pill,
.endpoint-meta-data-label,
.endpoints-table-container,
.group-by-label,
.ant-select-selector,
.ant-drawer-header {
border-color: var(--l1-border) !important;
}
.views-tabs .tab::before {
background: var(--l3-background);
}
.title {
color: var(--l2-foreground);
}
.domain-detail-drawer__endpoint {
.ant-typography {
color: var(--l2-foreground);
background: transparent;
}
}
.radio-button {
border: 1px solid var(--l1-border);
color: var(--l2-foreground);
}
.views-tabs {
.tab {
background: var(--l3-background);
}
.selected_view {
background: var(--l3-background);
}
.selected_view::before {
background: var(--l3-background);
border-left: 1px solid var(--l1-border);
}
}
}
.round-metric-tag {
color: var(--l1-foreground);
}
.group-by-container {
.group-by-label {
color: var(--l2-foreground);
}
}
.endpoints-table-container {
.ant-table {
.ant-table-cell {
color: var(--l1-foreground);
}
.table-row-light {
background: none;
}
.table-row-dark {
background: none;
}
.round-metric-tag {
color: var(--l1-foreground);
}
}
}
.endpoint-meta-data {
.endpoint-meta-data-pill {
.endpoint-meta-data-label {
color: var(--l2-foreground);
}
.endpoint-meta-data-value {
color: var(--l2-foreground);
}
}
}
.status-code-table-container {
.ant-table {
.ant-table-cell {
color: var(--l1-foreground);
}
.table-row-light {
background: none;
}
.table-row-dark {
background: none;
}
.round-metric-tag {
color: var(--l1-foreground);
}
}
}
.top-services-content {
border-color: var(--l1-border);
}
.dependent-services-container {
border: none;
padding: 10px 12px;
.top-services-item {
display: flex;
justify-content: space-between;
align-items: center;
.top-services-item-progress {
display: flex;
gap: 12px;
height: 34px;
width: 100%;
color: var(--l1-foreground);
padding: 0 12px;
margin-right: 12px;
align-items: center;
position: relative;
.top-services-item-key {
color: var(--l2-foreground);
}
.top-services-item-count {
background-color: var(--l3-background);
color: var(--l2-foreground);
}
.top-services-item-progress-bar {
background-color: var(--l2-background);
border: 1px solid var(--l1-border);
}
}
}
.ant-table {
.ant-table-thead > tr > th {
color: var(--l2-foreground);
}
.ant-table-cell {
color: var(--l2-foreground);
}
.ant-table-tbody > tr:hover > td {
background: var(--l2-background);
}
.table-row-dark {
background: var(--l3-background);
}
}
.top-services-item-percentage {
color: var(--l2-foreground);
}
.top-services-load-more {
color: var(--l2-foreground);
}
}
.error-state-container {
.error-state-content-wrapper {
.refresh-cta {
color: var(--l2-foreground);
border: 1px solid var(--l1-border);
}
}
}
.end-point-details-zero-state-wrapper {
.end-point-details-zero-state-content-wrapper {
.end-point-details-zero-state-text-content {
.title {
color: var(--l2-foreground);
}
.description {
color: var(--l1-foreground);
}
}
}
}
// Add border-bottom to table cells when pagination is not present
.ant-spin-container:not(:has(.ant-pagination)) .ant-table-cell {
border-bottom: 1px solid var(--l1-border) !important;
}
}

View File

@@ -71,7 +71,7 @@
}
.ant-table-thead > tr > th:has(.domain-list-name-col-header) {
background: var(--l2-background);
background: color-mix(in srgb, var(--l3-background) 60%, transparent);
}
.ant-table-cell {
@@ -83,7 +83,7 @@
}
.ant-table-cell:has(.domain-list-name-col-value) {
background: var(--l2-background);
background: color-mix(in srgb, var(--l3-background) 60%, transparent);
}
.round-metric-tag {
@@ -107,7 +107,7 @@
background: color-mix(
in srgb,
var(--l1-foreground) 4%,
var(--l2-background)
color-mix(in srgb, var(--l3-background) 60%, transparent)
);
}
@@ -147,7 +147,7 @@
}
.table-row-dark {
background: var(--l2-background);
background: var(--l3-background);
}
.expanded-clickable-row {

View File

@@ -98,6 +98,27 @@
align-items: center;
gap: 8px;
button {
display: flex;
justify-content: center;
align-items: center;
border: none;
border: 1px solid var(--l1-border);
box-shadow: none !important;
&.ant-btn-round {
padding: 8px 12px 8px 10px;
font-weight: 500;
}
&.ant-btn-round:disabled {
background-color: rgba(209, 209, 209, 0.074);
color: var(--muted-foreground);
}
}
.ant-select-focused {
border-color: transparent !important;
@@ -119,12 +140,6 @@
.hidden {
display: none;
}
button {
display: flex;
align-items: center;
justify-content: center;
}
}
.app-content {
@@ -218,6 +233,19 @@
justify-content: flex-end;
padding: 16px 16px;
margin: 0;
> button {
display: flex;
align-items: center;
border-radius: 2px;
background-color: var(--primary-background) !important;
color: var(--l1-foreground) !important;
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: 24px;
}
}
}
.title {
@@ -229,3 +257,97 @@
line-height: 20px;
}
}
.lightMode {
.explorer-options-container {
background: var(--l1-background);
}
.explorer-options {
background: transparent;
box-shadow: none;
border: 1px solid var(--l1-border);
backdrop-filter: blur(20px);
hr {
border-color: var(--l1-border);
}
.view-options,
.actions {
button {
border: 1px solid var(--l1-border);
color: var(--l2-foreground);
background-color: var(--l3-background);
}
.info-icon {
color: var(--l2-foreground);
}
}
}
.render-options {
color: var(--l2-foreground);
}
.explorer-update {
border: 1px solid var(--l1-border);
background: transparent;
box-shadow: 4px 4px 16px 4px rgba(255, 255, 255, 0.55);
backdrop-filter: blur(20px);
.action-icon {
border: 1px solid var(--l1-border);
background: var(--l3-background);
}
.ant-divider {
border-color: var(--l1-border);
}
}
.ant-tooltip-arrow {
border-top-color: var(--l1-border) !important;
}
.ant-tooltip-inner {
background-color: var(--l3-background);
color: var(--l2-foreground);
}
.save-view-modal {
.ant-modal-content {
background: var(--l2-background);
border-color: var(--l1-border);
.ant-modal-header {
background: var(--l2-background);
border-bottom: 1px solid var(--l1-border);
}
.ant-modal-body {
.ant-typography {
color: var(--l2-foreground);
}
.ant-color-picker-trigger {
border: 1px solid var(--l1-border);
background: var(--l1-background);
.ant-color-picker-color-block {
.ant-color-picker-color-block-inner {
svg {
fill: var(--l2-foreground);
}
}
}
}
}
}
.title {
color: var(--l2-foreground);
}
}
}

View File

@@ -8,8 +8,19 @@ import {
OPERATORS,
QUERY_BUILDER_FUNCTIONS,
} from 'constants/antlrQueryConstants';
import { FeatureKeys } from 'constants/features';
import { QueryParams } from 'constants/query';
import { useActiveLog } from 'hooks/logs/useActiveLog';
import { useGetSearchQueryParam } from 'hooks/queryBuilder/useGetSearchQueryParam';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { ICurrentQueryData } from 'hooks/useHandleExplorerTabChange';
import { useNotifications } from 'hooks/useNotifications';
import { ExplorerViews } from 'pages/LogsExplorer/utils';
import { useAppContext } from 'providers/App/App';
import {
BaseAutocompleteData,
DataTypes,
} from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TitleWrapper } from './BodyTitleRenderer.styles';
import { DROPDOWN_KEY } from './constant';
@@ -25,17 +36,32 @@ function BodyTitleRenderer({
parentIsArray = false,
nodeKey,
value,
handleChangeSelectedView,
}: BodyTitleRendererProps): JSX.Element {
const { onAddToQuery } = useActiveLog();
const { stagedQuery, updateQueriesData } = useQueryBuilder();
const { featureFlags } = useAppContext();
const [, setCopy] = useCopyToClipboard();
const { notifications } = useNotifications();
const viewName = useGetSearchQueryParam(QueryParams.viewName) || '';
const cleanedNodeKey = removeObjectFromString(nodeKey);
const isBodyJsonQueryEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.ENABLE_BODY_JSON_QUERY)
?.active || false;
// Group by is supported only for body json query enabled and not for array elements
const isGroupBySupported =
isBodyJsonQueryEnabled && !cleanedNodeKey.includes('[]');
const filterHandler = (isFilterIn: boolean) => (): void => {
if (parentIsArray) {
onAddToQuery(
generateFieldKeyForArray(
removeObjectFromString(nodeKey),
cleanedNodeKey,
getDataTypes(value),
isBodyJsonQueryEnabled,
),
`${value}`,
isFilterIn
@@ -45,7 +71,7 @@ function BodyTitleRenderer({
);
} else {
onAddToQuery(
`body.${removeObjectFromString(nodeKey)}`,
`body.${cleanedNodeKey}`,
`${value}`,
isFilterIn ? OPERATORS['='] : OPERATORS['!='],
getDataTypes(value),
@@ -53,10 +79,67 @@ function BodyTitleRenderer({
}
};
const groupByHandler = useCallback((): void => {
if (!stagedQuery) {
return;
}
const groupByKey = parentIsArray
? generateFieldKeyForArray(
cleanedNodeKey,
getDataTypes(value),
isBodyJsonQueryEnabled,
)
: `body.${cleanedNodeKey}`;
const fieldDataType = getDataTypes(value);
const normalizedDataType: DataTypes | undefined = Object.values(
DataTypes,
).includes(fieldDataType as DataTypes)
? (fieldDataType as DataTypes)
: undefined;
const updatedQuery = updateQueriesData(
stagedQuery,
'queryData',
(item, index) => {
if (index === 0) {
const newGroupByItem: BaseAutocompleteData = {
key: groupByKey,
type: '',
dataType: normalizedDataType,
};
return { ...item, groupBy: [...(item.groupBy || []), newGroupByItem] };
}
return item;
},
);
const queryData: ICurrentQueryData = {
name: viewName,
id: updatedQuery.id,
query: updatedQuery,
};
handleChangeSelectedView?.(ExplorerViews.TIMESERIES, queryData);
}, [
cleanedNodeKey,
handleChangeSelectedView,
isBodyJsonQueryEnabled,
parentIsArray,
stagedQuery,
updateQueriesData,
value,
viewName,
]);
const onClickHandler: MenuProps['onClick'] = (props): void => {
const mapper = {
[DROPDOWN_KEY.FILTER_IN]: filterHandler(true),
[DROPDOWN_KEY.FILTER_OUT]: filterHandler(false),
[DROPDOWN_KEY.GROUP_BY]: groupByHandler,
};
const handler = mapper[props.key];
@@ -76,6 +159,14 @@ function BodyTitleRenderer({
key: DROPDOWN_KEY.FILTER_OUT,
label: `Filter out ${value}`,
},
...(isGroupBySupported
? [
{
key: DROPDOWN_KEY.GROUP_BY,
label: `Group by ${nodeKey}`,
},
]
: []),
],
onClick: onClickHandler,
};
@@ -84,7 +175,6 @@ function BodyTitleRenderer({
(e: React.MouseEvent): void => {
// Prevent tree node expansion/collapse
e.stopPropagation();
const cleanedKey = removeObjectFromString(nodeKey);
let copyText: string;
// Check if value is an object or array
@@ -106,8 +196,8 @@ function BodyTitleRenderer({
if (copyText) {
const notificationMessage = isObject
? `${cleanedKey} object copied to clipboard`
: `${cleanedKey} copied to clipboard`;
? `${cleanedNodeKey} object copied to clipboard`
: `${cleanedNodeKey} copied to clipboard`;
notifications.success({
message: notificationMessage,
@@ -115,7 +205,7 @@ function BodyTitleRenderer({
});
}
},
[nodeKey, parentIsArray, setCopy, value, notifications],
[cleanedNodeKey, parentIsArray, setCopy, value, notifications],
);
return (

View File

@@ -1,3 +1,4 @@
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
import { MetricsType } from 'container/MetricsApplication/constant';
import { ILog } from 'types/api/logs/log';
@@ -6,6 +7,7 @@ export interface BodyTitleRendererProps {
nodeKey: string;
value: unknown;
parentIsArray?: boolean;
handleChangeSelectedView?: ChangeViewFunctionType;
}
export type AnyObject = { [key: string]: any };

View File

@@ -50,8 +50,7 @@
.ant-collapse-header {
align-items: center;
border-radius: 2px;
background-color: var(--l3-background);
border: 1px solid var(--l3-border);
background: color-mix(in srgb, var(--bg-robin-200) 4%, transparent);
padding: 8px;
@@ -71,7 +70,7 @@
.ant-collapse-content {
padding: 0;
background: var(--l2-background);
background: var(--l1-background);
border-top: 1px solid var(--l1-border);
.ant-collapse-content-box {

View File

@@ -1,8 +1,15 @@
import { ReactNode, useState } from 'react';
import MEditor, { EditorProps, Monaco } from '@monaco-editor/react';
import { Color } from '@signozhq/design-tokens';
import { Button } from '@signozhq/ui';
import { Collapse, Divider, Input, Switch, Tag, Typography } from 'antd';
import {
Button,
Collapse,
Divider,
Input,
Switch,
Tag,
Typography,
} from 'antd';
import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC';
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
import { OptionsQuery } from 'container/OptionsMenu/types';
@@ -171,17 +178,13 @@ function Overview({
{isAttributesExpanded && (
<Button
variant="link"
color="none"
className="action-btn"
prefix={<Search size={12} />}
icon={<Search size={12} />}
onClick={(e): void => {
e.stopPropagation();
handleSearchVisible();
}}
>
Search
</Button>
/>
)}
</div>
),

View File

@@ -1,6 +1,6 @@
.attribute-table-container {
.ant-table {
background: var(--l2-background);
background: var(--l1-background);
.ant-table-row:hover {
.ant-table-cell {
@@ -19,7 +19,6 @@
.ant-table-cell {
border: 1px solid var(--l1-border);
background: var(--l2-background);
}
.attribute-name {
@@ -55,10 +54,10 @@
}
.value-field-container {
background: var(--l2-background);
background: var(--l3-background);
&.attribute-pin {
background: var(--l2-background);
background: var(--l1-background);
}
.value-field {

View File

@@ -2,6 +2,7 @@ import React, { useCallback, useMemo, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { Color } from '@signozhq/design-tokens';
import { Button, Popover, Spin, Tooltip, Tree } from 'antd';
import type { DataNode } from 'antd/es/tree';
import GroupByIcon from 'assets/CustomIcons/GroupByIcon';
import cx from 'classnames';
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
@@ -57,7 +58,7 @@ interface ITableViewActionsProps {
}
// Memoized Tree Component
const MemoizedTree = React.memo<{ treeData: any[] }>(({ treeData }) => (
const MemoizedTree = React.memo<{ treeData: DataNode[] }>(({ treeData }) => (
<Tree
defaultExpandAll
showLine
@@ -74,50 +75,54 @@ const BodyContent: React.FC<{
record: DataType;
bodyHtml: { __html: string };
textToCopy: string;
}> = React.memo(({ fieldData, record, bodyHtml, textToCopy }) => {
const { isLoading, treeData, error } = useAsyncJSONProcessing(
fieldData.value,
record.field === 'body',
);
// Show JSON tree if available, otherwise show HTML content
if (record.field === 'body' && treeData) {
return <MemoizedTree treeData={treeData} />;
}
if (record.field === 'body' && isLoading) {
return (
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Spin size="small" />
<span style={{ color: Color.BG_SIENNA_400 }}>Processing JSON...</span>
</div>
handleChangeSelectedView?: ChangeViewFunctionType;
}> = React.memo(
({ fieldData, record, bodyHtml, textToCopy, handleChangeSelectedView }) => {
const { isLoading, treeData, error } = useAsyncJSONProcessing(
fieldData.value,
record.field === 'body',
handleChangeSelectedView,
);
}
if (record.field === 'body' && error) {
return (
<span
style={{ color: Color.BG_SIENNA_400, whiteSpace: 'pre-wrap', tabSize: 4 }}
>
Error parsing Body JSON
</span>
);
}
// Show JSON tree if available, otherwise show HTML content
if (record.field === 'body' && treeData) {
return <MemoizedTree treeData={treeData} />;
}
if (record.field === 'body') {
return (
<CopyClipboardHOC entityKey="body" textToCopy={textToCopy}>
if (record.field === 'body' && isLoading) {
return (
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Spin size="small" />
<span style={{ color: Color.BG_SIENNA_400 }}>Processing JSON...</span>
</div>
);
}
if (record.field === 'body' && error) {
return (
<span
style={{ color: Color.BG_SIENNA_400, whiteSpace: 'pre-wrap', tabSize: 4 }}
>
<span dangerouslySetInnerHTML={bodyHtml} />
Error parsing Body JSON
</span>
</CopyClipboardHOC>
);
}
);
}
return null;
});
if (record.field === 'body') {
return (
<CopyClipboardHOC entityKey="body" textToCopy={textToCopy}>
<span
style={{ color: Color.BG_SIENNA_400, whiteSpace: 'pre-wrap', tabSize: 4 }}
>
<span dangerouslySetInnerHTML={bodyHtml} />
</span>
</CopyClipboardHOC>
);
}
return null;
},
);
BodyContent.displayName = 'BodyContent';
@@ -319,6 +324,7 @@ export default function TableViewActions(
record={record}
bodyHtml={bodyHtml}
textToCopy={textToCopy}
handleChangeSelectedView={handleChangeSelectedView}
/>
);
@@ -342,6 +348,7 @@ export default function TableViewActions(
fieldData,
bodyHtml,
textToCopy,
handleChangeSelectedView,
formatTimezoneAdjustedTimestamp,
cleanTimestamp,
]);
@@ -355,6 +362,7 @@ export default function TableViewActions(
record={record}
bodyHtml={bodyHtml}
textToCopy={textToCopy}
handleChangeSelectedView={handleChangeSelectedView}
/>
{!isListViewPanel && !RESTRICTED_SELECTED_FIELDS.includes(fieldFilterKey) && (
<span className="action-btn">

View File

@@ -1,5 +1,8 @@
import { useEffect, useRef, useState } from 'react';
import { FeatureKeys } from 'constants/features';
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
import { isEmpty } from 'lodash-es';
import { useAppContext } from 'providers/App/App';
import { jsonToDataNodes, recursiveParseJSON } from '../utils';
@@ -9,6 +12,7 @@ const MAX_BODY_BYTES = 100 * 1024; // 100 KB
const useAsyncJSONProcessing = (
value: string,
shouldProcess: boolean,
handleChangeSelectedView?: ChangeViewFunctionType,
): {
isLoading: boolean;
treeData: any[] | null;
@@ -25,6 +29,10 @@ const useAsyncJSONProcessing = (
});
const processingRef = useRef<boolean>(false);
const { featureFlags } = useAppContext();
const isBodyJsonQueryEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.ENABLE_BODY_JSON_QUERY)
?.active || false;
// eslint-disable-next-line sonarjs/cognitive-complexity
useEffect((): (() => void) => {
@@ -47,7 +55,10 @@ const useAsyncJSONProcessing = (
try {
const parsedBody = recursiveParseJSON(value);
if (!isEmpty(parsedBody)) {
const treeData = jsonToDataNodes(parsedBody);
const treeData = jsonToDataNodes(parsedBody, {
isBodyJsonQueryEnabled,
handleChangeSelectedView,
});
setJsonState({ isLoading: false, treeData, error: null });
} else {
setJsonState({ isLoading: false, treeData: null, error: null });
@@ -73,7 +84,10 @@ const useAsyncJSONProcessing = (
try {
const parsedBody = recursiveParseJSON(value);
if (!isEmpty(parsedBody)) {
const treeData = jsonToDataNodes(parsedBody);
const treeData = jsonToDataNodes(parsedBody, {
isBodyJsonQueryEnabled,
handleChangeSelectedView,
});
setJsonState({ isLoading: false, treeData, error: null });
} else {
setJsonState({ isLoading: false, treeData: null, error: null });
@@ -101,7 +115,7 @@ const useAsyncJSONProcessing = (
return (): void => {
processingRef.current = false;
};
}, [value, shouldProcess]);
}, [value, shouldProcess, isBodyJsonQueryEnabled, handleChangeSelectedView]);
return jsonState;
};

View File

@@ -1,4 +1,5 @@
export const DROPDOWN_KEY = {
FILTER_IN: 'filterIn',
FILTER_OUT: 'filterOut',
GROUP_BY: 'groupBy',
};

View File

@@ -1,5 +1,6 @@
import Convert from 'ansi-to-html';
import type { DataNode } from 'antd/es/tree';
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
import { MetricsType } from 'container/MetricsApplication/constant';
import dompurify from 'dompurify';
import { uniqueId } from 'lodash-es';
@@ -34,13 +35,32 @@ export const recursiveParseJSON = (obj: string): Record<string, unknown> => {
}
};
export const computeDataNode = (
key: string,
valueIsArray: boolean,
value: unknown,
nodeKey: string,
parentIsArray: boolean,
): DataNode => ({
type JsonToDataNodesOptions = {
parentKey?: string;
parentIsArray?: boolean;
isBodyJsonQueryEnabled?: boolean;
handleChangeSelectedView?: ChangeViewFunctionType;
};
type ComputeDataNodeOptions = {
key: string;
valueIsArray: boolean;
value: unknown;
nodeKey: string;
parentIsArray: boolean;
isBodyJsonQueryEnabled?: boolean;
handleChangeSelectedView?: ChangeViewFunctionType;
};
export const computeDataNode = ({
key,
valueIsArray,
value,
nodeKey,
parentIsArray,
isBodyJsonQueryEnabled = false,
handleChangeSelectedView,
}: ComputeDataNodeOptions): DataNode => ({
key: uniqueId(),
title: (
<BodyTitleRenderer
@@ -48,20 +68,30 @@ export const computeDataNode = (
nodeKey={nodeKey}
value={value}
parentIsArray={parentIsArray}
handleChangeSelectedView={handleChangeSelectedView}
/>
),
children: jsonToDataNodes(
value as Record<string, unknown>,
valueIsArray ? `${nodeKey}[*]` : nodeKey,
valueIsArray,
),
children: jsonToDataNodes(value as Record<string, unknown>, {
parentKey: valueIsArray
? `${nodeKey}${isBodyJsonQueryEnabled ? '[]' : '[*]'}`
: nodeKey,
parentIsArray: valueIsArray,
isBodyJsonQueryEnabled,
handleChangeSelectedView,
}),
});
export function jsonToDataNodes(
json: Record<string, unknown>,
parentKey = '',
parentIsArray = false,
options: JsonToDataNodesOptions = {},
): DataNode[] {
const {
parentKey = '',
parentIsArray = false,
isBodyJsonQueryEnabled = false,
handleChangeSelectedView,
} = options;
return Object.entries(json).map(([key, value]) => {
let nodeKey = parentKey || key;
if (parentIsArray) {
@@ -74,7 +104,15 @@ export function jsonToDataNodes(
if (parentIsArray) {
if (typeof value === 'object' && value !== null) {
return computeDataNode(key, valueIsArray, value, nodeKey, parentIsArray);
return computeDataNode({
key,
valueIsArray,
value,
nodeKey,
parentIsArray,
isBodyJsonQueryEnabled,
handleChangeSelectedView,
});
}
return {
@@ -85,14 +123,31 @@ export function jsonToDataNodes(
nodeKey={nodeKey}
value={value}
parentIsArray={parentIsArray}
handleChangeSelectedView={handleChangeSelectedView}
/>
),
children: jsonToDataNodes({}, nodeKey, valueIsArray),
children: jsonToDataNodes(
{},
{
parentKey: nodeKey,
parentIsArray: valueIsArray,
isBodyJsonQueryEnabled,
handleChangeSelectedView,
},
),
};
}
if (typeof value === 'object' && value !== null) {
return computeDataNode(key, valueIsArray, value, nodeKey, parentIsArray);
return computeDataNode({
key,
valueIsArray,
value,
nodeKey,
parentIsArray,
isBodyJsonQueryEnabled,
handleChangeSelectedView,
});
}
return {
key: uniqueId(),
@@ -102,6 +157,7 @@ export function jsonToDataNodes(
nodeKey={nodeKey}
value={value}
parentIsArray={parentIsArray}
handleChangeSelectedView={handleChangeSelectedView}
/>
),
};
@@ -123,6 +179,7 @@ export function flattenObject(obj: AnyObject, prefix = ''): AnyObject {
export const generateFieldKeyForArray = (
fieldKey: string,
dataType: DataTypes,
isBodyJsonQueryEnabled = false,
): string => {
let lastDotIndex = fieldKey.lastIndexOf('.');
let resultNodeKey = fieldKey;
@@ -138,6 +195,16 @@ export const generateFieldKeyForArray = (
newResultNodeKey = resultNodeKey.substring(0, lastDotIndex);
}
}
// When filtering for a value inside an array, the query builder expects the
// last array segment to be referenced without the trailing `[]`.
// Examples:
// - has(body.config.features, 'fast_checkout')
// - has(body.config.features[].items, 'pen')
// - has(body.config.features[].items[].variants, 'ballpen')
if (isBodyJsonQueryEnabled && newResultNodeKey.endsWith('[]')) {
newResultNodeKey = newResultNodeKey.slice(0, -2);
}
return `body.${newResultNodeKey}`;
};

View File

@@ -17,9 +17,6 @@
&.is-resizing {
background: var(--l2-background-hover);
}
border: none !important;
background-color: var(--l2-background) !important;
}
.tanstack-header-content {

View File

@@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next';
import { Form, Input } from 'antd';
import { ProcessorFormField } from '../../AddNewProcessor/config';
import { FormLabelStyle } from '../styles';
function DescriptionTextArea({
fieldData,
@@ -12,7 +13,7 @@ function DescriptionTextArea({
<Form.Item
required={false}
name={fieldData.name}
label={fieldData.fieldName}
label={<FormLabelStyle>{fieldData.fieldName}</FormLabelStyle>}
key={fieldData.id}
>
<Input.TextArea rows={3} placeholder={t(fieldData.placeholder)} />

View File

@@ -8,6 +8,7 @@ import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { ProcessorFormField } from '../../../AddNewProcessor/config';
import { formValidationRules } from '../../../config';
import LogsFilterPreview from '../../../Preview/LogsFilterPreview';
import { FormLabelStyle } from '../../styles';
import './styles.scss';
@@ -67,7 +68,7 @@ function FilterInput({ fieldData }: FilterInputProps): JSX.Element {
return (
<Form.Item
required={false}
label={fieldData.fieldName}
label={<FormLabelStyle>{fieldData.fieldName}</FormLabelStyle>}
key={fieldData.id}
rules={formValidationRules}
name={fieldData.name}

View File

@@ -3,6 +3,7 @@ import { Form, Input } from 'antd';
import { ProcessorFormField } from '../../AddNewProcessor/config';
import { formValidationRules } from '../../config';
import { FormLabelStyle } from '../styles';
function NameInput({ fieldData }: NameInputProps): JSX.Element {
const { t } = useTranslation('pipeline');
@@ -10,7 +11,7 @@ function NameInput({ fieldData }: NameInputProps): JSX.Element {
return (
<Form.Item
required={false}
label={fieldData.fieldName}
label={<FormLabelStyle>{fieldData.fieldName}</FormLabelStyle>}
key={fieldData.id}
rules={formValidationRules}
name={fieldData.name}

View File

@@ -3,6 +3,7 @@ import { Form } from 'antd';
import TagInput from 'container/PipelinePage/components/TagInput';
import { ProcessorFormField } from '../../AddNewProcessor/config';
import { FormLabelStyle } from '../styles';
function ProcessorTags({
fieldData,
@@ -14,7 +15,7 @@ function ProcessorTags({
return (
<Form.Item
required={false}
label={fieldData.fieldName}
label={<FormLabelStyle>{fieldData.fieldName}</FormLabelStyle>}
key={fieldData.id}
name={fieldData.name}
>

View File

@@ -1,12 +1,11 @@
import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Button } from '@signozhq/ui';
import { Form, FormInstance, Modal } from 'antd';
import { Button, Divider, Form, FormInstance, Modal } from 'antd';
import { useAppContext } from 'providers/App/App';
import { ActionMode, ActionType, PipelineData } from 'types/api/pipeline/def';
import { v4 } from 'uuid';
import { ModalButtonWrapper } from '../styles';
import { ModalButtonWrapper, ModalTitle } from '../styles';
import { getEditedDataSource, getRecordIndex } from '../utils';
import { renderPipelineForm } from './utils';
@@ -91,39 +90,34 @@ function AddNewPipeline({
return (
<Modal
title={modalTitle}
title={<ModalTitle level={4}>{modalTitle}</ModalTitle>}
centered
open={isOpen}
width={800}
footer={null}
onCancel={onCancelModalHandler}
>
<Divider plain />
<Form
name="add-new-pipeline"
layout="vertical"
onFinish={onFinish}
autoComplete="off"
form={form}
className="add-new-pipeline-form"
>
{renderPipelineForm()}
<Divider plain />
<Form.Item>
<ModalButtonWrapper>
<Button
key="submit"
variant="solid"
color="primary"
type="primary"
htmlType="submit"
onClick={onOkModalHandler}
>
{isEdit ? t('update') : t('create')}
</Button>
<Button
key="cancel"
variant="solid"
color="secondary"
onClick={onCancelModalHandler}
>
<Button key="cancel" onClick={onCancelModalHandler}>
{t('cancel')}
</Button>
</ModalButtonWrapper>

View File

@@ -0,0 +1,7 @@
import styled from 'styled-components';
export const FormLabelStyle = styled.span`
font-size: 0.75rem;
font-weight: 400;
line-height: 1.25rem;
`;

View File

@@ -97,7 +97,6 @@
}
.steps-content {
padding: 0 16px;
max-height: 500px;
}
}

View File

@@ -218,9 +218,30 @@
.ant-modal-footer {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 16px 16px;
margin: 0;
.cancel-btn {
display: flex;
align-items: center;
border: none;
border-radius: 2px;
background: var(--l1-border);
}
.delete-btn {
display: flex;
align-items: center;
border: none;
border-radius: 2px;
background: var(--danger-background);
margin-left: 12px;
}
.delete-btn:hover {
color: var(--l1-foreground);
background: var(--bg-cherry-600);
}
}
}
.title {
@@ -231,3 +252,98 @@
line-height: 20px; /* 142.857% */
}
}
.lightMode {
.save-view-container {
.save-view-content {
.title {
color: var(--l1-foreground);
}
.ant-table-row {
.ant-table-cell {
background: var(--l2-background);
}
&:hover {
.ant-table-cell {
background: var(--l2-background) !important;
}
}
.column-render {
border: 1px solid var(--l1-border);
background: var(--l1-background);
.title-with-action {
.save-view-title {
.ant-typography {
color: var(--l1-foreground);
}
}
.action-btn {
.ant-typography {
color: var(--l1-foreground);
}
}
}
.view-details {
.view-tag {
background: var(--l2-background);
.tag-text {
color: var(--l1-foreground);
}
}
.view-created-by {
color: var(--l1-foreground);
}
.view-created-at {
.ant-typography {
color: var(--l1-foreground);
}
}
}
}
}
}
}
.delete-view-modal {
.ant-modal-content {
border: 1px solid var(--l1-border);
background: var(--card);
.ant-modal-header {
background: var(--card);
.title {
color: var(--l1-foreground);
}
}
.ant-modal-body {
.ant-typography {
color: var(--l1-foreground);
}
.save-view-input {
.ant-input {
background: var(--l1-background);
color: var(--l1-foreground);
}
}
}
.ant-modal-footer {
.cancel-btn {
background: var(--l3-background);
color: var(--l2-foreground);
}
}
}
}
}

View File

@@ -2,8 +2,15 @@ import { ChangeEvent, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom';
import { Color } from '@signozhq/design-tokens';
import { Button } from '@signozhq/ui';
import { ColorPicker, Input, Modal, Table, TableProps, Typography } from 'antd';
import {
Button,
ColorPicker,
Input,
Modal,
Table,
TableProps,
Typography,
} from 'antd';
import logEvent from 'api/common/logEvent';
import {
getViewDetailsUsingViewKey,
@@ -341,19 +348,15 @@ function SaveView(): JSX.Element {
footer={[
<Button
key="cancel"
variant="solid"
color="secondary"
onClick={hideDeleteViewModal}
className="cancel-btn"
prefix={<X size={16} />}
icon={<X size={16} />}
>
Cancel
</Button>,
<Button
key="submit"
variant="solid"
color="destructive"
prefix={<Trash2 size={16} />}
icon={<Trash2 size={16} />}
onClick={onDeleteHandler}
className="delete-btn"
disabled={isDeleteLoading}
@@ -379,9 +382,7 @@ function SaveView(): JSX.Element {
footer={[
<Button
key="submit"
variant="solid"
color="primary"
prefix={<Check size={16} color={Color.BG_VANILLA_100} />}
icon={<Check size={16} color={Color.BG_VANILLA_100} />}
onClick={onUpdateQueryHandler}
disabled={isViewUpdating}
data-testid="save-view"

View File

@@ -149,3 +149,62 @@
}
}
}
.lightMode {
.traces-module-container {
.trace-module {
.ant-tabs-tab {
.tab-item {
color: var(--l2-foreground);
}
}
.ant-tabs-tab-active {
.tab-item {
color: var(--l1-foreground);
}
}
.ant-tabs-nav::before {
border-bottom: 1px solid var(--l1-border) !important;
}
}
.old-switch {
color: var(--l2-foreground);
}
.trace-layout {
.flamegraph-waterfall-toggle {
color: var(--l2-foreground);
}
.span-list-toggle {
color: var(--l2-foreground);
}
.trace-visualisation-tabs {
.ant-tabs-tab {
background: var(--l3-background);
border: 1px solid var(--l1-border);
}
.ant-tabs-tab-active {
background-color: var(--l1-background);
.ant-btn {
color: var(--l1-foreground) !important;
}
}
.ant-tabs-ink-bar {
height: 1px !important;
background: var(--l1-background) !important;
}
.ant-tabs-nav::before {
border-bottom: 1px solid var(--l1-border);
}
}
}
}
}

View File

@@ -87,3 +87,49 @@
}
}
}
// Light mode styles
.lightMode {
.funnel-step-modal {
.ant-modal-content {
.ant-modal-header {
background: var(--l1-foreground);
.ant-modal-title {
color: var(--l2-background);
}
}
}
&__cancel-btn {
color: var(--l2-background);
}
}
.funnel-step-modal-content {
&__label {
color: var(--l2-background);
}
&__input {
background: var(--l2-foreground);
border: 1px solid var(--l1-border);
color: var(--l2-background);
&::placeholder {
color: var(--l2-foreground);
}
&.ant-input-textarea {
.ant-input {
background: var(--l2-foreground);
border: 1px solid var(--l1-border);
color: var(--l2-background);
&::placeholder {
color: var(--l2-foreground);
}
}
}
}
}
}

View File

@@ -87,3 +87,52 @@
}
}
}
// Light mode styles
.lightMode {
.funnel-step-modal {
.ant-modal-content {
.ant-modal-header {
.ant-modal-title {
color: var(--l2-background);
}
}
}
&__ok-btn {
background: var(--primary-background) !important;
}
&__cancel-btn {
color: var(--l2-background);
}
}
.funnel-step-modal-content {
&__label {
color: var(--l2-background);
}
&__input {
background: var(--l2-foreground);
border: 1px solid var(--l1-border);
color: var(--l2-background);
&::placeholder {
color: var(--l2-foreground);
}
&.ant-input-textarea {
.ant-input {
background: var(--l2-foreground);
border: 1px solid var(--l1-border);
color: var(--l2-background);
&::placeholder {
color: var(--l2-foreground);
}
}
}
}
}
}

View File

@@ -87,3 +87,48 @@
}
}
}
// Light mode styles
.lightMode {
.funnel-step-modal {
.ant-modal-content {
.ant-modal-header {
.ant-modal-title {
color: var(--l2-background);
}
}
}
&__cancel-btn {
color: var(--l2-background);
}
}
.funnel-step-modal-content {
&__label {
color: var(--l2-background);
}
&__input {
background: var(--l2-foreground);
border: 1px solid var(--l1-border);
color: var(--l2-background);
&::placeholder {
color: var(--l2-foreground);
}
&.ant-input-textarea {
.ant-input {
background: var(--l2-foreground);
border: 1px solid var(--l1-border);
color: var(--l2-background);
&::placeholder {
color: var(--l2-foreground);
}
}
}
}
}
}

View File

@@ -6,7 +6,7 @@
}
li:first-of-type {
.funnel-breadcrumb__title {
color: var(--l1-foreground);
color: var(--l2-foreground);
}
}
.ant-breadcrumb-separator {
@@ -24,3 +24,15 @@
line-height: 20px; /* 142.857% */
}
}
.lightMode {
.funnel-breadcrumb__title,
.ant-breadcrumb-separator {
color: var(--l2-foreground);
}
li:first-of-type {
.funnel-breadcrumb__title {
color: var(--l2-foreground);
}
}
}

View File

@@ -107,7 +107,6 @@ function FunnelConfiguration({
)}
<div className="funnel-configuration__steps">
{!isTraceDetailsPage && <StepsHeader />}
<StepsContent isTraceDetailsPage={isTraceDetailsPage} span={span} />
</div>
</>

View File

@@ -112,7 +112,6 @@
gap: 6px;
padding: 16px;
padding-left: 12px;
.ant-form-item {
margin: 0;
width: 100%;
@@ -122,8 +121,6 @@
display: flex;
flex-direction: column;
gap: 10px;
width: 100%;
.ant-select-selector {
background: var(--l3-background);
border: 1px solid var(--l1-border);
@@ -132,23 +129,15 @@
line-height: 16px;
}
}
&__service-and-span {
display: flex;
align-items: center;
gap: 12px;
.service,
.span {
flex: 1;
}
.ant-select-selection-placeholder {
color: var(--l2-foreground);
}
.ant-select {
width: 100%;
width: 239px;
}
}
&__where-filter {

View File

@@ -1,6 +1,4 @@
.steps-content {
padding: 0 16px;
.ant-btn {
box-shadow: none;
&-icon {
@@ -112,3 +110,29 @@
}
}
}
// Light mode styles
.lightMode {
.steps-content {
.ant-steps-item-process .ant-steps-item-icon,
.ant-steps-item-icon {
background-color: var(--l3-background) !important;
& > .ant-steps-icon {
color: var(--l1-foreground);
}
}
.ant-steps-item-tail {
&::after {
background-color: var(--l1-border) !important;
}
}
.latency-step-marker {
&::before {
background-color: var(--l3-background);
}
}
}
}

View File

@@ -52,3 +52,29 @@
}
}
}
.lightMode {
.steps-footer {
border-top: 1px solid var(--l1-border);
background: var(--l2-background);
&__left {
color: var(--l2-background);
}
&__valid-traces {
&--none {
color: var(--text-amber-600);
}
}
&__button {
&--save {
background: var(--l3-background);
}
&--run {
background-color: var(--bg-robin-400);
}
}
}
}

View File

@@ -1,14 +1,10 @@
.steps-header {
display: flex;
align-items: flex-start;
align-items: center;
justify-content: center;
gap: 6px;
padding: 16px;
margin-bottom: 16px;
margin-top: 16px;
flex-wrap: wrap;
&__label {
color: var(--l1-foreground);
color: var(--muted-foreground);
font-size: 12px;
font-weight: 600;
line-height: 18px;
@@ -16,7 +12,6 @@
text-transform: uppercase;
flex-shrink: 0;
}
&__divider {
width: 100%;
.ant-divider {
@@ -24,9 +19,8 @@
border-color: var(--l1-border);
}
}
&__time-range {
width: 100%;
min-width: 192px;
height: 32px;
flex-shrink: 0;
.timeSelection-input {
@@ -35,9 +29,23 @@
}
&,
input {
background: var(--l2-background);
background: var(--l3-background);
font-size: 12px;
}
}
}
}
.lightMode {
.steps-header {
&__label {
color: var(--l2-foreground);
}
.timeSelection-input {
&,
input {
background: unset;
}
}
}
}

View File

@@ -1,3 +1,4 @@
import { Divider } from 'antd';
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
import './StepsHeader.styles.scss';
@@ -6,12 +7,14 @@ function StepsHeader(): JSX.Element {
return (
<div className="steps-header">
<div className="steps-header__label">FUNNEL STEPS</div>
<div className="steps-header__divider">
<Divider dashed />
</div>
<div className="steps-header__time-range">
<DateTimeSelectionV2
showAutoRefresh={false}
showRefreshText={false}
hideShareModal
showRecentlyUsed={false}
/>
</div>
</div>

View File

@@ -28,3 +28,15 @@
margin-top: 8px;
}
}
.lightMode {
.empty-funnel-results {
&__title {
color: var(--l1-foreground);
}
&__description {
color: var(--l2-foreground);
}
}
}

View File

@@ -79,3 +79,44 @@
}
}
}
.lightMode {
.funnel-metrics {
background: var(--l1-foreground);
border: 1px solid var(--l1-border);
box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.05);
&__header {
border-bottom: 1px solid var(--l1-border);
}
&__title {
color: var(--l2-foreground);
}
&__subtitle {
&-label {
color: var(--l2-foreground);
}
&-value {
color: var(--l1-foreground);
}
}
&__item {
&:not(:last-child) {
border-right: 1px solid var(--l1-border);
}
&-title {
color: var(--l1-foreground);
}
&-value,
&-unit {
color: var(--l2-foreground);
}
}
}
}

View File

@@ -115,3 +115,39 @@
}
}
}
.lightMode {
.funnel-table {
border-color: var(--l1-border);
.ant-table {
.ant-table-thead > tr > th {
background: var(--l1-foreground);
color: var(--l2-foreground);
}
.ant-table-cell {
background: var(--l1-foreground);
color: var(--l1-background);
}
.ant-table-tbody > tr:hover > td {
background: rgba(0, 0, 0, 0.04);
}
.table-row-light {
background: none;
color: var(--l1-background);
}
.table-row-dark {
background: none;
color: var(--l1-background);
}
}
&__header {
background: var(--l1-foreground);
color: var(--l2-foreground);
}
}
}

View File

@@ -2,7 +2,6 @@
display: flex;
flex-direction: column;
gap: 20px;
&__steps-selector {
display: flex;
justify-content: center;
@@ -14,3 +13,26 @@
gap: 16px;
}
}
.lightMode {
.steps-transition-results {
&__steps-selector {
.views-tabs {
.tab {
background: var(--l1-foreground);
}
.selected_view {
background: var(--l3-background);
border: 1px solid var(--l1-border);
color: var(--l2-foreground);
}
.selected_view::before {
background: var(--l3-background);
border-left: 1px solid var(--l1-border);
}
}
}
}
}

View File

@@ -2,11 +2,10 @@
@use 'components/SearchBar/SearchBar.styles.scss';
.traces-funnels {
padding: 64px 0;
margin-top: 113px;
display: flex;
justify-content: center;
width: 100%;
.ant-btn-icon {
margin: 0 !important;
}

View File

@@ -17,3 +17,9 @@
padding-bottom: 24px;
}
}
.lightMode {
.delete-funnel-modal-content {
color: var(--l2-foreground) !important;
}
}

View File

@@ -60,3 +60,23 @@
}
}
}
.lightMode {
.funnels-empty {
&__content {
border: 1px dashed var(--l2-foreground);
}
&__icon {
color: var(--l1-background);
}
&__title {
color: var(--l1-background);
}
&__subtitle {
color: var(--l2-background);
}
}
}

View File

@@ -12,7 +12,7 @@
&,
input {
font-family: Inter;
background: var(--l2-background);
background: var(--l3-background);
font-size: 14px;
line-height: 18px;
font-style: normal;

View File

@@ -2,20 +2,6 @@ import { orange } from '@ant-design/colors';
import { Color } from '@signozhq/design-tokens';
import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator';
/** 8-bit alpha from a two-digit hex string (e.g. "40" → 64/255). */
function rgbaFromHexColor(hexColor: string, alphaByteHex: string): string {
const hex = hexColor.replace('#', '');
const r = parseInt(hex.slice(0, 2), 16);
const g = parseInt(hex.slice(2, 4), 16);
const b = parseInt(hex.slice(4, 6), 16);
const a = parseInt(alphaByteHex, 16) / 255;
return `rgba(${r}, ${g}, ${b}, ${a})`;
}
function rgbaFromHexColorOpaque(hexColor: string): string {
return rgbaFromHexColor(hexColor, 'ff');
}
export const getDefaultLogBackground = (
isReadOnly?: boolean,
isDarkMode?: boolean,
@@ -40,69 +26,38 @@ export const getActiveLogBackground = (
}
if (isDarkMode) {
switch (logType) {
case LogType.WARN:
return `background-color: ${rgbaFromHexColor(
Color.BG_AMBER_500,
'20',
)} !important;`;
case LogType.ERROR:
return `background-color: ${rgbaFromHexColor(
Color.BG_CHERRY_500,
'20',
)} !important;`;
case LogType.TRACE:
return `background-color: ${rgbaFromHexColor(
Color.BG_FOREST_400,
'20',
)} !important;`;
case LogType.DEBUG:
return `background-color: ${rgbaFromHexColor(
Color.BG_AQUA_500,
'20',
)} !important;`;
case LogType.FATAL:
return `background-color: ${rgbaFromHexColor(
Color.BG_SAKURA_500,
'20',
)} !important;`;
case LogType.INFO:
return `background-color: ${Color.BG_ROBIN_500}40 !important;`;
case LogType.WARN:
return `background-color: ${Color.BG_AMBER_500}40 !important;`;
case LogType.ERROR:
return `background-color: ${Color.BG_CHERRY_500}40 !important;`;
case LogType.TRACE:
return `background-color: ${Color.BG_FOREST_400}40 !important;`;
case LogType.DEBUG:
return `background-color: ${Color.BG_AQUA_500}40 !important;`;
case LogType.FATAL:
return `background-color: ${Color.BG_SAKURA_500}40 !important;`;
default:
return `background-color: ${rgbaFromHexColor(
Color.BG_ROBIN_500,
'20',
)} !important;`;
return `background-color: ${Color.BG_ROBIN_500}40 !important;`;
}
}
// Light mode - use lighter background colors
switch (logType) {
case LogType.INFO:
return `background-color: ${rgbaFromHexColorOpaque(
Color.BG_ROBIN_100,
)} !important;`;
return `background-color: ${Color.BG_ROBIN_100} !important;`;
case LogType.WARN:
return `background-color: ${rgbaFromHexColorOpaque(
Color.BG_AMBER_100,
)} !important;`;
return `background-color: ${Color.BG_AMBER_100} !important;`;
case LogType.ERROR:
return `background-color: ${rgbaFromHexColorOpaque(
Color.BG_CHERRY_100,
)} !important;`;
return `background-color: ${Color.BG_CHERRY_100} !important;`;
case LogType.TRACE:
return `background-color: ${rgbaFromHexColorOpaque(
Color.BG_FOREST_200,
)} !important;`;
return `background-color: ${Color.BG_FOREST_200} !important;`;
case LogType.DEBUG:
return `background-color: ${rgbaFromHexColorOpaque(
Color.BG_AQUA_100,
)} !important;`;
return `background-color: ${Color.BG_AQUA_100} !important;`;
case LogType.FATAL:
return `background-color: ${rgbaFromHexColorOpaque(
Color.BG_SAKURA_100,
)} !important;`;
return `background-color: ${Color.BG_SAKURA_100} !important;`;
default:
return `background-color: ${rgbaFromHexColorOpaque(
Color.BG_VANILLA_300,
)} !important;`;
return `background-color: ${Color.BG_VANILLA_300} !important;`;
}
};
@@ -120,5 +75,5 @@ export const getCustomHighlightBackground = (isHighlighted = false): string => {
return '';
}
return `background-color: ${rgbaFromHexColor(Color.BG_ROBIN_500, '20')};`;
return `background-color: ${Color.BG_ROBIN_500}20;`;
};

View File

@@ -27,11 +27,6 @@ const regions: Region[] = [
displayName: 'Canada (Central)',
},
{ id: 'ca-west-1', name: 'ca-west-1', displayName: 'Canada (West)' },
{
id: 'mx-central-1',
name: 'mx-central-1',
displayName: 'Mexico (Central)',
},
],
},
{
@@ -50,11 +45,6 @@ const regions: Region[] = [
name: 'ap-east-1',
displayName: 'Asia Pacific (Hong Kong)',
},
{
id: 'ap-east-2',
name: 'ap-east-2',
displayName: 'Asia Pacific (Taipei)',
},
{
id: 'ap-northeast-1',
name: 'ap-northeast-1',
@@ -103,17 +93,7 @@ const regions: Region[] = [
{
id: 'ap-southeast-5',
name: 'ap-southeast-5',
displayName: 'Asia Pacific (Malaysia)',
},
{
id: 'ap-southeast-6',
name: 'ap-southeast-6',
displayName: 'Asia Pacific (New Zealand)',
},
{
id: 'ap-southeast-7',
name: 'ap-southeast-7',
displayName: 'Asia Pacific (Thailand)',
displayName: 'Asia Pacific (Auckland)',
},
],
},

View File

@@ -8,7 +8,6 @@ var (
FeatureHideRootUser = featuretypes.MustNewName("hide_root_user")
FeatureGetMetersFromZeus = featuretypes.MustNewName("get_meters_from_zeus")
FeaturePutMetersInZeus = featuretypes.MustNewName("put_meters_in_zeus")
FeatureBodyJSONQuery = featuretypes.MustNewName("enable_body_json_query")
)
func MustNewRegistry() featuretypes.Registry {
@@ -53,14 +52,6 @@ func MustNewRegistry() featuretypes.Registry {
DefaultVariant: featuretypes.MustNewName("disabled"),
Variants: featuretypes.NewBooleanVariants(),
},
&featuretypes.Feature{
Name: FeatureBodyJSONQuery,
Kind: featuretypes.KindBoolean,
Stage: featuretypes.StageExperimental,
Description: "Controls whether body JSON querying is enabled for logs",
DefaultVariant: featuretypes.MustNewName("disabled"),
Variants: featuretypes.NewBooleanVariants(),
},
)
if err != nil {
panic(err)

View File

@@ -60,7 +60,6 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/constants"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
"github.com/SigNoz/signoz/pkg/querybuilder"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
@@ -1775,18 +1774,6 @@ func (aH *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
Route: "",
})
bodyJSONQuery := aH.Signoz.Flagger.BooleanOrEmpty(r.Context(), flagger.FeatureBodyJSONQuery, evalCtx)
if querybuilder.BodyJSONQueryEnabled {
bodyJSONQuery = querybuilder.BodyJSONQueryEnabled
}
featureSet = append(featureSet, &licensetypes.Feature{
Name: valuer.NewString(flagger.FeatureBodyJSONQuery.String()),
Active: bodyJSONQuery,
Usage: 0,
UsageLimit: -1,
Route: "",
})
if constants.IsDotMetricsEnabled {
for idx, feature := range featureSet {
if feature.Name == licensetypes.DotMetricsEnabled {

View File

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

View File

@@ -1,118 +0,0 @@
package sqlmigration
import (
"context"
"encoding/json"
"slices"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
)
var awsRegions = []string{
"af-south-1", "ap-east-1", "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "ap-south-1", "ap-south-2", "ap-southeast-1", "ap-southeast-2", "ap-southeast-3",
"ap-southeast-4", "ap-southeast-5", "ca-central-1", "ca-west-1", "eu-central-1", "eu-central-2", "eu-north-1", "eu-south-1", "eu-south-2", "eu-west-1", "eu-west-2", "eu-west-3",
"il-central-1", "me-central-1", "me-south-1", "sa-east-1", "us-east-1", "us-east-2", "us-west-1", "us-west-2",
}
var awsCloudProvider = valuer.NewString("aws")
type migrateAWSAllRegions struct {
sqlstore sqlstore.SQLStore
}
type cloudIntegrationAWSMigrationRow struct {
bun.BaseModel `bun:"table:cloud_integration"`
ID string `bun:"id"`
Config string `bun:"config"`
}
type awsConfig struct {
Regions []string `json:"regions"`
}
func NewMigrateAWSAllRegionsFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
return factory.NewProviderFactory(
factory.MustNewName("migrate_aws_all_regions"),
func(ctx context.Context, ps factory.ProviderSettings, c Config) (SQLMigration, error) {
return &migrateAWSAllRegions{sqlstore: sqlstore}, nil
},
)
}
func (migration *migrateAWSAllRegions) Register(migrations *migrate.Migrations) error {
if err := migrations.Register(migration.Up, migration.Down); err != nil {
return err
}
return nil
}
func (migration *migrateAWSAllRegions) Up(ctx context.Context, db *bun.DB) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() {
_ = tx.Rollback()
}()
accounts := make([]*cloudIntegrationAWSMigrationRow, 0)
err = tx.NewSelect().
Model(&accounts).
Where("provider = ?", awsCloudProvider.StringValue()).
Where("removed_at IS NULL").
Scan(ctx)
if err != nil {
return err
}
idsToUpdate := make([]string, 0)
for _, account := range accounts {
var cfg awsConfig
if err := json.Unmarshal([]byte(account.Config), &cfg); err != nil {
continue
}
if slices.Contains(cfg.Regions, "all") {
idsToUpdate = append(idsToUpdate, account.ID)
}
}
if len(idsToUpdate) == 0 {
return tx.Commit()
}
configBytes, err := migration.getAllRegionsConfig()
if err != nil {
return err
}
_, err = tx.NewUpdate().
TableExpr("cloud_integration").
Set("config = ?", string(configBytes)).
Where("id IN (?)", bun.In(idsToUpdate)).
Exec(ctx)
if err != nil {
return err
}
return tx.Commit()
}
func (migration *migrateAWSAllRegions) Down(context.Context, *bun.DB) error {
return nil
}
func (migration *migrateAWSAllRegions) getAllRegionsConfig() ([]byte, error) {
cfg := awsConfig{Regions: awsRegions}
newBytes, err := json.Marshal(cfg)
if err != nil {
return nil, err
}
return newBytes, nil
}

View File

@@ -12,7 +12,6 @@ var (
// AWS regions.
AWSRegionAFSouth1 = CloudProviderRegion{valuer.NewString("af-south-1")} // Africa (Cape Town).
AWSRegionAPEast1 = CloudProviderRegion{valuer.NewString("ap-east-1")} // Asia Pacific (Hong Kong).
AWSRegionAPEast2 = CloudProviderRegion{valuer.NewString("ap-east-2")} // Asia Pacific (Taipei).
AWSRegionAPNortheast1 = CloudProviderRegion{valuer.NewString("ap-northeast-1")} // Asia Pacific (Tokyo).
AWSRegionAPNortheast2 = CloudProviderRegion{valuer.NewString("ap-northeast-2")} // Asia Pacific (Seoul).
AWSRegionAPNortheast3 = CloudProviderRegion{valuer.NewString("ap-northeast-3")} // Asia Pacific (Osaka).
@@ -22,9 +21,6 @@ var (
AWSRegionAPSoutheast2 = CloudProviderRegion{valuer.NewString("ap-southeast-2")} // Asia Pacific (Sydney).
AWSRegionAPSoutheast3 = CloudProviderRegion{valuer.NewString("ap-southeast-3")} // Asia Pacific (Jakarta).
AWSRegionAPSoutheast4 = CloudProviderRegion{valuer.NewString("ap-southeast-4")} // Asia Pacific (Melbourne).
AWSRegionAPSoutheast5 = CloudProviderRegion{valuer.NewString("ap-southeast-5")} // Asia Pacific (Malaysia).
AWSRegionAPSoutheast6 = CloudProviderRegion{valuer.NewString("ap-southeast-6")} // Asia Pacific (New Zealand).
AWSRegionAPSoutheast7 = CloudProviderRegion{valuer.NewString("ap-southeast-7")} // Asia Pacific (Thailand).
AWSRegionCACentral1 = CloudProviderRegion{valuer.NewString("ca-central-1")} // Canada (Central).
AWSRegionCAWest1 = CloudProviderRegion{valuer.NewString("ca-west-1")} // Canada West (Calgary).
AWSRegionEUCentral1 = CloudProviderRegion{valuer.NewString("eu-central-1")} // Europe (Frankfurt).
@@ -38,7 +34,6 @@ var (
AWSRegionILCentral1 = CloudProviderRegion{valuer.NewString("il-central-1")} // Israel (Tel Aviv).
AWSRegionMECentral1 = CloudProviderRegion{valuer.NewString("me-central-1")} // Middle East (UAE).
AWSRegionMESouth1 = CloudProviderRegion{valuer.NewString("me-south-1")} // Middle East (Bahrain).
AWSRegionMXCentral1 = CloudProviderRegion{valuer.NewString("mx-central-1")} // Mexico (Central).
AWSRegionSAEast1 = CloudProviderRegion{valuer.NewString("sa-east-1")} // South America (Sao Paulo).
AWSRegionUSEast1 = CloudProviderRegion{valuer.NewString("us-east-1")} // US East (N. Virginia).
AWSRegionUSEast2 = CloudProviderRegion{valuer.NewString("us-east-2")} // US East (Ohio).
@@ -107,11 +102,11 @@ var (
func Enum() []any {
return []any{
// AWS regions.
AWSRegionAFSouth1, AWSRegionAPEast1, AWSRegionAPEast2, AWSRegionAPNortheast1, AWSRegionAPNortheast2, AWSRegionAPNortheast3,
AWSRegionAFSouth1, AWSRegionAPEast1, AWSRegionAPNortheast1, AWSRegionAPNortheast2, AWSRegionAPNortheast3,
AWSRegionAPSouth1, AWSRegionAPSouth2, AWSRegionAPSoutheast1, AWSRegionAPSoutheast2, AWSRegionAPSoutheast3,
AWSRegionAPSoutheast4, AWSRegionAPSoutheast5, AWSRegionAPSoutheast6, AWSRegionAPSoutheast7, AWSRegionCACentral1, AWSRegionCAWest1, AWSRegionEUCentral1, AWSRegionEUCentral2, AWSRegionEUNorth1,
AWSRegionAPSoutheast4, AWSRegionCACentral1, AWSRegionCAWest1, AWSRegionEUCentral1, AWSRegionEUCentral2, AWSRegionEUNorth1,
AWSRegionEUSouth1, AWSRegionEUSouth2, AWSRegionEUWest1, AWSRegionEUWest2, AWSRegionEUWest3,
AWSRegionILCentral1, AWSRegionMECentral1, AWSRegionMESouth1, AWSRegionMXCentral1, AWSRegionSAEast1, AWSRegionUSEast1, AWSRegionUSEast2,
AWSRegionILCentral1, AWSRegionMECentral1, AWSRegionMESouth1, AWSRegionSAEast1, AWSRegionUSEast1, AWSRegionUSEast2,
AWSRegionUSWest1, AWSRegionUSWest2,
// Azure regions.
AzureRegionAustraliaCentral, AzureRegionAustraliaCentral2, AzureRegionAustraliaEast, AzureRegionAustraliaSoutheast,
@@ -132,11 +127,11 @@ func Enum() []any {
var SupportedRegions = map[CloudProviderType][]CloudProviderRegion{
CloudProviderTypeAWS: {
AWSRegionAFSouth1, AWSRegionAPEast1, AWSRegionAPEast2, AWSRegionAPNortheast1, AWSRegionAPNortheast2, AWSRegionAPNortheast3,
AWSRegionAFSouth1, AWSRegionAPEast1, AWSRegionAPNortheast1, AWSRegionAPNortheast2, AWSRegionAPNortheast3,
AWSRegionAPSouth1, AWSRegionAPSouth2, AWSRegionAPSoutheast1, AWSRegionAPSoutheast2, AWSRegionAPSoutheast3,
AWSRegionAPSoutheast4, AWSRegionAPSoutheast5, AWSRegionAPSoutheast6, AWSRegionAPSoutheast7, AWSRegionCACentral1, AWSRegionCAWest1, AWSRegionEUCentral1, AWSRegionEUCentral2, AWSRegionEUNorth1,
AWSRegionAPSoutheast4, AWSRegionCACentral1, AWSRegionCAWest1, AWSRegionEUCentral1, AWSRegionEUCentral2, AWSRegionEUNorth1,
AWSRegionEUSouth1, AWSRegionEUSouth2, AWSRegionEUWest1, AWSRegionEUWest2, AWSRegionEUWest3,
AWSRegionILCentral1, AWSRegionMECentral1, AWSRegionMESouth1, AWSRegionMXCentral1, AWSRegionSAEast1, AWSRegionUSEast1, AWSRegionUSEast2,
AWSRegionILCentral1, AWSRegionMECentral1, AWSRegionMESouth1, AWSRegionSAEast1, AWSRegionUSEast1, AWSRegionUSEast2,
AWSRegionUSWest1, AWSRegionUSWest2,
},
CloudProviderTypeAzure: {