diff --git a/apps/api/src/app/symbol/interfaces/lookup-item.interface.ts b/apps/api/src/app/symbol/interfaces/lookup-item.interface.ts index e9c90b0bc..1931d1bfe 100644 --- a/apps/api/src/app/symbol/interfaces/lookup-item.interface.ts +++ b/apps/api/src/app/symbol/interfaces/lookup-item.interface.ts @@ -1,9 +1,11 @@ +import { DataProviderInfo } from '@ghostfolio/common/interfaces'; import { AssetClass, AssetSubClass, DataSource } from '@prisma/client'; export interface LookupItem { assetClass: AssetClass; assetSubClass: AssetSubClass; currency: string; + dataProviderInfo: DataProviderInfo; dataSource: DataSource; name: string; symbol: string; diff --git a/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts b/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts index 53b882fda..4ce7da1b0 100644 --- a/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts +++ b/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts @@ -12,6 +12,7 @@ import { IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; +import { DataProviderInfo } from '@ghostfolio/common/interfaces'; import { Injectable } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import * as Alphavantage from 'alphavantage'; @@ -35,6 +36,12 @@ export class AlphaVantageService implements DataProviderInterface { return !!this.configurationService.get('ALPHA_VANTAGE_API_KEY'); } + public getDataProviderInfo(): DataProviderInfo { + return { + isPremium: false + }; + } + public async getAssetProfile( aSymbol: string ): Promise> { @@ -114,10 +121,11 @@ export class AlphaVantageService implements DataProviderInterface { return { items: result?.bestMatches?.map((bestMatch) => { - return { + return { assetClass: undefined, assetSubClass: undefined, currency: bestMatch['8. currency'], + dataProviderInfo: this.getDataProviderInfo(), dataSource: this.getName(), name: bestMatch['2. name'], symbol: bestMatch['1. symbol'] diff --git a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts index 6c6d73a8c..c9fd47a1d 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts @@ -91,6 +91,14 @@ export class CoinGeckoService implements DataProviderInterface { return response; } + public getDataProviderInfo(): DataProviderInfo { + return { + isPremium: false, + name: 'CoinGecko', + url: 'https://coingecko.com' + }; + } + public async getDividends({}: GetDividendsParams) { return {}; } @@ -252,11 +260,4 @@ export class CoinGeckoService implements DataProviderInterface { return { items }; } - - private getDataProviderInfo(): DataProviderInfo { - return { - name: 'CoinGecko', - url: 'https://coingecko.com' - }; - } } diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index 57d970fd6..b8fa744eb 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -520,20 +520,15 @@ export class DataProviderService { return { items: lookupItems }; } - let dataSources = this.configurationService.get('DATA_SOURCES'); - - if ( - this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') && - user.subscription.type === 'Basic' - ) { - dataSources = dataSources.filter((dataSource) => { - return !this.isPremiumDataSource(DataSource[dataSource]); + let dataProviderServices = this.configurationService + .get('DATA_SOURCES') + .map((dataSource) => { + return this.getDataProvider(DataSource[dataSource]); }); - } - for (const dataSource of dataSources) { + for (const dataProviderService of dataProviderServices) { promises.push( - this.getDataProvider(DataSource[dataSource]).search({ + dataProviderService.search({ includeIndices, query }) @@ -555,6 +550,16 @@ export class DataProviderService { }) .sort(({ name: name1 }, { name: name2 }) => { return name1?.toLowerCase().localeCompare(name2?.toLowerCase()); + }) + .map((lookupItem) => { + if ( + !this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') || + user.subscription.type === 'Premium' + ) { + lookupItem.dataProviderInfo.isPremium = false; + } + + return lookupItem; }); return { @@ -602,14 +607,6 @@ export class DataProviderService { }); } - private isPremiumDataSource(aDataSource: DataSource) { - const premiumDataSources: DataSource[] = [ - DataSource.EOD_HISTORICAL_DATA, - DataSource.FINANCIAL_MODELING_PREP - ]; - return premiumDataSources.includes(aDataSource); - } - private transformHistoricalData({ allData, currency, diff --git a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts index d9ee298be..c46055999 100644 --- a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts +++ b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts @@ -16,6 +16,7 @@ import { REPLACE_NAME_PARTS } from '@ghostfolio/common/config'; import { DATE_FORMAT, isCurrency } from '@ghostfolio/common/helper'; +import { DataProviderInfo } from '@ghostfolio/common/interfaces'; import { Injectable, Logger } from '@nestjs/common'; import { AssetClass, @@ -58,6 +59,12 @@ export class EodHistoricalDataService implements DataProviderInterface { }; } + public getDataProviderInfo(): DataProviderInfo { + return { + isPremium: true + }; + } + public async getDividends({ from, requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), @@ -312,7 +319,8 @@ export class EodHistoricalDataService implements DataProviderInterface { dataSource, name, symbol, - currency: this.convertCurrency(currency) + currency: this.convertCurrency(currency), + dataProviderInfo: this.getDataProviderInfo() }; } ) diff --git a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts index 81cb96e90..fe1b18cc1 100644 --- a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts +++ b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts @@ -45,6 +45,14 @@ export class FinancialModelingPrepService implements DataProviderInterface { }; } + public getDataProviderInfo(): DataProviderInfo { + return { + isPremium: true, + name: 'Financial Modeling Prep', + url: 'https://financialmodelingprep.com/developer/docs' + }; + } + public async getDividends({}: GetDividendsParams) { return {}; } @@ -202,11 +210,4 @@ export class FinancialModelingPrepService implements DataProviderInterface { return { items }; } - - private getDataProviderInfo(): DataProviderInfo { - return { - name: 'Financial Modeling Prep', - url: 'https://financialmodelingprep.com/developer/docs' - }; - } } diff --git a/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts b/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts index d52d09adf..0f937ac45 100644 --- a/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts +++ b/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts @@ -14,6 +14,7 @@ import { import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; +import { DataProviderInfo } from '@ghostfolio/common/interfaces'; import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import { format } from 'date-fns'; @@ -40,6 +41,12 @@ export class GoogleSheetsService implements DataProviderInterface { }; } + public getDataProviderInfo(): DataProviderInfo { + return { + isPremium: false + }; + } + public async getDividends({}: GetDividendsParams) { return {}; } @@ -145,6 +152,10 @@ export class GoogleSheetsService implements DataProviderInterface { return 'INDEXSP:.INX'; } + public isPremium() { + return false; + } + public async search({ query }: GetSearchParams): Promise<{ items: LookupItem[] }> { @@ -177,7 +188,11 @@ export class GoogleSheetsService implements DataProviderInterface { } }); - return { items }; + return { + items: items.map((item) => { + return { ...item, dataProviderInfo: this.getDataProviderInfo() }; + }) + }; } private async getSheet({ diff --git a/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts b/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts index 044836d82..924605f09 100644 --- a/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts +++ b/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts @@ -3,6 +3,7 @@ import { IDataProviderHistoricalResponse, IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; +import { DataProviderInfo } from '@ghostfolio/common/interfaces'; import { Granularity } from '@ghostfolio/common/types'; import { DataSource, SymbolProfile } from '@prisma/client'; @@ -11,6 +12,8 @@ export interface DataProviderInterface { getAssetProfile(aSymbol: string): Promise>; + getDataProviderInfo(): DataProviderInfo; + getDividends({ from, granularity, symbol, to }: GetDividendsParams): Promise<{ [date: string]: IDataProviderHistoricalResponse; }>; diff --git a/apps/api/src/services/data-provider/manual/manual.service.ts b/apps/api/src/services/data-provider/manual/manual.service.ts index 74acc48e1..33d93bbe2 100644 --- a/apps/api/src/services/data-provider/manual/manual.service.ts +++ b/apps/api/src/services/data-provider/manual/manual.service.ts @@ -18,7 +18,10 @@ import { extractNumberFromString, getYesterday } from '@ghostfolio/common/helper'; -import { ScraperConfiguration } from '@ghostfolio/common/interfaces'; +import { + DataProviderInfo, + ScraperConfiguration +} from '@ghostfolio/common/interfaces'; import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import * as cheerio from 'cheerio'; @@ -59,6 +62,12 @@ export class ManualService implements DataProviderInterface { return assetProfile; } + public getDataProviderInfo(): DataProviderInfo { + return { + isPremium: false + }; + } + public async getDividends({}: GetDividendsParams) { return {}; } @@ -214,7 +223,11 @@ export class ManualService implements DataProviderInterface { return !isUUID(symbol); }); - return { items }; + return { + items: items.map((item) => { + return { ...item, dataProviderInfo: this.getDataProviderInfo() }; + }) + }; } public async test(scraperConfiguration: ScraperConfiguration) { diff --git a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts index d9b4bd0e4..31c813180 100644 --- a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts +++ b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts @@ -13,6 +13,7 @@ import { } from '@ghostfolio/api/services/interfaces/interfaces'; import { ghostfolioFearAndGreedIndexSymbol } from '@ghostfolio/common/config'; import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper'; +import { DataProviderInfo } from '@ghostfolio/common/interfaces'; import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import { format } from 'date-fns'; @@ -37,6 +38,12 @@ export class RapidApiService implements DataProviderInterface { }; } + public getDataProviderInfo(): DataProviderInfo { + return { + isPremium: false + }; + } + public async getDividends({}: GetDividendsParams) { return {}; } diff --git a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts index 47869d3e8..335162023 100644 --- a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts +++ b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts @@ -14,6 +14,7 @@ import { } from '@ghostfolio/api/services/interfaces/interfaces'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; +import { DataProviderInfo } from '@ghostfolio/common/interfaces'; import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import { addDays, format, isSameDay } from 'date-fns'; @@ -47,6 +48,12 @@ export class YahooFinanceService implements DataProviderInterface { }; } + public getDataProviderInfo(): DataProviderInfo { + return { + isPremium: false + }; + } + public async getDividends({ from, granularity = 'day', @@ -283,6 +290,7 @@ export class YahooFinanceService implements DataProviderInterface { assetSubClass, symbol, currency: marketDataItem.currency, + dataProviderInfo: this.getDataProviderInfo(), dataSource: this.getName(), name: this.yahooFinanceDataEnhancerService.formatName({ longName: quote.longname, diff --git a/libs/common/src/lib/interfaces/data-provider-info.interface.ts b/libs/common/src/lib/interfaces/data-provider-info.interface.ts index 59f3a0b69..79d7d6940 100644 --- a/libs/common/src/lib/interfaces/data-provider-info.interface.ts +++ b/libs/common/src/lib/interfaces/data-provider-info.interface.ts @@ -1,4 +1,5 @@ export interface DataProviderInfo { - name: string; - url: string; + isPremium: boolean; + name?: string; + url?: string; } diff --git a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.html b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.html index bc6a6dcc3..87f771502 100644 --- a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.html +++ b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.html @@ -15,12 +15,15 @@ - {{ lookupItem.name }} -
+ {{ lookupItem.name }} + @if (lookupItem.dataProviderInfo.isPremium) { + + } + {{ lookupItem.symbol | gfSymbol }} ยท {{ lookupItem.currency }} diff --git a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.module.ts b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.module.ts index d7b1ed2f8..259403043 100644 --- a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.module.ts +++ b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.module.ts @@ -7,6 +7,7 @@ import { MatInputModule } from '@angular/material/input'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; import { SymbolAutocompleteComponent } from '@ghostfolio/ui/symbol-autocomplete/symbol-autocomplete.component'; +import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator'; @NgModule({ declarations: [SymbolAutocompleteComponent], @@ -14,6 +15,7 @@ import { SymbolAutocompleteComponent } from '@ghostfolio/ui/symbol-autocomplete/ imports: [ CommonModule, FormsModule, + GfPremiumIndicatorModule, GfSymbolModule, MatAutocompleteModule, MatFormFieldModule,