mirror of
https://github.com/immich-app/immich.git
synced 2026-02-04 08:49:01 +03:00
fix: add asset upload medium test (#25144)
This commit is contained in:
14
mobile/openapi/lib/api/assets_api.dart
generated
14
mobile/openapi/lib/api/assets_api.dart
generated
@@ -1353,8 +1353,6 @@ class AssetsApi {
|
||||
///
|
||||
/// * [DateTime] fileModifiedAt (required):
|
||||
///
|
||||
/// * [List<AssetMetadataUpsertItemDto>] metadata (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
///
|
||||
/// * [String] slug:
|
||||
@@ -1370,10 +1368,12 @@ class AssetsApi {
|
||||
///
|
||||
/// * [String] livePhotoVideoId:
|
||||
///
|
||||
/// * [List<AssetMetadataUpsertItemDto>] metadata:
|
||||
///
|
||||
/// * [MultipartFile] sidecarData:
|
||||
///
|
||||
/// * [AssetVisibility] visibility:
|
||||
Future<Response> uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, List<AssetMetadataUpsertItemDto> metadata, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
|
||||
Future<Response> uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List<AssetMetadataUpsertItemDto>? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final apiPath = r'/assets';
|
||||
|
||||
@@ -1480,8 +1480,6 @@ class AssetsApi {
|
||||
///
|
||||
/// * [DateTime] fileModifiedAt (required):
|
||||
///
|
||||
/// * [List<AssetMetadataUpsertItemDto>] metadata (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
///
|
||||
/// * [String] slug:
|
||||
@@ -1497,11 +1495,13 @@ class AssetsApi {
|
||||
///
|
||||
/// * [String] livePhotoVideoId:
|
||||
///
|
||||
/// * [List<AssetMetadataUpsertItemDto>] metadata:
|
||||
///
|
||||
/// * [MultipartFile] sidecarData:
|
||||
///
|
||||
/// * [AssetVisibility] visibility:
|
||||
Future<AssetMediaResponseDto?> uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, List<AssetMetadataUpsertItemDto> metadata, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
|
||||
final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, metadata, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, );
|
||||
Future<AssetMediaResponseDto?> uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List<AssetMetadataUpsertItemDto>? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
|
||||
final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, metadata: metadata, sidecarData: sidecarData, visibility: visibility, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
|
||||
@@ -15605,8 +15605,7 @@
|
||||
"deviceAssetId",
|
||||
"deviceId",
|
||||
"fileCreatedAt",
|
||||
"fileModifiedAt",
|
||||
"metadata"
|
||||
"fileModifiedAt"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
||||
@@ -484,7 +484,7 @@ export type AssetMediaCreateDto = {
|
||||
filename?: string;
|
||||
isFavorite?: boolean;
|
||||
livePhotoVideoId?: string;
|
||||
metadata: AssetMetadataUpsertItemDto[];
|
||||
metadata?: AssetMetadataUpsertItemDto[];
|
||||
sidecarData?: Blob;
|
||||
visibility?: AssetVisibility;
|
||||
};
|
||||
|
||||
@@ -78,7 +78,7 @@ export class AssetMediaCreateDto extends AssetMediaBase {
|
||||
@Optional()
|
||||
@ValidateNested({ each: true })
|
||||
@IsArray()
|
||||
metadata!: AssetMetadataUpsertItemDto[];
|
||||
metadata?: AssetMetadataUpsertItemDto[];
|
||||
|
||||
@ApiProperty({ type: 'string', format: 'binary', required: false })
|
||||
[UploadFieldName.SIDECAR_DATA]?: any;
|
||||
|
||||
@@ -258,6 +258,10 @@ export class AssetRepository {
|
||||
}
|
||||
|
||||
upsertMetadata(id: string, items: Array<{ key: string; value: object }>) {
|
||||
if (items.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.db
|
||||
.insertInto('asset_metadata')
|
||||
.values(items.map((item) => ({ assetId: id, ...item })))
|
||||
|
||||
@@ -69,6 +69,7 @@ import { UserTable } from 'src/schema/tables/user.table';
|
||||
import { BASE_SERVICE_DEPENDENCIES, BaseService } from 'src/services/base.service';
|
||||
import { MetadataService } from 'src/services/metadata.service';
|
||||
import { SyncService } from 'src/services/sync.service';
|
||||
import { UploadFile } from 'src/types';
|
||||
import { mockEnvData } from 'test/repositories/config.repository.mock';
|
||||
import { newTelemetryRepositoryMock } from 'test/repositories/telemetry.repository.mock';
|
||||
import { factory, newDate, newEmbedding, newUuid } from 'test/small.factory';
|
||||
@@ -746,6 +747,17 @@ const loginResponse = (): LoginResponseDto => {
|
||||
};
|
||||
};
|
||||
|
||||
const uploadFile = (file: Partial<UploadFile> = {}) => {
|
||||
return {
|
||||
uuid: newUuid(),
|
||||
checksum: randomBytes(32),
|
||||
originalPath: '/path/to/file.jpg',
|
||||
originalName: 'file.jpg',
|
||||
size: 123_456,
|
||||
...file,
|
||||
};
|
||||
};
|
||||
|
||||
export const mediumFactory = {
|
||||
assetInsert,
|
||||
assetFaceInsert,
|
||||
@@ -760,4 +772,5 @@ export const mediumFactory = {
|
||||
loginDetails,
|
||||
loginResponse,
|
||||
tagInsert,
|
||||
uploadFile,
|
||||
};
|
||||
|
||||
100
server/test/medium/specs/services/asset-media.service.spec.ts
Normal file
100
server/test/medium/specs/services/asset-media.service.spec.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { Kysely } from 'kysely';
|
||||
import { AssetMediaStatus } from 'src/dtos/asset-media-response.dto';
|
||||
import { AccessRepository } from 'src/repositories/access.repository';
|
||||
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||
import { EventRepository } from 'src/repositories/event.repository';
|
||||
import { JobRepository } from 'src/repositories/job.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { StorageRepository } from 'src/repositories/storage.repository';
|
||||
import { UserRepository } from 'src/repositories/user.repository';
|
||||
import { DB } from 'src/schema';
|
||||
import { AssetMediaService } from 'src/services/asset-media.service';
|
||||
import { AssetService } from 'src/services/asset.service';
|
||||
import { mediumFactory, newMediumService } from 'test/medium.factory';
|
||||
import { factory } from 'test/small.factory';
|
||||
import { getKyselyDB } from 'test/utils';
|
||||
|
||||
let defaultDatabase: Kysely<DB>;
|
||||
|
||||
const setup = (db?: Kysely<DB>) => {
|
||||
return newMediumService(AssetMediaService, {
|
||||
database: db || defaultDatabase,
|
||||
real: [AccessRepository, AssetRepository, UserRepository],
|
||||
mock: [EventRepository, LoggingRepository, JobRepository, StorageRepository],
|
||||
});
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
defaultDatabase = await getKyselyDB();
|
||||
});
|
||||
|
||||
describe(AssetService.name, () => {
|
||||
describe('uploadAsset', () => {
|
||||
it('should work', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
|
||||
ctx.getMock(StorageRepository).utimes.mockResolvedValue();
|
||||
ctx.getMock(EventRepository).emit.mockResolvedValue();
|
||||
ctx.getMock(JobRepository).queue.mockResolvedValue();
|
||||
|
||||
const { user } = await ctx.newUser();
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: asset.id, fileSizeInByte: 12_345 });
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
const file = mediumFactory.uploadFile();
|
||||
|
||||
await expect(
|
||||
sut.uploadAsset(
|
||||
auth,
|
||||
{
|
||||
deviceId: 'some-id',
|
||||
deviceAssetId: 'some-id',
|
||||
fileModifiedAt: new Date(),
|
||||
fileCreatedAt: new Date(),
|
||||
assetData: Buffer.from('some data'),
|
||||
},
|
||||
file,
|
||||
),
|
||||
).resolves.toEqual({
|
||||
id: expect.any(String),
|
||||
status: AssetMediaStatus.CREATED,
|
||||
});
|
||||
|
||||
expect(ctx.getMock(EventRepository).emit).toHaveBeenCalledWith('AssetCreate', {
|
||||
asset: expect.objectContaining({ deviceAssetId: 'some-id' }),
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with an empty metadata list', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
|
||||
ctx.getMock(StorageRepository).utimes.mockResolvedValue();
|
||||
ctx.getMock(EventRepository).emit.mockResolvedValue();
|
||||
ctx.getMock(JobRepository).queue.mockResolvedValue();
|
||||
|
||||
const { user } = await ctx.newUser();
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: asset.id, fileSizeInByte: 12_345 });
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
const file = mediumFactory.uploadFile();
|
||||
|
||||
await expect(
|
||||
sut.uploadAsset(
|
||||
auth,
|
||||
{
|
||||
deviceId: 'some-id',
|
||||
deviceAssetId: 'some-id',
|
||||
fileModifiedAt: new Date(),
|
||||
fileCreatedAt: new Date(),
|
||||
assetData: Buffer.from('some data'),
|
||||
metadata: [],
|
||||
},
|
||||
file,
|
||||
),
|
||||
).resolves.toEqual({
|
||||
id: expect.any(String),
|
||||
status: AssetMediaStatus.CREATED,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user