mirror of
https://github.com/immich-app/immich
synced 2026-08-15 13:03:57 +00:00
refactor: find or fail (#30697)
This commit is contained in:
parent
9773d72428
commit
927b4d0ea2
13 changed files with 48 additions and 74 deletions
|
|
@ -19,6 +19,7 @@ import { AlbumAssetCount, AlbumInfoOptions } from 'src/repositories/album.reposi
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
||||||
import { asDateTimeString } from 'src/utils/date';
|
import { asDateTimeString } from 'src/utils/date';
|
||||||
|
import { findOrFail } from 'src/utils/misc';
|
||||||
import { getPreferences } from 'src/utils/preferences';
|
import { getPreferences } from 'src/utils/preferences';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -351,11 +352,7 @@ export class AlbumService extends BaseService {
|
||||||
await this.albumUserRepository.update({ albumId: id, userId }, { role: dto.role });
|
await this.albumUserRepository.update({ albumId: id, userId }, { role: dto.role });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findOrFail(id: string, authUserId: string, options: AlbumInfoOptions) {
|
private findOrFail(id: string, authUserId: string, options: AlbumInfoOptions) {
|
||||||
const album = await this.albumRepository.getById(id, options, authUserId);
|
return findOrFail(() => this.albumRepository.getById(id, options, authUserId), 'Album');
|
||||||
if (!album) {
|
|
||||||
throw new BadRequestException('Album not found');
|
|
||||||
}
|
|
||||||
return album;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ import {
|
||||||
} from 'src/utils/asset.util';
|
} from 'src/utils/asset.util';
|
||||||
import { updateLockedColumns } from 'src/utils/database';
|
import { updateLockedColumns } from 'src/utils/database';
|
||||||
import { extractTimeZone } from 'src/utils/date';
|
import { extractTimeZone } from 'src/utils/date';
|
||||||
|
import { findOrFail } from 'src/utils/misc';
|
||||||
import { transformOcrBoundingBox } from 'src/utils/transform';
|
import { transformOcrBoundingBox } from 'src/utils/transform';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -496,12 +497,8 @@ export class AssetService extends BaseService {
|
||||||
await this.jobRepository.queueAll(jobs);
|
await this.jobRepository.queueAll(jobs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findOrFail(id: string) {
|
private findOrFail(id: string) {
|
||||||
const asset = await this.assetRepository.getById(id);
|
return findOrFail(() => this.assetRepository.getById(id), 'Asset');
|
||||||
if (!asset) {
|
|
||||||
throw new BadRequestException('Asset not found');
|
|
||||||
}
|
|
||||||
return asset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateExif(dto: {
|
private async updateExif(dto: {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import { AssetTable } from 'src/schema/tables/asset.table';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { JobOf } from 'src/types';
|
import { JobOf } from 'src/types';
|
||||||
import { mimeTypes } from 'src/utils/mime-types';
|
import { mimeTypes } from 'src/utils/mime-types';
|
||||||
import { handlePromiseError } from 'src/utils/misc';
|
import { findOrFail, handlePromiseError } from 'src/utils/misc';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LibraryService extends BaseService {
|
export class LibraryService extends BaseService {
|
||||||
|
|
@ -793,11 +793,7 @@ export class LibraryService extends BaseService {
|
||||||
return JobStatus.Success;
|
return JobStatus.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findOrFail(id: string) {
|
private findOrFail(id: string) {
|
||||||
const library = await this.libraryRepository.get(id);
|
return findOrFail(() => this.libraryRepository.get(id), 'Library');
|
||||||
if (!library) {
|
|
||||||
throw new BadRequestException('Library not found');
|
|
||||||
}
|
|
||||||
return library;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { Memory } from 'src/database';
|
import { Memory } from 'src/database';
|
||||||
import { OnJob } from 'src/decorators';
|
import { OnJob } from 'src/decorators';
|
||||||
|
|
@ -8,6 +8,7 @@ import { MemoryCreateDto, MemoryResponseDto, MemorySearchDto, MemoryUpdateDto, m
|
||||||
import { DatabaseLock, JobName, MemoryType, Permission, QueueName, SystemMetadataKey } from 'src/enum';
|
import { DatabaseLock, JobName, MemoryType, Permission, QueueName, SystemMetadataKey } from 'src/enum';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
||||||
|
import { findOrFail } from 'src/utils/misc';
|
||||||
|
|
||||||
const DAYS = 3;
|
const DAYS = 3;
|
||||||
|
|
||||||
|
|
@ -162,11 +163,7 @@ export class MemoryService extends BaseService {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findOrFail(id: string) {
|
private findOrFail(id: string) {
|
||||||
const memory = await this.memoryRepository.get(id);
|
return findOrFail(() => this.memoryRepository.get(id), 'Memory');
|
||||||
if (!memory) {
|
|
||||||
throw new BadRequestException('Memory not found');
|
|
||||||
}
|
|
||||||
return memory;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ import { JobItem, JobOf } from 'src/types';
|
||||||
import { getDimensions } from 'src/utils/asset.util';
|
import { getDimensions } from 'src/utils/asset.util';
|
||||||
import { ImmichFileResponse } from 'src/utils/file';
|
import { ImmichFileResponse } from 'src/utils/file';
|
||||||
import { mimeTypes } from 'src/utils/mime-types';
|
import { mimeTypes } from 'src/utils/mime-types';
|
||||||
import { isFacialRecognitionEnabled } from 'src/utils/misc';
|
import { findOrFail, isFacialRecognitionEnabled } from 'src/utils/misc';
|
||||||
import { Point, transformPoints } from 'src/utils/transform';
|
import { Point, transformPoints } from 'src/utils/transform';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -614,12 +614,8 @@ export class PersonService extends BaseService {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findOrFail(id: string) {
|
private findOrFail(id: string) {
|
||||||
const person = await this.personRepository.getById(id);
|
return findOrFail(() => this.personRepository.getById(id), 'Person');
|
||||||
if (!person) {
|
|
||||||
throw new BadRequestException('Person not found');
|
|
||||||
}
|
|
||||||
return person;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO return a asset face response
|
// TODO return a asset face response
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import {
|
||||||
} from 'src/dtos/shared-link.dto';
|
} from 'src/dtos/shared-link.dto';
|
||||||
import { Permission, SharedLinkType } from 'src/enum';
|
import { Permission, SharedLinkType } from 'src/enum';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { getExternalDomain, OpenGraphTags } from 'src/utils/misc';
|
import { findOrFail, getExternalDomain, OpenGraphTags } from 'src/utils/misc';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SharedLinkService extends BaseService {
|
export class SharedLinkService extends BaseService {
|
||||||
|
|
@ -143,12 +143,8 @@ export class SharedLinkService extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: replace `userId` with permissions and access control checks
|
// TODO: replace `userId` with permissions and access control checks
|
||||||
private async findOrFail(userId: string, id: string) {
|
private findOrFail(userId: string, id: string) {
|
||||||
const sharedLink = await this.sharedLinkRepository.get(userId, id);
|
return findOrFail(() => this.sharedLinkRepository.get(userId, id), 'Shared link');
|
||||||
if (!sharedLink) {
|
|
||||||
throw new BadRequestException('Shared link not found');
|
|
||||||
}
|
|
||||||
return sharedLink;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async addAssets(auth: AuthDto, id: string, dto: AssetIdsDto): Promise<AssetIdsResponseDto[]> {
|
async addAssets(auth: AuthDto, id: string, dto: AssetIdsDto): Promise<AssetIdsResponseDto[]> {
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,9 @@ describe(StackService.name, () => {
|
||||||
|
|
||||||
it('should fail if stack could not be found', async () => {
|
it('should fail if stack could not be found', async () => {
|
||||||
mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
|
mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
|
||||||
|
mocks.stack.getById.mockResolvedValue(void 0);
|
||||||
|
|
||||||
await expect(sut.get(authStub.admin, 'stack-id')).rejects.toBeInstanceOf(Error);
|
await expect(sut.get(authStub.admin, 'stack-id')).rejects.toBeInstanceOf(BadRequestException);
|
||||||
|
|
||||||
expect(mocks.access.stack.checkOwnerAccess).toHaveBeenCalled();
|
expect(mocks.access.stack.checkOwnerAccess).toHaveBeenCalled();
|
||||||
expect(mocks.stack.getById).toHaveBeenCalledWith('stack-id');
|
expect(mocks.stack.getById).toHaveBeenCalledWith('stack-id');
|
||||||
|
|
@ -124,8 +125,9 @@ describe(StackService.name, () => {
|
||||||
|
|
||||||
it('should fail if stack could not be found', async () => {
|
it('should fail if stack could not be found', async () => {
|
||||||
mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
|
mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
|
||||||
|
mocks.stack.getById.mockResolvedValue(void 0);
|
||||||
|
|
||||||
await expect(sut.update(AuthFactory.create(), 'stack-id', {})).rejects.toBeInstanceOf(Error);
|
await expect(sut.update(AuthFactory.create(), 'stack-id', {})).rejects.toBeInstanceOf(BadRequestException);
|
||||||
|
|
||||||
expect(mocks.stack.getById).toHaveBeenCalledWith('stack-id');
|
expect(mocks.stack.getById).toHaveBeenCalledWith('stack-id');
|
||||||
expect(mocks.stack.update).not.toHaveBeenCalled();
|
expect(mocks.stack.update).not.toHaveBeenCalled();
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto, mapStack } from 'src/dtos/stack.dto';
|
import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto, mapStack } from 'src/dtos/stack.dto';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
|
import { findOrFail } from 'src/utils/misc';
|
||||||
import { UUIDAssetIDParamDto } from 'src/validation';
|
import { UUIDAssetIDParamDto } from 'src/validation';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -77,12 +78,7 @@ export class StackService extends BaseService {
|
||||||
await this.eventRepository.emit('StackUpdate', { stackId, userId: auth.user.id });
|
await this.eventRepository.emit('StackUpdate', { stackId, userId: auth.user.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findOrFail(id: string) {
|
private findOrFail(id: string) {
|
||||||
const stack = await this.stackRepository.getById(id);
|
return findOrFail(() => this.stackRepository.getById(id), 'Asset stack');
|
||||||
if (!stack) {
|
|
||||||
throw new Error('Asset stack not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
return stack;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import { TagAssetTable } from 'src/schema/tables/tag-asset.table';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
||||||
import { updateLockedColumns } from 'src/utils/database';
|
import { updateLockedColumns } from 'src/utils/database';
|
||||||
|
import { findOrFail } from 'src/utils/misc';
|
||||||
import { upsertTags } from 'src/utils/tag';
|
import { upsertTags } from 'src/utils/tag';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -146,12 +147,8 @@ export class TagService extends BaseService {
|
||||||
return JobStatus.Success;
|
return JobStatus.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findOrFail(id: string) {
|
private findOrFail(id: string) {
|
||||||
const tag = await this.tagRepository.get(id);
|
return findOrFail(() => this.tagRepository.get(id), 'Tag');
|
||||||
if (!tag) {
|
|
||||||
throw new BadRequestException('Tag not found');
|
|
||||||
}
|
|
||||||
return tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateTags(assetId: string) {
|
private async updateTags(assetId: string) {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import { JobName, UserMetadataKey, UserStatus } from 'src/enum';
|
||||||
import { UserFindOptions } from 'src/repositories/user.repository';
|
import { UserFindOptions } from 'src/repositories/user.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { getCalendarHeatmap } from 'src/services/shared/user-methods';
|
import { getCalendarHeatmap } from 'src/services/shared/user-methods';
|
||||||
|
import { findOrFail } from 'src/utils/misc';
|
||||||
import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
|
import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -158,11 +159,7 @@ export class UserAdminService extends BaseService {
|
||||||
return mapPreferences(newPreferences);
|
return mapPreferences(newPreferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findOrFail(id: string, options: UserFindOptions) {
|
private findOrFail(id: string, options: UserFindOptions) {
|
||||||
const user = await this.userRepository.get(id, options);
|
return findOrFail(() => this.userRepository.get(id, options), 'User');
|
||||||
if (!user) {
|
|
||||||
throw new BadRequestException('User not found');
|
|
||||||
}
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import { getCalendarHeatmap } from 'src/services/shared/user-methods';
|
||||||
import { JobOf, UserMetadataItem } from 'src/types';
|
import { JobOf, UserMetadataItem } from 'src/types';
|
||||||
import { ImmichFileResponse } from 'src/utils/file';
|
import { ImmichFileResponse } from 'src/utils/file';
|
||||||
import { mimeTypes } from 'src/utils/mime-types';
|
import { mimeTypes } from 'src/utils/mime-types';
|
||||||
|
import { findOrFail } from 'src/utils/misc';
|
||||||
import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
|
import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
|
||||||
import { generateProfileImage } from 'src/utils/profile-image';
|
import { generateProfileImage } from 'src/utils/profile-image';
|
||||||
|
|
||||||
|
|
@ -302,11 +303,7 @@ export class UserService extends BaseService {
|
||||||
return DateTime.now().minus({ days: delayUntilDeletion }) > DateTime.fromJSDate(user.deletedAt);
|
return DateTime.now().minus({ days: delayUntilDeletion }) > DateTime.fromJSDate(user.deletedAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findOrFail(id: string, options: UserFindOptions) {
|
private findOrFail(id: string, options: UserFindOptions) {
|
||||||
const user = await this.userRepository.get(id, options);
|
return findOrFail(() => this.userRepository.get(id, options), 'User');
|
||||||
if (!user) {
|
|
||||||
throw new BadRequestException('User not found');
|
|
||||||
}
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import {
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { PluginMethodSearchResponse } from 'src/repositories/plugin.repository';
|
import { PluginMethodSearchResponse } from 'src/repositories/plugin.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
|
import { findOrFail } from 'src/utils/misc';
|
||||||
import { getWorkflowTriggers, isMethodCompatible, resolveMethod } from 'src/utils/workflow';
|
import { getWorkflowTriggers, isMethodCompatible, resolveMethod } from 'src/utils/workflow';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -104,11 +105,7 @@ export class WorkflowService extends BaseService {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findOrFail(id: string) {
|
private findOrFail(id: string) {
|
||||||
const workflow = await this.workflowRepository.get(id);
|
return findOrFail(() => this.workflowRepository.get(id), 'Workflow');
|
||||||
if (!workflow) {
|
|
||||||
throw new BadRequestException('Workflow not found');
|
|
||||||
}
|
|
||||||
return workflow;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { INestApplication } from '@nestjs/common';
|
import { BadRequestException, INestApplication } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
ApiBodyOptions,
|
ApiBodyOptions,
|
||||||
DocumentBuilder,
|
DocumentBuilder,
|
||||||
|
|
@ -111,6 +111,15 @@ export const handlePromiseError = <T>(promise: Promise<T>, logger: LoggingReposi
|
||||||
promise.catch((error: Error | any) => logger.error(`Promise error: ${error}`, error?.stack));
|
promise.catch((error: Error | any) => logger.error(`Promise error: ${error}`, error?.stack));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const findOrFail = async <T>(find: () => Promise<T>, entity: string): Promise<NonNullable<T>> => {
|
||||||
|
const value = await find();
|
||||||
|
if (!value) {
|
||||||
|
throw new BadRequestException(`${entity} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
export interface OpenGraphTags {
|
export interface OpenGraphTags {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue