0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-21 00:52:43 -05:00

fix(server): person repo methods (#12524)

This commit is contained in:
Jason Rasmussen 2024-09-10 09:48:29 -04:00 committed by GitHub
parent 27050af57b
commit d634ef2d2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 85 additions and 81 deletions

View file

@ -301,7 +301,7 @@ export class StorageCore {
return this.assetRepository.update({ id, sidecarPath: newPath }); return this.assetRepository.update({ id, sidecarPath: newPath });
} }
case PersonPathType.FACE: { case PersonPathType.FACE: {
return this.personRepository.update([{ id, thumbnailPath: newPath }]); return this.personRepository.update({ id, thumbnailPath: newPath });
} }
} }
} }

View file

@ -54,7 +54,8 @@ export interface IPersonRepository {
getAssets(personId: string): Promise<AssetEntity[]>; getAssets(personId: string): Promise<AssetEntity[]>;
create(entities: Partial<PersonEntity>[]): Promise<PersonEntity[]>; create(person: Partial<PersonEntity>): Promise<PersonEntity>;
createAll(people: Partial<PersonEntity>[]): Promise<string[]>;
createFaces(entities: Partial<AssetFaceEntity>[]): Promise<string[]>; createFaces(entities: Partial<AssetFaceEntity>[]): Promise<string[]>;
delete(entities: PersonEntity[]): Promise<void>; delete(entities: PersonEntity[]): Promise<void>;
deleteAll(): Promise<void>; deleteAll(): Promise<void>;
@ -74,6 +75,7 @@ export interface IPersonRepository {
reassignFace(assetFaceId: string, newPersonId: string): Promise<number>; reassignFace(assetFaceId: string, newPersonId: string): Promise<number>;
getNumberOfPeople(userId: string): Promise<PeopleStatistics>; getNumberOfPeople(userId: string): Promise<PeopleStatistics>;
reassignFaces(data: UpdateFacesData): Promise<number>; reassignFaces(data: UpdateFacesData): Promise<number>;
update(entities: Partial<PersonEntity>[]): Promise<PersonEntity[]>; update(person: Partial<PersonEntity>): Promise<PersonEntity>;
updateAll(people: Partial<PersonEntity>[]): Promise<void>;
getLatestFaceDate(): Promise<string | undefined>; getLatestFaceDate(): Promise<string | undefined>;
} }

View file

@ -280,8 +280,13 @@ export class PersonRepository implements IPersonRepository {
return result; return result;
} }
create(entities: Partial<PersonEntity>[]): Promise<PersonEntity[]> { create(person: Partial<PersonEntity>): Promise<PersonEntity> {
return this.personRepository.save(entities); return this.save(person);
}
async createAll(people: Partial<PersonEntity>[]): Promise<string[]> {
const results = await this.personRepository.save(people);
return results.map((person) => person.id);
} }
async createFaces(entities: AssetFaceEntity[]): Promise<string[]> { async createFaces(entities: AssetFaceEntity[]): Promise<string[]> {
@ -297,8 +302,12 @@ export class PersonRepository implements IPersonRepository {
}); });
} }
async update(entities: Partial<PersonEntity>[]): Promise<PersonEntity[]> { async update(person: Partial<PersonEntity>): Promise<PersonEntity> {
return await this.personRepository.save(entities); return this.save(person);
}
async updateAll(people: Partial<PersonEntity>[]): Promise<void> {
await this.personRepository.save(people);
} }
@GenerateSql({ params: [[{ assetId: DummyValue.UUID, personId: DummyValue.UUID }]] }) @GenerateSql({ params: [[{ assetId: DummyValue.UUID, personId: DummyValue.UUID }]] })
@ -320,4 +329,9 @@ export class PersonRepository implements IPersonRepository {
.getRawOne(); .getRawOne();
return result?.latestDate; return result?.latestDate;
} }
private async save(person: Partial<PersonEntity>): Promise<PersonEntity> {
const { id } = await this.personRepository.save(person);
return this.personRepository.findOneByOrFail({ id });
}
} }

View file

@ -115,7 +115,7 @@ export class AuditService {
} }
case PersonPathType.FACE: { case PersonPathType.FACE: {
await this.personRepository.update([{ id, thumbnailPath: pathValue }]); await this.personRepository.update({ id, thumbnailPath: pathValue });
break; break;
} }

