diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 08a65ed..729fd9d 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -34,8 +34,8 @@ export class AuthController { await this.collectionService.createSider(user); if (createUserDto.publicBundleId) { const publicBundleCollection: Collection = await this.collectionService.getCollectionById(createUserDto.publicBundleId, user); - const pictoIdsFromBundle: number[] = await Promise.all(publicBundleCollection?.pictos.map(async (picto) => await this.collectionService.copyPicto(rootId, picto, user))); - const collectionIdsFromBundle: number[] = await Promise.all(publicBundleCollection?.collections.map(async (collection) => await this.collectionService.copyCollection(rootId, collection.id, user))) + const pictoIdsFromBundle: number[] = await Promise.all(publicBundleCollection?.pictos.map(async (picto) => this.collectionService.copyPicto(rootId, picto, user))); + const collectionIdsFromBundle: number[] = await Promise.all(publicBundleCollection?.collections.map(async (collection) => this.collectionService.copyCollectionWithTransaction(rootId, collection.id, user))); const modifyCollectionDto : modifyCollectionDto = { meaning : null, speech : null, @@ -52,7 +52,6 @@ export class AuthController { const multipleShareCollectionDto: multipleShareCollectionDto = { access: 1, usernames: user.directSharers, role: 'editor'} await this.collectionService.shareCollectionVerification(rootId, user, multipleShareCollectionDto); } - console.log(await this.collectionService.getCollectionById(rootId, user)) return; } diff --git a/src/collection/collection.controller.ts b/src/collection/collection.controller.ts index fe043e7..7951dff 100644 --- a/src/collection/collection.controller.ts +++ b/src/collection/collection.controller.ts @@ -79,23 +79,8 @@ export class CollectionController { @UseGuards(AuthGuard()) @Post('copy') @ApiOperation({summary : 'copy a collection with its ID'}) - async copyCollection(@Body() copyCollectionDto: copyCollectionDto, @GetUser() user: User): Promise{ - const fatherCollection = await this.collectionService.getCollectionById(copyCollectionDto.fatherCollectionId, user); - const copiedId = await this.collectionService.copyCollection(copyCollectionDto.fatherCollectionId, copyCollectionDto.collectionId, user); - let fatherCollectionsIds = fatherCollection.collections.map(collection => { - return collection.id; - }) - fatherCollectionsIds.push(copiedId); - const modifyCollectionDto : modifyCollectionDto = { - meaning : null, - speech : null, - pictoIds : null, - priority : 10, - color : null, - collectionIds : fatherCollectionsIds, - pictohubId: null - } - await this.collectionService.modifyCollection(copyCollectionDto.fatherCollectionId, user, modifyCollectionDto, null); + async copyCollection(@Body() copyCollectionDto: copyCollectionDto, @GetUser() user: User): Promise{ + await this.collectionService.copyCollectionWithTransaction(copyCollectionDto.fatherCollectionId, copyCollectionDto.collectionId, user); return this.getCollectionById(copyCollectionDto.fatherCollectionId, user); } diff --git a/src/collection/collection.repository.ts b/src/collection/collection.repository.ts index 51658d9..8e04f16 100644 --- a/src/collection/collection.repository.ts +++ b/src/collection/collection.repository.ts @@ -10,7 +10,7 @@ import { Picto } from 'src/entities/picto.entity'; import { User } from 'src/entities/user.entity'; import { parseNumberArray } from 'src/utilities/tools'; import { CustomRepository } from 'src/utilities/typeorm-ex.decorator'; -import { Repository } from 'typeorm'; +import { EntityManager, Repository } from 'typeorm'; import { createCollectionDto } from './dto/collection.create.dto'; import { modifyCollectionDto } from './dto/collection.modify.dto'; import { SearchCollectionDto } from './dto/collection.search.public.dto'; @@ -26,6 +26,7 @@ export class CollectionRepository extends Repository { createCollectionDto: createCollectionDto, user: User, filename: string, + manager?: EntityManager, ): Promise { let { meaning, speech, pictoIds, collectionIds, color, pictohubId } = createCollectionDto; @@ -65,7 +66,12 @@ export class CollectionRepository extends Repository { throw new NotFoundException(`filename not found`); } try { + if (!manager) { await collection.save(); + } + else { + await manager.save(Collection, collection); + } } catch (error) { throw new InternalServerErrorException(error); } @@ -77,6 +83,7 @@ export class CollectionRepository extends Repository { modifyCollectionDto: modifyCollectionDto, user: User, filename: string, + manager?: EntityManager ): Promise { let { meaning, @@ -124,8 +131,13 @@ export class CollectionRepository extends Repository { collection.color = color; } try { + if (!manager) { await collection.save(); + } else { + await manager.save(Collection, collection); + } } catch (error) { + console.error(error); throw new InternalServerErrorException( `could not save collection properly`, ); diff --git a/src/collection/collection.service.ts b/src/collection/collection.service.ts index f3a59b2..83b11da 100644 --- a/src/collection/collection.service.ts +++ b/src/collection/collection.service.ts @@ -6,7 +6,7 @@ import { Collection } from 'src/entities/collection.entity'; import { Notif } from 'src/entities/notification.entity'; import { Picto } from 'src/entities/picto.entity'; import { User } from 'src/entities/user.entity'; -import { createPictoDto } from 'src/picto/dto/picto.create.dto'; +import { createPictoDto } from 'src/picto/dto/picto.create.dto'; import { PictoService } from 'src/picto/picto.service'; import { CollectionRepository } from './collection.repository'; import { createCollectionDto } from './dto/collection.create.dto'; @@ -16,34 +16,40 @@ import { MoveToCollectionDto } from './dto/collection.move.dto'; import { publicCollectionDto } from './dto/collection.public.dto'; import { SearchCollectionDto } from './dto/collection.search.public.dto'; import { shareCollectionDto, multipleShareCollectionDto } from './dto/collection.share.dto'; +import { EntityManager } from 'typeorm'; @Injectable() export class CollectionService { constructor( @InjectRepository(CollectionRepository) - private collectionRepository : CollectionRepository, + private collectionRepository: CollectionRepository, @Inject(forwardRef(() => AuthService)) - private authService : AuthService, + private authService: AuthService, @Inject(forwardRef(() => PictoService)) - private pictoService : PictoService, + private pictoService: PictoService, ) { } - async getCollectionCount(): Promise{ + async getCollectionCount(): Promise { return await this.collectionRepository.createQueryBuilder('collection').getCount() } - async getCollectionById(id: number, user : User): Promise{ - const collection = await this.collectionRepository.findOne({relations: ["pictos", "collections"],where : {id}}); - - if(!collection) { - throw new NotFoundException(`Collection with ID '${id}' not found`); + async getCollectionById(id: number, user: User, manager?: EntityManager): Promise { + let collection: Collection; + if (!manager) { + collection = await this.collectionRepository.findOne({ relations: ["pictos", "collections"], where: { id } }); } else { - let viewer : number; - let editor : number; + collection = await manager.findOne(Collection, { relations: ["pictos", "collections"], where: { id } }); + + } + if (!collection) { + throw new NotFoundException(`Collection with ID '${id}' not found`); + } else { + let viewer: number; + let editor: number; viewer = collection.viewers.indexOf(user.username); editor = collection.editors.indexOf(user.username); - if(collection.public === true || viewer!=-1 || editor!=-1 || collection.userId === user.id){ + if (collection.public === true || viewer != -1 || editor != -1 || collection.userId === user.id) { return this.verifyAcces(collection, user); } else { throw new UnauthorizedException(`User ${user.username} does not have access to this collection`); @@ -51,140 +57,140 @@ export class CollectionService { } } - verifyAcces(collection: Collection, user: User): Collection{ - let viewer : number; - let editor : number; - for(let index = 0; index < collection.collections.length; index++){ + verifyAcces(collection: Collection, user: User): Collection { + let viewer: number; + let editor: number; + for (let index = 0; index < collection.collections.length; index++) { viewer = collection.collections[index].viewers.indexOf(user.username); editor = collection.collections[index].editors.indexOf(user.username); - if(collection.collections[index].public === false && viewer ===-1 && editor ===-1 && collection.collections[index].userId != user.id){ + if (collection.collections[index].public === false && viewer === -1 && editor === -1 && collection.collections[index].userId != user.id) { collection.collections.splice(index, 1); - } + } } - for(let index = 0; index < collection.pictos.length; index++){ + for (let index = 0; index < collection.pictos.length; index++) { viewer = collection.pictos[index].viewers.indexOf(user.username); editor = collection.pictos[index].editors.indexOf(user.username); - if(collection.pictos[index].public === false && viewer ===-1 && editor ===-1 && collection.pictos[index].userId != user.id){ + if (collection.pictos[index].public === false && viewer === -1 && editor === -1 && collection.pictos[index].userId != user.id) { collection.pictos.splice(index, 1); - } + } } return collection; } - async getAllUserCollections(user:User): Promise{ - const collection = await this.collectionRepository.find({relations: ["pictos", "collections"],where : {userId: user.id}}); + async getAllUserCollections(user: User): Promise { + const collection = await this.collectionRepository.find({ relations: ["pictos", "collections"], where: { userId: user.id } }); return collection; - } + } async createCollection(createCollectionDto: createCollectionDto, user: User, filename: string): Promise { createCollectionDto.collectionIds = await this.verifyOwnership(createCollectionDto.collectionIds, user); return this.collectionRepository.createCollection(createCollectionDto, user, filename); } - async createRoot(user: User): Promise{ + async createRoot(user: User): Promise { return this.collectionRepository.createRoot(user); } - async createSider(user: User): Promise{ + async createSider(user: User): Promise { return this.collectionRepository.createSider(user); } - async createShared (user: User): Promise{ + async createShared(user: User): Promise { return this.collectionRepository.createShared(user); } - async deleteCollection(deleteCollectionDto: deleteCollectionDto, user: User): Promise{ - const collection = await this.getCollectionById(deleteCollectionDto.collectionId, user); - if(deleteCollectionDto.fatherId){ - deleteCollectionDto.fatherId=Number(deleteCollectionDto.fatherId); - const fatherCollection = await this.getCollectionById(deleteCollectionDto.fatherId, user); - let fatherCollectionsIds = fatherCollection.collections.map(collection => {return collection.id;}) - fatherCollectionsIds.splice(fatherCollectionsIds.indexOf(deleteCollectionDto.collectionId),1); - const modifyCollectionDto : modifyCollectionDto = { - meaning : null, - speech : null, - pictoIds : null, - priority : 10, - color : null, - collectionIds : fatherCollectionsIds, - pictohubId: null - } - await this.modifyCollection(deleteCollectionDto.fatherId, user, modifyCollectionDto, null); + async deleteCollection(deleteCollectionDto: deleteCollectionDto, user: User): Promise { + const collection = await this.getCollectionById(deleteCollectionDto.collectionId, user); + if (deleteCollectionDto.fatherId) { + deleteCollectionDto.fatherId = Number(deleteCollectionDto.fatherId); + const fatherCollection = await this.getCollectionById(deleteCollectionDto.fatherId, user); + let fatherCollectionsIds = fatherCollection.collections.map(collection => { return collection.id; }) + fatherCollectionsIds.splice(fatherCollectionsIds.indexOf(deleteCollectionDto.collectionId), 1); + const modifyCollectionDto: modifyCollectionDto = { + meaning: null, + speech: null, + pictoIds: null, + priority: 10, + color: null, + collectionIds: fatherCollectionsIds, + pictohubId: null } - try{ - const result = await this.collectionRepository.delete({ - id: deleteCollectionDto.collectionId, - userId: user.id, - }); - } catch(error){ - if(error.code === "23503"){ - return; - } else { - throw new InternalServerErrorException(`couldn't delete collection with id ${deleteCollectionDto.collectionId}`); - } + await this.modifyCollection(deleteCollectionDto.fatherId, user, modifyCollectionDto, null); + } + try { + const result = await this.collectionRepository.delete({ + id: deleteCollectionDto.collectionId, + userId: user.id, + }); + } catch (error) { + if (error.code === "23503") { + return; + } else { + throw new InternalServerErrorException(`couldn't delete collection with id ${deleteCollectionDto.collectionId}`); } + } } - async autoShare(collection : Collection, fatherCollection: Collection): Promise{ + async autoShare(collection: Collection, fatherCollection: Collection): Promise { return this.collectionRepository.autoShare(collection, fatherCollection); } - async modifyCollection(id: number, user: User, modifyCollectionDto: modifyCollectionDto, filename: string): Promise{ - const collection=await this.getCollectionById(id, user); + async modifyCollection(id: number, user: User, modifyCollectionDto: modifyCollectionDto, filename: string, manager?: EntityManager): Promise { + const collection = await this.getCollectionById(id, user, manager); const index = collection.editors.indexOf(user.username); - if(collection.userId===user.id || index!=-1){ - modifyCollectionDto = await this.verifyOwnership(modifyCollectionDto, user); - if(collection.public){ + if (collection.userId === user.id || index != -1) { + modifyCollectionDto = await this.verifyOwnership(modifyCollectionDto, user, manager); + if (collection.public) { const admins = await this.authService.admins(); - admins.map(async(admin) => { + admins.map(async (admin) => { const notification = await this.createNotif(collection, admin, "public collection", "modified"); this.authService.pushNotification(admin, notification); }); } - return this.collectionRepository.modifyCollection(collection, modifyCollectionDto, user, filename); + return this.collectionRepository.modifyCollection(collection, modifyCollectionDto, user, filename, manager); } else { throw new UnauthorizedException(`User '${user.username}' is not authorized to modify this collection`); } - + } - async shareCollectionVerification(id: number, user: User, multipleShareCollectionDto: multipleShareCollectionDto): Promise{ + async shareCollectionVerification(id: number, user: User, multipleShareCollectionDto: multipleShareCollectionDto): Promise { //filter all the users that don't exist and remove them from the operation let sharers = await Promise.all(multipleShareCollectionDto.usernames.map(async username => { const sharer = await this.authService.findWithUsername(username); const exists = this.authService.verifyExistence(sharer) - if(exists && (sharer.username !== user.username)){ + if (exists && (sharer.username !== user.username)) { return sharer; } })); sharers = sharers.filter(Boolean); - multipleShareCollectionDto.usernames = sharers.map(sharer => {return sharer.username}); - if(sharers.length>0){ - const collection=await this.getCollectionById(id, user); - if(collection){ + multipleShareCollectionDto.usernames = sharers.map(sharer => { return sharer.username }); + if (sharers.length > 0) { + const collection = await this.getCollectionById(id, user); + if (collection) { const editor = collection.editors.indexOf(user.username); - if(multipleShareCollectionDto.role==="editor" && !(collection.userId === user.id || editor!=-1)){ + if (multipleShareCollectionDto.role === "editor" && !(collection.userId === user.id || editor != -1)) { throw new UnauthorizedException(`${user.username} cannot share to ${sharers} as editor being a viewer youself`); } // here we add the shared collection in the 'hared with me' collection of each sharer and send notifications if necessary - for(let sharer of sharers){ + for (let sharer of sharers) { const sharedWithMe = await this.getCollectionById(sharer.shared, sharer); this.collectionRepository.pushCollection(sharedWithMe, collection); const sharerRoot = await this.getCollectionById(sharer.root, sharer); this.collectionRepository.pushCollection(sharerRoot, collection); - if(multipleShareCollectionDto.access==1){ - if((collection.editors.indexOf(sharer.username)==-1) && (collection.viewers.indexOf(sharer.username)==-1)){ + if (multipleShareCollectionDto.access == 1) { + if ((collection.editors.indexOf(sharer.username) == -1) && (collection.viewers.indexOf(sharer.username) == -1)) { const notification = await this.createNotif(collection, user, "collection", "share"); this.authService.pushNotification(sharer, notification); } } - if (multipleShareCollectionDto.access == 0){ - if((collection.editors.indexOf(sharer.username)!==-1) || (collection.viewers.indexOf(sharer.username)!==-1)){ + if (multipleShareCollectionDto.access == 0) { + if ((collection.editors.indexOf(sharer.username) !== -1) || (collection.viewers.indexOf(sharer.username) !== -1)) { const notification = await this.createNotif(collection, user, "collection", "unshare"); - this.authService.pushNotification(sharer, notification); + this.authService.pushNotification(sharer, notification); } } } - return this.shareCollectionById(id, multipleShareCollectionDto, user); + return this.shareCollectionById(id, multipleShareCollectionDto, user); } else { throw new NotFoundException(`Collection with ID '${id}' not found`); } @@ -193,180 +199,220 @@ export class CollectionService { } } - async createNotif(collection: Collection, user: User, type: string, operation: string): Promise{ - try{ + async createNotif(collection: Collection, user: User, type: string, operation: string): Promise { + try { const notification: Notif = new Notif(type, operation, collection.id.toString(), collection.meaning, user.username); return notification; - } catch(error){ + } catch (error) { throw new InternalServerErrorException(`could not create notification ${error}`); } - } + } - async shareCollectionById(collectionId : number, multipleShareCollectionDto: multipleShareCollectionDto, user: User): Promise{ + async shareCollectionById(collectionId: number, multipleShareCollectionDto: multipleShareCollectionDto, user: User): Promise { let collection = await this.getCollectionById(collectionId, user); - try{ + try { collection.collections.map(collection => this.shareCollectionById(collection.id, multipleShareCollectionDto, user)); - } catch(error){} - try{ + } catch (error) { } + try { collection.pictos.map(picto => this.collectionRepository.sharePictoFromDto(picto, multipleShareCollectionDto)); - } catch(error){} - try{ - collection=await this.collectionRepository.shareCollectionFromDto(collection, multipleShareCollectionDto); - } catch(error){} + } catch (error) { } + try { + collection = await this.collectionRepository.shareCollectionFromDto(collection, multipleShareCollectionDto); + } catch (error) { } return collection; } - async publishCollectionById(collectionId : number, publicCollectionDto: publicCollectionDto, user: User): Promise{ + async publishCollectionById(collectionId: number, publicCollectionDto: publicCollectionDto, user: User): Promise { let collection = await this.getCollectionById(collectionId, user); - if(collection.userId === user.id){ - try{ + if (collection.userId === user.id) { + try { collection.collections.map(collection => this.publishCollectionById(collection.id, publicCollectionDto, user)); - } catch(error){} - try{ + } catch (error) { } + try { collection.pictos.map(picto => this.collectionRepository.publishPicto(picto, publicCollectionDto.publish, user)); - } catch(error){} - try{ - collection=await this.collectionRepository.publishCollection(collection, publicCollectionDto.publish, user); - } catch(error){} + } catch (error) { } + try { + collection = await this.collectionRepository.publishCollection(collection, publicCollectionDto.publish, user); + } catch (error) { } } return collection; } - async verifyOwnership(verificationDto : any, user: User){ - try{ - for(var i=0; i { + try { + for (var i = 0; i < verificationDto.collectionIds.length; i++) { + try { + const collection = await this.getCollectionById(verificationDto.collectionIds[i], user, manager); + } catch (error) { + i = i - 1; verificationDto.collectionIds.splice(i, 1); } } - } catch(error){} - try{ - for(var i=0; i{ + async pushPicto(collection: Collection, picto: Picto): Promise { return this.collectionRepository.pushPicto(collection, picto); } - async getPublicCollection(SearchCollectionDto: SearchCollectionDto): Promise{ + async getPublicCollection(SearchCollectionDto: SearchCollectionDto): Promise { return this.collectionRepository.getPublicCollections(SearchCollectionDto); } - - async copyPicto(fatherId: number, picto: Picto, user: User): Promise{ + async copyCollectionWithTransaction(fatherId: number, collectionId: number, user: User): Promise { + return await this.collectionRepository.manager.transaction(async manager => { + const fatherCollection = await this.getCollectionById(fatherId, user, manager); + const copiedId = await this.copyCollectionRecursive(fatherId, collectionId, user, manager); + let fatherCollectionsIds = fatherCollection.collections.map(collection => { + return collection.id; + }) + fatherCollectionsIds.push(copiedId); + const modifyCollectionDto: modifyCollectionDto = { + meaning: null, + speech: null, + pictoIds: undefined, + priority: 10, + color: null, + collectionIds: fatherCollectionsIds, + pictohubId: null + } + await this.modifyCollection(fatherId, user, modifyCollectionDto, null, manager); + return copiedId; + }); + } + async copyCollectionRecursive(fatherId: number, collectionId: number, user: User, entityManager: EntityManager): Promise { + try { + const collection = await entityManager.findOne(Collection, { + where: { id: collectionId }, + relations: ["pictos", "collections"], + }); + if (collection) { + const createCollectionDto = { + meaning: collection.meaning, + speech: collection.speech, + color: collection.color, + pictohubId: null, + collectionIds: await Promise.all(collection.collections.map(child => this.copyCollectionRecursive(collection.id, child.id, user, entityManager))), + pictoIds: await Promise.all(collection.pictos.map(picto => this.copyPicto(collection.id, picto, user, entityManager))), + fatherCollectionId: fatherId, + share: 0, + userId: user.id, + image : collection.image + }; + const copiedCollection = await this.collectionRepository.createCollection(createCollectionDto, user, collection.image, entityManager); + return copiedCollection.id; + } + } catch (error) { + console.log(error); + throw new InternalServerErrorException(`couldn't copy Collection`); + } + } + async copyPicto(fatherId: number, picto: Picto, user: User, manager?: EntityManager): Promise { const editor = picto.editors.indexOf(user.username); const viewer = picto.viewers.indexOf(user.username); - if(picto.userId===user.id || editor!=-1 || viewer!=-1 || picto.public){ - const createPictoDto : createPictoDto = { - meaning : picto.meaning, - speech : picto.speech, - color : picto.color, - collectionIds : null, - fatherCollectionId : fatherId, - share : 1, + if (picto.userId === user.id || editor != -1 || viewer != -1 || picto.public) { + const createPictoDto: createPictoDto = { + meaning: picto.meaning, + speech: picto.speech, + color: picto.color, + collectionIds: null, + fatherCollectionId: fatherId, + share: 1, pictohubId: null, - } - const copiedPicto = await this.pictoService.createPicto(createPictoDto, user, picto.image); - return copiedPicto.id; + } + const copiedPicto = await this.pictoService.createPicto(createPictoDto, user, picto.image, manager); + return copiedPicto.id; } else { return null; } } - async copyCollection(fatherId: number, collectionId: number, user: User): Promise{ - try{ - const collection = await this.getCollectionById(collectionId, user); - if(collection){ - const createCollectionDto : createCollectionDto = { - meaning : collection.meaning, - speech : collection.speech, - color : collection.color, - pictohubId: null, - collectionIds : await Promise.all(collection.collections.map(child => {return this.copyCollection(collection.id, child.id, user);})), - pictoIds : await Promise.all(collection.pictos.map(child => {return this.copyPicto(collection.id, child, user);})), - fatherCollectionId : fatherId, - share : 0, - } - const copiedCollection = await this.createCollection(createCollectionDto, user, collection.image); - return copiedCollection.id; - } - } catch(error){ - if(error.status == "401" || error.status == "404"){ - return null; - } else { - console.log(error); - throw new InternalServerErrorException(`couldn't copy Collection`); - } - } - } - async moveToCollection(user: User, moveToCollectionDto: MoveToCollectionDto, fatherCollectionId: number): Promise { + // this ugly ass looking code implicitly checks if user has access to said ressources when we run getByID const targetCollection = await this.getCollectionById(moveToCollectionDto.targetCollectionId, user); + const fatherCollection = await this.getCollectionById(fatherCollectionId, user); + const sourceCollection = moveToCollectionDto.sourceCollectionId ? await this.getCollectionById(moveToCollectionDto.sourceCollectionId, user) : null; + const sourcePictogram = moveToCollectionDto.sourcePictoId ? await this.pictoService.getPictoById(moveToCollectionDto.sourcePictoId, user) : null + console.info(`User ${user.username} has access to all ressources needed`); + // now that we made sure user has access to ressource, we run transactions if (moveToCollectionDto.sourceCollectionId) { - const modifyCollection = new modifyCollectionDto() - modifyCollection.collectionIds = targetCollection.collections.map(collection => collection.id); - modifyCollection.collectionIds.push(moveToCollectionDto.sourceCollectionId) - const deleteCollection = new deleteCollectionDto() - deleteCollection.collectionId = Number(moveToCollectionDto.sourceCollectionId); - deleteCollection.fatherId = fatherCollectionId; - await this.collectionRepository.modifyCollection(targetCollection, modifyCollection, user, null) - await this.deleteCollection(deleteCollection, user) + console.info(`Moving Collection ${sourceCollection.meaning} to ${targetCollection.meaning}`); + await this.collectionRepository.manager.transaction(async manager => { + // I want your manager !! + // Perform the INSERT operation within the transaction + await manager.query( + `INSERT INTO collection_collections_collection ("collectionId_1", "collectionId_2") VALUES ($1, $2)`, + [moveToCollectionDto.targetCollectionId, moveToCollectionDto.sourceCollectionId] + ); + // Perform the DELETE operation within the same transaction + await manager.query( + `DELETE FROM collection_collections_collection WHERE "collectionId_1" = $1 AND "collectionId_2" = $2`, + [fatherCollectionId, moveToCollectionDto.sourceCollectionId] + ); + }); + console.info(`Operation successful`); } else if (moveToCollectionDto.sourcePictoId) { - const modifyCollection = new modifyCollectionDto(); - modifyCollection.pictoIds = targetCollection.pictos.map(collection => collection.id); - modifyCollection.pictoIds.push(moveToCollectionDto.sourcePictoId); - const deletePicto = new deletePictoDto(); - deletePicto.fatherId = fatherCollectionId; - deletePicto.pictoId = Number(moveToCollectionDto.sourcePictoId); - await this.collectionRepository.modifyCollection(targetCollection, modifyCollection, user, null) - await this.pictoService.deletePicto(deletePicto, user) + console.info(`Moving Picto ${sourcePictogram.meaning} to ${targetCollection.meaning}`); + await this.collectionRepository.manager.transaction(async manager => { + // I want your manager !! + // Perform the INSERT operation within the transaction + await manager.query( + `INSERT INTO collection_pictos_picto ("collectionId", "pictoId") VALUES ($1, $2)`, + [moveToCollectionDto.targetCollectionId, moveToCollectionDto.sourcePictoId] + ); + // Perform the DELETE operation within the same transaction + await manager.query( + `DELETE FROM collection_pictos_picto WHERE "collectionId" = $1 AND "pictoId" = $2`, + [fatherCollectionId, moveToCollectionDto.sourcePictoId] + ); + }); + console.info(`Operation successful`); } return await this.getCollectionById(fatherCollectionId, user); } - - async deleteAllCollections(user: User): Promise{ + + async deleteAllCollections(user: User): Promise { const collections = await this.getAllUserCollections(user); console.log(`User ${user.username} has ${collections.length} collections`); - try{ - await Promise.all(collections.map(collection => - this.modifyCollection(collection.id, user, {meaning: null, speech: null, priority: null, color: null, pictohubId: null, collectionIds: [], pictoIds: []}, null) + try { + await Promise.all(collections.map(collection => + this.modifyCollection(collection.id, user, { meaning: null, speech: null, priority: null, color: null, pictohubId: null, collectionIds: [], pictoIds: [] }, null) )); await Promise.all(collections.map(collection => - this.deleteCollection({collectionId: collection.id, fatherId: null}, user) + this.deleteCollection({ collectionId: collection.id, fatherId: null }, user) )); - } catch(error){ + } catch (error) { throw new InternalServerErrorException(`couldn't delete all collections`); } } - async getOrphanedCollections(user: User): Promise{ - const rootCollectionId = user.root; + async getOrphanedCollections(user: User): Promise { + const rootCollectionId = user.root; - const queryBuilder = this.collectionRepository.createQueryBuilder('collection'); - queryBuilder.where('collection.userId = :userId', { userId: user.id }) - .andWhere(qb => { - // This subquery checks for collections not linked to the root collection - const subQuery = qb.subQuery() - .select('link."collectionId_2"') // Corrected column name - .from('collection_collections_collection', 'link') // Corrected join table name - .where('link."collectionId_1" = :rootCollectionId', { rootCollectionId }) // Corrected column name - .orWhere('link."collectionId_2" = :rootCollectionId', { rootCollectionId }) // Corrected column name - .getQuery(); - return 'collection.id NOT IN ' + subQuery; - }); - const orphanedCollections = await queryBuilder.getMany(); - // Filter 'shared' collection and 'sider' collection - return orphanedCollections.filter(collection => collection.id !== user.shared && collection.id !== user.sider); - } + const queryBuilder = this.collectionRepository.createQueryBuilder('collection'); + queryBuilder.where('collection.userId = :userId', { userId: user.id }) + .andWhere(qb => { + // This subquery checks for collections not linked to the root collection + const subQuery = qb.subQuery() + .select('link."collectionId_2"') // Corrected column name + .from('collection_collections_collection', 'link') // Corrected join table name + .where('link."collectionId_1" = :rootCollectionId', { rootCollectionId }) // Corrected column name + .orWhere('link."collectionId_2" = :rootCollectionId', { rootCollectionId }) // Corrected column name + .getQuery(); + return 'collection.id NOT IN ' + subQuery; + }); + const orphanedCollections = await queryBuilder.getMany(); + // Filter 'shared' collection and 'sider' collection + return orphanedCollections.filter(collection => collection.id !== user.shared && collection.id !== user.sider); + } } \ No newline at end of file diff --git a/src/picto/picto.repository.ts b/src/picto/picto.repository.ts index dd79e56..36d2a05 100644 --- a/src/picto/picto.repository.ts +++ b/src/picto/picto.repository.ts @@ -4,7 +4,7 @@ import { Picto } from 'src/entities/picto.entity'; import { User } from 'src/entities/user.entity'; import { parseNumberArray } from 'src/utilities/tools'; import { CustomRepository } from 'src/utilities/typeorm-ex.decorator'; -import { Repository } from 'typeorm'; +import { EntityManager, Repository } from 'typeorm'; import { createPictoDto } from './dto/picto.create.dto'; import { modifyPictoDto } from './dto/picto.modify.dto'; import { sharePictoDto } from './dto/picto.share.dto'; @@ -15,6 +15,7 @@ export class PictoRepository extends Repository { createPictoDto: createPictoDto, user: User, filename: string, + manager?: EntityManager ): Promise { let { meaning, speech, collectionIds, color, pictohubId } = createPictoDto; const picto = new Picto(); @@ -43,7 +44,11 @@ export class PictoRepository extends Repository { ); } try { - await picto.save(); + if (!manager) { + await picto.save(); + } else { + await manager.save(Picto, picto); + } } catch (error) { throw new InternalServerErrorException(error); } diff --git a/src/picto/picto.service.ts b/src/picto/picto.service.ts index 10c91c0..447a640 100644 --- a/src/picto/picto.service.ts +++ b/src/picto/picto.service.ts @@ -12,6 +12,7 @@ import { CollectionService } from 'src/collection/collection.service'; import { deletePictoDto } from './dto/picto.delete.dto'; import { modifyCollectionDto } from 'src/collection/dto/collection.modify.dto'; import { Notif } from 'src/entities/notification.entity'; +import { EntityManager } from 'typeorm'; @Injectable() export class PictoService { @@ -24,8 +25,13 @@ export class PictoService { private collectionService : CollectionService, ) { } - async getPictoById(id: number, user : User): Promise{ - const picto = await this.pictoRepository.findOne({where : {id}}); + async getPictoById(id: number, user : User, manager?: EntityManager): Promise{ + let picto: Picto; + if (!manager){ + picto = await this.pictoRepository.findOne({where : {id}}); + } else { + picto = await manager.findOne(Picto,{where : {id}, relations: ["collections"]}); + } if(!picto) { throw new NotFoundException(`Picto with ID '${id}' not found`); } else { @@ -77,8 +83,8 @@ export class PictoService { return found; } - async createPicto(createPictoDto: createPictoDto, user: User, filename: string): Promise { - return this.pictoRepository.createPicto(createPictoDto, user, filename); + async createPicto(createPictoDto: createPictoDto, user: User, filename: string, manager?: EntityManager): Promise { + return this.pictoRepository.createPicto(createPictoDto, user, filename, manager); } async deletePicto(deletePictoDto: deletePictoDto, user: User): Promise {