start of caching, csrf interceptor, and device service

This commit is contained in:
Jesse Lucas 2020-03-20 19:44:21 -04:00
parent 03c6fb2f82
commit 05f01a5d94
11 changed files with 85 additions and 33 deletions

View File

@ -9,6 +9,7 @@ import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { FlexLayoutModule } from '@angular/flex-layout'; import { FlexLayoutModule } from '@angular/flex-layout';
import { httpInterceptorProviders } from './http-interceptors';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
@ -28,6 +29,7 @@ import { InMemoryConfigDataService } from './in-memory-config-data.service';
import { deviceID } from './api-utils'; import { deviceID } from './api-utils';
import { environment } from '../environments/environment'; import { environment } from '../environments/environment';
import { ChartItemComponent } from './charts/chart-item/chart-item.component'; import { ChartItemComponent } from './charts/chart-item/chart-item.component';
import { CSRFInterceptor } from './http-interceptors/csrf-intercepor';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -61,7 +63,7 @@ import { ChartItemComponent } from './charts/chart-item/chart-item.component';
[] : HttpClientInMemoryWebApiModule.forRoot(InMemoryConfigDataService, [] : HttpClientInMemoryWebApiModule.forRoot(InMemoryConfigDataService,
{ dataEncapsulation: false, delay: 200 }), { dataEncapsulation: false, delay: 200 }),
], ],
providers: [], providers: [httpInterceptorProviders],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })

View File

@ -21,7 +21,7 @@ export class FolderChartComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
for (let state in Folder.StateType) { for (let state in Folder.StateType) {
console.log(state); // console.log(state);
} }
} }

View File

