Last active
March 13, 2023 10:38
-
-
Save aasumitro/9d21fcd52e199f3ad54e5202eedabb55 to your computer and use it in GitHub Desktop.
ANGULAR
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} from '@angular/core'; | |
import {CONFIGS} from '../config'; | |
import {RestService} from './rest.service'; | |
@Injectable({providedIn: "root"}) | |
export class AuthRestService { | |
private pathResourceURL = CONFIGS.api.rest.path; | |
constructor(private restService: RestService) {} | |
getValidateSession(): Promise<any> { | |
return new Promise((resolve, reject) => { | |
const path = this.pathResourceURL.auth.validateSession | |
this.restService | |
.getReq(path, null) | |
.then((res) => resolve(res)) | |
.catch((err) => reject(err)) | |
}); | |
} | |
} |
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
export const CONFIGS = { | |
api: { | |
rest: { | |
dev_url: "localhost:8000/api", | |
prod_url: "becoop.bakode.id/api", | |
version: "v1", | |
path: { | |
auth: { | |
validateSession: "auth/session", | |
validateCredential: "auth/credential", | |
validateAccess: "auth/access" | |
} | |
} | |
} | |
}, | |
infra: { | |
sentry: {}, | |
firebase: {} | |
} | |
} |
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
// noinspection DuplicatedCode | |
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; | |
import {TestBed} from '@angular/core/testing'; | |
import {ProductUsecase} from './product.usecase'; | |
import {RouterTestingModule} from '@angular/router/testing'; | |
import {fakeResponseForSpecifiedUsecase, fakeResponseWithPayloadObject} from '../../_mocks/fake-data'; | |
import {CONFIG} from '../../../../services/config.service'; | |
import {ResponseStatus} from '../../enums/response.status'; | |
describe('ProductUsecase', () => { | |
let httpTestingController: HttpTestingController; | |
let usecase: ProductUsecase; | |
beforeEach(() => { | |
TestBed.configureTestingModule({ | |
imports: [HttpClientTestingModule, RouterTestingModule], | |
providers: [ProductUsecase] | |
}); | |
httpTestingController = TestBed.inject(HttpTestingController); | |
usecase = TestBed.inject(ProductUsecase); | |
}); | |
afterEach(() => { | |
httpTestingController.verify(); | |
}); | |
it('should be created', () => { | |
expect(usecase).toBeTruthy(); | |
}); | |
const testsCase = [ | |
{ | |
title: 'should call products$, expect success', | |
error: false, | |
mockData: fakeResponseForSpecifiedUsecase, | |
usecaseFunc: 'products$', | |
params: {}, | |
pathUrl: CONFIG.URL.myPharma.v1.products, | |
reqMethod: 'GET', | |
}, | |
{ | |
title: 'should call products$, expect failed', | |
error: true, | |
errorData: () => new ErrorEvent('Network Error'), | |
usecaseFunc: 'products$', | |
params: {}, | |
pathUrl: CONFIG.URL.myPharma.v1.products, | |
reqMethod: 'GET', | |
}, | |
{ | |
title: 'should call product$, expect success', | |
error: false, | |
mockData: fakeResponseForSpecifiedUsecase, | |
usecaseFunc: 'product$', | |
params: '12345', | |
pathUrl: CONFIG.URL.myPharma.v1.productDetail('12345'), | |
reqMethod: 'GET', | |
}, | |
{ | |
title: 'should call product$, expect failed', | |
error: true, | |
errorData: () => new ErrorEvent('Network Error'), | |
usecaseFunc: 'product$', | |
params: '12345', | |
pathUrl: CONFIG.URL.myPharma.v1.productDetail('12345'), | |
reqMethod: 'GET', | |
}, | |
{ | |
title: 'should call productReviews$, expect success', | |
error: false, | |
mockData: fakeResponseForSpecifiedUsecase, | |
usecaseFunc: 'productReviews$', | |
params: ['12345', {}], | |
pathUrl: CONFIG.URL.myPharma.v1.productReviews('12345'), | |
reqMethod: 'GET', | |
}, | |
{ | |
title: 'should call productReviews$, expect failed', | |
error: true, | |
errorData: () => new ErrorEvent('Network Error'), | |
usecaseFunc: 'productReviews$', | |
params: ['12345', {}], | |
pathUrl: CONFIG.URL.myPharma.v1.productReviews('12345'), | |
reqMethod: 'GET', | |
}, | |
{ | |
title: 'should call productReviewCount$, expect success', | |
error: false, | |
mockData: fakeResponseWithPayloadObject, | |
usecaseFunc: 'productReviewCount$', | |
params: '12345', | |
pathUrl: CONFIG.URL.myPharma.v1.productReviewCount('12345'), | |
reqMethod: 'GET', | |
}, | |
{ | |
title: 'should call productReviewCount$, expect failed', | |
error: true, | |
errorData: () => new ErrorEvent('Network Error'), | |
usecaseFunc: 'productReviewCount$', | |
params: '12345', | |
pathUrl: CONFIG.URL.myPharma.v1.productReviewCount('12345'), | |
reqMethod: 'GET', | |
}, | |
{ | |
title: 'should call productSearch$, expect success', | |
error: false, | |
mockData: fakeResponseForSpecifiedUsecase, | |
usecaseFunc: 'productSearch$', | |
params: {}, | |
pathUrl: CONFIG.URL.myPharma.v1.searchProduct, | |
reqMethod: 'GET', | |
}, | |
{ | |
title: 'should call productSearch$, expect failed', | |
error: true, | |
errorData: () => new ErrorEvent('Network Error'), | |
usecaseFunc: 'productSearch$', | |
params: {}, | |
pathUrl: CONFIG.URL.myPharma.v1.searchProduct, | |
reqMethod: 'GET', | |
}, | |
{ | |
title: 'should call performGiveReview, expect success', | |
error: false, | |
mockData: fakeResponseForSpecifiedUsecase, | |
usecaseFunc: 'performGiveReview', | |
params: [{}, '12345'], | |
pathUrl: CONFIG.URL.myPharma.v1.addReviewToProduct('12345'), | |
reqMethod: 'POST', | |
}, | |
]; | |
testsCase.forEach(test => { | |
it(test.title, done => { | |
switch (test.usecaseFunc) { | |
case'products$': | |
usecase.products$(test.params).subscribe(data => { | |
if (data.state !== ResponseStatus.loading) { | |
validateData(test, data, done); | |
} | |
}); | |
break; | |
case'product$': | |
usecase.product$(test.params as string).subscribe(data => { | |
if (data.state !== ResponseStatus.loading) { | |
validateData(test, data, done); | |
} | |
}); | |
break; | |
case'productReviews$': | |
usecase.productReviews$(test.params[0] as string, test.params[1]).subscribe(data => { | |
if (data.state !== ResponseStatus.loading) { | |
validateData(test, data, done); | |
} | |
}); | |
break; | |
case'productSearch$': | |
usecase.productSearch$(test.params).subscribe(data => { | |
if (data.state !== ResponseStatus.loading) { | |
validateData(test, data, done); | |
} | |
}); | |
break; | |
case'productReviewCount$': | |
usecase.productReviewCount$(test.params as string).subscribe(data => { | |
if (data.state !== ResponseStatus.loading) { | |
validateData(test, data, done); | |
} | |
}); | |
break; | |
case 'performGiveReview': | |
usecase.performGiveReview(test.params[0], test.params[1]); | |
done(); | |
break; | |
} | |
const url = `${CONFIG.apiEndpoint}${test.pathUrl}`; | |
const req = httpTestingController.expectOne(url); | |
expect(req.request.method).toEqual(test.reqMethod); | |
if (!test.error) { req.flush(test.mockData); } | |
if (test.error) { req.error(test.errorData()); } | |
}); | |
}); | |
const validateData = (test, data, done) => { | |
if (!test.error) { | |
expect(data.state).toEqual(ResponseStatus.success); | |
expect(data.error).toEqual(null); | |
expect(data.value).toBeDefined(); | |
expect(data.value).toBeInstanceOf(Object); | |
if (data.value instanceof Array) { | |
expect(data.value.length).toBeGreaterThan(0); | |
} | |
done(); | |
} | |
if (test.error) { | |
expect(data.state).toEqual(ResponseStatus.error); | |
expect(data.value).toEqual(null); | |
expect(data.error).toBeTruthy(); | |
expect(data?.error?.message).toBeDefined(); | |
done(); | |
} | |
}; | |
}); |
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} from '@angular/core'; | |
import {MyPharmaService} from '../../../../services/mypharma.service'; | |
import {defer, from, Observable, of, throwError} from 'rxjs'; | |
import {HttpRequestState} from '../../utils/http-request-state.util'; | |
import {catchError, concatMap, delay, map, retryWhen, startWith} from 'rxjs/operators'; | |
import {ResponseStatus} from '../../enums/response.status'; | |
import {Product, ProductRating, ProductRatingCount} from '../models/product'; | |
import {Paging} from '../models/paging'; | |
@Injectable() | |
export class ProductUsecase | |
{ | |
constructor(private myPharmaService: MyPharmaService) {} | |
products$(params: any): Observable<HttpRequestState<Product[]>> { | |
return (defer(() => | |
from(this.myPharmaService.getProducts(params)).pipe( | |
retryWhen(errors => errors.pipe( | |
concatMap((error, index) => { | |
if (index >= 2) { | |
return throwError(error); | |
} | |
return of(error).pipe(delay(1000)); | |
}) | |
)), | |
).pipe( | |
map((data) => ({ | |
state: ResponseStatus.success, | |
value: data?.payload?.products as Product[], | |
paging: data?.payload as Paging, | |
error: null | |
})), | |
catchError((error) => of({ | |
state: ResponseStatus.error, | |
value: null, | |
error | |
})), | |
startWith({ | |
state: ResponseStatus.loading, | |
value: null, | |
error: null | |
}), | |
))); | |
} | |
product$(productId: string): Observable<HttpRequestState<Product>> { | |
return defer(() => | |
from(this.myPharmaService.getProductDetail(productId)).pipe( | |
retryWhen(errors => errors.pipe( | |
concatMap((error, index) => { | |
if (index >= 2) { | |
return throwError(error); | |
} | |
return of(error).pipe(delay(1000)); | |
}) | |
)), | |
).pipe( | |
map((data: any) => ({ | |
state: ResponseStatus.success, | |
value: data?.payload?.product as Product, | |
error: null | |
})), | |
catchError((error) => of({ | |
state: ResponseStatus.error, | |
value: null, | |
error | |
})), | |
startWith({ | |
state: ResponseStatus.loading, | |
value: null, | |
error: null | |
}), | |
) | |
); | |
} | |
productReviews$(productId: string, params: any): Observable<HttpRequestState<ProductRating[]>> { | |
return defer(() => | |
from(this.myPharmaService.getProductReviews(productId, params)).pipe( | |
retryWhen(errors => errors.pipe( | |
concatMap((error, index) => { | |
if (index >= 2) { | |
return throwError(error); | |
} | |
return of(error).pipe(delay(1000)); | |
}) | |
)), | |
).pipe( | |
map((data: any) => ({ | |
state: ResponseStatus.success, | |
value: data?.payload?.productReviews as ProductRating[], | |
paging: data?.payload, | |
error: null | |
})), | |
catchError((error) => of({ | |
state: ResponseStatus.error, | |
value: null, | |
error | |
})), | |
startWith({ | |
state: ResponseStatus.loading, | |
value: null, | |
error: null | |
}), | |
) | |
); | |
} | |
productReviewCount$(productId: string): Observable<HttpRequestState<ProductRatingCount>> { | |
return defer(() => | |
from(this.myPharmaService.getProductReviewCount(productId)).pipe( | |
retryWhen(errors => errors.pipe( | |
concatMap((error, index) => { | |
if (index >= 2) { | |
return throwError(error); | |
} | |
return of(error).pipe(delay(1000)); | |
}) | |
)), | |
).pipe( | |
map((data: any) => ({ | |
state: ResponseStatus.success, | |
value: data?.payload as ProductRatingCount, | |
error: null | |
})), | |
catchError((error) => of({ | |
state: ResponseStatus.error, | |
value: null, | |
error | |
})), | |
startWith({ | |
state: ResponseStatus.loading, | |
value: null, | |
error: null | |
}), | |
) | |
); | |
} | |
productSearch$(params: any): Observable<HttpRequestState<Product[]>> { | |
return (defer(() => | |
from(this.myPharmaService.searchProduct(params)).pipe( | |
retryWhen(errors => errors.pipe( | |
concatMap((error, index) => { | |
if (index >= 2) { | |
return throwError(error); | |
} | |
return of(error).pipe(delay(1000)); | |
}) | |
)), | |
).pipe( | |
map((data) => ({ | |
state: ResponseStatus.success, | |
value: data?.payload?.searchResult as Product[], | |
paging: data?.payload as Paging, | |
error: null | |
})), | |
catchError((error) => of({ | |
state: ResponseStatus.error, | |
value: null, | |
error | |
})), | |
startWith({ | |
state: ResponseStatus.loading, | |
value: null, | |
error: null | |
}), | |
))); | |
} | |
performGiveReview = async (params: any, productId: string) => | |
await this.myPharmaService.addProductReview(params, productId); | |
} |
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, isDevMode} from '@angular/core'; | |
import {HttpClient, HttpErrorResponse, HttpHeaders} from '@angular/common/http'; | |
import {Router} from '@angular/router'; | |
import {CONFIGS} from '../config'; | |
import {catchError, map} from 'rxjs'; | |
@Injectable({providedIn: "root"}) | |
export class RestService { | |
private AUTH_PATH = 'auth' | |
constructor( | |
public http: HttpClient, | |
private router: Router | |
) {} | |
async getReq(pathUrl: string, params: any) { | |
const headers = new HttpHeaders(); | |
headers.append('Content-Type', 'application/json'); | |
const url = `${this.getAPIUrl()}/${pathUrl}`; | |
return this.http.get<any>(url, {headers, params}).pipe( | |
map(response => response), | |
catchError(this.handleError), | |
); | |
} | |
async postReq(pathUrl: string, body: any) { | |
const headers = new HttpHeaders(); | |
headers.append('Content-Type', 'application/json'); | |
const url = `${this.getAPIUrl()}/${pathUrl}`; | |
return this.http.post<any>(url, body, { headers }).pipe( | |
map(response => response), | |
catchError(this.handleError) | |
); | |
} | |
private getAPIUrl(): String { | |
// noinspection HttpUrlsUsage | |
const url = isDevMode() | |
? `http://${CONFIGS.api.rest.dev_url}` | |
: `https://${CONFIGS.api.rest.prod_url}`; | |
return `${url}/${CONFIGS.api.rest.version}`; | |
} | |
private async handleError(error: HttpErrorResponse) { | |
if (error.status === 401) { | |
await this.router.navigateByUrl( | |
this.AUTH_PATH, | |
{ replaceUrl: true }, | |
); | |
} | |
return Promise.reject(error); | |
} | |
} |
Author
aasumitro
commented
Mar 13, 2023
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment