mirror of
https://github.com/immich-app/immich.git
synced 2025-06-03 05:34:32 -04:00
refactor: migrate library repository to kysely (#15271)
This commit is contained in:
parent
81568dbda3
commit
a2207f2eef
@ -1,3 +1,5 @@
|
|||||||
|
import { Insertable, Updateable } from 'kysely';
|
||||||
|
import { Libraries } from 'src/db';
|
||||||
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
||||||
import { LibraryEntity } from 'src/entities/library.entity';
|
import { LibraryEntity } from 'src/entities/library.entity';
|
||||||
|
|
||||||
@ -6,10 +8,10 @@ export const ILibraryRepository = 'ILibraryRepository';
|
|||||||
export interface ILibraryRepository {
|
export interface ILibraryRepository {
|
||||||
getAll(withDeleted?: boolean): Promise<LibraryEntity[]>;
|
getAll(withDeleted?: boolean): Promise<LibraryEntity[]>;
|
||||||
getAllDeleted(): Promise<LibraryEntity[]>;
|
getAllDeleted(): Promise<LibraryEntity[]>;
|
||||||
get(id: string, withDeleted?: boolean): Promise<LibraryEntity | null>;
|
get(id: string, withDeleted?: boolean): Promise<LibraryEntity | undefined>;
|
||||||
create(library: Partial<LibraryEntity>): Promise<LibraryEntity>;
|
create(library: Insertable<Libraries>): Promise<LibraryEntity>;
|
||||||
delete(id: string): Promise<void>;
|
delete(id: string): Promise<void>;
|
||||||
softDelete(id: string): Promise<void>;
|
softDelete(id: string): Promise<void>;
|
||||||
update(library: Partial<LibraryEntity>): Promise<LibraryEntity>;
|
update(id: string, library: Updateable<Libraries>): Promise<LibraryEntity>;
|
||||||
getStatistics(id: string): Promise<LibraryStatsResponseDto | undefined>;
|
getStatistics(id: string): Promise<LibraryStatsResponseDto | undefined>;
|
||||||
}
|
}
|
||||||
|
@ -1,150 +1,137 @@
|
|||||||
-- NOTE: This file is auto generated by ./sql-generator
|
-- NOTE: This file is auto generated by ./sql-generator
|
||||||
|
|
||||||
-- LibraryRepository.get
|
-- LibraryRepository.get
|
||||||
SELECT DISTINCT
|
select
|
||||||
"distinctAlias"."LibraryEntity_id" AS "ids_LibraryEntity_id"
|
"libraries".*,
|
||||||
FROM
|
|
||||||
(
|
(
|
||||||
SELECT
|
select
|
||||||
"LibraryEntity"."id" AS "LibraryEntity_id",
|
to_json(obj)
|
||||||
"LibraryEntity"."name" AS "LibraryEntity_name",
|
from
|
||||||
"LibraryEntity"."ownerId" AS "LibraryEntity_ownerId",
|
(
|
||||||
"LibraryEntity"."importPaths" AS "LibraryEntity_importPaths",
|
select
|
||||||
"LibraryEntity"."exclusionPatterns" AS "LibraryEntity_exclusionPatterns",
|
"users"."id",
|
||||||
"LibraryEntity"."createdAt" AS "LibraryEntity_createdAt",
|
"users"."email",
|
||||||
"LibraryEntity"."updatedAt" AS "LibraryEntity_updatedAt",
|
"users"."createdAt",
|
||||||
"LibraryEntity"."deletedAt" AS "LibraryEntity_deletedAt",
|
"users"."profileImagePath",
|
||||||
"LibraryEntity"."refreshedAt" AS "LibraryEntity_refreshedAt",
|
"users"."isAdmin",
|
||||||
"LibraryEntity__LibraryEntity_owner"."id" AS "LibraryEntity__LibraryEntity_owner_id",
|
"users"."shouldChangePassword",
|
||||||
"LibraryEntity__LibraryEntity_owner"."name" AS "LibraryEntity__LibraryEntity_owner_name",
|
"users"."deletedAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."isAdmin" AS "LibraryEntity__LibraryEntity_owner_isAdmin",
|
"users"."oauthId",
|
||||||
"LibraryEntity__LibraryEntity_owner"."email" AS "LibraryEntity__LibraryEntity_owner_email",
|
"users"."updatedAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."storageLabel" AS "LibraryEntity__LibraryEntity_owner_storageLabel",
|
"users"."storageLabel",
|
||||||
"LibraryEntity__LibraryEntity_owner"."oauthId" AS "LibraryEntity__LibraryEntity_owner_oauthId",
|
"users"."name",
|
||||||
"LibraryEntity__LibraryEntity_owner"."profileImagePath" AS "LibraryEntity__LibraryEntity_owner_profileImagePath",
|
"users"."quotaSizeInBytes",
|
||||||
"LibraryEntity__LibraryEntity_owner"."shouldChangePassword" AS "LibraryEntity__LibraryEntity_owner_shouldChangePassword",
|
"users"."quotaUsageInBytes",
|
||||||
"LibraryEntity__LibraryEntity_owner"."createdAt" AS "LibraryEntity__LibraryEntity_owner_createdAt",
|
"users"."status",
|
||||||
"LibraryEntity__LibraryEntity_owner"."deletedAt" AS "LibraryEntity__LibraryEntity_owner_deletedAt",
|
"users"."profileChangedAt"
|
||||||
"LibraryEntity__LibraryEntity_owner"."status" AS "LibraryEntity__LibraryEntity_owner_status",
|
from
|
||||||
"LibraryEntity__LibraryEntity_owner"."updatedAt" AS "LibraryEntity__LibraryEntity_owner_updatedAt",
|
"users"
|
||||||
"LibraryEntity__LibraryEntity_owner"."quotaSizeInBytes" AS "LibraryEntity__LibraryEntity_owner_quotaSizeInBytes",
|
where
|
||||||
"LibraryEntity__LibraryEntity_owner"."quotaUsageInBytes" AS "LibraryEntity__LibraryEntity_owner_quotaUsageInBytes",
|
"users"."id" = "libraries"."ownerId"
|
||||||
"LibraryEntity__LibraryEntity_owner"."profileChangedAt" AS "LibraryEntity__LibraryEntity_owner_profileChangedAt"
|
) as obj
|
||||||
FROM
|
) as "owner"
|
||||||
"libraries" "LibraryEntity"
|
from
|
||||||
LEFT JOIN "users" "LibraryEntity__LibraryEntity_owner" ON "LibraryEntity__LibraryEntity_owner"."id" = "LibraryEntity"."ownerId"
|
"libraries"
|
||||||
AND (
|
where
|
||||||
"LibraryEntity__LibraryEntity_owner"."deletedAt" IS NULL
|
"libraries"."id" = $1
|
||||||
)
|
and "libraries"."deletedAt" is null
|
||||||
WHERE
|
|
||||||
((("LibraryEntity"."id" = $1)))
|
|
||||||
AND ("LibraryEntity"."deletedAt" IS NULL)
|
|
||||||
) "distinctAlias"
|
|
||||||
ORDER BY
|
|
||||||
"LibraryEntity_id" ASC
|
|
||||||
LIMIT
|
|
||||||
1
|
|
||||||
|
|
||||||
-- LibraryRepository.getAll
|
-- LibraryRepository.getAll
|
||||||
SELECT
|
select
|
||||||
"LibraryEntity"."id" AS "LibraryEntity_id",
|
"libraries".*,
|
||||||
"LibraryEntity"."name" AS "LibraryEntity_name",
|
(
|
||||||
"LibraryEntity"."ownerId" AS "LibraryEntity_ownerId",
|
select
|
||||||
"LibraryEntity"."importPaths" AS "LibraryEntity_importPaths",
|
to_json(obj)
|
||||||
"LibraryEntity"."exclusionPatterns" AS "LibraryEntity_exclusionPatterns",
|
from
|
||||||
"LibraryEntity"."createdAt" AS "LibraryEntity_createdAt",
|
(
|
||||||
"LibraryEntity"."updatedAt" AS "LibraryEntity_updatedAt",
|
select
|
||||||
"LibraryEntity"."deletedAt" AS "LibraryEntity_deletedAt",
|
"users"."id",
|
||||||
"LibraryEntity"."refreshedAt" AS "LibraryEntity_refreshedAt",
|
"users"."email",
|
||||||
"LibraryEntity__LibraryEntity_owner"."id" AS "LibraryEntity__LibraryEntity_owner_id",
|
"users"."createdAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."name" AS "LibraryEntity__LibraryEntity_owner_name",
|
"users"."profileImagePath",
|
||||||
"LibraryEntity__LibraryEntity_owner"."isAdmin" AS "LibraryEntity__LibraryEntity_owner_isAdmin",
|
"users"."isAdmin",
|
||||||
"LibraryEntity__LibraryEntity_owner"."email" AS "LibraryEntity__LibraryEntity_owner_email",
|
"users"."shouldChangePassword",
|
||||||
"LibraryEntity__LibraryEntity_owner"."storageLabel" AS "LibraryEntity__LibraryEntity_owner_storageLabel",
|
"users"."deletedAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."oauthId" AS "LibraryEntity__LibraryEntity_owner_oauthId",
|
"users"."oauthId",
|
||||||
"LibraryEntity__LibraryEntity_owner"."profileImagePath" AS "LibraryEntity__LibraryEntity_owner_profileImagePath",
|
"users"."updatedAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."shouldChangePassword" AS "LibraryEntity__LibraryEntity_owner_shouldChangePassword",
|
"users"."storageLabel",
|
||||||
"LibraryEntity__LibraryEntity_owner"."createdAt" AS "LibraryEntity__LibraryEntity_owner_createdAt",
|
"users"."name",
|
||||||
"LibraryEntity__LibraryEntity_owner"."deletedAt" AS "LibraryEntity__LibraryEntity_owner_deletedAt",
|
"users"."quotaSizeInBytes",
|
||||||
"LibraryEntity__LibraryEntity_owner"."status" AS "LibraryEntity__LibraryEntity_owner_status",
|
"users"."quotaUsageInBytes",
|
||||||
"LibraryEntity__LibraryEntity_owner"."updatedAt" AS "LibraryEntity__LibraryEntity_owner_updatedAt",
|
"users"."status",
|
||||||
"LibraryEntity__LibraryEntity_owner"."quotaSizeInBytes" AS "LibraryEntity__LibraryEntity_owner_quotaSizeInBytes",
|
"users"."profileChangedAt"
|
||||||
"LibraryEntity__LibraryEntity_owner"."quotaUsageInBytes" AS "LibraryEntity__LibraryEntity_owner_quotaUsageInBytes",
|
from
|
||||||
"LibraryEntity__LibraryEntity_owner"."profileChangedAt" AS "LibraryEntity__LibraryEntity_owner_profileChangedAt"
|
"users"
|
||||||
FROM
|
where
|
||||||
"libraries" "LibraryEntity"
|
"users"."id" = "libraries"."ownerId"
|
||||||
LEFT JOIN "users" "LibraryEntity__LibraryEntity_owner" ON "LibraryEntity__LibraryEntity_owner"."id" = "LibraryEntity"."ownerId"
|
) as obj
|
||||||
AND (
|
) as "owner"
|
||||||
"LibraryEntity__LibraryEntity_owner"."deletedAt" IS NULL
|
from
|
||||||
)
|
"libraries"
|
||||||
WHERE
|
where
|
||||||
"LibraryEntity"."deletedAt" IS NULL
|
"libraries"."deletedAt" is null
|
||||||
ORDER BY
|
order by
|
||||||
"LibraryEntity"."createdAt" ASC
|
"createdAt" asc
|
||||||
|
|
||||||
-- LibraryRepository.getAllDeleted
|
-- LibraryRepository.getAllDeleted
|
||||||
SELECT
|
select
|
||||||
"LibraryEntity"."id" AS "LibraryEntity_id",
|
"libraries".*,
|
||||||
"LibraryEntity"."name" AS "LibraryEntity_name",
|
(
|
||||||
"LibraryEntity"."ownerId" AS "LibraryEntity_ownerId",
|
select
|
||||||
"LibraryEntity"."importPaths" AS "LibraryEntity_importPaths",
|
to_json(obj)
|
||||||
"LibraryEntity"."exclusionPatterns" AS "LibraryEntity_exclusionPatterns",
|
from
|
||||||
"LibraryEntity"."createdAt" AS "LibraryEntity_createdAt",
|
(
|
||||||
"LibraryEntity"."updatedAt" AS "LibraryEntity_updatedAt",
|
select
|
||||||
"LibraryEntity"."deletedAt" AS "LibraryEntity_deletedAt",
|
"users"."id",
|
||||||
"LibraryEntity"."refreshedAt" AS "LibraryEntity_refreshedAt",
|
"users"."email",
|
||||||
"LibraryEntity__LibraryEntity_owner"."id" AS "LibraryEntity__LibraryEntity_owner_id",
|
"users"."createdAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."name" AS "LibraryEntity__LibraryEntity_owner_name",
|
"users"."profileImagePath",
|
||||||
"LibraryEntity__LibraryEntity_owner"."isAdmin" AS "LibraryEntity__LibraryEntity_owner_isAdmin",
|
"users"."isAdmin",
|
||||||
"LibraryEntity__LibraryEntity_owner"."email" AS "LibraryEntity__LibraryEntity_owner_email",
|
"users"."shouldChangePassword",
|
||||||
"LibraryEntity__LibraryEntity_owner"."storageLabel" AS "LibraryEntity__LibraryEntity_owner_storageLabel",
|
"users"."deletedAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."oauthId" AS "LibraryEntity__LibraryEntity_owner_oauthId",
|
"users"."oauthId",
|
||||||
"LibraryEntity__LibraryEntity_owner"."profileImagePath" AS "LibraryEntity__LibraryEntity_owner_profileImagePath",
|
"users"."updatedAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."shouldChangePassword" AS "LibraryEntity__LibraryEntity_owner_shouldChangePassword",
|
"users"."storageLabel",
|
||||||
"LibraryEntity__LibraryEntity_owner"."createdAt" AS "LibraryEntity__LibraryEntity_owner_createdAt",
|
"users"."name",
|
||||||
"LibraryEntity__LibraryEntity_owner"."deletedAt" AS "LibraryEntity__LibraryEntity_owner_deletedAt",
|
"users"."quotaSizeInBytes",
|
||||||
"LibraryEntity__LibraryEntity_owner"."status" AS "LibraryEntity__LibraryEntity_owner_status",
|
"users"."quotaUsageInBytes",
|
||||||
"LibraryEntity__LibraryEntity_owner"."updatedAt" AS "LibraryEntity__LibraryEntity_owner_updatedAt",
|
"users"."status",
|
||||||
"LibraryEntity__LibraryEntity_owner"."quotaSizeInBytes" AS "LibraryEntity__LibraryEntity_owner_quotaSizeInBytes",
|
"users"."profileChangedAt"
|
||||||
"LibraryEntity__LibraryEntity_owner"."quotaUsageInBytes" AS "LibraryEntity__LibraryEntity_owner_quotaUsageInBytes",
|
from
|
||||||
"LibraryEntity__LibraryEntity_owner"."profileChangedAt" AS "LibraryEntity__LibraryEntity_owner_profileChangedAt"
|
"users"
|
||||||
FROM
|
where
|
||||||
"libraries" "LibraryEntity"
|
"users"."id" = "libraries"."ownerId"
|
||||||
LEFT JOIN "users" "LibraryEntity__LibraryEntity_owner" ON "LibraryEntity__LibraryEntity_owner"."id" = "LibraryEntity"."ownerId"
|
) as obj
|
||||||
WHERE
|
) as "owner"
|
||||||
((NOT ("LibraryEntity"."deletedAt" IS NULL)))
|
from
|
||||||
ORDER BY
|
"libraries"
|
||||||
"LibraryEntity"."createdAt" ASC
|
where
|
||||||
|
"libraries"."deletedAt" is not null
|
||||||
|
order by
|
||||||
|
"createdAt" asc
|
||||||
|
|
||||||
-- LibraryRepository.getStatistics
|
-- LibraryRepository.getStatistics
|
||||||
SELECT
|
select
|
||||||
"libraries"."id" AS "libraries_id",
|
count("assets"."id") filter (
|
||||||
"libraries"."name" AS "libraries_name",
|
where
|
||||||
"libraries"."ownerId" AS "libraries_ownerId",
|
(
|
||||||
"libraries"."importPaths" AS "libraries_importPaths",
|
"assets"."type" = $1
|
||||||
"libraries"."exclusionPatterns" AS "libraries_exclusionPatterns",
|
and "assets"."isVisible" = $2
|
||||||
"libraries"."createdAt" AS "libraries_createdAt",
|
)
|
||||||
"libraries"."updatedAt" AS "libraries_updatedAt",
|
) as "photos",
|
||||||
"libraries"."deletedAt" AS "libraries_deletedAt",
|
count(*) filter (
|
||||||
"libraries"."refreshedAt" AS "libraries_refreshedAt",
|
where
|
||||||
COUNT("assets"."id") FILTER (
|
(
|
||||||
WHERE
|
"assets"."type" = $3
|
||||||
"assets"."type" = 'IMAGE'
|
and "assets"."isVisible" = $4
|
||||||
AND "assets"."isVisible"
|
)
|
||||||
) AS "photos",
|
) as "videos",
|
||||||
COUNT("assets"."id") FILTER (
|
coalesce(sum("exif"."fileSizeInByte"), $5) as "usage"
|
||||||
WHERE
|
from
|
||||||
"assets"."type" = 'VIDEO'
|
"libraries"
|
||||||
AND "assets"."isVisible"
|
inner join "assets" on "assets"."libraryId" = "libraries"."id"
|
||||||
) AS "videos",
|
inner join "exif" on "exif"."assetId" = "assets"."id"
|
||||||
COALESCE(SUM("exif"."fileSizeInByte"), 0) AS "usage"
|
where
|
||||||
FROM
|
"libraries"."id" = $6
|
||||||
"libraries" "libraries"
|
group by
|
||||||
LEFT JOIN "assets" "assets" ON "assets"."libraryId" = "libraries"."id"
|
|
||||||
AND ("assets"."deletedAt" IS NULL)
|
|
||||||
LEFT JOIN "exif" "exif" ON "exif"."assetId" = "assets"."id"
|
|
||||||
WHERE
|
|
||||||
("libraries"."id" = $1)
|
|
||||||
AND ("libraries"."deletedAt" IS NULL)
|
|
||||||
GROUP BY
|
|
||||||
"libraries"."id"
|
"libraries"."id"
|
||||||
|
@ -1,84 +1,122 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { ExpressionBuilder, Insertable, Kysely, Updateable } from 'kysely';
|
||||||
|
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||||
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
|
import { DB, Libraries } from 'src/db';
|
||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
||||||
import { LibraryEntity } from 'src/entities/library.entity';
|
import { LibraryEntity } from 'src/entities/library.entity';
|
||||||
|
import { AssetType } from 'src/enum';
|
||||||
import { ILibraryRepository } from 'src/interfaces/library.interface';
|
import { ILibraryRepository } from 'src/interfaces/library.interface';
|
||||||
import { IsNull, Not } from 'typeorm';
|
|
||||||
import { Repository } from 'typeorm/repository/Repository.js';
|
const userColumns = [
|
||||||
|
'users.id',
|
||||||
|
'users.email',
|
||||||
|
'users.createdAt',
|
||||||
|
'users.profileImagePath',
|
||||||
|
'users.isAdmin',
|
||||||
|
'users.shouldChangePassword',
|
||||||
|
'users.deletedAt',
|
||||||
|
'users.oauthId',
|
||||||
|
'users.updatedAt',
|
||||||
|
'users.storageLabel',
|
||||||
|
'users.name',
|
||||||
|
'users.quotaSizeInBytes',
|
||||||
|
'users.quotaUsageInBytes',
|
||||||
|
'users.status',
|
||||||
|
'users.profileChangedAt',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const withOwner = (eb: ExpressionBuilder<DB, 'libraries'>) => {
|
||||||
|
return jsonObjectFrom(eb.selectFrom('users').whereRef('users.id', '=', 'libraries.ownerId').select(userColumns)).as(
|
||||||
|
'owner',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LibraryRepository implements ILibraryRepository {
|
export class LibraryRepository implements ILibraryRepository {
|
||||||
constructor(@InjectRepository(LibraryEntity) private repository: Repository<LibraryEntity>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
get(id: string, withDeleted = false): Promise<LibraryEntity | null> {
|
get(id: string, withDeleted = false): Promise<LibraryEntity | undefined> {
|
||||||
return this.repository.findOneOrFail({
|
return this.db
|
||||||
where: {
|
.selectFrom('libraries')
|
||||||
id,
|
.selectAll('libraries')
|
||||||
},
|
.select(withOwner)
|
||||||
relations: { owner: true },
|
.where('libraries.id', '=', id)
|
||||||
withDeleted,
|
.$if(!withDeleted, (qb) => qb.where('libraries.deletedAt', 'is', null))
|
||||||
});
|
.executeTakeFirst() as Promise<LibraryEntity | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [] })
|
@GenerateSql({ params: [] })
|
||||||
getAll(withDeleted = false): Promise<LibraryEntity[]> {
|
getAll(withDeleted = false): Promise<LibraryEntity[]> {
|
||||||
return this.repository.find({
|
return this.db
|
||||||
relations: {
|
.selectFrom('libraries')
|
||||||
owner: true,
|
.selectAll('libraries')
|
||||||
},
|
.select(withOwner)
|
||||||
order: {
|
.orderBy('createdAt', 'asc')
|
||||||
createdAt: 'ASC',
|
.$if(!withDeleted, (qb) => qb.where('libraries.deletedAt', 'is', null))
|
||||||
},
|
.execute() as unknown as Promise<LibraryEntity[]>;
|
||||||
withDeleted,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql()
|
@GenerateSql()
|
||||||
getAllDeleted(): Promise<LibraryEntity[]> {
|
getAllDeleted(): Promise<LibraryEntity[]> {
|
||||||
return this.repository.find({
|
return this.db
|
||||||
where: {
|
.selectFrom('libraries')
|
||||||
deletedAt: Not(IsNull()),
|
.selectAll('libraries')
|
||||||
},
|
.select(withOwner)
|
||||||
relations: {
|
.where('libraries.deletedAt', 'is not', null)
|
||||||
owner: true,
|
.orderBy('createdAt', 'asc')
|
||||||
},
|
.execute() as unknown as Promise<LibraryEntity[]>;
|
||||||
order: {
|
|
||||||
createdAt: 'ASC',
|
|
||||||
},
|
|
||||||
withDeleted: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create(library: Omit<LibraryEntity, 'id' | 'createdAt' | 'updatedAt' | 'ownerId'>): Promise<LibraryEntity> {
|
create(library: Insertable<Libraries>): Promise<LibraryEntity> {
|
||||||
return this.repository.save(library);
|
return this.db
|
||||||
|
.insertInto('libraries')
|
||||||
|
.values(library)
|
||||||
|
.returningAll()
|
||||||
|
.executeTakeFirstOrThrow() as Promise<LibraryEntity>;
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string): Promise<void> {
|
async delete(id: string): Promise<void> {
|
||||||
await this.repository.delete({ id });
|
await this.db.deleteFrom('libraries').where('libraries.id', '=', id).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
async softDelete(id: string): Promise<void> {
|
async softDelete(id: string): Promise<void> {
|
||||||
await this.repository.softDelete({ id });
|
await this.db.updateTable('libraries').set({ deletedAt: new Date() }).where('libraries.id', '=', id).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(library: Partial<LibraryEntity>): Promise<LibraryEntity> {
|
update(id: string, library: Updateable<Libraries>): Promise<LibraryEntity> {
|
||||||
return this.save(library);
|
return this.db
|
||||||
|
.updateTable('libraries')
|
||||||
|
.set(library)
|
||||||
|
.where('libraries.id', '=', id)
|
||||||
|
.returningAll()
|
||||||
|
.executeTakeFirstOrThrow() as Promise<LibraryEntity>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
async getStatistics(id: string): Promise<LibraryStatsResponseDto | undefined> {
|
async getStatistics(id: string): Promise<LibraryStatsResponseDto | undefined> {
|
||||||
const stats = await this.repository
|
const stats = await this.db
|
||||||
.createQueryBuilder('libraries')
|
.selectFrom('libraries')
|
||||||
.addSelect(`COUNT(assets.id) FILTER (WHERE assets.type = 'IMAGE' AND assets.isVisible)`, 'photos')
|
.innerJoin('assets', 'assets.libraryId', 'libraries.id')
|
||||||
.addSelect(`COUNT(assets.id) FILTER (WHERE assets.type = 'VIDEO' AND assets.isVisible)`, 'videos')
|
.innerJoin('exif', 'exif.assetId', 'assets.id')
|
||||||
.addSelect('COALESCE(SUM(exif.fileSizeInByte), 0)', 'usage')
|
.select((eb) =>
|
||||||
.leftJoin('libraries.assets', 'assets')
|
eb.fn
|
||||||
.leftJoin('assets.exifInfo', 'exif')
|
.count('assets.id')
|
||||||
|
.filterWhere((eb) => eb.and([eb('assets.type', '=', AssetType.IMAGE), eb('assets.isVisible', '=', true)]))
|
||||||
|
.as('photos'),
|
||||||
|
)
|
||||||
|
.select((eb) =>
|
||||||
|
eb.fn
|
||||||
|
.countAll()
|
||||||
|
.filterWhere((eb) => eb.and([eb('assets.type', '=', AssetType.VIDEO), eb('assets.isVisible', '=', true)]))
|
||||||
|
.as('videos'),
|
||||||
|
)
|
||||||
|
.select((eb) => eb.fn.coalesce((eb) => eb.fn.sum('exif.fileSizeInByte'), eb.val(0)).as('usage'))
|
||||||
.groupBy('libraries.id')
|
.groupBy('libraries.id')
|
||||||
.where('libraries.id = :id', { id })
|
.where('libraries.id', '=', id)
|
||||||
.getRawOne();
|
.executeTakeFirst();
|
||||||
|
|
||||||
if (!stats) {
|
if (!stats) {
|
||||||
return;
|
return;
|
||||||
@ -91,9 +129,4 @@ export class LibraryRepository implements ILibraryRepository {
|
|||||||
total: Number(stats.photos) + Number(stats.videos),
|
total: Number(stats.photos) + Number(stats.videos),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async save(library: Partial<LibraryEntity>) {
|
|
||||||
const { id } = await this.repository.save(library);
|
|
||||||
return this.repository.findOneByOrFail({ id });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ describe(LibraryService.name, () => {
|
|||||||
Promise.resolve(
|
Promise.resolve(
|
||||||
[libraryStub.externalLibraryWithImportPaths1, libraryStub.externalLibraryWithImportPaths2].find(
|
[libraryStub.externalLibraryWithImportPaths1, libraryStub.externalLibraryWithImportPaths2].find(
|
||||||
(library) => library.id === id,
|
(library) => library.id === id,
|
||||||
) || null,
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -190,8 +190,6 @@ describe(LibraryService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should fail when library can't be found", async () => {
|
it("should fail when library can't be found", async () => {
|
||||||
libraryMock.get.mockResolvedValue(null);
|
|
||||||
|
|
||||||
await expect(sut.handleQueueSyncFiles({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SKIPPED);
|
await expect(sut.handleQueueSyncFiles({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SKIPPED);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -242,8 +240,6 @@ describe(LibraryService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should fail when library can't be found", async () => {
|
it("should fail when library can't be found", async () => {
|
||||||
libraryMock.get.mockResolvedValue(null);
|
|
||||||
|
|
||||||
await expect(sut.handleQueueSyncAssets({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SKIPPED);
|
await expect(sut.handleQueueSyncAssets({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SKIPPED);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -630,7 +626,6 @@ describe(LibraryService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error when a library is not found', async () => {
|
it('should throw an error when a library is not found', async () => {
|
||||||
libraryMock.get.mockResolvedValue(null);
|
|
||||||
await expect(sut.get(libraryStub.externalLibrary1.id)).rejects.toBeInstanceOf(BadRequestException);
|
await expect(sut.get(libraryStub.externalLibrary1.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||||
expect(libraryMock.get).toHaveBeenCalledWith(libraryStub.externalLibrary1.id);
|
expect(libraryMock.get).toHaveBeenCalledWith(libraryStub.externalLibrary1.id);
|
||||||
});
|
});
|
||||||
@ -825,7 +820,10 @@ describe(LibraryService.name, () => {
|
|||||||
await expect(sut.update('library-id', { importPaths: [`${cwd}/foo/bar`] })).resolves.toEqual(
|
await expect(sut.update('library-id', { importPaths: [`${cwd}/foo/bar`] })).resolves.toEqual(
|
||||||
mapLibrary(libraryStub.externalLibrary1),
|
mapLibrary(libraryStub.externalLibrary1),
|
||||||
);
|
);
|
||||||
expect(libraryMock.update).toHaveBeenCalledWith(expect.objectContaining({ id: 'library-id' }));
|
expect(libraryMock.update).toHaveBeenCalledWith(
|
||||||
|
'library-id',
|
||||||
|
expect.objectContaining({ importPaths: [`${cwd}/foo/bar`] }),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1015,7 +1013,7 @@ describe(LibraryService.name, () => {
|
|||||||
Promise.resolve(
|
Promise.resolve(
|
||||||
[libraryStub.externalLibraryWithImportPaths1, libraryStub.externalLibraryWithImportPaths2].find(
|
[libraryStub.externalLibraryWithImportPaths1, libraryStub.externalLibraryWithImportPaths2].find(
|
||||||
(library) => library.id === id,
|
(library) => library.id === id,
|
||||||
) || null,
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -311,7 +311,7 @@ export class LibraryService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const library = await this.libraryRepository.update({ id, ...dto });
|
const library = await this.libraryRepository.update(id, dto);
|
||||||
return mapLibrary(library);
|
return mapLibrary(library);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,7 +571,7 @@ export class LibraryService extends BaseService {
|
|||||||
this.logger.debug(`No non-excluded assets found in any import path for library ${library.id}`);
|
this.logger.debug(`No non-excluded assets found in any import path for library ${library.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.libraryRepository.update({ id: job.id, refreshedAt: new Date() });
|
await this.libraryRepository.update(job.id, { refreshedAt: new Date() });
|
||||||
|
|
||||||
return JobStatus.SUCCESS;
|
return JobStatus.SUCCESS;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user