@ -1,5 +1,4 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { deviceID } from './api-utils';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -8,14 +7,6 @@ export class CookieService {
constructor() { } constructor() { }
getCSRFHeader(): any {
const dID: String = deviceID();
const csrfCookie = 'CSRF-Token-' + dID
const csrfHeader = {};
csrfHeader['X-CSRF-Token-' + dID] = this.getCookie(csrfCookie);
return csrfHeader;
}
getCookie(name: string): string { getCookie(name: string): string {
let ca: Array<string> = document.cookie.split(';'); let ca: Array<string> = document.cookie.split(';');
let caLen: number = ca.length; let caLen: number = ca.length;

View File

@ -13,25 +13,25 @@ import Folder from './folder'
providedIn: 'root' providedIn: 'root'
}) })
export class DbStatusService { export class DbStatusService {
private folderStatus: Object = {};
private headers: HttpHeaders;
private dbStatusUrl = environment.production ? apiURL + 'rest/db/status' : 'api/dbStatus'; private dbStatusUrl = environment.production ? apiURL + 'rest/db/status' : 'api/dbStatus';
private statuses: Map<string, Folder.Status>;
constructor(private http: HttpClient, private cookieService: CookieService) { constructor(private http: HttpClient, private cookieService: CookieService) {
this.headers = new HttpHeaders(this.cookieService.getCSRFHeader()) this.statuses = new Map();
} }
getFolderStatus(id: string): Observable<Folder.Status> { getFolderStatus(id: string): Observable<Folder.Status> {
let httpOptions: { headers: HttpHeaders } | // First check to see if we have a cached value
{ headers: HttpHeaders, params: HttpParams }; if (this.statuses.has(id)) {
return of(this.statuses.get(id));
}
let httpOptions: { params: HttpParams };
if (id) { if (id) {
httpOptions = { httpOptions = {
headers: this.headers,
params: new HttpParams().set('folder', id) params: new HttpParams().set('folder', id)
}; };
} else { } else { }
httpOptions = { headers: this.headers };
}
return this.http return this.http
.get<Folder.Status>(this.dbStatusUrl, httpOptions) .get<Folder.Status>(this.dbStatusUrl, httpOptions)
@ -41,13 +41,13 @@ export class DbStatusService {
// Remove from array in developement // Remove from array in developement
// in-memory-web-api returns arrays // in-memory-web-api returns arrays
if (!environment.production) { if (!environment.production) {
console.log("status res!", res);
const a: any = res as any; const a: any = res as any;
if (a.length > 0) { if (a.length > 0) {
return res[0]; res = res[0];
} }
return {};
} }
// cache result
this.statuses.set(id, res)
return res; return res;
}) })
); );

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { DeviceService } from './device.service';
describe('DeviceService', () => {
let service: DeviceService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DeviceService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,9 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class DeviceService {
constructor() { }
}

View File

@ -26,7 +26,7 @@ export class FolderService {
startIndex = startIndex + 1; startIndex = startIndex + 1;
this.dbStatusService.getFolderStatus(folder.id).subscribe( this.dbStatusService.getFolderStatus(folder.id).subscribe(
status => { status => {
folder["status"] = status; folder.status = status;
observer.next(folder); observer.next(folder);
// recursively get the status of the next folder // recursively get the status of the next folder
@ -41,6 +41,7 @@ export class FolderService {
*/ */
getAll(): Observable<Folder> { getAll(): Observable<Folder> {
const folderObservable: Observable<Folder> = new Observable((observer) => { const folderObservable: Observable<Folder> = new Observable((observer) => {
this.systemConfigService.getFolders().subscribe( this.systemConfigService.getFolders().subscribe(
folders => { folders => {
this.folders = folders; this.folders = folders;
@ -54,4 +55,3 @@ export class FolderService {
return folderObservable return folderObservable
} }
} }

View File

@ -0,0 +1,29 @@
import { Injectable } from '@angular/core';
import { deviceID } from '../api-utils';
import {
HttpInterceptor, HttpHandler, HttpRequest, HttpHeaders
} from '@angular/common/http';
import { CookieService } from '../cookie.service';
@Injectable()
export class CSRFInterceptor implements HttpInterceptor {
constructor(private cookieService: CookieService) { }
intercept(req: HttpRequest<any>, next: HttpHandler) {
const dID: String = deviceID();
const csrfCookie = 'CSRF-Token-' + dID
// Clone the request and replace the original headers with
// cloned headers, updated with the CSRF information.
const csrfReq = req.clone({
headers: req.headers.set('X-CSRF-Token-' + dID,
this.cookieService.getCookie(csrfCookie))
});
// send cloned request with header to the next handler.
return next.handle(csrfReq);
}
}

View File

@ -0,0 +1,8 @@
/* "Barrel" of Http Interceptors */
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { CSRFInterceptor } from './csrf-intercepor';
/** Http interceptor providers in outside-in order */
export const httpInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: CSRFInterceptor, multi: true },
];

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { config } from './mock/mock-config' import { config } from './mocks/mock-config'
import { dbStatus } from './mock/mock-db-status' import { dbStatus } from './mocks/mock-db-status'
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'

View File

@ -20,17 +20,14 @@ export class SystemConfigService {
private devicesSubject: Subject<Device[]> = new Subject(); private devicesSubject: Subject<Device[]> = new Subject();
private systemConfigUrl = environment.production ? apiURL + 'rest/system/config' : 'api/config'; private systemConfigUrl = environment.production ? apiURL + 'rest/system/config' : 'api/config';
private httpOptions: any;
private checkInterval: number = 100; private checkInterval: number = 100;
constructor(private http: HttpClient, private cookieService: CookieService) { constructor(private http: HttpClient) { }
this.httpOptions = { headers: new HttpHeaders(this.cookieService.getCSRFHeader()) };
}
getSystemConfig(): Observable<any> { getSystemConfig(): Observable<any> {
return this.http return this.http
.get(this.systemConfigUrl, this.httpOptions) .get(this.systemConfigUrl)
.pipe( .pipe(
retry(apiRetry), retry(apiRetry),
map(res => { map(res => {