Skip to content

Instantly share code, notes, and snippets.

@jweisman
Last active September 4, 2020 12:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jweisman/7cb7b298a191206dfa985cd7f9fb5df6 to your computer and use it in GitHub Desktop.
Save jweisman/7cb7b298a191206dfa985cd7f9fb5df6 to your computer and use it in GitHub Desktop.
Ex Libris Cloud App Angular Component for use with an API Proxy
<h1>Using a Proxy to Access Data from Other Institutions</h1>
<div class="loading-shade" *ngIf="loading"><mat-spinner diameter="50"></mat-spinner></div>
<mat-form-field appearance="fill">
<mat-label>Intitution</mat-label>
<mat-select #instcode>
<mat-option *ngFor="let inst of instcodes" [value]="inst">{{inst}}</mat-option>
</mat-select>
</mat-form-field>
<button mat-flat-button color="primary" (click)="test(instcode.value)">Test connection</button>
<mat-form-field appearance="fill">
<mat-label>Username</mat-label>
<input matInput #username>
</mat-form-field>
<button mat-flat-button color="primary" (click)="proxyGet(instcode.value, username.value)">Retrieve user from the remote institution</button>
<section>
<mat-card class="example-card">
<mat-card-header>
<mat-card-title>API Result</mat-card-title>
</mat-card-header>
<mat-card-content>
<textarea #apiResultArea [value]="apiResult | json"></textarea>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="primary" (click)="proxyPut(apiResultArea.value, instcode.value, username.value)"
[disabled]="!apiResult || loading">Update in the remote institution</button>
</mat-card-actions>
</mat-card>
</section>
mat-form-field {
width: 100%;
margin-top: 10px;
}
section {
margin-top: 20px;
}
textarea {
padding: 2px;
border-width: 1px;
$width: calc(100% - 6px);
width: $width;
max-width: $width;
height: 150px;
font-family: monospace;
display: block;
}
import { Component, OnInit } from '@angular/core';
import { HttpMethod } from '@exlibris/exl-cloudapp-angular-lib';
import { ToastrService } from 'ngx-toastr';
import { RestClient } from './rest-client';
import { finalize } from 'rxjs/operators';
@Component({
selector: 'app-proxy',
templateUrl: './proxy.component.html',
styleUrls: ['./proxy.component.scss']
})
export class ProxyComponent implements OnInit {
apiResult: any;
loading = false;
constructor(
private toastr: ToastrService,
private rest: RestClient,
) { }
ngOnInit() { }
test(instCode: string) {
this.loading = true;
this.rest.call('/conf/general', instCode)
.pipe(finalize(()=>this.loading=false))
.subscribe({
next: config => this.toastr.success(`Successfully connected to "${config.institution.desc}"`),
error: e => this.toastr.error('Could not connect to proxy: ' + e.message)
})
}
proxyGet(instCode: string, userName: string) {
this.loading = true;
this.rest.call(`/users/${userName}`, instCode)
.pipe(finalize(()=>this.loading=false))
.subscribe({
next: results => this.apiResult = results,
error: e => this.toastr.error('Could not retrieve data: ' + e.message)
});
}
proxyPut(requestBody: any, instCode: string, userName: string) {
this.loading = true;
let req = {
url: `/users/${userName}`,
method: HttpMethod.PUT,
requestBody: requestBody
};
this.rest.call(req, instCode)
.pipe(finalize(()=>this.loading=false))
.subscribe({
next: () => this.toastr.success('Success'),
error: e => this.toastr.error('Could not retrieve data: ' + e.message)
})
}
}
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { environment } from '../../environments/environment';
import { CloudAppEventsService, HttpMethod } from '@exlibris/exl-cloudapp-angular-lib';
import { catchError } from 'rxjs/operators';
enum AllowedHeader {
ACCEPT = "Accept",
CONTENT_TYPE = "Content-Type",
FOR_INST_CODE = "X-For-InstCode",
}
export interface Request {
url: string;
method?: HttpMethod;
headers?: {
[header in AllowedHeader]?: string;
};
queryParams?: {
[param: string]: any;
};
requestBody?: any;
}
export function restClientCreator(http: HttpClient, eventsService: CloudAppEventsService) {
return new RestClient(http, eventsService);
}
const PROXY_URL = environment.proxyUrl;
@Injectable()
export class RestClient {
private _token: string;
public constructor(public http: HttpClient, private eventsService: CloudAppEventsService) {
this.eventsService.getAuthToken().subscribe(token => this._token = token);
}
call<T = any>(request: string | Request, instCode: string): Observable<T> {
let req: Request = typeof request == 'string' ? { url: request, method: HttpMethod.GET } : request;
if (!req.headers) req.headers = {};
let headers = new HttpHeaders({
'content-type': req.headers["Content-Type"] || 'application/json',
'accept': req.headers.Accept || 'application/json',
'authorization': `Bearer ${this._token}`,
'x-for-instcode': instCode
});
let params = new HttpParams(req.queryParams);
const url = PROXY_URL + req.url;
const options = { headers, params };
switch (req.method) {
case HttpMethod.GET:
return wrapError(this.http.get<T>(url, options));
case HttpMethod.PUT:
return wrapError(this.http.put<T>(url, req.requestBody, options));
case HttpMethod.POST:
return wrapError(this.http.post<T>(url, req.requestBody, options));
case HttpMethod.DELETE:
return wrapError(this.http.delete<T>(url, options));
}
}
}
const wrapError = (obs: Observable<any>): Observable<any> => {
return obs.pipe(
catchError(err=>{
if (err.error && err.error.errorList) {
err.message = err.error.errorList.error[0].errorMessage
};
return throwError(err);
})
)
}
@jweisman
Copy link
Author

jweisman commented Sep 4, 2020

Add this component to your Cloud App and add the rest client to the app.module.ts file as follows:

...
  providers: [
    {
      provide: RestClient,
      useFactory: restClientCreator,
      deps: [HttpClient, CloudAppEventsService]
   },
  ],
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment