Adding a user, so we can display who is connected, and putting this in the navbar.

Adding an HTTP Interceptor, which, when in development mode, sends over a header that mimics what would come from the shibboleth single sign on.
This commit is contained in:
Dan 2021-02-15 16:23:26 -05:00
parent 3509ccf268
commit a840cce9a1
9 changed files with 86 additions and 3 deletions

View File

@ -1,5 +1,5 @@
import {APP_BASE_HREF, PlatformLocation} from '@angular/common';
import {HttpClientModule} from '@angular/common/http';
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import {NgModule} from '@angular/core';
import {FlexLayoutModule} from '@angular/flex-layout';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
@ -54,6 +54,7 @@ import {MatDividerModule} from '@angular/material/divider';
import { SidebarComponent } from './sidebar/sidebar.component';
import {MatListModule} from '@angular/material/list';
import { CustomDatePipe } from './custom-date-adapter';
import {DevHeaderInterceptorInterceptor} from './dev-header-interceptor.interceptor';
@ -137,6 +138,7 @@ export function getBaseHref(platformLocation: PlatformLocation): string {
{provide: 'APP_ENVIRONMENT', useClass: ThisEnvironment},
{provide: APP_BASE_HREF, useFactory: getBaseHref, deps: [PlatformLocation]},
{provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: {appearance: 'outline'}},
{provide: HTTP_INTERCEPTORS, useClass: DevHeaderInterceptorInterceptor, multi: true}
],
bootstrap: [AppComponent],
entryComponents: []

View File

@ -18,7 +18,7 @@ export class CustomDatePipe implements PipeTransform {
@Pipe({ name: 'date' })
@Injectable()
export class CustomDateAdapter extends NativeDateAdapter {
export class CustomDateAdapter extends NativeDateAdapter implements PipeTransform {
getFirstDayOfWeek(): number {
return 1;

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { DevHeaderInterceptorInterceptor } from './dev-header-interceptor.interceptor';
describe('DevHeaderInterceptorInterceptor', () => {
beforeEach(() => TestBed.configureTestingModule({
providers: [
DevHeaderInterceptorInterceptor
]
}));
it('should be created', () => {
const interceptor: DevHeaderInterceptorInterceptor = TestBed.inject(DevHeaderInterceptorInterceptor);
expect(interceptor).toBeTruthy();
});
});

View File

@ -0,0 +1,27 @@
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
import { Observable } from 'rxjs';
import {environment} from '../environments/environment.runtime';
@Injectable()
export class DevHeaderInterceptorInterceptor implements HttpInterceptor {
// This adds a uid header to all requests in development mode to mimic the data that is added
// automatically by the shibboleth single-sign-on service that the production system sits behind.
constructor() {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
// Clone the request to add the new header
if (!environment.production) {
const clonedRequest = request.clone({ headers: request.headers.append('X-Remote-Uid', 'devuser') });
return next.handle(clonedRequest);
} else {
return next.handle(request);
}
}
}

View File

@ -0,0 +1,4 @@
export interface User {
uid: string;
display_name: string;
}

View File

@ -6,5 +6,6 @@
<button id="nav_home" mat-icon-button routerLink="/"><mat-icon>home</mat-icon></button>
<button id="nav_dash" mat-icon-button [routerLink]="['/dashboard', 'graphs']"><mat-icon>dashboard</mat-icon></button>
<button id="nav_settings" mat-icon-button routerLink="/settings"><mat-icon>settings</mat-icon></button>
<div *ngIf="user">{{user.display_name}}</div>
</mat-toolbar-row>
</mat-toolbar>

View File

@ -4,18 +4,33 @@ import {MatIconModule} from '@angular/material/icon';
import {MatToolbarModule} from '@angular/material/toolbar';
import { NavbarComponent } from './navbar.component';
import {HttpClient} from '@angular/common/http';
import {FakeMatIconRegistry} from '@angular/material/icon/testing';
import {ApiService} from '../services/api.service';
import {APP_BASE_HREF} from '@angular/common';
import {MockEnvironment} from '../testing/environment.mock';
import {HttpClientTestingModule} from '@angular/common/http/testing';
describe('NavbarComponent', () => {
let component: NavbarComponent;
let fixture: ComponentFixture<NavbarComponent>;
const mockEnvironment = new MockEnvironment();
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ NavbarComponent ],
imports: [
HttpClientTestingModule,
MatButtonModule,
MatIconModule,
MatToolbarModule,
],
providers: [
HttpClient,
FakeMatIconRegistry,
ApiService,
{provide: 'APP_ENVIRONMENT', useValue: mockEnvironment},
{provide: APP_BASE_HREF, useValue: '/'},
]
})
.compileComponents();

View File

@ -1,6 +1,8 @@
import {Component, OnInit} from '@angular/core';
import {AppDefaults} from '../models/appDefaults.interface';
import {SettingsService} from '../services/settings.service';
import {ApiService} from '../services/api.service';
import {User} from '../models/user.interface';
@Component({
selector: 'app-navbar',
@ -10,12 +12,17 @@ import {SettingsService} from '../services/settings.service';
export class NavbarComponent implements OnInit {
settings: AppDefaults;
locationId: string;
user: User;
constructor(private settingsService: SettingsService) {
constructor(private settingsService: SettingsService,
private apiService: ApiService) {
this.settingsService.settings.subscribe(settings => {
this.settings = settings;
this.locationId = !this.hasDefaultId ? this.settings.locationId : '0000';
});
this.apiService.getUser().subscribe(user => {
this.user = user;
});
}
get hasDefaultId(): boolean {

View File

@ -9,6 +9,7 @@ import { Sample } from '../models/sample.interface';
import { InventoryDeposit } from '../models/deposit.interface';
import { IvyFile } from '../models/ivyfile.interface';
import { HttpClient, HttpParams } from '@angular/common/http';
import {User} from '../models/user.interface';
@ -21,6 +22,7 @@ export class ApiService {
sample: '/sample',
deposit: '/deposit',
ivy_file: '/ivy_file',
user: '/user'
};
constructor(
@ -80,6 +82,15 @@ export class ApiService {
.pipe(catchError(err => this._handleError(err)));
}
getUser(): Observable<User> {
const url = this.apiRoot + this.endpoints.user;
return this.httpClient
.get<User>(url)
.pipe(timeout(1000), catchError(err => this._handleError(err)))
.pipe(catchError(err => this._handleError(err)));
}
private _handleError(error: ApiError): Observable<never> {
return throwError(error.message || 'Could not complete your request; please try again later.');
}