Skip to content

Commit 1fc0d78

Browse files
committed
Split grpc connection out into node and web impls
1 parent f7591d6 commit 1fc0d78

File tree

8 files changed

+4732
-2506
lines changed

8 files changed

+4732
-2506
lines changed

package-lock.json

Lines changed: 4141 additions & 2224 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"nice-grpc": "^2.1.11",
5959
"nice-grpc-client-middleware-retry": "^3.1.10",
6060
"nice-grpc-common": "^2.0.2",
61+
"nice-grpc-web": "^3.3.7",
6162
"uuid": "^9.0.1"
6263
},
6364
"devDependencies": {
@@ -93,7 +94,7 @@
9394
"ts-node": "^10.9.2",
9495
"ts-proto": "^1.163.0",
9596
"tsup": "^8.0.2",
96-
"typedoc": "^0.25.12",
97+
"typedoc": "^0.26.0",
9798
"typedoc-plugin-extras": "^3.0.0",
9899
"typescript": "^5.3.3"
99100
},

src/collections/types/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ export type {
1010
IsPrimitiveField,
1111
IsWeaviateField,
1212
NestedKeys,
13-
NonRefKeys,
1413
NonReferenceInputs,
14+
NonRefKeys,
1515
PrimitiveKeys,
1616
QueryNested,
1717
QueryProperty,
1818
QueryReference,
19-
RefKeys,
2019
ReferenceInput,
2120
ReferenceInputs,
21+
RefKeys,
2222
} from './internal.js';
2323
export * from './query.js';
2424

src/connection/grpc.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { isAbortError } from 'abort-controller-x';
22

33
import ConnectionGQL from './gql.js';
4-
import { InternalConnectionParams } from './http.js';
4+
import { InternalConnectionParams } from './transports/http.js';
55

66
import { ConsistencyLevel } from '../data/index.js';
77

src/connection/http.ts

Lines changed: 3 additions & 278 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,8 @@
1-
import { isAbortError } from 'abort-controller-x';
2-
import { Agent } from 'http';
3-
41
import OpenidConfigurationGetter from '../misc/openidConfigurationGetter.js';
52

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';
546

557
export interface ConnectionDetails {
568
host: string;
@@ -200,230 +152,3 @@ export default class ConnectionREST {
200152
}
201153

202154
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

Comments
 (0)