-
Notifications
You must be signed in to change notification settings - Fork 154
Expand Record Object Mapping to allow Parameter Mapping #1362
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 6.x
Are you sure you want to change the base?
Changes from 23 commits
7fdbc14
8974b0e
8bed085
596cb31
cdfcbf3
70bbadb
68f84e0
6466d4b
232b11a
86254dc
66ead65
a107800
2e53950
de76027
1040d63
c3e03f4
0127f10
6cf1e45
224031e
9c410e9
1f34571
d102b06
0587594
d760fcd
eae1683
f59c279
17b760e
e26d70b
8c52440
9601d8e
0bc3c1b
2f3ac83
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ import Integer, { isInt, int } from '../integer' | |
| import { NumberOrInteger } from '../graph-types' | ||
| import { EncryptionLevel } from '../types' | ||
| import { stringify } from '../json' | ||
| import { Rules, validateAndcleanParameters } from '../mapping.highlevel' | ||
MaxAake marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| const ENCRYPTION_ON: EncryptionLevel = 'ENCRYPTION_ON' | ||
| const ENCRYPTION_OFF: EncryptionLevel = 'ENCRYPTION_OFF' | ||
|
|
@@ -62,15 +63,16 @@ function isObject (obj: any): boolean { | |
| * @throws TypeError when either given query or parameters are invalid. | ||
| */ | ||
| function validateQueryAndParameters ( | ||
| query: string | String | { text: string, parameters?: any }, | ||
| query: string | String | { text: string, parameters?: any, parameterRules?: Rules }, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| parameters?: any, | ||
| opt?: { skipAsserts: boolean } | ||
| opt?: { skipAsserts?: boolean, parameterRules?: Rules } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| ): { | ||
| validatedQuery: string | ||
| params: any | ||
| } { | ||
| let validatedQuery: string = '' | ||
| let params = parameters ?? {} | ||
| let parameterRules = opt?.parameterRules | ||
| const skipAsserts: boolean = opt?.skipAsserts ?? false | ||
|
|
||
| if (typeof query === 'string') { | ||
|
|
@@ -80,9 +82,11 @@ function validateQueryAndParameters ( | |
| } else if (typeof query === 'object' && query.text != null) { | ||
| validatedQuery = query.text | ||
| params = query.parameters ?? {} | ||
| parameterRules = query.parameterRules | ||
| } | ||
|
|
||
| if (!skipAsserts) { | ||
| params = validateAndcleanParameters(params, parameterRules) | ||
| assertCypherQuery(validatedQuery) | ||
| assertQueryParameters(params) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -29,14 +29,15 @@ export interface Rule { | |||||
| optional?: boolean | ||||||
| from?: string | ||||||
| convert?: (recordValue: any, field: string) => any | ||||||
| parameterConversion?: (objectValue: any) => any | ||||||
| validate?: (recordValue: any, field: string) => void | ||||||
|
Comment on lines
29
to
33
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I've missed it, but I couldn't see a place where these options are properly documented. Specifically, the |
||||||
| } | ||||||
|
|
||||||
| export type Rules = Record<string, Rule> | ||||||
|
|
||||||
| export let rulesRegistry: Record<string, Rules> = {} | ||||||
|
|
||||||
| let nameMapping: (name: string) => string = (name) => name | ||||||
| export let nameMapping: (name: string) => string = (name) => name | ||||||
MaxAake marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| function register <T extends {} = Object> (constructor: GenericConstructor<T>, rules: Rules): void { | ||||||
| rulesRegistry[constructor.name] = rules | ||||||
|
|
@@ -71,15 +72,13 @@ function getCaseTranslator (databaseConvention: string, codeConvention: string): | |||||
| export const RecordObjectMapping = Object.freeze({ | ||||||
| /** | ||||||
| * Clears all registered type mappings from the record object mapping registry. | ||||||
| * @experimental Part of the Record Object Mapping preview feature | ||||||
| */ | ||||||
| clearMappingRegistry, | ||||||
| /** | ||||||
| * Creates a translation function from record key names to object property names, for use with the {@link translateIdentifiers} function | ||||||
| * | ||||||
| * Recognized naming conventions are "camelCase", "PascalCase", "snake_case", "kebab-case", "SCREAMING_SNAKE_CASE" | ||||||
| * | ||||||
| * @experimental Part of the Record Object Mapping preview feature | ||||||
| * @param {string} databaseConvention The naming convention in use in database result Records | ||||||
| * @param {string} codeConvention The naming convention in use in JavaScript object properties | ||||||
| * @returns {function} translation function | ||||||
|
|
@@ -101,7 +100,6 @@ export const RecordObjectMapping = Object.freeze({ | |||||
| * resultTransformer: neo4j.resultTransformers.hydrated(Person) | ||||||
| * }) | ||||||
| * | ||||||
| * @experimental Part of the Record Object Mapping preview feature | ||||||
| * @param {GenericConstructor} constructor The constructor function of the class to set rules for | ||||||
| * @param {Rules} rules The rules to set for the provided class | ||||||
| */ | ||||||
|
|
@@ -130,7 +128,6 @@ export const RecordObjectMapping = Object.freeze({ | |||||
| * //or by registering them to the mapping registry | ||||||
| * RecordObjectMapping.register(Person, personRules) | ||||||
| * | ||||||
| * @experimental Part of the Record Object Mapping preview feature | ||||||
| * @param {function} translationFunction A function translating the names of your JS object property names to record key names | ||||||
| */ | ||||||
| translateIdentifiers | ||||||
|
|
@@ -179,6 +176,49 @@ export function valueAs (value: unknown, field: string, rule?: Rule): unknown { | |||||
|
|
||||||
| return ((rule?.convert) != null) ? rule.convert(value, field) : value | ||||||
| } | ||||||
|
|
||||||
| export function optionalParameterConversion (value: unknown, rule?: Rule): unknown { | ||||||
| if (rule?.optional === true && value == null) { | ||||||
| return value | ||||||
| } | ||||||
| return ((rule?.parameterConversion) != null) ? rule.parameterConversion(value) : value | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| } | ||||||
|
|
||||||
| export function validateAndcleanParameters (params: Record<string, any>, suppliedRules?: Rules): Record<string, any> { | ||||||
| const cleanedParams: Record<string, any> = {} | ||||||
| // @ts-expect-error | ||||||
| const parameterRules = getRules(params.constructor, suppliedRules) | ||||||
MaxAake marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| if (parameterRules !== undefined) { | ||||||
MaxAake marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| for (const key in parameterRules) { | ||||||
| if (!(parameterRules?.[key]?.optional === true)) { | ||||||
MaxAake marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
MaxAake marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| let param = params[key] | ||||||
| if (parameterRules[key]?.parameterConversion !== undefined) { | ||||||
| param = parameterRules[key].parameterConversion(params[key]) | ||||||
| } | ||||||
| if (param === undefined) { | ||||||
| throw newError('Parameter object did not include required parameter.') | ||||||
MaxAake marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| } | ||||||
| if (parameterRules[key].validate != null) { | ||||||
MaxAake marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| parameterRules[key].validate(param, key) | ||||||
| // @ts-expect-error | ||||||
| if (parameterRules[key].apply !== undefined) { | ||||||
MaxAake marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| for (const entryKey in param) { | ||||||
| // @ts-expect-error | ||||||
| parameterRules[key].apply.validate(param[entryKey], entryKey) | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| const mappedKey = parameterRules[key].from ?? nameMapping(key) | ||||||
|
|
||||||
| cleanedParams[mappedKey] = param | ||||||
| } | ||||||
| } | ||||||
| return cleanedParams | ||||||
| } else { | ||||||
| return params | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| function getRules<T extends {} = Object> (constructorOrRules: Rules | GenericConstructor<T>, rules: Rules | undefined): Rules | undefined { | ||||||
| const rulesDefined = typeof constructorOrRules === 'object' ? constructorOrRules : rules | ||||||
| if (rulesDefined != null) { | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.