-
Notifications
You must be signed in to change notification settings - Fork 0
task#24 create a user service
The goal for this task is creating a new service. The service shall provide a CRUD (create, read, update delete) functionality for users plus a function to get all users:
| verb | url | data | Remark |
|---|---|---|---|
| get | /users | array of user objects | role: admin |
| get | /users/:id | user object | role: admin or own user |
| get | /users/:username | user object | role: admin or own user |
| delete | /users/:id | user object | role: admin |
| post | /users | user object | role: admin |
| put | /users:id | user object | role: admin or own user |
The mock server is checking the api calls based on the Bearer token. Each api call shall be working either as an;
- admin role or for some verbs.
- or as a user role for own user profile.
The following domain model has been discovered during the analysis of the auction case study:

- Create a folder
modelin the foldersrc/app/shared. - Create user class
user.tsbased on analysis of the case study in the foldersrc/app/shared/model. - Upgrade the mock server for the new
verbspost and put. - Upgrade the unit test for the mock server for the new
verbspost and put. - Create a user service
UserServiceby creating the fileuser.service.tsin the foldersrc/app/shared/servicewhich supports the CRUD-operations of the user object - Create a unit test for
UserService. - Create a service
CreateUserServiceby creating the filecreate-user.service.tsin the foldersrc/app/shared/helperto create the default test users admin and user - Create a unit test for
CreateUserService.
Verify the result by unit tests for the user service and the default user service.
The user class is using the fields which were discovered during the analysis of the auction case study.
- Create a folder
modelin the foldersrc/app/shared. - Create a file
user.tsin the foldersrc/app/shared/model.
export class User {
id?: number;
username: string;
password: string;
firstName: string;
lastName: string;
email?: string;
thresholdOpenPayment?: number;
locked?: boolean;
}Since we shall support the new verbs post and put for the user, we must upgrade our
mock server by adding a new user and changing a user. Since we have already a
register function for adding a user, we can use it the for post verb. For the
put verb we shall write a new function changeUser().
function changeUser() {
const user = body;
if (!user.id) user.id = idFromUrl();
let users = JSON.parse(localStorage.getItem('users')) || [];
users = users.filter(x => x.id === idFromUrl());
if (users.length > 0) {
// delete this user
let users = JSON.parse(localStorage.getItem('users')) || [];
users = users.filter(x => x.id !== idFromUrl());
// add changed user
users.push(user);
localStorage.setItem('users', JSON.stringify(users));
return ok(user);
} else {
return noContent('User with id ' + idFromUrl() + ' not found.')
}
}function handle route add in the switch area
case url.endsWith('/users') && method === 'POST':
response = register();
break;
case url.match(/\/users\/\d+$/) && method === 'PUT':
response = changeUser();
break;Upgrade the unit test of the mock server
Add new unit tests to the file mock-backend.interceptor.service.spec.ts before the
test function: it('should authenticate a user', (done) => {.
it('should create a postUser with valid token and role admin', (done) => {
httpClient.post<User>('/users', postUser, httpOptionsJwtToken)
.subscribe((data) => {
console.log('mockTestResponse', data);
expect(data.status).toBe(200);
expect(data.body.id).toBeGreaterThan(0);
postUserId = data.body.id;
done();
},
(err) => {
console.log('mockTestResponse', err);
done();
}, () => {
// console.log('Test complete');
done();
});
});
it('should change a postUser with valid token and role admin', (done) => {
postUser.firstName = "changed";
httpClient.put<User>('/users/' + postUserId, postUser, httpOptionsJwtToken)
.subscribe((data) => {
console.log('mockTestResponse', data);
expect(data.status).toBe(200);
expect(data.body.id).toBeGreaterThan(0);
expect(data.body.id).toBe(postUser.id);
expect(data.body.firstName).toEqual ("changed");
done();
},
(err) => {
console.log('mockTestResponse', err);
done();
}, () => {
// console.log('Test complete');
done();
});
});
it('should delete the postUser with valid token and role admin', (done) => {
httpClient.delete<User>('/users/' + postUserId, httpOptionsJwtToken)
.subscribe((data) => {
console.log('mockTestResponse', data);
expect(data.status).toBe(200);
done();
},
(err) => {
console.log('mockTestResponse', err);
done();
fail();
}, () => {
// console.log('Test complete');
done();
});
});Put the variable on the top section of the unit test file mock-backend-interceptor.spec.ts:
const postUser: User = {
firstName: 'post',
lastName: 'post',
username: 'post',
password: 'post'
};
let regUserId: number = 0;
let postUserId: number = 0;
let adminUserId: number = 0;Please create a folder service in the folder src/app/shared.
Create a file user.service.ts in the folder src/app/shared/service or
use the generator.
ng generate service shared/service/user
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {User} from '../model/user';
import {environment} from '../../../environments/environment';
@Injectable({providedIn: 'root'})
export class UserService {
private authApiUrl: string;
constructor(private http: HttpClient) {
this.authApiUrl = environment.endpoints.backendAuthUrl
}
get(id: number) {
return this.http.get<User>(`${this.authApiUrl}/users/${id}`);
}
getByName(name: string) {
return this.http.get<User>(`${this.authApiUrl}/users/${name}`);
}
getAll() {
return this.http.get<User[]>(`${this.authApiUrl}/users`);
}
register(user: User) {
return this.http.post<any>(`${this.authApiUrl}/users/register`, user);
}
delete(id: number) {
return this.http.delete(`this.authApiUrl/users/${id}`);
}
}Create the unit test file user.service.spec.ts in the folder src/app/shared/service.
import {TestBed} from '@angular/core/testing';
import {UserService} from './user.service';
import {RouterTestingModule} from '@angular/router/testing';
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
import {environment} from '../../../environments/environment';
describe('UserService', () => {
let service: UserService;
let httpMock;
let expectedResult;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule,
HttpClientTestingModule
],
providers: [
UserService
]
});
expectedResult = {};
service = TestBed.get(UserService);
httpMock = TestBed.get(HttpTestingController);
});
afterEach(() => {
httpMock.verify();
});
it('should be created', () => {
const service = TestBed.get(UserService);
expect(service).toBeTruthy();
});
it('should call correct URL', () => {
const userName = 'user';
service.getByName(userName).subscribe(() => {});
const req = httpMock.expectOne({ method: 'GET' });
const resourceUrl = `${environment.endpoints.backendAuthUrl}/users/${userName}`;
expect(req.request.url).toEqual(`${resourceUrl}`);
});
it('should return User', () => {
const userName = 'user';
service.getByName(userName).subscribe(received => {
expectedResult = received;
});
const req = httpMock.expectOne({ method: 'GET' });
const user = {
firstName: userName,
lastName: userName,
username: userName,
password: userName,
email: userName + '@mail.com',
thresholdOpenPayment: 1000,
locked: false
};
req.flush(user);
expect(expectedResult).toEqual(user);
});
it('should propagate not found response', () => {
service.getByName('user').subscribe(null, (_error: any) => {
expectedResult = _error.status;
});
const req = httpMock.expectOne({ method: 'GET' });
req.flush('Invalid request parameters', {
status: 404,
statusText: 'Bad Request'
});
expect(expectedResult).toEqual(404);
});
});We want to create default test and admin users for test purposes. We will use the register function to create:
| username | password | role |
|---|---|---|
| user | user | user |
| admin | admin | user, admin |
Create the file create-user.service.ts in the folder src/app/shared/helper.
ng generate service shared/helper/create-user
import {first} from 'rxjs/operators';
import {Injectable} from '@angular/core';
import {Router} from '@angular/router';
import {User} from '../model/user';
import {UserService} from '../service/user.service';
@Injectable({providedIn: 'root'})
export class CreateUserService {
constructor(
private router: Router,
private userService: UserService
) {
}
private registerUser(user: User) {
this.userService.register(user)
.pipe(first())
.subscribe(
data => {},
error => {}
);
}
checkAndRegisterUser(userName) {
let users = JSON.parse(localStorage.getItem('users')) || [];
const user: User = users.find(x => x.username === userName );
if (users.length === 0 || !user) {
// user not yet in localStorage
const user = {
firstName: userName,
lastName: userName,
username: userName,
password: userName,
email: userName + '@mail.com',
thresholdOpenPayment: 1000,
locked: false
};
this.registerUser(user);
}
}
/**
* Creates default users for test purposes
* Used only in development environment
*/
createDefaultUsers() {
this.checkAndRegisterUser('admin');
this.checkAndRegisterUser('user');
}
}
Create the unit test file create-user.service.spec.ts in the folder src/app/shared/helper.
import {fakeAsync, TestBed, tick} from '@angular/core/testing';
import {CreateUserService} from './create-user.service';
import {User} from '../model/user';
import {RouterTestingModule} from '@angular/router/testing';
import {HttpClientTestingModule} from '@angular/common/http/testing';
import {HTTP_INTERCEPTORS} from '@angular/common/http';
import {MockBackendInterceptor} from './mock/mock-backend-interceptor.service';
describe('CreateUserService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule,
HttpClientTestingModule
],
providers: [
CreateUserService,
{
provide: HTTP_INTERCEPTORS,
useClass: MockBackendInterceptor,
multi: true
}]
});
});
it('should be created', () => {
const service = TestBed.get(CreateUserService);
expect(service).toBeTruthy();
});
it('should create a user', fakeAsync ( () => {
const service = TestBed.get(CreateUserService);
// create test user
const testuser = 'testuser';
service.checkAndRegisterUser(testuser);
tick(20000);
// check
let users = JSON.parse(localStorage.getItem('users')) || [];
const user: User = users.find(x => x.username === testuser && x.password === testuser);
expect(user.username).toEqual(testuser);
// delete testuser
users = users.filter(x => x.id !== user.id);
localStorage.setItem('users', JSON.stringify(users));
}));
async function delay(ms: number) {
await new Promise(resolve => setTimeout(()=>resolve(), ms)).then(()=>console.log("fired"));
}
});