Skip to content

Commit 2cf6122

Browse files
IdanIdan
authored andcommitted
Implement: update user password feature
Add validation and checks for Data Transfor Object properties' values and types Check business coherence before updating user's password Refactor call for database query to retrieve user role by centralising it in the services folder Add update user password feature tests and separate them by semantic, both in the feature folder and in the tests folder Implement customised error codes for update user password feature
1 parent 260a82f commit 2cf6122

40 files changed

Lines changed: 4251 additions & 31 deletions

File tree

.github/workflows/docker-image.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Docker Compose CI
22

33
on:
44
push:
5-
branches: [ "main" ]
5+
branches: [ "main", "feature/update-user-password" ]
66
pull_request:
7-
branches: [ "main" ]
7+
branches: [ "main", "feature/update-user-password" ]
88
workflow_dispatch:
99

1010
jobs:

app.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import { RegisterUserRouter } from "./features/RegisterUser/RegisterUserMain/rou
33
import { AuthenticateUserRouter } from './features/AuthenticateUser/AuthenticateUserMain/router';
44
import { RetrieveUserRouter } from './features/RetrieveUser/RetrieveUserMain/router';
55
import { UpdateUserInformationRouter } from './features/UpdateUserInformation/UpdateUserInformationMain/router';
6+
import { UpdateUserPasswordRouter } from './features/UpdateUserPassword/UpdateUserPasswordMain/router';
67

78
const app: Express = express();
89

910
app.use("/user", RegisterUserRouter);
1011
app.use("/user", RetrieveUserRouter);
1112
app.use("/user", UpdateUserInformationRouter);
13+
app.use("/user", UpdateUserPasswordRouter);
1214
app.use("/login", AuthenticateUserRouter);
1315

1416
export { app };

features/AuthenticateUser/AuthenticateUserMain/router.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ AuthenticateUserRouter.post("/", jsonParser, async (request: Request, response:
4949
})
5050
});
5151
} else {
52-
response.status(500)
52+
response.status(401)
5353
.json("Couldn't log in");
5454
}
5555

features/UpdateUserInformation/UpdateUserInformationMain/database/UserInformationUpdateOnPostgreSQLDatabase.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,6 @@ export default class UserInformationUpdateOnPostgreSQLDatabase implements Update
4848

4949
public async getUserRole(userId: string): Promise<string|false>
5050
{
51-
const queryResult: QueryResult|undefined = await this.postgreSQLDatabase.query(
52-
"SELECT role FROM application_users WHERE id = $1",
53-
[userId]
54-
);
55-
56-
if (queryResult !== undefined && queryResult.rowCount !== null && queryResult.rowCount > 0) {
57-
switch(queryResult.rows[0].role) {
58-
case "USER_ROLE":
59-
return "User";
60-
61-
default:
62-
return false;
63-
}
64-
} else {
65-
return false;
66-
}
51+
return await this.postgreSQLDatabase.getUserRole(userId);
6752
}
6853
}

features/UpdateUserInformation/UpdateUserInformationMain/router.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,29 @@ import type { Request, Response, Router } from "express";
33
import bodyParser from "body-parser";
44
import type { NextHandleFunction } from "connect";
55

6-
import UpdateUserInformationAuthenticationMiddleware from "./middleware/UpdateUserInformationAuthenticationMiddleware";
76
import UserInformationUpdateOnPostgreSQLDatabase from "./database/UserInformationUpdateOnPostgreSQLDatabase";
87
import UpdateUserInformationInputJoiValidation from "./validation/UpdateUserInformationInputJoiValidation";
98
import UpdateUserInformationRequest from "../UpdateUserInformationController/UpdateUserInformationRequest";
109
import UpdateUserInformationController from "../UpdateUserInformationController/UpdateUserInformationController";
1110
import UpdateUserInformationResponse from "../UpdateUserInformationController/UpdateUserInformationResponse";
1211

