Merge remote-tracking branch 'origin/2.2.x'

# Conflicts:
#	spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/client/InvoiceClient.java
#	spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/resource/InvoiceResource.java
This commit is contained in:
Olga MaciaszekSharma
2021-03-10 17:44:36 +01:00
5 changed files with 285 additions and 7 deletions

View File

@@ -22,6 +22,7 @@ import java.util.List;
import feign.Contract;
import feign.Feign;
import feign.Logger;
import feign.QueryMapEncoder;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
@@ -47,6 +48,7 @@ import org.springframework.cloud.openfeign.clientconfig.FeignClientConfigurer;
import org.springframework.cloud.openfeign.support.AbstractFormWriter;
import org.springframework.cloud.openfeign.support.FeignEncoderProperties;
import org.springframework.cloud.openfeign.support.PageableSpringEncoder;
import org.springframework.cloud.openfeign.support.PageableSpringQueryMapEncoder;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
@@ -67,6 +69,7 @@ import static feign.form.ContentType.MULTIPART;
* @author Darren Foong
* @author Jonatan Ivanov
* @author Olga Maciaszek-Sharma
* @author Hyeonmin Park
*/
@Configuration(proxyBeanMethods = false)
public class FeignClientsConfiguration {
@@ -119,6 +122,13 @@ public class FeignClientsConfiguration {
return encoder;
}
@Bean
@ConditionalOnClass(name = "org.springframework.data.domain.Pageable")
@ConditionalOnMissingBean
public QueryMapEncoder feignQueryMapEncoderPageable() {
return new PageableSpringQueryMapEncoder();
}
@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.openfeign.support;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import feign.querymap.BeanQueryMapEncoder;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
/**
* Provides support for encoding Pageable annotated as
* {@link org.springframework.cloud.openfeign.SpringQueryMap}.
*
* @author Hyeonmin Park
* @since 2.2.8
*/
public class PageableSpringQueryMapEncoder extends BeanQueryMapEncoder {
@Override
public Map<String, Object> encode(Object object) {
if (supports(object)) {
Map<String, Object> queryMap = new HashMap<>();
if (object instanceof Pageable) {
Pageable pageable = (Pageable) object;
if (pageable.isPaged()) {
queryMap.put("page", pageable.getPageNumber());
queryMap.put("size", pageable.getPageSize());
}
if (pageable.getSort() != null) {
applySort(queryMap, pageable.getSort());
}
}
else if (object instanceof Sort) {
Sort sort = (Sort) object;
applySort(queryMap, sort);
}
return queryMap;
}
else {
return super.encode(object);
}
}
private void applySort(Map<String, Object> queryMap, Sort sort) {
List<String> sortQueries = new ArrayList<>();
for (Sort.Order order : sort) {
sortQueries.add(order.getProperty() + "%2C" + order.getDirection());
}
if (!sortQueries.isEmpty()) {
queryMap.put("sort", sortQueries);
}
}
protected boolean supports(Object object) {
return object instanceof Pageable || object instanceof Sort;
}
}

View File

@@ -66,7 +66,6 @@ public class FeignPageableEncodingTests {
@Test
public void testPageable() {
// given
Pageable pageable = PageRequest.of(0, 10, Sort.Direction.ASC, "sortProperty");
@@ -85,7 +84,6 @@ public class FeignPageableEncodingTests {
assertThat(order.getDirection()).isEqualTo(Sort.Direction.ASC);
assertThat(order.getProperty()).isEqualTo("sortProperty");
}
}
@Test
@@ -112,7 +110,6 @@ public class FeignPageableEncodingTests {
Sort.Order order = optionalOrder.get();
assertThat(order.getDirection()).isEqualTo(Sort.Direction.DESC);
assertThat(order.getProperty()).isEqualTo("sortProperty");
}
@Test
@@ -143,7 +140,131 @@ public class FeignPageableEncodingTests {
Sort.Order secondOrder = orderList.get(1);
assertThat(secondOrder.getDirection()).isEqualTo(Sort.Direction.ASC);
assertThat(secondOrder.getProperty()).isEqualTo("sortProperty2");
}
@Test
public void testPageableWithoutSort() {
// given
Pageable pageable = PageRequest.of(0, 10);
// when
final ResponseEntity<Page<Invoice>> response = this.invoiceClient.getInvoicesPaged(pageable);
// then
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
assertThat(pageable.getPageSize()).isEqualTo(response.getBody().getSize());
assertThat(response.getBody().getPageable().getSort().isSorted()).isFalse();
List<Invoice> invoiceList = response.getBody().getContent();
assertThat(invoiceList).hasSizeGreaterThanOrEqualTo(1);
}
@Test
public void testPageableWithoutSortWithBody() {
// given
Pageable pageable = PageRequest.of(0, 10);
// when
final ResponseEntity<Page<Invoice>> response = this.invoiceClient.getInvoicesPagedWithBody(pageable,
"InvoiceTitleFromBody");
// then
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
assertThat(pageable.getPageSize()).isEqualTo(response.getBody().getSize());
List<Invoice> invoiceList = response.getBody().getContent();
assertThat(invoiceList).hasSizeGreaterThanOrEqualTo(1);
Invoice firstInvoice = invoiceList.get(0);
assertThat(firstInvoice.getTitle()).startsWith("InvoiceTitleFromBody");
}
@Test
public void testPageableWithBody() {
// given
Pageable pageable = PageRequest.of(0, 10,
Sort.by(Sort.Order.desc("sortProperty1"), Sort.Order.asc("sortProperty2")));
// when
final ResponseEntity<Page<Invoice>> response = this.invoiceClient.getInvoicesPagedWithBody(pageable,
"InvoiceTitleFromBody");
// then
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
assertThat(pageable.getPageSize()).isEqualTo(response.getBody().getSize());
List<Invoice> invoiceList = response.getBody().getContent();
assertThat(invoiceList).hasSizeGreaterThanOrEqualTo(1);
Invoice firstInvoice = invoiceList.get(0);
assertThat(firstInvoice.getTitle()).startsWith("InvoiceTitleFromBody");
Sort sort = response.getBody().getPageable().getSort();
assertThat(sort).hasSize(2);
List<Sort.Order> orderList = sort.toList();
assertThat(orderList).hasSize(2);
Sort.Order firstOrder = orderList.get(0);
assertThat(firstOrder.getDirection()).isEqualTo(Sort.Direction.DESC);
assertThat(firstOrder.getProperty()).isEqualTo("sortProperty1");
Sort.Order secondOrder = orderList.get(1);
assertThat(secondOrder.getDirection()).isEqualTo(Sort.Direction.ASC);
assertThat(secondOrder.getProperty()).isEqualTo("sortProperty2");
}
@Test
public void testUnpagedWithBody() {
// given
Pageable unpaged = Pageable.unpaged();
// when
final ResponseEntity<Page<Invoice>> response = this.invoiceClient.getInvoicesPagedWithBody(unpaged,
"InvoiceTitleFromBody");
// then
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
List<Invoice> invoiceList = response.getBody().getContent();
assertThat(invoiceList).hasSizeGreaterThanOrEqualTo(1);
Invoice firstInvoice = invoiceList.get(0);
assertThat(firstInvoice.getTitle()).startsWith("InvoiceTitleFromBody");
}
@Test
public void testSortWithBody() {
// given
Sort sort = Sort.by(Sort.Order.desc("amount"));
// when
final ResponseEntity<Page<Invoice>> response = this.invoiceClient.getInvoicesSortedWithBody(sort,
"InvoiceTitleFromBody");
// then
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
assertThat(sort).isEqualTo(response.getBody().getSort());
List<Invoice> invoiceList = response.getBody().getContent();
assertThat(invoiceList).hasSizeGreaterThanOrEqualTo(1);
Invoice firstInvoice = invoiceList.get(0);
assertThat(firstInvoice.getTitle()).startsWith("InvoiceTitleFromBody");
for (int ind = 0; ind < invoiceList.size() - 1; ind++) {
assertThat(invoiceList.get(ind).getAmount()).isGreaterThanOrEqualTo(invoiceList.get(ind + 1).getAmount());
}
}
@EnableFeignClients(clients = InvoiceClient.class)

View File

@@ -19,10 +19,12 @@ package org.springframework.cloud.openfeign.encoding.app.client;
import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
import org.springframework.data.domain.Page;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@@ -30,6 +32,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
* Simple Feign client for retrieving the invoice list.
*
* @author Jakub Narloch
* @author Hyeonmin Park
*/
@FeignClient("local")
public interface InvoiceClient {
@@ -37,6 +40,16 @@ public interface InvoiceClient {
@RequestMapping(value = "invoicesPaged", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<Page<Invoice>> getInvoicesPaged(org.springframework.data.domain.Pageable pageable);
@RequestMapping(value = "invoicesPagedWithBody", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<Page<Invoice>> getInvoicesPagedWithBody(
@SpringQueryMap org.springframework.data.domain.Pageable pageable, @RequestBody String titlePrefix);
@RequestMapping(value = "invoicesSortedWithBody", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<Page<Invoice>> getInvoicesSortedWithBody(@SpringQueryMap org.springframework.data.domain.Sort sort,
@RequestBody String titlePrefix);
@RequestMapping(value = "invoices", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<List<Invoice>> getInvoices();

View File

@@ -18,12 +18,14 @@ package org.springframework.cloud.openfeign.encoding.app.resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
@@ -35,6 +37,7 @@ import org.springframework.web.bind.annotation.RestController;
* An sample REST controller, that potentially returns large response - used for testing.
*
* @author Jakub Narloch
* @author Hyeonmin Park
*/
@RestController
public class InvoiceResource {
@@ -42,7 +45,7 @@ public class InvoiceResource {
@RequestMapping(value = "invoices", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Invoice>> getInvoices() {
return ResponseEntity.ok(createInvoiceList(100));
return ResponseEntity.ok(createInvoiceList(null, 100, null));
}
@RequestMapping(value = "invoices", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE,
@@ -54,18 +57,69 @@ public class InvoiceResource {
@RequestMapping(value = "invoicesPaged", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Page<Invoice>> getInvoicesPaged(org.springframework.data.domain.Pageable pageable) {
Page<Invoice> page = new PageImpl<>(createInvoiceList(pageable.getPageSize()), pageable, 100);
Page<Invoice> page = new PageImpl<>(createInvoiceList(null, pageable.getPageSize(), pageable.getSort()),
pageable, 100);
return ResponseEntity.ok(page);
}
private List<Invoice> createInvoiceList(int count) {
@RequestMapping(value = "invoicesPagedWithBody", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Page<Invoice>> getInvoicesPagedWithBody(org.springframework.data.domain.Pageable pageable,
@RequestBody String titlePrefix) {
Page<Invoice> page = new PageImpl<>(createInvoiceList(titlePrefix, pageable.getPageSize(), pageable.getSort()),
pageable, 100);
return ResponseEntity.ok(page);
}
@RequestMapping(value = "invoicesSortedWithBody", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Page<Invoice>> getInvoicesSortedWithBody(org.springframework.data.domain.Sort sort,
@RequestBody String titlePrefix) {
Page<Invoice> page = new PageImpl<>(createInvoiceList(titlePrefix, 100, sort), PageRequest.of(0, 100, sort),
100);
return ResponseEntity.ok(page);
}
private List<Invoice> createInvoiceList(String titlePrefix, int count, org.springframework.data.domain.Sort sort) {
if (titlePrefix == null) {
titlePrefix = "Invoice";
}
final List<Invoice> invoices = new ArrayList<>();
for (int ind = 0; ind < count; ind++) {
final Invoice invoice = new Invoice();
invoice.setTitle("Invoice " + (ind + 1));
invoice.setTitle(titlePrefix + " " + (ind + 1));
invoice.setAmount(new BigDecimal(String.format(Locale.US, "%.2f", Math.random() * 1000)));
invoices.add(invoice);
}
if (sort != null) {
Comparator<Invoice> comparatorForSort = null;
for (org.springframework.data.domain.Sort.Order order : sort) {
Comparator<Invoice> comparatorForOrder;
if (order.getProperty().equals("title")) {
comparatorForOrder = Comparator.comparing(Invoice::getTitle);
}
else if (order.getProperty().equals("amount")) {
comparatorForOrder = Comparator.comparing(Invoice::getAmount);
}
else {
continue;
}
if (order.isDescending()) {
comparatorForOrder = comparatorForOrder.reversed();
}
if (comparatorForSort == null) {
comparatorForSort = comparatorForOrder;
}
else {
comparatorForSort = comparatorForSort.thenComparing(comparatorForOrder);
}
}
if (comparatorForSort != null) {
invoices.sort(comparatorForSort);
}
}
return invoices;
}