Skip to content

Commit

Permalink
Merge branch 'master' into MODDCB-84
Browse files Browse the repository at this point in the history
  • Loading branch information
gurleenkaurbp authored Dec 12, 2023
2 parents 64872ce + cadbcb0 commit 57822c0
Show file tree
Hide file tree
Showing 40 changed files with 569 additions and 143 deletions.
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
package org.folio.dcb.client.feign;

import org.folio.dcb.domain.dto.CirculationItemRequest;
import org.folio.dcb.domain.dto.CirculationItem;
import org.folio.dcb.domain.dto.CirculationItemCollection;
import org.folio.spring.config.FeignClientConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "circulation-item", configuration = FeignClientConfiguration.class)
public interface CirculationItemClient {
@PostMapping("/{circulationItemId}")
CirculationItemRequest createCirculationItem(@PathVariable("circulationItemId") String circulationItemId, @RequestBody CirculationItemRequest circulationRequest);
CirculationItem createCirculationItem(@PathVariable("circulationItemId") String circulationItemId, @RequestBody CirculationItem circulationRequest);

@GetMapping("/{circulationItemId}")
CirculationItemRequest retrieveCirculationItemById(@PathVariable("circulationItemId") String circulationItemId);
CirculationItem retrieveCirculationItemById(@PathVariable("circulationItemId") String circulationItemId);

@PutMapping("/{circulationItemId}")
CirculationItemRequest updateCirculationItem(@PathVariable("circulationItemId") String circulationItemId, @RequestBody CirculationItemRequest circulationRequest);
}
@GetMapping("/items")
CirculationItemCollection fetchItemByIdAndBarcode(@RequestParam("query") String query);}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

