1
0
mirror of https://github.com/immich-app/immich.git synced 2025-01-25 17:15:28 +02:00

chore(server): Use ChunkedSet in Access repository (#6943)

This change simplifies the Access repository methods, by using the
`ChunkedSet` decorator. As the methods expect sets, the `chunks` util
needed to be fixed, so it returns chunks of the same type it received.

Now `chunks` is overloaded, to have proper typing based on the input
parameter.
This commit is contained in:
Michael Manganiello 2024-02-06 17:03:25 -05:00 committed by GitHub
parent 61768ce89e
commit 4164bcfd0d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 233 additions and 282 deletions

View File

@ -178,23 +178,25 @@ export function Optional({ nullable, ...validationOptions }: OptionalOptions = {
} }
/** /**
* Chunks an array or set into smaller arrays of the specified size. * Chunks an array or set into smaller collections of the same type and specified size.
* *
* @param collection The collection to chunk. * @param collection The collection to chunk.
* @param size The size of each chunk. * @param size The size of each chunk.
*/ */
export function chunks<T>(collection: Array<T> | Set<T>, size: number): T[][] { export function chunks<T>(collection: Array<T>, size: number): Array<Array<T>>;
export function chunks<T>(collection: Set<T>, size: number): Array<Set<T>>;
export function chunks<T>(collection: Array<T> | Set<T>, size: number): Array<Array<T>> | Array<Set<T>> {
if (collection instanceof Set) { if (collection instanceof Set) {
const result = []; const result = [];
let chunk = []; let chunk = new Set<T>();
for (const element of collection) { for (const element of collection) {
chunk.push(element); chunk.add(element);
if (chunk.length === size) { if (chunk.size === size) {
result.push(chunk); result.push(chunk);
chunk = []; chunk = new Set<T>();
} }
} }
if (chunk.length > 0) { if (chunk.size > 0) {
result.push(chunk); result.push(chunk);
} }
return result; return result;

View File

@ -1,4 +1,4 @@
import { IAccessRepository, chunks, setUnion } from '@app/domain'; import { IAccessRepository } from '@app/domain';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { Brackets, In, Repository } from 'typeorm'; import { Brackets, In, Repository } from 'typeorm';
import { import {
@ -12,7 +12,8 @@ import {
SharedLinkEntity, SharedLinkEntity,
UserTokenEntity, UserTokenEntity,
} from '../entities'; } from '../entities';
import { DATABASE_PARAMETER_CHUNK_SIZE, DummyValue, GenerateSql } from '../infra.util'; import { DummyValue, GenerateSql } from '../infra.util';
import { ChunkedSet } from '../infra.utils';
type IActivityAccess = IAccessRepository['activity']; type IActivityAccess = IAccessRepository['activity'];
type IAlbumAccess = IAccessRepository['album']; type IAlbumAccess = IAccessRepository['album'];
@ -30,72 +31,63 @@ class ActivityAccess implements IActivityAccess {
) {} ) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkOwnerAccess(userId: string, activityIds: Set<string>): Promise<Set<string>> { async checkOwnerAccess(userId: string, activityIds: Set<string>): Promise<Set<string>> {
if (activityIds.size === 0) { if (activityIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.activityRepository
chunks(activityIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .find({
this.activityRepository select: { id: true },
.find({ where: {
select: { id: true }, id: In([...activityIds]),
where: { userId,
id: In(idChunk), },
userId, })
}, .then((activities) => new Set(activities.map((activity) => activity.id)));
})
.then((activities) => new Set(activities.map((activity) => activity.id))),
),
).then((results) => setUnion(...results));
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkAlbumOwnerAccess(userId: string, activityIds: Set<string>): Promise<Set<string>> { async checkAlbumOwnerAccess(userId: string, activityIds: Set<string>): Promise<Set<string>> {
if (activityIds.size === 0) { if (activityIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.activityRepository
chunks(activityIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .find({
this.activityRepository select: { id: true },
.find({ where: {
select: { id: true }, id: In([...activityIds]),
where: { album: {
id: In(idChunk), ownerId: userId,
album: { },
ownerId: userId, },
}, })
}, .then((activities) => new Set(activities.map((activity) => activity.id)));
})
.then((activities) => new Set(activities.map((activity) => activity.id))),
),
).then((results) => setUnion(...results));
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkCreateAccess(userId: string, albumIds: Set<string>): Promise<Set<string>> { async checkCreateAccess(userId: string, albumIds: Set<string>): Promise<Set<string>> {
if (albumIds.size === 0) { if (albumIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.albumRepository
chunks(albumIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .createQueryBuilder('album')
this.albumRepository .select('album.id')
.createQueryBuilder('album') .leftJoin('album.sharedUsers', 'sharedUsers')
.select('album.id') .where('album.id IN (:...albumIds)', { albumIds: [...albumIds] })
.leftJoin('album.sharedUsers', 'sharedUsers') .andWhere('album.isActivityEnabled = true')
.where('album.id IN (:...albumIds)', { albumIds: idChunk }) .andWhere(
.andWhere('album.isActivityEnabled = true') new Brackets((qb) => {
.andWhere( qb.where('album.ownerId = :userId', { userId }).orWhere('sharedUsers.id = :userId', { userId });
new Brackets((qb) => { }),
qb.where('album.ownerId = :userId', { userId }).orWhere('sharedUsers.id = :userId', { userId }); )
}), .getMany()
) .then((albums) => new Set(albums.map((album) => album.id)));
.getMany()
.then((albums) => new Set(albums.map((album) => album.id))),
),
).then((results) => setUnion(...results));
} }
} }
@ -106,71 +98,61 @@ class AlbumAccess implements IAlbumAccess {
) {} ) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkOwnerAccess(userId: string, albumIds: Set<string>): Promise<Set<string>> { async checkOwnerAccess(userId: string, albumIds: Set<string>): Promise<Set<string>> {
if (albumIds.size === 0) { if (albumIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.albumRepository
chunks(albumIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .find({
this.albumRepository select: { id: true },
.find({ where: {
select: { id: true }, id: In([...albumIds]),
where: { ownerId: userId,
id: In(idChunk), },
ownerId: userId, })
}, .then((albums) => new Set(albums.map((album) => album.id)));
})
.then((albums) => new Set(albums.map((album) => album.id))),
),
).then((results) => setUnion(...results));
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkSharedAlbumAccess(userId: string, albumIds: Set<string>): Promise<Set<string>> { async checkSharedAlbumAccess(userId: string, albumIds: Set<string>): Promise<Set<string>> {
if (albumIds.size === 0) { if (albumIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.albumRepository
chunks(albumIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .find({
this.albumRepository select: { id: true },
.find({ where: {
select: { id: true }, id: In([...albumIds]),
where: { sharedUsers: {
id: In(idChunk), id: userId,
sharedUsers: { },
id: userId, },
}, })
}, .then((albums) => new Set(albums.map((album) => album.id)));
})
.then((albums) => new Set(albums.map((album) => album.id))),
),
).then((results) => setUnion(...results));
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkSharedLinkAccess(sharedLinkId: string, albumIds: Set<string>): Promise<Set<string>> { async checkSharedLinkAccess(sharedLinkId: string, albumIds: Set<string>): Promise<Set<string>> {
if (albumIds.size === 0) { if (albumIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.sharedLinkRepository
chunks(albumIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .find({
this.sharedLinkRepository select: { albumId: true },
.find({ where: {
select: { albumId: true }, id: sharedLinkId,
where: { albumId: In([...albumIds]),
id: sharedLinkId, },
albumId: In(idChunk), })
}, .then(
}) (sharedLinks) => new Set(sharedLinks.flatMap((sharedLink) => (sharedLink.albumId ? [sharedLink.albumId] : []))),
.then( );
(sharedLinks) =>
new Set(sharedLinks.flatMap((sharedLink) => (sharedLink.albumId ? [sharedLink.albumId] : []))),
),
),
).then((results) => setUnion(...results));
} }
} }
@ -183,132 +165,120 @@ class AssetAccess implements IAssetAccess {
) {} ) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkAlbumAccess(userId: string, assetIds: Set<string>): Promise<Set<string>> { async checkAlbumAccess(userId: string, assetIds: Set<string>): Promise<Set<string>> {
if (assetIds.size === 0) { if (assetIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.albumRepository
chunks(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .createQueryBuilder('album')
this.albumRepository .innerJoin('album.assets', 'asset')
.createQueryBuilder('album') .leftJoin('album.sharedUsers', 'sharedUsers')
.innerJoin('album.assets', 'asset') .select('asset.id', 'assetId')
.leftJoin('album.sharedUsers', 'sharedUsers') .addSelect('asset.livePhotoVideoId', 'livePhotoVideoId')
.select('asset.id', 'assetId') .where('array["asset"."id", "asset"."livePhotoVideoId"] && array[:...assetIds]::uuid[]', {
.addSelect('asset.livePhotoVideoId', 'livePhotoVideoId') assetIds: [...assetIds],
.where('array["asset"."id", "asset"."livePhotoVideoId"] && array[:...assetIds]::uuid[]', { })
assetIds: idChunk, .andWhere(
}) new Brackets((qb) => {
.andWhere( qb.where('album.ownerId = :userId', { userId }).orWhere('sharedUsers.id = :userId', { userId });
new Brackets((qb) => { }),
qb.where('album.ownerId = :userId', { userId }).orWhere('sharedUsers.id = :userId', { userId }); )
}), .getRawMany()
) .then((rows) => {
.getRawMany() const allowedIds = new Set<string>();
.then((rows) => { for (const row of rows) {
const allowedIds = new Set<string>(); if (row.assetId && assetIds.has(row.assetId)) {
for (const row of rows) { allowedIds.add(row.assetId);
if (row.assetId && assetIds.has(row.assetId)) { }
allowedIds.add(row.assetId); if (row.livePhotoVideoId && assetIds.has(row.livePhotoVideoId)) {
} allowedIds.add(row.livePhotoVideoId);
if (row.livePhotoVideoId && assetIds.has(row.livePhotoVideoId)) { }
allowedIds.add(row.livePhotoVideoId); }
} return allowedIds;
} });
return allowedIds;
}),
),
).then((results) => setUnion(...results));
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkOwnerAccess(userId: string, assetIds: Set<string>): Promise<Set<string>> { async checkOwnerAccess(userId: string, assetIds: Set<string>): Promise<Set<string>> {
if (assetIds.size === 0) { if (assetIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.assetRepository
chunks(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .find({
this.assetRepository select: { id: true },
.find({ where: {
select: { id: true }, id: In([...assetIds]),
where: { ownerId: userId,
id: In(idChunk), },
ownerId: userId, withDeleted: true,
}, })
withDeleted: true, .then((assets) => new Set(assets.map((asset) => asset.id)));
})
.then((assets) => new Set(assets.map((asset) => asset.id))),
),
).then((results) => setUnion(...results));
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkPartnerAccess(userId: string, assetIds: Set<string>): Promise<Set<string>> { async checkPartnerAccess(userId: string, assetIds: Set<string>): Promise<Set<string>> {
if (assetIds.size === 0) { if (assetIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.partnerRepository
chunks(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .createQueryBuilder('partner')
this.partnerRepository .innerJoin('partner.sharedBy', 'sharedBy')
.createQueryBuilder('partner') .innerJoin('sharedBy.assets', 'asset')
.innerJoin('partner.sharedBy', 'sharedBy') .select('asset.id', 'assetId')
.innerJoin('sharedBy.assets', 'asset') .where('partner.sharedWithId = :userId', { userId })
.select('asset.id', 'assetId') .andWhere('asset.id IN (:...assetIds)', { assetIds: [...assetIds] })
.where('partner.sharedWithId = :userId', { userId }) .getRawMany()
.andWhere('asset.id IN (:...assetIds)', { assetIds: idChunk }) .then((rows) => new Set(rows.map((row) => row.assetId)));
.getRawMany()
.then((rows) => new Set(rows.map((row) => row.assetId))),
),
).then((results) => setUnion(...results));
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkSharedLinkAccess(sharedLinkId: string, assetIds: Set<string>): Promise<Set<string>> { async checkSharedLinkAccess(sharedLinkId: string, assetIds: Set<string>): Promise<Set<string>> {
if (assetIds.size === 0) { if (assetIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.sharedLinkRepository
chunks(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .createQueryBuilder('sharedLink')
this.sharedLinkRepository .leftJoin('sharedLink.album', 'album')
.createQueryBuilder('sharedLink') .leftJoin('sharedLink.assets', 'assets')
.leftJoin('sharedLink.album', 'album') .leftJoin('album.assets', 'albumAssets')
.leftJoin('sharedLink.assets', 'assets') .select('assets.id', 'assetId')
.leftJoin('album.assets', 'albumAssets') .addSelect('albumAssets.id', 'albumAssetId')
.select('assets.id', 'assetId') .addSelect('assets.livePhotoVideoId', 'assetLivePhotoVideoId')
.addSelect('albumAssets.id', 'albumAssetId') .addSelect('albumAssets.livePhotoVideoId', 'albumAssetLivePhotoVideoId')
.addSelect('assets.livePhotoVideoId', 'assetLivePhotoVideoId') .where('sharedLink.id = :sharedLinkId', { sharedLinkId })
.addSelect('albumAssets.livePhotoVideoId', 'albumAssetLivePhotoVideoId') .andWhere(
.where('sharedLink.id = :sharedLinkId', { sharedLinkId }) 'array["assets"."id", "assets"."livePhotoVideoId", "albumAssets"."id", "albumAssets"."livePhotoVideoId"] && array[:...assetIds]::uuid[]',
.andWhere( {
'array["assets"."id", "assets"."livePhotoVideoId", "albumAssets"."id", "albumAssets"."livePhotoVideoId"] && array[:...assetIds]::uuid[]', assetIds: [...assetIds],
{ },
assetIds: idChunk, )
}, .getRawMany()
) .then((rows) => {
.getRawMany() const allowedIds = new Set<string>();
.then((rows) => { for (const row of rows) {
const allowedIds = new Set<string>(); if (row.assetId && assetIds.has(row.assetId)) {
for (const row of rows) { allowedIds.add(row.assetId);
if (row.assetId && assetIds.has(row.assetId)) { }
allowedIds.add(row.assetId); if (row.assetLivePhotoVideoId && assetIds.has(row.assetLivePhotoVideoId)) {
} allowedIds.add(row.assetLivePhotoVideoId);
if (row.assetLivePhotoVideoId && assetIds.has(row.assetLivePhotoVideoId)) { }
allowedIds.add(row.assetLivePhotoVideoId); if (row.albumAssetId && assetIds.has(row.albumAssetId)) {
} allowedIds.add(row.albumAssetId);
if (row.albumAssetId && assetIds.has(row.albumAssetId)) { }
allowedIds.add(row.albumAssetId); if (row.albumAssetLivePhotoVideoId && assetIds.has(row.albumAssetLivePhotoVideoId)) {
} allowedIds.add(row.albumAssetLivePhotoVideoId);
if (row.albumAssetLivePhotoVideoId && assetIds.has(row.albumAssetLivePhotoVideoId)) { }
allowedIds.add(row.albumAssetLivePhotoVideoId); }
} return allowedIds;
} });
return allowedIds;
}),
),
).then((results) => setUnion(...results));
} }
} }
@ -316,24 +286,21 @@ class AuthDeviceAccess implements IAuthDeviceAccess {
constructor(private tokenRepository: Repository<UserTokenEntity>) {} constructor(private tokenRepository: Repository<UserTokenEntity>) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkOwnerAccess(userId: string, deviceIds: Set<string>): Promise<Set<string>> { async checkOwnerAccess(userId: string, deviceIds: Set<string>): Promise<Set<string>> {
if (deviceIds.size === 0) { if (deviceIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.tokenRepository
chunks(deviceIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .find({
this.tokenRepository select: { id: true },
.find({ where: {
select: { id: true }, userId,
where: { id: In([...deviceIds]),
userId, },
id: In(idChunk), })
}, .then((tokens) => new Set(tokens.map((token) => token.id)));
})
.then((tokens) => new Set(tokens.map((token) => token.id))),
),
).then((results) => setUnion(...results));
} }
} }
@ -344,43 +311,37 @@ class LibraryAccess implements ILibraryAccess {
) {} ) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkOwnerAccess(userId: string, libraryIds: Set<string>): Promise<Set<string>> { async checkOwnerAccess(userId: string, libraryIds: Set<string>): Promise<Set<string>> {
if (libraryIds.size === 0) { if (libraryIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.libraryRepository
chunks(libraryIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .find({
this.libraryRepository select: { id: true },
.find({ where: {
select: { id: true }, id: In([...libraryIds]),
where: { ownerId: userId,
id: In(idChunk), },
ownerId: userId, })
}, .then((libraries) => new Set(libraries.map((library) => library.id)));
})
.then((libraries) => new Set(libraries.map((library) => library.id))),
),
).then((results) => setUnion(...results));
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkPartnerAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>> { async checkPartnerAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>> {
if (partnerIds.size === 0) { if (partnerIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.partnerRepository
chunks(partnerIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .createQueryBuilder('partner')
this.partnerRepository .select('partner.sharedById')
.createQueryBuilder('partner') .where('partner.sharedById IN (:...partnerIds)', { partnerIds: [...partnerIds] })
.select('partner.sharedById') .andWhere('partner.sharedWithId = :userId', { userId })
.where('partner.sharedById IN (:...partnerIds)', { partnerIds: idChunk }) .getMany()
.andWhere('partner.sharedWithId = :userId', { userId }) .then((partners) => new Set(partners.map((partner) => partner.sharedById)));
.getMany()
.then((partners) => new Set(partners.map((partner) => partner.sharedById))),
),
).then((results) => setUnion(...results));
} }
} }
@ -388,22 +349,19 @@ class TimelineAccess implements ITimelineAccess {
constructor(private partnerRepository: Repository<PartnerEntity>) {} constructor(private partnerRepository: Repository<PartnerEntity>) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkPartnerAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>> { async checkPartnerAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>> {
if (partnerIds.size === 0) { if (partnerIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.partnerRepository
chunks(partnerIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .createQueryBuilder('partner')
this.partnerRepository .select('partner.sharedById')
.createQueryBuilder('partner') .where('partner.sharedById IN (:...partnerIds)', { partnerIds: [...partnerIds] })
.select('partner.sharedById') .andWhere('partner.sharedWithId = :userId', { userId })
.where('partner.sharedById IN (:...partnerIds)', { partnerIds: idChunk }) .getMany()
.andWhere('partner.sharedWithId = :userId', { userId }) .then((partners) => new Set(partners.map((partner) => partner.sharedById)));
.getMany()
.then((partners) => new Set(partners.map((partner) => partner.sharedById))),
),
).then((results) => setUnion(...results));
} }
} }
@ -414,47 +372,41 @@ class PersonAccess implements IPersonAccess {
) {} ) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkOwnerAccess(userId: string, personIds: Set<string>): Promise<Set<string>> { async checkOwnerAccess(userId: string, personIds: Set<string>): Promise<Set<string>> {
if (personIds.size === 0) { if (personIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.personRepository
chunks(personIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .find({
this.personRepository select: { id: true },
.find({ where: {
select: { id: true }, id: In([...personIds]),
where: { ownerId: userId,
id: In(idChunk), },
ownerId: userId, })
}, .then((persons) => new Set(persons.map((person) => person.id)));
})
.then((persons) => new Set(persons.map((person) => person.id))),
),
).then((results) => setUnion(...results));
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkFaceOwnerAccess(userId: string, assetFaceIds: Set<string>): Promise<Set<string>> { async checkFaceOwnerAccess(userId: string, assetFaceIds: Set<string>): Promise<Set<string>> {
if (assetFaceIds.size === 0) { if (assetFaceIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.assetFaceRepository
chunks(assetFaceIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .find({
this.assetFaceRepository select: { id: true },
.find({ where: {
select: { id: true }, id: In([...assetFaceIds]),
where: { asset: {
id: In(idChunk), ownerId: userId,
asset: { },
ownerId: userId, },
}, })
}, .then((faces) => new Set(faces.map((face) => face.id)));
})
.then((faces) => new Set(faces.map((face) => face.id))),
),
).then((results) => setUnion(...results));
} }
} }
@ -462,22 +414,19 @@ class PartnerAccess implements IPartnerAccess {
constructor(private partnerRepository: Repository<PartnerEntity>) {} constructor(private partnerRepository: Repository<PartnerEntity>) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 })
async checkUpdateAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>> { async checkUpdateAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>> {
if (partnerIds.size === 0) { if (partnerIds.size === 0) {
return new Set(); return new Set();
} }
return Promise.all( return this.partnerRepository
chunks(partnerIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => .createQueryBuilder('partner')
this.partnerRepository .select('partner.sharedById')
.createQueryBuilder('partner') .where('partner.sharedById IN (:...partnerIds)', { partnerIds: [...partnerIds] })
.select('partner.sharedById') .andWhere('partner.sharedWithId = :userId', { userId })
.where('partner.sharedById IN (:...partnerIds)', { partnerIds: idChunk }) .getMany()
.andWhere('partner.sharedWithId = :userId', { userId }) .then((partners) => new Set(partners.map((partner) => partner.sharedById)));
.getMany()
.then((partners) => new Set(partners.map((partner) => partner.sharedById))),
),
).then((results) => setUnion(...results));
} }
} }