-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/#31 cms profile page #43
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { NgModule } from '@angular/core'; | ||
| import { RouterModule, Routes } from '@angular/router'; | ||
|
|
||
| import { ProfileComponent } from './profile.component'; | ||
|
|
||
| const routes: Routes = [ | ||
| { path: '', component: ProfileComponent } | ||
| ]; | ||
|
|
||
| @NgModule({ | ||
| imports: [RouterModule.forChild(routes)], | ||
| exports: [RouterModule] | ||
| }) | ||
| export class ProfileRoutingModule { } | ||
|
|
||
| export const routedComponents = [ProfileComponent]; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| <h3>Profile</h3> | ||
| <form [formGroup]="profileForm"> | ||
| <mat-form-field> | ||
| <input matInput type="text" placeholder="Email" formControlName="email"> | ||
| </mat-form-field> | ||
| <button mat-raised-button type="button" (click)="onClickUpdate()">Update</button> | ||
| </form> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| import { Component, OnInit } from '@angular/core'; | ||
| import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; | ||
|
|
||
| import { Role, User } from '../../../common/entities'; | ||
|
|
||
| import { UserService } from '../core'; | ||
|
|
||
| @Component({ | ||
| selector: 'profile', | ||
| templateUrl: 'profile.component.html', | ||
| styleUrls: ['profile.component.scss'] | ||
| }) | ||
|
|
||
| export class ProfileComponent implements OnInit { | ||
|
|
||
| public user: User; | ||
| public profileForm: FormGroup; | ||
|
|
||
| constructor( | ||
| private formBuilder: FormBuilder, | ||
|
Member
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. Let's practice declaring constructor params as
Author
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. Alright |
||
| private userService: UserService | ||
| ) { } | ||
|
|
||
| // interface methods | ||
| public ngOnInit() { | ||
| this.loadProfile(); | ||
| this.buildProfileForm(); | ||
| this.patchProfileFormValue(this.user); | ||
| } | ||
|
|
||
| // event methods | ||
| public onClickUpdate() { | ||
| const data = this.profileForm.getRawValue(); | ||
| this.updateProfile(data); | ||
| } | ||
|
|
||
| private loadProfile() { | ||
| this.userService.currentUser$.subscribe((user: User) => { | ||
|
Member
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. Break the line in the |
||
| this.user = user; | ||
| }); | ||
| } | ||
|
|
||
| private buildProfileForm() { | ||
| this.profileForm = this.formBuilder.group({ | ||
| email: ['', Validators.required] | ||
| }); | ||
| } | ||
|
|
||
| private patchProfileFormValue(user: User) { | ||
| this.profileForm.patchValue({ | ||
| email: user.email | ||
| }); | ||
| } | ||
|
|
||
| private updateProfile(data: User) { | ||
| this.userService | ||
| .updateProfile(data) | ||
| .subscribe((user: User) => { | ||
| this.user = user; | ||
| this.patchProfileFormValue(this.user); | ||
| }, (error: Error) => { | ||
| console.log(error); | ||
| }); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { NgModule } from '@angular/core'; | ||
|
|
||
| import { SharedModule } from '../shared'; | ||
|
|
||
| import { ProfileRoutingModule, routedComponents } from './profile-routing.module'; | ||
|
|
||
| @NgModule({ | ||
| imports: [ | ||
| SharedModule, | ||
| ProfileRoutingModule | ||
| ], | ||
| exports: [], | ||
| declarations: [ | ||
| routedComponents | ||
| ], | ||
| providers: [] | ||
| }) | ||
| export class ProfileModule { } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -105,4 +105,20 @@ describe('UserController', () => { | |
| .expect(200); | ||
| }); | ||
| }); | ||
|
|
||
| describe.only('updateProfileById', async () => { | ||
|
Member
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. Please remove |
||
| it('should update current user, /api/users/profile', async () => { | ||
| const data = await userService.login({ | ||
| email: `admin@test.com`, | ||
| password: `test` | ||
| }); | ||
| const response = await server | ||
| .put('/api/users/profile') | ||
| .set('x-access-token', data.token) | ||
| .send({ | ||
| email: 'updated-admin@test.com' | ||
| }); | ||
| expect(response.body).to.have.property('email', 'updated-admin@test.com'); | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common'; | ||
| import { Body, Controller, Get, Post, UseGuards, Put } from '@nestjs/common'; | ||
|
|
||
| import { AccessTokenGuard, CurrentUser } from '../core'; | ||
|
|
||
|
|
@@ -30,4 +30,14 @@ export class UserController { | |
| public getUsers() { | ||
| return this.userService.getUsers(); | ||
| } | ||
|
|
||
| @Put('profile') | ||
|
Member
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. Doesn't the proper http resource url for I believe this should be something
Member
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. Oh I see you are getting it to the
Author
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. hmm you are right. Do you have any idea on how to update user profile and at the same time can edit by the admin?
Author
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. maybe a different endpoint?
Member
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. Why not have a generic endpoint for updating an account? Like the resource url above mentioned as it just needed an accessToken to allow or disallow certain roles. I think having the currentUser and an admin decorator will satisfy the permissions. |
||
| @UseGuards(AccessTokenGuard) | ||
| public async updateProfile( | ||
| @CurrentUser() currentUser: User, | ||
| @Body() user: User | ||
| ): Promise<User> { | ||
| await this.userService.updateProfileById(currentUser.id, user); | ||
| return this.userService.getUserById(currentUser.id); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,8 +22,14 @@ export class UserRepository extends Repository<User> { | |
|
|
||
| public async getUserByEmail(email: string): Promise<User> { | ||
| return this.findOne({ | ||
| where: { email } | ||
| where: { email }, | ||
| relations: ['roles'] | ||
| }); | ||
| } | ||
|
|
||
| public updateUserById(id: number, data: User): Promise<void> { | ||
| delete data.created; | ||
|
Member
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. Why delete the |
||
| return this.updateById(id, data); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import { Component, Inject, UnauthorizedException } from '@nestjs/common'; | ||
| import { classToPlain } from 'class-transformer'; | ||
|
|
||
| import { BcryptService, JsonWebTokenService } from '../core'; | ||
| import { User } from './user.entity'; | ||
|
|
@@ -22,11 +23,7 @@ export class UserService { | |
| if (!isValidPassword) { | ||
| throw new UnauthorizedException(); | ||
| } | ||
| const token = this.jwtService.sign({ | ||
| id: user.id, | ||
| email: user.email, | ||
| roles: user.roles | ||
| }); | ||
| const token = this.jwtService.sign(classToPlain(user)); | ||
|
Member
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. Hahaha this is good haven't thought about this 👌 but I have a question, don't you think that this will always be unique to each other? Or do you think we should update the passed
Author
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. I think jwt returns unique token even the data is always the same |
||
| return { user, token }; | ||
| } | ||
|
|
||
|
|
@@ -39,4 +36,9 @@ export class UserService { | |
| public async getUsers() { | ||
| return this.userRepository.getUsers({}); | ||
| } | ||
|
|
||
| public updateProfileById(id: number, data: User) { | ||
| delete data.password; | ||
|
Member
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. Wait, we cannot update without
Author
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. Actually I want to separate the endpoint of changing password. I don't want to update the password always for just updating the profile. I can't see any real application that does that. I think it is better to break down all properties that needs to be updated |
||
| return this.userRepository.updateUserById(id, data); | ||
| } | ||
| } | ||
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.
Sorry but
ProfileRoutingModuleis not a good class name hahaha why not name files like this asroutes.ts?So when I look at the files inside the folder I could see the ff:
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.
Ahh I believe I posted before that we will johnpapa's style guide in client and in server side the nest-js style guide extension in vscode
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.
Does john papa's style do that naming?