Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed global variables #76

Merged
merged 32 commits into from
Oct 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cc2ac4b
Updated BaseEntity navigation properties to reference User type
ofuochi Oct 6, 2019
402d8c1
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 6, 2019
a435bb4
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 7, 2019
5f8d29c
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 7, 2019
2cc3314
Used auto mapper's array mapper for mapping array to DTO
ofuochi Oct 7, 2019
b4697ef
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 7, 2019
dd78def
Removed vscode local settings from version control
ofuochi Oct 7, 2019
06b1211
Updated createdBy field in user entity
ofuochi Oct 7, 2019
210a878
Added user auto mapper profile
ofuochi Oct 7, 2019
badf73e
Added user auto mapper profile
ofuochi Oct 7, 2019
0715bcb
Updated package.json and set tenant and user globally. Closes #16, cl…
ofuochi Oct 7, 2019
4f888a5
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 7, 2019
198c030
Implement save or update feature. Closes #11
ofuochi Oct 8, 2019
d9fa7a8
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 8, 2019
7b1aeb3
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 8, 2019
8d81c5a
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 8, 2019
641f509
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 8, 2019
48a9b72
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 8, 2019
ff62437
Moved hooks to base entity class and implemented soft-delete
ofuochi Oct 8, 2019
69d225d
Implement soft delete feature. Closes #56
ofuochi Oct 9, 2019
618cef8
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 9, 2019
0c4f979
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 9, 2019
d3776e2
Fixed #63, and refactor some part of test files (#14)
ofuochi Oct 10, 2019
875aa53
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 10, 2019
18d40e2
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 10, 2019
2748d20
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 13, 2019
e28852e
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 18, 2019
00116eb
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 18, 2019
c6756ba
Simplify returned entity in signUp
ofuochi Oct 18, 2019
d87688f
Merge branch 'master' of https://github.com/ofuochi/node-typescript-t…
ofuochi Oct 18, 2019
0bdd9ba
Removed global variables and wrote UserController tests
ofuochi Oct 19, 2019
9ee3a6b
Correct model class extensions
ofuochi Oct 19, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/domain/constants/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export const TYPES = {

EventDispatcher: Symbol("EventDispatcher"),
Agenda: Symbol("Agenda"),
TenantId: Symbol("TenantId")
TenantId: Symbol("TenantId"),
DecodedJwt: Symbol("DecodedJwt")
};
27 changes: 18 additions & 9 deletions src/domain/model/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@ import { Query } from "mongoose";

import { Writable } from "../utils/writable";
import { User } from "./user";
import { iocContainer } from "../../infrastructure/config/ioc";
import { TYPES } from "../constants/types";
import { DecodedJwt } from "../../ui/services/auth_service";

