Skip to content

Commit 55fe67d

Browse files
authored
fix(server): clear activity when asset is removed from album (#19019)
1 parent ed4c781 commit 55fe67d

File tree

3 files changed

+61
-0
lines changed

3 files changed

+61
-0
lines changed

e2e/src/api/specs/activity.e2e-spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
ReactionType,
88
createActivity as create,
99
createAlbum,
10+
removeAssetFromAlbum,
1011
} from '@immich/sdk';
1112
import { createUserDto, uuidDto } from 'src/fixtures';
1213
import { errorDto } from 'src/responses';
@@ -342,5 +343,36 @@ describe('/activities', () => {
342343

343344
expect(status).toBe(204);
344345
});
346+
347+
it('should return empty list when asset is removed', async () => {
348+
const album3 = await createAlbum(
349+
{
350+
createAlbumDto: {
351+
albumName: 'Album 3',
352+
assetIds: [asset.id],
353+
},
354+
},
355+
{ headers: asBearerAuth(admin.accessToken) },
356+
);
357+
358+
await createActivity({ albumId: album3.id, assetId: asset.id, type: ReactionType.Like });
359+
360+
await removeAssetFromAlbum(
361+
{
362+
id: album3.id,
363+
bulkIdsDto: {
364+
ids: [asset.id],
365+
},
366+
},
367+
{ headers: asBearerAuth(admin.accessToken) },
368+
);
369+
370+
const { status, body } = await request(app)
371+
.get('/activities')
372+
.query({ albumId: album.id })
373+
.set('Authorization', `Bearer ${admin.accessToken}`);
374+
expect(status).toEqual(200);
375+
expect(body).toEqual([]);
376+
});
345377
});
346378
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Kysely, sql } from 'kysely';
2+
3+
export async function up(db: Kysely<any>): Promise<void> {
4+
await sql`DELETE FROM activity AS a
5+
WHERE a."assetId" IS NOT NULL
6+
AND NOT EXISTS (
7+
SELECT 1
8+
FROM albums_assets_assets AS aaa
9+
WHERE a."albumId" = aaa."albumsId"
10+
AND a."assetId" = aaa."assetsId"
11+
);`.execute(db);
12+
await sql`ALTER TABLE "activity" ADD CONSTRAINT "fk_activity_album_asset_composite" FOREIGN KEY ("albumId", "assetId") REFERENCES "albums_assets_assets" ("albumsId", "assetsId") ON UPDATE NO ACTION ON DELETE CASCADE;`.execute(db);
13+
await sql`CREATE INDEX "IDX_86102d85cfa7f196073aebff68" ON "activity" ("albumId", "assetId")`.execute(db);
14+
}
15+
16+
export async function down(db: Kysely<any>): Promise<void> {
17+
await sql`DROP INDEX "IDX_86102d85cfa7f196073aebff68";`.execute(db);
18+
await sql`ALTER TABLE "activity" DROP CONSTRAINT "fk_activity_album_asset_composite";`.execute(db);
19+
}

server/src/schema/tables/activity.table.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
2+
import { AlbumAssetTable } from 'src/schema/tables/album-asset.table';
23
import { AlbumTable } from 'src/schema/tables/album.table';
34
import { AssetTable } from 'src/schema/tables/asset.table';
45
import { UserTable } from 'src/schema/tables/user.table';
@@ -7,6 +8,7 @@ import {
78
Column,
89
CreateDateColumn,
910
ForeignKeyColumn,
11+
ForeignKeyConstraint,
1012
Generated,
1113
Index,
1214
PrimaryGeneratedColumn,
@@ -27,6 +29,14 @@ import {
2729
name: 'CHK_2ab1e70f113f450eb40c1e3ec8',
2830
expression: `("comment" IS NULL AND "isLiked" = true) OR ("comment" IS NOT NULL AND "isLiked" = false)`,
2931
})
32+
@ForeignKeyConstraint({
33+
name: 'fk_activity_album_asset_composite',
34+
columns: ['albumId', 'assetId'],
35+
referenceTable: () => AlbumAssetTable,
36+
referenceColumns: ['albumsId', 'assetsId'],
37+
onUpdate: 'NO ACTION',
38+
onDelete: 'CASCADE',
39+
})
3040
export class ActivityTable {
3141
@PrimaryGeneratedColumn()
3242
id!: Generated<string>;

0 commit comments

Comments
 (0)