refactor(server): extract add/remove assets logic to utility function (#8329)
extract add/remove assets logic to utility function fix tests chore: generate sql foo
This commit is contained in:
@@ -518,10 +518,7 @@ describe(AlbumService.name, () => {
|
||||
updatedAt: expect.any(Date),
|
||||
albumThumbnailAssetId: 'asset-1',
|
||||
});
|
||||
expect(albumMock.addAssets).toHaveBeenCalledWith({
|
||||
albumId: 'album-123',
|
||||
assetIds: ['asset-1', 'asset-2', 'asset-3'],
|
||||
});
|
||||
expect(albumMock.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']);
|
||||
});
|
||||
|
||||
it('should not set the thumbnail if the album has one already', async () => {
|
||||
@@ -539,7 +536,7 @@ describe(AlbumService.name, () => {
|
||||
updatedAt: expect.any(Date),
|
||||
albumThumbnailAssetId: 'asset-id',
|
||||
});
|
||||
expect(albumMock.addAssets).toHaveBeenCalled();
|
||||
expect(albumMock.addAssetIds).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should allow a shared user to add assets', async () => {
|
||||
@@ -561,10 +558,7 @@ describe(AlbumService.name, () => {
|
||||
updatedAt: expect.any(Date),
|
||||
albumThumbnailAssetId: 'asset-1',
|
||||
});
|
||||
expect(albumMock.addAssets).toHaveBeenCalledWith({
|
||||
albumId: 'album-123',
|
||||
assetIds: ['asset-1', 'asset-2', 'asset-3'],
|
||||
});
|
||||
expect(albumMock.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']);
|
||||
});
|
||||
|
||||
it('should allow a shared link user to add assets', async () => {
|
||||
@@ -586,10 +580,7 @@ describe(AlbumService.name, () => {
|
||||
updatedAt: expect.any(Date),
|
||||
albumThumbnailAssetId: 'asset-1',
|
||||
});
|
||||
expect(albumMock.addAssets).toHaveBeenCalledWith({
|
||||
albumId: 'album-123',
|
||||
assetIds: ['asset-1', 'asset-2', 'asset-3'],
|
||||
});
|
||||
expect(albumMock.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']);
|
||||
|
||||
expect(accessMock.album.checkSharedLinkAccess).toHaveBeenCalledWith(
|
||||
authStub.adminSharedLink.sharedLink?.id,
|
||||
@@ -665,23 +656,23 @@ describe(AlbumService.name, () => {
|
||||
|
||||
describe('removeAssets', () => {
|
||||
it('should allow the owner to remove assets', async () => {
|
||||
accessMock.album.checkOwnerAccess.mockResolvedValueOnce(new Set(['album-123']));
|
||||
accessMock.album.checkOwnerAccess.mockResolvedValueOnce(new Set(['asset-id']));
|
||||
accessMock.album.checkOwnerAccess.mockResolvedValue(new Set(['album-123']));
|
||||
accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-id']));
|
||||
albumMock.getById.mockResolvedValue(_.cloneDeep(albumStub.oneAsset));
|
||||
albumMock.getAssetIds.mockResolvedValueOnce(new Set(['asset-id']));
|
||||
albumMock.getAssetIds.mockResolvedValue(new Set(['asset-id']));
|
||||
|
||||
await expect(sut.removeAssets(authStub.admin, 'album-123', { ids: ['asset-id'] })).resolves.toEqual([
|
||||
{ success: true, id: 'asset-id' },
|
||||
]);
|
||||
|
||||
expect(albumMock.update).toHaveBeenCalledWith({ id: 'album-123', updatedAt: expect.any(Date) });
|
||||
expect(albumMock.removeAssets).toHaveBeenCalledWith('album-123', ['asset-id']);
|
||||
expect(albumMock.removeAssetIds).toHaveBeenCalledWith('album-123', ['asset-id']);
|
||||
});
|
||||
|
||||
it('should skip assets not in the album', async () => {
|
||||
accessMock.album.checkOwnerAccess.mockResolvedValue(new Set(['album-123']));
|
||||
albumMock.getById.mockResolvedValue(_.cloneDeep(albumStub.empty));
|
||||
albumMock.getAssetIds.mockResolvedValueOnce(new Set());
|
||||
albumMock.getAssetIds.mockResolvedValue(new Set());
|
||||
|
||||
await expect(sut.removeAssets(authStub.admin, 'album-123', { ids: ['asset-id'] })).resolves.toEqual([
|
||||
{ success: false, id: 'asset-id', error: BulkIdErrorReason.NOT_FOUND },
|
||||
@@ -693,7 +684,7 @@ describe(AlbumService.name, () => {
|
||||
it('should skip assets without user permission to remove', async () => {
|
||||
accessMock.album.checkSharedAlbumAccess.mockResolvedValue(new Set(['album-123']));
|
||||
albumMock.getById.mockResolvedValue(_.cloneDeep(albumStub.oneAsset));
|
||||
albumMock.getAssetIds.mockResolvedValueOnce(new Set(['asset-id']));
|
||||
albumMock.getAssetIds.mockResolvedValue(new Set(['asset-id']));
|
||||
|
||||
await expect(sut.removeAssets(authStub.admin, 'album-123', { ids: ['asset-id'] })).resolves.toEqual([
|
||||
{
|
||||
@@ -707,10 +698,10 @@ describe(AlbumService.name, () => {
|
||||
});
|
||||
|
||||
it('should reset the thumbnail if it is removed', async () => {
|
||||
accessMock.album.checkOwnerAccess.mockResolvedValueOnce(new Set(['album-123']));
|
||||
accessMock.album.checkOwnerAccess.mockResolvedValueOnce(new Set(['asset-id']));
|
||||
accessMock.album.checkOwnerAccess.mockResolvedValue(new Set(['album-123']));
|
||||
accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-id']));
|
||||
albumMock.getById.mockResolvedValue(_.cloneDeep(albumStub.twoAssets));
|
||||
albumMock.getAssetIds.mockResolvedValueOnce(new Set(['asset-id']));
|
||||
albumMock.getAssetIds.mockResolvedValue(new Set(['asset-id']));
|
||||
|
||||
await expect(sut.removeAssets(authStub.admin, 'album-123', { ids: ['asset-id'] })).resolves.toEqual([
|
||||
{ success: true, id: 'asset-id' },
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
mapAlbumWithAssets,
|
||||
mapAlbumWithoutAssets,
|
||||
} from 'src/dtos/album.dto';
|
||||
import { BulkIdErrorReason, BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||
import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { AlbumEntity } from 'src/entities/album.entity';
|
||||
import { AssetEntity } from 'src/entities/asset.entity';
|
||||
@@ -21,13 +21,13 @@ import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||
import { AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } from 'src/interfaces/album.interface';
|
||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
||||
import { setUnion } from 'src/utils/set';
|
||||
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
||||
|
||||
@Injectable()
|
||||
export class AlbumService {
|
||||
private access: AccessCore;
|
||||
constructor(
|
||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
||||
@Inject(IAccessRepository) private accessRepository: IAccessRepository,
|
||||
@Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
|
||||
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
||||
@Inject(IUserRepository) private userRepository: IUserRepository,
|
||||
@@ -164,37 +164,20 @@ export class AlbumService {
|
||||
|
||||
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
||||
const album = await this.findOrFail(id, { withAssets: false });
|
||||
|
||||
await this.access.requirePermission(auth, Permission.ALBUM_READ, id);
|
||||
|
||||
const existingAssetIds = await this.albumRepository.getAssetIds(id, dto.ids);
|
||||
const notPresentAssetIds = dto.ids.filter((id) => !existingAssetIds.has(id));
|
||||
const allowedAssetIds = await this.access.checkAccess(auth, Permission.ASSET_SHARE, notPresentAssetIds);
|
||||
const results = await addAssets(
|
||||
auth,
|
||||
{ accessRepository: this.accessRepository, repository: this.albumRepository },
|
||||
{ id, assetIds: dto.ids },
|
||||
);
|
||||
|
||||
const results: BulkIdResponseDto[] = [];
|
||||
for (const assetId of dto.ids) {
|
||||
const hasAsset = existingAssetIds.has(assetId);
|
||||
if (hasAsset) {
|
||||
results.push({ id: assetId, success: false, error: BulkIdErrorReason.DUPLICATE });
|
||||
continue;
|
||||
}
|
||||
|
||||
const hasAccess = allowedAssetIds.has(assetId);
|
||||
if (!hasAccess) {
|
||||
results.push({ id: assetId, success: false, error: BulkIdErrorReason.NO_PERMISSION });
|
||||
continue;
|
||||
}
|
||||
|
||||
results.push({ id: assetId, success: true });
|
||||
}
|
||||
|
||||
const newAssetIds = results.filter(({ success }) => success).map(({ id }) => id);
|
||||
if (newAssetIds.length > 0) {
|
||||
await this.albumRepository.addAssets({ albumId: id, assetIds: newAssetIds });
|
||||
const { id: firstNewAssetId } = results.find(({ success }) => success) || {};
|
||||
if (firstNewAssetId) {
|
||||
await this.albumRepository.update({
|
||||
id,
|
||||
updatedAt: new Date(),
|
||||
albumThumbnailAssetId: album.albumThumbnailAssetId ?? newAssetIds[0],
|
||||
albumThumbnailAssetId: album.albumThumbnailAssetId ?? firstNewAssetId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -206,31 +189,14 @@ export class AlbumService {
|
||||
|
||||
await this.access.requirePermission(auth, Permission.ALBUM_READ, id);
|
||||
|
||||
const existingAssetIds = await this.albumRepository.getAssetIds(id, dto.ids);
|
||||
const canRemove = await this.access.checkAccess(auth, Permission.ALBUM_REMOVE_ASSET, existingAssetIds);
|
||||
const canShare = await this.access.checkAccess(auth, Permission.ASSET_SHARE, existingAssetIds);
|
||||
const allowedAssetIds = setUnion(canRemove, canShare);
|
||||
|
||||
const results: BulkIdResponseDto[] = [];
|
||||
for (const assetId of dto.ids) {
|
||||
const hasAsset = existingAssetIds.has(assetId);
|
||||
if (!hasAsset) {
|
||||
results.push({ id: assetId, success: false, error: BulkIdErrorReason.NOT_FOUND });
|
||||
continue;
|
||||
}
|
||||
|
||||
const hasAccess = allowedAssetIds.has(assetId);
|
||||
if (!hasAccess) {
|
||||
results.push({ id: assetId, success: false, error: BulkIdErrorReason.NO_PERMISSION });
|
||||
continue;
|
||||
}
|
||||
|
||||
results.push({ id: assetId, success: true });
|
||||
}
|
||||
const results = await removeAssets(
|
||||
auth,
|
||||
{ accessRepository: this.accessRepository, repository: this.albumRepository },
|
||||
{ id, assetIds: dto.ids, permissions: [Permission.ASSET_SHARE, Permission.ALBUM_REMOVE_ASSET] },
|
||||
);
|
||||
|
||||
const removedIds = results.filter(({ success }) => success).map(({ id }) => id);
|
||||
if (removedIds.length > 0) {
|
||||
await this.albumRepository.removeAssets(id, removedIds);
|
||||
await this.albumRepository.update({ id, updatedAt: new Date() });
|
||||
if (album.albumThumbnailAssetId && removedIds.includes(album.albumThumbnailAssetId)) {
|
||||
await this.albumRepository.updateThumbnails();
|
||||
|
||||
Reference in New Issue
Block a user