From 0abc234936a9a3a18734f41c507cb7fe8cf3a796 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Tue, 1 Apr 2025 13:45:45 +0100 Subject: [PATCH 1/2] Use REST instead of gRPC for `tenants.getByName` when available due to filtering issues with rbac --- src/collections/tenants/index.ts | 27 ++++++++++++++------- src/collections/tenants/integration.test.ts | 21 +++------------- src/utils/dbVersion.ts | 9 ++++++- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/collections/tenants/index.ts b/src/collections/tenants/index.ts index f72c6959..320f166d 100644 --- a/src/collections/tenants/index.ts +++ b/src/collections/tenants/index.ts @@ -1,5 +1,5 @@ import { ConnectionGRPC } from '../../connection/index.js'; -import { WeaviateUnsupportedFeatureError } from '../../errors.js'; +import { WeaviateUnexpectedStatusCodeError, WeaviateUnsupportedFeatureError } from '../../errors.js'; import { Tenant as TenantREST } from '../../openapi/types.js'; import { TenantsCreator, TenantsDeleter, TenantsGetter, TenantsUpdater } from '../../schema/index.js'; import { DbVersionSupport } from '../../utils/dbVersion.js'; @@ -17,12 +17,10 @@ const parseValueOrValueArray = (value: V | V[]) => (Array.isArray(value) ? va const parseStringOrTenant = (tenant: string | T) => typeof tenant === 'string' ? tenant : tenant.name; -const parseTenantREST = (tenant: TenantREST): Tenant => { - return { - name: tenant.name!, - activityStatus: Deserialize.activityStatusREST(tenant.activityStatus), - }; -}; +const parseTenantREST = (tenant: TenantREST): Tenant => ({ + name: tenant.name!, + activityStatus: Deserialize.activityStatusREST(tenant.activityStatus), +}); const tenants = ( connection: ConnectionGRPC, @@ -53,9 +51,20 @@ const tenants = ( return check.supports ? getGRPC() : getREST(); }, getByNames: (tenants: (string | T)[]) => getGRPC(tenants.map(parseStringOrTenant)), - getByName: (tenant: string | T) => { + getByName: async (tenant: string | T) => { const tenantName = parseStringOrTenant(tenant); - return getGRPC([tenantName]).then((tenants) => tenants[tenantName] || null); + if (await dbVersionSupport.supportsTenantGetRESTMethod().then((check) => !check.supports)) { + return getGRPC([tenantName]).then((tenants) => tenants[tenantName] ?? null); + } + return connection + .get(`/schema/${collection}/tenants/${tenantName}`) + .then(parseTenantREST) + .catch((err) => { + if (err instanceof WeaviateUnexpectedStatusCodeError && err.code === 404) { + return null; + } + throw err; + }); }, remove: (tenants: string | T | (string | T)[]) => new TenantsDeleter( diff --git a/src/collections/tenants/integration.test.ts b/src/collections/tenants/integration.test.ts index b574759a..af3c6c95 100644 --- a/src/collections/tenants/integration.test.ts +++ b/src/collections/tenants/integration.test.ts @@ -84,34 +84,19 @@ describe('Testing of the collection.tenants methods', () => { describe('getByName and getByNames', () => { it('should be able to get a tenant by name string', async () => { - const query = () => collection.tenants.getByName('hot'); - if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { - await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); - return; - } - const result = await query(); + const result = await collection.tenants.getByName('hot'); expect(result).toHaveProperty('name', 'hot'); expect(result).toHaveProperty('activityStatus', 'ACTIVE'); }); it('should be able to get a tenant by tenant object', async () => { - const query = () => collection.tenants.getByName({ name: 'hot' }); - if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { - await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); - return; - } - const result = await query(); + const result = await collection.tenants.getByName({ name: 'hot' }); expect(result).toHaveProperty('name', 'hot'); expect(result).toHaveProperty('activityStatus', 'ACTIVE'); }); it('should fail to get a non-existing tenant', async () => { - const query = () => collection.tenants.getByName('non-existing'); - if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { - await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); - return; - } - const result = await query(); + const result = await collection.tenants.getByName('non-existing'); expect(result).toBeNull(); }); diff --git a/src/utils/dbVersion.ts b/src/utils/dbVersion.ts index d5265095..a945e414 100644 --- a/src/utils/dbVersion.ts +++ b/src/utils/dbVersion.ts @@ -151,11 +151,18 @@ export class DbVersionSupport { return { version: version, supports: version.isAtLeast(1, 25, 0), - message: this.errorMessage('Tenants get method', version.show(), '1.25.0'), + message: this.errorMessage('Tenants get method over gRPC', version.show(), '1.25.0'), }; }); }; + supportsTenantGetRESTMethod = () => + this.dbVersionProvider.getVersion().then((version) => ({ + version: version, + supports: version.isAtLeast(1, 28, 0), + message: this.errorMessage('Tenant get method over REST', version.show(), '1.28.0'), + })); + supportsDynamicVectorIndex = () => { return this.dbVersionProvider.getVersion().then((version) => { return { From 1d62717659c941fd95d7fcd9ae1ed62fbfa5253a Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Tue, 1 Apr 2025 14:14:59 +0100 Subject: [PATCH 2/2] Add back tests of unsupported error throwing for <1.25 --- src/collections/tenants/integration.test.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/collections/tenants/integration.test.ts b/src/collections/tenants/integration.test.ts index af3c6c95..b574759a 100644 --- a/src/collections/tenants/integration.test.ts +++ b/src/collections/tenants/integration.test.ts @@ -84,19 +84,34 @@ describe('Testing of the collection.tenants methods', () => { describe('getByName and getByNames', () => { it('should be able to get a tenant by name string', async () => { - const result = await collection.tenants.getByName('hot'); + const query = () => collection.tenants.getByName('hot'); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const result = await query(); expect(result).toHaveProperty('name', 'hot'); expect(result).toHaveProperty('activityStatus', 'ACTIVE'); }); it('should be able to get a tenant by tenant object', async () => { - const result = await collection.tenants.getByName({ name: 'hot' }); + const query = () => collection.tenants.getByName({ name: 'hot' }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const result = await query(); expect(result).toHaveProperty('name', 'hot'); expect(result).toHaveProperty('activityStatus', 'ACTIVE'); }); it('should fail to get a non-existing tenant', async () => { - const result = await collection.tenants.getByName('non-existing'); + const query = () => collection.tenants.getByName('non-existing'); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const result = await query(); expect(result).toBeNull(); });