mirror of
https://github.com/immich-app/immich.git
synced 2025-06-03 21:54:21 -04:00
parent
5dac315af7
commit
a373034629
@ -1,5 +1,6 @@
|
|||||||
import { Selectable } from 'kysely';
|
import { Selectable } from 'kysely';
|
||||||
import { Exif as DatabaseExif } from 'src/db';
|
import { Exif as DatabaseExif } from 'src/db';
|
||||||
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import {
|
import {
|
||||||
AlbumUserRole,
|
AlbumUserRole,
|
||||||
AssetFileType,
|
AssetFileType,
|
||||||
@ -147,6 +148,7 @@ export type Asset = {
|
|||||||
originalPath: string;
|
originalPath: string;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
sidecarPath: string | null;
|
sidecarPath: string | null;
|
||||||
|
stack?: Stack | null;
|
||||||
stackId: string | null;
|
stackId: string | null;
|
||||||
thumbhash: Buffer<ArrayBufferLike> | null;
|
thumbhash: Buffer<ArrayBufferLike> | null;
|
||||||
type: AssetType;
|
type: AssetType;
|
||||||
@ -159,6 +161,15 @@ export type SidecarWriteAsset = {
|
|||||||
tags: Array<{ value: string }>;
|
tags: Array<{ value: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Stack = {
|
||||||
|
id: string;
|
||||||
|
primaryAssetId: string;
|
||||||
|
owner?: User;
|
||||||
|
ownerId: string;
|
||||||
|
assets: AssetEntity[];
|
||||||
|
assetCount?: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type AuthSharedLink = {
|
export type AuthSharedLink = {
|
||||||
id: string;
|
id: string;
|
||||||
expiresAt: Date | null;
|
expiresAt: Date | null;
|
||||||
@ -276,7 +287,7 @@ export const columns = {
|
|||||||
'quotaSizeInBytes',
|
'quotaSizeInBytes',
|
||||||
'quotaUsageInBytes',
|
'quotaUsageInBytes',
|
||||||
],
|
],
|
||||||
tagDto: ['id', 'value', 'createdAt', 'updatedAt', 'color', 'parentId'],
|
tag: ['tags.id', 'tags.value', 'tags.createdAt', 'tags.updatedAt', 'tags.color', 'tags.parentId'],
|
||||||
apiKey: ['id', 'name', 'userId', 'createdAt', 'updatedAt', 'permissions'],
|
apiKey: ['id', 'name', 'userId', 'createdAt', 'updatedAt', 'permissions'],
|
||||||
syncAsset: [
|
syncAsset: [
|
||||||
'id',
|
'id',
|
||||||
@ -292,6 +303,7 @@ export const columns = {
|
|||||||
'isVisible',
|
'isVisible',
|
||||||
'updateId',
|
'updateId',
|
||||||
],
|
],
|
||||||
|
stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'],
|
||||||
syncAssetExif: [
|
syncAssetExif: [
|
||||||
'exif.assetId',
|
'exif.assetId',
|
||||||
'exif.description',
|
'exif.description',
|
||||||
@ -320,4 +332,35 @@ export const columns = {
|
|||||||
'exif.fps',
|
'exif.fps',
|
||||||
'exif.updateId',
|
'exif.updateId',
|
||||||
],
|
],
|
||||||
|
exif: [
|
||||||
|
'exif.assetId',
|
||||||
|
'exif.autoStackId',
|
||||||
|
'exif.bitsPerSample',
|
||||||
|
'exif.city',
|
||||||
|
'exif.colorspace',
|
||||||
|
'exif.country',
|
||||||
|
'exif.dateTimeOriginal',
|
||||||
|
'exif.description',
|
||||||
|
'exif.exifImageHeight',
|
||||||
|
'exif.exifImageWidth',
|
||||||
|
'exif.exposureTime',
|
||||||
|
'exif.fileSizeInByte',
|
||||||
|
'exif.fNumber',
|
||||||
|
'exif.focalLength',
|
||||||
|
'exif.fps',
|
||||||
|
'exif.iso',
|
||||||
|
'exif.latitude',
|
||||||
|
'exif.lensModel',
|
||||||
|
'exif.livePhotoCID',
|
||||||
|
'exif.longitude',
|
||||||
|
'exif.make',
|
||||||
|
'exif.model',
|
||||||
|
'exif.modifyDate',
|
||||||
|
'exif.orientation',
|
||||||
|
'exif.profileDescription',
|
||||||
|
'exif.projectionType',
|
||||||
|
'exif.rating',
|
||||||
|
'exif.state',
|
||||||
|
'exif.timeZone',
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ArrayMinSize } from 'class-validator';
|
import { ArrayMinSize } from 'class-validator';
|
||||||
|
import { Stack } from 'src/database';
|
||||||
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { StackEntity } from 'src/entities/stack.entity';
|
|
||||||
import { ValidateUUID } from 'src/validation';
|
import { ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export class StackCreateDto {
|
export class StackCreateDto {
|
||||||
@ -27,7 +27,7 @@ export class StackResponseDto {
|
|||||||
assets!: AssetResponseDto[];
|
assets!: AssetResponseDto[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mapStack = (stack: StackEntity, { auth }: { auth?: AuthDto }) => {
|
export const mapStack = (stack: Stack, { auth }: { auth?: AuthDto }) => {
|
||||||
const primary = stack.assets.filter((asset) => asset.id === stack.primaryAssetId);
|
const primary = stack.assets.filter((asset) => asset.id === stack.primaryAssetId);
|
||||||
const others = stack.assets.filter((asset) => asset.id !== stack.primaryAssetId);
|
const others = stack.assets.filter((asset) => asset.id !== stack.primaryAssetId);
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { DeduplicateJoinsPlugin, ExpressionBuilder, Kysely, SelectQueryBuilder, sql } from 'kysely';
|
import { DeduplicateJoinsPlugin, ExpressionBuilder, Kysely, SelectQueryBuilder, sql } from 'kysely';
|
||||||
import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres';
|
import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||||
import { AssetFace, AssetFile, Exif, Tag, User } from 'src/database';
|
import { AssetFace, AssetFile, Exif, Stack, Tag, User } from 'src/database';
|
||||||
import { DB } from 'src/db';
|
import { DB } from 'src/db';
|
||||||
import { AlbumEntity } from 'src/entities/album.entity';
|
import { AlbumEntity } from 'src/entities/album.entity';
|
||||||
import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity';
|
import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity';
|
||||||
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
|
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
|
||||||
import { StackEntity } from 'src/entities/stack.entity';
|
|
||||||
import { AssetFileType, AssetStatus, AssetType } from 'src/enum';
|
import { AssetFileType, AssetStatus, AssetType } from 'src/enum';
|
||||||
import { TimeBucketSize } from 'src/repositories/asset.repository';
|
import { TimeBucketSize } from 'src/repositories/asset.repository';
|
||||||
import { AssetSearchBuilderOptions } from 'src/repositories/search.repository';
|
import { AssetSearchBuilderOptions } from 'src/repositories/search.repository';
|
||||||
@ -50,7 +49,7 @@ export class AssetEntity {
|
|||||||
albums?: AlbumEntity[];
|
albums?: AlbumEntity[];
|
||||||
faces!: AssetFace[];
|
faces!: AssetFace[];
|
||||||
stackId?: string | null;
|
stackId?: string | null;
|
||||||
stack?: StackEntity | null;
|
stack?: Stack | null;
|
||||||
jobStatus?: AssetJobStatusEntity;
|
jobStatus?: AssetJobStatusEntity;
|
||||||
duplicateId!: string | null;
|
duplicateId!: string | null;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
|
||||||
|
|
||||||
export class StackEntity {
|
|
||||||
id!: string;
|
|
||||||
ownerId!: string;
|
|
||||||
assets!: AssetEntity[];
|
|
||||||
primaryAsset!: AssetEntity;
|
|
||||||
primaryAssetId!: string;
|
|
||||||
assetCount?: number;
|
|
||||||
}
|
|
@ -15,7 +15,35 @@ select
|
|||||||
"assets"
|
"assets"
|
||||||
inner join lateral (
|
inner join lateral (
|
||||||
select
|
select
|
||||||
"exif".*
|
"exif"."assetId",
|
||||||
|
"exif"."autoStackId",
|
||||||
|
"exif"."bitsPerSample",
|
||||||
|
"exif"."city",
|
||||||
|
"exif"."colorspace",
|
||||||
|
"exif"."country",
|
||||||
|
"exif"."dateTimeOriginal",
|
||||||
|
"exif"."description",
|
||||||
|
"exif"."exifImageHeight",
|
||||||
|
"exif"."exifImageWidth",
|
||||||
|
"exif"."exposureTime",
|
||||||
|
"exif"."fileSizeInByte",
|
||||||
|
"exif"."fNumber",
|
||||||
|
"exif"."focalLength",
|
||||||
|
"exif"."fps",
|
||||||
|
"exif"."iso",
|
||||||
|
"exif"."latitude",
|
||||||
|
"exif"."lensModel",
|
||||||
|
"exif"."livePhotoCID",
|
||||||
|
"exif"."longitude",
|
||||||
|
"exif"."make",
|
||||||
|
"exif"."model",
|
||||||
|
"exif"."modifyDate",
|
||||||
|
"exif"."orientation",
|
||||||
|
"exif"."profileDescription",
|
||||||
|
"exif"."projectionType",
|
||||||
|
"exif"."rating",
|
||||||
|
"exif"."state",
|
||||||
|
"exif"."timeZone"
|
||||||
from
|
from
|
||||||
"exif"
|
"exif"
|
||||||
where
|
where
|
||||||
@ -52,7 +80,12 @@ select
|
|||||||
from
|
from
|
||||||
(
|
(
|
||||||
select
|
select
|
||||||
"tags".*
|
"tags"."id",
|
||||||
|
"tags"."value",
|
||||||
|
"tags"."createdAt",
|
||||||
|
"tags"."updatedAt",
|
||||||
|
"tags"."color",
|
||||||
|
"tags"."parentId"
|
||||||
from
|
from
|
||||||
"tags"
|
"tags"
|
||||||
inner join "tag_asset" on "tags"."id" = "tag_asset"."tagsId"
|
inner join "tag_asset" on "tags"."id" = "tag_asset"."tagsId"
|
||||||
@ -65,7 +98,35 @@ select
|
|||||||
"assets"
|
"assets"
|
||||||
inner join lateral (
|
inner join lateral (
|
||||||
select
|
select
|
||||||
"exif".*
|
"exif"."assetId",
|
||||||
|
"exif"."autoStackId",
|
||||||
|
"exif"."bitsPerSample",
|
||||||
|
"exif"."city",
|
||||||
|
"exif"."colorspace",
|
||||||
|
"exif"."country",
|
||||||
|
"exif"."dateTimeOriginal",
|
||||||
|
"exif"."description",
|
||||||
|
"exif"."exifImageHeight",
|
||||||
|
"exif"."exifImageWidth",
|
||||||
|
"exif"."exposureTime",
|
||||||
|
"exif"."fileSizeInByte",
|
||||||
|
"exif"."fNumber",
|
||||||
|
"exif"."focalLength",
|
||||||
|
"exif"."fps",
|
||||||
|
"exif"."iso",
|
||||||
|
"exif"."latitude",
|
||||||
|
"exif"."lensModel",
|
||||||
|
"exif"."livePhotoCID",
|
||||||
|
"exif"."longitude",
|
||||||
|
"exif"."make",
|
||||||
|
"exif"."model",
|
||||||
|
"exif"."modifyDate",
|
||||||
|
"exif"."orientation",
|
||||||
|
"exif"."profileDescription",
|
||||||
|
"exif"."projectionType",
|
||||||
|
"exif"."rating",
|
||||||
|
"exif"."state",
|
||||||
|
"exif"."timeZone"
|
||||||
from
|
from
|
||||||
"exif"
|
"exif"
|
||||||
where
|
where
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
-- TagRepository.get
|
-- TagRepository.get
|
||||||
select
|
select
|
||||||
"id",
|
"tags"."id",
|
||||||
"value",
|
"tags"."value",
|
||||||
"createdAt",
|
"tags"."createdAt",
|
||||||
"updatedAt",
|
"tags"."updatedAt",
|
||||||
"color",
|
"tags"."color",
|
||||||
"parentId"
|
"tags"."parentId"
|
||||||
from
|
from
|
||||||
"tags"
|
"tags"
|
||||||
where
|
where
|
||||||
@ -15,12 +15,12 @@ where
|
|||||||
|
|
||||||
-- TagRepository.getByValue
|
-- TagRepository.getByValue
|
||||||
select
|
select
|
||||||
"id",
|
"tags"."id",
|
||||||
"value",
|
"tags"."value",
|
||||||
"createdAt",
|
"tags"."createdAt",
|
||||||
"updatedAt",
|
"tags"."updatedAt",
|
||||||
"color",
|
"tags"."color",
|
||||||
"parentId"
|
"tags"."parentId"
|
||||||
from
|
from
|
||||||
"tags"
|
"tags"
|
||||||
where
|
where
|
||||||
@ -42,12 +42,12 @@ rollback
|
|||||||
|
|
||||||
-- TagRepository.getAll
|
-- TagRepository.getAll
|
||||||
select
|
select
|
||||||
"id",
|
"tags"."id",
|
||||||
"value",
|
"tags"."value",
|
||||||
"createdAt",
|
"tags"."createdAt",
|
||||||
"updatedAt",
|
"tags"."updatedAt",
|
||||||
"color",
|
"tags"."color",
|
||||||
"parentId"
|
"tags"."parentId"
|
||||||
from
|
from
|
||||||
"tags"
|
"tags"
|
||||||
where
|
where
|
||||||
|
@ -2,9 +2,10 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { ExpressionBuilder, Kysely, Updateable } from 'kysely';
|
import { ExpressionBuilder, Kysely, Updateable } from 'kysely';
|
||||||
import { jsonArrayFrom } from 'kysely/helpers/postgres';
|
import { jsonArrayFrom } from 'kysely/helpers/postgres';
|
||||||
import { InjectKysely } from 'nestjs-kysely';
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
import { DB } from 'src/db';
|
import { columns } from 'src/database';
|
||||||
|
import { AssetStack, DB } from 'src/db';
|
||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { StackEntity } from 'src/entities/stack.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { asUuid } from 'src/utils/database';
|
import { asUuid } from 'src/utils/database';
|
||||||
|
|
||||||
export interface StackSearch {
|
export interface StackSearch {
|
||||||
@ -18,7 +19,7 @@ const withAssets = (eb: ExpressionBuilder<DB, 'asset_stack'>, withTags = false)
|
|||||||
.selectFrom('assets')
|
.selectFrom('assets')
|
||||||
.selectAll('assets')
|
.selectAll('assets')
|
||||||
.innerJoinLateral(
|
.innerJoinLateral(
|
||||||
(eb) => eb.selectFrom('exif').selectAll('exif').whereRef('exif.assetId', '=', 'assets.id').as('exifInfo'),
|
(eb) => eb.selectFrom('exif').select(columns.exif).whereRef('exif.assetId', '=', 'assets.id').as('exifInfo'),
|
||||||
(join) => join.onTrue(),
|
(join) => join.onTrue(),
|
||||||
)
|
)
|
||||||
.$if(withTags, (eb) =>
|
.$if(withTags, (eb) =>
|
||||||
@ -26,7 +27,7 @@ const withAssets = (eb: ExpressionBuilder<DB, 'asset_stack'>, withTags = false)
|
|||||||
jsonArrayFrom(
|
jsonArrayFrom(
|
||||||
eb
|
eb
|
||||||
.selectFrom('tags')
|
.selectFrom('tags')
|
||||||
.selectAll('tags')
|
.select(columns.tag)
|
||||||
.innerJoin('tag_asset', 'tags.id', 'tag_asset.tagsId')
|
.innerJoin('tag_asset', 'tags.id', 'tag_asset.tagsId')
|
||||||
.whereRef('tag_asset.assetsId', '=', 'assets.id'),
|
.whereRef('tag_asset.assetsId', '=', 'assets.id'),
|
||||||
).as('tags'),
|
).as('tags'),
|
||||||
@ -35,7 +36,9 @@ const withAssets = (eb: ExpressionBuilder<DB, 'asset_stack'>, withTags = false)
|
|||||||
.select((eb) => eb.fn.toJson('exifInfo').as('exifInfo'))
|
.select((eb) => eb.fn.toJson('exifInfo').as('exifInfo'))
|
||||||
.where('assets.deletedAt', 'is', null)
|
.where('assets.deletedAt', 'is', null)
|
||||||
.whereRef('assets.stackId', '=', 'asset_stack.id'),
|
.whereRef('assets.stackId', '=', 'asset_stack.id'),
|
||||||
).as('assets');
|
)
|
||||||
|
.$castTo<AssetEntity[]>()
|
||||||
|
.as('assets');
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -43,17 +46,17 @@ export class StackRepository {
|
|||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [{ ownerId: DummyValue.UUID }] })
|
@GenerateSql({ params: [{ ownerId: DummyValue.UUID }] })
|
||||||
search(query: StackSearch): Promise<StackEntity[]> {
|
search(query: StackSearch) {
|
||||||
return this.db
|
return this.db
|
||||||
.selectFrom('asset_stack')
|
.selectFrom('asset_stack')
|
||||||
.selectAll('asset_stack')
|
.selectAll('asset_stack')
|
||||||
.select(withAssets)
|
.select(withAssets)
|
||||||
.where('asset_stack.ownerId', '=', query.ownerId)
|
.where('asset_stack.ownerId', '=', query.ownerId)
|
||||||
.$if(!!query.primaryAssetId, (eb) => eb.where('asset_stack.primaryAssetId', '=', query.primaryAssetId!))
|
.$if(!!query.primaryAssetId, (eb) => eb.where('asset_stack.primaryAssetId', '=', query.primaryAssetId!))
|
||||||
.execute() as unknown as Promise<StackEntity[]>;
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(entity: { ownerId: string; assetIds: string[] }): Promise<StackEntity> {
|
async create(entity: { ownerId: string; assetIds: string[] }) {
|
||||||
return this.db.transaction().execute(async (tx) => {
|
return this.db.transaction().execute(async (tx) => {
|
||||||
const stacks = await tx
|
const stacks = await tx
|
||||||
.selectFrom('asset_stack')
|
.selectFrom('asset_stack')
|
||||||
@ -116,7 +119,7 @@ export class StackRepository {
|
|||||||
.selectAll('asset_stack')
|
.selectAll('asset_stack')
|
||||||
.select(withAssets)
|
.select(withAssets)
|
||||||
.where('id', '=', newRecord.id)
|
.where('id', '=', newRecord.id)
|
||||||
.executeTakeFirst() as unknown as Promise<StackEntity>;
|
.executeTakeFirstOrThrow();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,23 +132,23 @@ export class StackRepository {
|
|||||||
await this.db.deleteFrom('asset_stack').where('id', 'in', ids).execute();
|
await this.db.deleteFrom('asset_stack').where('id', 'in', ids).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
update(id: string, entity: Updateable<StackEntity>): Promise<StackEntity> {
|
update(id: string, entity: Updateable<AssetStack>) {
|
||||||
return this.db
|
return this.db
|
||||||
.updateTable('asset_stack')
|
.updateTable('asset_stack')
|
||||||
.set(entity)
|
.set(entity)
|
||||||
.where('id', '=', asUuid(id))
|
.where('id', '=', asUuid(id))
|
||||||
.returningAll('asset_stack')
|
.returningAll('asset_stack')
|
||||||
.returning((eb) => withAssets(eb, true))
|
.returning((eb) => withAssets(eb, true))
|
||||||
.executeTakeFirstOrThrow() as unknown as Promise<StackEntity>;
|
.executeTakeFirstOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
getById(id: string): Promise<StackEntity | undefined> {
|
getById(id: string) {
|
||||||
return this.db
|
return this.db
|
||||||
.selectFrom('asset_stack')
|
.selectFrom('asset_stack')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.select((eb) => withAssets(eb, true))
|
.select((eb) => withAssets(eb, true))
|
||||||
.where('id', '=', asUuid(id))
|
.where('id', '=', asUuid(id))
|
||||||
.executeTakeFirst() as Promise<StackEntity | undefined>;
|
.executeTakeFirst();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,14 @@ export class TagRepository {
|
|||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
get(id: string) {
|
get(id: string) {
|
||||||
return this.db.selectFrom('tags').select(columns.tagDto).where('id', '=', id).executeTakeFirst();
|
return this.db.selectFrom('tags').select(columns.tag).where('id', '=', id).executeTakeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
|
||||||
getByValue(userId: string, value: string) {
|
getByValue(userId: string, value: string) {
|
||||||
return this.db
|
return this.db
|
||||||
.selectFrom('tags')
|
.selectFrom('tags')
|
||||||
.select(columns.tagDto)
|
.select(columns.tag)
|
||||||
.where('userId', '=', userId)
|
.where('userId', '=', userId)
|
||||||
.where('value', '=', value)
|
.where('value', '=', value)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
@ -68,12 +68,7 @@ export class TagRepository {
|
|||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
getAll(userId: string) {
|
getAll(userId: string) {
|
||||||
return this.db
|
return this.db.selectFrom('tags').select(columns.tag).where('userId', '=', userId).orderBy('value asc').execute();
|
||||||
.selectFrom('tags')
|
|
||||||
.select(columns.tagDto)
|
|
||||||
.where('userId', '=', userId)
|
|
||||||
.orderBy('value asc')
|
|
||||||
.execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [{ userId: DummyValue.UUID, color: DummyValue.STRING, value: DummyValue.STRING }] })
|
@GenerateSql({ params: [{ userId: DummyValue.UUID, color: DummyValue.STRING, value: DummyValue.STRING }] })
|
||||||
|
@ -592,8 +592,8 @@ describe(AssetService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update stack primary asset if deleted asset was primary asset in a stack', async () => {
|
it('should update stack primary asset if deleted asset was primary asset in a stack', async () => {
|
||||||
mocks.stack.update.mockResolvedValue(factory.stack() as unknown as any);
|
mocks.stack.update.mockResolvedValue(factory.stack() as any);
|
||||||
mocks.asset.getById.mockResolvedValue(assetStub.primaryImage as AssetEntity);
|
mocks.asset.getById.mockResolvedValue(assetStub.primaryImage);
|
||||||
|
|
||||||
await sut.handleAssetDeletion({ id: assetStub.primaryImage.id, deleteOnDisk: true });
|
await sut.handleAssetDeletion({ id: assetStub.primaryImage.id, deleteOnDisk: true });
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ export class AssetService extends BaseService {
|
|||||||
|
|
||||||
// Replace the parent of the stack children with a new asset
|
// Replace the parent of the stack children with a new asset
|
||||||
if (asset.stack?.primaryAssetId === id) {
|
if (asset.stack?.primaryAssetId === id) {
|
||||||
const stackAssetIds = asset.stack.assets.map((a) => a.id);
|
const stackAssetIds = asset.stack?.assets.map((a) => a.id) ?? [];
|
||||||
if (stackAssetIds.length > 2) {
|
if (stackAssetIds.length > 2) {
|
||||||
const newPrimaryAssetId = stackAssetIds.find((a) => a !== id)!;
|
const newPrimaryAssetId = stackAssetIds.find((a) => a !== id)!;
|
||||||
await this.stackRepository.update(asset.stack.id, {
|
await this.stackRepository.update(asset.stack.id, {
|
||||||
|
3
server/test/fixtures/asset.stub.ts
vendored
3
server/test/fixtures/asset.stub.ts
vendored
@ -1,6 +1,5 @@
|
|||||||
import { AssetFile, Exif } from 'src/database';
|
import { AssetFile, Exif } from 'src/database';
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { StackEntity } from 'src/entities/stack.entity';
|
|
||||||
import { AssetFileType, AssetStatus, AssetType } from 'src/enum';
|
import { AssetFileType, AssetStatus, AssetType } from 'src/enum';
|
||||||
import { StorageAsset } from 'src/types';
|
import { StorageAsset } from 'src/types';
|
||||||
import { authStub } from 'test/fixtures/auth.stub';
|
import { authStub } from 'test/fixtures/auth.stub';
|
||||||
@ -27,7 +26,7 @@ const fullsizeFile: AssetFile = {
|
|||||||
|
|
||||||
const files: AssetFile[] = [fullsizeFile, previewFile, thumbnailFile];
|
const files: AssetFile[] = [fullsizeFile, previewFile, thumbnailFile];
|
||||||
|
|
||||||
export const stackStub = (stackId: string, assets: AssetEntity[]): StackEntity => {
|
export const stackStub = (stackId: string, assets: AssetEntity[]) => {
|
||||||
return {
|
return {
|
||||||
id: stackId,
|
id: stackId,
|
||||||
assets,
|
assets,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user