12+
import AuthenticationMiddleware from "../../../services/middleware/AuthenticationMiddleware";
13+
1314
import BadRequestError from "../../../services/errors/BadRequestError";
1415
import UnauthorisedActionError from "../../../services/errors/UnauthorisedActionError";
1516

16-
1717
const UpdateUserInformationRouter: Router = express.Router();
1818
const jsonParser: NextHandleFunction = bodyParser.json();
1919

20-
UpdateUserInformationRouter.put("/", jsonParser, UpdateUserInformationAuthenticationMiddleware, async (request: Request, response: Response) => {
20+
UpdateUserInformationRouter.put("/", jsonParser, AuthenticationMiddleware, async (request: Request, response: Response) => {
2121
const userInformationUpdateOnPostgreSQLDatabase: UserInformationUpdateOnPostgreSQLDatabase = new UserInformationUpdateOnPostgreSQLDatabase();
2222

2323
try {
2424
const updateUserInformationInputJoiValidation: UpdateUserInformationInputJoiValidation = new UpdateUserInformationInputJoiValidation();
2525

2626
await userInformationUpdateOnPostgreSQLDatabase.connect();
2727

28-
const updateUserInformationRequest = composeRetrieveUserRequest(request.body, request.params.userId);
28+
const updateUserInformationRequest = composeUpdateUserInformationRequest(request.body, request.params.userId);
2929

3030
const updateUserInformationController: UpdateUserInformationController = new UpdateUserInformationController();
3131
const updateUserInformationResponse: UpdateUserInformationResponse = await updateUserInformationController.handleUpdateUserInformationRequest(updateUserInformationRequest, userInformationUpdateOnPostgreSQLDatabase, updateUserInformationInputJoiValidation);
@@ -53,7 +53,7 @@ UpdateUserInformationRouter.put("/", jsonParser, UpdateUserInformationAuthentica
5353
}
5454
});
5555

56-
const composeRetrieveUserRequest = (requestBody: any, userId: string): UpdateUserInformationRequest => {
56+
const composeUpdateUserInformationRequest = (requestBody: any, userId: string): UpdateUserInformationRequest => {
5757
const updateUserInformationRequest = new UpdateUserInformationRequest();
5858
updateUserInformationRequest.setUserId(userId)
5959
.setUpdatedUserId(requestBody.updatedUserId)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import UserPasswordUpdateInput from "../UpdateUserPasswordUseCase/UserPasswordUpdateInput";
2+
import UpdateUserPasswordInput from "../UpdateUserPasswordUseCase/UpdateUserPasswordInput";
3+
import UserPasswordUpdateOutput from "../UpdateUserPasswordUseCase/UserPasswordUpdateOutput";
4+
import UpdateUserPasswordRequest from "./UpdateUserPasswordRequest";
5+
import UpdateUserPasswordResponse from "./UpdateUserPasswordResponse";
6+
import UpdateUserPasswordGateway from "../UpdateUserPasswordUseCase/UpdateUserPasswordGateway";
7+
import UserPasswordUpdateInputValidator from "../UpdateUserPasswordUseCase/UserPasswordUpdateInputValidator";
8+
import UpdateUserPasswordPresenter from "./UpdateUserPasswordPresenter";
9+
import UpdateUserPasswordUseCase from "../UpdateUserPasswordUseCase/UpdateUserPasswordUseCase";
10+
11+
export default class UpdateUserPasswordController implements UpdateUserPasswordInput
12+
{
13+
public async handleUpdateUserPasswordRequest(updateUserPasswordRequest: UpdateUserPasswordRequest, updateUserPasswordGateway: UpdateUserPasswordGateway, userPasswordUpdateInputValidator: UserPasswordUpdateInputValidator): Promise<UpdateUserPasswordResponse>
14+
{
15+
const userPasswordUpdateInput: UserPasswordUpdateInput = await this.composeUserPasswordUpdateInput(updateUserPasswordRequest, userPasswordUpdateInputValidator);
16+
const userPasswordUpdateOutput: UserPasswordUpdateOutput = await this.updateUserPassword(userPasswordUpdateInput, updateUserPasswordGateway);
17+
18+
const updateUserPasswordPresenter: UpdateUserPasswordPresenter = new UpdateUserPasswordPresenter();
19+
updateUserPasswordPresenter.retrieveUserPasswordUpdateOutput(userPasswordUpdateOutput);
20+
21+
return updateUserPasswordPresenter.getUpdateUserPasswordResponse();
22+
}
23+
24+
public async updateUserPassword(userPasswordUpdateInput: UserPasswordUpdateInput, updateUserPasswordGateway: UpdateUserPasswordGateway): Promise<UserPasswordUpdateOutput>
25+
{
26+
const updateUserPasswordUseCase: UpdateUserPasswordUseCase = new UpdateUserPasswordUseCase();
27+
const userPasswordUpdateOutput: UserPasswordUpdateOutput = await updateUserPasswordUseCase.updateUserPassword(userPasswordUpdateInput, updateUserPasswordGateway);
28+
29+
return userPasswordUpdateOutput;
30+
}
31+
32+
private async composeUserPasswordUpdateInput(updateUserPasswordRequest: UpdateUserPasswordRequest, userPasswordUpdateInputValidator: UserPasswordUpdateInputValidator): Promise<UserPasswordUpdateInput>
33+
{
34+
const userPasswordUpdateInput: UserPasswordUpdateInput = new UserPasswordUpdateInput(userPasswordUpdateInputValidator);
35+
await userPasswordUpdateInput.setUserId(updateUserPasswordRequest.getUserId());
36+
await userPasswordUpdateInput.setUpdatedUserId(updateUserPasswordRequest.getUpdatedUserId());
37+
await userPasswordUpdateInput.setOrignalPassword(updateUserPasswordRequest.getOrignalPassword());
38+
await userPasswordUpdateInput.setChangedPassword(updateUserPasswordRequest.getChangedPassword());
39+
await userPasswordUpdateInput.setChangedPasswordConfirmation(updateUserPasswordRequest.getChangedPasswordConfirmation());
40+
41+
return userPasswordUpdateInput;
42+
}
43+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import UpdateUserPasswordResponse from "./UpdateUserPasswordResponse";
2+
import UserPasswordUpdateOutput from "../UpdateUserPasswordUseCase/UserPasswordUpdateOutput";
3+
import UpdateUserPasswordOutput from "../UpdateUserPasswordUseCase/UpdateUserPasswordOutput";
4+
5+
export default class UpdateUserPasswordPresenter implements UpdateUserPasswordOutput
6+
{
7+
private updateUserPasswordResponse!: UpdateUserPasswordResponse;
8+
9+
public getUpdateUserPasswordResponse(): UpdateUserPasswordResponse
10+
{
11+
return this.updateUserPasswordResponse;
12+
}
13+
14+
public retrieveUserPasswordUpdateOutput(userPasswordUpdateOutput: UserPasswordUpdateOutput): void
15+
{
16+
const updateUserPasswordResponse = new UpdateUserPasswordResponse();
17+
18+
updateUserPasswordResponse.setWetherTheUserPasswordWasUpdated(userPasswordUpdateOutput.userPasswordWasUpdated());
19+
20+
this.updateUserPasswordResponse = updateUserPasswordResponse;
21+
}
22+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import BadRequestError from "../../../services/errors/BadRequestError";
2+
import UpdateUserPasswordTypeValidation from "./UpdateUserPasswordTypeValidation";
3+
4+
export default class UpdateUserPasswordRequest
5+
{
6+
private userId!: string;
7+
private updatedUserId!: string;
8+
private orignalPassword!: string;
9+
private changedPassword!: string;
10+
private changedPasswordConfirmation!: string;
11+
private updateUserPasswordTypeValidation: UpdateUserPasswordTypeValidation
12+
13+
constructor(updateUserPasswordTypeValidation: UpdateUserPasswordTypeValidation)
14+
{
15+
this.updateUserPasswordTypeValidation = updateUserPasswordTypeValidation;
16+
}
17+
18+
public getUserId(): string
19+
{
20+
return this.userId;
21+
}
22+
23+
public setUserId(userId: string): UpdateUserPasswordRequest
24+
{
25+
this.updateUserPasswordTypeValidation.isString(userId, "User id");
26+
27+
if (userId.length === 0) {
28+
throw new BadRequestError("Cannot update a user's password without a user id", "user_id_not_informed");
29+
}
30+
31+
this.userId = userId;
32+
33+
return this;
34+
}
35+
36+
public getUpdatedUserId(): string
37+
{
38+
return this.updatedUserId;
39+
}
40+
41+
public setUpdatedUserId(updatedUserId: string): UpdateUserPasswordRequest
42+
{
43+
this.updateUserPasswordTypeValidation.isString(updatedUserId, "Updated user id");
44+
45+
if (updatedUserId.length === 0) {
46+
throw new BadRequestError("Cannot update a user's password without an updated user id", "updated_user_id_not_informed");
47+
}
48+
49+
this.updatedUserId = updatedUserId;
50+
51+
return this;
52+
}
53+
54+
public getOrignalPassword(): string
55+
{
56+
return this.orignalPassword;
57+
}
58+
59+
public setOrignalPassword(orignalPassword: string): UpdateUserPasswordRequest
60+
{
61+
this.updateUserPasswordTypeValidation.isString(orignalPassword, "Original Password");
62+
63+
if (orignalPassword.length === 0) {
64+
throw new BadRequestError("Cannot update a user's password without an original password informed", "original_password_not_informed");
65+
}
66+
67+
this.orignalPassword = orignalPassword;
68+
69+
return this;
70+
}
71+
72+
public getChangedPassword(): string
73+
{
74+
return this.changedPassword;
75+
}
76+
77+
public setChangedPassword(changedPassword: string): UpdateUserPasswordRequest
78+
{
79+
this.updateUserPasswordTypeValidation.isString(changedPassword, "Changed Password");
80+
81+
if (changedPassword.length === 0) {
82+
throw new BadRequestError("Cannot update a user's password without a changed password informed", "changed_password_not_informed");
83+
}
84+
85+
this.changedPassword = changedPassword;
86+
87+
return this;
88+
}
89+
90+
public getChangedPasswordConfirmation(): string
91+
{
92+
return this.changedPasswordConfirmation;
93+
}
94+
95+
public setChangedPasswordConfirmation(changedPasswordConfirmation: string): UpdateUserPasswordRequest
96+
{
97+
this.updateUserPasswordTypeValidation.isString(changedPasswordConfirmation, "Changed password confirmation");
98+
99+
if (changedPasswordConfirmation.length === 0) {
100+
throw new BadRequestError("Cannot update a user's password without a changed password confirmation informed", "changed_password_confirmation_not_informed");
101+
}
102+
103+
this.changedPasswordConfirmation = changedPasswordConfirmation;
104+
105+
return this;
106+
}
107+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export default class UpdateUserPasswordResponse
2+
{
3+
private _userPasswordWasUpdated!: boolean;
4+
5+
public userPasswordWasUpdated(): boolean
6+
{
7+
return this._userPasswordWasUpdated;
8+
}
9+
10+
public setWetherTheUserPasswordWasUpdated(userPasswordWasUpdated: boolean): UpdateUserPasswordResponse
11+
{
12+
this._userPasswordWasUpdated = userPasswordWasUpdated;
13+
14+
return this;
15+
}
16+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default interface UpdateUserPasswordTypeValidation
2+
{
3+
isString(testedVariable: any, variableName: string): boolean;
4+
}

0 commit comments

Comments
 (0)