EventRepositoryImpl.java

package com.tradecloud.repository.impl;

import com.tradecloud.authentication.MultiTenantUtil;
import com.tradecloud.authentication.User;
import com.tradecloud.domain.model.accounting.Snapshot;
import com.tradecloud.domain.model.events.*;
import com.tradecloud.domain.model.organisationalunit.OrganisationalUnit;
import com.tradecloud.dto.event.EventDTO;
import com.tradecloud.dto.event.EventsSearch;
import com.tradecloud.repository.EventRepository;
import com.tradecloud.repository.SearchMetaParams;
import com.tradecloud.repository.base.impl.RepositoryBaseImpl;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.query.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Event Repository.
 */
@Repository(value = "eventRepository")
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public class EventRepositoryImpl extends RepositoryBaseImpl<Event, EventsSearch> implements EventRepository {

    private static final long serialVersionUID = 1L;

    private static Logger logger = Logger.getLogger(EventRepositoryImpl.class);

    private final int DUPLICATE_EVENT_WITHIN_SECONDS = 20;  // 20 seconds

    @Override
    public void storeEvent(Event event) {
        saveOrUpdate(event);
    }

    @Override
    public void removeEvents(List<Long> eventIds) {
        List<Event> events = new ArrayList<Event>();
        for (Long eventId : eventIds) {
            events.add((Event) getCurrentSession().load(Event.class, eventId));
        }
        deleteAll(events);
    }

    @Override
    public List<OrderEvent> getOrderEvents() {
        return getAllEvents("findAllOrderEventsForUser");
    }

    @Override
    public List<CostingEvent> getCostingEvents() {
        return getAllEvents("findAllCostingEventsForUser");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<LogisticsEvent> getLogisticsEvents() {
        return getAllEvents("findAllLogisticEventsForUser");
    }

    @Override
    public List<CostingEvent> getCostingEvents(List<Long> eventIds) {
        return getEvents(eventIds, CostingEvent.class);
    }

    @Override
    public List<Event> getEvents(List<Long> eventIds) {
        return getEvents(eventIds, Event.class);
    }

    @Override
    public List<ProductEvent> getProductEvents() {
        return getAllEvents("findAllProductEventsForUser");
    }

    @Override
    public List<SuppliersEvent> getSupplierEvents() {
        return getAllEvents("findAllSupplierEventsForUser");
    }

    @Override
    public List<AccountingEvent> getAccountingEvents() {
        return getAllEvents("findAllAccountingEventsForUser");
    }

    @Override
    public List<AccountingEvent> getAccountingEvents(List<Long> eventIds) {
        return getEvents(eventIds, AccountingEvent.class);
    }

    @Override
    public Event getEvent(AccountingEvent.AccountingEventType accountingEventType, String reference) {
        return getEventByTypeAndReference("findEventByTypeAndReference", accountingEventType, reference);
    }

    @Override
    public List<DealEvent> getDealEvents() {
        String namedQuery = "findAllDealEventForUser";
        return getAllEvents(namedQuery);
    }

    private <T extends Event> List<T> getAllEvents(String namedQuery) {
        User user = MultiTenantUtil.getActiveUser();
        List<T> results =
                (List<T>) findByNamedQueryAndNamedParam(namedQuery, "consumerTag", user.getUsername());
        return results;
    }

    private Event getEventByTypeAndReference(String namedQuery,
                                             AccountingEvent.AccountingEventType accountingEventType,
                                             String reference) {
        List<Event> results =
                (List<Event>) getNamedQueryAndNamedParam(namedQuery,
                        new String[]{"eventType", "reference"}, new Object[]{accountingEventType.name(), reference});
        return results.isEmpty() ? null : results.get(0);
    }

    public List<DealEvent> getDealEvents(List<Long> eventIds) {
        return getEvents(eventIds, DealEvent.class);
    }

    private <T extends Event> List<T> getEvents(List<Long> eventIds, Class<T> aClass) {
        @SuppressWarnings("unchecked")
        List<T> dealEvents = getSessionCustom().createQuery("from " + aClass.getSimpleName() + " where id in (:eventIds)")
                .setParameterList("eventIds", eventIds).list();
        return dealEvents;
    }

    @Override
    public <T extends Event> long countByReferences(Set<String> referencesSet, Class<T> aClass) {
        long uniqueResult = (long) getSessionCustom().createQuery("select count(distinct e.reference) from " + aClass.getSimpleName() +
                        " e where reference in (:referencesSet)")
                .setParameterList("referencesSet", new ArrayList(referencesSet)).uniqueResult();
        return uniqueResult;
    }

//    @Override
//    public List<? extends Event> searchEvents(EventsSearch eventsSearch) {
//        DetachedCriteria searchCriteria =  DetachedCriteria.forClass(eventsSearch.getEventClass());
//        if (StringUtils.isNotEmpty(eventsSearch.getEventType())) {
//            searchCriteria.add(Restrictions.eq("eventType", eventsSearch.getEventType()));
//        }
//        if (eventsSearch.getReferences() != null && !eventsSearch.getReferences().isEmpty()) {
//            searchCriteria.add(Restrictions.in("reference", eventsSearch.getReferences()));
//        }
//        if (eventsSearch.getOrganisationalunit() != null && !eventsSearch.getOrganisationalunit().isEmpty()) {
//            searchCriteria.add(Restrictions.in("organisationalUnit", eventsSearch.getOrganisationalunit()));
//        }
//        if (eventsSearch.getConsumerTag() != null && !eventsSearch.getConsumerTag().isEmpty()) {
//            searchCriteria.add(Restrictions.in("consumerTag", eventsSearch.getConsumerTag()));
//        }
//      return   getExecutableCriteriaList(searchCriteria,eventsSearch.getSearchMetaParams());
//
//    }

    @Override
    public List<Event> search(EventsSearch eventsSearch) {
        CriteriaBuilder builder = getSessionCustom().getCriteriaBuilder();
        CriteriaQuery criteria = builder.createQuery(eventsSearch.getEventClass());
        Root root = criteria.from(eventsSearch.getEventClass());
        criteria.select(root);
        buidPredicates(eventsSearch, builder, criteria, root);
        SearchMetaParams metaParams = eventsSearch.getSearchMetaParams();
        criteria.orderBy(builder.desc(root.get("timestamp")));

        Query query = getSessionCustom().createQuery(criteria);
        if (metaParams != null && metaParams.isPaged()) {
            query.setFirstResult(metaParams.getRowIndex());
            query.setMaxResults(metaParams.getRowCount());
        }
        List resultList = query.getResultList();
        return resultList;
    }

    @Override
    public long count(EventsSearch eventsSearch) {
        CriteriaBuilder builder = getSessionCustom().getCriteriaBuilder();
        CriteriaQuery criteria = builder.createQuery(Long.class);
        Root root = criteria.from(eventsSearch.getEventClass());
        criteria.select(builder.count(root));
        buidPredicates(eventsSearch, builder, criteria, root);
        Query<Long> query = getSessionCustom().createQuery(criteria);
        return query.uniqueResult();
    }

    private void buidPredicates(EventsSearch eventsSearch, CriteriaBuilder builder, CriteriaQuery criteria, Root root) {
        List<Predicate> predicates = new ArrayList<>();
        if (StringUtils.isNotEmpty(eventsSearch.getEventType())) {
            predicates.add(builder.equal(root.get("eventType"), eventsSearch.getEventType()));
        }
        if (eventsSearch.getReferences() != null && !eventsSearch.getReferences().isEmpty()) {
            predicates.add(root.get("reference").in(eventsSearch.getReferences()));
        } else if (eventsSearch.getReference() != null && !eventsSearch.getReference().isEmpty()) {
            predicates.add(builder.like(builder.lower(root.get("reference")), "%" + eventsSearch.getReference().toLowerCase() + "%"));
        }
        if (eventsSearch.getOrganisationalunit() != null && !eventsSearch.getOrganisationalunit().isEmpty()) {
            predicates.add(root.get("organisationalUnit").in(eventsSearch.getOrganisationalunit()));
        }
        if (eventsSearch.getOrganisationalUnit() != null) {
            predicates.add(builder.equal(root.get("organisationalUnitId"), eventsSearch.getOrganisationalUnit().getId()));
        } else if (eventsSearch.getOrganisationalUnits() != null && !eventsSearch.getOrganisationalUnits().isEmpty()) {
            List<Long> collect = (List<Long>) eventsSearch.getOrganisationalUnits().stream()
                    .map(o -> ((OrganisationalUnit) o).getId()).collect(Collectors.toList());
            predicates.add(root.get("organisationalUnitId")
                    .in(collect));
        }
        if (eventsSearch.getConsumerTag() != null && !eventsSearch.getConsumerTag().isEmpty()) {
            predicates.add(builder.equal(root.get("consumerTag"), eventsSearch.getConsumerTag()));
        }
        if (eventsSearch.getReinstateUser() != null && !eventsSearch.getReinstateUser().isEmpty()) {
            predicates.add(builder.like(builder.lower(root.get("reinstateUser")), "%" + eventsSearch.getReinstateUser().toLowerCase() + "%"));
        }
        if (eventsSearch.getFrom() != null) {
            predicates.add(builder.greaterThanOrEqualTo(root.get("timestamp"), eventsSearch.getFrom()));
        }
        if (eventsSearch.getTo() != null) {
            predicates.add(builder.lessThanOrEqualTo(root.get("timestamp"), eventsSearch.getTo()));
        }
        if (eventsSearch.getStates() != null && !eventsSearch.getStates().isEmpty()) {
            predicates.add(root.get("state").in(eventsSearch.getStates()));
        } else if (StringUtils.isNotEmpty(eventsSearch.getState())) {
            predicates.add(builder.equal(root.get("state"), eventsSearch.getState()));
        }

        if (eventsSearch.getEventId() != null ) {
            predicates.add(builder.like(root.get("id").as(String.class), "%" + eventsSearch.getEventId()+ "%"));
        }

        if (eventsSearch.getPaymentId() != null ) {
            predicates.add(builder.equal(root.get("paymentId"), eventsSearch.getPaymentId()));
        }

        if (!predicates.isEmpty()) {
            Predicate[] toArray = predicates.stream().toArray(Predicate[]::new);
            criteria.where(toArray);
        }
    }

    @Override
    public void deleteDealExposure(String shipmentRef, String invoiceRef) {
        CriteriaBuilder builder = getSessionCustom().getCriteriaBuilder();
        CriteriaDelete criteriaDelete = builder.createCriteriaDelete(DealExposureEvent.class);
        final Root root = criteriaDelete.from(DealExposureEvent.class);
        criteriaDelete.where(builder.equal(root.get("shipmentReference"), shipmentRef),
                builder.equal(root.get("invoiceReference"), invoiceRef));
        Query query = getSessionCustom().createQuery(criteriaDelete);
        query.executeUpdate();
    }

    @Override
    public String findInvoiceExposureEventFailMessage(Long invoiceId) {
        return (String) getCurrentSession()
                .createNativeQuery("select e.notes from invoiceexposureevent c join event e on (e.id=c.id) " +
                        "where invoiceid=:invoiceId and state='Failed' order by e.timestamp desc limit 1;")
                .setParameter("invoiceId", invoiceId).uniqueResult();
    }

    @Override
    public String findDealExposureEventFailMessage(String invoiceRef, String shipRef) {
        return (String) getCurrentSession()
                .createNativeQuery("select e.notes from dealexposureevent c join event e on (e.id=c.id) " +
                        "where c.invoicereference=:invoicereference and c.shipmentreference=:shipmentreference  " +
                        " and state='Failed' order by e.timestamp desc limit 1;")
                .setParameter("invoicereference", invoiceRef).setParameter("shipmentreference", shipRef).uniqueResult();
    }

    @Override
    public List<String> findInvoiceExposureEventNotProcessed(Set<String> invoices, String shipmentRef) {
        return getCurrentSession()
                .createNativeQuery("select ci.reference from invoiceexposureevent e join event v on (v.id=e.id) " +
                        " join costsinvoice ci on (ci.id=e.invoiceid) where  v.state not in ('Consumed','Failed') " +
                        "and shipmentreference=:shipmentreference and ci.reference in (:invoices);")
                .setParameter("invoices", invoices).setParameter("shipmentreference", shipmentRef).list();
    }

    @Override
    public List<EventDTO> searchDTO(EventsSearch search) {
        CriteriaBuilder builder = getSessionCustom().getCriteriaBuilder();
        CriteriaQuery<EventDTO> criteria = builder.createQuery(search.getEventClass());
        Root root = criteria.from(search.getEventClass());
        Join<Event, Snapshot> snapshotJoin = root.join("snapshot", JoinType.LEFT);

        criteria.select(builder.construct(EventDTO.class, root.get("id"), root.get("timestamp"), root.get("reference"),
                root.get("organisationalUnit"), root.get("eventType"), root.get("state"), root.get("consumerTag"), root.get("reinstateUser"),
                root.get("consumedDate"), snapshotJoin.get("acknowledgeEventId"), root.get("notes")));

        //Expression<Class> localExp = builder.literal(root.getJavaType());
        //localExp.alias("instanceClass");

        buidPredicates(search, builder, criteria, root);
        SearchMetaParams metaParams = search.getSearchMetaParams();
        criteria.orderBy(builder.desc(root.get("timestamp")));

        //Query query = getSessionCustom().createQuery(criteria);
        TypedQuery<EventDTO> query = getSessionCustom().createQuery(criteria);
        if (metaParams != null && metaParams.isPaged()) {
            query.setFirstResult(metaParams.getRowIndex());
            query.setMaxResults(metaParams.getRowCount());
        }
        List resultList = query.getResultList();
        return resultList;
    }

    @Override
    public Long findLastEventId(String tableName, String reference, String evenType) {
        BigInteger bigInteger = (BigInteger) getCurrentSession()
                .createNativeQuery("select  e.id from " + tableName + " e join event v on (v.id=e.id) " +
                        " where  v.eventType =:evenType" +
                        " and v.reference=:reference order by v.created  desc limit 1;")
                .setParameter("evenType", evenType).setParameter("reference", reference).uniqueResult();
        return bigInteger!=null?bigInteger.longValue():null;
    }
}