@FeignClient(name = "request-storage", configuration = FeignClientConfiguration.class)
public interface CirculationRequestClient {
@GetMapping("/requests/{circulationItemId}")
CirculationRequest fetchRequestById(@PathVariable("circulationItemId") String circulationItemId);
@GetMapping("/requests/{requestId}")
CirculationRequest fetchRequestById(@PathVariable("requestId") String requestId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,21 @@ public Errors handleGlobalException(Exception ex) {
}

@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(NotFoundException.class)
public Errors handleNotFoundException(NotFoundException ex) {
@ExceptionHandler({
NotFoundException.class,
FeignException.NotFound.class
})
public Errors handleNotFoundException(Exception ex) {
logExceptionMessage(ex);
return createExternalError(ex.getMessage(), NOT_FOUND_ERROR);
}

@ResponseStatus(HttpStatus.CONFLICT)
@ExceptionHandler(ResourceAlreadyExistException.class)
public Errors handleAlreadyExistException(ResourceAlreadyExistException ex) {
@ExceptionHandler({
ResourceAlreadyExistException.class,
FeignException.Conflict.class
})
public Errors handleAlreadyExistException(Exception ex) {
logExceptionMessage(ex);
return createExternalError(ex.getMessage(), DUPLICATE_ERROR);
}
Expand All @@ -59,7 +65,8 @@ public Errors handleBadGatewayException(FeignException.BadGateway ex) {
MissingServletRequestParameterException.class,
MethodArgumentTypeMismatchException.class,
HttpMessageNotReadableException.class,
IllegalArgumentException.class
IllegalArgumentException.class,
FeignException.BadRequest.class
})
public Errors handleValidationErrors(Exception ex) {
logExceptionMessage(ex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.folio.dcb.domain.dto.TransactionStatus;
import org.folio.dcb.rest.resource.TransactionsApi;
import org.folio.dcb.domain.dto.TransactionStatusResponse;
import org.folio.dcb.service.TransactionAuditService;
import org.folio.dcb.service.TransactionsService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -17,24 +18,49 @@
public class TransactionApiController implements TransactionsApi {

private final TransactionsService transactionsService;
private final TransactionAuditService transactionAuditService;
@Override
public ResponseEntity<TransactionStatusResponse> getTransactionStatusById(String dcbTransactionId) {
log.info("getTransactionStatus:: by id {} ", dcbTransactionId);
TransactionStatusResponse transactionStatusResponse;
try {
transactionStatusResponse = transactionsService.getTransactionStatusById(dcbTransactionId);
} catch (Exception ex) {
transactionAuditService.logErrorIfTransactionAuditExists(dcbTransactionId, ex.getMessage());
throw ex;
}

return ResponseEntity.status(HttpStatus.OK)
.body(transactionsService.getTransactionStatusById(dcbTransactionId));
.body(transactionStatusResponse);
}

@Override
public ResponseEntity<TransactionStatusResponse> createCirculationRequest(String dcbTransactionId, DcbTransaction dcbTransaction) {
log.info("createCirculationRequest:: creating dcbTransaction {} with id {} ", dcbTransaction, dcbTransactionId);
TransactionStatusResponse transactionStatusResponse;
try {
transactionStatusResponse = transactionsService.createCirculationRequest(dcbTransactionId, dcbTransaction);
} catch (Exception ex) {
transactionAuditService.logErrorIfTransactionAuditNotExists(dcbTransactionId, dcbTransaction, ex.getMessage());
throw ex;
}

return ResponseEntity.status(HttpStatus.CREATED)
.body(transactionsService.createCirculationRequest(dcbTransactionId, dcbTransaction));
.body(transactionStatusResponse);
}

@Override
public ResponseEntity<TransactionStatusResponse> updateTransactionStatus(String dcbTransactionId, TransactionStatus transactionStatus) {
log.info("updateTransactionStatus:: updating dcbTransaction with id {} to status {} ", dcbTransactionId, transactionStatus.getStatus());
TransactionStatusResponse transactionStatusResponse;
try {
transactionStatusResponse = transactionsService.updateTransactionStatus(dcbTransactionId, transactionStatus);
} catch (Exception ex) {
transactionAuditService.logErrorIfTransactionAuditExists(dcbTransactionId, ex.getMessage());
throw ex;
}

return ResponseEntity.status(HttpStatus.OK)
.body(transactionsService.updateTransactionStatus(dcbTransactionId, transactionStatus));
.body(transactionStatusResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class TransactionAuditEntity extends TransactionAuditableEntity {
@ColumnTransformer(write = "?::jsonb")
@Column(columnDefinition = "jsonb")
private String after;
private String errorMessage;

private String transactionId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -19,6 +20,7 @@
import org.folio.dcb.domain.dto.TransactionStatus.StatusEnum;
import org.folio.dcb.listener.entity.TransactionAuditEntityListener;

import java.io.Serializable;
import java.util.UUID;

@Entity
Expand All @@ -30,7 +32,7 @@
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TransactionEntity extends AuditableEntity {
public class TransactionEntity extends AuditableEntity implements Serializable {
@Id
private String id;
@Convert(converter = UUIDConverter.class)
Expand All @@ -40,19 +42,18 @@ public class TransactionEntity extends AuditableEntity {
private String servicePointId;
private String servicePointName;
private String materialType;
private String pickupLibraryName;
private String pickupLibraryCode;
private String lendingLibraryCode;
@Convert(converter = UUIDConverter.class)
private String patronId;
private String patronGroup;
private String patronBarcode;
private String borrowingLibraryCode;
private UUID requestId;
@Enumerated(EnumType.STRING)
private StatusEnum status;
@Enumerated(EnumType.STRING)
private DcbTransaction.RoleEnum role;

@Transient
protected TransactionEntity savedState;

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@ public TransactionEntity mapToEntity(String transactionId, DcbTransaction dcbTra
.servicePointId(pickup.getServicePointId())
.servicePointName(pickup.getServicePointName())
.pickupLibraryCode(pickup.getLibraryCode())
.pickupLibraryName(pickup.getLibraryName())
.patronBarcode(patron.getBarcode())
.patronId(patron.getId())
.patronGroup(patron.getGroup())
.borrowingLibraryCode(patron.getBorrowingLibraryCode())

.role(dcbTransaction.getRole())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,26 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PostPersist;
import jakarta.persistence.PostLoad;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.SerializationUtils;
import org.folio.dcb.domain.entity.TransactionAuditEntity;
import org.folio.dcb.domain.entity.TransactionEntity;
import org.folio.dcb.service.impl.TransactionsServiceImpl;
import org.folio.dcb.utils.BeanUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Component
@Log4j2
public class TransactionAuditEntityListener {
private static final String CREATE_ACTION = "CREATE";
private static final String UPDATE_ACTION = "UPDATE";

@Autowired
private BeanUtil beanUtil;
@Autowired
private ObjectMapper objectMapper;
private static final Map<String, TransactionEntity> originalStateCache = new HashMap<>();

@PrePersist
public void onPrePersist(Object entity) throws JsonProcessingException {
Expand All @@ -44,37 +38,25 @@ public void onPrePersist(Object entity) throws JsonProcessingException {
getEntityManager().persist(transactionAuditEntity);
}

@PostPersist
public void onPostPersist(Object entity) {
TransactionEntity transactionEntity = (TransactionEntity) entity;
originalStateCache.put(transactionEntity.getId(), transactionEntity);
}

@PreUpdate
public void onPreUpdate(Object entity) throws JsonProcessingException {
log.debug("onPreUpdate:: creating transaction audit record");
TransactionEntity transactionEntity = (TransactionEntity) entity;
TransactionAuditEntity transactionAuditEntity = new TransactionAuditEntity();
transactionAuditEntity.setBefore(objectMapper.writeValueAsString(getTransactionEntity(transactionEntity.getId())));
transactionAuditEntity.setBefore(objectMapper.writeValueAsString(transactionEntity.getSavedState()));
transactionAuditEntity.setAfter(objectMapper.writeValueAsString(transactionEntity));
transactionAuditEntity.setTransactionId(transactionEntity.getId());
transactionAuditEntity.setAction(UPDATE_ACTION);

log.info("onPreUpdate:: creating transaction audit record {} with action {}", transactionEntity.getId(), UPDATE_ACTION);
getEntityManager().persist(transactionAuditEntity);
originalStateCache.put(transactionEntity.getId(), transactionEntity);
}

private TransactionEntity getTransactionEntity(String transactionId) {
// Try to get the original state from the cache
TransactionEntity originalState = originalStateCache.get(transactionId);

// If not found, fetch it from the database
if (Objects.isNull(originalState)) {
originalState = getTransactionsService().getTransactionEntityOrThrow(transactionId);
originalStateCache.put(transactionId, originalState);
}
return originalState;
//This method will be invoked when the transactionEntity is loaded and the transactionEntity is stored in a transient field
//The stored value will be used in onPreUpdate method's setBefore method.
@PostLoad
public void saveState(TransactionEntity transactionEntity){
transactionEntity.setSavedState(SerializationUtils.clone((transactionEntity)));
}

//EntityListeners are instantiated by JPA, not Spring,
Expand All @@ -83,7 +65,4 @@ private EntityManager getEntityManager() {
return beanUtil.getBean(EntityManager.class);
}

private TransactionsServiceImpl getTransactionsService() {
return beanUtil.getBean(TransactionsServiceImpl.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.folio.dcb.repository;

import org.folio.dcb.domain.entity.TransactionAuditEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface TransactionAuditRepository extends JpaRepository<TransactionAuditEntity, String> {

@Query(value = "SELECT * FROM transactions_audit " +
"WHERE transaction_id = :trnId " +
"and created_date = (SELECT MAX(created_date) FROM transactions_audit WHERE transaction_id = :trnId);", nativeQuery = true)
Optional<TransactionAuditEntity> findLatestTransactionAuditEntityByDcbTransactionId(@Param("trnId") String trnId);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package org.folio.dcb.service;

import org.folio.dcb.domain.dto.CirculationItemRequest;
import org.folio.dcb.domain.dto.CirculationItem;
import org.folio.dcb.domain.dto.DcbItem;

public interface CirculationItemService {

void checkIfItemExistsAndCreate(DcbItem dcbTransaction, String pickupServicePointId);

CirculationItemRequest fetchItemById(String itemId);
CirculationItem fetchItemById(String itemId);

}
2 changes: 1 addition & 1 deletion src/main/java/org/folio/dcb/service/LibraryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public interface LibraryService {
* @param dcbTransaction - dcbTransaction Entity
* @return TransactionStatusResponse
*/
TransactionStatusResponse createCirculation(String dcbTransactionId, DcbTransaction dcbTransaction, String pickupServicePointId);
TransactionStatusResponse createCirculation(String dcbTransactionId, DcbTransaction dcbTransaction);

/**
* Update transaction status based on transactionEntity
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/org/folio/dcb/service/TransactionAuditService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.folio.dcb.service;

import org.folio.dcb.domain.dto.DcbTransaction;

public interface TransactionAuditService {

void logErrorIfTransactionAuditExists(String dcbTransactionId, String errorMsg);
void logErrorIfTransactionAuditNotExists(String dcbTransactionId, DcbTransaction dcbTransaction, String errorMsg);

}
12 changes: 6 additions & 6 deletions src/main/java/org/folio/dcb/service/impl/BaseLibraryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.ObjectUtils;
import org.folio.dcb.domain.dto.CirculationItemRequest;
import org.folio.dcb.domain.dto.CirculationItem;
import org.folio.dcb.domain.dto.CirculationRequest;
import org.folio.dcb.domain.dto.DcbTransaction;
import org.folio.dcb.domain.dto.TransactionStatus;
Expand Down Expand Up @@ -88,13 +88,13 @@ public void checkUserTypeAndThrowIfMismatch(String userType) {

public void updateStatusByTransactionEntity(TransactionEntity transactionEntity) {
log.debug("updateTransactionStatus:: Received checkIn event for itemId: {}", transactionEntity.getItemId());
CirculationItemRequest circulationItemRequest = circulationItemService.fetchItemById(transactionEntity.getItemId());
var circulationItemRequestStatus = circulationItemRequest.getStatus().getName();
if (OPEN == transactionEntity.getStatus() && AWAITING_PICKUP == circulationItemRequestStatus) {
CirculationItem circulationItem = circulationItemService.fetchItemById(transactionEntity.getItemId());
var circulationItemStatus = circulationItem.getStatus().getName();
if (OPEN == transactionEntity.getStatus() && AWAITING_PICKUP == circulationItemStatus) {
log.info(UPDATE_STATUS_BY_TRANSACTION_ENTITY_LOG_MESSAGE_PATTERN,
transactionEntity.getStatus().getValue(), TransactionStatus.StatusEnum.AWAITING_PICKUP.getValue());
updateTransactionEntity(transactionEntity, TransactionStatus.StatusEnum.AWAITING_PICKUP);
} else if (TransactionStatus.StatusEnum.AWAITING_PICKUP == transactionEntity.getStatus() && CHECKED_OUT == circulationItemRequestStatus) {
} else if (TransactionStatus.StatusEnum.AWAITING_PICKUP == transactionEntity.getStatus() && CHECKED_OUT == circulationItemStatus) {
log.info(UPDATE_STATUS_BY_TRANSACTION_ENTITY_LOG_MESSAGE_PATTERN,
transactionEntity.getStatus().getValue(), ITEM_CHECKED_OUT.getValue());
updateTransactionEntity(transactionEntity, ITEM_CHECKED_OUT);
Expand All @@ -104,7 +104,7 @@ public void updateStatusByTransactionEntity(TransactionEntity transactionEntity)
updateTransactionEntity(transactionEntity, TransactionStatus.StatusEnum.ITEM_CHECKED_IN);
} else {
log.info("updateStatusByTransactionEntity:: Item status is {}. So status of transaction is not updated",
circulationItemRequestStatus);
circulationItemStatus);
}

}
Expand Down
Loading

0 comments on commit 57822c0

Please sign in to comment.