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 (4)
Showing
with 209 additions and 144 deletions
<div class="navbar">
<a class="logo" href="/">SELLPOINT</a>
<span class="logo" (click)="navigate('/')">SellPoint</span>
<nav>
<a href="/">/</a>
<a href="/annonse">/annonse</a>
<a href="/annonse/ny">/annonse/ny</a>
<a href="/register">/register</a>
<a href="/login">/login</a>
<a href="/profile">/profile</a>
<a href="/logout">/logout</a>
<span (click)="navigate('/annonse')">Annonser</span>
<span (click)="navigate('/annonse/ny')">Lag annonse</span>
<span *ngIf="!user.getUserId" (click)="navigate('/register')">Registrer</span>
<span *ngIf="!user.getUserId" (click)="navigate('/login')">Logg inn</span>
<span *ngIf="user.getUserId" (click)="navigate('/profile')">Profil</span>
<span *ngIf="user.getUserId" (click)="logout()">Logg ut</span>
</nav>
</div>
<div class="splash">
......@@ -23,11 +22,10 @@
<h2>Lorem Ipsum</h2>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
<div class="buttons">
<a href="/register" class="btn">Register deg!</a>
<a href="javascript:void(0)" class="btn pink">Les mer</a>
<button (click)="navigate('/register')" class="btn">Registrer deg!</button>
<button (click)="navigate('/')" class="btn pink">Les mer</button>
</div>
</div>
</div>
</div>
......
div.navbar {
background-color: #666;
background-color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
height: 70px;
height: 62px;
font-family: 'Josefin Sans', sans-serif;
padding-top: 8px;
a.logo {
span.logo {
padding: 10px;
background-color: white;
width: 200px;
margin-left: 10px;
font-size: 40px;
font-weight: bold;
cursor: pointer;
}
nav {
a{
span{
padding: 10px;
margin: 10px;
background-color: white;
cursor: pointer;
font-size: 25px;
}
}
}
......@@ -31,13 +36,10 @@ div.splash {
overflow: hidden;
color: #ffffff;
font-family: 'Inter', sans-serif;
a {
button {
color: #ffffff;
text-decoration: none;
}
a:visited {
color: #ffffff;
}
div {
position: relative;
}
......@@ -80,7 +82,7 @@ div.splash {
text-align: center;
justify-content: flex-end;
}
a.btn {
button.btn {
background: #13D05E;
width: 200px;
margin: 10px 0 10px 10px;
......@@ -88,8 +90,10 @@ div.splash {
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 4px 4px rgba(0, 0, 0, 0.25);
font-family: 'Josefin Sans', sans-serif;
font-size: 20px;
border: none;
cursor: pointer;
}
a.pink {
button.pink {
background: #FFA1A1;
}
......
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
......@@ -6,7 +7,8 @@ describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
RouterTestingModule,
HttpClientTestingModule
],
declarations: [
AppComponent
......
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { AuthService } from './authentication/auth.service';
import { User } from './models/user.model';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
export class AppComponent implements OnInit {
title: string = 'client';
user: User;
userSubscription: Subscription;
constructor(private authService: AuthService, private router: Router){}
ngOnInit() {
this.user = this.authService.getCurrentUser(false);
this.userSubscription = this.authService.userObservable.subscribe(user => {
this.user = user;
});
}
navigate(url) {
this.router.navigateByUrl(url);
}
logout() {
this.authService.logout();
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from '../shared/shared.module';
......
import { HttpClient, HttpEvent, HttpInterceptor, HttpResponse } from '@angular/common/http';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { User } from '../models/user.model';
import { tap, shareReplay } from 'rxjs/operators';
import { Router } from '@angular/router';
import { UserService } from '../users/user.service';
import { Subject } from 'rxjs';
interface IUserLogin {
username: string;
......@@ -16,7 +18,21 @@ export class AuthService {
loginUrl = "api/auth/login";
registrationUrl = "api/auth/register";
constructor(private http: HttpClient, private router: Router) { }
userObservable: Subject<User> = new Subject<User>();
constructor(private http: HttpClient, private router: Router, private userService: UserService) { }
getCurrentUser(route=true): User{
// Check for token expiration
if (this.checkTokenExpiration(route)) { // redirects to "/" if token is expired
// 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();
}
/**
* Logins an user, if given correct combination of username and password.
......@@ -41,19 +57,21 @@ export class AuthService {
private login_user(body: IUserLogin) {
// Pipes output to setSession function if a valid user is returned
return this.http.post(this.loginUrl, body).pipe(
tap(res =>this.setSession(res)),
shareReplay());
tap(res =>this.setSession(res)),
shareReplay()
);
}
// Set authentication token on localStorage if a valid user is received
private setSession(authResult) {
console.log(authResult);
localStorage.setItem('token', authResult.token);
this.userObservable.next(this.getCurrentUser());
}
/**
* Checks validity of token, redirects to homepage and removes it if it is expired
*/
checkTokenExpiration() {
checkTokenExpiration(route) {
const token = localStorage.getItem("token");
if (token) {
const {iat, exp} = JSON.parse(atob(token?.split(".")[1]));
......@@ -64,26 +82,31 @@ export class AuthService {
// Expired token
if (now < issued || now >= expires) {
this.logout();
this.router.navigateByUrl("/");
if (route) {
this.router.navigate(["/login"], {replaceUrl: true});
}
return false
}
return true;
}
}
this.router.navigateByUrl("/")
if (route) {
this.router.navigate(["/login"], {replaceUrl: true});
}
return false
}
/**
* Logout an user and redirects to the homepage
* Logout a user and redirects to the homepage
*/
logout() {
localStorage.removeItem("token");
this.router.navigateByUrl("/")
this.router.navigateByUrl("/");
this.userObservable.next(new User());
}
/**
* Register an user, if not duplicate, add to database.
* Register a user, if not duplicate, add to database.
*/
registerUser(user: User): Promise<string> {
return new Promise<string>(
......
......@@ -6,7 +6,7 @@ export class Post implements Deserializable, Serializable {
private title: string;
private description: string;
private timestamp: Date;
private owner: string;
private owner: number;
private imageUrl: string;
private price: number;
private categoryid: number;
......@@ -83,7 +83,7 @@ export class Post implements Deserializable, Serializable {
return this.owner;
}
set setOwner(owner: string) {
set setOwner(owner: number) {
this.owner = owner;
}
......
......@@ -22,7 +22,6 @@ export class User implements Deserializable, Serializable {
deserialize(input: Object): this {
Object.assign(this, input);
console.log(this);
return this;
}
......
......@@ -7,5 +7,7 @@
<p>Publisert: {{post.getTimestamp}}</p>
<p>Eier: {{post.getOwner}}</p>
<app-button text="Rediger annonse" (click)="editPost()"></app-button>
<app-button text="Slett annonse" (click)="deletePost()"></app-button>
<div *ngIf="userId == post.getOwner">
<app-button text="Rediger annonse" (click)="editPost()"></app-button>
<app-button text="Slett annonse" (click)="deletePost()"></app-button>
</div>
......@@ -2,7 +2,9 @@ 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 { AuthService } from 'src/app/authentication/auth.service';
import { Post } from 'src/app/models/post.model';
import { User } from 'src/app/models/user.model';
import { SharedModule } from 'src/app/shared/shared.module';
import { PostListComponent } from '../post-list/post-list.component';
import { PostService } from '../post.service';
......@@ -12,9 +14,19 @@ import { PostDetailsComponent } from './post-details.component';
describe('PostDetailsComponent', () => {
let component: PostDetailsComponent;
let fixture: ComponentFixture<PostDetailsComponent>;
let mockPostService;
let mockPostService, mockAuthService;
beforeEach(async () => {
// AuthService mock setup
mockAuthService = jasmine.createSpyObj(['getCurrentUser']);
mockAuthService.getCurrentUser.and.returnValue(new User({
userId: 4,
username: "tester",
email: "test@test.com",
password: "1234",
create_time: 513498
}));
// PostService mock setup
mockPostService = jasmine.createSpyObj(['getPost', 'deletePost']);
mockPostService.getPost.and.returnValue(
......@@ -25,7 +37,7 @@ describe('PostDetailsComponent', () => {
title: "Test",
description: "TestDescription",
timestamp: 23947298,
owner: "user",
owner: 4,
imageUrl: null,
price: 49,
categoryid: 2
......@@ -50,7 +62,8 @@ describe('PostDetailsComponent', () => {
],
providers: [
{ provide: ActivatedRoute, useValue: { snapshot: {params: {id: 5}}}},
{ provide: PostService, useValue: mockPostService }
{ provide: PostService, useValue: mockPostService },
{ provide: AuthService, useValue: mockAuthService }
]
})
.compileComponents();
......@@ -68,30 +81,32 @@ describe('PostDetailsComponent', () => {
it('should get post with id from url parameter', async () => {
// Waits for ngOnInit and checks that we get post
expect(component.post).not.toBeNull();
await fixture.whenStable();
expect(mockPostService.getPost).toHaveBeenCalledWith(5);
expect(component.post).toEqual(new Post({
id: 5,
title: "Test",
description: "TestDescription",
timestamp: 23947298,
owner: 4,
imageUrl: null,
price: 49,
categoryid: 2
}));
});
fixture.whenStable().then(() => {
expect(mockPostService.getPost).toHaveBeenCalledWith(5);
expect(component.post).toEqual(new Post({
id: 5,
title: "Test",
description: "TestDescription",
timestamp: 23947298,
owner: "user",
imageUrl: null,
price: 49,
categoryid: 2
}));
});
it('should get current user', async () => {
// Waits for ngOnInit and checks that we get post
await fixture.whenStable();
expect(mockAuthService.getCurrentUser).toHaveBeenCalledWith(false);
expect(component.userId).toBe(4);
});
it('should delete post with id', async () => {
// Waits for ngOnInit and checks that we can delete post
expect(component.post).not.toBeNull();
fixture.whenStable().then(() => {
component.deletePost();
expect(mockPostService.deletePost).toHaveBeenCalledWith(5);
});
await fixture.whenStable();
component.deletePost();
expect(mockPostService.deletePost).toHaveBeenCalledWith(5);
});
});
......@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core';
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';
@Component({
selector: 'app-post-details',
......@@ -11,10 +12,14 @@ import { ActivatedRoute, Router } from '@angular/router'
export class PostDetailsComponent implements OnInit {
post: Post = new Post();
userId: number = 0;
constructor(private postService: PostService, private activatedRoute: ActivatedRoute, private router: Router) { }
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 id parameter from URL
const id = this.activatedRoute.snapshot.params["id"];
......@@ -25,6 +30,7 @@ export class PostDetailsComponent implements OnInit {
console.log(error);
});
}
/**
* Moves to edit page
*/
......@@ -36,11 +42,14 @@ export class PostDetailsComponent implements OnInit {
* Deletes post in database and navigates to post list
*/
deletePost() {
this.postService.deletePost(this.post.getId).then(data => {
console.log("Successfully deleted post: " + this.post.getId);
this.router.navigateByUrl("/annonse");
}).catch(error => {
console.log(error);
});
// Check if we are the owner of the post
if (this.userId == this.post.getOwner) {
this.postService.deletePost(this.post.getId).then(data => {
console.log("Successfully deleted post: " + this.post.getId);
this.router.navigateByUrl("/annonse");
}).catch(error => {
console.log(error);
});
}
}
}
......@@ -3,8 +3,11 @@ import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { AuthService } from 'src/app/authentication/auth.service';
import { Category } from 'src/app/models/category.model';
import { User } from 'src/app/models/user.model';
import { SharedModule } from 'src/app/shared/shared.module';
import { UserLoginFormComponent } from 'src/app/users/user-login-form/user-login-form.component';
import { PostListComponent } from '../post-list/post-list.component';
import { PostService } from '../post.service';
......@@ -14,9 +17,19 @@ describe('PostFormComponent', () => {
let component: PostFormComponent;
let fixture: ComponentFixture<PostFormComponent>;
let router: Router;
let mockPostService;
let mockPostService, mockAuthService;
beforeEach(async () => {
// AuthService mock setup
mockAuthService = jasmine.createSpyObj(['getCurrentUser']);
mockAuthService.getCurrentUser.and.returnValue(new User({
userId: 4,
username: "tester",
email: "test@test.com",
password: "1234",
create_time: 513498
}));
// PostService mock setup
mockPostService = jasmine.createSpyObj(['getAllCategories', 'addPost', 'deletePost']);
mockPostService.getAllCategories.and.returnValue(
......@@ -45,10 +58,14 @@ describe('PostFormComponent', () => {
FormsModule,
SharedModule,
RouterTestingModule.withRoutes([
{ path: 'annonse', component: PostListComponent}
{ path: 'annonse', component: PostListComponent},
{ path: 'login', component: UserLoginFormComponent}
])
],
providers: [ { provide: PostService, useValue: mockPostService } ]
providers: [
{ provide: PostService, useValue: mockPostService },
{ provide: AuthService, useValue: mockAuthService }
]
})
.compileComponents();
});
......@@ -61,16 +78,20 @@ describe('PostFormComponent', () => {
router = TestBed.inject(Router);
});
it('should create and get all categories', async () => {
expect(component).toBeTruthy();
it('should get current user', async () => {
// Waits for ngOnInit and checks that we get categories
await fixture.whenStable();
expect(mockAuthService.getCurrentUser).toHaveBeenCalled();
expect(component.currentUser.getUserId).toBe(4);
});
it('should get all categories', async () => {
// Waits for ngOnInit and checks that we get categories
fixture.whenStable().then(() => {
expect(mockPostService.getAllCategories).toHaveBeenCalled();
expect(component.categories.length).toBe(2);
expect(component.categories[0].getCategoryId).toBe(1);
expect(component.categories[1].getName).toBe("Bil");
});
await fixture.whenStable();
expect(mockPostService.getAllCategories).toHaveBeenCalled();
expect(component.categories.length).toBe(2);
expect(component.categories[0].getCategoryId).toBe(1);
expect(component.categories[1].getName).toBe("Bil");
});
it('should validate form', () => {
......@@ -126,21 +147,17 @@ describe('PostFormComponent', () => {
it('should delete post with id', async () => {
component.id = 5;
expect(component.id).toBe(5);
// Waits for ngOnInit and checks that we can delete post
fixture.whenStable().then(() => {
component.deletePost();
expect(mockPostService.deletePost).toHaveBeenCalledWith(5);
});
await fixture.whenStable();
component.deletePost();
expect(mockPostService.deletePost).toHaveBeenCalledWith(5);
});
it('should not delete new post', async () => {
// Waits for ngOnInit and checks that we can delete post
expect(component.id).toBe(0);
fixture.whenStable().then(() => {
component.deletePost();
expect(mockPostService.deletePost).not.toHaveBeenCalledWith(5);
});
await fixture.whenStable();
component.deletePost();
expect(mockPostService.deletePost).not.toHaveBeenCalledWith(5);
});
});
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AuthService } from 'src/app/authentication/auth.service';
import { Category } from 'src/app/models/category.model';
import { Post } from 'src/app/models/post.model';
import { User } from 'src/app/models/user.model';
import { PostService } from '../post.service';
@Component({
......@@ -24,9 +26,17 @@ export class PostFormComponent implements OnInit {
categories: Array<Category>;
constructor(private postService: PostService, private router: Router, private activatedRoute: ActivatedRoute) { }
currentUser: User;
constructor(private postService: PostService, private router: Router, private activatedRoute: ActivatedRoute, private authService: AuthService) { }
ngOnInit() {
this.currentUser = this.authService.getCurrentUser();
if (!this.currentUser) {
this.router.navigate(["/login"], {replaceUrl: true});
}
const id = this.activatedRoute.snapshot.params["id"];
if (id) {
this.id = id;
......@@ -39,6 +49,10 @@ export class PostFormComponent implements OnInit {
this.categoryid = post.getCategory;
this.imageUrl = post.getImageUrl;
if (post.getOwner != this.currentUser.getUserId) {
this.router.navigateByUrl("/");
}
this.showImage(this.imageUrl);
}).catch(error => {
console.log(error);
......@@ -49,7 +63,7 @@ export class PostFormComponent implements OnInit {
this.postService.getAllCategories().then(categories => {
this.categories = categories;
}).catch (error => {
console.log("Error adding catrgories:" + error);
console.log("Error adding categories:" + error);
});
}
......@@ -91,7 +105,7 @@ export class PostFormComponent implements OnInit {
title: this.title,
description: this.description,
timestamp: new Date(),
owner: "admin",
owner: this.currentUser.getUserId,
imageUrl: this.imageUrl,
price: this.price,
categoryid: this.categoryid
......
<div class="postthumb">
<a href="javascript:void(0)"(click)="goToPost()">{{post.getTitle}}</a>
<a href="javascript:void(0)" (click)="goToPost()">{{post.getTitle}}</a>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { SharedModule } from '../shared.module';
import { PasswordInputComponent } from './password-input.component';
......@@ -10,7 +11,7 @@ describe('PasswordInputComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PasswordInputComponent ],
imports: [ FormsModule ]
imports: [ SharedModule, FormsModule ]
})
.compileComponents();
});
......
......@@ -18,6 +18,9 @@ export class UserLoginFormComponent implements OnInit {
constructor(private userService: UserService, private authService: AuthService, private router: Router) { }
ngOnInit(): void {
if (this.authService.getCurrentUser(false).getUserId) {
this.router.navigate(["/"], {replaceUrl: true});
}
}
/**
......@@ -49,7 +52,6 @@ export class UserLoginFormComponent implements OnInit {
// Logins the user
this.authService.login(request).then(status => {
console.log("User login1: " + JSON.stringify(status));
this.router.navigateByUrl("/");
}).catch(error => {
console.log("Error user login: " + error);
......
<p>user-logout works!</p>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { UserLogoutComponent } from './user-logout.component';
describe('UserLogoutComponent', () => {
let component: UserLogoutComponent;
let fixture: ComponentFixture<UserLogoutComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ UserLogoutComponent ],
imports: [ HttpClientTestingModule, RouterTestingModule ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(UserLogoutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { AuthService } from 'src/app/authentication/auth.service';
@Component({
selector: 'app-user-logout',
templateUrl: './user-logout.component.html',
styleUrls: ['./user-logout.component.scss']
})
export class UserLogoutComponent implements OnInit {
constructor(private authService: AuthService) { }
ngOnInit(): void {
this.authService.logout();
}
}