View Javadoc
1   package org.bremersee.apiclient.webflux.contract.spring;
2   
3   import static java.util.Objects.nonNull;
4   
5   import java.lang.reflect.Method;
6   import java.util.Optional;
7   import java.util.function.Function;
8   import org.bremersee.apiclient.webflux.Invocation;
9   import org.bremersee.apiclient.webflux.InvocationParameter;
10  import org.springframework.core.ResolvableType;
11  import org.springframework.http.MediaType;
12  import org.springframework.util.MultiValueMap;
13  import org.springframework.web.reactive.function.BodyInserters;
14  import org.springframework.web.reactive.function.client.WebClient.RequestBodyUriSpec;
15  import org.springframework.web.reactive.function.client.WebClient.RequestHeadersUriSpec;
16  
17  /**
18   * The form data inserter.
19   *
20   * @author Christian Bremer
21   */
22  public class FormDataInserter extends SingleBodyInserter<MultiValueMap<String, ?>> {
23  
24    private Function<Invocation, Optional<MediaType>> contentTypeResolver = new ContentTypeResolver();
25  
26    /**
27     * With content type resolver.
28     *
29     * @param contentTypeResolver the content type resolver
30     * @return the form data inserter
31     */
32    public FormDataInserter withContentTypeResolver(
33        Function<Invocation, Optional<MediaType>> contentTypeResolver) {
34  
35      if (nonNull(contentTypeResolver)) {
36        this.contentTypeResolver = contentTypeResolver;
37      }
38      return this;
39    }
40  
41    @Override
42    public boolean canInsert(Invocation invocation) {
43      return isFormData(invocation) && super.canInsert(invocation);
44    }
45  
46    /**
47     * Is form data.
48     *
49     * @param invocation the invocation
50     * @return the boolean
51     */
52    protected boolean isFormData(Invocation invocation) {
53      return contentTypeResolver.apply(invocation)
54          .filter(contentType -> contentType.isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED))
55          .isPresent();
56    }
57  
58    @Override
59    protected MultiValueMap<String, ?> mapBody(InvocationParameter invocationParameter) {
60      //noinspection unchecked
61      return (MultiValueMap<String, ?>) invocationParameter.getValue();
62    }
63  
64    @Override
65    protected boolean isPossibleBodyValue(InvocationParameter invocationParameter) {
66      return invocationParameter.getValue() instanceof MultiValueMap && isString(invocationParameter);
67    }
68  
69    private boolean isString(InvocationParameter invocationParameter) {
70      Method method = invocationParameter.getMethod();
71      int index = invocationParameter.getIndex();
72      return Optional.of(ResolvableType.forMethodParameter(method, index))
73          .filter(resolvableType -> resolvableType.getGenerics().length >= 2)
74          .map(resolvableType -> resolvableType.resolveGeneric(0))
75          .filter(String.class::isAssignableFrom)
76          .isPresent();
77    }
78  
79    @Override
80    protected RequestHeadersUriSpec<?> insert(
81        MultiValueMap<String, ?> body,
82        RequestBodyUriSpec requestBodyUriSpec) {
83  
84      //noinspection rawtypes,unchecked
85      return (RequestHeadersUriSpec) requestBodyUriSpec
86          .body(BodyInserters.fromFormData((MultiValueMap<String, String>) body));
87    }
88  
89  }