Skip to content

Commit f74aeb8

Browse files
committed
clean up api service
1 parent a034c60 commit f74aeb8

File tree

10 files changed

+291
-463
lines changed

10 files changed

+291
-463
lines changed

src/frontend_react/src/api/agentMessageService.tsx

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 91 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,104 @@
1-
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
2-
3-
export interface ApiError {
4-
status: number;
5-
message: string;
6-
data?: any;
7-
}
8-
9-
// Define classes for different error types
10-
export class BadRequestError extends Error {
11-
public readonly status = 400;
12-
public readonly data?: any;
13-
14-
constructor(message: string, data?: any) {
15-
super(message);
16-
this.name = 'BadRequestError';
17-
this.data = data;
18-
}
19-
}
1+
import { headerBuilder } from '../auth';
202

21-
export class UnauthorizedError extends Error {
22-
public readonly status = 401;
23-
public readonly data?: any;
3+
// Get base API URL
4+
const api = process.env.REACT_APP_API_BASE_URL || 'http://127.0.0.1:8000/api';
245

25-
constructor(message: string, data?: any) {
26-
super(message);
27-
this.name = 'UnauthorizedError';
28-
this.data = data;
29-
}
30-
}
6+
// Helper function to build URL with query parameters
7+
const buildUrl = (url: string, params?: Record<string, any>): string => {
8+
if (!params) return url;
319

32-
export class NotFoundError extends Error {
33-
public readonly status = 404;
34-
public readonly data?: any;
10+
const searchParams = new URLSearchParams();
11+
Object.entries(params).forEach(([key, value]) => {
12+
if (value !== undefined && value !== null) {
13+
searchParams.append(key, String(value));
14+
}
15+
});
3516

36-
constructor(message: string, data?: any) {
37-
super(message);
38-
this.name = 'NotFoundError';
39-
this.data = data;
40-
}
41-
}
17+
const queryString = searchParams.toString();
18+
return queryString ? `${url}?${queryString}` : url;
19+
};
4220

