From 84929172f83d7860be32c2ad22d845a5962a4227 Mon Sep 17 00:00:00 2001 From: Lars Mitsem Selbekk Date: Mon, 17 Jun 2024 15:11:34 +0200 Subject: [PATCH] feat(match-transfer): prevent double ISBN handout We were previously only checking that the same BLID was not transferred twice in a UserMatch, now we check that the same ISBN (or actually Item ID) is not transferred twice with different BLIDs. --- .../helpers/customer-item-active-blid.ts | 19 ++-- .../operations/match-getall-me.operation.ts | 62 ++++++------- .../match/operations/match-operation-utils.ts | 13 +-- .../match-transfer-item.operation.ts | 90 +++++++++++-------- 4 files changed, 88 insertions(+), 96 deletions(-) diff --git a/src/collections/customer-item/helpers/customer-item-active-blid.ts b/src/collections/customer-item/helpers/customer-item-active-blid.ts index ae41df58..9f9b9d3f 100644 --- a/src/collections/customer-item/helpers/customer-item-active-blid.ts +++ b/src/collections/customer-item/helpers/customer-item-active-blid.ts @@ -7,16 +7,17 @@ import { BlCollectionName } from "../../bl-collection"; import { customerItemSchema } from "../customer-item.schema"; export class CustomerItemActiveBlid { + private readonly _customerItemStorage: BlDocumentStorage; private customerItemActive: CustomerItemActive; private dbQueryBuilder: SEDbQueryBuilder; - constructor(private customerItemStorage?: BlDocumentStorage) { - this.customerItemStorage = customerItemStorage - ? customerItemStorage - : new BlDocumentStorage( - BlCollectionName.CustomerItems, - customerItemSchema, - ); + constructor(customerItemStorage?: BlDocumentStorage) { + this._customerItemStorage = + customerItemStorage ?? + new BlDocumentStorage( + BlCollectionName.CustomerItems, + customerItemSchema, + ); this.customerItemActive = new CustomerItemActive(); this.dbQueryBuilder = new SEDbQueryBuilder(); } @@ -34,9 +35,7 @@ export class CustomerItemActiveBlid { { fieldName: "blid", type: "string" }, ]); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const customerItems = await this.customerItemStorage.getByQuery(dbQuery); + const customerItems = await this._customerItemStorage.getByQuery(dbQuery); const activeCustomerItems = customerItems.filter((customerItem) => this.customerItemActive.isActive(customerItem), diff --git a/src/collections/match/operations/match-getall-me.operation.ts b/src/collections/match/operations/match-getall-me.operation.ts index 157a5059..894cc3d4 100644 --- a/src/collections/match/operations/match-getall-me.operation.ts +++ b/src/collections/match/operations/match-getall-me.operation.ts @@ -14,45 +14,39 @@ import { BlDocumentStorage } from "../../../storage/blDocumentStorage"; import { BlCollectionName } from "../../bl-collection"; import { itemSchema } from "../../item/item.schema"; import { uniqueItemSchema } from "../../unique-item/unique-item.schema"; -import { User } from "../../user/user"; -import { UserSchema } from "../../user/user.schema"; import { userDetailSchema } from "../../user-detail/user-detail.schema"; import { matchSchema } from "../match.schema"; export class GetMyMatchesOperation implements Operation { + private readonly _userDetailStorage: BlDocumentStorage; + private readonly _matchStorage: BlDocumentStorage; + private readonly _uniqueItemStorage: BlDocumentStorage; + private readonly _itemStorage: BlDocumentStorage; + constructor( - private userStorage?: BlDocumentStorage, - private userDetailStorage?: BlDocumentStorage, - private matchStorage?: BlDocumentStorage, - private uniqueItemStorage?: BlDocumentStorage, - private itemStorage?: BlDocumentStorage, + userDetailStorage?: BlDocumentStorage, + matchStorage?: BlDocumentStorage, + uniqueItemStorage?: BlDocumentStorage, + itemStorage?: BlDocumentStorage, ) { - this.userStorage ??= new BlDocumentStorage( - BlCollectionName.Users, - UserSchema, - ); - this.userDetailStorage ??= new BlDocumentStorage( - BlCollectionName.UserDetails, - userDetailSchema, - ); - this.matchStorage ??= new BlDocumentStorage( - BlCollectionName.Matches, - matchSchema, - ); - this.uniqueItemStorage ??= new BlDocumentStorage( - BlCollectionName.UniqueItems, - uniqueItemSchema, - ); - this.itemStorage ??= new BlDocumentStorage( - BlCollectionName.Items, - itemSchema, - ); + this._userDetailStorage = + userDetailStorage ?? + new BlDocumentStorage(BlCollectionName.UserDetails, userDetailSchema); + this._matchStorage = + matchStorage ?? + new BlDocumentStorage(BlCollectionName.Matches, matchSchema); + this._uniqueItemStorage = + uniqueItemStorage ?? + new BlDocumentStorage(BlCollectionName.UniqueItems, uniqueItemSchema); + this._itemStorage = + itemStorage ?? new BlDocumentStorage(BlCollectionName.Items, itemSchema); } async run(blApiRequest: BlApiRequest): Promise { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const matches = await getAllMatchesForUser(blApiRequest.user.details); + const matches = await getAllMatchesForUser( + blApiRequest.user!.details, + this._matchStorage, + ); if (matches.length === 0) { return new BlapiResponse(matches); @@ -60,11 +54,9 @@ export class GetMyMatchesOperation implements Operation { const matchesWithDetails = await addDetailsToAllMatches( matches, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - this.userDetailStorage, - this.itemStorage, - this.uniqueItemStorage, + this._userDetailStorage, + this._itemStorage, + this._uniqueItemStorage, ); return new BlapiResponse(matchesWithDetails); diff --git a/src/collections/match/operations/match-operation-utils.ts b/src/collections/match/operations/match-operation-utils.ts index f157aed3..0ceca7a1 100644 --- a/src/collections/match/operations/match-operation-utils.ts +++ b/src/collections/match/operations/match-operation-utils.ts @@ -13,7 +13,6 @@ import { BlCollectionName } from "../../bl-collection"; import { branchSchema } from "../../branch/branch.schema"; import { itemSchema } from "../../item/item.schema"; import { OrderActive } from "../../order/helpers/order-active/order-active"; -import { matchSchema } from "../match.schema"; export async function createMatchOrder( customerItem: CustomerItem, @@ -57,13 +56,6 @@ export async function createMatchOrder( const orderActive = new OrderActive(); const activeReceiverOrders = await orderActive.getActiveOrders(userDetailId); - console.log( - activeReceiverOrders.map((order) => - order.orderItems.filter((orderItem) => - orderActive.isOrderItemActive(orderItem), - ), - ), - ); const originalReceiverOrder = activeReceiverOrders.find((order) => order.orderItems .filter((orderItem) => orderActive.isOrderItemActive(orderItem)) @@ -118,6 +110,7 @@ export async function createMatchOrder( export async function getAllMatchesForUser( userDetailId: string, + matchStorage: BlDocumentStorage, ): Promise { const query = new SEDbQuery(); query.objectIdFilters = [ @@ -127,10 +120,6 @@ export async function getAllMatchesForUser( { fieldName: "receiver", value: [userDetailId] }, ]; - const matchStorage = new BlDocumentStorage( - BlCollectionName.Matches, - matchSchema, - ); try { return (await matchStorage.getByQuery(query)) as Match[]; } catch (e) { diff --git a/src/collections/match/operations/match-transfer-item.operation.ts b/src/collections/match/operations/match-transfer-item.operation.ts index b143cbc4..98ab2a9a 100644 --- a/src/collections/match/operations/match-transfer-item.operation.ts +++ b/src/collections/match/operations/match-transfer-item.operation.ts @@ -5,7 +5,7 @@ import { Match, MatchVariant, Order, - UserDetail, + UniqueItem, UserMatch, } from "@boklisten/bl-model"; @@ -15,6 +15,7 @@ import { } from "./match-operation-utils"; import { SystemUser } from "../../../auth/permission/permission.service"; import { Operation } from "../../../operation/operation"; +import { SEDbQuery } from "../../../query/se.db-query"; import { BlApiRequest } from "../../../request/bl-api-request"; import { BlDocumentStorage } from "../../../storage/blDocumentStorage"; import { BlCollectionName } from "../../bl-collection"; @@ -24,24 +25,33 @@ import { OrderToCustomerItemGenerator } from "../../customer-item/helpers/order- import { OrderItemMovedFromOrderHandler } from "../../order/helpers/order-item-moved-from-order-handler/order-item-moved-from-order-handler"; import { OrderValidator } from "../../order/helpers/order-validator/order-validator"; import { orderSchema } from "../../order/order.schema"; -import { userDetailSchema } from "../../user-detail/user-detail.schema"; +import { uniqueItemSchema } from "../../unique-item/unique-item.schema"; import { matchSchema } from "../match.schema"; export class MatchTransferItemOperation implements Operation { + private readonly _matchStorage: BlDocumentStorage; + private readonly _orderStorage: BlDocumentStorage; + private readonly _customerItemStorage: BlDocumentStorage; + private readonly _uniqueItemStorage: BlDocumentStorage; + constructor( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - private matchStorage?: BlDocumentStorage, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - private userDetailStorage?: BlDocumentStorage, + matchStorage?: BlDocumentStorage, + orderStorage?: BlDocumentStorage, + customerItemStorage?: BlDocumentStorage, + uniqueItemStorage?: BlDocumentStorage, ) { - this.matchStorage = + this._matchStorage = matchStorage ?? new BlDocumentStorage(BlCollectionName.Matches, matchSchema); - this.userDetailStorage = - userDetailStorage ?? - new BlDocumentStorage(BlCollectionName.UserDetails, userDetailSchema); + this._orderStorage = + orderStorage ?? + new BlDocumentStorage(BlCollectionName.Orders, orderSchema); + this._customerItemStorage = + customerItemStorage ?? + new BlDocumentStorage(BlCollectionName.CustomerItems, customerItemSchema); + this._uniqueItemStorage = + uniqueItemStorage ?? + new BlDocumentStorage(BlCollectionName.UniqueItems, uniqueItemSchema); } private isValidBlid(scannedText: string): boolean { @@ -68,11 +78,12 @@ export class MatchTransferItemOperation implements Operation { throw new BlError("blid is not a valid blid").code(803); } - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const receiverUserDetailId = blApiRequest.user.details; + const receiverUserDetailId = blApiRequest.user!.details; - const receiverMatches = await getAllMatchesForUser(receiverUserDetailId); + const receiverMatches = await getAllMatchesForUser( + receiverUserDetailId, + this._matchStorage, + ); const receiverUserMatches = receiverMatches.filter( (match) => match._variant === MatchVariant.UserMatch, ) as UserMatch[]; @@ -104,8 +115,23 @@ export class MatchTransferItemOperation implements Operation { if (receiverUserMatch.receivedBlIds.includes(customerItem.blid!)) { throw new BlError("Receiver has already received this item").code(806); } + + const receivedItemIds = await Promise.all( + receiverUserMatch.receivedBlIds.map(async (blId) => { + const uniqueItemQuery = new SEDbQuery(); + uniqueItemQuery.stringFilters = [{ fieldName: "blid", value: blId }]; + return (await this._uniqueItemStorage.getByQuery(uniqueItemQuery))[0]! + .item; + }), + ); + + if (receivedItemIds.includes(customerItem.item as string)) { + throw new BlError("Receiver has already received this item").code(806); + } + const senderMatches = await getAllMatchesForUser( customerItem.customer as string, + this._matchStorage, ); const senderUserMatches = senderMatches.filter( (match) => match._variant === MatchVariant.UserMatch, @@ -123,24 +149,9 @@ export class MatchTransferItemOperation implements Operation { userFeedback = `Boken du skannet tilhørte en annen elev enn den som ga deg den. Du skal beholde den, men eleven som ga deg boken er fortsatt ansvarlig for at den opprinnelige boken blir levert.`; } - const matchStorage = new BlDocumentStorage( - BlCollectionName.Matches, - matchSchema, - ); - - const orderStorage = new BlDocumentStorage( - BlCollectionName.Orders, - orderSchema, - ); - - const customerItemStorage = new BlDocumentStorage( - BlCollectionName.CustomerItems, - customerItemSchema, - ); - const orderValidator = new OrderValidator(); - await matchStorage.update( + await this._matchStorage.update( receiverUserMatch.id, { receivedBlIds: [...receiverUserMatch.receivedBlIds, customerItem.blid!], @@ -155,7 +166,7 @@ export class MatchTransferItemOperation implements Operation { receiverUserMatch.deadlineOverrides, ); - const placedReceiverOrder = await orderStorage.add( + const placedReceiverOrder = await this._orderStorage.add( receiverOrder, new SystemUser(), ); @@ -165,7 +176,7 @@ export class MatchTransferItemOperation implements Operation { await orderMovedToHandler.updateOrderItems(placedReceiverOrder); if (senderUserMatch !== undefined) { - await matchStorage.update( + await this._matchStorage.update( senderUserMatch.id, { deliveredBlIds: [ @@ -182,14 +193,14 @@ export class MatchTransferItemOperation implements Operation { true, ); - const placedSenderOrder = await orderStorage.add( + const placedSenderOrder = await this._orderStorage.add( senderOrder, new SystemUser(), ); await orderValidator.validate(placedSenderOrder, false); } - await customerItemStorage.update( + await this._customerItemStorage.update( customerItem.id, { returned: true, @@ -206,9 +217,10 @@ export class MatchTransferItemOperation implements Operation { throw new BlError("Failed to create new customer item"); } - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - await customerItemStorage.add(generatedCustomerItems[0], new SystemUser()); + await this._customerItemStorage.add( + generatedCustomerItems[0]!, + new SystemUser(), + ); return new BlapiResponse([{ feedback: userFeedback }]); }