From e53a73215f9e610afeafd6bdea86a9f4133805cf Mon Sep 17 00:00:00 2001 From: Gus Narea Date: Sun, 30 Jul 2023 12:30:39 +0100 Subject: [PATCH] refactor: Use AJV compilers (#209) Needed for #205 --- src/awala/routes/awala.routes.spec.ts | 7 +--- src/awala/routes/awala.routes.ts | 60 +++++++++++---------------- src/schemas/awala.schema.ts | 9 +++- src/utilities/ajv.ts | 7 ++++ src/utilities/validateMessage.spec.ts | 43 ------------------- src/utilities/validateMessage.ts | 18 -------- 6 files changed, 39 insertions(+), 105 deletions(-) create mode 100644 src/utilities/ajv.ts delete mode 100644 src/utilities/validateMessage.spec.ts delete mode 100644 src/utilities/validateMessage.ts diff --git a/src/awala/routes/awala.routes.spec.ts b/src/awala/routes/awala.routes.spec.ts index afcdb857..412272f8 100644 --- a/src/awala/routes/awala.routes.spec.ts +++ b/src/awala/routes/awala.routes.spec.ts @@ -177,7 +177,6 @@ describe('Awala routes', () => { expect(logs).toContainEqual( partialPinoLog('info', 'Refused invalid member bundle request', { publicKeyId: MEMBER_PUBLIC_KEY_MONGO_ID, - reason: expect.stringContaining('memberBundleStartDate'), }), ); }); @@ -214,7 +213,6 @@ describe('Awala routes', () => { expect(logs).toContainEqual( partialPinoLog('info', 'Refused invalid member bundle request', { publicKeyId: MEMBER_PUBLIC_KEY_MONGO_ID, - reason: expect.stringContaining('peerId'), }), ); }); @@ -235,7 +233,6 @@ describe('Awala routes', () => { expect(logs).toContainEqual( partialPinoLog('info', 'Refused invalid member bundle request', { publicKeyId: MEMBER_PUBLIC_KEY_MONGO_ID, - reason: expect.stringContaining('signature'), }), ); }); @@ -300,9 +297,7 @@ describe('Awala routes', () => { expect(response).toHaveProperty('statusCode', HTTP_STATUS_CODES.BAD_REQUEST); expect(logs).toContainEqual( - partialPinoLog('info', 'Refused invalid member bundle request', { - reason: expect.stringContaining('publicKeyImportToken'), - }), + partialPinoLog('info', 'Refused invalid member key import request'), ); }); diff --git a/src/awala/routes/awala.routes.ts b/src/awala/routes/awala.routes.ts index 9ecacbb8..94b09e46 100644 --- a/src/awala/routes/awala.routes.ts +++ b/src/awala/routes/awala.routes.ts @@ -5,14 +5,13 @@ import { HTTP_STATUS_CODES } from '../../utilities/http.js'; import type { PluginDone } from '../../utilities/fastify/PluginDone.js'; import type { FastifyTypedInstance } from '../../utilities/fastify/FastifyTypedInstance.js'; import { - MEMBER_BUNDLE_REQUEST_SCHEMA, - MEMBER_KEY_IMPORT_REQUEST_SCHEMA, + isMemberBundleRequest, + isMemberKeyImportRequest, type MemberBundleRequest, type MemberKeyImportRequest, } from '../../schemas/awala.schema.js'; import type { ServiceOptions } from '../../serviceTypes.js'; import { processMemberKeyImportToken } from '../../memberKeyImportToken.js'; -import { validateMessage } from '../../utilities/validateMessage.js'; import { createMemberBundleRequest } from '../../memberBundle.js'; import { getIncomingServiceMessageEvent, @@ -32,26 +31,18 @@ async function processMemberBundleRequest( options.logger.info('Refused invalid json format'); return false; } - const validationResult = validateMessage( - { - ...data, - peerId: incomingMessage.senderId, - }, - MEMBER_BUNDLE_REQUEST_SCHEMA, - ); - if (typeof validationResult === 'string') { - options.logger.info( - { - publicKeyId: (data as MemberBundleRequest).publicKeyId, - reason: validationResult, - }, - 'Refused invalid member bundle request', - ); - return false; + + const finalData = { ...data, peerId: incomingMessage.senderId }; + if (isMemberBundleRequest(finalData)) { + await createMemberBundleRequest(finalData, options); + return true; } - await createMemberBundleRequest(validationResult, options); - return true; + options.logger.info( + { publicKeyId: (data as MemberBundleRequest).publicKeyId }, + 'Refused invalid member bundle request', + ); + return false; } async function processMemberKeyImportRequest( @@ -64,28 +55,25 @@ async function processMemberKeyImportRequest( options.logger.info('Refused invalid json format'); return false; } - const validationResult = validateMessage(data, MEMBER_KEY_IMPORT_REQUEST_SCHEMA); - if (typeof validationResult === 'string') { - options.logger.info( + if (isMemberKeyImportRequest(data)) { + const result = await processMemberKeyImportToken( + incomingMessage.senderId, { - publicKeyImportToken: (data as MemberKeyImportRequest).publicKeyImportToken, - reason: validationResult, + publicKey: data.publicKey, + publicKeyImportToken: data.publicKeyImportToken, }, - 'Refused invalid member bundle request', + ceEmitter, + options, ); - return false; + return result.didSucceed; } - - const result = await processMemberKeyImportToken( - incomingMessage.senderId, + options.logger.info( { - publicKey: validationResult.publicKey, - publicKeyImportToken: validationResult.publicKeyImportToken, + publicKeyImportToken: (data as MemberKeyImportRequest).publicKeyImportToken, }, - ceEmitter, - options, + 'Refused invalid member key import request', ); - return result.didSucceed; + return false; } enum AwalaRequestMessageType { diff --git a/src/schemas/awala.schema.ts b/src/schemas/awala.schema.ts index 0cf9a443..3d375559 100644 --- a/src/schemas/awala.schema.ts +++ b/src/schemas/awala.schema.ts @@ -1,8 +1,10 @@ import type { FromSchema } from 'json-schema-to-ts'; +import { compileSchema } from '../utilities/ajv.js'; + import { BASE_64_REGEX } from './validation.js'; -export const MEMBER_BUNDLE_REQUEST_SCHEMA = { +const MEMBER_BUNDLE_REQUEST_SCHEMA = { type: 'object', properties: { @@ -15,7 +17,7 @@ export const MEMBER_BUNDLE_REQUEST_SCHEMA = { required: ['publicKeyId', 'memberBundleStartDate', 'signature', 'peerId'], } as const; -export const MEMBER_KEY_IMPORT_REQUEST_SCHEMA = { +const MEMBER_KEY_IMPORT_REQUEST_SCHEMA = { type: 'object', properties: { @@ -27,4 +29,7 @@ export const MEMBER_KEY_IMPORT_REQUEST_SCHEMA = { } as const; export type MemberBundleRequest = FromSchema; +export const isMemberBundleRequest = compileSchema(MEMBER_BUNDLE_REQUEST_SCHEMA); + export type MemberKeyImportRequest = FromSchema; +export const isMemberKeyImportRequest = compileSchema(MEMBER_KEY_IMPORT_REQUEST_SCHEMA); diff --git a/src/utilities/ajv.ts b/src/utilities/ajv.ts new file mode 100644 index 00000000..f8872f9e --- /dev/null +++ b/src/utilities/ajv.ts @@ -0,0 +1,7 @@ +import { type $Compiler, wrapCompilerAsTypeGuard } from 'json-schema-to-ts'; +import addFormats from 'ajv-formats'; +import Ajv from 'ajv'; + +const ajv = addFormats(new Ajv()); +const $compile: $Compiler = (schema) => ajv.compile(schema); +export const compileSchema = wrapCompilerAsTypeGuard($compile); diff --git a/src/utilities/validateMessage.spec.ts b/src/utilities/validateMessage.spec.ts deleted file mode 100644 index 2446bb86..00000000 --- a/src/utilities/validateMessage.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { FromSchema } from 'json-schema-to-ts'; - -import { validateMessage } from './validateMessage.js'; - -const TEST_SCHEMA = { - type: 'object', - - properties: { - testField: { type: 'string' }, - }, - - required: ['testField'], -} as const; - -type Test = FromSchema; - -describe('validateMessage', () => { - test('Valid data should return an object type', () => { - const testData: Test = { - testField: 'test', - }; - - const result = validateMessage( - { - testField: 'test', - }, - TEST_SCHEMA, - ); - - expect(result).toStrictEqual(testData); - }); - - test('Invalid data should return an error', () => { - const result = validateMessage( - { - testField: 1, - }, - TEST_SCHEMA, - ); - - expect(result).toBe('data/testField must be string'); - }); -}); diff --git a/src/utilities/validateMessage.ts b/src/utilities/validateMessage.ts deleted file mode 100644 index 5007a587..00000000 --- a/src/utilities/validateMessage.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { FromSchema, JSONSchema } from 'json-schema-to-ts'; -import addFormats from 'ajv-formats'; -import Ajv from 'ajv'; - -type ValidationResult = FromSchema | string; - -const ajv = addFormats(new Ajv()); - -export function validateMessage( - value: unknown, - schema: Schema, -): ValidationResult { - if (!ajv.validate(schema, value)) { - return ajv.errorsText(ajv.errors); - } - - return value as ValidationResult; -}