Browse Source

Merge branch 'main' into refactor/portfolio-filter-component

pull/5618/head
Thomas Kaul 2 months ago
committed by GitHub
parent
commit
20da049a1c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 6
      apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts
  3. 4
      apps/api/src/app/exchange-rate/exchange-rate.controller.ts
  4. 4
      apps/api/src/app/info/info.controller.ts
  5. 4
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  6. 4
      apps/api/src/app/portfolio/interfaces/get-values-params.interface.ts
  7. 4
      apps/api/src/app/symbol/symbol.controller.ts
  8. 10
      apps/api/src/app/symbol/symbol.service.ts
  9. 14
      apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts
  10. 2
      apps/api/src/services/data-provider/alpha-vantage/interfaces/interfaces.ts
  11. 12
      apps/api/src/services/data-provider/coingecko/coingecko.service.ts
  12. 24
      apps/api/src/services/data-provider/data-provider.service.ts
  13. 14
      apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts
  14. 14
      apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts
  15. 14
      apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts
  16. 12
      apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts
  17. 10
      apps/api/src/services/data-provider/interfaces/data-provider.interface.ts
  18. 12
      apps/api/src/services/data-provider/manual/manual.service.ts
  19. 2
      apps/api/src/services/data-provider/rapid-api/interfaces/interfaces.ts
  20. 8
      apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts
  21. 14
      apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts
  22. 4
      apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts
  23. 6
      apps/api/src/services/interfaces/interfaces.ts
  24. 4
      apps/api/src/services/market-data/market-data.service.ts
  25. 4
      apps/api/src/services/queues/data-gathering/data-gathering.processor.ts
  26. 12
      apps/api/src/services/queues/data-gathering/data-gathering.service.ts
  27. 2
      apps/api/src/services/queues/portfolio-snapshot/interfaces/portfolio-snapshot-queue-job.interface.ts
  28. 6
      apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts
  29. 4
      apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts
  30. 4
      apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.ts
  31. 2
      apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts
  32. 4
      apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts
  33. 4
      apps/client/src/app/components/rule/rule.component.ts
  34. 4
      apps/client/src/app/core/notification/alert-dialog/alert-dialog.component.ts
  35. 2
      apps/client/src/app/core/notification/alert-dialog/interfaces/interfaces.ts
  36. 4
      apps/client/src/app/core/notification/confirmation-dialog/confirmation-dialog.component.ts
  37. 2
      apps/client/src/app/core/notification/confirmation-dialog/interfaces/interfaces.ts
  38. 6
      apps/client/src/app/core/notification/interfaces/interfaces.ts
  39. 12
      apps/client/src/app/core/notification/notification.service.ts
  40. 4
      apps/client/src/app/pages/resources/glossary/resources-glossary.component.html
  41. 4
      apps/client/src/app/services/admin.service.ts
  42. 4
      apps/client/src/app/services/data.service.ts
  43. 4
      apps/client/src/main.ts
  44. 2
      libs/common/src/lib/interfaces/index.ts
  45. 4
      libs/common/src/lib/interfaces/responses/dividends-response.interface.ts
  46. 4
      libs/common/src/lib/interfaces/responses/historical-response.interface.ts
  47. 3
      libs/common/src/lib/interfaces/responses/info-response.interface.ts
  48. 4
      libs/common/src/lib/interfaces/responses/quotes-response.interface.ts
  49. 8
      libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts
  50. 40
      libs/ui/src/lib/assistant/assistant.component.ts
  51. 26
      libs/ui/src/lib/assistant/interfaces/interfaces.ts

2
CHANGELOG.md

@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Formatted the holdings table in the _Copy AI prompt to clipboard for analysis_ action on the analysis page (experimental) - Formatted the holdings table in the _Copy AI prompt to clipboard for analysis_ action on the analysis page (experimental)
- Formatted the holdings table in the _Copy portfolio data to clipboard for AI prompt_ action of the analysis page (experimental) - Formatted the holdings table in the _Copy portfolio data to clipboard for AI prompt_ action on the analysis page (experimental)
- Improved the language localization for German (`de`) - Improved the language localization for German (`de`)
## 2.209.0 - 2025-10-18 ## 2.209.0 - 2025-10-18

6
apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts

