diff --git a/CHANGELOG.md b/CHANGELOG.md index 21649ab604..1118f2a692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed the handling of the _Exclude from Analysis_ tag in the activities table +- Resolved a validation error caused by empty strings in the asset profile details dialog of the admin control panel ## 3.39.0 - 2026-08-01 diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts index c5562740fd..330688da18 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts @@ -11,6 +11,8 @@ import { DATE_FORMAT, getCountryName, getCurrencyFromSymbol, + getStringOrNull, + getStringOrUndefined, isCurrency } from '@ghostfolio/common/helper'; import { @@ -568,9 +570,10 @@ export class GfAssetProfileDialogComponent implements OnInit { this.assetProfileForm.controls.scraperConfiguration.controls.headers .value ?? '{}' ) as Record, - locale: + locale: getStringOrUndefined( this.assetProfileForm.controls.scraperConfiguration.controls.locale - ?.value ?? undefined, + ?.value + ), mode: this.assetProfileForm.controls.scraperConfiguration.controls.mode ?.value ?? undefined, @@ -619,7 +622,7 @@ export class GfAssetProfileDialogComponent implements OnInit { assetClass: this.assetProfileForm.controls.assetClass.value ?? undefined, assetSubClass: this.assetProfileForm.controls.assetSubClass.value ?? undefined, - comment: this.assetProfileForm.controls.comment.value ?? undefined, + comment: getStringOrNull(this.assetProfileForm.controls.comment.value), currency: this.assetProfileForm.controls.currency.value ?? undefined, dataGatheringFrequency: this.assetProfileForm.controls.dataGatheringFrequency.value ?? @@ -628,7 +631,7 @@ export class GfAssetProfileDialogComponent implements OnInit { ? this.assetProfileForm.controls.isActive.value : undefined, name: this.assetProfileForm.controls.name.value ?? undefined, - url: this.assetProfileForm.controls.url.value ?? undefined + url: getStringOrNull(this.assetProfileForm.controls.url.value) }; try { @@ -737,9 +740,10 @@ export class GfAssetProfileDialogComponent implements OnInit { this.assetProfileForm.controls.scraperConfiguration.controls.headers .value ?? '{}' ) as Record, - locale: + locale: getStringOrUndefined( this.assetProfileForm.controls.scraperConfiguration.controls.locale - ?.value ?? undefined, + ?.value + ), mode: this.assetProfileForm.controls.scraperConfiguration.controls .mode?.value, selector: diff --git a/libs/common/src/lib/dtos/update-asset-profile.dto.ts b/libs/common/src/lib/dtos/update-asset-profile.dto.ts index a6e2230e8e..a8eaad608a 100644 --- a/libs/common/src/lib/dtos/update-asset-profile.dto.ts +++ b/libs/common/src/lib/dtos/update-asset-profile.dto.ts @@ -35,7 +35,7 @@ export class UpdateAssetProfileDto { @IsOptional() @IsString() - comment?: string; + comment?: string | null; @IsArray() @IsOptional() @@ -96,5 +96,5 @@ export class UpdateAssetProfileDto { protocols: ['http', 'https'], require_protocol: true }) - url?: string; + url?: string | null; } diff --git a/libs/common/src/lib/helper.spec.ts b/libs/common/src/lib/helper.spec.ts index fd39b0652d..0da1613b50 100644 --- a/libs/common/src/lib/helper.spec.ts +++ b/libs/common/src/lib/helper.spec.ts @@ -5,6 +5,8 @@ import { import { extractNumberFromString, getNumberFormatGroup, + getStringOrNull, + getStringOrUndefined, isAccountExcluded, isCurrency, isCurrencySymbol @@ -144,7 +146,63 @@ describe('Helper', () => { }); }); - describe('Is account excluded', () => { + describe('Get string or null', () => { + it('String', () => { + expect(getStringOrNull('https://ghostfol.io')).toEqual( + 'https://ghostfol.io' + ); + }); + + it('String (with spaces)', () => { + expect(getStringOrNull(' https://ghostfol.io ')).toEqual( + 'https://ghostfol.io' + ); + }); + + it('Empty string', () => { + expect(getStringOrNull('')).toEqual(null); + }); + + it('Blank string', () => { + expect(getStringOrNull(' ')).toEqual(null); + }); + + it('Null', () => { + expect(getStringOrNull(null)).toEqual(null); + }); + + it('Undefined', () => { + expect(getStringOrNull(undefined)).toEqual(null); + }); + }); + + describe('Get string or undefined', () => { + it('String', () => { + expect(getStringOrUndefined('de-DE')).toEqual('de-DE'); + }); + + it('String (with spaces)', () => { + expect(getStringOrUndefined(' de-DE ')).toEqual('de-DE'); + }); + + it('Empty string', () => { + expect(getStringOrUndefined('')).toEqual(undefined); + }); + + it('Blank string', () => { + expect(getStringOrUndefined(' ')).toEqual(undefined); + }); + + it('Null', () => { + expect(getStringOrUndefined(null)).toEqual(undefined); + }); + + it('Undefined', () => { + expect(getStringOrUndefined(undefined)).toEqual(undefined); + }); + }); + + describe('Is account excluded', () => { it('Account with Exclude from Analysis tag', () => { expect( isAccountExcluded({ tags: [{ id: TAG_ID_EXCLUDE_FROM_ANALYSIS }] }) diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index 00a4e8db7e..58927337ee 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -397,6 +397,26 @@ export function getStartOfUtcDate(aDate: Date) { return date; } +export function getStringOrNull(aString: string | null | undefined) { + const trimmedString = aString?.trim(); + + if (trimmedString) { + return trimmedString; + } + + return null; +} + +export function getStringOrUndefined(aString: string | null | undefined) { + const trimmedString = aString?.trim(); + + if (trimmedString) { + return trimmedString; + } + + return undefined; +} + export function getSum(aArray: Big[]) { if (aArray?.length > 0) { return aArray.reduce((a, b) => a.plus(b), new Big(0));