refactor(nestjs): Implement users module

This commit is contained in:
Ahmed Bouhuolia
2025-05-20 17:55:58 +02:00
parent ce058b9416
commit 99fe5a6b0d
48 changed files with 1823 additions and 207 deletions

View File

@@ -11,13 +11,9 @@ import {
Put,
Get,
Body,
Req,
Res,
Next,
HttpCode,
Param,
} from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { BuildOrganizationService } from './commands/BuildOrganization.service';
import {
BuildOrganizationDto,
@@ -28,6 +24,7 @@ import { UpdateOrganizationService } from './commands/UpdateOrganization.service
import { IgnoreTenantInitializedRoute } from '../Tenancy/EnsureTenantIsInitialized.guard';
import { IgnoreTenantSeededRoute } from '../Tenancy/EnsureTenantIsSeeded.guards';
import { GetBuildOrganizationBuildJob } from './commands/GetBuildOrganizationJob.service';
import { OrganizationBaseCurrencyLocking } from './Organization/OrganizationBaseCurrencyLocking.service';
@ApiTags('Organization')
@Controller('organization')
@@ -39,6 +36,7 @@ export class OrganizationController {
private readonly getCurrentOrgService: GetCurrentOrganizationService,
private readonly updateOrganizationService: UpdateOrganizationService,
private readonly getBuildOrganizationJobService: GetBuildOrganizationBuildJob,
private readonly orgBaseCurrencyLockingService: OrganizationBaseCurrencyLocking,
) {}
@Post('build')
@@ -81,6 +79,14 @@ export class OrganizationController {
return { organization };
}
@Get('base-currency-mutate')
async baseCurrencyMutate() {
const abilities =
await this.orgBaseCurrencyLockingService.baseCurrencyMutateLocks();
return { abilities };
}
@Put()
@HttpCode(200)
@ApiOperation({ summary: 'Update organization information' })

View File

@@ -1,6 +1,7 @@
// @ts-nocheck
import { Injectable } from '@nestjs/common';
import { isEmpty } from 'lodash';
import { Injectable } from '@nestjs/common';
import { getPreventMutateBaseCurrencyModels } from '@/common/decorators/LockMutateBaseCurrency.decorator';
import { ModuleRef } from '@nestjs/core';
interface MutateBaseCurrencyLockMeta {
modelName: string;
@@ -9,26 +10,27 @@ interface MutateBaseCurrencyLockMeta {
@Injectable()
export class OrganizationBaseCurrencyLocking {
constructor(private readonly moduleRef: ModuleRef) {}
/**
* Retrieves the tenant models that have prevented mutation base currency.
*/
private getModelsPreventsMutate = (tenantId: number) => {
const Models = this.tenancy.models(tenantId);
private getModelsPreventsMutate() {
const lockedModels = getPreventMutateBaseCurrencyModels();
const filteredEntries = Object.entries(Models).filter(
const filteredEntries = Array.from(lockedModels).filter(
([key, Model]) => !!Model.preventMutateBaseCurrency,
);
return Object.fromEntries(filteredEntries);
};
}
/**
* Detarmines the mutation base currency model is locked.
* @param {Model} Model
* @returns {Promise<MutateBaseCurrencyLockMeta | false>}
*/
private isModelMutateLocked = async (
private async isModelMutateLocked(
Model,
): Promise<MutateBaseCurrencyLockMeta | false> => {
): Promise<MutateBaseCurrencyLockMeta | false> {
const validateQuery = Model.query();
if (typeof Model?.modifiers?.preventMutateBaseCurrency !== 'undefined') {
@@ -45,21 +47,24 @@ export class OrganizationBaseCurrencyLocking {
pluralName: Model.pluralName,
}
: false;
};
}
/**
* Retrieves the base currency mutation locks of the tenant models.
* @param {number} tenantId
* @returns {Promise<MutateBaseCurrencyLockMeta[]>}
*/
public async baseCurrencyMutateLocks(
tenantId: number,
): Promise<MutateBaseCurrencyLockMeta[]> {
const PreventedModels = this.getModelsPreventsMutate(tenantId);
public async baseCurrencyMutateLocks(): Promise<
MutateBaseCurrencyLockMeta[]
> {
const PreventedModels = this.getModelsPreventsMutate();
const opers = Object.entries(PreventedModels).map(([ModelName, Model]) => {
const InjectedModelProxy = this.moduleRef.get(ModelName, {
strict: false,
});
const InjectedModel = InjectedModelProxy();
const opers = Object.entries(PreventedModels).map(([ModelName, Model]) =>
this.isModelMutateLocked(Model),
);
return this.isModelMutateLocked(InjectedModel);
});
const results = await Promise.all(opers);
return results.filter(
@@ -69,12 +74,11 @@ export class OrganizationBaseCurrencyLocking {
/**
* Detarmines the base currency mutation locked.
* @param {number} tenantId
* @returns {Promise<boolean>}
*/
public isBaseCurrencyMutateLocked = async (tenantId: number) => {
const locks = await this.baseCurrencyMutateLocks(tenantId);
public async isBaseCurrencyMutateLocked() {
const locks = await this.baseCurrencyMutateLocks();
return !isEmpty(locks);
};
}
}

View File

@@ -1,102 +0,0 @@
import { Inject, Service } from 'typedi';
import { ObjectId } from 'mongodb';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { SeedMigration } from '@/lib/Seeder/SeedMigration';
import { Tenant } from '@/system/models';
import { ServiceError } from '@/exceptions';
import TenantDBManager from '@/services/Tenancy/TenantDBManager';
import config from '../../config';
import { ERRORS } from './constants';
import OrganizationService from './OrganizationService';
import TenantsManagerService from '@/services/Tenancy/TenantsManager';
@Service()
export default class OrganizationUpgrade {
@Inject()
private organizationService: OrganizationService;
@Inject()
private tenantsManager: TenantsManagerService;
@Inject('agenda')
private agenda: any;
/**
* Upgrades the given organization database.
* @param {number} tenantId - Tenant id.
* @returns {Promise<void>}
*/
public upgradeJob = async (tenantId: number): Promise<void> => {
const tenant = await Tenant.query()
.findById(tenantId)
.withGraphFetched('metadata');
// Validate tenant version.
this.validateTenantVersion(tenant);
// Initialize the tenant.
const seedContext = this.tenantsManager.getSeedMigrationContext(tenant);
// Database manager.
const dbManager = new TenantDBManager();
// Migrate the organization database schema.
await dbManager.migrate(tenant);
// Seeds the organization database data.
await new SeedMigration(seedContext.knex, seedContext).latest();
// Update the organization database version.
await this.organizationService.flagTenantDBBatch(tenantId);
// Remove the tenant job id.
await Tenant.markAsUpgraded(tenantId);
};
/**
* Running organization upgrade job.
* @param {number} tenantId - Tenant id.
* @return {Promise<void>}
*/
public upgrade = async (tenantId: number): Promise<{ jobId: string }> => {
const tenant = await Tenant.query().findById(tenantId);
// Validate tenant version.
this.validateTenantVersion(tenant);
// Validate tenant upgrade is not running.
this.validateTenantUpgradeNotRunning(tenant);
// Send welcome mail to the user.
const jobMeta = await this.agenda.now('organization-upgrade', {
tenantId,
});
// Transformes the mangodb id to string.
const jobId = new ObjectId(jobMeta.attrs._id).toString();
// Markes the tenant as currently building.
await Tenant.markAsUpgrading(tenantId, jobId);
return { jobId };
};
/**
* Validates the given tenant version.
* @param {ITenant} tenant
*/
private validateTenantVersion(tenant) {
if (tenant.databaseBatch >= config.databaseBatch) {
throw new ServiceError(ERRORS.TENANT_DATABASE_UPGRADED);
}
}
/**
* Validates the given tenant upgrade is not running.
* @param tenant
*/
private validateTenantUpgradeNotRunning(tenant) {
if (tenant.isUpgradeRunning) {
throw new ServiceError(ERRORS.TENANT_UPGRADE_IS_RUNNING);
}
}
}

View File

@@ -9,7 +9,7 @@ export class CommandOrganizationValidators {
constructor(
private readonly baseCurrencyMutateLocking: OrganizationBaseCurrencyLocking,
) {}
/**
* Validate mutate base currency ability.
* @param {Tenant} tenant -
@@ -23,9 +23,7 @@ export class CommandOrganizationValidators {
) {
if (tenant.isReady && newBaseCurrency !== oldBaseCurrency) {
const isLocked =
await this.baseCurrencyMutateLocking.isBaseCurrencyMutateLocked(
tenant.id,
);
await this.baseCurrencyMutateLocking.isBaseCurrencyMutateLocked();
if (isLocked) {
throw new ServiceError(ERRORS.BASE_CURRENCY_MUTATE_LOCKED);