Skip to content

Commit ac5af83

Browse files
committed
Add ReactiveDecoder
1 parent a7d8b92 commit ac5af83

File tree

3 files changed

+74
-38
lines changed

3 files changed

+74
-38
lines changed

core/src/main/java/feign/Types.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,10 @@ public static Type resolveLastTypeParameter(Type genericContext, Class<?> supert
357357
return types[types.length - 1];
358358
}
359359

360+
public static ParameterizedType parameterize(Class<?> rawClass, Type... typeArguments) {
361+
return new ParameterizedTypeImpl(rawClass.getEnclosingClass(), rawClass, typeArguments);
362+
}
363+
360364
static final class ParameterizedTypeImpl implements ParameterizedType {
361365

362366
private final Type ownerType;

reactive/README.md

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,34 @@ implementation to your classpath. Then configure Feign to use the reactive stre
1414
public interface GitHubReactor {
1515

1616
@RequestLine("GET /repos/{owner}/{repo}/contributors")
17-
Flux<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
17+
Flux<Contributor> contributorsFlux(@Param("owner") String owner, @Param("repo") String repo);
18+
19+
@RequestLine("GET /repos/{owner}/{repo}/contributors")
20+
Mono<List<Contributor>> contributorsMono(@Param("owner") String owner, @Param("repo") String repo);
1821

1922
class Contributor {
20-
String login;
21-
22-
public Contributor(String login) {
23-
this.login = login;
24-
}
23+
String login;
24+
25+
public String getLogin() {
26+
return login;
27+
}
28+
29+
public void setLogin(String login) {
30+
this.login = login;
31+
}
2532
}
2633
}
2734

2835
public class ExampleReactor {
2936
public static void main(String args[]) {
30-
GitHubReactor gitHub = ReactorFeign.builder()
37+
GitHubReactor gitHub = ReactorFeign.builder()
38+
.decoder(new ReactiveDecoder(new JacksonDecoder()))
3139
.target(GitHubReactor.class, "https://api.github.com");
3240

33-
List<Contributor> contributors = gitHub.contributors("OpenFeign", "feign")
34-
.collect(Collectors.toList())
41+
List<GitHubReactor.Contributor> contributorsFromFlux = gitHub.contributorsFlux("OpenFeign", "feign")
42+
.collectList()
43+
.block();
44+
List<GitHubReactor.Contributor> contributorsFromMono = gitHub.contributorsMono("OpenFeign", "feign")
3545
.block();
3646
}
3747
}
@@ -79,33 +89,5 @@ the wrapped in the appropriate reactive wrappers.
7989
### Iterable and Collections responses
8090

8191
Due to the Synchronous nature of Feign requests, methods that return `Iterable` types must specify the collection
82-
in the `Publisher`. For `Reactor` types, this limits the use of `Flux` as a response type. If you
83-
want to use `Flux`, you will need to manually convert the `Mono` or `Iterable` response types into
84-
`Flux` using the `fromIterable` method.
85-
92+
in the `Publisher`. For `Reactor` types, this limits the use of `Flux` as a response type.
8693

87-
```java
88-
public interface GitHub {
89-
90-
@RequestLine("GET /repos/{owner}/{repo}/contributors")
91-
Mono<List<Contributor>> contributors(@Param("owner") String owner, @Param("repo") String repo);
92-
93-
class Contributor {
94-
String login;
95-
96-
public Contributor(String login) {
97-
this.login = login;
98-
}
99-
}
100-
}
101-
102-
public class ExampleApplication {
103-
public static void main(String[] args) {
104-
GitHub gitHub = ReactorFeign.builder()
105-
.target(GitHub.class, "https://api.github.com");
106-
107-
Mono<List<Contributor>> contributors = gitHub.contributors("OpenFeign", "feign");
108-
Flux<Contributor> contributorFlux = Flux.fromIterable(contributors.block());
109-
}
110-
}
111-
```
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2012-2023 The Feign Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package feign.reactive;
15+
16+
import feign.FeignException;
17+
import feign.Response;
18+
import feign.Types;
19+
import feign.codec.Decoder;
20+
import reactor.core.publisher.Flux;
21+
import reactor.core.publisher.Mono;
22+
23+
import java.io.IOException;
24+
import java.lang.reflect.Type;
25+
import java.util.List;
26+
27+
public class ReactiveDecoder implements Decoder {
28+
29+
private final Decoder delegate;
30+
31+
public ReactiveDecoder(Decoder decoder) {
32+
this.delegate = decoder;
33+
}
34+
35+
@Override
36+
public Object decode(Response response, Type type) throws IOException, FeignException {
37+
Class<?> rawType = Types.getRawType(type);
38+
if (rawType.isAssignableFrom(Mono.class)) {
39+
Type lastType = Types.resolveLastTypeParameter(type, Mono.class);
40+
return Mono.just(delegate.decode(response, lastType));
41+
}
42+
if (rawType.isAssignableFrom(Flux.class)) {
43+
Type lastType = Types.resolveLastTypeParameter(type, Flux.class);
44+
Type listType = Types.parameterize(List.class, lastType);
45+
return Flux.fromIterable((Iterable)delegate.decode(response, listType));
46+
}
47+
48+
return delegate.decode(response, type);
49+
}
50+
}

0 commit comments

Comments
 (0)