From 07bf7b102f82489d171544e242639e5c3eb0c9a8 Mon Sep 17 00:00:00 2001 From: Peter van Vliet Date: Fri, 7 Feb 2025 09:36:44 +0100 Subject: [PATCH] #614: implemented buffer serializer --- .../serialization/src/SerializerBuilder.ts | 2 + .../src/serializers/BufferSerializer.ts | 39 +++++++++ .../serializers/errors/InvalidBufferString.ts | 8 ++ .../src/types/serialized/SerializedBuffer.ts | 9 +++ .../test/serializers/BufferSerializer.spec.ts | 80 +++++++++++++++++++ .../serializers/fixtures/buffers.fixture.ts | 26 ++++++ .../test/serializers/fixtures/index.ts | 1 + 7 files changed, 165 insertions(+) create mode 100644 packages/serialization/src/serializers/BufferSerializer.ts create mode 100644 packages/serialization/src/serializers/errors/InvalidBufferString.ts create mode 100644 packages/serialization/src/types/serialized/SerializedBuffer.ts create mode 100644 packages/serialization/test/serializers/BufferSerializer.spec.ts create mode 100644 packages/serialization/test/serializers/fixtures/buffers.fixture.ts diff --git a/packages/serialization/src/SerializerBuilder.ts b/packages/serialization/src/SerializerBuilder.ts index b033fd77..44fc1c7a 100644 --- a/packages/serialization/src/SerializerBuilder.ts +++ b/packages/serialization/src/SerializerBuilder.ts @@ -3,6 +3,7 @@ import Serializer from './Serializer'; import ClassResolver from './interfaces/ClassResolver'; import ArraySerializer from './serializers/ArraySerializer'; import BigIntSerializer from './serializers/BigIntSerializer'; +import BufferSerializer from './serializers/BufferSerializer'; import ClassSerializer from './serializers/ClassSerializer'; import DateSerializer from './serializers/DateSerializer'; import ErrorSerializer from './serializers/ErrorSerializer'; @@ -37,6 +38,7 @@ export default class SerializerBuilder serializer.addSerializer(new MapSerializer()); serializer.addSerializer(new ArraySerializer()); serializer.addSerializer(new TypedArraySerializer()); + serializer.addSerializer(new BufferSerializer()); return serializer; } diff --git a/packages/serialization/src/serializers/BufferSerializer.ts b/packages/serialization/src/serializers/BufferSerializer.ts new file mode 100644 index 00000000..a2419804 --- /dev/null +++ b/packages/serialization/src/serializers/BufferSerializer.ts @@ -0,0 +1,39 @@ + +import ValueSerializer from '../ValueSerializer'; +import SerializedBuffer from '../types/serialized/SerializedBuffer'; +import InvalidBufferString from './errors/InvalidBufferString'; + +export default class BufferSerializer extends ValueSerializer +{ + canSerialize(value: unknown): boolean + { + return value instanceof Buffer; + } + + canDeserialize(value: unknown): boolean + { + const buffer = value as SerializedBuffer; + + return buffer instanceof Object + && buffer.serialized === true + && buffer.name === 'Buffer' + && typeof buffer.base64 === 'string'; + } + + async serialize(buffer: Buffer): Promise + { + return { serialized: true, name: 'Buffer', base64: buffer.toString('base64') }; + } + + async deserialize(object: SerializedBuffer): Promise + { + try + { + return Buffer.from(object.base64, 'base64'); + } + catch + { + throw new InvalidBufferString(object.base64); + } + } +} diff --git a/packages/serialization/src/serializers/errors/InvalidBufferString.ts b/packages/serialization/src/serializers/errors/InvalidBufferString.ts new file mode 100644 index 00000000..c1e51b07 --- /dev/null +++ b/packages/serialization/src/serializers/errors/InvalidBufferString.ts @@ -0,0 +1,8 @@ + +export default class InvalidBufferString extends Error +{ + constructor(bufferString: string) + { + super(`Invalid Buffer string '${bufferString}'`); + } +} diff --git a/packages/serialization/src/types/serialized/SerializedBuffer.ts b/packages/serialization/src/types/serialized/SerializedBuffer.ts new file mode 100644 index 00000000..e75a611f --- /dev/null +++ b/packages/serialization/src/types/serialized/SerializedBuffer.ts @@ -0,0 +1,9 @@ + +import Serialized from '../Serialized'; + +type SerializedBuffer = Serialized & +{ + base64: string +}; + +export default SerializedBuffer; diff --git a/packages/serialization/test/serializers/BufferSerializer.spec.ts b/packages/serialization/test/serializers/BufferSerializer.spec.ts new file mode 100644 index 00000000..47c11226 --- /dev/null +++ b/packages/serialization/test/serializers/BufferSerializer.spec.ts @@ -0,0 +1,80 @@ + +import { describe, expect, it } from 'vitest'; + +import BufferSerializer from '../../src/serializers/BufferSerializer'; + +import { BUFFERS } from './fixtures'; + +const serializer = new BufferSerializer(); + +describe('serializers/BufferSerializer', () => +{ + describe('.canSerialize(value)', () => + { + it('should tell it can serialize a buffer', () => + { + const supportsBuffer = serializer.canSerialize(BUFFERS.VALID); + + expect(supportsBuffer).toBeTruthy(); + }); + + it('should tell it cannot serialize others', () => + { + const supportsNonObject = serializer.canSerialize(BUFFERS.NON_OBJECT); + const supportsNonBuffer = serializer.canSerialize(BUFFERS.NON_BUFFER); + + expect(supportsNonObject).toBeFalsy(); + expect(supportsNonBuffer).toBeFalsy(); + }); + }); + + describe('.canDeserialize(value)', () => + { + it('should tell it can deserialize a buffer', () => + { + const supportsBuffer = serializer.canDeserialize(BUFFERS.VALID_SERIALIZED); + + expect(supportsBuffer).toBeTruthy(); + }); + + it('should tell it cannot deserialize others', () => + { + const supportsNonObject = serializer.canDeserialize(BUFFERS.NON_OBJECT); + const supportsNotSerialized = serializer.canDeserialize(BUFFERS.NOT_SERIALIZED); + const supportsInvalidName = serializer.canDeserialize(BUFFERS.INVALID_NAME); + const supportsInvalidBufferValue = serializer.canDeserialize(BUFFERS.INVALID_BUFFER_VALUE); + + expect(supportsNonObject).toBeFalsy(); + expect(supportsNotSerialized).toBeFalsy(); + expect(supportsInvalidName).toBeFalsy(); + expect(supportsInvalidBufferValue).toBeFalsy(); + }); + }); + + describe('.serialize(buffer)', () => + { + it('should serialize a buffer', async () => + { + const resultValidBuffer = await serializer.serialize(BUFFERS.VALID); + + expect(resultValidBuffer).toStrictEqual(BUFFERS.VALID_SERIALIZED); + }); + }); + + describe('.deserialize(object)', () => + { + it('should deserialize a buffer', async () => + { + const resultValidBuffer = await serializer.deserialize(BUFFERS.VALID_SERIALIZED); + + expect(resultValidBuffer).toStrictEqual(BUFFERS.VALID); + }); + + it('should ignore invalid characters in buffer string', async () => + { + const resultInvalidBuffer = await serializer.deserialize(BUFFERS.INVALID_BUFFER_STRING); + + expect(resultInvalidBuffer).toStrictEqual(BUFFERS.VALID); + }); + }); +}); diff --git a/packages/serialization/test/serializers/fixtures/buffers.fixture.ts b/packages/serialization/test/serializers/fixtures/buffers.fixture.ts new file mode 100644 index 00000000..2e83fa30 --- /dev/null +++ b/packages/serialization/test/serializers/fixtures/buffers.fixture.ts @@ -0,0 +1,26 @@ + +const VALID_BASE64_DATA = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABEElEQVR42mL8//8/AyUYIIgB'; +const INVALID_BASE64_DATA = `##${VALID_BASE64_DATA}??`; + +const validBuffer = Buffer.from(VALID_BASE64_DATA, 'base64'); + +const serializedValidBuffer = { serialized: true, name: 'Buffer', base64: VALID_BASE64_DATA }; + +const nonObject = 42; +const nonBuffer = new Map(); +const notSerialized = { name: 'Buffer', base64: '' }; +const invalidName = { serialized: true, name: 'Map', base64: VALID_BASE64_DATA }; +const invalidBufferValue = { serialized: true, name: 'Buffer', base64: true }; +const invalidBufferString = { serialized: true, name: 'Buffer', base64: INVALID_BASE64_DATA }; + +export const BUFFERS = +{ + VALID: validBuffer, + VALID_SERIALIZED: serializedValidBuffer, + NON_OBJECT: nonObject, + NON_BUFFER: nonBuffer, + NOT_SERIALIZED: notSerialized, + INVALID_NAME: invalidName, + INVALID_BUFFER_VALUE: invalidBufferValue, + INVALID_BUFFER_STRING: invalidBufferString +}; diff --git a/packages/serialization/test/serializers/fixtures/index.ts b/packages/serialization/test/serializers/fixtures/index.ts index 31f53065..d5f30f29 100644 --- a/packages/serialization/test/serializers/fixtures/index.ts +++ b/packages/serialization/test/serializers/fixtures/index.ts @@ -1,6 +1,7 @@ export * from './arrays.fixture'; export * from './bigIntegers.fixture'; +export * from './buffers.fixture'; export * from './classes.fixture'; export * from './classResolver.fixture'; export * from './dates.fixture';