View file

@ -117,7 +117,7 @@ export class MediaService {
continue; continue;
} }
await this.personRepository.update([{ id: person.id, faceAssetId: face.id }]); await this.personRepository.update({ id: person.id, faceAssetId: face.id });
} }
jobs.push({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id: person.id } }); jobs.push({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id: person.id } });

View file

@ -1002,13 +1002,12 @@ describe(MetadataService.name, () => {
systemMock.get.mockResolvedValue({ metadata: { faces: { import: true } } }); systemMock.get.mockResolvedValue({ metadata: { faces: { import: true } } });
metadataMock.readTags.mockResolvedValue(metadataStub.withFaceNoName); metadataMock.readTags.mockResolvedValue(metadataStub.withFaceNoName);
personMock.getDistinctNames.mockResolvedValue([]); personMock.getDistinctNames.mockResolvedValue([]);
personMock.create.mockResolvedValue([]); personMock.createAll.mockResolvedValue([]);
personMock.replaceFaces.mockResolvedValue([]); personMock.replaceFaces.mockResolvedValue([]);
personMock.update.mockResolvedValue([]);
await sut.handleMetadataExtraction({ id: assetStub.image.id }); await sut.handleMetadataExtraction({ id: assetStub.image.id });
expect(personMock.create).toHaveBeenCalledWith([]); expect(personMock.createAll).toHaveBeenCalledWith([]);
expect(personMock.replaceFaces).toHaveBeenCalledWith(assetStub.primaryImage.id, [], SourceType.EXIF); expect(personMock.replaceFaces).toHaveBeenCalledWith(assetStub.primaryImage.id, [], SourceType.EXIF);
expect(personMock.update).toHaveBeenCalledWith([]); expect(personMock.updateAll).toHaveBeenCalledWith([]);
}); });
it('should skip importing faces with empty name', async () => { it('should skip importing faces with empty name', async () => {
@ -1016,13 +1015,12 @@ describe(MetadataService.name, () => {
systemMock.get.mockResolvedValue({ metadata: { faces: { import: true } } }); systemMock.get.mockResolvedValue({ metadata: { faces: { import: true } } });
metadataMock.readTags.mockResolvedValue(metadataStub.withFaceEmptyName); metadataMock.readTags.mockResolvedValue(metadataStub.withFaceEmptyName);
personMock.getDistinctNames.mockResolvedValue([]); personMock.getDistinctNames.mockResolvedValue([]);
personMock.create.mockResolvedValue([]); personMock.createAll.mockResolvedValue([]);
personMock.replaceFaces.mockResolvedValue([]); personMock.replaceFaces.mockResolvedValue([]);
personMock.update.mockResolvedValue([]);
await sut.handleMetadataExtraction({ id: assetStub.image.id }); await sut.handleMetadataExtraction({ id: assetStub.image.id });
expect(personMock.create).toHaveBeenCalledWith([]); expect(personMock.createAll).toHaveBeenCalledWith([]);
expect(personMock.replaceFaces).toHaveBeenCalledWith(assetStub.primaryImage.id, [], SourceType.EXIF); expect(personMock.replaceFaces).toHaveBeenCalledWith(assetStub.primaryImage.id, [], SourceType.EXIF);
expect(personMock.update).toHaveBeenCalledWith([]); expect(personMock.updateAll).toHaveBeenCalledWith([]);
}); });
it('should apply metadata face tags creating new persons', async () => { it('should apply metadata face tags creating new persons', async () => {
@ -1030,13 +1028,13 @@ describe(MetadataService.name, () => {
systemMock.get.mockResolvedValue({ metadata: { faces: { import: true } } }); systemMock.get.mockResolvedValue({ metadata: { faces: { import: true } } });
metadataMock.readTags.mockResolvedValue(metadataStub.withFace); metadataMock.readTags.mockResolvedValue(metadataStub.withFace);
personMock.getDistinctNames.mockResolvedValue([]); personMock.getDistinctNames.mockResolvedValue([]);
personMock.create.mockResolvedValue([personStub.withName]); personMock.createAll.mockResolvedValue([personStub.withName.id]);
personMock.replaceFaces.mockResolvedValue(['face-asset-uuid']); personMock.replaceFaces.mockResolvedValue(['face-asset-uuid']);
personMock.update.mockResolvedValue([personStub.withName]); personMock.update.mockResolvedValue(personStub.withName);
await sut.handleMetadataExtraction({ id: assetStub.primaryImage.id }); await sut.handleMetadataExtraction({ id: assetStub.primaryImage.id });
expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.primaryImage.id]); expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.primaryImage.id]);
expect(personMock.getDistinctNames).toHaveBeenCalledWith(assetStub.primaryImage.ownerId, { withHidden: true }); expect(personMock.getDistinctNames).toHaveBeenCalledWith(assetStub.primaryImage.ownerId, { withHidden: true });
expect(personMock.create).toHaveBeenCalledWith([expect.objectContaining({ name: personStub.withName.name })]); expect(personMock.createAll).toHaveBeenCalledWith([expect.objectContaining({ name: personStub.withName.name })]);
expect(personMock.replaceFaces).toHaveBeenCalledWith( expect(personMock.replaceFaces).toHaveBeenCalledWith(
assetStub.primaryImage.id, assetStub.primaryImage.id,
[ [
@ -1055,7 +1053,7 @@ describe(MetadataService.name, () => {
], ],
SourceType.EXIF, SourceType.EXIF,
); );
expect(personMock.update).toHaveBeenCalledWith([{ id: 'random-uuid', faceAssetId: 'random-uuid' }]); expect(personMock.updateAll).toHaveBeenCalledWith([{ id: 'random-uuid', faceAssetId: 'random-uuid' }]);
expect(jobMock.queueAll).toHaveBeenCalledWith([ expect(jobMock.queueAll).toHaveBeenCalledWith([
{ {
name: JobName.GENERATE_PERSON_THUMBNAIL, name: JobName.GENERATE_PERSON_THUMBNAIL,
@ -1069,13 +1067,13 @@ describe(MetadataService.name, () => {
systemMock.get.mockResolvedValue({ metadata: { faces: { import: true } } }); systemMock.get.mockResolvedValue({ metadata: { faces: { import: true } } });
metadataMock.readTags.mockResolvedValue(metadataStub.withFace); metadataMock.readTags.mockResolvedValue(metadataStub.withFace);
personMock.getDistinctNames.mockResolvedValue([{ id: personStub.withName.id, name: personStub.withName.name }]); personMock.getDistinctNames.mockResolvedValue([{ id: personStub.withName.id, name: personStub.withName.name }]);
personMock.create.mockResolvedValue([]); personMock.createAll.mockResolvedValue([]);
personMock.replaceFaces.mockResolvedValue(['face-asset-uuid']); personMock.replaceFaces.mockResolvedValue(['face-asset-uuid']);
personMock.update.mockResolvedValue([personStub.withName]); personMock.update.mockResolvedValue(personStub.withName);
await sut.handleMetadataExtraction({ id: assetStub.primaryImage.id }); await sut.handleMetadataExtraction({ id: assetStub.primaryImage.id });
expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.primaryImage.id]); expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.primaryImage.id]);
expect(personMock.getDistinctNames).toHaveBeenCalledWith(assetStub.primaryImage.ownerId, { withHidden: true }); expect(personMock.getDistinctNames).toHaveBeenCalledWith(assetStub.primaryImage.ownerId, { withHidden: true });
expect(personMock.create).toHaveBeenCalledWith([]); expect(personMock.createAll).toHaveBeenCalledWith([]);
expect(personMock.replaceFaces).toHaveBeenCalledWith( expect(personMock.replaceFaces).toHaveBeenCalledWith(
assetStub.primaryImage.id, assetStub.primaryImage.id,
[ [
@ -1094,7 +1092,7 @@ describe(MetadataService.name, () => {
], ],
SourceType.EXIF, SourceType.EXIF,
); );
expect(personMock.update).toHaveBeenCalledWith([]); expect(personMock.updateAll).toHaveBeenCalledWith([]);
expect(jobMock.queueAll).toHaveBeenCalledWith([]); expect(jobMock.queueAll).toHaveBeenCalledWith([]);
}); });
}); });

View file

@ -584,18 +584,15 @@ export class MetadataService {
this.logger.debug(`Creating missing persons: ${missing.map((p) => `${p.name}/${p.id}`)}`); this.logger.debug(`Creating missing persons: ${missing.map((p) => `${p.name}/${p.id}`)}`);
} }
const newPersons = await this.personRepository.create(missing); const newPersonIds = await this.personRepository.createAll(missing);
const faceIds = await this.personRepository.replaceFaces(asset.id, discoveredFaces, SourceType.EXIF); const faceIds = await this.personRepository.replaceFaces(asset.id, discoveredFaces, SourceType.EXIF);
this.logger.debug(`Created ${faceIds.length} faces for asset ${asset.id}`); this.logger.debug(`Created ${faceIds.length} faces for asset ${asset.id}`);
await this.personRepository.update(missingWithFaceAsset); await this.personRepository.updateAll(missingWithFaceAsset);
await this.jobRepository.queueAll( await this.jobRepository.queueAll(
newPersons.map((person) => ({ newPersonIds.map((id) => ({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id } })),
name: JobName.GENERATE_PERSON_THUMBNAIL,
data: { id: person.id },
})),
); );
} }

