Skip to content

Commit

Permalink
feat: custom decorators for filter
Browse files Browse the repository at this point in the history
  • Loading branch information
iamkhalidbashir committed Sep 25, 2023
1 parent 4bf89ca commit 5d803bd
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type FilterableFieldOptions = {
allowedComparisons?: FilterComparisonOperators<unknown>[]
filterRequired?: boolean
filterOnly?: boolean
filterDecorators?: PropertyDecorator[]
} & FieldOptions

export interface FilterableFieldDescriptor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { IsBoolean, IsDate, IsOptional, ValidateNested } from 'class-validator'
import { upperCaseFirst } from 'upper-case-first'

import { getGraphqlEnumMetadata } from '../../../common'
import { SkipIf } from '../../../decorators'
import { SkipIf, composeDecorators } from '../../../decorators'
import { IsUndefined } from '../../validators'
import { isInAllowedList } from '../helpers'
import { getOrCreateBooleanFieldComparison } from './boolean-field-comparison.type'
Expand Down Expand Up @@ -66,9 +66,13 @@ const getTypeName = (SomeType: ReturnTypeFuncValue): string => {
throw new Error(`Unable to create filter comparison for ${JSON.stringify(SomeType)}.`)
}

const isCustomFieldComparison = <T>(options: FilterComparisonOptions<T>): boolean => !!options.allowedComparisons
const isCustomFieldComparison = <T>(options: FilterComparisonOptions<T>): boolean =>
!!options.allowedComparisons || !!options.decorators

const getComparisonTypeName = <T>(fieldType: ReturnTypeFuncValue, options: FilterComparisonOptions<T>): string => {
if (options.filterTypeNamePrefix) {
return `${options.filterTypeNamePrefix}FilterComparison`
}
if (isCustomFieldComparison(options)) {
return `${upperCaseFirst(options.fieldName)}FilterComparison`
}
Expand All @@ -80,14 +84,17 @@ type FilterComparisonOptions<T> = {
fieldName: string
allowedComparisons?: FilterComparisonOperators<T>[]
returnTypeFunc?: ReturnTypeFunc<ReturnTypeFuncValue>
decorators?: PropertyDecorator[]
filterTypeNamePrefix?: string
}

/** @internal */
export function createFilterComparisonType<T>(options: FilterComparisonOptions<T>): Class<FilterFieldComparison<T>> {
const { FieldType, returnTypeFunc } = options
const { FieldType, returnTypeFunc, decorators = [] } = options
const fieldType = returnTypeFunc ? returnTypeFunc() : FieldType
const inputName = getComparisonTypeName(fieldType, options)
const generator = filterComparisonMap.get(inputName)
const CustomDecorator = () => composeDecorators(...decorators)

if (generator) {
return generator() as Class<FilterFieldComparison<T>>
Expand Down Expand Up @@ -129,61 +136,73 @@ export function createFilterComparisonType<T>(options: FilterComparisonOptions<T
@SkipIf(isNotAllowed('eq'), Field(() => fieldType, { nullable: true }))
@IsUndefined()
@Type(() => FieldType)
@CustomDecorator()
eq?: T

@SkipIf(isNotAllowed('neq'), Field(() => fieldType, { nullable: true }))
@IsUndefined()
@Type(() => FieldType)
@CustomDecorator()
neq?: T

@SkipIf(isNotAllowed('gt'), Field(() => fieldType, { nullable: true }))
@IsUndefined()
@Type(() => FieldType)
@CustomDecorator()
gt?: T

@SkipIf(isNotAllowed('gte'), Field(() => fieldType, { nullable: true }))
@IsUndefined()
@Type(() => FieldType)
@CustomDecorator()
gte?: T

@SkipIf(isNotAllowed('lt'), Field(() => fieldType, { nullable: true }))
@IsUndefined()
@Type(() => FieldType)
@CustomDecorator()
lt?: T

@SkipIf(isNotAllowed('lte'), Field(() => fieldType, { nullable: true }))
@IsUndefined()
@Type(() => FieldType)
@CustomDecorator()
lte?: T

@SkipIf(isNotAllowed('like'), Field(() => fieldType, { nullable: true }))
@IsUndefined()
@Type(() => FieldType)
@CustomDecorator()
like?: T

@SkipIf(isNotAllowed('notLike'), Field(() => fieldType, { nullable: true }))
@IsUndefined()
@Type(() => FieldType)
@CustomDecorator()
notLike?: T

@SkipIf(isNotAllowed('iLike'), Field(() => fieldType, { nullable: true }))
@IsUndefined()
@Type(() => FieldType)
@CustomDecorator()
iLike?: T

@SkipIf(isNotAllowed('notILike'), Field(() => fieldType, { nullable: true }))
@IsUndefined()
@Type(() => FieldType)
@CustomDecorator()
notILike?: T;

@SkipIf(isNotAllowed('in'), Field(() => [fieldType], { nullable: true }))
@IsUndefined()
@Type(() => FieldType)
@CustomDecorator()
in?: T[]

@SkipIf(isNotAllowed('notIn'), Field(() => [fieldType], { nullable: true }))
@IsUndefined()
@Type(() => FieldType)
@CustomDecorator()
notIn?: T[]

@SkipIf(isNotAllowed('between', allowedBetweenTypes), Field(() => FcBetween, { nullable: true }))
Expand Down
3 changes: 2 additions & 1 deletion packages/query-graphql/src/types/query/filter.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ function getOrCreateFilterType<T>(
FieldType: target,
fieldName: `${baseName}${upperCaseFirst(propertyName)}`,
allowedComparisons: advancedOptions?.allowedComparisons,
returnTypeFunc
returnTypeFunc,
decorators: advancedOptions?.filterDecorators
})
const nullable = advancedOptions?.filterRequired !== true
ValidateNested()(GraphQLFilter.prototype, propertyName)
Expand Down

0 comments on commit 5d803bd

Please sign in to comment.