Search code examples
javaspring-bootoption-type

Convert Logic of Optional into a lambda


I've written the following code, I tried to condense the code by writing a lambda using the .ifPresentOrElse() method of the Optional class, but I am not quite sure how to write my logic within the method. I am relatively new to streams, lambdas, and Java.

        /*Scheduled Job creates a question record for each external Id present within
         * our view, if user clicks on their experiment and scheduled job has not run yet
         * then they will receive a whitelabel page
         * What should happen:
         * if user attempts to view their experiment details and question record doesn't exist yet
         * a record of questions should be created upon API visit
         */
        //if question is already mapped to external id, return the question details
        Question questionObject;

        Optional<Question> questionOpt = questionRepository.findByExternalId(externalId);
        if (questionOpt.isPresent()) {
            questionObject = questionOpt.get();
        }
        else {
            //if not, make sure external id exists in view 
            Optional<SignalsExperimentCSView> expOpt = signalsExperimentCSViewRepository.findOneByExternalId(externalId);
            //call to check if record in view contains this external id
            if (expOpt.isPresent()) {
                // if present within the view create a record
                Question question = new Question();
                question.setExternalId(externalId);
                questionRepository.save(question);
                questionObject = question;
            } else {
                //if the above doesn't exist in view then return an experiment not found page
                //throw exception
                return new ResponseEntity<>(HttpStatus.NOT_FOUND);
            }
        }

Is it possible to write a lambda using the method .ifPresentOrElse() with my current logic?

EDIT I will post the whole class as the logic pertaining to the function might make more sense:

package com.pdb.testdbconn.service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import com.pdb.testdbconn.dto.ExperimentDataDto;
import com.pdb.testdbconn.dto.ExperimentDataDto.Compound;
import com.pdb.testdbconn.dto.ExperimentDetails;
import com.pdb.testdbconn.models.Question;
import com.pdb.testdbconn.repositories.ExperimentDetailsRepository;
import com.pdb.testdbconn.repositories.QuestionRepository;
import com.pdb.testdbconn.repositories.SignalsExperimentCSViewRepository;

@Service
public class ExperimentDetailsService {

    private static Logger logger = LoggerFactory.getLogger(ExperimentDetailsService.class);
    
    private ExperimentDetailsRepository experimentDetailsRepository;
    private QuestionRepository questionRepository;
    private SignalsExperimentCSViewRepository signalsExperimentCSViewRepository;

    public ExperimentDetailsService(ExperimentDetailsRepository experimentDetailsRepository,
            QuestionRepository questionRepository,
            SignalsExperimentCSViewRepository signalsExperimentCSViewRepository) {
        this.experimentDetailsRepository = experimentDetailsRepository;
        this.questionRepository = questionRepository;
        this.signalsExperimentCSViewRepository = signalsExperimentCSViewRepository;
    }


    public ResponseEntity<Object> getExperimentDetails(String externalId) {
        //custom query run within the repository
        List<ExperimentDetails> experimentDetails = experimentDetailsRepository.findByExternalId(externalId);
        //used for the custom response object
        Map<String, Object> responseObj = new HashMap<>();
        
        //create individual products from Experiment and map them into their own ds
        List<ExperimentDataDto.Compound> productDetails = experimentDetails.stream()   
        .map(experiment -> 
                {
                    //instantiate compounds to add as a list to each experiment record
                    Compound compound = new ExperimentDataDto.Compound();
                    compound.setName(experiment.getName());
                    compound.setControlledStatus(experiment.getControlledStatus());
                    compound.setJurisdictionName(experiment.getJurisdictionName());
                    compound.setResultComments(experiment.getResultComments());
                    compound.setCodeName(experiment.getCodeName());
                    compound.setCorporateId(experiment.getCorporateId()); 
                    return compound;
                }).distinct().collect(Collectors.toList());     

        logger.info("products: {}", productDetails);
        
        // Question question = questionRepository.findByExternalId(externalId).orElseThrow(null);

        
        /*Scheduled Job creates a question record for each external Id present within
         * our view, if user clicks on their experiment and scheduled job has not run yet
         * then will have whitelabel page
         * What should happen:
         * if user attempts to view their experiment details and question record doesn't exist yet
         * a record of questions should be created upon API visit
         */
        //if question is already mapped to external id, return the question details

        // Question questionObject;
      
        // Optional<Question> questionOpt = questionRepository.findByExternalId(externalId);
        // if (questionOpt.isPresent()) {
        //     questionObject = questionOpt.get();
        // }
        // else {
        //     //if not make sure external id exists in view 
        //     Optional<SignalsExperimentCSView> expOpt = signalsExperimentCSViewRepository.findOneByExternalId(externalId);
        //     //call to check if record in view contains this external id
        //     if (expOpt.isPresent()) {
        //         // if present within the view create a record
        //         Question question = new Question();
        //         question.setExternalId(externalId);
        //         questionRepository.save(question);
        //         questionObject = question;
        //     } else {
        //         //if the above doesn't exist in view then return an experiment not found page
        //         //throw exception
        //         return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        //     }
        // }
    

        //trying to refactor to logic above
        Question questionObject = questionRepository.findByExternalId(externalId)
                .or(() -> 
                    signalsExperimentCSViewRepository.findOneByExternalId(externalId)
                    .map(view -> {
                        Question question = new Question();
                        question.setExternalId(externalId);
                        questionRepository.save(question);
                        //I assume returning question is equivalent of setting quesitonObject
                        //to question
                        return question;
                    })

                    //wouldn't need .map(question -> ResponseEntity<>(question, HttpStatus.SUCCESS)) since logic is below
                    //in my response object
                    //receiving error on next line <> error on token 
                ).orElseThrow(ResponseEntity<>(HttpStatus.NOT_FOUND));
        
        logger.info("question: {}", questionObject);
        
        responseObj.put("externalID", experimentDetails.get(0).getExternalId());
        responseObj.put("isid", experimentDetails.get(0).getIsid());
        responseObj.put("products", productDetails);
        responseObj.put("question", questionObject);
        //add questions key/value
                        
        return new ResponseEntity<>(responseObj, HttpStatus.OK);
    }

}
        

Solution

  • You rather need the Optional.or method:

    return questionRepository.findByExternalId(externalId)
            .or(() ->
                    signalsExperimentCSViewRepository.findOneByExternalId(externalId)
                            .map(view -> {
                                Question question = new Question();
                                question.setExternalId(externalId);
                                questionRepository.save(question);
                                return question;
                            })
            )
            .map(question -> /* Map question to the ResponseEntity */)
            .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    

    or

    Question questionObject = questionRepository.findByExternalId(externalId)
            .or(() ->
                    signalsExperimentCSViewRepository.findOneByExternalId(externalId)
                            .map(view -> {
                                Question question = new Question();
                                question.setExternalId(externalId);
                                questionRepository.save(question);
                                return question;
                            })
            )
            .orElseThrow(() -> new HttpClientErrorException(HttpStatus.NOT_FOUND));
    
    /* Map questionObject to the ResponseEntity */