degath
degath

Reputation: 1621

Get mapping with multiple params as once

I have get mapping like this:

@GetMapping(value = "/topics", params = "dateRange")
public Set<Integer> getTopicsInRange(@RequestParam DateRange dateRange) {
    return topicService.getTopicsInRange(dateRange);
}

And now I would've like to test it as:

private final static DateRange VALID_DATE_RANGE = new DateRange(LocalDate.of(2018, 1, 1), LocalDate.of(2018, 2, 2));

@Test
public void gettingTopicsInRangeShouldReturn200(){
    given().
    .when()
    .get(String.format("/topics?dateRange=%s", VALID_DATE_RANGE)
    .then()
    .statusCode(200);
}

Expected status code <200> doesn't match actual status code <400>.

I know that my solution would've work if I would've change my DateRange param into 2 separated params as:

@GetMapping(value = "/topics", params = {"begin", "end"})
public Set<Integer> getTopicsInRange(@RequestParam Date begin, @RequestParam Date end) {
    return topicService.getTopicsInRange(begin, end);
}

and then test it as /topics?begin=value&end=value2 but it's not what I'm looking for (in case of DateRange would've like 10 fields making 10 params would've be really excessive I think) Any ideas how can I solve my problem?

EDIT This is my DateRange class:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class DateRange {
    LocalDate begin;
    LocalDate end;
}

Upvotes: 1

Views: 8121

Answers (2)

Nick
Nick

Reputation: 3961

Add DateTimeFormat annotation:

public class DateRange {

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate begin;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate end;

    // getter and setters
}

Receive object in controller:

@GetMapping(value = "/topics")
public Set<Integer> getTopicsInRange(DateRange dateRange) {
    return topicService.getTopicsInRange(dateRange);
}

And send params separately:

@Test
public void gettingTopicsInRangeShouldReturn200() {
    given()
    .queryParams("begin", LocalDate.of(2018, 1, 1).format(ISO_DATE),
        "end", LocalDate.of(2018, 2, 2))
    .when()
    .get("/topics")
    .then()
    .statusCode(200);
}

Upvotes: 2

Nikolai  Shevchenko
Nikolai Shevchenko

Reputation: 7531

You need a way to convert your DateRange class to String and vice versa. Since you are using Spring, this could be done like this:

1) Add conversion logic

@Data
@AllArgsConstructor
@NoArgsConstructor
public class DateRange {

    LocalDate begin;
    LocalDate end;

    private static final String DELIMITER = "_";

    public static DateRange fromFormattedString(String source) {
        if (source != null) {
            String[] tokens = source.split(DELIMITER);
            if (tokens.length == 2) {
                return new DateRange(
                        LocalDate.parse(tokens[0]), // begin
                        LocalDate.parse(tokens[1])  // end
                );
            }
        }
        return null;
    }

    public String toFormattedString() {
        return begin + DELIMITER + end;
    }
}

2) Create Spring converter

import org.springframework.core.convert.converter.Converter;

public class DateRangeConverter implements Converter<String, DateRange> {

    @Override
    public DateRange convert(String source) {
        return DateRange.fromFormattedString(source);
    }
}

3) Register this converter

This will allow Spring to handle DateRange objects passed as @RequestParam-s

@Configuration
public class WebApiConfiguration extends WebMvcConfigurationSupport {
    ...
    @Override
    public FormattingConversionService mvcConversionService() {
        FormattingConversionService f = super.mvcConversionService();
        f.addConverter(new DateRangeConverter());
        return f;
    }
    ...
}

And finally use like:

.get(String.format("/topics?dateRange=%s", VALID_DATE_RANGE.toFormattedString())

or (with raw string):

.get(String.format("/topics?dateRange=%s", "2018-1-1_2018-2-2")

BUT:

Nonetheless, I'd recommend you to use separate request params (begin, end, etc) even if there are 10 of them because it's:

1) RESTful way

2) error-proof, since the order request params are passed in, is not strict. On the other hand, packing params into single object will force you to keep an eye on params order. Moreover you must pack all params in string, so no optional params are allowed, otherwise unpacking logic could be broken

Upvotes: 0

Related Questions