diff --git a/CHANGELOG.md b/CHANGELOG.md index f9fdd3eda..9913c0e29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Introduced a DTO for the query parameters of the asset profiles endpoint +- Introduced a DTO for the query parameters of the symbol lookup endpoints + ### Changed -- Hardened the validation of the query parameters (`accounts`, `assetClasses`, `dataSource` and `tags`) in the endpoints with filters +- Introduced a maximum length for the comment in the API endpoints +- Introduced a maximum length for the search query and the symbol in the API endpoints +- Hardened the validation of the query parameters (`accounts`, `assetClasses`, `dataSource` and `tags`) in the API endpoints with filters ### Fixed diff --git a/apps/api/src/app/account/get-all-accounts.dto.ts b/apps/api/src/app/account/get-all-accounts.dto.ts index 61a248019..efee3e683 100644 --- a/apps/api/src/app/account/get-all-accounts.dto.ts +++ b/apps/api/src/app/account/get-all-accounts.dto.ts @@ -1,9 +1,11 @@ import { FilterDto } from '@ghostfolio/api/dtos/filter.dto'; +import { SEARCH_QUERY_MAXIMUM_LENGTH } from '@ghostfolio/common/config'; -import { IsOptional, IsString } from 'class-validator'; +import { IsOptional, IsString, MaxLength } from 'class-validator'; export class GetAllAccountsDto extends FilterDto { @IsOptional() @IsString() + @MaxLength(SEARCH_QUERY_MAXIMUM_LENGTH) query?: string; } diff --git a/apps/api/src/app/endpoints/asset-profiles/asset-profiles.controller.ts b/apps/api/src/app/endpoints/asset-profiles/asset-profiles.controller.ts index e1452c548..1db09f95e 100644 --- a/apps/api/src/app/endpoints/asset-profiles/asset-profiles.controller.ts +++ b/apps/api/src/app/endpoints/asset-profiles/asset-profiles.controller.ts @@ -17,7 +17,7 @@ import { } from '@ghostfolio/common/interfaces'; import { hasPermission } from '@ghostfolio/common/permissions'; import { permissions } from '@ghostfolio/common/permissions'; -import { MarketDataPreset, RequestWithUser } from '@ghostfolio/common/types'; +import { RequestWithUser } from '@ghostfolio/common/types'; import { Body, @@ -27,7 +27,6 @@ import { HttpException, Inject, Param, - ParseIntPipe, Patch, Post, Query, @@ -36,11 +35,12 @@ import { } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; -import { AssetProfileSplit, DataSource, Prisma } from '@prisma/client'; +import { AssetProfileSplit, DataSource } from '@prisma/client'; import { parseISO } from 'date-fns'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { AssetProfilesService } from './asset-profiles.service'; +import { GetAssetProfilesDto } from './get-asset-profiles.dto'; @AllowDuringImpersonation() @Controller('asset-profiles') @@ -56,14 +56,17 @@ export class AssetProfilesController { @HasPermission(permissions.accessAdminControl) @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async getAssetProfiles( - @Query('assetSubClasses') filterByAssetSubClasses?: string, - @Query('dataSource') filterByDataSource?: string, - @Query('presetId') presetId?: MarketDataPreset, - @Query('query') filterBySearchQuery?: string, - @Query('skip', new ParseIntPipe({ optional: true })) skip?: number, - @Query('sortColumn') sortColumn?: string, - @Query('sortDirection') sortDirection?: Prisma.SortOrder, - @Query('take', new ParseIntPipe({ optional: true })) take?: number + @Query() + { + assetSubClasses: filterByAssetSubClasses, + dataSource: filterByDataSource, + presetId, + query: filterBySearchQuery, + skip, + sortColumn, + sortDirection, + take + }: GetAssetProfilesDto ): Promise { const filters = this.apiService.buildFiltersFromQueryParams({ filterByAssetSubClasses, diff --git a/apps/api/src/app/endpoints/asset-profiles/get-asset-profiles.dto.ts b/apps/api/src/app/endpoints/asset-profiles/get-asset-profiles.dto.ts new file mode 100644 index 000000000..562a75391 --- /dev/null +++ b/apps/api/src/app/endpoints/asset-profiles/get-asset-profiles.dto.ts @@ -0,0 +1,64 @@ +import { SEARCH_QUERY_MAXIMUM_LENGTH } from '@ghostfolio/common/config'; +import { MarketDataPreset } from '@ghostfolio/common/types'; + +import { Prisma } from '@prisma/client'; +import { Type } from 'class-transformer'; +import { + IsIn, + IsInt, + IsOptional, + IsString, + MaxLength, + Min +} from 'class-validator'; + +export class GetAssetProfilesDto { + @IsOptional() + @IsString() + assetSubClasses?: string; + + @IsOptional() + @IsString() + dataSource?: string; + + @IsIn([ + 'BENCHMARKS', + 'CURRENCIES', + 'ETF_WITHOUT_COUNTRIES', + 'ETF_WITHOUT_SECTORS', + 'NO_ACTIVITIES' + ] as MarketDataPreset[]) + @IsOptional() + presetId?: MarketDataPreset; + + @IsOptional() + @IsString() + @MaxLength(SEARCH_QUERY_MAXIMUM_LENGTH) + query?: string; + + @IsInt() + @IsOptional() + @Min(0) + @Type(() => Number) + skip?: number; + + @IsIn([ + 'activitiesCount', + 'assetClass', + 'assetSubClass', + 'dataSource', + 'symbol' + ]) + @IsOptional() + sortColumn?: string; + + @IsIn(['asc', 'desc'] as Prisma.SortOrder[]) + @IsOptional() + sortDirection?: Prisma.SortOrder; + + @IsInt() + @IsOptional() + @Min(0) + @Type(() => Number) + take?: number; +} diff --git a/apps/api/src/app/endpoints/data-providers/ghostfolio/get-lookup.dto.ts b/apps/api/src/app/endpoints/data-providers/ghostfolio/get-lookup.dto.ts new file mode 100644 index 000000000..0778d1b5b --- /dev/null +++ b/apps/api/src/app/endpoints/data-providers/ghostfolio/get-lookup.dto.ts @@ -0,0 +1,16 @@ +import { SEARCH_QUERY_MAXIMUM_LENGTH } from '@ghostfolio/common/config'; + +import { Transform, TransformFnParams } from 'class-transformer'; +import { IsBoolean, IsString, MaxLength } from 'class-validator'; + +export class GetLookupDto { + @IsBoolean() + @Transform(({ value }: TransformFnParams) => { + return value === 'true'; + }) + includeIndices? = false; + + @IsString() + @MaxLength(SEARCH_QUERY_MAXIMUM_LENGTH) + query? = ''; +} diff --git a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.controller.ts b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.controller.ts index 4bdb84ea7..cd330f691 100644 --- a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.controller.ts +++ b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.controller.ts @@ -32,6 +32,7 @@ import { getReasonPhrase, StatusCodes } from 'http-status-codes'; import { GetDividendsDto } from './get-dividends.dto'; import { GetHistoricalDto } from './get-historical.dto'; +import { GetLookupDto } from './get-lookup.dto'; import { GetQuotesDto } from './get-quotes.dto'; import { GhostfolioService } from './ghostfolio.service'; @@ -142,10 +143,8 @@ export class GhostfolioController { @UseGuards(AuthGuard('api-key'), HasPermissionGuard) @Version('2') public async lookupSymbol( - @Query('includeIndices') includeIndicesParam = 'false', - @Query('query') query = '' + @Query() { includeIndices, query }: GetLookupDto ): Promise { - const includeIndices = includeIndicesParam === 'true'; await this.validateDailyRequestLimit(); try { diff --git a/apps/api/src/app/portfolio/get-holdings.dto.ts b/apps/api/src/app/portfolio/get-holdings.dto.ts index b776bfe0d..6e3d9669e 100644 --- a/apps/api/src/app/portfolio/get-holdings.dto.ts +++ b/apps/api/src/app/portfolio/get-holdings.dto.ts @@ -1,7 +1,8 @@ import { DateRangeFilterDto } from '@ghostfolio/api/dtos/date-range-filter.dto'; +import { SEARCH_QUERY_MAXIMUM_LENGTH } from '@ghostfolio/common/config'; import { HoldingType } from '@ghostfolio/common/types'; -import { IsIn, IsOptional, IsString } from 'class-validator'; +import { IsIn, IsOptional, IsString, MaxLength } from 'class-validator'; export class GetHoldingsDto extends DateRangeFilterDto { @IsIn(['ACTIVE', 'CLOSED'] as HoldingType[]) @@ -10,5 +11,6 @@ export class GetHoldingsDto extends DateRangeFilterDto { @IsOptional() @IsString() + @MaxLength(SEARCH_QUERY_MAXIMUM_LENGTH) query?: string; } diff --git a/apps/api/src/app/symbol/get-lookup.dto.ts b/apps/api/src/app/symbol/get-lookup.dto.ts new file mode 100644 index 000000000..0778d1b5b --- /dev/null +++ b/apps/api/src/app/symbol/get-lookup.dto.ts @@ -0,0 +1,16 @@ +import { SEARCH_QUERY_MAXIMUM_LENGTH } from '@ghostfolio/common/config'; + +import { Transform, TransformFnParams } from 'class-transformer'; +import { IsBoolean, IsString, MaxLength } from 'class-validator'; + +export class GetLookupDto { + @IsBoolean() + @Transform(({ value }: TransformFnParams) => { + return value === 'true'; + }) + includeIndices? = false; + + @IsString() + @MaxLength(SEARCH_QUERY_MAXIMUM_LENGTH) + query? = ''; +} diff --git a/apps/api/src/app/symbol/symbol.controller.ts b/apps/api/src/app/symbol/symbol.controller.ts index d28362175..9d8abb47b 100644 --- a/apps/api/src/app/symbol/symbol.controller.ts +++ b/apps/api/src/app/symbol/symbol.controller.ts @@ -27,6 +27,7 @@ import { isValid, parseISO } from 'date-fns'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { isEmpty } from 'lodash'; +import { GetLookupDto } from './get-lookup.dto'; import { SymbolService } from './symbol.service'; @Controller('symbol') @@ -43,11 +44,8 @@ export class SymbolController { @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseInterceptors(TransformDataSourceInResponseInterceptor) public async lookupSymbol( - @Query('includeIndices') includeIndicesParam = 'false', - @Query('query') query = '' + @Query() { includeIndices, query }: GetLookupDto ): Promise { - const includeIndices = includeIndicesParam === 'true'; - try { return this.symbolService.lookup({ includeIndices, diff --git a/apps/api/src/dtos/filter.dto.ts b/apps/api/src/dtos/filter.dto.ts index 6c50ec062..09206984a 100644 --- a/apps/api/src/dtos/filter.dto.ts +++ b/apps/api/src/dtos/filter.dto.ts @@ -1,6 +1,14 @@ +import { SYMBOL_MAXIMUM_LENGTH } from '@ghostfolio/common/config'; + import { AssetClass, DataSource } from '@prisma/client'; import { Transform, TransformFnParams } from 'class-transformer'; -import { IsEnum, IsOptional, IsString, IsUUID } from 'class-validator'; +import { + IsEnum, + IsOptional, + IsString, + IsUUID, + MaxLength +} from 'class-validator'; import { isString } from 'lodash'; export class FilterDto { @@ -24,6 +32,7 @@ export class FilterDto { @IsOptional() @IsString() + @MaxLength(SYMBOL_MAXIMUM_LENGTH) symbol?: string; @IsOptional() 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 3b9f942a7..b2b2293e1 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 @@ -2,6 +2,7 @@ import { AdminMarketDataService } from '@ghostfolio/client/components/admin-mark import { UserService } from '@ghostfolio/client/services/user/user.service'; import { ASSET_CLASS_MAPPING, + COMMENT_MAXIMUM_LENGTH, PROPERTY_IS_DATA_GATHERING_ENABLED } from '@ghostfolio/common/config'; import { UpdateAssetProfileDto } from '@ghostfolio/common/dtos'; @@ -229,6 +230,7 @@ export class GfAssetProfileDialogComponent implements OnInit { protected readonly canDeleteAssetProfile = canDeleteAssetProfile; protected canEditAssetProfile = true; + protected readonly COMMENT_MAXIMUM_LENGTH = COMMENT_MAXIMUM_LENGTH; protected countries: { [code: string]: { name: string; value: number }; diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 7e4a0c2f1..6e1336de0 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -467,6 +467,7 @@ cdkTextareaAutosize formControlName="comment" matInput + [maxlength]="COMMENT_MAXIMUM_LENGTH" (keyup.enter)="$event.stopPropagation()" > diff --git a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts index 4339f570b..ab3854b93 100644 --- a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts +++ b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts @@ -1,6 +1,9 @@ import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; -import { TAG_ID_DRAFT } from '@ghostfolio/common/config'; +import { + COMMENT_MAXIMUM_LENGTH, + TAG_ID_DRAFT +} from '@ghostfolio/common/config'; import { CreateAccountDto, UpdateAccountDto } from '@ghostfolio/common/dtos'; import { getStringOrNull } from '@ghostfolio/common/helper'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; @@ -62,6 +65,8 @@ import { CreateOrUpdateAccountDialogParams } from './interfaces/interfaces'; templateUrl: 'create-or-update-account-dialog.html' }) export class GfCreateOrUpdateAccountDialogComponent { + protected readonly COMMENT_MAXIMUM_LENGTH = COMMENT_MAXIMUM_LENGTH; + protected accountForm: FormGroup; protected currencies: string[] = []; protected filteredPlatforms: Observable | undefined; diff --git a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html index 86a034b53..5119790c9 100644 --- a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html +++ b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -91,6 +91,7 @@ cdkTextareaAutosize formControlName="comment" matInput + [maxlength]="COMMENT_MAXIMUM_LENGTH" (keyup.enter)="$event.stopPropagation()" > diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts index a5758fb2c..853787f80 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts @@ -1,6 +1,10 @@ import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; -import { ASSET_CLASS_MAPPING, DEFAULT_LOCALE } from '@ghostfolio/common/config'; +import { + ASSET_CLASS_MAPPING, + COMMENT_MAXIMUM_LENGTH, + DEFAULT_LOCALE +} from '@ghostfolio/common/config'; import { CreateOrderDto, UpdateOrderDto } from '@ghostfolio/common/dtos'; import { getDateFormatString, @@ -79,6 +83,7 @@ import { ActivityType } from './types/activity-type.type'; templateUrl: 'create-or-update-activity-dialog.html' }) export class GfCreateOrUpdateActivityDialogComponent { + protected readonly COMMENT_MAXIMUM_LENGTH = COMMENT_MAXIMUM_LENGTH; protected readonly DEFAULT_LOCALE = DEFAULT_LOCALE; protected activityForm: FormGroup; diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html index e797db911..f9494efdc 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html @@ -255,6 +255,7 @@ cdkTextareaAutosize formControlName="comment" matInput + [maxlength]="COMMENT_MAXIMUM_LENGTH" (keyup.enter)="$event.stopPropagation()" > diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index 5e11f8ae1..77e854239 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -60,6 +60,8 @@ export const BULL_BOARD_ROUTE = '/admin/queues'; export const CACHE_TTL_NO_CACHE = 1; export const CACHE_TTL_INFINITE = 0; +export const COMMENT_MAXIMUM_LENGTH = 2000; + export const DATA_GATHERING_QUEUE = 'DATA_GATHERING_QUEUE'; export const DATA_GATHERING_QUEUE_PRIORITY_HIGH = 1; export const DATA_GATHERING_QUEUE_PRIORITY_LOW = Number.MAX_SAFE_INTEGER; @@ -342,6 +344,7 @@ export const REPLACE_NAME_PARTS = [ 'Xtrackers (IE) Plc -' ]; +export const SEARCH_QUERY_MAXIMUM_LENGTH = 255; export const SEARCH_QUERY_MINIMUM_LENGTH = 2; export const SECTORS = [ @@ -378,6 +381,8 @@ export const SUPPORTED_LANGUAGE_CODES = [ 'zh' ] as const; +export const SYMBOL_MAXIMUM_LENGTH = 255; + export const TAG_ID_DEMO = 'efa08cb3-9b9d-4974-ac68-db13a19c4874'; export const TAG_ID_DRAFT = '0c077abd-eca2-4cbb-818c-6cefbf2d169a'; export const TAG_ID_EMERGENCY_FUND = '4452656d-9fa4-4bd0-ba38-70492e31d180'; diff --git a/libs/common/src/lib/dtos/create-account.dto.ts b/libs/common/src/lib/dtos/create-account.dto.ts index ccadff5f9..92879dbb2 100644 --- a/libs/common/src/lib/dtos/create-account.dto.ts +++ b/libs/common/src/lib/dtos/create-account.dto.ts @@ -1,3 +1,4 @@ +import { COMMENT_MAXIMUM_LENGTH } from '@ghostfolio/common/config'; import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { Transform, TransformFnParams } from 'class-transformer'; @@ -7,6 +8,7 @@ import { IsNumber, IsOptional, IsString, + MaxLength, ValidateIf } from 'class-validator'; import { isString } from 'lodash'; @@ -22,6 +24,7 @@ export class CreateAccountDto { @IsOptional() @IsString() + @MaxLength(COMMENT_MAXIMUM_LENGTH) @Transform(({ value }: TransformFnParams) => isString(value) ? value.trim() : value ) diff --git a/libs/common/src/lib/dtos/create-asset-profile.dto.ts b/libs/common/src/lib/dtos/create-asset-profile.dto.ts index 17a9d12b9..4197137fb 100644 --- a/libs/common/src/lib/dtos/create-asset-profile.dto.ts +++ b/libs/common/src/lib/dtos/create-asset-profile.dto.ts @@ -1,3 +1,7 @@ +import { + COMMENT_MAXIMUM_LENGTH, + SYMBOL_MAXIMUM_LENGTH +} from '@ghostfolio/common/config'; import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { AssetClass, AssetSubClass, DataSource, Prisma } from '@prisma/client'; @@ -9,6 +13,7 @@ import { IsOptional, IsString, IsUrl, + MaxLength, ValidateNested } from 'class-validator'; @@ -27,6 +32,7 @@ export class CreateAssetProfileDto { @IsOptional() @IsString() + @MaxLength(COMMENT_MAXIMUM_LENGTH) comment?: string; @IsArray() @@ -82,6 +88,7 @@ export class CreateAssetProfileDto { sectors?: Prisma.InputJsonArray; @IsString() + @MaxLength(SYMBOL_MAXIMUM_LENGTH) symbol: string; @IsOptional() diff --git a/libs/common/src/lib/dtos/create-order.dto.ts b/libs/common/src/lib/dtos/create-order.dto.ts index c70b66b02..1345f418a 100644 --- a/libs/common/src/lib/dtos/create-order.dto.ts +++ b/libs/common/src/lib/dtos/create-order.dto.ts @@ -1,3 +1,7 @@ +import { + COMMENT_MAXIMUM_LENGTH, + SYMBOL_MAXIMUM_LENGTH +} from '@ghostfolio/common/config'; import { IsAfter1970Constraint } from '@ghostfolio/common/validator-constraints/is-after-1970'; import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; @@ -12,6 +16,7 @@ import { IsNumber, IsOptional, IsString, + MaxLength, Min, Validate } from 'class-validator'; @@ -32,6 +37,7 @@ export class CreateOrderDto { @IsOptional() @IsString() + @MaxLength(COMMENT_MAXIMUM_LENGTH) @Transform(({ value }: TransformFnParams) => isString(value) ? value.trim() : value ) @@ -61,6 +67,7 @@ export class CreateOrderDto { quantity: number; @IsString() + @MaxLength(SYMBOL_MAXIMUM_LENGTH) symbol: string; @ArrayUnique() diff --git a/libs/common/src/lib/dtos/create-watchlist-item.dto.ts b/libs/common/src/lib/dtos/create-watchlist-item.dto.ts index 663965ef1..78808a024 100644 --- a/libs/common/src/lib/dtos/create-watchlist-item.dto.ts +++ b/libs/common/src/lib/dtos/create-watchlist-item.dto.ts @@ -1,10 +1,13 @@ +import { SYMBOL_MAXIMUM_LENGTH } from '@ghostfolio/common/config'; + import { DataSource } from '@prisma/client'; -import { IsEnum, IsString } from 'class-validator'; +import { IsEnum, IsString, MaxLength } from 'class-validator'; export class CreateWatchlistItemDto { @IsEnum(DataSource) dataSource: DataSource; @IsString() + @MaxLength(SYMBOL_MAXIMUM_LENGTH) symbol: string; } diff --git a/libs/common/src/lib/dtos/update-account.dto.ts b/libs/common/src/lib/dtos/update-account.dto.ts index 4e1570aad..deec684aa 100644 --- a/libs/common/src/lib/dtos/update-account.dto.ts +++ b/libs/common/src/lib/dtos/update-account.dto.ts @@ -1,3 +1,4 @@ +import { COMMENT_MAXIMUM_LENGTH } from '@ghostfolio/common/config'; import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { Transform, TransformFnParams } from 'class-transformer'; @@ -7,6 +8,7 @@ import { IsNumber, IsOptional, IsString, + MaxLength, ValidateIf } from 'class-validator'; import { isString } from 'lodash'; @@ -22,6 +24,7 @@ export class UpdateAccountDto { @IsOptional() @IsString() + @MaxLength(COMMENT_MAXIMUM_LENGTH) @Transform(({ value }: TransformFnParams) => isString(value) ? value.trim() : value ) 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 a8eaad608..965778761 100644 --- a/libs/common/src/lib/dtos/update-asset-profile.dto.ts +++ b/libs/common/src/lib/dtos/update-asset-profile.dto.ts @@ -1,3 +1,7 @@ +import { + COMMENT_MAXIMUM_LENGTH, + SYMBOL_MAXIMUM_LENGTH +} from '@ghostfolio/common/config'; import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { @@ -16,6 +20,7 @@ import { IsOptional, IsString, IsUrl, + MaxLength, ValidateNested } from 'class-validator'; @@ -35,6 +40,7 @@ export class UpdateAssetProfileDto { @IsOptional() @IsString() + @MaxLength(COMMENT_MAXIMUM_LENGTH) comment?: string | null; @IsArray() @@ -83,6 +89,7 @@ export class UpdateAssetProfileDto { @IsOptional() @IsString() + @MaxLength(SYMBOL_MAXIMUM_LENGTH) symbol?: string; @IsObject() diff --git a/libs/common/src/lib/dtos/update-order.dto.ts b/libs/common/src/lib/dtos/update-order.dto.ts index d8fdbe66b..69d993559 100644 --- a/libs/common/src/lib/dtos/update-order.dto.ts +++ b/libs/common/src/lib/dtos/update-order.dto.ts @@ -1,3 +1,7 @@ +import { + COMMENT_MAXIMUM_LENGTH, + SYMBOL_MAXIMUM_LENGTH +} from '@ghostfolio/common/config'; import { IsAfter1970Constraint } from '@ghostfolio/common/validator-constraints/is-after-1970'; import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; @@ -11,6 +15,7 @@ import { IsNumber, IsOptional, IsString, + MaxLength, Min, Validate } from 'class-validator'; @@ -31,6 +36,7 @@ export class UpdateOrderDto { @IsOptional() @IsString() + @MaxLength(COMMENT_MAXIMUM_LENGTH) @Transform(({ value }: TransformFnParams) => isString(value) ? value.trim() : value ) @@ -62,6 +68,7 @@ export class UpdateOrderDto { quantity: number; @IsString() + @MaxLength(SYMBOL_MAXIMUM_LENGTH) symbol: string; @ArrayUnique() diff --git a/libs/ui/src/lib/assistant/assistant.component.ts b/libs/ui/src/lib/assistant/assistant.component.ts index f612a2cad..bdbaa6cdb 100644 --- a/libs/ui/src/lib/assistant/assistant.component.ts +++ b/libs/ui/src/lib/assistant/assistant.component.ts @@ -1,3 +1,4 @@ +import { SEARCH_QUERY_MAXIMUM_LENGTH } from '@ghostfolio/common/config'; import { Filter, PortfolioPosition, User } from '@ghostfolio/common/interfaces'; import { InternalRoute } from '@ghostfolio/common/routes/interfaces/internal-route.interface'; import { internalRoutes } from '@ghostfolio/common/routes/routes'; @@ -130,6 +131,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { ); public searchFormControl = new FormControl(''); + public readonly SEARCH_QUERY_MAXIMUM_LENGTH = SEARCH_QUERY_MAXIMUM_LENGTH; public searchResults: SearchResults = { accounts: [], diff --git a/libs/ui/src/lib/assistant/assistant.html b/libs/ui/src/lib/assistant/assistant.html index 79e2f31a1..9c5fd5601 100644 --- a/libs/ui/src/lib/assistant/assistant.html +++ b/libs/ui/src/lib/assistant/assistant.html @@ -13,6 +13,7 @@ name="search" type="text" [formControl]="searchFormControl" + [maxlength]="SEARCH_QUERY_MAXIMUM_LENGTH" [placeholder]="placeholder" /> @if (deviceType !== 'mobile' && !searchFormControl.value) { diff --git a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.html b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.html index d786d724d..5e23c558a 100644 --- a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.html +++ b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.html @@ -4,6 +4,7 @@ matInput [formControl]="control" [matAutocomplete]="symbolAutocomplete" + [maxlength]="SEARCH_QUERY_MAXIMUM_LENGTH" /> ('symbolAutocomplete');