|
1 | | -import { isAbortError } from 'abort-controller-x'; |
2 | | -import { Agent } from 'http'; |
3 | | - |
4 | 1 | import OpenidConfigurationGetter from '../misc/openidConfigurationGetter.js'; |
5 | 2 |
|
6 | | -import { |
7 | | - WeaviateInsufficientPermissionsError, |
8 | | - WeaviateInvalidInputError, |
9 | | - WeaviateRequestTimeoutError, |
10 | | - WeaviateUnauthenticatedError, |
11 | | - WeaviateUnexpectedStatusCodeError, |
12 | | -} from '../errors.js'; |
13 | | -import { |
14 | | - ApiKey, |
15 | | - AuthAccessTokenCredentials, |
16 | | - AuthClientCredentials, |
17 | | - AuthUserPasswordCredentials, |
18 | | - OidcAuthenticator, |
19 | | -} from './auth.js'; |
20 | | - |
21 | | -/** |
22 | | - * You can only specify the gRPC proxy URL at this point in time. This is because ProxiesParams should be used to define tunnelling proxies |
23 | | - * and Weaviate does not support tunnelling proxies over HTTP/1.1 at this time. |
24 | | - * |
25 | | - * To use a forwarding proxy you should instead specify its URL as if it were the Weaviate instance itself. |
26 | | - */ |
27 | | -export type ProxiesParams = { |
28 | | - // http?: string; |
29 | | - // https?: string; |
30 | | - grpc?: string; |
31 | | -}; |
32 | | - |
33 | | -export type TimeoutParams = { |
34 | | - /** Define the configured timeout when querying data from Weaviate */ |
35 | | - query?: number; |
36 | | - /** Define the configured timeout when mutating data to Weaviate */ |
37 | | - insert?: number; |
38 | | - /** Define the configured timeout when initially connecting to Weaviate */ |
39 | | - init?: number; |
40 | | -}; |
41 | | - |
42 | | -export type InternalConnectionParams = { |
43 | | - authClientSecret?: AuthClientCredentials | AuthAccessTokenCredentials | AuthUserPasswordCredentials; |
44 | | - apiKey?: ApiKey; |
45 | | - host: string; |
46 | | - scheme?: string; |
47 | | - headers?: HeadersInit; |
48 | | - // http1Agent?: Agent; |
49 | | - grpcProxyUrl?: string; |
50 | | - agent?: Agent; |
51 | | - timeout?: TimeoutParams; |
52 | | - skipInitChecks?: boolean; |
53 | | -}; |
| 3 | +import { WeaviateInvalidInputError } from '../errors.js'; |
| 4 | +import { OidcAuthenticator } from './auth.js'; |
| 5 | +import { HttpClient, InternalConnectionParams, httpClient } from './transports/http.js'; |
54 | 6 |
|
55 | 7 | export interface ConnectionDetails { |
56 | 8 | host: string; |
@@ -200,230 +152,3 @@ export default class ConnectionREST { |
200 | 152 | } |
201 | 153 |
|
202 | 154 | export * from './auth.js'; |
203 | | - |
204 | | -export interface HttpClient { |
205 | | - close: () => void; |
206 | | - patch: (path: string, payload: any, bearerToken?: string) => any; |
207 | | - head: (path: string, payload: any, bearerToken?: string) => any; |
208 | | - post: <B, T>( |
209 | | - path: string, |
210 | | - payload: B, |
211 | | - expectReturnContent: boolean, |
212 | | - bearerToken: string |
213 | | - ) => Promise<T | undefined>; |
214 | | - get: <T>(path: string, expectReturnContent?: boolean, bearerToken?: string) => Promise<T>; |
215 | | - externalPost: (externalUrl: string, body: any, contentType: any) => any; |
216 | | - getRaw: (path: string, bearerToken?: string) => any; |
217 | | - delete: (path: string, payload: any, expectReturnContent?: boolean, bearerToken?: string) => any; |
218 | | - put: (path: string, payload: any, expectReturnContent?: boolean, bearerToken?: string) => any; |
219 | | - externalGet: (externalUrl: string) => Promise<any>; |
220 | | -} |
221 | | - |
222 | | -const fetchWithTimeout = ( |
223 | | - input: RequestInfo | URL, |
224 | | - timeout: number, |
225 | | - init?: RequestInit | undefined |
226 | | -): Promise<Response> => { |
227 | | - const controller = new AbortController(); |
228 | | - // Set a timeout to abort the request |
229 | | - const timeoutId = setTimeout(() => controller.abort(), timeout * 1000); |
230 | | - return fetch(input, { ...init, signal: controller.signal }) |
231 | | - .catch((error) => { |
232 | | - if (isAbortError(error)) { |
233 | | - throw new WeaviateRequestTimeoutError(`Request timed out after ${timeout}ms`); |
234 | | - } |
235 | | - throw error; // For other errors, rethrow them |
236 | | - }) |
237 | | - .finally(() => clearTimeout(timeoutId)); |
238 | | -}; |
239 | | - |
240 | | -export const httpClient = (config: InternalConnectionParams): HttpClient => { |
241 | | - const version = '/v1'; |
242 | | - const baseUri = `${config.host}${version}`; |
243 | | - const url = makeUrl(baseUri); |
244 | | - |
245 | | - return { |
246 | | - close: () => config.agent?.destroy(), |
247 | | - post: <B, T>( |
248 | | - path: string, |
249 | | - payload: B, |
250 | | - expectReturnContent: boolean, |
251 | | - bearerToken: string |
252 | | - ): Promise<T | undefined> => { |
253 | | - const request = { |
254 | | - method: 'POST', |
255 | | - headers: { |
256 | | - ...config.headers, |
257 | | - 'content-type': 'application/json', |
258 | | - ...getAuthHeaders(config, bearerToken), |
259 | | - }, |
260 | | - body: JSON.stringify(payload), |
261 | | - agent: config.agent, |
262 | | - }; |
263 | | - return fetchWithTimeout(url(path), config.timeout?.insert || 90, request).then( |
264 | | - checkStatus<T>(expectReturnContent) |
265 | | - ); |
266 | | - }, |
267 | | - put: <B, T>( |
268 | | - path: string, |
269 | | - payload: B, |
270 | | - expectReturnContent = true, |
271 | | - bearerToken = '' |
272 | | - ): Promise<T | undefined> => { |
273 | | - const request = { |
274 | | - method: 'PUT', |
275 | | - headers: { |
276 | | - ...config.headers, |
277 | | - 'content-type': 'application/json', |
278 | | - ...getAuthHeaders(config, bearerToken), |
279 | | - }, |
280 | | - body: JSON.stringify(payload), |
281 | | - agent: config.agent, |
282 | | - }; |
283 | | - return fetchWithTimeout(url(path), config.timeout?.insert || 90, request).then( |
284 | | - checkStatus<T>(expectReturnContent) |
285 | | - ); |
286 | | - }, |
287 | | - patch: <B, T>(path: string, payload: B, bearerToken = ''): Promise<T | undefined> => { |
288 | | - const request = { |
289 | | - method: 'PATCH', |
290 | | - headers: { |
291 | | - ...config.headers, |
292 | | - 'content-type': 'application/json', |
293 | | - ...getAuthHeaders(config, bearerToken), |
294 | | - }, |
295 | | - body: JSON.stringify(payload), |
296 | | - agent: config.agent, |
297 | | - }; |
298 | | - return fetchWithTimeout(url(path), config.timeout?.insert || 90, request).then(checkStatus<T>(false)); |
299 | | - }, |
300 | | - delete: <B>(path: string, payload: B | null = null, expectReturnContent = false, bearerToken = '') => { |
301 | | - const request = { |
302 | | - method: 'DELETE', |
303 | | - headers: { |
304 | | - ...config.headers, |
305 | | - 'content-type': 'application/json', |
306 | | - ...getAuthHeaders(config, bearerToken), |
307 | | - }, |
308 | | - body: payload ? JSON.stringify(payload) : undefined, |
309 | | - agent: config.agent, |
310 | | - }; |
311 | | - return fetchWithTimeout(url(path), config.timeout?.insert || 90, request).then( |
312 | | - checkStatus<undefined>(expectReturnContent) |
313 | | - ); |
314 | | - }, |
315 | | - head: <B>(path: string, payload: B | null = null, bearerToken = '') => { |
316 | | - const request = { |
317 | | - method: 'HEAD', |
318 | | - headers: { |
319 | | - ...config.headers, |
320 | | - 'content-type': 'application/json', |
321 | | - ...getAuthHeaders(config, bearerToken), |
322 | | - }, |
323 | | - body: payload ? JSON.stringify(payload) : undefined, |
324 | | - agent: config.agent, |
325 | | - }; |
326 | | - return fetchWithTimeout(url(path), config.timeout?.query || 30, request).then( |
327 | | - handleHeadResponse<undefined>(false) |
328 | | - ); |
329 | | - }, |
330 | | - get: <T>(path: string, expectReturnContent = true, bearerToken = ''): Promise<T> => { |
331 | | - const request = { |
332 | | - method: 'GET', |
333 | | - headers: { |
334 | | - ...config.headers, |
335 | | - ...getAuthHeaders(config, bearerToken), |
336 | | - }, |
337 | | - agent: config.agent, |
338 | | - }; |
339 | | - return fetchWithTimeout(url(path), config.timeout?.query || 30, request).then( |
340 | | - checkStatus<any>(expectReturnContent) |
341 | | - ); |
342 | | - }, |
343 | | - getRaw: (path: string, bearerToken = '') => { |
344 | | - // getRaw does not handle the status leaving this to the caller |
345 | | - const request = { |
346 | | - method: 'GET', |
347 | | - headers: { |
348 | | - ...config.headers, |
349 | | - ...getAuthHeaders(config, bearerToken), |
350 | | - }, |
351 | | - agent: config.agent, |
352 | | - }; |
353 | | - return fetchWithTimeout(url(path), config.timeout?.query || 30, request); |
354 | | - }, |
355 | | - externalGet: (externalUrl: string) => { |
356 | | - return fetch(externalUrl, { |
357 | | - method: 'GET', |
358 | | - headers: { |
359 | | - ...config.headers, |
360 | | - }, |
361 | | - }).then(checkStatus<any>(true)); |
362 | | - }, |
363 | | - externalPost: (externalUrl: string, body: any, contentType: any) => { |
364 | | - if (contentType == undefined || contentType == '') { |
365 | | - contentType = 'application/json'; |
366 | | - } |
367 | | - const request = { |
368 | | - body: undefined, |
369 | | - method: 'POST', |
370 | | - headers: { |
371 | | - ...config.headers, |
372 | | - 'content-type': contentType, |
373 | | - }, |
374 | | - }; |
375 | | - if (body != null) { |
376 | | - request.body = body; |
377 | | - } |
378 | | - return fetch(externalUrl, request).then(checkStatus<any>(true)); |
379 | | - }, |
380 | | - }; |
381 | | -}; |
382 | | - |
383 | | -const makeUrl = (basePath: string) => (path: string) => basePath + path; |
384 | | - |
385 | | -const checkStatus = |
386 | | - <T>(expectResponseBody: boolean) => |
387 | | - (res: Response) => { |
388 | | - if (res.status >= 400) { |
389 | | - return res.text().then((errText: string) => { |
390 | | - let err: string; |
391 | | - try { |
392 | | - // in case of invalid json response (like empty string) |
393 | | - err = JSON.stringify(JSON.parse(errText)); |
394 | | - } catch (e) { |
395 | | - err = errText; |
396 | | - } |
397 | | - if (res.status === 401) { |
398 | | - return Promise.reject(new WeaviateUnauthenticatedError(err)); |
399 | | - } else if (res.status === 403) { |
400 | | - return Promise.reject(new WeaviateInsufficientPermissionsError(403, err)); |
401 | | - } else { |
402 | | - return Promise.reject(new WeaviateUnexpectedStatusCodeError(res.status, err)); |
403 | | - } |
404 | | - }); |
405 | | - } |
406 | | - if (expectResponseBody) { |
407 | | - return res.json() as Promise<T>; |
408 | | - } |
409 | | - return Promise.resolve(undefined); |
410 | | - }; |
411 | | - |
412 | | -const handleHeadResponse = |
413 | | - <T>(expectResponseBody: boolean) => |
414 | | - (res: Response) => { |
415 | | - if (res.status == 200 || res.status == 204 || res.status == 404) { |
416 | | - return Promise.resolve(res.status == 200 || res.status == 204); |
417 | | - } |
418 | | - return checkStatus<T>(expectResponseBody)(res); |
419 | | - }; |
420 | | - |
421 | | -const getAuthHeaders = (config: InternalConnectionParams, bearerToken: string) => |
422 | | - bearerToken |
423 | | - ? { |
424 | | - Authorization: `Bearer ${bearerToken}`, |
425 | | - 'X-Weaviate-Cluster-Url': config.host, |
426 | | - // keeping for backwards compatibility for older clusters for now. On newer clusters, Embedding Service reuses Authorization header. |
427 | | - 'X-Weaviate-Api-Key': bearerToken, |
428 | | - } |
429 | | - : undefined; |
0 commit comments