diff --git a/src/common/default-value.ts b/src/common/default-value.ts new file mode 100644 index 0000000000..54f733a292 --- /dev/null +++ b/src/common/default-value.ts @@ -0,0 +1,17 @@ +const get = (type: any) => Reflect.getMetadata(key, type) ?? {}; + +const set = + (value: any): PropertyDecorator => + ({ constructor: type }, propertyKey) => + Reflect.defineMetadata(key, { ...get(type), [propertyKey]: value }, type); + +/** + * A helper to get/set default values for classes. + * Usage of this is discouraged in favor or more common practices. + * This is mostly useful with abstractions. + * Note that the defaults aren't automatically applied, this just holds + * a container for them - They need to be fetched explicitly. + */ +export const DefaultValue = { Get: get, Set: set }; + +const key = 'DefaultValue'; diff --git a/src/common/filter-field.ts b/src/common/filter-field.ts new file mode 100644 index 0000000000..c8b6154417 --- /dev/null +++ b/src/common/filter-field.ts @@ -0,0 +1,34 @@ +import { applyDecorators } from '@nestjs/common'; +import { Field } from '@nestjs/graphql'; +import { Transform, Type } from 'class-transformer'; +import { ValidateNested } from 'class-validator'; +import { HasRequiredKeys } from 'type-fest'; +import { DefaultValue } from './default-value'; +import { AbstractClassType } from './types'; + +/** + * A field that is a filter object probably for input on a list query. + */ +export const FilterField = ( + type: HasRequiredKeys extends true ? never : AbstractClassType, + options?: { + /** + * There are no external fields on the filter, so don't expose to GQL. + */ + internal?: boolean; + } +): PropertyDecorator => + applyDecorators( + ...(options?.internal + ? [] + : [ + Field(() => type, { + nullable: true, + defaultValue: {}, // Only for GQL schema & not always applied in TS + }), + ]), + Type(() => type), + ValidateNested(), + DefaultValue.Set({}), // Default when omitted + Transform(({ value }) => value || {}) // null -> {} + ); diff --git a/src/common/index.ts b/src/common/index.ts index 7436443131..7f4c859eca 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -14,6 +14,7 @@ export * from './mutation-placeholder.output'; export * from './exceptions'; export * from './expose-enum-order.helper'; export * from './fields.pipe'; +export * from './filter-field'; export * from './firstLettersOfWords'; export * from './fiscal-year'; export * from './generate-id'; diff --git a/src/common/pagination.input.ts b/src/common/pagination.input.ts index 5e36daeb29..7581578a21 100644 --- a/src/common/pagination.input.ts +++ b/src/common/pagination.input.ts @@ -3,6 +3,7 @@ import { Args, ArgsOptions, Field, InputType, Int } from '@nestjs/graphql'; import { Matches, Max, Min } from 'class-validator'; import { stripIndent } from 'common-tags'; import { DataObject } from './data-object'; +import { DefaultValue } from './default-value'; import { Order } from './order.enum'; import { AbstractClassType } from './types'; @@ -106,7 +107,7 @@ export const ListArg = ( name: 'input', type: () => input, nullable: true, - defaultValue: DataObject.defaultValue(input), + defaultValue: DataObject.defaultValue(input, DefaultValue.Get(input)), ...opts, }, ...pipes diff --git a/src/components/budget/dto/list-budget.dto.ts b/src/components/budget/dto/list-budget.dto.ts index 67c0e38eda..f632ca0db1 100644 --- a/src/components/budget/dto/list-budget.dto.ts +++ b/src/components/budget/dto/list-budget.dto.ts @@ -2,11 +2,12 @@ import { InputType, ObjectType } from '@nestjs/graphql'; import { Type } from 'class-transformer'; import { ValidateNested } from 'class-validator'; import { + FilterField, ID, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { BudgetRecord } from './budget-record.dto'; import { Budget } from './budget.dto'; @@ -15,15 +16,12 @@ export abstract class BudgetFilters { readonly projectId?: ID; } -const defaultFilters = {}; - @InputType() export class BudgetListInput extends SortablePaginationInput({ defaultSort: 'status', }) { - @Type(() => BudgetFilters) - @ValidateNested() - readonly filter: BudgetFilters = defaultFilters; + @FilterField(BudgetFilters, { internal: true }) + readonly filter: BudgetFilters; } @ObjectType() diff --git a/src/components/ceremony/dto/list-ceremony.dto.ts b/src/components/ceremony/dto/list-ceremony.dto.ts index b5fc8c08b4..a1c25cefc0 100644 --- a/src/components/ceremony/dto/list-ceremony.dto.ts +++ b/src/components/ceremony/dto/list-ceremony.dto.ts @@ -1,7 +1,5 @@ import { Field, InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; -import { PaginatedList, SortablePaginationInput } from '../../../common'; +import { FilterField, PaginatedList, SortablePaginationInput } from '~/common'; import { Ceremony } from './ceremony.dto'; import { CeremonyType } from './type.enum'; @@ -14,18 +12,14 @@ export abstract class CeremonyFilters { readonly type?: CeremonyType; } -const defaultFilters = {}; - @InputType() export class CeremonyListInput extends SortablePaginationInput< keyof Ceremony | 'projectName' | 'languageName' >({ defaultSort: 'projectName', }) { - @Field({ nullable: true }) - @Type(() => CeremonyFilters) - @ValidateNested() - readonly filter: CeremonyFilters = defaultFilters; + @FilterField(CeremonyFilters) + readonly filter: CeremonyFilters; } @ObjectType() diff --git a/src/components/engagement/dto/list-engagements.dto.ts b/src/components/engagement/dto/list-engagements.dto.ts index 9d0e36e95f..3c90a68e5e 100644 --- a/src/components/engagement/dto/list-engagements.dto.ts +++ b/src/components/engagement/dto/list-engagements.dto.ts @@ -1,12 +1,11 @@ import { Field, InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, ID, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { Engagement, IEngagement, @@ -25,18 +24,14 @@ export abstract class EngagementFilters { readonly projectId?: ID; } -const defaultFilters = {}; - @InputType() export class EngagementListInput extends SortablePaginationInput< keyof Engagement >({ defaultSort: 'createdAt', }) { - @Field({ nullable: true }) - @Type(() => EngagementFilters) - @ValidateNested() - readonly filter: EngagementFilters = defaultFilters; + @FilterField(EngagementFilters) + readonly filter: EngagementFilters; } @ObjectType() diff --git a/src/components/ethno-art/dto/list-ethno-art.dto.ts b/src/components/ethno-art/dto/list-ethno-art.dto.ts index 31d9a21637..82f2f237f2 100644 --- a/src/components/ethno-art/dto/list-ethno-art.dto.ts +++ b/src/components/ethno-art/dto/list-ethno-art.dto.ts @@ -1,21 +1,16 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; -import { PaginatedList, SortablePaginationInput } from '../../../common'; +import { FilterField, PaginatedList, SortablePaginationInput } from '~/common'; import { EthnoArt } from './ethno-art.dto'; @InputType() export abstract class EthnoArtFilters {} -const defaultFilters = {}; - @InputType() export class EthnoArtListInput extends SortablePaginationInput({ defaultSort: 'name', }) { - @Type(() => EthnoArtFilters) - @ValidateNested() - readonly filter: EthnoArtFilters = defaultFilters; + @FilterField(EthnoArtFilters, { internal: true }) + readonly filter: EthnoArtFilters; } @ObjectType() diff --git a/src/components/field-region/dto/list-field-region.dto.ts b/src/components/field-region/dto/list-field-region.dto.ts index 2c073e4c7b..3471556dd4 100644 --- a/src/components/field-region/dto/list-field-region.dto.ts +++ b/src/components/field-region/dto/list-field-region.dto.ts @@ -1,12 +1,11 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, ID, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { FieldRegion } from './field-region.dto'; @InputType() @@ -14,17 +13,14 @@ export abstract class FieldRegionFilters { readonly fieldZoneId?: ID; } -const defaultFilters = {}; - @InputType() export class FieldRegionListInput extends SortablePaginationInput< keyof FieldRegion >({ defaultSort: 'name', }) { - @Type(() => FieldRegionFilters) - @ValidateNested() - readonly filter: FieldRegionFilters = defaultFilters; + @FilterField(FieldRegionFilters, { internal: true }) + readonly filter: FieldRegionFilters; } @ObjectType() diff --git a/src/components/field-zone/dto/list-field-zone.dto.ts b/src/components/field-zone/dto/list-field-zone.dto.ts index a049d6328b..360e11ea5f 100644 --- a/src/components/field-zone/dto/list-field-zone.dto.ts +++ b/src/components/field-zone/dto/list-field-zone.dto.ts @@ -1,12 +1,11 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, ID, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { FieldZone } from './field-zone.dto'; @InputType() @@ -14,17 +13,14 @@ export abstract class FieldZoneFilters { readonly fieldZoneId?: ID; } -const defaultFilters = {}; - @InputType() export class FieldZoneListInput extends SortablePaginationInput< keyof FieldZone >({ defaultSort: 'name', }) { - @Type(() => FieldZoneFilters) - @ValidateNested() - readonly filter: FieldZoneFilters = defaultFilters; + @FilterField(FieldZoneFilters, { internal: true }) + readonly filter: FieldZoneFilters; } @ObjectType() diff --git a/src/components/file/dto/list.ts b/src/components/file/dto/list.ts index bf33d5f540..5535dcdb75 100644 --- a/src/components/file/dto/list.ts +++ b/src/components/file/dto/list.ts @@ -1,7 +1,5 @@ import { Field, InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; -import { PaginatedList, SortablePaginationInput } from '../../../common'; +import { FilterField, PaginatedList, SortablePaginationInput } from '~/common'; import { Directory, File, FileNode, IFileNode } from './node'; import { FileNodeType } from './type'; @@ -20,18 +18,14 @@ export abstract class FileFilters { readonly type?: FileNodeType; } -const defaultFilters = {}; - @InputType() export class FileListInput extends SortablePaginationInput< keyof File | keyof Directory >({ defaultSort: 'name', }) { - @Field({ nullable: true }) - @Type(() => FileFilters) - @ValidateNested() - readonly filter?: FileFilters = defaultFilters; + @FilterField(FileFilters) + readonly filter?: FileFilters; } @ObjectType() diff --git a/src/components/film/dto/list-film.dto.ts b/src/components/film/dto/list-film.dto.ts index 63c981f791..fb423af7e9 100644 --- a/src/components/film/dto/list-film.dto.ts +++ b/src/components/film/dto/list-film.dto.ts @@ -1,21 +1,16 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; -import { PaginatedList, SortablePaginationInput } from '../../../common'; +import { FilterField, PaginatedList, SortablePaginationInput } from '~/common'; import { Film } from './film.dto'; @InputType() export abstract class FilmFilters {} -const defaultFilters = {}; - @InputType() export class FilmListInput extends SortablePaginationInput({ defaultSort: 'name', }) { - @Type(() => FilmFilters) - @ValidateNested() - readonly filter: FilmFilters = defaultFilters; + @FilterField(FilmFilters, { internal: true }) + readonly filter: FilmFilters; } @ObjectType() diff --git a/src/components/language/dto/list-language.dto.ts b/src/components/language/dto/list-language.dto.ts index 3e139150da..e6ab408f1e 100644 --- a/src/components/language/dto/list-language.dto.ts +++ b/src/components/language/dto/list-language.dto.ts @@ -1,13 +1,12 @@ import { Field, InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, PaginatedList, SecuredList, SensitivitiesFilter, Sensitivity, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { Language } from './language.dto'; @InputType() @@ -49,16 +48,12 @@ export abstract class LanguageFilters { readonly pinned?: boolean; } -const defaultFilters = {}; - @InputType() export class LanguageListInput extends SortablePaginationInput({ defaultSort: 'name', }) { - @Field({ nullable: true }) - @Type(() => LanguageFilters) - @ValidateNested() - readonly filter: LanguageFilters = defaultFilters; + @FilterField(LanguageFilters) + readonly filter: LanguageFilters; } @ObjectType() diff --git a/src/components/literacy-material/dto/list-literacy-material.dto.ts b/src/components/literacy-material/dto/list-literacy-material.dto.ts index 78516b5dcf..2a4242aea1 100644 --- a/src/components/literacy-material/dto/list-literacy-material.dto.ts +++ b/src/components/literacy-material/dto/list-literacy-material.dto.ts @@ -1,23 +1,18 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; -import { PaginatedList, SortablePaginationInput } from '../../../common'; +import { FilterField, PaginatedList, SortablePaginationInput } from '~/common'; import { LiteracyMaterial } from './literacy-material.dto'; @InputType() export abstract class LiteracyMaterialFilters {} -const defaultFilters = {}; - @InputType() export class LiteracyMaterialListInput extends SortablePaginationInput< keyof LiteracyMaterial >({ defaultSort: 'name', }) { - @Type(() => LiteracyMaterialFilters) - @ValidateNested() - readonly filter: LiteracyMaterialFilters = defaultFilters; + @FilterField(LiteracyMaterialFilters, { internal: true }) + readonly filter: LiteracyMaterialFilters; } @ObjectType() diff --git a/src/components/location/dto/list-locations.dto.ts b/src/components/location/dto/list-locations.dto.ts index d2867e276a..96ef6516b0 100644 --- a/src/components/location/dto/list-locations.dto.ts +++ b/src/components/location/dto/list-locations.dto.ts @@ -1,12 +1,11 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, ID, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { Location } from './location.dto'; @InputType() @@ -14,15 +13,12 @@ export abstract class LocationFilters { readonly fundingAccountId?: ID; } -const defaultFilters = {}; - @InputType() export class LocationListInput extends SortablePaginationInput({ defaultSort: 'name', }) { - @Type(() => LocationFilters) - @ValidateNested() - readonly filter: LocationFilters = defaultFilters; + @FilterField(LocationFilters, { internal: true }) + readonly filter: LocationFilters; } @ObjectType() diff --git a/src/components/organization/dto/list-organization.dto.ts b/src/components/organization/dto/list-organization.dto.ts index 95feea1743..62c0789415 100644 --- a/src/components/organization/dto/list-organization.dto.ts +++ b/src/components/organization/dto/list-organization.dto.ts @@ -1,12 +1,11 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, ID, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { Organization } from './organization.dto'; @InputType() @@ -14,17 +13,14 @@ export abstract class OrganizationFilters { readonly userId?: ID; } -const defaultFilters = {}; - @InputType() export class OrganizationListInput extends SortablePaginationInput< keyof Organization >({ defaultSort: 'name', }) { - @Type(() => OrganizationFilters) - @ValidateNested() - readonly filter: OrganizationFilters = defaultFilters; + @FilterField(OrganizationFilters, { internal: true }) + readonly filter: OrganizationFilters; } @ObjectType() diff --git a/src/components/partner/dto/list-partner.dto.ts b/src/components/partner/dto/list-partner.dto.ts index 6c874217d7..d1a82763c5 100644 --- a/src/components/partner/dto/list-partner.dto.ts +++ b/src/components/partner/dto/list-partner.dto.ts @@ -1,12 +1,11 @@ import { Field, InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, ID, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { Partner } from './partner.dto'; @InputType() @@ -21,16 +20,12 @@ export abstract class PartnerFilters { readonly pinned?: boolean; } -const defaultFilters = {}; - @InputType() export class PartnerListInput extends SortablePaginationInput({ defaultSort: 'createdAt', }) { - @Field({ nullable: true }) - @Type(() => PartnerFilters) - @ValidateNested() - readonly filter: PartnerFilters = defaultFilters; + @FilterField(PartnerFilters) + readonly filter: PartnerFilters; } @ObjectType() diff --git a/src/components/partnership/dto/list-partnership.dto.ts b/src/components/partnership/dto/list-partnership.dto.ts index f6a789a84b..2554b354bb 100644 --- a/src/components/partnership/dto/list-partnership.dto.ts +++ b/src/components/partnership/dto/list-partnership.dto.ts @@ -1,12 +1,11 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, ID, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { Partnership } from './partnership.dto'; @InputType() @@ -14,17 +13,14 @@ export abstract class PartnershipFilters { readonly projectId?: ID; } -const defaultFilters = {}; - @InputType() export class PartnershipListInput extends SortablePaginationInput< keyof Partnership >({ defaultSort: 'createdAt', }) { - @Type(() => PartnershipFilters) - @ValidateNested() - readonly filter: PartnershipFilters = defaultFilters; + @FilterField(PartnershipFilters, { internal: true }) + readonly filter: PartnershipFilters; } @ObjectType() diff --git a/src/components/post/dto/list-posts.dto.ts b/src/components/post/dto/list-posts.dto.ts index 331b8cb3fb..6e2b03330c 100644 --- a/src/components/post/dto/list-posts.dto.ts +++ b/src/components/post/dto/list-posts.dto.ts @@ -1,13 +1,12 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, ID, Order, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { Post } from './post.dto'; @InputType() @@ -15,16 +14,13 @@ export abstract class PostFilters { readonly parentId?: ID; } -const defaultFilters = {}; - @InputType() export class PostListInput extends SortablePaginationInput({ defaultSort: 'createdAt', defaultOrder: Order.DESC, }) { - @Type(() => PostFilters) - @ValidateNested() - readonly filter: PostFilters = defaultFilters; + @FilterField(PostFilters, { internal: true }) + readonly filter: PostFilters; } @ObjectType() diff --git a/src/components/product/dto/list-product.dto.ts b/src/components/product/dto/list-product.dto.ts index 5c4ef264a1..c8587cb6db 100644 --- a/src/components/product/dto/list-product.dto.ts +++ b/src/components/product/dto/list-product.dto.ts @@ -1,13 +1,12 @@ import { Field, InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { stripIndent } from 'common-tags'; import { + FilterField, ID, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { ProductApproach } from './product-approach'; import { ProductMethodology } from './product-methodology'; import { AnyProduct, Product } from './product.dto'; @@ -38,16 +37,12 @@ export abstract class ProductFilters { readonly engagementId?: ID; } -const defaultFilters = {}; - @InputType() export class ProductListInput extends SortablePaginationInput({ defaultSort: 'createdAt', }) { - @Field({ nullable: true }) - @Type(() => ProductFilters) - @ValidateNested() - readonly filter: ProductFilters = defaultFilters; + @FilterField(ProductFilters) + readonly filter: ProductFilters; } @ObjectType() diff --git a/src/components/project-change-request/dto/project-change-request-list.dto.ts b/src/components/project-change-request/dto/project-change-request-list.dto.ts index ac8663aa13..e28fc81fdb 100644 --- a/src/components/project-change-request/dto/project-change-request-list.dto.ts +++ b/src/components/project-change-request/dto/project-change-request-list.dto.ts @@ -1,12 +1,11 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, ID, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { ProjectChangeRequest } from './project-change-request.dto'; @InputType() @@ -14,17 +13,14 @@ export abstract class ProjectChangeRequestFilters { readonly projectId?: ID; } -const defaultFilters = {}; - @InputType() export class ProjectChangeRequestListInput extends SortablePaginationInput< keyof ProjectChangeRequest >({ defaultSort: 'createdAt', }) { - @Type(() => ProjectChangeRequestFilters) - @ValidateNested() - readonly filter: ProjectChangeRequestFilters = defaultFilters; + @FilterField(ProjectChangeRequestFilters, { internal: true }) + readonly filter: ProjectChangeRequestFilters; } @ObjectType() diff --git a/src/components/project/dto/list-projects.dto.ts b/src/components/project/dto/list-projects.dto.ts index 61197e21c4..317b6b864c 100644 --- a/src/components/project/dto/list-projects.dto.ts +++ b/src/components/project/dto/list-projects.dto.ts @@ -3,13 +3,14 @@ import { Type } from 'class-transformer'; import { ValidateNested } from 'class-validator'; import { DateTimeFilter, + FilterField, ID, PaginatedList, SecuredList, SensitivitiesFilter, Sensitivity, SortablePaginationInput, -} from '../../../common'; +} from '~/common'; import { InternshipProject, IProject, @@ -93,16 +94,12 @@ export abstract class ProjectFilters { readonly userId?: ID; } -const defaultFilters = {}; - @InputType() export class ProjectListInput extends SortablePaginationInput({ defaultSort: 'name', }) { - @Field({ nullable: true }) - @Type(() => ProjectFilters) - @ValidateNested() - readonly filter: ProjectFilters = defaultFilters; + @FilterField(ProjectFilters) + readonly filter: ProjectFilters; } @ObjectType() diff --git a/src/components/project/project-member/dto/list-project-members.dto.ts b/src/components/project/project-member/dto/list-project-members.dto.ts index 6fc8cf9588..09aad3e0c6 100644 --- a/src/components/project/project-member/dto/list-project-members.dto.ts +++ b/src/components/project/project-member/dto/list-project-members.dto.ts @@ -1,13 +1,12 @@ import { Field, InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, ID, PaginatedList, + Role, SecuredList, SortablePaginationInput, -} from '../../../../common'; -import { Role } from '../../../authorization'; +} from '~/common'; import { ProjectMember } from './project-member.dto'; @InputType() @@ -21,18 +20,14 @@ export abstract class ProjectMemberFilters { readonly projectId?: ID; } -const defaultFilters = {}; - @InputType() export class ProjectMemberListInput extends SortablePaginationInput< keyof ProjectMember >({ defaultSort: 'createdAt', }) { - @Field({ nullable: true }) - @Type(() => ProjectMemberFilters) - @ValidateNested() - readonly filter: ProjectMemberFilters = defaultFilters; + @FilterField(ProjectMemberFilters) + readonly filter: ProjectMemberFilters; } @ObjectType() diff --git a/src/components/song/dto/list-story.dto.ts b/src/components/song/dto/list-story.dto.ts index c863f643b0..0c4468173d 100644 --- a/src/components/song/dto/list-story.dto.ts +++ b/src/components/song/dto/list-story.dto.ts @@ -1,21 +1,16 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; -import { PaginatedList, SortablePaginationInput } from '../../../common'; +import { FilterField, PaginatedList, SortablePaginationInput } from '~/common'; import { Song } from './song.dto'; @InputType() export abstract class SongFilters {} -const defaultFilters = {}; - @InputType() export class SongListInput extends SortablePaginationInput({ defaultSort: 'name', }) { - @Type(() => SongFilters) - @ValidateNested() - readonly filter: SongFilters = defaultFilters; + @FilterField(SongFilters, { internal: true }) + readonly filter: SongFilters; } @ObjectType() diff --git a/src/components/story/dto/list-story.dto.ts b/src/components/story/dto/list-story.dto.ts index fa8ff8a569..631497c184 100644 --- a/src/components/story/dto/list-story.dto.ts +++ b/src/components/story/dto/list-story.dto.ts @@ -1,21 +1,16 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; -import { PaginatedList, SortablePaginationInput } from '../../../common'; +import { FilterField, PaginatedList, SortablePaginationInput } from '~/common'; import { Story } from './story.dto'; @InputType() export abstract class StoryFilters {} -const defaultFilters = {}; - @InputType() export class StoryListInput extends SortablePaginationInput({ defaultSort: 'name', }) { - @Type(() => StoryFilters) - @ValidateNested() - readonly filter: StoryFilters = defaultFilters; + @FilterField(StoryFilters, { internal: true }) + readonly filter: StoryFilters; } @ObjectType() diff --git a/src/components/user/dto/list-users.dto.ts b/src/components/user/dto/list-users.dto.ts index 5ae71d677c..be37b6d00a 100644 --- a/src/components/user/dto/list-users.dto.ts +++ b/src/components/user/dto/list-users.dto.ts @@ -1,7 +1,5 @@ import { Field, InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; -import { PaginatedList, SortablePaginationInput } from '../../../common'; +import { FilterField, PaginatedList, SortablePaginationInput } from '~/common'; import { User } from './user.dto'; @InputType() @@ -13,16 +11,12 @@ export abstract class UserFilters { readonly pinned?: boolean; } -const defaultFilters = {}; - @InputType() export class UserListInput extends SortablePaginationInput({ defaultSort: 'id', // TODO How to sort on name? }) { - @Field({ nullable: true }) - @Type(() => UserFilters) - @ValidateNested() - readonly filter: UserFilters = defaultFilters; + @FilterField(UserFilters) + readonly filter: UserFilters; } @ObjectType() diff --git a/src/components/user/education/dto/list-education.dto.ts b/src/components/user/education/dto/list-education.dto.ts index 80bc12ddf7..4532473829 100644 --- a/src/components/user/education/dto/list-education.dto.ts +++ b/src/components/user/education/dto/list-education.dto.ts @@ -1,12 +1,11 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, ID, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../../common'; +} from '~/common'; import { Education } from './education.dto'; @InputType() @@ -14,17 +13,14 @@ export abstract class EducationFilters { readonly userId?: ID; } -const defaultFilters = {}; - @InputType() export class EducationListInput extends SortablePaginationInput< keyof Education >({ defaultSort: 'institution', }) { - @Type(() => EducationFilters) - @ValidateNested() - readonly filter: EducationFilters = defaultFilters; + @FilterField(EducationFilters, { internal: true }) + readonly filter: EducationFilters; } @ObjectType() diff --git a/src/components/user/unavailability/dto/list-unavailabilities.dto.ts b/src/components/user/unavailability/dto/list-unavailabilities.dto.ts index 5df0f8749c..70f5da2946 100644 --- a/src/components/user/unavailability/dto/list-unavailabilities.dto.ts +++ b/src/components/user/unavailability/dto/list-unavailabilities.dto.ts @@ -1,13 +1,12 @@ import { InputType, ObjectType } from '@nestjs/graphql'; -import { Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; import { + FilterField, ID, Order, PaginatedList, SecuredList, SortablePaginationInput, -} from '../../../../common'; +} from '~/common'; import { Unavailability } from './unavailability.dto'; @InputType() @@ -15,8 +14,6 @@ export abstract class UnavailabilityFilters { readonly userId?: ID; } -const defaultFilters = {}; - @InputType() export class UnavailabilityListInput extends SortablePaginationInput< keyof Unavailability @@ -24,9 +21,8 @@ export class UnavailabilityListInput extends SortablePaginationInput< defaultSort: 'start', defaultOrder: Order.DESC, }) { - @Type(() => UnavailabilityFilters) - @ValidateNested() - readonly filter: UnavailabilityFilters = defaultFilters; + @FilterField(UnavailabilityFilters, { internal: true }) + readonly filter: UnavailabilityFilters; } @ObjectType()