Feature/add data source as unique constraint to market data schema (#1889)

* Add dataSource as unique constraint to MarketData schema

* Update changelog
pull/1891/head
Thomas Kaul 2 years ago committed by GitHub
parent 3dafbf7fef
commit cddea0401f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## Unreleased
### Added
- Added `dataSource` as a unique constraint to the `MarketData` database schema
### Fixed ### Fixed
- Removed the unnecessary sort header of the comment column in the historical market data table of the admin control panel - Removed the unnecessary sort header of the comment column in the historical market data table of the admin control panel

@ -319,7 +319,8 @@ export class AdminController {
return this.marketDataService.updateMarketData({ return this.marketDataService.updateMarketData({
data: { ...data, dataSource }, data: { ...data, dataSource },
where: { where: {
date_symbol: { dataSource_date_symbol: {
dataSource,
date, date,
symbol symbol
} }

@ -91,6 +91,7 @@ describe('CurrentRateService', () => {
null, null,
[], [],
null, null,
null,
propertyService propertyService
); );
exchangeRateDataService = new ExchangeRateDataService( exchangeRateDataService = new ExchangeRateDataService(

@ -102,7 +102,7 @@ export class DataGatheringService {
symbol symbol
}, },
update: { marketPrice }, update: { marketPrice },
where: { date_symbol: { date, symbol } } where: { dataSource_date_symbol: { dataSource, date, symbol } }
}); });
} }
} catch (error) { } catch (error) {

@ -7,20 +7,22 @@ import { GoogleSheetsService } from '@ghostfolio/api/services/data-provider/goog
import { ManualService } from '@ghostfolio/api/services/data-provider/manual/manual.service'; import { ManualService } from '@ghostfolio/api/services/data-provider/manual/manual.service';
import { RapidApiService } from '@ghostfolio/api/services/data-provider/rapid-api/rapid-api.service'; import { RapidApiService } from '@ghostfolio/api/services/data-provider/rapid-api/rapid-api.service';
import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service'; import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service';
import { MarketDataModule } from '@ghostfolio/api/services/market-data.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma.module';
import { PropertyModule } from '@ghostfolio/api/services/property/property.module';
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile.module';
import { Module } from '@nestjs/common'; 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: [
ConfigurationModule, ConfigurationModule,
CryptocurrencyModule, CryptocurrencyModule,
DataEnhancerModule, DataEnhancerModule,
MarketDataModule,
PrismaModule, PrismaModule,
PropertyModule, PropertyModule,
SymbolProfileModule SymbolProfileModule

@ -6,6 +6,7 @@ import {
IDataProviderHistoricalResponse, IDataProviderHistoricalResponse,
IDataProviderResponse IDataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { MarketDataService } from '@ghostfolio/api/services/market-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { DATE_FORMAT, getStartOfUtcDate } from '@ghostfolio/common/helper'; import { DATE_FORMAT, getStartOfUtcDate } from '@ghostfolio/common/helper';
import { UserWithSettings } from '@ghostfolio/common/types'; import { UserWithSettings } from '@ghostfolio/common/types';
@ -25,6 +26,7 @@ export class DataProviderService {
private readonly configurationService: ConfigurationService, private readonly configurationService: ConfigurationService,
@Inject('DataProviderInterfaces') @Inject('DataProviderInterfaces')
private readonly dataProviderInterfaces: DataProviderInterface[], private readonly dataProviderInterfaces: DataProviderInterface[],
private readonly marketDataService: MarketDataService,
private readonly prismaService: PrismaService, private readonly prismaService: PrismaService,
private readonly propertyService: PropertyService private readonly propertyService: PropertyService
) { ) {
@ -276,35 +278,23 @@ export class DataProviderService {
); );
try { try {
const date = getStartOfUtcDate(new Date()); await this.marketDataService.updateMany({
data: Object.keys(response)
// Upsert quotes by imitating missing upsertMany functionality
// with $transaction
const upsertPromises = Object.keys(response)
.filter((symbol) => { .filter((symbol) => {
return ( return (
isNumber(response[symbol].marketPrice) && isNumber(response[symbol].marketPrice) &&
response[symbol].marketPrice > 0 response[symbol].marketPrice > 0
); );
}) })
.map((symbol) => .map((symbol) => {
this.prismaService.marketData.upsert({ return {
create: {
date,
symbol, symbol,
dataSource: response[symbol].dataSource, dataSource: response[symbol].dataSource,
date: getStartOfUtcDate(new Date()),
marketPrice: response[symbol].marketPrice marketPrice: response[symbol].marketPrice
}, };
update: {
marketPrice: response[symbol].marketPrice
},
where: {
date_symbol: { date, symbol }
}
}) })
); });
await this.prismaService.$transaction(upsertPromises);
} catch {} } catch {}
}) })
); );

@ -102,11 +102,46 @@ export class MarketDataService {
where, where,
create: { create: {
dataSource: data.dataSource, dataSource: data.dataSource,
date: where.date_symbol.date, date: where.dataSource_date_symbol.date,
marketPrice: data.marketPrice, marketPrice: data.marketPrice,
symbol: where.date_symbol.symbol symbol: where.dataSource_date_symbol.symbol
}, },
update: { marketPrice: data.marketPrice } update: { marketPrice: data.marketPrice }
}); });
} }
/**
* Upsert market data by imitating missing upsertMany functionality
* with $transaction
*/
public async updateMany({
data
}: {
data: Prisma.MarketDataUpdateInput[];
}): Promise<MarketData[]> {
const upsertPromises = data.map(
({ dataSource, date, marketPrice, symbol }) => {
return this.prismaService.marketData.upsert({
create: {
dataSource: <DataSource>dataSource,
date: <Date>date,
marketPrice: <number>marketPrice,
symbol: <string>symbol
},
update: {
marketPrice: <number>marketPrice
},
where: {
dataSource_date_symbol: {
dataSource: <DataSource>dataSource,
date: <Date>date,
symbol: <string>symbol
}
}
});
}
);
return this.prismaService.$transaction(upsertPromises);
}
} }

@ -0,0 +1,5 @@
-- DropIndex
DROP INDEX "MarketData_date_symbol_key";
-- CreateIndex
CREATE UNIQUE INDEX "MarketData_dataSource_date_symbol_key" ON "MarketData"("dataSource", "date", "symbol");

@ -66,7 +66,7 @@ model MarketData {
symbol String symbol String
marketPrice Float marketPrice Float
@@unique([date, symbol]) @@unique([dataSource, date, symbol])
@@index([symbol]) @@index([symbol])
} }

Loading…
Cancel
Save