mirror of
https://github.com/samsonjs/immich.git
synced 2026-04-27 15:07:45 +00:00
fix(server): prevent duplicate metadata items from being sent (#25267)
This commit is contained in:
parent
56dfdfd033
commit
21802ab5ba
2 changed files with 58 additions and 1 deletions
|
|
@ -2,7 +2,7 @@ import { BadRequestException } from '@nestjs/common';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { MapAsset } from 'src/dtos/asset-response.dto';
|
import { MapAsset } from 'src/dtos/asset-response.dto';
|
||||||
import { AssetJobName, AssetStatsResponseDto } from 'src/dtos/asset.dto';
|
import { AssetJobName, AssetStatsResponseDto } from 'src/dtos/asset.dto';
|
||||||
import { AssetStatus, AssetType, AssetVisibility, JobName, JobStatus } from 'src/enum';
|
import { AssetMetadataKey, AssetStatus, AssetType, AssetVisibility, JobName, JobStatus } from 'src/enum';
|
||||||
import { AssetStats } from 'src/repositories/asset.repository';
|
import { AssetStats } from 'src/repositories/asset.repository';
|
||||||
import { AssetService } from 'src/services/asset.service';
|
import { AssetService } from 'src/services/asset.service';
|
||||||
import { assetStub } from 'test/fixtures/asset.stub';
|
import { assetStub } from 'test/fixtures/asset.stub';
|
||||||
|
|
@ -777,4 +777,40 @@ describe(AssetService.name, () => {
|
||||||
expect(result).toEqual(assets.map((asset) => asset.deviceAssetId));
|
expect(result).toEqual(assets.map((asset) => asset.deviceAssetId));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('upsertMetadata', () => {
|
||||||
|
it('should throw a bad request exception if duplicate keys are sent', async () => {
|
||||||
|
const asset = factory.asset();
|
||||||
|
const items = [
|
||||||
|
{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'id1' } },
|
||||||
|
{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'id1' } },
|
||||||
|
];
|
||||||
|
|
||||||
|
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([asset.id]));
|
||||||
|
|
||||||
|
await expect(sut.upsertMetadata(authStub.admin, asset.id, { items })).rejects.toThrowError(
|
||||||
|
'Duplicate items are not allowed:',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mocks.asset.upsertBulkMetadata).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('upsertBulkMetadata', () => {
|
||||||
|
it('should throw a bad request exception if duplicate keys are sent', async () => {
|
||||||
|
const asset = factory.asset();
|
||||||
|
const items = [
|
||||||
|
{ assetId: asset.id, key: AssetMetadataKey.MobileApp, value: { iCloudId: 'id1' } },
|
||||||
|
{ assetId: asset.id, key: AssetMetadataKey.MobileApp, value: { iCloudId: 'id1' } },
|
||||||
|
];
|
||||||
|
|
||||||
|
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([asset.id]));
|
||||||
|
|
||||||
|
await expect(sut.upsertBulkMetadata(authStub.admin, { items })).rejects.toThrowError(
|
||||||
|
'Duplicate items are not allowed:',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mocks.asset.upsertBulkMetadata).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -414,11 +414,32 @@ export class AssetService extends BaseService {
|
||||||
|
|
||||||
async upsertBulkMetadata(auth: AuthDto, dto: AssetMetadataBulkUpsertDto): Promise<AssetMetadataBulkResponseDto[]> {
|
async upsertBulkMetadata(auth: AuthDto, dto: AssetMetadataBulkUpsertDto): Promise<AssetMetadataBulkResponseDto[]> {
|
||||||
await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: dto.items.map((item) => item.assetId) });
|
await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: dto.items.map((item) => item.assetId) });
|
||||||
|
|
||||||
|
const uniqueKeys = new Set<string>();
|
||||||
|
for (const item of dto.items) {
|
||||||
|
const key = `(${item.assetId}, ${item.key})`;
|
||||||
|
if (uniqueKeys.has(key)) {
|
||||||
|
throw new BadRequestException(`Duplicate items are not allowed: "${key}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueKeys.add(key);
|
||||||
|
}
|
||||||
|
|
||||||
return this.assetRepository.upsertBulkMetadata(dto.items);
|
return this.assetRepository.upsertBulkMetadata(dto.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
async upsertMetadata(auth: AuthDto, id: string, dto: AssetMetadataUpsertDto): Promise<AssetMetadataResponseDto[]> {
|
async upsertMetadata(auth: AuthDto, id: string, dto: AssetMetadataUpsertDto): Promise<AssetMetadataResponseDto[]> {
|
||||||
await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: [id] });
|
await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: [id] });
|
||||||
|
|
||||||
|
const uniqueKeys = new Set<string>();
|
||||||
|
for (const { key } of dto.items) {
|
||||||
|
if (uniqueKeys.has(key)) {
|
||||||
|
throw new BadRequestException(`Duplicate items are not allowed: "${key}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueKeys.add(key);
|
||||||
|
}
|
||||||
|
|
||||||
return this.assetRepository.upsertMetadata(id, dto.items);
|
return this.assetRepository.upsertMetadata(id, dto.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue