diff --git a/CHANGELOG.md b/CHANGELOG.md index 5abefa7bb..e7368c2f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Improved the data gathering handling on server restart + ## 1.35.0 - 08.08.2021 ### Changed diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 32a918147..3f93347a7 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -1,3 +1,4 @@ +import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { AdminData } from '@ghostfolio/common/interfaces'; @@ -7,6 +8,7 @@ import { Currency } from '@prisma/client'; @Injectable() export class AdminService { public constructor( + private readonly dataGatheringService: DataGatheringService, private readonly exchangeRateDataService: ExchangeRateDataService, private readonly prismaService: PrismaService ) {} @@ -68,18 +70,15 @@ export class AdminService { } private async getLastDataGathering() { - const lastDataGathering = await this.prismaService.property.findUnique({ - where: { key: 'LAST_DATA_GATHERING' } - }); + const lastDataGathering = + await this.dataGatheringService.getLastDataGathering(); - if (lastDataGathering?.value) { - return new Date(lastDataGathering.value); + if (lastDataGathering) { + return lastDataGathering; } const dataGatheringInProgress = - await this.prismaService.property.findUnique({ - where: { key: 'LOCKED_DATA_GATHERING' } - }); + await this.dataGatheringService.getIsInProgress(); if (dataGatheringInProgress) { return 'IN_PROGRESS'; diff --git a/apps/api/src/app/app.controller.ts b/apps/api/src/app/app.controller.ts index b02d56842..43b37ad18 100644 --- a/apps/api/src/app/app.controller.ts +++ b/apps/api/src/app/app.controller.ts @@ -1,12 +1,12 @@ +import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service'; import { Controller } from '@nestjs/common'; -import { PrismaService } from '../services/prisma.service'; import { RedisCacheService } from './redis-cache/redis-cache.service'; @Controller() export class AppController { public constructor( - private readonly prismaService: PrismaService, + private readonly dataGatheringService: DataGatheringService, private readonly redisCacheService: RedisCacheService ) { this.initialize(); @@ -15,17 +15,12 @@ export class AppController { private async initialize() { this.redisCacheService.reset(); - const isDataGatheringLocked = await this.prismaService.property.findUnique({ - where: { key: 'LOCKED_DATA_GATHERING' } - }); + const isDataGatheringInProgress = + await this.dataGatheringService.getIsInProgress(); - if (!isDataGatheringLocked) { - // Prepare for automatical data gather if not locked - await this.prismaService.property.deleteMany({ - where: { - OR: [{ key: 'LAST_DATA_GATHERING' }, { key: 'LOCKED_DATA_GATHERING' }] - } - }); + if (isDataGatheringInProgress) { + // Prepare for automatical data gathering, if hung up in progress state + await this.dataGatheringService.reset(); } } } diff --git a/apps/api/src/app/cache/cache.module.ts b/apps/api/src/app/cache/cache.module.ts index 7c5a01309..895c65d18 100644 --- a/apps/api/src/app/cache/cache.module.ts +++ b/apps/api/src/app/cache/cache.module.ts @@ -1,3 +1,10 @@ +import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; +import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service'; +import { DataProviderService } from '@ghostfolio/api/services/data-provider.service'; +import { AlphaVantageService } from '@ghostfolio/api/services/data-provider/alpha-vantage/alpha-vantage.service'; +import { GhostfolioScraperApiService } from '@ghostfolio/api/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service'; +import { RakutenRapidApiService } from '@ghostfolio/api/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service'; +import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { Module } from '@nestjs/common'; @@ -8,6 +15,16 @@ import { CacheService } from './cache.service'; @Module({ imports: [RedisCacheModule], controllers: [CacheController], - providers: [CacheService, PrismaService] + providers: [ + AlphaVantageService, + CacheService, + ConfigurationService, + DataGatheringService, + DataProviderService, + GhostfolioScraperApiService, + PrismaService, + RakutenRapidApiService, + YahooFinanceService + ] }) export class CacheModule {} diff --git a/apps/api/src/app/cache/cache.service.ts b/apps/api/src/app/cache/cache.service.ts index 854d33ef2..69c5fe66a 100644 --- a/apps/api/src/app/cache/cache.service.ts +++ b/apps/api/src/app/cache/cache.service.ts @@ -1,16 +1,14 @@ -import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service'; import { Injectable } from '@nestjs/common'; @Injectable() export class CacheService { - public constructor(private readonly prismaService: PrismaService) {} + public constructor( + private readonly dataGaterhingService: DataGatheringService + ) {} public async flush(): Promise { - await this.prismaService.property.deleteMany({ - where: { - OR: [{ key: 'LAST_DATA_GATHERING' }, { key: 'LOCKED_DATA_GATHERING' }] - } - }); + await this.dataGaterhingService.reset(); return; } diff --git a/apps/api/src/app/info/info.module.ts b/apps/api/src/app/info/info.module.ts index 8e3b49bb7..6ff7b5729 100644 --- a/apps/api/src/app/info/info.module.ts +++ b/apps/api/src/app/info/info.module.ts @@ -1,4 +1,10 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; +import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service'; +import { DataProviderService } from '@ghostfolio/api/services/data-provider.service'; +import { AlphaVantageService } from '@ghostfolio/api/services/data-provider/alpha-vantage/alpha-vantage.service'; +import { GhostfolioScraperApiService } from '@ghostfolio/api/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service'; +import { RakutenRapidApiService } from '@ghostfolio/api/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service'; +import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; @@ -14,6 +20,16 @@ import { InfoService } from './info.service'; }) ], controllers: [InfoController], - providers: [ConfigurationService, InfoService, PrismaService] + providers: [ + AlphaVantageService, + ConfigurationService, + DataGatheringService, + DataProviderService, + GhostfolioScraperApiService, + InfoService, + PrismaService, + RakutenRapidApiService, + YahooFinanceService + ] }) export class InfoModule {} diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index e4f641c7c..43aef61f6 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -1,4 +1,5 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; +import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { InfoItem } from '@ghostfolio/common/interfaces'; import { Subscription } from '@ghostfolio/common/interfaces/subscription.interface'; @@ -15,6 +16,7 @@ export class InfoService { public constructor( private readonly configurationService: ConfigurationService, + private readonly dataGatheringService: DataGatheringService, private readonly jwtService: JwtService, private readonly prismaService: PrismaService ) {} @@ -116,11 +118,10 @@ export class InfoService { } private async getLastDataGathering() { - const lastDataGathering = await this.prismaService.property.findUnique({ - where: { key: 'LAST_DATA_GATHERING' } - }); + const lastDataGathering = + await this.dataGatheringService.getLastDataGathering(); - return lastDataGathering?.value ? new Date(lastDataGathering.value) : null; + return lastDataGathering ?? null; } private async getStatistics() { diff --git a/apps/api/src/services/data-gathering.service.ts b/apps/api/src/services/data-gathering.service.ts index cbd1923e5..6bc01f221 100644 --- a/apps/api/src/services/data-gathering.service.ts +++ b/apps/api/src/services/data-gathering.service.ts @@ -250,6 +250,34 @@ export class DataGatheringService { }); } + public async getIsInProgress() { + return await this.prismaService.property.findUnique({ + where: { key: 'LOCKED_DATA_GATHERING' } + }); + } + + public async getLastDataGathering() { + const lastDataGathering = await this.prismaService.property.findUnique({ + where: { key: 'LAST_DATA_GATHERING' } + }); + + if (lastDataGathering?.value) { + return new Date(lastDataGathering.value); + } + + return undefined; + } + + public async reset() { + console.log('Data gathering has been reset.'); + + await this.prismaService.property.deleteMany({ + where: { + OR: [{ key: 'LAST_DATA_GATHERING' }, { key: 'LOCKED_DATA_GATHERING' }] + } + }); + } + private getBenchmarksToGather(startDate: Date): IDataGatheringItem[] { const benchmarksToGather = benchmarks.map(({ dataSource, symbol }) => { return { @@ -373,18 +401,13 @@ export class DataGatheringService { } private async isDataGatheringNeeded() { - const lastDataGathering = await this.prismaService.property.findUnique({ - where: { key: 'LAST_DATA_GATHERING' } - }); + const lastDataGathering = await this.getLastDataGathering(); const isDataGatheringLocked = await this.prismaService.property.findUnique({ where: { key: 'LOCKED_DATA_GATHERING' } }); - const diffInHours = differenceInHours( - new Date(), - new Date(lastDataGathering?.value) - ); + const diffInHours = differenceInHours(new Date(), lastDataGathering); return (diffInHours >= 1 || !lastDataGathering) && !isDataGatheringLocked; } diff --git a/apps/client/src/app/pages/admin/admin-page.component.ts b/apps/client/src/app/pages/admin/admin-page.component.ts index bc8669237..9e0097561 100644 --- a/apps/client/src/app/pages/admin/admin-page.component.ts +++ b/apps/client/src/app/pages/admin/admin-page.component.ts @@ -152,7 +152,7 @@ export class AdminPageComponent implements OnDestroy, OnInit { } else if (lastDataGathering === 'IN_PROGRESS') { this.dataGatheringInProgress = true; } else { - this.lastDataGathering = '-'; + this.lastDataGathering = 'Starting soon...'; } this.transactionCount = transactionCount;