From 5a9619d21e2ca780eea30aeb15b3277af90b3110 Mon Sep 17 00:00:00 2001 From: Yamaguchi Date: Mon, 15 Apr 2024 01:39:40 +0900 Subject: [PATCH 1/4] Fix displaying error message with Firefox --- frontend/src/app/backend.service.ts | 159 ++++++++++++++++++++-------- frontend/src/app/config.service.ts | 15 ++- 2 files changed, 123 insertions(+), 51 deletions(-) diff --git a/frontend/src/app/backend.service.ts b/frontend/src/app/backend.service.ts index ce7a469e..ad40a0ce 100644 --- a/frontend/src/app/backend.service.ts +++ b/frontend/src/app/backend.service.ts @@ -1,41 +1,57 @@ import { Injectable, NgModule } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; -import { ConfigService } from './config.service'; +import { Config, ConfigService } from './config.service'; @Injectable({ providedIn: 'root' }) @NgModule() export class BackendService { - backendUrl = 'http://localhost:3001'; - constructor(private http: HttpClient, private configService: ConfigService) { - if (configService.config && configService.config.backendUrl) { - this.backendUrl = configService.config.backendUrl; - } - } + constructor(private http: HttpClient, private configService: ConfigService) {} getBlocks(page: number, perPage: number): Observable { - return this.http.get(`${this.backendUrl}/api/blocks`, { - params: new HttpParams({ - fromObject: { page: page.toString(), perPage: perPage.toString() } + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get(`${config.backendUrl}/api/blocks`, { + params: new HttpParams({ + fromObject: { page: page.toString(), perPage: perPage.toString() } + }) + }); }) - }); + ); } searchBlock(query: string): Observable { - return this.http.get(`${this.backendUrl}/api/block/${query}`); + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get(`${config.backendUrl}/api/block/${query}`); + }) + ); } getBlock(blockHash: string): Observable { - return this.http.get(`${this.backendUrl}/api/block/${blockHash}`); + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get(`${config.backendUrl}/api/block/${blockHash}`); + }) + ); } getBlockByHeight(height: number): Observable { - return this.http.get(`${this.backendUrl}/api/block/height/${height}`); + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get(`${config.backendUrl}/api/block/height/${height}`); + }) + ); } getRawBlock(blockHash: string): Observable { - return this.http.get(`${this.backendUrl}/api/block/${blockHash}/raw`); + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get(`${config.backendUrl}/api/block/${blockHash}/raw`); + }) + ); } getBlockTransactions( @@ -43,70 +59,119 @@ export class BackendService { page: number, perPage: number ): Observable { - return this.http.get(`${this.backendUrl}/api/block/${blockHash}/txns`, { - params: new HttpParams({ - fromObject: { page: page.toString(), perPage: perPage.toString() } + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get( + `${config.backendUrl}/api/block/${blockHash}/txns`, + { + params: new HttpParams({ + fromObject: { page: page.toString(), perPage: perPage.toString() } + }) + } + ); }) - }); + ); } getTransactions(page: number, perPage: number): Observable { - return this.http.get(`${this.backendUrl}/api/transactions`, { - params: new HttpParams({ - fromObject: { page: page.toString(), perPage: perPage.toString() } + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get(`${config.backendUrl}/api/transactions`, { + params: new HttpParams({ + fromObject: { page: page.toString(), perPage: perPage.toString() } + }) + }); }) - }); + ); } getTransaction(txId: string): Observable { - return this.http.get(`${this.backendUrl}/api/tx/${txId}`); + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get(`${config.backendUrl}/api/tx/${txId}`); + }) + ); } getRawTransaction(txId: string): Observable { - return this.http.get(`${this.backendUrl}/api/tx/${txId}/rawData`); + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get(`${config.backendUrl}/api/tx/${txId}/rawData`); + }) + ); } getAddressInfo(address: string, lastSeenTxid?: string): Observable { - return this.http.get(`${this.backendUrl}/api/address/${address}`, { - params: new HttpParams({ - fromObject: { - lastSeenTxid: (lastSeenTxid || '').toString() - } + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get(`${config.backendUrl}/api/address/${address}`, { + params: new HttpParams({ + fromObject: { + lastSeenTxid: (lastSeenTxid || '').toString() + } + }) + }); }) - }); + ); } searchTransaction(query: string): Observable { - return this.http.get(`${this.backendUrl}/api/tx/${query}/get`); + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get(`${config.backendUrl}/api/tx/${query}/get`); + }) + ); } getColors(lastSeenColorId?: string): Observable { - return this.http.get(`${this.backendUrl}/api/colors`, { - params: new HttpParams({ - fromObject: { - lastSeenColorId: (lastSeenColorId || '').toString() - } + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get(`${config.backendUrl}/api/colors`, { + params: new HttpParams({ + fromObject: { + lastSeenColorId: (lastSeenColorId || '').toString() + } + }) + }); }) - }); + ); } getColor(colorId: string, lastSeenTxid?: string): Observable { - return this.http.get(`${this.backendUrl}/api/color/${colorId}`, { - params: new HttpParams({ - fromObject: { - lastSeenTxid: (lastSeenTxid || '').toString() - } + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get(`${config.backendUrl}/api/color/${colorId}`, { + params: new HttpParams({ + fromObject: { + lastSeenTxid: (lastSeenTxid || '').toString() + } + }) + }); }) - }); + ); } validateOpenedValue(opened_value: string): Observable { - return this.http.get(`${this.backendUrl}/api/validate/${opened_value}`); + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get( + `${config.backendUrl}/api/validate/${opened_value}` + ); + }) + ); } checkMaterialTrackingTransaction(txId: string): Observable { - return this.http.get( - `${this.backendUrl}/api/check_material_tracking_balance/${txId}` + return this.getConfig().pipe( + mergeMap((config: Config) => { + return this.http.get( + `${config.backendUrl}/api/check_material_tracking_balance/${txId}` + ); + }) ); } + + private getConfig(): Observable { + return this.configService.getConfig(); + } } diff --git a/frontend/src/app/config.service.ts b/frontend/src/app/config.service.ts index 87345133..065db6ac 100644 --- a/frontend/src/app/config.service.ts +++ b/frontend/src/app/config.service.ts @@ -34,10 +34,17 @@ export class ConfigService { } getConfig(): Observable { - return this.http.get(this.configUrl).pipe( - retry(3), // retry a failed request up to 3 times - catchError(this.handleError) // then handle the error - ); + if (this.config) { + return new Observable(observer => { + observer.next(this.config); + observer.complete(); + }); + } else { + return this.http.get(this.configUrl).pipe( + retry(3), // retry a failed request up to 3 times + catchError(this.handleError) // then handle the error + ); + } } private handleError(error: HttpErrorResponse) { From a109dec5c8e7806cfe9bbfab8e300eb469fbc5a5 Mon Sep 17 00:00:00 2001 From: Yamaguchi Date: Wed, 24 Apr 2024 14:45:54 +0900 Subject: [PATCH 2/4] Refactor methods in BackendService/ConfigService --- frontend/src/app/backend.service.ts | 143 +++++++--------------------- frontend/src/app/config.service.ts | 19 ++-- 2 files changed, 47 insertions(+), 115 deletions(-) diff --git a/frontend/src/app/backend.service.ts b/frontend/src/app/backend.service.ts index ad40a0ce..8c378d87 100644 --- a/frontend/src/app/backend.service.ts +++ b/frontend/src/app/backend.service.ts @@ -11,47 +11,23 @@ export class BackendService { constructor(private http: HttpClient, private configService: ConfigService) {} getBlocks(page: number, perPage: number): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get(`${config.backendUrl}/api/blocks`, { - params: new HttpParams({ - fromObject: { page: page.toString(), perPage: perPage.toString() } - }) - }); - }) - ); + return this.request('/api/blocks'); } searchBlock(query: string): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get(`${config.backendUrl}/api/block/${query}`); - }) - ); + return this.request(`/api/block/${query}`); } getBlock(blockHash: string): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get(`${config.backendUrl}/api/block/${blockHash}`); - }) - ); + return this.request(`/api/block/${blockHash}`); } getBlockByHeight(height: number): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get(`${config.backendUrl}/api/block/height/${height}`); - }) - ); + return this.request(`/api/block/height/${height}`); } getRawBlock(blockHash: string): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get(`${config.backendUrl}/api/block/${blockHash}/raw`); - }) - ); + return this.request(`/api/block/${blockHash}/raw`); } getBlockTransactions( @@ -59,114 +35,65 @@ export class BackendService { page: number, perPage: number ): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get( - `${config.backendUrl}/api/block/${blockHash}/txns`, - { - params: new HttpParams({ - fromObject: { page: page.toString(), perPage: perPage.toString() } - }) - } - ); - }) - ); + return this.request(`/api/block/${blockHash}/txns`, new HttpParams({ + fromObject: { page: page.toString(), perPage: perPage.toString() } + })); } getTransactions(page: number, perPage: number): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get(`${config.backendUrl}/api/transactions`, { - params: new HttpParams({ - fromObject: { page: page.toString(), perPage: perPage.toString() } - }) - }); - }) - ); + return this.request('/api/transactions', new HttpParams({ + fromObject: { page: page.toString(), perPage: perPage.toString() } + })); } getTransaction(txId: string): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get(`${config.backendUrl}/api/tx/${txId}`); - }) - ); + return this.request(`/api/tx/${txId}`); } getRawTransaction(txId: string): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get(`${config.backendUrl}/api/tx/${txId}/rawData`); - }) - ); + return this.request(`/api/tx/${txId}/rawData`); } getAddressInfo(address: string, lastSeenTxid?: string): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get(`${config.backendUrl}/api/address/${address}`, { - params: new HttpParams({ - fromObject: { - lastSeenTxid: (lastSeenTxid || '').toString() - } - }) - }); - }) - ); + return this.request(`/api/address/${address}`, new HttpParams({ + fromObject: { + lastSeenTxid: (lastSeenTxid || '').toString() + } + })); } searchTransaction(query: string): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get(`${config.backendUrl}/api/tx/${query}/get`); - }) - ); + return this.request(`/api/tx/${query}/get`); } getColors(lastSeenColorId?: string): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get(`${config.backendUrl}/api/colors`, { - params: new HttpParams({ - fromObject: { - lastSeenColorId: (lastSeenColorId || '').toString() - } - }) - }); - }) - ); + return this.request('/api/colors', new HttpParams({ + fromObject: { + lastSeenColorId: (lastSeenColorId || '').toString() + } + })); } getColor(colorId: string, lastSeenTxid?: string): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get(`${config.backendUrl}/api/color/${colorId}`, { - params: new HttpParams({ - fromObject: { - lastSeenTxid: (lastSeenTxid || '').toString() - } - }) - }); - }) - ); + return this.request(`/api/color/${colorId}`, new HttpParams({ + fromObject: { + lastSeenTxid: (lastSeenTxid || '').toString() + } + })); } validateOpenedValue(opened_value: string): Observable { - return this.getConfig().pipe( - mergeMap((config: Config) => { - return this.http.get( - `${config.backendUrl}/api/validate/${opened_value}` - ); - }) - ); + return this.request(`/api/validate/${opened_value}`); } checkMaterialTrackingTransaction(txId: string): Observable { + return this.request(`/api/check_material_tracking_balance/${txId}`); + } + + private request(url: string, params?: HttpParams): Observable { return this.getConfig().pipe( mergeMap((config: Config) => { - return this.http.get( - `${config.backendUrl}/api/check_material_tracking_balance/${txId}` - ); + return this.http.get(`${config.backendUrl}/${url}`, { params }); }) ); } diff --git a/frontend/src/app/config.service.ts b/frontend/src/app/config.service.ts index 065db6ac..7becbb7e 100644 --- a/frontend/src/app/config.service.ts +++ b/frontend/src/app/config.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; -import { catchError, retry } from 'rxjs/operators'; +import { catchError, mergeMap, retry } from 'rxjs/operators'; /*** * Config service to supply configuration items from assets/config.json file. @@ -21,6 +21,7 @@ export interface Config { export class ConfigService { configUrl = 'assets/config.json'; config: Config; + observable: Observable; constructor(private http: HttpClient) {} @@ -34,19 +35,23 @@ export class ConfigService { } getConfig(): Observable { - if (this.config) { + if (this.config && this.observable) { return new Observable(observer => { observer.next(this.config); observer.complete(); }); } else { - return this.http.get(this.configUrl).pipe( - retry(3), // retry a failed request up to 3 times - catchError(this.handleError) // then handle the error - ); + return this.observable = this.loadConfig(); } } + private loadConfig(): Observable { + return this.http.get(this.configUrl).pipe( + retry(3), // retry a failed request up to 3 times + catchError(this.handleError) // then handle the error + ); + } + private handleError(error: HttpErrorResponse) { if (error.error instanceof ErrorEvent) { // A client-side or network error occurred. Handle it accordingly. From 0697c433b37ea720fe1625617a26e18f05434d5e Mon Sep 17 00:00:00 2001 From: Yamaguchi Date: Wed, 24 Apr 2024 15:02:18 +0900 Subject: [PATCH 3/4] Fix prettier warnings --- frontend/src/app/backend.service.ts | 57 ++++++++++++++++++----------- frontend/src/app/config.service.ts | 2 +- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/backend.service.ts b/frontend/src/app/backend.service.ts index 8c378d87..e6b7074a 100644 --- a/frontend/src/app/backend.service.ts +++ b/frontend/src/app/backend.service.ts @@ -35,15 +35,21 @@ export class BackendService { page: number, perPage: number ): Observable { - return this.request(`/api/block/${blockHash}/txns`, new HttpParams({ - fromObject: { page: page.toString(), perPage: perPage.toString() } - })); + return this.request( + `/api/block/${blockHash}/txns`, + new HttpParams({ + fromObject: { page: page.toString(), perPage: perPage.toString() } + }) + ); } getTransactions(page: number, perPage: number): Observable { - return this.request('/api/transactions', new HttpParams({ - fromObject: { page: page.toString(), perPage: perPage.toString() } - })); + return this.request( + '/api/transactions', + new HttpParams({ + fromObject: { page: page.toString(), perPage: perPage.toString() } + }) + ); } getTransaction(txId: string): Observable { @@ -55,11 +61,14 @@ export class BackendService { } getAddressInfo(address: string, lastSeenTxid?: string): Observable { - return this.request(`/api/address/${address}`, new HttpParams({ - fromObject: { - lastSeenTxid: (lastSeenTxid || '').toString() - } - })); + return this.request( + `/api/address/${address}`, + new HttpParams({ + fromObject: { + lastSeenTxid: (lastSeenTxid || '').toString() + } + }) + ); } searchTransaction(query: string): Observable { @@ -67,19 +76,25 @@ export class BackendService { } getColors(lastSeenColorId?: string): Observable { - return this.request('/api/colors', new HttpParams({ - fromObject: { - lastSeenColorId: (lastSeenColorId || '').toString() - } - })); + return this.request( + '/api/colors', + new HttpParams({ + fromObject: { + lastSeenColorId: (lastSeenColorId || '').toString() + } + }) + ); } getColor(colorId: string, lastSeenTxid?: string): Observable { - return this.request(`/api/color/${colorId}`, new HttpParams({ - fromObject: { - lastSeenTxid: (lastSeenTxid || '').toString() - } - })); + return this.request( + `/api/color/${colorId}`, + new HttpParams({ + fromObject: { + lastSeenTxid: (lastSeenTxid || '').toString() + } + }) + ); } validateOpenedValue(opened_value: string): Observable { diff --git a/frontend/src/app/config.service.ts b/frontend/src/app/config.service.ts index 7becbb7e..3d985cdc 100644 --- a/frontend/src/app/config.service.ts +++ b/frontend/src/app/config.service.ts @@ -41,7 +41,7 @@ export class ConfigService { observer.complete(); }); } else { - return this.observable = this.loadConfig(); + return (this.observable = this.loadConfig()); } } From a2c7a996f206322f6c275920d9cac7194cd54fd0 Mon Sep 17 00:00:00 2001 From: Yamaguchi Date: Wed, 24 Apr 2024 15:58:18 +0900 Subject: [PATCH 4/4] Return Observable if already stored --- frontend/src/app/config.service.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/config.service.ts b/frontend/src/app/config.service.ts index 3d985cdc..fa69b3f5 100644 --- a/frontend/src/app/config.service.ts +++ b/frontend/src/app/config.service.ts @@ -35,14 +35,18 @@ export class ConfigService { } getConfig(): Observable { - if (this.config && this.observable) { + if (this.config) { return new Observable(observer => { observer.next(this.config); observer.complete(); }); - } else { - return (this.observable = this.loadConfig()); } + + if (this.observable) { + return this.observable; + } + + return (this.observable = this.loadConfig()); } private loadConfig(): Observable {