feat(web): show partners assets on the main timeline (#4933)
This commit is contained in:
@@ -40,6 +40,8 @@ export enum Permission {
|
||||
PERSON_READ = 'person.read',
|
||||
PERSON_WRITE = 'person.write',
|
||||
PERSON_MERGE = 'person.merge',
|
||||
|
||||
PARTNER_UPDATE = 'partner.update',
|
||||
}
|
||||
|
||||
let instance: AccessCore | null;
|
||||
@@ -242,6 +244,9 @@ export class AccessCore {
|
||||
case Permission.PERSON_MERGE:
|
||||
return this.repository.person.hasOwnerAccess(authUser.id, id);
|
||||
|
||||
case Permission.PARTNER_UPDATE:
|
||||
return this.repository.partner.hasUpdateAccess(authUser.id, id);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
newCommunicationRepositoryMock,
|
||||
newCryptoRepositoryMock,
|
||||
newJobRepositoryMock,
|
||||
newPartnerRepositoryMock,
|
||||
newStorageRepositoryMock,
|
||||
newSystemConfigRepositoryMock,
|
||||
} from '@test';
|
||||
@@ -23,6 +24,7 @@ import {
|
||||
ICommunicationRepository,
|
||||
ICryptoRepository,
|
||||
IJobRepository,
|
||||
IPartnerRepository,
|
||||
IStorageRepository,
|
||||
ISystemConfigRepository,
|
||||
JobItem,
|
||||
@@ -164,6 +166,7 @@ describe(AssetService.name, () => {
|
||||
let storageMock: jest.Mocked<IStorageRepository>;
|
||||
let communicationMock: jest.Mocked<ICommunicationRepository>;
|
||||
let configMock: jest.Mocked<ISystemConfigRepository>;
|
||||
let partnerMock: jest.Mocked<IPartnerRepository>;
|
||||
|
||||
it('should work', () => {
|
||||
expect(sut).toBeDefined();
|
||||
@@ -177,7 +180,18 @@ describe(AssetService.name, () => {
|
||||
jobMock = newJobRepositoryMock();
|
||||
storageMock = newStorageRepositoryMock();
|
||||
configMock = newSystemConfigRepositoryMock();
|
||||
sut = new AssetService(accessMock, assetMock, cryptoMock, jobMock, configMock, storageMock, communicationMock);
|
||||
partnerMock = newPartnerRepositoryMock();
|
||||
|
||||
sut = new AssetService(
|
||||
accessMock,
|
||||
assetMock,
|
||||
cryptoMock,
|
||||
jobMock,
|
||||
configMock,
|
||||
storageMock,
|
||||
communicationMock,
|
||||
partnerMock,
|
||||
);
|
||||
|
||||
when(assetMock.getById)
|
||||
.calledWith(assetStub.livePhotoStillAsset.id)
|
||||
@@ -327,7 +341,7 @@ describe(AssetService.name, () => {
|
||||
size: TimeBucketSize.DAY,
|
||||
}),
|
||||
).resolves.toEqual(expect.arrayContaining([{ timeBucket: 'bucket', count: 1 }]));
|
||||
expect(assetMock.getTimeBuckets).toBeCalledWith({ size: TimeBucketSize.DAY, userId: authStub.admin.id });
|
||||
expect(assetMock.getTimeBuckets).toBeCalledWith({ size: TimeBucketSize.DAY, userIds: [authStub.admin.id] });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -363,7 +377,7 @@ describe(AssetService.name, () => {
|
||||
size: TimeBucketSize.DAY,
|
||||
timeBucket: 'bucket',
|
||||
isArchived: true,
|
||||
userId: authStub.admin.id,
|
||||
userIds: [authStub.admin.id],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -380,9 +394,65 @@ describe(AssetService.name, () => {
|
||||
expect(assetMock.getTimeBucket).toBeCalledWith('bucket', {
|
||||
size: TimeBucketSize.DAY,
|
||||
timeBucket: 'bucket',
|
||||
userId: authStub.admin.id,
|
||||
userIds: [authStub.admin.id],
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if withParners is true and isArchived true or undefined', async () => {
|
||||
await expect(
|
||||
sut.getTimeBucket(authStub.admin, {
|
||||
size: TimeBucketSize.DAY,
|
||||
timeBucket: 'bucket',
|
||||
isArchived: true,
|
||||
withPartners: true,
|
||||
userId: authStub.admin.id,
|
||||
}),
|
||||
).rejects.toThrowError(BadRequestException);
|
||||
|
||||
await expect(
|
||||
sut.getTimeBucket(authStub.admin, {
|
||||
size: TimeBucketSize.DAY,
|
||||
timeBucket: 'bucket',
|
||||
isArchived: undefined,
|
||||
withPartners: true,
|
||||
userId: authStub.admin.id,
|
||||
}),
|
||||
).rejects.toThrowError(BadRequestException);
|
||||
});
|
||||
|
||||
it('should throw an error if withParners is true and isFavorite is either true or false', async () => {
|
||||
await expect(
|
||||
sut.getTimeBucket(authStub.admin, {
|
||||
size: TimeBucketSize.DAY,
|
||||
timeBucket: 'bucket',
|
||||
isFavorite: true,
|
||||
withPartners: true,
|
||||
userId: authStub.admin.id,
|
||||
}),
|
||||
).rejects.toThrowError(BadRequestException);
|
||||
|
||||
await expect(
|
||||
sut.getTimeBucket(authStub.admin, {
|
||||
size: TimeBucketSize.DAY,
|
||||
timeBucket: 'bucket',
|
||||
isFavorite: false,
|
||||
withPartners: true,
|
||||
userId: authStub.admin.id,
|
||||
}),
|
||||
).rejects.toThrowError(BadRequestException);
|
||||
});
|
||||
|
||||
it('should throw an error if withParners is true and isTrash is true', async () => {
|
||||
await expect(
|
||||
sut.getTimeBucket(authStub.admin, {
|
||||
size: TimeBucketSize.DAY,
|
||||
timeBucket: 'bucket',
|
||||
isTrashed: true,
|
||||
withPartners: true,
|
||||
userId: authStub.admin.id,
|
||||
}),
|
||||
).rejects.toThrowError(BadRequestException);
|
||||
});
|
||||
});
|
||||
|
||||
describe('downloadFile', () => {
|
||||
|
||||
@@ -16,9 +16,11 @@ import {
|
||||
ICommunicationRepository,
|
||||
ICryptoRepository,
|
||||
IJobRepository,
|
||||
IPartnerRepository,
|
||||
IStorageRepository,
|
||||
ISystemConfigRepository,
|
||||
ImmichReadStream,
|
||||
TimeBucketOptions,
|
||||
} from '../repositories';
|
||||
import { StorageCore, StorageFolder } from '../storage';
|
||||
import { SystemConfigCore } from '../system-config';
|
||||
@@ -83,6 +85,7 @@ export class AssetService {
|
||||
@Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
|
||||
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
|
||||
@Inject(ICommunicationRepository) private communicationRepository: ICommunicationRepository,
|
||||
@Inject(IPartnerRepository) private partnerRepository: IPartnerRepository,
|
||||
) {
|
||||
this.access = AccessCore.create(accessRepository);
|
||||
this.configCore = SystemConfigCore.create(configRepository);
|
||||
@@ -187,11 +190,25 @@ export class AssetService {
|
||||
await this.access.requirePermission(authUser, Permission.ARCHIVE_READ, [dto.userId]);
|
||||
}
|
||||
}
|
||||
|
||||
if (dto.withPartners) {
|
||||
const requestedArchived = dto.isArchived === true || dto.isArchived === undefined;
|
||||
const requestedFavorite = dto.isFavorite === true || dto.isFavorite === false;
|
||||
const requestedTrash = dto.isTrashed === true;
|
||||
|
||||
if (requestedArchived || requestedFavorite || requestedTrash) {
|
||||
throw new BadRequestException(
|
||||
'withPartners is only supported for non-archived, non-trashed, non-favorited assets',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getTimeBuckets(authUser: AuthUserDto, dto: TimeBucketDto): Promise<TimeBucketResponseDto[]> {
|
||||
await this.timeBucketChecks(authUser, dto);
|
||||
return this.assetRepository.getTimeBuckets(dto);
|
||||
const timeBucketOptions = await this.buildTimeBucketOptions(authUser, dto);
|
||||
|
||||
return this.assetRepository.getTimeBuckets(timeBucketOptions);
|
||||
}
|
||||
|
||||
async getTimeBucket(
|
||||
@@ -199,7 +216,8 @@ export class AssetService {
|
||||
dto: TimeBucketAssetDto,
|
||||
): Promise<AssetResponseDto[] | SanitizedAssetResponseDto[]> {
|
||||
await this.timeBucketChecks(authUser, dto);
|
||||
const assets = await this.assetRepository.getTimeBucket(dto.timeBucket, dto);
|
||||
const timeBucketOptions = await this.buildTimeBucketOptions(authUser, dto);
|
||||
const assets = await this.assetRepository.getTimeBucket(dto.timeBucket, timeBucketOptions);
|
||||
if (authUser.isShowMetadata) {
|
||||
return assets.map((asset) => mapAsset(asset, { withStack: true }));
|
||||
} else {
|
||||
@@ -207,6 +225,25 @@ export class AssetService {
|
||||
}
|
||||
}
|
||||
|
||||
async buildTimeBucketOptions(authUser: AuthUserDto, dto: TimeBucketDto): Promise<TimeBucketOptions> {
|
||||
const { userId, ...options } = dto;
|
||||
let userIds: string[] | undefined = undefined;
|
||||
|
||||
if (userId) {
|
||||
userIds = [userId];
|
||||
|
||||
if (dto.withPartners) {
|
||||
const partners = await this.partnerRepository.getAll(authUser.id);
|
||||
const partnersIds = partners
|
||||
.filter((partner) => partner.sharedBy && partner.sharedWith && partner.inTimeline)
|
||||
.map((partner) => partner.sharedById);
|
||||
|
||||
userIds.push(...partnersIds);
|
||||
}
|
||||
}
|
||||
|
||||
return { ...options, userIds };
|
||||
}
|
||||
async downloadFile(authUser: AuthUserDto, id: string): Promise<ImmichReadStream> {
|
||||
await this.access.requirePermission(authUser, Permission.ASSET_DOWNLOAD, id);
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ export class TimeBucketDto {
|
||||
@IsBoolean()
|
||||
@Transform(toBoolean)
|
||||
withStacked?: boolean;
|
||||
|
||||
@Optional()
|
||||
@IsBoolean()
|
||||
@Transform(toBoolean)
|
||||
withPartners?: boolean;
|
||||
}
|
||||
|
||||
export class TimeBucketAssetDto extends TimeBucketDto {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
import { UserResponseDto } from '../user';
|
||||
|
||||
export class UpdatePartnerDto {
|
||||
@IsNotEmpty()
|
||||
inTimeline!: boolean;
|
||||
}
|
||||
|
||||
export class PartnerResponseDto extends UserResponseDto {
|
||||
inTimeline?: boolean;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { authStub, newPartnerRepositoryMock, partnerStub } from '@test';
|
||||
import { UserResponseDto } from '../index';
|
||||
import { IPartnerRepository, PartnerDirection } from '../repositories';
|
||||
import { IAccessRepository, IPartnerRepository, PartnerDirection } from '../repositories';
|
||||
import { PartnerResponseDto } from './partner.dto';
|
||||
import { PartnerService } from './partner.service';
|
||||
|
||||
const responseDto = {
|
||||
admin: <UserResponseDto>{
|
||||
admin: <PartnerResponseDto>{
|
||||
email: 'admin@test.com',
|
||||
firstName: 'admin_first_name',
|
||||
id: 'admin_id',
|
||||
@@ -20,8 +20,9 @@ const responseDto = {
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
externalPath: null,
|
||||
memoriesEnabled: true,
|
||||
inTimeline: true,
|
||||
},
|
||||
user1: <UserResponseDto>{
|
||||
user1: <PartnerResponseDto>{
|
||||
email: 'immich@test.com',
|
||||
firstName: 'immich_first_name',
|
||||
id: 'user-id',
|
||||
@@ -36,16 +37,18 @@ const responseDto = {
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
externalPath: null,
|
||||
memoriesEnabled: true,
|
||||
inTimeline: true,
|
||||
},
|
||||
};
|
||||
|
||||
describe(PartnerService.name, () => {
|
||||
let sut: PartnerService;
|
||||
let partnerMock: jest.Mocked<IPartnerRepository>;
|
||||
let accessMock: jest.Mocked<IAccessRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
partnerMock = newPartnerRepositoryMock();
|
||||
sut = new PartnerService(partnerMock);
|
||||
sut = new PartnerService(partnerMock, accessMock);
|
||||
});
|
||||
|
||||
it('should work', () => {
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import { PartnerEntity } from '@app/infra/entities';
|
||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||
import { AccessCore, Permission } from '../access';
|
||||
import { AuthUserDto } from '../auth';
|
||||
import { IPartnerRepository, PartnerDirection, PartnerIds } from '../repositories';
|
||||
import { UserResponseDto, mapUser } from '../user';
|
||||
import { IAccessRepository, IPartnerRepository, PartnerDirection, PartnerIds } from '../repositories';
|
||||
import { mapUser } from '../user';
|
||||
import { PartnerResponseDto, UpdatePartnerDto } from './partner.dto';
|
||||
|
||||
@Injectable()
|
||||
export class PartnerService {
|
||||
constructor(@Inject(IPartnerRepository) private repository: IPartnerRepository) {}
|
||||
private access: AccessCore;
|
||||
constructor(
|
||||
@Inject(IPartnerRepository) private repository: IPartnerRepository,
|
||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
||||
) {
|
||||
this.access = AccessCore.create(accessRepository);
|
||||
}
|
||||
|
||||
async create(authUser: AuthUserDto, sharedWithId: string): Promise<UserResponseDto> {
|
||||
async create(authUser: AuthUserDto, sharedWithId: string): Promise<PartnerResponseDto> {
|
||||
const partnerId: PartnerIds = { sharedById: authUser.id, sharedWithId };
|
||||
const exists = await this.repository.get(partnerId);
|
||||
if (exists) {
|
||||
@@ -29,7 +37,7 @@ export class PartnerService {
|
||||
await this.repository.remove(partner);
|
||||
}
|
||||
|
||||
async getAll(authUser: AuthUserDto, direction: PartnerDirection): Promise<UserResponseDto[]> {
|
||||
async getAll(authUser: AuthUserDto, direction: PartnerDirection): Promise<PartnerResponseDto[]> {
|
||||
const partners = await this.repository.getAll(authUser.id);
|
||||
const key = direction === PartnerDirection.SharedBy ? 'sharedById' : 'sharedWithId';
|
||||
return partners
|
||||
@@ -38,8 +46,22 @@ export class PartnerService {
|
||||
.map((partner) => this.map(partner, direction));
|
||||
}
|
||||
|
||||
private map(partner: PartnerEntity, direction: PartnerDirection): UserResponseDto {
|
||||
async update(authUser: AuthUserDto, sharedById: string, dto: UpdatePartnerDto): Promise<PartnerResponseDto> {
|
||||
await this.access.requirePermission(authUser, Permission.PARTNER_UPDATE, sharedById);
|
||||
const partnerId: PartnerIds = { sharedById, sharedWithId: authUser.id };
|
||||
|
||||
const entity = await this.repository.update({ ...partnerId, inTimeline: dto.inTimeline });
|
||||
return this.map(entity, PartnerDirection.SharedWith);
|
||||
}
|
||||
|
||||
private map(partner: PartnerEntity, direction: PartnerDirection): PartnerResponseDto {
|
||||
// this is opposite to return the non-me user of the "partner"
|
||||
return mapUser(direction === PartnerDirection.SharedBy ? partner.sharedWith : partner.sharedBy);
|
||||
const user = mapUser(
|
||||
direction === PartnerDirection.SharedBy ? partner.sharedWith : partner.sharedBy,
|
||||
) as PartnerResponseDto;
|
||||
|
||||
user.inTimeline = partner.inTimeline;
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,4 +35,8 @@ export interface IAccessRepository {
|
||||
person: {
|
||||
hasOwnerAccess(userId: string, personId: string): Promise<boolean>;
|
||||
};
|
||||
|
||||
partner: {
|
||||
hasUpdateAccess(userId: string, partnerId: string): Promise<boolean>;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export interface TimeBucketOptions {
|
||||
isTrashed?: boolean;
|
||||
albumId?: string;
|
||||
personId?: string;
|
||||
userId?: string;
|
||||
userIds?: string[];
|
||||
withStacked?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,4 +17,5 @@ export interface IPartnerRepository {
|
||||
get(partner: PartnerIds): Promise<PartnerEntity | null>;
|
||||
create(partner: PartnerIds): Promise<PartnerEntity>;
|
||||
remove(entity: PartnerEntity): Promise<void>;
|
||||
update(entity: Partial<PartnerEntity>): Promise<PartnerEntity>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AuthUserDto, PartnerDirection, PartnerService, UserResponseDto } from '@app/domain';
|
||||
import { Controller, Delete, Get, Param, Post, Query } from '@nestjs/common';
|
||||
import { AuthUserDto, PartnerDirection, PartnerService } from '@app/domain';
|
||||
import { PartnerResponseDto, UpdatePartnerDto } from '@app/domain/partner/partner.dto';
|
||||
import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common';
|
||||
import { ApiQuery, ApiTags } from '@nestjs/swagger';
|
||||
import { AuthUser, Authenticated } from '../app.guard';
|
||||
import { UseValidation } from '../app.utils';
|
||||
@@ -17,15 +18,24 @@ export class PartnerController {
|
||||
getPartners(
|
||||
@AuthUser() authUser: AuthUserDto,
|
||||
@Query('direction') direction: PartnerDirection,
|
||||
): Promise<UserResponseDto[]> {
|
||||
): Promise<PartnerResponseDto[]> {
|
||||
return this.service.getAll(authUser, direction);
|
||||
}
|
||||
|
||||
@Post(':id')
|
||||
createPartner(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<UserResponseDto> {
|
||||
createPartner(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<PartnerResponseDto> {
|
||||
return this.service.create(authUser, id);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
updatePartner(
|
||||
@AuthUser() authUser: AuthUserDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Body() dto: UpdatePartnerDto,
|
||||
): Promise<PartnerResponseDto> {
|
||||
return this.service.update(authUser, id, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
removePartner(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
||||
return this.service.remove(authUser, id);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CreateDateColumn, Entity, JoinColumn, ManyToOne, PrimaryColumn, UpdateDateColumn } from 'typeorm';
|
||||
import { Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, PrimaryColumn, UpdateDateColumn } from 'typeorm';
|
||||
|
||||
import { UserEntity } from './user.entity';
|
||||
|
||||
@@ -23,4 +23,7 @@ export class PartnerEntity {
|
||||
|
||||
@UpdateDateColumn({ type: 'timestamptz' })
|
||||
updatedAt!: Date;
|
||||
|
||||
@Column({ type: 'boolean', default: false })
|
||||
inTimeline!: boolean;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class AdddInTimelineToPartnersTable1699562570201 implements MigrationInterface {
|
||||
name = 'AdddInTimelineToPartnersTable1699562570201'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "partners" ADD "inTimeline" boolean NOT NULL DEFAULT false`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "partners" DROP COLUMN "inTimeline"`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -249,4 +249,15 @@ export class AccessRepository implements IAccessRepository {
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
partner = {
|
||||
hasUpdateAccess: (userId: string, partnerId: string): Promise<boolean> => {
|
||||
return this.partnerRepository.exist({
|
||||
where: {
|
||||
sharedById: partnerId,
|
||||
sharedWithId: userId,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -519,7 +519,7 @@ export class AssetRepository implements IAssetRepository {
|
||||
}
|
||||
|
||||
private getBuilder(options: TimeBucketOptions) {
|
||||
const { isArchived, isFavorite, isTrashed, albumId, personId, userId, withStacked } = options;
|
||||
const { isArchived, isFavorite, isTrashed, albumId, personId, userIds, withStacked } = options;
|
||||
|
||||
let builder = this.repository
|
||||
.createQueryBuilder('asset')
|
||||
@@ -532,11 +532,11 @@ export class AssetRepository implements IAssetRepository {
|
||||
builder = builder.leftJoin('asset.albums', 'album').andWhere('album.id = :albumId', { albumId });
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
builder = builder.andWhere('asset.ownerId = :userId', { userId });
|
||||
if (userIds) {
|
||||
builder = builder.andWhere('asset.ownerId IN (:...userIds )', { userIds });
|
||||
}
|
||||
|
||||
if (isArchived != undefined) {
|
||||
if (isArchived !== undefined) {
|
||||
builder = builder.andWhere('asset.isArchived = :isArchived', { isArchived });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { IPartnerRepository, PartnerIds } from '@app/domain';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { DeepPartial, Repository } from 'typeorm';
|
||||
import { PartnerEntity } from '../entities';
|
||||
|
||||
@Injectable()
|
||||
@@ -16,12 +16,22 @@ export class PartnerRepository implements IPartnerRepository {
|
||||
return this.repository.findOne({ where: { sharedById, sharedWithId } });
|
||||
}
|
||||
|
||||
async create({ sharedById, sharedWithId }: PartnerIds): Promise<PartnerEntity> {
|
||||
await this.repository.save({ sharedBy: { id: sharedById }, sharedWith: { id: sharedWithId } });
|
||||
return this.repository.findOneOrFail({ where: { sharedById, sharedWithId } });
|
||||
create({ sharedById, sharedWithId }: PartnerIds): Promise<PartnerEntity> {
|
||||
return this.save({ sharedBy: { id: sharedById }, sharedWith: { id: sharedWithId } });
|
||||
}
|
||||
|
||||
async remove(entity: PartnerEntity): Promise<void> {
|
||||
await this.repository.remove(entity);
|
||||
}
|
||||
|
||||
update(entity: Partial<PartnerEntity>): Promise<PartnerEntity> {
|
||||
return this.save(entity);
|
||||
}
|
||||
|
||||
private async save(entity: DeepPartial<PartnerEntity>): Promise<PartnerEntity> {
|
||||
await this.repository.save(entity);
|
||||
return this.repository.findOneOrFail({
|
||||
where: { sharedById: entity.sharedById, sharedWithId: entity.sharedWithId },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user