refactor(server)*: tsconfigs (#2689)

* refactor(server): tsconfigs

* chore: dummy commit

* fix: start.sh

* chore: restore original entry scripts
This commit is contained in:
Jason Rasmussen
2023-06-08 11:01:07 -04:00
committed by GitHub
parent a2130aa6c5
commit 8ebac41318
465 changed files with 209 additions and 332 deletions

View File

@@ -0,0 +1,18 @@
export const IGeocodingRepository = 'IGeocodingRepository';
export interface GeoPoint {
latitude: number;
longitude: number;
}
export interface ReverseGeocodeResult {
country: string | null;
state: string | null;
city: string | null;
}
export interface IGeocodingRepository {
init(): Promise<void>;
reverseGeocode(point: GeoPoint): Promise<ReverseGeocodeResult>;
deleteCache(): Promise<void>;
}

View File

@@ -0,0 +1,2 @@
export * from './geocoding.repository';
export * from './metadata.service';

View File

@@ -0,0 +1,104 @@
import { constants } from 'fs/promises';
import { assetEntityStub, newAssetRepositoryMock, newJobRepositoryMock, newStorageRepositoryMock } from '@test';
import { IAssetRepository, WithoutProperty, WithProperty } from '../asset';
import { IJobRepository, JobName } from '../job';
import { IStorageRepository } from '../storage';
import { MetadataService } from './metadata.service';
describe(MetadataService.name, () => {
let sut: MetadataService;
let assetMock: jest.Mocked<IAssetRepository>;
let jobMock: jest.Mocked<IJobRepository>;
let storageMock: jest.Mocked<IStorageRepository>;
beforeEach(async () => {
assetMock = newAssetRepositoryMock();
jobMock = newJobRepositoryMock();
storageMock = newStorageRepositoryMock();
sut = new MetadataService(assetMock, jobMock, storageMock);
});
it('should be defined', () => {
expect(sut).toBeDefined();
});
describe('handleQueueSidecar', () => {
it('should queue assets with sidecar files', async () => {
assetMock.getWith.mockResolvedValue({ items: [assetEntityStub.sidecar], hasNextPage: false });
await sut.handleQueueSidecar({ force: true });
expect(assetMock.getWith).toHaveBeenCalledWith({ take: 1000, skip: 0 }, WithProperty.SIDECAR);
expect(assetMock.getWithout).not.toHaveBeenCalled();
expect(jobMock.queue).toHaveBeenCalledWith({
name: JobName.SIDECAR_SYNC,
data: { id: assetEntityStub.sidecar.id },
});
});
it('should queue assets without sidecar files', async () => {
assetMock.getWithout.mockResolvedValue({ items: [assetEntityStub.image], hasNextPage: false });
await sut.handleQueueSidecar({ force: false });
expect(assetMock.getWithout).toHaveBeenCalledWith({ take: 1000, skip: 0 }, WithoutProperty.SIDECAR);
expect(assetMock.getWith).not.toHaveBeenCalled();
expect(jobMock.queue).toHaveBeenCalledWith({
name: JobName.SIDECAR_DISCOVERY,
data: { id: assetEntityStub.image.id },
});
});
});
describe('handleSidecarSync', () => {
it('should not error', async () => {
await sut.handleSidecarSync();
});
});
describe('handleSidecarDiscovery', () => {
it('should skip hidden assets', async () => {
assetMock.getByIds.mockResolvedValue([assetEntityStub.livePhotoMotionAsset]);
await sut.handleSidecarDiscovery({ id: assetEntityStub.livePhotoMotionAsset.id });
expect(storageMock.checkFileExists).not.toHaveBeenCalled();
});
it('should skip assets with a sidecar path', async () => {
assetMock.getByIds.mockResolvedValue([assetEntityStub.sidecar]);
await sut.handleSidecarDiscovery({ id: assetEntityStub.sidecar.id });
expect(storageMock.checkFileExists).not.toHaveBeenCalled();
});
it('should do nothing when a sidecar is not found ', async () => {
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
storageMock.checkFileExists.mockResolvedValue(false);
await sut.handleSidecarDiscovery({ id: assetEntityStub.image.id });
expect(assetMock.save).not.toHaveBeenCalled();
});
it('should update a image asset when a sidecar is found', async () => {
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
assetMock.save.mockResolvedValue(assetEntityStub.image);
storageMock.checkFileExists.mockResolvedValue(true);
await sut.handleSidecarDiscovery({ id: assetEntityStub.image.id });
expect(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.ext.xmp', constants.W_OK);
expect(assetMock.save).toHaveBeenCalledWith({
id: assetEntityStub.image.id,
sidecarPath: '/original/path.ext.xmp',
});
});
it('should update a video asset when a sidecar is found', async () => {
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
assetMock.save.mockResolvedValue(assetEntityStub.video);
storageMock.checkFileExists.mockResolvedValue(true);
await sut.handleSidecarDiscovery({ id: assetEntityStub.video.id });
expect(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.ext.xmp', constants.W_OK);
expect(assetMock.save).toHaveBeenCalledWith({
id: assetEntityStub.image.id,
sidecarPath: '/original/path.ext.xmp',
});
});
});
});

View File

@@ -0,0 +1,54 @@
import { Inject } from '@nestjs/common';
import { constants } from 'fs/promises';
import { IAssetRepository, WithoutProperty, WithProperty } from '../asset';
import { usePagination } from '../domain.util';
import { IBaseJob, IEntityJob, IJobRepository, JobName, JOBS_ASSET_PAGINATION_SIZE } from '../job';
import { IStorageRepository } from '../storage';
export class MetadataService {
constructor(
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
@Inject(IJobRepository) private jobRepository: IJobRepository,
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
) {}
async handleQueueSidecar(job: IBaseJob) {
const { force } = job;
const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => {
return force
? this.assetRepository.getWith(pagination, WithProperty.SIDECAR)
: this.assetRepository.getWithout(pagination, WithoutProperty.SIDECAR);
});
for await (const assets of assetPagination) {
for (const asset of assets) {
const name = force ? JobName.SIDECAR_SYNC : JobName.SIDECAR_DISCOVERY;
await this.jobRepository.queue({ name, data: { id: asset.id } });
}
}
return true;
}
async handleSidecarSync() {
// TODO: optimize to only queue assets with recent xmp changes
return true;
}
async handleSidecarDiscovery({ id }: IEntityJob) {
const [asset] = await this.assetRepository.getByIds([id]);
if (!asset || !asset.isVisible || asset.sidecarPath) {
return false;
}
const sidecarPath = `${asset.originalPath}.xmp`;
const exists = await this.storageRepository.checkFileExists(sidecarPath, constants.W_OK);
if (!exists) {
return false;
}
await this.assetRepository.save({ id: asset.id, sidecarPath });
return true;
}
}