Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • miburgos/spring2021-sellpoint
  • tdt4140-group-58/spring2021-sellpoint
2 results
Show changes
Commits on Source (14)
Showing
with 363 additions and 15 deletions
......@@ -6,6 +6,7 @@ import { PostListComponent } from './posts/post-list/post-list.component';
import { UserRegistrationFormComponent } from './users/user-registration-form/user-registration-form.component';
import { UserLoginFormComponent } from './users/user-login-form/user-login-form.component';
import { UserProfileComponent } from './users/user-profile/user-profile.component';
import { UserGuestProfileComponent } from './users/user-guest-profile/user-guest-profile.component';
const routes: Routes = [
{ path: 'annonse/ny', component: PostFormComponent },
......@@ -13,6 +14,7 @@ const routes: Routes = [
{ path: 'annonse', component: PostListComponent },
{ path: 'annonse/:id', component: PostDetailsComponent },
{ path: 'user/:id', component: UserGuestProfileComponent },
{ path: 'profile', component: UserProfileComponent },
{ path: 'register', component: UserRegistrationFormComponent },
{ path: 'login', component: UserLoginFormComponent }
......
......@@ -128,6 +128,7 @@ div.splash {
}
div.wrapper {
padding: 5%;
min-height: calc(45vh - 200px);
}
div.footer {
......
......@@ -28,7 +28,7 @@ export class AuthService {
// Get user data from JWT token
const token = localStorage.getItem('token');
const user_data = JSON.parse(atob(token.split(".")[1])).data[0];
return new User(user_data);
}
return new User();
......
......@@ -7,6 +7,7 @@ export class User implements Deserializable, Serializable {
private email: string;
private password: string;
private create_time: Date;
private isAdmin: number;
constructor(input: any = null) {
if (input) {
......@@ -17,21 +18,24 @@ export class User implements Deserializable, Serializable {
this.email = null;
this.password = null;
this.create_time = new Date();
this.isAdmin = 0;
}
}
deserialize(input: Object): this {
Object.assign(this, input);
console.log(this);
return this;
}
serialize(): Object {
return {
userId: this.userId,
username: this.username,
email: this.email,
password: this.password,
create_time: this.create_time
create_time: this.create_time,
isAdmin: this.isAdmin,
};
}
......@@ -75,4 +79,11 @@ export class User implements Deserializable, Serializable {
this.create_time = create_time;
}
get getIsAdmin() {
return this.isAdmin;
}
set setIsAdmin(isAdmin: number) {
isAdmin = this.isAdmin;
}
}
\ No newline at end of file
......@@ -7,7 +7,12 @@
<p>Publisert: {{post.getTimestamp}}</p>
<p>Eier: {{post.getOwner}}</p>
<p>a: {{isAdmin}}</p>
<p>b: {{userId}}</p>
<div *ngIf="userId == post.getOwner">
<app-button text="Rediger annonse" (click)="editPost()"></app-button>
</div>
<div *ngIf="userId == post.getOwner || isAdmin == 1">
<app-button text="Slett annonse" (click)="deletePost()"></app-button>
</div>
......@@ -3,6 +3,7 @@ import { Post } from 'src/app/models/post.model';
import { PostService } from '../post.service';
import { ActivatedRoute, Router } from '@angular/router'
import { AuthService } from 'src/app/authentication/auth.service';
import { User } from 'src/app/models/user.model';
@Component({
selector: 'app-post-details',
......@@ -12,13 +13,18 @@ import { AuthService } from 'src/app/authentication/auth.service';
export class PostDetailsComponent implements OnInit {
post: Post = new Post();
user: User = new User();
isAdmin: number = 0;
userId: number = 0;
constructor(private postService: PostService, private activatedRoute: ActivatedRoute, private router: Router, private authService: AuthService) { }
ngOnInit(): void {
// Gets ID from current user
this.userId = this.authService.getCurrentUser(false).getUserId;
// Gets current user information
this.user = this.authService.getCurrentUser(false);
// If user is logged in, assign userId and isAdmin
this.userId = this.user.getUserId; // 0
this.isAdmin = this.user.getIsAdmin; // 0
// Gets id parameter from URL
const id = this.activatedRoute.snapshot.params["id"];
......
......@@ -20,7 +20,8 @@ import { PostThumbnailComponent } from './post-thumbnail/post-thumbnail.componen
CommonModule,
SharedModule,
FormsModule
]
],
exports: [ PostThumbnailComponent ]
})
export class PostModule { }
......@@ -331,5 +331,76 @@ describe('PostService', () => {
req.error(new ErrorEvent("400"));
});
});
describe('getPostsByUserId', () => {
it('should get posts by user', () => {
// Gets posts by category and checks values
service.getPostsByUserId(2).then(posts => {
for (let i = 0; i < posts.length; i++) {
expect(posts[i].getId).toBe(i + 1);
expect(posts[i].getTitle).toBe("Test" + (i + 1));
expect(posts[i].getOwner).toBe("2");
}
}).catch(error => {
fail();
});
// Mocks and checks HTTP request
const req = httpMock.expectOne("api/post/?userId=2");
expect(req.request.method).toBe("GET");
req.flush({
data: [{
id: 1,
title: "Test1",
description: "TestDescription",
timestamp: 23947298234,
owner: "2",
imageUrl: null,
price: 49,
categoryid: 2
}, {
id: 2,
title: "Test2",
description: "TestDescription",
timestamp: 23453246527,
owner: "2",
imageUrl: null,
price: 159,
categoryid: 2
}]
});
});
it('should reject on invalid post', () => {
// Gets invalid post, should go to catch
service.getPostsByUserId(420).then(posts => {
fail();
}).catch(error => {});
// Mocks and checks HTTP request
const req = httpMock.expectOne("api/post/?userId=420");
expect(req.request.method).toBe("GET");
req.flush({
data: [{
id: 0,
title: "Test",
description: "TestDescription",
timestamp: 23947298
}]
});
});
it('should reject on http error', () => {
// Gets HTTP error instead of post, should catch
service.getPostsByUserId(520).then(posts => {
fail();
}).catch(error => {});
// Mocks and checks HTTP request
const req = httpMock.expectOne("api/post/?userId=520");
expect(req.request.method).toBe("GET");
req.error(new ErrorEvent("400"));
});
});
});
......@@ -232,4 +232,38 @@ export class PostService {
private get_posts_by_category(categoryId: number) {
return this.http.get(this.postUrl, {params: {categoryid: String(categoryId)}});
}
/**
* Get all posts in database by specified user.
*/
getPostsByUserId(userId: number): Promise<Array<Post>> {
return new Promise<Array<Post>>(
(resolve, reject) => {
this.get_posts_by_user_id(userId).subscribe((data: any) => {
try {
let outputPosts = [];
for (let post of data.data) {
outputPosts.push(new Post(post));
if (!post.id || post.id == 0) {
reject("Could not deserialize Post");
return;
}
}
resolve(outputPosts);
} catch (err: any) {
reject(err);
}
},
(err: any) => {
console.log(err.message);
reject(err);
});
}
);
}
private get_posts_by_user_id(userId: number) {
return this.http.get(this.postUrl, {params: {userId: String(userId)}});
}
}
<h1>User information</h1>
<p>Userid: {{user.getUserId}}</p>
<p>username: {{user.getUsername}}</p>
<p>email: {{user.getEmail}}</p>
<h1>Users posts:</h1>
<app-post-thumbnail *ngFor="let post of userPosts" [post]="post"></app-post-thumbnail>
\ No newline at end of file
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { User } from 'src/app/models/user.model';
import { UserService } from '../user.service';
import { UserGuestProfileComponent } from './user-guest-profile.component';
describe('UserGuestProfileComponent', () => {
let component: UserGuestProfileComponent;
let fixture: ComponentFixture<UserGuestProfileComponent>;
let mockPostService;
const dateNow = new Date();
beforeEach(async () => {
// UserService mock setup
mockPostService = jasmine.createSpyObj(['getUser']);
mockPostService.getUser.and.returnValue(
new Promise<User>(
(resolve) => {
resolve(new User({
userId: 1,
username: "Test",
email: "test@email.com",
password: "testPassword",
create_time: dateNow,
}));
})
);
await TestBed.configureTestingModule({
declarations: [ UserGuestProfileComponent ],
imports: [ HttpClientTestingModule, RouterTestingModule ],
providers: [
{ provide: ActivatedRoute, useValue: { snapshot: {params: {id: 1}}}},
{ provide: UserService, useValue: mockPostService }
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(UserGuestProfileComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should get user with id from url parameter', async () => {
// Waits for ngOnInit and checks that we get user
expect(component.user).not.toBeNull();
fixture.whenStable().then(() => {
expect(mockPostService.getUser).toHaveBeenCalledWith(1);
expect(component.user).toEqual(new User({
userId: 1,
username: "Test",
email: "test@email.com",
password: "testPassword",
create_time: dateNow,
}));
});
});
});
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'
import { AuthService } from 'src/app/authentication/auth.service';
import { Post } from 'src/app/models/post.model';
import { User } from 'src/app/models/user.model';
import { UserService } from '../user.service';
import { PostService } from 'src/app/posts/post.service';
@Component({
selector: 'app-user-guest-profile',
templateUrl: './user-guest-profile.component.html',
styleUrls: ['./user-guest-profile.component.scss']
})
export class UserGuestProfileComponent implements OnInit {
user: User = new User();
userPosts: Array<Post> = [];
constructor(private userService: UserService, private authService: AuthService, private postService: PostService, private activatedRoute: ActivatedRoute, private router: Router) { }
ngOnInit(): void {
// Gets id parameter from URL
const id = +this.activatedRoute.snapshot.params["id"];
// Gets User with id from database
this.userService.getUser(id).then(user => {
this.user = user;
this.getPosts();
}).catch (error => {
// Navigate to home on error or invalid userid
console.log("Error getting user: " + error);
this.router.navigateByUrl("/");
});
}
getPosts() {
// Gets all posts from database, and displays them
this.postService.getPostsByUserId(this.user.getUserId).then(posts => {
this.userPosts = posts;
console.log(this.userPosts,[this.user.getUserId]);
}).catch(error => {
console.log(error);
});
}
}
<p>user-profile works!</p>
<p>Userid: {{user.getUserId}}</p>
<p>username: {{user.getUsername}}</p>
<p>email: {{user.getEmail}}</p>
\ No newline at end of file
<p>email: {{user.getEmail}}</p>
<div>
<app-button text="Slett bruker" (click)="deleteUser()"></app-button>
</div>
\ No newline at end of file
......@@ -3,14 +3,16 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AuthService } from 'src/app/authentication/auth.service';
import { User } from 'src/app/models/user.model';
import { SharedModule } from 'src/app/shared/shared.module';
import { UserLoginFormComponent } from '../user-login-form/user-login-form.component';
import { UserService } from '../user.service';
import { UserProfileComponent } from './user-profile.component';
describe('UserProfileComponent', () => {
let component: UserProfileComponent;
let fixture: ComponentFixture<UserProfileComponent>;
let mockAuthService;
let mockAuthService, mockUserService;
beforeEach(async () => {
// AuthService mock setup
......@@ -20,8 +22,18 @@ describe('UserProfileComponent', () => {
username: "tester",
email: "test@test.com",
password: "1234",
create_time: 513498
create_time: 513498,
isAdmin: 0
}));
// UserService mock setup
mockUserService = jasmine.createSpyObj(['deleteUser']);
mockUserService.deleteUser.and.returnValue(
new Promise<any>(
(resolve) => {
resolve({data: []})
})
);
await TestBed.configureTestingModule({
......@@ -33,7 +45,8 @@ describe('UserProfileComponent', () => {
])
],
providers: [
{ provide: AuthService, useValue: mockAuthService }
{ provide: AuthService, useValue: mockAuthService },
{ provide: UserService, useValue: mockUserService }
]
})
.compileComponents();
......@@ -57,7 +70,15 @@ describe('UserProfileComponent', () => {
username: "tester",
email: "test@test.com",
password: "1234",
create_time: 513498
create_time: 513498,
isAdmin: 0
}));
});
it('should delete current user', async () => {
// Waits for ngOnInit and checks that we can delete the current user
await fixture.whenStable();
component.deleteUser();
expect(mockUserService.deleteUser).toHaveBeenCalledWith(4);
});
});
......@@ -13,9 +13,22 @@ import { UserService } from '../user.service';
export class UserProfileComponent implements OnInit {
user: User = new User();
constructor(private authService: AuthService, private router: Router) { }
constructor(private authService: AuthService, private userService: UserService, private router: Router) { }
ngOnInit(): void {
this.user = this.authService.getCurrentUser();
}
/**
* Deletes user in database and navigates to login
*/
deleteUser() {
this.userService.deleteUser(this.user.getUserId).then(data => {
console.log("Successfully deleted user: " + this.user.getUserId);
this.authService.logout();
this.router.navigateByUrl("/login");
}).catch(error => {
console.log(error);
});
}
}
......@@ -60,6 +60,7 @@ export class UserRegistrationFormComponent implements OnInit {
username: this.username,
email: this.email,
password: this.password,
isAdmin: 0,
});
// Adds user to database and redirects to the homepage afterwards
......
......@@ -2,21 +2,25 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from '../shared/shared.module';
import { FormsModule } from '@angular/forms';
import { PostModule } from '../posts/post.module';
import { UserRegistrationFormComponent } from './user-registration-form/user-registration-form.component';
import { UserProfileComponent } from './user-profile/user-profile.component';
import { UserLoginFormComponent } from './user-login-form/user-login-form.component';
import { UserGuestProfileComponent } from './user-guest-profile/user-guest-profile.component';
@NgModule({
declarations: [
UserRegistrationFormComponent,
UserProfileComponent,
UserLoginFormComponent
UserLoginFormComponent,
UserGuestProfileComponent
],
imports: [
CommonModule,
SharedModule,
FormsModule
FormsModule,
PostModule
]
})
......
......@@ -40,6 +40,7 @@ describe('UserService', () => {
email: "blob@planet.us",
password: "Hyttepine",
create_time: 1613552549000,
isAdmin: 0
}]
});
});
......@@ -59,6 +60,7 @@ describe('UserService', () => {
username: "zorg",
email: "blob@planet.us",
password: "Hyttepine",
isAdmin: 0
}]
});
});
......@@ -75,6 +77,35 @@ describe('UserService', () => {
req.error(new ErrorEvent("400"));
});
});
describe('deleteUser', () => {
it('should delete user', () => {
// Deletes user with id = 2
service.deleteUser(2)
.then(data => {})
.catch(error => {
fail();
});
// Mocks and checks HTTP request
const req = httpMock.expectOne("api/user/2");
expect(req.request.method).toBe("DELETE");
req.flush({
data: []
});
});
it('should reject on http error', () => {
// Deletes user with id = 2, but should catch HTTP error
service.deleteUser(2).then(data => {
fail();
}).catch(error => {});
// Mocks and checks HTTP request
const req = httpMock.expectOne("api/user/2");
expect(req.request.method).toBe("DELETE");
req.error(new ErrorEvent("400"));
});
});
});
......@@ -76,4 +76,28 @@ export class UserService {
private get_all_users() {
return this.http.get(this.userUrl);
}
}
/**
* Deletes an user from the database by id.
*/
deleteUser(id: number): Promise<User> {
return new Promise<User>(
(resolve, reject) => {
this.delete_user(id).subscribe((data: any) => {
try {
resolve(data);
} catch (err: any) {
reject(err);
}
},
(err: any) => {
console.log(err.message);
reject(err);
});
}
);
}
private delete_user(id: number) {
return this.http.delete(this.userUrl + id);
}
}
\ No newline at end of file