diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js index 7f4469bf1..221dce776 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js @@ -74,7 +74,9 @@ export default class BoltProtocol { { disableLosslessIntegers, useBigInt } = {}, createResponseHandler = () => null, log, - onProtocolError + onProtocolError, + hydrationHooks, + dehydrationHooks ) { this._server = server || {} this._chunker = chunker @@ -86,11 +88,13 @@ export default class BoltProtocol { this._fatalError = null this._lastMessageSignature = null this._config = { disableLosslessIntegers, useBigInt } + this._hydrationHooks = hydrationHooks + this._dehydrationHooks = dehydrationHooks } get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v2.js b/packages/bolt-connection/src/bolt/bolt-protocol-v2.js index 399457cf2..7a38e350d 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v2.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v2.js @@ -37,7 +37,7 @@ export default class BoltProtocol extends BoltProtocolV1 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v3.js b/packages/bolt-connection/src/bolt/bolt-protocol-v3.js index 98e5bb88f..b2c40eead 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v3.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v3.js @@ -48,7 +48,7 @@ export default class BoltProtocol extends BoltProtocolV2 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.js index 47bec83db..07c0bf94e 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.js @@ -46,7 +46,7 @@ export default class BoltProtocol extends BoltProtocolV3 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js index 6d1b0f702..1c32fa5ca 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js @@ -49,6 +49,8 @@ export default class BoltProtocol extends BoltProtocolV4 { createResponseHandler = () => null, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) { super( @@ -57,7 +59,9 @@ export default class BoltProtocol extends BoltProtocolV4 { packstreamConfig, createResponseHandler, log, - onProtocolError + onProtocolError, + hydrationHooks, + dehydrationHooks ) this._serversideRouting = serversideRouting } @@ -68,7 +72,7 @@ export default class BoltProtocol extends BoltProtocolV4 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x2.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x2.js index 96e607164..1150f3065 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x2.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x2.js @@ -34,7 +34,7 @@ export default class BoltProtocol extends BoltProtocolV41 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js index 8713ae17b..78d41be0a 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js @@ -39,7 +39,7 @@ export default class BoltProtocol extends BoltProtocolV42 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } @@ -126,6 +126,6 @@ export default class BoltProtocol extends BoltProtocolV42 { this._transformer = new Transformer(Object.values({ ...transformersFactories, ...utcTransformersFactories - }).map(create => create(this._config, this._log))) + }).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.js index 9b23ce11e..e795eb2ed 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.js @@ -39,7 +39,7 @@ export default class BoltProtocol extends BoltProtocolV43 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } @@ -178,6 +178,6 @@ export default class BoltProtocol extends BoltProtocolV43 { this._transformer = new Transformer(Object.values({ ...transformersFactories, ...utcTransformersFactories - }).map(create => create(this._config, this._log))) + }).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js index 222ca88fb..51e028d19 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js @@ -37,7 +37,7 @@ export default class BoltProtocol extends BoltProtocolV44 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js index ea8c9c4cd..6b85cb123 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js @@ -37,7 +37,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x2.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x2.js index c64848244..b8b8a37e6 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x2.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x2.js @@ -36,7 +36,7 @@ export default class BoltProtocol extends BoltProtocolV5x1 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x3.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x3.js index 1b085989f..6675d6c17 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x3.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x3.js @@ -36,7 +36,7 @@ export default class BoltProtocol extends BoltProtocolV5x2 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x4.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x4.js index f9763f81a..0f86d6d6e 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x4.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x4.js @@ -36,7 +36,7 @@ export default class BoltProtocol extends BoltProtocolV5x3 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/bolt-connection/src/bolt/create.js b/packages/bolt-connection/src/bolt/create.js index 9638b654c..dfcc91ae2 100644 --- a/packages/bolt-connection/src/bolt/create.js +++ b/packages/bolt-connection/src/bolt/create.js @@ -48,6 +48,8 @@ import ResponseHandler from './response-handler' * @param {boolean} config.disableLosslessIntegers Disable the lossless integers * @param {boolean} packstreamConfig.useBigInt if this connection should convert all received integers to native BigInt numbers. * @param {boolean} config.serversideRouting It's using server side routing + * @param {HydatrationHooks} config.hydrationHooks Hydatration hooks used to map types + * @param {DehydrationHooks} config.dehydrationHooks Hydatration hooks used to map types */ export default function create ({ version, @@ -59,7 +61,9 @@ export default function create ({ serversideRouting, server, // server info log, - observer + observer, + hydrationHooks, + dehydrationHooks } = {}) { const createResponseHandler = protocol => { const responseHandler = new ResponseHandler({ @@ -94,7 +98,9 @@ export default function create ({ serversideRouting, createResponseHandler, observer.onProtocolError.bind(observer), - log + log, + hydrationHooks, + dehydrationHooks ) } @@ -106,7 +112,9 @@ function createProtocol ( serversideRouting, createResponseHandler, onProtocolError, - log + log, + hydrationHooks, + dehydrationHooks ) { switch (version) { case 1: @@ -116,7 +124,9 @@ function createProtocol ( packingConfig, createResponseHandler, log, - onProtocolError + onProtocolError, + hydrationHooks, + dehydrationHooks ) case 2: return new BoltProtocolV2( @@ -125,7 +135,9 @@ function createProtocol ( packingConfig, createResponseHandler, log, - onProtocolError + onProtocolError, + hydrationHooks, + dehydrationHooks ) case 3: return new BoltProtocolV3( @@ -134,7 +146,9 @@ function createProtocol ( packingConfig, createResponseHandler, log, - onProtocolError + onProtocolError, + hydrationHooks, + dehydrationHooks ) case 4.0: return new BoltProtocolV4x0( @@ -143,7 +157,9 @@ function createProtocol ( packingConfig, createResponseHandler, log, - onProtocolError + onProtocolError, + hydrationHooks, + dehydrationHooks ) case 4.1: return new BoltProtocolV4x1( @@ -153,6 +169,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 4.2: @@ -163,6 +181,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 4.3: @@ -173,6 +193,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 4.4: @@ -183,6 +205,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 5.0: @@ -193,6 +217,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 5.1: @@ -203,6 +229,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 5.2: @@ -213,6 +241,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 5.3: @@ -222,6 +252,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting) case 5.4: return new BoltProtocolV5x4(server, @@ -230,6 +262,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting) default: throw newError('Unknown Bolt protocol version: ' + version) diff --git a/packages/bolt-connection/src/bolt/transformer.js b/packages/bolt-connection/src/bolt/transformer.js index f7311f6c7..9b8959bf3 100644 --- a/packages/bolt-connection/src/bolt/transformer.js +++ b/packages/bolt-connection/src/bolt/transformer.js @@ -18,7 +18,7 @@ */ import { structure } from '../packstream' -import { internal } from 'neo4j-driver-core' +import { internal, isDate, isDateTime, isDuration, isInt, isLocalDateTime, isLocalTime, isPoint, isTime } from 'neo4j-driver-core' const { objectUtil } = internal @@ -30,9 +30,26 @@ export default class Transformer { /** * Constructor * @param {TypeTransformer[]} transformers The type transformers + * @param {HydatrationHooks} hydration The hydration hooks + * @param {DehydrationHooks} dehydrationHooks the DehydrationHooks */ - constructor (transformers) { + constructor (transformers, hydration, dehydrationHooks) { this._transformers = transformers + hydration = hydration || {} + this._hydrators = [ + [isDuration, hydration.Duration], + [isLocalTime, hydration.LocalTime], + [isLocalDateTime, hydration.LocalDateTime], + [isTime, hydration.Time], + [isDate, hydration.Date], + [isDateTime, hydration.DateTime], + [isInt, hydration.Integer], + [isPoint, hydration.Point] + ] + .map(([isTypeInstance, hydrate]) => ({ isTypeInstance, hydrate })) + .filter(({ hydrate }) => hydrate != null) + + this._dehydrationHooks = dehydrationHooks || [] this._transformersPerSignature = new Map(transformers.map(typeTransformer => [typeTransformer.signature, typeTransformer])) this.fromStructure = this.fromStructure.bind(this) this.toStructure = this.toStructure.bind(this) @@ -47,25 +64,43 @@ export default class Transformer { */ fromStructure (struct) { try { - if (struct instanceof structure.Structure && this._transformersPerSignature.has(struct.signature)) { - const { fromStructure } = this._transformersPerSignature.get(struct.signature) - return fromStructure(struct) + const value = this._fromStructure(struct) + const hydrator = this._hydrators.find(({ isTypeInstance }) => isTypeInstance(value)) + if (hydrator != null) { + return hydrator.hydrate(value) } - return struct + return value } catch (error) { return objectUtil.createBrokenObject(error) } } + _fromStructure (struct) { + if (struct instanceof structure.Structure && this._transformersPerSignature.has(struct.signature)) { + const { fromStructure } = this._transformersPerSignature.get(struct.signature) + return fromStructure(struct) + } + return struct + } + /** * Transform from object to structure * @param {} type The object to be transoformed in structure * @returns {|structure.Structure} The structure or the object, if any transformer was found */ toStructure (type) { - const transformer = this._transformers.find(({ isTypeInstance }) => isTypeInstance(type)) + const dehydratedType = this._dehydrateType(type) + const transformer = this._transformers.find(({ isTypeInstance }) => isTypeInstance(dehydratedType)) if (transformer !== undefined) { - return transformer.toStructure(type) + return transformer.toStructure(dehydratedType) + } + return dehydratedType + } + + _dehydrateType (type) { + const dehydation = this._dehydrationHooks.find(({ isTypeInstance }) => isTypeInstance(type)) + if (dehydation !== undefined) { + return dehydation.dehydrate(type) } return type } diff --git a/packages/bolt-connection/src/connection-provider/connection-provider-routing.js b/packages/bolt-connection/src/connection-provider/connection-provider-routing.js index 4102adf78..5a6e3c9f0 100644 --- a/packages/bolt-connection/src/connection-provider/connection-provider-routing.js +++ b/packages/bolt-connection/src/connection-provider/connection-provider-routing.js @@ -778,7 +778,7 @@ function _isFailFastError (error) { } function _isFailFastSecurityError (error) { - return error.code.startsWith('Neo.ClientError.Security.') && + return error.code != null && error.code.startsWith('Neo.ClientError.Security.') && ![ AUTHORIZATION_EXPIRED_CODE ].includes(error.code) diff --git a/packages/bolt-connection/src/connection/connection-channel.js b/packages/bolt-connection/src/connection/connection-channel.js index 29b650d35..1a7a6af93 100644 --- a/packages/bolt-connection/src/connection/connection-channel.js +++ b/packages/bolt-connection/src/connection/connection-channel.js @@ -75,7 +75,9 @@ export function createChannelConnection ( onProtocolError: conn._handleProtocolError.bind(conn), onErrorApplyTransformation: error => conn.handleAndTransformError(error, conn._address) - } + }, + hydrationHooks: config.hydrationHooks, + dehydrationHooks: config.dehydrationHooks }) const connection = new ChannelConnection( diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 82f0c76cd..8fdf142aa 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -76,6 +76,11 @@ import NotificationFilter, { notificationFilterMinimumSeverityLevel, NotificationFilterMinimumSeverityLevel } from './notification-filter' +import { + HydatrationHooks, + DehytrationHook, + DehydrationHooks +} from './mapping' import Result, { QueryResult, ResultObserver } from './result' import EagerResult from './result-eager' import ConnectionProvider from './connection-provider' @@ -265,7 +270,10 @@ export type { NotificationSeverityLevel, NotificationFilter, NotificationFilterDisabledCategory, - NotificationFilterMinimumSeverityLevel + NotificationFilterMinimumSeverityLevel, + HydatrationHooks, + DehytrationHook, + DehydrationHooks } export default forExport diff --git a/packages/core/src/mapping.ts b/packages/core/src/mapping.ts new file mode 100644 index 000000000..a5ebf1d6e --- /dev/null +++ b/packages/core/src/mapping.ts @@ -0,0 +1,60 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Integer from './integer' +import { Point } from './spatial-types' +import { DateTime, Duration, LocalDateTime, LocalTime, Time } from './temporal-types' + +/** + * Defines methods for hydrate and dehydrate objects + * + * @interface + */ +export class HydatrationHooks { + public readonly Duration?: (duration: Duration) => V + public readonly LocalTime?: (localTime: LocalTime) => V + public readonly LocalDateTime?: (localDateTime: LocalDateTime) => V + public readonly Time?: (time: Time) => V + public readonly Date?: (date: Date) => V + public readonly DateTime?: (dateTime: DateTime) => V + public readonly Integer?: (integer: Integer) => V + public readonly Point?: (point: Point) => V + + constructor () { + throw new Error('This class should not be instantiated') + } +} + +export class DehytrationHook { + constructor () { + throw new Error('This class should not be instantiated') + } + + public isTypeInstance (value: unknown): value is I { + throw new Error('Not Implemented') + } + + public dehydrate (value: I): O { + throw new Error('Not Implemented') + } +} + +export type DehydrationHooks = DehytrationHook[] +// bytes |> unpack |> fromStructure |> hydrate +// type |> dehydrate |> toStructure |> pack diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 6989dbdd9..52bdf46ec 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -17,6 +17,7 @@ * limitations under the License. */ +import { DehydrationHooks, HydatrationHooks } from './mapping' import NotificationFilter from './notification-filter' /** @@ -81,6 +82,8 @@ export class Config { resolver?: (address: string) => string[] | Promise userAgent?: string telemetryDisabled?: boolean + hydrationHooks?: HydatrationHooks + dehydrationHooks?: DehydrationHooks /** * @constructor diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v1.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v1.js index 149723d2d..faa89ab67 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v1.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v1.js @@ -74,7 +74,9 @@ export default class BoltProtocol { { disableLosslessIntegers, useBigInt } = {}, createResponseHandler = () => null, log, - onProtocolError + onProtocolError, + hydrationHooks, + dehydrationHooks ) { this._server = server || {} this._chunker = chunker @@ -86,11 +88,13 @@ export default class BoltProtocol { this._fatalError = null this._lastMessageSignature = null this._config = { disableLosslessIntegers, useBigInt } + this._hydrationHooks = hydrationHooks + this._dehydrationHooks = dehydrationHooks } get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v2.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v2.js index fb627f399..e2b682448 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v2.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v2.js @@ -37,7 +37,7 @@ export default class BoltProtocol extends BoltProtocolV1 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v3.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v3.js index 2025d4c21..1d309095a 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v3.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v3.js @@ -48,7 +48,7 @@ export default class BoltProtocol extends BoltProtocolV2 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x0.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x0.js index 053d7d948..6f7ccacc9 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x0.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x0.js @@ -46,7 +46,7 @@ export default class BoltProtocol extends BoltProtocolV3 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x1.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x1.js index 28e4d25db..7b66c8085 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x1.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x1.js @@ -49,6 +49,8 @@ export default class BoltProtocol extends BoltProtocolV4 { createResponseHandler = () => null, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) { super( @@ -57,7 +59,9 @@ export default class BoltProtocol extends BoltProtocolV4 { packstreamConfig, createResponseHandler, log, - onProtocolError + onProtocolError, + hydrationHooks, + dehydrationHooks ) this._serversideRouting = serversideRouting } @@ -68,7 +72,7 @@ export default class BoltProtocol extends BoltProtocolV4 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x2.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x2.js index 176de4db0..9d7b5a2c5 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x2.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x2.js @@ -34,7 +34,7 @@ export default class BoltProtocol extends BoltProtocolV41 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x3.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x3.js index 15aded3c1..9cadefec8 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x3.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x3.js @@ -39,7 +39,7 @@ export default class BoltProtocol extends BoltProtocolV42 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } @@ -126,6 +126,6 @@ export default class BoltProtocol extends BoltProtocolV42 { this._transformer = new Transformer(Object.values({ ...transformersFactories, ...utcTransformersFactories - }).map(create => create(this._config, this._log))) + }).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x4.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x4.js index 1947a105e..2fc485bc3 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x4.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x4.js @@ -39,7 +39,7 @@ export default class BoltProtocol extends BoltProtocolV43 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } @@ -178,6 +178,6 @@ export default class BoltProtocol extends BoltProtocolV43 { this._transformer = new Transformer(Object.values({ ...transformersFactories, ...utcTransformersFactories - }).map(create => create(this._config, this._log))) + }).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x0.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x0.js index 5a252a5fc..9fbd01d2c 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x0.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x0.js @@ -37,7 +37,7 @@ export default class BoltProtocol extends BoltProtocolV44 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js index 115b437ed..6d2f9c464 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js @@ -37,7 +37,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x2.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x2.js index 7add56928..0dd950066 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x2.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x2.js @@ -36,7 +36,7 @@ export default class BoltProtocol extends BoltProtocolV5x1 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x3.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x3.js index f02b86caf..875bcbbe0 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x3.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x3.js @@ -36,7 +36,7 @@ export default class BoltProtocol extends BoltProtocolV5x2 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x4.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x4.js index b1b27cf0b..8a50c1159 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x4.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x4.js @@ -36,7 +36,7 @@ export default class BoltProtocol extends BoltProtocolV5x3 { get transformer () { if (this._transformer === undefined) { - this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)), this._hydrationHooks, this._dehydrationHooks) } return this._transformer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/create.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/create.js index 9d81145c2..c3a07e317 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/create.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/create.js @@ -48,6 +48,8 @@ import ResponseHandler from './response-handler.js' * @param {boolean} config.disableLosslessIntegers Disable the lossless integers * @param {boolean} packstreamConfig.useBigInt if this connection should convert all received integers to native BigInt numbers. * @param {boolean} config.serversideRouting It's using server side routing + * @param {HydatrationHooks} config.hydrationHooks Hydatration hooks used to map types + * @param {DehydrationHooks} config.dehydrationHooks Hydatration hooks used to map types */ export default function create ({ version, @@ -59,7 +61,9 @@ export default function create ({ serversideRouting, server, // server info log, - observer + observer, + hydrationHooks, + dehydrationHooks } = {}) { const createResponseHandler = protocol => { const responseHandler = new ResponseHandler({ @@ -94,7 +98,9 @@ export default function create ({ serversideRouting, createResponseHandler, observer.onProtocolError.bind(observer), - log + log, + hydrationHooks, + dehydrationHooks ) } @@ -106,7 +112,9 @@ function createProtocol ( serversideRouting, createResponseHandler, onProtocolError, - log + log, + hydrationHooks, + dehydrationHooks ) { switch (version) { case 1: @@ -116,7 +124,9 @@ function createProtocol ( packingConfig, createResponseHandler, log, - onProtocolError + onProtocolError, + hydrationHooks, + dehydrationHooks ) case 2: return new BoltProtocolV2( @@ -125,7 +135,9 @@ function createProtocol ( packingConfig, createResponseHandler, log, - onProtocolError + onProtocolError, + hydrationHooks, + dehydrationHooks ) case 3: return new BoltProtocolV3( @@ -134,7 +146,9 @@ function createProtocol ( packingConfig, createResponseHandler, log, - onProtocolError + onProtocolError, + hydrationHooks, + dehydrationHooks ) case 4.0: return new BoltProtocolV4x0( @@ -143,7 +157,9 @@ function createProtocol ( packingConfig, createResponseHandler, log, - onProtocolError + onProtocolError, + hydrationHooks, + dehydrationHooks ) case 4.1: return new BoltProtocolV4x1( @@ -153,6 +169,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 4.2: @@ -163,6 +181,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 4.3: @@ -173,6 +193,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 4.4: @@ -183,6 +205,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 5.0: @@ -193,6 +217,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 5.1: @@ -203,6 +229,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 5.2: @@ -213,6 +241,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting ) case 5.3: @@ -222,6 +252,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting) case 5.4: return new BoltProtocolV5x4(server, @@ -230,6 +262,8 @@ function createProtocol ( createResponseHandler, log, onProtocolError, + hydrationHooks, + dehydrationHooks, serversideRouting) default: throw newError('Unknown Bolt protocol version: ' + version) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/transformer.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/transformer.js index 4424e6d82..d48ab9756 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/transformer.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/transformer.js @@ -18,7 +18,7 @@ */ import { structure } from '../packstream/index.js' -import { internal } from '../../core/index.ts' +import { internal, isDate, isDateTime, isDuration, isInt, isLocalDateTime, isLocalTime, isPoint, isTime } from '../../core/index.ts' const { objectUtil } = internal @@ -30,9 +30,26 @@ export default class Transformer { /** * Constructor * @param {TypeTransformer[]} transformers The type transformers + * @param {HydatrationHooks} hydration The hydration hooks + * @param {DehydrationHooks} dehydrationHooks the DehydrationHooks */ - constructor (transformers) { + constructor (transformers, hydration, dehydrationHooks) { this._transformers = transformers + hydration = hydration || {} + this._hydrators = [ + [isDuration, hydration.Duration], + [isLocalTime, hydration.LocalTime], + [isLocalDateTime, hydration.LocalDateTime], + [isTime, hydration.Time], + [isDate, hydration.Date], + [isDateTime, hydration.DateTime], + [isInt, hydration.Integer], + [isPoint, hydration.Point] + ] + .map(([isTypeInstance, hydrate]) => ({ isTypeInstance, hydrate })) + .filter(({ hydrate }) => hydrate != null) + + this._dehydrationHooks = dehydrationHooks || [] this._transformersPerSignature = new Map(transformers.map(typeTransformer => [typeTransformer.signature, typeTransformer])) this.fromStructure = this.fromStructure.bind(this) this.toStructure = this.toStructure.bind(this) @@ -47,25 +64,43 @@ export default class Transformer { */ fromStructure (struct) { try { - if (struct instanceof structure.Structure && this._transformersPerSignature.has(struct.signature)) { - const { fromStructure } = this._transformersPerSignature.get(struct.signature) - return fromStructure(struct) + const value = this._fromStructure(struct) + const hydrator = this._hydrators.find(({ isTypeInstance }) => isTypeInstance(value)) + if (hydrator != null) { + return hydrator.hydrate(value) } - return struct + return value } catch (error) { return objectUtil.createBrokenObject(error) } } + _fromStructure (struct) { + if (struct instanceof structure.Structure && this._transformersPerSignature.has(struct.signature)) { + const { fromStructure } = this._transformersPerSignature.get(struct.signature) + return fromStructure(struct) + } + return struct + } + /** * Transform from object to structure * @param {} type The object to be transoformed in structure * @returns {|structure.Structure} The structure or the object, if any transformer was found */ toStructure (type) { - const transformer = this._transformers.find(({ isTypeInstance }) => isTypeInstance(type)) + const dehydratedType = this._dehydrateType(type) + const transformer = this._transformers.find(({ isTypeInstance }) => isTypeInstance(dehydratedType)) if (transformer !== undefined) { - return transformer.toStructure(type) + return transformer.toStructure(dehydratedType) + } + return dehydratedType + } + + _dehydrateType (type) { + const dehydation = this._dehydrationHooks.find(({ isTypeInstance }) => isTypeInstance(type)) + if (dehydation !== undefined) { + return dehydation.dehydrate(type) } return type } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-routing.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-routing.js index 79a23b81a..75311150a 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-routing.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-routing.js @@ -778,7 +778,7 @@ function _isFailFastError (error) { } function _isFailFastSecurityError (error) { - return error.code.startsWith('Neo.ClientError.Security.') && + return error.code != null && error.code.startsWith('Neo.ClientError.Security.') && ![ AUTHORIZATION_EXPIRED_CODE ].includes(error.code) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js index 8e55080cc..88c40ab79 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js @@ -75,7 +75,9 @@ export function createChannelConnection ( onProtocolError: conn._handleProtocolError.bind(conn), onErrorApplyTransformation: error => conn.handleAndTransformError(error, conn._address) - } + }, + hydrationHooks: config.hydrationHooks, + dehydrationHooks: config.dehydrationHooks }) const connection = new ChannelConnection( diff --git a/packages/neo4j-driver-deno/lib/core/index.ts b/packages/neo4j-driver-deno/lib/core/index.ts index 0242df6c3..2e1d854b0 100644 --- a/packages/neo4j-driver-deno/lib/core/index.ts +++ b/packages/neo4j-driver-deno/lib/core/index.ts @@ -76,6 +76,11 @@ import NotificationFilter, { notificationFilterMinimumSeverityLevel, NotificationFilterMinimumSeverityLevel } from './notification-filter.ts' +import { + HydatrationHooks, + DehytrationHook, + DehydrationHooks +} from './mapping.ts' import Result, { QueryResult, ResultObserver } from './result.ts' import EagerResult from './result-eager.ts' import ConnectionProvider from './connection-provider.ts' @@ -265,7 +270,10 @@ export type { NotificationSeverityLevel, NotificationFilter, NotificationFilterDisabledCategory, - NotificationFilterMinimumSeverityLevel + NotificationFilterMinimumSeverityLevel, + HydatrationHooks, + DehytrationHook, + DehydrationHooks } export default forExport diff --git a/packages/neo4j-driver-deno/lib/core/mapping.ts b/packages/neo4j-driver-deno/lib/core/mapping.ts new file mode 100644 index 000000000..40ed13682 --- /dev/null +++ b/packages/neo4j-driver-deno/lib/core/mapping.ts @@ -0,0 +1,60 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Integer from './integer.ts' +import { Point } from './spatial-types.ts' +import { DateTime, Duration, LocalDateTime, LocalTime, Time } from './temporal-types.ts' + +/** + * Defines methods for hydrate and dehydrate objects + * + * @interface + */ +export class HydatrationHooks { + public readonly Duration?: (duration: Duration) => V + public readonly LocalTime?: (localTime: LocalTime) => V + public readonly LocalDateTime?: (localDateTime: LocalDateTime) => V + public readonly Time?: (time: Time) => V + public readonly Date?: (date: Date) => V + public readonly DateTime?: (dateTime: DateTime) => V + public readonly Integer?: (integer: Integer) => V + public readonly Point?: (point: Point) => V + + constructor () { + throw new Error('This class should not be instantiated') + } +} + +export class DehytrationHook { + constructor () { + throw new Error('This class should not be instantiated') + } + + public isTypeInstance (value: unknown): value is I { + throw new Error('Not Implemented') + } + + public dehydrate (value: I): O { + throw new Error('Not Implemented') + } +} + +export type DehydrationHooks = DehytrationHook[] +// bytes |> unpack |> fromStructure |> hydrate +// type |> dehydrate |> toStructure |> pack diff --git a/packages/neo4j-driver-deno/lib/core/types.ts b/packages/neo4j-driver-deno/lib/core/types.ts index 8c88aca5f..5c14f955f 100644 --- a/packages/neo4j-driver-deno/lib/core/types.ts +++ b/packages/neo4j-driver-deno/lib/core/types.ts @@ -17,6 +17,7 @@ * limitations under the License. */ +import { DehydrationHooks, HydatrationHooks } from './mapping.ts' import NotificationFilter from './notification-filter.ts' /** @@ -81,6 +82,8 @@ export class Config { resolver?: (address: string) => string[] | Promise userAgent?: string telemetryDisabled?: boolean + hydrationHooks?: HydatrationHooks + dehydrationHooks?: DehydrationHooks /** * @constructor diff --git a/packages/neo4j-driver-deno/lib/mod.ts b/packages/neo4j-driver-deno/lib/mod.ts index d32a84300..6ef9df191 100644 --- a/packages/neo4j-driver-deno/lib/mod.ts +++ b/packages/neo4j-driver-deno/lib/mod.ts @@ -99,7 +99,10 @@ import { Transaction, TransactionPromise, types as coreTypes, - UnboundRelationship + UnboundRelationship, + DehytrationHook, + DehydrationHooks, + HydatrationHooks } from './core/index.ts' // @deno-types=./bolt-connection/types/index.d.ts import { DirectConnectionProvider, RoutingConnectionProvider } from './bolt-connection/index.js' @@ -518,6 +521,9 @@ export type { NotificationSeverityLevel, NotificationFilter, NotificationFilterDisabledCategory, - NotificationFilterMinimumSeverityLevel + NotificationFilterMinimumSeverityLevel, + HydatrationHooks, + DehytrationHook, + DehydrationHooks } export default forExport diff --git a/packages/neo4j-driver-lite/src/index.ts b/packages/neo4j-driver-lite/src/index.ts index 994491996..052bbde38 100644 --- a/packages/neo4j-driver-lite/src/index.ts +++ b/packages/neo4j-driver-lite/src/index.ts @@ -99,7 +99,10 @@ import { Transaction, TransactionPromise, types as coreTypes, - UnboundRelationship + UnboundRelationship, + DehytrationHook, + DehydrationHooks, + HydatrationHooks } from 'neo4j-driver-core' import { DirectConnectionProvider, RoutingConnectionProvider } from 'neo4j-driver-bolt-connection' @@ -517,6 +520,9 @@ export type { NotificationSeverityLevel, NotificationFilter, NotificationFilterDisabledCategory, - NotificationFilterMinimumSeverityLevel + NotificationFilterMinimumSeverityLevel, + HydatrationHooks, + DehytrationHook, + DehydrationHooks } export default forExport diff --git a/packages/neo4j-driver/types/index.d.ts b/packages/neo4j-driver/types/index.d.ts index 490ba1cfb..89ec1e75f 100644 --- a/packages/neo4j-driver/types/index.d.ts +++ b/packages/neo4j-driver/types/index.d.ts @@ -89,7 +89,10 @@ import { notificationFilterMinimumSeverityLevel, AuthTokenManager, AuthTokenAndExpiration, - types as coreTypes + types as coreTypes, + DehytrationHook, + DehydrationHooks, + HydatrationHooks } from 'neo4j-driver-core' import { AuthToken, @@ -378,7 +381,10 @@ export type { NotificationFilterDisabledCategory, NotificationFilterMinimumSeverityLevel, AuthTokenManager, - AuthTokenAndExpiration + AuthTokenAndExpiration, + DehytrationHook, + DehydrationHooks, + HydatrationHooks } export default forExport diff --git a/packages/testkit-backend/src/cypher-native-binders.js b/packages/testkit-backend/src/cypher-native-binders.js index ff9498e7e..47920f8a0 100644 --- a/packages/testkit-backend/src/cypher-native-binders.js +++ b/packages/testkit-backend/src/cypher-native-binders.js @@ -1,14 +1,10 @@ - export default function CypherNativeBinders (neo4j) { function valueResponse (name, value) { - return { name: name, data: { value: value } } + return { name, data: { value } } } function objectToCypher (obj) { return objectMapper(obj, nativeToCypher) } - function objectToNative (obj) { - return objectMapper(obj, cypherToNative) - } function objectMemberBitIntToNumber (obj, recursive = false) { return objectMapper(obj, val => { @@ -171,25 +167,52 @@ export default function CypherNativeBinders (neo4j) { return { name, data: map } } - function cypherToNative (c) { - const { - name, - data - } = c - switch (name) { - case 'CypherString': - return data.value - case 'CypherInt': - return BigInt(data.value) - case 'CypherFloat': - return data.value - case 'CypherNull': - return data.value - case 'CypherBool': - return data.value - case 'CypherList': - return data.value.map(cypherToNative) - case 'CypherDateTime': + function parseAuthToken (authToken) { + switch (authToken.scheme) { + case 'basic': + return neo4j.auth.basic( + authToken.principal, + authToken.credentials, + authToken.realm + ) + case 'kerberos': + return neo4j.auth.kerberos(authToken.credentials) + case 'bearer': + return neo4j.auth.bearer(authToken.credentials) + default: + return neo4j.auth.custom( + authToken.principal, + authToken.credentials, + authToken.realm, + authToken.scheme, + authToken.parameters + ) + } + } + + function isTestkitTypeWithName (...names) { + return value => value != null && typeof value === 'object' && names.includes(value.name) + } + + this.dehydrationHooks = [ + { + isTypeInstance: isTestkitTypeWithName( + 'CypherString', + 'CypherFloat', + 'CypherNull', + 'CypherBool', + 'CypherList', + 'CypherMap' + ), + dehydrate: ({ data: { value } }) => value + }, + { + isTypeInstance: isTestkitTypeWithName('CypherInt'), + dehydrate: ({ data: { value } }) => BigInt(value) + }, + { + isTypeInstance: isTestkitTypeWithName('CypherDateTime'), + dehydrate: ({ data }) => { if (data.utc_offset_s == null && data.timezone_id == null) { return new neo4j.LocalDateTime( data.year, @@ -212,7 +235,11 @@ export default function CypherNativeBinders (neo4j) { data.utc_offset_s, data.timezone_id ) - case 'CypherTime': + } + }, + { + isTypeInstance: isTestkitTypeWithName('CypherTime'), + dehydrate: ({ data }) => { if (data.utc_offset_s == null) { return new neo4j.LocalTime( data.hour, @@ -228,59 +255,43 @@ export default function CypherNativeBinders (neo4j) { data.nanosecond, data.utc_offset_s ) - case 'CypherDate': - return new neo4j.Date( - data.year, - data.month, - data.day - ) - case 'CypherDuration': - return new neo4j.Duration( - data.months, - data.days, - data.seconds, - data.nanoseconds - ) - case 'CypherMap': - return Object.entries(data.value).reduce((acc, [key, val]) => { - acc[key] = cypherToNative(val) - return acc - }, {}) + } + }, + { + isTypeInstance: isTestkitTypeWithName('CypherDate'), + dehydrate: ({ data }) => new neo4j.Date( + data.year, + data.month, + data.day + ) + }, + { + isTypeInstance: isTestkitTypeWithName('CypherDuration'), + dehydrate: ({ data }) => new neo4j.Duration( + data.months, + data.days, + data.seconds, + data.nanoseconds + ) + }, + { + isTypeInstance: value => value != null && typeof value === 'object' && typeof value.name === 'string', + dehydrate: (value) => { + console.log(`Type ${value.name} is not handle by dehydrationHooks`, value) + const err = 'Unable to convert ' + value + ' to native type' + console.log(err) + throw Error(err) + } } - console.log(`Type ${name} is not handle by cypherToNative`, c) - const err = 'Unable to convert ' + c + ' to native type' - console.log(err) - throw Error(err) - } + ] + + this.hydrationHooks = { - function parseAuthToken (authToken) { - switch (authToken.scheme) { - case 'basic': - return neo4j.auth.basic( - authToken.principal, - authToken.credentials, - authToken.realm - ) - case 'kerberos': - return neo4j.auth.kerberos(authToken.credentials) - case 'bearer': - return neo4j.auth.bearer(authToken.credentials) - default: - return neo4j.auth.custom( - authToken.principal, - authToken.credentials, - authToken.realm, - authToken.scheme, - authToken.parameters - ) - } } this.valueResponse = valueResponse this.objectToCypher = objectToCypher - this.objectToNative = objectToNative this.objectMemberBitIntToNumber = objectMemberBitIntToNumber this.nativeToCypher = nativeToCypher - this.cypherToNative = cypherToNative this.parseAuthToken = parseAuthToken } diff --git a/packages/testkit-backend/src/request-handlers-rx.js b/packages/testkit-backend/src/request-handlers-rx.js index c093932be..9fda8e614 100644 --- a/packages/testkit-backend/src/request-handlers-rx.js +++ b/packages/testkit-backend/src/request-handlers-rx.js @@ -98,8 +98,8 @@ export function SessionClose (_, context, data, wire) { export function SessionRun (_, context, data, wire) { const { sessionId, cypher, timeout } = data const session = context.getSession(sessionId) - const params = context.binder.objectToNative(data.params) - const metadata = context.binder.objectToNative(data.txMeta) + const params = data.param + const metadata = data.txMeta let rxResult try { @@ -138,7 +138,7 @@ export function ResultConsume (_, context, data, wire) { export function SessionBeginTransaction (_, context, data, wire) { const { sessionId, timeout } = data const session = context.getSession(sessionId) - const metadata = context.binder.objectToNative(data.txMeta) + const metadata = data.txMeta try { return session.beginTransaction({ metadata, timeout }) @@ -159,11 +159,6 @@ export function SessionBeginTransaction (_, context, data, wire) { export function TransactionRun (_, context, data, wire) { const { txId, cypher, params } = data const tx = context.getTx(txId) - if (params) { - for (const [key, value] of Object.entries(params)) { - params[key] = context.binder.cypherToNative(value) - } - } tx.tx.run(cypher, params) ._toObservable() @@ -215,7 +210,7 @@ export function TransactionClose (_, context, data, wire) { export function SessionReadTransaction (_, context, data, wire) { const { sessionId } = data const session = context.getSession(sessionId) - const metadata = context.binder.objectToNative(data.txMeta) + const metadata = data.txMeta try { return session.executeRead(tx => { @@ -235,7 +230,7 @@ export function SessionReadTransaction (_, context, data, wire) { export function SessionWriteTransaction (_, context, data, wire) { const { sessionId } = data const session = context.getSession(sessionId) - const metadata = context.binder.objectToNative(data.txMeta) + const metadata = data.txMeta try { return session.executeWrite(tx => { diff --git a/packages/testkit-backend/src/request-handlers.js b/packages/testkit-backend/src/request-handlers.js index bdb98bc2b..6fdb207b6 100644 --- a/packages/testkit-backend/src/request-handlers.js +++ b/packages/testkit-backend/src/request-handlers.js @@ -40,6 +40,7 @@ export function NewDriver ({ neo4j }, context, data, wire) { userAgent, resolver, useBigInt: true, + dehydrationHooks: context.binder.dehydrationHooks, logging: neo4j.logging.console(context.logLevel || context.environmentLogLevel) } if ('encrypted' in data) { @@ -165,8 +166,8 @@ export function SessionClose (_, context, data, wire) { export function SessionRun (_, context, data, wire) { const { sessionId, cypher, timeout } = data const session = context.getSession(sessionId) - const params = context.binder.objectToNative(data.params) - const metadata = context.binder.objectToNative(data.txMeta) + const params = data.params + const metadata = data.txMeta let result try { @@ -241,7 +242,7 @@ export function ResultList (_, context, data, wire) { export function SessionReadTransaction (_, context, data, wire) { const { sessionId } = data const session = context.getSession(sessionId) - const metadata = context.binder.objectToNative(data.txMeta) + const metadata = data.txMeta return session .executeRead( @@ -258,11 +259,6 @@ export function SessionReadTransaction (_, context, data, wire) { export function TransactionRun (_, context, data, wire) { const { txId, cypher, params } = data const tx = context.getTx(txId) - if (params) { - for (const [key, value] of Object.entries(params)) { - params[key] = context.binder.cypherToNative(value) - } - } const result = tx.tx.run(cypher, params) const id = context.addResult(result) @@ -287,7 +283,7 @@ export function RetryableNegative (_, context, data, wire) { export function SessionBeginTransaction (_, context, data, wire) { const { sessionId, timeout } = data const session = context.getSession(sessionId) - const metadata = context.binder.objectToNative(data.txMeta) + const metadata = data.txMeta try { return session.beginTransaction({ metadata, timeout }) @@ -341,7 +337,7 @@ export function SessionLastBookmarks (_, context, data, wire) { export function SessionWriteTransaction (_, context, data, wire) { const { sessionId } = data const session = context.getSession(sessionId) - const metadata = context.binder.objectToNative(data.txMeta) + const metadata = data.txMeta return session .executeWrite( @@ -654,11 +650,6 @@ export function ForcedRoutingTableUpdate (_, context, { driverId, database, book export function ExecuteQuery ({ neo4j }, context, { driverId, cypher, params, config }, wire) { const driver = context.getDriver(driverId) - if (params) { - for (const [key, value] of Object.entries(params)) { - params[key] = context.binder.cypherToNative(value) - } - } const configuration = {} if (config) { diff --git a/packages/testkit-backend/src/summary-binder.js b/packages/testkit-backend/src/summary-binder.js index 62af6c1d0..169d8ac9a 100644 --- a/packages/testkit-backend/src/summary-binder.js +++ b/packages/testkit-backend/src/summary-binder.js @@ -53,7 +53,7 @@ export function nativeToTestkitSummary (summary, binder) { database: summary.database.name, query: { text: summary.query.text, - parameters: binder.objectToCypher(summary.query.parameters) + parameters: summary.query.parameters }, serverInfo: { agent: summary.server.agent,