import { Injectable } from '@angular/core'; import { Http, Response } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import { AsyncSubject } from 'rxjs/AsyncSubject'; import 'rxjs/add/operator/switchMap'; import { LocationService } from 'app/shared/location.service'; import { Logger } from 'app/shared/logger.service'; const FILE_NOT_FOUND_URL = 'file-not-found'; export interface DocumentContents { title: string; contents: string; } @Injectable() export class DocumentService { private cache = new Map>(); private fileNotFoundPath = this.computePath(FILE_NOT_FOUND_URL); currentDocument: Observable; constructor(private logger: Logger, private http: Http, location: LocationService) { // Whenever the URL changes we try to get the appropriate doc this.currentDocument = location.currentUrl.switchMap(url => this.getDocument(url)); } private getDocument(url: string) { this.logger.log('getting document', url); const path = this.computePath(url); if ( !this.cache.has(path)) { this.cache.set(path, this.fetchDocument(path)); } return this.cache.get(path); } private fetchDocument(path: string) { this.logger.log('fetching document from', path); const subject = new AsyncSubject(); this.http .get(path) .map(res => res.json()) .catch((error: Response) => { if (error.status === 404) { if (path !== this.fileNotFoundPath) { this.logger.error(`Document file not found at '${path}'`); // using `getDocument` means that we can fetch the 404 doc contents from the server and cache it return this.getDocument(FILE_NOT_FOUND_URL); } else { return Observable.of({ title: 'Not Found', contents: 'Document not found' }); } } else { throw error; } }) .subscribe(subject); return subject.asObservable(); } private computePath(url) { url = '/' + url; url = url.endsWith('/') ? url + 'index' : url; return 'content/docs' + url + '.json'; } }