Code Junkie
Code Junkie

Reputation: 7788

Grails error handling in controller for loop

I'm iterating through an uploaded CSV file within my controller with a for loop. If any of my records contain errors, I need to be able to display the entire list of errors within the GSP and not save any of the records. If no errors are present, save the entire batch of records.

I'm struggling in grails to understand how to do this. Do you iterate the CSV file twice, the first time looking for errors, the second time to handle the db commit? Do I create a list of errors and send them back as a flash.message?

My Code

def uploadFile() {
    CsvToBean<EffortDetail> csvToBean = new CsvToBean<>();

    Map<String, String> columnMapping = new HashMap<String, String>();
    columnMapping.put("ExternalID", "alternateId");
    columnMapping.put("Full Name", "fullName");
    columnMapping.put("OrgKey", "orgkey");
    columnMapping.put("expenseDate", "expenseDate");
    columnMapping.put("projectHours", "projectHours");
    columnMapping.put("totalHours", "totalHours");

    HeaderColumnNameTranslateMappingStrategy<EffortDetail> strategy = new HeaderColumnNameTranslateMappingStrategy<EffortDetail>();
    strategy.setType(EffortDetail.class);
    strategy.setColumnMapping(columnMapping);

    MultipartFile file = request.getFile('file');

    if(file.empty) {
        flash.message = "File cannot be empty"
    } else {
        List<EffortDetail> effortDetails = null;
        CSVReader reader = new CSVReader(new InputStreamReader(file.getInputStream()));
        effortDetails = csvToBean.parse(strategy, reader);

        int count = 0;

        //iterate for errors
        for(EffortDetail effortDetail : effortDetails) {
            println "loop 1 " + count++

            def recoveryDetailInstance = recoveryDetailService.populate(effortDetail)
            // Test code to try and throw a list of flash messages
            flash.message = count++;
        }

        count = 0;

        //Iterate for commit
        for(EffortDetail effortDetail : effortDetails) {
            println "loop 2 " + count++

            def recoveryDetailInstance = recoveryDetailService.populate(effortDetail)
            recoveryDetailInstance.save(flush:true,failOnError:true)
        }
    }

    redirect (action:'upload')
}

.gsp

        <div class="col-md-12">
            <g:if test="${flash.message}">
                <div class="message" role="status">
                    ${flash.message}
                </div>
            </g:if>
            <g:eachError bean="${recoveryDetailInstance}">
                <li>${it}</li>
            </g:eachError>
            <g:uploadForm action="uploadFile">
                <span class="button"> <input type="file" name="file" /> <input
                    type="submit" class="upload" value="upload" />
                </span>
            </g:uploadForm>
        </div>

Upvotes: 0

Views: 373

Answers (1)

Tri
Tri

Reputation: 528

You need to look into transaction. Here's another Stackoverflow thread that I believe does exactly what you're looking for.

Grails - Saving multiple object, Rollback all object if one fails to save

The TLDR is to move your processing logic from the controller to a Service class method. Then combine your loop into a single loop that does both the validation and the domain save. If anything errors out you can return the list of failed domain objects after invoking the rollback.

Upvotes: 2

Related Questions