From 5f1157ebf5dcf0ef915c08b0e6f58b2b384c9d4f Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Sun, 24 Apr 2022 15:58:18 +0200 Subject: [PATCH] Setup tagging system --- apps/api/src/app/info/info.module.ts | 4 ++- apps/api/src/app/info/info.service.ts | 7 +++-- apps/api/src/app/order/order.service.ts | 14 +++++++++ .../src/app/portfolio/portfolio.controller.ts | 6 +++- .../src/app/portfolio/portfolio.service.ts | 1 + apps/api/src/app/user/user.controller.ts | 2 +- apps/api/src/app/user/user.module.ts | 4 ++- apps/api/src/app/user/user.service.ts | 14 +++++++-- apps/api/src/services/tag/tag.module.ts | 11 +++++++ apps/api/src/services/tag/tag.service.ts | 30 +++++++++++++++++++ .../allocations/allocations-page.component.ts | 8 +++-- .../allocations/allocations-page.html | 1 + .../src/lib/interfaces/info-item.interface.ts | 2 ++ .../src/lib/interfaces/user.interface.ts | 3 +- prisma/schema.prisma | 7 +++++ 15 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 apps/api/src/services/tag/tag.module.ts create mode 100644 apps/api/src/services/tag/tag.service.ts diff --git a/apps/api/src/app/info/info.module.ts b/apps/api/src/app/info/info.module.ts index 5828ac963..338747ebc 100644 --- a/apps/api/src/app/info/info.module.ts +++ b/apps/api/src/app/info/info.module.ts @@ -6,6 +6,7 @@ import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-d 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 { TagModule } from '@ghostfolio/api/services/tag/tag.module'; import { Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; @@ -26,7 +27,8 @@ import { InfoService } from './info.service'; PrismaModule, PropertyModule, RedisCacheModule, - SymbolProfileModule + SymbolProfileModule, + TagModule ], providers: [InfoService] }) diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index 60df05c51..032b05f27 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -4,6 +4,7 @@ import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.se import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; +import { TagService } from '@ghostfolio/api/services/tag/tag.service'; import { DEMO_USER_ID, PROPERTY_IS_READ_ONLY_MODE, @@ -33,7 +34,8 @@ export class InfoService { private readonly jwtService: JwtService, private readonly prismaService: PrismaService, private readonly propertyService: PropertyService, - private readonly redisCacheService: RedisCacheService + private readonly redisCacheService: RedisCacheService, + private readonly tagService: TagService ) {} public async get(): Promise { @@ -105,7 +107,8 @@ export class InfoService { demoAuthToken: this.getDemoAuthToken(), lastDataGathering: await this.getLastDataGathering(), statistics: await this.getStatistics(), - subscriptions: await this.getSubscriptions() + subscriptions: await this.getSubscriptions(), + tags: await this.tagService.get() }; } diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index ee01b3092..131b09b15 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -152,11 +152,13 @@ export class OrderService { public async getOrders({ includeDrafts = false, + tags, types, userCurrency, userId }: { includeDrafts?: boolean; + tags?: string[]; types?: TypeOfOrder[]; userCurrency: string; userId: string; @@ -167,6 +169,18 @@ export class OrderService { where.isDraft = false; } + if (tags?.length > 0) { + where.tags = { + some: { + OR: tags.map((tag) => { + return { + name: tag + }; + }) + } + }; + } + if (types) { where.OR = types.map((type) => { return { diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index b62ea98db..9f9b20c56 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -161,7 +161,11 @@ export class PortfolioController { this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') && this.request.user.subscription.type === 'Basic'; - return { accounts, hasError, holdings: isBasicUser ? {} : holdings }; + return { + hasError, + accounts: tags ? {} : accounts, + holdings: isBasicUser ? {} : holdings + }; } @Get('investments') diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 15b496dc2..86fc1398f 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -1197,6 +1197,7 @@ export class PortfolioService { const orders = await this.orderService.getOrders({ includeDrafts, + tags, userCurrency, userId, types: ['BUY', 'SELL'] diff --git a/apps/api/src/app/user/user.controller.ts b/apps/api/src/app/user/user.controller.ts index 5bd14cfaa..e79f61dd4 100644 --- a/apps/api/src/app/user/user.controller.ts +++ b/apps/api/src/app/user/user.controller.ts @@ -34,7 +34,7 @@ import { UserService } from './user.service'; export class UserController { public constructor( private readonly configurationService: ConfigurationService, - private jwtService: JwtService, + private readonly jwtService: JwtService, private readonly propertyService: PropertyService, @Inject(REQUEST) private readonly request: RequestWithUser, private readonly userService: UserService diff --git a/apps/api/src/app/user/user.module.ts b/apps/api/src/app/user/user.module.ts index 976f5b6c3..6a705524f 100644 --- a/apps/api/src/app/user/user.module.ts +++ b/apps/api/src/app/user/user.module.ts @@ -2,6 +2,7 @@ import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscriptio import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; +import { TagModule } from '@ghostfolio/api/services/tag/tag.module'; import { Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; @@ -19,7 +20,8 @@ import { UserService } from './user.service'; }), PrismaModule, PropertyModule, - SubscriptionModule + SubscriptionModule, + TagModule ], providers: [UserService] }) diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index feed46434..0995ace2f 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -2,6 +2,7 @@ import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscripti import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; +import { TagService } from '@ghostfolio/api/services/tag/tag.service'; import { PROPERTY_IS_READ_ONLY_MODE, baseCurrency, @@ -13,7 +14,6 @@ import { hasRole, permissions } from '@ghostfolio/common/permissions'; -import { SubscriptionType } from '@ghostfolio/common/types/subscription.type'; import { Injectable } from '@nestjs/common'; import { Prisma, Role, User, ViewMode } from '@prisma/client'; @@ -30,7 +30,8 @@ export class UserService { private readonly configurationService: ConfigurationService, private readonly prismaService: PrismaService, private readonly propertyService: PropertyService, - private readonly subscriptionService: SubscriptionService + private readonly subscriptionService: SubscriptionService, + private readonly tagService: TagService ) {} public async getUser( @@ -51,12 +52,21 @@ export class UserService { orderBy: { User: { alias: 'asc' } }, where: { GranteeUser: { id } } }); + let tags = await this.tagService.getByUser(id); + + if ( + this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') && + subscription.type === 'Basic' + ) { + tags = []; + } return { alias, id, permissions, subscription, + tags, access: access.map((accessItem) => { return { alias: accessItem.User.alias, diff --git a/apps/api/src/services/tag/tag.module.ts b/apps/api/src/services/tag/tag.module.ts new file mode 100644 index 000000000..32d905884 --- /dev/null +++ b/apps/api/src/services/tag/tag.module.ts @@ -0,0 +1,11 @@ +import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; +import { Module } from '@nestjs/common'; + +import { TagService } from './tag.service'; + +@Module({ + exports: [TagService], + imports: [PrismaModule], + providers: [TagService] +}) +export class TagModule {} diff --git a/apps/api/src/services/tag/tag.service.ts b/apps/api/src/services/tag/tag.service.ts new file mode 100644 index 000000000..534a6e73d --- /dev/null +++ b/apps/api/src/services/tag/tag.service.ts @@ -0,0 +1,30 @@ +import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class TagService { + public constructor(private readonly prismaService: PrismaService) {} + + public async get() { + return this.prismaService.tag.findMany({ + orderBy: { + name: 'asc' + } + }); + } + + public async getByUser(userId: string) { + return this.prismaService.tag.findMany({ + orderBy: { + name: 'asc' + }, + where: { + orders: { + some: { + userId + } + } + } + }); + } +} diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts index 954fd4abd..ed8d9e199 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts @@ -74,7 +74,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { value: number; }; }; - public tags = ['high-risk', 'pension']; + public tags: string[] = []; public user: User; @@ -128,6 +128,10 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { if (state?.user) { this.user = state.user; + this.tags = this.user.tags.map((tag) => { + return tag.name; + }); + this.changeDetectorRef.markForCheck(); } }); @@ -300,7 +304,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { } } - if (position.assetClass === AssetClass.EQUITY) { + if (position.dataSource) { this.symbols[prettifySymbol(symbol)] = { dataSource: position.dataSource, name: position.name, diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.html b/apps/client/src/app/pages/portfolio/allocations/allocations-page.html index aaf4ee655..eebf007d6 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.html +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.html @@ -4,6 +4,7 @@

Allocations

diff --git a/libs/common/src/lib/interfaces/info-item.interface.ts b/libs/common/src/lib/interfaces/info-item.interface.ts index 443bb061a..f9d53b186 100644 --- a/libs/common/src/lib/interfaces/info-item.interface.ts +++ b/libs/common/src/lib/interfaces/info-item.interface.ts @@ -1,3 +1,4 @@ +import { Tag } from '@prisma/client'; import { Statistics } from './statistics.interface'; import { Subscription } from './subscription.interface'; @@ -13,4 +14,5 @@ export interface InfoItem { stripePublicKey?: string; subscriptions: Subscription[]; systemMessage?: string; + tags: Tag[]; } diff --git a/libs/common/src/lib/interfaces/user.interface.ts b/libs/common/src/lib/interfaces/user.interface.ts index 71288ccdb..a6bd3dab0 100644 --- a/libs/common/src/lib/interfaces/user.interface.ts +++ b/libs/common/src/lib/interfaces/user.interface.ts @@ -1,5 +1,5 @@ import { Access } from '@ghostfolio/api/app/user/interfaces/access.interface'; -import { Account } from '@prisma/client'; +import { Account, Tag } from '@prisma/client'; import { UserSettings } from './user-settings.interface'; @@ -14,4 +14,5 @@ export interface User { expiresAt?: Date; type: 'Basic' | 'Premium'; }; + tags: Tag[]; } diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e220b4ebb..db2ee93c8 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -79,6 +79,7 @@ model Order { quantity Float SymbolProfile SymbolProfile @relation(fields: [symbolProfileId], references: [id]) symbolProfileId String + tags Tag[] type Type unitPrice Float updatedAt DateTime @updatedAt @@ -148,6 +149,12 @@ model Subscription { userId String } +model Tag { + id String @id @default(uuid()) + name String @unique + orders Order[] +} + model User { Access Access[] @relation("accessGet") AccessGive Access[] @relation(name: "accessGive")