Browse Source

RX5 => RX6

pull/297/head
Sebastian 8 years ago
parent
commit
ecca2e150b
  1. 9
      src/Squidex/app-config/webpack.run.base.js
  2. 4
      src/Squidex/app/features/administration/guards/unset-user.guard.spec.ts
  3. 3
      src/Squidex/app/features/administration/guards/unset-user.guard.ts
  4. 6
      src/Squidex/app/features/administration/guards/user-must-exist.guard.spec.ts
  5. 9
      src/Squidex/app/features/administration/guards/user-must-exist.guard.ts
  6. 17
      src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts
  7. 15
      src/Squidex/app/features/administration/pages/users/users-page.component.ts
  8. 11
      src/Squidex/app/features/administration/services/event-consumers.service.spec.ts
  9. 26
      src/Squidex/app/features/administration/services/event-consumers.service.ts
  10. 16
      src/Squidex/app/features/administration/services/users.service.spec.ts
  11. 42
      src/Squidex/app/features/administration/services/users.service.ts
  12. 19
      src/Squidex/app/features/administration/state/event-consumers.state.spec.ts
  13. 50
      src/Squidex/app/features/administration/state/event-consumers.state.ts
  14. 28
      src/Squidex/app/features/administration/state/users.state.spec.ts
  15. 76
      src/Squidex/app/features/administration/state/users.state.ts
  16. 5
      src/Squidex/app/features/api/pages/graphql/graphql-page.component.ts
  17. 4
      src/Squidex/app/features/apps/pages/apps-page.component.ts
  18. 11
      src/Squidex/app/features/assets/pages/assets-page.component.ts
  19. 9
      src/Squidex/app/features/content/pages/content/content-history.component.ts
  20. 21
      src/Squidex/app/features/content/pages/content/content-page.component.ts
  21. 28
      src/Squidex/app/features/content/pages/contents/contents-page.component.ts
  22. 3
      src/Squidex/app/features/content/pages/schemas/schemas-page.component.ts
  23. 11
      src/Squidex/app/features/content/shared/contents-selector.component.ts
  24. 23
      src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts
  25. 11
      src/Squidex/app/features/rules/pages/events/rule-events-page.component.ts
  26. 13
      src/Squidex/app/features/rules/pages/rules/rules-page.component.ts
  27. 13
      src/Squidex/app/features/schemas/pages/schema/field.component.ts
  28. 5
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts
  29. 6
      src/Squidex/app/features/schemas/pages/schema/types/boolean-validation.component.ts
  30. 11
      src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.ts
  31. 11
      src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts
  32. 6
      src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.ts
  33. 11
      src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts
  34. 11
      src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts
  35. 5
      src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts
  36. 15
      src/Squidex/app/features/settings/pages/backups/backups-page.component.ts
  37. 5
      src/Squidex/app/features/settings/pages/clients/client.component.ts
  38. 7
      src/Squidex/app/features/settings/pages/clients/clients-page.component.ts
  39. 15
      src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts
  40. 3
      src/Squidex/app/features/settings/pages/languages/language.component.ts
  41. 5
      src/Squidex/app/features/settings/pages/languages/languages-page.component.ts
  42. 3
      src/Squidex/app/features/settings/pages/patterns/pattern.component.ts
  43. 5
      src/Squidex/app/features/settings/pages/patterns/patterns-page.component.ts
  44. 7
      src/Squidex/app/features/settings/pages/plans/plans-page.component.ts
  45. 12
      src/Squidex/app/framework/angular/animations.ts
  46. 34
      src/Squidex/app/framework/angular/forms/autocomplete.component.ts
  47. 4
      src/Squidex/app/framework/angular/forms/control-errors.component.ts
  48. 4
      src/Squidex/app/framework/angular/forms/jscript-editor.component.ts
  49. 4
      src/Squidex/app/framework/angular/forms/json-editor.component.ts
  50. 85
      src/Squidex/app/framework/angular/http/http-extensions-impl.ts
  51. 79
      src/Squidex/app/framework/angular/http/http-extensions.ts
  52. 4
      src/Squidex/app/framework/angular/routers/can-deactivate.guard.spec.ts
  53. 2
      src/Squidex/app/framework/declarations.ts
  54. 1
      src/Squidex/app/framework/internal.ts
  55. 4
      src/Squidex/app/framework/services/analytics.service.ts
  56. 8
      src/Squidex/app/framework/services/dialog.service.spec.ts
  57. 3
      src/Squidex/app/framework/services/message-bus.service.ts
  58. 5
      src/Squidex/app/framework/state.ts
  59. 4
      src/Squidex/app/framework/utils/date-helper.spec.ts
  60. 6
      src/Squidex/app/framework/utils/immutable-array.spec.ts
  61. 4
      src/Squidex/app/framework/utils/modal-view.spec.ts
  62. 22
      src/Squidex/app/framework/utils/pager.spec.ts
  63. 22
      src/Squidex/app/framework/utils/rxjs-extensions-impl.ts
  64. 25
      src/Squidex/app/framework/utils/rxjs-extensions.ts
  65. 2
      src/Squidex/app/framework/utils/types.spec.ts
  66. 9
      src/Squidex/app/shared/components/assets-list.component.ts
  67. 7
      src/Squidex/app/shared/components/assets-selector.component.ts
  68. 8
      src/Squidex/app/shared/components/history.component.ts
  69. 17
      src/Squidex/app/shared/components/pipes.ts
  70. 3
      src/Squidex/app/shared/components/schema-category.component.ts
  71. 6
      src/Squidex/app/shared/guards/app-must-exist.guard.spec.ts
  72. 8
      src/Squidex/app/shared/guards/app-must-exist.guard.ts
  73. 6
      src/Squidex/app/shared/guards/content-must-exist.guard.spec.ts
  74. 9
      src/Squidex/app/shared/guards/content-must-exist.guard.ts
  75. 4
      src/Squidex/app/shared/guards/load-apps.guard.spec.ts
  76. 3
      src/Squidex/app/shared/guards/load-apps.guard.ts
  77. 4
      src/Squidex/app/shared/guards/load-languages.guard.spec.ts
  78. 3
      src/Squidex/app/shared/guards/load-languages.guard.ts
  79. 6
      src/Squidex/app/shared/guards/must-be-authenticated.guard.spec.ts
  80. 10
      src/Squidex/app/shared/guards/must-be-authenticated.guard.ts
  81. 6
      src/Squidex/app/shared/guards/must-be-not-authenticated.guard.spec.ts
  82. 10
      src/Squidex/app/shared/guards/must-be-not-authenticated.guard.ts
  83. 8
      src/Squidex/app/shared/guards/schema-must-exist-published.guard.spec.ts
  84. 9
      src/Squidex/app/shared/guards/schema-must-exist-published.guard.ts
  85. 6
      src/Squidex/app/shared/guards/schema-must-exist.guard.spec.ts
  86. 9
      src/Squidex/app/shared/guards/schema-must-exist.guard.ts
  87. 4
      src/Squidex/app/shared/guards/unset-app.guard.spec.ts
  88. 3
      src/Squidex/app/shared/guards/unset-app.guard.ts
  89. 4
      src/Squidex/app/shared/guards/unset-content.guard.spec.ts
  90. 3
      src/Squidex/app/shared/guards/unset-content.guard.ts
  91. 23
      src/Squidex/app/shared/interceptors/auth.interceptor.spec.ts
  92. 32
      src/Squidex/app/shared/interceptors/auth.interceptor.ts
  93. 14
      src/Squidex/app/shared/services/app-clients.service.spec.ts
  94. 48
      src/Squidex/app/shared/services/app-clients.service.ts
  95. 6
      src/Squidex/app/shared/services/app-contributors.service.spec.ts
  96. 32
      src/Squidex/app/shared/services/app-contributors.service.ts
  97. 9
      src/Squidex/app/shared/services/app-languages.service.spec.ts
  98. 40
      src/Squidex/app/shared/services/app-languages.service.ts
  99. 8
      src/Squidex/app/shared/services/app-patterns.service.spec.ts
  100. 28
      src/Squidex/app/shared/services/app-patterns.service.ts

9
src/Squidex/app-config/webpack.run.base.js

