-
-
Save dennishn/7155ce946e6c40f70f3e4aa2e703b06b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import {Injectable, Inject} from '@angular/core'; | |
| import { | |
| Http, | |
| Headers, | |
| Response, | |
| BrowserXhr, | |
| RequestOptions, | |
| RequestOptionsArgs, | |
| RequestMethod, | |
| Request | |
| } from '@angular/http'; | |
| import {Observable} from 'rxjs/Observable'; | |
| import {Observer} from 'rxjs/Observer'; | |
| import {Subject} from 'rxjs/Subject'; | |
| import 'rxjs/add/observable/throw'; | |
| import 'rxjs/add/observable/fromPromise'; | |
| import 'rxjs/add/operator/map'; | |
| import 'rxjs/add/operator/mergeMap'; | |
| import 'rxjs/add/operator/toPromise'; | |
| import 'rxjs/add/operator/catch'; | |
| import 'rxjs/add/operator/finally'; | |
| import {ResponseOptions} from "@angular/http"; | |
| import {API_ENDPOINTS, IApiEndpoints} from "../config/API_ENDPOINTS.config"; | |
| import {APPLICATION_SETTINGS, IApplicationSettings} from "../config/APPLICATION_SETTINGS.config"; | |
| import {BehaviorSubject} from "rxjs"; | |
| import {User} from '../authentication/user.model'; | |
| // Annoying that these arent exposed in Angular since we need to adda a Progress Response Type (to track upload progress) | |
| enum ResponseType { | |
| Basic = 0, | |
| Cors = 1, | |
| Default = 2, | |
| Error = 3, | |
| Opaque = 4, | |
| Progress = 5 | |
| } | |
| @Injectable() | |
| export class AuthHttpService { | |
| private STORAGE_IDENTIFIER: string; | |
| public TOKEN: any; | |
| private _currentUser: BehaviorSubject<any>; | |
| public currentUser: Observable<any>; | |
| constructor(@Inject(API_ENDPOINTS) private endpoints: IApiEndpoints, | |
| @Inject(APPLICATION_SETTINGS) private appSettings: IApplicationSettings, | |
| private http: Http, | |
| private browserXhr: BrowserXhr) { | |
| this.STORAGE_IDENTIFIER = appSettings.storageIdentifier + 'AUTH'; | |
| this._currentUser = new BehaviorSubject<any>(null); | |
| this.currentUser = this._currentUser.asObservable(); | |
| } | |
| public get(url: string, options?: RequestOptionsArgs): Observable<Response> { | |
| /** | |
| * Why are you making `body` part of the payload here? Its a GET request, and you can't attach payloads to these | |
| */ | |
| return this.requestHelper({body: '', method: RequestMethod.Get, url: url}, options); | |
| } | |
| /** | |
| * Great solution with the first argument of the requestHelper being a RequestOptionsArgs! | |
| */ | |
| public post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> { | |
| return this.requestHelper({body: body, method: RequestMethod.Post, url: url}, options); | |
| } | |
| public put(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> { | |
| return this.requestHelper({body: body, method: RequestMethod.Put, url: url}, options); | |
| } | |
| public delete(url: string, options?: RequestOptionsArgs): Observable<Response> { | |
| return this.requestHelper({body: '', method: RequestMethod.Delete, url: url}, options); | |
| } | |
| public patch(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> { | |
| return this.requestHelper({body: body, method: RequestMethod.Patch, url: url}, options); | |
| } | |
| public upload(url: string, body: any, options?: RequestOptionsArgs, forceMethod?: string): Observable<Response> { | |
| return new Observable<Response>((responseObserver: Observer<Response>) => { | |
| const _xhr: XMLHttpRequest = this.browserXhr.build(); | |
| const method = forceMethod || 'POST'; | |
| _xhr.open(method, url); | |
| const onLoad = () => { | |
| const headers: Headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders()); | |
| const responseUrl: string = this.getResponseURL(_xhr) || url; | |
| let responseOptions = new ResponseOptions({ | |
| body: _xhr.response, | |
| status: _xhr.status, | |
| headers: headers, | |
| type: ResponseType.Default, | |
| statusText: _xhr.statusText || 'OK', | |
| url: responseUrl | |
| }); | |
| let response = new Response(responseOptions); | |
| response.ok = (_xhr.status >= 200 && _xhr.status < 300); | |
| if (response.ok) { | |
| responseObserver.next(response); | |
| responseObserver.complete(); | |
| return; | |
| } | |
| responseObserver.error(response); | |
| }; | |
| const onError = (err: ErrorEvent) => { | |
| let responseOptions = new ResponseOptions({ | |
| body: err, | |
| type: ResponseType.Error, | |
| status: _xhr.status, | |
| statusText: _xhr.statusText | |
| }); | |
| responseObserver.error(new Response(responseOptions)); | |
| }; | |
| const onProgress = (progress: ProgressEvent) => { | |
| const percentageComplete = Math.round(progress.loaded / progress.total * 100); | |
| let responseOptions = new ResponseOptions({ | |
| body: percentageComplete, | |
| type: ResponseType.Progress, | |
| status: _xhr.status, | |
| statusText: _xhr.statusText | |
| }); | |
| // For the callee of the .uploadFile method | |
| responseObserver.next(new Response(responseOptions)); | |
| }; | |
| //to be refactored!!!!! | |
| // let reqOptions = this.addAuthHeader(options, token); | |
| // reqOptions.headers.forEach((values, name) => { | |
| // _xhr.setRequestHeader(name, values.join(',')); | |
| // }); | |
| _xhr.addEventListener('load', onLoad); | |
| _xhr.addEventListener('error', onError); | |
| _xhr.upload.addEventListener('progress', onProgress); | |
| _xhr.send(body); | |
| return () => { | |
| _xhr.removeEventListener('load', onLoad); | |
| _xhr.removeEventListener('error', onError); | |
| _xhr.upload.removeEventListener('progress', onProgress); | |
| _xhr.abort(); | |
| } | |
| }); | |
| } | |
| private request(request: Request): Observable<Response> { | |
| /** | |
| * What are you going to do if the Token Refresh is denied? Theres no Catch :o | |
| */ | |
| return Observable.fromPromise(this.tokenGetter()).mergeMap((token: string) => { | |
| request.headers.set('Authorization', 'Bearer ' + token); | |
| return this.http.request(request); | |
| }); | |
| } | |
| private tokenGetter(): Promise<string> { | |
| /** | |
| * Why are you working with a promise? I would stick with Observables as you do throughout the rest of the code | |
| */ | |
| return new Promise((resolve, reject) => { | |
| let token = localStorage.getItem(this.STORAGE_IDENTIFIER + '_TOKEN'); | |
| let decodedToken; | |
| let decodedTokenToDate; | |
| if (token) { | |
| /** | |
| * This really needs to go into its own isolated method | |
| */ | |
| //decode the token | |
| let base64Url = token.split('.')[1]; | |
| let base64 = base64Url.replace('-', '+').replace('_', '/'); | |
| decodedToken = JSON.parse(window.atob(base64)); | |
| console.log('base64', decodedToken) | |
| decodedTokenToDate = decodedToken.exp * 1000; | |
| } | |
| console.log('rett'); | |
| if (decodedTokenToDate < new Date()) { | |
| console.log('its invalid') | |
| /** | |
| * Why not use your requestHelper method here to build the headers etc.? This solution is not DRY. | |
| */ | |
| let additionalHeaders = new Headers(); | |
| additionalHeaders.append('Authorization', 'Bearer ' + token); | |
| additionalHeaders.append('N-Meta', 'web;development'); | |
| let requestOptions = new RequestOptions(); | |
| requestOptions.headers = additionalHeaders; | |
| console.log('inside refresh token'); | |
| let tokenUpdateUrl = [ | |
| this.endpoints.root.production, | |
| this.endpoints.usersProperties.refreshToken | |
| ].join('/'); | |
| /** | |
| * Shouldnt this request be an Authenticated request? In postman you need to send your Bearer Token... | |
| */ | |
| this.http.patch(tokenUpdateUrl, null, requestOptions) | |
| /** | |
| * What are you going to do if the Token Refresh is denied? Theres no Catch :o | |
| */ | |
| .subscribe( | |
| response => { | |
| let body = response.json(); | |
| localStorage.setItem(this.STORAGE_IDENTIFIER + '_TOKEN', body.token); | |
| resolve(body.token); | |
| } | |
| ) | |
| } else { | |
| //resolve the promise by senind the token | |
| resolve(token); | |
| } | |
| }) | |
| } | |
| private requestHelper(requestArgs: RequestOptionsArgs, additionalOptions?: RequestOptionsArgs): Observable<Response> { | |
| let options = new RequestOptions(requestArgs); | |
| if (additionalOptions) { | |
| options = options.merge(additionalOptions); | |
| } | |
| console.log(options); | |
| if (!options.headers) { | |
| options.headers = new Headers(); | |
| } | |
| if (!options.headers.has('N-Meta')) { | |
| options.headers.append('N-Meta', 'web;development'); | |
| } | |
| return this.request(new Request(options)); | |
| } | |
| // More "hidden" gems in the Http files in Angular ... They really dont want to make it easy :( | |
| private getResponseURL(xhr: any): string { | |
| if ('responseURL' in xhr) { | |
| return xhr.responseURL; | |
| } | |
| if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) { | |
| return xhr.getResponseHeader('X-Request-URL'); | |
| } | |
| return; | |
| } | |
| /** | |
| * Check if the user is authenticated based on their auth token being present in Storage | |
| * @returns {Promise<boolean>|PromiseLike<boolean>} | |
| */ | |
| isAuthenticated() { | |
| const token = localStorage.getItem(this.STORAGE_IDENTIFIER + '_TOKEN'); | |
| return !!token; | |
| } | |
| /** | |
| * Helper method for updating user and authentication state | |
| * @param data | |
| * @returns {Observable<T>} | |
| * @private | |
| */ | |
| private _setUser(data) { | |
| let user = Object.assign(new User(), data); | |
| this._currentUser.next(user); | |
| localStorage.setItem(this.STORAGE_IDENTIFIER + '_TOKEN', user.token); | |
| } | |
| /** | |
| * | |
| * @returns {Observable<R>} | |
| */ | |
| me(): Observable<any> { | |
| let url = [ | |
| this.endpoints.root.production, | |
| this.endpoints.usersProperties.me | |
| ].join('/'); | |
| return this.get(url) | |
| .map(response => { | |
| let body = response.json(); | |
| this._setUser(body); | |
| return body; | |
| }) | |
| // .catch(this._handleError.bind(this)); | |
| } | |
| /** | |
| * Login | |
| * | |
| * @param email {string} | |
| * @param password {string} | |
| * @returns {Observable<R>} | |
| */ | |
| login(email: string, password: string): Observable<any> { | |
| let url = [ | |
| this.endpoints.root.production, | |
| this.endpoints.usersProperties.login | |
| ].join('/'); | |
| let payload = { | |
| email: email, | |
| password: password, | |
| }; | |
| return this.post(url, payload) | |
| .map(response => { | |
| let body = response.json(); | |
| console.log(body); | |
| this._setUser(body); | |
| console.log('body', body); | |
| return body; | |
| // return Observable.fromPromise(this._setUser(body)); | |
| } | |
| ) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment