From 4794eeca885fa310c7694ef656ce68a03501e9c4 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Tue, 8 Apr 2025 12:40:03 -0400 Subject: [PATCH] refactor: database types (#17468) --- server/src/database.ts | 28 +++++++++++++++++++++++++- server/src/dtos/memory.dto.ts | 4 ++-- server/src/dtos/tag.dto.ts | 4 ++-- server/src/entities/asset.entity.ts | 4 ++-- server/src/services/api-key.service.ts | 4 ++-- server/src/types.ts | 22 -------------------- server/src/utils/tag.ts | 6 +++--- server/test/fixtures/tag.stub.ts | 6 +++--- server/test/small.factory.ts | 5 +++-- 9 files changed, 44 insertions(+), 39 deletions(-) diff --git a/server/src/database.ts b/server/src/database.ts index f4e49f07ab4..5f2d5c51230 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -1,5 +1,6 @@ import { UserMetadataEntity } from 'src/entities/user-metadata.entity'; -import { AssetStatus, AssetType, Permission, UserStatus } from 'src/enum'; +import { AssetStatus, AssetType, MemoryType, Permission, UserStatus } from 'src/enum'; +import { OnThisDayData } from 'src/types'; export type AuthUser = { id: string; @@ -38,6 +39,31 @@ export type ApiKey = { permissions: Permission[]; }; +export type Tag = { + id: string; + value: string; + createdAt: Date; + updatedAt: Date; + color: string | null; + parentId: string | null; +}; + +export type Memory = { + id: string; + createdAt: Date; + updatedAt: Date; + deletedAt: Date | null; + memoryAt: Date; + seenAt: Date | null; + showAt: Date | null; + hideAt: Date | null; + type: MemoryType; + data: OnThisDayData; + ownerId: string; + isSaved: boolean; + assets: Asset[]; +}; + export type User = { id: string; name: string; diff --git a/server/src/dtos/memory.dto.ts b/server/src/dtos/memory.dto.ts index 36f4631ef59..b3054d7a4c2 100644 --- a/server/src/dtos/memory.dto.ts +++ b/server/src/dtos/memory.dto.ts @@ -1,11 +1,11 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsEnum, IsInt, IsObject, IsPositive, ValidateNested } from 'class-validator'; +import { Memory } from 'src/database'; import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { AssetEntity } from 'src/entities/asset.entity'; import { MemoryType } from 'src/enum'; -import { MemoryItem } from 'src/types'; import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; class MemoryBaseDto { @@ -89,7 +89,7 @@ export class MemoryResponseDto { assets!: AssetResponseDto[]; } -export const mapMemory = (entity: MemoryItem, auth: AuthDto): MemoryResponseDto => { +export const mapMemory = (entity: Memory, auth: AuthDto): MemoryResponseDto => { return { id: entity.id, createdAt: entity.createdAt, diff --git a/server/src/dtos/tag.dto.ts b/server/src/dtos/tag.dto.ts index 3c5e74647c0..a35801d07e6 100644 --- a/server/src/dtos/tag.dto.ts +++ b/server/src/dtos/tag.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsHexColor, IsNotEmpty, IsString } from 'class-validator'; -import { TagItem } from 'src/types'; +import { Tag } from 'src/database'; import { Optional, ValidateHexColor, ValidateUUID } from 'src/validation'; export class TagCreateDto { @@ -51,7 +51,7 @@ export class TagResponseDto { color?: string; } -export function mapTag(entity: TagItem): TagResponseDto { +export function mapTag(entity: Tag): TagResponseDto { return { id: entity.id, parentId: entity.parentId ?? undefined, diff --git a/server/src/entities/asset.entity.ts b/server/src/entities/asset.entity.ts index 50f2d6c5d1b..ef27e0db5fc 100644 --- a/server/src/entities/asset.entity.ts +++ b/server/src/entities/asset.entity.ts @@ -1,5 +1,6 @@ import { DeduplicateJoinsPlugin, ExpressionBuilder, Kysely, SelectQueryBuilder, sql } from 'kysely'; import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'; +import { Tag } from 'src/database'; import { DB } from 'src/db'; import { AlbumEntity } from 'src/entities/album.entity'; import { AssetFaceEntity } from 'src/entities/asset-face.entity'; @@ -12,7 +13,6 @@ import { UserEntity } from 'src/entities/user.entity'; import { AssetFileType, AssetStatus, AssetType } from 'src/enum'; import { TimeBucketSize } from 'src/repositories/asset.repository'; import { AssetSearchBuilderOptions } from 'src/repositories/search.repository'; -import { TagItem } from 'src/types'; import { anyUuid, asUuid } from 'src/utils/database'; export const ASSET_CHECKSUM_CONSTRAINT = 'UQ_assets_owner_checksum'; @@ -49,7 +49,7 @@ export class AssetEntity { originalFileName!: string; sidecarPath!: string | null; exifInfo?: ExifEntity; - tags?: TagItem[]; + tags?: Tag[]; sharedLinks!: SharedLinkEntity[]; albums?: AlbumEntity[]; faces!: AssetFaceEntity[]; diff --git a/server/src/services/api-key.service.ts b/server/src/services/api-key.service.ts index 5459b56889d..33861d82cd9 100644 --- a/server/src/services/api-key.service.ts +++ b/server/src/services/api-key.service.ts @@ -1,9 +1,9 @@ import { BadRequestException, Injectable } from '@nestjs/common'; +import { ApiKey } from 'src/database'; import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto, APIKeyUpdateDto } from 'src/dtos/api-key.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { Permission } from 'src/enum'; import { BaseService } from 'src/services/base.service'; -import { ApiKeyItem } from 'src/types'; import { isGranted } from 'src/utils/access'; @Injectable() @@ -58,7 +58,7 @@ export class ApiKeyService extends BaseService { return keys.map((key) => this.map(key)); } - private map(entity: ApiKeyItem): APIKeyResponseDto { + private map(entity: ApiKey): APIKeyResponseDto { return { id: entity.id, name: entity.name, diff --git a/server/src/types.ts b/server/src/types.ts index 6620621da87..fdbcc990e7a 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -14,8 +14,6 @@ import { VideoCodec, } from 'src/enum'; import { ActivityRepository } from 'src/repositories/activity.repository'; -import { ApiKeyRepository } from 'src/repositories/api-key.repository'; -import { MemoryRepository } from 'src/repositories/memory.repository'; import { SearchRepository } from 'src/repositories/search.repository'; import { SessionRepository } from 'src/repositories/session.repository'; @@ -24,8 +22,6 @@ export type DeepPartial = T extends object ? { [K in keyof T]?: DeepPartial = Pick; type IActivityRepository = RepositoryInterface; -type IApiKeyRepository = RepositoryInterface; -type IMemoryRepository = RepositoryInterface; type ISearchRepository = RepositoryInterface; type ISessionRepository = RepositoryInterface; @@ -35,26 +31,8 @@ export type ActivityItem = export type SearchPlacesItem = Awaited>[0]; -export type ApiKeyItem = - | Awaited> - | NonNullable>> - | Awaited>[0]; - -export type MemoryItem = - | Awaited> - | Awaited>[0]; - export type SessionItem = Awaited>[0]; -export type TagItem = { - id: string; - value: string; - createdAt: Date; - updatedAt: Date; - color: string | null; - parentId: string | null; -}; - export interface CropOptions { top: number; left: number; diff --git a/server/src/utils/tag.ts b/server/src/utils/tag.ts index b095fcfd85e..4e8a86a7f60 100644 --- a/server/src/utils/tag.ts +++ b/server/src/utils/tag.ts @@ -1,15 +1,15 @@ +import { Tag } from 'src/database'; import { TagRepository } from 'src/repositories/tag.repository'; -import { TagItem } from 'src/types'; type UpsertRequest = { userId: string; tags: string[] }; export const upsertTags = async (repository: TagRepository, { userId, tags }: UpsertRequest) => { tags = [...new Set(tags)]; - const results: TagItem[] = []; + const results: Tag[] = []; for (const tag of tags) { const parts = tag.split('/').filter(Boolean); - let parent: TagItem | undefined; + let parent: Tag | undefined; for (const part of parts) { const value = parent ? `${parent.value}/${part}` : part; diff --git a/server/test/fixtures/tag.stub.ts b/server/test/fixtures/tag.stub.ts index 1a19c2a002a..7a2cacf1269 100644 --- a/server/test/fixtures/tag.stub.ts +++ b/server/test/fixtures/tag.stub.ts @@ -1,7 +1,7 @@ +import { Tag } from 'src/database'; import { TagResponseDto } from 'src/dtos/tag.dto'; -import { TagItem } from 'src/types'; -const parent = Object.freeze({ +const parent = Object.freeze({ id: 'tag-parent', createdAt: new Date('2021-01-01T00:00:00Z'), updatedAt: new Date('2021-01-01T00:00:00Z'), @@ -10,7 +10,7 @@ const parent = Object.freeze({ parentId: null, }); -const child = Object.freeze({ +const child = Object.freeze({ id: 'tag-child', createdAt: new Date('2021-01-01T00:00:00Z'), updatedAt: new Date('2021-01-01T00:00:00Z'), diff --git a/server/test/small.factory.ts b/server/test/small.factory.ts index 3c20eebc2c1..ea8c95e3753 100644 --- a/server/test/small.factory.ts +++ b/server/test/small.factory.ts @@ -5,6 +5,7 @@ import { AuthApiKey, AuthUser, Library, + Memory, Partner, SidecarWriteAsset, User, @@ -12,7 +13,7 @@ import { } from 'src/database'; import { AuthDto } from 'src/dtos/auth.dto'; import { AssetStatus, AssetType, MemoryType, Permission, UserStatus } from 'src/enum'; -import { ActivityItem, MemoryItem, OnThisDayData } from 'src/types'; +import { ActivityItem, OnThisDayData } from 'src/types'; export const newUuid = () => randomUUID() as string; export const newUuids = () => @@ -196,7 +197,7 @@ const libraryFactory = (library: Partial = {}) => ({ ...library, }); -const memoryFactory = (memory: Partial = {}) => ({ +const memoryFactory = (memory: Partial = {}) => ({ id: newUuid(), createdAt: newDate(), updatedAt: newDate(),