<commit_before>package com.dottydingo.hyperion.service.persistence;

import com.dottydingo.hyperion.api.ApiObject;
import com.dottydingo.hyperion.exception.BadRequestException;
import com.dottydingo.hyperion.exception.NotFoundException;
import com.dottydingo.hyperion.exception.ValidationException;
import com.dottydingo.hyperion.service.configuration.ApiVersionPlugin;
import com.dottydingo.hyperion.service.configuration.EntityPlugin;
import com.dottydingo.hyperion.service.context.RequestContext;
import com.dottydingo.hyperion.service.model.PersistentObject;
import com.dottydingo.hyperion.service.query.Mapper;
import com.dottydingo.hyperion.service.query.PredicateBuilder;
import com.dottydingo.hyperion.service.query.RsqlPredicateBuilder;
import com.dottydingo.hyperion.service.sort.SortBuilder;
import com.dottydingo.hyperion.service.translation.Translator;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.domain.Specifications;
import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.springframework.core.GenericTypeResolver.resolveTypeArguments;

/**
 */
public class SpringJpaPersistenceOperations<C extends ApiObject, P extends PersistentObject<ID>, ID extends Serializable>
        implements PersistenceOperations<C,ID>
{
    private Sort defaultSort = new Sort("id");
    private HyperionJpaRepository<P,ID> jpaRepository;
    private RsqlPredicateBuilder predicateBuilder;
    private PersistenceFilter<P> persistenceFilter = new EmptyPersistenceFilter<P>();


    public void setJpaRepository(HyperionJpaRepository<P, ID> jpaRepository)
    {
        this.jpaRepository = jpaRepository;
    }

    public void setPredicateBuilder(RsqlPredicateBuilder predicateBuilder)
    {
        this.predicateBuilder = predicateBuilder;
    }

    public void setPersistenceFilter(PersistenceFilter<P> persistenceFilter)
    {
        this.persistenceFilter = persistenceFilter;
    }

    @Override
    @Transactional(readOnly = true)
    public List<C> findByIds(List<ID> ids, RequestContext context)
    {

        ApiVersionPlugin<C,P> apiVersionPlugin = context.getApiVersionPlugin();


        Iterable<P> iterable = jpaRepository.findAll(ids);

        List<P> result = new ArrayList<P>();
        for (P p : iterable)
        {
            if(persistenceFilter.isVisible(p,context))
                result.add(p);
        }

        return apiVersionPlugin.getTranslator().convertPersistent(result,context);
    }

    @Override
    @Transactional(readOnly = true)
    public QueryResult<C> query(String query, Integer start, Integer limit, String sort, RequestContext context)
    {
        ApiVersionPlugin<C,P> apiVersionPlugin = context.getApiVersionPlugin();

        int size = limit == null ? 500 : limit;
        int pageStart = start == null ? 1 : start;

        Pageable pageable = new RangePageAdapter(pageStart,size,getSort(context.getEntityPlugin(), sort));

        List<Specification<P>> specificationList = new ArrayList<Specification<P>>();
        if(query != null && query.length() > 0)
        {
            specificationList.add(new QuerySpecification(predicateBuilder, query, getDomainType()));
        }

        Specification<P> filter = persistenceFilter.getFilterSpecification(context);
        if(filter != null)
            specificationList.add(filter);

        Specifications<P> specification = null;
        for (Specification<P> spec : specificationList)
        {
            if(specification == null)
                specification = Specifications.where(spec);
            else
                specification = specification.and(spec);
        }

        Page<P> all = jpaRepository.findAll(specification,pageable);

        List<P> list = all.getContent();

        List<C> converted = apiVersionPlugin.getTranslator().convertPersistent(list,context);

        QueryResult<C> queryResult= new QueryResult<C>();
        queryResult.setItems(converted);
        queryResult.setResponseCount(list.size());
        queryResult.setTotalCount(all.getTotalElements());
        queryResult.setStart(start == null ? 1 : (start));

        return queryResult;
    }

    @Override
    @Transactional(readOnly = false)
    public C createItem(C clientObject, RequestContext context)
    {
        ApiVersionPlugin<C,P> apiVersionPlugin = context.getApiVersionPlugin();

        apiVersionPlugin.getValidator().validateCreate(clientObject);

        Translator<C,P> translator = apiVersionPlugin.getTranslator();
        P persistent = translator.convertClient(clientObject, context);

        if(!persistenceFilter.canCreate(persistent,context))
            return null;

        P saved = jpaRepository.save(persistent);
        C toReturn = translator.convertPersistent(saved,context);

        return toReturn;
    }

    @Override
    @Transactional(readOnly = false)
    public C updateItem(List<ID> ids, C item, RequestContext context)
    {
        ApiVersionPlugin<C,P> apiVersionPlugin = context.getApiVersionPlugin();

        Translator<C,P> translator = apiVersionPlugin.getTranslator();

        P existing = jpaRepository.findOne(ids.get(0));

        if(existing == null)
            throw new NotFoundException(
                    String.format("%s with id %s was not found.",context.getEntity(),item.getId()));

        apiVersionPlugin.getValidator().validateUpdate(item,existing);

        if(!persistenceFilter.canUpdate(existing,context))
        {
            return null;
        }

        // todo this needs a better implementation...
        ID oldId = existing.getId();

        translator.copyClient(item, existing,context);
<commit_msg>Issue #10 - Updating a non-existent item with no id in the payload results in an invalid error message.<commit_after>package com.dottydingo.hyperion.service.persistence;

import com.dottydingo.hyperion.api.ApiObject;
import com.dottydingo.hyperion.exception.BadRequestException;
import com.dottydingo.hyperion.exception.NotFoundException;
import com.dottydingo.hyperion.exception.ValidationException;
import com.dottydingo.hyperion.service.configuration.ApiVersionPlugin;
import com.dottydingo.hyperion.service.configuration.EntityPlugin;
import com.dottydingo.hyperion.service.context.RequestContext;
import com.dottydingo.hyperion.service.model.PersistentObject;
import com.dottydingo.hyperion.service.query.Mapper;
import com.dottydingo.hyperion.service.query.PredicateBuilder;
import com.dottydingo.hyperion.service.query.RsqlPredicateBuilder;
import com.dottydingo.hyperion.service.sort.SortBuilder;
import com.dottydingo.hyperion.service.translation.Translator;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.domain.Specifications;
import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.springframework.core.GenericTypeResolver.resolveTypeArguments;

/**
 */
public class SpringJpaPersistenceOperations<C extends ApiObject, P extends PersistentObject<ID>, ID extends Serializable>
        implements PersistenceOperations<C,ID>
{
    private Sort defaultSort = new Sort("id");
    private HyperionJpaRepository<P,ID> jpaRepository;
    private RsqlPredicateBuilder predicateBuilder;
    private PersistenceFilter<P> persistenceFilter = new EmptyPersistenceFilter<P>();


    public void setJpaRepository(HyperionJpaRepository<P, ID> jpaRepository)
    {
        this.jpaRepository = jpaRepository;
    }

    public void setPredicateBuilder(RsqlPredicateBuilder predicateBuilder)
    {
        this.predicateBuilder = predicateBuilder;
    }

    public void setPersistenceFilter(PersistenceFilter<P> persistenceFilter)
    {
        this.persistenceFilter = persistenceFilter;
    }

    @Override
    @Transactional(readOnly = true)
    public List<C> findByIds(List<ID> ids, RequestContext context)
    {

        ApiVersionPlugin<C,P> apiVersionPlugin = context.getApiVersionPlugin();


        Iterable<P> iterable = jpaRepository.findAll(ids);

        List<P> result = new ArrayList<P>();
        for (P p : iterable)
        {
            if(persistenceFilter.isVisible(p,context))
                result.add(p);
        }

        return apiVersionPlugin.getTranslator().convertPersistent(result,context);
    }

    @Override
    @Transactional(readOnly = true)
    public QueryResult<C> query(String query, Integer start, Integer limit, String sort, RequestContext context)
    {
        ApiVersionPlugin<C,P> apiVersionPlugin = context.getApiVersionPlugin();

        int size = limit == null ? 500 : limit;
        int pageStart = start == null ? 1 : start;

        Pageable pageable = new RangePageAdapter(pageStart,size,getSort(context.getEntityPlugin(), sort));

        List<Specification<P>> specificationList = new ArrayList<Specification<P>>();
        if(query != null && query.length() > 0)
        {
            specificationList.add(new QuerySpecification(predicateBuilder, query, getDomainType()));
        }

        Specification<P> filter = persistenceFilter.getFilterSpecification(context);
        if(filter != null)
            specificationList.add(filter);

        Specifications<P> specification = null;
        for (Specification<P> spec : specificationList)
        {
            if(specification == null)
                specification = Specifications.where(spec);
            else
                specification = specification.and(spec);
        }

        Page<P> all = jpaRepository.findAll(specification,pageable);

        List<P> list = all.getContent();

        List<C> converted = apiVersionPlugin.getTranslator().convertPersistent(list,context);

        QueryResult<C> queryResult= new QueryResult<C>();
        queryResult.setItems(converted);
        queryResult.setResponseCount(list.size());
        queryResult.setTotalCount(all.getTotalElements());
        queryResult.setStart(start == null ? 1 : (start));

        return queryResult;
    }

    @Override
    @Transactional(readOnly = false)
    public C createItem(C clientObject, RequestContext context)
    {
        ApiVersionPlugin<C,P> apiVersionPlugin = context.getApiVersionPlugin();

        apiVersionPlugin.getValidator().validateCreate(clientObject);

        Translator<C,P> translator = apiVersionPlugin.getTranslator();
        P persistent = translator.convertClient(clientObject, context);

        if(!persistenceFilter.canCreate(persistent,context))
            return null;

        P saved = jpaRepository.save(persistent);
        C toReturn = translator.convertPersistent(saved,context);

        return toReturn;
    }

    @Override
    @Transactional(readOnly = false)
    public C updateItem(List<ID> ids, C item, RequestContext context)
    {
        ApiVersionPlugin<C,P> apiVersionPlugin = context.getApiVersionPlugin();

        Translator<C,P> translator = apiVersionPlugin.getTranslator();

        P existing = jpaRepository.findOne(ids.get(0));

        if(existing == null)
            throw new NotFoundException(
                    String.format("%s with id %s was not found.",context.getEntity(),ids.get(0)));

        apiVersionPlugin.getValidator().validateUpdate(item,existing);

        if(!persistenceFilter.canUpdate(existing,context))
        {
            return null;
        }

        // todo this needs a better implementation...
        ID oldId = existing.getId();

        translator.copyClient(item, existing,context);

{'fraction_non_alphanumeric': 0.09832366215344938, 'fraction_numerical': 0.0013700838168923275, 'mean_word_length': 3.703942380591357, 'pattern_counts': {'":': 0, '<': 69, '<?xml version=': 0, '>': 71, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2149026', 'n_tokens_mistral': 3428, 'n_tokens_neox': 3327, 'n_words': 623}