diff --git a/CHANGELOG.md b/CHANGELOG.md index fcb0fedf2..7d48f28e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added the symbol mapping attribute to the symbol profile model + ### Changed - Improved the registration page +### Todo + +- Apply data migration (`yarn database:migrate`) + ## 1.68.0 - 01.11.2021 ### Changed @@ -66,7 +74,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Todo -- Apply data migration (`yarn prisma migrate deploy`) +- Apply data migration (`yarn database:migrate`) ## 1.62.0 - 17.10.2021 @@ -164,7 +172,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Todo -- Apply data migration (`yarn prisma migrate deploy`) +- Apply data migration (`yarn database:migrate`) ## 1.55.0 - 20.09.2021 @@ -179,7 +187,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Todo -- Apply data migration (`yarn prisma migrate deploy`) +- Apply data migration (`yarn database:migrate`) ## 1.54.0 - 18.09.2021 @@ -200,7 +208,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Todo -- Apply data migration (`yarn prisma migrate deploy`) +- Apply data migration (`yarn database:migrate`) ## 1.53.0 - 13.09.2021 @@ -322,7 +330,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Todo -- Apply data migration (`yarn prisma migrate deploy`) +- Apply data migration (`yarn database:migrate`) ## 1.41.0 - 21.08.2021 @@ -375,7 +383,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Todo -- Apply data migration (`yarn prisma migrate deploy`) +- Apply data migration (`yarn database:migrate`) ## 1.38.0 - 14.08.2021 @@ -435,7 +443,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Todo -- Apply data migration (`yarn prisma migrate deploy`) +- Apply data migration (`yarn database:migrate`) ## 1.34.0 - 07.08.2021 diff --git a/README.md b/README.md index a4ed80f52..03f8dc8a2 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ docker-compose -f docker/docker-compose-build-local.yml up Run the following command to setup the database once Ghostfolio is running: ```bash -docker-compose -f docker/docker-compose-build-local.yml exec ghostfolio yarn setup:database +docker-compose -f docker/docker-compose-build-local.yml exec ghostfolio yarn database:setup ``` ### Fetch Historical Data @@ -112,6 +112,14 @@ Open http://localhost:3333 in your browser and accomplish these steps: 1. Go to the _Admin Control Panel_ and click _Gather All Data_ to fetch historical data 1. Click _Sign out_ and check out the _Live Demo_ +### Migrate Database + +With the following command you can keep your database schema in sync after a Ghostfolio version update: + +```bash +docker-compose -f docker/docker-compose-build-local.yml exec ghostfolio yarn database:migrate +``` + ## Development ### Prerequisites @@ -126,7 +134,7 @@ Open http://localhost:3333 in your browser and accomplish these steps: 1. Run `cd docker` 1. Run `docker compose up -d` to start [PostgreSQL](https://www.postgresql.org) and [Redis](https://redis.io) 1. Run `cd -` to go back to the project root directory -1. Run `yarn setup:database` to initialize the database schema and populate your database with (example) data +1. Run `yarn database:setup` to initialize the database schema and populate your database with (example) data 1. Start server and client (see [_Development_](#Development)) 1. Login as _Admin_ with the following _Security Token_: `ae76872ae8f3419c6d6f64bf51888ecbcc703927a342d815fafe486acdb938da07d0cf44fca211a0be74a423238f535362d390a41e81e633a9ce668a6e31cdf9` 1. Go to the _Admin Control Panel_ and click _Gather All Data_ to fetch historical data diff --git a/apps/api/src/app/cache/cache.module.ts b/apps/api/src/app/cache/cache.module.ts index b346ee444..a823c2d1e 100644 --- a/apps/api/src/app/cache/cache.module.ts +++ b/apps/api/src/app/cache/cache.module.ts @@ -6,6 +6,7 @@ import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.se import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data.module'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile.module'; import { Module } from '@nestjs/common'; import { CacheController } from './cache.controller'; @@ -15,7 +16,8 @@ import { CacheController } from './cache.controller'; DataGatheringModule, DataProviderModule, ExchangeRateDataModule, - RedisCacheModule + RedisCacheModule, + SymbolProfileModule ], controllers: [CacheController], providers: [ diff --git a/apps/api/src/app/info/info.module.ts b/apps/api/src/app/info/info.module.ts index 0db322a85..48c36e7ad 100644 --- a/apps/api/src/app/info/info.module.ts +++ b/apps/api/src/app/info/info.module.ts @@ -4,6 +4,7 @@ import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.se import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data.module'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile.module'; import { Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; @@ -18,7 +19,8 @@ import { InfoService } from './info.service'; JwtModule.register({ secret: process.env.JWT_SECRET_KEY, signOptions: { expiresIn: '30 days' } - }) + }), + SymbolProfileModule ], controllers: [InfoController], providers: [ diff --git a/apps/api/src/app/portfolio/portfolio.module.ts b/apps/api/src/app/portfolio/portfolio.module.ts index 112f0e4ef..2af37562c 100644 --- a/apps/api/src/app/portfolio/portfolio.module.ts +++ b/apps/api/src/app/portfolio/portfolio.module.ts @@ -8,7 +8,7 @@ import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data- import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data.module'; import { ImpersonationModule } from '@ghostfolio/api/services/impersonation.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; -import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile.service'; +import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile.module'; import { Module } from '@nestjs/common'; import { CurrentRateService } from './current-rate.service'; @@ -27,6 +27,7 @@ import { RulesService } from './rules.service'; ImpersonationModule, OrderModule, PrismaModule, + SymbolProfileModule, UserModule ], controllers: [PortfolioController], @@ -35,8 +36,7 @@ import { RulesService } from './rules.service'; CurrentRateService, MarketDataService, PortfolioService, - RulesService, - SymbolProfileService + RulesService ] }) export class PortfolioModule {} diff --git a/apps/api/src/services/data-gathering.module.ts b/apps/api/src/services/data-gathering.module.ts index 85f2f4983..f458e3bda 100644 --- a/apps/api/src/services/data-gathering.module.ts +++ b/apps/api/src/services/data-gathering.module.ts @@ -6,6 +6,7 @@ import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; import { Module } from '@nestjs/common'; import { ExchangeRateDataModule } from './exchange-rate-data.module'; +import { SymbolProfileModule } from './symbol-profile.module'; @Module({ imports: [ @@ -13,7 +14,8 @@ import { ExchangeRateDataModule } from './exchange-rate-data.module'; DataEnhancerModule, DataProviderModule, ExchangeRateDataModule, - PrismaModule + PrismaModule, + SymbolProfileModule ], providers: [DataGatheringService], exports: [DataEnhancerModule, DataGatheringService] diff --git a/apps/api/src/services/data-gathering.service.ts b/apps/api/src/services/data-gathering.service.ts index 65e08fcf5..1e3ecb478 100644 --- a/apps/api/src/services/data-gathering.service.ts +++ b/apps/api/src/services/data-gathering.service.ts @@ -1,3 +1,4 @@ +import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile.service'; import { benchmarks, ghostfolioFearAndGreedIndexSymbol @@ -32,7 +33,8 @@ export class DataGatheringService { private readonly dataProviderService: DataProviderService, private readonly exchangeRateDataService: ExchangeRateDataService, private readonly ghostfolioScraperApi: GhostfolioScraperApiService, - private readonly prismaService: PrismaService + private readonly prismaService: PrismaService, + private readonly symbolProfileService: SymbolProfileService ) {} public async gather7Days() { @@ -132,13 +134,22 @@ export class DataGatheringService { } const currentData = await this.dataProviderService.get(dataGatheringItems); + const symbolProfiles = await this.symbolProfileService.getSymbolProfiles( + dataGatheringItems.map(({ symbol }) => { + return symbol; + }) + ); for (const [symbol, response] of Object.entries(currentData)) { + const symbolMapping = symbolProfiles.find((symbolProfile) => { + return symbolProfile.symbol === symbol; + })?.symbolMapping; + for (const dataEnhancer of this.dataEnhancers) { try { currentData[symbol] = await dataEnhancer.enhance({ response, - symbol + symbol: symbolMapping[dataEnhancer.getName()] ?? symbol }); } catch (error) { console.error(`Failed to enhance data for symbol ${symbol}`, error); diff --git a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts index eb018c1c9..d7dfb3b42 100644 --- a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts +++ b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts @@ -70,4 +70,8 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface { return Promise.resolve(response); } + + public getName() { + return 'TRACKINSIGHT'; + } } diff --git a/apps/api/src/services/data-provider/interfaces/data-enhancer.interface.ts b/apps/api/src/services/data-provider/interfaces/data-enhancer.interface.ts index 1a0d4f87a..26585b320 100644 --- a/apps/api/src/services/data-provider/interfaces/data-enhancer.interface.ts +++ b/apps/api/src/services/data-provider/interfaces/data-enhancer.interface.ts @@ -8,4 +8,6 @@ export interface DataEnhancerInterface { response: IDataProviderResponse; symbol: string; }): Promise; + + getName(): string; } diff --git a/apps/api/src/services/interfaces/symbol-profile.interface.ts b/apps/api/src/services/interfaces/symbol-profile.interface.ts index 2747319d1..80e9da1e7 100644 --- a/apps/api/src/services/interfaces/symbol-profile.interface.ts +++ b/apps/api/src/services/interfaces/symbol-profile.interface.ts @@ -5,13 +5,14 @@ import { AssetClass, AssetSubClass, DataSource } from '@prisma/client'; export interface EnhancedSymbolProfile { assetClass: AssetClass; assetSubClass: AssetSubClass; + countries: Country[]; createdAt: Date; currency: string | null; dataSource: DataSource; id: string; name: string | null; - updatedAt: Date; - symbol: string; - countries: Country[]; sectors: Sector[]; + symbol: string; + symbolMapping?: { [key: string]: string }; + updatedAt: Date; } diff --git a/apps/api/src/services/symbol-profile.module.ts b/apps/api/src/services/symbol-profile.module.ts new file mode 100644 index 000000000..ac1337e87 --- /dev/null +++ b/apps/api/src/services/symbol-profile.module.ts @@ -0,0 +1,11 @@ +import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; +import { Module } from '@nestjs/common'; + +import { SymbolProfileService } from './symbol-profile.service'; + +@Module({ + imports: [PrismaModule], + providers: [SymbolProfileService], + exports: [SymbolProfileService] +}) +export class SymbolProfileModule {} diff --git a/apps/api/src/services/symbol-profile.service.ts b/apps/api/src/services/symbol-profile.service.ts index 188683efd..acf8118e2 100644 --- a/apps/api/src/services/symbol-profile.service.ts +++ b/apps/api/src/services/symbol-profile.service.ts @@ -29,7 +29,8 @@ export class SymbolProfileService { return symbolProfiles.map((symbolProfile) => ({ ...symbolProfile, countries: this.getCountries(symbolProfile), - sectors: this.getSectors(symbolProfile) + sectors: this.getSectors(symbolProfile), + symbolMapping: this.getSymbolMapping(symbolProfile) })); } @@ -61,4 +62,12 @@ export class SymbolProfileService { } ); } + + private getSymbolMapping(symbolProfile: SymbolProfile) { + return ( + (symbolProfile['symbolMapping'] as { + [key: string]: string; + }) ?? {} + ); + } } diff --git a/package.json b/package.json index 629c49be7..be0563899 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,10 @@ "database:format-schema": "prisma format", "database:generate-typings": "prisma generate", "database:gui": "prisma studio", + "database:migrate": "prisma migrate deploy", "database:push": "prisma db push", "database:seed": "prisma db seed --preview-feature", + "database:setup": "yarn database:push && yarn database:seed", "dep-graph": "nx dep-graph", "e2e": "ng e2e", "format": "nx format:write", @@ -33,7 +35,6 @@ "nx": "nx", "postinstall": "prisma generate && ngcc --properties es2015 browser module main", "replace-placeholders-in-build": "node ./replace.build.js", - "setup:database": "yarn database:push && yarn database:seed", "start": "node dist/apps/api/main", "start:client": "ng serve client --hmr -o", "start:prod": "node apps/api/main", diff --git a/prisma/migrations/20211107082008_added_symbol_mapping_to_symbol_profile/migration.sql b/prisma/migrations/20211107082008_added_symbol_mapping_to_symbol_profile/migration.sql new file mode 100644 index 000000000..a6cdeb92c --- /dev/null +++ b/prisma/migrations/20211107082008_added_symbol_mapping_to_symbol_profile/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "SymbolProfile" ADD COLUMN "symbolMapping" JSONB; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b008492dd..d087f5463 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -130,6 +130,7 @@ model SymbolProfile { updatedAt DateTime @updatedAt sectors Json? symbol String + symbolMapping Json? @@unique([dataSource, symbol]) }