Browse Source

Administration refactored.

pull/271/head
Sebastian Stehle 8 years ago
parent
commit
f6d2bfce21
  1. 9
      src/Squidex/app/app.module.ts
  2. 4
      src/Squidex/app/features/administration/module.ts
  3. 4
      src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts
  4. 4
      src/Squidex/app/features/administration/pages/users/user-page.component.html
  5. 91
      src/Squidex/app/features/administration/pages/users/user-page.component.ts
  6. 6
      src/Squidex/app/features/administration/pages/users/users-page.component.html
  7. 17
      src/Squidex/app/features/administration/pages/users/users-page.component.ts
  8. 51
      src/Squidex/app/features/administration/state/users.state.ts
  9. 2
      src/Squidex/app/framework/angular/control-errors.component.ts
  10. 14
      src/Squidex/app/framework/module.ts
  11. 2
      src/Squidex/app/shared/declarations-base.ts
  12. 86
      src/Squidex/app/shared/guards/resolve-user.guard.spec.ts
  13. 48
      src/Squidex/app/shared/guards/resolve-user.guard.ts
  14. 9
      src/Squidex/app/shared/module.ts
  15. 132
      src/Squidex/app/shared/services/event-consumers.service.spec.ts
  16. 88
      src/Squidex/app/shared/services/event-consumers.service.ts
  17. 215
      src/Squidex/app/shared/services/users.service.spec.ts
  18. 120
      src/Squidex/app/shared/services/users.service.ts

9
src/Squidex/app/app.module.ts

