mirror-immich/server/src/repositories/workflow.repository.ts

186 lines
6.4 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { Insertable, Kysely, Updateable } from 'kysely';
import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres';
import { InjectKysely } from 'nestjs-kysely';
import { columns } from 'src/database';
import { DummyValue, GenerateSql } from 'src/decorators';
import { WorkflowSearchDto } from 'src/dtos/workflow.dto';
import { DB } from 'src/schema';
import { WorkflowStepTable } from 'src/schema/tables/workflow-step.table';
import { WorkflowTable } from 'src/schema/tables/workflow.table';
export type WorkflowStepUpsert = Omit<Insertable<WorkflowStepTable>, 'workflowId' | 'order'>;
@Injectable()
export class WorkflowRepository {
constructor(@InjectKysely() private db: Kysely<DB>) {}
private queryBuilder(db?: Kysely<DB>) {
return (db ?? this.db)
.selectFrom('workflow')
.select([
'workflow.id',
'workflow.name',
'workflow.description',
'workflow.trigger',
'workflow.enabled',
'workflow.createdAt',
'workflow.updatedAt',
])
.select((eb) => [
jsonArrayFrom(
eb
.selectFrom('workflow_step')
.innerJoin('plugin_method', 'plugin_method.id', 'workflow_step.pluginMethodId')
.innerJoin('plugin', 'plugin.id', 'plugin_method.pluginId')
.whereRef('workflow.id', '=', 'workflow_step.workflowId')
.select([
'plugin.name as pluginName',
'plugin_method.name as methodName',
'workflow_step.config',
'workflow_step.enabled',
]),
).as('steps'),
]);
}
@GenerateSql({ params: [DummyValue.UUID] })
search(dto: WorkflowSearchDto & { userId?: string }) {
return this.queryBuilder()
.$if(!!dto.id, (qb) => qb.where('id', '=', dto.id!))
.$if(!!dto.userId, (qb) => qb.where('ownerId', '=', dto.userId!))
.$if(!!dto.trigger, (qb) => qb.where('trigger', '=', dto.trigger!))
.$if(dto.enabled !== undefined, (qb) => qb.where('enabled', '=', dto.enabled!))
.orderBy('createdAt', 'desc')
.execute();
}
@GenerateSql({ params: [DummyValue.UUID] })
get(id: string) {
return this.queryBuilder().where('id', '=', id).executeTakeFirst();
}
@GenerateSql({ params: [DummyValue.UUID] })
getForWorkflowRun(id: string) {
return this.db
.selectFrom('workflow')
.select(['workflow.id', 'workflow.name', 'workflow.trigger'])
.select((eb) => [
jsonArrayFrom(
eb
.selectFrom('workflow_step')
.innerJoin('plugin_method', 'plugin_method.id', 'workflow_step.pluginMethodId')
.whereRef('workflow_step.workflowId', '=', 'workflow.id')
.where('workflow_step.enabled', '=', true)
.select([
'workflow_step.id',
'workflow_step.config',
'plugin_method.pluginId as pluginId',
'plugin_method.name as methodName',
'plugin_method.types as types',
'plugin_method.hostFunctions',
]),
).as('steps'),
])
.where('id', '=', id)
.where('enabled', '=', true)
.executeTakeFirst();
}
create(dto: Insertable<WorkflowTable>, steps?: WorkflowStepUpsert[]) {
return this.db.transaction().execute(async (tx) => {
const { id } = await tx.insertInto('workflow').values(dto).returning(['id']).executeTakeFirstOrThrow();
return this.replaceAndReturn(tx, id, steps);
});
}
update(id: string, dto: Updateable<WorkflowTable>, steps?: WorkflowStepUpsert[]) {
return this.db.transaction().execute(async (tx) => {
if (Object.values(dto).some((prop) => prop !== undefined)) {
await tx.updateTable('workflow').set(dto).where('id', '=', id).executeTakeFirstOrThrow();
}
return this.replaceAndReturn(tx, id, steps);
});
}
async updateStep(id: string, dto: Updateable<WorkflowStepTable>) {
await this.db.updateTable('workflow_step').where('workflow_step.id', '=', id).set(dto).execute();
}
private async replaceAndReturn(tx: Kysely<DB>, workflowId: string, steps?: WorkflowStepUpsert[]) {
if (steps) {
await tx.deleteFrom('workflow_step').where('workflowId', '=', workflowId).execute();
if (steps.length > 0) {
await tx
.insertInto('workflow_step')
.values(
steps.map((step, i) => ({
workflowId,
enabled: step.enabled ?? true,
pluginMethodId: step.pluginMethodId,
config: step.config,
order: i,
})),
)
.returningAll()
.execute();
}
}
return this.queryBuilder(tx).where('id', '=', workflowId).executeTakeFirstOrThrow();
}
@GenerateSql({ params: [DummyValue.UUID] })
async delete(id: string) {
await this.db.deleteFrom('workflow').where('id', '=', id).execute();
}
getForAssetV1(assetId: string) {
return this.db
.selectFrom('asset')
.leftJoin('asset_exif', 'asset_exif.assetId', 'asset.id')
.select((eb) => [
...columns.workflowAssetV1,
jsonObjectFrom(
eb
.selectFrom('asset_exif')
.select([
'asset_exif.make',
'asset_exif.model',
'asset_exif.orientation',
'asset_exif.dateTimeOriginal',
'asset_exif.modifyDate',
'asset_exif.exifImageWidth',
'asset_exif.exifImageHeight',
'asset_exif.fileSizeInByte',
'asset_exif.lensModel',
'asset_exif.fNumber',
'asset_exif.focalLength',
'asset_exif.iso',
'asset_exif.latitude',
'asset_exif.longitude',
'asset_exif.city',
'asset_exif.state',
'asset_exif.country',
'asset_exif.description',
'asset_exif.fps',
'asset_exif.exposureTime',
'asset_exif.livePhotoCID',
'asset_exif.timeZone',
'asset_exif.projectionType',
'asset_exif.profileDescription',
'asset_exif.colorspace',
'asset_exif.bitsPerSample',
'asset_exif.autoStackId',
'asset_exif.rating',
'asset_exif.tags',
'asset_exif.updatedAt',
])
.whereRef('asset_exif.assetId', '=', 'asset.id'),
).as('exifInfo'),
])
.where('id', '=', assetId)
.executeTakeFirstOrThrow();
}
}