import SDKAdapter from '../../core/adapter.js' import { SDKCollection } from '../../core/collection.js' import { SDKModel } from '../../core/model.js' import { SDKModelSchema } from '../../core/schema.js' import tenantSchema from '../../schemas/tenant.json' assert { type: 'json' } import { QueryContext } from '../auth.js' import { Datasource } from '../datasources.js' import { Library } from '../libraries.js' export class TenantError extends Error { name = 'TenantError' } interface ITenantData { [key: string]: string | number | boolean URL?: string } declare type ITenantLabels = ITenantData declare type Annotations = ITenantData export interface Tenant { annotations?: Annotations id: string name: string displayName: string organizationId: string serviceId: string labels: ITenantLabels systemId: string } export interface IGetTenantsResponse { totalCount: number pageSize: number pageNumber: number data: Tenant[] } export interface TenantParams extends Tenant { datasources: Datasource[] } export interface TenantModel extends TenantParams {} export class Tenants extends SDKAdapter { fetch(query: { name?: string authorization?: string xmProjectId?: string limit?: number systemId: string }): Promise { return this.search(query) } /** Searches for tenants through inventory api request */ async search(query: { name?: string authorization?: string xmProjectId?: string limit?: number systemId: string }): Promise { if (!query.systemId) throw new Error('Provide systemId to search for tenants') const limit = query.limit || Infinity let result: TenantModel[] = [] for (let page = 1; result.length < limit; page++) { try { const url = `${this.sdk.inventory}/api/inventory/v1/tenants?${ query.name ? `name=${query.name}&` : '' }state=Active&systemId=${query.systemId}&labels=Environment%3D${ this.sdk.inventoryEnv }&pageSize=100&pageNumber=${page}` if (!query.authorization) await this.sdk.preflight(url, {}) const tenantResponse = await this.sdk.fetchJSON(url, { headers: { Authorization: query.authorization || `Bearer ${this.sdk.accessToken}` } }) const noProjectIdInQuery = !query.xmProjectId /** Filter tenants with projectId, matching projectId if provided */ const filteredTenants = tenantResponse.data.filter(function (tenant: TenantModel) { const projectId = TenantModel.generateProjectIdFromTenant(tenant) return projectId && (noProjectIdInQuery || query.xmProjectId === projectId) }) result.push(...filteredTenants) if (tenantResponse?.data?.length < 100) break } catch (e) { console.log('Searching for tenants failed', e) return null } } return result.slice(0, limit) } } export const RegionsMap = { 'West Europe': 'euw', 'East US': 'use', 'West US': 'usw', 'West US 2': 'usw', 'Japan East': 'jpe', 'East Japan': 'jpe', 'Australia East': 'aue', 'East Australia': 'aue', 'Southeast Asia': 'sg', eu1: 'euw', us1: 'use', us2: 'usw', jp1: 'jpe', au1: 'aue' } export class TenantModel extends SDKModel implements TenantParams { datasources: ReturnType get adapter() { return this.sdk.Tenants } defineProperties() { super.defineProperties() this.sdk.utils.defineCollectionAccessor(this, 'datasources', this.constructDatasources(), {}, this.getHiddenProps()) } getHiddenProps() { return ['datasources'] } constructDatasources() { const datasources = SDKCollection.construct(this.sdk.Datasource, () => ({ externalSourceId: this.name, type: 'xmTemplate' })) return datasources } constructForms() { return SDKCollection.construct(this.sdk.Form, () => ({})) } getRegionCode() { return this.labels?.['RegionCode']?.toString() || RegionsMap[this.labels?.['Region'] as keyof typeof RegionsMap] } getProjectId(): string { return TenantModel.generateProjectIdFromTenant(this) } getProjectName(): string { return TenantModel.generateProjectNameFromTenant(this) } getURL() { return `https://xmc-${this.name}.${this.sdk.sitecorePostfix}` } getEnvironmentId() { return this.annotations?.['XMCloud.EnvironmentId']?.toString() || null } getContext() { const context = {} as QueryContext if (this.name) context.tenantName = this.name if (this.organizationId) context.organization = this.organizationId if (this.systemId) context.systemId = this.systemId return context } static generateProjectIdFromTenant(tenant: Tenant): string { if (tenant.annotations && tenant.annotations['XMCloud.ProjectId']) return tenant.annotations['XMCloud.ProjectId'] as string else if (tenant.name.split('-')[1]) return `${tenant.organizationId}-${tenant.name.split('-')[1]}` else return null } static generateProjectNameFromTenant(tenant: Tenant): string { return tenant.annotations[Object.keys(tenant.annotations).find((a) => a.match(/project.*name/i))] as string } static get schema(): SDKModelSchema { return new SDKModelSchema(TenantModel, tenantSchema) } }