mirror of
https://github.com/immich-app/immich.git
synced 2025-07-31 15:08:44 -04:00
fix(server): clear activity when asset is removed from album (#19019)
This commit is contained in:
parent
ed4c7817e7
commit
55fe67dd20
@ -7,6 +7,7 @@ import {
|
|||||||
ReactionType,
|
ReactionType,
|
||||||
createActivity as create,
|
createActivity as create,
|
||||||
createAlbum,
|
createAlbum,
|
||||||
|
removeAssetFromAlbum,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
@ -342,5 +343,36 @@ describe('/activities', () => {
|
|||||||
|
|
||||||
expect(status).toBe(204);
|
expect(status).toBe(204);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return empty list when asset is removed', async () => {
|
||||||
|
const album3 = await createAlbum(
|
||||||
|
{
|
||||||
|
createAlbumDto: {
|
||||||
|
albumName: 'Album 3',
|
||||||
|
assetIds: [asset.id],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ headers: asBearerAuth(admin.accessToken) },
|
||||||
|
);
|
||||||
|
|
||||||
|
await createActivity({ albumId: album3.id, assetId: asset.id, type: ReactionType.Like });
|
||||||
|
|
||||||
|
await removeAssetFromAlbum(
|
||||||
|
{
|
||||||
|
id: album3.id,
|
||||||
|
bulkIdsDto: {
|
||||||
|
ids: [asset.id],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ headers: asBearerAuth(admin.accessToken) },
|
||||||
|
);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/activities')
|
||||||
|
.query({ albumId: album.id })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toEqual(200);
|
||||||
|
expect(body).toEqual([]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import { Kysely, sql } from 'kysely';
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await sql`DELETE FROM activity AS a
|
||||||
|
WHERE a."assetId" IS NOT NULL
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM albums_assets_assets AS aaa
|
||||||
|
WHERE a."albumId" = aaa."albumsId"
|
||||||
|
AND a."assetId" = aaa."assetsId"
|
||||||
|
);`.execute(db);
|
||||||
|
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);
|
||||||
|
await sql`CREATE INDEX "IDX_86102d85cfa7f196073aebff68" ON "activity" ("albumId", "assetId")`.execute(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(db: Kysely<any>): Promise<void> {
|
||||||
|
await sql`DROP INDEX "IDX_86102d85cfa7f196073aebff68";`.execute(db);
|
||||||
|
await sql`ALTER TABLE "activity" DROP CONSTRAINT "fk_activity_album_asset_composite";`.execute(db);
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||||
|
import { AlbumAssetTable } from 'src/schema/tables/album-asset.table';
|
||||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||||
import { UserTable } from 'src/schema/tables/user.table';
|
import { UserTable } from 'src/schema/tables/user.table';
|
||||||
@ -7,6 +8,7 @@ import {
|
|||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
ForeignKeyColumn,
|
ForeignKeyColumn,
|
||||||
|
ForeignKeyConstraint,
|
||||||
Generated,
|
Generated,
|
||||||
Index,
|
Index,
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
@ -27,6 +29,14 @@ import {
|
|||||||
name: 'CHK_2ab1e70f113f450eb40c1e3ec8',
|
name: 'CHK_2ab1e70f113f450eb40c1e3ec8',
|
||||||
expression: `("comment" IS NULL AND "isLiked" = true) OR ("comment" IS NOT NULL AND "isLiked" = false)`,
|
expression: `("comment" IS NULL AND "isLiked" = true) OR ("comment" IS NOT NULL AND "isLiked" = false)`,
|
||||||
})
|
})
|
||||||
|
@ForeignKeyConstraint({
|
||||||
|
name: 'fk_activity_album_asset_composite',
|
||||||
|
columns: ['albumId', 'assetId'],
|
||||||
|
referenceTable: () => AlbumAssetTable,
|
||||||
|
referenceColumns: ['albumsId', 'assetsId'],
|
||||||
|
onUpdate: 'NO ACTION',
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
export class ActivityTable {
|
export class ActivityTable {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id!: Generated<string>;
|
id!: Generated<string>;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user