weaviate-client
Version:
JS/TS client for Weaviate
281 lines (280 loc) • 13.6 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { isAbortError } from 'abort-controller-x';
import OpenidConfigurationGetter from '../misc/openidConfigurationGetter.js';
import { WeaviateInsufficientPermissionsError, WeaviateInvalidInputError, WeaviateRequestTimeoutError, WeaviateUnauthenticatedError, WeaviateUnexpectedStatusCodeError, } from '../errors.js';
import { OidcAuthenticator, } from './auth.js';
export default class ConnectionREST {
constructor(params) {
this.postReturn = (path, payload) => {
if (this.authEnabled) {
return this.login().then((token) => this.http.post(path, payload, true, token));
}
return this.http.post(path, payload, true, '');
};
this.postEmpty = (path, payload) => {
if (this.authEnabled) {
return this.login().then((token) => this.http.post(path, payload, false, token));
}
return this.http.post(path, payload, false, '');
};
this.put = (path, payload, expectReturnContent = true) => {
if (this.authEnabled) {
return this.login().then((token) => this.http.put(path, payload, expectReturnContent, token));
}
return this.http.put(path, payload, expectReturnContent);
};
this.patch = (path, payload) => {
if (this.authEnabled) {
return this.login().then((token) => this.http.patch(path, payload, token));
}
return this.http.patch(path, payload);
};
this.delete = (path, payload, expectReturnContent = false) => {
if (this.authEnabled) {
return this.login().then((token) => this.http.delete(path, payload, expectReturnContent, token));
}
return this.http.delete(path, payload, expectReturnContent);
};
this.head = (path, payload) => {
if (this.authEnabled) {
return this.login().then((token) => this.http.head(path, payload, token));
}
return this.http.head(path, payload);
};
this.get = (path, expectReturnContent = true) => {
if (this.authEnabled) {
return this.login().then((token) => this.http.get(path, expectReturnContent, token));
}
return this.http.get(path, expectReturnContent);
};
this.login = () => __awaiter(this, void 0, void 0, function* () {
if (this.apiKey) {
return this.apiKey;
}
if (!this.oidcAuth) {
return '';
}
const localConfig = yield new OpenidConfigurationGetter(this.http).do();
if (localConfig === undefined) {
console.warn('client is configured for authentication, but server is not');
return '';
}
if (Date.now() >= this.oidcAuth.getExpiresAt()) {
yield this.oidcAuth.refresh(localConfig);
}
return this.oidcAuth.getAccessToken();
});
this.getDetails = () => __awaiter(this, void 0, void 0, function* () {
return ({
host: new URL(this.host).host, // removes default port
bearerToken: this.authEnabled ? yield this.login().then((token) => `Bearer ${token}`) : undefined,
headers: this.headers,
});
});
params = this.sanitizeParams(params);
this.host = params.host;
this.headers = params.headers;
this.http = httpClient(params);
this.authEnabled = this.parseAuthParams(params);
}
parseAuthParams(params) {
var _a;
if (params.authClientSecret && params.apiKey) {
throw new WeaviateInvalidInputError('must provide one of authClientSecret (OIDC) or apiKey, cannot provide both');
}
if (params.authClientSecret) {
this.oidcAuth = new OidcAuthenticator(this.http, params.authClientSecret);
return true;
}
if (params.apiKey) {
this.apiKey = (_a = params.apiKey) === null || _a === void 0 ? void 0 : _a.apiKey;
return true;
}
return false;
}
sanitizeParams(params) {
// Remove trailing slashes from the host
while (params.host.endsWith('/')) {
params.host = params.host.slice(0, -1);
}
const protocolPattern = /^(https?|ftp|file)(?::\/\/)/;
const extractedSchemeMatch = params.host.match(protocolPattern);
// Check for the existence of scheme in params
if (params.scheme) {
// If the host contains a scheme different than provided scheme, replace it and throw a warning
if (extractedSchemeMatch && extractedSchemeMatch[1] !== `${params.scheme}`) {
throw new WeaviateInvalidInputError(`The host contains a different protocol than specified in the scheme (scheme: ${params.scheme} != host: ${extractedSchemeMatch[1]})`);
}
else if (!extractedSchemeMatch) {
// If no scheme in the host, simply prefix with the provided scheme
params.host = `${params.scheme}://${params.host}`;
}
// If there's no scheme in params, ensure the host starts with a recognized protocol
}
else if (!extractedSchemeMatch) {
throw new WeaviateInvalidInputError('The host must start with a recognized protocol (e.g., http or https) if no scheme is provided.');
}
return params;
}
}
export * from './auth.js';
const fetchWithTimeout = (input, timeout, init) => {
const controller = new AbortController();
// Set a timeout to abort the request
const timeoutId = setTimeout(() => controller.abort(), timeout * 1000);
return fetch(input, Object.assign(Object.assign({}, init), { signal: controller.signal }))
.catch((error) => {
if (isAbortError(error)) {
throw new WeaviateRequestTimeoutError(`Request timed out after ${timeout}ms`);
}
throw error; // For other errors, rethrow them
})
.finally(() => clearTimeout(timeoutId));
};
export const httpClient = (config) => {
const version = '/v1';
const baseUri = `${config.host}${version}`;
const url = makeUrl(baseUri);
return {
close: () => { var _a; return (_a = config.agent) === null || _a === void 0 ? void 0 : _a.destroy(); },
post: (path, payload, expectReturnContent, bearerToken) => {
var _a;
const request = {
method: 'POST',
headers: Object.assign(Object.assign(Object.assign({}, config.headers), { 'content-type': 'application/json' }), getAuthHeaders(config, bearerToken)),
body: JSON.stringify(payload),
agent: config.agent,
};
return fetchWithTimeout(url(path), ((_a = config.timeout) === null || _a === void 0 ? void 0 : _a.insert) || 90, request).then(checkStatus(expectReturnContent));
},
put: (path, payload, expectReturnContent = true, bearerToken = '') => {
var _a;
const request = {
method: 'PUT',
headers: Object.assign(Object.assign(Object.assign({}, config.headers), { 'content-type': 'application/json' }), getAuthHeaders(config, bearerToken)),
body: JSON.stringify(payload),
agent: config.agent,
};
return fetchWithTimeout(url(path), ((_a = config.timeout) === null || _a === void 0 ? void 0 : _a.insert) || 90, request).then(checkStatus(expectReturnContent));
},
patch: (path, payload, bearerToken = '') => {
var _a;
const request = {
method: 'PATCH',
headers: Object.assign(Object.assign(Object.assign({}, config.headers), { 'content-type': 'application/json' }), getAuthHeaders(config, bearerToken)),
body: JSON.stringify(payload),
agent: config.agent,
};
return fetchWithTimeout(url(path), ((_a = config.timeout) === null || _a === void 0 ? void 0 : _a.insert) || 90, request).then(checkStatus(false));
},
delete: (path, payload = null, expectReturnContent = false, bearerToken = '') => {
var _a;
const request = {
method: 'DELETE',
headers: Object.assign(Object.assign(Object.assign({}, config.headers), { 'content-type': 'application/json' }), getAuthHeaders(config, bearerToken)),
body: payload ? JSON.stringify(payload) : undefined,
agent: config.agent,
};
return fetchWithTimeout(url(path), ((_a = config.timeout) === null || _a === void 0 ? void 0 : _a.insert) || 90, request).then(checkStatus(expectReturnContent));
},
head: (path, payload = null, bearerToken = '') => {
var _a;
const request = {
method: 'HEAD',
headers: Object.assign(Object.assign(Object.assign({}, config.headers), { 'content-type': 'application/json' }), getAuthHeaders(config, bearerToken)),
body: payload ? JSON.stringify(payload) : undefined,
agent: config.agent,
};
return fetchWithTimeout(url(path), ((_a = config.timeout) === null || _a === void 0 ? void 0 : _a.query) || 30, request).then(handleHeadResponse(false));
},
get: (path, expectReturnContent = true, bearerToken = '') => {
var _a;
const request = {
method: 'GET',
headers: Object.assign(Object.assign({}, config.headers), getAuthHeaders(config, bearerToken)),
agent: config.agent,
};
return fetchWithTimeout(url(path), ((_a = config.timeout) === null || _a === void 0 ? void 0 : _a.query) || 30, request).then(checkStatus(expectReturnContent));
},
getRaw: (path, bearerToken = '') => {
var _a;
// getRaw does not handle the status leaving this to the caller
const request = {
method: 'GET',
headers: Object.assign(Object.assign({}, config.headers), getAuthHeaders(config, bearerToken)),
agent: config.agent,
};
return fetchWithTimeout(url(path), ((_a = config.timeout) === null || _a === void 0 ? void 0 : _a.query) || 30, request);
},
externalGet: (externalUrl) => {
return fetch(externalUrl, {
method: 'GET',
headers: Object.assign({}, config.headers),
}).then(checkStatus(true));
},
externalPost: (externalUrl, body, contentType) => {
if (contentType == undefined || contentType == '') {
contentType = 'application/json';
}
const request = {
body: undefined,
method: 'POST',
headers: Object.assign(Object.assign({}, config.headers), { 'content-type': contentType }),
};
if (body != null) {
request.body = body;
}
return fetch(externalUrl, request).then(checkStatus(true));
},
};
};
const makeUrl = (basePath) => (path) => basePath + path;
const checkStatus = (expectResponseBody) => (res) => {
if (res.status >= 400) {
return res.text().then((errText) => {
let err;
try {
// in case of invalid json response (like empty string)
err = JSON.stringify(JSON.parse(errText));
}
catch (e) {
err = errText;
}
if (res.status === 401) {
return Promise.reject(new WeaviateUnauthenticatedError(err));
}
else if (res.status === 403) {
return Promise.reject(new WeaviateInsufficientPermissionsError(403, err));
}
else {
return Promise.reject(new WeaviateUnexpectedStatusCodeError(res.status, err));
}
});
}
if (expectResponseBody) {
return res.json();
}
return Promise.resolve(undefined);
};
const handleHeadResponse = (expectResponseBody) => (res) => {
if (res.status == 200 || res.status == 204 || res.status == 404) {
return Promise.resolve(res.status == 200 || res.status == 204);
}
return checkStatus(expectResponseBody)(res);
};
const getAuthHeaders = (config, bearerToken) => bearerToken
? {
Authorization: `Bearer ${bearerToken}`,
'X-Weaviate-Cluster-Url': config.host,
// keeping for backwards compatibility for older clusters for now. On newer clusters, Embedding Service reuses Authorization header.
'X-Weaviate-Api-Key': bearerToken,
}
: undefined;