View file

@ -241,18 +241,18 @@ describe(PersonService.name, () => {
}); });
it("should update a person's name", async () => { it("should update a person's name", async () => {
personMock.update.mockResolvedValue([personStub.withName]); personMock.update.mockResolvedValue(personStub.withName);
personMock.getAssets.mockResolvedValue([assetStub.image]); personMock.getAssets.mockResolvedValue([assetStub.image]);
accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1'])); accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1']));
await expect(sut.update(authStub.admin, 'person-1', { name: 'Person 1' })).resolves.toEqual(responseDto); await expect(sut.update(authStub.admin, 'person-1', { name: 'Person 1' })).resolves.toEqual(responseDto);
expect(personMock.update).toHaveBeenCalledWith([{ id: 'person-1', name: 'Person 1' }]); expect(personMock.update).toHaveBeenCalledWith({ id: 'person-1', name: 'Person 1' });
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1'])); expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
}); });
it("should update a person's date of birth", async () => { it("should update a person's date of birth", async () => {
personMock.update.mockResolvedValue([personStub.withBirthDate]); personMock.update.mockResolvedValue(personStub.withBirthDate);
personMock.getAssets.mockResolvedValue([assetStub.image]); personMock.getAssets.mockResolvedValue([assetStub.image]);
accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1'])); accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1']));
@ -264,25 +264,25 @@ describe(PersonService.name, () => {
isHidden: false, isHidden: false,
updatedAt: expect.any(Date), updatedAt: expect.any(Date),
}); });
expect(personMock.update).toHaveBeenCalledWith([{ id: 'person-1', birthDate: '1976-06-30' }]); expect(personMock.update).toHaveBeenCalledWith({ id: 'person-1', birthDate: '1976-06-30' });
expect(jobMock.queue).not.toHaveBeenCalled(); expect(jobMock.queue).not.toHaveBeenCalled();
expect(jobMock.queueAll).not.toHaveBeenCalled(); expect(jobMock.queueAll).not.toHaveBeenCalled();
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1'])); expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
}); });
it('should update a person visibility', async () => { it('should update a person visibility', async () => {
personMock.update.mockResolvedValue([personStub.withName]); personMock.update.mockResolvedValue(personStub.withName);
personMock.getAssets.mockResolvedValue([assetStub.image]); personMock.getAssets.mockResolvedValue([assetStub.image]);
accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1'])); accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1']));
await expect(sut.update(authStub.admin, 'person-1', { isHidden: false })).resolves.toEqual(responseDto); await expect(sut.update(authStub.admin, 'person-1', { isHidden: false })).resolves.toEqual(responseDto);
expect(personMock.update).toHaveBeenCalledWith([{ id: 'person-1', isHidden: false }]); expect(personMock.update).toHaveBeenCalledWith({ id: 'person-1', isHidden: false });
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1'])); expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
}); });
it("should update a person's thumbnailPath", async () => { it("should update a person's thumbnailPath", async () => {
personMock.update.mockResolvedValue([personStub.withName]); personMock.update.mockResolvedValue(personStub.withName);
personMock.getFacesByIds.mockResolvedValue([faceStub.face1]); personMock.getFacesByIds.mockResolvedValue([faceStub.face1]);
accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set([assetStub.image.id])); accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set([assetStub.image.id]));
accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1'])); accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1']));
@ -291,7 +291,7 @@ describe(PersonService.name, () => {
sut.update(authStub.admin, 'person-1', { featureFaceAssetId: faceStub.face1.assetId }), sut.update(authStub.admin, 'person-1', { featureFaceAssetId: faceStub.face1.assetId }),
).resolves.toEqual(responseDto); ).resolves.toEqual(responseDto);
expect(personMock.update).toHaveBeenCalledWith([{ id: 'person-1', faceAssetId: faceStub.face1.id }]); expect(personMock.update).toHaveBeenCalledWith({ id: 'person-1', faceAssetId: faceStub.face1.id });
expect(personMock.getFacesByIds).toHaveBeenCalledWith([ expect(personMock.getFacesByIds).toHaveBeenCalledWith([
{ {
assetId: faceStub.face1.assetId, assetId: faceStub.face1.assetId,
@ -441,11 +441,11 @@ describe(PersonService.name, () => {
describe('createPerson', () => { describe('createPerson', () => {
it('should create a new person', async () => { it('should create a new person', async () => {
personMock.create.mockResolvedValue([personStub.primaryPerson]); personMock.create.mockResolvedValue(personStub.primaryPerson);
await expect(sut.create(authStub.admin, {})).resolves.toBe(personStub.primaryPerson); await expect(sut.create(authStub.admin, {})).resolves.toBe(personStub.primaryPerson);
expect(personMock.create).toHaveBeenCalledWith([{ ownerId: authStub.admin.user.id }]); expect(personMock.create).toHaveBeenCalledWith({ ownerId: authStub.admin.user.id });
}); });
}); });
@ -819,7 +819,7 @@ describe(PersonService.name, () => {
systemMock.get.mockResolvedValue({ machineLearning: { facialRecognition: { minFaces: 1 } } }); systemMock.get.mockResolvedValue({ machineLearning: { facialRecognition: { minFaces: 1 } } });
searchMock.searchFaces.mockResolvedValue(faces); searchMock.searchFaces.mockResolvedValue(faces);
personMock.getFaceByIdWithAssets.mockResolvedValue(faceStub.noPerson1); personMock.getFaceByIdWithAssets.mockResolvedValue(faceStub.noPerson1);
personMock.create.mockResolvedValue([faceStub.primaryFace1.person]); personMock.create.mockResolvedValue(faceStub.primaryFace1.person);
await sut.handleRecognizeFaces({ id: faceStub.noPerson1.id }); await sut.handleRecognizeFaces({ id: faceStub.noPerson1.id });
@ -844,16 +844,14 @@ describe(PersonService.name, () => {
systemMock.get.mockResolvedValue({ machineLearning: { facialRecognition: { minFaces: 1 } } }); systemMock.get.mockResolvedValue({ machineLearning: { facialRecognition: { minFaces: 1 } } });
searchMock.searchFaces.mockResolvedValue(faces); searchMock.searchFaces.mockResolvedValue(faces);
personMock.getFaceByIdWithAssets.mockResolvedValue(faceStub.noPerson1); personMock.getFaceByIdWithAssets.mockResolvedValue(faceStub.noPerson1);
personMock.create.mockResolvedValue([personStub.withName]); personMock.create.mockResolvedValue(personStub.withName);
await sut.handleRecognizeFaces({ id: faceStub.noPerson1.id }); await sut.handleRecognizeFaces({ id: faceStub.noPerson1.id });
expect(personMock.create).toHaveBeenCalledWith([ expect(personMock.create).toHaveBeenCalledWith({
{
ownerId: faceStub.noPerson1.asset.ownerId, ownerId: faceStub.noPerson1.asset.ownerId,
faceAssetId: faceStub.noPerson1.id, faceAssetId: faceStub.noPerson1.id,
}, });
]);
expect(personMock.reassignFaces).toHaveBeenCalledWith({ expect(personMock.reassignFaces).toHaveBeenCalledWith({
faceIds: [faceStub.noPerson1.id], faceIds: [faceStub.noPerson1.id],
newPersonId: personStub.withName.id, newPersonId: personStub.withName.id,
@ -865,7 +863,7 @@ describe(PersonService.name, () => {
searchMock.searchFaces.mockResolvedValue(faces); searchMock.searchFaces.mockResolvedValue(faces);
personMock.getFaceByIdWithAssets.mockResolvedValue(faceStub.noPerson1); personMock.getFaceByIdWithAssets.mockResolvedValue(faceStub.noPerson1);
personMock.create.mockResolvedValue([personStub.withName]); personMock.create.mockResolvedValue(personStub.withName);
await sut.handleRecognizeFaces({ id: faceStub.noPerson1.id }); await sut.handleRecognizeFaces({ id: faceStub.noPerson1.id });
@ -884,7 +882,7 @@ describe(PersonService.name, () => {
systemMock.get.mockResolvedValue({ machineLearning: { facialRecognition: { minFaces: 3 } } }); systemMock.get.mockResolvedValue({ machineLearning: { facialRecognition: { minFaces: 3 } } });
searchMock.searchFaces.mockResolvedValue(faces); searchMock.searchFaces.mockResolvedValue(faces);
personMock.getFaceByIdWithAssets.mockResolvedValue(faceStub.noPerson1); personMock.getFaceByIdWithAssets.mockResolvedValue(faceStub.noPerson1);
personMock.create.mockResolvedValue([personStub.withName]); personMock.create.mockResolvedValue(personStub.withName);
await sut.handleRecognizeFaces({ id: faceStub.noPerson1.id }); await sut.handleRecognizeFaces({ id: faceStub.noPerson1.id });
@ -906,7 +904,7 @@ describe(PersonService.name, () => {
systemMock.get.mockResolvedValue({ machineLearning: { facialRecognition: { minFaces: 3 } } }); systemMock.get.mockResolvedValue({ machineLearning: { facialRecognition: { minFaces: 3 } } });
searchMock.searchFaces.mockResolvedValueOnce(faces).mockResolvedValueOnce([]); searchMock.searchFaces.mockResolvedValueOnce(faces).mockResolvedValueOnce([]);
personMock.getFaceByIdWithAssets.mockResolvedValue(faceStub.noPerson1); personMock.getFaceByIdWithAssets.mockResolvedValue(faceStub.noPerson1);
personMock.create.mockResolvedValue([personStub.withName]); personMock.create.mockResolvedValue(personStub.withName);
await sut.handleRecognizeFaces({ id: faceStub.noPerson1.id, deferred: true }); await sut.handleRecognizeFaces({ id: faceStub.noPerson1.id, deferred: true });
@ -979,12 +977,10 @@ describe(PersonService.name, () => {
processInvalidImages: false, processInvalidImages: false,
}, },
); );
expect(personMock.update).toHaveBeenCalledWith([ expect(personMock.update).toHaveBeenCalledWith({
{
id: 'person-1', id: 'person-1',
thumbnailPath: 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', thumbnailPath: 'upload/thumbs/admin_id/pe/rs/person-1.jpeg',
}, });
]);
}); });
it('should generate a thumbnail without going negative', async () => { it('should generate a thumbnail without going negative', async () => {
@ -1103,7 +1099,7 @@ describe(PersonService.name, () => {
it('should merge two people with smart merge', async () => { it('should merge two people with smart merge', async () => {
personMock.getById.mockResolvedValueOnce(personStub.randomPerson); personMock.getById.mockResolvedValueOnce(personStub.randomPerson);
personMock.getById.mockResolvedValueOnce(personStub.primaryPerson); personMock.getById.mockResolvedValueOnce(personStub.primaryPerson);
personMock.update.mockResolvedValue([{ ...personStub.randomPerson, name: personStub.primaryPerson.name }]); personMock.update.mockResolvedValue({ ...personStub.randomPerson, name: personStub.primaryPerson.name });
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-3'])); accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-3']));
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-1'])); accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-1']));
@ -1116,12 +1112,10 @@ describe(PersonService.name, () => {
oldPersonId: personStub.primaryPerson.id, oldPersonId: personStub.primaryPerson.id,
}); });
expect(personMock.update).toHaveBeenCalledWith([ expect(personMock.update).toHaveBeenCalledWith({
{
id: personStub.randomPerson.id, id: personStub.randomPerson.id,
name: personStub.primaryPerson.name, name: personStub.primaryPerson.name,
}, });
]);
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1'])); expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
}); });

View file

@ -173,7 +173,7 @@ export class PersonService {
const assetFace = await this.repository.getRandomFace(personId); const assetFace = await this.repository.getRandomFace(personId);
if (assetFace !== null) { if (assetFace !== null) {
await this.repository.update([{ id: personId, faceAssetId: assetFace.id }]); await this.repository.update({ id: personId, faceAssetId: assetFace.id });
jobs.push({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id: personId } }); jobs.push({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id: personId } });
} }
} }
@ -211,16 +211,13 @@ export class PersonService {
return assets.map((asset) => mapAsset(asset)); return assets.map((asset) => mapAsset(asset));
} }
async create(auth: AuthDto, dto: PersonCreateDto): Promise<PersonResponseDto> { create(auth: AuthDto, dto: PersonCreateDto): Promise<PersonResponseDto> {
const [created] = await this.repository.create([ return this.repository.create({
{
ownerId: auth.user.id, ownerId: auth.user.id,
name: dto.name, name: dto.name,
birthDate: dto.birthDate, birthDate: dto.birthDate,
isHidden: dto.isHidden, isHidden: dto.isHidden,
}, });
]);
return created;
} }
async update(auth: AuthDto, id: string, dto: PersonUpdateDto): Promise<PersonResponseDto> { async update(auth: AuthDto, id: string, dto: PersonUpdateDto): Promise<PersonResponseDto> {
@ -239,7 +236,7 @@ export class PersonService {
faceId = face.id; faceId = face.id;
} }
const [person] = await this.repository.update([{ id, faceAssetId: faceId, name, birthDate, isHidden }]); const person = await this.repository.update({ id, faceAssetId: faceId, name, birthDate, isHidden });
if (assetId) { if (assetId) {
await this.jobRepository.queue({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id } }); await this.jobRepository.queue({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id } });
@ -501,7 +498,7 @@ export class PersonService {
if (isCore && !personId) { if (isCore && !personId) {
this.logger.log(`Creating new person for face ${id}`); this.logger.log(`Creating new person for face ${id}`);
const [newPerson] = await this.repository.create([{ ownerId: face.asset.ownerId, faceAssetId: face.id }]); const newPerson = await this.repository.create({ ownerId: face.asset.ownerId, faceAssetId: face.id });
await this.jobRepository.queue({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id: newPerson.id } }); await this.jobRepository.queue({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id: newPerson.id } });
personId = newPerson.id; personId = newPerson.id;
} }
@ -577,7 +574,7 @@ export class PersonService {
} as const; } as const;
await this.mediaRepository.generateThumbnail(inputPath, thumbnailPath, thumbnailOptions); await this.mediaRepository.generateThumbnail(inputPath, thumbnailPath, thumbnailOptions);
await this.repository.update([{ id: person.id, thumbnailPath }]); await this.repository.update({ id: person.id, thumbnailPath });
return JobStatus.SUCCESS; return JobStatus.SUCCESS;
} }
@ -624,7 +621,7 @@ export class PersonService {
} }
if (Object.keys(update).length > 0) { if (Object.keys(update).length > 0) {
[primaryPerson] = await this.repository.update([{ id: primaryPerson.id, ...update }]); primaryPerson = await this.repository.update({ id: primaryPerson.id, ...update });
} }
const mergeName = mergePerson.name || mergePerson.id; const mergeName = mergePerson.name || mergePerson.id;

View file

@ -13,9 +13,11 @@ export const newPersonRepositoryMock = (): Mocked<IPersonRepository> => {
getDistinctNames: vitest.fn(), getDistinctNames: vitest.fn(),
create: vitest.fn(), create: vitest.fn(),
createAll: vitest.fn(),
update: vitest.fn(), update: vitest.fn(),
deleteAll: vitest.fn(), updateAll: vitest.fn(),
delete: vitest.fn(), delete: vitest.fn(),
deleteAll: vitest.fn(),
deleteAllFaces: vitest.fn(), deleteAllFaces: vitest.fn(),
getStatistics: vitest.fn(), getStatistics: vitest.fn(),