fix db schema annotations

This commit is contained in:
mgabor
2024-04-16 22:02:40 +02:00
parent 9677d6108a
commit 1b56fb8914
9 changed files with 38 additions and 51 deletions
+2 -2
View File
@@ -125,9 +125,9 @@ export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDt
if (entity.sharedUsers) { if (entity.sharedUsers) {
for (const permission of entity.sharedUsers) { for (const permission of entity.sharedUsers) {
sharedUsers.push(mapUser(permission.users)); sharedUsers.push(mapUser(permission.user));
sharedUsersV2.push({ sharedUsersV2.push({
user: mapUser(permission.users), user: mapUser(permission.user),
readonly: permission.readonly, readonly: permission.readonly,
}); });
} }
+9 -7
View File
@@ -1,20 +1,22 @@
import { AlbumEntity } from 'src/entities/album.entity'; import { AlbumEntity } from 'src/entities/album.entity';
import { UserEntity } from 'src/entities/user.entity'; import { UserEntity } from 'src/entities/user.entity';
import { Column, Entity, Index, ManyToOne, PrimaryColumn } from 'typeorm'; import { Column, Entity, Index, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm';
@Entity('albums_shared_users_users') @Entity('albums_shared_users_users')
// Indices for JoinTable // Pre-existing indices from original album <--> user ManyToMany mapping
@Index('IDX_427c350ad49bd3935a50baab73', ['albums']) @Index('IDX_427c350ad49bd3935a50baab73', ['album'])
@Index('IDX_f48513bf9bccefd6ff3ad30bd0', ['users']) @Index('IDX_f48513bf9bccefd6ff3ad30bd0', ['user'])
export class AlbumUserEntity { export class AlbumUserEntity {
@PrimaryColumn({ type: 'uuid', name: 'albumsId' }) @PrimaryColumn({ type: 'uuid', name: 'albumsId' })
@JoinColumn({ name: 'albumsId' })
@ManyToOne(() => AlbumEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) @ManyToOne(() => AlbumEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
albums!: AlbumEntity; album!: AlbumEntity;
@PrimaryColumn({ type: 'uuid', name: 'usersId' }) @PrimaryColumn({ type: 'uuid', name: 'usersId' })
@JoinColumn({ name: 'usersId' })
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) @ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
users!: UserEntity; user!: UserEntity;
@Column({ default: true }) @Column({ default: false })
readonly!: boolean; readonly!: boolean;
} }
+1 -1
View File
@@ -53,7 +53,7 @@ export class AlbumEntity {
@Column({ comment: 'Asset ID to be used as thumbnail', nullable: true }) @Column({ comment: 'Asset ID to be used as thumbnail', nullable: true })
albumThumbnailAssetId!: string | null; albumThumbnailAssetId!: string | null;
@OneToMany(() => AlbumUserEntity, (permission) => permission.albums) @OneToMany(() => AlbumUserEntity, (permission) => permission.album)
sharedUsers!: AlbumUserEntity[]; sharedUsers!: AlbumUserEntity[];
@ManyToMany(() => AssetEntity, (asset) => asset.albums) @ManyToMany(() => AssetEntity, (asset) => asset.albums)
@@ -1,14 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class SetReadonlyDefault1712905931092 implements MigrationInterface {
name = 'SetReadonlyDefault1712905931092'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ALTER COLUMN "readonly" SET DEFAULT true`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ALTER COLUMN "readonly" SET DEFAULT false`);
}
}
@@ -1,10 +1,11 @@
import { MigrationInterface, QueryRunner } from "typeorm"; import { MigrationInterface, QueryRunner } from "typeorm";
export class AddAlbumPermission1712905775156 implements MigrationInterface { export class AddAlbumUserReadonly1713298646379 implements MigrationInterface {
name = 'AddAlbumPermission1712905775156' name = 'AddAlbumUserReadonly1713298646379'
public async up(queryRunner: QueryRunner): Promise<void> { public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD "readonly" boolean NOT NULL DEFAULT false`); await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD "readonly" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ALTER COLUMN "readonly" SET DEFAULT true`);
} }
public async down(queryRunner: QueryRunner): Promise<void> { public async down(queryRunner: QueryRunner): Promise<void> {
+1 -1
View File
@@ -132,7 +132,7 @@ class AlbumAccess implements IAlbumAccess {
where: { where: {
id: In([...albumIds]), id: In([...albumIds]),
sharedUsers: { sharedUsers: {
users: Equal(userId), user: Equal(userId),
// If write is needed we check for it, otherwise both are accepted // If write is needed we check for it, otherwise both are accepted
readonly: readWrite === 'write' ? false : undefined, readonly: readWrite === 'write' ? false : undefined,
}, },
@@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { AlbumUserEntity } from 'src/entities/album-user.entity'; import { AlbumUserEntity } from 'src/entities/album-user.entity';
import { AlbumPermissionId, IAlbumUserRepository } from 'src/interfaces/album-user.interface'; import { AlbumPermissionId, IAlbumUserRepository } from 'src/interfaces/album-user.interface';
import { Instrumentation } from 'src/utils/instrumentation'; import { Instrumentation } from 'src/utils/instrumentation';
import { Equal, Repository } from 'typeorm'; import { Repository } from 'typeorm';
@Instrumentation() @Instrumentation()
@Injectable() @Injectable()
@@ -11,21 +11,19 @@ export class AlbumUserRepository implements IAlbumUserRepository {
constructor(@InjectRepository(AlbumUserEntity) private repository: Repository<AlbumUserEntity>) {} constructor(@InjectRepository(AlbumUserEntity) private repository: Repository<AlbumUserEntity>) {}
async create(dto: Partial<AlbumUserEntity>): Promise<AlbumUserEntity> { async create(dto: Partial<AlbumUserEntity>): Promise<AlbumUserEntity> {
const { users, albums } = await this.repository.save(dto); const { user, album } = await this.repository.save(dto);
return this.repository.findOneOrFail({ where: { users, albums }, relations: { users: true } }); return this.repository.findOneOrFail({ where: { user, album }, relations: { user: true } });
} }
async update({ userId, albumId }: AlbumPermissionId, dto: Partial<AlbumUserEntity>): Promise<AlbumUserEntity> { async update({ userId, albumId }: AlbumPermissionId, dto: Partial<AlbumUserEntity>): Promise<AlbumUserEntity> {
// @ts-expect-error I'm pretty sure I messed something up with the entity because await this.repository.update({ user: { id: userId }, album: { id: albumId } }, dto);
// if I follow what typescript says I get postgres errors
await this.repository.update({ users: userId, albums: albumId }, dto);
return this.repository.findOneOrFail({ return this.repository.findOneOrFail({
where: { users: Equal(userId), albums: Equal(albumId) }, where: { user: { id: userId }, album: { id: albumId } },
relations: { users: true }, relations: { user: true },
}); });
} }
async delete({ userId, albumId }: AlbumPermissionId): Promise<void> { async delete({ userId, albumId }: AlbumPermissionId): Promise<void> {
await this.repository.delete({ users: { id: userId }, albums: { id: albumId } }); await this.repository.delete({ user: { id: userId }, album: { id: albumId } });
} }
} }
+10 -10
View File
@@ -23,7 +23,7 @@ export class AlbumRepository implements IAlbumRepository {
getById(id: string, options: AlbumInfoOptions): Promise<AlbumEntity | null> { getById(id: string, options: AlbumInfoOptions): Promise<AlbumEntity | null> {
const relations: FindOptionsRelations<AlbumEntity> = { const relations: FindOptionsRelations<AlbumEntity> = {
owner: true, owner: true,
sharedUsers: { users: true }, sharedUsers: { user: true },
assets: false, assets: false,
sharedLinks: true, sharedLinks: true,
}; };
@@ -52,7 +52,7 @@ export class AlbumRepository implements IAlbumRepository {
}, },
relations: { relations: {
owner: true, owner: true,
sharedUsers: { users: true }, sharedUsers: { user: true },
}, },
}); });
} }
@@ -62,9 +62,9 @@ export class AlbumRepository implements IAlbumRepository {
return this.repository.find({ return this.repository.find({
where: [ where: [
{ ownerId, assets: { id: assetId } }, { ownerId, assets: { id: assetId } },
{ sharedUsers: { users: Equal(ownerId) }, assets: { id: assetId } }, { sharedUsers: { user: Equal(ownerId) }, assets: { id: assetId } },
], ],
relations: { owner: true, sharedUsers: { users: true } }, relations: { owner: true, sharedUsers: { user: true } },
order: { createdAt: 'DESC' }, order: { createdAt: 'DESC' },
}); });
} }
@@ -129,7 +129,7 @@ export class AlbumRepository implements IAlbumRepository {
@GenerateSql({ params: [DummyValue.UUID] }) @GenerateSql({ params: [DummyValue.UUID] })
getOwned(ownerId: string): Promise<AlbumEntity[]> { getOwned(ownerId: string): Promise<AlbumEntity[]> {
return this.repository.find({ return this.repository.find({
relations: { sharedUsers: { users: true }, sharedLinks: true, owner: true }, relations: { sharedUsers: { user: true }, sharedLinks: true, owner: true },
where: { ownerId }, where: { ownerId },
order: { createdAt: 'DESC' }, order: { createdAt: 'DESC' },
}); });
@@ -141,11 +141,11 @@ export class AlbumRepository implements IAlbumRepository {
@GenerateSql({ params: [DummyValue.UUID] }) @GenerateSql({ params: [DummyValue.UUID] })
getShared(ownerId: string): Promise<AlbumEntity[]> { getShared(ownerId: string): Promise<AlbumEntity[]> {
return this.repository.find({ return this.repository.find({
relations: { sharedUsers: { users: true }, sharedLinks: true, owner: true }, relations: { sharedUsers: { user: true }, sharedLinks: true, owner: true },
where: [ where: [
{ sharedUsers: { users: Equal(ownerId) } }, { sharedUsers: { user: Equal(ownerId) } },
{ sharedLinks: { userId: ownerId } }, { sharedLinks: { userId: ownerId } },
{ ownerId, sharedUsers: { users: Not(IsNull()) } }, { ownerId, sharedUsers: { user: Not(IsNull()) } },
], ],
order: { createdAt: 'DESC' }, order: { createdAt: 'DESC' },
}); });
@@ -158,7 +158,7 @@ export class AlbumRepository implements IAlbumRepository {
getNotShared(ownerId: string): Promise<AlbumEntity[]> { getNotShared(ownerId: string): Promise<AlbumEntity[]> {
return this.repository.find({ return this.repository.find({
relations: { sharedUsers: true, sharedLinks: true, owner: true }, relations: { sharedUsers: true, sharedLinks: true, owner: true },
where: { ownerId, sharedUsers: { users: IsNull() }, sharedLinks: { id: IsNull() } }, where: { ownerId, sharedUsers: { user: IsNull() }, sharedLinks: { id: IsNull() } },
order: { createdAt: 'DESC' }, order: { createdAt: 'DESC' },
}); });
} }
@@ -282,7 +282,7 @@ export class AlbumRepository implements IAlbumRepository {
where: { id }, where: { id },
relations: { relations: {
owner: true, owner: true,
sharedUsers: { users: true }, sharedUsers: { user: true },
sharedLinks: true, sharedLinks: true,
assets: true, assets: true,
}, },
+5 -5
View File
@@ -128,7 +128,7 @@ export class AlbumService {
ownerId: auth.user.id, ownerId: auth.user.id,
albumName: dto.albumName, albumName: dto.albumName,
description: dto.description, description: dto.description,
sharedUsers: dto.sharedWithUserIds?.map((userId) => ({ users: { id: userId } }) as AlbumUserEntity) ?? [], sharedUsers: dto.sharedWithUserIds?.map((userId) => ({ user: { id: userId } }) as AlbumUserEntity) ?? [],
assets, assets,
albumThumbnailAssetId: assets[0]?.id || null, albumThumbnailAssetId: assets[0]?.id || null,
}); });
@@ -221,7 +221,7 @@ export class AlbumService {
throw new BadRequestException('Cannot be shared with owner'); throw new BadRequestException('Cannot be shared with owner');
} }
const exists = album.sharedUsers.find(({ users: { id } }) => id === userId); const exists = album.sharedUsers.find(({ user: { id } }) => id === userId);
if (exists) { if (exists) {
throw new BadRequestException('User already added'); throw new BadRequestException('User already added');
} }
@@ -232,7 +232,7 @@ export class AlbumService {
} }
album.sharedUsers.push( album.sharedUsers.push(
await this.albumPermissionRepository.create({ users: { id: userId }, albums: { id } } as AlbumUserEntity), await this.albumPermissionRepository.create({ user: { id: userId }, album: { id } } as AlbumUserEntity),
); );
} }
@@ -250,7 +250,7 @@ export class AlbumService {
throw new BadRequestException('Cannot remove album owner'); throw new BadRequestException('Cannot remove album owner');
} }
const exists = album.sharedUsers.find(({ users: { id } }) => id === userId); const exists = album.sharedUsers.find(({ user: { id } }) => id === userId);
if (!exists) { if (!exists) {
throw new BadRequestException('Album not shared with user'); throw new BadRequestException('Album not shared with user');
} }
@@ -268,7 +268,7 @@ export class AlbumService {
const album = await this.findOrFail(id, { withAssets: false }); const album = await this.findOrFail(id, { withAssets: false });
const permission = album.sharedUsers.find(({ users: { id } }) => id === userId); const permission = album.sharedUsers.find(({ user: { id } }) => id === userId);
if (!permission) { if (!permission) {
throw new BadRequestException('Album not shared with user'); throw new BadRequestException('Album not shared with user');
} }