Commit bfcf3f14 authored by Harold Boissenin's avatar Harold Boissenin
Browse files

update library

No related merge requests found
Showing with 75 additions and 169 deletions
+75 -169
.gitignore 0 → 100644
...@@ -3,8 +3,11 @@ import { ...@@ -3,8 +3,11 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
EventEmitter,
Output,
} from '@angular/core'; } from '@angular/core';
// TODO injecter
import { Configuration } from '../configuration'; import { Configuration } from '../configuration';
import { DebugStateService } from '../debug-state.service'; import { DebugStateService } from '../debug-state.service';
...@@ -26,7 +29,7 @@ interface ResponseInfo { ...@@ -26,7 +29,7 @@ interface ResponseInfo {
} }
@Component({ @Component({
selector: 'lib-api-debug', selector: 'app-api-debug',
templateUrl: './api-debug.component.html', templateUrl: './api-debug.component.html',
styleUrls: ['./api-debug.component.scss'], styleUrls: ['./api-debug.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
...@@ -37,6 +40,9 @@ export class ApiDebugComponent { ...@@ -37,6 +40,9 @@ export class ApiDebugComponent {
private readonly ids: string[] = []; private readonly ids: string[] = [];
private readonly apiPrefix: string; private readonly apiPrefix: string;
@Output()
public countChanged = new EventEmitter<number>();
constructor( constructor(
interceptor: ApiDebugInterceptor, interceptor: ApiDebugInterceptor,
public readonly debug: DebugStateService, public readonly debug: DebugStateService,
...@@ -45,7 +51,9 @@ export class ApiDebugComponent { ...@@ -45,7 +51,9 @@ export class ApiDebugComponent {
) { ) {
this.apiPrefix = conf.apiBaseURL; this.apiPrefix = conf.apiBaseURL;
interceptor.events$.subscribe(ev => this.handle(ev)); interceptor.events$.subscribe(ev => {
this.handle(ev);
});
} }
public get unseenCount(): number { public get unseenCount(): number {
...@@ -82,6 +90,7 @@ export class ApiDebugComponent { ...@@ -82,6 +90,7 @@ export class ApiDebugComponent {
private handle(ev: DebugEvent): void { private handle(ev: DebugEvent): void {
if (ev.type === 'request') { if (ev.type === 'request') {
this.addRequest(ev.id, ev.request); this.addRequest(ev.id, ev.request);
this.countChanged.emit(this.ids.length);
this.cd.markForCheck(); this.cd.markForCheck();
return; return;
} }
...@@ -98,7 +107,10 @@ export class ApiDebugComponent { ...@@ -98,7 +107,10 @@ export class ApiDebugComponent {
this.cd.markForCheck(); this.cd.markForCheck();
} }
private addRequest(id: string, { urlWithParams, method }: HttpRequest<any>): void { private addRequest(
id: string,
{ urlWithParams, method }: HttpRequest<any>
): void {
if (urlWithParams.startsWith(this.apiPrefix)) { if (urlWithParams.startsWith(this.apiPrefix)) {
urlWithParams = new URL(urlWithParams).pathname; urlWithParams = new URL(urlWithParams).pathname;
} }
...@@ -113,7 +125,10 @@ export class ApiDebugComponent { ...@@ -113,7 +125,10 @@ export class ApiDebugComponent {
this.ids.unshift(id); this.ids.unshift(id);
} }
private setResponse(request: RequestInfo, { status, statusText, headers }: HttpResponseBase): void { private setResponse(
request: RequestInfo,
{ status, statusText, headers }: HttpResponseBase
): void {
request.response = { request.response = {
status, status,
statusText, statusText,
......
...@@ -9,6 +9,7 @@ import * as _ from 'lodash'; ...@@ -9,6 +9,7 @@ import * as _ from 'lodash';
import { Observable, ReplaySubject } from 'rxjs'; import { Observable, ReplaySubject } from 'rxjs';
import { finalize, tap } from 'rxjs/operators'; import { finalize, tap } from 'rxjs/operators';
// TODO injecter
import { Configuration } from '../configuration'; import { Configuration } from '../configuration';
export interface RequestDebugEvent { export interface RequestDebugEvent {
......
import { HttpHandler, HttpRequest } from '@angular/common/http';
import { inject, TestBed } from '@angular/core/testing';
import { AppError } from '@devatscience/ngx-errors';
import { of } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ApiInterceptor } from './api.interceptor';
import { Configuration } from '../configuration';
import { MarbleFormatter, MarbleTestScheduler } from '../testing/marbles';
describe('ApiInterceptor', () => {
const API_BASE_URL = 'http://test.com';
const API_PATH = '/foo/bar';
const API_URL = `${API_BASE_URL}${API_PATH}`;
const OTHER_URL = 'http://example.com/foo/bar';
const RESPONSE = 'OK';
const VALUES = {
n: null as null,
r: RESPONSE,
};
let scheduler: MarbleTestScheduler<any>;
let next: jasmine.SpyObj<HttpHandler>;
let conf: Configuration;
beforeEach(() => {
scheduler = new MarbleTestScheduler(new MarbleFormatter(VALUES));
next = jasmine.createSpyObj('HttpHandler', ['handle']);
conf = new Configuration();
conf.apiBaseURL = API_BASE_URL;
TestBed.configureTestingModule({
providers: [ApiInterceptor, { provide: Configuration, useValue: conf }],
});
});
it('should be created', inject(
[ApiInterceptor],
(service: ApiInterceptor) => {
expect(service).toBeTruthy();
}
));
function testUrl(input: string, expected: string): void {
const NEXT_M = /***/ '--r';
const INTER_M = /**/ '--r';
const req = new HttpRequest('GET', input);
scheduler.run(({ expectObservable, cold }) => {
next.handle.and.callFake((r: HttpRequest<any>) => {
expect(r.url).toEqual(expected);
return cold(NEXT_M);
});
inject([ApiInterceptor], (service: ApiInterceptor) =>
expectObservable(service.intercept(req, next)).toBe(INTER_M)
)();
});
}
it('should prepend the base URL to hostless URLs', () => testUrl(API_PATH, API_URL));
it('should not prepend the base URL to full API URLs', () => testUrl(API_URL, API_URL));
it('should not change other URLs', () => testUrl(OTHER_URL, OTHER_URL));
it('should classify errors', () => {
const NEXT_M = /***/ '--#';
const INTER_M = /**/ '--(e|)';
const ERR_IN = new Error('Error');
(VALUES as any).e = new AppError(ERR_IN, ERR_IN.message, ERR_IN.stack, false);
const req = new HttpRequest('GET', '/some/url');
scheduler
.withValues(VALUES)
.withError(ERR_IN)
.run(({ expectObservable, cold }) => {
next.handle.and.callFake(() => cold(NEXT_M));
inject([ApiInterceptor], (service: ApiInterceptor) =>
expectObservable(service.intercept(req, next).pipe(catchError(e => of(e)))).toBe(INTER_M)
)();
});
});
});
import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { normalizeError } from '@devatscience/ngx-errors';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Configuration } from '../configuration';
/**
* Préfixe l'URL par BASE_URL si elle commence par "/" (c-à-d que c'est un chemin absolu),
* et ajoute l'authentification si nécessaire.
*/
@Injectable()
export class ApiInterceptor implements HttpInterceptor {
private readonly NON_API_PREFIX = /^(https?:)?\/\//i;
public constructor(private readonly conf: Configuration) {}
public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!req.url.match(this.NON_API_PREFIX)) {
req = req.clone({ url: this.conf.apiBaseURL + req.url });
}
return next.handle(req).pipe(catchError((err: any) => throwError(normalizeError(err))));
}
}
...@@ -44,3 +44,11 @@ export class Configuration implements IConfiguration { ...@@ -44,3 +44,11 @@ export class Configuration implements IConfiguration {
} }
} }
} }
export function configurationFactory(): Configuration {
return new Configuration({
apiBaseURL: 'http://baliste-api.dvp',
debug: true,
debugToolbarURL: 'http://baliste-api.dvp/_wdt',
});
}
...@@ -2,8 +2,8 @@ import { Injectable } from '@angular/core'; ...@@ -2,8 +2,8 @@ import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators'; import { distinctUntilChanged, map } from 'rxjs/operators';
// TODO change this // TODO injecter
const environment = { export const environment = {
production: false, production: false,
apiURL: 'http://baliste-api.dvp', apiURL: 'http://baliste-api.dvp',
api: 'http://127.0.0.101:8040', api: 'http://127.0.0.101:8040',
......
...@@ -3,7 +3,7 @@ import { Component } from '@angular/core'; ...@@ -3,7 +3,7 @@ import { Component } from '@angular/core';
import { DebugStateService } from './debug-state.service'; import { DebugStateService } from './debug-state.service';
@Component({ @Component({
selector: 'lib-debug-toggle', selector: 'app-debug-toggle',
template: ` template: `
<ng-container *ngIf="state.available"> <ng-container *ngIf="state.available">
<p-toggleButton <p-toggleButton
......
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { LOCALE_ID, NgModule } from '@angular/core'; import { LOCALE_ID, NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms'; import { ReactiveFormsModule } from '@angular/forms';
import { import {
...@@ -16,8 +15,7 @@ import { TreeModule } from 'primeng/tree'; ...@@ -16,8 +15,7 @@ import { TreeModule } from 'primeng/tree';
import { TreeTableModule } from 'primeng/treetable'; import { TreeTableModule } from 'primeng/treetable';
import { ApiDebugComponent } from './api/api-debug.component'; import { ApiDebugComponent } from './api/api-debug.component';
import { ApiDebugInterceptor } from './api/api-debug.interceptor'; import { Configuration, configurationFactory } from './configuration';
import { Configuration } from './configuration';
import { DumpPanelComponent } from './dump-panel.component'; import { DumpPanelComponent } from './dump-panel.component';
import { DumpValueComponent } from './dump-value.component'; import { DumpValueComponent } from './dump-value.component';
import { SpyDisplayComponent } from './spy/spy-display.component'; import { SpyDisplayComponent } from './spy/spy-display.component';
...@@ -25,27 +23,16 @@ import { WatchDisplayComponent } from './watch/watch-display.component'; ...@@ -25,27 +23,16 @@ import { WatchDisplayComponent } from './watch/watch-display.component';
import { WatchComponent } from './watch/watch.component'; import { WatchComponent } from './watch/watch.component';
import { WatchService } from './watch/watch.service'; import { WatchService } from './watch/watch.service';
export function configurationFactory(): Configuration {
return new Configuration({
apiBaseURL: 'http://baliste-api.dvp',
debug: true,
debugToolbarURL: 'http://baliste-api.dvp/_wdt',
});
}
const EXPORTED_COMPONENTS = [ const EXPORTED_COMPONENTS = [
// ApiDebugComponent,
WatchComponent, WatchComponent,
DumpPanelComponent, DumpPanelComponent,
ApiDebugComponent,
]; ];
@NgModule({ @NgModule({
imports: [ imports: [
//
CommonModule, CommonModule,
ReactiveFormsModule, ReactiveFormsModule,
SidebarModule, SidebarModule,
ButtonModule, ButtonModule,
TreeModule, TreeModule,
...@@ -58,17 +45,10 @@ const EXPORTED_COMPONENTS = [ ...@@ -58,17 +45,10 @@ const EXPORTED_COMPONENTS = [
TooltipModule, TooltipModule,
], ],
providers: [ providers: [
ApiDebugInterceptor,
WatchService, WatchService,
{
provide: HTTP_INTERCEPTORS,
useExisting: ApiDebugInterceptor,
multi: true,
},
{ provide: Configuration, useFactory: configurationFactory }, { provide: Configuration, useFactory: configurationFactory },
{ provide: LOCALE_ID, useValue: 'fr' }, { provide: LOCALE_ID, useValue: 'fr' },
], ],
declarations: [ declarations: [
// //
DumpValueComponent, DumpValueComponent,
......
...@@ -18,17 +18,17 @@ ...@@ -18,17 +18,17 @@
<p-tabView> <p-tabView>
<p-tabPanel header="Watches ({{ watchCount }})"> <p-tabPanel header="Watches ({{ watchCount }})">
<div class="data-panel"> <div class="data-panel">
<lib-watch-display (countChanged)="setWatchCount($event)"></lib-watch-display> <app-watch-display (countChanged)="setWatchCount($event)"></app-watch-display>
</div> </div>
</p-tabPanel> </p-tabPanel>
<p-tabPanel header="Spies ({{ spyCount }})"> <p-tabPanel header="Spies ({{ spyCount }})">
<div class="data-panel"> <div class="data-panel">
<lib-spy-display (countChanged)="setSpyCount($event)"></lib-spy-display> <app-spy-display (countChanged)="setSpyCount($event)"></app-spy-display>
</div> </div>
</p-tabPanel> </p-tabPanel>
<p-tabPanel header="API ({{ apiCallCount }})"> <p-tabPanel header="API ({{ apiCallCount }})">
<div class="data-panel"> <div class="data-panel">
<lib-api-debug></lib-api-debug> <app-api-debug (countChanged)="setApiCallCount($event)"></app-api-debug>
</div> </div>
</p-tabPanel> </p-tabPanel>
</p-tabView> </p-tabView>
......
import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core'; import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
} from '@angular/core';
import { DebugStateService } from './debug-state.service'; import { DebugStateService } from './debug-state.service';
@Component({ @Component({
selector: 'lib-dump-panel', selector: 'app-dump-panel',
templateUrl: './dump-panel.component.html', templateUrl: './dump-panel.component.html',
styleUrls: ['./dump-panel.component.scss'], styleUrls: ['./dump-panel.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
...@@ -27,4 +31,9 @@ export class DumpPanelComponent { ...@@ -27,4 +31,9 @@ export class DumpPanelComponent {
this.spyCount = count; this.spyCount = count;
this.cd.markForCheck(); this.cd.markForCheck();
} }
public setApiCallCount(count: number): void {
this.apiCallCount = count;
this.cd.markForCheck();
}
} }
...@@ -4,7 +4,7 @@ import { TreeNode } from 'primeng/api'; ...@@ -4,7 +4,7 @@ import { TreeNode } from 'primeng/api';
type PropertyPath = Array<string | number>; type PropertyPath = Array<string | number>;
@Component({ @Component({
selector: 'lib-dump-value', selector: 'app-dump-value',
templateUrl: './dump-value.component.html', templateUrl: './dump-value.component.html',
styleUrls: ['./dump-value.component.scss'], styleUrls: ['./dump-value.component.scss'],
}) })
......
export * from './rxjs/index'; // TODO make a package ? export * from './api/api-debug.component';
export * from './api/api-debug.interceptor';
export * from './debug.module'; export * from './debug.module';
export * from './debug-state.service';
export * from './debug-toggle.component';
export * from './dump-panel.component'; export * from './dump-panel.component';
export * from './dump-value.component'; export * from './dump-value.component';
export * from './rxjs/index';
export * from './spy/spy-display.component'; export * from './spy/spy-display.component';
export * from './watch/watch.component'; export * from './watch/watch.component';
export * from './watch/watch-display.component'; export * from './watch/watch-display.component';
export * from './watch/watch.service'; export * from './watch/watch.service';
export * from './configuration'; export * from './configuration';
export * from './debug.module';
export * from './debug-state.service';
export * from './debug-toggle.component';
export * from './dump-panel.component';
export * from './dump-value.component';
...@@ -31,6 +31,9 @@ function installHook<T extends Hookable<K, M>, M extends (this: T, ...args: any[ ...@@ -31,6 +31,9 @@ function installHook<T extends Hookable<K, M>, M extends (this: T, ...args: any[
/** /**
* Modifie une méthode d'un objet pour éxecuter une fonction avant son éxecution normale. * Modifie une méthode d'un objet pour éxecuter une fonction avant son éxecution normale.
* *
* @param {object} target Le prototype à modifier.
* @param {string|symbol} name Le nom de la méthode à surcharger.
* @param {function} hook La fonction à ajouter.
*/ */
export function hookBefore< export function hookBefore<
T extends Hookable<K, M>, T extends Hookable<K, M>,
...@@ -51,6 +54,9 @@ export function hookBefore< ...@@ -51,6 +54,9 @@ export function hookBefore<
/** /**
* Modifie une méthode d'un objet pour éxecuter une fonction après son éxecution normale. * Modifie une méthode d'un objet pour éxecuter une fonction après son éxecution normale.
* *
* @param {object} target Le prototype à modifier.
* @param {string|symbol} name Le nom de la méthode à surcharger.
* @param {function} hook La fonction à ajouter.
*/ */
export function hookAfter< export function hookAfter<
T extends Hookable<K, M>, T extends Hookable<K, M>,
...@@ -71,6 +77,9 @@ export function hookAfter< ...@@ -71,6 +77,9 @@ export function hookAfter<
/** /**
* Modifie une méthode d'un objet pour éxecuter inconditionnellement une fonction après son éxecution normale. * Modifie une méthode d'un objet pour éxecuter inconditionnellement une fonction après son éxecution normale.
* *
* @param {object} target Le prototype à modifier.
* @param {string|symbol} name Le nom de la méthode à surcharger.
* @param {function} hook La fonction à ajouter.
*/ */
export function hookFinally< export function hookFinally<
T extends Hookable<K, M>, T extends Hookable<K, M>,
......
...@@ -47,8 +47,8 @@ function ParamFromMap<K extends 'paramMap' | 'queryParamMap'>( ...@@ -47,8 +47,8 @@ function ParamFromMap<K extends 'paramMap' | 'queryParamMap'>(
* *
* La propriété décorée doit implémenter Observer<string>. * La propriété décorée doit implémenter Observer<string>.
* *
* param {string|null} param Nom du paramètre. Par défaut égal au nom de la propriété minus le '$' final. * @param {string|null} param Nom du paramètre. Par défaut égal au nom de la propriété minus le '$' final.
* param {string} routeProperty nom de la propriété du Component contenant l'ActivatedRoute. Par défaut 'route'. * @param {string} routeProperty nom de la propriété du Component contenant l'ActivatedRoute. Par défaut 'route'.
*/ */
export function RouteParam(param: string = null, routeProperty: string = 'route') { export function RouteParam(param: string = null, routeProperty: string = 'route') {
return ParamFromMap(param, routeProperty, 'paramMap'); return ParamFromMap(param, routeProperty, 'paramMap');
...@@ -59,8 +59,8 @@ export function RouteParam(param: string = null, routeProperty: string = 'route' ...@@ -59,8 +59,8 @@ export function RouteParam(param: string = null, routeProperty: string = 'route'
* *
* La propriété décorée doit implémenter Observer<string>. * La propriété décorée doit implémenter Observer<string>.
* *
* param {string|null} param Nom du paramètre. Par défaut égal au nom de la propriété minus le '$' final. * @param {string|null} param Nom du paramètre. Par défaut égal au nom de la propriété minus le '$' final.
* param {string} routeProperty nom de la propriété du Component contenant l'ActivatedRoute. Par défaut 'route'. * @param {string} routeProperty nom de la propriété du Component contenant l'ActivatedRoute. Par défaut 'route'.
*/ */
export function QueryParam(param: string = null, routeProperty: string = 'route') { export function QueryParam(param: string = null, routeProperty: string = 'route') {
return ParamFromMap(param, routeProperty, 'queryParamMap'); return ParamFromMap(param, routeProperty, 'queryParamMap');
......
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { MarbleTestScheduler } from '../testing/marbles';
import { safeCombineLatest } from './safe-combine-latest.observable'; import { safeCombineLatest } from './safe-combine-latest.observable';
import { MarbleTestScheduler } from './testing/marbles';
describe('safe-combine-latest', () => { describe('safe-combine-latest', () => {
let scheduler: MarbleTestScheduler<any>; let scheduler: MarbleTestScheduler<any>;
......
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { MarbleTestScheduler } from '../testing/marbles';
import { safeForkJoin } from './safe-fork-join.observable'; import { safeForkJoin } from './safe-fork-join.observable';
import { MarbleTestScheduler } from './testing/marbles';
describe('safe-fork-join', () => { describe('safe-fork-join', () => {
let scheduler: MarbleTestScheduler<any>; let scheduler: MarbleTestScheduler<any>;
......
import { OnDestroy, OnInit } from '@angular/core'; import { OnDestroy, OnInit } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { MarbleTestScheduler } from '../testing/marbles';
import { SubscribeOnInit } from './subscribe-on-init.decorator'; import { SubscribeOnInit } from './subscribe-on-init.decorator';
import { MarbleTestScheduler } from './testing/marbles';
describe('@SubscribeOnInit', () => { describe('@SubscribeOnInit', () => {
const VALUES = { a: 'a' }; const VALUES = { a: 'a' };
......
File moved
// tslint:disable-next-line:no-reference // tslint:disable-next-line:no-reference
///<reference path="../../../node_modules/@types/jasmine/index.d.ts"/> ///<reference path="node_modules/@types/jasmine/index.d.ts"/>
/* tslint:disable:rxjs-no-internal */ /* tslint:disable:rxjs-no-internal */
import * as _ from 'lodash'; import * as _ from 'lodash';
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment