mirror of
https://github.com/immich-app/immich.git
synced 2025-02-11 01:18:24 -05:00
refactor(server): view repository (#12755)
This commit is contained in:
parent
f53e4721cf
commit
1e6ef5c9e4
11 changed files with 159 additions and 166 deletions
|
@ -147,8 +147,6 @@ export type AssetPathEntity = Pick<AssetEntity, 'id' | 'originalPath' | 'isOffli
|
||||||
export const IAssetRepository = 'IAssetRepository';
|
export const IAssetRepository = 'IAssetRepository';
|
||||||
|
|
||||||
export interface IAssetRepository {
|
export interface IAssetRepository {
|
||||||
getAssetsByOriginalPath(userId: string, partialPath: string): Promise<AssetEntity[]>;
|
|
||||||
getUniqueOriginalPaths(userId: string): Promise<string[]>;
|
|
||||||
create(asset: AssetCreate): Promise<AssetEntity>;
|
create(asset: AssetCreate): Promise<AssetEntity>;
|
||||||
getByIds(
|
getByIds(
|
||||||
ids: string[],
|
ids: string[],
|
||||||
|
|
8
server/src/interfaces/view.interface.ts
Normal file
8
server/src/interfaces/view.interface.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
|
|
||||||
|
export const IViewRepository = 'IViewRepository';
|
||||||
|
|
||||||
|
export interface IViewRepository {
|
||||||
|
getAssetsByOriginalPath(userId: string, partialPath: string): Promise<AssetEntity[]>;
|
||||||
|
getUniqueOriginalPaths(userId: string): Promise<string[]>;
|
||||||
|
}
|
|
@ -1134,109 +1134,6 @@ WHERE
|
||||||
AND "asset"."ownerId" IN ($1)
|
AND "asset"."ownerId" IN ($1)
|
||||||
AND "asset"."updatedAt" > $2
|
AND "asset"."updatedAt" > $2
|
||||||
|
|
||||||
-- AssetRepository.getAssetsByOriginalPath
|
|
||||||
SELECT
|
|
||||||
"asset"."id" AS "asset_id",
|
|
||||||
"asset"."deviceAssetId" AS "asset_deviceAssetId",
|
|
||||||
"asset"."ownerId" AS "asset_ownerId",
|
|
||||||
"asset"."libraryId" AS "asset_libraryId",
|
|
||||||
"asset"."deviceId" AS "asset_deviceId",
|
|
||||||
"asset"."type" AS "asset_type",
|
|
||||||
"asset"."originalPath" AS "asset_originalPath",
|
|
||||||
"asset"."thumbhash" AS "asset_thumbhash",
|
|
||||||
"asset"."encodedVideoPath" AS "asset_encodedVideoPath",
|
|
||||||
"asset"."createdAt" AS "asset_createdAt",
|
|
||||||
"asset"."updatedAt" AS "asset_updatedAt",
|
|
||||||
"asset"."deletedAt" AS "asset_deletedAt",
|
|
||||||
"asset"."fileCreatedAt" AS "asset_fileCreatedAt",
|
|
||||||
"asset"."localDateTime" AS "asset_localDateTime",
|
|
||||||
"asset"."fileModifiedAt" AS "asset_fileModifiedAt",
|
|
||||||
"asset"."isFavorite" AS "asset_isFavorite",
|
|
||||||
"asset"."isArchived" AS "asset_isArchived",
|
|
||||||
"asset"."isExternal" AS "asset_isExternal",
|
|
||||||
"asset"."isOffline" AS "asset_isOffline",
|
|
||||||
"asset"."checksum" AS "asset_checksum",
|
|
||||||
"asset"."duration" AS "asset_duration",
|
|
||||||
"asset"."isVisible" AS "asset_isVisible",
|
|
||||||
"asset"."livePhotoVideoId" AS "asset_livePhotoVideoId",
|
|
||||||
"asset"."originalFileName" AS "asset_originalFileName",
|
|
||||||
"asset"."sidecarPath" AS "asset_sidecarPath",
|
|
||||||
"asset"."stackId" AS "asset_stackId",
|
|
||||||
"asset"."duplicateId" AS "asset_duplicateId",
|
|
||||||
"exifInfo"."assetId" AS "exifInfo_assetId",
|
|
||||||
"exifInfo"."description" AS "exifInfo_description",
|
|
||||||
"exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth",
|
|
||||||
"exifInfo"."exifImageHeight" AS "exifInfo_exifImageHeight",
|
|
||||||
"exifInfo"."fileSizeInByte" AS "exifInfo_fileSizeInByte",
|
|
||||||
"exifInfo"."orientation" AS "exifInfo_orientation",
|
|
||||||
"exifInfo"."dateTimeOriginal" AS "exifInfo_dateTimeOriginal",
|
|
||||||
"exifInfo"."modifyDate" AS "exifInfo_modifyDate",
|
|
||||||
"exifInfo"."timeZone" AS "exifInfo_timeZone",
|
|
||||||
"exifInfo"."latitude" AS "exifInfo_latitude",
|
|
||||||
"exifInfo"."longitude" AS "exifInfo_longitude",
|
|
||||||
"exifInfo"."projectionType" AS "exifInfo_projectionType",
|
|
||||||
"exifInfo"."city" AS "exifInfo_city",
|
|
||||||
"exifInfo"."livePhotoCID" AS "exifInfo_livePhotoCID",
|
|
||||||
"exifInfo"."autoStackId" AS "exifInfo_autoStackId",
|
|
||||||
"exifInfo"."state" AS "exifInfo_state",
|
|
||||||
"exifInfo"."country" AS "exifInfo_country",
|
|
||||||
"exifInfo"."make" AS "exifInfo_make",
|
|
||||||
"exifInfo"."model" AS "exifInfo_model",
|
|
||||||
"exifInfo"."lensModel" AS "exifInfo_lensModel",
|
|
||||||
"exifInfo"."fNumber" AS "exifInfo_fNumber",
|
|
||||||
"exifInfo"."focalLength" AS "exifInfo_focalLength",
|
|
||||||
"exifInfo"."iso" AS "exifInfo_iso",
|
|
||||||
"exifInfo"."exposureTime" AS "exifInfo_exposureTime",
|
|
||||||
"exifInfo"."profileDescription" AS "exifInfo_profileDescription",
|
|
||||||
"exifInfo"."colorspace" AS "exifInfo_colorspace",
|
|
||||||
"exifInfo"."bitsPerSample" AS "exifInfo_bitsPerSample",
|
|
||||||
"exifInfo"."rating" AS "exifInfo_rating",
|
|
||||||
"exifInfo"."fps" AS "exifInfo_fps",
|
|
||||||
"stack"."id" AS "stack_id",
|
|
||||||
"stack"."ownerId" AS "stack_ownerId",
|
|
||||||
"stack"."primaryAssetId" AS "stack_primaryAssetId",
|
|
||||||
"stackedAssets"."id" AS "stackedAssets_id",
|
|
||||||
"stackedAssets"."deviceAssetId" AS "stackedAssets_deviceAssetId",
|
|
||||||
"stackedAssets"."ownerId" AS "stackedAssets_ownerId",
|
|
||||||
"stackedAssets"."libraryId" AS "stackedAssets_libraryId",
|
|
||||||
"stackedAssets"."deviceId" AS "stackedAssets_deviceId",
|
|
||||||
"stackedAssets"."type" AS "stackedAssets_type",
|
|
||||||
"stackedAssets"."originalPath" AS "stackedAssets_originalPath",
|
|
||||||
"stackedAssets"."thumbhash" AS "stackedAssets_thumbhash",
|
|
||||||
"stackedAssets"."encodedVideoPath" AS "stackedAssets_encodedVideoPath",
|
|
||||||
"stackedAssets"."createdAt" AS "stackedAssets_createdAt",
|
|
||||||
"stackedAssets"."updatedAt" AS "stackedAssets_updatedAt",
|
|
||||||
"stackedAssets"."deletedAt" AS "stackedAssets_deletedAt",
|
|
||||||
"stackedAssets"."fileCreatedAt" AS "stackedAssets_fileCreatedAt",
|
|
||||||
"stackedAssets"."localDateTime" AS "stackedAssets_localDateTime",
|
|
||||||
"stackedAssets"."fileModifiedAt" AS "stackedAssets_fileModifiedAt",
|
|
||||||
"stackedAssets"."isFavorite" AS "stackedAssets_isFavorite",
|
|
||||||
"stackedAssets"."isArchived" AS "stackedAssets_isArchived",
|
|
||||||
"stackedAssets"."isExternal" AS "stackedAssets_isExternal",
|
|
||||||
"stackedAssets"."isOffline" AS "stackedAssets_isOffline",
|
|
||||||
"stackedAssets"."checksum" AS "stackedAssets_checksum",
|
|
||||||
"stackedAssets"."duration" AS "stackedAssets_duration",
|
|
||||||
"stackedAssets"."isVisible" AS "stackedAssets_isVisible",
|
|
||||||
"stackedAssets"."livePhotoVideoId" AS "stackedAssets_livePhotoVideoId",
|
|
||||||
"stackedAssets"."originalFileName" AS "stackedAssets_originalFileName",
|
|
||||||
"stackedAssets"."sidecarPath" AS "stackedAssets_sidecarPath",
|
|
||||||
"stackedAssets"."stackId" AS "stackedAssets_stackId",
|
|
||||||
"stackedAssets"."duplicateId" AS "stackedAssets_duplicateId"
|
|
||||||
FROM
|
|
||||||
"assets" "asset"
|
|
||||||
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
|
|
||||||
LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId"
|
|
||||||
LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id"
|
|
||||||
AND ("stackedAssets"."deletedAt" IS NULL)
|
|
||||||
WHERE
|
|
||||||
"asset"."ownerId" = $1
|
|
||||||
AND (
|
|
||||||
"asset"."originalPath" LIKE $2
|
|
||||||
AND "asset"."originalPath" NOT LIKE $3
|
|
||||||
)
|
|
||||||
ORDER BY
|
|
||||||
regexp_replace("asset"."originalPath", '.*/(.+)', '\1') ASC
|
|
||||||
|
|
||||||
-- AssetRepository.upsertFile
|
-- AssetRepository.upsertFile
|
||||||
INSERT INTO
|
INSERT INTO
|
||||||
"asset_files" (
|
"asset_files" (
|
||||||
|
|
78
server/src/queries/view.repository.sql
Normal file
78
server/src/queries/view.repository.sql
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
-- NOTE: This file is auto generated by ./sql-generator
|
||||||
|
|
||||||
|
-- ViewRepository.getAssetsByOriginalPath
|
||||||
|
SELECT
|
||||||
|
"asset"."id" AS "asset_id",
|
||||||
|
"asset"."deviceAssetId" AS "asset_deviceAssetId",
|
||||||
|
"asset"."ownerId" AS "asset_ownerId",
|
||||||
|
"asset"."libraryId" AS "asset_libraryId",
|
||||||
|
"asset"."deviceId" AS "asset_deviceId",
|
||||||
|
"asset"."type" AS "asset_type",
|
||||||
|
"asset"."originalPath" AS "asset_originalPath",
|
||||||
|
"asset"."thumbhash" AS "asset_thumbhash",
|
||||||
|
"asset"."encodedVideoPath" AS "asset_encodedVideoPath",
|
||||||
|
"asset"."createdAt" AS "asset_createdAt",
|
||||||
|
"asset"."updatedAt" AS "asset_updatedAt",
|
||||||
|
"asset"."deletedAt" AS "asset_deletedAt",
|
||||||
|
"asset"."fileCreatedAt" AS "asset_fileCreatedAt",
|
||||||
|
"asset"."localDateTime" AS "asset_localDateTime",
|
||||||
|
"asset"."fileModifiedAt" AS "asset_fileModifiedAt",
|
||||||
|
"asset"."isFavorite" AS "asset_isFavorite",
|
||||||
|
"asset"."isArchived" AS "asset_isArchived",
|
||||||
|
"asset"."isExternal" AS "asset_isExternal",
|
||||||
|
"asset"."isOffline" AS "asset_isOffline",
|
||||||
|
"asset"."checksum" AS "asset_checksum",
|
||||||
|
"asset"."duration" AS "asset_duration",
|
||||||
|
"asset"."isVisible" AS "asset_isVisible",
|
||||||
|
"asset"."livePhotoVideoId" AS "asset_livePhotoVideoId",
|
||||||
|
"asset"."originalFileName" AS "asset_originalFileName",
|
||||||
|
"asset"."sidecarPath" AS "asset_sidecarPath",
|
||||||
|
"asset"."stackId" AS "asset_stackId",
|
||||||
|
"asset"."duplicateId" AS "asset_duplicateId",
|
||||||
|
"exifInfo"."assetId" AS "exifInfo_assetId",
|
||||||
|
"exifInfo"."description" AS "exifInfo_description",
|
||||||
|
"exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth",
|
||||||
|
"exifInfo"."exifImageHeight" AS "exifInfo_exifImageHeight",
|
||||||
|
"exifInfo"."fileSizeInByte" AS "exifInfo_fileSizeInByte",
|
||||||
|
"exifInfo"."orientation" AS "exifInfo_orientation",
|
||||||
|
"exifInfo"."dateTimeOriginal" AS "exifInfo_dateTimeOriginal",
|
||||||
|
"exifInfo"."modifyDate" AS "exifInfo_modifyDate",
|
||||||
|
"exifInfo"."timeZone" AS "exifInfo_timeZone",
|
||||||
|
"exifInfo"."latitude" AS "exifInfo_latitude",
|
||||||
|
"exifInfo"."longitude" AS "exifInfo_longitude",
|
||||||
|
"exifInfo"."projectionType" AS "exifInfo_projectionType",
|
||||||
|
"exifInfo"."city" AS "exifInfo_city",
|
||||||
|
"exifInfo"."livePhotoCID" AS "exifInfo_livePhotoCID",
|
||||||
|
"exifInfo"."autoStackId" AS "exifInfo_autoStackId",
|
||||||
|
"exifInfo"."state" AS "exifInfo_state",
|
||||||
|
"exifInfo"."country" AS "exifInfo_country",
|
||||||
|
"exifInfo"."make" AS "exifInfo_make",
|
||||||
|
"exifInfo"."model" AS "exifInfo_model",
|
||||||
|
"exifInfo"."lensModel" AS "exifInfo_lensModel",
|
||||||
|
"exifInfo"."fNumber" AS "exifInfo_fNumber",
|
||||||
|
"exifInfo"."focalLength" AS "exifInfo_focalLength",
|
||||||
|
"exifInfo"."iso" AS "exifInfo_iso",
|
||||||
|
"exifInfo"."exposureTime" AS "exifInfo_exposureTime",
|
||||||
|
"exifInfo"."profileDescription" AS "exifInfo_profileDescription",
|
||||||
|
"exifInfo"."colorspace" AS "exifInfo_colorspace",
|
||||||
|
"exifInfo"."bitsPerSample" AS "exifInfo_bitsPerSample",
|
||||||
|
"exifInfo"."rating" AS "exifInfo_rating",
|
||||||
|
"exifInfo"."fps" AS "exifInfo_fps"
|
||||||
|
FROM
|
||||||
|
"assets" "asset"
|
||||||
|
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
|
||||||
|
WHERE
|
||||||
|
(
|
||||||
|
(
|
||||||
|
"asset"."isVisible" = $1
|
||||||
|
AND "asset"."isArchived" = $2
|
||||||
|
AND "asset"."ownerId" = $3
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
"asset"."originalPath" LIKE $4
|
||||||
|
AND "asset"."originalPath" NOT LIKE $5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND ("asset"."deletedAt" IS NULL)
|
||||||
|
ORDER BY
|
||||||
|
regexp_replace("asset"."originalPath", '.*/(.+)', '\1') ASC
|
|
@ -836,50 +836,6 @@ export class AssetRepository implements IAssetRepository {
|
||||||
return builder.getMany();
|
return builder.getMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUniqueOriginalPaths(userId: string): Promise<string[]> {
|
|
||||||
const builder = this.getBuilder({
|
|
||||||
userIds: [userId],
|
|
||||||
exifInfo: false,
|
|
||||||
withStacked: false,
|
|
||||||
isArchived: false,
|
|
||||||
isTrashed: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const results = await builder
|
|
||||||
.select("DISTINCT substring(asset.originalPath FROM '^(.*/)[^/]*$')", 'directoryPath')
|
|
||||||
.getRawMany();
|
|
||||||
|
|
||||||
return results.map((row: { directoryPath: string }) => row.directoryPath.replaceAll(/^\/|\/$/g, ''));
|
|
||||||
}
|
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
|
|
||||||
async getAssetsByOriginalPath(userId: string, partialPath: string): Promise<AssetEntity[]> {
|
|
||||||
const normalizedPath = partialPath.replaceAll(/^\/|\/$/g, '');
|
|
||||||
|
|
||||||
const builder = this.getBuilder({
|
|
||||||
userIds: [userId],
|
|
||||||
exifInfo: true,
|
|
||||||
withStacked: false,
|
|
||||||
isArchived: false,
|
|
||||||
isTrashed: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const assets = await builder
|
|
||||||
.where('asset.ownerId = :userId', { userId })
|
|
||||||
.andWhere(
|
|
||||||
new Brackets((qb) => {
|
|
||||||
qb.where('asset.originalPath LIKE :likePath', { likePath: `%${normalizedPath}/%` }).andWhere(
|
|
||||||
'asset.originalPath NOT LIKE :notLikePath',
|
|
||||||
{ notLikePath: `%${normalizedPath}/%/%` },
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.orderBy(String.raw`regexp_replace(asset.originalPath, '.*/(.+)', '\1')`, 'ASC')
|
|
||||||
.getMany();
|
|
||||||
|
|
||||||
return assets;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GenerateSql({ params: [{ assetId: DummyValue.UUID, type: AssetFileType.PREVIEW, path: '/path/to/file' }] })
|
@GenerateSql({ params: [{ assetId: DummyValue.UUID, type: AssetFileType.PREVIEW, path: '/path/to/file' }] })
|
||||||
async upsertFile({ assetId, type, path }: { assetId: string; type: AssetFileType; path: string }): Promise<void> {
|
async upsertFile({ assetId, type, path }: { assetId: string; type: AssetFileType; path: string }): Promise<void> {
|
||||||
await this.fileRepository.upsert({ assetId, type, path }, { conflictPaths: ['assetId', 'type'] });
|
await this.fileRepository.upsert({ assetId, type, path }, { conflictPaths: ['assetId', 'type'] });
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { IStorageRepository } from 'src/interfaces/storage.interface';
|
||||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
||||||
import { ITagRepository } from 'src/interfaces/tag.interface';
|
import { ITagRepository } from 'src/interfaces/tag.interface';
|
||||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
import { IUserRepository } from 'src/interfaces/user.interface';
|
||||||
|
import { IViewRepository } from 'src/interfaces/view.interface';
|
||||||
import { AccessRepository } from 'src/repositories/access.repository';
|
import { AccessRepository } from 'src/repositories/access.repository';
|
||||||
import { ActivityRepository } from 'src/repositories/activity.repository';
|
import { ActivityRepository } from 'src/repositories/activity.repository';
|
||||||
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
|
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
|
||||||
|
@ -62,6 +63,7 @@ import { StorageRepository } from 'src/repositories/storage.repository';
|
||||||
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
|
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
|
||||||
import { TagRepository } from 'src/repositories/tag.repository';
|
import { TagRepository } from 'src/repositories/tag.repository';
|
||||||
import { UserRepository } from 'src/repositories/user.repository';
|
import { UserRepository } from 'src/repositories/user.repository';
|
||||||
|
import { ViewRepository } from 'src/repositories/view-repository';
|
||||||
|
|
||||||
export const repositories = [
|
export const repositories = [
|
||||||
{ provide: IAccessRepository, useClass: AccessRepository },
|
{ provide: IAccessRepository, useClass: AccessRepository },
|
||||||
|
@ -96,4 +98,5 @@ export const repositories = [
|
||||||
{ provide: ISystemMetadataRepository, useClass: SystemMetadataRepository },
|
{ provide: ISystemMetadataRepository, useClass: SystemMetadataRepository },
|
||||||
{ provide: ITagRepository, useClass: TagRepository },
|
{ provide: ITagRepository, useClass: TagRepository },
|
||||||
{ provide: IUserRepository, useClass: UserRepository },
|
{ provide: IUserRepository, useClass: UserRepository },
|
||||||
|
{ provide: IViewRepository, useClass: ViewRepository },
|
||||||
];
|
];
|
||||||
|
|
48
server/src/repositories/view-repository.ts
Normal file
48
server/src/repositories/view-repository.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
|
import { IViewRepository } from 'src/interfaces/view.interface';
|
||||||
|
import { Brackets, Repository } from 'typeorm';
|
||||||
|
|
||||||
|
export class ViewRepository implements IViewRepository {
|
||||||
|
constructor(@InjectRepository(AssetEntity) private assetRepository: Repository<AssetEntity>) {}
|
||||||
|
|
||||||
|
async getUniqueOriginalPaths(userId: string): Promise<string[]> {
|
||||||
|
const results = await this.assetRepository
|
||||||
|
.createQueryBuilder('asset')
|
||||||
|
.where({
|
||||||
|
isVisible: true,
|
||||||
|
isArchived: false,
|
||||||
|
ownerId: userId,
|
||||||
|
})
|
||||||
|
.select("DISTINCT substring(asset.originalPath FROM '^(.*/)[^/]*$')", 'directoryPath')
|
||||||
|
.getRawMany();
|
||||||
|
|
||||||
|
return results.map((row: { directoryPath: string }) => row.directoryPath.replaceAll(/^\/|\/$/g, ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
|
||||||
|
async getAssetsByOriginalPath(userId: string, partialPath: string): Promise<AssetEntity[]> {
|
||||||
|
const normalizedPath = partialPath.replaceAll(/^\/|\/$/g, '');
|
||||||
|
const assets = await this.assetRepository
|
||||||
|
.createQueryBuilder('asset')
|
||||||
|
.where({
|
||||||
|
isVisible: true,
|
||||||
|
isArchived: false,
|
||||||
|
ownerId: userId,
|
||||||
|
})
|
||||||
|
.leftJoinAndSelect('asset.exifInfo', 'exifInfo')
|
||||||
|
.andWhere(
|
||||||
|
new Brackets((qb) => {
|
||||||
|
qb.where('asset.originalPath LIKE :likePath', { likePath: `%${normalizedPath}/%` }).andWhere(
|
||||||
|
'asset.originalPath NOT LIKE :notLikePath',
|
||||||
|
{ notLikePath: `%${normalizedPath}/%/%` },
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.orderBy(String.raw`regexp_replace(asset.originalPath, '.*/(.+)', '\1')`, 'ASC')
|
||||||
|
.getMany();
|
||||||
|
|
||||||
|
return assets;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,20 @@
|
||||||
import { mapAsset } from 'src/dtos/asset-response.dto';
|
import { mapAsset } from 'src/dtos/asset-response.dto';
|
||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { IViewRepository } from 'src/interfaces/view.interface';
|
||||||
|
|
||||||
import { ViewService } from 'src/services/view.service';
|
import { ViewService } from 'src/services/view.service';
|
||||||
import { assetStub } from 'test/fixtures/asset.stub';
|
import { assetStub } from 'test/fixtures/asset.stub';
|
||||||
import { authStub } from 'test/fixtures/auth.stub';
|
import { authStub } from 'test/fixtures/auth.stub';
|
||||||
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
|
import { newViewRepositoryMock } from 'test/repositories/view.repository.mock';
|
||||||
|
|
||||||
import { Mocked } from 'vitest';
|
import { Mocked } from 'vitest';
|
||||||
|
|
||||||
describe(ViewService.name, () => {
|
describe(ViewService.name, () => {
|
||||||
let sut: ViewService;
|
let sut: ViewService;
|
||||||
let assetMock: Mocked<IAssetRepository>;
|
let viewMock: Mocked<IViewRepository>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
assetMock = newAssetRepositoryMock();
|
viewMock = newViewRepositoryMock();
|
||||||
|
|
||||||
sut = new ViewService(assetMock);
|
sut = new ViewService(viewMock);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', () => {
|
it('should work', () => {
|
||||||
|
@ -25,12 +24,12 @@ describe(ViewService.name, () => {
|
||||||
describe('getUniqueOriginalPaths', () => {
|
describe('getUniqueOriginalPaths', () => {
|
||||||
it('should return unique original paths', async () => {
|
it('should return unique original paths', async () => {
|
||||||
const mockPaths = ['path1', 'path2', 'path3'];
|
const mockPaths = ['path1', 'path2', 'path3'];
|
||||||
assetMock.getUniqueOriginalPaths.mockResolvedValue(mockPaths);
|
viewMock.getUniqueOriginalPaths.mockResolvedValue(mockPaths);
|
||||||
|
|
||||||
const result = await sut.getUniqueOriginalPaths(authStub.admin);
|
const result = await sut.getUniqueOriginalPaths(authStub.admin);
|
||||||
|
|
||||||
expect(result).toEqual(mockPaths);
|
expect(result).toEqual(mockPaths);
|
||||||
expect(assetMock.getUniqueOriginalPaths).toHaveBeenCalledWith(authStub.admin.user.id);
|
expect(viewMock.getUniqueOriginalPaths).toHaveBeenCalledWith(authStub.admin.user.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -45,11 +44,11 @@ describe(ViewService.name, () => {
|
||||||
|
|
||||||
const mockAssetReponseDto = mockAssets.map((a) => mapAsset(a, { auth: authStub.admin }));
|
const mockAssetReponseDto = mockAssets.map((a) => mapAsset(a, { auth: authStub.admin }));
|
||||||
|
|
||||||
assetMock.getAssetsByOriginalPath.mockResolvedValue(mockAssets);
|
viewMock.getAssetsByOriginalPath.mockResolvedValue(mockAssets);
|
||||||
|
|
||||||
const result = await sut.getAssetsByOriginalPath(authStub.admin, path);
|
const result = await sut.getAssetsByOriginalPath(authStub.admin, path);
|
||||||
expect(result).toEqual(mockAssetReponseDto);
|
expect(result).toEqual(mockAssetReponseDto);
|
||||||
await expect(assetMock.getAssetsByOriginalPath(authStub.admin.user.id, path)).resolves.toEqual(mockAssets);
|
await expect(viewMock.getAssetsByOriginalPath(authStub.admin.user.id, path)).resolves.toEqual(mockAssets);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
import { Inject } from '@nestjs/common';
|
import { Inject } from '@nestjs/common';
|
||||||
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 { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { IViewRepository } from 'src/interfaces/view.interface';
|
||||||
|
|
||||||
export class ViewService {
|
export class ViewService {
|
||||||
constructor(@Inject(IAssetRepository) private assetRepository: IAssetRepository) {}
|
constructor(@Inject(IViewRepository) private viewRepository: IViewRepository) {}
|
||||||
|
|
||||||
getUniqueOriginalPaths(auth: AuthDto): Promise<string[]> {
|
getUniqueOriginalPaths(auth: AuthDto): Promise<string[]> {
|
||||||
return this.assetRepository.getUniqueOriginalPaths(auth.user.id);
|
return this.viewRepository.getUniqueOriginalPaths(auth.user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAssetsByOriginalPath(auth: AuthDto, path: string): Promise<AssetResponseDto[]> {
|
async getAssetsByOriginalPath(auth: AuthDto, path: string): Promise<AssetResponseDto[]> {
|
||||||
const assets = await this.assetRepository.getAssetsByOriginalPath(auth.user.id, path);
|
const assets = await this.viewRepository.getAssetsByOriginalPath(auth.user.id, path);
|
||||||
|
|
||||||
return assets.map((asset) => mapAsset(asset, { auth }));
|
return assets.map((asset) => mapAsset(asset, { auth }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,5 @@ export const newAssetRepositoryMock = (): Mocked<IAssetRepository> => {
|
||||||
getChangedDeltaSync: vitest.fn(),
|
getChangedDeltaSync: vitest.fn(),
|
||||||
getDuplicates: vitest.fn(),
|
getDuplicates: vitest.fn(),
|
||||||
upsertFile: vitest.fn(),
|
upsertFile: vitest.fn(),
|
||||||
getAssetsByOriginalPath: vitest.fn(),
|
|
||||||
getUniqueOriginalPaths: vitest.fn(),
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
9
server/test/repositories/view.repository.mock.ts
Normal file
9
server/test/repositories/view.repository.mock.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { IViewRepository } from 'src/interfaces/view.interface';
|
||||||
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
|
export const newViewRepositoryMock = (): Mocked<IViewRepository> => {
|
||||||
|
return {
|
||||||
|
getAssetsByOriginalPath: vitest.fn(),
|
||||||
|
getUniqueOriginalPaths: vitest.fn(),
|
||||||
|
};
|
||||||
|
};
|
Loading…
Add table
Reference in a new issue