Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sandeepsuvit committed May 18, 2020
0 parents commit d4cb975
Show file tree
Hide file tree
Showing 42 changed files with 1,221 additions and 0 deletions.
25 changes: 25 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'prettier/@typescript-eslint',
],
root: true,
env: {
node: true,
jest: true,
},
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-use-before-define': 'off',
},
};
34 changes: 34 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# compiled output
/dist
/node_modules

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# OS
.DS_Store

# Tests
/coverage
/.nyc_output

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
4 changes: 4 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}
8 changes: 8 additions & 0 deletions .release-it.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"git": {
"commitMessage": "chore(): release v${version}"
},
"github": {
"release": true
}
}
154 changes: 154 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# nestjs-tenancy

## Description

[Mongoose](http://mongoosejs.com/) multitenancy module for [Nest](https://github.com/nestjs/nest).

## Installation

```bash
$ npm i --save @needle-innovision/nestjs-tenancy
```

## Basic usage

**app.module.ts**

```typescript
import { Module } from "@nestjs/common";
import { TenancyModule } from "@needle-innovision/nestjs-tenancy";
import { CatsModule } from "./cat.module.ts";

@Module({
imports: [
TenancyModule.forRoot({
tenantIdentifier: 'X-TenantId',
options: {},
uri: (tenantId: string) => `mongodb://localhost/test-tenant-${tenantId}`,
}),
CatsModule,
],
})
export class AppModule {}
```

Create class that describes your schema

**cat.model.ts**

```typescript
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

@Schema()
export class Cat extends Document {
@Prop()
name: string;

@Prop()
age: number;

@Prop()
breed: string;
}

export const CatSchema = SchemaFactory.createForClass(Cat);
```

Inject Cat for `CatsModule`

**cat.module.ts**

```typescript
import { Module } from '@nestjs/common';
import { TenancyModule } from '../../../lib';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
import { Cat, CatSchema } from './schemas/cat.schema';

@Module({
imports: [
TenancyModule.forFeature([{ name: Cat.name, schema: CatSchema }])
],
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule { }
```

Get the cat model in a service

**cats.service.ts**

```typescript
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { CreateCatDto } from './dto/create-cat.dto';
import { Cat } from './schemas/cat.schema';

@Injectable()
export class CatsService {
constructor(
@InjectModel(Cat.name) private readonly catModel: Model<Cat>
) { }

async create(createCatDto: CreateCatDto): Promise<Cat> {
const createdCat = new this.catModel(createCatDto);
return createdCat.save();
}

async findAll(): Promise<Cat[]> {
return this.catModel.find().exec();
}
}
```

Finally, use the service in a controller!

**cats.controller.ts**

```typescript

import { Body, Controller, Get, Post } from '@nestjs/common';
import { CatsService } from './cats.service';
import { CreateCatDto } from './dto/create-cat.dto';
import { Cat } from './schemas/cat.schema';

@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) { }

@Post()
async create(@Body() createCatDto: CreateCatDto) {
return this.catsService.create(createCatDto);
}

@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
```

## Requirements

1. @nest/mongoose +6.4.0
2. @nestjs/common +6.10.1
3. @nestjs/core +6.10.1
4. mongoose (with typings `@types/mongoose`) +5.7.12

## Test

```bash
# e2e tests
$ npm run test:e2e
```

## Stay in touch

- Author - [Sandeep K](https://github.com/sandeepsuvit)

## License

Nest is [MIT licensed](LICENSE).
2 changes: 2 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

export * from './dist';
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './dist';
1 change: 1 addition & 0 deletions lib/decorators/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './tenancy.decorator';
16 changes: 16 additions & 0 deletions lib/decorators/tenancy.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Inject } from '@nestjs/common';
import { getTeanantConnectionToken, getTenantModelToken } from '../utils';

/**
* Get the instance of the tenant model object
*
* @param model any
*/
export const InjectTenancyModel = (model: string) => Inject(getTenantModelToken(model));

/**
* Get the instance of the tenant connection
*
* @param name any
*/
export const InjectTenancyConnection = (name?: string) => Inject(getTeanantConnectionToken(name));
1 change: 1 addition & 0 deletions lib/factories/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './tenancy.factory';
48 changes: 48 additions & 0 deletions lib/factories/tenancy.factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Provider } from '@nestjs/common';
import { Connection } from 'mongoose';
import { ModelDefinition } from '../interfaces';
import { CONNECTION_MAP, MODEL_DEFINITION_MAP, TENANT_CONNECTION } from '../tenancy.constants';
import { ConnectionMap, ModelDefinitionMap } from '../types';
import { getTenantModelDefinitionToken, getTenantModelToken } from '../utils';

export const createTeanancyProviders = (definitions: ModelDefinition[]): Provider[] => {
const providers: Provider[] = [];

for (const definition of definitions) {
// Extract the definition data
const { name, schema, collection } = definition;

providers.push({
provide: getTenantModelDefinitionToken(name),
useFactory: (
modelDefinitionMap: ModelDefinitionMap,
connectionMap: ConnectionMap,
) => {
const exists = modelDefinitionMap.has(name);
if (!exists) {
modelDefinitionMap.set(name, { ...definition });

connectionMap.forEach((connection: Connection) => {
connection.model(name, schema, collection);
});
}
},
inject: [
MODEL_DEFINITION_MAP,
CONNECTION_MAP,
],
});

// Creating Models with connections attached
providers.push({
provide: getTenantModelToken(name),
useFactory(tenantConnection: Connection) {
return tenantConnection.model(name, schema, collection);
},
inject: [TENANT_CONNECTION],
});
}

// Return the list of providers mapping
return providers;
};
6 changes: 6 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from './types';
export * from './interfaces';
export * from './decorators';
export * from './utils';
export * from './factories';
export * from './tenancy.module';
2 changes: 2 additions & 0 deletions lib/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './model-definition.interface';
export * from './tenancy-options.interface';
7 changes: 7 additions & 0 deletions lib/interfaces/model-definition.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Schema } from 'mongoose';

export interface ModelDefinition {
name: string;
schema: Schema;
collection?: string;
}
60 changes: 60 additions & 0 deletions lib/interfaces/tenancy-options.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { ModuleMetadata, Type } from '@nestjs/common/interfaces';

/**
* Options for synchronous setup
*
* @export
* @interface TenancyModuleOptions
*/
export interface TenancyModuleOptions extends Record<string, any> {
/**
* If `true`, tenant id will be extracted from the subdomain
*/
isTenantFromSubdomain?: boolean;

/**
* Tenant id will be extracted using the keyword from the request header
*/
tenantIdentifier?: string;

/**
* URI for the tenant database
*/
uri: (uri: string) => string;

/**
* Options for the database
*/
options?: any;

/**
* Whitelist following subdomains
*/
whitelist?: any;
}

/**
* For creating options dynamically
*
* To use this the class implementing `TenancyOptionsFactory` should
* implement the method `createTenancyOptions` under it.
*
* @export
* @interface TenancyOptionsFactory
*/
export interface TenancyOptionsFactory {
createTenancyOptions():Promise<TenancyModuleOptions> | TenancyModuleOptions;
}

/**
* Options for asynchronous setup
*
* @export
* @interface TenancyModuleAsyncOptions
*/
export interface TenancyModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
useExisting?: Type<TenancyOptionsFactory>;
useClass?: Type<TenancyOptionsFactory>;
useFactory?: (...args: any[]) => Promise<TenancyModuleOptions> | TenancyModuleOptions;
inject?: any[];
}
Loading

0 comments on commit d4cb975

Please sign in to comment.