mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	refactor(server): mime types (#3197)
* refactor(server): mime type check * chore: open api * chore: remove duplicate test
This commit is contained in:
		@@ -156,10 +156,7 @@ describe(AssetService.name, () => {
 | 
			
		||||
 | 
			
		||||
      await expect(sut.downloadFile(authStub.admin, 'asset-1')).resolves.toEqual({ stream });
 | 
			
		||||
 | 
			
		||||
      expect(storageMock.createReadStream).toHaveBeenCalledWith(
 | 
			
		||||
        assetEntityStub.image.originalPath,
 | 
			
		||||
        assetEntityStub.image.mimeType,
 | 
			
		||||
      );
 | 
			
		||||
      expect(storageMock.createReadStream).toHaveBeenCalledWith(assetEntityStub.image.originalPath, 'image/jpeg');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should download an archive', async () => {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import { DateTime } from 'luxon';
 | 
			
		||||
import { extname } from 'path';
 | 
			
		||||
import { AccessCore, IAccessRepository, Permission } from '../access';
 | 
			
		||||
import { AuthUserDto } from '../auth';
 | 
			
		||||
import { mimeTypes } from '../domain.constant';
 | 
			
		||||
import { HumanReadableSize, usePagination } from '../domain.util';
 | 
			
		||||
import { ImmichReadStream, IStorageRepository } from '../storage';
 | 
			
		||||
import { IAssetRepository } from './asset.repository';
 | 
			
		||||
@@ -20,7 +21,6 @@ export enum UploadFieldName {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface UploadFile {
 | 
			
		||||
  mimeType: string;
 | 
			
		||||
  checksum: Buffer;
 | 
			
		||||
  originalPath: string;
 | 
			
		||||
  originalName: string;
 | 
			
		||||
@@ -68,7 +68,7 @@ export class AssetService {
 | 
			
		||||
      throw new BadRequestException('Asset not found');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this.storageRepository.createReadStream(asset.originalPath, asset.mimeType);
 | 
			
		||||
    return this.storageRepository.createReadStream(asset.originalPath, mimeTypes.lookup(asset.originalPath));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async getDownloadInfo(authUser: AuthUserDto, dto: DownloadDto): Promise<DownloadResponseDto> {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,6 @@ export class AssetResponseDto {
 | 
			
		||||
  updatedAt!: Date;
 | 
			
		||||
  isFavorite!: boolean;
 | 
			
		||||
  isArchived!: boolean;
 | 
			
		||||
  mimeType!: string | null;
 | 
			
		||||
  duration!: string;
 | 
			
		||||
  exifInfo?: ExifResponseDto;
 | 
			
		||||
  smartInfo?: SmartInfoResponseDto;
 | 
			
		||||
@@ -50,7 +49,6 @@ export function mapAsset(entity: AssetEntity): AssetResponseDto {
 | 
			
		||||
    updatedAt: entity.updatedAt,
 | 
			
		||||
    isFavorite: entity.isFavorite,
 | 
			
		||||
    isArchived: entity.isArchived,
 | 
			
		||||
    mimeType: entity.mimeType,
 | 
			
		||||
    duration: entity.duration ?? '0:00:00.00000',
 | 
			
		||||
    exifInfo: entity.exifInfo ? mapExif(entity.exifInfo) : undefined,
 | 
			
		||||
    smartInfo: entity.smartInfo ? mapSmartInfo(entity.smartInfo) : undefined,
 | 
			
		||||
@@ -77,7 +75,6 @@ export function mapAssetWithoutExif(entity: AssetEntity): AssetResponseDto {
 | 
			
		||||
    updatedAt: entity.updatedAt,
 | 
			
		||||
    isFavorite: entity.isFavorite,
 | 
			
		||||
    isArchived: entity.isArchived,
 | 
			
		||||
    mimeType: entity.mimeType,
 | 
			
		||||
    duration: entity.duration ?? '0:00:00.00000',
 | 
			
		||||
    exifInfo: undefined,
 | 
			
		||||
    smartInfo: entity.smartInfo ? mapSmartInfo(entity.smartInfo) : undefined,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import { BadRequestException } from '@nestjs/common';
 | 
			
		||||
import { extname } from 'node:path';
 | 
			
		||||
import pkg from 'src/../../package.json';
 | 
			
		||||
 | 
			
		||||
const [major, minor, patch] = pkg.version.split('.');
 | 
			
		||||
@@ -28,92 +29,78 @@ export function assertMachineLearningEnabled() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ASSET_MIME_TYPES = [
 | 
			
		||||
  'image/3fr',
 | 
			
		||||
  'image/ari',
 | 
			
		||||
  'image/arw',
 | 
			
		||||
  'image/avif',
 | 
			
		||||
  'image/cap',
 | 
			
		||||
  'image/cin',
 | 
			
		||||
  'image/cr2',
 | 
			
		||||
  'image/cr3',
 | 
			
		||||
  'image/crw',
 | 
			
		||||
  'image/dcr',
 | 
			
		||||
  'image/dng',
 | 
			
		||||
  'image/erf',
 | 
			
		||||
  'image/fff',
 | 
			
		||||
  'image/gif',
 | 
			
		||||
  'image/heic',
 | 
			
		||||
  'image/heif',
 | 
			
		||||
  'image/iiq',
 | 
			
		||||
  'image/jpeg',
 | 
			
		||||
  'image/jxl',
 | 
			
		||||
  'image/k25',
 | 
			
		||||
  'image/kdc',
 | 
			
		||||
  'image/mrw',
 | 
			
		||||
  'image/nef',
 | 
			
		||||
  'image/orf',
 | 
			
		||||
  'image/ori',
 | 
			
		||||
  'image/pef',
 | 
			
		||||
  'image/png',
 | 
			
		||||
  'image/raf',
 | 
			
		||||
  'image/raw',
 | 
			
		||||
  'image/rwl',
 | 
			
		||||
  'image/sr2',
 | 
			
		||||
  'image/srf',
 | 
			
		||||
  'image/srw',
 | 
			
		||||
  'image/tiff',
 | 
			
		||||
  'image/webp',
 | 
			
		||||
  'image/x-adobe-dng',
 | 
			
		||||
  'image/x-arriflex-ari',
 | 
			
		||||
  'image/x-canon-cr2',
 | 
			
		||||
  'image/x-canon-cr3',
 | 
			
		||||
  'image/x-canon-crw',
 | 
			
		||||
  'image/x-epson-erf',
 | 
			
		||||
  'image/x-fuji-raf',
 | 
			
		||||
  'image/x-hasselblad-3fr',
 | 
			
		||||
  'image/x-hasselblad-fff',
 | 
			
		||||
  'image/x-kodak-dcr',
 | 
			
		||||
  'image/x-kodak-k25',
 | 
			
		||||
  'image/x-kodak-kdc',
 | 
			
		||||
  'image/x-leica-rwl',
 | 
			
		||||
  'image/x-minolta-mrw',
 | 
			
		||||
  'image/x-nikon-nef',
 | 
			
		||||
  'image/x-olympus-orf',
 | 
			
		||||
  'image/x-olympus-ori',
 | 
			
		||||
  'image/x-panasonic-raw',
 | 
			
		||||
  'image/x-pentax-pef',
 | 
			
		||||
  'image/x-phantom-cin',
 | 
			
		||||
  'image/x-phaseone-cap',
 | 
			
		||||
  'image/x-phaseone-iiq',
 | 
			
		||||
  'image/x-samsung-srw',
 | 
			
		||||
  'image/x-sigma-x3f',
 | 
			
		||||
  'image/x-sony-arw',
 | 
			
		||||
  'image/x-sony-sr2',
 | 
			
		||||
  'image/x-sony-srf',
 | 
			
		||||
  'image/x3f',
 | 
			
		||||
  'video/3gpp',
 | 
			
		||||
  'video/avi',
 | 
			
		||||
  'video/mp2t',
 | 
			
		||||
  'video/mp4',
 | 
			
		||||
  'video/mpeg',
 | 
			
		||||
  'video/msvideo',
 | 
			
		||||
  'video/quicktime',
 | 
			
		||||
  'video/vnd.avi',
 | 
			
		||||
  'video/webm',
 | 
			
		||||
  'video/x-flv',
 | 
			
		||||
  'video/x-matroska',
 | 
			
		||||
  'video/x-ms-wmv',
 | 
			
		||||
  'video/x-msvideo',
 | 
			
		||||
];
 | 
			
		||||
export const LIVE_PHOTO_MIME_TYPES = ASSET_MIME_TYPES;
 | 
			
		||||
export const SIDECAR_MIME_TYPES = ['application/xml', 'text/xml'];
 | 
			
		||||
export const PROFILE_MIME_TYPES = [
 | 
			
		||||
  'image/jpeg',
 | 
			
		||||
  'image/png',
 | 
			
		||||
  'image/heic',
 | 
			
		||||
  'image/heif',
 | 
			
		||||
  'image/dng',
 | 
			
		||||
  'image/webp',
 | 
			
		||||
  'image/avif',
 | 
			
		||||
];
 | 
			
		||||
const profile: Record<string, string> = {
 | 
			
		||||
  '.avif': 'image/avif',
 | 
			
		||||
  '.dng': 'image/x-adobe-dng',
 | 
			
		||||
  '.heic': 'image/heic',
 | 
			
		||||
  '.heif': 'image/heif',
 | 
			
		||||
  '.jpeg': 'image/jpeg',
 | 
			
		||||
  '.jpg': 'image/jpeg',
 | 
			
		||||
  '.png': 'image/png',
 | 
			
		||||
  '.webp': 'image/webp',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const image: Record<string, string> = {
 | 
			
		||||
  ...profile,
 | 
			
		||||
  '.3fr': 'image/x-hasselblad-3fr',
 | 
			
		||||
  '.ari': 'image/x-arriflex-ari',
 | 
			
		||||
  '.arw': 'image/x-sony-arw',
 | 
			
		||||
  '.cap': 'image/x-phaseone-cap',
 | 
			
		||||
  '.cin': 'image/x-phantom-cin',
 | 
			
		||||
  '.cr2': 'image/x-canon-cr2',
 | 
			
		||||
  '.cr3': 'image/x-canon-cr3',
 | 
			
		||||
  '.crw': 'image/x-canon-crw',
 | 
			
		||||
  '.dcr': 'image/x-kodak-dcr',
 | 
			
		||||
  '.erf': 'image/x-epson-erf',
 | 
			
		||||
  '.fff': 'image/x-hasselblad-fff',
 | 
			
		||||
  '.gif': 'image/gif',
 | 
			
		||||
  '.iiq': 'image/x-phaseone-iiq',
 | 
			
		||||
  '.k25': 'image/x-kodak-k25',
 | 
			
		||||
  '.kdc': 'image/x-kodak-kdc',
 | 
			
		||||
  '.mrw': 'image/x-minolta-mrw',
 | 
			
		||||
  '.nef': 'image/x-nikon-nef',
 | 
			
		||||
  '.orf': 'image/x-olympus-orf',
 | 
			
		||||
  '.ori': 'image/x-olympus-ori',
 | 
			
		||||
  '.pef': 'image/x-pentax-pef',
 | 
			
		||||
  '.raf': 'image/x-fuji-raf',
 | 
			
		||||
  '.raw': 'image/x-panasonic-raw',
 | 
			
		||||
  '.rwl': 'image/x-leica-rwl',
 | 
			
		||||
  '.sr2': 'image/x-sony-sr2',
 | 
			
		||||
  '.srf': 'image/x-sony-srf',
 | 
			
		||||
  '.srw': 'image/x-samsung-srw',
 | 
			
		||||
  '.tiff': 'image/tiff',
 | 
			
		||||
  '.x3f': 'image/x-sigma-x3f',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const video: Record<string, string> = {
 | 
			
		||||
  '.3gp': 'video/3gpp',
 | 
			
		||||
  '.avi': 'video/x-msvideo',
 | 
			
		||||
  '.flv': 'video/x-flv',
 | 
			
		||||
  '.mkv': 'video/x-matroska',
 | 
			
		||||
  '.mov': 'video/quicktime',
 | 
			
		||||
  '.mp2t': 'video/mp2t',
 | 
			
		||||
  '.mp4': 'video/mp4',
 | 
			
		||||
  '.mpeg': 'video/mpeg',
 | 
			
		||||
  '.webm': 'video/webm',
 | 
			
		||||
  '.wmv': 'video/x-ms-wmv',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const sidecar: Record<string, string> = {
 | 
			
		||||
  '.xmp': 'application/xml',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const isType = (filename: string, lookup: Record<string, string>) => !!lookup[extname(filename).toLowerCase()];
 | 
			
		||||
const getType = (filename: string, lookup: Record<string, string>) => lookup[extname(filename).toLowerCase()];
 | 
			
		||||
 | 
			
		||||
export const mimeTypes = {
 | 
			
		||||
  image,
 | 
			
		||||
  profile,
 | 
			
		||||
  sidecar,
 | 
			
		||||
  video,
 | 
			
		||||
 | 
			
		||||
  isAsset: (filename: string) => isType(filename, image) || isType(filename, video),
 | 
			
		||||
  isProfile: (filename: string) => isType(filename, profile),
 | 
			
		||||
  isSidecar: (filename: string) => isType(filename, sidecar),
 | 
			
		||||
  isVideo: (filename: string) => isType(filename, video),
 | 
			
		||||
  lookup: (filename: string) => getType(filename, { ...image, ...video, ...sidecar }) || 'application/octet-stream',
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -268,7 +268,7 @@ describe(FacialRecognitionService.name, () => {
 | 
			
		||||
 | 
			
		||||
      expect(assetMock.getByIds).toHaveBeenCalledWith(['asset-1']);
 | 
			
		||||
      expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id');
 | 
			
		||||
      expect(mediaMock.crop).toHaveBeenCalledWith('/uploads/user-id/thumbs/path.ext', {
 | 
			
		||||
      expect(mediaMock.crop).toHaveBeenCalledWith('/uploads/user-id/thumbs/path.jpg', {
 | 
			
		||||
        left: 95,
 | 
			
		||||
        top: 95,
 | 
			
		||||
        width: 110,
 | 
			
		||||
@@ -289,7 +289,7 @@ describe(FacialRecognitionService.name, () => {
 | 
			
		||||
 | 
			
		||||
      await sut.handleGenerateFaceThumbnail(face.start);
 | 
			
		||||
 | 
			
		||||
      expect(mediaMock.crop).toHaveBeenCalledWith('/uploads/user-id/thumbs/path.ext', {
 | 
			
		||||
      expect(mediaMock.crop).toHaveBeenCalledWith('/uploads/user-id/thumbs/path.jpg', {
 | 
			
		||||
        left: 0,
 | 
			
		||||
        top: 0,
 | 
			
		||||
        width: 510,
 | 
			
		||||
@@ -306,7 +306,7 @@ describe(FacialRecognitionService.name, () => {
 | 
			
		||||
 | 
			
		||||
      await sut.handleGenerateFaceThumbnail(face.end);
 | 
			
		||||
 | 
			
		||||
      expect(mediaMock.crop).toHaveBeenCalledWith('/uploads/user-id/thumbs/path.ext', {
 | 
			
		||||
      expect(mediaMock.crop).toHaveBeenCalledWith('/uploads/user-id/thumbs/path.jpg', {
 | 
			
		||||
        left: 297,
 | 
			
		||||
        top: 297,
 | 
			
		||||
        width: 202,
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,7 @@ describe(MediaService.name, () => {
 | 
			
		||||
      await sut.handleGenerateJpegThumbnail({ id: assetEntityStub.image.id });
 | 
			
		||||
 | 
			
		||||
      expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id');
 | 
			
		||||
      expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.ext', 'upload/thumbs/user-id/asset-id.jpeg', {
 | 
			
		||||
      expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/asset-id.jpeg', {
 | 
			
		||||
        size: 1440,
 | 
			
		||||
        format: 'jpeg',
 | 
			
		||||
      });
 | 
			
		||||
@@ -167,11 +167,11 @@ describe(MediaService.name, () => {
 | 
			
		||||
      await sut.handleGenerateWebpThumbnail({ id: assetEntityStub.image.id });
 | 
			
		||||
 | 
			
		||||
      expect(mediaMock.resize).toHaveBeenCalledWith(
 | 
			
		||||
        '/uploads/user-id/thumbs/path.ext',
 | 
			
		||||
        '/uploads/user-id/thumbs/path.ext',
 | 
			
		||||
        '/uploads/user-id/thumbs/path.jpg',
 | 
			
		||||
        '/uploads/user-id/thumbs/path.webp',
 | 
			
		||||
        { format: 'webp', size: 250 },
 | 
			
		||||
      );
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith({ id: 'asset-id', webpPath: '/uploads/user-id/thumbs/path.ext' });
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith({ id: 'asset-id', webpPath: '/uploads/user-id/thumbs/path.webp' });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@@ -195,7 +195,7 @@ describe(MediaService.name, () => {
 | 
			
		||||
 | 
			
		||||
      await sut.handleGenerateThumbhashThumbnail({ id: assetEntityStub.image.id });
 | 
			
		||||
 | 
			
		||||
      expect(mediaMock.generateThumbhash).toHaveBeenCalledWith('/uploads/user-id/thumbs/path.ext');
 | 
			
		||||
      expect(mediaMock.generateThumbhash).toHaveBeenCalledWith('/uploads/user-id/thumbs/path.jpg');
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith({ id: 'asset-id', thumbhash: thumbhashBuffer });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 
 | 
			
		||||
@@ -89,10 +89,10 @@ export class MediaService {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const webpPath = asset.resizePath.replace('jpeg', 'webp');
 | 
			
		||||
    const webpPath = asset.resizePath.replace('jpeg', 'webp').replace('jpg', 'webp');
 | 
			
		||||
 | 
			
		||||
    await this.mediaRepository.resize(asset.resizePath, webpPath, { size: WEBP_THUMBNAIL_SIZE, format: 'webp' });
 | 
			
		||||
    await this.assetRepository.save({ id: asset.id, webpPath: webpPath });
 | 
			
		||||
    await this.assetRepository.save({ id: asset.id, webpPath });
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -82,10 +82,10 @@ describe(MetadataService.name, () => {
 | 
			
		||||
      assetMock.save.mockResolvedValue(assetEntityStub.image);
 | 
			
		||||
      storageMock.checkFileExists.mockResolvedValue(true);
 | 
			
		||||
      await sut.handleSidecarDiscovery({ id: assetEntityStub.image.id });
 | 
			
		||||
      expect(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.ext.xmp', constants.R_OK);
 | 
			
		||||
      expect(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.jpg.xmp', constants.R_OK);
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith({
 | 
			
		||||
        id: assetEntityStub.image.id,
 | 
			
		||||
        sidecarPath: '/original/path.ext.xmp',
 | 
			
		||||
        sidecarPath: '/original/path.jpg.xmp',
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ import { PersonService } from './person.service';
 | 
			
		||||
const responseDto: PersonResponseDto = {
 | 
			
		||||
  id: 'person-1',
 | 
			
		||||
  name: 'Person 1',
 | 
			
		||||
  thumbnailPath: '/path/to/thumbnail',
 | 
			
		||||
  thumbnailPath: '/path/to/thumbnail.jpg',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe(PersonService.name, () => {
 | 
			
		||||
@@ -74,7 +74,7 @@ describe(PersonService.name, () => {
 | 
			
		||||
    it('should serve the thumbnail', async () => {
 | 
			
		||||
      personMock.getById.mockResolvedValue(personStub.noName);
 | 
			
		||||
      await sut.getThumbnail(authStub.admin, 'person-1');
 | 
			
		||||
      expect(storageMock.createReadStream).toHaveBeenCalledWith('/path/to/thumbnail', 'image/jpeg');
 | 
			
		||||
      expect(storageMock.createReadStream).toHaveBeenCalledWith('/path/to/thumbnail.jpg', 'image/jpeg');
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@@ -150,7 +150,7 @@ describe(PersonService.name, () => {
 | 
			
		||||
      expect(personMock.delete).toHaveBeenCalledWith(personStub.noName);
 | 
			
		||||
      expect(jobMock.queue).toHaveBeenCalledWith({
 | 
			
		||||
        name: JobName.DELETE_FILES,
 | 
			
		||||
        data: { files: ['/path/to/thumbnail'] },
 | 
			
		||||
        data: { files: ['/path/to/thumbnail.jpg'] },
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import { PersonEntity } from '@app/infra/entities';
 | 
			
		||||
import { BadRequestException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common';
 | 
			
		||||
import { AssetResponseDto, mapAsset } from '../asset';
 | 
			
		||||
import { AuthUserDto } from '../auth';
 | 
			
		||||
import { mimeTypes } from '../domain.constant';
 | 
			
		||||
import { IJobRepository, JobName } from '../job';
 | 
			
		||||
import { ImmichReadStream, IStorageRepository } from '../storage';
 | 
			
		||||
import { mapPerson, PersonResponseDto, PersonUpdateDto } from './person.dto';
 | 
			
		||||
@@ -44,7 +45,7 @@ export class PersonService {
 | 
			
		||||
      throw new NotFoundException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this.storageRepository.createReadStream(person.thumbnailPath, 'image/jpeg');
 | 
			
		||||
    return this.storageRepository.createReadStream(person.thumbnailPath, mimeTypes.lookup(person.thumbnailPath));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async getAssets(authUser: AuthUserDto, personId: string): Promise<AssetResponseDto[]> {
 | 
			
		||||
 
 | 
			
		||||
@@ -56,11 +56,11 @@ describe(StorageTemplateService.name, () => {
 | 
			
		||||
      userMock.getList.mockResolvedValue([userEntityStub.user1]);
 | 
			
		||||
 | 
			
		||||
      when(storageMock.checkFileExists)
 | 
			
		||||
        .calledWith('upload/library/user-id/2023/2023-02-23/asset-id.ext')
 | 
			
		||||
        .calledWith('upload/library/user-id/2023/2023-02-23/asset-id.jpg')
 | 
			
		||||
        .mockResolvedValue(true);
 | 
			
		||||
 | 
			
		||||
      when(storageMock.checkFileExists)
 | 
			
		||||
        .calledWith('upload/library/user-id/2023/2023-02-23/asset-id+1.ext')
 | 
			
		||||
        .calledWith('upload/library/user-id/2023/2023-02-23/asset-id+1.jpg')
 | 
			
		||||
        .mockResolvedValue(false);
 | 
			
		||||
 | 
			
		||||
      await sut.handleMigration();
 | 
			
		||||
@@ -69,7 +69,7 @@ describe(StorageTemplateService.name, () => {
 | 
			
		||||
      expect(storageMock.checkFileExists).toHaveBeenCalledTimes(2);
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith({
 | 
			
		||||
        id: assetEntityStub.image.id,
 | 
			
		||||
        originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.ext',
 | 
			
		||||
        originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
 | 
			
		||||
      });
 | 
			
		||||
      expect(userMock.getList).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
@@ -79,7 +79,7 @@ describe(StorageTemplateService.name, () => {
 | 
			
		||||
        items: [
 | 
			
		||||
          {
 | 
			
		||||
            ...assetEntityStub.image,
 | 
			
		||||
            originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.ext',
 | 
			
		||||
            originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        hasNextPage: false,
 | 
			
		||||
@@ -99,7 +99,7 @@ describe(StorageTemplateService.name, () => {
 | 
			
		||||
        items: [
 | 
			
		||||
          {
 | 
			
		||||
            ...assetEntityStub.image,
 | 
			
		||||
            originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.ext',
 | 
			
		||||
            originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        hasNextPage: false,
 | 
			
		||||
@@ -126,12 +126,12 @@ describe(StorageTemplateService.name, () => {
 | 
			
		||||
 | 
			
		||||
      expect(assetMock.getAll).toHaveBeenCalled();
 | 
			
		||||
      expect(storageMock.moveFile).toHaveBeenCalledWith(
 | 
			
		||||
        '/original/path.ext',
 | 
			
		||||
        'upload/library/user-id/2023/2023-02-23/asset-id.ext',
 | 
			
		||||
        '/original/path.jpg',
 | 
			
		||||
        'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
 | 
			
		||||
      );
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith({
 | 
			
		||||
        id: assetEntityStub.image.id,
 | 
			
		||||
        originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.ext',
 | 
			
		||||
        originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@@ -147,12 +147,12 @@ describe(StorageTemplateService.name, () => {
 | 
			
		||||
 | 
			
		||||
      expect(assetMock.getAll).toHaveBeenCalled();
 | 
			
		||||
      expect(storageMock.moveFile).toHaveBeenCalledWith(
 | 
			
		||||
        '/original/path.ext',
 | 
			
		||||
        'upload/library/label-1/2023/2023-02-23/asset-id.ext',
 | 
			
		||||
        '/original/path.jpg',
 | 
			
		||||
        'upload/library/label-1/2023/2023-02-23/asset-id.jpg',
 | 
			
		||||
      );
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith({
 | 
			
		||||
        id: assetEntityStub.image.id,
 | 
			
		||||
        originalPath: 'upload/library/label-1/2023/2023-02-23/asset-id.ext',
 | 
			
		||||
        originalPath: 'upload/library/label-1/2023/2023-02-23/asset-id.jpg',
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@@ -168,8 +168,8 @@ describe(StorageTemplateService.name, () => {
 | 
			
		||||
 | 
			
		||||
      expect(assetMock.getAll).toHaveBeenCalled();
 | 
			
		||||
      expect(storageMock.moveFile).toHaveBeenCalledWith(
 | 
			
		||||
        '/original/path.ext',
 | 
			
		||||
        'upload/library/user-id/2023/2023-02-23/asset-id.ext',
 | 
			
		||||
        '/original/path.jpg',
 | 
			
		||||
        'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
 | 
			
		||||
      );
 | 
			
		||||
      expect(assetMock.save).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
@@ -187,11 +187,11 @@ describe(StorageTemplateService.name, () => {
 | 
			
		||||
      expect(assetMock.getAll).toHaveBeenCalled();
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith({
 | 
			
		||||
        id: assetEntityStub.image.id,
 | 
			
		||||
        originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.ext',
 | 
			
		||||
        originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
 | 
			
		||||
      });
 | 
			
		||||
      expect(storageMock.moveFile.mock.calls).toEqual([
 | 
			
		||||
        ['/original/path.ext', 'upload/library/user-id/2023/2023-02-23/asset-id.ext'],
 | 
			
		||||
        ['upload/library/user-id/2023/2023-02-23/asset-id.ext', '/original/path.ext'],
 | 
			
		||||
        ['/original/path.jpg', 'upload/library/user-id/2023/2023-02-23/asset-id.jpg'],
 | 
			
		||||
        ['upload/library/user-id/2023/2023-02-23/asset-id.jpg', '/original/path.jpg'],
 | 
			
		||||
      ]);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@@ -200,7 +200,7 @@ describe(StorageTemplateService.name, () => {
 | 
			
		||||
        items: [
 | 
			
		||||
          {
 | 
			
		||||
            ...assetEntityStub.image,
 | 
			
		||||
            originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.ext',
 | 
			
		||||
            originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
 | 
			
		||||
            isReadOnly: true,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user