UNPKG

@sourceloop/ctrl-plane-tenant-management-service

Version:

Tenant Management microservice for SaaS control plane

200 lines 9.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OnboardingService = void 0; const tslib_1 = require("tslib"); const core_1 = require("@loopback/core"); const repository_1 = require("@loopback/repository"); const rest_1 = require("@loopback/rest"); const core_2 = require("@sourceloop/core"); const enums_1 = require("../enums"); const models_1 = require("../models"); const repositories_1 = require("../repositories"); const utils_1 = require("../utils"); const lead_authenticator_service_1 = require("./lead-authenticator.service"); const lodash_1 = require("lodash"); /** * Helper service for onboarding tenants. */ let OnboardingService = class OnboardingService { /** * Constructs a new instance of the OnboardingService. * @param {LeadRepository} leadRepository - Repository for managing leads. * @param {TenantRepository} tenantRepository - Repository for managing tenants. * @param {ContactRepository} contactRepository - Repository for managing contacts. * @param {AddressRepository} addressRepository - Repository for managing addresses. * @param {LeadAuthenticator} leadAuthenticator - Service for authenticating leads. * @param {ILogger} logger - Logger service for logging messages. */ constructor(leadRepository, tenantRepository, contactRepository, addressRepository, leadAuthenticator, logger) { this.leadRepository = leadRepository; this.tenantRepository = tenantRepository; this.contactRepository = contactRepository; this.addressRepository = addressRepository; this.leadAuthenticator = leadAuthenticator; this.logger = logger; } /** * The addLead function creates a new lead, triggers a validation email, and * returns the new lead. * @param lead - The `lead` parameter is an object of type `Lead` with the `id` * property omitted. * @returns The `addLead` function is returning the newly created lead object. */ async addLead(lead) { var _a, _b, _c, _d, _e, _f; const existing = await this.leadRepository.findOne({ where: { email: lead.email, isValidated: true, }, }); if (existing) { this.logger.error(`Lead with email ${lead.email} already exists`); throw new rest_1.HttpErrors.Conflict(); } let addressId = undefined; if ((_a = lead.address) === null || _a === void 0 ? void 0 : _a.country) { const address = await this.addressRepository.create(new models_1.Address({ country: (_b = lead.address) === null || _b === void 0 ? void 0 : _b.country, address: (_c = lead.address) === null || _c === void 0 ? void 0 : _c.address, city: (_d = lead.address) === null || _d === void 0 ? void 0 : _d.city, state: (_e = lead.address) === null || _e === void 0 ? void 0 : _e.state, zip: (_f = lead.address) === null || _f === void 0 ? void 0 : _f.zip, })); addressId = address.id; } const newLead = await this.leadRepository.create(new models_1.Lead({ isValidated: false, addressId, ...(0, lodash_1.omit)(lead, 'address'), })); const key = await this.leadAuthenticator.triggerValidationMail(newLead); // triggered notification sunny return { key, id: newLead.id }; } /** * The startOnboarding function checks if a lead user exists and is validated, and * if not, updates the lead user's validation status and triggers tenant creation. * @param {LeadUser} lead - The `lead` parameter is an object of type `LeadUser`. * It represents a lead user who is going through the onboarding process. */ async onboardForLead(dto, lead) { const existing = await this.leadRepository.findOne({ where: { id: lead.id, isValidated: true, }, include: ['tenant'], }); if (!existing) { this.logger.error(`Valid Lead with id ${lead.id} does not exist`); throw new rest_1.HttpErrors.Unauthorized(); } if (existing.tenant) { this.logger.error(`Lead with id ${lead.id} has a tenant`); throw new rest_1.HttpErrors.Unauthorized(); } await this.leadRepository.updateById(lead.id, { isValidated: true, }); return this.onboard({ ...dto, contact: new models_1.Contact({ email: existing.email, type: 'admin', firstName: existing.firstName, lastName: existing.lastName, isPrimary: true, }), }, existing); } /** * The `setupTenant` function creates a new tenant with the provided information and an optional lead, * and returns the created tenant. * @param {TenantOnboardDTO} dto - The `dto` parameter is an object of type `TenantOnboardDTO` which * contains the necessary information to onboard a new tenant. It includes properties such as `key`, * `country`, `address`, `city`, `state`, `zip`, and `name`. * @param {Lead} [lead] - The `lead` parameter is an optional parameter of type `Lead`. It represents * the lead associated with the tenant being onboarded. If a lead is provided, their information will * be used to create a contact for the tenant. If no lead is provided, the contact will not be created. * @returns a Promise that resolves to a Tenant object. */ async onboard(dto, lead) { var _a; const transaction = await this.tenantRepository.beginTransaction(); try { let address = null; if (lead === null || lead === void 0 ? void 0 : lead.addressId) { address = await this.addressRepository.findById(lead.addressId); if ((0, utils_1.hasAnyOf)(dto, ['address', 'city', 'state', 'country', 'zip']) && (!(0, utils_1.weakEqual)(address.address, dto.address) || !(0, utils_1.weakEqual)(address.city, dto.city) || !(0, utils_1.weakEqual)(address.state, dto.state) || !(0, utils_1.weakEqual)(address.country, dto.country) || !(0, utils_1.weakEqual)(address.zip, dto.zip))) { throw new rest_1.HttpErrors.BadRequest('Address mismatch with Lead'); } } else if (dto.country) { address = await this.addressRepository.create({ country: dto.country, address: dto.address, city: dto.city, state: dto.state, zip: dto.zip, }, { transaction }); } else { // Do Nothing } const tenant = await this.tenantRepository.create({ key: dto.key, name: (_a = dto.name) !== null && _a !== void 0 ? _a : lead === null || lead === void 0 ? void 0 : lead.companyName, leadId: lead === null || lead === void 0 ? void 0 : lead.id, domains: dto.domains, status: enums_1.TenantStatus.PENDINGPROVISION, addressId: address === null || address === void 0 ? void 0 : address.id, }, { transaction }); const contactHostName = dto.contact.email.split('@')[1]; if (!dto.domains.includes(contactHostName)) { throw new rest_1.HttpErrors.BadRequest('Contact email domain does not match tenant domains'); } await this.contactRepository.create({ email: dto.contact.email, firstName: dto.contact.firstName, lastName: dto.contact.lastName, tenantId: tenant.id, isPrimary: dto.contact.isPrimary, }, { transaction }); const res = await this.tenantRepository.findById(tenant.id, { include: [ { relation: 'contacts' }, { relation: 'resources' }, { relation: 'lead' }, { relation: 'address' }, ], }, { transaction }); await transaction.commit(); return res; } catch (error) { await transaction.rollback(); throw error; } } }; exports.OnboardingService = OnboardingService; exports.OnboardingService = OnboardingService = tslib_1.__decorate([ (0, core_1.injectable)({ scope: core_1.BindingScope.REQUEST }), tslib_1.__param(0, (0, repository_1.repository)(repositories_1.LeadRepository)), tslib_1.__param(1, (0, repository_1.repository)(repositories_1.TenantRepository)), tslib_1.__param(2, (0, repository_1.repository)(repositories_1.ContactRepository)), tslib_1.__param(3, (0, repository_1.repository)(repositories_1.AddressRepository)), tslib_1.__param(4, (0, core_1.service)(lead_authenticator_service_1.LeadAuthenticator)), tslib_1.__param(5, (0, core_1.inject)(core_2.LOGGER.LOGGER_INJECT)), tslib_1.__metadata("design:paramtypes", [repositories_1.LeadRepository, repositories_1.TenantRepository, repositories_1.ContactRepository, repositories_1.AddressRepository, lead_authenticator_service_1.LeadAuthenticator, Object]) ], OnboardingService); //# sourceMappingURL=onboarding.service.js.map