Compare commits

...

4 Commits

Author SHA1 Message Date
Karan Balani
82b391af2c chore: minor fixes 2026-01-21 11:44:11 +05:30
Karan Balani
84bf240362 chore: fix error handling 2026-01-21 11:34:56 +05:30
Karan Balani
a26e28b048 chore: fix model 2026-01-21 01:25:22 +05:30
Karan Balani
8456d7f272 feat: use new v2 gateway apis for ingestion keys 2026-01-20 23:25:04 +05:30
13 changed files with 197 additions and 122 deletions

View File

@@ -1,28 +1,43 @@
import { GatewayApiV1Instance } from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { GatewayApiV2Instance } from 'api';
import axios from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import {
CreatedIngestionKeyProps,
CreateIngestionKeyProps,
IngestionKeyProps,
} from 'types/api/ingestionKeys/types';
import { ErrorStatusCode } from 'types/common';
const createIngestionKey = async (
props: CreateIngestionKeyProps,
): Promise<SuccessResponse<IngestionKeyProps> | ErrorResponse> => {
): Promise<SuccessResponse<CreatedIngestionKeyProps> | ErrorResponse> => {
try {
const response = await GatewayApiV1Instance.post('/workspaces/me/keys', {
const response = await GatewayApiV2Instance.post('/ingestion_keys', {
...props,
});
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
message: 'success',
payload: response.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
if (axios.isAxiosError(error)) {
const errResponse: ErrorResponse = {
statusCode:
(error.response?.status as ErrorStatusCode) || (500 as ErrorStatusCode),
error: error.response?.data?.error?.message || 'Something went wrong',
message: error.response?.data?.error?.message || 'An error occurred',
payload: null,
};
throw errResponse;
}
throw {
statusCode: 500,
error: 'Unknown error',
message: 'An unknown error occurred',
payload: null,
};
}
};

View File

@@ -1,25 +1,37 @@
import { GatewayApiV1Instance } from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { GatewayApiV2Instance } from 'api';
import axios from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { AllIngestionKeyProps } from 'types/api/ingestionKeys/types';
import { ErrorStatusCode } from 'types/common';
const deleteIngestionKey = async (
id: string,
): Promise<SuccessResponse<AllIngestionKeyProps> | ErrorResponse> => {
): Promise<SuccessResponse<void> | ErrorResponse> => {
try {
const response = await GatewayApiV1Instance.delete(
`/workspaces/me/keys/${id}`,
);
await GatewayApiV2Instance.delete(`/ingestion_keys/${id}`);
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
message: 'success',
payload: undefined,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
if (axios.isAxiosError(error)) {
const errResponse: ErrorResponse = {
statusCode:
(error.response?.status as ErrorStatusCode) || (500 as ErrorStatusCode),
error: error.response?.data?.error?.message || 'Something went wrong',
message: error.response?.data?.error?.message || 'An error occurred',
payload: null,
};
throw errResponse;
}
throw {
statusCode: 500,
error: 'Unknown error',
message: 'An unknown error occurred',
payload: null,
};
}
};

View File

@@ -1,4 +1,4 @@
import { GatewayApiV1Instance } from 'api';
import { GatewayApiV2Instance } from 'api';
import { AxiosResponse } from 'axios';
import {
AllIngestionKeyProps,
@@ -11,11 +11,11 @@ export const getAllIngestionKeys = (
// eslint-disable-next-line @typescript-eslint/naming-convention
const { search, per_page, page } = props;
const BASE_URL = '/workspaces/me/keys';
const BASE_URL = '/ingestion_keys';
const URL_QUERY_PARAMS =
search && search.length > 0
? `/search?name=${search}&page=1&per_page=100`
: `?page=${page}&per_page=${per_page}`;
return GatewayApiV1Instance.get(`${BASE_URL}${URL_QUERY_PARAMS}`);
return GatewayApiV2Instance.get(`${BASE_URL}${URL_QUERY_PARAMS}`);
};

View File

@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-throw-literal */
import { GatewayApiV1Instance } from 'api';
import { GatewayApiV2Instance } from 'api';
import axios from 'axios';
import {
AddLimitProps,
@@ -24,18 +24,20 @@ const createLimitForIngestionKey = async (
props: AddLimitProps,
): Promise<SuccessResponse<LimitSuccessProps> | ErrorResponse> => {
try {
const response = await GatewayApiV1Instance.post(
`/workspaces/me/keys/${props.keyID}/limits`,
const response = await GatewayApiV2Instance.post(
`/ingestion_keys/${props.keyID}/limits`,
{
...props,
signal: props.signal,
config: props.config,
tags: props.tags,
},
);
return {
statusCode: 200,
statusCode: 201,
error: null,
message: response.data.status,
payload: response.data.data,
message: 'success',
payload: response.data,
};
} catch (error) {
if (axios.isAxiosError(error)) {

View File

@@ -1,25 +1,37 @@
import { GatewayApiV1Instance } from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { GatewayApiV2Instance } from 'api';
import axios from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { AllIngestionKeyProps } from 'types/api/ingestionKeys/types';
import { ErrorStatusCode } from 'types/common';
const deleteLimitsForIngestionKey = async (
id: string,
): Promise<SuccessResponse<AllIngestionKeyProps> | ErrorResponse> => {
): Promise<SuccessResponse<void> | ErrorResponse> => {
try {
const response = await GatewayApiV1Instance.delete(
`/workspaces/me/limits/${id}`,
);
await GatewayApiV2Instance.delete(`/ingestion_keys/limits/${id}`);
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
message: 'success',
payload: undefined,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
if (axios.isAxiosError(error)) {
const errResponse: ErrorResponse = {
statusCode:
(error.response?.status as ErrorStatusCode) || (500 as ErrorStatusCode),
error: error.response?.data?.error?.message || 'Something went wrong',
message: error.response?.data?.error?.message || 'An error occurred',
payload: null,
};
throw errResponse;
}
throw {
statusCode: 500,
error: 'Unknown error',
message: 'An unknown error occurred',
payload: null,
};
}
};

View File

@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-throw-literal */
import { GatewayApiV1Instance } from 'api';
import { GatewayApiV2Instance } from 'api';
import axios from 'axios';
import {
LimitSuccessProps,
@@ -22,20 +22,18 @@ interface ErrorResponse {
const updateLimitForIngestionKey = async (
props: UpdateLimitProps,
): Promise<SuccessResponse<LimitSuccessProps> | ErrorResponse> => {
): Promise<SuccessResponse<void> | ErrorResponse> => {
try {
const response = await GatewayApiV1Instance.patch(
`/workspaces/me/limits/${props.limitID}`,
{
config: props.config,
},
);
await GatewayApiV2Instance.patch(`/ingestion_keys/limits/${props.limitID}`, {
config: props.config,
tags: props.tags,
});
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
message: 'success',
payload: undefined,
};
} catch (error) {
if (axios.isAxiosError(error)) {

View File

@@ -1,18 +1,18 @@
import { GatewayApiV1Instance } from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { GatewayApiV2Instance } from 'api';
import axios from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import {
IngestionKeysPayloadProps,
CreatedIngestionKeyProps,
UpdateIngestionKeyProps,
} from 'types/api/ingestionKeys/types';
import { ErrorStatusCode } from 'types/common';
const updateIngestionKey = async (
props: UpdateIngestionKeyProps,
): Promise<SuccessResponse<IngestionKeysPayloadProps> | ErrorResponse> => {
): Promise<SuccessResponse<CreatedIngestionKeyProps> | ErrorResponse> => {
try {
const response = await GatewayApiV1Instance.patch(
`/workspaces/me/keys/${props.id}`,
const response = await GatewayApiV2Instance.patch(
`/ingestion_keys/${props.id}`,
{
...props.data,
},
@@ -21,11 +21,26 @@ const updateIngestionKey = async (
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
message: 'success',
payload: response.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
if (axios.isAxiosError(error)) {
const errResponse: ErrorResponse = {
statusCode:
(error.response?.status as ErrorStatusCode) || (500 as ErrorStatusCode),
error: error.response?.data?.error?.message || 'Something went wrong',
message: error.response?.data?.error?.message || 'An error occurred',
payload: null,
};
throw errResponse;
}
throw {
statusCode: 500,
error: 'Unknown error',
message: 'An unknown error occurred',
payload: null,
};
}
};

View File

@@ -5,7 +5,7 @@ export const apiV3 = '/api/v3/';
export const apiV4 = '/api/v4/';
export const apiV5 = '/api/v5/';
export const gatewayApiV1 = '/api/gateway/v1/';
export const gatewayApiV2 = '/api/gateway/v2/';
export const gatewayApiV2 = '/api/v2/gateway/';
export const apiAlertManager = '/api/alertmanager/';
export default apiV1;

View File

@@ -107,11 +107,14 @@ export const disabledDate = (current: any): boolean =>
export const showErrorNotification = (
notifications: NotificationInstance,
err: Error,
err: Error | ErrorResponse,
): void => {
notifications.error({
message: err.message || SOMETHING_WENT_WRONG,
});
const message =
(err as ErrorResponse)?.error ||
(err as Error)?.message ||
SOMETHING_WENT_WRONG;
notifications.error({ message });
};
type ExpiryOption = {
@@ -271,14 +274,14 @@ function MultiIngestionSettings(): JSX.Element {
});
useEffect(() => {
setActiveAPIKey(IngestionKeys?.data.data[0]);
setActiveAPIKey(IngestionKeys?.data?.data?.keys[0]);
}, [IngestionKeys]);
useEffect(() => {
setDataSource(IngestionKeys?.data.data || []);
setTotalIngestionKeys(IngestionKeys?.data?._pagination?.total || 0);
setDataSource(IngestionKeys?.data?.data?.keys || []);
setTotalIngestionKeys(IngestionKeys?.data?.data?._pagination?.total || 0);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [IngestionKeys?.data?.data]);
}, [IngestionKeys?.data?.data?.keys]);
useEffect(() => {
if (isError) {
@@ -310,8 +313,7 @@ function MultiIngestionSettings(): JSX.Element {
mutate: createIngestionKey,
isLoading: isLoadingCreateAPIKey,
} = useMutation(createIngestionKeyApi, {
onSuccess: (data) => {
setActiveAPIKey(data.payload);
onSuccess: () => {
setUpdatedTags([]);
hideAddViewModal();
refetchAPIKeys();
@@ -592,7 +594,7 @@ function MultiIngestionSettings(): JSX.Element {
const payload: UpdateLimitProps = {
limitID: signal.id,
signal: signal.signal,
tags: [],
config: {},
};

View File

@@ -14,8 +14,12 @@ interface TestIngestionKeyProps extends Omit<IngestionKeyProps, 'limits'> {
limits?: LimitProps[];
}
interface TestAllIngestionKeyProps extends Omit<AllIngestionKeyProps, 'data'> {
data: TestIngestionKeyProps[];
interface TestAllIngestionKeyProps {
status: string;
data: {
keys: TestIngestionKeyProps[];
_pagination: { page: number; per_page: number; pages: number; total: number };
};
}
// Mock useHistory.push to capture navigation URL used by MultiIngestionSettings
@@ -88,26 +92,28 @@ describe('MultiIngestionSettings Page', () => {
// Arrange API response with a metrics daily count limit so the alert button is visible
const response: TestAllIngestionKeyProps = {
status: 'success',
data: [
{
name: 'Key One',
expires_at: TEST_EXPIRES_AT,
value: 'secret',
workspace_id: TEST_WORKSPACE_ID,
id: 'k1',
created_at: TEST_CREATED_UPDATED,
updated_at: TEST_CREATED_UPDATED,
tags: [],
limits: [
{
id: 'l1',
signal: 'metrics',
config: { day: { count: 1000 } },
},
],
},
],
_pagination: { page: 1, per_page: 10, pages: 1, total: 1 },
data: {
keys: [
{
name: 'Key One',
expires_at: TEST_EXPIRES_AT,
value: 'secret',
workspace_id: TEST_WORKSPACE_ID,
id: 'k1',
created_at: TEST_CREATED_UPDATED,
updated_at: TEST_CREATED_UPDATED,
tags: [],
limits: [
{
id: 'l1',
signal: 'metrics',
config: { day: { count: 1000 } },
},
],
},
],
_pagination: { page: 1, per_page: 10, pages: 1, total: 1 },
},
};
server.use(
@@ -177,26 +183,28 @@ describe('MultiIngestionSettings Page', () => {
// Arrange API response with a logs daily size limit so the alert button is visible
const response: TestAllIngestionKeyProps = {
status: 'success',
data: [
{
name: 'Key Two',
expires_at: TEST_EXPIRES_AT,
value: 'secret',
workspace_id: TEST_WORKSPACE_ID,
id: 'k2',
created_at: TEST_CREATED_UPDATED,
updated_at: TEST_CREATED_UPDATED,
tags: [],
limits: [
{
id: 'l2',
signal: 'logs',
config: { day: { size: 2048 } },
},
],
},
],
_pagination: { page: 1, per_page: 10, pages: 1, total: 1 },
data: {
keys: [
{
name: 'Key Two',
expires_at: TEST_EXPIRES_AT,
value: 'secret',
workspace_id: TEST_WORKSPACE_ID,
id: 'k2',
created_at: TEST_CREATED_UPDATED,
updated_at: TEST_CREATED_UPDATED,
tags: [],
limits: [
{
id: 'l2',
signal: 'logs',
config: { day: { size: 2048 } },
},
],
},
],
_pagination: { page: 1, per_page: 10, pages: 1, total: 1 },
},
};
server.use(

View File

@@ -69,8 +69,11 @@ export default function OnboardingIngestionDetails(): JSX.Element {
};
useEffect(() => {
if (ingestionKeys?.data.data && ingestionKeys?.data.data.length > 0) {
setFirstIngestionKey(ingestionKeys?.data.data[0]);
if (
ingestionKeys?.data.data?.keys &&
ingestionKeys?.data.data.keys.length > 0
) {
setFirstIngestionKey(ingestionKeys?.data.data.keys[0]);
}
}, [ingestionKeys]);

View File

@@ -24,15 +24,20 @@ export interface AddLimitProps {
keyID: string;
signal: string;
config: LimitSettings;
tags?: string[];
}
export interface UpdateLimitProps {
limitID: string;
signal: string;
config: LimitSettings;
tags?: string[];
}
export interface LimitSuccessProps {
status: string;
response: unknown;
}
export interface CreateLimitProps {
id: string;
}

View File

@@ -53,13 +53,16 @@ export interface PaginationProps {
}
export interface AllIngestionKeyProps {
data: {
keys: IngestionKeyProps[];
_pagination: PaginationProps;
};
status: string;
data: IngestionKeyProps[];
_pagination: PaginationProps;
}
export interface CreateIngestionKeyProp {
data: IngestionKeyProps;
export interface CreatedIngestionKeyProps {
id: string;
value: string;
}
export interface DeleteIngestionKeyPayloadProps {