From bb5346eb1ae09765c1144723b9e57d5a641445d8 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sun, 16 Jul 2017 10:01:09 +0200 Subject: [PATCH] Refactored some services. --- .../pages/graphql/graphql-page.component.ts | 8 +- .../framework/angular/http-extensions-impl.ts | 142 ++++++ .../app/framework/angular/http-extensions.ts | 7 +- .../app/framework/angular/http-utils.ts | 72 --- src/Squidex/app/framework/declarations.ts | 2 +- src/Squidex/app/framework/module.ts | 6 +- src/Squidex/app/shared/declarations-base.ts | 2 + .../shared/interceptors/auth.interceptor.ts | 51 ++ src/Squidex/app/shared/module.ts | 11 +- .../services/app-clients.service.spec.ts | 167 +++---- .../shared/services/app-clients.service.ts | 47 +- .../services/app-contributors.service.spec.ts | 112 ++--- .../services/app-contributors.service.ts | 23 +- .../services/app-languages.service.spec.ts | 140 +++--- .../shared/services/app-languages.service.ts | 28 +- .../app/shared/services/apps.service.spec.ts | 106 ++--- .../app/shared/services/apps.service.ts | 16 +- .../shared/services/assets.service.spec.ts | 354 -------------- .../app/shared/services/assets.service.ts | 135 +++--- .../app/shared/services/auth.service.ts | 89 +--- .../shared/services/contents.service.spec.ts | 290 ----------- .../app/shared/services/contents.service.ts | 37 +- .../services/event-consumers.service.spec.ts | 109 ----- .../services/event-consumers.service.ts | 23 +- .../app/shared/services/help.service.spec.ts | 77 --- .../app/shared/services/help.service.ts | 7 +- .../shared/services/history.service.spec.ts | 68 --- .../app/shared/services/history.service.ts | 15 +- .../shared/services/languages.service.spec.ts | 62 --- .../app/shared/services/languages.service.ts | 11 +- .../app/shared/services/plans.service.spec.ts | 99 ---- .../app/shared/services/plans.service.ts | 19 +- .../shared/services/schemas.fields.spec.ts | 175 ------- .../shared/services/schemas.service.spec.ts | 450 ------------------ .../app/shared/services/schemas.service.ts | 70 ++- .../shared/services/usages.service.spec.ts | 149 ------ .../app/shared/services/usages.service.ts | 38 +- .../services/users-provider.service.spec.ts | 103 ---- .../app/shared/services/users.service.spec.ts | 339 ------------- .../app/shared/services/users.service.ts | 49 +- .../app/shared/services/webhooks.service.1.ts | 99 ++++ .../shared/services/webhooks.service.spec.ts | 114 ----- .../app/shared/services/webhooks.service.ts | 24 +- src/Squidex/package.json | 35 +- src/Squidex/tslint.json | 4 - 45 files changed, 873 insertions(+), 3111 deletions(-) create mode 100644 src/Squidex/app/framework/angular/http-extensions-impl.ts delete mode 100644 src/Squidex/app/framework/angular/http-utils.ts create mode 100644 src/Squidex/app/shared/interceptors/auth.interceptor.ts delete mode 100644 src/Squidex/app/shared/services/assets.service.spec.ts delete mode 100644 src/Squidex/app/shared/services/contents.service.spec.ts delete mode 100644 src/Squidex/app/shared/services/event-consumers.service.spec.ts delete mode 100644 src/Squidex/app/shared/services/help.service.spec.ts delete mode 100644 src/Squidex/app/shared/services/history.service.spec.ts delete mode 100644 src/Squidex/app/shared/services/languages.service.spec.ts delete mode 100644 src/Squidex/app/shared/services/plans.service.spec.ts delete mode 100644 src/Squidex/app/shared/services/schemas.fields.spec.ts delete mode 100644 src/Squidex/app/shared/services/schemas.service.spec.ts delete mode 100644 src/Squidex/app/shared/services/usages.service.spec.ts delete mode 100644 src/Squidex/app/shared/services/users-provider.service.spec.ts delete mode 100644 src/Squidex/app/shared/services/users.service.spec.ts create mode 100644 src/Squidex/app/shared/services/webhooks.service.1.ts delete mode 100644 src/Squidex/app/shared/services/webhooks.service.spec.ts diff --git a/src/Squidex/app/features/api/pages/graphql/graphql-page.component.ts b/src/Squidex/app/features/api/pages/graphql/graphql-page.component.ts index 6feba81e2..f260e3d70 100644 --- a/src/Squidex/app/features/api/pages/graphql/graphql-page.component.ts +++ b/src/Squidex/app/features/api/pages/graphql/graphql-page.component.ts @@ -6,6 +6,7 @@ */ import { Component, ElementRef, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; @@ -16,7 +17,6 @@ import { ApiUrlConfig, AppComponentBase, AppsStoreService, - AuthService, NotificationService } from 'shared'; @@ -31,8 +31,8 @@ export class GraphQLPageComponent extends AppComponentBase implements OnInit { public graphiQLContainer: ElementRef; constructor(apps: AppsStoreService, notifications: NotificationService, - private readonly authService: AuthService, - private readonly apiUrl: ApiUrlConfig + private readonly apiUrl: ApiUrlConfig, + private readonly http: HttpClient ) { super(notifications, apps); } @@ -48,7 +48,7 @@ export class GraphQLPageComponent extends AppComponentBase implements OnInit { private request(params: any) { return this.appNameOnce() - .switchMap(app => this.authService.authPost(this.apiUrl.buildUrl(`api/content/${app}/graphql`), params).map(r => r.json())) + .switchMap(app => this.http.post(this.apiUrl.buildUrl(`api/content/${app}/graphql`), params)) .toPromise(); } } diff --git a/src/Squidex/app/framework/angular/http-extensions-impl.ts b/src/Squidex/app/framework/angular/http-extensions-impl.ts new file mode 100644 index 000000000..c5be747af --- /dev/null +++ b/src/Squidex/app/framework/angular/http-extensions-impl.ts @@ -0,0 +1,142 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Sebastian Stehle. All rights reserved + */ + +import { HttpClient, HttpErrorResponse, HttpHeaders, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { Version } from './../utils/version'; + +export class EntityCreatedDto { + constructor( + public readonly id: any + ) { + } +} + +export class ErrorDto { + public get displayMessage(): string { + let result = this.message; + + if (this.details && this.details.length > 0) { + const detailMessage = this.details[0]; + + const lastChar = result[result.length - 1]; + + if (lastChar !== '.' && lastChar !== ',') { + result += '.'; + } + + result += ' '; + result += detailMessage; + } + + const lastChar = result[result.length - 1]; + + if (lastChar !== '.') { + result += '.'; + } + + return result; + } + + constructor( + public readonly statusCode: number, + public readonly message: string, + public readonly details: string[] = [] + ) { + } +} + +export module HTTP { + export function getVersioned(http: HttpClient, url: string, version?: Version): Observable { + if (version) { + return http.get(url, { observe: 'response', headers: new HttpHeaders().set('If-Match', version.value) }) + .do((response: HttpResponse) => { + if (version && response.status.toString().indexOf('2') === 0 && response.headers) { + const etag = response.headers.get('etag'); + + if (etag) { + version.update(etag); + } + } + }).map((response: HttpResponse) => response.body); + } else { + return http.get(url, { observe: 'response' }).map((response: HttpResponse) => response.body); + } + } + + export function postVersioned(http: HttpClient, url: string, body: any, version?: Version): Observable { + if (version) { + return http.post(url, body, { observe: 'response', headers: new HttpHeaders().set('If-Match', version.value) }) + .do((response: HttpResponse) => { + if (version && response.status.toString().indexOf('2') === 0 && response.headers) { + const etag = response.headers.get('etag'); + + if (etag) { + version.update(etag); + } + } + }).map((response: HttpResponse) => response.body); + } else { + return http.post(url, body, { observe: 'response' }).map((response: HttpResponse) => response.body); + } + } + + export function putVersioned(http: HttpClient, url: string, body: any, version?: Version): Observable { + if (version) { + return http.put(url, body, { observe: 'response', headers: new HttpHeaders().set('If-Match', version.value) }) + .do((response: HttpResponse) => { + if (version && response.status.toString().indexOf('2') === 0 && response.headers) { + const etag = response.headers.get('etag'); + + if (etag) { + version.update(etag); + } + } + }).map((response: HttpResponse) => response.body); + } else { + return http.put(url, body, { observe: 'response' }).map((response: HttpResponse) => response.body); + } + } + + export function deleteVersioned(http: HttpClient, url: string, version?: Version): Observable { + if (version) { + return http.delete(url, { observe: 'response', headers: new HttpHeaders().set('If-Match', version.value) }) + .do((response: HttpResponse) => { + if (version && response.status.toString().indexOf('2') === 0 && response.headers) { + const etag = response.headers.get('etag'); + + if (etag) { + version.update(etag); + } + } + }).map((response: HttpResponse) => response.body); + } else { + return http.delete(url, { observe: 'response' }).map((response: HttpResponse) => response.body); + } + } +} + +export function pretifyError(message: string): Observable { + return this.catch((response: HttpErrorResponse) => { + let result = new ErrorDto(500, message); + + if (!(response.error instanceof Error)) { + try { + 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, response.error.message, response.error.details); + } + } catch (e) { + result = result; + } + } + + return Observable.throw(result); + }); +} \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/http-extensions.ts b/src/Squidex/app/framework/angular/http-extensions.ts index 5dcc40c9a..ae960375d 100644 --- a/src/Squidex/app/framework/angular/http-extensions.ts +++ b/src/Squidex/app/framework/angular/http-extensions.ts @@ -6,12 +6,13 @@ */ import { Observable } from 'rxjs/Observable'; -import { catchError } from './http-utils'; + +import { pretifyError } from './http-extensions-impl'; declare module 'rxjs/Observable' { interface Observable { - catchError(message: string): Observable; + pretifyError(message: string): Observable; } } -Observable.prototype['catchError'] = catchError; \ No newline at end of file +Observable.prototype['pretifyError'] = pretifyError; \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/http-utils.ts b/src/Squidex/app/framework/angular/http-utils.ts deleted file mode 100644 index 2dec6e0de..000000000 --- a/src/Squidex/app/framework/angular/http-utils.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Response } from '@angular/http'; -import { Observable } from 'rxjs'; - -export class EntityCreatedDto { - constructor( - public readonly id: any - ) { - } -} - -export class ErrorDto { - public get displayMessage(): string { - let result = this.message; - - if (this.details && this.details.length > 0) { - const detailMessage = this.details[0]; - - const lastChar = result[result.length - 1]; - - if (lastChar !== '.' && lastChar !== ',') { - result += '.'; - } - - result += ' '; - result += detailMessage; - } - - const lastChar = result[result.length - 1]; - - if (lastChar !== '.') { - result += '.'; - } - - return result; - } - - constructor( - public readonly statusCode: number, - public readonly message: string, - public readonly details: string[] = [] - ) { - } -} - -export function catchError(message: string): Observable { - return this.catch((error: any | Response) => { - let result = new ErrorDto(500, message); - - if (error instanceof Response) { - try { - const body = error.json(); - - if (error.status === 412) { - result = new ErrorDto(error.status, 'Failed to make the update. Another user has made a change. Please reload.'); - } else if (error.status !== 500) { - result = new ErrorDto(error.status, body.message, body.details); - } - } catch (e) { - result = result; - } - } - - return Observable.throw(result); - }); -} \ No newline at end of file diff --git a/src/Squidex/app/framework/declarations.ts b/src/Squidex/app/framework/declarations.ts index 85711ecdf..b157293f3 100644 --- a/src/Squidex/app/framework/declarations.ts +++ b/src/Squidex/app/framework/declarations.ts @@ -18,7 +18,7 @@ export * from './angular/file-drop.directive'; export * from './angular/focus-on-change.directive'; export * from './angular/focus-on-init.directive'; export * from './angular/geolocation-editor.component'; -export * from './angular/http-utils'; +export * from './angular/http-extensions-impl'; export * from './angular/image-source.directive'; export * from './angular/indeterminate-value.directive'; export * from './angular/json-editor.component'; diff --git a/src/Squidex/app/framework/module.ts b/src/Squidex/app/framework/module.ts index af2f67710..15e6ceab2 100644 --- a/src/Squidex/app/framework/module.ts +++ b/src/Squidex/app/framework/module.ts @@ -6,9 +6,9 @@ */ import { CommonModule } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; import { ModuleWithProviders, NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { HttpModule } from '@angular/http'; import { RouterModule } from '@angular/router'; import { @@ -68,7 +68,7 @@ import { @NgModule({ imports: [ - HttpModule, + HttpClientModule, FormsModule, CommonModule, RouterModule, @@ -163,7 +163,7 @@ import { TitleComponent, ToggleComponent, UserReportComponent, - HttpModule, + HttpClientModule, FormsModule, CommonModule, RouterModule, diff --git a/src/Squidex/app/shared/declarations-base.ts b/src/Squidex/app/shared/declarations-base.ts index 59e9901a4..721b70cac 100644 --- a/src/Squidex/app/shared/declarations-base.ts +++ b/src/Squidex/app/shared/declarations-base.ts @@ -14,6 +14,8 @@ export * from './guards/resolve-published-schema.guard'; export * from './guards/resolve-schema.guard'; export * from './guards/resolve-user.guard'; +export * from './interceptors/auth.interceptor'; + export * from './services/app-contributors.service'; export * from './services/app-clients.service'; export * from './services/app-languages.service'; diff --git a/src/Squidex/app/shared/interceptors/auth.interceptor.ts b/src/Squidex/app/shared/interceptors/auth.interceptor.ts new file mode 100644 index 000000000..db9035384 --- /dev/null +++ b/src/Squidex/app/shared/interceptors/auth.interceptor.ts @@ -0,0 +1,51 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Sebastian Stehle. All rights reserved + */ + +import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http'; +import { Injectable} from '@angular/core'; +import { Observable } from 'rxjs'; + +import { AuthService } from './../services/auth.service'; +import { ApiUrlConfig } from 'framework'; + +@Injectable() +export class AuthInterceptor implements HttpInterceptor { + private baseUrl: string; + + constructor(apiUrlConfig: ApiUrlConfig, + private readonly authService: AuthService + ) { + this.baseUrl = apiUrlConfig.buildUrl(''); + } + + public intercept(req: HttpRequest, next: HttpHandler): Observable> { + if (req.url.indexOf(this.baseUrl) === 0 && !req.headers.has('NoAuth')) { + const authReq = req.clone({ + headers: req.headers + .set('Authorization', this.authService.user ? this.authService.user.authToken : '') + .set('Accept-Language', '*') + .set('Pragma', 'no-cache') + }); + + return next.handle(authReq) + .catch((error: HttpErrorResponse) => { + if (error.status === 404 && (!this.authService.user || this.authService.user.isExpired)) { + this.authService.logoutRedirect(); + + return Observable.empty(); + } else if (error.status === 401 || error.status === 403) { + this.authService.logoutRedirect(); + + return Observable.empty(); + } + return Observable.throw(error); + }); + } else { + return next.handle(req); + } + } +} \ No newline at end of file diff --git a/src/Squidex/app/shared/module.ts b/src/Squidex/app/shared/module.ts index be42623bd..bc1d806e6 100644 --- a/src/Squidex/app/shared/module.ts +++ b/src/Squidex/app/shared/module.ts @@ -5,9 +5,9 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { ModuleWithProviders, NgModule } from '@angular/core'; import { DndModule } from 'ng2-dnd'; -import { ProgressHttpModule } from 'angular-progress-http'; import { SqxFrameworkModule } from 'framework'; @@ -21,6 +21,7 @@ import { AppMustExistGuard, AssetComponent, AssetsService, + AuthInterceptor, AuthService, ContentsService, EventConsumersService, @@ -56,7 +57,6 @@ import { @NgModule({ imports: [ - ProgressHttpModule, DndModule, SqxFrameworkModule ], @@ -122,7 +122,12 @@ export class SqxSharedModule { UserManagementService, UsersProviderService, UsersService, - WebhooksService + WebhooksService, + { + provide: HTTP_INTERCEPTORS, + useClass: AuthInterceptor, + multi: true + } ] }; } diff --git a/src/Squidex/app/shared/services/app-clients.service.spec.ts b/src/Squidex/app/shared/services/app-clients.service.spec.ts index 83312ac6c..e59305869 100644 --- a/src/Squidex/app/shared/services/app-clients.service.spec.ts +++ b/src/Squidex/app/shared/services/app-clients.service.spec.ts @@ -5,144 +5,124 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Http, Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { It, IMock, Mock, Times } from 'typemoq'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { inject, TestBed } from '@angular/core/testing'; import { AccessTokenDto, ApiUrlConfig, AppClientDto, AppClientsService, - AuthService, CreateAppClientDto, UpdateAppClientDto, Version } from './../'; describe('AppClientsService', () => { - let authService: IMock; - let appClientsService: AppClientsService; let version = new Version('1'); - let http: IMock; beforeEach(() => { - http = Mock.ofType(Http); - - authService = Mock.ofType(AuthService); - appClientsService = new AppClientsService(authService.object, new ApiUrlConfig('http://service/p/'), http.object); + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule + ], + providers: [ + AppClientsService, + { provide: ApiUrlConfig, useValue: new ApiUrlConfig('http://service/p/') } + ] + }); }); - it('should make get request to get app clients', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/clients', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: [ - { - id: 'client1', - name: 'Client 1', - secret: 'secret1' - }, - { - id: 'client2', - name: 'Client 2', - secret: 'secret2' - } - ] - }) - ) - )) - .verifiable(Times.once()); + afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => { + httpMock.verify(); + })); + + it('should make get request to get app clients', + inject([AppClientsService, HttpTestingController], (appClientsService: AppClientsService, httpMock: HttpTestingController) => { let clients: AppClientDto[] | null = null; appClientsService.getClients('my-app', version).subscribe(result => { clients = result; - }).unsubscribe(); + }); + + const req = httpMock.expectOne('http://service/p/api/apps/my-app/clients'); + + expect(req.request.method).toEqual('GET'); + expect(req.request.headers.get('If-Match')).toEqual('1'); + + req.flush([ + { + id: 'client1', + name: 'Client 1', + secret: 'secret1' + }, + { + id: 'client2', + name: 'Client 2', + secret: 'secret2' + } + ]); expect(clients).toEqual( [ new AppClientDto('client1', 'Client 1', 'secret1'), new AppClientDto('client2', 'Client 2', 'secret2') ]); + })); - authService.verifyAll(); - }); + it('should make post request to create client', + inject([AppClientsService, HttpTestingController], (appClientsService: AppClientsService, httpMock: HttpTestingController) => { - it('should make post request to create client', () => { const dto = new CreateAppClientDto('client1'); - authService.setup(x => x.authPost('http://service/p/api/apps/my-app/clients', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - id: 'client1', - name: 'Client 1', - secret: 'secret1' - } - }) - ) - )) - .verifiable(Times.once()); - let client: AppClientDto | null = null; appClientsService.postClient('my-app', dto, version).subscribe(result => { client = result; }); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/clients'); + + expect(req.request.method).toEqual('POST'); + expect(req.request.headers.get('If-Match')).toEqual('1'); + + req.flush({ id: 'client1', name: 'Client 1', secret: 'secret1' }); + expect(client).toEqual( new AppClientDto('client1', 'Client 1', 'secret1')); + })); - authService.verifyAll(); - }); + it('should make put request to rename client', + inject([AppClientsService, HttpTestingController], (appClientsService: AppClientsService, httpMock: HttpTestingController) => { - it('should make put request to rename client', () => { const dto = new UpdateAppClientDto('Client 1 New'); - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/clients/client1', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); + appClientsService.updateClient('my-app', 'client1', dto, version).subscribe(); - appClientsService.updateClient('my-app', 'client1', dto, version); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/clients/client1'); - authService.verifyAll(); - }); + expect(req.request.method).toEqual('PUT'); + expect(req.request.headers.get('If-Match')).toEqual('1'); - it('should make delete request to remove client', () => { - authService.setup(x => x.authDelete('http://service/p/api/apps/my-app/clients/client1', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); + req.flush({}); + })); - appClientsService.deleteClient('my-app', 'client1', version); + it('should make delete request to remove client', + inject([AppClientsService, HttpTestingController], (appClientsService: AppClientsService, httpMock: HttpTestingController) => { - authService.verifyAll(); - }); + appClientsService.deleteClient('my-app', 'client1', version).subscribe(); - it('should make form request to create token', () => { - const body = 'grant_type=client_credentials&scope=squidex-api&client_id=my-app:myClientId&client_secret=mySecret'; + const req = httpMock.expectOne('http://service/p/api/apps/my-app/clients/client1'); - http.setup(x => x.post('http://service/p/identity-server/connect/token', body, It.isAny())) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - access_token: 'token1', token_type: 'type1' - } - }) - ) - )) - .verifiable(Times.once()); + expect(req.request.method).toEqual('DELETE'); + expect(req.request.headers.get('If-Match')).toEqual('1'); + + req.flush({}); + })); + + it('should make form request to create token', + inject([AppClientsService, HttpTestingController], (appClientsService: AppClientsService, httpMock: HttpTestingController) => { let accessTokenDto: AccessTokenDto | null = null; @@ -150,9 +130,16 @@ describe('AppClientsService', () => { accessTokenDto = result; }); + const body = 'grant_type=client_credentials&scope=squidex-api&client_id=my-app:myClientId&client_secret=mySecret'; + + const req = httpMock.expectOne('http://service/p/identity-server/connect/token'); + + expect(req.request.method).toEqual('POST'); + expect(req.request.body).toEqual(body); + + req.flush({ access_token: 'token1', token_type: 'type1' }); + expect(accessTokenDto).toEqual( new AccessTokenDto('token1', 'type1')); - - http.verifyAll(); - }); + })); }); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/app-clients.service.ts b/src/Squidex/app/shared/services/app-clients.service.ts index 2b265091e..f4452b6ad 100644 --- a/src/Squidex/app/shared/services/app-clients.service.ts +++ b/src/Squidex/app/shared/services/app-clients.service.ts @@ -5,14 +5,17 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Headers, Http, RequestOptions } from '@angular/http'; import { Observable } from 'rxjs'; import 'framework/angular/http-extensions'; -import { ApiUrlConfig, Version } from 'framework'; -import { AuthService } from './auth.service'; +import { + ApiUrlConfig, + HTTP, + Version +} from 'framework'; export class AppClientDto { constructor( @@ -48,17 +51,15 @@ export class AccessTokenDto { @Injectable() export class AppClientsService { constructor( - private readonly authService: AuthService, - private readonly apiUrl: ApiUrlConfig, - private readonly http: Http + private readonly http: HttpClient, + private readonly apiUrl: ApiUrlConfig ) { } public getClients(appName: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients`); - return this.authService.authGet(url, version) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url, version) .map(response => { const items: any[] = response; @@ -69,51 +70,51 @@ export class AppClientsService { item.secret); }); }) - .catchError('Failed to load clients. Please reload.'); + .pretifyError('Failed to load clients. Please reload.'); } public postClient(appName: string, dto: CreateAppClientDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients`); - return this.authService.authPost(url, dto, version) - .map(response => response.json()) + return HTTP.postVersioned(this.http, url, dto, version) .map(response => { return new AppClientDto( response.id, response.name, response.secret); }) - .catchError('Failed to add client. Please reload.'); + .pretifyError('Failed to add client. Please reload.'); } public updateClient(appName: string, id: string, dto: UpdateAppClientDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients/${id}`); - return this.authService.authPut(url, dto, version) - .catchError('Failed to revoke client. Please reload.'); + return HTTP.putVersioned(this.http, url, dto, version) + .pretifyError('Failed to revoke client. Please reload.'); } public deleteClient(appName: string, id: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients/${id}`); - return this.authService.authDelete(url, version) - .catchError('Failed to revoke client. Please reload.'); + return HTTP.deleteVersioned(this.http, url, version) + .pretifyError('Failed to revoke client. Please reload.'); } public createToken(appName: string, client: AppClientDto): Observable { - const options = new RequestOptions({ - headers: new Headers({ - 'Content-Type': 'application/x-www-form-urlencoded' + const options = { + headers: new HttpHeaders({ + 'Content-Type': 'application/x-www-form-urlencoded', 'NoAuth': 'true' }) - }); + }; const body = `grant_type=client_credentials&scope=squidex-api&client_id=${appName}:${client.id}&client_secret=${client.secret}`; const url = this.apiUrl.buildUrl('identity-server/connect/token'); return this.http.post(url, body, options) - .map(response => response.json()) - .map(response => new AccessTokenDto(response.access_token, response.token_type)) - .catchError('Failed to create token. Please retry.'); + .map((response: any) => { + return new AccessTokenDto(response.access_token, response.token_type); + }) + .pretifyError('Failed to create token. Please retry.'); } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/app-contributors.service.spec.ts b/src/Squidex/app/shared/services/app-contributors.service.spec.ts index 312146616..dde363a33 100644 --- a/src/Squidex/app/shared/services/app-contributors.service.spec.ts +++ b/src/Squidex/app/shared/services/app-contributors.service.spec.ts @@ -5,94 +5,96 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { IMock, Mock, Times } from 'typemoq'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { inject, TestBed } from '@angular/core/testing'; import { ApiUrlConfig, AppContributorDto, AppContributorsDto, AppContributorsService, - AuthService, Version } from './../'; describe('AppContributorsService', () => { - let authService: IMock; - let appContributorsService: AppContributorsService; let version = new Version('1'); beforeEach(() => { - authService = Mock.ofType(AuthService); - appContributorsService = new AppContributorsService(authService.object, new ApiUrlConfig('http://service/p/')); + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule + ], + providers: [ + AppContributorsService, + { provide: ApiUrlConfig, useValue: new ApiUrlConfig('http://service/p/') } + ] + }); }); - it('should make get request to get app contributors', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/contributors', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - contributors: [ - { - contributorId: '123', - permission: 'Owner' - }, - { - contributorId: '456', - permission: 'Owner' - } - ], - maxContributors: 100 - } - }) - ) - )) - .verifiable(Times.once()); + afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => { + httpMock.verify(); + })); + + it('should make get request to get app contributors', + inject([AppContributorsService, HttpTestingController], (appContributorsService: AppContributorsService, httpMock: HttpTestingController) => { let contributors: AppContributorsDto | null = null; appContributorsService.getContributors('my-app', version).subscribe(result => { contributors = result; - }).unsubscribe(); + }); + + const req = httpMock.expectOne('http://service/p/api/apps/my-app/contributors'); + + expect(req.request.method).toEqual('GET'); + expect(req.request.headers.get('If-Match')).toEqual('1'); + + req.flush({ + contributors: [ + { + contributorId: '123', + permission: 'Owner' + }, + { + contributorId: '456', + permission: 'Owner' + } + ], + maxContributors: 100 + }); expect(contributors).toEqual( new AppContributorsDto([ new AppContributorDto('123', 'Owner'), new AppContributorDto('456', 'Owner') ], 100)); + })); - authService.verifyAll(); - }); + it('should make post request to assign contributor', + inject([AppContributorsService, HttpTestingController], (appContributorsService: AppContributorsService, httpMock: HttpTestingController) => { - it('should make post request to assign contributor', () => { const dto = new AppContributorDto('123', 'Owner'); - authService.setup(x => x.authPost('http://service/p/api/apps/my-app/contributors', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); + appContributorsService.postContributor('my-app', dto, version).subscribe(); - appContributorsService.postContributor('my-app', dto, version); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/contributors'); - authService.verifyAll(); - }); + expect(req.request.method).toEqual('POST'); + expect(req.request.headers.get('If-Match')).toEqual('1'); - it('should make delete request to remove contributor', () => { - authService.setup(x => x.authDelete('http://service/p/api/apps/my-app/contributors/123', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); + req.flush({}); + })); - appContributorsService.deleteContributor('my-app', '123', version); + it('should make delete request to remove contributor', + inject([AppContributorsService, HttpTestingController], (appContributorsService: AppContributorsService, httpMock: HttpTestingController) => { - authService.verifyAll(); - }); + appContributorsService.deleteContributor('my-app', '123', version).subscribe(); + + const req = httpMock.expectOne('http://service/p/api/apps/my-app/contributors/123'); + + expect(req.request.method).toEqual('DELETE'); + expect(req.request.headers.get('If-Match')).toEqual('1'); + + req.flush({}); + })); }); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/app-contributors.service.ts b/src/Squidex/app/shared/services/app-contributors.service.ts index f2e23a3df..236b06587 100644 --- a/src/Squidex/app/shared/services/app-contributors.service.ts +++ b/src/Squidex/app/shared/services/app-contributors.service.ts @@ -5,13 +5,17 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import 'framework/angular/http-extensions'; -import { ApiUrlConfig, Version } from 'framework'; -import { AuthService } from './auth.service'; +import { + ApiUrlConfig, + HTTP, + Version +} from 'framework'; export class AppContributorDto { constructor( @@ -32,7 +36,7 @@ export class AppContributorsDto { @Injectable() export class AppContributorsService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -40,8 +44,7 @@ export class AppContributorsService { public getContributors(appName: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors`); - return this.authService.authGet(url, version) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url, version) .map(response => { const items: any[] = response.contributors; @@ -53,20 +56,20 @@ export class AppContributorsService { }), response.maxContributors); }) - .catchError('Failed to load contributors. Please reload.'); + .pretifyError('Failed to load contributors. Please reload.'); } public postContributor(appName: string, dto: AppContributorDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors`); - return this.authService.authPost(url, dto, version) - .catchError('Failed to add contributors. Please reload.'); + return HTTP.postVersioned(this.http, url, dto, version) + .pretifyError('Failed to add contributors. Please reload.'); } public deleteContributor(appName: string, contributorId: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors/${contributorId}`); - return this.authService.authDelete(url, version) - .catchError('Failed to delete contributors. Please reload.'); + return HTTP.deleteVersioned(this.http, url, version) + .pretifyError('Failed to delete contributors. Please reload.'); } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/app-languages.service.spec.ts b/src/Squidex/app/shared/services/app-languages.service.spec.ts index 18365d860..efdad2282 100644 --- a/src/Squidex/app/shared/services/app-languages.service.spec.ts +++ b/src/Squidex/app/shared/services/app-languages.service.spec.ts @@ -5,123 +5,121 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { IMock, Mock, Times } from 'typemoq'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { inject, TestBed } from '@angular/core/testing'; import { AddAppLanguageDto, ApiUrlConfig, AppLanguageDto, AppLanguagesService, - AuthService, UpdateAppLanguageDto, Version } from './../'; describe('AppLanguagesService', () => { - let authService: IMock; - let appLanguagesService: AppLanguagesService; let version = new Version('1'); beforeEach(() => { - authService = Mock.ofType(AuthService); - appLanguagesService = new AppLanguagesService(authService.object, new ApiUrlConfig('http://service/p/')); + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule + ], + providers: [ + AppLanguagesService, + { provide: ApiUrlConfig, useValue: new ApiUrlConfig('http://service/p/') } + ] + }); }); - it('should make get request to get app languages', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/languages', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: [ - { - iso2Code: 'en', - englishName: 'English', - isMaster: true, - isOptional: true, - fallback: ['de', 'en'] - }, - { - iso2Code: 'it', - englishName: 'Italian' - } - ] - }) - ) - )) - .verifiable(Times.once()); + afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => { + httpMock.verify(); + })); + + it('should make get request to get app languages', + inject([AppLanguagesService, HttpTestingController], (appLanguagesService: AppLanguagesService, httpMock: HttpTestingController) => { let languages: AppLanguageDto[] | null = null; appLanguagesService.getLanguages('my-app', version).subscribe(result => { languages = result; - }).unsubscribe(); + }); + + const req = httpMock.expectOne('http://service/p/api/apps/my-app/languages'); + + expect(req.request.method).toEqual('GET'); + expect(req.request.headers.get('If-Match')).toEqual('1'); + + req.flush([ + { + iso2Code: 'en', + englishName: 'English', + isMaster: true, + isOptional: true, + fallback: ['de', 'en'] + }, + { + iso2Code: 'it', + isMaster: false, + isOptional: false, + englishName: 'Italian' + } + ]); expect(languages).toEqual( [ new AppLanguageDto('en', 'English', true, true, ['de', 'en']), new AppLanguageDto('it', 'Italian', false, false, []) ]); + })); - authService.verifyAll(); - }); + it('should make post request to add language', + inject([AppLanguagesService, HttpTestingController], (appLanguagesService: AppLanguagesService, httpMock: HttpTestingController) => { - it('should make post request to add language', () => { const dto = new AddAppLanguageDto('de'); - authService.setup(x => x.authPost('http://service/p/api/apps/my-app/languages', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - iso2Code: 'de', - englishName: 'German' - } - }) - ) - )) - .verifiable(Times.once()); - let language: AppLanguageDto | null = null; appLanguagesService.postLanguages('my-app', dto, version).subscribe(result => { language = result; }); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/languages'); + + expect(req.request.method).toEqual('POST'); + expect(req.request.headers.get('If-Match')).toEqual('1'); + + req.flush({ iso2Code: 'de', englishName: 'German' }); + expect(language).toEqual( new AppLanguageDto('de', 'German', false, false, [])); + })); - authService.verifyAll(); - }); + it('should make put request to make master language', + inject([AppLanguagesService, HttpTestingController], (appLanguagesService: AppLanguagesService, httpMock: HttpTestingController) => { - it('should make put request to make master language', () => { const dto = new UpdateAppLanguageDto(true, true, []); - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/languages/de', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); + appLanguagesService.updateLanguage('my-app', 'de', dto, version).subscribe(); - appLanguagesService.updateLanguage('my-app', 'de', dto, version); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/languages/de'); - authService.verifyAll(); - }); + expect(req.request.method).toEqual('PUT'); + expect(req.request.headers.get('If-Match')).toEqual('1'); - it('should make delete request to remove language', () => { - authService.setup(x => x.authDelete('http://service/p/api/apps/my-app/languages/de', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); + req.flush({}); + })); - appLanguagesService.deleteLanguage('my-app', 'de', version); + it('should make delete request to remove language', + inject([AppLanguagesService, HttpTestingController], (appLanguagesService: AppLanguagesService, httpMock: HttpTestingController) => { - authService.verifyAll(); - }); + appLanguagesService.deleteLanguage('my-app', 'de', version).subscribe(); + + const req = httpMock.expectOne('http://service/p/api/apps/my-app/languages/de'); + + expect(req.request.method).toEqual('DELETE'); + expect(req.request.headers.get('If-Match')).toEqual('1'); + + req.flush({}); + })); }); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/app-languages.service.ts b/src/Squidex/app/shared/services/app-languages.service.ts index 70957cf35..22d0dd998 100644 --- a/src/Squidex/app/shared/services/app-languages.service.ts +++ b/src/Squidex/app/shared/services/app-languages.service.ts @@ -5,13 +5,17 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import 'framework/angular/http-extensions'; -import { ApiUrlConfig, Version } from 'framework'; -import { AuthService } from './auth.service'; +import { + ApiUrlConfig, + HTTP, + Version +} from 'framework'; export class AppLanguageDto { constructor( @@ -43,7 +47,7 @@ export class UpdateAppLanguageDto { @Injectable() export class AppLanguagesService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -51,8 +55,7 @@ export class AppLanguagesService { public getLanguages(appName: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages`); - return this.authService.authGet(url, version) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url, version) .map(response => { const items: any[] = response; @@ -65,14 +68,13 @@ export class AppLanguagesService { item.fallback || []); }); }) - .catchError('Failed to load languages. Please reload.'); + .pretifyError('Failed to load languages. Please reload.'); } public postLanguages(appName: string, dto: AddAppLanguageDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages`); - return this.authService.authPost(url, dto, version) - .map(response => response.json()) + return HTTP.postVersioned(this.http, url, dto, version) .map(response => { return new AppLanguageDto( response.iso2Code, @@ -81,20 +83,20 @@ export class AppLanguagesService { response.isOptional === true, response.fallback || []); }) - .catchError('Failed to add language. Please reload.'); + .pretifyError('Failed to add language. Please reload.'); } public updateLanguage(appName: string, languageCode: string, dto: UpdateAppLanguageDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages/${languageCode}`); - return this.authService.authPut(url, dto, version) - .catchError('Failed to change language. Please reload.'); + return HTTP.putVersioned(this.http, url, dto, version) + .pretifyError('Failed to change language. Please reload.'); } public deleteLanguage(appName: string, languageCode: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages/${languageCode}`); - return this.authService.authDelete(url, version) - .catchError('Failed to add language. Please reload.'); + return HTTP.deleteVersioned(this.http, url, version) + .pretifyError('Failed to add language. Please reload.'); } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/apps.service.spec.ts b/src/Squidex/app/shared/services/apps.service.spec.ts index 7ac3e42ac..dcb96aa9d 100644 --- a/src/Squidex/app/shared/services/apps.service.spec.ts +++ b/src/Squidex/app/shared/services/apps.service.spec.ts @@ -5,92 +5,90 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { IMock, Mock, Times } from 'typemoq'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { inject, TestBed } from '@angular/core/testing'; import { ApiUrlConfig, AppDto, AppsService, - AuthService, CreateAppDto, DateTime, EntityCreatedDto } from './../'; describe('AppsService', () => { - let authService: IMock; - let appsService: AppsService; - beforeEach(() => { - authService = Mock.ofType(AuthService); - appsService = new AppsService(authService.object, new ApiUrlConfig('http://service/p/')); + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule + ], + providers: [ + AppsService, + { provide: ApiUrlConfig, useValue: new ApiUrlConfig('http://service/p/') } + ] + }); }); - it('should make get request to get apps', () => { - authService.setup(x => x.authGet('http://service/p/api/apps')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: [ - { - id: '123', - name: 'name1', - permission: 'Owner', - created: '2016-01-01', - lastModified: '2016-02-02' - }, - { - id: '456', - name: 'name2', - permission: 'Owner', - created: '2017-01-01', - lastModified: '2017-02-02' - } - ] - }) - ) - )) - .verifiable(Times.once()); + afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => { + httpMock.verify(); + })); + + it('should make get request to get apps', + inject([AppsService, HttpTestingController], (appsService: AppsService, httpMock: HttpTestingController) => { let apps: AppDto[] | null = null; appsService.getApps().subscribe(result => { apps = result; - }).unsubscribe(); + }); + + const req = httpMock.expectOne('http://service/p/api/apps'); + + expect(req.request.method).toEqual('GET'); + expect(req.request.headers.get('If-Match')).toBeUndefined(); + + req.flush([ + { + id: '123', + name: 'name1', + permission: 'Owner', + created: '2016-01-01', + lastModified: '2016-02-02' + }, + { + id: '456', + name: 'name2', + permission: 'Owner', + created: '2017-01-01', + lastModified: '2017-02-02' + } + ]); expect(apps).toEqual([ new AppDto('123', 'name1', 'Owner', DateTime.parseISO('2016-01-01'), DateTime.parseISO('2016-02-02')), new AppDto('456', 'name2', 'Owner', DateTime.parseISO('2017-01-01'), DateTime.parseISO('2017-02-02')) ]); + })); - authService.verifyAll(); - }); + it('should make post request to create app', + inject([AppsService, HttpTestingController], (appsService: AppsService, httpMock: HttpTestingController) => { - it('should make post request to create app', () => { const dto = new CreateAppDto('new-app'); - authService.setup(x => x.authPost('http://service/p/api/apps', dto)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - id: '123' - } - }) - ) - )) - .verifiable(Times.once()); - let created: EntityCreatedDto | null = null; appsService.postApp(dto).subscribe(result => { created = result; - }).unsubscribe(); + }); - expect(created).toEqual(new EntityCreatedDto('123')); + const req = httpMock.expectOne('http://service/p/api/apps'); - authService.verifyAll(); - }); + expect(req.request.method).toEqual('POST'); + expect(req.request.headers.get('If-Match')).toBeUndefined(); + + req.flush({ id: '123' }); + + expect(created).toEqual(new EntityCreatedDto('123')); + })); }); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/apps.service.ts b/src/Squidex/app/shared/services/apps.service.ts index ae06f0e3c..65d725bfb 100644 --- a/src/Squidex/app/shared/services/apps.service.ts +++ b/src/Squidex/app/shared/services/apps.service.ts @@ -5,6 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; @@ -13,11 +14,10 @@ import 'framework/angular/http-extensions'; import { ApiUrlConfig, DateTime, + HTTP, EntityCreatedDto } from 'framework'; -import { AuthService } from './auth.service'; - export class AppDto { constructor( public readonly id: string, @@ -39,7 +39,7 @@ export class CreateAppDto { @Injectable() export class AppsService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -47,8 +47,7 @@ export class AppsService { public getApps(): Observable { const url = this.apiUrl.buildUrl('/api/apps'); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { const items: any[] = response; @@ -61,17 +60,16 @@ export class AppsService { DateTime.parseISO(item.lastModified)); }); }) - .catchError('Failed to load apps. Please reload.'); + .pretifyError('Failed to load apps. Please reload.'); } public postApp(dto: CreateAppDto): Observable { const url = this.apiUrl.buildUrl('api/apps'); - return this.authService.authPost(url, dto) - .map(response => response.json()) + return HTTP.postVersioned(this.http, url, dto) .map(response => { return new EntityCreatedDto(response.id); }) - .catchError('Failed to create app. Please reload.'); + .pretifyError('Failed to create app. Please reload.'); } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/assets.service.spec.ts b/src/Squidex/app/shared/services/assets.service.spec.ts deleted file mode 100644 index 147a924c7..000000000 --- a/src/Squidex/app/shared/services/assets.service.spec.ts +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { ProgressHttp } from 'angular-progress-http'; -import { IMock, It, Mock, Times } from 'typemoq'; - -import { - ApiUrlConfig, - AssetsDto, - AssetDto, - AssetCreatedDto, - AssetReplacedDto, - AssetsService, - AuthService, - DateTime, - Profile, - UpdateAssetDto, - Version -} from './../'; - -describe('AssetsService', () => { - let authService: IMock; - let assetsService: AssetsService; - let progressHttp: IMock; - let version = new Version('1'); - - beforeEach(() => { - const factory = { - create: () => { - return null; - } - }; - - progressHttp = Mock.ofInstance(new ProgressHttp(null!, null!, factory, null!)); - progressHttp.setup(x => x.withUploadProgressListener(It.isAny())).returns(() => progressHttp.object); - - authService = Mock.ofType(AuthService); - authService.setup(x => x.user).returns(() => new Profile({})); - - assetsService = new AssetsService(authService.object, new ApiUrlConfig('http://service/p/'), progressHttp.object); - }); - - it('should make get request to get assets', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/assets?take=17&skip=13')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - total: 10, - items: [ - { - id: 'id1', - created: '2016-12-12T10:10', - createdBy: 'Created1', - lastModified: '2017-12-12T10:10', - lastModifiedBy: 'LastModifiedBy1', - fileName: 'my-asset1.png', - fileSize: 1024, - fileVersion: 2000, - mimeType: 'text/plain', - isImage: true, - pixelWidth: 1024, - pixelHeight: 2048, - version: 11 - }, - { - id: 'id2', - created: '2016-10-12T10:10', - createdBy: 'Created2', - lastModified: '2017-10-12T10:10', - lastModifiedBy: 'LastModifiedBy2', - fileName: 'my-asset2.png', - fileSize: 1024, - fileVersion: 2000, - mimeType: 'text/plain', - isImage: true, - pixelWidth: 1024, - pixelHeight: 2048, - version: 22 - } - ] - } - }) - ) - )) - .verifiable(Times.once()); - - let assets: AssetsDto | null = null; - - assetsService.getAssets('my-app', 17, 13).subscribe(result => { - assets = result; - }).unsubscribe(); - - expect(assets).toEqual( - new AssetsDto(10, [ - new AssetDto( - 'id1', 'Created1', 'LastModifiedBy1', - DateTime.parseISO_UTC('2016-12-12T10:10'), - DateTime.parseISO_UTC('2017-12-12T10:10'), - 'my-asset1.png', - 1024, - 2000, - 'text/plain', - true, - 1024, - 2048, - new Version('11')), - new AssetDto('id2', 'Created2', 'LastModifiedBy2', - DateTime.parseISO_UTC('2016-10-12T10:10'), - DateTime.parseISO_UTC('2017-10-12T10:10'), - 'my-asset2.png', - 1024, - 2000, - 'text/plain', - true, - 1024, - 2048, - new Version('22')) - ])); - - authService.verifyAll(); - }); - - it('should make get request to get asset', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/assets/123')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - id: 'id1', - created: '2016-12-12T10:10', - createdBy: 'Created1', - lastModified: '2017-12-12T10:10', - lastModifiedBy: 'LastModifiedBy1', - fileName: 'my-asset1.png', - fileSize: 1024, - fileVersion: 2000, - mimeType: 'text/plain', - isImage: true, - pixelWidth: 1024, - pixelHeight: 2048, - version: 11 - } - }) - ) - )) - .verifiable(Times.once()); - - let assets: AssetDto | null = null; - - assetsService.getAsset('my-app', '123').subscribe(result => { - assets = result; - }).unsubscribe(); - - expect(assets).toEqual( - new AssetDto( - 'id1', 'Created1', 'LastModifiedBy1', - DateTime.parseISO_UTC('2016-12-12T10:10'), - DateTime.parseISO_UTC('2017-12-12T10:10'), - 'my-asset1.png', - 1024, - 2000, - 'text/plain', - true, - 1024, - 2048, - new Version('11'))); - - authService.verifyAll(); - }); - - it('should append query to find by name', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/assets?query=my-query&take=17&skip=13')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - total: 10, - items: [] - } - }) - ) - )) - .verifiable(Times.once()); - - let assets: AssetsDto | null = null; - - assetsService.getAssets('my-app', 17, 13, 'my-query').subscribe(result => { - assets = result; - }).unsubscribe(); - - authService.verifyAll(); - }); - - it('should append mime types to find by types', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/assets?mimeTypes=text/plain,image/png&take=17&skip=13')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - total: 10, - items: [] - } - }) - ) - )) - .verifiable(Times.once()); - - let assets: AssetsDto | null = null; - - assetsService.getAssets('my-app', 17, 13, undefined, ['text/plain', 'image/png']).subscribe(result => { - assets = result; - }).unsubscribe(); - - authService.verifyAll(); - }); - - it('should append mime types to find by ids', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/assets?ids=12,23&take=17&skip=13')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - total: 10, - items: [] - } - }) - ) - )) - .verifiable(Times.once()); - - let assets: AssetsDto | null = null; - - assetsService.getAssets('my-app', 17, 13, undefined, undefined, ['12', '23']).subscribe(result => { - assets = result; - }).unsubscribe(); - - authService.verifyAll(); - }); - - it('should make post request to create asset', () => { - progressHttp.setup(x => x.post('http://service/p/api/apps/my-app/assets', It.isAny(), It.isAny())) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - id: 'id1', - fileName: 'my-asset1.png', - fileSize: 1024, - fileVersion: 2, - mimeType: 'text/plain', - isImage: true, - pixelWidth: 1024, - pixelHeight: 2048, - version: 11 - } - }) - ) - )) - .verifiable(Times.once()); - - let asset: AssetCreatedDto | null = null; - - assetsService.uploadFile('my-app', null!).subscribe(result => { - asset = result; - }); - - expect(asset).toEqual( - new AssetCreatedDto( - 'id1', - 'my-asset1.png', - 1024, 2, - 'text/plain', - true, - 1024, - 2048, - new Version('11'))); - - authService.verifyAll(); - }); - - it('should make put request to replace asset content', () => { - progressHttp.setup(x => x.put('http://service/p/api/apps/my-app/assets/123/content', It.isAny(), It.isAny())) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - fileSize: 1024, - fileVersion: 2, - mimeType: 'text/plain', - isImage: true, - pixelWidth: 1024, - pixelHeight: 2048, - version: 11 - } - }) - ) - )) - .verifiable(Times.once()); - - let asset: AssetReplacedDto | null = null; - - assetsService.replaceFile('my-app', '123', null!, version).subscribe(result => { - asset = result; - }); - - expect(asset).toEqual( - new AssetReplacedDto( - 1024, 2, - 'text/plain', - true, - 1024, - 2048, - new Version('11'))); - - authService.verifyAll(); - }); - - it('should make put request to update asset', () => { - const dto = new UpdateAssetDto('My-Asset.pdf'); - - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/assets/123', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - assetsService.putAsset('my-app', '123', dto, version); - - authService.verifyAll(); - }); - - it('should make delete request to delete asset', () => { - authService.setup(x => x.authDelete('http://service/p/api/apps/my-app/assets/123', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - assetsService.deleteAsset('my-app', '123', version); - - authService.verifyAll(); - }); -}); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/assets.service.ts b/src/Squidex/app/shared/services/assets.service.ts index a48b64517..a2a4eca74 100644 --- a/src/Squidex/app/shared/services/assets.service.ts +++ b/src/Squidex/app/shared/services/assets.service.ts @@ -5,19 +5,17 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient, HttpEventType, HttpHeaders, HttpRequest, HttpResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Headers } from '@angular/http'; import { Observable } from 'rxjs'; -import { ProgressHttp } from 'angular-progress-http'; import { ApiUrlConfig, DateTime, + HTTP, Version } from 'framework'; -import { AuthService } from './auth.service'; - export class AssetsDto { constructor( public readonly total: number, @@ -83,9 +81,8 @@ export class AssetReplacedDto { @Injectable() export class AssetsService { constructor( - private readonly authService: AuthService, - private readonly apiUrl: ApiUrlConfig, - private readonly http: ProgressHttp + private readonly http: HttpClient, + private readonly apiUrl: ApiUrlConfig ) { } @@ -111,8 +108,7 @@ export class AssetsService { const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets?${fullQuery}`); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { const items: any[] = response.items; @@ -133,38 +129,40 @@ export class AssetsService { new Version(item.version.toString())); })); }) - .catchError('Failed to load assets. Please reload.'); + .pretifyError('Failed to load assets. Please reload.'); } public uploadFile(appName: string, file: File): Observable { return new Observable(subscriber => { const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets`); - const content = new FormData(); - const headers = new Headers({ - 'Authorization': `${this.authService.user!.user.token_type} ${this.authService.user!.user.access_token}` + const req = new HttpRequest('POST', url, getFormData(file), { + reportProgress: true }); - content.append('file', file); - - this.http.withUploadProgressListener(progress => subscriber.next(progress.percentage)) - .post(url, content, { headers }) - .map(response => response.json()) - .map(response => { - return new AssetCreatedDto( - response.id, - response.fileName, - response.fileSize, - response.fileVersion, - response.mimeType, - response.isImage, - response.pixelWidth, - response.pixelHeight, - new Version(response.version.toString())); - }) - .catchError('Failed to upload asset. Please reload.') - .subscribe(value => { - subscriber.next(value); + this.http.request(req) + .pretifyError('Failed to upload asset. Please reload.') + .subscribe(event => { + if (event.type === HttpEventType.UploadProgress) { + const percentDone = Math.round(100 * event.loaded / event.total); + + subscriber.next(percentDone); + } else if (event instanceof HttpResponse) { + const response = event.body; + + const dto = new AssetCreatedDto( + response.id, + response.fileName, + response.fileSize, + response.fileVersion, + response.mimeType, + response.isImage, + response.pixelWidth, + response.pixelHeight, + new Version(response.version.toString())); + + subscriber.next(dto); + } }, err => { subscriber.error(err); }, () => { @@ -176,7 +174,7 @@ export class AssetsService { public getAsset(appName: string, id: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/${id}`); - return this.authService.authGet(url) + return HTTP.getVersioned(this.http, url) .map(response => response.json()) .map(response => { return new AssetDto( @@ -194,40 +192,41 @@ export class AssetsService { response.pixelHeight, new Version(response.version.toString())); }) - .catchError('Failed to load assets. Please reload.'); + .pretifyError('Failed to load assets. Please reload.'); } public replaceFile(appName: string, id: string, file: File, version?: Version): Observable { return new Observable(subscriber => { const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/${id}/content`); - const content = new FormData(); - const headers = new Headers({ - 'Authorization': `${this.authService.user!.user.token_type} ${this.authService.user!.user.access_token}` + const req = new HttpRequest('POST', url, file, { + headers: new HttpHeaders({ + 'If-Match': version.value + }), + reportProgress: true }); - if (version && version.value.length > 0) { - headers.append('If-Match', version.value); - } - - content.append('file', file); - - this.http.withUploadProgressListener(progress => subscriber.next(progress.percentage)) - .put(url, content, { headers }) - .map(response => response.json()) - .map(response => { - return new AssetReplacedDto( - response.fileSize, - response.fileVersion, - response.mimeType, - response.isImage, - response.pixelWidth, - response.pixelHeight, - new Version(response.version.toString())); - }) - .catchError('Failed to replace asset. Please reload.') - .subscribe(value => { - subscriber.next(value); + this.http.request(req) + .pretifyError('Failed to replace asset. Please reload.') + .subscribe(event => { + if (event.type === HttpEventType.UploadProgress) { + const percentDone = Math.round(100 * event.loaded / event.total); + + subscriber.next(percentDone); + } else if (event instanceof HttpResponse) { + const response = event.body; + + const dto = new AssetReplacedDto( + response.fileSize, + response.fileVersion, + response.mimeType, + response.isImage, + response.pixelWidth, + response.pixelHeight, + new Version(response.version.toString())); + + subscriber.next(dto); + } }, err => { subscriber.error(err); }, () => { @@ -239,14 +238,22 @@ export class AssetsService { public putAsset(appName: string, id: string, dto: UpdateAssetDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/${id}`); - return this.authService.authPut(url, dto, version) - .catchError('Failed to delete asset. Please reload.'); + return HTTP.putVersioned(this.http, url, dto, version) + .pretifyError('Failed to delete asset. Please reload.'); } public deleteAsset(appName: string, id: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/${id}`); - return this.authService.authDelete(url, version) - .catchError('Failed to delete asset. Please reload.'); + return HTTP.deleteVersioned(this.http, url, version) + .pretifyError('Failed to delete asset. Please reload.'); } +} + +function getFormData(file: File) { + const formData = new FormData(); + + formData.append('file', file); + + return formData; } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/auth.service.ts b/src/Squidex/app/shared/services/auth.service.ts index 214b735d0..4d5616a20 100644 --- a/src/Squidex/app/shared/services/auth.service.ts +++ b/src/Squidex/app/shared/services/auth.service.ts @@ -6,7 +6,6 @@ */ import { Injectable } from '@angular/core'; -import { Headers, Http, RequestOptions, Response } from '@angular/http'; import { Observable, Subject } from 'rxjs'; import { @@ -15,7 +14,7 @@ import { UserManager } from 'oidc-client'; -import { ApiUrlConfig, Version } from 'framework'; +import { ApiUrlConfig } from 'framework'; export class Profile { public get id(): string { @@ -34,6 +33,14 @@ export class Profile { return this.user.profile['role'] && this.user.profile['role'].toUpperCase() === 'ADMINISTRATOR'; } + public get isExpired(): boolean { + return this.user.expired; + } + + public get authToken(): string { + return `${this.user.token_type} ${this.user.access_token}`; + } + public get token(): string { return `subject:${this.id}`; } @@ -65,9 +72,7 @@ export class AuthService { return this.isAuthenticatedChangedPublished$; } - constructor(apiUrl: ApiUrlConfig, - private readonly http: Http - ) { + constructor(apiUrl: ApiUrlConfig) { if (!apiUrl) { return; } @@ -166,78 +171,4 @@ export class AuthService { return resultPromise; } - - public authGet(url: string, version?: Version, options?: RequestOptions): Observable { - options = this.setRequestOptions(options, version); - - return this.checkResponse(this.http.get(url, options), version); - } - - public authPut(url: string, data: any, version?: Version, options?: RequestOptions): Observable { - options = this.setRequestOptions(options, version); - - return this.checkResponse(this.http.put(url, data, options), version); - } - - public authDelete(url: string, version?: Version, options?: RequestOptions): Observable { - options = this.setRequestOptions(options, version); - - return this.checkResponse(this.http.delete(url, options), version); - } - - public authPost(url: string, data: any, version?: Version, options?: RequestOptions): Observable { - options = this.setRequestOptions(options, version); - - return this.checkResponse(this.http.post(url, data, options), version); - } - - private checkResponse(responseStream: Observable, version?: Version) { - return responseStream - .do((response: Response) => { - if (version && response.status.toString().indexOf('2') === 0 && response.headers) { - const etag = response.headers.get('etag'); - - if (etag) { - version.update(etag); - } - } - }) - .catch((error: Response) => { - if (error.status === 404 && (!this.user || this.user.user.expired)) { - this.logoutRedirect(); - - return Observable.empty(); - } else if (error.status === 401 || error.status === 403) { - this.logoutRedirect(); - - return Observable.empty(); - } - return Observable.throw(error); - }); - } - - private setRequestOptions(options?: RequestOptions, version?: Version) { - if (!options) { - options = new RequestOptions(); - } - - if (!options.headers) { - options.headers = new Headers(); - options.headers.append('Content-Type', 'application/json'); - } - - options.headers.append('Accept-Language', '*'); - - if (version && version.value.length > 0) { - options.headers.append('If-Match', version.value); - } - - if (this.currentUser && this.currentUser.user) { - options.headers.append('Authorization', `${this.currentUser.user.token_type} ${this.currentUser.user.access_token}`); - } - - options.headers.append('Pragma', 'no-cache'); - - return options; - } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/contents.service.spec.ts b/src/Squidex/app/shared/services/contents.service.spec.ts deleted file mode 100644 index 728fddd98..000000000 --- a/src/Squidex/app/shared/services/contents.service.spec.ts +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { IMock, It, Mock, Times } from 'typemoq'; - -import { - ApiUrlConfig, - AuthService, - ContentDto, - ContentsDto, - ContentsService, - DateTime, - Version -} from './../'; - -describe('ContentsService', () => { - let authService: IMock; - let contentsService: ContentsService; - let version = new Version('1'); - - beforeEach(() => { - authService = Mock.ofType(AuthService); - contentsService = new ContentsService(authService.object, new ApiUrlConfig('http://service/p/')); - }); - - it('should make get request to get contents', () => { - authService.setup(x => x.authGet('http://service/p/api/content/my-app/my-schema?nonPublished=true&hidden=true&$top=17&$skip=13')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - total: 10, - items: [ - { - id: 'id1', - isPublished: true, - created: '2016-12-12T10:10', - createdBy: 'Created1', - lastModified: '2017-12-12T10:10', - lastModifiedBy: 'LastModifiedBy1', - version: 11, - data: {} - }, - { - id: 'id2', - isPublished: true, - created: '2016-10-12T10:10', - createdBy: 'Created2', - lastModified: '2017-10-12T10:10', - lastModifiedBy: 'LastModifiedBy2', - version: 22, - data: {} - } - ] - } - }) - ) - )) - .verifiable(Times.once()); - - let contents: ContentsDto | null = null; - - contentsService.getContents('my-app', 'my-schema', 17, 13).subscribe(result => { - contents = result; - }).unsubscribe(); - - expect(contents).toEqual( - new ContentsDto(10, [ - new ContentDto('id1', true, 'Created1', 'LastModifiedBy1', - DateTime.parseISO_UTC('2016-12-12T10:10'), - DateTime.parseISO_UTC('2017-12-12T10:10'), - {}, - new Version('11')), - new ContentDto('id2', true, 'Created2', 'LastModifiedBy2', - DateTime.parseISO_UTC('2016-10-12T10:10'), - DateTime.parseISO_UTC('2017-10-12T10:10'), - {}, - new Version('22')) - ])); - - authService.verifyAll(); - }); - - it('should append query to get request as search', () => { - authService.setup(x => x.authGet('http://service/p/api/content/my-app/my-schema?nonPublished=true&hidden=true&$search="my-query"&$top=17&$skip=13')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - total: 10, - items: [] - } - }) - ) - )) - .verifiable(Times.once()); - - let contents: ContentsDto | null = null; - - contentsService.getContents('my-app', 'my-schema', 17, 13, 'my-query').subscribe(result => { - contents = result; - }).unsubscribe(); - - authService.verifyAll(); - }); - - it('should append ids to get request with ids', () => { - authService.setup(x => x.authGet('http://service/p/api/content/my-app/my-schema?nonPublished=true&hidden=true&$top=17&$skip=13&ids=id1,id2')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - total: 10, - items: [] - } - }) - ) - )) - .verifiable(Times.once()); - - let contents: ContentsDto | null = null; - - contentsService.getContents('my-app', 'my-schema', 17, 13, null, ['id1', 'id2']).subscribe(result => { - contents = result; - }).unsubscribe(); - - authService.verifyAll(); - }); - - it('should append query to get request as plain query string', () => { - authService.setup(x => x.authGet('http://service/p/api/content/my-app/my-schema?nonPublished=true&hidden=true&$filter=my-filter&$top=17&$skip=13')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - total: 10, - items: [] - } - }) - ) - )) - .verifiable(Times.once()); - - let contents: ContentsDto | null = null; - - contentsService.getContents('my-app', 'my-schema', 17, 13, '$filter=my-filter').subscribe(result => { - contents = result; - }).unsubscribe(); - - authService.verifyAll(); - }); - - it('should make get request to get content', () => { - authService.setup(x => x.authGet('http://service/p/api/content/my-app/my-schema/content1?hidden=true', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - id: 'id1', - isPublished: true, - created: '2016-12-12T10:10', - createdBy: 'Created1', - lastModified: '2017-12-12T10:10', - lastModifiedBy: 'LastModifiedBy1', - version: 11, - data: {} - } - }) - ) - )) - .verifiable(Times.once()); - - let content: ContentDto | null = null; - - contentsService.getContent('my-app', 'my-schema', 'content1', version).subscribe(result => { - content = result; - }).unsubscribe(); - - expect(content).toEqual( - new ContentDto('id1', true, 'Created1', 'LastModifiedBy1', - DateTime.parseISO_UTC('2016-12-12T10:10'), - DateTime.parseISO_UTC('2017-12-12T10:10'), - {}, - new Version('11'))); - - authService.verifyAll(); - }); - - it('should make post request to create content', () => { - const dto = {}; - - authService.setup(x => x.authPost('http://service/p/api/content/my-app/my-schema?publish=true', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - id: 'id1', - isPublished: true, - created: '2016-12-12T10:10', - createdBy: 'Created1', - lastModified: '2017-12-12T10:10', - lastModifiedBy: 'LastModifiedBy1', - version: 11, - data: {} - } - }) - ) - )) - .verifiable(Times.once()); - - let content: ContentDto | null = null; - - contentsService.postContent('my-app', 'my-schema', dto, true, version).subscribe(result => { - content = result; - }); - - expect(content).toEqual( - new ContentDto('id1', true, 'Created1', 'LastModifiedBy1', - DateTime.parseISO_UTC('2016-12-12T10:10'), - DateTime.parseISO_UTC('2017-12-12T10:10'), - {}, - new Version('11'))); - - authService.verifyAll(); - }); - - it('should make put request to update content', () => { - const dto = {}; - - authService.setup(x => x.authPut('http://service/p/api/content/my-app/my-schema/content1', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - contentsService.putContent('my-app', 'my-schema', 'content1', dto, version); - - authService.verifyAll(); - }); - - it('should make put request to publish content', () => { - authService.setup(x => x.authPut('http://service/p/api/content/my-app/my-schema/content1/publish', It.isAny(), version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - contentsService.publishContent('my-app', 'my-schema', 'content1', version); - - authService.verifyAll(); - }); - - it('should make put request to unpublish content', () => { - authService.setup(x => x.authPut('http://service/p/api/content/my-app/my-schema/content1/unpublish', It.isAny(), version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - contentsService.unpublishContent('my-app', 'my-schema', 'content1', version); - - authService.verifyAll(); - }); - - it('should make delete request to delete content', () => { - authService.setup(x => x.authDelete('http://service/p/api/content/my-app/my-schema/content1', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - contentsService.deleteContent('my-app', 'my-schema', 'content1', version); - - authService.verifyAll(); - }); -}); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/contents.service.ts b/src/Squidex/app/shared/services/contents.service.ts index 47a86fc79..5ef750a0e 100644 --- a/src/Squidex/app/shared/services/contents.service.ts +++ b/src/Squidex/app/shared/services/contents.service.ts @@ -5,6 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; @@ -13,11 +14,10 @@ import 'framework/angular/http-extensions'; import { ApiUrlConfig, DateTime, + HTTP, Version } from 'framework'; -import { AuthService } from './auth.service'; - export class ContentsDto { constructor( public readonly total: number, @@ -43,7 +43,7 @@ export class ContentDto { @Injectable() export class ContentsService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -75,8 +75,7 @@ export class ContentsService { const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}?nonPublished=true&hidden=true${fullQuery}`); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { const items: any[] = response.items; @@ -92,14 +91,13 @@ export class ContentsService { new Version(item.version.toString())); })); }) - .catchError('Failed to load contents. Please reload.'); + .pretifyError('Failed to load contents. Please reload.'); } public getContent(appName: string, schemaName: string, id: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}?hidden=true`); - return this.authService.authGet(url, version) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url, version) .map(response => { return new ContentDto( response.id, @@ -111,14 +109,13 @@ export class ContentsService { response.data, new Version(response.version.toString())); }) - .catchError('Failed to load content. Please reload.'); + .pretifyError('Failed to load content. Please reload.'); } public postContent(appName: string, schemaName: string, dto: any, publish: boolean, version?: Version): Observable { const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}?publish=${publish}`); - return this.authService.authPost(url, dto, version) - .map(response => response.json()) + return HTTP.postVersioned(this.http, url, dto, version) .map(response => { return new ContentDto( response.id, @@ -130,34 +127,34 @@ export class ContentsService { response.data, new Version(response.version.toString())); }) - .catchError('Failed to create content. Please reload.'); + .pretifyError('Failed to create content. Please reload.'); } public putContent(appName: string, schemaName: string, id: string, dto: any, version?: Version): Observable { const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}`); - return this.authService.authPut(url, dto, version) - .catchError('Failed to update content. Please reload.'); + return HTTP.putVersioned(this.http, url, dto, version) + .pretifyError('Failed to update content. Please reload.'); } public publishContent(appName: string, schemaName: string, id: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/publish`); - return this.authService.authPut(url, {}, version) - .catchError('Failed to publish content. Please reload.'); + return HTTP.putVersioned(this.http, url, {}, version) + .pretifyError('Failed to publish content. Please reload.'); } public unpublishContent(appName: string, schemaName: string, id: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/unpublish`); - return this.authService.authPut(url, {}, version) - .catchError('Failed to unpublish content. Please reload.'); + return HTTP.putVersioned(this.http, url, {}, version) + .pretifyError('Failed to unpublish content. Please reload.'); } public deleteContent(appName: string, schemaName: string, id: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}`); - return this.authService.authDelete(url, version) - .catchError('Failed to delete content. Please reload.'); + return HTTP.deleteVersioned(this.http, url, version) + .pretifyError('Failed to delete content. Please reload.'); } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/event-consumers.service.spec.ts b/src/Squidex/app/shared/services/event-consumers.service.spec.ts deleted file mode 100644 index f6b875fd6..000000000 --- a/src/Squidex/app/shared/services/event-consumers.service.spec.ts +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { IMock, It, Mock, Times } from 'typemoq'; - -import { - ApiUrlConfig, - AuthService, - EventConsumerDto, - EventConsumersService -} from './../'; - -describe('EventConsumersService', () => { - let authService: IMock; - let eventConsumersService: EventConsumersService; - - beforeEach(() => { - authService = Mock.ofType(AuthService); - eventConsumersService = new EventConsumersService(authService.object, new ApiUrlConfig('http://service/p/')); - }); - - it('should make get request to get event consumers', () => { - authService.setup(x => x.authGet('http://service/p/api/event-consumers')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: [ - { - name: 'event-consumer1', - position: '13', - isStopped: true, - isResetting: true, - error: 'an error 1' - }, - { - name: 'event-consumer2', - position: '29', - isStopped: true, - isResetting: true, - error: 'an error 2' - } - ] - }) - ) - )) - .verifiable(Times.once()); - - let eventConsumers: EventConsumerDto[] | null = null; - - eventConsumersService.getEventConsumers().subscribe(result => { - eventConsumers = result; - }).unsubscribe(); - - expect(eventConsumers).toEqual([ - new EventConsumerDto('event-consumer1', true, true, 'an error 1', '13'), - new EventConsumerDto('event-consumer2', true, true, 'an error 2', '29') - ]); - - authService.verifyAll(); - }); - - it('should make put request to start event consumer', () => { - authService.setup(x => x.authPut('http://service/p/api/event-consumers/event-consumer1/start', It.isAny())) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - eventConsumersService.startEventConsumer('event-consumer1'); - - authService.verifyAll(); - }); - - it('should make put request to stop event consumer', () => { - authService.setup(x => x.authPut('http://service/p/api/event-consumers/event-consumer1/stop', It.isAny())) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - eventConsumersService.stopEventConsumer('event-consumer1'); - - authService.verifyAll(); - }); - - it('should make put request to reset event consumer', () => { - authService.setup(x => x.authPut('http://service/p/api/event-consumers/event-consumer1/reset', It.isAny())) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - eventConsumersService.resetEventConsumer('event-consumer1'); - - authService.verifyAll(); - }); -}); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/event-consumers.service.ts b/src/Squidex/app/shared/services/event-consumers.service.ts index 1a7adb36c..65c98f1aa 100644 --- a/src/Squidex/app/shared/services/event-consumers.service.ts +++ b/src/Squidex/app/shared/services/event-consumers.service.ts @@ -5,13 +5,13 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import 'framework/angular/http-extensions'; -import { ApiUrlConfig } from 'framework'; -import { AuthService } from './auth.service'; +import { ApiUrlConfig, HTTP } from 'framework'; export class EventConsumerDto { constructor( @@ -27,7 +27,7 @@ export class EventConsumerDto { @Injectable() export class EventConsumersService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -35,8 +35,7 @@ export class EventConsumersService { public getEventConsumers(): Observable { const url = this.apiUrl.buildUrl('/api/event-consumers'); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { const items: any[] = response; @@ -49,27 +48,27 @@ export class EventConsumersService { item.position); }); }) - .catchError('Failed to load event consumers. Please reload.'); + .pretifyError('Failed to load event consumers. Please reload.'); } public startEventConsumer(name: string): Observable { const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/start`); - return this.authService.authPut(url, {}) - .catchError('Failed to start event consumer. Please reload.'); + return HTTP.putVersioned(this.http, url, {}) + .pretifyError('Failed to start event consumer. Please reload.'); } public stopEventConsumer(name: string): Observable { const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/stop`); - return this.authService.authPut(url, {}) - .catchError('Failed to stop event consumer. Please reload.'); + return HTTP.putVersioned(this.http, url, {}) + .pretifyError('Failed to stop event consumer. Please reload.'); } public resetEventConsumer(name: string): Observable { const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/reset`); - return this.authService.authPut(url, {}) - .catchError('Failed to reset event consumer. Please reload.'); + return HTTP.putVersioned(this.http, url, {}) + .pretifyError('Failed to reset event consumer. Please reload.'); } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/help.service.spec.ts b/src/Squidex/app/shared/services/help.service.spec.ts deleted file mode 100644 index 3f3768cdd..000000000 --- a/src/Squidex/app/shared/services/help.service.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Http, Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { IMock, Mock, Times } from 'typemoq'; - -import { HelpService } from './../'; - -describe('AppClientsService', () => { - let helpService: HelpService; - let http: IMock; - - beforeEach(() => { - http = Mock.ofType(Http); - - helpService = new HelpService(http.object); - }); - - it('should make get request to get help sections', () => { - http.setup(x => x.get('https://api.gitbook.com/book/squidex/squidex/contents/01-chapter/02-article.json')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - sections: [ - { - content: 'A test content with' - }, - { - content: 'A test content with a A Link' - }, - { - content: 'A test content with a Glossary Link' - } - ] - } - }) - ) - )) - .verifiable(Times.once()); - - let helpSections: string[] | null = null; - - helpService.getHelp('01-chapter/02-article').subscribe(result => { - helpSections = result; - }); - - expect(helpSections).toEqual([ - 'A test content with', - 'A test content with a A Link', - 'A test content with a Glossary Link' - ]); - - http.verifyAll(); - }); - - it('should return empty sections if get request fails', () => { - http.setup(x => x.get('https://api.gitbook.com/book/squidex/squidex/contents/01-chapter/02-article.json')) - .returns(() => Observable.throw('An error')) - .verifiable(Times.once()); - - let helpSections: string[] | null = null; - - helpService.getHelp('01-chapter/02-article').subscribe(result => { - helpSections = result; - }); - - expect(helpSections).toEqual([]); - - http.verifyAll(); - }); -}); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/help.service.ts b/src/Squidex/app/shared/services/help.service.ts index e0e0c12aa..8f94ef66b 100644 --- a/src/Squidex/app/shared/services/help.service.ts +++ b/src/Squidex/app/shared/services/help.service.ts @@ -5,14 +5,14 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Http } from '@angular/http'; import { Observable } from 'rxjs'; @Injectable() export class HelpService { constructor( - private readonly http: Http + private readonly http: HttpClient ) { } @@ -20,8 +20,7 @@ export class HelpService { const url = `https://api.gitbook.com/book/squidex/squidex/contents/${helpPage}.json`; return this.http.get(url) - .map(response => response.json()) - .map(response => { + .map((response: any) => { const result: string[] = []; for (let section of response.sections) { diff --git a/src/Squidex/app/shared/services/history.service.spec.ts b/src/Squidex/app/shared/services/history.service.spec.ts deleted file mode 100644 index dd353d9f5..000000000 --- a/src/Squidex/app/shared/services/history.service.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { IMock, Mock, Times } from 'typemoq'; - -import { DateTime } from 'framework'; - -import { - ApiUrlConfig, - AuthService, - HistoryEventDto, - HistoryService -} from './../'; - -describe('HistoryService', () => { - let authService: IMock; - let languageService: HistoryService; - - beforeEach(() => { - authService = Mock.ofType(AuthService); - languageService = new HistoryService(authService.object, new ApiUrlConfig('http://service/p/')); - }); - - it('should make get request to get history events', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/history?channel=settings.contributors')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: [ - { - actor: 'User1', - eventId: '1', - message: 'Message 1', - created: '2016-12-12T10:10' - }, - { - actor: 'User2', - eventId: '2', - message: 'Message 2', - created: '2016-12-13T10:10' - } - ] - }) - ) - )) - .verifiable(Times.once()); - - let events: HistoryEventDto[] | null = null; - - languageService.getHistory('my-app', 'settings.contributors').subscribe(result => { - events = result; - }).unsubscribe(); - - expect(events).toEqual( - [ - new HistoryEventDto('1', 'User1', 'Message 1', DateTime.parseISO_UTC('2016-12-12T10:10')), - new HistoryEventDto('2', 'User2', 'Message 2', DateTime.parseISO_UTC('2016-12-13T10:10')) - ]); - - authService.verifyAll(); - }); -}); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/history.service.ts b/src/Squidex/app/shared/services/history.service.ts index 7214f513c..c27ba997e 100644 --- a/src/Squidex/app/shared/services/history.service.ts +++ b/src/Squidex/app/shared/services/history.service.ts @@ -5,13 +5,17 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import 'framework/angular/http-extensions'; -import { ApiUrlConfig, DateTime } from 'framework'; -import { AuthService } from './auth.service'; +import { + ApiUrlConfig, + HTTP, + DateTime +} from 'framework'; export class HistoryEventDto { constructor( @@ -26,7 +30,7 @@ export class HistoryEventDto { @Injectable() export class HistoryService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -34,8 +38,7 @@ export class HistoryService { public getHistory(appName: string, channel: string): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/history?channel=${channel}`); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { const items: any[] = response; @@ -47,6 +50,6 @@ export class HistoryService { DateTime.parseISO_UTC(item.created)); }); }) - .catchError('Failed to load history. Please reload.'); + .pretifyError('Failed to load history. Please reload.'); } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/languages.service.spec.ts b/src/Squidex/app/shared/services/languages.service.spec.ts deleted file mode 100644 index 471d7e29c..000000000 --- a/src/Squidex/app/shared/services/languages.service.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { IMock, Mock, Times } from 'typemoq'; - -import { - ApiUrlConfig, - AuthService, - LanguageDto, - LanguageService -} from './../'; - -describe('LanguageService', () => { - let authService: IMock; - let languageService: LanguageService; - - beforeEach(() => { - authService = Mock.ofType(AuthService); - languageService = new LanguageService(authService.object, new ApiUrlConfig('http://service/p/')); - }); - - it('should make get request to get languages', () => { - authService.setup(x => x.authGet('http://service/p/api/languages')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: [ - { - iso2Code: 'de', - englishName: 'German' - }, - { - iso2Code: 'en', - englishName: 'English' - } - ] - }) - ) - )) - .verifiable(Times.once()); - - let languages: LanguageDto[] | null = null; - - languageService.getLanguages().subscribe(result => { - languages = result; - }).unsubscribe(); - - expect(languages).toEqual( - [ - new LanguageDto('de', 'German'), - new LanguageDto('en', 'English') - ]); - - authService.verifyAll(); - }); -}); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/languages.service.ts b/src/Squidex/app/shared/services/languages.service.ts index 08f6c8c91..f4cdfb3e5 100644 --- a/src/Squidex/app/shared/services/languages.service.ts +++ b/src/Squidex/app/shared/services/languages.service.ts @@ -5,13 +5,13 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import 'framework/angular/http-extensions'; -import { ApiUrlConfig } from 'framework'; -import { AuthService } from './auth.service'; +import { ApiUrlConfig, HTTP } from 'framework'; export class LanguageDto { constructor( @@ -24,7 +24,7 @@ export class LanguageDto { @Injectable() export class LanguageService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -32,8 +32,7 @@ export class LanguageService { public getLanguages(): Observable { const url = this.apiUrl.buildUrl('api/languages'); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { const items: any[] = response; @@ -43,6 +42,6 @@ export class LanguageService { item.englishName); }); }) - .catchError('Failed to load languages. Please reload.'); + .pretifyError('Failed to load languages. Please reload.'); } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/plans.service.spec.ts b/src/Squidex/app/shared/services/plans.service.spec.ts deleted file mode 100644 index 388b0b508..000000000 --- a/src/Squidex/app/shared/services/plans.service.spec.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { IMock, Mock, Times } from 'typemoq'; - -import { - AppPlansDto, - ApiUrlConfig, - AuthService, - ChangePlanDto, - PlanDto, - PlansService, - Version -} from './../'; - -describe('PlansService', () => { - let authService: IMock; - let plansService: PlansService; - let version = new Version('1'); - - beforeEach(() => { - authService = Mock.ofType(AuthService); - plansService = new PlansService(authService.object, new ApiUrlConfig('http://service/p/')); - }); - - it('should make get request to get app plans', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/plans', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - currentPlanId: '123', - hasConfigured: true, - hasPortal: true, - planOwner: '456', - plans: [{ - id: 'free', - name: 'Free', - costs: '14 €', - maxApiCalls: 1000, - maxAssetSize: 1500, - maxContributors: 2500 - }, { - id: 'prof', - name: 'Prof', - costs: '18 €', - maxApiCalls: 4000, - maxAssetSize: 5500, - maxContributors: 6500 - }] - } - }) - ) - )) - .verifiable(Times.once()); - - let plans: AppPlansDto | null = null; - - plansService.getPlans('my-app', version).subscribe(result => { - plans = result; - }).unsubscribe(); - - expect(plans).toEqual( - new AppPlansDto( - '123', - '456', - true, - true, - [ - new PlanDto('free', 'Free', '14 €', 1000, 1500, 2500), - new PlanDto('prof', 'Prof', '18 €', 4000, 5500, 6500) - ] - )); - - authService.verifyAll(); - }); - - it('should make put request to change plan', () => { - const dto = new ChangePlanDto('enterprise'); - - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/plan', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - plansService.putPlan('my-app', dto, version); - - authService.verifyAll(); - }); -}); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/plans.service.ts b/src/Squidex/app/shared/services/plans.service.ts index eb53552c9..43da39c2c 100644 --- a/src/Squidex/app/shared/services/plans.service.ts +++ b/src/Squidex/app/shared/services/plans.service.ts @@ -5,13 +5,17 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import 'framework/angular/http-extensions'; -import { ApiUrlConfig, Version } from 'framework'; -import { AuthService } from './auth.service'; +import { + ApiUrlConfig, + HTTP, + Version +} from 'framework'; export class AppPlansDto { constructor( @@ -46,7 +50,7 @@ export class ChangePlanDto { @Injectable() export class PlansService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -54,8 +58,7 @@ export class PlansService { public getPlans(appName: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/plans`); - return this.authService.authGet(url, version) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url, version) .map(response => { const items: any[] = response.plans; @@ -74,13 +77,13 @@ export class PlansService { item.maxContributors); })); }) - .catchError('Failed to load plans. Please reload.'); + .pretifyError('Failed to load plans. Please reload.'); } public putPlan(appName: string, dto: ChangePlanDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/plan`); - return this.authService.authPut(url, dto, version) - .catchError('Failed to change plan. Please reload.'); + return HTTP.putVersioned(this.http, url, dto, version) + .pretifyError('Failed to change plan. Please reload.'); } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/schemas.fields.spec.ts b/src/Squidex/app/shared/services/schemas.fields.spec.ts deleted file mode 100644 index 92bf4a9d4..000000000 --- a/src/Squidex/app/shared/services/schemas.fields.spec.ts +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { - AssetsFieldPropertiesDto, - BooleanFieldPropertiesDto, - DateTimeFieldPropertiesDto, - FieldDto, - FieldPropertiesDto, - GeolocationFieldPropertiesDto, - JsonFieldPropertiesDto, - NumberFieldPropertiesDto, - ReferencesFieldPropertiesDto, - StringFieldPropertiesDto -} from './../'; - -describe('AssetsField', () => { - const field = createField(new AssetsFieldPropertiesDto(null, null, null, true, false, 1, 1)); - - it('should create validators', () => { - expect(field.createValidators().length).toBe(3); - }); - - it('should format to empty string if null', () => { - expect(field.formatValue(null)).toBe(''); - }); - - it('should format to asset count', () => { - expect(field.formatValue([1, 2, 3])).toBe('3 Asset(s)'); - }); - - it('should return zero formatting if other type', () => { - expect(field.formatValue(1)).toBe('0 Assets'); - }); -}); - -describe('BooleanField', () => { - const field = createField(new BooleanFieldPropertiesDto(null, null, null, true, false, 'Checkbox')); - - it('should create validators', () => { - expect(field.createValidators().length).toBe(1); - }); - - it('should format to empty string if null', () => { - expect(field.formatValue(null)).toBe(''); - }); - - it('should format to checkmark if true', () => { - expect(field.formatValue(true)).toBe('✔'); - }); - - it('should format to minus if false', () => { - expect(field.formatValue(false)).toBe('-'); - }); -}); - -describe('DateTimeField', () => { - const field = createField(new DateTimeFieldPropertiesDto(null, null, null, true, false, 'Date')); - - it('should create validators', () => { - expect(field.createValidators().length).toBe(1); - }); - - it('should format to empty string if null', () => { - expect(field.formatValue(null)).toBe(''); - }); - - it('should format to input if parsing failed', () => { - expect(field.formatValue(true)).toBe(true); - }); - - it('should format to date', () => { - const dateField = createField(new DateTimeFieldPropertiesDto(null, null, null, true, false, 'Date')); - - expect(dateField.formatValue('2017-12-12T16:00:00Z')).toBe('2017-12-12'); - }); - - it('should format to date', () => { - const dateTimeField = createField(new DateTimeFieldPropertiesDto(null, null, null, true, false, 'DateTime')); - - expect(dateTimeField.formatValue('2017-12-12T16:00:00Z').substr(0, 10)).toBe('2017-12-12'); - }); -}); - -describe('GeolocationField', () => { - const field = createField(new GeolocationFieldPropertiesDto(null, null, null, true, false, 'Default')); - - it('should create validators', () => { - expect(field.createValidators().length).toBe(1); - }); - - it('should format to empty string if null', () => { - expect(field.formatValue(null)).toBe(''); - }); - - it('should format to latitude and longitude', () => { - expect(field.formatValue({ latitude: 42, longitude: 3.14 })).toBe('3.14, 42'); - }); -}); - -describe('JsonField', () => { - const field = createField(new JsonFieldPropertiesDto(null, null, null, true, false)); - - it('should create validators', () => { - expect(field.createValidators().length).toBe(1); - }); - - it('should format to empty string if null', () => { - expect(field.formatValue(null)).toBe(''); - }); - - it('should format to constant', () => { - expect(field.formatValue({})).toBe(''); - }); -}); - -describe('NumberField', () => { - const field = createField(new NumberFieldPropertiesDto(null, null, null, true, false, 'Input', undefined, 3, 1, [1, 2, 3])); - - it('should create validators', () => { - expect(field.createValidators().length).toBe(4); - }); - - it('should format to empty string if null', () => { - expect(field.formatValue(null)).toBe(''); - }); - - it('should format to number', () => { - expect(field.formatValue(42)).toBe(42); - }); -}); - -describe('ReferencesField', () => { - const field = createField(new ReferencesFieldPropertiesDto(null, null, null, true, false, 1, 1)); - - it('should create validators', () => { - expect(field.createValidators().length).toBe(3); - }); - - it('should format to empty string if null', () => { - expect(field.formatValue(null)).toBe(''); - }); - - it('should format to asset count', () => { - expect(field.formatValue([1, 2, 3])).toBe('3 Reference(s)'); - }); - - it('should return zero formatting if other type', () => { - expect(field.formatValue(1)).toBe('0 References'); - }); -}); - -describe('NumberField', () => { - const field = createField(new StringFieldPropertiesDto(null, null, null, true, false, 'Input', undefined, 'pattern', undefined, 3, 1, ['1', '2'])); - - it('should create validators', () => { - expect(field.createValidators().length).toBe(5); - }); - - it('should format to empty string if null', () => { - expect(field.formatValue(null)).toBe(''); - }); - - it('should format to string', () => { - expect(field.formatValue('hello')).toBe('hello'); - }); -}); - -function createField(properties: FieldPropertiesDto) { - return new FieldDto(1, 'field1', false, false, 'languages', properties); -} \ No newline at end of file diff --git a/src/Squidex/app/shared/services/schemas.service.spec.ts b/src/Squidex/app/shared/services/schemas.service.spec.ts deleted file mode 100644 index 16f450b57..000000000 --- a/src/Squidex/app/shared/services/schemas.service.spec.ts +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { It, IMock, Mock, Times } from 'typemoq'; - -import { - AddFieldDto, - ApiUrlConfig, - AuthService, - CreateSchemaDto, - createProperties, - DateTime, - EntityCreatedDto, - FieldDto, - SchemaDetailsDto, - SchemaDto, - SchemaPropertiesDto, - SchemasService, - UpdateFieldDto, - UpdateSchemaDto, - Version -} from './../'; - -describe('SchemasService', () => { - let authService: IMock; - let schemasService: SchemasService; - let version = new Version('1'); - - beforeEach(() => { - authService = Mock.ofType(AuthService); - schemasService = new SchemasService(authService.object, new ApiUrlConfig('http://service/p/')); - }); - - it('should throw if creating invalid property type', () => { - expect(() => createProperties('invalid')).toThrow('Invalid properties type'); - }); - - it('should make get request to get schemas', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/schemas')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: [ - { - id: 'id1', - name: 'name1', - properties: { - label: 'label1', - hints: 'hints1' - }, - isPublished: true, - created: '2016-12-12T10:10', - createdBy: 'Created1', - lastModified: '2017-12-12T10:10', - lastModifiedBy: 'LastModifiedBy1', - version: 11, - data: {} - }, - { - id: 'id2', - name: 'name2', - properties: { - label: 'label2', - hints: 'hints2' - }, - isPublished: true, - created: '2016-10-12T10:10', - createdBy: 'Created2', - lastModified: '2017-10-12T10:10', - lastModifiedBy: 'LastModifiedBy2', - version: 22, - data: {} - } - ] - }) - ) - )) - .verifiable(Times.once()); - - let schemas: SchemaDto[] | null = null; - - schemasService.getSchemas('my-app').subscribe(result => { - schemas = result; - }).unsubscribe(); - - expect(schemas).toEqual([ - new SchemaDto('id1', 'name1', new SchemaPropertiesDto('label1', 'hints1'), true, 'Created1', 'LastModifiedBy1', - DateTime.parseISO_UTC('2016-12-12T10:10'), - DateTime.parseISO_UTC('2017-12-12T10:10'), - new Version('11')), - new SchemaDto('id2', 'name2', new SchemaPropertiesDto('label2', 'hints2'), true, 'Created2', 'LastModifiedBy2', - DateTime.parseISO_UTC('2016-10-12T10:10'), - DateTime.parseISO_UTC('2017-10-12T10:10'), - new Version('22')) - ]); - - authService.verifyAll(); - }); - - it('should make get request to get schema', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/schemas/my-schema')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - id: 'id1', - name: 'name1', - properties: { - label: 'label1', - hints: 'hints1' - }, - isPublished: true, - created: '2016-12-12T10:10', - createdBy: 'Created1', - lastModified: '2017-12-12T10:10', - lastModifiedBy: 'LastModifiedBy1', - version: 11, - fields: [ - { - fieldId: 1, - name: 'field1', - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'Number' - } - }, - { - fieldId: 2, - name: 'field2', - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'String' - } - }, - { - fieldId: 3, - name: 'field3', - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'Boolean' - } - }, - { - fieldId: 4, - name: 'field4', - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'DateTime' - } - }, - { - fieldId: 5, - name: 'field5', - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'Json' - } - }, - { - fieldId: 6, - name: 'field6', - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'Geolocation' - } - }, - { - fieldId: 7, - name: 'field7', - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'Assets' - } - }, - { - fieldId: 8, - name: 'field8', - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'References' - } - } - ] - } - }) - ) - )) - .verifiable(Times.once()); - - let schema: SchemaDetailsDto | null = null; - - schemasService.getSchema('my-app', 'my-schema', version).subscribe(result => { - schema = result; - }).unsubscribe(); - - expect(schema).toEqual( - new SchemaDetailsDto('id1', 'name1', new SchemaPropertiesDto('label1', 'hints1'), true, 'Created1', 'LastModifiedBy1', - DateTime.parseISO_UTC('2016-12-12T10:10'), - DateTime.parseISO_UTC('2017-12-12T10:10'), - new Version('11'), - [ - new FieldDto(1, 'field1', true, true, 'language', createProperties('Number')), - new FieldDto(2, 'field2', true, true, 'language', createProperties('String')), - new FieldDto(3, 'field3', true, true, 'language', createProperties('Boolean')), - new FieldDto(4, 'field4', true, true, 'language', createProperties('DateTime')), - new FieldDto(5, 'field5', true, true, 'language', createProperties('Json')), - new FieldDto(6, 'field6', true, true, 'language', createProperties('Geolocation')), - new FieldDto(7, 'field7', true, true, 'language', createProperties('Assets')), - new FieldDto(8, 'field8', true, true, 'language', createProperties('References')) - ])); - - authService.verifyAll(); - }); - - it('should make post request to create schema', () => { - const dto = new CreateSchemaDto('name'); - - authService.setup(x => x.authPost('http://service/p/api/apps/my-app/schemas', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - id: 'my-schema' - } - }) - ) - )) - .verifiable(Times.once()); - - let created: EntityCreatedDto | null = null; - - schemasService.postSchema('my-app', dto, version).subscribe(result => { - created = result; - }); - - expect(created).toEqual( - new EntityCreatedDto('my-schema')); - - authService.verifyAll(); - }); - - it('should make post request to add field', () => { - const dto = new AddFieldDto('name', 'invariant', createProperties('Number')); - - authService.setup(x => x.authPost('http://service/p/api/apps/my-app/schemas/my-schema/fields', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - id: 123 - } - }) - ) - )) - .verifiable(Times.once()); - - let created: EntityCreatedDto | null = null; - - schemasService.postField('my-app', 'my-schema', dto, version).subscribe(result => { - created = result; - }); - - expect(created).toEqual( - new EntityCreatedDto(123)); - - authService.verifyAll(); - }); - - it('should make put request to update schema', () => { - const dto = new UpdateSchemaDto('label', 'hints'); - - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/schemas/my-schema', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - schemasService.putSchema('my-app', 'my-schema', dto, version); - - authService.verifyAll(); - }); - - it('should make put request to update field', () => { - const dto = new UpdateFieldDto(createProperties('Number')); - - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/schemas/my-schema/fields/1', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - schemasService.putField('my-app', 'my-schema', 1, dto, version); - - authService.verifyAll(); - }); - - it('should make put request to update field ordering', () => { - const dto = [1, 2, 3]; - - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/schemas/my-schema/fields/ordering', It.isAny(), version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - schemasService.putFieldOrdering('my-app', 'my-schema', dto, version); - - authService.verifyAll(); - }); - - it('should make put request to publish schema', () => { - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/schemas/my-schema/publish', It.isAny(), version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - schemasService.publishSchema('my-app', 'my-schema', version); - - authService.verifyAll(); - }); - - it('should make put request to unpublish schema', () => { - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/schemas/my-schema/unpublish', It.isAny(), version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - schemasService.unpublishSchema('my-app', 'my-schema', version); - - authService.verifyAll(); - }); - - it('should make put request to enable field', () => { - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/enable', It.isAny(), version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - schemasService.enableField('my-app', 'my-schema', 1, version); - - authService.verifyAll(); - }); - - it('should make put request to disable field', () => { - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/disable', It.isAny(), version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - schemasService.disableField('my-app', 'my-schema', 1, version); - - authService.verifyAll(); - }); - - it('should make put request to show field', () => { - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/show', It.isAny(), version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - schemasService.showField('my-app', 'my-schema', 1, version); - - authService.verifyAll(); - }); - - it('should make put request to hide field', () => { - authService.setup(x => x.authPut('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/hide', It.isAny(), version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - schemasService.hideField('my-app', 'my-schema', 1, version); - - authService.verifyAll(); - }); - - it('should make delete request to delete field', () => { - authService.setup(x => x.authDelete('http://service/p/api/apps/my-app/schemas/my-schema/fields/1', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - schemasService.deleteField('my-app', 'my-schema', 1, version); - - authService.verifyAll(); - }); - - it('should make delete request to delete schema', () => { - authService.setup(x => x.authDelete('http://service/p/api/apps/my-app/schemas/my-schema', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - schemasService.deleteSchema('my-app', 'my-schema', version); - - authService.verifyAll(); - }); -}); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/schemas.service.ts b/src/Squidex/app/shared/services/schemas.service.ts index 7e9cc1ee0..35494b4a5 100644 --- a/src/Squidex/app/shared/services/schemas.service.ts +++ b/src/Squidex/app/shared/services/schemas.service.ts @@ -5,6 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { ValidatorFn, Validators } from '@angular/forms'; import { Observable } from 'rxjs'; @@ -15,12 +16,11 @@ import { ApiUrlConfig, DateTime, EntityCreatedDto, + HTTP, ValidatorsEx, Version } from 'framework'; -import { AuthService } from './auth.service'; - export const fieldTypes: string[] = [ 'Assets', 'Boolean', @@ -483,7 +483,7 @@ export class CreateSchemaDto { @Injectable() export class SchemasService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -491,8 +491,7 @@ export class SchemasService { public getSchemas(appName: string): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { const items: any[] = response; @@ -510,14 +509,13 @@ export class SchemasService { new Version(item.version.toString())); }); }) - .catchError('Failed to load schemas. Please reload.'); + .pretifyError('Failed to load schemas. Please reload.'); } public getSchema(appName: string, id: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${id}`); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { const fields = response.fields.map((item: any) => { const propertiesDto = @@ -547,105 +545,103 @@ export class SchemasService { new Version(response.version.toString()), fields); }) - .catchError('Failed to load schema. Please reload.'); + .pretifyError('Failed to load schema. Please reload.'); } public postSchema(appName: string, dto: CreateSchemaDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`); - return this.authService.authPost(url, dto, version) - .map(response => response.json()) + return HTTP.postVersioned(this.http, url, dto, version) .map(response => { return new EntityCreatedDto(response.id); }) - .catchError('Failed to create schema. Please reload.'); + .pretifyError('Failed to create schema. Please reload.'); } public postField(appName: string, schemaName: string, dto: AddFieldDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields`); - return this.authService.authPost(url, dto, version) - .map(response => response.json()) + return HTTP.postVersioned(this.http, url, dto, version) .map(response => { return new EntityCreatedDto(response.id); }) - .catchError('Failed to add field. Please reload.'); + .pretifyError('Failed to add field. Please reload.'); } public putSchema(appName: string, schemaName: string, dto: UpdateSchemaDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`); - return this.authService.authPut(url, dto, version) - .catchError('Failed to update schema. Please reload.'); + return HTTP.putVersioned(this.http, url, dto, version) + .pretifyError('Failed to update schema. Please reload.'); } public putFieldOrdering(appName: string, schemaName: string, dto: number[], version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/ordering`); - return this.authService.authPut(url, { fieldIds: dto }, version) - .catchError('Failed to reorder fields. Please reload.'); + return HTTP.putVersioned(this.http, url, { fieldIds: dto }, version) + .pretifyError('Failed to reorder fields. Please reload.'); } public publishSchema(appName: string, schemaName: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/publish`); - return this.authService.authPut(url, {}, version) - .catchError('Failed to publish schema. Please reload.'); + return HTTP.putVersioned(this.http, url, {}, version) + .pretifyError('Failed to publish schema. Please reload.'); } public unpublishSchema(appName: string, schemaName: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/unpublish`); - return this.authService.authPut(url, {}, version) - .catchError('Failed to unpublish schema. Please reload.'); + return HTTP.putVersioned(this.http, url, {}, version) + .pretifyError('Failed to unpublish schema. Please reload.'); } public putField(appName: string, schemaName: string, fieldId: number, dto: UpdateFieldDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}`); - return this.authService.authPut(url, dto, version) - .catchError('Failed to update field. Please reload.'); + return HTTP.putVersioned(this.http, url, dto, version) + .pretifyError('Failed to update field. Please reload.'); } public enableField(appName: string, schemaName: string, fieldId: number, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/enable`); - return this.authService.authPut(url, {}, version) - .catchError('Failed to enable field. Please reload.'); + return HTTP.putVersioned(this.http, url, {}, version) + .pretifyError('Failed to enable field. Please reload.'); } public disableField(appName: string, schemaName: string, fieldId: number, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/disable`); - return this.authService.authPut(url, {}, version) - .catchError('Failed to disable field. Please reload.'); + return HTTP.putVersioned(this.http, url, {}, version) + .pretifyError('Failed to disable field. Please reload.'); } public showField(appName: string, schemaName: string, fieldId: number, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/show`); - return this.authService.authPut(url, {}, version) - .catchError('Failed to show field. Please reload.'); + return HTTP.putVersioned(this.http, url, {}, version) + .pretifyError('Failed to show field. Please reload.'); } public hideField(appName: string, schemaName: string, fieldId: number, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/hide`); - return this.authService.authPut(url, {}, version) - .catchError('Failed to hide field. Please reload.'); + return HTTP.putVersioned(this.http, url, {}, version) + .pretifyError('Failed to hide field. Please reload.'); } public deleteField(appName: string, schemaName: string, fieldId: number, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}`); - return this.authService.authDelete(url, version) - .catchError('Failed to delete field. Please reload.'); + return HTTP.deleteVersioned(this.http, url, version) + .pretifyError('Failed to delete field. Please reload.'); } public deleteSchema(appName: string, schemaName: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`); - return this.authService.authDelete(url, version) - .catchError('Failed to delete schema. Please reload.'); + return HTTP.deleteVersioned(this.http, url, version) + .pretifyError('Failed to delete schema. Please reload.'); } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/usages.service.spec.ts b/src/Squidex/app/shared/services/usages.service.spec.ts deleted file mode 100644 index 4d908d5e9..000000000 --- a/src/Squidex/app/shared/services/usages.service.spec.ts +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { IMock, Mock, Times } from 'typemoq'; - -import { - ApiUrlConfig, - AuthService, - CallsUsageDto, - CurrentCallsDto, - CurrentStorageDto, - DateTime, - StorageUsageDto, - UsagesService -} from './../'; - -describe('UsagesService', () => { - let authService: IMock; - let usagesService: UsagesService; - - beforeEach(() => { - authService = Mock.ofType(AuthService); - usagesService = new UsagesService(authService.object, new ApiUrlConfig('http://service/p/')); - }); - - it('should make get request to get calls usages', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/usages/calls/2017-10-12/2017-10-13')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: [ - { - date: '2017-10-12', - count: 10, - averageMs: 130 - }, - { - date: '2017-10-13', - count: 13, - averageMs: 170 - } - ] - }) - ) - )) - .verifiable(Times.once()); - - let usages: CallsUsageDto[] | null = null; - - usagesService.getCallsUsages('my-app', DateTime.parseISO_UTC('2017-10-12'), DateTime.parseISO_UTC('2017-10-13')).subscribe(result => { - usages = result; - }).unsubscribe(); - - expect(usages).toEqual( - [ - new CallsUsageDto(DateTime.parseISO_UTC('2017-10-12'), 10, 130), - new CallsUsageDto(DateTime.parseISO_UTC('2017-10-13'), 13, 170) - ]); - - authService.verifyAll(); - }); - - it('should make get request to get month calls', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/usages/calls/month')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { count: 130, maxAllowed: 150 } - }) - ) - )) - .verifiable(Times.once()); - - let usages: CurrentCallsDto | null = null; - - usagesService.getMonthCalls('my-app').subscribe(result => { - usages = result; - }).unsubscribe(); - - expect(usages).toEqual(new CurrentCallsDto(130, 150)); - - authService.verifyAll(); - }); - - it('should make get request to get storage usages', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/usages/storage/2017-10-12/2017-10-13')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: [ - { - date: '2017-10-12', - count: 10, - size: 130 - }, - { - date: '2017-10-13', - count: 13, - size: 170 - } - ] - }) - ) - )) - .verifiable(Times.once()); - - let usages: StorageUsageDto[] | null = null; - - usagesService.getStorageUsages('my-app', DateTime.parseISO_UTC('2017-10-12'), DateTime.parseISO_UTC('2017-10-13')).subscribe(result => { - usages = result; - }).unsubscribe(); - - expect(usages).toEqual( - [ - new StorageUsageDto(DateTime.parseISO_UTC('2017-10-12'), 10, 130), - new StorageUsageDto(DateTime.parseISO_UTC('2017-10-13'), 13, 170) - ]); - - authService.verifyAll(); - }); - - it('should make get request to get today storage', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/usages/storage/today')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { size: 130, maxAllowed: 150 } - }) - ) - )) - .verifiable(Times.once()); - - let usages: CurrentStorageDto | null = null; - - usagesService.getTodayStorage('my-app').subscribe(result => { - usages = result; - }).unsubscribe(); - - expect(usages).toEqual(new CurrentStorageDto(130, 150)); - - authService.verifyAll(); - }); -}); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/usages.service.ts b/src/Squidex/app/shared/services/usages.service.ts index dbb8120bd..7418abb2f 100644 --- a/src/Squidex/app/shared/services/usages.service.ts +++ b/src/Squidex/app/shared/services/usages.service.ts @@ -5,13 +5,17 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import 'framework/angular/http-extensions'; -import { ApiUrlConfig, DateTime } from 'framework'; -import { AuthService } from './auth.service'; +import { + ApiUrlConfig, + DateTime, + HTTP +} from 'framework'; export class CallsUsageDto { constructor( @@ -50,7 +54,7 @@ export class CurrentCallsDto { @Injectable() export class UsagesService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -58,26 +62,27 @@ export class UsagesService { public getMonthCalls(app: string): Observable { const url = this.apiUrl.buildUrl(`api/apps/${app}/usages/calls/month`); - return this.authService.authGet(url) - .map(response => response.json()) - .map(response => new CurrentCallsDto(response.count, response.maxAllowed)) - .catchError('Failed to load monthly api calls. Please reload.'); + return HTTP.getVersioned(this.http, url) + .map(response => { + return new CurrentCallsDto(response.count, response.maxAllowed); + }) + .pretifyError('Failed to load monthly api calls. Please reload.'); } public getTodayStorage(app: string): Observable { const url = this.apiUrl.buildUrl(`api/apps/${app}/usages/storage/today`); - return this.authService.authGet(url) - .map(response => response.json()) - .map(response => new CurrentStorageDto(response.size, response.maxAllowed)) - .catchError('Failed to load todays storage size. Please reload.'); + return HTTP.getVersioned(this.http, url) + .map(response => { + return new CurrentStorageDto(response.size, response.maxAllowed); + }) + .pretifyError('Failed to load todays storage size. Please reload.'); } public getCallsUsages(app: string, fromDate: DateTime, toDate: DateTime): Observable { const url = this.apiUrl.buildUrl(`api/apps/${app}/usages/calls/${fromDate.toStringFormat('YYYY-MM-DD')}/${toDate.toStringFormat('YYYY-MM-DD')}`); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { const items: any[] = response; @@ -88,14 +93,13 @@ export class UsagesService { item.averageMs); }); }) - .catchError('Failed to load calls usage. Please reload.'); + .pretifyError('Failed to load calls usage. Please reload.'); } public getStorageUsages(app: string, fromDate: DateTime, toDate: DateTime): Observable { const url = this.apiUrl.buildUrl(`api/apps/${app}/usages/storage/${fromDate.toStringFormat('YYYY-MM-DD')}/${toDate.toStringFormat('YYYY-MM-DD')}`); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { const items: any[] = response; @@ -106,6 +110,6 @@ export class UsagesService { item.size); }); }) - .catchError('Failed to load storage usage. Please reload.'); + .pretifyError('Failed to load storage usage. Please reload.'); } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/users-provider.service.spec.ts b/src/Squidex/app/shared/services/users-provider.service.spec.ts deleted file mode 100644 index ba1c62647..000000000 --- a/src/Squidex/app/shared/services/users-provider.service.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Observable } from 'rxjs'; -import { IMock, Mock, Times } from 'typemoq'; - -import { - AuthService, - Profile, - UserDto, - UsersProviderService, - UsersService -} from './../'; - -describe('UsersProviderService', () => { - let authService: IMock; - let usersService: IMock; - let usersProviderService: UsersProviderService; - - beforeEach(() => { - authService = Mock.ofType(AuthService); - usersService = Mock.ofType(UsersService); - usersProviderService = new UsersProviderService(usersService.object, authService.object); - }); - - it('Should return users service when user not cached', () => { - const user = new UserDto('123', 'mail@domain.com', 'User1', 'path/to/image', true); - - usersService.setup(x => x.getUser('123')) - .returns(() => Observable.of(user)).verifiable(Times.once()); - - let resultingUser: UserDto | null = null; - - usersProviderService.getUser('123').subscribe(result => { - resultingUser = result; - }).unsubscribe(); - - expect(resultingUser).toBe(user); - - usersService.verifyAll(); - }); - - it('Should return provide user from cache', () => { - const user = new UserDto('123', 'mail@domain.com', 'User1', 'path/to/image', true); - - usersService.setup(x => x.getUser('123')) - .returns(() => Observable.of(user)).verifiable(Times.once()); - - usersProviderService.getUser('123'); - - let resultingUser: UserDto | null = null; - - usersProviderService.getUser('123').subscribe(result => { - resultingUser = result; - }).unsubscribe(); - - expect(resultingUser).toBe(user); - - usersService.verifyAll(); - }); - - it('Should return me when user is current user', () => { - const user = new UserDto('123', 'mail@domain.com', 'User1', 'path/to/image', true); - - authService.setup(x => x.user) - .returns(() => new Profile({ profile: { sub: '123'}})); - - usersService.setup(x => x.getUser('123')) - .returns(() => Observable.of(user)).verifiable(Times.once()); - - let resultingUser: UserDto | null = null; - - usersProviderService.getUser('123').subscribe(result => { - resultingUser = result; - }).unsubscribe(); - - expect(resultingUser).toEqual(new UserDto('123', 'mail@domain.com', 'Me', 'path/to/image', true)); - - usersService.verifyAll(); - }); - - it('Should return invalid user when not found', () => { - authService.setup(x => x.user) - .returns(() => new Profile({ profile: { sub: '123'}})); - - usersService.setup(x => x.getUser('123')) - .returns(() => Observable.throw('NOT FOUND')).verifiable(Times.once()); - - let resultingUser: UserDto | null = null; - - usersProviderService.getUser('123').subscribe(result => { - resultingUser = result; - }).unsubscribe(); - - expect(resultingUser).toEqual(new UserDto('NOT FOUND', 'NOT FOUND', 'NOT FOUND', null, false)); - - usersService.verifyAll(); - }); -}); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/users.service.spec.ts b/src/Squidex/app/shared/services/users.service.spec.ts deleted file mode 100644 index 188cdfcbf..000000000 --- a/src/Squidex/app/shared/services/users.service.spec.ts +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { It, IMock, Mock, Times } from 'typemoq'; - -import { - ApiUrlConfig, - AuthService, - CreateUserDto, - UpdateUserDto, - UserCreatedDto, - UserDto, - UserManagementService, - UsersDto, - UsersService -} from './../'; - -describe('UsersService', () => { - let authService: IMock; - let usersService: UsersService; - - beforeEach(() => { - authService = Mock.ofType(AuthService); - usersService = new UsersService(authService.object, new ApiUrlConfig('http://service/p/')); - }); - - it('should make get request to get many users', () => { - authService.setup(x => x.authGet('http://service/p/api/users?query=')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: [ - { - id: '123', - email: 'mail1@domain.com', - displayName: 'User1', - pictureUrl: 'path/to/image1', - isLocked: true - }, - { - id: '456', - email: 'mail2@domain.com', - displayName: 'User2', - pictureUrl: 'path/to/image2', - isLocked: true - } - ] - }) - ) - )) - .verifiable(Times.once()); - - let user: UserDto[] | null = null; - - usersService.getUsers().subscribe(result => { - user = result; - }).unsubscribe(); - - expect(user).toEqual( - [ - new UserDto('123', 'mail1@domain.com', 'User1', 'path/to/image1', true), - new UserDto('456', 'mail2@domain.com', 'User2', 'path/to/image2', true) - ]); - - authService.verifyAll(); - }); - - it('should make get request with query to get many users', () => { - authService.setup(x => x.authGet('http://service/p/api/users?query=my-query')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: [ - { - id: '123', - email: 'mail1@domain.com', - displayName: 'User1', - pictureUrl: 'path/to/image1', - isLocked: true - }, - { - id: '456', - email: 'mail2@domain.com', - displayName: 'User2', - pictureUrl: 'path/to/image2', - isLocked: true - } - ] - }) - ) - )) - .verifiable(Times.once()); - - let user: UserDto[] | null = null; - - usersService.getUsers('my-query').subscribe(result => { - user = result; - }).unsubscribe(); - - expect(user).toEqual( - [ - new UserDto('123', 'mail1@domain.com', 'User1', 'path/to/image1', true), - new UserDto('456', 'mail2@domain.com', 'User2', 'path/to/image2', true) - ]); - - authService.verifyAll(); - }); - - it('should make get request to get single user', () => { - authService.setup(x => x.authGet('http://service/p/api/users/123')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - id: '123', - email: 'mail1@domain.com', - displayName: 'User1', - pictureUrl: 'path/to/image1', - isLocked: true - } - }) - ) - )) - .verifiable(Times.once()); - - let user: UserDto | null = null; - - usersService.getUser('123').subscribe(result => { - user = result; - }).unsubscribe(); - - expect(user).toEqual(new UserDto('123', 'mail1@domain.com', 'User1', 'path/to/image1', true)); - - authService.verifyAll(); - }); -}); - -describe('UserManagementService', () => { - let authService: IMock; - let userManagementService: UserManagementService; - - beforeEach(() => { - authService = Mock.ofType(AuthService); - userManagementService = new UserManagementService(authService.object, new ApiUrlConfig('http://service/p/')); - }); - - it('should make get request to get many users', () => { - authService.setup(x => x.authGet('http://service/p/api/user-management?take=20&skip=30&query=')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - total: 100, - items: [ - { - id: '123', - email: 'mail1@domain.com', - displayName: 'User1', - pictureUrl: 'path/to/image1', - isLocked: true - }, - { - id: '456', - email: 'mail2@domain.com', - displayName: 'User2', - pictureUrl: 'path/to/image2', - isLocked: true - } - ] - } - }) - ) - )) - .verifiable(Times.once()); - - let user: UsersDto | null = null; - - userManagementService.getUsers(20, 30).subscribe(result => { - user = result; - }).unsubscribe(); - - expect(user).toEqual( - new UsersDto(100, [ - new UserDto('123', 'mail1@domain.com', 'User1', 'path/to/image1', true), - new UserDto('456', 'mail2@domain.com', 'User2', 'path/to/image2', true) - ])); - - authService.verifyAll(); - }); - - it('should make get request with query to get many users', () => { - authService.setup(x => x.authGet('http://service/p/api/user-management?take=20&skip=30&query=my-query')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - total: 100, - items: [ - { - id: '123', - email: 'mail1@domain.com', - displayName: 'User1', - pictureUrl: 'path/to/image1', - isLocked: true - }, - { - id: '456', - email: 'mail2@domain.com', - displayName: 'User2', - pictureUrl: 'path/to/image2', - isLocked: true - } - ] - } - }) - ) - )) - .verifiable(Times.once()); - - let user: UsersDto | null = null; - - userManagementService.getUsers(20, 30, 'my-query').subscribe(result => { - user = result; - }).unsubscribe(); - - expect(user).toEqual( - new UsersDto(100, [ - new UserDto('123', 'mail1@domain.com', 'User1', 'path/to/image1', true), - new UserDto('456', 'mail2@domain.com', 'User2', 'path/to/image2', true) - ])); - - authService.verifyAll(); - }); - - it('should make get request to get single user', () => { - authService.setup(x => x.authGet('http://service/p/api/user-management/123')) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - id: '123', - email: 'mail1@domain.com', - displayName: 'User1', - pictureUrl: 'path/to/image1', - isLocked: true - } - }) - ) - )) - .verifiable(Times.once()); - - let user: UserDto | null = null; - - userManagementService.getUser('123').subscribe(result => { - user = result; - }).unsubscribe(); - - expect(user).toEqual(new UserDto('123', 'mail1@domain.com', 'User1', 'path/to/image1', true)); - - authService.verifyAll(); - }); - - it('should make post request to create user', () => { - const dto = new CreateUserDto('mail@squidex.io', 'Squidex User', 'password'); - - authService.setup(x => x.authPost('http://service/p/api/user-management', It.isAny())) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { - id: '123', - pictureUrl: 'path/to/image1' - } - }) - ) - )) - .verifiable(Times.once()); - - let user: UserDto | null = null; - - userManagementService.postUser(dto).subscribe(result => { - user = result; - }).unsubscribe(); - - expect(user).toEqual(new UserCreatedDto('123', 'path/to/image1')); - - authService.verifyAll(); - }); - - it('should make put request to update user', () => { - const dto = new UpdateUserDto('mail@squidex.io', 'Squidex User', 'password'); - - authService.setup(x => x.authPut('http://service/p/api/user-management/123', It.isAny())) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - userManagementService.putUser('123', dto); - - authService.verifyAll(); - }); - - it('should make put request to lock user', () => { - authService.setup(x => x.authPut('http://service/p/api/user-management/123/lock', It.isAny())) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - userManagementService.lockUser('123'); - - authService.verifyAll(); - }); - - it('should make put request to unlock user', () => { - authService.setup(x => x.authPut('http://service/p/api/user-management/123/unlock', It.isAny())) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - userManagementService.unlockUser('123'); - - authService.verifyAll(); - }); -}); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/users.service.ts b/src/Squidex/app/shared/services/users.service.ts index 3fc740766..a4bafd8fb 100644 --- a/src/Squidex/app/shared/services/users.service.ts +++ b/src/Squidex/app/shared/services/users.service.ts @@ -5,13 +5,13 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import 'framework/angular/http-extensions'; -import { ApiUrlConfig } from 'framework'; -import { AuthService } from './auth.service'; +import { ApiUrlConfig, HTTP } from 'framework'; export class UsersDto { constructor( @@ -61,7 +61,7 @@ export class UserDto { @Injectable() export class UsersService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -69,8 +69,7 @@ export class UsersService { public getUsers(query?: string): Observable { const url = this.apiUrl.buildUrl(`api/users?query=${query || ''}`); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { const items: any[] = response; @@ -83,14 +82,13 @@ export class UsersService { item.isLocked); }); }) - .catchError('Failed to load users. Please reload.'); + .pretifyError('Failed to load users. Please reload.'); } public getUser(id: string): Observable { const url = this.apiUrl.buildUrl(`api/users/${id}`); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { return new UserDto( response.id, @@ -99,14 +97,14 @@ export class UsersService { response.pictureUrl, response.isLocked); }) - .catchError('Failed to load user. Please reload.'); + .pretifyError('Failed to load user. Please reload.'); } } @Injectable() export class UserManagementService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -114,8 +112,7 @@ export class UserManagementService { public getUsers(take: number, skip: number, query?: string): Observable { const url = this.apiUrl.buildUrl(`api/user-management?take=${take}&skip=${skip}&query=${query || ''}`); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { const items: any[] = response.items; @@ -130,14 +127,13 @@ export class UserManagementService { return new UsersDto(response.total, users); }) - .catchError('Failed to load users. Please reload.'); + .pretifyError('Failed to load users. Please reload.'); } public getUser(id: string): Observable { const url = this.apiUrl.buildUrl(`api/user-management/${id}`); - return this.authService.authGet(url) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url) .map(response => { return new UserDto( response.id, @@ -146,36 +142,37 @@ export class UserManagementService { response.pictureUrl, response.isLocked); }) - .catchError('Failed to load user. Please reload.'); + .pretifyError('Failed to load user. Please reload.'); } public postUser(dto: CreateUserDto): Observable { const url = this.apiUrl.buildUrl('api/user-management'); - return this.authService.authPost(url, dto) - .map(response => response.json()) - .map(response => new UserCreatedDto(response.id, response.pictureUrl)) - .catchError('Failed to create user. Please reload.'); + return HTTP.postVersioned(this.http, url, dto) + .map(response => { + return new UserCreatedDto(response.id, response.pictureUrl); + }) + .pretifyError('Failed to create user. Please reload.'); } public putUser(id: string, dto: UpdateUserDto): Observable { const url = this.apiUrl.buildUrl(`api/user-management/${id}`); - return this.authService.authPut(url, dto) - .catchError('Failed to update user. Please reload.'); + return HTTP.putVersioned(this.http, url, dto) + .pretifyError('Failed to update user. Please reload.'); } public lockUser(id: string): Observable { const url = this.apiUrl.buildUrl(`api/user-management/${id}/lock`); - return this.authService.authPut(url, {}) - .catchError('Failed to load users. Please retry.'); + return HTTP.putVersioned(this.http, url, {}) + .pretifyError('Failed to load users. Please retry.'); } public unlockUser(id: string): Observable { const url = this.apiUrl.buildUrl(`api/user-management/${id}/unlock`); - return this.authService.authPut(url, {}) - .catchError('Failed to load users. Please retry.'); + return HTTP.putVersioned(this.http, url, {}) + .pretifyError('Failed to load users. Please retry.'); } } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/webhooks.service.1.ts b/src/Squidex/app/shared/services/webhooks.service.1.ts new file mode 100644 index 000000000..2dde8e134 --- /dev/null +++ b/src/Squidex/app/shared/services/webhooks.service.1.ts @@ -0,0 +1,99 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Sebastian Stehle. All rights reserved + */ + +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +import 'framework/angular/http-extensions'; + +import { + ApiUrlConfig, + HTTP, + Version +} from 'framework'; + +export class WebhookDto { + constructor( + public readonly id: string, + public readonly schemaId: string, + public readonly sharedSecret: string, + public readonly url: string, + public readonly totalSucceeded: number, + public readonly totalFailed: number, + public readonly totalTimedout: number, + public readonly averageRequestTimeMs: number, + public readonly lastDumps: string[] + ) { + } +} + +export class WebhookCreatedDto { + constructor( + public readonly id: string, + public readonly sharedSecret: string + ) { + } +} + +export class CreateWebhookDto { + constructor( + public readonly url: string + ) { + } +} + +@Injectable() +export class WebhooksService { + constructor( + private readonly http: HttpClient, + private readonly apiUrl: ApiUrlConfig + ) { + } + + public getWebhooks(appName: string, version?: Version): Observable { + const url = this.apiUrl.buildUrl(`api/apps/${appName}/webhooks`); + + return HTTP.getVersioned(this.http, url, version) + .map(response => { + const items: any[] = response; + + return items.map(item => { + return new WebhookDto( + item.id, + item.schemaId, + item.sharedSecret, + item.url, + item.totalSucceeded, + item.totalFailed, + item.totalTimedout, + item.averageRequestTimeMs, + item.lastDumps); + }); + }) + .pretifyError('Failed to load webhooks. Please reload.'); + } + + public postWebhook(appName: string, schemaName: string, dto: CreateWebhookDto, version?: Version): Observable { + const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/webhooks`); + + return HTTP.postVersioned(this.http, url, dto, version) + .map(response => { + return new WebhookCreatedDto( + response.id, + response.sharedSecret); + }) + .pretifyError('Failed to create webhook. Please reload.'); + } + + public deleteWebhook(appName: string, schemaName: string, id: string, version?: Version): Observable { + const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/webhooks/${id}`); + + return HTTP.deleteVersioned(this.http, url, version) + .pretifyError('Failed to delete webhook. Please reload.'); + } +} \ No newline at end of file diff --git a/src/Squidex/app/shared/services/webhooks.service.spec.ts b/src/Squidex/app/shared/services/webhooks.service.spec.ts deleted file mode 100644 index 5c2092eee..000000000 --- a/src/Squidex/app/shared/services/webhooks.service.spec.ts +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { Response, ResponseOptions } from '@angular/http'; -import { Observable } from 'rxjs'; -import { IMock, Mock, Times } from 'typemoq'; - -import { - ApiUrlConfig, - AuthService, - CreateWebhookDto, - Version, - WebhookCreatedDto, - WebhookDto, - WebhooksService -} from './../'; - -describe('WebhooksService', () => { - let authService: IMock; - let webhooksService: WebhooksService; - let version = new Version('1'); - - beforeEach(() => { - authService = Mock.ofType(AuthService); - webhooksService = new WebhooksService(authService.object, new ApiUrlConfig('http://service/p/')); - }); - - it('should make get request to get app webhooks', () => { - authService.setup(x => x.authGet('http://service/p/api/apps/my-app/webhooks', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: [{ - id: 'id1', - schemaId: 'schemaId1', - sharedSecret: 'token1', - url: 'http://squidex.io/1', - totalSucceeded: 1, - totalFailed: 2, - totalTimedout: 3, - averageRequestTimeMs: 4, - lastDumps: ['dump1'] - }, { - id: 'id2', - schemaId: 'schemaId2', - sharedSecret: 'token2', - url: 'http://squidex.io/2', - totalSucceeded: 5, - totalFailed: 6, - totalTimedout: 7, - averageRequestTimeMs: 8, - lastDumps: ['dump2'] - }] - }) - ) - )) - .verifiable(Times.once()); - - let webhooks: WebhookDto[] | null = null; - - webhooksService.getWebhooks('my-app', version).subscribe(result => { - webhooks = result; - }).unsubscribe(); - - expect(webhooks).toEqual([ - new WebhookDto('id1', 'schemaId1', 'token1', 'http://squidex.io/1', 1, 2, 3, 4, ['dump1']), - new WebhookDto('id2', 'schemaId2', 'token2', 'http://squidex.io/2', 5, 6, 7, 8, ['dump2']) - ]); - - authService.verifyAll(); - }); - - it('should make post request to create webhook', () => { - const dto = new CreateWebhookDto('http://squidex.io/hook'); - - authService.setup(x => x.authPost('http://service/p/api/apps/my-app/schemas/my-schema/webhooks', dto, version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions({ - body: { id: 'id1', sharedSecret: 'token1' } - }) - ) - )) - .verifiable(Times.once()); - - let webhook: WebhookCreatedDto | null = null; - - webhooksService.postWebhook('my-app', 'my-schema', dto, version).subscribe(result => { - webhook = result; - }).unsubscribe(); - - expect(webhook).toEqual(new WebhookCreatedDto('id1', 'token1')); - - authService.verifyAll(); - }); - - it('should make delete request to delete webhook', () => { - authService.setup(x => x.authDelete('http://service/p/api/apps/my-app/schemas/my-schema/webhooks/123', version)) - .returns(() => Observable.of( - new Response( - new ResponseOptions() - ) - )) - .verifiable(Times.once()); - - webhooksService.deleteWebhook('my-app', 'my-schema', '123', version); - - authService.verifyAll(); - }); -}); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/webhooks.service.ts b/src/Squidex/app/shared/services/webhooks.service.ts index 9d5710522..2dde8e134 100644 --- a/src/Squidex/app/shared/services/webhooks.service.ts +++ b/src/Squidex/app/shared/services/webhooks.service.ts @@ -5,13 +5,17 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import 'framework/angular/http-extensions'; -import { ApiUrlConfig, Version } from 'framework'; -import { AuthService } from './auth.service'; +import { + ApiUrlConfig, + HTTP, + Version +} from 'framework'; export class WebhookDto { constructor( @@ -46,7 +50,7 @@ export class CreateWebhookDto { @Injectable() export class WebhooksService { constructor( - private readonly authService: AuthService, + private readonly http: HttpClient, private readonly apiUrl: ApiUrlConfig ) { } @@ -54,8 +58,7 @@ export class WebhooksService { public getWebhooks(appName: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/webhooks`); - return this.authService.authGet(url, version) - .map(response => response.json()) + return HTTP.getVersioned(this.http, url, version) .map(response => { const items: any[] = response; @@ -72,26 +75,25 @@ export class WebhooksService { item.lastDumps); }); }) - .catchError('Failed to load webhooks. Please reload.'); + .pretifyError('Failed to load webhooks. Please reload.'); } public postWebhook(appName: string, schemaName: string, dto: CreateWebhookDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/webhooks`); - return this.authService.authPost(url, dto, version) - .map(response => response.json()) + return HTTP.postVersioned(this.http, url, dto, version) .map(response => { return new WebhookCreatedDto( response.id, response.sharedSecret); }) - .catchError('Failed to create webhook. Please reload.'); + .pretifyError('Failed to create webhook. Please reload.'); } public deleteWebhook(appName: string, schemaName: string, id: string, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/webhooks/${id}`); - return this.authService.authDelete(url, version) - .catchError('Failed to delete webhook. Please reload.'); + return HTTP.deleteVersioned(this.http, url, version) + .pretifyError('Failed to delete webhook. Please reload.'); } } \ No newline at end of file diff --git a/src/Squidex/package.json b/src/Squidex/package.json index 4f9823f5a..8ae20ef85 100644 --- a/src/Squidex/package.json +++ b/src/Squidex/package.json @@ -15,15 +15,15 @@ "build:clean": "rimraf wwwroot/build" }, "dependencies": { - "@angular/animations": "4.2.5", - "@angular/common": "4.2.5", - "@angular/compiler": "4.2.5", - "@angular/core": "4.2.5", - "@angular/forms": "4.2.5", - "@angular/http": "4.2.5", - "@angular/platform-browser": "4.2.5", - "@angular/platform-browser-dynamic": "4.2.5", - "@angular/router": "4.2.5", + "@angular/animations": "4.3.0", + "@angular/common": "4.3.0", + "@angular/compiler": "4.3.0", + "@angular/core": "4.3.0", + "@angular/forms": "4.3.0", + "@angular/http": "4.3.0", + "@angular/platform-browser": "4.3.0", + "@angular/platform-browser-dynamic": "4.3.0", + "@angular/router": "4.3.0", "angular-progress-http": "0.5.1", "angular2-chartjs": "^0.2.0", "babel-polyfill": "6.23.0", @@ -36,25 +36,26 @@ "oidc-client": "1.3.0", "pikaday": "1.6.1", "progressbar.js": "1.0.1", - "react": "^15.6.1", - "react-dom": "^15.6.1", + "react": "15.6.1", + "react-dom": "15.6.1", "redoc": "1.16.1", "rxjs": "5.4.2", - "zone.js": "0.8.12" + "zone.js": "0.8.13" }, "devDependencies": { - "@angular/compiler-cli": "4.2.5", - "@angular/tsc-wrapped": "4.2.5", - "@ngtools/webpack": "1.5.0", + "@angular/compiler-cli": "4.3.0", + "@angular/tsc-wrapped": "4.3.0", + "@ngtools/webpack": "1.5.1", "@types/core-js": "0.9.35", "@types/jasmine": "2.5.43", "@types/mousetrap": "1.5.33", "@types/node": "7.0.5", - "@types/react": "^15.0.35", - "@types/react-dom": "^15.5.1", + "@types/react": "15.0.38", + "@types/react-dom": "15.5.1", "angular2-router-loader": "0.3.5", "angular2-template-loader": "0.6.2", "awesome-typescript-loader": "3.2.1", + "codelyzer": "^3.1.2", "cpx": "1.5.0", "css-loader": "0.28.4", "exports-loader": "0.6.4", diff --git a/src/Squidex/tslint.json b/src/Squidex/tslint.json index f62988398..908da3bab 100644 --- a/src/Squidex/tslint.json +++ b/src/Squidex/tslint.json @@ -21,10 +21,6 @@ true, "check-accessor" ], - "member-ordering": [ - true, - "variables-before-functions" - ], "no-arg": true, "no-bitwise": true, "no-console": [