Skip to content

Instantly share code, notes, and snippets.

@iget-master
Created September 28, 2018 12:39
Show Gist options
  • Save iget-master/ac542c09062b441d9613cb0e2b3354a2 to your computer and use it in GitHub Desktop.
Save iget-master/ac542c09062b441d9613cb0e2b3354a2 to your computer and use it in GitHub Desktop.
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