-
Notifications
You must be signed in to change notification settings - Fork 0
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
[TM-1677] Implement user verification BE #56
Open
Scriptmatico
wants to merge
8
commits into
staging
Choose a base branch
from
feat/TM-1677-implement-user-verification
base: staging
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 6 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
6ca7d9f
WIP
Scriptmatico 8deed39
add unit test
Scriptmatico 7a91331
fix lint issue
Scriptmatico e348709
missing declare controller into module
Scriptmatico 5dcedfa
add suffix dto
Scriptmatico 4c2a137
add NoBearerAuth
Scriptmatico 072906b
[TM-1677] improve search verification token
Scriptmatico 8a9a0d8
[TM-1677] lint issue
Scriptmatico File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
apps/user-service/src/auth/dto/verification-user-request.dto.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { ApiProperty } from "@nestjs/swagger"; | ||
import { IsNotEmpty } from "class-validator"; | ||
|
||
export class VerificationUserRequest { | ||
@IsNotEmpty() | ||
@ApiProperty() | ||
token: string; | ||
} |
9 changes: 9 additions & 0 deletions
9
apps/user-service/src/auth/dto/verification-user-response.dto.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { JsonApiDto } from "@terramatch-microservices/common/decorators"; | ||
import { JsonApiAttributes } from "@terramatch-microservices/common/dto/json-api-attributes"; | ||
import { ApiProperty } from "@nestjs/swagger"; | ||
|
||
@JsonApiDto({ type: "verifications", id: "uuid" }) | ||
export class VerificationUserResponseDto extends JsonApiAttributes<VerificationUserResponseDto> { | ||
@ApiProperty() | ||
verified: boolean; | ||
} |
53 changes: 53 additions & 0 deletions
53
apps/user-service/src/auth/verification-user.controller.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { Test, TestingModule } from "@nestjs/testing"; | ||
import { createMock, DeepMocked } from "@golevelup/ts-jest"; | ||
import { NotFoundException } from "@nestjs/common"; | ||
import { faker } from "@faker-js/faker"; | ||
import { VerificationUserController } from "./verification-user.controller"; | ||
import { VerificationUserService } from "./verification-user.service"; | ||
|
||
describe("VerificationUserController", () => { | ||
let controller: VerificationUserController; | ||
let verificationUserService: DeepMocked<VerificationUserService>; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
controllers: [VerificationUserController], | ||
providers: [ | ||
{ | ||
provide: VerificationUserService, | ||
useValue: (verificationUserService = createMock<VerificationUserService>()) | ||
} | ||
] | ||
}).compile(); | ||
|
||
controller = module.get<VerificationUserController>(VerificationUserController); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.restoreAllMocks(); | ||
}); | ||
|
||
it("should successfully verify a user when token is valid", async () => { | ||
const uuid = faker.string.uuid(); | ||
verificationUserService.verify.mockResolvedValue({ uuid, isVerified: true }); | ||
|
||
const result = await controller.verifyUser({ token: "my token" }); | ||
expect(result).toMatchObject({ | ||
data: { id: uuid, type: "verifications", attributes: { verified: true } } | ||
}); | ||
}); | ||
|
||
it("should throw NotFoundException if verification is not found", async () => { | ||
verificationUserService.verify.mockRejectedValue(new NotFoundException("Verification not found")); | ||
|
||
await expect(controller.verifyUser({ token: "my token" })).rejects.toThrow( | ||
new NotFoundException("Verification not found") | ||
); | ||
}); | ||
|
||
it("should throw NotFoundException if user is not found", async () => { | ||
verificationUserService.verify.mockRejectedValue(new NotFoundException("User not found")); | ||
|
||
await expect(controller.verifyUser({ token: "my token" })).rejects.toThrow(new NotFoundException("User not found")); | ||
}); | ||
}); |
28 changes: 28 additions & 0 deletions
28
apps/user-service/src/auth/verification-user.controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Controller, Body, Post, HttpStatus, BadRequestException } from "@nestjs/common"; | ||
import { ApiOperation } from "@nestjs/swagger"; | ||
import { ExceptionResponse, JsonApiResponse } from "@terramatch-microservices/common/decorators"; | ||
import { buildJsonApi, JsonApiDocument } from "@terramatch-microservices/common/util"; | ||
import { VerificationUserService } from "./verification-user.service"; | ||
import { VerificationUserRequest } from "./dto/verification-user-request.dto"; | ||
import { VerificationUserResponseDto } from "./dto/verification-user-response.dto"; | ||
import { NoBearerAuth } from "@terramatch-microservices/common/guards"; | ||
|
||
@Controller("auth/v3/verifications") | ||
export class VerificationUserController { | ||
constructor(private readonly verificationUserService: VerificationUserService) {} | ||
|
||
@Post() | ||
@NoBearerAuth | ||
@ApiOperation({ | ||
operationId: "verifyUser", | ||
description: "Receive a token to verify a user and return the verification status" | ||
}) | ||
@JsonApiResponse({ status: HttpStatus.CREATED, data: { type: VerificationUserResponseDto } }) | ||
@ExceptionResponse(BadRequestException, { description: "Invalid request" }) | ||
async verifyUser(@Body() { token }: VerificationUserRequest): Promise<JsonApiDocument> { | ||
const { uuid, isVerified } = await this.verificationUserService.verify(token); | ||
return buildJsonApi() | ||
.addData(uuid, new VerificationUserResponseDto({ verified: isVerified })) | ||
.document.serialize(); | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
apps/user-service/src/auth/verification-user.service.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { Test, TestingModule } from "@nestjs/testing"; | ||
import { User, Verification } from "@terramatch-microservices/database/entities"; | ||
import { NotFoundException } from "@nestjs/common"; | ||
import { VerificationUserService } from "./verification-user.service"; | ||
import { UserFactory } from "@terramatch-microservices/database/factories"; | ||
import { VerificationFactory } from "@terramatch-microservices/database/factories/verification.factory"; | ||
|
||
describe("VerificationUserService", () => { | ||
let service: VerificationUserService; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [VerificationUserService] | ||
}).compile(); | ||
|
||
service = module.get<VerificationUserService>(VerificationUserService); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.restoreAllMocks(); | ||
}); | ||
|
||
it("should throw when user is not found", async () => { | ||
jest.spyOn(User, "findOne").mockImplementation(() => Promise.resolve(null)); | ||
await expect(service.verify("my token")).rejects.toThrow(NotFoundException); | ||
}); | ||
|
||
it("should throw when verification is not found", async () => { | ||
const user = await UserFactory.create(); | ||
jest.spyOn(User, "findOne").mockImplementation(() => Promise.resolve(user)); | ||
jest.spyOn(Verification, "findOne").mockImplementation(() => Promise.resolve(null)); | ||
await expect(service.verify("my token")).rejects.toThrow(NotFoundException); | ||
}); | ||
|
||
it("should verify an user", async () => { | ||
const user = await UserFactory.create(); | ||
jest.spyOn(User, "findOne").mockImplementation(() => Promise.resolve(user)); | ||
const verification = await VerificationFactory.create({ userId: user.id }); | ||
jest.spyOn(Verification, "findOne").mockImplementation(() => Promise.resolve(verification)); | ||
const destroySpy = jest.spyOn(verification, "destroy").mockResolvedValue(); | ||
|
||
const result = await service.verify(verification.token); | ||
expect(user.emailAddressVerifiedAt).toBeDefined(); | ||
expect(destroySpy).toHaveBeenCalled(); | ||
expect(result).toStrictEqual({ uuid: user.uuid, isVerified: true }); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Injectable, NotFoundException, LoggerService } from "@nestjs/common"; | ||
import { User } from "@terramatch-microservices/database/entities"; | ||
import { TMLogService } from "@terramatch-microservices/common/util/tm-log.service"; | ||
import { Verification } from "@terramatch-microservices/database/entities/verification.entity"; | ||
|
||
@Injectable() | ||
export class VerificationUserService { | ||
protected readonly logger: LoggerService = new TMLogService(VerificationUserService.name); | ||
|
||
async verify(token: string) { | ||
const verification = await Verification.findOne({ where: { token } }); | ||
|
||
if (verification == null) { | ||
throw new NotFoundException("Verification token not found"); | ||
} | ||
|
||
const user = await User.findOne({ | ||
where: { id: verification.userId }, | ||
attributes: ["id", "uuid", "emailAddressVerifiedAt"] | ||
}); | ||
if (user == null) { | ||
throw new NotFoundException("User not found"); | ||
} | ||
try { | ||
user.emailAddressVerifiedAt = new Date(); | ||
await user.save(); | ||
await verification.destroy(); | ||
return { uuid: user.uuid, isVerified: true }; | ||
} catch (error) { | ||
this.logger.error(error); | ||
return { uuid: user.uuid, isVerified: false }; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { AutoIncrement, Column, Model, PrimaryKey, Table } from "sequelize-typescript"; | ||
import { BIGINT, STRING } from "sequelize"; | ||
|
||
@Table({ tableName: "verifications", underscored: true }) | ||
export class Verification extends Model<Verification> { | ||
@PrimaryKey | ||
@AutoIncrement | ||
@Column(BIGINT.UNSIGNED) | ||
override id: number; | ||
|
||
@Column(STRING) | ||
token: string | null; | ||
|
||
@Column(BIGINT.UNSIGNED) | ||
userId: number; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This userId should get a foreign key and this entity should get a relationship to user:
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { FactoryGirl } from "factory-girl-ts"; | ||
import { faker } from "@faker-js/faker"; | ||
import { Verification } from "../entities"; | ||
|
||
export const VerificationFactory = FactoryGirl.define(Verification, async () => ({ | ||
token: faker.lorem.word(), | ||
userId: faker.number | ||
})); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the BelongsTo change requested on the verification entity, this can be a single DB query:
And then use
verification.user
below instead ofuser
.