restore: bulk actions (#3730)

* feat: improve bulk isArchive and isFavorite updates

* chore: open api
This commit is contained in:
Jason Rasmussen
2023-08-16 16:04:55 -04:00
committed by GitHub
parent 8568ec838a
commit bab739efbd
30 changed files with 734 additions and 57 deletions

View File

@@ -79,6 +79,7 @@ export interface IAssetRepository {
getLastUpdatedAssetForAlbumId(albumId: string): Promise<AssetEntity | null>;
deleteAll(ownerId: string): Promise<void>;
getAll(pagination: PaginationOptions, options?: AssetSearchOptions): Paginated<AssetEntity>;
updateAll(ids: string[], options: Partial<AssetEntity>): Promise<void>;
save(asset: Partial<AssetEntity>): Promise<AssetEntity>;
findLivePhotoMatch(options: LivePhotoSearchOptions): Promise<AssetEntity | null>;
getMapMarkers(ownerId: string, options?: MapMarkerSearchOptions): Promise<MapMarker[]>;

View File

@@ -514,4 +514,22 @@ describe(AssetService.name, () => {
expect(assetMock.getStatistics).toHaveBeenCalledWith(authStub.admin.id, {});
});
});
describe('updateAll', () => {
it('should require asset write access for all ids', async () => {
accessMock.asset.hasOwnerAccess.mockResolvedValue(false);
await expect(
sut.updateAll(authStub.admin, {
ids: ['asset-1'],
isArchived: false,
}),
).rejects.toBeInstanceOf(BadRequestException);
});
it('should update all assets', async () => {
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
await sut.updateAll(authStub.admin, { ids: ['asset-1', 'asset-2'], isArchived: true });
expect(assetMock.updateAll).toHaveBeenCalledWith(['asset-1', 'asset-2'], { isArchived: true });
});
});
});

View File

@@ -11,6 +11,7 @@ import { HumanReadableSize, usePagination } from '../domain.util';
import { ImmichReadStream, IStorageRepository, StorageCore, StorageFolder } from '../storage';
import { IAssetRepository } from './asset.repository';
import {
AssetBulkUpdateDto,
AssetIdsDto,
DownloadArchiveInfo,
DownloadInfoDto,
@@ -268,4 +269,10 @@ export class AssetService {
const stats = await this.assetRepository.getStatistics(authUser.id, dto);
return mapStats(stats);
}
async updateAll(authUser: AuthUserDto, dto: AssetBulkUpdateDto) {
const { ids, ...options } = dto;
await this.access.requirePermission(authUser, Permission.ASSET_UPDATE, ids);
await this.assetRepository.updateAll(ids, options);
}
}

View File

@@ -0,0 +1,12 @@
import { IsBoolean, IsOptional } from 'class-validator';
import { BulkIdsDto } from '../response-dto';
export class AssetBulkUpdateDto extends BulkIdsDto {
@IsOptional()
@IsBoolean()
isFavorite?: boolean;
@IsOptional()
@IsBoolean()
isArchived?: boolean;
}

View File

@@ -1,5 +1,6 @@
export * from './asset-ids.dto';
export * from './asset-statistics.dto';
export * from './asset.dto';
export * from './download.dto';
export * from './map-marker.dto';
export * from './memory-lane.dto';