diff --git a/src/Resource.ts b/src/Resource.ts index 75a2db62..9db8b4ee 100644 --- a/src/Resource.ts +++ b/src/Resource.ts @@ -1,9 +1,9 @@ /* eslint-disable no-param-reassign */ +import { BaseRecord, BaseResource, Filter, flat, ValidationError } from 'admin-bro' import { BaseEntity } from 'typeorm' -import { BaseResource, ValidationError, Filter, BaseRecord, flat } from 'admin-bro' - import { Property } from './Property' import { convertFilter } from './utils/convertFilter' +import { reconstructPostArrays } from './utils/reconstructPostArrays' type ParamsType = Record; @@ -129,12 +129,28 @@ export class Resource extends BaseResource { /** Converts params from string to final type */ private prepareParams(params: Record): Record { - const preparedParams: Record = { ...params } - + const preparedParams = reconstructPostArrays(params) this.properties().forEach((property) => { - const param = flat.get(preparedParams, property.path()) const key = property.path() + let param + let applyTransformation + if (property.column.isArray) { + applyTransformation = (transformation: (v: T) => any) => (array: T[]) => { + if (array === null || array === undefined) { + return array + } + if (!Array.isArray(array)) { + throw Error(`Expected params["${key}"] to be an array. Received ${String(array)}`) + } + return array.map(transformation) + } + param = preparedParams[key] + } else { + applyTransformation = (transformation: (v: T) => any) => (val: T) => transformation(val) + param = flat.get(preparedParams, key) + } + // eslint-disable-next-line no-continue if (param === undefined) { return } @@ -145,21 +161,33 @@ export class Resource extends BaseResource { } if (type === 'number') { - preparedParams[key] = Number(param) + preparedParams[key] = applyTransformation(Number)(param) + } + + if (type === 'string') { + preparedParams[key] = applyTransformation(String)(param) } if (type === 'reference') { - if (param === null) { - preparedParams[property.column.propertyName] = null - } else { + let paramValue + const transformation = (p: any) => ({ // references cannot be stored as an IDs in typeorm, so in order to mimic this) and // not fetching reference resource) change this: // { postId: "1" } // to that: // { post: { id: 1 } } - const id = (property.column.type === Number) ? Number(param) : param - preparedParams[property.column.propertyName] = { id } + id: (property.column.type === Number) ? Number(p) : p, + }) + if (property.column.isArray) { + paramValue = param + .map((val: any) => transformation(val)) + .filter((val: any) => val !== null) + } else if (param === null) { + paramValue = null + } else { + paramValue = transformation(param) } + preparedParams[property.column.propertyName] = paramValue } }) return preparedParams diff --git a/src/utils/reconstructPostArrays.ts b/src/utils/reconstructPostArrays.ts new file mode 100644 index 00000000..8e2cdb69 --- /dev/null +++ b/src/utils/reconstructPostArrays.ts @@ -0,0 +1,31 @@ +/** + * @param obj an object where arrays are coded as xxx.1, xxx.2, etc. + * @returns an object with all arrays reconstructed, and single values simply copied. + * + * e.g. + * { 'foo.0': 'abc', 'foo.1': 'def', name: 'john smith' } + * would return + * { 'foo': ['abc', 'def'], name: 'john smith' } + */ + +export const reconstructPostArrays = (obj: Record): Record => { + const allKeys = Object.keys(obj) + const resultObj = {} + for (const key of allKeys) { + // Does the key look like `foo.56` ? + const match = key.match(/^([^.]+)\.(\d+)$/) + if (match) { + // Yes it does, so the real key is probably `foo`, and this is the item #56. + const realKey = match[1] + const indexInArray = +match[2] + if (!resultObj[realKey]) { + resultObj[realKey] = [] + } + resultObj[realKey][indexInArray] = obj[key] + } else { + // No, it is a single value, we just copy it. + resultObj[key] = obj[key] + } + } + return resultObj +}