Skip to content

Commit

Permalink
feat: Allow passing custom filters to typeorm adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
JonaszJestem committed May 5, 2021
1 parent f6de206 commit c186fdd
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/Resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BaseEntity } from 'typeorm'
import { BaseResource, ValidationError, Filter, BaseRecord, flat } from 'admin-bro'

import { Property } from './Property'
import { convertFilter } from './utils/convertFilter'
import { convertFilter } from './utils/filter/filter.converter'

type ParamsType = Record<string, any>;

Expand Down
36 changes: 0 additions & 36 deletions src/utils/convertFilter.ts

This file was deleted.

20 changes: 20 additions & 0 deletions src/utils/filter/custom-filter.parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { FilterParser } from './filter.types'

/**
* It wasn't possible to pass raw filters to adapters with admin-bro
* This solution allows you to pass custom filters to typeorm adapter modyfing list handler
*
* In your custom list handler modify creating filters in this way:
*
* ```
* // That makes `Filter` class to create proper filter object.
* filters[propertyToFilterBy] = 1;
* const filter = await new Filter(filters, resource).populate();
* // This parser recognizes `custom` field and passes the value directly to typeorm
* filter.filters[propertyToFilterBy].custom = In([1,2,3]);
*
*/
export const CustomParser: FilterParser = {
isParserForType: (filter) => (filter as any)?.custom,
parse: (filter, fieldKey) => ({ filterKey: fieldKey, filterValue: (filter as any)?.custom }),
}
35 changes: 35 additions & 0 deletions src/utils/filter/date-filter.parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Between, LessThanOrEqual, MoreThanOrEqual } from 'typeorm'
import { FilterParser } from './filter.types'

export const DateParser: FilterParser = {
isParserForType: (filter) => ['date', 'datetime'].includes(filter.property.type()),
parse: (filter, fieldKey) => {
if (
typeof filter.value !== 'string'
&& filter.value.from
&& filter.value.to
) {
return {
filterKey: fieldKey,
filterValue: Between(
new Date(filter.value.from),
new Date(filter.value.to),
),
}
}
if (typeof filter.value !== 'string' && filter.value.from) {
return {
filterKey: fieldKey,
filterValue: MoreThanOrEqual(new Date(filter.value.from).toISOString()),
}
}
if (typeof filter.value !== 'string' && filter.value.to) {
return {
filterKey: fieldKey,
filterValue: LessThanOrEqual(new Date(filter.value.to)),
}
}

throw new Error('Cannot parse date filter')
},
}
7 changes: 7 additions & 0 deletions src/utils/filter/default-filter.parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Like } from 'typeorm'
import { FilterParser } from './filter.types'

export const DefaultParser: FilterParser = {
isParserForType: (filter) => filter.property.type() === 'string',
parse: (filter, fieldKey) => ({ filterKey: fieldKey, filterValue: Like(`%${filter.value}%`) }),
}
7 changes: 7 additions & 0 deletions src/utils/filter/enum-filter.parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Property } from '../../Property'
import { FilterParser } from './filter.types'

export const EnumParser: FilterParser = {
isParserForType: (filter) => (filter.property as Property).column.type === 'enum',
parse: (filter, fieldKey) => ({ filterKey: fieldKey, filterValue: filter.value }),
}
29 changes: 29 additions & 0 deletions src/utils/filter/filter.converter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Filter } from 'admin-bro'
import { BaseEntity, FindConditions } from 'typeorm'
import { DefaultParser } from './default-filter.parser'
import { parsers } from './filter.utils'

export const convertFilter = (
filterObject?: Filter,
): FindConditions<BaseEntity> => {
if (!filterObject) {
return {}
}

const { filters } = filterObject
const where = {}

Object.entries(filters).forEach(([fieldKey, filter]) => {
const parser = parsers.find((p) => p.isParserForType(filter))

if (parser) {
const { filterValue, filterKey } = parser.parse(filter, fieldKey)
where[filterKey] = filterValue
} else {
const { filterValue, filterKey } = DefaultParser.parse(filter, fieldKey)
where[filterKey] = filterValue
}
})

return where
}
10 changes: 10 additions & 0 deletions src/utils/filter/filter.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { FilterElement } from 'admin-bro'
import { FindManyOptions } from 'typeorm'

export type FilterParser = {
isParserForType: (filter: FilterElement) => boolean;
parse: (
filter: FilterElement,
fieldKey: string
) => { filterKey: string; filterValue: FindManyOptions['where'] };
};
22 changes: 22 additions & 0 deletions src/utils/filter/filter.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { CustomParser } from './custom-filter.parser'
import { DateParser } from './date-filter.parser'
import { EnumParser } from './enum-filter.parser'
import { JSONParser } from './json-filter.parser'
import { ReferenceParser } from './reference-filter.parser'

export const safeParseJSON = (json: string): any | null => {
try {
return JSON.parse(json)
} catch (e) {
return null
}
}

export const parsers = [
// Has to be the first one, as it is intended to use custom filter if user overrides that
CustomParser,
DateParser,
EnumParser,
ReferenceParser,
JSONParser,
]
12 changes: 12 additions & 0 deletions src/utils/filter/json-filter.parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { FilterParser } from './filter.types'
import { safeParseJSON } from './filter.utils'

export const JSONParser: FilterParser = {
isParserForType: (filter) => ['boolean', 'number', 'float', 'object', 'array'].includes(
filter.property.type(),
),
parse: (filter, fieldKey) => ({
filterKey: fieldKey,
filterValue: safeParseJSON(filter.value as string),
}),
}
12 changes: 12 additions & 0 deletions src/utils/filter/reference-filter.parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Property } from '../../Property'
import { FilterParser } from './filter.types'

export const ReferenceParser: FilterParser = {
isParserForType: (filter) => filter.property.type() === 'reference',
parse: (filter) => {
const [column] = (filter.property as Property).column.propertyPath.split(
'.',
)
return { filterKey: column, filterValue: filter.value }
},
}

0 comments on commit c186fdd

Please sign in to comment.