Release/1.258.0 (#1878)
* Release 1.258.0 * Introduce data source mapping * Update changelog
This commit is contained in:
parent
ffa020ee2a
commit
5eff8402db
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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: [
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
},
|
},
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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';
|
||||||
|
@ -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": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user