@ -17,15 +17,6 @@ module.exports = webpackMerge(commonConfig, {
}, },
plugins: [ plugins: [
/**
* Shares common code between the pages.
*
* See: https://webpack.js.org/plugins/commons-chunk-plugin/
*/
new webpack.optimize.CommonsChunkPlugin({
name: ['app', 'shims']
}),
/** /**
* Simplifies creation of HTML files to serve your webpack bundles. * Simplifies creation of HTML files to serve your webpack bundles.
* This is especially useful for webpack bundles that include a hash in the filename * This is especially useful for webpack bundles that include a hash in the filename

4
src/Squidex/app/features/administration/guards/unset-user.guard.spec.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { UsersState } from './../state/users.state'; import { UsersState } from './../state/users.state';
@ -22,7 +22,7 @@ describe('UnsetUserGuard', () => {
it('should unset user', () => { it('should unset user', () => {
usersState.setup(x => x.select(null)) usersState.setup(x => x.select(null))
.returns(() => Observable.of(null)); .returns(() => of(null));
let result: boolean; let result: boolean;

3
src/Squidex/app/features/administration/guards/unset-user.guard.ts

@ -8,6 +8,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router'; import { CanActivate } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { UsersState } from './../state/users.state'; import { UsersState } from './../state/users.state';
@ -19,6 +20,6 @@ export class UnsetUserGuard implements CanActivate {
} }
public canActivate(): Observable<boolean> { public canActivate(): Observable<boolean> {
return this.usersState.select(null).map(u => u === null); return this.usersState.select(null).pipe(map(u => u === null));
} }
} }

6
src/Squidex/app/features/administration/guards/user-must-exist.guard.spec.ts

@ -6,7 +6,7 @@
*/ */
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { UserDto } from './../services/users.service'; import { UserDto } from './../services/users.service';
@ -32,7 +32,7 @@ describe('UserMustExistGuard', () => {
it('should load user and return true when found', () => { it('should load user and return true when found', () => {
usersState.setup(x => x.select('123')) usersState.setup(x => x.select('123'))
.returns(() => Observable.of(<UserDto>{})); .returns(() => of(<UserDto>{}));
let result: boolean; let result: boolean;
@ -47,7 +47,7 @@ describe('UserMustExistGuard', () => {
it('should load user and return false when not found', () => { it('should load user and return false when not found', () => {
usersState.setup(x => x.select('123')) usersState.setup(x => x.select('123'))
.returns(() => Observable.of(null)); .returns(() => of(null));
let result: boolean; let result: boolean;

9
src/Squidex/app/features/administration/guards/user-must-exist.guard.ts

@ -8,6 +8,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { allParams } from '@app/framework'; import { allParams } from '@app/framework';
@ -25,13 +26,13 @@ export class UserMustExistGuard implements CanActivate {
const userId = allParams(route)['userId']; const userId = allParams(route)['userId'];
const result = const result =
this.usersState.select(userId) this.usersState.select(userId).pipe(
.do(dto => { tap(dto => {
if (!dto) { if (!dto) {
this.router.navigate(['/404']); this.router.navigate(['/404']);
} }
}) }),
.map(u => u !== null); map(u => u !== null));
return result; return result;
} }

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

@ -6,7 +6,8 @@
*/ */
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Observable, Subscription } from 'rxjs'; import { Subscription, timer } from 'rxjs';
import { onErrorResumeNext, switchMap } from 'rxjs/operators';
import { ModalView } from '@app/shared'; import { ModalView } from '@app/shared';
@ -34,28 +35,28 @@ export class EventConsumersPageComponent implements OnDestroy, OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.eventConsumersState.load(false, true).onErrorResumeNext().subscribe(); this.eventConsumersState.load(false, true).pipe(onErrorResumeNext()).subscribe();
this.timerSubscription = this.timerSubscription =
Observable.timer(2000, 2000) timer(2000, 2000).pipe(
.switchMap(x => this.eventConsumersState.load(true, true).onErrorResumeNext()) switchMap(x => this.eventConsumersState.load(true, true)), onErrorResumeNext())
.subscribe(); .subscribe();
} }
public reload() { public reload() {
this.eventConsumersState.load(true, false).onErrorResumeNext().subscribe(); this.eventConsumersState.load(true, false).pipe(onErrorResumeNext()).subscribe();
} }
public start(es: EventConsumerDto) { public start(es: EventConsumerDto) {
this.eventConsumersState.start(es).onErrorResumeNext().subscribe(); this.eventConsumersState.start(es).pipe(onErrorResumeNext()).subscribe();
} }
public stop(es: EventConsumerDto) { public stop(es: EventConsumerDto) {
this.eventConsumersState.stop(es).onErrorResumeNext().subscribe(); this.eventConsumersState.stop(es).pipe(onErrorResumeNext()).subscribe();
} }
public reset(es: EventConsumerDto) { public reset(es: EventConsumerDto) {
this.eventConsumersState.reset(es).onErrorResumeNext().subscribe(); this.eventConsumersState.reset(es).pipe(onErrorResumeNext()).subscribe();
} }
public trackByEventConsumer(index: number, es: EventConsumerDto) { public trackByEventConsumer(index: number, es: EventConsumerDto) {

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

@ -7,6 +7,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { onErrorResumeNext } from 'rxjs/operators';
import { UserDto } from './../../services/users.service'; import { UserDto } from './../../services/users.service';
import { UsersState } from './../../state/users.state'; import { UsersState } from './../../state/users.state';
@ -25,31 +26,31 @@ export class UsersPageComponent implements OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.usersState.load().onErrorResumeNext().subscribe(); this.usersState.load().pipe(onErrorResumeNext()).subscribe();
} }
public reload() { public reload() {
this.usersState.load(true).onErrorResumeNext().subscribe(); this.usersState.load(true).pipe(onErrorResumeNext()).subscribe();
} }
public search() { public search() {
this.usersState.search(this.usersFilter.value).onErrorResumeNext().subscribe(); this.usersState.search(this.usersFilter.value).pipe(onErrorResumeNext()).subscribe();
} }
public goPrev() { public goPrev() {
this.usersState.goPrev().onErrorResumeNext().subscribe(); this.usersState.goPrev().pipe(onErrorResumeNext()).subscribe();
} }
public goNext() { public goNext() {
this.usersState.goNext().onErrorResumeNext().subscribe(); this.usersState.goNext().pipe(onErrorResumeNext()).subscribe();
} }
public lock(user: UserDto) { public lock(user: UserDto) {
this.usersState.lock(user).onErrorResumeNext().subscribe(); this.usersState.lock(user).pipe(onErrorResumeNext()).subscribe();
} }
public unlock(user: UserDto) { public unlock(user: UserDto) {
this.usersState.unlock(user).onErrorResumeNext().subscribe(); this.usersState.unlock(user).pipe(onErrorResumeNext()).subscribe();
} }
public trackByUser(index: number, userInfo: { user: UserDto }) { public trackByUser(index: number, userInfo: { user: UserDto }) {

11
src/Squidex/app/features/administration/services/event-consumers.service.spec.ts

@ -32,7 +32,7 @@ describe('EventConsumersService', () => {
it('should make get request to get event consumers', it('should make get request to get event consumers',
inject([EventConsumersService, HttpTestingController], (eventConsumersService: EventConsumersService, httpMock: HttpTestingController) => { inject([EventConsumersService, HttpTestingController], (eventConsumersService: EventConsumersService, httpMock: HttpTestingController) => {
let eventConsumers: EventConsumerDto[] | null = null; let eventConsumers: EventConsumerDto[];
eventConsumersService.getEventConsumers().subscribe(result => { eventConsumersService.getEventConsumers().subscribe(result => {
eventConsumers = result; eventConsumers = result;
@ -60,10 +60,11 @@ describe('EventConsumersService', () => {
} }
]); ]);
expect(eventConsumers).toEqual([ expect(eventConsumers!).toEqual(
new EventConsumerDto('event-consumer1', true, true, 'an error 1', '13'), [
new EventConsumerDto('event-consumer2', true, true, 'an error 2', '29') 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', it('should make put request to start event consumer',

26
src/Squidex/app/features/administration/services/event-consumers.service.ts

@ -8,13 +8,13 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import '@app/framework/angular/http/http-extensions';
import { import {
ApiUrlConfig, ApiUrlConfig,
HTTP, HTTP,
Model Model,
pretifyError
} from '@app/shared'; } from '@app/shared';
export class EventConsumerDto extends Model { export class EventConsumerDto extends Model {
@ -44,8 +44,8 @@ export class EventConsumersService {
public getEventConsumers(): Observable<EventConsumerDto[]> { public getEventConsumers(): Observable<EventConsumerDto[]> {
const url = this.apiUrl.buildUrl('/api/event-consumers'); const url = this.apiUrl.buildUrl('/api/event-consumers');
return HTTP.getVersioned<any>(this.http, url) return HTTP.getVersioned<any>(this.http, url).pipe(
.map(response => { map(response => {
const body = response.payload.body; const body = response.payload.body;
const items: any[] = body; const items: any[] = body;
@ -58,28 +58,28 @@ export class EventConsumersService {
item.error, item.error,
item.position); item.position);
}); });
}) }),
.pretifyError('Failed to load event consumers. Please reload.'); pretifyError('Failed to load event consumers. Please reload.'));
} }
public putStart(name: string): Observable<any> { public putStart(name: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/start`); const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/start`);
return HTTP.putVersioned(this.http, url, {}) return HTTP.putVersioned(this.http, url, {}).pipe(
.pretifyError('Failed to start event consumer. Please reload.'); pretifyError('Failed to start event consumer. Please reload.'));
} }
public putStop(name: string): Observable<any> { public putStop(name: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/stop`); const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/stop`);
return HTTP.putVersioned(this.http, url, {}) return HTTP.putVersioned(this.http, url, {}).pipe(
.pretifyError('Failed to stop event consumer. Please reload.'); pretifyError('Failed to stop event consumer. Please reload.'));
} }
public putReset(name: string): Observable<any> { public putReset(name: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/reset`); const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/reset`);
return HTTP.putVersioned(this.http, url, {}) return HTTP.putVersioned(this.http, url, {}).pipe(
.pretifyError('Failed to reset event consumer. Please reload.'); pretifyError('Failed to reset event consumer. Please reload.'));
} }
} }

16
src/Squidex/app/features/administration/services/users.service.spec.ts

@ -38,7 +38,7 @@ describe('UsersService', () => {
it('should make get request to get many users', it('should make get request to get many users',
inject([UsersService, HttpTestingController], (userManagementService: UsersService, httpMock: HttpTestingController) => { inject([UsersService, HttpTestingController], (userManagementService: UsersService, httpMock: HttpTestingController) => {
let users: UsersDto | null = null; let users: UsersDto;
userManagementService.getUsers(20, 30).subscribe(result => { userManagementService.getUsers(20, 30).subscribe(result => {
users = result; users = result;
@ -67,7 +67,7 @@ describe('UsersService', () => {
] ]
}); });
expect(users).toEqual( expect(users!).toEqual(
new UsersDto(100, [ new UsersDto(100, [
new UserDto('123', 'mail1@domain.com', 'User1', true), new UserDto('123', 'mail1@domain.com', 'User1', true),
new UserDto('456', 'mail2@domain.com', 'User2', true) new UserDto('456', 'mail2@domain.com', 'User2', true)
@ -77,7 +77,7 @@ describe('UsersService', () => {
it('should make get request with query to get many users', it('should make get request with query to get many users',
inject([UsersService, HttpTestingController], (userManagementService: UsersService, httpMock: HttpTestingController) => { inject([UsersService, HttpTestingController], (userManagementService: UsersService, httpMock: HttpTestingController) => {
let users: UsersDto | null = null; let users: UsersDto;
userManagementService.getUsers(20, 30, 'my-query').subscribe(result => { userManagementService.getUsers(20, 30, 'my-query').subscribe(result => {
users = result; users = result;
@ -106,7 +106,7 @@ describe('UsersService', () => {
] ]
}); });
expect(users).toEqual( expect(users!).toEqual(
new UsersDto(100, [ new UsersDto(100, [
new UserDto('123', 'mail1@domain.com', 'User1', true), new UserDto('123', 'mail1@domain.com', 'User1', true),
new UserDto('456', 'mail2@domain.com', 'User2', true) new UserDto('456', 'mail2@domain.com', 'User2', true)
@ -116,7 +116,7 @@ describe('UsersService', () => {
it('should make get request to get single user', it('should make get request to get single user',
inject([UsersService, HttpTestingController], (userManagementService: UsersService, httpMock: HttpTestingController) => { inject([UsersService, HttpTestingController], (userManagementService: UsersService, httpMock: HttpTestingController) => {
let user: UserDto | null = null; let user: UserDto;
userManagementService.getUser('123').subscribe(result => { userManagementService.getUser('123').subscribe(result => {
user = result; user = result;
@ -135,7 +135,7 @@ describe('UsersService', () => {
isLocked: true isLocked: true
}); });
expect(user).toEqual(new UserDto('123', 'mail1@domain.com', 'User1', true)); expect(user!).toEqual(new UserDto('123', 'mail1@domain.com', 'User1', true));
})); }));
it('should make post request to create user', it('should make post request to create user',
@ -143,7 +143,7 @@ describe('UsersService', () => {
const dto = new CreateUserDto('mail@squidex.io', 'Squidex User', 'password'); const dto = new CreateUserDto('mail@squidex.io', 'Squidex User', 'password');
let user: UserDto | null = null; let user: UserDto;
userManagementService.postUser(dto).subscribe(result => { userManagementService.postUser(dto).subscribe(result => {
user = result; user = result;
@ -156,7 +156,7 @@ describe('UsersService', () => {
req.flush({ id: '123', pictureUrl: 'path/to/image1' }); req.flush({ id: '123', pictureUrl: 'path/to/image1' });
expect(user).toEqual(new UserDto('123', dto.email, dto.displayName, false)); expect(user!).toEqual(new UserDto('123', dto.email, dto.displayName, false));
})); }));
it('should make put request to update user', it('should make put request to update user',

42
src/Squidex/app/features/administration/services/users.service.ts

@ -8,13 +8,13 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import '@app/framework/angular/http/http-extensions';
import { import {
ApiUrlConfig, ApiUrlConfig,
HTTP, HTTP,
Model Model,
pretifyError
} from '@app/shared'; } from '@app/shared';
export class UsersDto extends Model { export class UsersDto extends Model {
@ -74,8 +74,8 @@ export class UsersService {
public getUsers(take: number, skip: number, query?: string): Observable<UsersDto> { public getUsers(take: number, skip: number, query?: string): Observable<UsersDto> {
const url = this.apiUrl.buildUrl(`api/user-management?take=${take}&skip=${skip}&query=${query || ''}`); const url = this.apiUrl.buildUrl(`api/user-management?take=${take}&skip=${skip}&query=${query || ''}`);
return HTTP.getVersioned<any>(this.http, url) return HTTP.getVersioned<any>(this.http, url).pipe(
.map(response => { map(response => {
const body = response.payload.body; const body = response.payload.body;
const items: any[] = body.items; const items: any[] = body.items;
@ -89,15 +89,15 @@ export class UsersService {
}); });
return new UsersDto(body.total, users); return new UsersDto(body.total, users);
}) }),
.pretifyError('Failed to load users. Please reload.'); pretifyError('Failed to load users. Please reload.'));
} }
public getUser(id: string): Observable<UserDto> { public getUser(id: string): Observable<UserDto> {
const url = this.apiUrl.buildUrl(`api/user-management/${id}`); const url = this.apiUrl.buildUrl(`api/user-management/${id}`);
return HTTP.getVersioned<any>(this.http, url) return HTTP.getVersioned<any>(this.http, url).pipe(
.map(response => { map(response => {
const body = response.payload.body; const body = response.payload.body;
return new UserDto( return new UserDto(
@ -105,15 +105,15 @@ export class UsersService {
body.email, body.email,
body.displayName, body.displayName,
body.isLocked); body.isLocked);
}) }),
.pretifyError('Failed to load user. Please reload.'); pretifyError('Failed to load user. Please reload.'));
} }
public postUser(dto: CreateUserDto): Observable<UserDto> { public postUser(dto: CreateUserDto): Observable<UserDto> {
const url = this.apiUrl.buildUrl('api/user-management'); const url = this.apiUrl.buildUrl('api/user-management');
return HTTP.postVersioned<any>(this.http, url, dto) return HTTP.postVersioned<any>(this.http, url, dto).pipe(
.map(response => { map(response => {
const body = response.payload.body; const body = response.payload.body;
return new UserDto( return new UserDto(
@ -121,28 +121,28 @@ export class UsersService {
dto.email, dto.email,
dto.displayName, dto.displayName,
false); false);
}) }),
.pretifyError('Failed to create user. Please reload.'); pretifyError('Failed to create user. Please reload.'));
} }
public putUser(id: string, dto: UpdateUserDto): Observable<any> { public putUser(id: string, dto: UpdateUserDto): Observable<any> {
const url = this.apiUrl.buildUrl(`api/user-management/${id}`); const url = this.apiUrl.buildUrl(`api/user-management/${id}`);
return HTTP.putVersioned(this.http, url, dto) return HTTP.putVersioned(this.http, url, dto).pipe(
.pretifyError('Failed to update user. Please reload.'); pretifyError('Failed to update user. Please reload.'));
} }
public lockUser(id: string): Observable<any> { public lockUser(id: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/user-management/${id}/lock`); const url = this.apiUrl.buildUrl(`api/user-management/${id}/lock`);
return HTTP.putVersioned(this.http, url, {}) return HTTP.putVersioned(this.http, url, {}).pipe(
.pretifyError('Failed to load users. Please retry.'); pretifyError('Failed to load users. Please retry.'));
} }
public unlockUser(id: string): Observable<any> { public unlockUser(id: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/user-management/${id}/unlock`); const url = this.apiUrl.buildUrl(`api/user-management/${id}/unlock`);
return HTTP.putVersioned(this.http, url, {}) return HTTP.putVersioned(this.http, url, {}).pipe(
.pretifyError('Failed to load users. Please retry.'); pretifyError('Failed to load users. Please retry.'));
} }
} }

19
src/Squidex/app/features/administration/state/event-consumers.state.spec.ts

@ -5,7 +5,8 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Observable } from 'rxjs'; import { of, throwError } from 'rxjs';
import { onErrorResumeNext } from 'rxjs/operators';
import { IMock, It, Mock, Times } from 'typemoq'; import { IMock, It, Mock, Times } from 'typemoq';
import { DialogService } from '@app/shared'; import { DialogService } from '@app/shared';
@ -29,7 +30,7 @@ describe('EventConsumersState', () => {
eventConsumersService = Mock.ofType<EventConsumersService>(); eventConsumersService = Mock.ofType<EventConsumersService>();
eventConsumersService.setup(x => x.getEventConsumers()) eventConsumersService.setup(x => x.getEventConsumers())
.returns(() => Observable.of(oldConsumers)); .returns(() => of(oldConsumers));
eventConsumersState = new EventConsumersState(dialogs.object, eventConsumersService.object); eventConsumersState = new EventConsumersState(dialogs.object, eventConsumersService.object);
eventConsumersState.load().subscribe(); eventConsumersState.load().subscribe();
@ -50,25 +51,25 @@ describe('EventConsumersState', () => {
it('should show notification on load error when silent is true', () => { it('should show notification on load error when silent is true', () => {
eventConsumersService.setup(x => x.getEventConsumers()) eventConsumersService.setup(x => x.getEventConsumers())
.returns(() => Observable.throw({})); .returns(() => throwError({}));
eventConsumersState.load(true, true).onErrorResumeNext().subscribe(); eventConsumersState.load(true, true).pipe(onErrorResumeNext()).subscribe();
dialogs.verify(x => x.notifyError(It.isAny()), Times.once()); dialogs.verify(x => x.notifyError(It.isAny()), Times.once());
}); });
it('should not show notification on load error when flag is false', () => { it('should not show notification on load error when flag is false', () => {
eventConsumersService.setup(x => x.getEventConsumers()) eventConsumersService.setup(x => x.getEventConsumers())
.returns(() => Observable.throw({})); .returns(() => throwError({}));
eventConsumersState.load().onErrorResumeNext().subscribe(); eventConsumersState.load().pipe(onErrorResumeNext()).subscribe();
dialogs.verify(x => x.notifyError(It.isAny()), Times.never()); dialogs.verify(x => x.notifyError(It.isAny()), Times.never());
}); });
it('should unmark as stopped when started', () => { it('should unmark as stopped when started', () => {
eventConsumersService.setup(x => x.putStart(oldConsumers[1].name)) eventConsumersService.setup(x => x.putStart(oldConsumers[1].name))
.returns(() => Observable.of({})); .returns(() => of({}));
eventConsumersState.start(oldConsumers[1]).subscribe(); eventConsumersState.start(oldConsumers[1]).subscribe();
@ -79,7 +80,7 @@ describe('EventConsumersState', () => {
it('should mark as stopped when stopped', () => { it('should mark as stopped when stopped', () => {
eventConsumersService.setup(x => x.putStop(oldConsumers[0].name)) eventConsumersService.setup(x => x.putStop(oldConsumers[0].name))
.returns(() => Observable.of({})); .returns(() => of({}));
eventConsumersState.stop(oldConsumers[0]).subscribe(); eventConsumersState.stop(oldConsumers[0]).subscribe();
@ -90,7 +91,7 @@ describe('EventConsumersState', () => {
it('should mark as resetting when reset', () => { it('should mark as resetting when reset', () => {
eventConsumersService.setup(x => x.putReset(oldConsumers[0].name)) eventConsumersService.setup(x => x.putReset(oldConsumers[0].name))
.returns(() => Observable.of({})); .returns(() => of({}));
eventConsumersState.reset(oldConsumers[0]).subscribe(); eventConsumersState.reset(oldConsumers[0]).subscribe();

50
src/Squidex/app/features/administration/state/event-consumers.state.ts

@ -6,13 +6,13 @@
*/ */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable, throwError } from 'rxjs';
import { catchError, distinctUntilChanged, map, tap } from 'rxjs/operators';
import '@app/framework/utils/rxjs-extensions';
import { import {
DialogService, DialogService,
ImmutableArray, ImmutableArray,
notify,
State State
} from '@app/shared'; } from '@app/shared';
@ -27,12 +27,12 @@ interface Snapshot {
@Injectable() @Injectable()
export class EventConsumersState extends State<Snapshot> { export class EventConsumersState extends State<Snapshot> {
public eventConsumers = public eventConsumers =
this.changes.map(x => x.eventConsumers) this.changes.pipe(map(x => x.eventConsumers),
.distinctUntilChanged(); distinctUntilChanged());
public isLoaded = public isLoaded =
this.changes.map(x => !!x.isLoaded) this.changes.pipe(map(x => !!x.isLoaded),
.distinctUntilChanged(); distinctUntilChanged());
constructor( constructor(
private readonly dialogs: DialogService, private readonly dialogs: DialogService,
@ -46,8 +46,8 @@ export class EventConsumersState extends State<Snapshot> {
this.resetState(); this.resetState();
} }
return this.eventConsumersService.getEventConsumers() return this.eventConsumersService.getEventConsumers().pipe(
.do(dtos => { tap(dtos => {
if (isReload && !silent) { if (isReload && !silent) {
this.dialogs.notifyInfo('Event Consumers reloaded.'); this.dialogs.notifyInfo('Event Consumers reloaded.');
} }
@ -57,38 +57,38 @@ export class EventConsumersState extends State<Snapshot> {
return { ...s, eventConsumers, isLoaded: true }; return { ...s, eventConsumers, isLoaded: true };
}); });
}) }),
.catch(error => { catchError(error => {
if (silent) { if (silent) {
this.dialogs.notifyError(error); this.dialogs.notifyError(error);
} }
return Observable.throw(error); return throwError(error);
}); }));
} }
public start(eventConsumer: EventConsumerDto): Observable<any> { public start(eventConsumer: EventConsumerDto): Observable<any> {
return this.eventConsumersService.putStart(eventConsumer.name) return this.eventConsumersService.putStart(eventConsumer.name).pipe(
.do(() => { tap(() => {
this.replaceEventConsumer(setStopped(eventConsumer, false)); this.replaceEventConsumer(setStopped(eventConsumer, false));
}) }),
.notify(this.dialogs); notify(this.dialogs));
} }
public stop(eventConsumer: EventConsumerDto): Observable<any> { public stop(eventConsumer: EventConsumerDto): Observable<any> {
return this.eventConsumersService.putStop(eventConsumer.name) return this.eventConsumersService.putStop(eventConsumer.name).pipe(
.do(() => { tap(() => {
this.replaceEventConsumer(setStopped(eventConsumer, true)); this.replaceEventConsumer(setStopped(eventConsumer, true));
}) }),
.notify(this.dialogs); notify(this.dialogs));
} }
public reset(eventConsumer: EventConsumerDto): Observable<any> { public reset(eventConsumer: EventConsumerDto): Observable<any> {
return this.eventConsumersService.putReset(eventConsumer.name) return this.eventConsumersService.putReset(eventConsumer.name).pipe(
.do(() => { tap(() => {
this.replaceEventConsumer(reset(eventConsumer)); this.replaceEventConsumer(reset(eventConsumer));
}) }),
.notify(this.dialogs); notify(this.dialogs));
} }
private replaceEventConsumer(eventConsumer: EventConsumerDto) { private replaceEventConsumer(eventConsumer: EventConsumerDto) {

28
src/Squidex/app/features/administration/state/users.state.spec.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Observable } from 'rxjs'; import { of, throwError } from 'rxjs';
import { IMock, It, Mock, Times } from 'typemoq'; import { IMock, It, Mock, Times } from 'typemoq';
import { AuthService, DialogService } from '@app/shared'; import { AuthService, DialogService } from '@app/shared';
@ -44,7 +44,7 @@ describe('UsersState', () => {
usersService = Mock.ofType<UsersService>(); usersService = Mock.ofType<UsersService>();
usersService.setup(x => x.getUsers(10, 0, undefined)) usersService.setup(x => x.getUsers(10, 0, undefined))
.returns(() => Observable.of(new UsersDto(200, oldUsers))); .returns(() => of(new UsersDto(200, oldUsers)));
usersState = new UsersState(authService.object, dialogs.object, usersService.object); usersState = new UsersState(authService.object, dialogs.object, usersService.object);
usersState.load().subscribe(); usersState.load().subscribe();
@ -76,7 +76,7 @@ describe('UsersState', () => {
]; ];
usersService.setup(x => x.getUsers(10, 0, undefined)) usersService.setup(x => x.getUsers(10, 0, undefined))
.returns(() => Observable.of(new UsersDto(200, newUsers))); .returns(() => of(new UsersDto(200, newUsers)));
usersState.load().subscribe(); usersState.load().subscribe();
@ -98,7 +98,7 @@ describe('UsersState', () => {
it('should return user on select and load when not loaded', () => { it('should return user on select and load when not loaded', () => {
usersService.setup(x => x.getUser('id3')) usersService.setup(x => x.getUser('id3'))
.returns(() => Observable.of(newUser)); .returns(() => of(newUser));
let selectedUser: UserDto; let selectedUser: UserDto;
@ -127,7 +127,7 @@ describe('UsersState', () => {
it('should return null on select when user is not found', () => { it('should return null on select when user is not found', () => {
usersService.setup(x => x.getUser('unknown')) usersService.setup(x => x.getUser('unknown'))
.returns(() => Observable.throw({})); .returns(() => throwError({}));
let selectedUser: UserDto; let selectedUser: UserDto;
@ -141,7 +141,7 @@ describe('UsersState', () => {
it('should mark as locked when locked', () => { it('should mark as locked when locked', () => {
usersService.setup(x => x.lockUser('id1')) usersService.setup(x => x.lockUser('id1'))
.returns(() => Observable.of({})); .returns(() => of({}));
usersState.select('id1').subscribe(); usersState.select('id1').subscribe();
usersState.lock(oldUsers[0]).subscribe(); usersState.lock(oldUsers[0]).subscribe();
@ -149,12 +149,12 @@ describe('UsersState', () => {
const user_1 = usersState.snapshot.users.at(0); const user_1 = usersState.snapshot.users.at(0);
expect(user_1.user.isLocked).toBeTruthy(); expect(user_1.user.isLocked).toBeTruthy();
expect(user_1).toBe(usersState.snapshot.selectedUser); expect(user_1).toBe(usersState.snapshot.selectedUser!);
}); });
it('should unmark as locked when unlocked', () => { it('should unmark as locked when unlocked', () => {
usersService.setup(x => x.unlockUser('id2')) usersService.setup(x => x.unlockUser('id2'))
.returns(() => Observable.of({})); .returns(() => of({}));
usersState.select('id2').subscribe(); usersState.select('id2').subscribe();
usersState.unlock(oldUsers[1]).subscribe(); usersState.unlock(oldUsers[1]).subscribe();
@ -162,14 +162,14 @@ describe('UsersState', () => {
const user_1 = usersState.snapshot.users.at(1); const user_1 = usersState.snapshot.users.at(1);
expect(user_1.user.isLocked).toBeFalsy(); expect(user_1.user.isLocked).toBeFalsy();
expect(user_1).toBe(usersState.snapshot.selectedUser); expect(user_1).toBe(usersState.snapshot.selectedUser!);
}); });
it('should update user properties when updated', () => { it('should update user properties when updated', () => {
const request = new UpdateUserDto('new@mail.com', 'New'); const request = new UpdateUserDto('new@mail.com', 'New');
usersService.setup(x => x.putUser('id1', request)) usersService.setup(x => x.putUser('id1', request))
.returns(() => Observable.of({})); .returns(() => of({}));
usersState.select('id1').subscribe(); usersState.select('id1').subscribe();
usersState.update(oldUsers[0], request).subscribe(); usersState.update(oldUsers[0], request).subscribe();
@ -178,14 +178,14 @@ describe('UsersState', () => {
expect(user_1.user.email).toEqual('new@mail.com'); expect(user_1.user.email).toEqual('new@mail.com');
expect(user_1.user.displayName).toEqual('New'); expect(user_1.user.displayName).toEqual('New');
expect(user_1).toBe(usersState.snapshot.selectedUser); expect(user_1).toBe(usersState.snapshot.selectedUser!);
}); });
it('should add user to snapshot when created', () => { it('should add user to snapshot when created', () => {
const request = new CreateUserDto(newUser.email, newUser.displayName, 'password'); const request = new CreateUserDto(newUser.email, newUser.displayName, 'password');
usersService.setup(x => x.postUser(request)) usersService.setup(x => x.postUser(request))
.returns(() => Observable.of(newUser)); .returns(() => of(newUser));
usersState.create(request).subscribe(); usersState.create(request).subscribe();
@ -199,7 +199,7 @@ describe('UsersState', () => {
it('should load next page and prev page when paging', () => { it('should load next page and prev page when paging', () => {
usersService.setup(x => x.getUsers(10, 10, undefined)) usersService.setup(x => x.getUsers(10, 10, undefined))
.returns(() => Observable.of(new UsersDto(200, []))); .returns(() => of(new UsersDto(200, [])));
usersState.goNext().subscribe(); usersState.goNext().subscribe();
usersState.goPrev().subscribe(); usersState.goPrev().subscribe();
@ -210,7 +210,7 @@ describe('UsersState', () => {
it('should load with query when searching', () => { it('should load with query when searching', () => {
usersService.setup(x => x.getUsers(10, 0, 'my-query')) usersService.setup(x => x.getUsers(10, 0, 'my-query'))
.returns(() => Observable.of(new UsersDto(0, []))); .returns(() => of(new UsersDto(0, [])));
usersState.search('my-query').subscribe(); usersState.search('my-query').subscribe();

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

@ -7,7 +7,8 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable, of } from 'rxjs';
import { catchError, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
import '@app/framework/utils/rxjs-extensions'; import '@app/framework/utils/rxjs-extensions';
@ -16,6 +17,7 @@ import {
DialogService, DialogService,
Form, Form,
ImmutableArray, ImmutableArray,
notify,
Pager, Pager,
State, State,
ValidatorsEx ValidatorsEx
@ -89,20 +91,20 @@ interface Snapshot {
@Injectable() @Injectable()
export class UsersState extends State<Snapshot> { export class UsersState extends State<Snapshot> {
public users = public users =
this.changes.map(x => x.users) this.changes.pipe(map(x => x.users),
.distinctUntilChanged(); distinctUntilChanged());
public usersPager = public usersPager =
this.changes.map(x => x.usersPager) this.changes.pipe(map(x => x.usersPager),
.distinctUntilChanged(); distinctUntilChanged());
public selectedUser = public selectedUser =
this.changes.map(x => x.selectedUser) this.changes.pipe(map(x => x.selectedUser),
.distinctUntilChanged(); distinctUntilChanged());
public isLoaded = public isLoaded =
this.changes.map(x => !!x.isLoaded) this.changes.pipe(map(x => !!x.isLoaded),
.distinctUntilChanged(); distinctUntilChanged());
constructor( constructor(
private readonly authState: AuthService, private readonly authState: AuthService,
@ -113,24 +115,24 @@ export class UsersState extends State<Snapshot> {
} }
public select(id: string | null): Observable<UserDto | null> { public select(id: string | null): Observable<UserDto | null> {
return this.loadUser(id) return this.loadUser(id).pipe(
.do(selectedUser => { tap(selectedUser => {
this.next(s => ({ ...s, selectedUser })); this.next(s => ({ ...s, selectedUser }));
}) }),
.map(x => x && x.user); map(x => x && x.user));
} }
private loadUser(id: string | null) { private loadUser(id: string | null) {
return !id ? return !id ?
Observable.of(null) : of(null) :
Observable.of(this.snapshot.users.find(x => x.user.id === id)) of(this.snapshot.users.find(x => x.user.id === id)).pipe(
.switchMap(user => { switchMap(user => {
if (!user) { if (!user) {
return this.usersService.getUser(id).map(x => this.createUser(x)).catch(() => Observable.of(null)); return this.usersService.getUser(id).pipe(map(x => this.createUser(x)), catchError(() => of(null)));
} else { } else {
return Observable.of(user); return of(user);
} }
}); }));
} }
public load(isReload = false): Observable<any> { public load(isReload = false): Observable<any> {
@ -145,8 +147,8 @@ export class UsersState extends State<Snapshot> {
return this.usersService.getUsers( return this.usersService.getUsers(
this.snapshot.usersPager.pageSize, this.snapshot.usersPager.pageSize,
this.snapshot.usersPager.skip, this.snapshot.usersPager.skip,
this.snapshot.usersQuery) this.snapshot.usersQuery).pipe(
.do(dtos => { tap(dtos => {
if (isReload) { if (isReload) {
this.dialogs.notifyInfo('Users reloaded.'); this.dialogs.notifyInfo('Users reloaded.');
} }
@ -163,43 +165,43 @@ export class UsersState extends State<Snapshot> {
return { ...s, users, usersPager, selectedUser, isLoaded: true }; return { ...s, users, usersPager, selectedUser, isLoaded: true };
}); });
}) }),
.notify(this.dialogs); notify(this.dialogs));
} }
public create(request: CreateUserDto): Observable<UserDto> { public create(request: CreateUserDto): Observable<UserDto> {
return this.usersService.postUser(request) return this.usersService.postUser(request).pipe(
.do(dto => { tap(dto => {
this.next(s => { this.next(s => {
const users = s.users.pushFront(this.createUser(dto)); const users = s.users.pushFront(this.createUser(dto));
const usersPager = s.usersPager.incrementCount(); const usersPager = s.usersPager.incrementCount();
return { ...s, users, usersPager }; return { ...s, users, usersPager };
}); });
}); }));
} }
public update(user: UserDto, request: UpdateUserDto): Observable<any> { public update(user: UserDto, request: UpdateUserDto): Observable<any> {
return this.usersService.putUser(user.id, request) return this.usersService.putUser(user.id, request).pipe(
.do(() => { tap(() => {
this.replaceUser(update(user, request)); this.replaceUser(update(user, request));
}); }));
} }
public lock(user: UserDto): Observable<any> { public lock(user: UserDto): Observable<any> {
return this.usersService.lockUser(user.id) return this.usersService.lockUser(user.id).pipe(
.do(() => { tap(() => {
this.replaceUser(setLocked(user, true)); this.replaceUser(setLocked(user, true));
}) }),
.notify(this.dialogs); notify(this.dialogs));
} }
public unlock(user: UserDto): Observable<any> { public unlock(user: UserDto): Observable<any> {
return this.usersService.unlockUser(user.id) return this.usersService.unlockUser(user.id).pipe(
.do(() => { tap(() => {
this.replaceUser(setLocked(user, false)); this.replaceUser(setLocked(user, false));
}) }),
.notify(this.dialogs); notify(this.dialogs));
} }
public search(query: string): Observable<any> { public search(query: string): Observable<any> {

5
src/Squidex/app/features/api/pages/graphql/graphql-page.component.ts

@ -6,7 +6,8 @@
*/ */
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { catchError } from 'rxjs/operators';
import * as React from 'react'; import * as React from 'react';
import * as ReactDOM from 'react-dom'; import * as ReactDOM from 'react-dom';
@ -42,7 +43,7 @@ export class GraphQLPageComponent implements OnInit {
} }
private request(params: any) { private request(params: any) {
return this.graphQlService.query(this.appsState.appName, params).catch(response => Observable.of(response.error)).toPromise(); return this.graphQlService.query(this.appsState.appName, params).pipe(catchError(response => of(response.error))).toPromise();
} }
} }

4
src/Squidex/app/features/apps/pages/apps-page.component.ts

@ -6,6 +6,7 @@
*/ */
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { take } from 'rxjs/operators';
import { import {
AppsState, AppsState,
@ -33,7 +34,8 @@ export class AppsPageComponent implements OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.appsState.apps.take(1) this.appsState.apps.pipe(
take(1))
.subscribe(apps => { .subscribe(apps => {
if (this.onboardingService.shouldShow('dialog') && apps.length === 0) { if (this.onboardingService.shouldShow('dialog') && apps.length === 0) {
this.onboardingService.disable('dialog'); this.onboardingService.disable('dialog');

11
src/Squidex/app/features/assets/pages/assets-page.component.ts

@ -9,6 +9,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { onErrorResumeNext } from 'rxjs/operators';
import { AppsState, AssetsState } from '@app/shared'; import { AppsState, AssetsState } from '@app/shared';
@ -27,23 +28,23 @@ export class AssetsPageComponent implements OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.assetsState.load().onErrorResumeNext().subscribe(); this.assetsState.load().pipe(onErrorResumeNext()).subscribe();
} }
public reload() { public reload() {
this.assetsState.load(true).onErrorResumeNext().subscribe(); this.assetsState.load(true).pipe(onErrorResumeNext()).subscribe();
} }
public search() { public search() {
this.assetsState.search(this.assetsFilter.value).onErrorResumeNext().subscribe(); this.assetsState.search(this.assetsFilter.value).pipe(onErrorResumeNext()).subscribe();
} }
public goNext() { public goNext() {
this.assetsState.goNext().onErrorResumeNext().subscribe(); this.assetsState.goNext().pipe(onErrorResumeNext()).subscribe();
} }
public goPrev() { public goPrev() {
this.assetsState.goPrev().onErrorResumeNext().subscribe(); this.assetsState.goPrev().pipe(onErrorResumeNext()).subscribe();
} }
} }

9
src/Squidex/app/features/content/pages/content/content-history.component.ts

@ -7,7 +7,8 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable, timer } from 'rxjs';
import { delay, merge, switchMap } from 'rxjs/operators';
import { import {
allParams, allParams,
@ -48,8 +49,10 @@ export class ContentHistoryComponent {
} }
public events: Observable<HistoryEventDto[]> = public events: Observable<HistoryEventDto[]> =
Observable.timer(0, 10000).merge(this.messageBus.of(HistoryChannelUpdated).delay(1000)) timer(0, 10000).pipe(
.switchMap(app => this.historyService.getHistory(this.appsState.appName, this.channel)); merge(this.messageBus.of(HistoryChannelUpdated)
.pipe(delay(1000))),
switchMap(app => this.historyService.getHistory(this.appsState.appName, this.channel)));
constructor( constructor(
private readonly appsState: AppsState, private readonly appsState: AppsState,

21
src/Squidex/app/features/content/pages/content/content-page.component.ts

@ -7,7 +7,8 @@
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Observable, Subscription } from 'rxjs'; import { Observable, of, Subscription } from 'rxjs';
import { filter, map, onErrorResumeNext, switchMap } from 'rxjs/operators';
import { ContentVersionSelected } from './../messages'; import { ContentVersionSelected } from './../messages';
@ -87,7 +88,7 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy,
}); });
this.selectedSchemaSubscription = this.selectedSchemaSubscription =
this.schemasState.selectedSchema.filter(s => !!s).map(s => s!) this.schemasState.selectedSchema.pipe(filter(s => !!s), map(s => s!))
.subscribe(schema => { .subscribe(schema => {
this.schema = schema; this.schema = schema;
@ -95,7 +96,7 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy,
}); });
this.contentSubscription = this.contentSubscription =
this.contentsState.selectedContent.filter(c => !!c).map(c => c!) this.contentsState.selectedContent.pipe(filter(c => !!c), map(c => c!))
.subscribe(content => { .subscribe(content => {
this.content = content; this.content = content;
@ -111,7 +112,7 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy,
public canDeactivate(): Observable<boolean> { public canDeactivate(): Observable<boolean> {
if (!this.contentForm.form.dirty || !this.content) { if (!this.contentForm.form.dirty || !this.content) {
return Observable.of(true); return of(true);
} else { } else {
return this.dialogs.confirmUnsavedChanges(); return this.dialogs.confirmUnsavedChanges();
} }
@ -175,7 +176,7 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy,
} }
public discardChanges() { public discardChanges() {
this.contentsState.discardChanges(this.content).onErrorResumeNext().subscribe(); this.contentsState.discardChanges(this.content).pipe(onErrorResumeNext()).subscribe();
} }
public publish() { public publish() {
@ -195,21 +196,21 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy,
} }
public delete() { public delete() {
this.contentsState.deleteMany([this.content]).onErrorResumeNext() this.contentsState.deleteMany([this.content]).pipe(onErrorResumeNext())
.subscribe(() => { .subscribe(() => {
this.back(); this.back();
}); });
} }
public publishChanges() { public publishChanges() {
this.dueTimeSelector.selectDueTime('Publish') this.dueTimeSelector.selectDueTime('Publish').pipe(
.switchMap(d => this.contentsState.publishChanges(this.content, d)).onErrorResumeNext() switchMap(d => this.contentsState.publishChanges(this.content, d)), onErrorResumeNext())
.subscribe(); .subscribe();
} }
private changeContentItems(action: string, status: string) { private changeContentItems(action: string, status: string) {
this.dueTimeSelector.selectDueTime(action) this.dueTimeSelector.selectDueTime(action).pipe(
.switchMap(d => this.contentsState.changeStatus(this.content, action, status, d)).onErrorResumeNext() switchMap(d => this.contentsState.changeStatus(this.content, action, status, d)), onErrorResumeNext())
.subscribe(); .subscribe();
} }

28
src/Squidex/app/features/content/pages/contents/contents-page.component.ts

@ -7,6 +7,7 @@
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { onErrorResumeNext, switchMap, tap } from 'rxjs/operators';
import { import {
AppLanguageDto, AppLanguageDto,
@ -72,7 +73,7 @@ export class ContentsPageComponent implements OnDestroy, OnInit {
this.schema = schema!; this.schema = schema!;
this.contentsState.init().onErrorResumeNext().subscribe(); this.contentsState.init().pipe(onErrorResumeNext()).subscribe();
}); });
this.contentsSubscription = this.contentsSubscription =
@ -90,15 +91,15 @@ export class ContentsPageComponent implements OnDestroy, OnInit {
} }
public reload() { public reload() {
this.contentsState.load(true).onErrorResumeNext().subscribe(); this.contentsState.load(true).pipe(onErrorResumeNext()).subscribe();
} }
public deleteSelected() { public deleteSelected() {
this.contentsState.deleteMany(this.select()).onErrorResumeNext().subscribe(); this.contentsState.deleteMany(this.select()).pipe(onErrorResumeNext()).subscribe();
} }
public delete(content: ContentDto) { public delete(content: ContentDto) {
this.contentsState.deleteMany([content]).onErrorResumeNext().subscribe(); this.contentsState.deleteMany([content]).pipe(onErrorResumeNext()).subscribe();
} }
public publish(content: ContentDto) { public publish(content: ContentDto) {
@ -138,36 +139,37 @@ export class ContentsPageComponent implements OnDestroy, OnInit {
return; return;
} }
this.dueTimeSelector.selectDueTime(action) this.dueTimeSelector.selectDueTime(action).pipe(
.do(() => { tap(() => {
this.resetSelection(); this.resetSelection();
}) }),
.switchMap(d => this.contentsState.changeManyStatus(contents, action, d)).onErrorResumeNext() switchMap(d => this.contentsState.changeManyStatus(contents, action, d)),
onErrorResumeNext())
.subscribe(); .subscribe();
} }
public goArchive(isArchive: boolean) { public goArchive(isArchive: boolean) {
this.resetSelection(); this.resetSelection();
this.contentsState.goArchive(isArchive).onErrorResumeNext().subscribe(); this.contentsState.goArchive(isArchive).pipe(onErrorResumeNext()).subscribe();
} }
public goPrev() { public goPrev() {
this.resetSelection(); this.resetSelection();
this.contentsState.goPrev().onErrorResumeNext().subscribe(); this.contentsState.goPrev().pipe(onErrorResumeNext()).subscribe();
} }
public goNext() { public goNext() {
this.resetSelection(); this.resetSelection();
this.contentsState.goNext().onErrorResumeNext().subscribe(); this.contentsState.goNext().pipe(onErrorResumeNext()).subscribe();
} }
public search(query: string) { public search(query: string) {
this.resetSelection(); this.resetSelection();
this.contentsState.search(query).onErrorResumeNext().subscribe(); this.contentsState.search(query).pipe(onErrorResumeNext()).subscribe();
} }
public isItemSelected(content: ContentDto): boolean { public isItemSelected(content: ContentDto): boolean {

3
src/Squidex/app/features/content/pages/schemas/schemas-page.component.ts

@ -7,6 +7,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { onErrorResumeNext } from 'rxjs/operators';
import { AppsState, SchemasState } from '@app/shared'; import { AppsState, SchemasState } from '@app/shared';
@ -25,7 +26,7 @@ export class SchemasPageComponent implements OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.schemasState.load().onErrorResumeNext().subscribe(); this.schemasState.load().pipe(onErrorResumeNext()).subscribe();
} }
public trackByCategory(index: number, category: string) { public trackByCategory(index: number, category: string) {

11
src/Squidex/app/features/content/shared/contents-selector.component.ts

@ -6,6 +6,7 @@
*/ */
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
ContentDto, ContentDto,
@ -51,23 +52,23 @@ export class ContentsSelectorComponent implements OnInit {
public ngOnInit() { public ngOnInit() {
this.contentsState.schema = this.schema; this.contentsState.schema = this.schema;
this.contentsState.load().onErrorResumeNext().subscribe(); this.contentsState.load().pipe(onErrorResumeNext()).subscribe();
} }
public reload() { public reload() {
this.contentsState.load(true).onErrorResumeNext().subscribe(); this.contentsState.load(true).pipe(onErrorResumeNext()).subscribe();
} }
public search(query: string) { public search(query: string) {
this.contentsState.search(query).onErrorResumeNext().subscribe(); this.contentsState.search(query).pipe(onErrorResumeNext()).subscribe();
} }
public goNext() { public goNext() {
this.contentsState.goNext().onErrorResumeNext().subscribe(); this.contentsState.goNext().pipe(onErrorResumeNext()).subscribe();
} }
public goPrev() { public goPrev() {
this.contentsState.goPrev().onErrorResumeNext().subscribe(); this.contentsState.goPrev().pipe(onErrorResumeNext()).subscribe();
} }
public isItemSelected(content: ContentDto) { public isItemSelected(content: ContentDto) {

23
src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts

@ -7,6 +7,7 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { filter, map, switchMap } from 'rxjs/operators';
import { import {
AppDto, AppDto,
@ -39,7 +40,7 @@ export class DashboardPageComponent implements OnDestroy, OnInit {
public chartCallsCount: any; public chartCallsCount: any;
public chartCallsPerformance: any; public chartCallsPerformance: any;
public app = this.appsState.selectedApp.filter(x => !!x).map(x => <AppDto>x); public app = this.appsState.selectedApp.pipe(filter(x => !!x), map(x => <AppDto>x));
public chartOptions = { public chartOptions = {
responsive: true, responsive: true,
@ -86,31 +87,31 @@ export class DashboardPageComponent implements OnDestroy, OnInit {
public ngOnInit() { public ngOnInit() {
this.subscriptions.push( this.subscriptions.push(
this.app this.app.pipe(
.switchMap(app => this.usagesService.getTodayStorage(app.name)) switchMap(app => this.usagesService.getTodayStorage(app.name)))
.subscribe(dto => { .subscribe(dto => {
this.assetsCurrent = dto.size; this.assetsCurrent = dto.size;
this.assetsMax = dto.maxAllowed; this.assetsMax = dto.maxAllowed;
})); }));
this.subscriptions.push( this.subscriptions.push(
this.app this.app.pipe(
.switchMap(app => this.usagesService.getMonthCalls(app.name)) switchMap(app => this.usagesService.getMonthCalls(app.name)))
.subscribe(dto => { .subscribe(dto => {
this.callsCurrent = dto.count; this.callsCurrent = dto.count;
this.callsMax = dto.maxAllowed; this.callsMax = dto.maxAllowed;
})); }));
this.subscriptions.push( this.subscriptions.push(
this.app this.app.pipe(
.switchMap(app => this.historyService.getHistory(app.name, '')) switchMap(app => this.historyService.getHistory(app.name, '')))
.subscribe(dto => { .subscribe(dto => {
this.history = dto; this.history = dto;
})); }));
this.subscriptions.push( this.subscriptions.push(
this.app this.app.pipe(
.switchMap(app => this.usagesService.getStorageUsages(app.name, DateTime.today().addDays(-20), DateTime.today())) switchMap(app => this.usagesService.getStorageUsages(app.name, DateTime.today().addDays(-20), DateTime.today())))
.subscribe(dtos => { .subscribe(dtos => {
this.chartStorageCount = { this.chartStorageCount = {
labels: createLabels(dtos), labels: createLabels(dtos),
@ -144,8 +145,8 @@ export class DashboardPageComponent implements OnDestroy, OnInit {
})); }));
this.subscriptions.push( this.subscriptions.push(
this.app this.app.pipe(
.switchMap(app => this.usagesService.getCallsUsages(app.name, DateTime.today().addDays(-20), DateTime.today())) switchMap(app => this.usagesService.getCallsUsages(app.name, DateTime.today().addDays(-20), DateTime.today())))
.subscribe(dtos => { .subscribe(dtos => {
this.chartCallsCount = { this.chartCallsCount = {
labels: createLabels(dtos), labels: createLabels(dtos),

11
src/Squidex/app/features/rules/pages/events/rule-events-page.component.ts

@ -6,6 +6,7 @@
*/ */
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
AppsState, AppsState,
@ -28,23 +29,23 @@ export class RuleEventsPageComponent implements OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.ruleEventsState.load().onErrorResumeNext().subscribe(); this.ruleEventsState.load().pipe(onErrorResumeNext()).subscribe();
} }
public reload() { public reload() {
this.ruleEventsState.load(true).onErrorResumeNext().subscribe(); this.ruleEventsState.load(true).pipe(onErrorResumeNext()).subscribe();
} }
public goNext() { public goNext() {
this.ruleEventsState.goNext().onErrorResumeNext().subscribe(); this.ruleEventsState.goNext().pipe(onErrorResumeNext()).subscribe();
} }
public goPrev() { public goPrev() {
this.ruleEventsState.goPrev().onErrorResumeNext().subscribe(); this.ruleEventsState.goPrev().pipe(onErrorResumeNext()).subscribe();
} }
public enqueue(event: RuleEventDto) { public enqueue(event: RuleEventDto) {
this.ruleEventsState.enqueue(event).onErrorResumeNext().subscribe(); this.ruleEventsState.enqueue(event).pipe(onErrorResumeNext()).subscribe();
} }
public selectEvent(id: string) { public selectEvent(id: string) {

13
src/Squidex/app/features/rules/pages/rules/rules-page.component.ts

@ -6,6 +6,7 @@
*/ */
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
AppsState, AppsState,
@ -39,24 +40,24 @@ export class RulesPageComponent implements OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.schemasState.load().onErrorResumeNext().subscribe(); this.schemasState.load().pipe(onErrorResumeNext()).subscribe();
this.rulesState.load().onErrorResumeNext().subscribe(); this.rulesState.load().pipe(onErrorResumeNext()).subscribe();
} }
public reload() { public reload() {
this.rulesState.load(true).onErrorResumeNext().subscribe(); this.rulesState.load(true).pipe(onErrorResumeNext()).subscribe();
} }
public delete(rule: RuleDto) { public delete(rule: RuleDto) {
this.rulesState.delete(rule).onErrorResumeNext().subscribe(); this.rulesState.delete(rule).pipe(onErrorResumeNext()).subscribe();
} }
public toggle(rule: RuleDto) { public toggle(rule: RuleDto) {
if (rule.isEnabled) { if (rule.isEnabled) {
this.rulesState.disable(rule).onErrorResumeNext().subscribe(); this.rulesState.disable(rule).pipe(onErrorResumeNext()).subscribe();
} else { } else {
this.rulesState.enable(rule).onErrorResumeNext().subscribe(); this.rulesState.enable(rule).pipe(onErrorResumeNext()).subscribe();
} }
} }

13
src/Squidex/app/features/schemas/pages/schema/field.component.ts

@ -7,6 +7,7 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
AppPatternDto, AppPatternDto,
@ -84,23 +85,23 @@ export class FieldComponent implements OnInit {
} }
public deleteField() { public deleteField() {
this.schemasState.deleteField(this.schema, this.field).onErrorResumeNext().subscribe(); this.schemasState.deleteField(this.schema, this.field).pipe(onErrorResumeNext()).subscribe();
} }
public enableField() { public enableField() {
this.schemasState.enableField(this.schema, this.field).onErrorResumeNext().subscribe(); this.schemasState.enableField(this.schema, this.field).pipe(onErrorResumeNext()).subscribe();
} }
public disableField() { public disableField() {
this.schemasState.disableField(this.schema, this.field).onErrorResumeNext().subscribe(); this.schemasState.disableField(this.schema, this.field).pipe(onErrorResumeNext()).subscribe();
} }
public showField() { public showField() {
this.schemasState.showField(this.schema, this.field).onErrorResumeNext().subscribe(); this.schemasState.showField(this.schema, this.field).pipe(onErrorResumeNext()).subscribe();
} }
public hideField() { public hideField() {
this.schemasState.hideField(this.schema, this.field).onErrorResumeNext().subscribe(); this.schemasState.hideField(this.schema, this.field).pipe(onErrorResumeNext()).subscribe();
} }
public sortFields(fields: NestedFieldDto[]) { public sortFields(fields: NestedFieldDto[]) {
@ -108,7 +109,7 @@ export class FieldComponent implements OnInit {
} }
public lockField() { public lockField() {
this.schemasState.lockField(this.schema, this.field).onErrorResumeNext().subscribe(); this.schemasState.lockField(this.schema, this.field).pipe(onErrorResumeNext()).subscribe();
} }
public trackByField(index: number, field: NestedFieldDto) { public trackByField(index: number, field: NestedFieldDto) {

5
src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts

@ -8,6 +8,7 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { filter, map, onErrorResumeNext } from 'rxjs/operators';
import { import {
AppsState, AppsState,
@ -65,10 +66,10 @@ export class SchemaPageComponent implements OnDestroy, OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.patternsState.load().onErrorResumeNext().subscribe(); this.patternsState.load().pipe(onErrorResumeNext()).subscribe();
this.selectedSchemaSubscription = this.selectedSchemaSubscription =
this.schemasState.selectedSchema.filter(s => !!s).map(s => s!) this.schemasState.selectedSchema.pipe(filter(s => !!s), map(s => s!))
.subscribe(schema => { .subscribe(schema => {
this.schema = schema; this.schema = schema;

6
src/Squidex/app/features/schemas/pages/schema/types/boolean-validation.component.ts

@ -8,6 +8,7 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { BooleanFieldPropertiesDto, FieldDto } from '@app/shared'; import { BooleanFieldPropertiesDto, FieldDto } from '@app/shared';
@ -36,8 +37,7 @@ export class BooleanValidationComponent implements OnInit {
new FormControl(this.properties.inlineEditable)); new FormControl(this.properties.inlineEditable));
this.showDefaultValue = this.showDefaultValue =
this.editForm.controls['isRequired'].valueChanges this.editForm.controls['isRequired'].valueChanges.pipe(
.startWith(this.properties.isRequired) startWith(this.properties.isRequired), map(x => !x));
.map(x => !x);
} }
} }

11
src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.ts

@ -8,6 +8,7 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { DateTimeFieldPropertiesDto, FieldDto, ValidatorsEx } from '@app/shared'; import { DateTimeFieldPropertiesDto, FieldDto, ValidatorsEx } from '@app/shared';
@ -51,13 +52,11 @@ export class DateTimeValidationComponent implements OnInit {
])); ]));
this.showDefaultValues = this.showDefaultValues =
this.editForm.controls['isRequired'].valueChanges this.editForm.controls['isRequired'].valueChanges.pipe(
.startWith(this.properties.isRequired) startWith(this.properties.isRequired), map(x => !x));
.map(x => !x);
this.showDefaultValue = this.showDefaultValue =
this.editForm.controls['calculatedDefaultValue'].valueChanges this.editForm.controls['calculatedDefaultValue'].valueChanges.pipe(
.startWith(this.properties.calculatedDefaultValue) startWith(this.properties.calculatedDefaultValue), map(x => !x));
.map(x => !x);
} }
} }

11
src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts

@ -8,6 +8,7 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { FieldDto, FloatConverter, NumberFieldPropertiesDto } from '@app/shared'; import { FieldDto, FloatConverter, NumberFieldPropertiesDto } from '@app/shared';
@ -52,14 +53,12 @@ export class NumberUIComponent implements OnDestroy, OnInit {
new FormControl(this.properties.inlineEditable)); new FormControl(this.properties.inlineEditable));
this.hideAllowedValues = this.hideAllowedValues =
this.editForm.controls['editor'].valueChanges this.editForm.controls['editor'].valueChanges.pipe(
.startWith(this.properties.editor) startWith(this.properties.editor), map(x => !(x && (x === 'Radio' || x === 'Dropdown'))));
.map(x => !(x && (x === 'Radio' || x === 'Dropdown')));
this.hideInlineEditable = this.hideInlineEditable =
this.editForm.controls['editor'].valueChanges this.editForm.controls['editor'].valueChanges.pipe(
.startWith(this.properties.editor) startWith(this.properties.editor), map(x => !(x && (x === 'Input' || x === 'Dropdown'))));
.map(x => !(x && (x === 'Input' || x === 'Dropdown')));
this.hideAllowedValuesSubscription = this.hideAllowedValuesSubscription =
this.hideAllowedValues.subscribe(isSelection => { this.hideAllowedValues.subscribe(isSelection => {

6
src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.ts

@ -8,6 +8,7 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { FieldDto, NumberFieldPropertiesDto } from '@app/shared'; import { FieldDto, NumberFieldPropertiesDto } from '@app/shared';
@ -39,8 +40,7 @@ export class NumberValidationComponent implements OnInit {
new FormControl(this.properties.defaultValue)); new FormControl(this.properties.defaultValue));
this.showDefaultValue = this.showDefaultValue =
this.editForm.controls['isRequired'].valueChanges this.editForm.controls['isRequired'].valueChanges.pipe(
.startWith(this.properties.isRequired) startWith(this.properties.isRequired), map(x => !x));
.map(x => !x);
} }
} }

11
src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts

@ -8,6 +8,7 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { FieldDto, StringFieldPropertiesDto } from '@app/shared'; import { FieldDto, StringFieldPropertiesDto } from '@app/shared';
@ -50,14 +51,12 @@ export class StringUIComponent implements OnDestroy, OnInit {
new FormControl(this.properties.inlineEditable)); new FormControl(this.properties.inlineEditable));
this.hideAllowedValues = this.hideAllowedValues =
this.editForm.controls['editor'].valueChanges this.editForm.controls['editor'].valueChanges.pipe(
.startWith(this.properties.editor) startWith(this.properties.editor), map(x => !(x && (x === 'Radio' || x === 'Dropdown'))));
.map(x => !(x && (x === 'Radio' || x === 'Dropdown')));
this.hideInlineEditable = this.hideInlineEditable =
this.editForm.controls['editor'].valueChanges this.editForm.controls['editor'].valueChanges.pipe(
.startWith(this.properties.editor) startWith(this.properties.editor), map(x => !(x && (x === 'Input' || x === 'Dropdown' || x === 'Slug'))));
.map(x => !(x && (x === 'Input' || x === 'Dropdown' || x === 'Slug')));
this.hideAllowedValuesSubscription = this.hideAllowedValuesSubscription =
this.hideAllowedValues.subscribe(isSelection => { this.hideAllowedValues.subscribe(isSelection => {

11
src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts

@ -8,6 +8,7 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { import {
AppPatternDto, AppPatternDto,
@ -65,14 +66,12 @@ export class StringValidationComponent implements OnDestroy, OnInit {
new FormControl(this.properties.defaultValue)); new FormControl(this.properties.defaultValue));
this.showDefaultValue = this.showDefaultValue =
this.editForm.controls['isRequired'].valueChanges this.editForm.controls['isRequired'].valueChanges.pipe(
.startWith(this.properties.isRequired) startWith(this.properties.isRequired), map(x => !x));
.map(x => !x);
this.showPatternSuggestions = this.showPatternSuggestions =
this.editForm.controls['pattern'].valueChanges this.editForm.controls['pattern'].valueChanges.pipe(
.startWith('') startWith(''), map(x => !x || x.trim().length === 0));
.map(x => !x || x.trim().length === 0);
this.showPatternMessage = this.showPatternMessage =
this.editForm.controls['pattern'].value && this.editForm.controls['pattern'].value.trim().length > 0; this.editForm.controls['pattern'].value && this.editForm.controls['pattern'].value.trim().length > 0;

5
src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts

@ -9,6 +9,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder, FormControl } from '@angular/forms'; import { FormBuilder, FormControl } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { map, onErrorResumeNext } from 'rxjs/operators';
import { import {
AppsState, AppsState,
@ -59,14 +60,14 @@ export class SchemasPageComponent implements OnDestroy, OnInit {
this.addSchemaDialog.show(); this.addSchemaDialog.show();
}); });
this.route.params.map(q => q['showDialog']) this.route.params.pipe(map(q => q['showDialog']))
.subscribe(showDialog => { .subscribe(showDialog => {
if (showDialog) { if (showDialog) {
this.addSchemaDialog.show(); this.addSchemaDialog.show();
} }
}); });
this.schemasState.load().onErrorResumeNext().subscribe(); this.schemasState.load().pipe(onErrorResumeNext()).subscribe();
} }
public removeCategory(name: string) { public removeCategory(name: string) {

15
src/Squidex/app/features/settings/pages/backups/backups-page.component.ts

@ -6,7 +6,8 @@
*/ */
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Observable, Subscription } from 'rxjs'; import { Subscription, timer } from 'rxjs';
import { onErrorResumeNext, switchMap } from 'rxjs/operators';
import { import {
AppsState, AppsState,
@ -33,24 +34,24 @@ export class BackupsPageComponent implements OnInit, OnDestroy {
} }
public ngOnInit() { public ngOnInit() {
this.backupsState.load(false, true).onErrorResumeNext().subscribe(); this.backupsState.load(false, true).pipe(onErrorResumeNext()).subscribe();
this.timerSubscription = this.timerSubscription =
Observable.timer(3000, 3000) timer(3000, 3000).pipe(
.switchMap(t => this.backupsState.load(true, true).onErrorResumeNext()) switchMap(t => this.backupsState.load(true, true)), onErrorResumeNext())
.subscribe(); .subscribe();
} }
public reload() { public reload() {
this.backupsState.load(true, false).onErrorResumeNext().subscribe(); this.backupsState.load(true, false).pipe(onErrorResumeNext()).subscribe();
} }
public start() { public start() {
this.backupsState.start().onErrorResumeNext().subscribe(); this.backupsState.start().pipe(onErrorResumeNext()).subscribe();
} }
public delete(backup: BackupDto) { public delete(backup: BackupDto) {
this.backupsState.delete(backup).onErrorResumeNext().subscribe(); this.backupsState.delete(backup).pipe(onErrorResumeNext()).subscribe();
} }
public trackByBackup(index: number, item: BackupDto) { public trackByBackup(index: number, item: BackupDto) {

5
src/Squidex/app/features/settings/pages/clients/client.component.ts

@ -7,6 +7,7 @@
import { Component, Input, OnChanges } from '@angular/core'; import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
AccessTokenDto, AccessTokenDto,
@ -54,11 +55,11 @@ export class ClientComponent implements OnChanges {
} }
public revoke() { public revoke() {
this.clientsState.revoke(this.client).onErrorResumeNext().subscribe(); this.clientsState.revoke(this.client).pipe(onErrorResumeNext()).subscribe();
} }
public update(permission: string) { public update(permission: string) {
this.clientsState.update(this.client, new UpdateAppClientDto(undefined, permission)).onErrorResumeNext().subscribe(); this.clientsState.update(this.client, new UpdateAppClientDto(undefined, permission)).pipe(onErrorResumeNext()).subscribe();
} }
public toggleRename() { public toggleRename() {

7
src/Squidex/app/features/settings/pages/clients/clients-page.component.ts

@ -7,6 +7,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
AppClientDto, AppClientDto,
@ -32,11 +33,11 @@ export class ClientsPageComponent implements OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.clientsState.load().onErrorResumeNext().subscribe(); this.clientsState.load().pipe(onErrorResumeNext()).subscribe();
} }
public reload() { public reload() {
this.clientsState.load(true).onErrorResumeNext().subscribe(); this.clientsState.load(true).pipe(onErrorResumeNext()).subscribe();
} }
public attachClient() { public attachClient() {
@ -45,7 +46,7 @@ export class ClientsPageComponent implements OnInit {
if (value) { if (value) {
const requestDto = new CreateAppClientDto(value.name); const requestDto = new CreateAppClientDto(value.name);
this.clientsState.attach(requestDto).onErrorResumeNext() this.clientsState.attach(requestDto).pipe(onErrorResumeNext())
.subscribe(() => { .subscribe(() => {
this.addClientForm.submitCompleted(); this.addClientForm.submitCompleted();
}, error => { }, error => {

15
src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts

@ -8,6 +8,7 @@
import { Component, Injectable, OnInit } from '@angular/core'; import { Component, Injectable, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { filter, onErrorResumeNext, withLatestFrom } from 'rxjs/operators';
import { import {
AppContributorDto, AppContributorDto,
@ -29,8 +30,8 @@ export class UsersDataSource implements AutocompleteSource {
} }
public find(query: string): Observable<any[]> { public find(query: string): Observable<any[]> {
return this.usersService.getUsers(query) return this.usersService.getUsers(query).pipe(
.withLatestFrom(this.contributorsState.contributors.filter(x => !!x), (users, contributors) => { withLatestFrom(this.contributorsState.contributors.pipe(filter(x => !!x)), (users, contributors) => {
const results: any[] = []; const results: any[] = [];
for (let user of users) { for (let user of users) {
@ -39,7 +40,7 @@ export class UsersDataSource implements AutocompleteSource {
} }
} }
return results; return results;
}); }));
} }
} }
@ -65,19 +66,19 @@ export class ContributorsPageComponent implements OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.contributorsState.load().onErrorResumeNext().subscribe(); this.contributorsState.load().pipe(onErrorResumeNext()).subscribe();
} }
public reload() { public reload() {
this.contributorsState.load(true).onErrorResumeNext().subscribe(); this.contributorsState.load(true).pipe(onErrorResumeNext()).subscribe();
} }
public remove(contributor: AppContributorDto) { public remove(contributor: AppContributorDto) {
this.contributorsState.revoke(contributor).onErrorResumeNext().subscribe(); this.contributorsState.revoke(contributor).pipe(onErrorResumeNext()).subscribe();
} }
public changePermission(contributor: AppContributorDto, permission: string) { public changePermission(contributor: AppContributorDto, permission: string) {
this.contributorsState.assign(new AppContributorDto(contributor.contributorId, permission)).onErrorResumeNext().subscribe(); this.contributorsState.assign(new AppContributorDto(contributor.contributorId, permission)).pipe(onErrorResumeNext()).subscribe();
} }
public assignContributor() { public assignContributor() {

3
src/Squidex/app/features/settings/pages/languages/language.component.ts

@ -7,6 +7,7 @@
import { Component, Input, OnChanges } from '@angular/core'; import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
AppLanguageDto, AppLanguageDto,
@ -56,7 +57,7 @@ export class LanguageComponent implements OnChanges {
} }
public remove() { public remove() {
this.languagesState.remove(this.language).onErrorResumeNext().subscribe(); this.languagesState.remove(this.language).pipe(onErrorResumeNext()).subscribe();
} }
public save() { public save() {

5
src/Squidex/app/features/settings/pages/languages/languages-page.component.ts

@ -8,6 +8,7 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
AddLanguageForm, AddLanguageForm,
@ -46,11 +47,11 @@ export class LanguagesPageComponent implements OnDestroy, OnInit {
} }
}); });
this.languagesState.load().onErrorResumeNext().subscribe(); this.languagesState.load().pipe(onErrorResumeNext()).subscribe();
} }
public reload() { public reload() {
this.languagesState.load(true).onErrorResumeNext().subscribe(); this.languagesState.load(true).pipe(onErrorResumeNext()).subscribe();
} }
public addLanguage() { public addLanguage() {

3
src/Squidex/app/features/settings/pages/patterns/pattern.component.ts

@ -7,6 +7,7 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
AppPatternDto, AppPatternDto,
@ -40,7 +41,7 @@ export class PatternComponent implements OnInit {
} }
public delete() { public delete() {
this.patternsState.delete(this.pattern).onErrorResumeNext().subscribe(); this.patternsState.delete(this.pattern).pipe(onErrorResumeNext()).subscribe();
} }
public save() { public save() {

5
src/Squidex/app/features/settings/pages/patterns/patterns-page.component.ts

@ -6,6 +6,7 @@
*/ */
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
AppPatternDto, AppPatternDto,
@ -26,11 +27,11 @@ export class PatternsPageComponent implements OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.patternsState.load().onErrorResumeNext().subscribe(); this.patternsState.load().pipe(onErrorResumeNext()).subscribe();
} }
public reload() { public reload() {
this.patternsState.load(true).onErrorResumeNext().subscribe(); this.patternsState.load(true).pipe(onErrorResumeNext()).subscribe();
} }
public trackByPattern(index: number, pattern: AppPatternDto) { public trackByPattern(index: number, pattern: AppPatternDto) {

7
src/Squidex/app/features/settings/pages/plans/plans-page.component.ts

@ -7,6 +7,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
ApiUrlConfig, ApiUrlConfig,
@ -38,15 +39,15 @@ export class PlansPageComponent implements OnInit {
this.overridePlanId = params['planId']; this.overridePlanId = params['planId'];
}).unsubscribe(); }).unsubscribe();
this.plansState.load(false, this.overridePlanId).onErrorResumeNext().subscribe(); this.plansState.load(false, this.overridePlanId).pipe(onErrorResumeNext()).subscribe();
} }
public reload() { public reload() {
this.plansState.load(true, this.overridePlanId).onErrorResumeNext().subscribe(); this.plansState.load(true, this.overridePlanId).pipe(onErrorResumeNext()).subscribe();
} }
public change(planId: string) { public change(planId: string) {
this.plansState.change(planId).onErrorResumeNext().subscribe(); this.plansState.change(planId).pipe(onErrorResumeNext()).subscribe();
} }
public trackByPlan(index: number, planInfo: { plan: PlanDto }) { public trackByPlan(index: number, planInfo: { plan: PlanDto }) {

12
src/Squidex/app/framework/angular/animations.ts

@ -7,14 +7,14 @@
import { import {
animate, animate,
AnimationEntryMetadata, AnimationTriggerMetadata,
state, state,
style, style,
transition, transition,
trigger trigger
} from '@angular/core'; } from '@angular/animations';
export function buildSlideRightAnimation(name = 'slideRight', timing = '150ms'): AnimationEntryMetadata { export function buildSlideRightAnimation(name = 'slideRight', timing = '150ms'): AnimationTriggerMetadata {
return trigger( return trigger(
name, [ name, [
transition(':enter', [ transition(':enter', [
@ -37,7 +37,7 @@ export function buildSlideRightAnimation(name = 'slideRight', timing = '150ms'):
); );
} }
export function buildSlideAnimation(name = 'slide', timing = '400ms'): AnimationEntryMetadata { export function buildSlideAnimation(name = 'slide', timing = '400ms'): AnimationTriggerMetadata {
return trigger( return trigger(
name, [ name, [
transition(':enter', [ transition(':enter', [
@ -60,7 +60,7 @@ export function buildSlideAnimation(name = 'slide', timing = '400ms'): Animation
); );
} }
export function buildFadeAnimation(name = 'fade', timing = '150ms'): AnimationEntryMetadata { export function buildFadeAnimation(name = 'fade', timing = '150ms'): AnimationTriggerMetadata {
return trigger( return trigger(
name, [ name, [
transition(':enter', [ transition(':enter', [
@ -83,7 +83,7 @@ export function buildFadeAnimation(name = 'fade', timing = '150ms'): AnimationEn
); );
} }
export function buildHeightAnimation(name = 'height', timing = '200ms'): AnimationEntryMetadata { export function buildHeightAnimation(name = 'height', timing = '200ms'): AnimationTriggerMetadata {
return trigger( return trigger(
name, [ name, [
transition(':enter', [ transition(':enter', [

34
src/Squidex/app/framework/angular/forms/autocomplete.component.ts

@ -7,7 +7,8 @@
import { Component, ContentChild, forwardRef, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core'; import { Component, ContentChild, forwardRef, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable, Subscription } from 'rxjs'; import { Observable, of, Subscription } from 'rxjs';
import { catchError, debounceTime, distinctUntilChanged, filter, map, switchMap, tap } from 'rxjs/operators';
export interface AutocompleteSource { export interface AutocompleteSource {
find(query: string): Observable<any[]>; find(query: string): Observable<any[]>;
@ -60,21 +61,22 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
public ngOnInit() { public ngOnInit() {
this.subscription = this.subscription =
this.queryInput.valueChanges this.queryInput.valueChanges.pipe(
.do(query => { tap(query => {
this.callChange(query); this.callChange(query);
}) }),
.map(query => <string>query) map(query => <string>query),
.map(query => query ? query.trim() : query) map(query => query ? query.trim() : query),
.do(query => { tap(query => {
if (!query) { if (!query) {
this.reset(); this.reset();
} }
}) }),
.distinctUntilChanged() distinctUntilChanged(),
.debounceTime(200) debounceTime(200),
.filter(query => !!query && !!this.source) filter(query => !!query && !!this.source),
.switchMap(query => this.source.find(query)).catch(error => Observable.of([])) switchMap(query => this.source.find(query)),
catchError(error => of([])))
.subscribe(items => { .subscribe(items => {
this.reset(); this.reset();
this.items = items || []; this.items = items || [];

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

@ -7,7 +7,7 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Host, Input, OnChanges, OnDestroy, Optional } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Host, Input, OnChanges, OnDestroy, Optional } from '@angular/core';
import { AbstractControl, FormGroupDirective } from '@angular/forms'; import { AbstractControl, FormGroupDirective } from '@angular/forms';
import { Observable, Subscription } from 'rxjs'; import { merge, Subscription } from 'rxjs';
import { fadeAnimation, Types } from '@app/framework/internal'; import { fadeAnimation, Types } from '@app/framework/internal';
@ -93,7 +93,7 @@ export class ControlErrorsComponent implements OnChanges, OnDestroy {
if (control) { if (control) {
this.controlSubscription = this.controlSubscription =
Observable.merge(control.valueChanges, control.statusChanges) merge(control.valueChanges, control.statusChanges)
.subscribe(() => { .subscribe(() => {
this.createMessages(); this.createMessages();
}); });

4
src/Squidex/app/framework/angular/forms/jscript-editor.component.ts

@ -8,6 +8,7 @@
import { AfterViewInit, Component, ElementRef, forwardRef, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ElementRef, forwardRef, ViewChild } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { ResourceLoaderService, Types } from '@app/framework/internal'; import { ResourceLoaderService, Types } from '@app/framework/internal';
@ -64,7 +65,8 @@ export class JscriptEditorComponent implements ControlValueAccessor, AfterViewIn
} }
public ngAfterViewInit() { public ngAfterViewInit() {
this.valueChanged.debounceTime(500) this.valueChanged.pipe(
debounceTime(500))
.subscribe(() => { .subscribe(() => {
this.changeValue(); this.changeValue();
}); });

4
src/Squidex/app/framework/angular/forms/json-editor.component.ts

@ -8,6 +8,7 @@
import { AfterViewInit, Component, ElementRef, forwardRef, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ElementRef, forwardRef, ViewChild } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { ResourceLoaderService } from '@app/framework/internal'; import { ResourceLoaderService } from '@app/framework/internal';
@ -71,7 +72,8 @@ export class JsonEditorComponent implements ControlValueAccessor, AfterViewInit
} }
public ngAfterViewInit() { public ngAfterViewInit() {
this.valueChanged.debounceTime(500) this.valueChanged.pipe(
debounceTime(500))
.subscribe(() => { .subscribe(() => {
this.changeValue(); this.changeValue();
}); });

85
src/Squidex/app/framework/angular/http/http-extensions-impl.ts

@ -1,85 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ErrorDto } from './../../utils/error';
import { Types} from './../../utils/types';
import { Version, Versioned } from './../../utils/version';
export module HTTP {
export function getVersioned<T>(http: HttpClient, url: string, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.get<T>(url, { observe: 'response', headers }), version);
}
export function postVersioned<T>(http: HttpClient, url: string, body: any, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.post<T>(url, body, { observe: 'response', headers }), version);
}
export function putVersioned<T>(http: HttpClient, url: string, body: any, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.put<T>(url, body, { observe: 'response', headers }), version);
}
export function patchVersioned<T>(http: HttpClient, url: string, body: any, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.request<T>('PATCH', url, { body, observe: 'response', headers }), version);
}
export function deleteVersioned<T>(http: HttpClient, url: string, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.delete<T>(url, { observe: 'response', headers }), version);
}
function createHeaders(version?: Version): HttpHeaders {
if (version && version.value && version.value.length > 0) {
return new HttpHeaders().set('If-Match', version.value);
} else {
return new HttpHeaders();
}
}
function handleVersion<T>(httpRequest: Observable<HttpResponse<T>>, version?: Version): Observable<Versioned<HttpResponse<T>>> {
return httpRequest.map((response: HttpResponse<T>) => {
const etag = response.headers.get('etag') || '';
return new Versioned(new Version(etag), response);
});
}
}
export function pretifyError(message: string): Observable<any> {
return this.catch((response: HttpErrorResponse) => {
let result: ErrorDto | null = null;
if (!Types.is(response.error, Error)) {
try {
const errorDto = Types.isObject(response.error) ? response.error : JSON.parse(response.error);
if (response.status === 412) {
result = new ErrorDto(response.status, 'Failed to make the update. Another user has made a change. Please reload.');
} else if (response.status !== 500) {
result = new ErrorDto(response.status, errorDto.message, errorDto.details);
}
} catch (e) {
result = new ErrorDto(500, 'Failed to make the request.');
}
}
result = result || new ErrorDto(500, message);
return Observable.throw(result);
});
}

79
src/Squidex/app/framework/angular/http/http-extensions.ts

@ -5,16 +5,81 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Observable } from 'rxjs/Observable'; import { HttpClient, HttpErrorResponse, HttpHeaders, HttpResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { pretifyError } from './http-extensions-impl'; import { ErrorDto } from './../../utils/error';
import { Types} from './../../utils/types';
import { Version, Versioned } from './../../utils/version';
/* tslint:disable:no-shadowed-variable */ export module HTTP {
export function getVersioned<T>(http: HttpClient, url: string, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
declare module 'rxjs/Observable' { return handleVersion(http.get<T>(url, { observe: 'response', headers }), version);
interface Observable<T> { }
pretifyError(message: string): Observable<any>;
export function postVersioned<T>(http: HttpClient, url: string, body: any, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.post<T>(url, body, { observe: 'response', headers }), version);
}
export function putVersioned<T>(http: HttpClient, url: string, body: any, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.put<T>(url, body, { observe: 'response', headers }), version);
}
export function patchVersioned<T>(http: HttpClient, url: string, body: any, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.request<T>('PATCH', url, { body, observe: 'response', headers }), version);
}
export function deleteVersioned<T>(http: HttpClient, url: string, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.delete<T>(url, { observe: 'response', headers }), version);
}
function createHeaders(version?: Version): HttpHeaders {
if (version && version.value && version.value.length > 0) {
return new HttpHeaders().set('If-Match', version.value);
} else {
return new HttpHeaders();
}
}
function handleVersion<T>(httpRequest: Observable<HttpResponse<T>>, version?: Version): Observable<Versioned<HttpResponse<T>>> {
return httpRequest.pipe(map((response: HttpResponse<T>) => {
const etag = response.headers.get('etag') || '';
return new Versioned(new Version(etag), response);
}));
} }
} }
Observable.prototype['pretifyError'] = pretifyError; export const pretifyError = (message: string) => <T>(source: Observable<T>) =>
source.pipe(catchError((response: HttpErrorResponse) => {
let result: ErrorDto | null = null;
if (!Types.is(response.error, Error)) {
try {
const errorDto = Types.isObject(response.error) ? response.error : JSON.parse(response.error);
if (response.status === 412) {
result = new ErrorDto(response.status, 'Failed to make the update. Another user has made a change. Please reload.');
} else if (response.status !== 500) {
result = new ErrorDto(response.status, errorDto.message, errorDto.details);
}
} catch (e) {
result = new ErrorDto(500, 'Failed to make the request.');
}
}
result = result || new ErrorDto(500, message);
return throwError(result);
}));

4
src/Squidex/app/framework/angular/routers/can-deactivate.guard.spec.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { CanDeactivateGuard } from './can-deactivate.guard'; import { CanDeactivateGuard } from './can-deactivate.guard';
@ -17,7 +17,7 @@ describe('CanDeactivateGuard', () => {
canDeactivate: () => { canDeactivate: () => {
called = true; called = true;
return Observable.of(true); return of(true);
} }
}; };

2
src/Squidex/app/framework/declarations.ts

@ -27,7 +27,7 @@ export * from './angular/forms/toggle.component';
export * from './angular/forms/transform-input.directive'; export * from './angular/forms/transform-input.directive';
export * from './angular/forms/validators'; export * from './angular/forms/validators';
export * from './angular/http/http-extensions-impl'; export * from './angular/http/http-extensions';
export * from './angular/modals/dialog-renderer.component'; export * from './angular/modals/dialog-renderer.component';
export * from './angular/modals/modal-dialog.component'; export * from './angular/modals/modal-dialog.component';

1
src/Squidex/app/framework/internal.ts

@ -27,6 +27,7 @@ export * from './utils/lazy';
export * from './utils/math-helper'; export * from './utils/math-helper';
export * from './utils/modal-view'; export * from './utils/modal-view';
export * from './utils/pager'; export * from './utils/pager';
export * from './utils/rxjs-extensions';
export * from './utils/string-helper'; export * from './utils/string-helper';
export * from './utils/types'; export * from './utils/types';
export * from './utils/version'; export * from './utils/version';

4
src/Squidex/app/framework/services/analytics.service.ts

@ -7,6 +7,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router'; import { NavigationEnd, Router } from '@angular/router';
import { filter } from 'rxjs/operators';
import { AnalyticsIdConfig } from '../configurations'; import { AnalyticsIdConfig } from '../configurations';
import { Types } from './../utils/types'; import { Types } from './../utils/types';
@ -32,7 +33,8 @@ export class AnalyticsService {
if (analyticsId && router && resourceLoader && window.location.hostname !== 'localhost') { if (analyticsId && router && resourceLoader && window.location.hostname !== 'localhost') {
this.gtag('config', analyticsId.value, { anonymize_ip: true }); this.gtag('config', analyticsId.value, { anonymize_ip: true });
router.events.filter(e => Types.is(e, NavigationEnd)) router.events.pipe(
filter(e => Types.is(e, NavigationEnd)))
.subscribe(() => { .subscribe(() => {
this.gtag('config', analyticsId.value, { page_path: window.location.pathname, anonymize_ip: true }); this.gtag('config', analyticsId.value, { page_path: window.location.pathname, anonymize_ip: true });
}); });

8
src/Squidex/app/framework/services/dialog.service.spec.ts

@ -70,7 +70,7 @@ describe('DialogService', () => {
const dialogService = new DialogService(); const dialogService = new DialogService();
const notification = Notification.error('Message'); const notification = Notification.error('Message');
let publishedNotification: Notification | null = null; let publishedNotification: Notification;
dialogService.notifications.subscribe(result => { dialogService.notifications.subscribe(result => {
publishedNotification = result; publishedNotification = result;
@ -78,13 +78,13 @@ describe('DialogService', () => {
dialogService.notify(notification); dialogService.notify(notification);
expect(publishedNotification).toBe(notification); expect(publishedNotification!).toBe(notification);
}); });
it('should publish dialog request', () => { it('should publish dialog request', () => {
const dialogService = new DialogService(); const dialogService = new DialogService();
let pushedDialog: DialogRequest | null = null; let pushedDialog: DialogRequest;
dialogService.dialogs.subscribe(result => { dialogService.dialogs.subscribe(result => {
pushedDialog = result; pushedDialog = result;
@ -92,6 +92,6 @@ describe('DialogService', () => {
dialogService.confirm('MyTitle', 'MyText'); dialogService.confirm('MyTitle', 'MyText');
expect(pushedDialog).toEqual(new DialogRequest('MyTitle', 'MyText')); expect(pushedDialog!).toEqual(new DialogRequest('MyTitle', 'MyText'));
}); });
}); });

3
src/Squidex/app/framework/services/message-bus.service.ts

@ -7,6 +7,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { filter, map } from 'rxjs/operators';
interface Message { interface Message {
channel: string; channel: string;
@ -30,6 +31,6 @@ export class MessageBus {
public of<T>(messageType: { new(...args: any[]): T }): Observable<T> { public of<T>(messageType: { new(...args: any[]): T }): Observable<T> {
const channel = (<any>messageType).name; const channel = (<any>messageType).name;
return this.message$.filter(m => m.channel === channel).map(m => m.data); return this.message$.pipe(filter(m => m.channel === channel), map(m => m.data));
} }
} }

5
src/Squidex/app/framework/state.ts

@ -7,6 +7,7 @@
import { AbstractControl } from '@angular/forms'; import { AbstractControl } from '@angular/forms';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ErrorDto, Types } from '@app/framework/internal'; import { ErrorDto, Types } from '@app/framework/internal';
import { fullValue} from './angular/forms/forms-helper'; import { fullValue} from './angular/forms/forms-helper';
@ -21,10 +22,10 @@ export class Form<T extends AbstractControl> {
private readonly state = new State<FormState>({ submitted: false }); private readonly state = new State<FormState>({ submitted: false });
public submitted = public submitted =
this.state.changes.map(s => s.submitted); this.state.changes.pipe(map(s => s.submitted));
public error = public error =
this.state.changes.map(s => s.error); this.state.changes.pipe(map(s => s.error));
constructor( constructor(
public readonly form: T public readonly form: T

4
src/Squidex/app/framework/utils/date-helper.spec.ts

@ -11,7 +11,7 @@ import { DateHelper } from './date-helper';
describe('DateHelper', () => { describe('DateHelper', () => {
it('should call config method of moment object', () => { it('should call config method of moment object', () => {
let called: string | null = null; let called: string;
DateHelper.setMoment({ DateHelper.setMoment({
locale: (l: string) => { called = l; } locale: (l: string) => { called = l; }
@ -19,7 +19,7 @@ describe('DateHelper', () => {
DateHelper.locale('en'); DateHelper.locale('en');
expect(called).toBe('en'); expect(called!).toBe('en');
}); });
it('should use global moment if not configured', () => { it('should use global moment if not configured', () => {

6
src/Squidex/app/framework/utils/immutable-array.spec.ts

@ -20,9 +20,9 @@ describe('ImmutableArray', () => {
const array_c = ImmutableArray.of([]); const array_c = ImmutableArray.of([]);
const array_d = ImmutableArray.empty(); const array_d = ImmutableArray.empty();
expect(array_b).toBe(array_a); expect(array_b).toBe(<any>array_a);
expect(array_c).toBe(array_a); expect(array_c).toBe(<any>array_a);
expect(array_d).toBe(array_a); expect(array_d).toBe(<any>array_a);
}); });
it('should create non empty instance', () => { it('should create non empty instance', () => {

4
src/Squidex/app/framework/utils/modal-view.spec.ts

@ -67,13 +67,13 @@ describe('ModalView', () => {
}); });
function checkValue(dialog: ModalView, expected: boolean) { function checkValue(dialog: ModalView, expected: boolean) {
let result: boolean | null = null; let result: boolean;
dialog.isOpen.subscribe(value => { dialog.isOpen.subscribe(value => {
result = value; result = value;
}).unsubscribe(); }).unsubscribe();
expect(result).toBe(expected); expect(result!).toBe(expected);
} }
}); });

22
src/Squidex/app/framework/utils/pager.spec.ts

@ -11,7 +11,7 @@ describe('Pager', () => {
it('should init with default values', () => { it('should init with default values', () => {
const pager_1 = new Pager(0); const pager_1 = new Pager(0);
expect(Object.assign({}, pager_1)).toEqual({ expect(Object.assign({}, pager_1)).toEqual(<any>{
page: 0, page: 0,
pageSize: 10, pageSize: 10,
itemFirst: 0, itemFirst: 0,
@ -26,7 +26,7 @@ describe('Pager', () => {
it('should init with page size and page', () => { it('should init with page size and page', () => {
const pager_1 = new Pager(23, 2, 10); const pager_1 = new Pager(23, 2, 10);
expect(Object.assign({}, pager_1)).toEqual({ expect(Object.assign({}, pager_1)).toEqual(<any>{
page: 2, page: 2,
pageSize: 10, pageSize: 10,
itemFirst: 21, itemFirst: 21,
@ -42,7 +42,7 @@ describe('Pager', () => {
const pager_1 = new Pager(23, 2, 10); const pager_1 = new Pager(23, 2, 10);
const pager_2 = pager_1.reset(); const pager_2 = pager_1.reset();
expect(Object.assign({}, pager_2)).toEqual({ expect(Object.assign({}, pager_2)).toEqual(<any>{
page: 0, page: 0,
pageSize: 10, pageSize: 10,
itemFirst: 0, itemFirst: 0,
@ -65,7 +65,7 @@ describe('Pager', () => {
const pager_1 = new Pager(23, 0, 10); const pager_1 = new Pager(23, 0, 10);
const pager_2 = pager_1.goNext(); const pager_2 = pager_1.goNext();
expect(Object.assign({}, pager_2)).toEqual({ expect(Object.assign({}, pager_2)).toEqual(<any>{
page: 1, page: 1,
pageSize: 10, pageSize: 10,
itemFirst: 11, itemFirst: 11,
@ -88,7 +88,7 @@ describe('Pager', () => {
const pager_1 = new Pager(23, 2, 10); const pager_1 = new Pager(23, 2, 10);
const pager_2 = pager_1.goPrev(); const pager_2 = pager_1.goPrev();
expect(Object.assign({}, pager_2)).toEqual({ expect(Object.assign({}, pager_2)).toEqual(<any>{
page: 1, page: 1,
pageSize: 10, pageSize: 10,
itemFirst: 11, itemFirst: 11,
@ -104,7 +104,7 @@ describe('Pager', () => {
const pager_1 = new Pager(23, 2, 10); const pager_1 = new Pager(23, 2, 10);
const pager_2 = pager_1.setCount(30); const pager_2 = pager_1.setCount(30);
expect(Object.assign({}, pager_2)).toEqual({ expect(Object.assign({}, pager_2)).toEqual(<any>{
page: 2, page: 2,
pageSize: 10, pageSize: 10,
itemFirst: 21, itemFirst: 21,
@ -120,7 +120,7 @@ describe('Pager', () => {
const pager_1 = new Pager(23, 1, 10); const pager_1 = new Pager(23, 1, 10);
const pager_2 = pager_1.incrementCount().incrementCount(); const pager_2 = pager_1.incrementCount().incrementCount();
expect(Object.assign({}, pager_2)).toEqual({ expect(Object.assign({}, pager_2)).toEqual(<any>{
page: 1, page: 1,
pageSize: 10, pageSize: 10,
itemFirst: 11, itemFirst: 11,
@ -136,7 +136,7 @@ describe('Pager', () => {
const pager_1 = new Pager(23, 2, 10); const pager_1 = new Pager(23, 2, 10);
const pager_2 = pager_1.incrementCount(); const pager_2 = pager_1.incrementCount();
expect(Object.assign({}, pager_2)).toEqual({ expect(Object.assign({}, pager_2)).toEqual(<any>{
page: 2, page: 2,
pageSize: 10, pageSize: 10,
itemFirst: 21, itemFirst: 21,
@ -152,7 +152,7 @@ describe('Pager', () => {
const pager_1 = new Pager(23, 1, 10); const pager_1 = new Pager(23, 1, 10);
const pager_2 = pager_1.decrementCount().decrementCount(); const pager_2 = pager_1.decrementCount().decrementCount();
expect(Object.assign({}, pager_2)).toEqual({ expect(Object.assign({}, pager_2)).toEqual(<any>{
page: 1, page: 1,
pageSize: 10, pageSize: 10,
itemFirst: 11, itemFirst: 11,
@ -168,7 +168,7 @@ describe('Pager', () => {
const pager_1 = new Pager(23, 2, 10); const pager_1 = new Pager(23, 2, 10);
const pager_2 = pager_1.decrementCount(); const pager_2 = pager_1.decrementCount();
expect(Object.assign({}, pager_2)).toEqual({ expect(Object.assign({}, pager_2)).toEqual(<any>{
page: 2, page: 2,
pageSize: 10, pageSize: 10,
itemFirst: 21, itemFirst: 21,
@ -184,7 +184,7 @@ describe('Pager', () => {
const pager_1 = new Pager(21, 2, 10); const pager_1 = new Pager(21, 2, 10);
const pager_2 = pager_1.decrementCount(); const pager_2 = pager_1.decrementCount();
expect(Object.assign({}, pager_2)).toEqual({ expect(Object.assign({}, pager_2)).toEqual(<any>{
page: 1, page: 1,
pageSize: 10, pageSize: 10,
itemFirst: 11, itemFirst: 11,

22
src/Squidex/app/framework/utils/rxjs-extensions-impl.ts

@ -1,22 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Observable } from 'rxjs';
import { DialogService } from './../services/dialog.service';
export function nextBy<T>(updater: (value: T) => T): void {
return this.next(updater(this.value));
}
export function notify(dialogs: DialogService) {
return this.catch((error: any) => {
dialogs.notifyError(error);
return Observable.throw(error);
});
}

25
src/Squidex/app/framework/utils/rxjs-extensions.ts

@ -5,27 +5,18 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { Observable } from 'rxjs/Observable'; import { catchError } from 'rxjs/operators';
import { DialogService } from './../services/dialog.service'; import { DialogService } from './../services/dialog.service';
import { nextBy, notify } from './rxjs-extensions-impl';
/* tslint:disable:no-shadowed-variable */ /* tslint:disable:no-shadowed-variable */
declare module 'rxjs/BehaviorSubject' { export const notify = (dialogs: DialogService) => <T>(source: Observable<T>) =>
interface Observable<T> { source.pipe(catchError(error => {
notify(dialogs: DialogService): Observable<T>; dialogs.notifyError(error);
}
}
BehaviorSubject.prototype['nextBy'] = nextBy;
declare module 'rxjs/Observable' { return throwError(error);
interface Observable<T> { }));
notify(dialogs: DialogService): Observable<T>;
}
}
Observable.prototype['notify'] = notify; export const nextBy = <T>(subject: BehaviorSubject<T>, updater: (value: T) => T) => subject.next(updater(this.value));

2
src/Squidex/app/framework/utils/types.spec.ts

@ -10,7 +10,7 @@ import { Types } from './types';
describe('Types', () => { describe('Types', () => {
it('should calculate hash string', () => { it('should calculate hash string', () => {
expect(Types.hash(null)).toBe('null'); expect(Types.hash(null)).toBe('null');
expect(Types.hash(undefined)).toBe(undefined); expect(Types.hash(undefined)).toBeUndefined();
expect(Types.hash(new RegExp('.*'))).toEqual('{}'); expect(Types.hash(new RegExp('.*'))).toEqual('{}');
}); });

9
src/Squidex/app/shared/components/assets-list.component.ts

@ -8,6 +8,7 @@
// tslint:disable:prefer-for-of // tslint:disable:prefer-for-of
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
AssetDto, AssetDto,
@ -42,19 +43,19 @@ export class AssetsListComponent {
} }
public search() { public search() {
this.state.load().onErrorResumeNext().subscribe(); this.state.load().pipe(onErrorResumeNext()).subscribe();
} }
public delete(asset: AssetDto) { public delete(asset: AssetDto) {
this.state.delete(asset).onErrorResumeNext().subscribe(); this.state.delete(asset).pipe(onErrorResumeNext()).subscribe();
} }
public goNext() { public goNext() {
this.state.goNext().onErrorResumeNext().subscribe(); this.state.goNext().pipe(onErrorResumeNext()).subscribe();
} }
public goPrev() { public goPrev() {
this.state.goPrev().onErrorResumeNext().subscribe(); this.state.goPrev().pipe(onErrorResumeNext()).subscribe();
} }
public trackByAsset(index: number, asset: AssetDto) { public trackByAsset(index: number, asset: AssetDto) {

7
src/Squidex/app/shared/components/assets-selector.component.ts

@ -9,6 +9,7 @@
import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
AssetDto, AssetDto,
@ -39,17 +40,17 @@ export class AssetsSelectorComponent implements OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.state.load().onErrorResumeNext().subscribe(); this.state.load().pipe(onErrorResumeNext()).subscribe();
this.assetsFilter.setValue(this.state.snapshot.assetsQuery); this.assetsFilter.setValue(this.state.snapshot.assetsQuery);
} }
public reload() { public reload() {
this.state.load(true).onErrorResumeNext().subscribe(); this.state.load(true).pipe(onErrorResumeNext()).subscribe();
} }
public search() { public search() {
this.state.search(this.assetsFilter.value).onErrorResumeNext().subscribe(); this.state.search(this.assetsFilter.value).pipe(onErrorResumeNext()).subscribe();
} }
public complete() { public complete() {

8
src/Squidex/app/shared/components/history.component.ts

@ -7,7 +7,8 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable, timer } from 'rxjs';
import { delay, merge, switchMap } from 'rxjs/operators';
import { import {
allParams, allParams,
@ -27,8 +28,9 @@ export class HistoryComponent {
private readonly channel = this.calculateChannel(); private readonly channel = this.calculateChannel();
public events: Observable<HistoryEventDto[]> = public events: Observable<HistoryEventDto[]> =
Observable.timer(0, 10000).merge(this.messageBus.of(HistoryChannelUpdated).delay(1000)) timer(0, 10000).pipe(
.switchMap(app => this.historyService.getHistory(this.appsState.appName, this.channel)); merge(this.messageBus.of(HistoryChannelUpdated).pipe(delay(1000))),
switchMap(app => this.historyService.getHistory(this.appsState.appName, this.channel)));
constructor( constructor(
private readonly appsState: AppsState, private readonly appsState: AppsState,

17
src/Squidex/app/shared/components/pipes.ts

@ -6,7 +6,8 @@
*/ */
import { ChangeDetectorRef, OnDestroy, Pipe, PipeTransform } from '@angular/core'; import { ChangeDetectorRef, OnDestroy, Pipe, PipeTransform } from '@angular/core';
import { Observable, Subscription } from 'rxjs'; import { Observable, of, Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
import { import {
ApiUrlConfig, ApiUrlConfig,
@ -61,7 +62,7 @@ export class UserNamePipe extends UserAsyncPipe implements PipeTransform {
} }
public transform(userId: string, placeholder = 'Me'): string | null { public transform(userId: string, placeholder = 'Me'): string | null {
return super.transformInternal(userId, users => users.getUser(userId, placeholder).map(u => u.displayName)); return super.transformInternal(userId, users => users.getUser(userId, placeholder).pipe(map(u => u.displayName)));
} }
} }
@ -79,12 +80,12 @@ export class UserNameRefPipe extends UserAsyncPipe implements PipeTransform {
const parts = userId.split(':'); const parts = userId.split(':');
if (parts[0] === 'subject') { if (parts[0] === 'subject') {
return users.getUser(parts[1], placeholder).map(u => u.displayName); return users.getUser(parts[1], placeholder).pipe(map(u => u.displayName));
} else { } else {
if (parts[1].endsWith('client')) { if (parts[1].endsWith('client')) {
return Observable.of(parts[1]); return of(parts[1]);
} else { } else {
return Observable.of(`${parts[1]}-client`); return of(`${parts[1]}-client`);
} }
} }
}); });
@ -133,7 +134,7 @@ export class UserPicturePipe extends UserAsyncPipe implements PipeTransform {
} }
public transform(userId: string): string | null { public transform(userId: string): string | null {
return super.transformInternal(userId, users => users.getUser(userId).map(u => this.apiUrl.buildUrl(`api/users/${u.id}/picture`))); return super.transformInternal(userId, users => users.getUser(userId).pipe(map(u => this.apiUrl.buildUrl(`api/users/${u.id}/picture`))));
} }
} }
@ -153,9 +154,9 @@ export class UserPictureRefPipe extends UserAsyncPipe implements PipeTransform {
const parts = userId.split(':'); const parts = userId.split(':');
if (parts[0] === 'subject') { if (parts[0] === 'subject') {
return users.getUser(parts[1]).map(u => this.apiUrl.buildUrl(`api/users/${u.id}/picture`)); return users.getUser(parts[1]).pipe(map(u => this.apiUrl.buildUrl(`api/users/${u.id}/picture`)));
} else { } else {
return Observable.of('/images/client.png'); return of('/images/client.png');
} }
}); });
} }

3
src/Squidex/app/shared/components/schema-category.component.ts

@ -6,6 +6,7 @@
*/ */
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
import { onErrorResumeNext } from 'rxjs/operators';
import { import {
fadeAnimation, fadeAnimation,
@ -90,7 +91,7 @@ export class SchemaCategoryComponent implements OnInit, OnChanges {
} }
public changeCategory(schema: SchemaDto) { public changeCategory(schema: SchemaDto) {
this.schemasState.changeCategory(schema, this.name).onErrorResumeNext().subscribe(); this.schemasState.changeCategory(schema, this.name).pipe(onErrorResumeNext()).subscribe();
} }
public trackBySchema(index: number, schema: SchemaDto) { public trackBySchema(index: number, schema: SchemaDto) {

6
src/Squidex/app/shared/guards/app-must-exist.guard.spec.ts

@ -6,7 +6,7 @@
*/ */
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { AppsState } from '@app/shared'; import { AppsState } from '@app/shared';
@ -34,7 +34,7 @@ describe('AppMustExistGuard', () => {
it('should navigate to 404 page if app is not found', () => { it('should navigate to 404 page if app is not found', () => {
appsState.setup(x => x.select('my-app')) appsState.setup(x => x.select('my-app'))
.returns(() => Observable.of(null)); .returns(() => of(null));
let result: boolean; let result: boolean;
@ -49,7 +49,7 @@ describe('AppMustExistGuard', () => {
it('should return true if app is found', () => { it('should return true if app is found', () => {
appsState.setup(x => x.select('my-app')) appsState.setup(x => x.select('my-app'))
.returns(() => Observable.of(<any>{})); .returns(() => of(<any>{}));
let result: boolean; let result: boolean;

8
src/Squidex/app/shared/guards/app-must-exist.guard.ts

@ -8,6 +8,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { AppsState } from './../state/apps.state'; import { AppsState } from './../state/apps.state';
@ -23,12 +24,13 @@ export class AppMustExistGuard implements CanActivate {
const appName = route.params['appName']; const appName = route.params['appName'];
const result = const result =
this.appsState.select(appName) this.appsState.select(appName).pipe(
.do(dto => { tap(dto => {
if (!dto) { if (!dto) {
this.router.navigate(['/404']); this.router.navigate(['/404']);
} }
}).map(a => !!a); }),
map(a => !!a));
return result; return result;
} }

6
src/Squidex/app/shared/guards/content-must-exist.guard.spec.ts

@ -6,7 +6,7 @@
*/ */
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { ContentDto } from './../services/contents.service'; import { ContentDto } from './../services/contents.service';
@ -32,7 +32,7 @@ describe('ContentMustExistGuard', () => {
it('should load content and return true when found', () => { it('should load content and return true when found', () => {
contentsState.setup(x => x.select('123')) contentsState.setup(x => x.select('123'))
.returns(() => Observable.of(<ContentDto>{})); .returns(() => of(<ContentDto>{}));
let result: boolean; let result: boolean;
@ -47,7 +47,7 @@ describe('ContentMustExistGuard', () => {
it('should load content and return false when not found', () => { it('should load content and return false when not found', () => {
contentsState.setup(x => x.select('123')) contentsState.setup(x => x.select('123'))
.returns(() => Observable.of(null)); .returns(() => of(null));
let result: boolean; let result: boolean;

9
src/Squidex/app/shared/guards/content-must-exist.guard.ts

@ -8,6 +8,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { allParams } from '@app/framework'; import { allParams } from '@app/framework';
@ -25,13 +26,13 @@ export class ContentMustExistGuard implements CanActivate {
const contentId = allParams(route)['contentId']; const contentId = allParams(route)['contentId'];
const result = const result =
this.contentsState.select(contentId) this.contentsState.select(contentId).pipe(
.do(dto => { tap(dto => {
if (!dto) { if (!dto) {
this.router.navigate(['/404']); this.router.navigate(['/404']);
} }
}) }),
.map(u => u !== null); map(u => u !== null));
return result; return result;
} }

4
src/Squidex/app/shared/guards/load-apps.guard.spec.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { AppsState } from '@app/shared'; import { AppsState } from '@app/shared';
@ -23,7 +23,7 @@ describe('LoadAppsGuard', () => {
it('should load apps', () => { it('should load apps', () => {
appsState.setup(x => x.load()) appsState.setup(x => x.load())
.returns(() => Observable.of(null)); .returns(() => of(null));
let result = false; let result = false;

3
src/Squidex/app/shared/guards/load-apps.guard.ts

@ -8,6 +8,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router'; import { CanActivate } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AppsState } from './../state/apps.state'; import { AppsState } from './../state/apps.state';
@ -19,6 +20,6 @@ export class LoadAppsGuard implements CanActivate {
} }
public canActivate(): Observable<boolean> { public canActivate(): Observable<boolean> {
return this.appsState.load().map(a => true); return this.appsState.load().pipe(map(a => true));
} }
} }

4
src/Squidex/app/shared/guards/load-languages.guard.spec.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { LanguagesState } from '@app/shared'; import { LanguagesState } from '@app/shared';
@ -23,7 +23,7 @@ describe('LoadLanguagesGuard', () => {
it('should load languages', () => { it('should load languages', () => {
languagesState.setup(x => x.load()) languagesState.setup(x => x.load())
.returns(() => Observable.of(null)); .returns(() => of(null));
let result = false; let result = false;

3
src/Squidex/app/shared/guards/load-languages.guard.ts

@ -8,6 +8,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router'; import { CanActivate } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { LanguagesState } from './../state/languages.state'; import { LanguagesState } from './../state/languages.state';
@ -19,6 +20,6 @@ export class LoadLanguagesGuard implements CanActivate {
} }
public canActivate(): Observable<boolean> { public canActivate(): Observable<boolean> {
return this.languagesState.load().map(a => true); return this.languagesState.load().pipe(map(a => true));
} }
} }

6
src/Squidex/app/shared/guards/must-be-authenticated.guard.spec.ts

@ -6,7 +6,7 @@
*/ */
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { AuthService } from '@app/shared'; import { AuthService } from '@app/shared';
@ -28,7 +28,7 @@ describe('MustBeAuthenticatedGuard', () => {
it('should navigate to default page if not authenticated', () => { it('should navigate to default page if not authenticated', () => {
authService.setup(x => x.userChanges) authService.setup(x => x.userChanges)
.returns(() => Observable.of(null)); .returns(() => of(null));
let result: boolean; let result: boolean;
@ -43,7 +43,7 @@ describe('MustBeAuthenticatedGuard', () => {
it('should return true if authenticated', () => { it('should return true if authenticated', () => {
authService.setup(x => x.userChanges) authService.setup(x => x.userChanges)
.returns(() => Observable.of(<any>{})); .returns(() => of(<any>{}));
let result: boolean; let result: boolean;

10
src/Squidex/app/shared/guards/must-be-authenticated.guard.ts

@ -8,6 +8,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router'; import { CanActivate, Router } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, take, tap } from 'rxjs/operators';
import { AuthService } from './../services/auth.service'; import { AuthService } from './../services/auth.service';
@ -20,12 +21,13 @@ export class MustBeAuthenticatedGuard implements CanActivate {
} }
public canActivate(): Observable<boolean> { public canActivate(): Observable<boolean> {
return this.authService.userChanges.take(1) return this.authService.userChanges.pipe(
.do(user => { take(1),
tap(user => {
if (!user) { if (!user) {
this.router.navigate(['']); this.router.navigate(['']);
} }
}) }),
.map(user => !!user); map(user => !!user));
} }
} }

6
src/Squidex/app/shared/guards/must-be-not-authenticated.guard.spec.ts

@ -6,7 +6,7 @@
*/ */
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { AuthService } from '@app/shared'; import { AuthService } from '@app/shared';
@ -28,7 +28,7 @@ describe('MustNotBeAuthenticatedGuard', () => {
it('should navigate to app page if authenticated', () => { it('should navigate to app page if authenticated', () => {
authService.setup(x => x.userChanges) authService.setup(x => x.userChanges)
.returns(() => Observable.of(<any>{})); .returns(() => of(<any>{}));
let result: boolean; let result: boolean;
@ -43,7 +43,7 @@ describe('MustNotBeAuthenticatedGuard', () => {
it('should return true if not authenticated', () => { it('should return true if not authenticated', () => {
authService.setup(x => x.userChanges) authService.setup(x => x.userChanges)
.returns(() => Observable.of(null)); .returns(() => of(null));
let result: boolean; let result: boolean;

10
src/Squidex/app/shared/guards/must-be-not-authenticated.guard.ts

@ -8,6 +8,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router'; import { CanActivate, Router } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, take, tap } from 'rxjs/operators';
import { AuthService } from './../services/auth.service'; import { AuthService } from './../services/auth.service';
@ -20,12 +21,13 @@ export class MustBeNotAuthenticatedGuard implements CanActivate {
} }
public canActivate(): Observable<boolean> { public canActivate(): Observable<boolean> {
return this.authService.userChanges.take(1) return this.authService.userChanges.pipe(
.do(user => { take(1),
tap(user => {
if (user) { if (user) {
this.router.navigate(['app']); this.router.navigate(['app']);
} }
}) }),
.map(user => !user); map(user => !user));
} }
} }

8
src/Squidex/app/shared/guards/schema-must-exist-published.guard.spec.ts

@ -6,7 +6,7 @@
*/ */
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { SchemaDetailsDto } from './../services/schemas.service'; import { SchemaDetailsDto } from './../services/schemas.service';
@ -32,7 +32,7 @@ describe('SchemaMustExistPublishedGuard', () => {
it('should load schema and return true when found', () => { it('should load schema and return true when found', () => {
schemasState.setup(x => x.select('123')) schemasState.setup(x => x.select('123'))
.returns(() => Observable.of(<SchemaDetailsDto>{ isPublished: true })); .returns(() => of(<SchemaDetailsDto>{ isPublished: true }));
let result: boolean; let result: boolean;
@ -47,7 +47,7 @@ describe('SchemaMustExistPublishedGuard', () => {
it('should load schema and return false when not found', () => { it('should load schema and return false when not found', () => {
schemasState.setup(x => x.select('123')) schemasState.setup(x => x.select('123'))
.returns(() => Observable.of(<SchemaDetailsDto>{ isPublished: false })); .returns(() => of(<SchemaDetailsDto>{ isPublished: false }));
let result: boolean; let result: boolean;
@ -62,7 +62,7 @@ describe('SchemaMustExistPublishedGuard', () => {
it('should load schema and return false when not found', () => { it('should load schema and return false when not found', () => {
schemasState.setup(x => x.select('123')) schemasState.setup(x => x.select('123'))
.returns(() => Observable.of(null)); .returns(() => of(null));
let result: boolean; let result: boolean;

9
src/Squidex/app/shared/guards/schema-must-exist-published.guard.ts

@ -8,6 +8,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { allParams } from '@app/framework'; import { allParams } from '@app/framework';
@ -25,13 +26,13 @@ export class SchemaMustExistPublishedGuard implements CanActivate {
const schemaName = allParams(route)['schemaName']; const schemaName = allParams(route)['schemaName'];
const result = const result =
this.schemasState.select(schemaName) this.schemasState.select(schemaName).pipe(
.do(dto => { tap(dto => {
if (!dto || !dto.isPublished) { if (!dto || !dto.isPublished) {
this.router.navigate(['/404']); this.router.navigate(['/404']);
} }
}) }),
.map(s => s !== null && s.isPublished); map(s => s !== null && s.isPublished));
return result; return result;
} }

6
src/Squidex/app/shared/guards/schema-must-exist.guard.spec.ts

@ -6,7 +6,7 @@
*/ */
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { SchemaDetailsDto } from './../services/schemas.service'; import { SchemaDetailsDto } from './../services/schemas.service';
@ -32,7 +32,7 @@ describe('SchemaMustExistGuard', () => {
it('should load schema and return true when found', () => { it('should load schema and return true when found', () => {
schemasState.setup(x => x.select('123')) schemasState.setup(x => x.select('123'))
.returns(() => Observable.of(<SchemaDetailsDto>{})); .returns(() => of(<SchemaDetailsDto>{}));
let result: boolean; let result: boolean;
@ -47,7 +47,7 @@ describe('SchemaMustExistGuard', () => {
it('should load schema and return false when not found', () => { it('should load schema and return false when not found', () => {
schemasState.setup(x => x.select('123')) schemasState.setup(x => x.select('123'))
.returns(() => Observable.of(null)); .returns(() => of(null));
let result: boolean; let result: boolean;

9
src/Squidex/app/shared/guards/schema-must-exist.guard.ts

@ -8,6 +8,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { allParams } from '@app/framework'; import { allParams } from '@app/framework';
@ -25,13 +26,13 @@ export class SchemaMustExistGuard implements CanActivate {
const schemaName = allParams(route)['schemaName']; const schemaName = allParams(route)['schemaName'];
const result = const result =
this.schemasState.select(schemaName) this.schemasState.select(schemaName).pipe(
.do(dto => { tap(dto => {
if (!dto) { if (!dto) {
this.router.navigate(['/404']); this.router.navigate(['/404']);
} }
}) }),
.map(s => s !== null); map(s => s !== null));
return result; return result;
} }

4
src/Squidex/app/shared/guards/unset-app.guard.spec.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { AppsState } from '@app/shared'; import { AppsState } from '@app/shared';
@ -23,7 +23,7 @@ describe('UnsetAppGuard', () => {
it('should unselect app', () => { it('should unselect app', () => {
appsState.setup(x => x.select(null)) appsState.setup(x => x.select(null))
.returns(() => Observable.of(null)); .returns(() => of(null));
let result = false; let result = false;

3
src/Squidex/app/shared/guards/unset-app.guard.ts

@ -8,6 +8,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router'; import { CanActivate } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AppsState } from './../state/apps.state'; import { AppsState } from './../state/apps.state';
@ -19,6 +20,6 @@ export class UnsetAppGuard implements CanActivate {
} }
public canActivate(): Observable<boolean> { public canActivate(): Observable<boolean> {
return this.appsState.select(null).map(a => a === null); return this.appsState.select(null).pipe(map(a => a === null));
} }
} }

4
src/Squidex/app/shared/guards/unset-content.guard.spec.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { ContentsState } from './../state/contents.state'; import { ContentsState } from './../state/contents.state';
@ -22,7 +22,7 @@ describe('UnsetContentGuard', () => {
it('should unset content', () => { it('should unset content', () => {
contentsState.setup(x => x.select(null)) contentsState.setup(x => x.select(null))
.returns(() => Observable.of(null)); .returns(() => of(null));
let result: boolean; let result: boolean;

3
src/Squidex/app/shared/guards/unset-content.guard.ts

@ -8,6 +8,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router'; import { CanActivate } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ContentsState } from './../state/contents.state'; import { ContentsState } from './../state/contents.state';
@ -19,6 +20,6 @@ export class UnsetContentGuard implements CanActivate {
} }
public canActivate(): Observable<boolean> { public canActivate(): Observable<boolean> {
return this.usersState.select(null).map(u => u === null); return this.usersState.select(null).pipe(map(u => u === null));
} }
} }

23
src/Squidex/app/shared/interceptors/auth.interceptor.spec.ts

@ -8,7 +8,8 @@
import { HTTP_INTERCEPTORS, HttpClient, HttpHeaders } from '@angular/common/http'; import { HTTP_INTERCEPTORS, HttpClient, HttpHeaders } from '@angular/common/http';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { inject, TestBed } from '@angular/core/testing'; import { inject, TestBed } from '@angular/core/testing';
import { Observable } from 'rxjs'; import { of } from 'rxjs';
import { onErrorResumeNext } from 'rxjs/operators';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { import {
@ -46,7 +47,7 @@ describe('AuthInterceptor', () => {
it('should append headers to request', it('should append headers to request',
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.userChanges).returns(() => Observable.of(<any>{ authToken: 'letmein' })); authService.setup(x => x.userChanges).returns(() => of(<any>{ authToken: 'letmein' }));
http.get('http://service/p/apps').subscribe(); http.get('http://service/p/apps').subscribe();
@ -61,7 +62,7 @@ describe('AuthInterceptor', () => {
it('should not append headers for no auth headers', it('should not append headers for no auth headers',
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.userChanges).returns(() => Observable.of(<any>{ authToken: 'letmein' })); authService.setup(x => x.userChanges).returns(() => of(<any>{ authToken: 'letmein' }));
http.get('http://service/p/apps', { headers: new HttpHeaders().set('NoAuth', '') }).subscribe(); http.get('http://service/p/apps', { headers: new HttpHeaders().set('NoAuth', '') }).subscribe();
@ -76,7 +77,7 @@ describe('AuthInterceptor', () => {
it('should not append headers for other requests', it('should not append headers for other requests',
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.userChanges).returns(() => Observable.of(<any>{ authToken: 'letmein' })); authService.setup(x => x.userChanges).returns(() => of(<any>{ authToken: 'letmein' }));
http.get('http://cloud/p/apps').subscribe(); http.get('http://cloud/p/apps').subscribe();
@ -91,10 +92,10 @@ describe('AuthInterceptor', () => {
it(`should logout for 401 status code`, it(`should logout for 401 status code`,
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.userChanges).returns(() => Observable.of(<any>{ authToken: 'letmein' })); authService.setup(x => x.userChanges).returns(() => of(<any>{ authToken: 'letmein' }));
authService.setup(x => x.loginSilent()).returns(() => Observable.of(<any>{ authToken: 'letmereallyin' })); authService.setup(x => x.loginSilent()).returns(() => of(<any>{ authToken: 'letmereallyin' }));
http.get('http://service/p/apps').onErrorResumeNext().subscribe(); http.get('http://service/p/apps').pipe(onErrorResumeNext()).subscribe();
httpMock.expectOne('http://service/p/apps').error(<any>{}, { status: 401 }); httpMock.expectOne('http://service/p/apps').error(<any>{}, { status: 401 });
httpMock.expectOne('http://service/p/apps').error(<any>{}, { status: 401 }); httpMock.expectOne('http://service/p/apps').error(<any>{}, { status: 401 });
@ -106,9 +107,9 @@ describe('AuthInterceptor', () => {
it(`should logout for ${statusCode} status code`, it(`should logout for ${statusCode} status code`,
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.userChanges).returns(() => Observable.of(<any>{ authToken: 'letmein' })); authService.setup(x => x.userChanges).returns(() => of(<any>{ authToken: 'letmein' }));
http.get('http://service/p/apps').onErrorResumeNext().subscribe(); http.get('http://service/p/apps').pipe(onErrorResumeNext()).subscribe();
const req = httpMock.expectOne('http://service/p/apps'); const req = httpMock.expectOne('http://service/p/apps');
@ -122,9 +123,9 @@ describe('AuthInterceptor', () => {
it(`should not logout for ${statusCode} status code`, it(`should not logout for ${statusCode} status code`,
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.userChanges).returns(() => Observable.of(<any>{ authToken: 'letmein' })); authService.setup(x => x.userChanges).returns(() => of(<any>{ authToken: 'letmein' }));
http.get('http://service/p/apps').onErrorResumeNext().subscribe(); http.get('http://service/p/apps').pipe(onErrorResumeNext()).subscribe();
const req = httpMock.expectOne('http://service/p/apps'); const req = httpMock.expectOne('http://service/p/apps');

32
src/Squidex/app/shared/interceptors/auth.interceptor.ts

@ -7,7 +7,8 @@
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable} from '@angular/core'; import { Injectable} from '@angular/core';
import { Observable } from 'rxjs'; import { empty, Observable, throwError } from 'rxjs';
import { catchError, switchMap, take } from 'rxjs/operators';
import { ApiUrlConfig } from '@app/framework'; import { ApiUrlConfig } from '@app/framework';
@ -25,9 +26,11 @@ export class AuthInterceptor implements HttpInterceptor {
public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.indexOf(this.baseUrl) === 0 && !req.headers.has('NoAuth')) { if (req.url.indexOf(this.baseUrl) === 0 && !req.headers.has('NoAuth')) {
return this.authService.userChanges.take(1).switchMap(user => { return this.authService.userChanges.pipe(
return this.makeRequest(req, next, user, true); take(1),
}); switchMap(user => {
return this.makeRequest(req, next, user, true);
}));
} else { } else {
return next.handle(req); return next.handle(req);
} }
@ -43,22 +46,23 @@ export class AuthInterceptor implements HttpInterceptor {
.set('Pragma', 'no-cache') .set('Pragma', 'no-cache')
}); });
return next.handle(authReq) return next.handle(authReq).pipe(
.catch((error: HttpErrorResponse) => { catchError((error: HttpErrorResponse) => {
if (error.status === 401 && renew) { if (error.status === 401 && renew) {
return this.authService.loginSilent() return this.authService.loginSilent().pipe(
.catch(_ => { catchError(_ => {
this.authService.logoutRedirect(); this.authService.logoutRedirect();
return Observable.empty<Profile>(); return empty();
}) }),
.switchMap(u => this.makeRequest(req, next, u)); switchMap(u => this.makeRequest(req, next, u)));
} else if (error.status === 401 || error.status === 403) { } else if (error.status === 401 || error.status === 403) {
this.authService.logoutRedirect(); this.authService.logoutRedirect();
return Observable.empty<HttpEvent<any>>(); return empty();
} }
return Observable.throw(error);
}); return throwError(error);
}));
} }
} }

14
src/Squidex/app/shared/services/app-clients.service.spec.ts

@ -43,7 +43,7 @@ describe('AppClientsService', () => {
it('should make get request to get app clients', it('should make get request to get app clients',
inject([AppClientsService, HttpTestingController], (appClientsService: AppClientsService, httpMock: HttpTestingController) => { inject([AppClientsService, HttpTestingController], (appClientsService: AppClientsService, httpMock: HttpTestingController) => {
let clients: AppClientsDto | null = null; let clients: AppClientsDto;
appClientsService.getClients('my-app').subscribe(result => { appClientsService.getClients('my-app').subscribe(result => {
clients = result; clients = result;
@ -73,7 +73,7 @@ describe('AppClientsService', () => {
} }
}); });
expect(clients).toEqual( expect(clients!).toEqual(
new AppClientsDto([ new AppClientsDto([
new AppClientDto('client1', 'Client 1', 'secret1', 'Editor'), new AppClientDto('client1', 'Client 1', 'secret1', 'Editor'),
new AppClientDto('client2', 'Client 2', 'secret2', 'Developer') new AppClientDto('client2', 'Client 2', 'secret2', 'Developer')
@ -85,7 +85,7 @@ describe('AppClientsService', () => {
const dto = new CreateAppClientDto('client1'); const dto = new CreateAppClientDto('client1');
let client: AppClientDto | null = null; let client: AppClientDto;
appClientsService.postClient('my-app', dto, version).subscribe(result => { appClientsService.postClient('my-app', dto, version).subscribe(result => {
client = result.payload; client = result.payload;
@ -98,8 +98,7 @@ describe('AppClientsService', () => {
req.flush({ id: 'client1', name: 'Client 1', secret: 'secret1', permission: 'Developer' }); req.flush({ id: 'client1', name: 'Client 1', secret: 'secret1', permission: 'Developer' });
expect(client).toEqual( expect(client!).toEqual(new AppClientDto('client1', 'Client 1', 'secret1', 'Developer'));
new AppClientDto('client1', 'Client 1', 'secret1', 'Developer'));
})); }));
it('should make put request to rename client', it('should make put request to rename client',
@ -133,7 +132,7 @@ describe('AppClientsService', () => {
it('should make form request to create token', it('should make form request to create token',
inject([AppClientsService, HttpTestingController], (appClientsService: AppClientsService, httpMock: HttpTestingController) => { inject([AppClientsService, HttpTestingController], (appClientsService: AppClientsService, httpMock: HttpTestingController) => {
let accessTokenDto: AccessTokenDto | null = null; let accessTokenDto: AccessTokenDto;
appClientsService.createToken('my-app', new AppClientDto('myClientId', 'myClient', 'mySecret', 'Editor')).subscribe(result => { appClientsService.createToken('my-app', new AppClientDto('myClientId', 'myClient', 'mySecret', 'Editor')).subscribe(result => {
accessTokenDto = result; accessTokenDto = result;
@ -148,7 +147,6 @@ describe('AppClientsService', () => {
req.flush({ access_token: 'token1', token_type: 'type1' }); req.flush({ access_token: 'token1', token_type: 'type1' });
expect(accessTokenDto).toEqual( expect(accessTokenDto!).toEqual(new AccessTokenDto('token1', 'type1'));
new AccessTokenDto('token1', 'type1'));
})); }));
}); });

48
src/Squidex/app/shared/services/app-clients.service.ts

@ -8,14 +8,14 @@
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import '@app/framework/angular/http/http-extensions';
import { import {
AnalyticsService, AnalyticsService,
ApiUrlConfig, ApiUrlConfig,
HTTP, HTTP,
Model, Model,
pretifyError,
Version, Version,
Versioned Versioned
} from '@app/framework'; } from '@app/framework';
@ -83,8 +83,8 @@ export class AppClientsService {
public getClients(appName: string): Observable<AppClientsDto> { public getClients(appName: string): Observable<AppClientsDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients`);
return HTTP.getVersioned<any>(this.http, url) return HTTP.getVersioned<any>(this.http, url).pipe(
.map(response => { map(response => {
const body = response.payload.body; const body = response.payload.body;
const items: any[] = body; const items: any[] = body;
@ -98,15 +98,15 @@ export class AppClientsService {
}); });
return new AppClientsDto(clients, response.version); return new AppClientsDto(clients, response.version);
}) }),
.pretifyError('Failed to load clients. Please reload.'); pretifyError('Failed to load clients. Please reload.'));
} }
public postClient(appName: string, dto: CreateAppClientDto, version: Version): Observable<Versioned<AppClientDto>> { public postClient(appName: string, dto: CreateAppClientDto, version: Version): Observable<Versioned<AppClientDto>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients`);
return HTTP.postVersioned<any>(this.http, url, dto, version) return HTTP.postVersioned<any>(this.http, url, dto, version).pipe(
.map(response => { map(response => {
const body = response.payload.body; const body = response.payload.body;
const client = new AppClientDto( const client = new AppClientDto(
@ -116,31 +116,31 @@ export class AppClientsService {
body.permission); body.permission);
return new Versioned(response.version, client); return new Versioned(response.version, client);
}) }),
.do(() => { tap(() => {
this.analytics.trackEvent('Client', 'Created', appName); this.analytics.trackEvent('Client', 'Created', appName);
}) }),
.pretifyError('Failed to add client. Please reload.'); pretifyError('Failed to add client. Please reload.'));
} }
public putClient(appName: string, id: string, dto: UpdateAppClientDto, version: Version): Observable<Versioned<any>> { public putClient(appName: string, id: string, dto: UpdateAppClientDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients/${id}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients/${id}`);
return HTTP.putVersioned(this.http, url, dto, version) return HTTP.putVersioned(this.http, url, dto, version).pipe(
.do(() => { tap(() => {
this.analytics.trackEvent('Client', 'Updated', appName); this.analytics.trackEvent('Client', 'Updated', appName);
}) }),
.pretifyError('Failed to revoke client. Please reload.'); pretifyError('Failed to revoke client. Please reload.'));
} }
public deleteClient(appName: string, id: string, version: Version): Observable<Versioned<any>> { public deleteClient(appName: string, id: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients/${id}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients/${id}`);
return HTTP.deleteVersioned(this.http, url, version) return HTTP.deleteVersioned(this.http, url, version).pipe(
.do(() => { tap(() => {
this.analytics.trackEvent('Client', 'Deleted', appName); this.analytics.trackEvent('Client', 'Deleted', appName);
}) }),
.pretifyError('Failed to revoke client. Please reload.'); pretifyError('Failed to revoke client. Please reload.'));
} }
public createToken(appName: string, client: AppClientDto): Observable<AccessTokenDto> { public createToken(appName: string, client: AppClientDto): Observable<AccessTokenDto> {
@ -154,10 +154,10 @@ export class AppClientsService {
const url = this.apiUrl.buildUrl('identity-server/connect/token'); const url = this.apiUrl.buildUrl('identity-server/connect/token');
return this.http.post(url, body, options) return this.http.post(url, body, options).pipe(
.map((response: any) => { map((response: any) => {
return new AccessTokenDto(response.access_token, response.token_type); return new AccessTokenDto(response.access_token, response.token_type);
}) }),
.pretifyError('Failed to create token. Please retry.'); pretifyError('Failed to create token. Please retry.'));
} }
} }

6
src/Squidex/app/shared/services/app-contributors.service.spec.ts

@ -41,7 +41,7 @@ describe('AppContributorsService', () => {
it('should make get request to get app contributors', it('should make get request to get app contributors',
inject([AppContributorsService, HttpTestingController], (appContributorsService: AppContributorsService, httpMock: HttpTestingController) => { inject([AppContributorsService, HttpTestingController], (appContributorsService: AppContributorsService, httpMock: HttpTestingController) => {
let contributors: AppContributorsDto | null = null; let contributors: AppContributorsDto;
appContributorsService.getContributors('my-app').subscribe(result => { appContributorsService.getContributors('my-app').subscribe(result => {
contributors = result; contributors = result;
@ -70,7 +70,7 @@ describe('AppContributorsService', () => {
} }
}); });
expect(contributors).toEqual( expect(contributors!).toEqual(
new AppContributorsDto([ new AppContributorsDto([
new AppContributorDto('123', 'Owner'), new AppContributorDto('123', 'Owner'),
new AppContributorDto('456', 'Owner') new AppContributorDto('456', 'Owner')
@ -82,7 +82,7 @@ describe('AppContributorsService', () => {
const dto = new AppContributorDto('123', 'Owner'); const dto = new AppContributorDto('123', 'Owner');
let contributorAssignedDto: ContributorAssignedDto | null = null; let contributorAssignedDto: ContributorAssignedDto;
appContributorsService.postContributor('my-app', dto, version).subscribe(result => { appContributorsService.postContributor('my-app', dto, version).subscribe(result => {
contributorAssignedDto = result.payload; contributorAssignedDto = result.payload;

32
src/Squidex/app/shared/services/app-contributors.service.ts

@ -8,14 +8,14 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import '@app/framework/angular/http/http-extensions';
import { import {
AnalyticsService, AnalyticsService,
ApiUrlConfig, ApiUrlConfig,
HTTP, HTTP,
Model, Model,
pretifyError,
Version, Version,
Versioned Versioned
} from '@app/framework'; } from '@app/framework';
@ -66,8 +66,8 @@ export class AppContributorsService {
public getContributors(appName: string): Observable<AppContributorsDto> { public getContributors(appName: string): Observable<AppContributorsDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors`);
return HTTP.getVersioned<any>(this.http, url) return HTTP.getVersioned<any>(this.http, url).pipe(
.map(response => { map(response => {
const body = response.payload.body; const body = response.payload.body;
const items: any[] = body.contributors; const items: any[] = body.contributors;
@ -79,34 +79,34 @@ export class AppContributorsService {
item.permission); item.permission);
}), }),
body.maxContributors, response.version); body.maxContributors, response.version);
}) }),
.pretifyError('Failed to load contributors. Please reload.'); pretifyError('Failed to load contributors. Please reload.'));
} }
public postContributor(appName: string, dto: AppContributorDto, version: Version): Observable<Versioned<ContributorAssignedDto>> { public postContributor(appName: string, dto: AppContributorDto, version: Version): Observable<Versioned<ContributorAssignedDto>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors`);
return HTTP.postVersioned(this.http, url, dto, version) return HTTP.postVersioned(this.http, url, dto, version).pipe(
.map(response => { map(response => {
const body: any = response.payload.body; const body: any = response.payload.body;
const result = new ContributorAssignedDto(body.contributorId); const result = new ContributorAssignedDto(body.contributorId);
return new Versioned(response.version, result); return new Versioned(response.version, result);
}) }),
.do(() => { tap(() => {
this.analytics.trackEvent('Contributor', 'Configured', appName); this.analytics.trackEvent('Contributor', 'Configured', appName);
}) }),
.pretifyError('Failed to add contributors. Please reload.'); pretifyError('Failed to add contributors. Please reload.'));
} }
public deleteContributor(appName: string, contributorId: string, version: Version): Observable<Versioned<any>> { public deleteContributor(appName: string, contributorId: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors/${contributorId}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors/${contributorId}`);
return HTTP.deleteVersioned(this.http, url, version) return HTTP.deleteVersioned(this.http, url, version).pipe(
.do(() => { tap(() => {
this.analytics.trackEvent('Contributor', 'Deleted', appName); this.analytics.trackEvent('Contributor', 'Deleted', appName);
}) }),
.pretifyError('Failed to delete contributors. Please reload.'); pretifyError('Failed to delete contributors. Please reload.'));
} }
} }

9
src/Squidex/app/shared/services/app-languages.service.spec.ts

@ -42,7 +42,7 @@ describe('AppLanguagesService', () => {
it('should make get request to get app languages', it('should make get request to get app languages',
inject([AppLanguagesService, HttpTestingController], (appLanguagesService: AppLanguagesService, httpMock: HttpTestingController) => { inject([AppLanguagesService, HttpTestingController], (appLanguagesService: AppLanguagesService, httpMock: HttpTestingController) => {
let languages: AppLanguagesDto | null = null; let languages: AppLanguagesDto;
appLanguagesService.getLanguages('my-app').subscribe(result => { appLanguagesService.getLanguages('my-app').subscribe(result => {
languages = result; languages = result;
@ -73,7 +73,7 @@ describe('AppLanguagesService', () => {
} }
}); });
expect(languages).toEqual( expect(languages!).toEqual(
new AppLanguagesDto([ new AppLanguagesDto([
new AppLanguageDto('en', 'English', true, true, ['de', 'en']), new AppLanguageDto('en', 'English', true, true, ['de', 'en']),
new AppLanguageDto('it', 'Italian', false, false, []) new AppLanguageDto('it', 'Italian', false, false, [])
@ -85,7 +85,7 @@ describe('AppLanguagesService', () => {
const dto = new AddAppLanguageDto('de'); const dto = new AddAppLanguageDto('de');
let language: AppLanguageDto | null = null; let language: AppLanguageDto;
appLanguagesService.postLanguage('my-app', dto, version).subscribe(result => { appLanguagesService.postLanguage('my-app', dto, version).subscribe(result => {
language = result.payload; language = result.payload;
@ -98,8 +98,7 @@ describe('AppLanguagesService', () => {
req.flush({ iso2Code: 'de', englishName: 'German' }); req.flush({ iso2Code: 'de', englishName: 'German' });
expect(language).toEqual( expect(language!).toEqual(new AppLanguageDto('de', 'German', false, false, []));
new AppLanguageDto('de', 'German', false, false, []));
})); }));
it('should make put request to make master language', it('should make put request to make master language',

40
src/Squidex/app/shared/services/app-languages.service.ts

@ -8,14 +8,14 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import '@app/framework/angular/http/http-extensions';
import { import {
AnalyticsService, AnalyticsService,
ApiUrlConfig, ApiUrlConfig,
HTTP, HTTP,
Model, Model,
pretifyError,
Version, Version,
Versioned Versioned
} from '@app/framework'; } from '@app/framework';
@ -77,8 +77,8 @@ export class AppLanguagesService {
public getLanguages(appName: string): Observable<AppLanguagesDto> { public getLanguages(appName: string): Observable<AppLanguagesDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages`);
return HTTP.getVersioned<any>(this.http, url) return HTTP.getVersioned<any>(this.http, url).pipe(
.map(response => { map(response => {
const body = response.payload.body; const body = response.payload.body;
const items: any[] = body; const items: any[] = body;
@ -93,15 +93,15 @@ export class AppLanguagesService {
}); });
return new AppLanguagesDto(languages, response.version); return new AppLanguagesDto(languages, response.version);
}) }),
.pretifyError('Failed to load languages. Please reload.'); pretifyError('Failed to load languages. Please reload.'));
} }
public postLanguage(appName: string, dto: AddAppLanguageDto, version: Version): Observable<Versioned<AppLanguageDto>> { public postLanguage(appName: string, dto: AddAppLanguageDto, version: Version): Observable<Versioned<AppLanguageDto>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages`);
return HTTP.postVersioned<any>(this.http, url, dto, version) return HTTP.postVersioned<any>(this.http, url, dto, version).pipe(
.map(response => { map(response => {
const body = response.payload.body; const body = response.payload.body;
const language = new AppLanguageDto( const language = new AppLanguageDto(
@ -112,30 +112,30 @@ export class AppLanguagesService {
body.fallback || []); body.fallback || []);
return new Versioned(response.version, language); return new Versioned(response.version, language);
}) }),
.do(() => { tap(() => {
this.analytics.trackEvent('Language', 'Added', appName); this.analytics.trackEvent('Language', 'Added', appName);
}) }),
.pretifyError('Failed to add language. Please reload.'); pretifyError('Failed to add language. Please reload.'));
} }
public putLanguage(appName: string, languageCode: string, dto: UpdateAppLanguageDto, version: Version): Observable<Versioned<any>> { public putLanguage(appName: string, languageCode: string, dto: UpdateAppLanguageDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages/${languageCode}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages/${languageCode}`);
return HTTP.putVersioned(this.http, url, dto, version) return HTTP.putVersioned(this.http, url, dto, version).pipe(
.do(() => { tap(() => {
this.analytics.trackEvent('Language', 'Updated', appName); this.analytics.trackEvent('Language', 'Updated', appName);
}) }),
.pretifyError('Failed to change language. Please reload.'); pretifyError('Failed to change language. Please reload.'));
} }
public deleteLanguage(appName: string, languageCode: string, version: Version): Observable<Versioned<any>> { public deleteLanguage(appName: string, languageCode: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages/${languageCode}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages/${languageCode}`);
return HTTP.deleteVersioned(this.http, url, version) return HTTP.deleteVersioned(this.http, url, version).pipe(
.do(() => { tap(() => {
this.analytics.trackEvent('Language', 'Deleted', appName); this.analytics.trackEvent('Language', 'Deleted', appName);
}) }),
.pretifyError('Failed to add language. Please reload.'); pretifyError('Failed to add language. Please reload.'));
} }
} }

8
src/Squidex/app/shared/services/app-patterns.service.spec.ts

@ -39,7 +39,7 @@ describe('AppPatternsService', () => {
it('should make get request to get patterns', it('should make get request to get patterns',
inject([AppPatternsService, HttpTestingController], (patternService: AppPatternsService, httpMock: HttpTestingController) => { inject([AppPatternsService, HttpTestingController], (patternService: AppPatternsService, httpMock: HttpTestingController) => {
let patterns: AppPatternsDto | null = null; let patterns: AppPatternsDto;
patternService.getPatterns('my-app').subscribe(result => { patternService.getPatterns('my-app').subscribe(result => {
patterns = result; patterns = result;
@ -68,7 +68,7 @@ describe('AppPatternsService', () => {
} }
}); });
expect(patterns).toEqual( expect(patterns!).toEqual(
new AppPatternsDto([ new AppPatternsDto([
new AppPatternDto('1', 'Number', '[0-9]', 'Message1'), new AppPatternDto('1', 'Number', '[0-9]', 'Message1'),
new AppPatternDto('2', 'Numbers', '[0-9]*', 'Message2') new AppPatternDto('2', 'Numbers', '[0-9]*', 'Message2')
@ -80,7 +80,7 @@ describe('AppPatternsService', () => {
const dto = new EditAppPatternDto('Number', '[0-9]', 'Message1'); const dto = new EditAppPatternDto('Number', '[0-9]', 'Message1');
let pattern: AppPatternDto | null = null; let pattern: AppPatternDto;
patternService.postPattern('my-app', dto, version).subscribe(result => { patternService.postPattern('my-app', dto, version).subscribe(result => {
pattern = result.payload; pattern = result.payload;
@ -98,7 +98,7 @@ describe('AppPatternsService', () => {
message: 'Message1' message: 'Message1'
}); });
expect(pattern).toEqual(new AppPatternDto('1', 'Number', '[0-9]', 'Message1')); expect(pattern!).toEqual(new AppPatternDto('1', 'Number', '[0-9]', 'Message1'));
})); }));
it('should make put request to update pattern', it('should make put request to update pattern',

28
src/Squidex/app/shared/services/app-patterns.service.ts

@ -8,13 +8,13 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import '@app/framework/angular/http/http-extensions';
import { import {
ApiUrlConfig, ApiUrlConfig,
HTTP, HTTP,
Model, Model,
pretifyError,
Version, Version,
Versioned Versioned
} from '@app/framework'; } from '@app/framework';
@ -68,8 +68,8 @@ export class AppPatternsService {
public getPatterns(appName: string): Observable<AppPatternsDto> { public getPatterns(appName: string): Observable<AppPatternsDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/patterns`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/patterns`);
return HTTP.getVersioned<any>(this.http, url) return HTTP.getVersioned<any>(this.http, url).pipe(
.map(response => { map(response => {
const body = response.payload.body; const body = response.payload.body;
const items: any[] = body; const items: any[] = body;
@ -83,15 +83,15 @@ export class AppPatternsService {
item.message); item.message);
}), }),
response.version); response.version);
}) }),
.pretifyError('Failed to add pattern. Please reload.'); pretifyError('Failed to add pattern. Please reload.'));
} }
public postPattern(appName: string, dto: EditAppPatternDto, version: Version): Observable<Versioned<AppPatternDto>> { public postPattern(appName: string, dto: EditAppPatternDto, version: Version): Observable<Versioned<AppPatternDto>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/patterns`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/patterns`);
return HTTP.postVersioned<any>(this.http, url, dto, version) return HTTP.postVersioned<any>(this.http, url, dto, version).pipe(
.map(response => { map(response => {
const body = response.payload.body; const body = response.payload.body;
const pattern = new AppPatternDto( const pattern = new AppPatternDto(
@ -101,21 +101,21 @@ export class AppPatternsService {
body.message); body.message);
return new Versioned(response.version, pattern); return new Versioned(response.version, pattern);
}) }),
.pretifyError('Failed to add pattern. Please reload.'); pretifyError('Failed to add pattern. Please reload.'));
} }
public putPattern(appName: string, id: string, dto: EditAppPatternDto, version: Version): Observable<Versioned<any>> { public putPattern(appName: string, id: string, dto: EditAppPatternDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/patterns/${id}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/patterns/${id}`);
return HTTP.putVersioned(this.http, url, dto, version) return HTTP.putVersioned(this.http, url, dto, version).pipe(
.pretifyError('Failed to update pattern. Please reload.'); pretifyError('Failed to update pattern. Please reload.'));
} }
public deletePattern(appName: string, id: string, version: Version): Observable<Versioned<any>> { public deletePattern(appName: string, id: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/patterns/${id}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/patterns/${id}`);
return HTTP.deleteVersioned(this.http, url, version) return HTTP.deleteVersioned(this.http, url, version).pipe(
.pretifyError('Failed to remove pattern. Please reload.'); pretifyError('Failed to remove pattern. Please reload.'));
} }
} }

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save