// eslint-disable-next-line
@pre<BaseEntity>("findOneAndUpdate", function(this: Query<BaseEntity>, next) {
try {
const entity = this.getUpdate();
const user = (global.currentUser && global.currentUser.user) as User;
if (user) {
entity.updatedBy = user.id;
if (iocContainer.isBound(TYPES.DecodedJwt)) {
const currentUser = iocContainer.get<DecodedJwt>(TYPES.DecodedJwt);
entity.updatedBy = currentUser.userId;
if (entity.isDeleted) {
entity.deletedBy = user.id;
entity.deletedBy = currentUser.userId;
entity.deletionTime = new Date();
}
}

next();
} catch (error) {
next(error);
Expand All @@ -25,16 +29,21 @@ import { User } from "./user";
// eslint-disable-next-line
@pre<BaseEntity>("save", function(next) {
try {
const user = global.currentUser && global.currentUser.user;
const creator = user || this.type === "User" ? this : undefined;
if (creator) this.setCreatedBy(creator);
if (iocContainer.isBound(TYPES.DecodedJwt)) {
const currentUser = iocContainer.get<DecodedJwt>(TYPES.DecodedJwt);
this.setCreatedBy(currentUser.userId);
} else if (this.type === "User") {
this.setCreatedBy(this);
}
next();
} catch (error) {
next(error);
}
})
export abstract class BaseEntity extends Typegoose {
abstract type: string;
abstract update(entity: Partial<BaseEntity>): void;

@Expose()
id?: any;

Expand Down Expand Up @@ -84,7 +93,7 @@ export abstract class BaseEntity extends Typegoose {
}

@instanceMethod
private setCreatedBy(user: BaseEntity) {
(this as Writable<BaseEntity>).createdBy = user.id;
private setCreatedBy(user: any) {
(this as Writable<BaseEntity>).createdBy = user;
}
}
4 changes: 0 additions & 4 deletions src/domain/model/interfaces/entity.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
export interface IMustHaveTenant {
tenant: any;
}
export interface IActiveStatus {
isActive: boolean;
deactivate(): void;
}
6 changes: 6 additions & 0 deletions src/domain/model/tenant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,10 @@ export class Tenant extends BaseEntity {
setDescription(description: string) {
(this as Writable<Tenant>).description = description;
}

@instanceMethod
update(tenant: Partial<this>): void {
if (this.name) this.setName(tenant.name as string);
if (this.description) this.setDescription(tenant.description as string);
}
}
22 changes: 14 additions & 8 deletions src/domain/model/user.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { index, instanceMethod, prop, Ref } from "@hasezoey/typegoose";

import { iocContainer } from "../../infrastructure/config/ioc";
import { TYPES } from "../constants/types";
import { Writable } from "../utils/writable";
import { BaseEntity } from "./base";
import { IMustHaveTenant } from "./interfaces/entity";
import { Tenant } from "./tenant";

export const MAX_NAME_LENGTH = 225;
export const PASSWORD_SALT_ROUND = 12;

export enum UserRole {
USER = "user",
ADMIN = "admin"
Expand Down Expand Up @@ -84,8 +87,7 @@ export class User extends BaseEntity implements IMustHaveTenant {
password: string;
tenantId?: string;
}) => {
const id =
tenantId || (global.currentUser && global.currentUser.tenant.id);
const id = tenantId || iocContainer.get<any>(TYPES.TenantId);

if (!id) throw new Error("Tenant Id is required");

Expand Down Expand Up @@ -119,11 +121,6 @@ export class User extends BaseEntity implements IMustHaveTenant {
(this as Writable<User>).username = username;
}

@instanceMethod
setTenant(tenant: Ref<Tenant>) {
(this as Writable<User>).tenant = tenant;
}

@instanceMethod
setFirstName(firstName: string) {
(this as Writable<User>).firstName = firstName;
Expand All @@ -138,4 +135,13 @@ export class User extends BaseEntity implements IMustHaveTenant {
setPassword(password: string) {
(this as Writable<User>).password = password;
}
@instanceMethod
update(user: Partial<this>): void {
if (this.firstName) this.setFirstName(user.firstName as string);
if (this.lastName) this.setLastName(user.lastName as string);
if (this.password) this.setPassword(user.password as string);
if (this.username) this.setUsername(user.username as string);
if (this.email) this.setEmail(user.email as string);
if (this.role) this.setRole(user.role as UserRole);
}
}
31 changes: 0 additions & 31 deletions src/domain/utils/globals.ts

This file was deleted.

93 changes: 46 additions & 47 deletions src/infrastructure/config/swagger.config.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,61 @@
import {
generateRoutes,
generateSwaggerSpec,
RoutesConfig,
SwaggerConfig
generateRoutes,
generateSwaggerSpec,
RoutesConfig,
SwaggerConfig
} from "tsoa";
import { config } from "./index";
import {
X_TENANT_ID,
X_AUTH_TOKEN_KEY
X_TENANT_ID,
X_AUTH_TOKEN_KEY
} from "../../ui/constants/header_constants";

const basePath = config.api.prefix;
const entryFile = "./src/index.ts";
const controllers = "./src/ui/api/controllers/*.ts";
const protocol =
process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test"
? "http"
: "https";
config.env === "development" || config.env === "test" ? "http" : "https";
export const swaggerGen = async () => {
const swaggerOptions: SwaggerConfig = {
basePath,
entryFile,
securityDefinitions: {
[X_TENANT_ID]: {
type: "apiKey",
in: "header",
name: X_TENANT_ID,
description: "Tenant ID"
},
[X_AUTH_TOKEN_KEY]: {
type: "apiKey",
in: "header",
name: X_AUTH_TOKEN_KEY,
description: "JWT access token"
}
},
noImplicitAdditionalProperties: "throw-on-extras",
host: process.env.HOST,
description: "Enterprise NodeJs/Typescript API boilerplate",
version: "1.0.0",
name: "node-typescript-boilerplate",
specVersion: 3,
schemes: [protocol],
outputDirectory: "./",
controllerPathGlobs: [controllers]
};
const swaggerOptions: SwaggerConfig = {
basePath,
entryFile,
securityDefinitions: {
[X_TENANT_ID]: {
type: "apiKey",
in: "header",
name: X_TENANT_ID,
description: "Tenant ID"
},
[X_AUTH_TOKEN_KEY]: {
type: "apiKey",
in: "header",
name: X_AUTH_TOKEN_KEY,
description: "JWT access token"
}
},
noImplicitAdditionalProperties: "throw-on-extras",
host: process.env.HOST,
description: "Enterprise NodeJs/Typescript API boilerplate",
version: "1.0.0",
name: "node-typescript-boilerplate",
specVersion: 3,
schemes: [protocol],
outputDirectory: "./",
controllerPathGlobs: [controllers]
};

const routeOptions: RoutesConfig = {
basePath,
entryFile,
middleware: "express",
authenticationModule: "./src/ui/api/middleware/auth_middleware",
iocModule: "./src/infrastructure/config/ioc",
routesDir: "./src/ui/api",
controllerPathGlobs: [controllers]
};
const routeOptions: RoutesConfig = {
basePath,
entryFile,
middleware: "express",
authenticationModule: "./src/ui/api/middleware/auth_middleware",
iocModule: "./src/infrastructure/config/ioc",
routesDir: "./src/ui/api",
controllerPathGlobs: [controllers]
};

await generateSwaggerSpec(swaggerOptions, routeOptions);
if (config.env !== "test")
await generateSwaggerSpec(swaggerOptions, routeOptions);

await generateRoutes(routeOptions, swaggerOptions);
await generateRoutes(routeOptions, swaggerOptions);
};
2 changes: 0 additions & 2 deletions src/infrastructure/db/db_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ export async function seedDefaultAdmin(tenantId: string) {
});
if (!defaultAdminUser) {
userInstance.setRole(UserRole.ADMIN);
userInstance.setTenant(tenantId as any);

await tenantModel.create(userInstance);
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/infrastructure/db/repositories/base_repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import { provideSingleton } from "../../config/ioc";
export class BaseRepository<TEntity extends BaseEntity, TModel extends Document>
implements IBaseRepository<TEntity> {
protected Model: Model<TModel>;
protected CurrentTenantId?: string =
global.currentUser && global.currentUser.tenant.id;

protected _constructor: () => TEntity;
public constructor(
@unmanaged() model: Model<TModel>,
Expand Down
2 changes: 1 addition & 1 deletion src/infrastructure/db/repositories/tenant_repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Tenant } from "../../../domain/model/tenant";
import { provideSingleton } from "../../config/ioc";
import { BaseRepository } from "./base_repository";

export interface TenantModel extends Tenant, Document {}
export type TenantModel = Tenant & Document;

@provideSingleton(TenantRepository)
export class TenantRepository extends BaseRepository<Tenant, TenantModel> {
Expand Down
2 changes: 1 addition & 1 deletion src/infrastructure/db/repositories/user_repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { User } from "../../../domain/model/user";
import { provideSingleton } from "../../config/ioc";
import { BaseRepository } from "./base_repository";

export interface UserModel extends User, Document {}
export type UserModel = User & Document;

@provideSingleton(UserRepository)
export class UserRepository extends BaseRepository<User, UserModel>
Expand Down
13 changes: 0 additions & 13 deletions src/types/custom.d.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/ui/api/controllers/tenant_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export class TenantController extends BaseController {

@Get("{tenantName}")
public async get(tenantName: string) {
return this._tenantService.get(tenantName);
const tenant = await this._tenantService.get(tenantName);
return tenant || this.setStatus(httpStatus.NOT_FOUND);
}
@Post()
@Security("X-Auth-Token", ["admin"])
Expand Down
19 changes: 19 additions & 0 deletions src/ui/api/controllers/user_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Body, Post, Route, Security, Tags } from "tsoa";
import { inject, provideSingleton } from "../../../infrastructure/config/ioc";
import { IUserService } from "../../interfaces/user_service";
import { UserDto, UserSignUpInput } from "../../models/user_dto";
import { UserService } from "../../services/user_service";
import { BaseController } from "./base_controller";

@Tags("Users")
@Route("users")
@provideSingleton(UserController)
export class UserController extends BaseController {
@inject(UserService) private readonly _userService: IUserService;

@Post()
@Security("X-Auth-Token", ["admin"])
async create(@Body() input: UserSignUpInput): Promise<UserDto> {
return this._userService.create(input);
}
}
Loading