Created
September 28, 2018 12:39
-
-
Save iget-master/ac542c09062b441d9613cb0e2b3354a2 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 {catchError, map} from 'rxjs/operators'; | |
import {Inject, Injectable} from '@angular/core'; | |
import {HttpClient, HttpErrorResponse} from '@angular/common/http'; | |
import {Observable} from 'rxjs/internal/Observable'; | |
import {environment} from '../../../environments/environment'; | |
import {User} from '../user/user'; | |
import {Model} from '../abstract/model'; | |
import {zip} from 'rxjs'; | |
import {of as observableOf} from 'rxjs'; | |
@Injectable() | |
export abstract class ApiProvider<TModel extends Model, TResponse> { | |
protected resourceName: string; | |
constructor( | |
public http: HttpClient | |
// @todo: Implement an AlertController | |
// public alertController: AlertController, | |
) { | |
} | |
/** | |
* Handle all HttpErrorResponse errors | |
* | |
* @param {HttpErrorResponse} error | |
* @returns {Observable<any>} | |
*/ | |
handleError(error: HttpErrorResponse) { | |
switch (error.status) { | |
case 0: | |
this.handleOfflineError(error); | |
break; | |
case 422: | |
this.handleFormError(error); | |
break; | |
case 500: | |
this.handleInternalError(error); | |
break; | |
} | |
throw error; | |
// return Observable.empty(); | |
} | |
/** | |
* Handle internal errors (uncaught errors) | |
* @param {HttpErrorResponse} error | |
*/ | |
handleInternalError(error: HttpErrorResponse) { | |
this.presentErrorAlert('Ocorreu um erro interno, tente novamente.'); | |
} | |
/** | |
* Handle form validation errors | |
* @param {HttpErrorResponse} error | |
*/ | |
handleFormError(error: HttpErrorResponse) { | |
const errors = []; | |
const failedFields = JSON.parse(error.error).errors; | |
for (const field in failedFields) { | |
if (failedFields.hasOwnProperty(field)) { | |
errors.push(failedFields[field]); | |
} | |
} | |
const message = errors.join(' '); | |
this.presentErrorAlert(message); | |
} | |
/** | |
* Handle Offline Errors | |
* @param {HttpErrorResponse} error | |
*/ | |
handleOfflineError(error: HttpErrorResponse) { | |
this.presentErrorAlert('Parece que você está sem internet!'); | |
} | |
/** | |
* Present an error alert containing given message | |
* @param {string} message | |
*/ | |
private presentErrorAlert(message: string) { | |
alert(message); | |
// @todo: Implement an AlertController | |
} | |
public load( | |
id: string|number, | |
columns?: Array<string>, | |
include?: Array<string> | |
): Observable<TModel> { | |
const url = new URL(`${environment.API.ENDPOINT}/${this.resourceName}/${id}`); | |
if (columns) { | |
url.searchParams.set('only', columns.join(',')); | |
} | |
if (include) { | |
url.searchParams.set('include', include.join(',')); | |
} | |
return this.http.get<SingleResponse<TResponse>>(url.toString()).pipe( | |
map((response) => { | |
return this.createFromResponse(response); | |
})); | |
} | |
public list( | |
page?: PaginationParameters, | |
columns?: Array<string>, | |
include?: Array<string>, | |
sortBy?: {direction: string, column: string}, | |
url?: URL | |
): Observable<Collection<TModel>> { | |
if (!url) { | |
url = new URL(`${environment.API.ENDPOINT}/${this.resourceName}`); | |
} | |
if (!page) { | |
url.searchParams.set('page_size', 'all'); | |
} else { | |
url.searchParams.set('page_size', page.size.toString()); | |
url.searchParams.set('page', page.page.toString()); | |
} | |
if (columns) { | |
url.searchParams.set('only', columns.join(',')); | |
} | |
if (include) { | |
url.searchParams.set('include', include.join(',')); | |
} | |
if (sortBy) { | |
let parsedSortBy = sortBy.column; | |
if (sortBy.direction === 'asc') { | |
parsedSortBy = '+' + parsedSortBy; | |
} else { | |
parsedSortBy = '-' + parsedSortBy; | |
} | |
url.searchParams.set('sort_by', parsedSortBy); | |
} | |
return this.http.get<CollectionResponse<TResponse>>(url.toString()).pipe( | |
map((response) => { | |
return { | |
data: this.collectionFromResponse(response), | |
meta: response.meta | |
}; | |
})); | |
} | |
/** | |
* | |
* @param {string} query | |
* @param {PaginationParameters} page | |
* @param {Array<string>} columns | |
* @param {Array<string>} include | |
* @param sortBy | |
* @returns {Observable<Collection<TModel extends Model>>} | |
*/ | |
public search( | |
query: string, | |
page: PaginationParameters, | |
columns?: Array<string>, | |
include?: Array<string>, | |
sortBy?: {direction: string, column: string} | |
): Observable<Collection<TModel>> { | |
query = encodeURI(query); | |
const url = new URL(`${environment.API.ENDPOINT}/${this.resourceName}/search/${query}`); | |
return this.list(page, columns, include, sortBy, url); | |
} | |
/** | |
* @param {string | number} id | |
* @param data | |
* @param include | |
* @returns {Observable<TModel extends Model>} | |
*/ | |
public update(id: string|number, data: any, include?: Array<string>): Observable<TModel> { | |
const url = new URL(`${environment.API.ENDPOINT}/${this.resourceName}/${id}`); | |
if (include) { | |
url.searchParams.set('include', include.join(',')); | |
} | |
return this.http.patch<SingleResponse<TResponse>>(url.toString(), data).pipe( | |
map((response) => { | |
return this.createFromResponse(response); | |
})); | |
} | |
/** | |
* @param data | |
* @returns {Observable<TModel extends Model>} | |
*/ | |
public create(data: any): Observable<TModel> { | |
const url = new URL(`${environment.API.ENDPOINT}/${this.resourceName}`); | |
return this.http.post<SingleResponse<TResponse>>(url.toString(), data).pipe( | |
map((response) => { | |
return this.createFromResponse(response); | |
})); | |
} | |
/** | |
* Try to destroy given models, and return an array with the destroyed | |
* models. | |
* @param {Array<TModel extends Model>} models | |
* @returns {Observable<Array<number>>} | |
*/ | |
public destroy(models: Array<TModel>): Observable<Array<TModel>> { | |
const requests: Array<Observable<any>> = models.map((model) => { | |
const url = new URL(`${environment.API.ENDPOINT}/${this.resourceName}/${model.primary_key}`); | |
return this.http.delete<Array<boolean>>(url.toString()).pipe( | |
map((response) => { | |
return { | |
id: model.primary_key, | |
destroyed: response | |
}; | |
})); | |
}); | |
return zip(...requests); | |
} | |
/** | |
* This method is responsible by calling TModel's static collectionFromResponse. | |
* It's a workaround to accessing it static methods due typescript limitation. | |
* | |
* You should override it on every service that extends this class, with the | |
* right model (TModel) for that service. | |
* | |
* @param {CollectionResponse<TResponse>} response | |
* @returns {Array<TModel extends Model>} | |
*/ | |
collectionFromResponse(response: CollectionResponse<TResponse>): Array<TModel> { | |
return Model.collectionFromResponse(response.data); | |
} | |
/** | |
* This method is responsible by calling TModel's static createFromResponse. | |
* It's a workaround to accessing it static methods due typescript limitation. | |
* | |
* You should override it on every service that extends this class, with the | |
* right model (TModel) for that service. | |
* | |
* @param {SingleResponse<TResponse>} response | |
* @returns {TModel extends Model} | |
*/ | |
createFromResponse(response: SingleResponse<TResponse>): TModel { | |
return Model.createFromResponse(response.data); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment