@sourceloop/ctrl-plane-tenant-management-service
Version:
Tenant Management microservice for SaaS control plane
200 lines • 9.65 kB
JavaScript
"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