@ -6,6 +6,10 @@
*/
import { ApplicationRef, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { DndModule } from 'ng2-dnd';
@ -56,6 +60,11 @@ export function configUserReport() {
BrowserModule,
BrowserAnimationsModule,
DndModule.forRoot(),
HttpClientModule,
FormsModule,
CommonModule,
RouterModule,
ReactiveFormsModule,
SqxFrameworkModule.forRoot(),
SqxSharedModule.forRoot(),
SqxShellModule,

4
src/Squidex/app/features/administration/module.ts

@ -9,6 +9,7 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import {
SqxSharedModule,
SqxFrameworkModule
} from 'shared';
@ -60,6 +61,7 @@ const routes: Routes = [
@NgModule({
imports: [
SqxSharedModule,
SqxFrameworkModule,
RouterModule.forChild(routes)
],
@ -71,6 +73,8 @@ const routes: Routes = [
],
providers: [
EventConsumersService,
UnsetUserGuard,
UserMustExistGuard,
UsersService,
UsersState
]

4
src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts

@ -10,13 +10,13 @@ import { Observable, Subscription } from 'rxjs';
import {
AppContext,
EventConsumerDto,
EventConsumersService,
fadeAnimation,
ImmutableArray,
ModalView
} from 'shared';
import { EventConsumerDto, EventConsumersService } from './../../services/event-consumers.service';
@Component({
selector: 'sqx-event-consumers-page',
styleUrls: ['./event-consumers-page.component.scss'],

4
src/Squidex/app/features/administration/pages/users/user-page.component.html

@ -14,10 +14,10 @@
<sqx-shortcut keys="ctrl+s" (trigger)="save()"></sqx-shortcut>
<h3 class="panel-title" *ngIf="isNewMode">
<h3 class="panel-title" *ngIf="!user">
New User
</h3>
<h3 class="panel-title" *ngIf="!isNewMode">
<h3 class="panel-title" *ngIf="user">
Edit User
</h3>
</div>

91
src/Squidex/app/features/administration/pages/users/user-page.component.ts

@ -6,7 +6,8 @@
*/
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { FormBuilder, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import {
@ -15,7 +16,7 @@ import {
ValidatorsEx
} from 'shared';
import { UsersState } from './../../state/users.state';
import { UserDto, UsersState } from './../../state/users.state';
@Component({
selector: 'sqx-user-page',
@ -29,21 +30,46 @@ export class UserPageComponent implements OnDestroy, OnInit {
private selectedUserSubscription: Subscription;
public userFormSubmitted = false;
public userForm: FormGroup;
public userFormError = '';
public userForm =
this.formBuilder.group({
email: ['',
[
Validators.email,
Validators.required,
Validators.maxLength(100)
]],
displayName: ['',
[
Validators.required,
Validators.maxLength(100)
]],
password: ['',
[
Validators.nullValidator
]],
passwordConfirm: ['',
[
ValidatorsEx.match('password', 'Passwords must be the same.')
]]
});
public user: UserDto | null;
public isCurrentUser = false;
public isNewMode = false;
constructor(public readonly ctx: AppContext,
private readonly formBuilder: FormBuilder,
private readonly router: Router,
private readonly state: UsersState
) {
}
public ngOnDestroy() {
if (this.selectedUserSubscription) {
this.selectedUserSubscription.unsubscribe();
}
}
public ngOnInit() {
this.selectedUserSubscription =
@ -58,62 +84,45 @@ export class UserPageComponent implements OnDestroy, OnInit {
const requestDto = this.userForm.value;
if (this.isNewMode) {
if (!this.user) {
this.state.createUser(requestDto)
.subscribe(() => {
this.ctx.notifyInfo('User created successfully.');
this.resetUserForm();
.subscribe(user => {
this.back();
}, error => {
this.resetUserForm(error.displayMessage);
this.resetFormState(error.displayMessage);
});
} else {
this.state.updateUser(requestDto)
this.state.updateUser(this.user!, requestDto)
.subscribe(() => {
this.ctx.notifyInfo('User saved successfully.');
this.resetUserForm();
this.resetFormState();
}, error => {
this.resetUserForm(error.displayMessage);
this.resetFormState(error.displayMessage);
});
}
}
}
private back() {
this.router.navigate(['../'], { relativeTo: this.ctx.route, replaceUrl: true });
}
private setupAndPopulateForm(user: UserDto | null) {
const userData: any = user || {};
this.user = user;
this.isNewMode = !user;
this.isCurrentUser = user !== null && user.id === this.ctx.userId;
this.userForm =
this.formBuilder.group({
email: [userData.email,
[
Validators.email,
Validators.required,
Validators.maxLength(100)
]],
displayName: [userData.displayName,
[
Validators.required,
Validators.maxLength(100)
]],
password: ['',
[
this.isNewMode ? Validators.required : Validators.nullValidator
]],
passwordConfirm: ['',
[
ValidatorsEx.match('password', 'Passwords must be the same.')
]]
});
this.userForm.controls['password'].setValidators(
user ?
Validators.nullValidator :
Validators.required);
this.resetFormState();
this.resetUserForm();
this.userForm.reset();
this.userForm.patchValue(user || {});
}
private resetUserForm(message: string = '') {
private resetFormState(message: string = '') {
this.userForm.enable();
this.userForm.controls['password'].reset();
this.userForm.controls['passwordConfirm'].reset();

6
src/Squidex/app/features/administration/pages/users/users-page.component.html

@ -56,7 +56,7 @@
<div sqxIgnoreScrollbar>
<table class="table table-items table-fixed">
<tbody>
<ng-template ngFor let-user [ngForOf]="state.userItems | async" [ngForTrackBy]="state.trackBy">
<ng-template ngFor let-user [ngForOf]="state.usersItems | async" [ngForTrackBy]="state.trackBy">
<tr [routerLink]="user.id" routerLinkActive="active">
<td class="cell-user">
<img class="user-picture" [attr.title]="user.name" [attr.src]="user | sqxUserDtoPicture" />
@ -93,10 +93,10 @@
<div class="float-right pagination">
<span class="pagination-text">{{pager.itemFirst}}-{{pager.itemLast}} of {{pager.numberOfItems}}</span>
<button class="btn btn-link btn-secondary pagination-button" [disabled]="!pager.canGoPrev" (click)="state.goPrev()">
<button class="btn btn-link btn-secondary pagination-button" [disabled]="!pager.canGoPrev" (click)="goPrev()">
<i class="icon-angle-left"></i>
</button>
<button class="btn btn-link btn-secondary pagination-button" [disabled]="!pager.canGoNext" (click)="state.goNext()">
<button class="btn btn-link btn-secondary pagination-button" [disabled]="!pager.canGoNext" (click)="goNext()">
<i class="icon-angle-right"></i>
</button>
</div>

17
src/Squidex/app/features/administration/pages/users/users-page.component.ts

@ -8,8 +8,9 @@
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { AppContext, UserDto } from 'shared';
import { AppContext } from 'shared';
import { UserDto } from './../../services/users.service';
import { UsersState } from './../../state/users.state';
@Component({
@ -33,7 +34,7 @@ export class UsersPageComponent implements OnInit {
}
public search() {
this.state.search(this.usersFilter.value);
this.state.search(this.usersFilter.value).subscribe();
}
public load(showInfo = false) {
@ -46,11 +47,19 @@ export class UsersPageComponent implements OnInit {
}
public lock(user: UserDto) {
this.state.lockUser(user.id).subscribe();
this.state.lockUser(user).subscribe();
}
public unlock(user: UserDto) {
this.state.unlockUser(user.id).subscribe();
this.state.unlockUser(user).subscribe();
}
public goPrev() {
this.state.goPrev().subscribe();
}
public goNext() {
this.state.goNext().subscribe();
}
}

51
src/Squidex/app/features/administration/state/users.state.ts

@ -67,65 +67,68 @@ export class UsersState {
});
}
public updateUser(request: CreateUserDto): Observable<any> {
const id = this.selectedUser.value!.id;
return this.usersService.putUser(id, request)
.do(() => {
this.dialogs.notify(Notification.info('User saved successsfull'));
this.usersItems.nextBy(v => v.replaceAll(x => x.id === id, u => u.update(request.email, request.displayName)));
});
}
public createUser(request: CreateUserDto): Observable<UserDto> {
return this.usersService.postUser(request)
.catch(error => this.notifyError(error))
.do(user => {
this.usersItems.nextBy(v => v.pushFront(user));
this.usersPager.nextBy(v => v.incrementCount());
});
}
public lockUser(id: string): Observable<any> {
return this.usersService.lockUser(id)
public updateUser(user: UserDto, request: CreateUserDto): Observable<any> {
return this.usersService.putUser(user.id, request)
.do(() => {
this.dialogs.notify(Notification.info('User saved successsfull'));
this.replaceUser(user.update(request.email, request.displayName));
});
}
public lockUser(user: UserDto): Observable<any> {
return this.usersService.lockUser(user.id)
.catch(error => this.notifyError(error))
.do(() => {
this.usersItems.nextBy(v => v.replaceAll(x => x.id === id, u => u.lock()));
this.replaceUser(user.lock());
});
}
public unlockUser(id: string): Observable<any> {
return this.usersService.lockUser(id)
public unlockUser(user: UserDto): Observable<any> {
return this.usersService.unlockUser(user.id)
.catch(error => this.notifyError(error))
.do(() => {
this.usersItems.nextBy(v => v.replaceAll(x => x.id === id, u => u.unlock()));
this.replaceUser(user.unlock());
});
}
public search(filter: string) {
public search(filter: string): Observable<any> {
this.usersPager.nextBy(v => new Pager(0));
this.usersQuery.nextBy(v => filter);
this.loadUsers();
return this.loadUsers();
}
public goNext() {
public goNext(): Observable<any> {
this.usersPager.nextBy(v => v.goNext());
this.loadUsers();
return this.loadUsers();
}
public goPrev() {
public goPrev(): Observable<any> {
this.usersPager.nextBy(v => v.goPrev());
this.loadUsers();
return this.loadUsers();
}
public trackBy(index: number, user: UserDto): any {
return user.id;
}
private replaceUser(user: UserDto) {
this.usersItems.nextBy(v => v.replaceBy('id', user));
this.selectedUser.nextBy(v => v !== null && v.id === user.id ? user : v);
}
private notifyError(error: string | ErrorDto) {
if (error instanceof ErrorDto) {
this.dialogs.notify(Notification.error(error.displayMessage));

2
src/Squidex/app/framework/angular/control-errors.component.ts

@ -71,7 +71,7 @@ export class ControlErrorsComponent implements OnChanges, OnDestroy {
if (this.fieldName) {
this.displayFieldName = this.fieldName;
} else if (this.for) {
if (this.for instanceof String) {
if (typeof this.for === 'string') {
this.displayFieldName = this.for.substr(0, 1).toUpperCase() + this.for.substr(1);
} else {
this.displayFieldName = 'field';

14
src/Squidex/app/framework/module.ts

@ -7,8 +7,6 @@
import { CommonModule } from '@angular/common';
import { ModuleWithProviders, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {
@ -77,10 +75,8 @@ import {
@NgModule({
imports: [
HttpClientModule,
FormsModule,
CommonModule,
RouterModule,
ReactiveFormsModule
],
declarations: [
@ -137,6 +133,7 @@ import {
],
exports: [
AutocompleteComponent,
CommonModule,
ConfirmClickDirective,
ControlErrorsComponent,
CopyDirective,
@ -151,6 +148,7 @@ import {
FileDropDirective,
FileSizePipe,
FocusOnInitDirective,
FormsModule,
FromNowPipe,
FullDateTimePipe,
IgnoreScrollbarDirective,
@ -171,6 +169,7 @@ import {
ParentLinkDirective,
PopupLinkDirective,
ProgressBarComponent,
ReactiveFormsModule,
RootViewDirective,
ScrollActiveDirective,
ShortcutComponent,
@ -185,12 +184,7 @@ import {
TitleComponent,
ToggleComponent,
TooltipComponent,
UserReportComponent,
HttpClientModule,
FormsModule,
CommonModule,
RouterModule,
ReactiveFormsModule
UserReportComponent
]
})
export class SqxFrameworkModule {

2
src/Squidex/app/shared/declarations-base.ts

@ -12,7 +12,6 @@ export * from './guards/resolve-app-languages.guard';
export * from './guards/resolve-content.guard';
export * from './guards/resolve-published-schema.guard';
export * from './guards/resolve-schema.guard';
export * from './guards/resolve-user.guard';
export * from './guards/unset-app.guard';
export * from './interceptors/auth.interceptor';
@ -27,7 +26,6 @@ export * from './services/assets.service';
export * from './services/auth.service';
export * from './services/backups.service';
export * from './services/contents.service';
export * from './services/event-consumers.service';
export * from './services/graphql.service';
export * from './services/help.service';
export * from './services/history.service';

86
src/Squidex/app/shared/guards/resolve-user.guard.spec.ts

@ -1,86 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { IMock, Mock } from 'typemoq';
import { Observable } from 'rxjs';
import { UserManagementService } from 'shared';
import { ResolveUserGuard } from './resolve-user.guard';
import { RouterMockup } from './router-mockup';
describe('ResolveUserGuard', () => {
const route = {
params: {},
parent: {
params: {
userId: 'my-user'
}
}
};
let usersService: IMock<UserManagementService>;
beforeEach(() => {
usersService = Mock.ofType(UserManagementService);
});
it('should throw if route does not contain parameter', () => {
const guard = new ResolveUserGuard(usersService.object, <any>new RouterMockup());
expect(() => guard.resolve(<any>{ params: {} }, <any>{})).toThrow('Route must contain user id.');
});
it('should navigate to 404 page if user is not found', (done) => {
usersService.setup(x => x.getUser('my-user'))
.returns(() => Observable.of(null!));
const router = new RouterMockup();
const guard = new ResolveUserGuard(usersService.object, <any>router);
guard.resolve(<any>route, <any>{})
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
done();
});
});
it('should navigate to 404 page if user loading fails', (done) => {
usersService.setup(x => x.getUser('my-user'))
.returns(() => Observable.throw(null!));
const router = new RouterMockup();
const guard = new ResolveUserGuard(usersService.object, <any>router);
guard.resolve(<any>route, <any>{})
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
done();
});
});
it('should return user if loading succeeded', (done) => {
const user: any = {};
usersService.setup(x => x.getUser('my-user'))
.returns(() => Observable.of(user));
const router = new RouterMockup();
const guard = new ResolveUserGuard(usersService.object, <any>router);
guard.resolve(<any>route, <any>{})
.subscribe(result => {
expect(result).toBe(user);
done();
});
});
});

48
src/Squidex/app/shared/guards/resolve-user.guard.ts

@ -1,48 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { allParams } from 'framework';
import { UserDto, UserManagementService } from './../services/users.service';
@Injectable()
export class ResolveUserGuard implements Resolve<UserDto | null> {
constructor(
private readonly userManagementService: UserManagementService,
private readonly router: Router
) {
}
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<UserDto | null> {
const params = allParams(route);
const userId = params['userId'];
if (!userId) {
throw 'Route must contain user id.';
}
const result =
this.userManagementService.getUser(userId)
.do(dto => {
if (!dto) {
this.router.navigate(['/404']);
}
})
.catch(error => {
this.router.navigate(['/404']);
return Observable.of(null);
});
return result;
}
}

9
src/Squidex/app/shared/module.ts

@ -7,6 +7,7 @@
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { ModuleWithProviders, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { DndModule } from 'ng2-dnd';
import { SqxFrameworkModule } from 'framework';
@ -28,7 +29,6 @@ import {
AuthService,
BackupsService,
ContentsService,
EventConsumersService,
FileIconPipe,
GeolocationEditorComponent,
GraphQlService,
@ -47,7 +47,6 @@ import {
ResolvePublishedSchemaGuard,
ResolveSchemaGuard,
SchemasService,
ResolveUserGuard,
RulesService,
UIService,
UnsetAppGuard,
@ -60,7 +59,6 @@ import {
UserIdPicturePipe,
UserPicturePipe,
UserPictureRefPipe,
UserManagementService,
UsersProviderService,
UsersService,
RichEditorComponent
@ -69,6 +67,7 @@ import {
@NgModule({
imports: [
DndModule,
RouterModule,
SqxFrameworkModule
],
declarations: [
@ -103,6 +102,7 @@ import {
HistoryComponent,
LanguageSelectorComponent,
MarkdownEditorComponent,
RouterModule,
UserDtoPicture,
UserEmailPipe,
UserEmailRefPipe,
@ -130,7 +130,6 @@ export class SqxSharedModule {
AuthService,
BackupsService,
ContentsService,
EventConsumersService,
GraphQlService,
HelpService,
HistoryService,
@ -142,13 +141,11 @@ export class SqxSharedModule {
ResolveContentGuard,
ResolvePublishedSchemaGuard,
ResolveSchemaGuard,
ResolveUserGuard,
RulesService,
SchemasService,
UIService,
UnsetAppGuard,
UsagesService,
UserManagementService,
UsersProviderService,
UsersService,
{

132
src/Squidex/app/shared/services/event-consumers.service.spec.ts

@ -1,132 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { inject, TestBed } from '@angular/core/testing';
import {
ApiUrlConfig,
EventConsumerDto,
EventConsumersService
} from './../';
describe('EventConsumerDto', () => {
it('should update isStopped property when starting', () => {
const consumer_1 = new EventConsumerDto('consumer', true, false, 'error', 'position');
const consumer_2 = consumer_1.start();
expect(consumer_2.isStopped).toBeFalsy();
});
it('should update isStopped property when starting', () => {
const consumer_1 = new EventConsumerDto('consumer', false, false, 'error', 'position');
const consumer_2 = consumer_1.stop();
expect(consumer_2.isStopped).toBeTruthy();
});
it('should update isResetting property when resetting', () => {
const consumer_1 = new EventConsumerDto('consumer', false, false, 'error', 'position');
const consumer_2 = consumer_1.reset();
expect(consumer_2.isResetting).toBeTruthy();
});
});
describe('EventConsumersService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule
],
providers: [
EventConsumersService,
{ provide: ApiUrlConfig, useValue: new ApiUrlConfig('http://service/p/') }
]
});
});
afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => {
httpMock.verify();
}));
it('should make get request to get event consumers',
inject([EventConsumersService, HttpTestingController], (eventConsumersService: EventConsumersService, httpMock: HttpTestingController) => {
let eventConsumers: EventConsumerDto[] | null = null;
eventConsumersService.getEventConsumers().subscribe(result => {
eventConsumers = result;
});
const req = httpMock.expectOne('http://service/p/api/event-consumers');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush([
{
name: 'event-consumer1',
position: '13',
isStopped: true,
isResetting: true,
error: 'an error 1'
},
{
name: 'event-consumer2',
position: '29',
isStopped: true,
isResetting: true,
error: 'an error 2'
}
]);
expect(eventConsumers).toEqual([
new EventConsumerDto('event-consumer1', true, true, 'an error 1', '13'),
new EventConsumerDto('event-consumer2', true, true, 'an error 2', '29')
]);
}));
it('should make put request to start event consumer',
inject([EventConsumersService, HttpTestingController], (eventConsumersService: EventConsumersService, httpMock: HttpTestingController) => {
eventConsumersService.startEventConsumer('event-consumer1').subscribe();
const req = httpMock.expectOne('http://service/p/api/event-consumers/event-consumer1/start');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({});
}));
it('should make put request to stop event consumer',
inject([EventConsumersService, HttpTestingController], (eventConsumersService: EventConsumersService, httpMock: HttpTestingController) => {
eventConsumersService.stopEventConsumer('event-consumer1').subscribe();
const req = httpMock.expectOne('http://service/p/api/event-consumers/event-consumer1/stop');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({});
}));
it('should make put request to reset event consumer',
inject([EventConsumersService, HttpTestingController], (eventConsumersService: EventConsumersService, httpMock: HttpTestingController) => {
eventConsumersService.resetEventConsumer('event-consumer1').subscribe();
const req = httpMock.expectOne('http://service/p/api/event-consumers/event-consumer1/reset');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({});
}));
});

88
src/Squidex/app/shared/services/event-consumers.service.ts

@ -1,88 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import 'framework/angular/http-extensions';
import { ApiUrlConfig, HTTP } from 'framework';
export class EventConsumerDto {
constructor(
public readonly name: string,
public readonly isStopped: boolean,
public readonly isResetting: boolean,
public readonly error: string,
public readonly position: string
) {
}
public start(): EventConsumerDto {
return new EventConsumerDto(this.name, false, false, this.error, this.position);
}
public stop(): EventConsumerDto {
return new EventConsumerDto(this.name, true, false, this.error, this.position);
}
public reset(): EventConsumerDto {
return new EventConsumerDto(this.name, this.isStopped, true, this.error, this.position);
}
}
@Injectable()
export class EventConsumersService {
constructor(
private readonly http: HttpClient,
private readonly apiUrl: ApiUrlConfig
) {
}
public getEventConsumers(): Observable<EventConsumerDto[]> {
const url = this.apiUrl.buildUrl('/api/event-consumers');
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const body = response.payload.body;
const items: any[] = body;
return items.map(item => {
return new EventConsumerDto(
item.name,
item.isStopped,
item.isResetting,
item.error,
item.position);
});
})
.pretifyError('Failed to load event consumers. Please reload.');
}
public startEventConsumer(name: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/start`);
return HTTP.putVersioned(this.http, url, {})
.pretifyError('Failed to start event consumer. Please reload.');
}
public stopEventConsumer(name: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/stop`);
return HTTP.putVersioned(this.http, url, {})
.pretifyError('Failed to stop event consumer. Please reload.');
}
public resetEventConsumer(name: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/reset`);
return HTTP.putVersioned(this.http, url, {})
.pretifyError('Failed to reset event consumer. Please reload.');
}
}

215
src/Squidex/app/shared/services/users.service.spec.ts

@ -10,38 +10,10 @@ import { inject, TestBed } from '@angular/core/testing';
import {
ApiUrlConfig,
CreateUserDto,
UpdateUserDto,
UserDto,
UserManagementService,
UsersDto,
UsersService
} from './../';
describe('UserDto', () => {
it('should update email and display name property when unlocking', () => {
const user_1 = new UserDto('1', 'sebastian@squidex.io', 'Sebastian', 'picture', true);
const user_2 = user_1.update('qaisar@squidex.io', 'Qaisar');
expect(user_2.email).toEqual('qaisar@squidex.io');
expect(user_2.displayName).toEqual('Qaisar');
});
it('should update isLocked property when locking', () => {
const user_1 = new UserDto('1', 'sebastian@squidex.io', 'Sebastian', 'picture', false);
const user_2 = user_1.lock();
expect(user_2.isLocked).toBeTruthy();
});
it('should update isLocked property when unlocking', () => {
const user_1 = new UserDto('1', 'sebastian@squidex.io', 'Sebastian', 'picture', true);
const user_2 = user_1.unlock();
expect(user_2.isLocked).toBeFalsy();
});
});
describe('UsersService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
@ -160,190 +132,3 @@ describe('UsersService', () => {
expect(user).toEqual(new UserDto('123', 'mail1@domain.com', 'User1', 'path/to/image1', true));
}));
});
describe('UserManagementService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule
],
providers: [
UserManagementService,
{ provide: ApiUrlConfig, useValue: new ApiUrlConfig('http://service/p/') }
]
});
});
afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => {
httpMock.verify();
}));
it('should make get request to get many users',
inject([UserManagementService, HttpTestingController], (userManagementService: UserManagementService, httpMock: HttpTestingController) => {
let users: UsersDto | null = null;
userManagementService.getUsers(20, 30).subscribe(result => {
users = result;
});
const req = httpMock.expectOne('http://service/p/api/user-management?take=20&skip=30&query=');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({
total: 100,
items: [
{
id: '123',
email: 'mail1@domain.com',
displayName: 'User1',
pictureUrl: 'path/to/image1',
isLocked: true
},
{
id: '456',
email: 'mail2@domain.com',
displayName: 'User2',
pictureUrl: 'path/to/image2',
isLocked: true
}
]
});
expect(users).toEqual(
new UsersDto(100, [
new UserDto('123', 'mail1@domain.com', 'User1', 'path/to/image1', true),
new UserDto('456', 'mail2@domain.com', 'User2', 'path/to/image2', true)
]));
}));
it('should make get request with query to get many users',
inject([UserManagementService, HttpTestingController], (userManagementService: UserManagementService, httpMock: HttpTestingController) => {
let users: UsersDto | null = null;
userManagementService.getUsers(20, 30, 'my-query').subscribe(result => {
users = result;
});
const req = httpMock.expectOne('http://service/p/api/user-management?take=20&skip=30&query=my-query');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({
total: 100,
items: [
{
id: '123',
email: 'mail1@domain.com',
displayName: 'User1',
pictureUrl: 'path/to/image1',
isLocked: true
},
{
id: '456',
email: 'mail2@domain.com',
displayName: 'User2',
pictureUrl: 'path/to/image2',
isLocked: true
}
]
});
expect(users).toEqual(
new UsersDto(100, [
new UserDto('123', 'mail1@domain.com', 'User1', 'path/to/image1', true),
new UserDto('456', 'mail2@domain.com', 'User2', 'path/to/image2', true)
]));
}));
it('should make get request to get single user',
inject([UserManagementService, HttpTestingController], (userManagementService: UserManagementService, httpMock: HttpTestingController) => {
let user: UserDto | null = null;
userManagementService.getUser('123').subscribe(result => {
user = result;
});
const req = httpMock.expectOne('http://service/p/api/user-management/123');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({
id: '123',
email: 'mail1@domain.com',
displayName: 'User1',
pictureUrl: 'path/to/image1',
isLocked: true
});
expect(user).toEqual(new UserDto('123', 'mail1@domain.com', 'User1', 'path/to/image1', true));
}));
it('should make post request to create user',
inject([UserManagementService, HttpTestingController], (userManagementService: UserManagementService, httpMock: HttpTestingController) => {
const dto = new CreateUserDto('mail@squidex.io', 'Squidex User', 'password');
let user: UserDto | null = null;
userManagementService.postUser(dto).subscribe(result => {
user = result;
});
const req = httpMock.expectOne('http://service/p/api/user-management');
expect(req.request.method).toEqual('POST');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({ id: '123', pictureUrl: 'path/to/image1' });
expect(user).toEqual(new UserDto('123', dto.email, dto.displayName, 'path/to/image1', false));
}));
it('should make put request to update user',
inject([UserManagementService, HttpTestingController], (userManagementService: UserManagementService, httpMock: HttpTestingController) => {
const dto = new UpdateUserDto('mail@squidex.io', 'Squidex User', 'password');
userManagementService.putUser('123', dto).subscribe();
const req = httpMock.expectOne('http://service/p/api/user-management/123');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({});
}));
it('should make put request to lock user',
inject([UserManagementService, HttpTestingController], (userManagementService: UserManagementService, httpMock: HttpTestingController) => {
userManagementService.lockUser('123').subscribe();
const req = httpMock.expectOne('http://service/p/api/user-management/123/lock');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({});
}));
it('should make put request to unlock user',
inject([UserManagementService, HttpTestingController], (userManagementService: UserManagementService, httpMock: HttpTestingController) => {
userManagementService.unlockUser('123').subscribe();
const req = httpMock.expectOne('http://service/p/api/user-management/123/unlock');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({});
}));
});

120
src/Squidex/app/shared/services/users.service.ts

@ -13,14 +13,6 @@ import 'framework/angular/http-extensions';
import { ApiUrlConfig, HTTP } from 'framework';
export class UsersDto {
constructor(
public readonly total: number,
public readonly items: UserDto[]
) {
}
}
export class UserDto {
constructor(
public readonly id: string,
@ -30,36 +22,6 @@ export class UserDto {
public readonly isLocked: boolean
) {
}
public update(email: string, displayName: string): UserDto {
return new UserDto(this.id, email, displayName, this.pictureUrl, this.isLocked);
}
public lock(): UserDto {
return new UserDto(this.id, this.email, this.displayName, this.pictureUrl, true);
}
public unlock(): UserDto {
return new UserDto(this.id, this.email, this.displayName, this.pictureUrl, false);
}
}
export class CreateUserDto {
constructor(
public readonly email: string,
public readonly displayName: string,
public readonly password: string
) {
}
}
export class UpdateUserDto {
constructor(
public readonly email: string,
public readonly displayName: string,
public readonly password: string
) {
}
}
@Injectable()
@ -108,85 +70,3 @@ export class UsersService {
.pretifyError('Failed to load user. Please reload.');
}
}
@Injectable()
export class UserManagementService {
constructor(
private readonly http: HttpClient,
private readonly apiUrl: ApiUrlConfig
) {
}
public getUsers(take: number, skip: number, query?: string): Observable<UsersDto> {
const url = this.apiUrl.buildUrl(`api/user-management?take=${take}&skip=${skip}&query=${query || ''}`);
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const body = response.payload.body;
const items: any[] = body.items;
const users = items.map(item => {
return new UserDto(
item.id,
item.email,
item.displayName,
item.pictureUrl,
item.isLocked);
});
return new UsersDto(body.total, users);
})
.pretifyError('Failed to load users. Please reload.');
}
public getUser(id: string): Observable<UserDto> {
const url = this.apiUrl.buildUrl(`api/user-management/${id}`);
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const body = response.payload.body;
return new UserDto(
body.id,
body.email,
body.displayName,
body.pictureUrl,
body.isLocked);
})
.pretifyError('Failed to load user. Please reload.');
}
public postUser(dto: CreateUserDto): Observable<UserDto> {
const url = this.apiUrl.buildUrl('api/user-management');
return HTTP.postVersioned<any>(this.http, url, dto)
.map(response => {
const body = response.payload.body;
return new UserDto(body.id, dto.email, dto.displayName, body.pictureUrl, false);
})
.pretifyError('Failed to create user. Please reload.');
}
public putUser(id: string, dto: UpdateUserDto): Observable<any> {
const url = this.apiUrl.buildUrl(`api/user-management/${id}`);
return HTTP.putVersioned(this.http, url, dto)
.pretifyError('Failed to update user. Please reload.');
}
public lockUser(id: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/user-management/${id}/lock`);
return HTTP.putVersioned(this.http, url, {})
.pretifyError('Failed to load users. Please retry.');
}
public unlockUser(id: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/user-management/${id}/unlock`);
return HTTP.putVersioned(this.http, url, {})
.pretifyError('Failed to load users. Please retry.');
}
}
Loading…
Cancel
Save