From c168ed24ae2256e18f359446977706a1b7637c55 Mon Sep 17 00:00:00 2001 From: Pascal Heitz Date: Fri, 12 Mar 2021 02:48:36 +0100 Subject: [PATCH 1/4] array transformation --- src/Resource.ts | 58 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/src/Resource.ts b/src/Resource.ts index 75a2db62..dcaa8846 100644 --- a/src/Resource.ts +++ b/src/Resource.ts @@ -1,7 +1,6 @@ /* 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' @@ -129,39 +128,76 @@ export class Resource extends BaseResource { /** Converts params from string to final type */ private prepareParams(params: Record): Record { + console.log(' params', params) const preparedParams: Record = { ...params } - + const paramKeys = Object.keys(params) this.properties().forEach((property) => { - const param = flat.get(preparedParams, property.path()) + const propertyPath = property.path() + console.log(' propertyPath', propertyPath) const key = property.path() + let param + let transformValue + if (property.column.isArray) { + transformValue = (transformFun: (v: T) => any) => (array: T[]) => array.map(transformFun) + const regexp = new RegExp(`^${propertyPath}\\.(\\d+)$`) + for (const paramKey of paramKeys) { + const match = paramKey.match(regexp) + if (match) { + if (!param) { + param = [] + } + const positionInArray = +match[1] + param[positionInArray] = preparedParams[paramKey] + delete preparedParams[paramKey] + } + } + } else { + transformValue = (transformFun: (v: T) => any) => (val: T) => transformFun(val) + param = flat.get(preparedParams, propertyPath) + } + console.log(' param', param) + // eslint-disable-next-line no-continue if (param === undefined) { return } const type = property.type() + console.log(' type', type) if (type === 'mixed') { - preparedParams[key] = param + preparedParams[key] = transformValue((v: any) => v)(param) } if (type === 'number') { - preparedParams[key] = Number(param) + preparedParams[key] = transformValue(Number)(param) + } + + if (type === 'string') { + preparedParams[key] = transformValue(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 = transformValue(transformation)(param) + } else if (param === null) { + paramValue = null + } else { + paramValue = transformation(param) } + preparedParams[property.column.propertyName] = paramValue } }) + console.log(' preparedParams', preparedParams) return preparedParams } From e6983baae7aaffe336285b13d1b0ae7bafe8804c Mon Sep 17 00:00:00 2001 From: Pascal Heitz Date: Fri, 12 Mar 2021 03:06:38 +0100 Subject: [PATCH 2/4] reconstructPostArrays --- src/Resource.ts | 42 +++++++++--------------------- src/utils/reconstructPostArrays.ts | 31 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 29 deletions(-) create mode 100644 src/utils/reconstructPostArrays.ts diff --git a/src/Resource.ts b/src/Resource.ts index dcaa8846..d821cc07 100644 --- a/src/Resource.ts +++ b/src/Resource.ts @@ -3,6 +3,7 @@ import { BaseRecord, BaseResource, Filter, flat, ValidationError } from 'admin-b import { BaseEntity } from 'typeorm' import { Property } from './Property' import { convertFilter } from './utils/convertFilter' +import { reconstructPostArrays } from './utils/reconstructPostArrays' type ParamsType = Record; @@ -128,52 +129,35 @@ export class Resource extends BaseResource { /** Converts params from string to final type */ private prepareParams(params: Record): Record { - console.log(' params', params) - const preparedParams: Record = { ...params } - const paramKeys = Object.keys(params) + const preparedParams = reconstructPostArrays(params) this.properties().forEach((property) => { - const propertyPath = property.path() - console.log(' propertyPath', propertyPath) const key = property.path() let param - let transformValue + let applyTransformation if (property.column.isArray) { - transformValue = (transformFun: (v: T) => any) => (array: T[]) => array.map(transformFun) - const regexp = new RegExp(`^${propertyPath}\\.(\\d+)$`) - for (const paramKey of paramKeys) { - const match = paramKey.match(regexp) - if (match) { - if (!param) { - param = [] - } - const positionInArray = +match[1] - param[positionInArray] = preparedParams[paramKey] - delete preparedParams[paramKey] - } - } + applyTransformation = (transformFun: (v: T) => any) => (array: T[]) => array.map(transformFun) + param = preparedParams[key] } else { - transformValue = (transformFun: (v: T) => any) => (val: T) => transformFun(val) - param = flat.get(preparedParams, propertyPath) + applyTransformation = (transformFun: (v: T) => any) => (val: T) => transformFun(val) + param = flat.get(preparedParams, key) } - console.log(' param', param) // eslint-disable-next-line no-continue if (param === undefined) { return } const type = property.type() - console.log(' type', type) if (type === 'mixed') { - preparedParams[key] = transformValue((v: any) => v)(param) + preparedParams[key] = param } if (type === 'number') { - preparedParams[key] = transformValue(Number)(param) + preparedParams[key] = applyTransformation(Number)(param) } if (type === 'string') { - preparedParams[key] = transformValue(String)(param) + preparedParams[key] = applyTransformation(String)(param) } if (type === 'reference') { @@ -186,9 +170,10 @@ export class Resource extends BaseResource { // { post: { id: 1 } } id: (property.column.type === Number) ? Number(p) : p, }) - if (property.column.isArray) { - paramValue = transformValue(transformation)(param) + paramValue = param + .map((val: any) => transformation(val)) + .filter((val: any) => val !== null) } else if (param === null) { paramValue = null } else { @@ -197,7 +182,6 @@ export class Resource extends BaseResource { preparedParams[property.column.propertyName] = paramValue } }) - console.log(' preparedParams', preparedParams) 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 +} From 8990826729ada9e1bcebebab37b52c516f0f11c7 Mon Sep 17 00:00:00 2001 From: Pascal Heitz Date: Fri, 12 Mar 2021 03:16:09 +0100 Subject: [PATCH 3/4] assert isArray --- src/Resource.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Resource.ts b/src/Resource.ts index d821cc07..4220a7f7 100644 --- a/src/Resource.ts +++ b/src/Resource.ts @@ -136,10 +136,15 @@ export class Resource extends BaseResource { let param let applyTransformation if (property.column.isArray) { - applyTransformation = (transformFun: (v: T) => any) => (array: T[]) => array.map(transformFun) + applyTransformation = (transformation: (v: T) => any) => (array: T[]) => { + 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 = (transformFun: (v: T) => any) => (val: T) => transformFun(val) + applyTransformation = (transformation: (v: T) => any) => (val: T) => transformation(val) param = flat.get(preparedParams, key) } From 3962c23ad04d7b46a5c5e6bf379b5d97b17e79d6 Mon Sep 17 00:00:00 2001 From: Pascal Heitz Date: Fri, 12 Mar 2021 03:18:37 +0100 Subject: [PATCH 4/4] authorize null/undefined in array column --- src/Resource.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Resource.ts b/src/Resource.ts index 4220a7f7..9db8b4ee 100644 --- a/src/Resource.ts +++ b/src/Resource.ts @@ -137,8 +137,11 @@ export class Resource extends BaseResource { 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)}`) + throw Error(`Expected params["${key}"] to be an array. Received ${String(array)}`) } return array.map(transformation) }