mirror of
https://github.com/SigNoz/signoz.git
synced 2026-04-25 05:10:27 +01:00
Compare commits
2 Commits
feat/cloud
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb10f51cc5 | ||
|
|
cd16081a1e |
@@ -167,7 +167,7 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
azureCloudProviderModule := implcloudprovider.NewAzureCloudProvider(defStore)
|
||||
azureCloudProviderModule := implcloudprovider.NewAzureCloudProvider()
|
||||
cloudProvidersMap := map[cloudintegrationtypes.CloudProviderType]cloudintegration.CloudProviderModule{
|
||||
cloudintegrationtypes.CloudProviderTypeAWS: awsCloudProviderModule,
|
||||
cloudintegrationtypes.CloudProviderTypeAzure: azureCloudProviderModule,
|
||||
|
||||
@@ -18,7 +18,6 @@ func NewAWSCloudProvider(defStore cloudintegrationtypes.ServiceDefinitionStore)
|
||||
return &awscloudprovider{serviceDefinitions: defStore}, nil
|
||||
}
|
||||
|
||||
// TODO: move URL construction logic to cloudintegrationtypes and add unit tests for it.
|
||||
func (provider *awscloudprovider) GetConnectionArtifact(ctx context.Context, account *cloudintegrationtypes.Account, req *cloudintegrationtypes.GetConnectionArtifactRequest) (*cloudintegrationtypes.ConnectionArtifact, error) {
|
||||
baseURL := fmt.Sprintf(cloudintegrationtypes.CloudFormationQuickCreateBaseURL.StringValue(), req.Config.AWS.DeploymentRegion)
|
||||
u, _ := url.Parse(baseURL)
|
||||
|
||||
@@ -2,48 +2,27 @@ package implcloudprovider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||
"github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
|
||||
)
|
||||
|
||||
type azurecloudprovider struct {
|
||||
serviceDefinitions cloudintegrationtypes.ServiceDefinitionStore
|
||||
}
|
||||
type azurecloudprovider struct{}
|
||||
|
||||
func NewAzureCloudProvider(defStore cloudintegrationtypes.ServiceDefinitionStore) cloudintegration.CloudProviderModule {
|
||||
return &azurecloudprovider{
|
||||
serviceDefinitions: defStore,
|
||||
}
|
||||
func NewAzureCloudProvider() cloudintegration.CloudProviderModule {
|
||||
return &azurecloudprovider{}
|
||||
}
|
||||
|
||||
func (provider *azurecloudprovider) GetConnectionArtifact(ctx context.Context, account *cloudintegrationtypes.Account, req *cloudintegrationtypes.GetConnectionArtifactRequest) (*cloudintegrationtypes.ConnectionArtifact, error) {
|
||||
connectionArtifact, err := cloudintegrationtypes.NewAzureConnectionArtifact(account.ID, req.Config.AgentVersion, req.Credentials, req.Config.Azure)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cloudintegrationtypes.ConnectionArtifact{
|
||||
Azure: connectionArtifact,
|
||||
}, nil
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (provider *azurecloudprovider) ListServiceDefinitions(ctx context.Context) ([]*cloudintegrationtypes.ServiceDefinition, error) {
|
||||
return provider.serviceDefinitions.List(ctx, cloudintegrationtypes.CloudProviderTypeAzure)
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (provider *azurecloudprovider) GetServiceDefinition(ctx context.Context, serviceID cloudintegrationtypes.ServiceID) (*cloudintegrationtypes.ServiceDefinition, error) {
|
||||
serviceDef, err := provider.serviceDefinitions.Get(ctx, cloudintegrationtypes.CloudProviderTypeAzure, serviceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// override cloud integration dashboard id.
|
||||
for index, dashboard := range serviceDef.Assets.Dashboards {
|
||||
serviceDef.Assets.Dashboards[index].ID = cloudintegrationtypes.GetCloudIntegrationDashboardID(cloudintegrationtypes.CloudProviderTypeAzure, serviceID.StringValue(), dashboard.ID)
|
||||
}
|
||||
|
||||
return serviceDef, nil
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (provider *azurecloudprovider) BuildIntegrationConfig(
|
||||
@@ -51,56 +30,5 @@ func (provider *azurecloudprovider) BuildIntegrationConfig(
|
||||
account *cloudintegrationtypes.Account,
|
||||
services []*cloudintegrationtypes.StorableCloudIntegrationService,
|
||||
) (*cloudintegrationtypes.ProviderIntegrationConfig, error) {
|
||||
sort.Slice(services, func(i, j int) bool {
|
||||
return services[i].Type.StringValue() < services[j].Type.StringValue()
|
||||
})
|
||||
|
||||
var strategies []*cloudintegrationtypes.AzureTelemetryCollectionStrategy
|
||||
|
||||
for _, storedSvc := range services {
|
||||
svcCfg, err := cloudintegrationtypes.NewServiceConfigFromJSON(cloudintegrationtypes.CloudProviderTypeAzure, storedSvc.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
svcDef, err := provider.GetServiceDefinition(ctx, storedSvc.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
strategy := svcDef.TelemetryCollectionStrategy.Azure
|
||||
if strategy == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
logsEnabled := svcCfg.IsLogsEnabled(cloudintegrationtypes.CloudProviderTypeAzure)
|
||||
metricsEnabled := svcCfg.IsMetricsEnabled(cloudintegrationtypes.CloudProviderTypeAzure)
|
||||
|
||||
if !logsEnabled && !metricsEnabled {
|
||||
continue
|
||||
}
|
||||
|
||||
entry := &cloudintegrationtypes.AzureTelemetryCollectionStrategy{
|
||||
ResourceProvider: strategy.ResourceProvider,
|
||||
ResourceType: strategy.ResourceType,
|
||||
}
|
||||
|
||||
if metricsEnabled && strategy.Metrics != nil {
|
||||
entry.Metrics = strategy.Metrics
|
||||
}
|
||||
|
||||
if logsEnabled && strategy.Logs != nil {
|
||||
entry.Logs = strategy.Logs
|
||||
}
|
||||
|
||||
strategies = append(strategies, entry)
|
||||
}
|
||||
|
||||
return &cloudintegrationtypes.ProviderIntegrationConfig{
|
||||
Azure: cloudintegrationtypes.NewAzureIntegrationConfig(
|
||||
account.Config.Azure.DeploymentRegion,
|
||||
account.Config.Azure.ResourceGroups,
|
||||
strategies,
|
||||
),
|
||||
}, nil
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
@@ -429,14 +429,10 @@ func (module *module) Collect(ctx context.Context, orgID valuer.UUID) (map[strin
|
||||
stats["cloudintegration.aws.connectedaccounts.count"] = awsAccountsCount
|
||||
}
|
||||
|
||||
// get connected accounts for Azure
|
||||
azureAccountsCount, err := module.store.CountConnectedAccounts(ctx, orgID, cloudintegrationtypes.CloudProviderTypeAzure)
|
||||
if err == nil {
|
||||
stats["cloudintegration.azure.connectedaccounts.count"] = azureAccountsCount
|
||||
}
|
||||
|
||||
// NOTE: not adding stats for services for now.
|
||||
|
||||
// TODO: add more cloud providers when supported
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"react",
|
||||
"react-perf",
|
||||
"typescript",
|
||||
"unicorn",
|
||||
"jsx-a11y",
|
||||
"import",
|
||||
"jest",
|
||||
@@ -206,6 +205,8 @@
|
||||
"@typescript-eslint/explicit-function-return-type": "error",
|
||||
// Requires explicit return types on functions
|
||||
"@typescript-eslint/no-var-requires": "error",
|
||||
"@typescript-eslint/no-useless-default-assignment": "off", // provide unsafe fixes in our codebase due to bad typing
|
||||
"@typescript-eslint/no-duplicate-type-constituents": "off", // provide fixes that breaks some assumptions, eg: type A = L, B = L, C = A | B (removes B)
|
||||
// Disallows require() in TypeScript (use import instead)
|
||||
// Disabled - using TypeScript instead
|
||||
"react/jsx-props-no-spreading": "off",
|
||||
@@ -338,82 +339,6 @@
|
||||
"react/no-array-index-key": "warn",
|
||||
// TODO: Changed to warn during oxlint migration, should be changed to error,
|
||||
|
||||
"unicorn/error-message": "warn",
|
||||
"unicorn/escape-case": "warn",
|
||||
"unicorn/new-for-builtins": "warn",
|
||||
"unicorn/no-abusive-eslint-disable": "warn",
|
||||
"unicorn/no-console-spaces": "warn",
|
||||
"unicorn/no-instanceof-array": "warn",
|
||||
"unicorn/no-invalid-remove-event-listener": "warn",
|
||||
"unicorn/no-new-array": "warn",
|
||||
"unicorn/no-new-buffer": "warn",
|
||||
"unicorn/no-thenable": "warn",
|
||||
"unicorn/no-unreadable-array-destructuring": "warn",
|
||||
"unicorn/no-useless-fallback-in-spread": "warn",
|
||||
"unicorn/no-useless-length-check": "warn",
|
||||
"unicorn/no-useless-promise-resolve-reject": "warn",
|
||||
"unicorn/no-useless-spread": "warn",
|
||||
"unicorn/no-zero-fractions": "warn",
|
||||
"unicorn/number-literal-case": "warn",
|
||||
"unicorn/prefer-array-find": "warn",
|
||||
"unicorn/prefer-array-flat": "warn",
|
||||
"unicorn/prefer-array-flat-map": "warn",
|
||||
"unicorn/prefer-array-index-of": "warn",
|
||||
"unicorn/prefer-array-some": "warn",
|
||||
"unicorn/prefer-at": "warn",
|
||||
"unicorn/prefer-code-point": "warn",
|
||||
"unicorn/prefer-date-now": "warn",
|
||||
"unicorn/prefer-default-parameters": "warn",
|
||||
"unicorn/prefer-includes": "warn",
|
||||
"unicorn/prefer-modern-math-apis": "warn",
|
||||
"unicorn/prefer-native-coercion-functions": "warn",
|
||||
"unicorn/prefer-node-protocol": "off",
|
||||
"unicorn/prefer-number-properties": "warn",
|
||||
"unicorn/prefer-optional-catch-binding": "warn",
|
||||
"unicorn/prefer-regexp-test": "warn",
|
||||
"unicorn/prefer-set-has": "warn",
|
||||
"unicorn/prefer-string-replace-all": "warn",
|
||||
"unicorn/prefer-string-slice": "warn",
|
||||
"unicorn/prefer-string-starts-ends-with": "warn",
|
||||
"unicorn/prefer-string-trim-start-end": "warn",
|
||||
"unicorn/prefer-type-error": "warn",
|
||||
"unicorn/require-array-join-separator": "warn",
|
||||
"unicorn/require-number-to-fixed-digits-argument": "warn",
|
||||
"unicorn/throw-new-error": "warn",
|
||||
"unicorn/consistent-function-scoping": "warn",
|
||||
"unicorn/explicit-length-check": "warn",
|
||||
"unicorn/filename-case": [
|
||||
"warn",
|
||||
{
|
||||
"case": "kebabCase"
|
||||
}
|
||||
],
|
||||
"unicorn/no-array-for-each": "warn",
|
||||
"unicorn/no-lonely-if": "warn",
|
||||
"unicorn/no-negated-condition": "warn",
|
||||
"unicorn/no-null": "warn",
|
||||
"unicorn/no-object-as-default-parameter": "warn",
|
||||
"unicorn/no-static-only-class": "warn",
|
||||
"unicorn/no-this-assignment": "warn",
|
||||
"unicorn/no-unreadable-iife": "warn",
|
||||
"unicorn/no-useless-switch-case": "warn",
|
||||
"unicorn/no-useless-undefined": "warn",
|
||||
"unicorn/prefer-add-event-listener": "warn",
|
||||
"unicorn/prefer-dom-node-append": "warn",
|
||||
"unicorn/prefer-dom-node-dataset": "warn",
|
||||
"unicorn/prefer-dom-node-remove": "warn",
|
||||
"unicorn/prefer-dom-node-text-content": "warn",
|
||||
"unicorn/prefer-keyboard-event-key": "warn",
|
||||
"unicorn/prefer-math-trunc": "warn",
|
||||
"unicorn/prefer-modern-dom-apis": "warn",
|
||||
"unicorn/prefer-negative-index": "warn",
|
||||
"unicorn/prefer-prototype-methods": "warn",
|
||||
"unicorn/prefer-query-selector": "warn",
|
||||
"unicorn/prefer-reflect-apply": "warn",
|
||||
"unicorn/prefer-set-size": "warn",
|
||||
"unicorn/prefer-spread": "warn",
|
||||
"unicorn/prefer-ternary": "warn",
|
||||
"unicorn/require-post-message-target-origin": "warn",
|
||||
"oxc/bad-array-method-on-arguments": "error",
|
||||
"oxc/bad-bitwise-operator": "error",
|
||||
"oxc/bad-comparison-sequence": "error",
|
||||
|
||||
@@ -214,9 +214,9 @@
|
||||
"msw": "1.3.2",
|
||||
"npm-run-all": "latest",
|
||||
"orval": "7.18.0",
|
||||
"oxfmt": "0.41.0",
|
||||
"oxlint": "1.59.0",
|
||||
"oxlint-tsgolint": "0.20.0",
|
||||
"oxfmt": "0.46.0",
|
||||
"oxlint": "1.61.0",
|
||||
"oxlint-tsgolint": "0.21.1",
|
||||
"portfinder-sync": "^0.0.2",
|
||||
"postcss": "8.5.6",
|
||||
"postcss-scss": "4.0.9",
|
||||
|
||||
@@ -16,5 +16,6 @@
|
||||
"roles": "Roles",
|
||||
"role_details": "Role Details",
|
||||
"members": "Members",
|
||||
"service_accounts": "Service Accounts"
|
||||
"service_accounts": "Service Accounts",
|
||||
"mcp_server": "MCP Server"
|
||||
}
|
||||
|
||||
@@ -53,5 +53,6 @@
|
||||
"METER": "SigNoz | Meter",
|
||||
"ROLES_SETTINGS": "SigNoz | Roles",
|
||||
"MEMBERS_SETTINGS": "SigNoz | Members",
|
||||
"SERVICE_ACCOUNTS_SETTINGS": "SigNoz | Service Accounts"
|
||||
"SERVICE_ACCOUNTS_SETTINGS": "SigNoz | Service Accounts",
|
||||
"MCP_SERVER": "SigNoz | MCP Server"
|
||||
}
|
||||
|
||||
@@ -16,5 +16,6 @@
|
||||
"roles": "Roles",
|
||||
"role_details": "Role Details",
|
||||
"members": "Members",
|
||||
"service_accounts": "Service Accounts"
|
||||
"service_accounts": "Service Accounts",
|
||||
"mcp_server": "MCP Server"
|
||||
}
|
||||
|
||||
@@ -76,5 +76,6 @@
|
||||
"METER": "SigNoz | Meter",
|
||||
"ROLES_SETTINGS": "SigNoz | Roles",
|
||||
"MEMBERS_SETTINGS": "SigNoz | Members",
|
||||
"SERVICE_ACCOUNTS_SETTINGS": "SigNoz | Service Accounts"
|
||||
"SERVICE_ACCOUNTS_SETTINGS": "SigNoz | Service Accounts",
|
||||
"MCP_SERVER": "SigNoz | MCP Server"
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import {
|
||||
GlobalConfigData,
|
||||
GlobalConfigDataProps,
|
||||
} from 'types/api/globalConfig/types';
|
||||
|
||||
const getGlobalConfig = async (): Promise<
|
||||
SuccessResponseV2<GlobalConfigData>
|
||||
> => {
|
||||
try {
|
||||
const response = await axios.get<GlobalConfigDataProps>(`/global/config`);
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default getGlobalConfig;
|
||||
@@ -32,6 +32,7 @@ function CreateServiceAccountModal(): JSX.Element {
|
||||
SA_QUERY_PARAMS.CREATE_SA,
|
||||
parseAsBoolean.withDefault(false),
|
||||
);
|
||||
const [, setSelectedAccountId] = useQueryState(SA_QUERY_PARAMS.ACCOUNT);
|
||||
|
||||
const { showErrorModal, isErrorModalVisible } = useErrorModal();
|
||||
|
||||
@@ -50,11 +51,12 @@ function CreateServiceAccountModal(): JSX.Element {
|
||||
const { mutate: createServiceAccount, isLoading: isSubmitting } =
|
||||
useCreateServiceAccount({
|
||||
mutation: {
|
||||
onSuccess: async () => {
|
||||
onSuccess: async (response) => {
|
||||
toast.success('Service account created successfully');
|
||||
reset();
|
||||
await setIsOpen(null);
|
||||
await invalidateListServiceAccounts(queryClient);
|
||||
await setSelectedAccountId(response.data.id);
|
||||
},
|
||||
onError: (err) => {
|
||||
const errMessage = convertToApiError(
|
||||
@@ -67,7 +69,7 @@ function CreateServiceAccountModal(): JSX.Element {
|
||||
|
||||
function handleClose(): void {
|
||||
reset();
|
||||
setIsOpen(null);
|
||||
void setIsOpen(null);
|
||||
}
|
||||
|
||||
function handleCreate(values: FormValues): void {
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__tab-group {
|
||||
[data-slot='toggle-group'] {
|
||||
border-radius: 2px;
|
||||
|
||||
@@ -87,6 +87,7 @@ const ROUTES = {
|
||||
HOME_PAGE: '/',
|
||||
PUBLIC_DASHBOARD: '/public/dashboard/:dashboardId',
|
||||
SERVICE_ACCOUNTS_SETTINGS: '/settings/service-accounts',
|
||||
MCP_SERVER: '/settings/mcp-server',
|
||||
} as const;
|
||||
|
||||
export default ROUTES;
|
||||
|
||||
@@ -48,7 +48,8 @@ import { initialQueryMeterWithType } from 'constants/queryBuilder';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { INITIAL_ALERT_THRESHOLD_STATE } from 'container/CreateAlertV2/context/constants';
|
||||
import dayjs from 'dayjs';
|
||||
import { useGetGlobalConfig } from 'hooks/globalConfig/useGetGlobalConfig';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import { useGetGlobalConfig } from 'api/generated/services/global';
|
||||
import useDebouncedFn from 'hooks/useDebouncedFunction';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { cloneDeep, isNil, isUndefined } from 'lodash-es';
|
||||
@@ -358,6 +359,8 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
error: globalConfigError,
|
||||
} = useGetGlobalConfig();
|
||||
|
||||
const globalConfigApiError = convertToApiError(globalConfigError);
|
||||
|
||||
const { mutate: createIngestionKey, isLoading: isLoadingCreateAPIKey } =
|
||||
useCreateIngestionKey<AxiosError<RenderErrorResponseDTO>>();
|
||||
|
||||
@@ -966,7 +969,7 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
|
||||
<div className="ingestion-key-value">
|
||||
<Typography.Text>
|
||||
{APIKey?.value?.substring(0, 2)}********
|
||||
{APIKey?.value?.slice(0, 2)}********
|
||||
{APIKey?.value
|
||||
?.substring(APIKey?.value?.length ? APIKey.value.length - 2 : 0)
|
||||
?.trim()}
|
||||
@@ -1604,11 +1607,11 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
title={
|
||||
<div className="ingestion-url-error-content">
|
||||
<Typography.Text className="ingestion-url-error-code">
|
||||
{globalConfigError?.getErrorCode()}
|
||||
{globalConfigApiError?.getErrorCode()}
|
||||
</Typography.Text>
|
||||
|
||||
<Typography.Text className="ingestion-url-error-message">
|
||||
{globalConfigError?.getErrorMessage()}
|
||||
{globalConfigApiError?.getErrorMessage()}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
.mcp-auth-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-8);
|
||||
padding: var(--padding-4) var(--padding-5);
|
||||
border: 1px solid var(--l2-border);
|
||||
border-radius: 8px;
|
||||
background: var(--l2-background);
|
||||
|
||||
&__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-5);
|
||||
font-size: var(--label-medium-500-font-size);
|
||||
font-weight: var(--label-medium-500-font-weight);
|
||||
color: var(--l1-foreground);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__description {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--foreground);
|
||||
line-height: var(--line-height-18);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-3);
|
||||
}
|
||||
|
||||
&__field-label {
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: 500;
|
||||
color: var(--foreground);
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
&__endpoint-value {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-2);
|
||||
padding: var(--padding-2) var(--padding-3);
|
||||
border: 1px solid var(--l3-border);
|
||||
border-radius: 6px;
|
||||
background: var(--l3-background);
|
||||
font-family: var(--font-family-sf-mono, monospace);
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
color: var(--l1-foreground);
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__cta-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-10);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&__helper-text {
|
||||
font-size: var(--paragraph-small-400-font-size);
|
||||
color: var(--foreground);
|
||||
line-height: var(--paragraph-small-400-line-height);
|
||||
}
|
||||
|
||||
&__info-banner {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-2);
|
||||
padding: var(--padding-2) var(--padding-3);
|
||||
border-left: 3px solid var(--bg-robin-400);
|
||||
background: var(--l3-background);
|
||||
border-radius: 4px;
|
||||
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
color: var(--bg-robin-400);
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { render, screen, userEvent } from 'tests/test-utils';
|
||||
|
||||
import AuthCard from './AuthCard';
|
||||
|
||||
const mockOnCopyInstanceUrl = jest.fn();
|
||||
const mockOnCreateServiceAccount = jest.fn();
|
||||
|
||||
const defaultProps = {
|
||||
instanceUrl: 'http://localhost',
|
||||
onCopyInstanceUrl: mockOnCopyInstanceUrl,
|
||||
onCreateServiceAccount: mockOnCreateServiceAccount,
|
||||
};
|
||||
|
||||
describe('AuthCard', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders the instance URL', () => {
|
||||
render(<AuthCard {...defaultProps} isAdmin />);
|
||||
|
||||
expect(screen.getByTestId('mcp-instance-url')).toHaveTextContent(
|
||||
'http://localhost',
|
||||
);
|
||||
});
|
||||
|
||||
it('shows Create Service Account button for admin', () => {
|
||||
render(<AuthCard {...defaultProps} isAdmin />);
|
||||
|
||||
expect(screen.getByText('Create service account')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'Only admins can create API keys. Ask your workspace admin for a key with read access, then paste it into the API Key field.',
|
||||
),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows info banner for non-admin', () => {
|
||||
render(<AuthCard {...defaultProps} isAdmin={false} />);
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Only admins can create API keys. Ask your workspace admin for a key with read access, then paste it into the API Key field.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('Create service account')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onCopyInstanceUrl when copy button is clicked', async () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
render(<AuthCard {...defaultProps} isAdmin />);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole('button', { name: 'Copy SigNoz instance URL' }),
|
||||
);
|
||||
|
||||
expect(mockOnCopyInstanceUrl).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls onCreateServiceAccount when admin clicks the CTA', async () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
render(<AuthCard {...defaultProps} isAdmin />);
|
||||
|
||||
await user.click(screen.getByText('Create service account'));
|
||||
|
||||
expect(mockOnCreateServiceAccount).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import { Badge, Button } from '@signozhq/ui';
|
||||
import { Info, KeyRound } from '@signozhq/icons';
|
||||
import CopyIconButton from '../CopyIconButton';
|
||||
|
||||
import './AuthCard.styles.scss';
|
||||
|
||||
interface AuthCardProps {
|
||||
isAdmin: boolean;
|
||||
instanceUrl: string;
|
||||
onCopyInstanceUrl: () => void;
|
||||
onCreateServiceAccount: () => void;
|
||||
}
|
||||
|
||||
function AuthCard({
|
||||
isAdmin,
|
||||
instanceUrl,
|
||||
onCopyInstanceUrl,
|
||||
onCreateServiceAccount,
|
||||
}: AuthCardProps): JSX.Element {
|
||||
return (
|
||||
<section className="mcp-auth-card">
|
||||
<h3 className="mcp-auth-card__title">
|
||||
<Badge color="secondary" variant="default">
|
||||
2
|
||||
</Badge>
|
||||
Authenticate from your client
|
||||
</h3>
|
||||
<p className="mcp-auth-card__description">
|
||||
On first connect, your client opens a SigNoz authorization page asking for
|
||||
two values:
|
||||
</p>
|
||||
|
||||
<div className="mcp-auth-card__field">
|
||||
<span className="mcp-auth-card__field-label">SigNoz Instance URL</span>
|
||||
<div className="mcp-auth-card__endpoint-value">
|
||||
<span data-testid="mcp-instance-url">{instanceUrl}</span>
|
||||
<CopyIconButton
|
||||
ariaLabel="Copy SigNoz instance URL"
|
||||
onCopy={onCopyInstanceUrl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mcp-auth-card__field">
|
||||
<span className="mcp-auth-card__field-label">API Key</span>
|
||||
{isAdmin ? (
|
||||
<div className="mcp-auth-card__cta-row">
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
prefix={<KeyRound size={14} />}
|
||||
onClick={onCreateServiceAccount}
|
||||
>
|
||||
Create service account
|
||||
</Button>
|
||||
<span className="mcp-auth-card__helper-text">
|
||||
Create a service account, then add a new key inside it - paste that key
|
||||
into the API Key field.
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mcp-auth-card__info-banner">
|
||||
<Info size={14} />
|
||||
<span className="mcp-auth-card__helper-text">
|
||||
Only admins can create API keys. Ask your workspace admin for a key with
|
||||
read access, then paste it into the API Key field.
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default AuthCard;
|
||||
@@ -0,0 +1,66 @@
|
||||
.mcp-client-tabs-root {
|
||||
button[data-variant='primary'] {
|
||||
border: none;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
// Remove default tab content padding/margin — the card provides spacing.
|
||||
--tab-content-padding: 0;
|
||||
--tab-content-margin: var(--spacing-4) 0 0;
|
||||
}
|
||||
|
||||
.mcp-client-tabs {
|
||||
&__snippet-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-6);
|
||||
margin-top: var(--spacing-2);
|
||||
|
||||
.learn-more {
|
||||
width: fit-content;
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
}
|
||||
|
||||
&__install-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-10);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&__helper-text {
|
||||
font-size: var(--paragraph-small-400-font-size);
|
||||
color: var(--foreground);
|
||||
line-height: var(--paragraph-small-400-line-height);
|
||||
}
|
||||
|
||||
&__endpoint-value {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-2);
|
||||
padding: var(--padding-2) var(--padding-3);
|
||||
border: 1px solid var(--l3-border);
|
||||
border-radius: 6px;
|
||||
background: var(--l3-background);
|
||||
font-family: var(--font-family-sf-mono, monospace);
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
color: var(--l1-foreground);
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__snippet-pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
&__instructions {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--foreground);
|
||||
line-height: var(--line-height-20);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { render, screen, userEvent } from 'tests/test-utils';
|
||||
|
||||
import ClientTabs from './ClientTabs';
|
||||
import { MCP_CLIENTS } from '../clients';
|
||||
|
||||
jest.mock('utils/navigation', () => ({
|
||||
openInNewTab: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockOnTabChange = jest.fn();
|
||||
const mockOnCopySnippet = jest.fn();
|
||||
const mockOnInstallClick = jest.fn();
|
||||
const mockOnDocsLinkClick = jest.fn();
|
||||
|
||||
const MCP_ENDPOINT = 'https://mcp.us.signoz.cloud/mcp';
|
||||
|
||||
const defaultProps = {
|
||||
endpoint: MCP_ENDPOINT,
|
||||
activeTab: MCP_CLIENTS[0].key,
|
||||
onTabChange: mockOnTabChange,
|
||||
onCopySnippet: mockOnCopySnippet,
|
||||
onInstallClick: mockOnInstallClick,
|
||||
onDocsLinkClick: mockOnDocsLinkClick,
|
||||
};
|
||||
|
||||
describe('ClientTabs', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders a tab for each MCP client', () => {
|
||||
render(<ClientTabs {...defaultProps} />);
|
||||
|
||||
MCP_CLIENTS.forEach((client) => {
|
||||
expect(screen.getByText(client.label)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the snippet for clients that provide one (Cursor)', () => {
|
||||
render(<ClientTabs {...defaultProps} activeTab="cursor" />);
|
||||
|
||||
// The snippet is rendered inside a <pre> element; check its content
|
||||
const snippetPre = document.querySelector('.mcp-client-tabs__snippet-pre');
|
||||
expect(snippetPre).toBeInTheDocument();
|
||||
expect(snippetPre?.textContent).toContain(MCP_ENDPOINT);
|
||||
expect(snippetPre?.textContent).toContain('mcpServers');
|
||||
});
|
||||
|
||||
it('renders endpoint block and instructions text for clients without a snippet (Claude Desktop)', () => {
|
||||
render(<ClientTabs {...defaultProps} activeTab="claude-desktop" />);
|
||||
|
||||
const snippetPre = document.querySelector('.mcp-client-tabs__snippet-pre');
|
||||
expect(snippetPre?.textContent).toBe(MCP_ENDPOINT);
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Open Claude Desktop, go to Settings → Connectors → Add custom connector, and paste the endpoint URL above. Claude Desktop does not read remote MCP servers from claude_desktop_config.json - the connector UI is the only supported path.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows enabled install button when endpoint is set (Cursor)', () => {
|
||||
render(<ClientTabs {...defaultProps} activeTab="cursor" />);
|
||||
|
||||
const installBtn = screen.getByRole('button', {
|
||||
name: 'Add to Cursor',
|
||||
});
|
||||
expect(installBtn).toBeEnabled();
|
||||
});
|
||||
|
||||
it('shows disabled install button when endpoint is missing (Cursor)', () => {
|
||||
render(<ClientTabs {...defaultProps} endpoint="" activeTab="cursor" />);
|
||||
|
||||
const installBtn = screen.getByRole('button', {
|
||||
name: 'Add to Cursor',
|
||||
});
|
||||
expect(installBtn).toBeDisabled();
|
||||
});
|
||||
|
||||
it('calls onCopySnippet with client key and snippet on copy', async () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
const cursorClient = MCP_CLIENTS.find((c) => c.key === 'cursor')!;
|
||||
|
||||
render(<ClientTabs {...defaultProps} activeTab="cursor" />);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole('button', {
|
||||
name: `Copy ${cursorClient.label} config`,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(mockOnCopySnippet).toHaveBeenCalledWith(
|
||||
'cursor',
|
||||
cursorClient.snippet!(MCP_ENDPOINT),
|
||||
);
|
||||
});
|
||||
|
||||
it('copy button is disabled when no endpoint', () => {
|
||||
render(<ClientTabs {...defaultProps} endpoint="" activeTab="cursor" />);
|
||||
|
||||
const cursorClient = MCP_CLIENTS.find((c) => c.key === 'cursor')!;
|
||||
const copyBtn = screen.getByRole('button', {
|
||||
name: `Copy ${cursorClient.label} config`,
|
||||
});
|
||||
|
||||
expect(copyBtn).toBeDisabled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
import { useMemo } from 'react';
|
||||
import { Button, Tabs } from '@signozhq/ui';
|
||||
import LearnMore from 'components/LearnMore/LearnMore';
|
||||
import { Download } from '@signozhq/icons';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
import CopyIconButton from '../CopyIconButton';
|
||||
import { docsUrl, MCP_CLIENTS, McpClient } from '../clients';
|
||||
|
||||
import './ClientTabs.styles.scss';
|
||||
|
||||
const ENDPOINT_PLACEHOLDER = 'https://mcp.<region>.signoz.cloud/mcp';
|
||||
|
||||
interface ClientTabsProps {
|
||||
endpoint: string;
|
||||
activeTab: string;
|
||||
onTabChange: (key: string) => void;
|
||||
onCopySnippet: (clientKey: string, snippet: string) => void;
|
||||
onInstallClick: (clientKey: string) => void;
|
||||
onDocsLinkClick: (target: string) => void;
|
||||
}
|
||||
|
||||
function ClientTabs({
|
||||
endpoint,
|
||||
activeTab,
|
||||
onTabChange,
|
||||
onCopySnippet,
|
||||
onInstallClick,
|
||||
onDocsLinkClick,
|
||||
}: ClientTabsProps): JSX.Element {
|
||||
const items = useMemo(
|
||||
() =>
|
||||
MCP_CLIENTS.map((client: McpClient) => {
|
||||
const snippet = client.snippet
|
||||
? client.snippet(endpoint || ENDPOINT_PLACEHOLDER)
|
||||
: null;
|
||||
const installHref =
|
||||
client.installUrl && endpoint ? client.installUrl(endpoint) : null;
|
||||
|
||||
const installLabel = client.installLabel ?? `Add to ${client.label}`;
|
||||
|
||||
return {
|
||||
key: client.key,
|
||||
label: client.label,
|
||||
children: (
|
||||
<div className="mcp-client-tabs__snippet-wrapper">
|
||||
{snippet !== null ? (
|
||||
<div className="mcp-client-tabs__endpoint-value mcp-client-tabs__snippet">
|
||||
<pre className="mcp-client-tabs__snippet-pre">{snippet}</pre>
|
||||
<CopyIconButton
|
||||
ariaLabel={`Copy ${client.label} config`}
|
||||
disabled={!endpoint}
|
||||
onCopy={(): void => onCopySnippet(client.key, snippet)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="mcp-client-tabs__endpoint-value mcp-client-tabs__snippet">
|
||||
<pre className="mcp-client-tabs__snippet-pre">
|
||||
{endpoint || ENDPOINT_PLACEHOLDER}
|
||||
</pre>
|
||||
<CopyIconButton
|
||||
ariaLabel="Copy MCP endpoint"
|
||||
disabled={!endpoint}
|
||||
onCopy={(): void => onCopySnippet(client.key, endpoint)}
|
||||
/>
|
||||
</div>
|
||||
<p className="mcp-client-tabs__instructions">
|
||||
{client.instructions ?? ''}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{client.installUrl && (
|
||||
<div className="mcp-client-tabs__install-row">
|
||||
{installHref ? (
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
prefix={<Download size={14} />}
|
||||
onClick={(): void => {
|
||||
onInstallClick(client.key);
|
||||
openInNewTab(installHref);
|
||||
}}
|
||||
>
|
||||
{installLabel}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
disabled
|
||||
prefix={<Download size={14} />}
|
||||
>
|
||||
{installLabel}
|
||||
</Button>
|
||||
)}
|
||||
<span className="mcp-client-tabs__helper-text">
|
||||
Or copy the config below for manual setup.
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<LearnMore
|
||||
text={`${client.label} setup docs`}
|
||||
url={docsUrl(client.docsPath)}
|
||||
onClick={(): void => onDocsLinkClick(`client-${client.key}`)}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[endpoint, onCopySnippet, onInstallClick, onDocsLinkClick],
|
||||
);
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
className="mcp-client-tabs-root"
|
||||
value={activeTab}
|
||||
onChange={onTabChange}
|
||||
items={items}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default ClientTabs;
|
||||
@@ -0,0 +1,5 @@
|
||||
.mcp-copy-btn {
|
||||
&:hover {
|
||||
background-color: var(--l3-background-hover) !important;
|
||||
}
|
||||
}
|
||||
40
frontend/src/container/MCPServerSettings/CopyIconButton.tsx
Normal file
40
frontend/src/container/MCPServerSettings/CopyIconButton.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Button, Tooltip, TooltipProvider } from '@signozhq/ui';
|
||||
import { Copy } from '@signozhq/icons';
|
||||
import './CopyIconButton.styles.scss';
|
||||
|
||||
interface CopyIconButtonProps {
|
||||
ariaLabel: string;
|
||||
onCopy: () => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
function CopyIconButton({
|
||||
ariaLabel,
|
||||
onCopy,
|
||||
disabled = false,
|
||||
}: CopyIconButtonProps): JSX.Element {
|
||||
const tooltipTitle = disabled
|
||||
? 'Enter your Cloud region first'
|
||||
: 'Copy to clipboard';
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip title={tooltipTitle}>
|
||||
<span>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
aria-label={ariaLabel}
|
||||
disabled={disabled}
|
||||
className="mcp-copy-btn"
|
||||
prefix={<Copy size={14} />}
|
||||
onClick={onCopy}
|
||||
/>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default CopyIconButton;
|
||||
@@ -0,0 +1,60 @@
|
||||
.mcp-settings {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-12);
|
||||
padding: var(--padding-8) var(--padding-2) var(--padding-6) var(--padding-4);
|
||||
max-width: 880px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-2);
|
||||
}
|
||||
|
||||
&__header-title {
|
||||
font-size: var(--label-large-500-font-size);
|
||||
font-weight: var(--label-large-500-font-weight);
|
||||
color: var(--l1-foreground);
|
||||
letter-spacing: -0.09px;
|
||||
line-height: var(--line-height-normal);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__header-subtitle {
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
font-weight: var(--paragraph-base-400-font-weight);
|
||||
color: var(--foreground);
|
||||
letter-spacing: -0.07px;
|
||||
line-height: var(--paragraph-base-400-line-height);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mcp-settings__card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-8);
|
||||
padding: var(--padding-4) var(--padding-5);
|
||||
border: 1px solid var(--l2-border);
|
||||
border-radius: 8px;
|
||||
background: var(--l2-background);
|
||||
|
||||
&-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-5);
|
||||
font-size: var(--label-medium-500-font-size);
|
||||
font-weight: var(--label-medium-500-font-weight);
|
||||
color: var(--l1-foreground);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&-description {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--foreground);
|
||||
line-height: var(--line-height-18);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
import { render, screen, userEvent } from 'tests/test-utils';
|
||||
|
||||
import MCPServerSettings from './MCPServerSettings';
|
||||
|
||||
const mockLogEvent = jest.fn();
|
||||
const mockCopyToClipboard = jest.fn();
|
||||
const mockHistoryPush = jest.fn();
|
||||
const mockUseGetGlobalConfig = jest.fn();
|
||||
const mockToastSuccess = jest.fn();
|
||||
const mockToastWarning = jest.fn();
|
||||
|
||||
jest.mock('api/common/logEvent', () => ({
|
||||
__esModule: true,
|
||||
default: (...args: unknown[]): unknown => mockLogEvent(...args),
|
||||
}));
|
||||
|
||||
jest.mock('api/generated/services/global', () => ({
|
||||
useGetGlobalConfig: (...args: unknown[]): unknown =>
|
||||
mockUseGetGlobalConfig(...args),
|
||||
}));
|
||||
|
||||
jest.mock('react-use', () => ({
|
||||
__esModule: true,
|
||||
useCopyToClipboard: (): [unknown, jest.Mock] => [null, mockCopyToClipboard],
|
||||
}));
|
||||
|
||||
jest.mock('@signozhq/ui', () => ({
|
||||
...jest.requireActual('@signozhq/ui'),
|
||||
toast: {
|
||||
success: (...args: unknown[]): unknown => mockToastSuccess(...args),
|
||||
warning: (...args: unknown[]): unknown => mockToastWarning(...args),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('lib/history', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
push: (...args: unknown[]): unknown => mockHistoryPush(...args),
|
||||
location: { pathname: '/', search: '', hash: '', state: null },
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('utils/basePath', () => ({
|
||||
getBaseUrl: (): string => 'http://localhost',
|
||||
getBasePath: (): string => '/',
|
||||
withBasePath: (p: string): string => p,
|
||||
}));
|
||||
|
||||
const MCP_URL = 'https://mcp.us.signoz.cloud/mcp';
|
||||
|
||||
function setupGlobalConfig({ mcpUrl }: { mcpUrl: string | null }): void {
|
||||
mockUseGetGlobalConfig.mockReturnValue({
|
||||
data: { data: { mcp_url: mcpUrl, ingestion_url: '' }, status: 'success' },
|
||||
isLoading: false,
|
||||
});
|
||||
}
|
||||
|
||||
describe('MCPServerSettings', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('shows loading spinner while config is loading', () => {
|
||||
mockUseGetGlobalConfig.mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: true,
|
||||
});
|
||||
|
||||
render(<MCPServerSettings />);
|
||||
|
||||
expect(document.querySelector('.ant-spin-spinning')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('mcp-settings')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows fallback page when mcp_url is not configured', () => {
|
||||
setupGlobalConfig({ mcpUrl: null });
|
||||
|
||||
render(<MCPServerSettings />);
|
||||
|
||||
expect(
|
||||
screen.getByText('MCP Server is available on SigNoz'),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('mcp-settings')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders main settings page when mcp_url is present', () => {
|
||||
setupGlobalConfig({ mcpUrl: MCP_URL });
|
||||
|
||||
render(<MCPServerSettings />);
|
||||
|
||||
expect(screen.getByTestId('mcp-settings')).toBeInTheDocument();
|
||||
expect(screen.getByText('SigNoz MCP Server')).toBeInTheDocument();
|
||||
expect(screen.getByText('Configure your client')).toBeInTheDocument();
|
||||
expect(screen.getByText('Authenticate from your client')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('fires PAGE_VIEWED analytics event on mount', () => {
|
||||
setupGlobalConfig({ mcpUrl: MCP_URL });
|
||||
|
||||
render(<MCPServerSettings />, undefined, { role: 'ADMIN' });
|
||||
|
||||
expect(mockLogEvent).toHaveBeenCalledWith('MCP Settings: Page viewed', {
|
||||
role: 'ADMIN',
|
||||
});
|
||||
});
|
||||
|
||||
it('admin sees the Create Service Account CTA', () => {
|
||||
setupGlobalConfig({ mcpUrl: MCP_URL });
|
||||
|
||||
render(<MCPServerSettings />, undefined, { role: 'ADMIN' });
|
||||
|
||||
expect(screen.getByText('Create service account')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'Only admins can create API keys. Ask your workspace admin for a key with read access, then paste it into the API Key field.',
|
||||
),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('non-admin sees an info banner instead of the CTA', () => {
|
||||
setupGlobalConfig({ mcpUrl: MCP_URL });
|
||||
|
||||
render(<MCPServerSettings />, undefined, { role: 'VIEWER' });
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Only admins can create API keys. Ask your workspace admin for a key with read access, then paste it into the API Key field.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('Create service account')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('navigates to service accounts when admin clicks Create CTA', async () => {
|
||||
setupGlobalConfig({ mcpUrl: MCP_URL });
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
render(<MCPServerSettings />, undefined, { role: 'ADMIN' });
|
||||
|
||||
await user.click(screen.getByText('Create service account'));
|
||||
|
||||
expect(mockHistoryPush).toHaveBeenCalledWith(
|
||||
'/settings/service-accounts?create-sa=true',
|
||||
);
|
||||
});
|
||||
|
||||
it('copies instance URL and shows success toast', async () => {
|
||||
setupGlobalConfig({ mcpUrl: MCP_URL });
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
render(<MCPServerSettings />);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole('button', { name: 'Copy SigNoz instance URL' }),
|
||||
);
|
||||
|
||||
expect(mockCopyToClipboard).toHaveBeenCalledWith('http://localhost');
|
||||
expect(mockToastSuccess).toHaveBeenCalledWith(
|
||||
'Instance URL copied to clipboard',
|
||||
);
|
||||
});
|
||||
});
|
||||
144
frontend/src/container/MCPServerSettings/MCPServerSettings.tsx
Normal file
144
frontend/src/container/MCPServerSettings/MCPServerSettings.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { SA_QUERY_PARAMS } from 'container/ServiceAccountsSettings/constants';
|
||||
import { useGetGlobalConfig } from 'api/generated/services/global';
|
||||
import history from 'lib/history';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
import { getBaseUrl } from 'utils/basePath';
|
||||
|
||||
import { Badge, toast } from '@signozhq/ui';
|
||||
import Spinner from 'components/Spinner';
|
||||
import AuthCard from './AuthCard/AuthCard';
|
||||
import ClientTabs from './ClientTabs/ClientTabs';
|
||||
import NotCloudFallback from './NotCloudFallback/NotCloudFallback';
|
||||
import UseCasesCard from './UseCasesCard/UseCasesCard';
|
||||
import { MCP_CLIENTS } from './clients';
|
||||
|
||||
import './MCPServerSettings.styles.scss';
|
||||
|
||||
const ANALYTICS = {
|
||||
PAGE_VIEWED: 'MCP Settings: Page viewed',
|
||||
CREATE_SA_CLICKED: 'MCP Settings: Create service account clicked',
|
||||
CLIENT_TAB_SELECTED: 'MCP Settings: Client tab selected',
|
||||
SNIPPET_COPIED: 'MCP Settings: Client snippet copied',
|
||||
ONE_CLICK_INSTALL_CLICKED: 'MCP Settings: One-click install clicked',
|
||||
INSTANCE_URL_COPIED: 'MCP Settings: Instance URL copied',
|
||||
DOCS_LINK_CLICKED: 'MCP Settings: Docs link clicked',
|
||||
} as const;
|
||||
|
||||
function MCPServerSettings(): JSX.Element {
|
||||
const { user } = useAppContext();
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
|
||||
const isAdmin = user.role === USER_ROLES.ADMIN;
|
||||
const instanceUrl = getBaseUrl();
|
||||
|
||||
const { data: globalConfig, isLoading: isConfigLoading } =
|
||||
useGetGlobalConfig();
|
||||
const endpoint = globalConfig?.data?.mcp_url ?? '';
|
||||
|
||||
const [activeTab, setActiveTab] = useState<string>(MCP_CLIENTS[0]?.key ?? '');
|
||||
|
||||
useEffect(() => {
|
||||
void logEvent(ANALYTICS.PAGE_VIEWED, {
|
||||
role: user.role,
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handleCopySnippet = useCallback(
|
||||
(clientKey: string, snippet: string) => {
|
||||
if (!endpoint) {
|
||||
toast.warning('Enter your Cloud region before copying');
|
||||
return;
|
||||
}
|
||||
copyToClipboard(snippet);
|
||||
toast.success('Snippet copied to clipboard');
|
||||
void logEvent(ANALYTICS.SNIPPET_COPIED, { client: clientKey });
|
||||
},
|
||||
[endpoint, copyToClipboard],
|
||||
);
|
||||
|
||||
const handleCreateServiceAccount = useCallback(() => {
|
||||
void logEvent(ANALYTICS.CREATE_SA_CLICKED, {});
|
||||
history.push(
|
||||
`${ROUTES.SERVICE_ACCOUNTS_SETTINGS}?${SA_QUERY_PARAMS.CREATE_SA}=true`,
|
||||
);
|
||||
}, []);
|
||||
|
||||
const handleCopyInstanceUrl = useCallback(() => {
|
||||
copyToClipboard(instanceUrl);
|
||||
toast.success('Instance URL copied to clipboard');
|
||||
void logEvent(ANALYTICS.INSTANCE_URL_COPIED, {});
|
||||
}, [copyToClipboard, instanceUrl]);
|
||||
|
||||
const handleDocsLinkClick = useCallback((target: string) => {
|
||||
void logEvent(ANALYTICS.DOCS_LINK_CLICKED, { target });
|
||||
}, []);
|
||||
|
||||
const handleInstallClick = useCallback((clientKey: string) => {
|
||||
void logEvent(ANALYTICS.ONE_CLICK_INSTALL_CLICKED, { client: clientKey });
|
||||
}, []);
|
||||
|
||||
const handleTabChange = useCallback((key: string) => {
|
||||
setActiveTab(key);
|
||||
void logEvent(ANALYTICS.CLIENT_TAB_SELECTED, { client: key });
|
||||
}, []);
|
||||
|
||||
if (isConfigLoading) {
|
||||
return <Spinner tip="Loading..." height="70vh" />;
|
||||
}
|
||||
|
||||
if (!endpoint) {
|
||||
return <NotCloudFallback />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mcp-settings" data-testid="mcp-settings">
|
||||
<header className="mcp-settings__header">
|
||||
<h1 className="mcp-settings__header-title">SigNoz MCP Server</h1>
|
||||
<p className="mcp-settings__header-subtitle">
|
||||
Connect AI assistants like Claude, Cursor, VS Code, and Codex to your
|
||||
SigNoz data via the Model Context Protocol. Authenticate from your MCP
|
||||
client with a service-account API key.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<section className="mcp-settings__card">
|
||||
<h3 className="mcp-settings__card-title">
|
||||
<Badge color="secondary" variant="default">
|
||||
1
|
||||
</Badge>
|
||||
Configure your client
|
||||
</h3>
|
||||
<p className="mcp-settings__card-description">
|
||||
Add SigNoz to your MCP client. Use a one-click install where available, or
|
||||
copy the config for manual setup. On first connect, the client will open a
|
||||
SigNoz authorization page - use the instance URL and API key from step 2.
|
||||
</p>
|
||||
<ClientTabs
|
||||
endpoint={endpoint}
|
||||
activeTab={activeTab}
|
||||
onTabChange={handleTabChange}
|
||||
onCopySnippet={handleCopySnippet}
|
||||
onInstallClick={handleInstallClick}
|
||||
onDocsLinkClick={handleDocsLinkClick}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<AuthCard
|
||||
isAdmin={isAdmin}
|
||||
instanceUrl={instanceUrl}
|
||||
onCopyInstanceUrl={handleCopyInstanceUrl}
|
||||
onCreateServiceAccount={handleCreateServiceAccount}
|
||||
/>
|
||||
|
||||
<UseCasesCard onDocsLinkClick={handleDocsLinkClick} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MCPServerSettings;
|
||||
@@ -0,0 +1,41 @@
|
||||
.not-cloud-fallback {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
min-height: 60vh;
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-6);
|
||||
padding: var(--padding-6);
|
||||
border: 1px dashed var(--l2-border);
|
||||
border-radius: 8px;
|
||||
background: var(--l2-background);
|
||||
max-width: 50%;
|
||||
|
||||
.learn-more {
|
||||
width: fit-content;
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-2);
|
||||
font-size: var(--label-large-500-font-size);
|
||||
font-weight: var(--label-large-500-font-weight);
|
||||
color: var(--l1-foreground);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__body {
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
font-weight: var(--paragraph-base-400-font-weight);
|
||||
color: var(--foreground);
|
||||
line-height: var(--paragraph-base-400-line-height);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { useCallback } from 'react';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import LearnMore from 'components/LearnMore/LearnMore';
|
||||
|
||||
import './NotCloudFallback.styles.scss';
|
||||
import { MCP_DOCS_URL } from '../clients';
|
||||
|
||||
const DOCS_LINK_EVENT = 'MCP Settings: Docs link clicked';
|
||||
|
||||
function NotCloudFallback(): JSX.Element {
|
||||
const handleDocsClick = useCallback(() => {
|
||||
void logEvent(DOCS_LINK_EVENT, { target: 'fallback' });
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="not-cloud-fallback">
|
||||
<div className="not-cloud-fallback__content">
|
||||
<h2 className="not-cloud-fallback__title">
|
||||
MCP Server is available on SigNoz
|
||||
</h2>
|
||||
<p className="not-cloud-fallback__body">
|
||||
Users can follow the docs to setup the MCP server against their SigNoz
|
||||
instance.
|
||||
</p>
|
||||
<LearnMore
|
||||
text="View MCP Server docs"
|
||||
url={MCP_DOCS_URL}
|
||||
onClick={handleDocsClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default NotCloudFallback;
|
||||
@@ -0,0 +1,35 @@
|
||||
.mcp-use-cases-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-4);
|
||||
padding: var(--padding-4) var(--padding-5);
|
||||
border: 1px solid var(--l2-border);
|
||||
border-radius: 8px;
|
||||
background: var(--l2-background);
|
||||
|
||||
&__title {
|
||||
font-size: var(--label-medium-500-font-size);
|
||||
font-weight: var(--label-medium-500-font-weight);
|
||||
color: var(--l1-foreground);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.learn-more {
|
||||
width: fit-content;
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
|
||||
&__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-1);
|
||||
padding-left: var(--padding-4);
|
||||
margin: 0;
|
||||
|
||||
li {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--l1-foreground);
|
||||
line-height: var(--line-height-20);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { useCallback } from 'react';
|
||||
import LearnMore from 'components/LearnMore/LearnMore';
|
||||
import { MCP_USE_CASES_URL } from '../clients';
|
||||
|
||||
import './UseCasesCard.styles.scss';
|
||||
|
||||
interface UseCasesCardProps {
|
||||
onDocsLinkClick: (target: string) => void;
|
||||
}
|
||||
|
||||
function UseCasesCard({ onDocsLinkClick }: UseCasesCardProps): JSX.Element {
|
||||
const handleClick = useCallback(
|
||||
() => onDocsLinkClick('use-cases'),
|
||||
[onDocsLinkClick],
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="mcp-use-cases-card">
|
||||
<h3 className="mcp-use-cases-card__title">What you can do with it</h3>
|
||||
<ul className="mcp-use-cases-card__list">
|
||||
<li>Ask your AI assistant to investigate a spiking error rate.</li>
|
||||
<li>Debug a slow service by walking through recent traces.</li>
|
||||
<li>Summarize an alert and suggest likely root causes.</li>
|
||||
<li>Generate dashboards or queries from a natural-language description.</li>
|
||||
</ul>
|
||||
<LearnMore
|
||||
text="See more use cases"
|
||||
url={MCP_USE_CASES_URL}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default UseCasesCard;
|
||||
108
frontend/src/container/MCPServerSettings/clients.ts
Normal file
108
frontend/src/container/MCPServerSettings/clients.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { DOCS_BASE_URL } from 'constants/app';
|
||||
|
||||
export interface McpClient {
|
||||
key: string;
|
||||
label: string;
|
||||
docsPath: string;
|
||||
snippet: ((endpoint: string) => string) | null;
|
||||
instructions?: string;
|
||||
installUrl?: (endpoint: string) => string;
|
||||
installLabel?: string;
|
||||
}
|
||||
|
||||
function b64url(input: string): string {
|
||||
if (typeof btoa === 'function') {
|
||||
return btoa(input);
|
||||
}
|
||||
// fallback for non-browser TS contexts (never hit at runtime)
|
||||
return Buffer.from(input, 'utf8').toString('base64');
|
||||
}
|
||||
|
||||
export const MCP_CLIENTS: McpClient[] = [
|
||||
{
|
||||
key: 'cursor',
|
||||
label: 'Cursor',
|
||||
docsPath: '/docs/ai/signoz-mcp-server/#cursor',
|
||||
snippet: (endpoint): string =>
|
||||
JSON.stringify(
|
||||
{
|
||||
mcpServers: {
|
||||
signoz: {
|
||||
url: endpoint,
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
installUrl: (endpoint): string => {
|
||||
const config = b64url(JSON.stringify({ url: endpoint }));
|
||||
return `cursor://anysphere.cursor-deeplink/mcp/install?name=SigNoz&config=${config}`;
|
||||
},
|
||||
installLabel: 'Add to Cursor',
|
||||
},
|
||||
{
|
||||
key: 'claude-code',
|
||||
label: 'Claude Code',
|
||||
docsPath: '/docs/ai/signoz-mcp-server/#claude-code',
|
||||
snippet: (endpoint): string =>
|
||||
`claude mcp add --scope user --transport http signoz ${endpoint}`,
|
||||
},
|
||||
{
|
||||
key: 'vscode',
|
||||
label: 'VS Code',
|
||||
docsPath: '/docs/ai/signoz-mcp-server/#vs-code',
|
||||
snippet: (endpoint): string =>
|
||||
JSON.stringify(
|
||||
{
|
||||
servers: {
|
||||
signoz: {
|
||||
type: 'http',
|
||||
url: endpoint,
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
installUrl: (endpoint): string => {
|
||||
const payload = encodeURIComponent(
|
||||
JSON.stringify({
|
||||
name: 'signoz',
|
||||
config: { type: 'http', url: endpoint },
|
||||
}),
|
||||
);
|
||||
return `vscode:mcp/install?${payload}`;
|
||||
},
|
||||
installLabel: 'Add to VS Code',
|
||||
},
|
||||
{
|
||||
key: 'claude-desktop',
|
||||
label: 'Claude Desktop',
|
||||
docsPath: '/docs/ai/signoz-mcp-server/#claude-desktop',
|
||||
snippet: null,
|
||||
instructions:
|
||||
'Open Claude Desktop, go to Settings → Connectors → Add custom connector, and paste the endpoint URL above. Claude Desktop does not read remote MCP servers from claude_desktop_config.json - the connector UI is the only supported path.',
|
||||
},
|
||||
{
|
||||
key: 'codex',
|
||||
label: 'Codex',
|
||||
docsPath: '/docs/ai/signoz-mcp-server/#codex',
|
||||
snippet: (endpoint): string => `codex mcp add signoz --url ${endpoint}`,
|
||||
},
|
||||
{
|
||||
key: 'other',
|
||||
label: 'Other',
|
||||
docsPath: '/docs/ai/signoz-mcp-server/',
|
||||
snippet: null,
|
||||
instructions:
|
||||
'Most MCP clients that support remote HTTP servers will accept the endpoint URL above. Add it as a new MCP server in your client and paste your SigNoz API key when the client prompts for authentication. See the docs for client-specific instructions.',
|
||||
},
|
||||
];
|
||||
|
||||
export function docsUrl(path: string): string {
|
||||
return `${DOCS_BASE_URL}${path}`;
|
||||
}
|
||||
|
||||
export const MCP_DOCS_URL = `${DOCS_BASE_URL}/docs/ai/signoz-mcp-server/`;
|
||||
export const MCP_USE_CASES_URL = `${DOCS_BASE_URL}/docs/ai/use-cases/`;
|
||||
@@ -15,7 +15,7 @@ import logEvent from 'api/common/logEvent';
|
||||
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
|
||||
import { DOCS_BASE_URL } from 'constants/app';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { useGetGlobalConfig } from 'hooks/globalConfig/useGetGlobalConfig';
|
||||
import { useGetGlobalConfig } from 'api/generated/services/global';
|
||||
import useDebouncedFn from 'hooks/useDebouncedFunction';
|
||||
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
|
||||
@@ -5,7 +5,8 @@ import logEvent from 'api/common/logEvent';
|
||||
import { useGetIngestionKeys } from 'api/generated/services/gateway';
|
||||
import { GatewaytypesIngestionKeyDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import { DOCS_BASE_URL } from 'constants/app';
|
||||
import { useGetGlobalConfig } from 'hooks/globalConfig/useGetGlobalConfig';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import { useGetGlobalConfig } from 'api/generated/services/global';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { ArrowUpRight, Copy, Info, Key, TriangleAlert } from 'lucide-react';
|
||||
import { withBasePath } from 'utils/basePath';
|
||||
@@ -59,6 +60,8 @@ export default function OnboardingIngestionDetails(): JSX.Element {
|
||||
error: globalConfigError,
|
||||
} = useGetGlobalConfig();
|
||||
|
||||
const globalConfigApiError = convertToApiError(globalConfigError);
|
||||
|
||||
const handleCopyKey = (text: string): void => {
|
||||
handleCopyToClipboard(text);
|
||||
notifications.success({
|
||||
@@ -155,11 +158,11 @@ export default function OnboardingIngestionDetails(): JSX.Element {
|
||||
title={
|
||||
<div className="ingestion-url-error-content">
|
||||
<Typography.Text className="ingestion-url-error-code">
|
||||
{globalConfigError?.getErrorCode()}
|
||||
{globalConfigApiError?.getErrorCode()}
|
||||
</Typography.Text>
|
||||
|
||||
<Typography.Text className="ingestion-url-error-message">
|
||||
{globalConfigError?.getErrorMessage()}
|
||||
{globalConfigApiError?.getErrorMessage()}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -192,7 +192,8 @@ const onboardingConfigWithLinks = [
|
||||
'setup',
|
||||
],
|
||||
imgUrl: signozBrandLogoUrl,
|
||||
link: '/docs/ai/signoz-mcp-server/',
|
||||
link: '/settings/mcp-server',
|
||||
internalRedirect: true,
|
||||
},
|
||||
{
|
||||
dataSource: 'migrate-from-datadog',
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
Settings,
|
||||
Shield,
|
||||
Slack,
|
||||
Sparkles,
|
||||
Unplug,
|
||||
User,
|
||||
UserPlus,
|
||||
@@ -337,6 +338,13 @@ export const settingsNavSections: SettingsNavSection[] = [
|
||||
isEnabled: false,
|
||||
itemKey: 'integrations',
|
||||
},
|
||||
{
|
||||
key: ROUTES.MCP_SERVER,
|
||||
label: 'MCP Server',
|
||||
icon: <Sparkles size={16} />,
|
||||
isEnabled: false,
|
||||
itemKey: 'mcp-server',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
@@ -157,6 +157,7 @@ export const routesToSkip = [
|
||||
ROUTES.ORG_SETTINGS,
|
||||
ROUTES.MEMBERS_SETTINGS,
|
||||
ROUTES.SERVICE_ACCOUNTS_SETTINGS,
|
||||
ROUTES.MCP_SERVER,
|
||||
ROUTES.INGESTION_SETTINGS,
|
||||
ROUTES.ERROR_DETAIL,
|
||||
ROUTES.LOGS_PIPELINES,
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { useQuery, UseQueryResult } from 'react-query';
|
||||
import getGlobalConfig from 'api/globalConfig/getGlobalConfig';
|
||||
import { SuccessResponseV2 } from 'types/api';
|
||||
import APIError from 'types/api/error';
|
||||
import { GlobalConfigData } from 'types/api/globalConfig/types';
|
||||
|
||||
export const useGetGlobalConfig = (): UseQueryResult<
|
||||
SuccessResponseV2<GlobalConfigData>,
|
||||
APIError
|
||||
> =>
|
||||
useQuery<SuccessResponseV2<GlobalConfigData>, APIError>({
|
||||
queryKey: ['getGlobalConfig'],
|
||||
queryFn: () => getGlobalConfig(),
|
||||
});
|
||||
@@ -83,7 +83,8 @@ function SettingsPage(): JSX.Element {
|
||||
item.key === ROUTES.ORG_SETTINGS ||
|
||||
item.key === ROUTES.MEMBERS_SETTINGS ||
|
||||
item.key === ROUTES.SERVICE_ACCOUNTS_SETTINGS ||
|
||||
item.key === ROUTES.SHORTCUTS
|
||||
item.key === ROUTES.SHORTCUTS ||
|
||||
item.key === ROUTES.MCP_SERVER
|
||||
? true
|
||||
: item.isEnabled,
|
||||
}));
|
||||
@@ -95,7 +96,8 @@ function SettingsPage(): JSX.Element {
|
||||
isEnabled:
|
||||
item.key === ROUTES.INGESTION_SETTINGS ||
|
||||
item.key === ROUTES.INTEGRATIONS ||
|
||||
item.key === ROUTES.SHORTCUTS
|
||||
item.key === ROUTES.SHORTCUTS ||
|
||||
item.key === ROUTES.MCP_SERVER
|
||||
? true
|
||||
: item.isEnabled,
|
||||
}));
|
||||
@@ -114,7 +116,8 @@ function SettingsPage(): JSX.Element {
|
||||
item.key === ROUTES.ORG_SETTINGS ||
|
||||
item.key === ROUTES.MEMBERS_SETTINGS ||
|
||||
item.key === ROUTES.SERVICE_ACCOUNTS_SETTINGS ||
|
||||
item.key === ROUTES.INGESTION_SETTINGS
|
||||
item.key === ROUTES.INGESTION_SETTINGS ||
|
||||
item.key === ROUTES.MCP_SERVER
|
||||
? true
|
||||
: item.isEnabled,
|
||||
}));
|
||||
@@ -125,7 +128,8 @@ function SettingsPage(): JSX.Element {
|
||||
...item,
|
||||
isEnabled:
|
||||
item.key === ROUTES.INTEGRATIONS ||
|
||||
item.key === ROUTES.INGESTION_SETTINGS
|
||||
item.key === ROUTES.INGESTION_SETTINGS ||
|
||||
item.key === ROUTES.MCP_SERVER
|
||||
? true
|
||||
: item.isEnabled,
|
||||
}));
|
||||
|
||||
@@ -56,6 +56,7 @@ describe('SettingsPage nav sections', () => {
|
||||
'sso',
|
||||
'integrations',
|
||||
'ingestion',
|
||||
'mcp-server',
|
||||
])('renders "%s" element', (id) => {
|
||||
expect(screen.getByTestId(id)).toBeInTheDocument();
|
||||
});
|
||||
@@ -81,9 +82,12 @@ describe('SettingsPage nav sections', () => {
|
||||
expect(screen.getByTestId(id)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.each(['billing', 'roles'])('does not render "%s" element', (id) => {
|
||||
expect(screen.queryByTestId(id)).not.toBeInTheDocument();
|
||||
});
|
||||
it.each(['billing', 'roles', 'mcp-server'])(
|
||||
'does not render "%s" element',
|
||||
(id) => {
|
||||
expect(screen.queryByTestId(id)).not.toBeInTheDocument();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('Self-hosted Admin', () => {
|
||||
@@ -95,12 +99,16 @@ describe('SettingsPage nav sections', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.each(['roles', 'members', 'integrations', 'sso', 'ingestion'])(
|
||||
'renders "%s" element',
|
||||
(id) => {
|
||||
expect(screen.getByTestId(id)).toBeInTheDocument();
|
||||
},
|
||||
);
|
||||
it.each([
|
||||
'roles',
|
||||
'members',
|
||||
'integrations',
|
||||
'sso',
|
||||
'ingestion',
|
||||
'mcp-server',
|
||||
])('renders "%s" element', (id) => {
|
||||
expect(screen.getByTestId(id)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('section structure', () => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import GeneralSettings from 'container/GeneralSettings';
|
||||
import GeneralSettingsCloud from 'container/GeneralSettingsCloud';
|
||||
import IngestionSettings from 'container/IngestionSettings/IngestionSettings';
|
||||
import MultiIngestionSettings from 'container/IngestionSettings/MultiIngestionSettings';
|
||||
import MCPServerSettings from 'container/MCPServerSettings/MCPServerSettings';
|
||||
import MySettings from 'container/MySettings';
|
||||
import OrganizationSettings from 'container/OrganizationSettings';
|
||||
import RolesSettings from 'container/RolesSettings';
|
||||
@@ -24,6 +25,7 @@ import {
|
||||
Pencil,
|
||||
Plus,
|
||||
Shield,
|
||||
Sparkles,
|
||||
User,
|
||||
Users,
|
||||
} from 'lucide-react';
|
||||
@@ -205,6 +207,19 @@ export const serviceAccountsSettings = (
|
||||
},
|
||||
];
|
||||
|
||||
export const mcpServerSettings = (t: TFunction): RouteTabProps['routes'] => [
|
||||
{
|
||||
Component: MCPServerSettings,
|
||||
name: (
|
||||
<div className="periscope-tab">
|
||||
<Sparkles size={16} /> {t('routes:mcp_server').toString()}
|
||||
</div>
|
||||
),
|
||||
route: ROUTES.MCP_SERVER,
|
||||
key: ROUTES.MCP_SERVER,
|
||||
},
|
||||
];
|
||||
|
||||
export const createAlertChannels = (t: TFunction): RouteTabProps['routes'] => [
|
||||
{
|
||||
Component: (): JSX.Element => (
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
generalSettings,
|
||||
ingestionSettings,
|
||||
keyboardShortcuts,
|
||||
mcpServerSettings,
|
||||
membersSettings,
|
||||
multiIngestionSettings,
|
||||
mySettings,
|
||||
@@ -79,6 +80,7 @@ export const getRoutes = (
|
||||
...createAlertChannels(t),
|
||||
...editAlertChannels(t),
|
||||
...keyboardShortcuts(t),
|
||||
...mcpServerSettings(t),
|
||||
);
|
||||
|
||||
return settings;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
export interface GlobalConfigData {
|
||||
external_url: string;
|
||||
ingestion_url: string;
|
||||
}
|
||||
|
||||
export interface GlobalConfigDataProps {
|
||||
status: string;
|
||||
data: GlobalConfigData;
|
||||
}
|
||||
@@ -132,4 +132,5 @@ export const routePermission: Record<keyof typeof ROUTES, ROLES[]> = {
|
||||
METER_EXPLORER_VIEWS: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
PUBLIC_DASHBOARD: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
ALERT_TYPE_SELECTION: ['ADMIN', 'EDITOR'],
|
||||
MCP_SERVER: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
};
|
||||
|
||||
@@ -4216,225 +4216,225 @@
|
||||
resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.101.0.tgz#5692200d09d6f87341eac3f8e70e403173c5283e"
|
||||
integrity sha512-nuFhqlUzJX+gVIPPfuE6xurd4lST3mdcWOhyK/rZO0B9XWMKm79SuszIQEnSMmmDhq1DC8WWVYGVd+6F93o1gQ==
|
||||
|
||||
"@oxfmt/binding-android-arm-eabi@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.41.0.tgz#09438448ab9479730582cd00fb86e4c33795a364"
|
||||
integrity sha512-REfrqeMKGkfMP+m/ScX4f5jJBSmVNYcpoDF8vP8f8eYPDuPGZmzp56NIUsYmx3h7f6NzC6cE3gqh8GDWrJHCKw==
|
||||
"@oxfmt/binding-android-arm-eabi@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.46.0.tgz#d2da704ab6c741e535de3445d0994290b77b3fc0"
|
||||
integrity sha512-b1doV4WRcJU+BESSlCvCjV+5CEr/T6h0frArAdV26Nir+gGNFNaylvDiiMPfF1pxeV0txZEs38ojzJaxBYg+ng==
|
||||
|
||||
"@oxfmt/binding-android-arm64@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.41.0.tgz#b01eef08ff2a6be90f5805fe28339ec0130a955c"
|
||||
integrity sha512-s0b1dxNgb2KomspFV2LfogC2XtSJB42POXF4bMCLJyvQmAGos4ZtjGPfQreToQEaY0FQFjz3030ggI36rF1q5g==
|
||||
"@oxfmt/binding-android-arm64@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.46.0.tgz#203341eaf489c325a01e247c6209f13f39f0777f"
|
||||
integrity sha512-v6+HhjsoV3GO0u2u9jLSAZrvWfTraDxKofUIQ7/ktS7tzS+epVsxdHmeM+XxuNcAY/nWxxU1Sg4JcGTNRXraBA==
|
||||
|
||||
"@oxfmt/binding-darwin-arm64@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.41.0.tgz#a2e24b7c52d40e3bb01939fa9c994d56923294ce"
|
||||
integrity sha512-EGXGualADbv/ZmamE7/2DbsrYmjoPlAmHEpTL4vapLF4EfVD6fr8/uQDFnPJkUBjiSWFJZtFNsGeN1B6V3owmA==
|
||||
"@oxfmt/binding-darwin-arm64@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.46.0.tgz#ce653c87e00305c84edb380ecac3db789c3ea9a4"
|
||||
integrity sha512-3eeooJGrqGIlI5MyryDZsAcKXSmKIgAD4yYtfRrRJzXZ0UTFZtiSveIur56YPrGMYZwT4XyVhHsMqrNwr1XeFA==
|
||||
|
||||
"@oxfmt/binding-darwin-x64@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.41.0.tgz#c114c0a30195a65cfe0247e2ffd000416df4d4bc"
|
||||
integrity sha512-WxySJEvdQQYMmyvISH3qDpTvoS0ebnIP63IMxLLWowJyPp/AAH0hdWtlo+iGNK5y3eVfa5jZguwNaQkDKWpGSw==
|
||||
"@oxfmt/binding-darwin-x64@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.46.0.tgz#71016678036fab249172c763d2f59a3535f0fe91"
|
||||
integrity sha512-QG8BDM0CXWbu84k2SKmCqfEddPQPFiBicwtYnLqHRWZZl57HbtOLRMac/KTq2NO4AEc4ICCBpFxJIV9zcqYfkQ==
|
||||
|
||||
"@oxfmt/binding-freebsd-x64@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.41.0.tgz#a9057a31860ec79970a16b5a0a801a51fab99168"
|
||||
integrity sha512-Y2kzMkv3U3oyuYaR4wTfGjOTYTXiFC/hXmG0yVASKkbh02BJkvD98Ij8bIevr45hNZ0DmZEgqiXF+9buD4yMYQ==
|
||||
"@oxfmt/binding-freebsd-x64@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.46.0.tgz#fd939be2e39c29fead415f41c70eb52f94d53d6a"
|
||||
integrity sha512-9DdCqS/n2ncu/Chazvt3cpgAjAmIGQDz7hFKSrNItMApyV/Ja9mz3hD4JakIE3nS8PW9smEbPWnb389QLBY4nw==
|
||||
|
||||
"@oxfmt/binding-linux-arm-gnueabihf@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.41.0.tgz#1a2be3cbbf2d9e90808858ba8fc38b24023ac44c"
|
||||
integrity sha512-ptazDjdUyhket01IjPTT6ULS1KFuBfTUU97osTP96X5y/0oso+AgAaJzuH81oP0+XXyrWIHbRzozSAuQm4p48g==
|
||||
"@oxfmt/binding-linux-arm-gnueabihf@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.46.0.tgz#09f4f3894e140340256a02665832bdcf66d8b5c3"
|
||||
integrity sha512-Dgs7VeE2jT0LHMhw6tPEt0xQYe54kBqHEovmWsv4FVQlegCOvlIJNx0S8n4vj8WUtpT+Z6BD2HhKJPLglLxvZg==
|
||||
|
||||
"@oxfmt/binding-linux-arm-musleabihf@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.41.0.tgz#f0a12feca29bfaf7437d94c32919ff3ac60c213f"
|
||||
integrity sha512-UkoL2OKxFD+56bPEBcdGn+4juTW4HRv/T6w1dIDLnvKKWr6DbarB/mtHXlADKlFiJubJz8pRkttOR7qjYR6lTA==
|
||||
"@oxfmt/binding-linux-arm-musleabihf@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.46.0.tgz#35ca79c2c643dd5dd7eb1e1c0cbc5205eecba8ee"
|
||||
integrity sha512-Zxn3adhTH13JKnU4xXJj8FeEfF680XjXh3gSShKl57HCMBRde2tUJTgogV/1MSHA80PJEVrDa7r66TLVq3Ia7Q==
|
||||
|
||||
"@oxfmt/binding-linux-arm64-gnu@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.41.0.tgz#b304c7ede72119f7fe7ce7861007c0ee6eb60c08"
|
||||
integrity sha512-gofu0PuumSOHYczD8p62CPY4UF6ee+rSLZJdUXkpwxg6pILiwSDBIouPskjF/5nF3A7QZTz2O9KFNkNxxFN9tA==
|
||||
"@oxfmt/binding-linux-arm64-gnu@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.46.0.tgz#5f30f09b2d109ec18f1d9de64414718f4c9975be"
|
||||
integrity sha512-+TWipjrgVM8D7aIdDD0tlr3teLTTvQTn7QTE5BpT10H1Fj82gfdn9X6nn2sDgx/MepuSCfSnzFNJq2paLL0OiA==
|
||||
|
||||
"@oxfmt/binding-linux-arm64-musl@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.41.0.tgz#295d6ce8d846ca1a287165bf4ff6e7b3cd51f823"
|
||||
integrity sha512-VfVZxL0+6RU86T8F8vKiDBa+iHsr8PAjQmKGBzSCAX70b6x+UOMFl+2dNihmKmUwqkCazCPfYjt6SuAPOeQJ3g==
|
||||
"@oxfmt/binding-linux-arm64-musl@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.46.0.tgz#a550718469f40dcb8a382eb5a89b031d7f47ee16"
|
||||
integrity sha512-aAUPBWJ1lGwwnxZUEDLJ94+Iy6MuwJwPxUgO4sCA5mEEyDk7b+cDQ+JpX1VR150Zoyd+D49gsrUzpUK5h587Eg==
|
||||
|
||||
"@oxfmt/binding-linux-ppc64-gnu@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.41.0.tgz#6c702f37bd69bb88bf78264eef5df9dbcdcada49"
|
||||
integrity sha512-bwzokz2eGvdfJbc0i+zXMJ4BBjQPqg13jyWpEEZDOrBCQ91r8KeY2Mi2kUeuMTZNFXju+jcAbAbpyJxRGla0eg==
|
||||
"@oxfmt/binding-linux-ppc64-gnu@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.46.0.tgz#51d25e82d6c965aa09660ef31a19e6d8a79253a9"
|
||||
integrity sha512-ufBCJukyFX/UDrokP/r6BGDoTInnsDs7bxyzKAgMiZlt2Qu8GPJSJ6Zm6whIiJzKk0naxA8ilwmbO1LMw6Htxw==
|
||||
|
||||
"@oxfmt/binding-linux-riscv64-gnu@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.41.0.tgz#9dac4f7f4cf10d94ade5a109554f810a43525595"
|
||||
integrity sha512-POLM//PCH9uqDeNDwWL3b3DkMmI3oI2cU6hwc2lnztD1o7dzrQs3R9nq555BZ6wI7t2lyhT9CS+CRaz5X0XqLA==
|
||||
"@oxfmt/binding-linux-riscv64-gnu@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.46.0.tgz#02565fd79a18de150f9a76a00f76fdd3a5e7f0fa"
|
||||
integrity sha512-eqtlC2YmPqjun76R1gVfGLuKWx7NuEnLEAudZ7n6ipSKbCZTqIKSs1b5Y8K/JHZsRpLkeSmAAjig5HOIg8fQzQ==
|
||||
|
||||
"@oxfmt/binding-linux-riscv64-musl@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.41.0.tgz#ddd573ad4fc8e5d017ab27177eaabea700d68754"
|
||||
integrity sha512-NNK7PzhFqLUwx/G12Xtm6scGv7UITvyGdAR5Y+TlqsG+essnuRWR4jRNODWRjzLZod0T3SayRbnkSIWMBov33w==
|
||||
"@oxfmt/binding-linux-riscv64-musl@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.46.0.tgz#d148eb5b52a0c96ce90361ed733443df2f03e5c5"
|
||||
integrity sha512-yccVOO2nMXkQLGgy0He3EQEwKD7NF0zEk+/OWmroznkqXyJdN6bfK0LtNnr6/14Bh3FjpYq7bP33l/VloCnxpA==
|
||||
|
||||
"@oxfmt/binding-linux-s390x-gnu@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.41.0.tgz#3300ee81681382c1ec0a4013807c362c27ef4821"
|
||||
integrity sha512-qVf/zDC5cN9eKe4qI/O/m445er1IRl6swsSl7jHkqmOSVfknwCe5JXitYjZca+V/cNJSU/xPlC5EFMabMMFDpw==
|
||||
"@oxfmt/binding-linux-s390x-gnu@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.46.0.tgz#22cbee991e79147e7a0d5db2cc9aa74bf5bd6d89"
|
||||
integrity sha512-aAf7fG23OQCey6VRPj9IeCraoYtpgtx0ZyJ1CXkPyT1wjzBE7c3xtuxHe/AdHaJfVVb/SXpSk8Gl1LzyQupSqw==
|
||||
|
||||
"@oxfmt/binding-linux-x64-gnu@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.41.0.tgz#be9068d48bb521eea83ea6bea2b0e08b39d7638b"
|
||||
integrity sha512-ojxYWu7vUb6ysYqVCPHuAPVZHAI40gfZ0PDtZAMwVmh2f0V8ExpPIKoAKr7/8sNbAXJBBpZhs2coypIo2jJX4w==
|
||||
"@oxfmt/binding-linux-x64-gnu@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.46.0.tgz#35fdda5137b108ec5cc0a8656a04fabdd0edd652"
|
||||
integrity sha512-q0JPsTMyJNjYrBvYFDz4WbVsafNZaPCZv4RnFypRotLqpKROtBZcEaXQW4eb9YmvLU3NckVemLJnzkSZSdmOxw==
|
||||
|
||||
"@oxfmt/binding-linux-x64-musl@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.41.0.tgz#e0232abd879874823213b71016042d001a740acb"
|
||||
integrity sha512-O2exZLBxoCMIv2vlvcbkdedazJPTdG0VSup+0QUCfYQtx751zCZNboX2ZUOiQ/gDTdhtXvSiot0h6GEGkOyalA==
|
||||
"@oxfmt/binding-linux-x64-musl@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.46.0.tgz#dc399b372b27b26534d516a5a68927843f400603"
|
||||
integrity sha512-7LsLY9Cw57GPkhSR+duI3mt9baRczK/DtHYSldQ4BEU92da9igBQNl4z7Vq5U9NNPsh1FmpKvv1q9WDtiUQR1A==
|
||||
|
||||
"@oxfmt/binding-openharmony-arm64@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.41.0.tgz#3e5893cde8cae02494cbb5493de9c46613e77058"
|
||||
integrity sha512-N+31/VoL+z+NNBt8viy3I4NaIdPbiYeOnB884LKqvXldaE2dRztdPv3q5ipfZYv0RwFp7JfqS4I27K/DSHCakg==
|
||||
"@oxfmt/binding-openharmony-arm64@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.46.0.tgz#b7c89ecd6e1827dca86779f4d08dc200bad5915c"
|
||||
integrity sha512-lHiBOz8Duaku7JtRNLlps3j++eOaICPZSd8FCVmTDM4DFOPT71Bjn7g6iar1z7StXlKRweUKxWUs4sA+zWGDXg==
|
||||
|
||||
"@oxfmt/binding-win32-arm64-msvc@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.41.0.tgz#31d8dfeeddc855dbecc9f45d11cd892635d2e42f"
|
||||
integrity sha512-Z7NAtu/RN8kjCQ1y5oDD0nTAeRswh3GJ93qwcW51srmidP7XPBmZbLlwERu1W5veCevQJtPS9xmkpcDTYsGIwQ==
|
||||
"@oxfmt/binding-win32-arm64-msvc@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.46.0.tgz#ec0ea70875374bf0a1c949bac6a2177e23425bb8"
|
||||
integrity sha512-/5ktYUliP89RhgC37DBH1x20U5zPSZMy3cMEcO0j3793rbHP9MWsknBwQB6eozRzWmYrh0IFM/p20EbPvDlYlg==
|
||||
|
||||
"@oxfmt/binding-win32-ia32-msvc@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.41.0.tgz#873fb408a3e8952fae67763b225e4ea77356e926"
|
||||
integrity sha512-uNxxP3l4bJ6VyzIeRqCmBU2Q0SkCFgIhvx9/9dJ9V8t/v+jP1IBsuaLwCXGR8JPHtkj4tFp+RHtUmU2ZYAUpMA==
|
||||
"@oxfmt/binding-win32-ia32-msvc@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.46.0.tgz#fa7b6bf31602c814ffdd8e3bfad355569b2f08da"
|
||||
integrity sha512-3WTnoiuIr8XvV0DIY7SN+1uJSwKf4sPpcbHfobcRT9JutGcLaef/miyBB87jxd3aqH+mS0+G5lsgHuXLUwjjpQ==
|
||||
|
||||
"@oxfmt/binding-win32-x64-msvc@0.41.0":
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.41.0.tgz#0adf46bca6908c6b49c790e598f4972b6f4cd191"
|
||||
integrity sha512-49ZSpbZ1noozyPapE8SUOSm3IN0Ze4b5nkO+4+7fq6oEYQQJFhE0saj5k/Gg4oewVPdjn0L3ZFeWk2Vehjcw7A==
|
||||
"@oxfmt/binding-win32-x64-msvc@0.46.0":
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.46.0.tgz#0ddd94829568c94101abe88896e48f724891e4f2"
|
||||
integrity sha512-IXxiQpkYnOwNfP23vzwSfhdpxJzyiPTY7eTn6dn3DsriKddESzM8i6kfq9R7CD/PUJwCvQT22NgtygBeug3KoA==
|
||||
|
||||
"@oxlint-tsgolint/darwin-arm64@0.20.0":
|
||||
version "0.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint-tsgolint/darwin-arm64/-/darwin-arm64-0.20.0.tgz#6d704883904fa6e5fc922a7cef882bad4c734a0b"
|
||||
integrity sha512-KKQcIHZHMxqpHUA1VXIbOG6chNCFkUWbQy6M+AFVtPKkA/3xAeJkJ3njoV66bfzwPHRcWQO+kcj5XqtbkjakoA==
|
||||
"@oxlint-tsgolint/darwin-arm64@0.21.1":
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint-tsgolint/darwin-arm64/-/darwin-arm64-0.21.1.tgz#b4390b047d246608b130e2bde238306659c2b292"
|
||||
integrity sha512-7TLjyWe4wG9saJc992VWmaHq2hwKfOEEVTjheReXJXaDhavMZI4X9a6nKhbEng4IVkYtzjD2jw16vw2WFXLYLw==
|
||||
|
||||
"@oxlint-tsgolint/darwin-x64@0.20.0":
|
||||
version "0.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint-tsgolint/darwin-x64/-/darwin-x64-0.20.0.tgz#676d7a1cf82216d91e9bc143f676e7dbe59ce596"
|
||||
integrity sha512-7HeVMuclGfG+NLZi2ybY0T4fMI7/XxO/208rJk+zEIloKkVnlh11Wd241JMGwgNFXn+MLJbOqOfojDb2Dt4L1g==
|
||||
"@oxlint-tsgolint/darwin-x64@0.21.1":
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint-tsgolint/darwin-x64/-/darwin-x64-0.21.1.tgz#18f607e47c04459962d2ddf1461491f5690c3f8f"
|
||||
integrity sha512-7wf9Wf75nTzA7zpL9myhFe2RKvfuqGUOADNvUooCjEWvh7hmPz3lSEqTMh5Z/VQhzsG04mM9ACyghxhRzq7zFw==
|
||||
|
||||
"@oxlint-tsgolint/linux-arm64@0.20.0":
|
||||
version "0.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint-tsgolint/linux-arm64/-/linux-arm64-0.20.0.tgz#5a2e67cf18394b6b868301cf3d84c8b8be7ab6b9"
|
||||
integrity sha512-zxhUwz+WSxE6oWlZLK2z2ps9yC6ebmgoYmjAl0Oa48+GqkZ56NVgo+wb8DURNv6xrggzHStQxqQxe3mK51HZag==
|
||||
"@oxlint-tsgolint/linux-arm64@0.21.1":
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint-tsgolint/linux-arm64/-/linux-arm64-0.21.1.tgz#da5932c940362faa261c67085c9eb215a8f48bab"
|
||||
integrity sha512-IPuQN/Vd0Rjklg/cCGBbQyUuRBp2f6LQXpZYwk5ivOR6V/+CgiYsv8pn/PVY7gjeyoNvPQrXB7xMjHUO2YZbdw==
|
||||
|
||||
"@oxlint-tsgolint/linux-x64@0.20.0":
|
||||
version "0.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint-tsgolint/linux-x64/-/linux-x64-0.20.0.tgz#68468ad9253668748736191b6a7af51ae737c0a4"
|
||||
integrity sha512-/1l6FnahC9im8PK+Ekkx/V3yetO/PzZnJegE2FXcv/iXEhbeVxP/ouiTYcUQu9shT1FWJCSNti1VJHH+21Y1dg==
|
||||
"@oxlint-tsgolint/linux-x64@0.21.1":
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint-tsgolint/linux-x64/-/linux-x64-0.21.1.tgz#26c916b25c852bb63c9bd95f61bd0228400ddef0"
|
||||
integrity sha512-d1niGuTbh2qiv7dR7tqkbOcM5cIR63of0lMBFdEQavL1KrJV8zuRdwdi68K7MNGdgoR+J5A9ajpGGvsHwp1bPg==
|
||||
|
||||
"@oxlint-tsgolint/win32-arm64@0.20.0":
|
||||
version "0.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint-tsgolint/win32-arm64/-/win32-arm64-0.20.0.tgz#32a0eba60f0a88cd6d6d53cc03eb1b2e43ca0c50"
|
||||
integrity sha512-oPZ5Yz8sVdo7P/5q+i3IKeix31eFZ55JAPa1+RGPoe9PoaYVsdMvR6Jvib6YtrqoJnFPlg3fjEjlEPL8VBKYJA==
|
||||
"@oxlint-tsgolint/win32-arm64@0.21.1":
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint-tsgolint/win32-arm64/-/win32-arm64-0.21.1.tgz#c6e78be4394f29abd9106137c5f0a9e7ee4e0d97"
|
||||
integrity sha512-ICu9y2JLnFPvFqstnWPPNqBM8LK8BWw2OTeaR0UgEMm4hOSbrZAKv1/hwZYyiLqnCNjBL87AGSQIgTHCYlsipw==
|
||||
|
||||
"@oxlint-tsgolint/win32-x64@0.20.0":
|
||||
version "0.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint-tsgolint/win32-x64/-/win32-x64-0.20.0.tgz#90d5582b1df2ae877b2c5d7620e3a55acf9cc45f"
|
||||
integrity sha512-4stx8RHj3SP9vQyRF/yZbz5igtPvYMEUR8CUoha4BVNZihi39DpCR8qkU7lpjB5Ga1DRMo2pHaA4bdTOMaY4mw==
|
||||
"@oxlint-tsgolint/win32-x64@0.21.1":
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint-tsgolint/win32-x64/-/win32-x64-0.21.1.tgz#e0ff1eebe8837effe74af3f76c9ae6afc8992e58"
|
||||
integrity sha512-cTEFCFjCj6iXfrSHcvajSPNqhEA4TxSzU3gFxbdGSAUTNXGToU99IbdhWAPSbhcucoym0XE4Zl7E41NiSkNTug==
|
||||
|
||||
"@oxlint/binding-android-arm-eabi@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.59.0.tgz#d16603910761e22b7346b10e1293fd22c9051020"
|
||||
integrity sha512-etYDw/UaEv936AQUd/CRMBVd+e+XuuU6wC+VzOv1STvsTyZenLChepLWqLtnyTTp4YMlM22ypzogDDwqYxv5cg==
|
||||
"@oxlint/binding-android-arm-eabi@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.61.0.tgz#c7a8fb22ac3084d6f4c873cdc9779f4f9e38b805"
|
||||
integrity sha512-6eZBPgiigK5txqoVgRqxbaxiom4lM8AP8CyKPPvpzKnQ3iFRFOIDc+0AapF+qsUSwjOzr5SGk4SxQDpQhkSJMQ==
|
||||
|
||||
"@oxlint/binding-android-arm64@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-android-arm64/-/binding-android-arm64-1.59.0.tgz#a54b26068fcaf6ab7f6cae9d84f0c3d7e29d3c9c"
|
||||
integrity sha512-TgLc7XVLKH2a4h8j3vn1MDjfK33i9MY60f/bKhRGWyVzbk5LCZ4X01VZG7iHrMmi5vYbAp8//Ponigx03CLsdw==
|
||||
"@oxlint/binding-android-arm64@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-android-arm64/-/binding-android-arm64-1.61.0.tgz#5fe7c707e12513ede292da015d13e93781771e9f"
|
||||
integrity sha512-CkwLR69MUnyv5wjzebvbbtTSUwqLxM35CXE79bHqDIK+NtKmPEUpStTcLQRZMCo4MP0qRT6TXIQVpK0ZVScnMA==
|
||||
|
||||
"@oxlint/binding-darwin-arm64@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.59.0.tgz#097fecd40271af5882fadcdb6ea904973c642e97"
|
||||
integrity sha512-DXyFPf5ZKldMLloRHx/B9fsxsiTQomaw7cmEW3YIJko2HgCh+GUhp9gGYwHrqlLJPsEe3dYj9JebjX92D3j3AA==
|
||||
"@oxlint/binding-darwin-arm64@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.61.0.tgz#1f58a4c592b76e293e5b9072cc0e4e7a5bef8191"
|
||||
integrity sha512-8JbefTkbmvqkqWjmQrHke+MdpgT2UghhD/ktM4FOQSpGeCgbMToJEKdl9zwhr/YWTl92i4QI1KiTwVExpcUN8A==
|
||||
|
||||
"@oxlint/binding-darwin-x64@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.59.0.tgz#b7b3a898406c9c05a44647b43a2e3f646dfcc2bc"
|
||||
integrity sha512-LgvrsdgVLX1qWqIEmNsSmMXJhpAWdtUQ0M+oR0CySwi+9IHWyOGuIL8w8+u/kbZNMyZr4WUyYB5i0+D+AKgkLg==
|
||||
"@oxlint/binding-darwin-x64@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.61.0.tgz#eade7425cc943c3146f3e0f0fb4d08d615ee78a9"
|
||||
integrity sha512-uWpoxDT47hTnDLcdEh5jVbso8rlTTu5o0zuqa9J8E0JAKmIWn7kGFEIB03Pycn2hd2vKxybPGLhjURy/9We5FQ==
|
||||
|
||||
"@oxlint/binding-freebsd-x64@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.59.0.tgz#e7a513e5d06d9f0df18d75bd2ed0466420e5078f"
|
||||
integrity sha512-bOJhqX/ny4hrFuTPlyk8foSRx/vLRpxJh0jOOKN2NWW6FScXHPAA5rQbrwdQPcgGB5V8Ua51RS03fke8ssBcug==
|
||||
"@oxlint/binding-freebsd-x64@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.61.0.tgz#8d82382a07ce4895c91694a810dee7f39866951b"
|
||||
integrity sha512-K/o4hEyW7flfMel0iBVznmMBt7VIMHGdjADocHKpK1DUF9erpWnJ+BSSWd2W0c8K3mPtpph+CuHzRU6CI3l9jQ==
|
||||
|
||||
"@oxlint/binding-linux-arm-gnueabihf@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.59.0.tgz#38d0d20b3fd6b25672a34328125e99c45fff39d1"
|
||||
integrity sha512-vVUXxYMF9trXCsz4m9H6U0IjehosVHxBzVgJUxly1uz4W1PdDyicaBnpC0KRXsHYretLVe+uS9pJy8iM57Kujw==
|
||||
"@oxlint/binding-linux-arm-gnueabihf@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.61.0.tgz#df76a5700651cda73ca5578de7308f9479b7cb1b"
|
||||
integrity sha512-P6040ZkcyweJ0Po9yEFqJCdvZnf3VNCGs1SIHgXDf8AAQNC6ID/heXQs9iSgo2FH7gKaKq32VWc59XZwL34C5Q==
|
||||
|
||||
"@oxlint/binding-linux-arm-musleabihf@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.59.0.tgz#76b57f78729d4854458886b68cedfb776586356b"
|
||||
integrity sha512-TULQW8YBPGRWg5yZpFPL54HLOnJ3/HiX6VenDPi6YfxB/jlItwSMFh3/hCeSNbh+DAMaE1Py0j5MOaivHkI/9Q==
|
||||
"@oxlint/binding-linux-arm-musleabihf@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.61.0.tgz#b198e5697cfb6cc55eff5ab5b9766732e143492d"
|
||||
integrity sha512-bwxrGCzTZkuB+THv2TQ1aTkVEfv5oz8sl+0XZZCpoYzErJD8OhPQOTA0ENPd1zJz8QsVdSzSrS2umKtPq4/JXg==
|
||||
|
||||
"@oxlint/binding-linux-arm64-gnu@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.59.0.tgz#64ab5de73c894265726c83cc7e431ba924436e2a"
|
||||
integrity sha512-Gt54Y4eqSgYJ90xipm24xeyaPV854706o/kiT8oZvUt3VDY7qqxdqyGqchMaujd87ib+/MXvnl9WkK8Cc1BExg==
|
||||
"@oxlint/binding-linux-arm64-gnu@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.61.0.tgz#a5cdae7ef9f9b79526ef764fb174bb362c3737ee"
|
||||
integrity sha512-vkhb9/wKguMkLlrm3FoJW/Xmdv31GgYAE+x8lxxQ+7HeOxXUySI0q36a3NTVIuQUdLzxCI1zzMGsk1o37FOe3w==
|
||||
|
||||
"@oxlint/binding-linux-arm64-musl@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.59.0.tgz#d6bc4914a8d80125e06b61891c125aa1acf326a7"
|
||||
integrity sha512-3CtsKp7NFB3OfqQzbuAecrY7GIZeiv7AD+xutU4tefVQzlfmTI7/ygWLrvkzsDEjTlMq41rYHxgsn6Yh8tybmA==
|
||||
"@oxlint/binding-linux-arm64-musl@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.61.0.tgz#227f6d94a3c00140a88ea338436783bd9da6f4ae"
|
||||
integrity sha512-bl1dQh8LnVqsj6oOQAcxwbuOmNJkwc4p6o//HTBZhNTzJy21TLDwAviMqUFNUxDHkPGpmdKTSN4tWTjLryP8xg==
|
||||
|
||||
"@oxlint/binding-linux-ppc64-gnu@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.59.0.tgz#c999194b32e6fc75709d451070bf56720af6d537"
|
||||
integrity sha512-K0diOpT3ncDmOfl9I1HuvpEsAuTxkts0VYwIv/w6Xiy9CdwyPBVX88Ga9l8VlGgMrwBMnSY4xIvVlVY/fkQk7Q==
|
||||
"@oxlint/binding-linux-ppc64-gnu@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.61.0.tgz#e504e6cd8691fe460b202599d4c962fc7ad98f94"
|
||||
integrity sha512-QoOX6KB2IiEpyOj/HKqaxi+NQHPnOgNgnr22n9N4ANJCzXkUlj1UmeAbFb4PpqdlHIzvGDM5xZ0OKtcLq9RhiQ==
|
||||
|
||||
"@oxlint/binding-linux-riscv64-gnu@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.59.0.tgz#d85938028f4c4666fe299bbc3c87cceae907fd2b"
|
||||
integrity sha512-xAU7+QDU6kTJJ7mJLOGgo7oOjtAtkKyFZ0Yjdb5cEo3DiCCPFLvyr08rWiQh6evZ7RiUTf+o65NY/bqttzJiQQ==
|
||||
"@oxlint/binding-linux-riscv64-gnu@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.61.0.tgz#efe70d9e570756fe19fb60d05a39dd58e9e0f32d"
|
||||
integrity sha512-1TGcTerjY6p152wCof3oKElccq3xHljS/Mucp04gV/4ATpP6nO7YNnp7opEg6SHkv2a57/b4b8Ndm9znJ1/qAw==
|
||||
|
||||
"@oxlint/binding-linux-riscv64-musl@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.59.0.tgz#9225a568a0138fe0f252785cfffe86d2b35d2e92"
|
||||
integrity sha512-KUmZmKlTTyauOnvUNVxK7G40sSSx0+w5l1UhaGsC6KPpOYHenx2oqJTnabmpLJicok7IC+3Y6fXAUOMyexaeJQ==
|
||||
"@oxlint/binding-linux-riscv64-musl@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.61.0.tgz#f9c42095192f235b03a5d3d1f1fbfb45002d5a93"
|
||||
integrity sha512-65wXEmZIrX2ADwC8i/qFL4EWLSbeuBpAm3suuX1vu4IQkKd+wLT/HU/BOl84kp91u2SxPkPDyQgu4yrqp8vwVA==
|
||||
|
||||
"@oxlint/binding-linux-s390x-gnu@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.59.0.tgz#75dc546c7c92f3a2c52885ffe5031810bea2f759"
|
||||
integrity sha512-4usRxC8gS0PGdkHnRmwJt/4zrQNZyk6vL0trCxwZSsAKM+OxhB8nKiR+mhjdBbl8lbMh2gc3bZpNN/ik8c4c2A==
|
||||
"@oxlint/binding-linux-s390x-gnu@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.61.0.tgz#23773cd6f8087f98c54383caccb98808fab6cca0"
|
||||
integrity sha512-TVvhgMvor7Qa6COeXxCJ7ENOM+lcAOGsQ0iUdPSCv2hxb9qSHLQ4XF1h50S6RE1gBOJ0WV3rNukg4JJJP1LWRA==
|
||||
|
||||
"@oxlint/binding-linux-x64-gnu@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.59.0.tgz#aee13be2b05d1ec9859b00693f7729c0438370f8"
|
||||
integrity sha512-s/rNE2gDmbwAOOP493xk2X7M8LZfI1LJFSSW1+yanz3vuQCFPiHkx4GY+O1HuLUDtkzGlhtMrIcxxzyYLv308w==
|
||||
"@oxlint/binding-linux-x64-gnu@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.61.0.tgz#f46b036cf74b382c03f6a00e18cd1ae53096517f"
|
||||
integrity sha512-SjpS5uYuFoDnDdZPwZE59ndF95AsY47R5MliuneTWR1pDm2CxGJaYXbKULI71t5TVfLQUWmrHEGRL9xvuq6dnA==
|
||||
|
||||
"@oxlint/binding-linux-x64-musl@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.59.0.tgz#0a068ef0041332b1cd08132ba0ce70b86c271991"
|
||||
integrity sha512-+yYj1udJa2UvvIUmEm0IcKgc0UlPMgz0nsSTvkPL2y6n0uU5LgIHSwVu4AHhrve6j9BpVSoRksnz8c9QcvITJA==
|
||||
"@oxlint/binding-linux-x64-musl@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.61.0.tgz#cff6c4c24de0863cecbce1c1f43adcafacd9011d"
|
||||
integrity sha512-gGfAeGD4sNJGILZbc/yKcIimO9wQnPMoYp9swAaKeEtwsSQAbU+rsdQze5SBtIP6j0QDzeYd4XSSUCRCF+LIeQ==
|
||||
|
||||
"@oxlint/binding-openharmony-arm64@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.59.0.tgz#cc7cdce8aeebcee2d5e8e7fac3d3712d012df77f"
|
||||
integrity sha512-bUplUb48LYsB3hHlQXP2ZMOenpieWoOyppLAnnAhuPag3MGPnt+7caxE3w/Vl9wpQsTA3gzLntQi9rxWrs7Xqg==
|
||||
"@oxlint/binding-openharmony-arm64@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.61.0.tgz#9fdd87fc10c2c51c510470f7c4136ab2df9ee7b3"
|
||||
integrity sha512-OlVT0LrG/ct33EVtWRyR+B/othwmDWeRxfi13wUdPeb3lAT5TgTcFDcfLfarZtzB4W1nWF/zICMgYdkggX2WmQ==
|
||||
|
||||
"@oxlint/binding-win32-arm64-msvc@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.59.0.tgz#6fe74088bac2c9a560aba8475071e5ff4e8be9ba"
|
||||
integrity sha512-/HLsLuz42rWl7h7ePdmMTpHm2HIDmPtcEMYgm5BBEHiEiuNOrzMaUpd2z7UnNni5LGN9obJy2YoAYBLXQwazrA==
|
||||
"@oxlint/binding-win32-arm64-msvc@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.61.0.tgz#25a87f62859a6703f2520d665f98f6c0f88a70e0"
|
||||
integrity sha512-vI//NZPJk6DToiovPtaiwD4iQ7kO1r5ReWQD0sOOyKRtP3E2f6jxin4uvwi3OvDzHA2EFfd7DcZl5dtkQh7g1w==
|
||||
|
||||
"@oxlint/binding-win32-ia32-msvc@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.59.0.tgz#9223a0a4ffa47fd9c0e56e8cf7933253abd93688"
|
||||
integrity sha512-rUPy+JnanpPwV/aJCPnxAD1fW50+XPI0VkWr7f0vEbqcdsS8NpB24Rw6RsS7SdpFv8Dw+8ugCwao5nCFbqOUSg==
|
||||
"@oxlint/binding-win32-ia32-msvc@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.61.0.tgz#06f7df42b074f28ef27540d38be69875e5d5afcb"
|
||||
integrity sha512-0ySj4/4zd2XjePs3XAQq7IigIstN4LPQZgCyigX5/ERMLjdWAJfnxcTsrtxZxuij8guJW8foXuHmhGxW0H4dDA==
|
||||
|
||||
"@oxlint/binding-win32-x64-msvc@1.59.0":
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.59.0.tgz#e2d94ab9567a2c1302e15ef927a7363675b740ba"
|
||||
integrity sha512-xkE7puteDS/vUyRngLXW0t8WgdWoS/tfxXjhP/P7SMqPDx+hs44SpssO3h3qmTqECYEuXBUPzcAw5257Ka+ofA==
|
||||
"@oxlint/binding-win32-x64-msvc@1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.61.0.tgz#7bc84b261bb8b515ade20f1044547cc8bbee44ce"
|
||||
integrity sha512-0xgSiyeqDLDZxXoe9CVJrOx3TUVsfyoOY7cNi03JbItNcC9WCZqrSNdrAbHONxhSPaVh/lzfnDcON1RqSUMhHw==
|
||||
|
||||
"@parcel/watcher-android-arm64@2.5.1":
|
||||
version "2.5.1"
|
||||
@@ -15039,69 +15039,69 @@ own-keys@^1.0.1:
|
||||
object-keys "^1.1.1"
|
||||
safe-push-apply "^1.0.0"
|
||||
|
||||
oxfmt@0.41.0:
|
||||
version "0.41.0"
|
||||
resolved "https://registry.yarnpkg.com/oxfmt/-/oxfmt-0.41.0.tgz#7dbfb63d19704a85412f36678c3ace092ce88673"
|
||||
integrity sha512-sKLdJZdQ3bw6x9qKiT7+eID4MNEXlDHf5ZacfIircrq6Qwjk0L6t2/JQlZZrVHTXJawK3KaMuBoJnEJPcqCEdg==
|
||||
oxfmt@0.46.0:
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/oxfmt/-/oxfmt-0.46.0.tgz#33789146055129820102203b4397eb4932ada687"
|
||||
integrity sha512-CopwJOwPAjZ9p76fCvz+mSOJTw9/NY3cSksZK3VO/bUQ8UoEcketNgUuYS0UB3p+R9XnXe7wGGXUmyFxc7QxJA==
|
||||
dependencies:
|
||||
tinypool "2.1.0"
|
||||
optionalDependencies:
|
||||
"@oxfmt/binding-android-arm-eabi" "0.41.0"
|
||||
"@oxfmt/binding-android-arm64" "0.41.0"
|
||||
"@oxfmt/binding-darwin-arm64" "0.41.0"
|
||||
"@oxfmt/binding-darwin-x64" "0.41.0"
|
||||
"@oxfmt/binding-freebsd-x64" "0.41.0"
|
||||
"@oxfmt/binding-linux-arm-gnueabihf" "0.41.0"
|
||||
"@oxfmt/binding-linux-arm-musleabihf" "0.41.0"
|
||||
"@oxfmt/binding-linux-arm64-gnu" "0.41.0"
|
||||
"@oxfmt/binding-linux-arm64-musl" "0.41.0"
|
||||
"@oxfmt/binding-linux-ppc64-gnu" "0.41.0"
|
||||
"@oxfmt/binding-linux-riscv64-gnu" "0.41.0"
|
||||
"@oxfmt/binding-linux-riscv64-musl" "0.41.0"
|
||||
"@oxfmt/binding-linux-s390x-gnu" "0.41.0"
|
||||
"@oxfmt/binding-linux-x64-gnu" "0.41.0"
|
||||
"@oxfmt/binding-linux-x64-musl" "0.41.0"
|
||||
"@oxfmt/binding-openharmony-arm64" "0.41.0"
|
||||
"@oxfmt/binding-win32-arm64-msvc" "0.41.0"
|
||||
"@oxfmt/binding-win32-ia32-msvc" "0.41.0"
|
||||
"@oxfmt/binding-win32-x64-msvc" "0.41.0"
|
||||
"@oxfmt/binding-android-arm-eabi" "0.46.0"
|
||||
"@oxfmt/binding-android-arm64" "0.46.0"
|
||||
"@oxfmt/binding-darwin-arm64" "0.46.0"
|
||||
"@oxfmt/binding-darwin-x64" "0.46.0"
|
||||
"@oxfmt/binding-freebsd-x64" "0.46.0"
|
||||
"@oxfmt/binding-linux-arm-gnueabihf" "0.46.0"
|
||||
"@oxfmt/binding-linux-arm-musleabihf" "0.46.0"
|
||||
"@oxfmt/binding-linux-arm64-gnu" "0.46.0"
|
||||
"@oxfmt/binding-linux-arm64-musl" "0.46.0"
|
||||
"@oxfmt/binding-linux-ppc64-gnu" "0.46.0"
|
||||
"@oxfmt/binding-linux-riscv64-gnu" "0.46.0"
|
||||
"@oxfmt/binding-linux-riscv64-musl" "0.46.0"
|
||||
"@oxfmt/binding-linux-s390x-gnu" "0.46.0"
|
||||
"@oxfmt/binding-linux-x64-gnu" "0.46.0"
|
||||
"@oxfmt/binding-linux-x64-musl" "0.46.0"
|
||||
"@oxfmt/binding-openharmony-arm64" "0.46.0"
|
||||
"@oxfmt/binding-win32-arm64-msvc" "0.46.0"
|
||||
"@oxfmt/binding-win32-ia32-msvc" "0.46.0"
|
||||
"@oxfmt/binding-win32-x64-msvc" "0.46.0"
|
||||
|
||||
oxlint-tsgolint@0.20.0:
|
||||
version "0.20.0"
|
||||
resolved "https://registry.yarnpkg.com/oxlint-tsgolint/-/oxlint-tsgolint-0.20.0.tgz#e011319219faa572faf340c40d65708404b37e10"
|
||||
integrity sha512-/Uc9TQyN1l8w9QNvXtVHYtz+SzDJHKpb5X0UnHodl0BVzijUPk0LPlDOHAvogd1UI+iy9ZSF6gQxEqfzUxCULQ==
|
||||
oxlint-tsgolint@0.21.1:
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/oxlint-tsgolint/-/oxlint-tsgolint-0.21.1.tgz#92e87d455283b346087fbff6d0481da9b5276640"
|
||||
integrity sha512-O2hxiT14C2HJkwzBU6CQBFPoagSd/IcV+Tt3e3UUaXFwbW4BO5DSDPSSboc3UM5MIDY+MLyepvtQwBQafNxWdw==
|
||||
optionalDependencies:
|
||||
"@oxlint-tsgolint/darwin-arm64" "0.20.0"
|
||||
"@oxlint-tsgolint/darwin-x64" "0.20.0"
|
||||
"@oxlint-tsgolint/linux-arm64" "0.20.0"
|
||||
"@oxlint-tsgolint/linux-x64" "0.20.0"
|
||||
"@oxlint-tsgolint/win32-arm64" "0.20.0"
|
||||
"@oxlint-tsgolint/win32-x64" "0.20.0"
|
||||
"@oxlint-tsgolint/darwin-arm64" "0.21.1"
|
||||
"@oxlint-tsgolint/darwin-x64" "0.21.1"
|
||||
"@oxlint-tsgolint/linux-arm64" "0.21.1"
|
||||
"@oxlint-tsgolint/linux-x64" "0.21.1"
|
||||
"@oxlint-tsgolint/win32-arm64" "0.21.1"
|
||||
"@oxlint-tsgolint/win32-x64" "0.21.1"
|
||||
|
||||
oxlint@1.59.0:
|
||||
version "1.59.0"
|
||||
resolved "https://registry.yarnpkg.com/oxlint/-/oxlint-1.59.0.tgz#7bc0b9abe87b96ed7d640f90cb0956b9558f3b96"
|
||||
integrity sha512-0xBLeGGjP4vD9pygRo8iuOkOzEU1MqOnfiOl7KYezL/QvWL8NUg6n03zXc7ZVqltiOpUxBk2zgHI3PnRIEdAvw==
|
||||
oxlint@1.61.0:
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/oxlint/-/oxlint-1.61.0.tgz#e714742cfe7b815713feb14600b0ffe963d539a4"
|
||||
integrity sha512-ZC0ALuhDZ6ivOFG+sy0D0pEDN49EvsId98zVlmYdkcXHsEM14m/qTNUEsUpiFiCVbpIxYtVBmmLE87nsbUHohQ==
|
||||
optionalDependencies:
|
||||
"@oxlint/binding-android-arm-eabi" "1.59.0"
|
||||
"@oxlint/binding-android-arm64" "1.59.0"
|
||||
"@oxlint/binding-darwin-arm64" "1.59.0"
|
||||
"@oxlint/binding-darwin-x64" "1.59.0"
|
||||
"@oxlint/binding-freebsd-x64" "1.59.0"
|
||||
"@oxlint/binding-linux-arm-gnueabihf" "1.59.0"
|
||||
"@oxlint/binding-linux-arm-musleabihf" "1.59.0"
|
||||
"@oxlint/binding-linux-arm64-gnu" "1.59.0"
|
||||
"@oxlint/binding-linux-arm64-musl" "1.59.0"
|
||||
"@oxlint/binding-linux-ppc64-gnu" "1.59.0"
|
||||
"@oxlint/binding-linux-riscv64-gnu" "1.59.0"
|
||||
"@oxlint/binding-linux-riscv64-musl" "1.59.0"
|
||||
"@oxlint/binding-linux-s390x-gnu" "1.59.0"
|
||||
"@oxlint/binding-linux-x64-gnu" "1.59.0"
|
||||
"@oxlint/binding-linux-x64-musl" "1.59.0"
|
||||
"@oxlint/binding-openharmony-arm64" "1.59.0"
|
||||
"@oxlint/binding-win32-arm64-msvc" "1.59.0"
|
||||
"@oxlint/binding-win32-ia32-msvc" "1.59.0"
|
||||
"@oxlint/binding-win32-x64-msvc" "1.59.0"
|
||||
"@oxlint/binding-android-arm-eabi" "1.61.0"
|
||||
"@oxlint/binding-android-arm64" "1.61.0"
|
||||
"@oxlint/binding-darwin-arm64" "1.61.0"
|
||||
"@oxlint/binding-darwin-x64" "1.61.0"
|
||||
"@oxlint/binding-freebsd-x64" "1.61.0"
|
||||
"@oxlint/binding-linux-arm-gnueabihf" "1.61.0"
|
||||
"@oxlint/binding-linux-arm-musleabihf" "1.61.0"
|
||||
"@oxlint/binding-linux-arm64-gnu" "1.61.0"
|
||||
"@oxlint/binding-linux-arm64-musl" "1.61.0"
|
||||
"@oxlint/binding-linux-ppc64-gnu" "1.61.0"
|
||||
"@oxlint/binding-linux-riscv64-gnu" "1.61.0"
|
||||
"@oxlint/binding-linux-riscv64-musl" "1.61.0"
|
||||
"@oxlint/binding-linux-s390x-gnu" "1.61.0"
|
||||
"@oxlint/binding-linux-x64-gnu" "1.61.0"
|
||||
"@oxlint/binding-linux-x64-musl" "1.61.0"
|
||||
"@oxlint/binding-openharmony-arm64" "1.61.0"
|
||||
"@oxlint/binding-win32-arm64-msvc" "1.61.0"
|
||||
"@oxlint/binding-win32-ia32-msvc" "1.61.0"
|
||||
"@oxlint/binding-win32-x64-msvc" "1.61.0"
|
||||
|
||||
p-cancelable@^2.0.0:
|
||||
version "2.1.1"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -134,24 +134,6 @@
|
||||
"unit": "Count",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "azure_webapplicationfirewallcaptcharequestcount_total",
|
||||
"unit": "Count",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "azure_webapplicationfirewalljsrequestcount_total",
|
||||
"unit": "Count",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "azure_websocketconnections_total",
|
||||
"unit": "Count",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
}
|
||||
],
|
||||
"logs": [
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -440,4 +440,3 @@ func (handler *handler) AgentCheckIn(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
render.Success(rw, http.StatusOK, cloudintegrationtypes.NewGettableAgentCheckIn(provider, resp))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,39 +1,5 @@
|
||||
package cloudintegrationtypes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
var (
|
||||
AgentArmTemplateStorePath = "https://signoz-integrations.s3.us-east-1.amazonaws.com/azure-arm-template-%s.json"
|
||||
AgentDeploymentStackName = "signoz-integration"
|
||||
|
||||
// Default values for fixed ARM template parameters.
|
||||
armDefaultRgName = "signoz-integration-rg"
|
||||
armDefaultContainerEnvName = "signoz-integration-agent-env"
|
||||
armDefaultDeploymentEnv = "production"
|
||||
|
||||
// ARM template parameter key names used in both CLI and PowerShell deployment commands.
|
||||
armParamLocation = "location"
|
||||
armParamSignozAPIKey = "signozApiKey"
|
||||
armParamSignozAPIUrl = "signozApiUrl"
|
||||
armParamSignozIngestionURL = "signozIngestionUrl"
|
||||
armParamSignozIngestionKey = "signozIngestionKey"
|
||||
armParamAccountID = "signozIntegrationAccountId"
|
||||
armParamAgentVersion = "signozIntegrationAgentVersion"
|
||||
armParamRgName = "rgName"
|
||||
armParamContainerEnvName = "containerEnvName"
|
||||
armParamDeploymentEnv = "deploymentEnv"
|
||||
|
||||
// command templates.
|
||||
azureCLITemplate = template.Must(template.New("azureCLI").Parse(azureCLITemplateStr()))
|
||||
azurePowerShellTemplate = template.Must(template.New("azurePS").Parse(azurePowerShellTemplateStr()))
|
||||
)
|
||||
|
||||
type AzureAccountConfig struct {
|
||||
DeploymentRegion string `json:"deploymentRegion" required:"true"`
|
||||
ResourceGroups []string `json:"resourceGroups" required:"true" nullable:"false"`
|
||||
@@ -85,36 +51,6 @@ type AzureIntegrationConfig struct {
|
||||
TelemetryCollectionStrategy []*AzureTelemetryCollectionStrategy `json:"telemetryCollectionStrategy" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
// azureTemplateData is the data struct passed to both command templates.
|
||||
// All fields are exported so text/template can access them.
|
||||
type azureTemplateData struct {
|
||||
// Deploy parameter values.
|
||||
TemplateURL string
|
||||
Location string
|
||||
SignozAPIKey string
|
||||
SignozAPIUrl string
|
||||
SignozIngestionURL string
|
||||
SignozIngestionKey string
|
||||
AccountID string
|
||||
AgentVersion string
|
||||
// ARM parameter key names (from package-level vars).
|
||||
StackName string
|
||||
ParamLocation string
|
||||
ParamSignozAPIKey string
|
||||
ParamSignozAPIUrl string
|
||||
ParamSignozIngestionURL string
|
||||
ParamSignozIngestionKey string
|
||||
ParamAccountID string
|
||||
ParamAgentVersion string
|
||||
ParamRgName string
|
||||
ParamContainerEnvName string
|
||||
ParamDeploymentEnv string
|
||||
// Fixed default values.
|
||||
DefaultRgName string
|
||||
DefaultContainerEnvName string
|
||||
DefaultDeploymentEnv string
|
||||
}
|
||||
|
||||
func NewAzureIntegrationConfig(
|
||||
deploymentRegion string,
|
||||
resourceGroups []string,
|
||||
@@ -126,105 +62,3 @@ func NewAzureIntegrationConfig(
|
||||
TelemetryCollectionStrategy: strategies,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAzureConnectionArtifact(
|
||||
accountID valuer.UUID,
|
||||
agentVersion string,
|
||||
creds *Credentials,
|
||||
cfg *AzurePostableAccountConfig,
|
||||
) (*AzureConnectionArtifact, error) {
|
||||
data := azureTemplateData{
|
||||
TemplateURL: fmt.Sprintf(AgentArmTemplateStorePath, agentVersion),
|
||||
Location: cfg.DeploymentRegion,
|
||||
SignozAPIKey: creds.SigNozAPIKey,
|
||||
SignozAPIUrl: creds.SigNozAPIURL,
|
||||
SignozIngestionURL: creds.IngestionURL,
|
||||
SignozIngestionKey: creds.IngestionKey,
|
||||
AccountID: accountID.StringValue(),
|
||||
AgentVersion: agentVersion,
|
||||
StackName: AgentDeploymentStackName,
|
||||
ParamLocation: armParamLocation,
|
||||
ParamSignozAPIKey: armParamSignozAPIKey,
|
||||
ParamSignozAPIUrl: armParamSignozAPIUrl,
|
||||
ParamSignozIngestionURL: armParamSignozIngestionURL,
|
||||
ParamSignozIngestionKey: armParamSignozIngestionKey,
|
||||
ParamAccountID: armParamAccountID,
|
||||
ParamAgentVersion: armParamAgentVersion,
|
||||
ParamRgName: armParamRgName,
|
||||
ParamContainerEnvName: armParamContainerEnvName,
|
||||
ParamDeploymentEnv: armParamDeploymentEnv,
|
||||
DefaultRgName: armDefaultRgName,
|
||||
DefaultContainerEnvName: armDefaultContainerEnvName,
|
||||
DefaultDeploymentEnv: armDefaultDeploymentEnv,
|
||||
}
|
||||
|
||||
cliCommand, err := newAzureConnectionCLICommand(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
psCommand, err := newAzureConnectionPowerShellCommand(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &AzureConnectionArtifact{
|
||||
CLICommand: cliCommand,
|
||||
CloudPowerShellCommand: psCommand,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newAzureConnectionCLICommand(data azureTemplateData) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
err := azureCLITemplate.Execute(&buf, data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func newAzureConnectionPowerShellCommand(data azureTemplateData) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
err := azurePowerShellTemplate.Execute(&buf, data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func azureCLITemplateStr() string {
|
||||
return `az stack sub create \
|
||||
--name {{.StackName}} \
|
||||
--location {{.Location}} \
|
||||
--template-uri {{.TemplateURL}} \
|
||||
--parameters \
|
||||
{{.ParamLocation}}='{{.Location}}' \
|
||||
{{.ParamSignozAPIKey}}='{{.SignozAPIKey}}' \
|
||||
{{.ParamSignozAPIUrl}}='{{.SignozAPIUrl}}' \
|
||||
{{.ParamSignozIngestionURL}}='{{.SignozIngestionURL}}' \
|
||||
{{.ParamSignozIngestionKey}}='{{.SignozIngestionKey}}' \
|
||||
{{.ParamAccountID}}='{{.AccountID}}' \
|
||||
{{.ParamAgentVersion}}='{{.AgentVersion}}' \
|
||||
--action-on-unmanage deleteAll \
|
||||
--deny-settings-mode denyDelete`
|
||||
}
|
||||
|
||||
func azurePowerShellTemplateStr() string {
|
||||
return "New-AzSubscriptionDeploymentStack `\n" +
|
||||
" -Name \"{{.StackName}}\" `\n" +
|
||||
" -Location \"{{.Location}}\" `\n" +
|
||||
" -TemplateUri \"{{.TemplateURL}}\" `\n" +
|
||||
" -TemplateParameterObject @{\n" +
|
||||
" {{.ParamLocation}} = \"{{.Location}}\"\n" +
|
||||
" {{.ParamSignozAPIKey}} = \"{{.SignozAPIKey}}\"\n" +
|
||||
" {{.ParamSignozAPIUrl}} = \"{{.SignozAPIUrl}}\"\n" +
|
||||
" {{.ParamSignozIngestionURL}} = \"{{.SignozIngestionURL}}\"\n" +
|
||||
" {{.ParamSignozIngestionKey}} = \"{{.SignozIngestionKey}}\"\n" +
|
||||
" {{.ParamAccountID}} = \"{{.AccountID}}\"\n" +
|
||||
" {{.ParamAgentVersion}} = \"{{.AgentVersion}}\"\n" +
|
||||
" {{.ParamRgName}} = \"{{.DefaultRgName}}\"\n" +
|
||||
" {{.ParamContainerEnvName}} = \"{{.DefaultContainerEnvName}}\"\n" +
|
||||
" {{.ParamDeploymentEnv}} = \"{{.DefaultDeploymentEnv}}\"\n" +
|
||||
" } `\n" +
|
||||
" -ActionOnUnmanage \"deleteAll\" `\n" +
|
||||
" -DenySettingsMode \"denyDelete\""
|
||||
}
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
// file is generated by AI
|
||||
package cloudintegrationtypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
var testTemplateData = azureTemplateData{
|
||||
TemplateURL: fmt.Sprintf(AgentArmTemplateStorePath, "v0.1.0"),
|
||||
Location: "eastus",
|
||||
SignozAPIKey: "test-api-key",
|
||||
SignozAPIUrl: "https://signoz.example.com",
|
||||
SignozIngestionURL: "https://ingest.example.com",
|
||||
SignozIngestionKey: "test-ingest-key",
|
||||
AccountID: "acct-123",
|
||||
AgentVersion: "v0.1.0",
|
||||
StackName: AgentDeploymentStackName,
|
||||
ParamLocation: armParamLocation,
|
||||
ParamSignozAPIKey: armParamSignozAPIKey,
|
||||
ParamSignozAPIUrl: armParamSignozAPIUrl,
|
||||
ParamSignozIngestionURL: armParamSignozIngestionURL,
|
||||
ParamSignozIngestionKey: armParamSignozIngestionKey,
|
||||
ParamAccountID: armParamAccountID,
|
||||
ParamAgentVersion: armParamAgentVersion,
|
||||
ParamRgName: armParamRgName,
|
||||
ParamContainerEnvName: armParamContainerEnvName,
|
||||
ParamDeploymentEnv: armParamDeploymentEnv,
|
||||
DefaultRgName: armDefaultRgName,
|
||||
DefaultContainerEnvName: armDefaultContainerEnvName,
|
||||
DefaultDeploymentEnv: armDefaultDeploymentEnv,
|
||||
}
|
||||
|
||||
func TestNewAzureConnectionCLICommand(t *testing.T) {
|
||||
cmd, err := newAzureConnectionCLICommand(testTemplateData)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
mustContain := []string{
|
||||
"az stack sub create",
|
||||
fmt.Sprintf("--name %s", AgentDeploymentStackName),
|
||||
fmt.Sprintf("--location %s", testTemplateData.Location),
|
||||
fmt.Sprintf("--template-uri %s", testTemplateData.TemplateURL),
|
||||
fmt.Sprintf("%s='%s'", armParamLocation, testTemplateData.Location),
|
||||
fmt.Sprintf("%s='%s'", armParamSignozAPIKey, testTemplateData.SignozAPIKey),
|
||||
fmt.Sprintf("%s='%s'", armParamSignozAPIUrl, testTemplateData.SignozAPIUrl),
|
||||
fmt.Sprintf("%s='%s'", armParamSignozIngestionURL, testTemplateData.SignozIngestionURL),
|
||||
fmt.Sprintf("%s='%s'", armParamSignozIngestionKey, testTemplateData.SignozIngestionKey),
|
||||
fmt.Sprintf("%s='%s'", armParamAccountID, testTemplateData.AccountID),
|
||||
fmt.Sprintf("%s='%s'", armParamAgentVersion, testTemplateData.AgentVersion),
|
||||
"--action-on-unmanage deleteAll",
|
||||
"--deny-settings-mode denyDelete",
|
||||
}
|
||||
|
||||
for _, fragment := range mustContain {
|
||||
if !strings.Contains(cmd, fragment) {
|
||||
t.Errorf("CLI command missing %q\ngot:\n%s", fragment, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// Lines must be joined with the bash line-continuation separator.
|
||||
if !strings.Contains(cmd, " \\\n") {
|
||||
t.Errorf("CLI command missing line-continuation separator ' \\\\\\n'\ngot:\n%s", cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAzureConnectionPowerShellCommand(t *testing.T) {
|
||||
cmd, err := newAzureConnectionPowerShellCommand(testTemplateData)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
mustContain := []string{
|
||||
"New-AzSubscriptionDeploymentStack",
|
||||
fmt.Sprintf("-Name \"%s\"", AgentDeploymentStackName),
|
||||
fmt.Sprintf("-Location \"%s\"", testTemplateData.Location),
|
||||
fmt.Sprintf("-TemplateUri \"%s\"", testTemplateData.TemplateURL),
|
||||
armParamLocation,
|
||||
armParamSignozAPIKey,
|
||||
armParamSignozAPIUrl,
|
||||
armParamSignozIngestionURL,
|
||||
armParamSignozIngestionKey,
|
||||
armParamAccountID,
|
||||
armParamAgentVersion,
|
||||
armParamRgName,
|
||||
armParamContainerEnvName,
|
||||
armParamDeploymentEnv,
|
||||
armDefaultRgName,
|
||||
armDefaultContainerEnvName,
|
||||
armDefaultDeploymentEnv,
|
||||
"-ActionOnUnmanage \"deleteAll\"",
|
||||
"-DenySettingsMode \"denyDelete\"",
|
||||
}
|
||||
|
||||
for _, fragment := range mustContain {
|
||||
if !strings.Contains(cmd, fragment) {
|
||||
t.Errorf("PowerShell command missing %q\ngot:\n%s", fragment, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// Final command must not end with a line-continuation backtick.
|
||||
if strings.HasSuffix(strings.TrimSpace(cmd), "`") {
|
||||
t.Errorf("PowerShell command must not end with a backtick\ngot:\n%s", cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAzureConnectionArtifact(t *testing.T) {
|
||||
accountID := valuer.GenerateUUID()
|
||||
agentVersion := "v0.1.0"
|
||||
creds := &Credentials{
|
||||
SigNozAPIURL: "https://signoz.example.com",
|
||||
SigNozAPIKey: "test-api-key",
|
||||
IngestionURL: "https://ingest.example.com",
|
||||
IngestionKey: "test-ingest-key",
|
||||
}
|
||||
cfg := &AzurePostableAccountConfig{
|
||||
DeploymentRegion: "eastus",
|
||||
ResourceGroups: []string{"rg1"},
|
||||
}
|
||||
|
||||
artifact, err := NewAzureConnectionArtifact(accountID, agentVersion, creds, cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if artifact.CLICommand == "" {
|
||||
t.Error("CLICommand must not be empty")
|
||||
}
|
||||
if artifact.CloudPowerShellCommand == "" {
|
||||
t.Error("CloudPowerShellCommand must not be empty")
|
||||
}
|
||||
if !strings.Contains(artifact.CLICommand, accountID.StringValue()) {
|
||||
t.Errorf("CLICommand must contain accountID %q", accountID.StringValue())
|
||||
}
|
||||
if !strings.Contains(artifact.CloudPowerShellCommand, accountID.StringValue()) {
|
||||
t.Errorf("CloudPowerShellCommand must contain accountID %q", accountID.StringValue())
|
||||
}
|
||||
}
|
||||
@@ -239,10 +239,6 @@ func (service *CloudIntegrationService) Update(provider CloudProviderType, servi
|
||||
}
|
||||
|
||||
// other validations happen in newStorableServiceConfig
|
||||
case CloudProviderTypeAzure:
|
||||
if config.Azure == nil {
|
||||
return errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "Azure config is required for Azure service")
|
||||
}
|
||||
default:
|
||||
return errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider.StringValue())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user