From 2eb28a8d0d8b1fd29cdeba3602e4dc238645c06f Mon Sep 17 00:00:00 2001 From: almog8k Date: Wed, 5 Nov 2025 18:12:07 +0200 Subject: [PATCH 1/2] fix: move zod validation insdie try-catch block in createExport --- src/export/controllers/exportController.ts | 10 +++++-- tests/integration/export/export.spec.ts | 32 ++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/export/controllers/exportController.ts b/src/export/controllers/exportController.ts index aab1d3c..2b0b19e 100644 --- a/src/export/controllers/exportController.ts +++ b/src/export/controllers/exportController.ts @@ -1,9 +1,10 @@ import { Logger } from '@map-colonies/js-logger'; import { RequestHandler } from 'express'; import httpStatus from 'http-status-codes'; +import { HttpError } from '@map-colonies/error-types'; import { injectable, inject } from 'tsyringe'; import { CallbackExportResponse } from '@map-colonies/raster-shared'; -import { CreateExportRequest, createExportRequestSchema } from '@src/utils/zod/schemas'; +import { createExportRequestSchema } from '@src/utils/zod/schemas'; import { SERVICES } from '../../common/constants'; import { ExportManager } from '../models/exportManager'; import { ICreateExportJobResponse, IJobStatusResponse } from '../../common/interfaces'; @@ -19,10 +20,13 @@ export class ExportController { ) {} public createExport: CreateExportHandler = async (req, res, next) => { - const exportRequest: CreateExportRequest = createExportRequestSchema.parse(req.body); try { + const exportRequest = createExportRequestSchema.safeParse(req.body); + if (!exportRequest.success) { + throw new HttpError(exportRequest.error.message, httpStatus.BAD_REQUEST); + } this.logger.debug({ msg: `Creating export request:`, exportRequest }); - const jobCreated = await this.manager.createExport(exportRequest); + const jobCreated = await this.manager.createExport(exportRequest.data); return res.status(httpStatus.OK).json(jobCreated); } catch (err) { next(err); diff --git a/tests/integration/export/export.spec.ts b/tests/integration/export/export.spec.ts index 56d1cc8..b4f6368 100644 --- a/tests/integration/export/export.spec.ts +++ b/tests/integration/export/export.spec.ts @@ -47,6 +47,7 @@ import { layerWithMultiPolygonFootprint } from '@tests/mocks/geometryMocks'; import { getTestContainerConfig, resetContainer } from '../testContainerConfig'; import { getApp } from '../../../src/app'; import { ExportSender } from './helpers/exportSender'; +import { CreateExportRequest } from '@src/utils/zod/schemas'; jest.mock('uuid', () => ({ v4: jest.fn() })); @@ -478,6 +479,37 @@ describe('export', function () { }); describe('Bad Path', function () { + it('should return 400 bad request when zod validation fails with invalid request body', async function () { + // Send completely invalid request body that will fail Zod parsing + const invalidRequest = { + dbId: 123, // Should be string + crs: ['invalid'], // Should be string + priority: 'high', // Should be number + roi: 'not-a-valid-geojson', // Should be valid FeatureCollection + callbackURLs: 'not-an-array', // Should be array + }; + + const response = await requestSender.export(invalidRequest as unknown as CreateExportRequest); + + expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); + expect(response.body).toHaveProperty('message'); + expect(response).toSatisfyApiSpec(); + }); + + it('should return 400 bad request when zod validation fails with missing required fields', async function () { + // Send request without required dbId field + const invalidRequest = { + crs: 'EPSG:4326', + description: 'test', + }; + + const response = await requestSender.export(invalidRequest as unknown as CreateExportRequest); + + expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); + expect(response.body).toHaveProperty('message'); + expect(response).toSatisfyApiSpec(); + }); + it('should return not found status code when layer not found', async function () { const layerId = createExportRequestWithoutCallback.dbId; From ef4d82d02b04fea10aca852e109a165ef666fbb2 Mon Sep 17 00:00:00 2001 From: almog8k Date: Wed, 5 Nov 2025 18:15:28 +0200 Subject: [PATCH 2/2] chore: lint fix --- tests/integration/export/export.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/export/export.spec.ts b/tests/integration/export/export.spec.ts index b4f6368..c80c4e6 100644 --- a/tests/integration/export/export.spec.ts +++ b/tests/integration/export/export.spec.ts @@ -44,10 +44,10 @@ import { CallbackUrlsTargetArray, ExportJobParameters } from '@map-colonies/rast import { JobExportResponse } from '@src/common/interfaces'; import { JobManagerWrapper } from '@src/clients/jobManagerWrapper'; import { layerWithMultiPolygonFootprint } from '@tests/mocks/geometryMocks'; +import { CreateExportRequest } from '@src/utils/zod/schemas'; import { getTestContainerConfig, resetContainer } from '../testContainerConfig'; import { getApp } from '../../../src/app'; import { ExportSender } from './helpers/exportSender'; -import { CreateExportRequest } from '@src/utils/zod/schemas'; jest.mock('uuid', () => ({ v4: jest.fn() }));