From f08b0b570b6651a9e2787ee845957f65d0d36410 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 29 Jun 2024 16:30:40 +0200 Subject: [PATCH] Feature/support derived currencies in currency validation (#3529) * Support derived currencies in currency validation * Update changelog --- CHANGELOG.md | 1 + .../api/src/app/account/create-account.dto.ts | 5 ++- .../api/src/app/account/update-account.dto.ts | 5 ++- .../src/app/admin/update-asset-profile.dto.ts | 5 ++- apps/api/src/app/order/create-order.dto.ts | 6 +-- apps/api/src/app/order/update-order.dto.ts | 6 +-- .../src/app/user/update-user-setting.dto.ts | 4 +- apps/api/src/validators/is-currency-code.ts | 44 +++++++++++++++++++ test/import/ok-derived-currency.json | 30 +++++++++++++ 9 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 apps/api/src/validators/is-currency-code.ts create mode 100644 test/import/ok-derived-currency.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bd423470..0ba81e462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Added support for derived currencies in the currency validation - Added support for automatic deletion of unused asset profiles when deleting activities ### Fixed diff --git a/apps/api/src/app/account/create-account.dto.ts b/apps/api/src/app/account/create-account.dto.ts index b719c2619..f3c88316f 100644 --- a/apps/api/src/app/account/create-account.dto.ts +++ b/apps/api/src/app/account/create-account.dto.ts @@ -1,7 +1,8 @@ +import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; + import { Transform, TransformFnParams } from 'class-transformer'; import { IsBoolean, - IsISO4217CurrencyCode, IsNumber, IsOptional, IsString, @@ -20,7 +21,7 @@ export class CreateAccountDto { ) comment?: string; - @IsISO4217CurrencyCode() + @IsCurrencyCode() currency: string; @IsOptional() diff --git a/apps/api/src/app/account/update-account.dto.ts b/apps/api/src/app/account/update-account.dto.ts index 1b84a9bf6..6b87af71b 100644 --- a/apps/api/src/app/account/update-account.dto.ts +++ b/apps/api/src/app/account/update-account.dto.ts @@ -1,7 +1,8 @@ +import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; + import { Transform, TransformFnParams } from 'class-transformer'; import { IsBoolean, - IsISO4217CurrencyCode, IsNumber, IsOptional, IsString, @@ -20,7 +21,7 @@ export class UpdateAccountDto { ) comment?: string; - @IsISO4217CurrencyCode() + @IsCurrencyCode() currency: string; @IsString() diff --git a/apps/api/src/app/admin/update-asset-profile.dto.ts b/apps/api/src/app/admin/update-asset-profile.dto.ts index e3de3cab1..8c9ae220b 100644 --- a/apps/api/src/app/admin/update-asset-profile.dto.ts +++ b/apps/api/src/app/admin/update-asset-profile.dto.ts @@ -1,8 +1,9 @@ +import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; + import { AssetClass, AssetSubClass, Prisma } from '@prisma/client'; import { IsArray, IsEnum, - IsISO4217CurrencyCode, IsObject, IsOptional, IsString, @@ -26,7 +27,7 @@ export class UpdateAssetProfileDto { @IsOptional() countries?: Prisma.InputJsonArray; - @IsISO4217CurrencyCode() + @IsCurrencyCode() @IsOptional() currency?: string; diff --git a/apps/api/src/app/order/create-order.dto.ts b/apps/api/src/app/order/create-order.dto.ts index 72aba3a3b..6f52e7032 100644 --- a/apps/api/src/app/order/create-order.dto.ts +++ b/apps/api/src/app/order/create-order.dto.ts @@ -1,3 +1,4 @@ +import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; import { IsAfter1970Constraint } from '@ghostfolio/common/validator-constraints/is-after-1970'; import { @@ -12,7 +13,6 @@ import { IsArray, IsBoolean, IsEnum, - IsISO4217CurrencyCode, IsISO8601, IsNumber, IsOptional, @@ -42,10 +42,10 @@ export class CreateOrderDto { ) comment?: string; - @IsISO4217CurrencyCode() + @IsCurrencyCode() currency: string; - @IsISO4217CurrencyCode() + @IsCurrencyCode() @IsOptional() customCurrency?: string; diff --git a/apps/api/src/app/order/update-order.dto.ts b/apps/api/src/app/order/update-order.dto.ts index 2fd33b743..eabd1f418 100644 --- a/apps/api/src/app/order/update-order.dto.ts +++ b/apps/api/src/app/order/update-order.dto.ts @@ -1,3 +1,4 @@ +import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; import { IsAfter1970Constraint } from '@ghostfolio/common/validator-constraints/is-after-1970'; import { @@ -11,7 +12,6 @@ import { Transform, TransformFnParams } from 'class-transformer'; import { IsArray, IsEnum, - IsISO4217CurrencyCode, IsISO8601, IsNumber, IsOptional, @@ -41,10 +41,10 @@ export class UpdateOrderDto { ) comment?: string; - @IsISO4217CurrencyCode() + @IsCurrencyCode() currency: string; - @IsISO4217CurrencyCode() + @IsCurrencyCode() @IsOptional() customCurrency?: string; diff --git a/apps/api/src/app/user/update-user-setting.dto.ts b/apps/api/src/app/user/update-user-setting.dto.ts index d260b3aaf..1fc02ff4d 100644 --- a/apps/api/src/app/user/update-user-setting.dto.ts +++ b/apps/api/src/app/user/update-user-setting.dto.ts @@ -1,3 +1,4 @@ +import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; import type { ColorScheme, DateRange, @@ -7,7 +8,6 @@ import type { import { IsArray, IsBoolean, - IsISO4217CurrencyCode, IsISO8601, IsIn, IsNumber, @@ -21,7 +21,7 @@ export class UpdateUserSettingDto { @IsOptional() annualInterestRate?: number; - @IsISO4217CurrencyCode() + @IsCurrencyCode() @IsOptional() baseCurrency?: string; diff --git a/apps/api/src/validators/is-currency-code.ts b/apps/api/src/validators/is-currency-code.ts new file mode 100644 index 000000000..8e8530552 --- /dev/null +++ b/apps/api/src/validators/is-currency-code.ts @@ -0,0 +1,44 @@ +import { DERIVED_CURRENCIES } from '@ghostfolio/common/config'; + +import { + registerDecorator, + ValidationOptions, + ValidatorConstraint, + ValidatorConstraintInterface, + ValidationArguments +} from 'class-validator'; +import { isISO4217CurrencyCode } from 'class-validator'; + +export function IsCurrencyCode(validationOptions?: ValidationOptions) { + return function (object: Object, propertyName: string) { + registerDecorator({ + propertyName, + constraints: [], + options: validationOptions, + target: object.constructor, + validator: IsExtendedCurrencyConstraint + }); + }; +} + +@ValidatorConstraint({ async: false }) +export class IsExtendedCurrencyConstraint + implements ValidatorConstraintInterface +{ + public defaultMessage(args: ValidationArguments) { + return '$value must be a valid ISO4217 currency code'; + } + + public validate(currency: any) { + // Return true if currency is a standard ISO 4217 code or a derived currency + return ( + isISO4217CurrencyCode(currency) || + [ + ...DERIVED_CURRENCIES.map((derivedCurrency) => { + return derivedCurrency.currency; + }), + 'USX' + ].includes(currency) + ); + } +} diff --git a/test/import/ok-derived-currency.json b/test/import/ok-derived-currency.json new file mode 100644 index 000000000..b43be395d --- /dev/null +++ b/test/import/ok-derived-currency.json @@ -0,0 +1,30 @@ +{ + "meta": { + "date": "2024-06-28T00:00:00.000Z", + "version": "dev" + }, + "accounts": [ + { + "balance": 2000, + "currency": "USD", + "id": "b2d3fe1d-d6a8-41a3-be39-07ef5e9480f0", + "isExcluded": false, + "name": "My Online Trading Account", + "platformId": null + } + ], + "activities": [ + { + "accountId": "b2d3fe1d-d6a8-41a3-be39-07ef5e9480f0", + "comment": null, + "fee": 0, + "quantity": 5, + "type": "BUY", + "unitPrice": 10875.00, + "currency": "ZAc", + "dataSource": "YAHOO", + "date": "2024-06-27T22:00:00.000Z", + "symbol": "JSE.JO" + } + ] +}