43-
export class ServerError extends Error {
44-
public readonly status = 500;
45-
public readonly data?: any;
21+
// Fetch with Authentication Headers
22+
const fetchWithAuth = async (url: string, method: string = "GET", body: BodyInit | null = null) => {
23+
const token = localStorage.getItem('token'); // Get the token from localStorage
24+
const authHeaders = headerBuilder(); // Get authentication headers
4625

47-
constructor(message: string, data?: any) {
48-
super(message);
49-
this.name = 'ServerError';
50-
this.data = data;
51-
}
52-
}
53-
54-
export class ApiClient {
55-
private client: AxiosInstance;
56-
57-
constructor(baseURL: string) {
58-
this.client = axios.create({
59-
baseURL,
60-
headers: {
61-
'Content-Type': 'application/json',
62-
},
63-
withCredentials: true, // Include credentials for cross-origin requests if needed
64-
});
65-
66-
// Add request interceptor to handle authentication
67-
this.client.interceptors.request.use(
68-
(config) => {
69-
// Get token from localStorage or other storage mechanism
70-
const token = localStorage.getItem('authToken');
71-
if (token) {
72-
config.headers.Authorization = `Bearer ${token}`;
73-
}
74-
return config;
75-
},
76-
(error) => Promise.reject(error)
77-
);
78-
79-
// Add response interceptor to handle errors
80-
this.client.interceptors.response.use(
81-
(response) => response,
82-
(error: AxiosError) => {
83-
const { response } = error;
84-
85-
if (!response) {
86-
return Promise.reject(new ServerError('Network error or server is not responding'));
87-
} const { status, data } = response;
88-
const message = (data as any)?.detail || (data as any)?.message || 'An error occurred';
89-
90-
switch (status) {
91-
case 400:
92-
return Promise.reject(new BadRequestError(message, data));
93-
case 401:
94-
// Optionally clear auth state here
95-
return Promise.reject(new UnauthorizedError(message, data));
96-
case 404:
97-
return Promise.reject(new NotFoundError(message, data));
98-
case 500:
99-
return Promise.reject(new ServerError(message, data));
100-
default:
101-
return Promise.reject(new Error(`Unexpected error: ${message}`));
102-
}
103-
}
104-
);
105-
}
26+
const headers: Record<string, string> = {
27+
...authHeaders, // Include auth headers from headerBuilder
28+
};
10629

107-
async get<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
108-
const response: AxiosResponse<T> = await this.client.get(path, config);
109-
return response.data;
30+
if (token) {
31+
headers['Authorization'] = `Bearer ${token}`; // Add the token to the Authorization header
11032
}
11133

112-
async post<T>(path: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
113-
const response: AxiosResponse<T> = await this.client.post(path, data, config);
114-
return response.data;
34+
// If body is FormData, do not set Content-Type header
35+
if (body && body instanceof FormData) {
36+
delete headers['Content-Type'];
37+
} else {
38+
headers['Content-Type'] = 'application/json';
39+
body = body ? JSON.stringify(body) : null;
11540
}
11641

117-
async put<T>(path: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
118-
const response: AxiosResponse<T> = await this.client.put(path, data, config);
119-
return response.data;
42+
const options: RequestInit = {
43+
method,
44+
headers,
45+
body: body || undefined,
46+
};
47+
48+
try {
49+
const response = await fetch(`${api}${url}`, options);
50+
console.log('response', response);
51+
52+
if (!response.ok) {
53+
const errorText = await response.text();
54+
throw new Error(errorText || 'Something went wrong');
55+
}
56+
57+
const isJson = response.headers.get('content-type')?.includes('application/json');
58+
return isJson ? await response.json() : null;
59+
} catch (error) {
60+
console.error('API Error:', (error as Error).message);
61+
throw error;
12062
}
121-
122-
async delete<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
123-
const response: AxiosResponse<T> = await this.client.delete(path, config);
124-
return response.data;
125-
}
126-
127-
async patch<T>(path: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
128-
const response: AxiosResponse<T> = await this.client.patch(path, data, config);
129-
return response.data;
63+
};
64+
65+
// Vanilla Fetch without Auth for Login
66+
const fetchWithoutAuth = async (url: string, method: string = "POST", body: BodyInit | null = null) => {
67+
const headers: Record<string, string> = {
68+
'Content-Type': 'application/json',
69+
};
70+
71+
const options: RequestInit = {
72+
method,
73+
headers,
74+
body: body ? JSON.stringify(body) : undefined,
75+
};
76+
77+
try {
78+
const response = await fetch(`${api}${url}`, options);
79+
80+
if (!response.ok) {
81+
const errorText = await response.text();
82+
throw new Error(errorText || 'Login failed');
83+
}
84+
85+
const isJson = response.headers.get('content-type')?.includes('application/json');
86+
return isJson ? await response.json() : null;
87+
} catch (error) {
88+
console.error('Login Error:', (error as Error).message);
89+
throw error;
13090
}
131-
}
132-
133-
// Create and export a default API client instance
134-
const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || 'http://127.0.0.1:8000/api';
135-
export const apiClient = new ApiClient(API_BASE_URL);
91+
};
92+
93+
// Authenticated requests (with token) and login (without token)
94+
export const apiClient = {
95+
get: (url: string, config?: { params?: Record<string, any> }) => {
96+
const finalUrl = buildUrl(url, config?.params);
97+
return fetchWithAuth(finalUrl, 'GET');
98+
},
99+
post: (url: string, body?: any) => fetchWithAuth(url, 'POST', body),
100+
put: (url: string, body?: any) => fetchWithAuth(url, 'PUT', body),
101+
delete: (url: string) => fetchWithAuth(url, 'DELETE'),
102+
upload: (url: string, formData: FormData) => fetchWithAuth(url, 'POST', formData),
103+
login: (url: string, body?: any) => fetchWithoutAuth(url, 'POST', body), // For login without auth
104+
};

0 commit comments

Comments
 (0)