@ -8,7 +8,7 @@ import {
GetQuotesParams, GetQuotesParams,
GetSearchParams GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { import {
@ -114,7 +114,7 @@ export class GhostfolioService {
try { try {
const promises: Promise<{ const promises: Promise<{
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
}>[] = []; }>[] = [];
for (const dataProviderService of this.getDataProviderServices()) { for (const dataProviderService of this.getDataProviderServices()) {
@ -156,7 +156,7 @@ export class GhostfolioService {
try { try {
const promises: Promise<{ const promises: Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}>[] = []; }>[] = [];
for (const dataProviderService of this.getDataProviderServices()) { for (const dataProviderService of this.getDataProviderServices()) {

4
apps/api/src/app/exchange-rate/exchange-rate.controller.ts

@ -1,5 +1,5 @@
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
import { import {
Controller, Controller,
@ -25,7 +25,7 @@ export class ExchangeRateController {
public async getExchangeRate( public async getExchangeRate(
@Param('dateString') dateString: string, @Param('dateString') dateString: string,
@Param('symbol') symbol: string @Param('symbol') symbol: string
): Promise<IDataProviderHistoricalResponse> { ): Promise<DataProviderHistoricalResponse> {
const date = parseISO(dateString); const date = parseISO(dateString);
const exchangeRate = await this.exchangeRateService.getExchangeRate({ const exchangeRate = await this.exchangeRateService.getExchangeRate({

4
apps/api/src/app/info/info.controller.ts

@ -1,5 +1,5 @@
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor';
import { InfoItem } from '@ghostfolio/common/interfaces'; import { InfoResponse } from '@ghostfolio/common/interfaces';
import { Controller, Get, UseInterceptors } from '@nestjs/common'; import { Controller, Get, UseInterceptors } from '@nestjs/common';
@ -11,7 +11,7 @@ export class InfoController {
@Get() @Get()
@UseInterceptors(TransformDataSourceInResponseInterceptor) @UseInterceptors(TransformDataSourceInResponseInterceptor)
public async getInfo(): Promise<InfoItem> { public async getInfo(): Promise<InfoResponse> {
return this.infoService.get(); return this.infoService.get();
} }
} }

4
apps/api/src/app/portfolio/calculator/portfolio-calculator.ts

@ -9,7 +9,7 @@ import { getFactor } from '@ghostfolio/api/helper/portfolio.helper';
import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor'; import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper';
import { import {
@ -193,7 +193,7 @@ export abstract class PortfolioCalculator {
} }
const currencies: { [symbol: string]: string } = {}; const currencies: { [symbol: string]: string } = {};
const dataGatheringItems: IDataGatheringItem[] = []; const dataGatheringItems: DataGatheringItem[] = [];
let firstIndex = transactionPoints.length; let firstIndex = transactionPoints.length;
let firstTransactionPoint: TransactionPoint = null; let firstTransactionPoint: TransactionPoint = null;
let totalInterestWithCurrencyEffect = new Big(0); let totalInterestWithCurrencyEffect = new Big(0);

4
apps/api/src/app/portfolio/interfaces/get-values-params.interface.ts

@ -1,8 +1,8 @@
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { DateQuery } from './date-query.interface'; import { DateQuery } from './date-query.interface';
export interface GetValuesParams { export interface GetValuesParams {
dataGatheringItems: IDataGatheringItem[]; dataGatheringItems: DataGatheringItem[];
dateQuery: DateQuery; dateQuery: DateQuery;
} }

4
apps/api/src/app/symbol/symbol.controller.ts

@ -1,7 +1,7 @@
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor'; import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor';
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor';
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
import { LookupResponse } from '@ghostfolio/common/interfaces'; import { LookupResponse } from '@ghostfolio/common/interfaces';
import type { RequestWithUser } from '@ghostfolio/common/types'; import type { RequestWithUser } from '@ghostfolio/common/types';
@ -97,7 +97,7 @@ export class SymbolController {
@Param('dataSource') dataSource: DataSource, @Param('dataSource') dataSource: DataSource,
@Param('dateString') dateString: string, @Param('dateString') dateString: string,
@Param('symbol') symbol: string @Param('symbol') symbol: string
): Promise<IDataProviderHistoricalResponse> { ): Promise<DataProviderHistoricalResponse> {
const date = parseISO(dateString); const date = parseISO(dateString);
if (!isDate(date)) { if (!isDate(date)) {

10
apps/api/src/app/symbol/symbol.service.ts

@ -1,7 +1,7 @@
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { import {
IDataGatheringItem, DataGatheringItem,
IDataProviderHistoricalResponse DataProviderHistoricalResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { DATE_FORMAT } from '@ghostfolio/common/helper';
@ -27,7 +27,7 @@ export class SymbolService {
dataGatheringItem, dataGatheringItem,
includeHistoricalData includeHistoricalData
}: { }: {
dataGatheringItem: IDataGatheringItem; dataGatheringItem: DataGatheringItem;
includeHistoricalData?: number; includeHistoricalData?: number;
}): Promise<SymbolItem> { }): Promise<SymbolItem> {
const quotes = await this.dataProviderService.getQuotes({ const quotes = await this.dataProviderService.getQuotes({
@ -75,10 +75,10 @@ export class SymbolService {
dataSource, dataSource,
date = new Date(), date = new Date(),
symbol symbol
}: IDataGatheringItem): Promise<IDataProviderHistoricalResponse> { }: DataGatheringItem): Promise<DataProviderHistoricalResponse> {
let historicalData: { let historicalData: {
[symbol: string]: { [symbol: string]: {
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
}; };
} = { } = {
[symbol]: {} [symbol]: {}

14
apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts

@ -8,8 +8,8 @@ import {
GetSearchParams GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import { import {
IDataProviderHistoricalResponse, DataProviderHistoricalResponse,
IDataProviderResponse DataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { DATE_FORMAT } from '@ghostfolio/common/helper';
@ -23,7 +23,7 @@ import { DataSource, SymbolProfile } from '@prisma/client';
import * as Alphavantage from 'alphavantage'; import * as Alphavantage from 'alphavantage';
import { format, isAfter, isBefore, parse } from 'date-fns'; import { format, isAfter, isBefore, parse } from 'date-fns';
import { IAlphaVantageHistoricalResponse } from './interfaces/interfaces'; import { AlphaVantageHistoricalResponse } from './interfaces/interfaces';
@Injectable() @Injectable()
export class AlphaVantageService implements DataProviderInterface { export class AlphaVantageService implements DataProviderInterface {
@ -68,11 +68,11 @@ export class AlphaVantageService implements DataProviderInterface {
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}> { }> {
try { try {
const historicalData: { const historicalData: {
[symbol: string]: IAlphaVantageHistoricalResponse[]; [symbol: string]: AlphaVantageHistoricalResponse[];
} = await this.alphaVantage.crypto.daily( } = await this.alphaVantage.crypto.daily(
symbol symbol
.substring(0, symbol.length - DEFAULT_CURRENCY.length) .substring(0, symbol.length - DEFAULT_CURRENCY.length)
@ -81,7 +81,7 @@ export class AlphaVantageService implements DataProviderInterface {
); );
const response: { const response: {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
} = {}; } = {};
response[symbol] = {}; response[symbol] = {};
@ -115,7 +115,7 @@ export class AlphaVantageService implements DataProviderInterface {
} }
public async getQuotes({}: GetQuotesParams): Promise<{ public async getQuotes({}: GetQuotesParams): Promise<{
[symbol: string]: IDataProviderResponse; [symbol: string]: DataProviderResponse;
}> { }> {
return {}; return {};
} }

2
apps/api/src/services/data-provider/alpha-vantage/interfaces/interfaces.ts

@ -1 +1 @@
export interface IAlphaVantageHistoricalResponse {} export interface AlphaVantageHistoricalResponse {}

12
apps/api/src/services/data-provider/coingecko/coingecko.service.ts

@ -8,8 +8,8 @@ import {
GetSearchParams GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import { import {
IDataProviderHistoricalResponse, DataProviderHistoricalResponse,
IDataProviderResponse DataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { DATE_FORMAT } from '@ghostfolio/common/helper';
@ -109,7 +109,7 @@ export class CoinGeckoService implements DataProviderInterface {
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}> { }> {
try { try {
const { error, prices, status } = await fetch( const { error, prices, status } = await fetch(
@ -133,7 +133,7 @@ export class CoinGeckoService implements DataProviderInterface {
} }
const result: { const result: {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
} = { } = {
[symbol]: {} [symbol]: {}
}; };
@ -166,8 +166,8 @@ export class CoinGeckoService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> {
const response: { [symbol: string]: IDataProviderResponse } = {}; const response: { [symbol: string]: DataProviderResponse } = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return response; return response;

24
apps/api/src/services/data-provider/data-provider.service.ts

@ -2,8 +2,8 @@ import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.s
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import { import {
IDataProviderHistoricalResponse, DataProviderHistoricalResponse,
IDataProviderResponse DataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
@ -215,10 +215,10 @@ export class DataProviderService implements OnModuleInit {
from: Date, from: Date,
to: Date to: Date
): Promise<{ ): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}> { }> {
let response: { let response: {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
} = {}; } = {};
if (isEmpty(aItems) || !isValid(from) || !isValid(to)) { if (isEmpty(aItems) || !isValid(from) || !isValid(to)) {
@ -284,7 +284,7 @@ export class DataProviderService implements OnModuleInit {
from: Date; from: Date;
to: Date; to: Date;
}): Promise<{ }): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}> { }> {
for (const { currency, rootCurrency } of DERIVED_CURRENCIES) { for (const { currency, rootCurrency } of DERIVED_CURRENCIES) {
if ( if (
@ -317,11 +317,11 @@ export class DataProviderService implements OnModuleInit {
); );
const result: { const result: {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
} = {}; } = {};
const promises: Promise<{ const promises: Promise<{
data: { [date: string]: IDataProviderHistoricalResponse }; data: { [date: string]: DataProviderHistoricalResponse };
symbol: string; symbol: string;
}>[] = []; }>[] = [];
for (const { dataSource, symbol } of assetProfileIdentifiers) { for (const { dataSource, symbol } of assetProfileIdentifiers) {
@ -329,7 +329,7 @@ export class DataProviderService implements OnModuleInit {
if (dataProvider.canHandle(symbol)) { if (dataProvider.canHandle(symbol)) {
if (symbol === `${DEFAULT_CURRENCY}USX`) { if (symbol === `${DEFAULT_CURRENCY}USX`) {
const data: { const data: {
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
} = {}; } = {};
for (const date of eachDayOfInterval({ end: to, start: from })) { for (const date of eachDayOfInterval({ end: to, start: from })) {
@ -399,10 +399,10 @@ export class DataProviderService implements OnModuleInit {
useCache?: boolean; useCache?: boolean;
user?: UserWithSettings; user?: UserWithSettings;
}): Promise<{ }): Promise<{
[symbol: string]: IDataProviderResponse; [symbol: string]: DataProviderResponse;
}> { }> {
const response: { const response: {
[symbol: string]: IDataProviderResponse; [symbol: string]: DataProviderResponse;
} = {}; } = {};
const startTimeTotal = performance.now(); const startTimeTotal = performance.now();
@ -716,7 +716,7 @@ export class DataProviderService implements OnModuleInit {
}: { }: {
allData: { allData: {
data: { data: {
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
}; };
symbol: string; symbol: string;
}[]; }[];
@ -728,7 +728,7 @@ export class DataProviderService implements OnModuleInit {
})?.data; })?.data;
const data: { const data: {
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
} = {}; } = {};
for (const date in rootData) { for (const date in rootData) {

14
apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts

@ -8,8 +8,8 @@ import {
GetSearchParams GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import { import {
IDataProviderHistoricalResponse, DataProviderHistoricalResponse,
IDataProviderResponse DataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
import { import {
@ -89,7 +89,7 @@ export class EodHistoricalDataService implements DataProviderInterface {
symbol, symbol,
to to
}: GetDividendsParams): Promise<{ }: GetDividendsParams): Promise<{
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
}> { }> {
symbol = this.convertToEodSymbol(symbol); symbol = this.convertToEodSymbol(symbol);
@ -99,7 +99,7 @@ export class EodHistoricalDataService implements DataProviderInterface {
try { try {
const response: { const response: {
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
} = {}; } = {};
const historicalResult = await fetch( const historicalResult = await fetch(
@ -141,7 +141,7 @@ export class EodHistoricalDataService implements DataProviderInterface {
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}> { }> {
symbol = this.convertToEodSymbol(symbol); symbol = this.convertToEodSymbol(symbol);
@ -198,8 +198,8 @@ export class EodHistoricalDataService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> {
const response: { [symbol: string]: IDataProviderResponse } = {}; const response: { [symbol: string]: DataProviderResponse } = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return response; return response;

14
apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts

@ -9,8 +9,8 @@ import {
GetSearchParams GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import { import {
IDataProviderHistoricalResponse, DataProviderHistoricalResponse,
IDataProviderResponse DataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { import {
@ -245,7 +245,7 @@ export class FinancialModelingPrepService implements DataProviderInterface {
try { try {
const response: { const response: {
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
} = {}; } = {};
const dividends = await fetch( const dividends = await fetch(
@ -289,11 +289,11 @@ export class FinancialModelingPrepService implements DataProviderInterface {
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}> { }> {
const MAX_YEARS_PER_REQUEST = 5; const MAX_YEARS_PER_REQUEST = 5;
const result: { const result: {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
} = { } = {
[symbol]: {} [symbol]: {}
}; };
@ -353,8 +353,8 @@ export class FinancialModelingPrepService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> {
const response: { [symbol: string]: IDataProviderResponse } = {}; const response: { [symbol: string]: DataProviderResponse } = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return response; return response;

14
apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts

@ -9,8 +9,8 @@ import {
GetSearchParams GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import { import {
IDataProviderHistoricalResponse, DataProviderHistoricalResponse,
IDataProviderResponse DataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { import {
@ -111,10 +111,10 @@ export class GhostfolioService implements DataProviderInterface {
symbol, symbol,
to to
}: GetDividendsParams): Promise<{ }: GetDividendsParams): Promise<{
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
}> { }> {
let dividends: { let dividends: {
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
} = {}; } = {};
try { try {
@ -164,7 +164,7 @@ export class GhostfolioService implements DataProviderInterface {
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}> { }> {
try { try {
const response = await fetch( const response = await fetch(
@ -228,9 +228,9 @@ export class GhostfolioService implements DataProviderInterface {
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
symbols symbols
}: GetQuotesParams): Promise<{ }: GetQuotesParams): Promise<{
[symbol: string]: IDataProviderResponse; [symbol: string]: DataProviderResponse;
}> { }> {
let quotes: { [symbol: string]: IDataProviderResponse } = {}; let quotes: { [symbol: string]: DataProviderResponse } = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return quotes; return quotes;

12
apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts

@ -8,8 +8,8 @@ import {
GetSearchParams GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import { import {
IDataProviderHistoricalResponse, DataProviderHistoricalResponse,
IDataProviderResponse DataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
@ -60,7 +60,7 @@ export class GoogleSheetsService implements DataProviderInterface {
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}> { }> {
try { try {
const sheet = await this.getSheet({ const sheet = await this.getSheet({
@ -71,7 +71,7 @@ export class GoogleSheetsService implements DataProviderInterface {
const rows = await sheet.getRows(); const rows = await sheet.getRows();
const historicalData: { const historicalData: {
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
} = {}; } = {};
rows rows
@ -104,8 +104,8 @@ export class GoogleSheetsService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> {
const response: { [symbol: string]: IDataProviderResponse } = {}; const response: { [symbol: string]: DataProviderResponse } = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return response; return response;

10
apps/api/src/services/data-provider/interfaces/data-provider.interface.ts

@ -1,6 +1,6 @@
import { import {
IDataProviderHistoricalResponse, DataProviderHistoricalResponse,
IDataProviderResponse DataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { import {
DataProviderInfo, DataProviderInfo,
@ -26,7 +26,7 @@ export interface DataProviderInterface {
symbol, symbol,
to to
}: GetDividendsParams): Promise<{ }: GetDividendsParams): Promise<{
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
}>; }>;
getHistorical({ getHistorical({
@ -36,7 +36,7 @@ export interface DataProviderInterface {
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}>; // TODO: Return only one symbol }>; // TODO: Return only one symbol
getMaxNumberOfSymbolsPerRequest?(): number; getMaxNumberOfSymbolsPerRequest?(): number;
@ -46,7 +46,7 @@ export interface DataProviderInterface {
getQuotes({ getQuotes({
requestTimeout, requestTimeout,
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }>; }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }>;
getTestSymbol(): string; getTestSymbol(): string;

12
apps/api/src/services/data-provider/manual/manual.service.ts

@ -8,8 +8,8 @@ import {
GetSearchParams GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import { import {
IDataProviderHistoricalResponse, DataProviderHistoricalResponse,
IDataProviderResponse DataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
@ -77,7 +77,7 @@ export class ManualService implements DataProviderInterface {
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}> { }> {
try { try {
const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles(
@ -88,7 +88,7 @@ export class ManualService implements DataProviderInterface {
if (defaultMarketPrice) { if (defaultMarketPrice) {
const historical: { const historical: {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
} = { } = {
[symbol]: {} [symbol]: {}
}; };
@ -132,8 +132,8 @@ export class ManualService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> {
const response: { [symbol: string]: IDataProviderResponse } = {}; const response: { [symbol: string]: DataProviderResponse } = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return response; return response;

2
apps/api/src/services/data-provider/rapid-api/interfaces/interfaces.ts

@ -1 +1 @@
export interface IRapidApiResponse {} export interface RapidApiResponse {}

8
apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts

@ -8,8 +8,8 @@ import {
GetSearchParams GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import { import {
IDataProviderHistoricalResponse, DataProviderHistoricalResponse,
IDataProviderResponse DataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { import {
ghostfolioFearAndGreedIndexSymbol, ghostfolioFearAndGreedIndexSymbol,
@ -59,7 +59,7 @@ export class RapidApiService implements DataProviderInterface {
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}> { }> {
try { try {
if ( if (
@ -96,7 +96,7 @@ export class RapidApiService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> {
if (symbols.length <= 0) { if (symbols.length <= 0) {
return {}; return {};
} }

14
apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts

@ -10,8 +10,8 @@ import {
GetSearchParams GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import { import {
IDataProviderHistoricalResponse, DataProviderHistoricalResponse,
IDataProviderResponse DataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { DATE_FORMAT } from '@ghostfolio/common/helper';
@ -96,7 +96,7 @@ export class YahooFinanceService implements DataProviderInterface {
) )
); );
const response: { const response: {
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
} = {}; } = {};
for (const historicalItem of historicalResult) { for (const historicalItem of historicalResult) {
@ -124,7 +124,7 @@ export class YahooFinanceService implements DataProviderInterface {
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
}> { }> {
if (isSameDay(from, to)) { if (isSameDay(from, to)) {
to = addDays(to, 1); to = addDays(to, 1);
@ -145,7 +145,7 @@ export class YahooFinanceService implements DataProviderInterface {
); );
const response: { const response: {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: DataProviderHistoricalResponse };
} = {}; } = {};
response[symbol] = {}; response[symbol] = {};
@ -183,8 +183,8 @@ export class YahooFinanceService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> {
const response: { [symbol: string]: IDataProviderResponse } = {}; const response: { [symbol: string]: DataProviderResponse } = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return response; return response;

4
apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts

@ -1,6 +1,6 @@
import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor'; import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor';
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service';
@ -29,7 +29,7 @@ import ms from 'ms';
@Injectable() @Injectable()
export class ExchangeRateDataService { export class ExchangeRateDataService {
private currencies: string[] = []; private currencies: string[] = [];
private currencyPairs: IDataGatheringItem[] = []; private currencyPairs: DataGatheringItem[] = [];
private exchangeRates: { [currencyPair: string]: number } = {}; private exchangeRates: { [currencyPair: string]: number } = {};
public constructor( public constructor(

6
apps/api/src/services/interfaces/interfaces.ts

@ -6,11 +6,11 @@ import { MarketState } from '@ghostfolio/common/types';
import { DataSource } from '@prisma/client'; import { DataSource } from '@prisma/client';
export interface IDataProviderHistoricalResponse { export interface DataProviderHistoricalResponse {
marketPrice: number; marketPrice: number;
} }
export interface IDataProviderResponse { export interface DataProviderResponse {
currency: string; currency: string;
dataProviderInfo?: DataProviderInfo; dataProviderInfo?: DataProviderInfo;
dataSource: DataSource; dataSource: DataSource;
@ -18,6 +18,6 @@ export interface IDataProviderResponse {
marketState: MarketState; marketState: MarketState;
} }
export interface IDataGatheringItem extends AssetProfileIdentifier { export interface DataGatheringItem extends AssetProfileIdentifier {
date?: Date; date?: Date;
} }

4
apps/api/src/services/market-data/market-data.service.ts

@ -1,6 +1,6 @@
import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-data.dto'; import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-data.dto';
import { DateQuery } from '@ghostfolio/api/app/portfolio/interfaces/date-query.interface'; import { DateQuery } from '@ghostfolio/api/app/portfolio/interfaces/date-query.interface';
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { resetHours } from '@ghostfolio/common/helper'; import { resetHours } from '@ghostfolio/common/helper';
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
@ -30,7 +30,7 @@ export class MarketDataService {
dataSource, dataSource,
date = new Date(), date = new Date(),
symbol symbol
}: IDataGatheringItem): Promise<MarketData> { }: DataGatheringItem): Promise<MarketData> {
return await this.prismaService.marketData.findFirst({ return await this.prismaService.marketData.findFirst({
where: { where: {
dataSource, dataSource,

4
apps/api/src/services/queues/data-gathering/data-gathering.processor.ts

@ -1,6 +1,6 @@
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { AssetProfileDelistedError } from '@ghostfolio/api/services/data-provider/errors/asset-profile-delisted.error'; import { AssetProfileDelistedError } from '@ghostfolio/api/services/data-provider/errors/asset-profile-delisted.error';
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
import { import {
@ -99,7 +99,7 @@ export class DataGatheringProcessor {
), ),
name: GATHER_HISTORICAL_MARKET_DATA_PROCESS_JOB_NAME name: GATHER_HISTORICAL_MARKET_DATA_PROCESS_JOB_NAME
}) })
public async gatherHistoricalMarketData(job: Job<IDataGatheringItem>) { public async gatherHistoricalMarketData(job: Job<DataGatheringItem>) {
const { dataSource, date, symbol } = job.data; const { dataSource, date, symbol } = job.data;
try { try {

12
apps/api/src/services/queues/data-gathering/data-gathering.service.ts

@ -1,7 +1,7 @@
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface'; import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service';
@ -94,7 +94,7 @@ export class DataGatheringService {
}); });
} }
public async gatherSymbol({ dataSource, date, symbol }: IDataGatheringItem) { public async gatherSymbol({ dataSource, date, symbol }: DataGatheringItem) {
await this.marketDataService.deleteMany({ dataSource, symbol }); await this.marketDataService.deleteMany({ dataSource, symbol });
const dataGatheringItems = (await this.getSymbolsMax()) const dataGatheringItems = (await this.getSymbolsMax())
@ -276,7 +276,7 @@ export class DataGatheringService {
dataGatheringItems, dataGatheringItems,
priority priority
}: { }: {
dataGatheringItems: IDataGatheringItem[]; dataGatheringItems: DataGatheringItem[];
priority: number; priority: number;
}) { }) {
await this.addJobsToQueue( await this.addJobsToQueue(
@ -348,7 +348,7 @@ export class DataGatheringService {
}); });
} }
private async getCurrencies7D(): Promise<IDataGatheringItem[]> { private async getCurrencies7D(): Promise<DataGatheringItem[]> {
const assetProfileIdentifiersWithCompleteMarketData = const assetProfileIdentifiersWithCompleteMarketData =
await this.getAssetProfileIdentifiersWithCompleteMarketData(); await this.getAssetProfileIdentifiersWithCompleteMarketData();
@ -376,7 +376,7 @@ export class DataGatheringService {
withUserSubscription = false withUserSubscription = false
}: { }: {
withUserSubscription?: boolean; withUserSubscription?: boolean;
}): Promise<IDataGatheringItem[]> { }): Promise<DataGatheringItem[]> {
const symbolProfiles = const symbolProfiles =
await this.symbolProfileService.getActiveSymbolProfilesByUserSubscription( await this.symbolProfileService.getActiveSymbolProfilesByUserSubscription(
{ {
@ -407,7 +407,7 @@ export class DataGatheringService {
}); });
} }
private async getSymbolsMax(): Promise<IDataGatheringItem[]> { private async getSymbolsMax(): Promise<DataGatheringItem[]> {
const benchmarkAssetProfileIdMap: { [key: string]: boolean } = {}; const benchmarkAssetProfileIdMap: { [key: string]: boolean } = {};
( (
(await this.propertyService.getByKey<BenchmarkProperty[]>( (await this.propertyService.getByKey<BenchmarkProperty[]>(

2
apps/api/src/services/queues/portfolio-snapshot/interfaces/portfolio-snapshot-queue-job.interface.ts

@ -1,7 +1,7 @@
import { Filter } from '@ghostfolio/common/interfaces'; import { Filter } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
export interface IPortfolioSnapshotQueueJob { export interface PortfolioSnapshotQueueJob {
calculationType: PerformanceCalculationType; calculationType: PerformanceCalculationType;
filters: Filter[]; filters: Filter[];
userCurrency: string; userCurrency: string;

6
apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts

@ -16,7 +16,7 @@ import { Injectable, Logger } from '@nestjs/common';
import { Job } from 'bull'; import { Job } from 'bull';
import { addMilliseconds } from 'date-fns'; import { addMilliseconds } from 'date-fns';
import { IPortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface'; import { PortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface';
@Injectable() @Injectable()
@Processor(PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE) @Processor(PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE)
@ -37,9 +37,7 @@ export class PortfolioSnapshotProcessor {
), ),
name: PORTFOLIO_SNAPSHOT_PROCESS_JOB_NAME name: PORTFOLIO_SNAPSHOT_PROCESS_JOB_NAME
}) })
public async calculatePortfolioSnapshot( public async calculatePortfolioSnapshot(job: Job<PortfolioSnapshotQueueJob>) {
job: Job<IPortfolioSnapshotQueueJob>
) {
try { try {
const startTime = performance.now(); const startTime = performance.now();

4
apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts

@ -1,13 +1,13 @@
import { Job, JobOptions } from 'bull'; import { Job, JobOptions } from 'bull';
import { setTimeout } from 'timers/promises'; import { setTimeout } from 'timers/promises';
import { IPortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface'; import { PortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface';
export const PortfolioSnapshotServiceMock = { export const PortfolioSnapshotServiceMock = {
addJobToQueue({ addJobToQueue({
opts opts
}: { }: {
data: IPortfolioSnapshotQueueJob; data: PortfolioSnapshotQueueJob;
name: string; name: string;
opts?: JobOptions; opts?: JobOptions;
}): Promise<Job<any>> { }): Promise<Job<any>> {

4
apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.ts

@ -4,7 +4,7 @@ import { InjectQueue } from '@nestjs/bull';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { JobOptions, Queue } from 'bull'; import { JobOptions, Queue } from 'bull';
import { IPortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface'; import { PortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface';
@Injectable() @Injectable()
export class PortfolioSnapshotService { export class PortfolioSnapshotService {
@ -18,7 +18,7 @@ export class PortfolioSnapshotService {
name, name,
opts opts
}: { }: {
data: IPortfolioSnapshotQueueJob; data: PortfolioSnapshotQueueJob;
name: string; name: string;
opts?: JobOptions; opts?: JobOptions;
}) { }) {

2
apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts

@ -3,7 +3,7 @@ import {
XRayRulesSettings XRayRulesSettings
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
export interface IRuleSettingsDialogParams { export interface RuleSettingsDialogParams {
categoryName: string; categoryName: string;
locale: string; locale: string;
rule: PortfolioReportRule; rule: PortfolioReportRule;

4
apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts

@ -12,7 +12,7 @@ import {
} from '@angular/material/dialog'; } from '@angular/material/dialog';
import { MatSliderModule } from '@angular/material/slider'; import { MatSliderModule } from '@angular/material/slider';
import { IRuleSettingsDialogParams } from './interfaces/interfaces'; import { RuleSettingsDialogParams } from './interfaces/interfaces';
@Component({ @Component({
imports: [ imports: [
@ -31,7 +31,7 @@ export class GfRuleSettingsDialogComponent {
public settings: XRayRulesSettings['AccountClusterRiskCurrentInvestment']; public settings: XRayRulesSettings['AccountClusterRiskCurrentInvestment'];
public constructor( public constructor(
@Inject(MAT_DIALOG_DATA) public data: IRuleSettingsDialogParams, @Inject(MAT_DIALOG_DATA) public data: RuleSettingsDialogParams,
public dialogRef: MatDialogRef<GfRuleSettingsDialogComponent> public dialogRef: MatDialogRef<GfRuleSettingsDialogComponent>
) {} ) {}
} }

4
apps/client/src/app/components/rule/rule.component.ts

@ -31,7 +31,7 @@ import { DeviceDetectorService } from 'ngx-device-detector';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { Subject, takeUntil } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
import { IRuleSettingsDialogParams } from './rule-settings-dialog/interfaces/interfaces'; import { RuleSettingsDialogParams } from './rule-settings-dialog/interfaces/interfaces';
import { GfRuleSettingsDialogComponent } from './rule-settings-dialog/rule-settings-dialog.component'; import { GfRuleSettingsDialogComponent } from './rule-settings-dialog/rule-settings-dialog.component';
@Component({ @Component({
@ -83,7 +83,7 @@ export class GfRuleComponent implements OnInit {
rule, rule,
categoryName: this.categoryName, categoryName: this.categoryName,
settings: this.settings settings: this.settings
} as IRuleSettingsDialogParams, } as RuleSettingsDialogParams,
width: this.deviceType === 'mobile' ? '100vw' : '50rem' width: this.deviceType === 'mobile' ? '100vw' : '50rem'
}); });

4
apps/client/src/app/core/notification/alert-dialog/alert-dialog.component.ts

@ -2,7 +2,7 @@ import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { IAlertDialogParams } from './interfaces/interfaces'; import { AlertDialogParams } from './interfaces/interfaces';
@Component({ @Component({
imports: [MatButtonModule, MatDialogModule], imports: [MatButtonModule, MatDialogModule],
@ -17,7 +17,7 @@ export class GfAlertDialogComponent {
public constructor(public dialogRef: MatDialogRef<GfAlertDialogComponent>) {} public constructor(public dialogRef: MatDialogRef<GfAlertDialogComponent>) {}
public initialize(aParams: IAlertDialogParams) { public initialize(aParams: AlertDialogParams) {
this.discardLabel = aParams.discardLabel; this.discardLabel = aParams.discardLabel;
this.message = aParams.message; this.message = aParams.message;
this.title = aParams.title; this.title = aParams.title;

2
apps/client/src/app/core/notification/alert-dialog/interfaces/interfaces.ts

@ -1,4 +1,4 @@
export interface IAlertDialogParams { export interface AlertDialogParams {
confirmLabel?: string; confirmLabel?: string;
discardLabel?: string; discardLabel?: string;
message?: string; message?: string;

4
apps/client/src/app/core/notification/confirmation-dialog/confirmation-dialog.component.ts

@ -3,7 +3,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { ConfirmationDialogType } from './confirmation-dialog.type'; import { ConfirmationDialogType } from './confirmation-dialog.type';
import { IConfirmDialogParams } from './interfaces/interfaces'; import { ConfirmDialogParams } from './interfaces/interfaces';
@Component({ @Component({
imports: [MatButtonModule, MatDialogModule], imports: [MatButtonModule, MatDialogModule],
@ -29,7 +29,7 @@ export class GfConfirmationDialogComponent {
} }
} }
public initialize(aParams: IConfirmDialogParams) { public initialize(aParams: ConfirmDialogParams) {
this.confirmLabel = aParams.confirmLabel; this.confirmLabel = aParams.confirmLabel;
this.confirmType = aParams.confirmType; this.confirmType = aParams.confirmType;
this.discardLabel = aParams.discardLabel; this.discardLabel = aParams.discardLabel;

2
apps/client/src/app/core/notification/confirmation-dialog/interfaces/interfaces.ts

@ -1,6 +1,6 @@
import { ConfirmationDialogType } from '../confirmation-dialog.type'; import { ConfirmationDialogType } from '../confirmation-dialog.type';
export interface IConfirmDialogParams { export interface ConfirmDialogParams {
confirmLabel?: string; confirmLabel?: string;
confirmType: ConfirmationDialogType; confirmType: ConfirmationDialogType;
discardLabel?: string; discardLabel?: string;

6
apps/client/src/app/core/notification/interfaces/interfaces.ts

@ -1,13 +1,13 @@
import { ConfirmationDialogType } from '../confirmation-dialog/confirmation-dialog.type'; import { ConfirmationDialogType } from '../confirmation-dialog/confirmation-dialog.type';
export interface IAlertParams { export interface AlertParams {
discardFn?: () => void; discardFn?: () => void;
discardLabel?: string; discardLabel?: string;
message?: string; message?: string;
title: string; title: string;
} }
export interface IConfirmParams { export interface ConfirmParams {
confirmFn: () => void; confirmFn: () => void;
confirmLabel?: string; confirmLabel?: string;
confirmType?: ConfirmationDialogType; confirmType?: ConfirmationDialogType;
@ -18,7 +18,7 @@ export interface IConfirmParams {
title: string; title: string;
} }
export interface IPromptParams { export interface PromptParams {
confirmFn: (value: string) => void; confirmFn: (value: string) => void;
confirmLabel?: string; confirmLabel?: string;
defaultValue?: string; defaultValue?: string;

12
apps/client/src/app/core/notification/notification.service.ts

@ -8,9 +8,9 @@ import { GfAlertDialogComponent } from './alert-dialog/alert-dialog.component';
import { GfConfirmationDialogComponent } from './confirmation-dialog/confirmation-dialog.component'; import { GfConfirmationDialogComponent } from './confirmation-dialog/confirmation-dialog.component';
import { ConfirmationDialogType } from './confirmation-dialog/confirmation-dialog.type'; import { ConfirmationDialogType } from './confirmation-dialog/confirmation-dialog.type';
import { import {
IAlertParams, AlertParams,
IConfirmParams, ConfirmParams,
IPromptParams PromptParams
} from './interfaces/interfaces'; } from './interfaces/interfaces';
import { GfPromptDialogComponent } from './prompt-dialog/prompt-dialog.component'; import { GfPromptDialogComponent } from './prompt-dialog/prompt-dialog.component';
@ -21,7 +21,7 @@ export class NotificationService {
public constructor(private matDialog: MatDialog) {} public constructor(private matDialog: MatDialog) {}
public alert(aParams: IAlertParams) { public alert(aParams: AlertParams) {
if (!aParams.discardLabel) { if (!aParams.discardLabel) {
aParams.discardLabel = translate('CLOSE'); aParams.discardLabel = translate('CLOSE');
} }
@ -45,7 +45,7 @@ export class NotificationService {
}); });
} }
public confirm(aParams: IConfirmParams) { public confirm(aParams: ConfirmParams) {
if (!aParams.confirmLabel) { if (!aParams.confirmLabel) {
aParams.confirmLabel = translate('YES'); aParams.confirmLabel = translate('YES');
} }
@ -78,7 +78,7 @@ export class NotificationService {
}); });
} }
public prompt(aParams: IPromptParams) { public prompt(aParams: PromptParams) {
if (!aParams.confirmLabel) { if (!aParams.confirmLabel) {
aParams.confirmLabel = translate('OK'); aParams.confirmLabel = translate('OK');
} }

4
apps/client/src/app/pages/resources/glossary/resources-glossary.component.html

@ -105,8 +105,8 @@
<h3 class="h5 mt-0">Personal Finance Tools</h3> <h3 class="h5 mt-0">Personal Finance Tools</h3>
<div class="mb-1"> <div class="mb-1">
Personal finance tools are software applications that help Personal finance tools are software applications that help
individuals manage their money, track expenses, set budgets, manage your money, track expenses, set budgets, monitor
monitor investments, and make informed financial decisions. investments, and make informed financial decisions.
</div> </div>
<div> <div>
<a [routerLink]="routerLinkResourcesPersonalFinanceTools" <a [routerLink]="routerLinkResourcesPersonalFinanceTools"

4
apps/client/src/app/services/admin.service.ts

@ -1,7 +1,7 @@
import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto'; import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto';
import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto'; import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto';
import { UpdatePlatformDto } from '@ghostfolio/api/app/platform/update-platform.dto'; import { UpdatePlatformDto } from '@ghostfolio/api/app/platform/update-platform.dto';
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
import { import {
HEADER_KEY_SKIP_INTERCEPTOR, HEADER_KEY_SKIP_INTERCEPTOR,
HEADER_KEY_TOKEN HEADER_KEY_TOKEN
@ -208,7 +208,7 @@ export class AdminService {
}) { }) {
const url = `/api/v1/symbol/${dataSource}/${symbol}/${dateString}`; const url = `/api/v1/symbol/${dataSource}/${symbol}/${dateString}`;
return this.http.get<IDataProviderHistoricalResponse>(url); return this.http.get<DataProviderHistoricalResponse>(url);
} }
public patchAssetProfile( public patchAssetProfile(

4
apps/client/src/app/services/data.service.ts

@ -19,7 +19,7 @@ import { DeleteOwnUserDto } from '@ghostfolio/api/app/user/delete-own-user.dto';
import { UserItem } from '@ghostfolio/api/app/user/interfaces/user-item.interface'; import { UserItem } from '@ghostfolio/api/app/user/interfaces/user-item.interface';
import { UpdateOwnAccessTokenDto } from '@ghostfolio/api/app/user/update-own-access-token.dto'; import { UpdateOwnAccessTokenDto } from '@ghostfolio/api/app/user/update-own-access-token.dto';
import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto'; import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto';
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
import { PropertyDto } from '@ghostfolio/api/services/property/property.dto'; import { PropertyDto } from '@ghostfolio/api/services/property/property.dto';
import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { DATE_FORMAT } from '@ghostfolio/common/helper';
import { import {
@ -291,7 +291,7 @@ export class DataService {
date: Date; date: Date;
symbol: string; symbol: string;
}) { }) {
return this.http.get<IDataProviderHistoricalResponse>( return this.http.get<DataProviderHistoricalResponse>(
`/api/v1/exchange-rate/${symbol}/${format(date, DATE_FORMAT, { in: utc })}` `/api/v1/exchange-rate/${symbol}/${format(date, DATE_FORMAT, { in: utc })}`
); );
} }

4
apps/client/src/main.ts

@ -1,5 +1,5 @@
import { locale } from '@ghostfolio/common/config'; import { locale } from '@ghostfolio/common/config';
import { InfoItem } from '@ghostfolio/common/interfaces'; import { InfoResponse } from '@ghostfolio/common/interfaces';
import { filterGlobalPermissions } from '@ghostfolio/common/permissions'; import { filterGlobalPermissions } from '@ghostfolio/common/permissions';
import { enableProdMode } from '@angular/core'; import { enableProdMode } from '@angular/core';
@ -11,7 +11,7 @@ import { environment } from './environments/environment';
(async () => { (async () => {
const response = await fetch('/api/v1/info'); const response = await fetch('/api/v1/info');
const info: InfoItem = await response.json(); const info: InfoResponse = await response.json();
const utmSource = window.localStorage.getItem('utm_source') as const utmSource = window.localStorage.getItem('utm_source') as
| 'ios' | 'ios'
| 'trusted-web-activity'; | 'trusted-web-activity';

2
libs/common/src/lib/interfaces/index.ts

@ -50,6 +50,7 @@ import type { DividendsResponse } from './responses/dividends-response.interface
import type { ResponseError } from './responses/errors.interface'; import type { ResponseError } from './responses/errors.interface';
import type { HistoricalResponse } from './responses/historical-response.interface'; import type { HistoricalResponse } from './responses/historical-response.interface';
import type { ImportResponse } from './responses/import-response.interface'; import type { ImportResponse } from './responses/import-response.interface';
import type { InfoResponse } from './responses/info-response.interface';
import type { LookupResponse } from './responses/lookup-response.interface'; import type { LookupResponse } from './responses/lookup-response.interface';
import type { MarketDataDetailsResponse } from './responses/market-data-details-response.interface'; import type { MarketDataDetailsResponse } from './responses/market-data-details-response.interface';
import type { MarketDataOfMarketsResponse } from './responses/market-data-of-markets-response.interface'; import type { MarketDataOfMarketsResponse } from './responses/market-data-of-markets-response.interface';
@ -112,6 +113,7 @@ export {
HoldingWithParents, HoldingWithParents,
ImportResponse, ImportResponse,
InfoItem, InfoItem,
InfoResponse,
InvestmentItem, InvestmentItem,
LineChartItem, LineChartItem,
LookupItem, LookupItem,

4
libs/common/src/lib/interfaces/responses/dividends-response.interface.ts

@ -1,7 +1,7 @@
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
export interface DividendsResponse { export interface DividendsResponse {
dividends: { dividends: {
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
}; };
} }

4
libs/common/src/lib/interfaces/responses/historical-response.interface.ts

@ -1,7 +1,7 @@
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
export interface HistoricalResponse { export interface HistoricalResponse {
historicalData: { historicalData: {
[date: string]: IDataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
}; };
} }

3
libs/common/src/lib/interfaces/responses/info-response.interface.ts

@ -0,0 +1,3 @@
import { InfoItem } from '../index';
export interface InfoResponse extends InfoItem {}

4
libs/common/src/lib/interfaces/responses/quotes-response.interface.ts

@ -1,5 +1,5 @@
import { IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces';
export interface QuotesResponse { export interface QuotesResponse {
quotes: { [symbol: string]: IDataProviderResponse }; quotes: { [symbol: string]: DataProviderResponse };
} }

8
libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts

@ -18,8 +18,8 @@ import { Params, RouterModule } from '@angular/router';
import { SearchMode } from '../enums/search-mode'; import { SearchMode } from '../enums/search-mode';
import { import {
IAssetSearchResultItem, AssetSearchResultItem,
ISearchResultItem SearchResultItem
} from '../interfaces/interfaces'; } from '../interfaces/interfaces';
@Component({ @Component({
@ -37,7 +37,7 @@ export class GfAssistantListItemComponent
return this.hasFocus; return this.hasFocus;
} }
@Input() item: ISearchResultItem; @Input() item: SearchResultItem;
@Output() clicked = new EventEmitter<void>(); @Output() clicked = new EventEmitter<void>();
@ -86,7 +86,7 @@ export class GfAssistantListItemComponent
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }
public isAsset(item: ISearchResultItem): item is IAssetSearchResultItem { public isAsset(item: SearchResultItem): item is AssetSearchResultItem {
return ( return (
(item.mode === SearchMode.ASSET_PROFILE || (item.mode === SearchMode.ASSET_PROFILE ||
item.mode === SearchMode.HOLDING) && item.mode === SearchMode.HOLDING) &&

40
libs/ui/src/lib/assistant/assistant.component.ts

@ -62,9 +62,9 @@ import {
import { GfAssistantListItemComponent } from './assistant-list-item/assistant-list-item.component'; import { GfAssistantListItemComponent } from './assistant-list-item/assistant-list-item.component';
import { SearchMode } from './enums/search-mode'; import { SearchMode } from './enums/search-mode';
import { import {
IDateRangeOption, DateRangeOption,
ISearchResultItem, SearchResultItem,
ISearchResults SearchResults
} from './interfaces/interfaces'; } from './interfaces/interfaces';
@Component({ @Component({
@ -140,7 +140,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
public accounts: AccountWithPlatform[] = []; public accounts: AccountWithPlatform[] = [];
public assetClasses: Filter[] = []; public assetClasses: Filter[] = [];
public dateRangeFormControl = new FormControl<string>(undefined); public dateRangeFormControl = new FormControl<string>(undefined);
public dateRangeOptions: IDateRangeOption[] = []; public dateRangeOptions: DateRangeOption[] = [];
public holdings: PortfolioPosition[] = []; public holdings: PortfolioPosition[] = [];
public isLoading = { public isLoading = {
accounts: false, accounts: false,
@ -159,7 +159,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
} }
); );
public searchFormControl = new FormControl(''); public searchFormControl = new FormControl('');
public searchResults: ISearchResults = { public searchResults: SearchResults = {
accounts: [], accounts: [],
assetProfiles: [], assetProfiles: [],
holdings: [], holdings: [],
@ -226,7 +226,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
assetProfiles: [], assetProfiles: [],
holdings: [], holdings: [],
quickLinks: [] quickLinks: []
} as ISearchResults; } as SearchResults;
if (!searchTerm) { if (!searchTerm) {
return of(results).pipe( return of(results).pipe(
@ -241,7 +241,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
); );
} }
const accounts$: Observable<Partial<ISearchResults>> = const accounts$: Observable<Partial<SearchResults>> =
this.searchAccounts(searchTerm).pipe( this.searchAccounts(searchTerm).pipe(
map((accounts) => ({ map((accounts) => ({
accounts: accounts.slice( accounts: accounts.slice(
@ -251,7 +251,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
})), })),
catchError((error) => { catchError((error) => {
console.error('Error fetching accounts for assistant:', error); console.error('Error fetching accounts for assistant:', error);
return of({ accounts: [] as ISearchResultItem[] }); return of({ accounts: [] as SearchResultItem[] });
}), }),
tap(() => { tap(() => {
this.isLoading.accounts = false; this.isLoading.accounts = false;
@ -259,7 +259,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}) })
); );
const assetProfiles$: Observable<Partial<ISearchResults>> = this const assetProfiles$: Observable<Partial<SearchResults>> = this
.hasPermissionToAccessAdminControl .hasPermissionToAccessAdminControl
? this.searchAssetProfiles(searchTerm).pipe( ? this.searchAssetProfiles(searchTerm).pipe(
map((assetProfiles) => ({ map((assetProfiles) => ({
@ -273,21 +273,21 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
'Error fetching asset profiles for assistant:', 'Error fetching asset profiles for assistant:',
error error
); );
return of({ assetProfiles: [] as ISearchResultItem[] }); return of({ assetProfiles: [] as SearchResultItem[] });
}), }),
tap(() => { tap(() => {
this.isLoading.assetProfiles = false; this.isLoading.assetProfiles = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}) })
) )
: of({ assetProfiles: [] as ISearchResultItem[] }).pipe( : of({ assetProfiles: [] as SearchResultItem[] }).pipe(
tap(() => { tap(() => {
this.isLoading.assetProfiles = false; this.isLoading.assetProfiles = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}) })
); );
const holdings$: Observable<Partial<ISearchResults>> = const holdings$: Observable<Partial<SearchResults>> =
this.searchHoldings(searchTerm).pipe( this.searchHoldings(searchTerm).pipe(
map((holdings) => ({ map((holdings) => ({
holdings: holdings.slice( holdings: holdings.slice(
@ -297,7 +297,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
})), })),
catchError((error) => { catchError((error) => {
console.error('Error fetching holdings for assistant:', error); console.error('Error fetching holdings for assistant:', error);
return of({ holdings: [] as ISearchResultItem[] }); return of({ holdings: [] as SearchResultItem[] });
}), }),
tap(() => { tap(() => {
this.isLoading.holdings = false; this.isLoading.holdings = false;
@ -305,7 +305,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}) })
); );
const quickLinks$: Observable<Partial<ISearchResults>> = of( const quickLinks$: Observable<Partial<SearchResults>> = of(
this.searchQuickLinks(searchTerm) this.searchQuickLinks(searchTerm)
).pipe( ).pipe(
map((quickLinks) => ({ map((quickLinks) => ({
@ -322,7 +322,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
return merge(accounts$, assetProfiles$, holdings$, quickLinks$).pipe( return merge(accounts$, assetProfiles$, holdings$, quickLinks$).pipe(
scan( scan(
(acc: ISearchResults, curr: Partial<ISearchResults>) => ({ (acc: SearchResults, curr: Partial<SearchResults>) => ({
...acc, ...acc,
...curr ...curr
}), }),
@ -331,7 +331,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
assetProfiles: [], assetProfiles: [],
holdings: [], holdings: [],
quickLinks: [] quickLinks: []
} as ISearchResults } as SearchResults
) )
); );
}), }),
@ -622,7 +622,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}, this.PRESELECTION_DELAY); }, this.PRESELECTION_DELAY);
} }
private searchAccounts(aSearchTerm: string): Observable<ISearchResultItem[]> { private searchAccounts(aSearchTerm: string): Observable<SearchResultItem[]> {
return this.dataService return this.dataService
.fetchAccounts({ .fetchAccounts({
filters: [ filters: [
@ -652,7 +652,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
private searchAssetProfiles( private searchAssetProfiles(
aSearchTerm: string aSearchTerm: string
): Observable<ISearchResultItem[]> { ): Observable<SearchResultItem[]> {
return this.adminService return this.adminService
.fetchAdminMarketData({ .fetchAdminMarketData({
filters: [ filters: [
@ -685,7 +685,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
); );
} }
private searchHoldings(aSearchTerm: string): Observable<ISearchResultItem[]> { private searchHoldings(aSearchTerm: string): Observable<SearchResultItem[]> {
return this.dataService return this.dataService
.fetchPortfolioHoldings({ .fetchPortfolioHoldings({
filters: [ filters: [
@ -717,7 +717,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
); );
} }
private searchQuickLinks(aSearchTerm: string): ISearchResultItem[] { private searchQuickLinks(aSearchTerm: string): SearchResultItem[] {
const searchTerm = aSearchTerm.toLowerCase(); const searchTerm = aSearchTerm.toLowerCase();
const allRoutes = Object.values(internalRoutes) const allRoutes = Object.values(internalRoutes)

26
libs/ui/src/lib/assistant/interfaces/interfaces.ts

@ -3,38 +3,38 @@ import { AccountWithValue, DateRange } from '@ghostfolio/common/types';
import { SearchMode } from '../enums/search-mode'; import { SearchMode } from '../enums/search-mode';
export interface IAccountSearchResultItem export interface AccountSearchResultItem
extends Pick<AccountWithValue, 'id' | 'name'> { extends Pick<AccountWithValue, 'id' | 'name'> {
mode: SearchMode.ACCOUNT; mode: SearchMode.ACCOUNT;
routerLink: string[]; routerLink: string[];
} }
export interface IAssetSearchResultItem extends AssetProfileIdentifier { export interface AssetSearchResultItem extends AssetProfileIdentifier {
assetSubClassString: string; assetSubClassString: string;
currency: string; currency: string;
mode: SearchMode.ASSET_PROFILE | SearchMode.HOLDING; mode: SearchMode.ASSET_PROFILE | SearchMode.HOLDING;
name: string; name: string;
} }
export interface IDateRangeOption { export interface DateRangeOption {
label: string; label: string;
value: DateRange; value: DateRange;
} }
export interface IQuickLinkSearchResultItem { export interface QuickLinkSearchResultItem {
mode: SearchMode.QUICK_LINK; mode: SearchMode.QUICK_LINK;
name: string; name: string;
routerLink: string[]; routerLink: string[];
} }
export type ISearchResultItem = export type SearchResultItem =
| IAccountSearchResultItem | AccountSearchResultItem
| IAssetSearchResultItem | AssetSearchResultItem
| IQuickLinkSearchResultItem; | QuickLinkSearchResultItem;
export interface ISearchResults { export interface SearchResults {
accounts: ISearchResultItem[]; accounts: SearchResultItem[];
assetProfiles: ISearchResultItem[]; assetProfiles: SearchResultItem[];
holdings: ISearchResultItem[]; holdings: SearchResultItem[];
quickLinks: ISearchResultItem[]; quickLinks: SearchResultItem[];
} }

Loading…
Cancel
Save