Skip to content
This repository has been archived by the owner on Mar 29, 2024. It is now read-only.

refactor product service to support mock apis #11

Merged
merged 1 commit into from Mar 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 24 additions & 13 deletions src/app/services/product.service.ts
Expand Up @@ -2,48 +2,58 @@ import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { catchError, find, map } from 'rxjs/operators';

import { environment } from '../../environments/environment';
import { Product } from '../models'
import { MatSnackBar } from '@angular/material/snack-bar';

@Injectable({
providedIn: 'root'
})
export class ProductService {

readonly imageUrl = 'https://fruitshoppe.firebaseapp.com/images'; // URL of product images folder
readonly productsEndpoint = '/products'; // API route to retrieve products
readonly apiUrl: string;

constructor(
private sanitizer: DomSanitizer,
private http: HttpClient
private http: HttpClient,
private snackBar: MatSnackBar,
) {
// based on whether to use mocks or the API server, build the full API URL
this.apiUrl = environment.useMockApi ? `${environment.localApiRoot}${this.productsEndpoint}` :
`${environment.remoteApiRoot}${this.productsEndpoint}`;
this.apiUrl = environment.useMockApi ? `${environment.localApiRoot}/products.json` :
`${environment.remoteApiRoot}/products`;
}

/**
* Gets an observable that emits a product from the catalog.
* @param id the identifier of the product
*/
getProduct(id: number): Observable<Product | null> {
const url = `${this.apiUrl}/${id}`;
// if using the mock API, just get all products and find the one with the given id
if (environment.useMockApi) {
return this.http.get<Product>(this.apiUrl).pipe(
map(product => this.setImageUrl(product)),
find(product => product.id === id),
catchError(this.handleError<Product>('getProduct', null))
);
} else {
const url = `${this.apiUrl}/${id}`;

return this.http.get<Product>(url).pipe(
map(product => this.setImageUrl(product)),
catchError(this.handleError<Product>('getProduct', null))
);
return this.http.get<Product>(url).pipe(
map(product => this.setImageUrl(product)),
catchError(this.handleError<Product>('getProduct', null))
);
}
}

/**
* Gets an observable that emits a list of products from the catalog.
* @param query An optional query string to search for products
*/
getProducts(query?: string): Observable<Product[]> {
// searching for products will not work for mocks
// TODO (van) allow query parameter to filter mock products.json
const url = query ? `${this.apiUrl}?q=${query}` : this.apiUrl;

return this.http.get<Product[]>(url).pipe(
Expand All @@ -59,9 +69,10 @@ export class ProductService {
*/
private handleError<T>(operation = 'operation', result?: T) {
return (error: Error): Observable<T> => {
console.error(`${operation} failed: ${error.message}`);
const { message } = error;

// TODO (van) possibly open a snackbar to inform the user of error
console.error(`${operation} failed: ${message}`);
this.snackBar.open(`Failed to get product(s). ${message}`, 'OK');

return of(result as T);
};
Expand Down
File renamed without changes.