Closed alexonozor closed 3 years ago
Hi @alexonozor I didn't really catch the usecase there. Cloud you give some examples of your usecase here. Ideally this tenancy module acts on the connection object via the request context properties which you inject via InjectTenancyModel
and get the connection of that particular document which belongs to the tenant. The services and controller doesn't actually matter. You can still use your normal mongoose InjectModel
in your service's for your default connection though.
Ok my Tenant service I added
tenant.service.ts
export class TenantsService {
constructor(
@InjectTenancyModel('Tenant') private tenantModel: Model<any>,
@InjectModel('Tenant') private tenantFreeModel: Model<any>,
private staffService: StaffsService
) { }
// I want to call the search without the validation it still shows errors 404. "message": "X-TENANT-ID is not supplied",
async search(params): Promise<any[]> {
return this.tenantFreeModel.find({ "tenantId": { "$regex": params, "$options": "i" }})
.select("_id name email photo")
}
async delete(tenantId: string): Promise<any> {
return this.tenantModel.deleteOne({ _id: tenantId });
}
}
tenant.module.ts
import { Module } from '@nestjs/common';
import { TenantsController } from './tenants.controller';
import { TenantsService } from './tenants.service';
import { TenantSchema } from '../schemas/tanant.schema';
import { TenancyModule } from '@needle-innovision/nestjs-tenancy';
import { ConfigService } from '@nestjs/config';
import { StaffsService } from 'src/staffs/staffs.service';
@Module({
imports: [
TenancyModule.forFeature([{ name: 'Tenant', schema: TenantSchema }
])],
controllers: [TenantsController],
providers: [TenantsService, StaffsService, ConfigService],
exports: [TenantsService]
})
export class TenantModule {}
app.module.ts
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true, load: [configuration] }),
TenancyModule.forRoot({
tenantIdentifier: 'X-TENANT-ID',
options: () => { },
uri: (tenantId: string) => `${process.env.DATABASE_URL}/${tenantId}`,
}),
InvitationModule,
UsersModule,
ShippingsModule,
CategoriesModule,
AuthModule,
FoodsModule,
CartsModule,
OrdersModule,
FavoritesModule,
StaffsModule,
TenantModule,
MailModule,
CollectionsModule
],
providers: [ConfigService]
})
export class AppModule {
constructor() {
console.log(console.log(process.env.DATABASE_URL))
}
}
Hi @alexonozor sorry for the late reply,
I am bit confused the way you have arranged the Tenant
model in the TenantsService
. Sorry if i misunderstood your implementation. Here is what i normally use it for your reference.
app.module.ts
would look like this
@Module({
imports: [
// Load the default configuration file
ConfigModule.forRoot({ load: configuration, isGlobal: true }),
// Mongoose default connection for master data
MongooseModule.forRootAsync({
useFactory: async (cfs: ConfigService) => cfs.get(`database`),
inject: [ConfigService],
}),
// Multi tenancy configuration
TenancyModule.forRootAsync({
imports: [MasterDatastoreModule], // For accessing Custom Tenancy Validator exported via `MasterDatastoreModule`
useFactory: async (cfs: ConfigService, tVal: CustomTenantValidator) => {
return {
...cfs.get(`tenant`), // Contains connection properties for tenant db
// Custom validator to check if the tenant exist in common database
validator: (tenantId: string) => tVal.setTenantId(tenantId),
}
},
inject: [ConfigService, CustomTenantValidator],
}),
MasterDatastoreModule,
TenantDataModule,
...other modules
],
controllers: [AppController],
providers: [
AppService,
],
})
export class AppModule {}
// Master module
@Module({
imports: [
// Register all master level entities here
MongooseModule.forFeature([
{ name: 'Account', schema: AccountSchema },
]),
],
controllers: [],
providers: [
MongoConnectionService,
CustomTenantValidator,
],
exports: [
MongooseModule,
DbUtilsService,
CustomTenantValidator,
],
})
export class MasterDatastoreModule {}
// Tenant Module that exports tenant db related schemas
@Module({
imports: [
// Register all tenant level entities here
TenancyModule.forFeature([
// Module entities
{ name: 'Project', schema: ProjectSchema },
]),
],
providers: [
],
exports: [
]
})
export class TenantDatastoreModule {}
TenantsModule
@Module({
imports: [
TenantDatastoreModule,
MasterDatastoreModule
],
controllers: [TenantsController],
providers: [TenantsService],
exports: [TenantsService]
})
export class TenantsModule {}
TenantsService
i would have operations related to all tenants that are in the master db
@Injectable()
export class TenantsService {
constructor(
@InjectModel('Account')
private readonly accountModel: Model<IAccountModel>,
@InjectModel('AccountOwner')
private readonly accountOwnerModel: Model<IAccountOwnerModel>,
@InjectModel('AccountSubscription')
private readonly accountSubscriptionModel: Model<IAccountSubscriptionModel>
) { }
// Example method inside this service
private async createAccountOwner(
ownerDto: CreateCustomerDto,
): Promise<IAccountOwnerModel> {
try {
const owner = new AccountOwnerDto({
firstName: ownerDto.firstName,
lastName: ownerDto.lastName,
email: ownerDto.email,
phone: ownerDto.phone,
});
const ownerModel = new this.accountOwnerModel(owner);
// Persist the details to master db
return await ownerModel.save();
} catch (error) {
throw new InternalServerErrorException(error);
}
}
}
@Module({
imports: [
TenantDatastoreModule,
],
controllers: [ProjectsController],
providers: [ProjectsService],
exports: [ProjectsService]
})
export class ProjectsModule {}
@Injectable()
export class ProjectsService {
constructor(
@InjectTenancyModel('Project')
private readonly projectModel: PaginateModel<IProjectModel>
) {}
async findAllProjects(
page: number,
limit: number,
userId: string,
): Promise<PaginateResult<IProjectModel>> {
// Useful to create query condition
const query = {};
// Params for pagination
const options = {
// sort: { createdOn: -1 },
page: Number(page),
limit: Number(limit),
populate: {
path: 'owner',
select: BaseUserDetails,
populate: {
path: 'profile',
select: BaseProfileDetails,
}
}
};
// Fetch the record from the tenant db
return await this.projectModel.paginate(query, options);
}
}
If this still doesn't help you i would recommend you to create a sample github project with your issue and share it with me to debug it.
Thanks, it works.
could you share the full project you already apply this library?
Hi there, I would like to exclude a particular service/controller method from been validated by the tenantId validation that comes from the header.