diff --git a/.changeset/fresh-trains-wave.md b/.changeset/fresh-trains-wave.md new file mode 100644 index 0000000..4df1fb3 --- /dev/null +++ b/.changeset/fresh-trains-wave.md @@ -0,0 +1,5 @@ +--- +'@totalsoft/multitenancy-core': minor +--- + +Get all tenants/configurations diff --git a/packages/multitenancy-core/README.md b/packages/multitenancy-core/README.md index 7767f3f..720ca16 100644 --- a/packages/multitenancy-core/README.md +++ b/packages/multitenancy-core/README.md @@ -54,6 +54,22 @@ const { tenantConfiguration } = require("@totalsoft/multitenancy-core") //} const tenantSpecifficValue = tenantConfiguration.getValue('3c841325-eccc-4670-a577-09546df7b1fc', "tenantProp") ``` +The `getAll` function retrieves all tenant configurations from environment variables: + +```javascript +const { tenantConfiguration } = require("@totalsoft/multitenancy-core") + +// process.env = { +// IS_MULTITENANT: 'true', +// MultiTenancy__Tenants__Tenant1__TenantId: '3c841325-eccc-4670-a577-09546df7b1fc', +// MultiTenancy__Tenants__Tenant1__Name: 'tenant 1 name', +// MultiTenancy__Tenants__Tenant1__Enabled: 'true', +// MultiTenancy__Tenants__Tenant2__TenantId: '9c841325-eccc-4670-a577-09546df7b1fc', +// MultiTenancy__Tenants__Tenant2__Name: 'tenant 2 name', +// MultiTenancy__Tenants__Tenant2__Enabled: 'false', +//} +const tenants = tenantConfiguration.getAll(); +``` The `getConnectionInfo` extension reads the configuration to return a connection information object: ```javascript const { tenantConfiguration } = require("@totalsoft/multitenancy-core") @@ -92,20 +108,40 @@ const { tenantService } = require("@totalsoft/multitenancy-core") // IS_MULTITENANT: 'true', // MultiTenancy__Tenants__Tenant1__TenantId: tenantId, // MultiTenancy__Tenants__Tenant1__Name: 'Tenant 1 name' -} +//} const res = await tenantService.getTenantFromId(tenantId) ``` +### tenantService.getAll + +The `getAll` function retrieves all enabled tenants: + +```javascript +const { tenantService } = require("@totalsoft/multitenancy-core") + +// process.env = { +// IS_MULTITENANT: 'true', +// MultiTenancy__Tenants__Tenant1__TenantId: '3c841325-eccc-4670-a577-09546df7b1fc', +// MultiTenancy__Tenants__Tenant1__Name: 'tenant 1 name', +// MultiTenancy__Tenants__Tenant1__Enabled: 'true', +// MultiTenancy__Tenants__Tenant2__TenantId: '9c841325-eccc-4670-a577-09546df7b1fc', +// MultiTenancy__Tenants__Tenant2__Name: 'tenant 2 name', +// MultiTenancy__Tenants__Tenant2__Enabled: 'false', +//} +const tenants = await tenantService.getAll(); +``` + The returned `Tenant` object has the following structure: ```typescript export interface Tenant { id: string, code: string, name?: string + enabled: boolean } ``` -The service throws an exception when the tenant with the specified id is not found. +This function is useful when you need to retrieve all enabled tenants at once, for example, when initializing your application or when performing bulk operations on all tenants. ### tenantContextAccessor Allows propagating a tenant context across async/await calls. diff --git a/packages/multitenancy-core/__tests__/tenant-configuration.test.ts b/packages/multitenancy-core/__tests__/tenant-configuration.test.ts index 5c176ba..095a182 100644 --- a/packages/multitenancy-core/__tests__/tenant-configuration.test.ts +++ b/packages/multitenancy-core/__tests__/tenant-configuration.test.ts @@ -153,4 +153,32 @@ describe('logging plugin tests:', () => { expect(connectionInfo.password).toBe(pass) expect(connectionInfo.otherParams).toBe(otherParams) }) + + it('should load all tenant configurations:', () => { + // Arrange + const tenants = [ + { tenantId: '3c841325-eccc-4670-a577-09546df7b1fc', tenantProp: 'tenant 1 speciffic prop', code: 'tenant1' }, + { tenantId: '9a841325-eccc-4670-a577-09546df7b1fc', tenantProp: 'tenant 2 speciffic prop', code: 'tenant2' } + ] + + process.env = { + IS_MULTITENANT: 'true', + ...tenants.reduce( + (env, { tenantId, tenantProp, code }, _) => ({ + ...env, + [`MultiTenancy__Tenants__${code}__TenantId`]: tenantId, + [`MultiTenancy__Tenants__${code}__TenantProp`]: tenantProp + }), + {} + ) + } + + const { tenantConfiguration } = require('../src') + + // Act + const res = tenantConfiguration.getAll() + + // Assert + expect(res).toEqual(tenants) + }) }) diff --git a/packages/multitenancy-core/__tests__/tenant-service.test.ts b/packages/multitenancy-core/__tests__/tenant-service.test.ts index 9e52a8c..502b2ab 100644 --- a/packages/multitenancy-core/__tests__/tenant-service.test.ts +++ b/packages/multitenancy-core/__tests__/tenant-service.test.ts @@ -79,4 +79,57 @@ describe('tenant service tests:', () => { ///Assert await expect(action).rejects.toThrowError('disabled') }) + + it('should load tenants', async () => { + // Arrange + const tenants = [ + { id: '3c841325-eccc-4670-a577-09546df7b1fc', name: 'tenant 1 name', code: 'tenant1', enabled: true }, + { id: '9c841325-eccc-4670-a577-09546df7b1fc', name: 'tenant 2 name', code: 'tenant2', enabled: true } + ] + + process.env = { + IS_MULTITENANT: 'true', + ...tenants.reduce( + (env, { id, name, code }) => ({ + ...env, + [`MultiTenancy__Tenants__${code}__TenantId`]: id, + [`MultiTenancy__Tenants__${code}__Name`]: name + }), + {} + ) + } + + const { tenantService } = require('../src') + + // Act + const res = await tenantService.getAll() + + // Assert + expect(res).toEqual(tenants) + }) + + it('should return an empty array if no tenants are enabled', async () => { + // Arrange + const tenant = { + id: '3c841325-eccc-4670-a577-09546df7b1fc', + name: 'tenant 1 name', + code: 'tenant1', + enabled: 'false' + } + + process.env = { + IS_MULTITENANT: 'true', + [`MultiTenancy__Tenants__${tenant.code}__TenantId`]: tenant.id, + [`MultiTenancy__Tenants__${tenant.code}__Name`]: tenant.name, + [`MultiTenancy__Tenants__${tenant.code}__Enabled`]: tenant.enabled + } + + const { tenantService } = require('../src') + + // Act + const res = await tenantService.getAll() + + // Assert + expect(res).toHaveLength(0) + }) }) diff --git a/packages/multitenancy-core/src/tenantConfiguration.ts b/packages/multitenancy-core/src/tenantConfiguration.ts index 1b3ec47..a95a151 100644 --- a/packages/multitenancy-core/src/tenantConfiguration.ts +++ b/packages/multitenancy-core/src/tenantConfiguration.ts @@ -5,7 +5,7 @@ import objectPath from 'object-path' import humps from 'humps' import debounce from 'debounce' import deepmerge from 'deepmerge' -import { TenantMapByCode, TenantMapById } from './types' +import { TenantMapByCode, TenantMapById, TenantSection } from './types' const { IS_MULTITENANT } = process.env const isMultiTenant = JSON.parse(IS_MULTITENANT || 'false') const debounceTimeoutMs = 5000 @@ -33,6 +33,28 @@ export function getValue(tenantId: string, key?: string) { return tenantValue || defaultValue } +/** + * Retrieves all tenant configurations. + * If multi-tenancy is disabled, an empty array is returned. + * If multi-tenancy is enabled, the function merges the default configuration with each tenant configuration + * and returns an array of merged configurations. + * @returns An array of TenantSection objects representing the merged tenant configurations. + */ +export function getAll(): TenantSection[] { + if (!isMultiTenant) { + return [] + } + + const defaults = _getDefaultsDebounced() + const allTenantsMap = _getTenantsDebounced() + + return Object.entries(allTenantsMap).map(([tid, tenantSection]) => { + return _isObject(defaults) && _isObject(tenantSection) + ? deepmerge(defaults, tenantSection) + : tenantSection || defaults + }) +} + function _getDefaults(): TenantMapByCode { const defaultsPrefix = 'MultiTenancy__Defaults__' const defaults = _loadFromEnv(defaultsPrefix) diff --git a/packages/multitenancy-core/src/tenantService.ts b/packages/multitenancy-core/src/tenantService.ts index bf54055..2ffe1ff 100644 --- a/packages/multitenancy-core/src/tenantService.ts +++ b/packages/multitenancy-core/src/tenantService.ts @@ -23,3 +23,14 @@ export async function getTenantFromId(tenantId: string): Promise } return tenant } + +/** + * Retrieves all enabled tenants. + * @returns A promise that resolves to an array of Tenant objects. + */ +export async function getAll(): Promise { + const configTenants = tenantConfiguration.getAll() + return configTenants + .filter(tenant => tenant.enabled !== 'false') + .map(({ tenantId: id, name, code }) => ({ id, name, code, enabled: true })) +} diff --git a/packages/multitenancy-core/src/types.ts b/packages/multitenancy-core/src/types.ts index a8c676d..60b1b40 100644 --- a/packages/multitenancy-core/src/types.ts +++ b/packages/multitenancy-core/src/types.ts @@ -27,6 +27,7 @@ export interface TenantMapByCode { export interface TenantSection { tenantId: string code: string + [key: string]: any } export interface TenantContext {