Browse Source

fix: allow empty URL when saving asset profile (#6811)

`@IsOptional()` from class-validator only skips validation for `undefined`
or `null` - not for empty string `""`. The asset profile dialog submits
`url: ""` when the optional URL field is left blank, so `@IsUrl()` runs
and the form fails with "url must be a URL address" despite URL being
documented as optional.

Fix: add a `@Transform` that coerces empty/whitespace-only strings to
`undefined`, letting `@IsOptional()` short-circuit the validation chain.
Same pattern as the `comment` field in `update-account.dto.ts`.

Applied to both `UpdateAssetProfileDto` (the form path in the bug repro)
and `CreateAssetProfileDto` (same flaw, same flow). Platform DTOs are
left alone since their `url` field is required (not optional).

Closes #6811.
pull/6822/head
Booyaka101 3 weeks ago
parent
commit
6761d09ffd
  1. 4
      CHANGELOG.md
  2. 5
      libs/common/src/lib/dtos/create-asset-profile.dto.ts
  3. 5
      libs/common/src/lib/dtos/update-asset-profile.dto.ts

4
CHANGELOG.md

@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Upgraded `stripe` from version `20.4.1` to `21.0.1` - Upgraded `stripe` from version `20.4.1` to `21.0.1`
### Fixed
- Fixed the asset profile validation to allow saving with an empty URL field
## 3.1.0 - 2026-04-29 ## 3.1.0 - 2026-04-29
### Added ### Added

5
libs/common/src/lib/dtos/create-asset-profile.dto.ts

@ -1,6 +1,7 @@
import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code';
import { AssetClass, AssetSubClass, DataSource, Prisma } from '@prisma/client'; import { AssetClass, AssetSubClass, DataSource, Prisma } from '@prisma/client';
import { Transform, TransformFnParams } from 'class-transformer';
import { import {
IsArray, IsArray,
IsBoolean, IsBoolean,
@ -9,6 +10,7 @@ import {
IsString, IsString,
IsUrl IsUrl
} from 'class-validator'; } from 'class-validator';
import { isString } from 'lodash';
export class CreateAssetProfileDto { export class CreateAssetProfileDto {
@IsEnum(AssetClass, { each: true }) @IsEnum(AssetClass, { each: true })
@ -77,5 +79,8 @@ export class CreateAssetProfileDto {
protocols: ['https'], protocols: ['https'],
require_protocol: true require_protocol: true
}) })
@Transform(({ value }: TransformFnParams) =>
isString(value) && value.trim() === '' ? undefined : value
)
url?: string; url?: string;
} }

5
libs/common/src/lib/dtos/update-asset-profile.dto.ts

@ -1,6 +1,7 @@
import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code';
import { AssetClass, AssetSubClass, DataSource, Prisma } from '@prisma/client'; import { AssetClass, AssetSubClass, DataSource, Prisma } from '@prisma/client';
import { Transform, TransformFnParams } from 'class-transformer';
import { import {
IsArray, IsArray,
IsBoolean, IsBoolean,
@ -10,6 +11,7 @@ import {
IsString, IsString,
IsUrl IsUrl
} from 'class-validator'; } from 'class-validator';
import { isString } from 'lodash';
export class UpdateAssetProfileDto { export class UpdateAssetProfileDto {
@IsEnum(AssetClass, { each: true }) @IsEnum(AssetClass, { each: true })
@ -67,5 +69,8 @@ export class UpdateAssetProfileDto {
protocols: ['https'], protocols: ['https'],
require_protocol: true require_protocol: true
}) })
@Transform(({ value }: TransformFnParams) =>
isString(value) && value.trim() === '' ? undefined : value
)
url?: string; url?: string;
} }

Loading…
Cancel
Save