Feature/respect data source in symbol data endpoint (#370)

* Respect data source in symbol data endpoint

* Respect data source in the data provider service

* Combine symbol with data source in get() of data provider service

* Improve search functionality for multiple data sources

* Update changelog
This commit is contained in:
Thomas Kaul
2021-09-18 19:32:22 +02:00
committed by GitHub
parent 641fe4e8f4
commit 0f72673ef4
23 changed files with 387 additions and 181 deletions

View File

@@ -29,6 +29,7 @@ import {
} from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { DateRange } from '@ghostfolio/common/types';
import { DataSource } from '@prisma/client';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject, Subscription } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@@ -112,7 +113,10 @@ export class HomePageComponent implements OnDestroy, OnInit {
if (this.hasPermissionToAccessFearAndGreedIndex) {
this.dataService
.fetchSymbolItem(ghostfolioFearAndGreedIndexSymbol)
.fetchSymbolItem({
dataSource: DataSource.RAKUTEN,
symbol: ghostfolioFearAndGreedIndexSymbol
})
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ marketPrice }) => {
this.fearAndGreedIndex = marketPrice;

View File

@@ -3,7 +3,8 @@ import {
ChangeDetectorRef,
Component,
Inject,
OnDestroy
OnDestroy,
ViewChild
} from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
@@ -11,6 +12,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { DataService } from '@ghostfolio/client/services/data.service';
import { Currency } from '@prisma/client';
import { isString } from 'lodash';
import { EMPTY, Observable, Subject } from 'rxjs';
import {
catchError,
@@ -31,13 +33,18 @@ import { CreateOrUpdateTransactionDialogParams } from './interfaces/interfaces';
templateUrl: 'create-or-update-transaction-dialog.html'
})
export class CreateOrUpdateTransactionDialog implements OnDestroy {
@ViewChild('autocomplete') autocomplete;
public currencies: Currency[] = [];
public currentMarketPrice = null;
public filteredLookupItems: Observable<LookupItem[]>;
public isLoading = false;
public platforms: { id: string; name: string }[];
public searchSymbolCtrl = new FormControl(
this.data.transaction.symbol,
{
dataSource: this.data.transaction.dataSource,
name: this.data.transaction.symbol
},
Validators.required
);
@@ -60,9 +67,9 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
startWith(''),
debounceTime(400),
distinctUntilChanged(),
switchMap((aQuery: string) => {
if (aQuery) {
return this.dataService.fetchSymbols(aQuery);
switchMap((query: string) => {
if (isString(query)) {
return this.dataService.fetchSymbols(query);
}
return [];
@@ -71,7 +78,10 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
if (this.data.transaction.symbol) {
this.dataService
.fetchSymbolItem(this.data.transaction.symbol)
.fetchSymbolItem({
dataSource: this.data.transaction.dataSource,
symbol: this.data.transaction.symbol
})
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ marketPrice }) => {
this.currentMarketPrice = marketPrice;
@@ -85,9 +95,21 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
this.data.transaction.unitPrice = this.currentMarketPrice;
}
public displayFn(aLookupItem: LookupItem) {
return aLookupItem?.name ?? '';
}
public onBlurSymbol() {
const symbol = this.searchSymbolCtrl.value;
this.updateSymbol(symbol);
this.data.transaction.currency = null;
this.data.transaction.dataSource = null;
if (this.autocomplete.isOpen) {
this.searchSymbolCtrl.setErrors({ incorrect: true });
} else {
this.data.transaction.unitPrice = null;
}
this.changeDetectorRef.markForCheck();
}
public onCancel(): void {
@@ -95,7 +117,8 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
}
public onUpdateSymbol(event: MatAutocompleteSelectedEvent) {
this.updateSymbol(event.option.value);
this.data.transaction.dataSource = event.option.value.dataSource;
this.updateSymbol(event.option.value.symbol);
}
public ngOnDestroy() {
@@ -106,10 +129,15 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
private updateSymbol(symbol: string) {
this.isLoading = true;
this.searchSymbolCtrl.setErrors(null);
this.data.transaction.symbol = symbol;
this.dataService
.fetchSymbolItem(this.data.transaction.symbol)
.fetchSymbolItem({
dataSource: this.data.transaction.dataSource,
symbol: this.data.transaction.symbol
})
.pipe(
catchError(() => {
this.data.transaction.currency = null;

View File

@@ -28,18 +28,19 @@
matInput
required
[formControl]="searchSymbolCtrl"
[matAutocomplete]="auto"
[matAutocomplete]="autocomplete"
(blur)="onBlurSymbol()"
/>
<mat-autocomplete
#auto="matAutocomplete"
#autocomplete="matAutocomplete"
[displayWith]="displayFn"
(optionSelected)="onUpdateSymbol($event)"
>
<ng-container>
<mat-option
*ngFor="let lookupItem of filteredLookupItems | async"
class="autocomplete"
[value]="lookupItem.symbol"
[value]="lookupItem"
>
<span class="mr-2 symbol">{{ lookupItem.symbol | gfSymbol }}</span
><span><b>{{ lookupItem.name }}</b></span>

View File

@@ -29,8 +29,11 @@ import {
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
import { permissions } from '@ghostfolio/common/permissions';
import { DateRange } from '@ghostfolio/common/types';
import { Order as OrderModel } from '@prisma/client';
import { Account as AccountModel } from '@prisma/client';
import {
Account as AccountModel,
DataSource,
Order as OrderModel
} from '@prisma/client';
import { parseISO } from 'date-fns';
import { cloneDeep } from 'lodash';
import { Observable } from 'rxjs';
@@ -108,8 +111,14 @@ export class DataService {
return info;
}
public fetchSymbolItem(aSymbol: string) {
return this.http.get<SymbolItem>(`/api/symbol/${aSymbol}`);
public fetchSymbolItem({
dataSource,
symbol
}: {
dataSource: DataSource;
symbol: string;
}) {
return this.http.get<SymbolItem>(`/api/symbol/${dataSource}/${symbol}`);
}
public fetchPositions({