Feature/more discreet data provider warning (#589)

* Upgrade http-status-codes to version 2.2.0

* Make the data provider warning more discreet

* Update changelog
pull/590/head
Thomas Kaul 2 years ago committed by GitHub
parent 994275e093
commit db1d474ddf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Made the data provider warning more discreet
- Upgraded `http-status-codes` from version `2.1.4` to `2.2.0`
- Upgraded `ngx-device-detector` from version `2.1.1` to `3.0.0`
- Upgraded `ngx-markdown` from version `12.0.1` to `13.0.0`
- Upgraded `ngx-stripe` from version `12.0.2` to `13.0.0`

@ -51,7 +51,7 @@ export class PortfolioController {
@Get('investments')
@UseGuards(AuthGuard('jwt'))
public async findAll(
@Headers('impersonation-id') impersonationId,
@Headers('impersonation-id') impersonationId: string,
@Res() res: Response
): Promise<InvestmentItem[]> {
if (
@ -87,7 +87,7 @@ export class PortfolioController {
@Get('chart')
@UseGuards(AuthGuard('jwt'))
public async getChart(
@Headers('impersonation-id') impersonationId,
@Headers('impersonation-id') impersonationId: string,
@Query('range') range,
@Res() res: Response
): Promise<PortfolioChart> {
@ -98,18 +98,14 @@ export class PortfolioController {
let chartData = historicalDataContainer.items;
let hasNullValue = false;
let hasError = false;
chartData.forEach((chartDataItem) => {
if (hasNotDefinedValuesInObject(chartDataItem)) {
hasNullValue = true;
hasError = true;
}
});
if (hasNullValue) {
res.status(StatusCodes.ACCEPTED);
}
if (
impersonationId ||
this.userService.isRestrictedView(this.request.user)
@ -131,6 +127,7 @@ export class PortfolioController {
}
return <any>res.json({
hasError,
chart: chartData,
isAllTimeHigh: historicalDataContainer.isAllTimeHigh,
isAllTimeLow: historicalDataContainer.isAllTimeLow
@ -140,7 +137,7 @@ export class PortfolioController {
@Get('details')
@UseGuards(AuthGuard('jwt'))
public async getDetails(
@Headers('impersonation-id') impersonationId,
@Headers('impersonation-id') impersonationId: string,
@Query('range') range,
@Res() res: Response
): Promise<PortfolioDetails> {
@ -152,6 +149,8 @@ export class PortfolioController {
return <any>res.json({ accounts: {}, holdings: {} });
}
let hasError = false;
const { accounts, holdings, hasErrors } =
await this.portfolioService.getDetails(
impersonationId,
@ -160,7 +159,7 @@ export class PortfolioController {
);
if (hasErrors || hasNotDefinedValuesInObject(holdings)) {
res.status(StatusCodes.ACCEPTED);
hasError = true;
}
if (
@ -198,43 +197,38 @@ export class PortfolioController {
}
}
return <any>res.json({ accounts, holdings });
return <any>res.json({ accounts, hasError, holdings });
}
@Get('performance')
@UseGuards(AuthGuard('jwt'))
public async getPerformance(
@Headers('impersonation-id') impersonationId,
@Headers('impersonation-id') impersonationId: string,
@Query('range') range,
@Res() res: Response
): Promise<PortfolioPerformance> {
): Promise<{ hasErrors: boolean; performance: PortfolioPerformance }> {
const performanceInformation = await this.portfolioService.getPerformance(
impersonationId,
range
);
if (performanceInformation?.hasErrors) {
res.status(StatusCodes.ACCEPTED);
}
let performance = performanceInformation.performance;
if (
impersonationId ||
this.userService.isRestrictedView(this.request.user)
) {
performance = nullifyValuesInObject(performance, [
'currentGrossPerformance',
'currentValue'
]);
performanceInformation.performance = nullifyValuesInObject(
performanceInformation.performance,
['currentGrossPerformance', 'currentValue']
);
}
return <any>res.json(performance);
return <any>res.json(performanceInformation);
}
@Get('positions')
@UseGuards(AuthGuard('jwt'))
public async getPositions(
@Headers('impersonation-id') impersonationId,
@Headers('impersonation-id') impersonationId: string,
@Query('range') range,
@Res() res: Response
): Promise<PortfolioPositions> {
@ -243,10 +237,6 @@ export class PortfolioController {
range
);
if (result?.hasErrors) {
res.status(StatusCodes.ACCEPTED);
}
if (
impersonationId ||
this.userService.isRestrictedView(this.request.user)
@ -353,7 +343,7 @@ export class PortfolioController {
@Get('position/:symbol')
@UseGuards(AuthGuard('jwt'))
public async getPosition(
@Headers('impersonation-id') impersonationId,
@Headers('impersonation-id') impersonationId: string,
@Param('symbol') symbol
): Promise<PortfolioPositionDetail> {
let position = await this.portfolioService.getPosition(
@ -387,7 +377,7 @@ export class PortfolioController {
@Get('report')
@UseGuards(AuthGuard('jwt'))
public async getReport(
@Headers('impersonation-id') impersonationId,
@Headers('impersonation-id') impersonationId: string,
@Res() res: Response
): Promise<PortfolioReport> {
if (

@ -29,6 +29,7 @@ export class HomeOverviewComponent implements OnDestroy, OnInit {
{ label: 'Max', value: 'max' }
];
public deviceType: string;
public hasError: boolean;
public hasImpersonationId: boolean;
public historicalDataItems: LineChartItem[];
public isAllTimeHigh: boolean;
@ -116,7 +117,8 @@ export class HomeOverviewComponent implements OnDestroy, OnInit {
.fetchPortfolioPerformance({ range: this.dateRange })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((response) => {
this.performance = response;
this.hasError = response.hasErrors;
this.performance = response.performance;
this.isLoadingPerformance = false;
this.changeDetectorRef.markForCheck();

@ -1,15 +1,5 @@
<div
class="
align-items-center
container
d-flex
flex-column
h-100
justify-content-center
overview
p-0
position-relative
"
class="align-items-center container d-flex flex-column h-100 justify-content-center overview p-0 position-relative"
>
<div class="row w-100">
<div class="chart-container col">
@ -37,6 +27,8 @@
<gf-portfolio-performance
class="pb-4"
[baseCurrency]="user?.settings?.baseCurrency"
[deviceType]="deviceType"
[hasError]="hasError"
[isAllTimeHigh]="isAllTimeHigh"
[isAllTimeLow]="isAllTimeLow"
[isLoading]="isLoadingPerformance"

@ -1,12 +1,15 @@
<div class="container p-0">
<div
class="no-gutters row"
[ngClass]="{
'text-danger': isAllTimeLow,
'text-success': isAllTimeHigh
}"
>
<div class="flex-grow-1"></div>
<div class="no-gutters row">
<div
class="flex-grow-1 status text-muted text-right"
[title]="
hasError
? 'Sorry! Our data provider partner is experiencing the hiccups.'
: ''
"
>
<ion-icon *ngIf="hasError" name="alert-circle-outline"></ion-icon>
</div>
<div *ngIf="isLoading" class="align-items-center d-flex">
<ngx-skeleton-loader
animation="pulse"
@ -20,6 +23,10 @@
<div
class="display-4 font-weight-bold m-0 text-center value-container"
[hidden]="isLoading"
[ngClass]="{
'text-danger': isAllTimeLow,
'text-success': isAllTimeHigh
}"
>
<span #value id="value"></span>
</div>

@ -1,6 +1,10 @@
:host {
display: block;
.status {
font-size: 1.33rem;
}
.value-container {
#value {
font-variant-numeric: tabular-nums;

@ -19,6 +19,8 @@ import { isNumber } from 'lodash';
})
export class PortfolioPerformanceComponent implements OnChanges, OnInit {
@Input() baseCurrency: string;
@Input() deviceType: string;
@Input() hasError: boolean;
@Input() isAllTimeHigh: boolean;
@Input() isAllTimeLow: boolean;
@Input() isLoading: boolean;
@ -44,7 +46,11 @@ export class PortfolioPerformanceComponent implements OnChanges, OnInit {
this.unit = this.baseCurrency;
new CountUp('value', this.performance?.currentValue, {
decimalPlaces: 2,
decimalPlaces:
this.deviceType === 'mobile' &&
this.performance?.currentValue >= 100000
? 0
: 2,
duration: 1,
separator: `'`
}).start();

@ -4,8 +4,7 @@ import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
HttpResponse
HttpRequest
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import {
@ -43,26 +42,6 @@ export class HttpResponseInterceptor implements HttpInterceptor {
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
tap((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
if (event.status === StatusCodes.ACCEPTED) {
if (!this.snackBarRef) {
this.snackBarRef = this.snackBar.open(
'Sorry! Our data provider partner is experiencing a mild case of the hiccups ;(',
'Try again?',
{ duration: 6000 }
);
this.snackBarRef.afterDismissed().subscribe(() => {
this.snackBarRef = undefined;
});
this.snackBarRef.onAction().subscribe(() => {
window.location.reload();
});
}
}
}
return event;
}),
catchError((error: HttpErrorResponse) => {

@ -181,7 +181,10 @@ export class DataService {
}
public fetchPortfolioPerformance(aParams: { [param: string]: any }) {
return this.http.get<PortfolioPerformance>('/api/portfolio/performance', {
return this.http.get<{
hasErrors: boolean;
performance: PortfolioPerformance;
}>('/api/portfolio/performance', {
params: aParams
});
}

@ -1,6 +1,7 @@
import { HistoricalDataItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position-detail.interface';
export interface PortfolioChart {
hasError: boolean;
isAllTimeHigh: boolean;
isAllTimeLow: boolean;
chart: HistoricalDataItem[];

@ -94,7 +94,7 @@
"cryptocurrencies": "7.0.0",
"date-fns": "2.22.1",
"envalid": "7.2.1",
"http-status-codes": "2.1.4",
"http-status-codes": "2.2.0",
"ionicons": "5.5.1",
"lodash": "4.17.21",
"ngx-device-detector": "3.0.0",

@ -10032,10 +10032,10 @@ http-signature@~1.2.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
http-status-codes@2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.1.4.tgz#453d99b4bd9424254c4f6a9a3a03715923052798"
integrity sha512-MZVIsLKGVOVE1KEnldppe6Ij+vmemMuApDfjhVSLzyYP+td0bREEYyAoIw9yFePoBXManCuBqmiNP5FqJS5Xkg==
http-status-codes@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.2.0.tgz#bb2efe63d941dfc2be18e15f703da525169622be"
integrity sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng==
https-browserify@^1.0.0:
version "1.0.0"

Loading…
Cancel
Save