Marvin Froeder
2 years ago
committed by
GitHub
8 changed files with 466 additions and 0 deletions
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
Wikipedia Example with Springboot |
||||
================================= |
||||
|
||||
This is an example of advanced json response parsing, including pagination. |
@ -0,0 +1,118 @@
@@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!-- |
||||
|
||||
Copyright 2012-2022 The Feign 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 |
||||
|
||||
http://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. |
||||
|
||||
--> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<parent> |
||||
<groupId>io.github.openfeign</groupId> |
||||
<artifactId>parent</artifactId> |
||||
<version>12.0-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<groupId>io.github.openfeign</groupId> |
||||
<artifactId>feign-example-wikipedia-with-springboot</artifactId> |
||||
<packaging>jar</packaging> |
||||
<name>Wikipedia Example</name> |
||||
|
||||
<properties> |
||||
<main.basedir>${project.basedir}/..</main.basedir> |
||||
</properties> |
||||
|
||||
<dependencyManagement> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>org.springframework.cloud</groupId> |
||||
<artifactId>spring-cloud-dependencies</artifactId> |
||||
<version>2021.0.4</version> |
||||
<type>pom</type> |
||||
<scope>import</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
</dependencyManagement> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>io.github.openfeign</groupId> |
||||
<artifactId>feign-core</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>io.github.openfeign</groupId> |
||||
<artifactId>feign-gson</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.cloud</groupId> |
||||
<artifactId>spring-cloud-starter-openfeign</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.apache.commons</groupId> |
||||
<artifactId>commons-exec</artifactId> |
||||
<version>1.3</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
<build> |
||||
<defaultGoal>package</defaultGoal> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-maven-plugin</artifactId> |
||||
<configuration> |
||||
<mainClass>example.wikipedia.WikipediaApplication</mainClass> |
||||
<layout>ZIP</layout> |
||||
</configuration> |
||||
<executions> |
||||
<execution> |
||||
<goals> |
||||
<goal>repackage</goal> |
||||
</goals> |
||||
</execution> |
||||
</executions> |
||||
</plugin> |
||||
|
||||
<plugin> |
||||
<groupId>org.skife.maven</groupId> |
||||
<artifactId>really-executable-jar-maven-plugin</artifactId> |
||||
<version>1.5.0</version> |
||||
<configuration> |
||||
<programFile>wikipedia</programFile> |
||||
</configuration> |
||||
<executions> |
||||
<execution> |
||||
<phase>package</phase> |
||||
<goals> |
||||
<goal>really-executable-jar</goal> |
||||
</goals> |
||||
</execution> |
||||
</executions> |
||||
</plugin> |
||||
<plugin> |
||||
<groupId>org.apache.maven.plugins</groupId> |
||||
<artifactId>maven-failsafe-plugin</artifactId> |
||||
<version>${maven-surefire-plugin.version}</version> |
||||
<executions> |
||||
<execution> |
||||
<goals> |
||||
<goal>integration-test</goal> |
||||
<goal>verify</goal> |
||||
</goals> |
||||
</execution> |
||||
</executions> |
||||
</plugin> |
||||
</plugins> |
||||
</build> |
||||
</project> |
@ -0,0 +1,97 @@
@@ -0,0 +1,97 @@
|
||||
/* |
||||
* Copyright 2012-2022 The Feign 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 |
||||
* |
||||
* http://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 example.wikipedia; |
||||
|
||||
import com.google.gson.TypeAdapter; |
||||
import com.google.gson.stream.JsonReader; |
||||
import com.google.gson.stream.JsonWriter; |
||||
import java.io.IOException; |
||||
|
||||
abstract class ResponseAdapter<X> extends TypeAdapter<WikipediaClient.Response<X>> { |
||||
|
||||
/** |
||||
* name of the key inside the {@code query} dict which holds the elements desired. ex. {@code |
||||
* pages}. |
||||
*/ |
||||
protected abstract String query(); |
||||
|
||||
/** |
||||
* Parses the contents of a result object. |
||||
* <p/> |
||||
* <br> |
||||
* ex. If {@link #query()} is {@code pages}, then this would parse the value of each key in the |
||||
* dict {@code pages}. In the example below, this would first start at line {@code 3}. |
||||
* <p/> |
||||
* |
||||
* <pre> |
||||
* "pages": { |
||||
* "2576129": { |
||||
* "pageid": 2576129, |
||||
* "title": "Burchell's zebra", |
||||
* --snip-- |
||||
* </pre> |
||||
*/ |
||||
protected abstract X build(JsonReader reader) throws IOException; |
||||
|
||||
/** |
||||
* the wikipedia api doesn't use json arrays, rather a series of nested objects. |
||||
*/ |
||||
@Override |
||||
public WikipediaClient.Response<X> read(JsonReader reader) throws IOException { |
||||
WikipediaClient.Response<X> pages = new WikipediaClient.Response<X>(); |
||||
reader.beginObject(); |
||||
while (reader.hasNext()) { |
||||
String nextName = reader.nextName(); |
||||
if ("query".equals(nextName)) { |
||||
reader.beginObject(); |
||||
while (reader.hasNext()) { |
||||
if (query().equals(reader.nextName())) { |
||||
reader.beginObject(); |
||||
while (reader.hasNext()) { |
||||
// each element is in form: "id" : { object }
|
||||
// this advances the pointer to the value and skips the key
|
||||
reader.nextName(); |
||||
reader.beginObject(); |
||||
pages.add(build(reader)); |
||||
reader.endObject(); |
||||
} |
||||
reader.endObject(); |
||||
} else { |
||||
reader.skipValue(); |
||||
} |
||||
} |
||||
reader.endObject(); |
||||
} else if ("continue".equals(nextName)) { |
||||
reader.beginObject(); |
||||
while (reader.hasNext()) { |
||||
if ("gsroffset".equals(reader.nextName())) { |
||||
pages.nextOffset = reader.nextLong(); |
||||
} else { |
||||
reader.skipValue(); |
||||
} |
||||
} |
||||
reader.endObject(); |
||||
} else { |
||||
reader.skipValue(); |
||||
} |
||||
} |
||||
reader.endObject(); |
||||
return pages; |
||||
} |
||||
|
||||
@Override |
||||
public void write(JsonWriter out, WikipediaClient.Response<X> response) throws IOException { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
} |
@ -0,0 +1,83 @@
@@ -0,0 +1,83 @@
|
||||
/* |
||||
* Copyright 2012-2022 The Feign 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 |
||||
* |
||||
* http://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 example.wikipedia; |
||||
|
||||
import example.wikipedia.WikipediaClient.Page; |
||||
import example.wikipedia.WikipediaClient.Response; |
||||
import example.wikipedia.WikipediaClient.Wikipedia; |
||||
import java.util.Iterator; |
||||
import javax.annotation.PostConstruct; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
import org.springframework.cloud.openfeign.EnableFeignClients; |
||||
|
||||
@SpringBootApplication |
||||
@EnableFeignClients |
||||
public class WikipediaApplication { |
||||
|
||||
public static void main(String[] args) { |
||||
SpringApplication.run(WikipediaApplication.class, args).start();; |
||||
} |
||||
|
||||
@Autowired |
||||
private Wikipedia wikipedia; |
||||
|
||||
@PostConstruct |
||||
public void run() { |
||||
System.out.println("Let's search for PTAL!"); |
||||
Iterator<Page> pages = lazySearch(wikipedia, "PTAL"); |
||||
while (pages.hasNext()) { |
||||
System.out.println(pages.next().title); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* this will lazily continue searches, making new http calls as necessary. |
||||
* |
||||
* @param wikipedia used to search |
||||
* @param query see {@link Wikipedia#search(String)}. |
||||
*/ |
||||
static Iterator<Page> lazySearch(final Wikipedia wikipedia, final String query) { |
||||
final Response<Page> first = wikipedia.search(query); |
||||
if (first.nextOffset == null) { |
||||
return first.iterator(); |
||||
} |
||||
return new Iterator<Page>() { |
||||
Iterator<Page> current = first.iterator(); |
||||
Long nextOffset = first.nextOffset; |
||||
|
||||
@Override |
||||
public boolean hasNext() { |
||||
while (!current.hasNext() && nextOffset != null) { |
||||
System.out.println("Wow.. even more results than " + nextOffset); |
||||
Response<Page> nextPage = wikipedia.resumeSearch(query, nextOffset); |
||||
current = nextPage.iterator(); |
||||
nextOffset = nextPage.nextOffset; |
||||
} |
||||
return current.hasNext(); |
||||
} |
||||
|
||||
@Override |
||||
public Page next() { |
||||
return current.next(); |
||||
} |
||||
|
||||
@Override |
||||
public void remove() { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
/* |
||||
* Copyright 2012-2022 The Feign 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 |
||||
* |
||||
* http://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 example.wikipedia; |
||||
|
||||
import java.util.ArrayList; |
||||
import org.springframework.cloud.openfeign.FeignClient; |
||||
import org.springframework.web.bind.annotation.PathVariable; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RequestMethod; |
||||
|
||||
public class WikipediaClient { |
||||
|
||||
|
||||
|
||||
@FeignClient(value = "jplaceholder", url = "https://en.wikipedia.org/", |
||||
configuration = WikipediaClientConfiguration.class) |
||||
public static interface Wikipedia { |
||||
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, |
||||
value = "/w/api.php?action=query&continue=&generator=search&prop=info&format=json&gsrsearch={search}") |
||||
Response<Page> search(@PathVariable("search") String search); |
||||
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, |
||||
value = "/w/api.php?action=query&continue=&generator=search&prop=info&format=json&gsrsearch={search}&gsroffset={offset}") |
||||
Response<Page> resumeSearch(@PathVariable("search") String search, |
||||
@PathVariable("offset") long offset); |
||||
} |
||||
|
||||
static class Page { |
||||
|
||||
long id; |
||||
String title; |
||||
} |
||||
|
||||
public static class Response<X> extends ArrayList<X> { |
||||
|
||||
/** |
||||
* when present, the position to resume the list. |
||||
*/ |
||||
Long nextOffset; |
||||
} |
||||
} |
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
/* |
||||
* Copyright 2012-2022 The Feign 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 |
||||
* |
||||
* http://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 example.wikipedia; |
||||
|
||||
import com.google.gson.Gson; |
||||
import com.google.gson.GsonBuilder; |
||||
import com.google.gson.reflect.TypeToken; |
||||
import com.google.gson.stream.JsonReader; |
||||
import example.wikipedia.WikipediaClient.Page; |
||||
import example.wikipedia.WikipediaClient.Response; |
||||
import feign.codec.Decoder; |
||||
import feign.gson.GsonDecoder; |
||||
import java.io.IOException; |
||||
import org.springframework.context.annotation.Bean; |
||||
|
||||
public class WikipediaClientConfiguration { |
||||
|
||||
static ResponseAdapter<Page> pagesAdapter = new ResponseAdapter<Page>() { |
||||
|
||||
@Override |
||||
protected String query() { |
||||
return "pages"; |
||||
} |
||||
|
||||
@Override |
||||
protected Page build(JsonReader reader) throws IOException { |
||||
Page page = new Page(); |
||||
while (reader.hasNext()) { |
||||
String key = reader.nextName(); |
||||
if (key.equals("pageid")) { |
||||
page.id = reader.nextLong(); |
||||
} else if (key.equals("title")) { |
||||
page.title = reader.nextString(); |
||||
} else { |
||||
reader.skipValue(); |
||||
} |
||||
} |
||||
return page; |
||||
} |
||||
}; |
||||
|
||||
|
||||
@Bean |
||||
public Decoder decoder() { |
||||
Gson gson = new GsonBuilder() |
||||
.registerTypeAdapter(new TypeToken<Response<Page>>() {}.getType(), pagesAdapter) |
||||
.create(); |
||||
|
||||
return new GsonDecoder(gson); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
/* |
||||
* Copyright 2012-2022 The Feign 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 |
||||
* |
||||
* http://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 feign.example.wikipedia; |
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat; |
||||
import java.io.File; |
||||
import java.util.Arrays; |
||||
import org.apache.commons.exec.CommandLine; |
||||
import org.apache.commons.exec.DefaultExecutor; |
||||
import org.hamcrest.CoreMatchers; |
||||
import org.junit.Test; |
||||
|
||||
/** |
||||
* Run main for {@link WikipediaExampleIT} |
||||
*/ |
||||
public class WikipediaExampleIT { |
||||
|
||||
@Test |
||||
public void runMain() throws Exception { |
||||
final String jar = Arrays.stream(new File("target").listFiles()) |
||||
.filter(file -> file.getName().startsWith("feign-example-wikipedia-with-springboot") |
||||
&& file.getName().endsWith(".jar")) |
||||
.findFirst() |
||||
.map(File::getAbsolutePath) |
||||
.get(); |
||||
|
||||
final String line = "java -jar " + jar; |
||||
final CommandLine cmdLine = CommandLine.parse(line); |
||||
final int exitValue = new DefaultExecutor().execute(cmdLine); |
||||
|
||||
assertThat(exitValue, CoreMatchers.equalTo(0)); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue