All files / controllers imageController.ts

84.61% Statements 33/39
77.77% Branches 7/9
100% Functions 3/3
83.78% Lines 31/37

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 951x 1x 1x           1x   1x 1x               1x 1x     1x   1x   1x               1x       1x         1x         1x                 1x 2x 2x 2x   1x 1x 1x 1x                       1x 2x 2x 2x   1x 1x 1x 1x                
import { ImageService } from '../services/imageService';
import { ApiResponse } from '../common/apiResponse';
import { ValidationError } from '../common/errors';
import {
  LambdaEvent,
  PresignUploadRequest,
  PresignUploadResponse,
} from '../dto/image.dto';
import { getLogger } from '../common/logger';
 
const logger = getLogger('ImageController');
const imageService = new ImageService();
 
/**
 * Generate a presigned S3 upload URL for image upload
 * @param event - Lambda event containing filename and contentType in request body
 * @returns ApiResponse with imageId and presigned upload URL
 * @throws ValidationError if filename or contentType is missing or invalid
 */
export async function presignUpload(event: LambdaEvent): Promise<ApiResponse> {
  const body: PresignUploadRequest = event.body
    ? (JSON.parse(event.body) as PresignUploadRequest)
    : {};
  logger.info('PresignUpload invoked', event);
 
  const { filename, contentType } = body;
 
  Iif (!filename || !contentType) {
    logger.warn('PresignUpload validation failed', event, {
      filename,
      contentType,
    });
    throw new ValidationError('filename and contentType are required');
  }
 
  const result = await imageService.generatePresignedUpload(
    filename,
    contentType
  );
  logger.info('PresignUpload URL generated', event, {
    imageId: result.imageId,
    uploadUrl: result.uploadUrl,
  });
 
  const response: PresignUploadResponse = {
    imageId: result.imageId,
    url: result.uploadUrl,
  };
 
  return ApiResponse.created({ success: true, data: response });
}
 
/**
 * Retrieve image metadata by ID
 * @param event - Lambda event with image ID in path parameters
 * @returns ApiResponse with image metadata or 404 if not found
 * @throws ValidationError if image ID is missing
 */
export async function getImage(event: LambdaEvent): Promise<ApiResponse> {
  logger.info('getImage invoked', event);
  const id = event.pathParameters?.id;
  if (!id) throw new ValidationError('Image ID is required');
 
  const metadata = await imageService.getImageMetaData(id);
  if (!metadata) {
    logger.warn('getImage not found', event, { id });
    return ApiResponse.notFound('Image not found');
  }
  logger.info('getImage success', event, metadata.imageId);
  return ApiResponse.ok(metadata);
}
 
/**
 * Delete an image and its metadata
 * @param event - Lambda event with image ID in path parameters
 * @returns ApiResponse with 204 No Content on success or 404 if not found
 * @throws ValidationError if image ID is missing
 */
export async function deleteImage(event: LambdaEvent): Promise<ApiResponse> {
  logger.info('deleteImage invoked', event);
  const id = event.pathParameters?.id;
  if (!id) throw new ValidationError('Image ID is required');
 
  const deleted = await imageService.deleteImage(id);
  if (!deleted) {
    logger.warn('deleteImage not found', event, { id });
    return ApiResponse.notFound('Image not found');
  }
  logger.info('deleteImage success', event, { id });
  return ApiResponse.no_content({
    success: true,
    message: 'Image deleted successfully',
  });
}