diff --git a/src/http/routes/admin/tenants.ts b/src/http/routes/admin/tenants.ts index da95b902a..0fce9dd2a 100644 --- a/src/http/routes/admin/tenants.ts +++ b/src/http/routes/admin/tenants.ts @@ -111,8 +111,8 @@ interface tenantDBInterface { id: string anon_key: string database_url: string - database_pool_url?: string - database_pool_mode?: string + database_pool_url?: string | null + database_pool_mode?: string | null max_connections?: number jwt_secret: string jwks: { keys?: JwksConfigKey[] } | null @@ -495,15 +495,15 @@ export default async function routes(fastify: FastifyInstance) { tenantInfo.feature_s3_protocol = features?.s3Protocol?.enabled } - if (databasePoolUrl) { - tenantInfo.database_pool_url = encrypt(databasePoolUrl) + if (databasePoolUrl !== undefined) { + tenantInfo.database_pool_url = databasePoolUrl === null ? null : encrypt(databasePoolUrl) } if (maxConnections) { tenantInfo.max_connections = Number(maxConnections) } - if (databasePoolMode) { + if (databasePoolMode !== undefined) { tenantInfo.database_pool_mode = databasePoolMode } diff --git a/src/test/admin-tenants.test.ts b/src/test/admin-tenants.test.ts index 33bd5de94..436445f2f 100644 --- a/src/test/admin-tenants.test.ts +++ b/src/test/admin-tenants.test.ts @@ -2,24 +2,35 @@ import * as migrations from '@internal/database/migrations' import { multitenantKnex } from '@internal/database/multitenant-db' import { adminApp } from './common' -describe('admin tenant delete route', () => { - beforeAll(async () => { - await migrations.runMultitenantMigrations() - }) +const adminHeaders = { + apikey: process.env.ADMIN_API_KEYS!, + 'content-type': 'application/json', +} - afterAll(async () => { - await adminApp.close() - await multitenantKnex.destroy() - }) +const baseTenantBody = { + anonKey: 'anon', + databaseUrl: 'postgresql://postgres:postgres@127.0.0.1:5433/postgres', + jwtSecret: 'secret', + serviceKey: 'service', + jwks: null, + fileSizeLimit: 1024, +} + +beforeAll(async () => { + await migrations.runMultitenantMigrations() +}) +afterAll(async () => { + await adminApp.close() + await multitenantKnex.destroy() +}) + +describe('admin tenant delete route', () => { it('accepts an empty json delete request', async () => { const response = await adminApp.inject({ method: 'DELETE', url: '/tenants/abc', - headers: { - apikey: process.env.ADMIN_API_KEYS!, - 'content-type': 'application/json', - }, + headers: adminHeaders, payload: '', }) @@ -27,3 +38,49 @@ describe('admin tenant delete route', () => { expect(response.body).toBe('') }) }) + +describe('admin tenant put route nullable pool fields', () => { + const tenantId = 'put-nullable-pool' + + beforeAll(async () => { + await multitenantKnex('tenants').where('id', tenantId).delete() + }) + + afterAll(async () => { + await multitenantKnex('tenants').where('id', tenantId).delete() + }) + + it('clears database_pool_url and database_pool_mode when put with null', async () => { + const initial = await adminApp.inject({ + method: 'PUT', + url: `/tenants/${tenantId}`, + headers: adminHeaders, + payload: JSON.stringify({ + ...baseTenantBody, + databasePoolUrl: 'postgresql://postgres:postgres@127.0.0.1:6454/postgres', + databasePoolMode: 'transaction', + }), + }) + expect(initial.statusCode).toBe(204) + + const seeded = await multitenantKnex('tenants').first().where('id', tenantId) + expect(seeded?.database_pool_url).toBeTruthy() + expect(seeded?.database_pool_mode).toBe('transaction') + + const cleared = await adminApp.inject({ + method: 'PUT', + url: `/tenants/${tenantId}`, + headers: adminHeaders, + payload: JSON.stringify({ + ...baseTenantBody, + databasePoolUrl: null, + databasePoolMode: null, + }), + }) + expect(cleared.statusCode).toBe(204) + + const after = await multitenantKnex('tenants').first().where('id', tenantId) + expect(after?.database_pool_url).toBeNull() + expect(after?.database_pool_mode).toBeNull() + }) +})