Release/1.258.0 (#1878)

* Release 1.258.0
  * Introduce data source mapping

* Update changelog
This commit is contained in:
Thomas Kaul 2023-04-20 09:07:22 +02:00 committed by GitHub
parent ffa020ee2a
commit 5eff8402db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 85 additions and 17 deletions

View File

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 1.258.0 - 2023-04-20
### Added
- Introduced a data source mapping
## 1.257.0 - 2023-04-18 ## 1.257.0 - 2023-04-18
### Added ### Added

View File

@ -6,6 +6,7 @@ import { DataSource, MarketData } from '@prisma/client';
import { CurrentRateService } from './current-rate.service'; import { CurrentRateService } from './current-rate.service';
import { GetValueObject } from './interfaces/get-value-object.interface'; import { GetValueObject } from './interfaces/get-value-object.interface';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
jest.mock('@ghostfolio/api/services/market-data.service', () => { jest.mock('@ghostfolio/api/services/market-data.service', () => {
return { return {
@ -67,14 +68,32 @@ jest.mock('@ghostfolio/api/services/exchange-rate-data.service', () => {
}; };
}); });
jest.mock('@ghostfolio/api/services/property/property.service', () => {
return {
PropertyService: jest.fn().mockImplementation(() => {
return {
getByKey: (key: string) => Promise.resolve({})
};
})
};
});
describe('CurrentRateService', () => { describe('CurrentRateService', () => {
let currentRateService: CurrentRateService; let currentRateService: CurrentRateService;
let dataProviderService: DataProviderService; let dataProviderService: DataProviderService;
let exchangeRateDataService: ExchangeRateDataService; let exchangeRateDataService: ExchangeRateDataService;
let marketDataService: MarketDataService; let marketDataService: MarketDataService;
let propertyService: PropertyService;
beforeAll(async () => { beforeAll(async () => {
dataProviderService = new DataProviderService(null, [], null); propertyService = new PropertyService(null);
dataProviderService = new DataProviderService(
null,
[],
null,
propertyService
);
exchangeRateDataService = new ExchangeRateDataService( exchangeRateDataService = new ExchangeRateDataService(
null, null,
null, null,

View File

@ -14,6 +14,7 @@ import { Module } from '@nestjs/common';
import { DataEnhancerModule } from './data-enhancer/data-enhancer.module'; import { DataEnhancerModule } from './data-enhancer/data-enhancer.module';
import { YahooFinanceDataEnhancerService } from './data-enhancer/yahoo-finance/yahoo-finance.service'; import { YahooFinanceDataEnhancerService } from './data-enhancer/yahoo-finance/yahoo-finance.service';
import { DataProviderService } from './data-provider.service'; import { DataProviderService } from './data-provider.service';
import { PropertyModule } from '@ghostfolio/api/services/property/property.module';
@Module({ @Module({
imports: [ imports: [
@ -21,6 +22,7 @@ import { DataProviderService } from './data-provider.service';
CryptocurrencyModule, CryptocurrencyModule,
DataEnhancerModule, DataEnhancerModule,
PrismaModule, PrismaModule,
PropertyModule,
SymbolProfileModule SymbolProfileModule
], ],
providers: [ providers: [

View File

@ -14,15 +14,29 @@ import { Inject, Injectable, Logger } from '@nestjs/common';
import { DataSource, MarketData, SymbolProfile } from '@prisma/client'; import { DataSource, MarketData, SymbolProfile } from '@prisma/client';
import { format, isValid } from 'date-fns'; import { format, isValid } from 'date-fns';
import { groupBy, isEmpty } from 'lodash'; import { groupBy, isEmpty } from 'lodash';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { PROPERTY_DATA_SOURCE_MAPPING } from '@ghostfolio/common/config';
@Injectable() @Injectable()
export class DataProviderService { export class DataProviderService {
private dataProviderMapping: { [dataProviderName: string]: string };
public constructor( public constructor(
private readonly configurationService: ConfigurationService, private readonly configurationService: ConfigurationService,
@Inject('DataProviderInterfaces') @Inject('DataProviderInterfaces')
private readonly dataProviderInterfaces: DataProviderInterface[], private readonly dataProviderInterfaces: DataProviderInterface[],
private readonly prismaService: PrismaService private readonly prismaService: PrismaService,
) {} private readonly propertyService: PropertyService
) {
this.initialize();
}
public async initialize() {
this.dataProviderMapping =
((await this.propertyService.getByKey(PROPERTY_DATA_SOURCE_MAPPING)) as {
[dataProviderName: string]: string;
}) ?? {};
}
public async getDividends({ public async getDividends({
dataSource, dataSource,
@ -314,6 +328,21 @@ export class DataProviderService {
private getDataProvider(providerName: DataSource) { private getDataProvider(providerName: DataSource) {
for (const dataProviderInterface of this.dataProviderInterfaces) { for (const dataProviderInterface of this.dataProviderInterfaces) {
if (this.dataProviderMapping[dataProviderInterface.getName()]) {
const mappedDataProviderInterface = this.dataProviderInterfaces.find(
(currentDataProviderInterface) => {
return (
currentDataProviderInterface.getName() ===
this.dataProviderMapping[dataProviderInterface.getName()]
);
}
);
if (mappedDataProviderInterface) {
return mappedDataProviderInterface;
}
}
if (dataProviderInterface.getName() === providerName) { if (dataProviderInterface.getName() === providerName) {
return dataProviderInterface; return dataProviderInterface;
} }

View File

@ -146,12 +146,20 @@ export class EodHistoricalDataService implements DataProviderInterface {
result: { [symbol: string]: IDataProviderResponse }, result: { [symbol: string]: IDataProviderResponse },
{ close, code, timestamp } { close, code, timestamp }
) => { ) => {
result[code] = { const currency = this.convertCurrency(
currency: this.convertCurrency(searchResponse?.items[0]?.currency), searchResponse?.items[0]?.currency
dataSource: DataSource.EOD_HISTORICAL_DATA, );
marketPrice: close,
marketState: isToday(new Date(timestamp * 1000)) ? 'open' : 'closed' if (currency) {
}; result[code] = {
currency,
dataSource: DataSource.EOD_HISTORICAL_DATA,
marketPrice: close,
marketState: isToday(new Date(timestamp * 1000))
? 'open'
: 'closed'
};
}
return result; return result;
}, },

View File

@ -62,7 +62,8 @@ export class ExchangeRateDataService {
getYesterday() getYesterday()
); );
if (Object.keys(result).length !== this.currencyPairs.length) { // TODO: add fallback
/*if (Object.keys(result).length !== this.currencyPairs.length) {
// Load currencies directly from data provider as a fallback // Load currencies directly from data provider as a fallback
// if historical data is not fully available // if historical data is not fully available
const historicalData = await this.dataProviderService.getQuotes( const historicalData = await this.dataProviderService.getQuotes(
@ -72,13 +73,15 @@ export class ExchangeRateDataService {
); );
Object.keys(historicalData).forEach((key) => { Object.keys(historicalData).forEach((key) => {
result[key] = { if (isNumber(historicalData[key].marketPrice)) {
[format(getYesterday(), DATE_FORMAT)]: { result[key] = {
marketPrice: historicalData[key].marketPrice [format(getYesterday(), DATE_FORMAT)]: {
} marketPrice: historicalData[key].marketPrice
}; }
};
}
}); });
} }*/
const resultExtended = result; const resultExtended = result;

View File

@ -73,6 +73,7 @@ export const PROPERTY_BENCHMARKS = 'BENCHMARKS';
export const PROPERTY_COUNTRIES_OF_SUBSCRIBERS = 'COUNTRIES_OF_SUBSCRIBERS'; export const PROPERTY_COUNTRIES_OF_SUBSCRIBERS = 'COUNTRIES_OF_SUBSCRIBERS';
export const PROPERTY_COUPONS = 'COUPONS'; export const PROPERTY_COUPONS = 'COUPONS';
export const PROPERTY_CURRENCIES = 'CURRENCIES'; export const PROPERTY_CURRENCIES = 'CURRENCIES';
export const PROPERTY_DATA_SOURCE_MAPPING = 'DATA_SOURCE_MAPPING';
export const PROPERTY_DEMO_USER_ID = 'DEMO_USER_ID'; export const PROPERTY_DEMO_USER_ID = 'DEMO_USER_ID';
export const PROPERTY_IS_READ_ONLY_MODE = 'IS_READ_ONLY_MODE'; export const PROPERTY_IS_READ_ONLY_MODE = 'IS_READ_ONLY_MODE';
export const PROPERTY_IS_USER_SIGNUP_ENABLED = 'IS_USER_SIGNUP_ENABLED'; export const PROPERTY_IS_USER_SIGNUP_ENABLED = 'IS_USER_SIGNUP_ENABLED';

View File

@ -1,6 +1,6 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "1.257.0", "version": "1.258.0",
"homepage": "https://ghostfol.io", "homepage": "https://ghostfol.io",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {