ChuckNorris
Test app that invokes a ChuckNorris random joke API
Structure
pom.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <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/xsd/maven-4.0.0.xsd">
3 <modelVersion>4.0.0</modelVersion>
4 <groupId>bitarus.allowed.org</groupId>
5 <artifactId>chucknorris</artifactId>
6 <version>0.1.0</version>
7 <parent>
8 <groupId>org.springframework.boot</groupId>
9 <artifactId>spring-boot-starter-parent</artifactId>
10 <version>2.1.6.RELEASE</version>
11 </parent>
12 <dependencies>
13 <dependency>
14 <groupId>org.springframework.boot</groupId>
15 <artifactId>spring-boot-starter-thymeleaf</artifactId>
16 </dependency>
17 <dependency>
18 <groupId>org.springframework.boot</groupId>
19 <artifactId>spring-boot-starter-web</artifactId>
20 </dependency>
21 <dependency>
22 <groupId>com.google.code.gson</groupId>
23 <artifactId>gson</artifactId>
24 <version>2.8.6</version>
25 <scope>compile</scope>
26 </dependency>
27 </dependencies>
28 <properties>
29 <start-class>chucknorris.bitarus.allowed.org.Application</start-class>
30 </properties>
31 <build>
32 <plugins>
33 <plugin>
34 <groupId>org.springframework.boot</groupId>
35 <artifactId>spring-boot-maven-plugin</artifactId>
36 </plugin>
37 </plugins>
38 </build>
39 <repositories>
40 <repository>
41 <id>spring-milestone</id>
42 <url>http://repo.spring.io/libs-release</url>
43 </repository>
44 </repositories>
45 <pluginRepositories>
46 <pluginRepository>
47 <id>spring-milestone</id>
48 <url>http://repo.spring.io/libs-release</url>
49 </pluginRepository>
50 </pluginRepositories>
51 </project>
Application.java
1 package chucknorris.bitarus.allowed.org;
2
3 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
4 import org.springframework.boot.SpringApplication;
5 import org.springframework.context.annotation.ComponentScan;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8
9 @ComponentScan //scans for @Component beans
10 @EnableAutoConfiguration
11 public class Application {
12 private static Logger logger;
13
14 public static void main(String[] args) {
15 logger = LoggerFactory.getLogger(Application.class);
16 logger.info("Starting application");
17 SpringApplication.run(Application.class, args);
18 }
19 }
ChuckNorrisController.java
1 package chucknorris.bitarus.allowed.org;
2
3 import org.springframework.stereotype.Controller;
4 import org.springframework.web.bind.annotation.RequestMapping;
5 import org.springframework.ui.Model;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8 import org.springframework.web.bind.annotation.ResponseBody;
9
10 import java.io.BufferedReader;
11 import java.io.InputStreamReader;
12 import java.io.OutputStream;
13 import java.net.HttpURLConnection;
14 import java.net.URL;
15 import org.springframework.http.HttpMethod;
16 import com.google.gson.Gson;
17 import java.util.stream.Stream;
18 import java.util.stream.Collectors;
19 import java.util.List;
20 import java.util.ArrayList;
21
22 @Controller
23 public class ChuckNorrisController{
24 private final Logger logger = LoggerFactory.getLogger(ChuckNorrisController.class);
25
26 public ChuckNorrisController(){
27 logger.info("ChuckNorrisController created");
28 }
29
30 @RequestMapping("/chucknorris")
31 @ResponseBody
32 // http://localhost:8080/chucknorris
33 public JokeResponse chucknorris() {
34 String ret = "";
35 Gson gson = new Gson();
36
37 try {
38 URL url = new URL("https://api.chucknorris.io/jokes/random");
39 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
40 connection.connect();
41 logger.info( Integer.toString(connection.getResponseCode() ));
42
43 try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"))) {
44 StringBuilder response = new StringBuilder();
45 String responseLine = null;
46 while ((responseLine = br.readLine()) != null) {
47 response.append(responseLine.trim());
48 }
49
50 Joke joke = gson.fromJson(response.toString(), Joke.class);
51
52
53 List<Joke> jokesList = new ArrayList<Joke>();
54 jokesList.add(joke);
55 List<Joke> jokesList2 = jokesList.stream().map( item ->{item.setValue(item.getValue().toUpperCase()); return item;} ).collect(Collectors.toList());
56 joke = jokesList2.get(0);
57 ret = joke.getValue();
58 }
59
60 }
61 catch(Exception ex){
62 logger.error("error",ex);
63 }
64
65 JokeResponse jr = new JokeResponse();
66 jr.setResponse(ret);
67 return jr;
68 }
69 }
Joke.java
1 package chucknorris.bitarus.allowed.org;
2 import com.google.gson.annotations.SerializedName;
3
4 public class Joke{
5
6 @SerializedName("categories")
7 private String[] categories;
8 @SerializedName("created_at")
9 private String createdAt;
10 @SerializedName("icon_url")
11 private String iconUrl;
12 @SerializedName("id")
13 private String id;
14 @SerializedName("updated_at")
15 private String updatedAt;
16 @SerializedName("url")
17 private String url;
18 @SerializedName("value")
19 private String value;
20
21 public String[] getCategories(){
22 return this.categories;
23 }
24
25 public String getCreatedAt(){
26 return this.createdAt;
27 }
28
29 public String getIconUrl(){
30 return this.iconUrl;
31 }
32
33 public String getId(){
34 return this.id;
35 }
36
37 public String getUpdatedAt(){
38 return this.updatedAt;
39 }
40
41 public String getUrl(){
42 return this.url;
43 }
44
45 public String getValue(){
46 return this.value;
47 }
48
49 public void setCategories(String[] categories){
50 this.categories = categories;
51 }
52
53 public void setCreatedAt(String createdAt ){
54 this.createdAt = createdAt;
55 }
56
57 public void setIconUrl(String iconUrl){
58 this.iconUrl=iconUrl;
59 }
60
61 public void setId(String id){
62 this.id=id;
63 }
64
65 public void setUpdatedAt(String updatedAt){
66 this.updatedAt=updatedAt;
67 }
68
69 public void setUrl(String url){
70 this.url=url;
71 }
72
73 public void setValue(String value){
74 this.value=value;
75 }
76 }
JokeResponse.java
1 package chucknorris.bitarus.allowed.org;
2 import com.google.gson.annotations.SerializedName;
3
4 public class JokeResponse{
5
6 @SerializedName("response")
7 private String response;
8
9 public String getResponse(){
10 return this.response;
11 }
12
13 public void setResponse(String response){
14 this.response = response;
15 }
16 }
curl call
1 curl https://api.chucknorris.io/jokes/random 2>/dev/null | json_reformat
2 {
3 "categories": [
4
5 ],
6 "created_at": "2020-01-05 13:42:26.991637",
7 "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
8 "id": "H-fvHao6QmiQxunoX2llBg",
9 "updated_at": "2020-01-05 13:42:26.991637",
10 "url": "https://api.chucknorris.io/jokes/H-fvHao6QmiQxunoX2llBg",
11 "value": "A doctor once tried to test Chuck Norris' reflexes by lightly tapping on his knee with a small wooden mallet. Needless to say, that doctor is now in orbit."
12 }
== Example 0.2 ==
Structure
1 .
2 ├── pom.xml
3 ├── src
4 │ └── main
5 │ ├── java
6 │ │ └── com
7 │ │ └── mooo
8 │ │ └── bitarus
9 │ │ └── chucknorris
10 │ │ ├── Application.java
11 │ │ ├── ChuckNorrisController.java
12 │ │ ├── JokeEntity.java
13 │ │ ├── Joke.java
14 │ │ ├── JokeMapper.java
15 │ │ ├── JokeProjection.java
16 │ │ ├── JokeRepository.java
17 │ │ ├── JokeResponse.java
18 │ │ └── QueryDSLConfiguration.java
19 │ └── resources
20 │ └── application.yaml
pom.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <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/xsd/maven-4.0.0.xsd">
3 <modelVersion>4.0.0</modelVersion>
4 <groupId>com.mooo.bitarus</groupId>
5 <artifactId>chucknorris</artifactId>
6 <version>0.2.0</version>
7 <parent>
8 <groupId>org.springframework.boot</groupId>
9 <artifactId>spring-boot-starter-parent</artifactId>
10 <version>3.2.5</version>
11 </parent>
12 <dependencies>
13 <dependency>
14 <groupId>org.mapstruct</groupId>
15 <artifactId>mapstruct</artifactId>
16 <version>${mapstruct.version}</version>
17 </dependency>
18 <dependency>
19 <groupId>org.springdoc</groupId>
20 <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
21 <version>2.5.0</version>
22 </dependency>
23 <dependency>
24 <groupId>org.springframework.boot</groupId>
25 <artifactId>spring-boot-starter-thymeleaf</artifactId>
26 </dependency>
27 <dependency>
28 <groupId>org.springframework.boot</groupId>
29 <artifactId>spring-boot-starter-web</artifactId>
30 </dependency>
31 <dependency>
32 <groupId>org.springframework.boot</groupId>
33 <artifactId>spring-boot-starter-data-jpa</artifactId>
34 </dependency>
35 <dependency>
36 <groupId>org.hsqldb</groupId>
37 <artifactId>hsqldb</artifactId>
38 <version>2.7.2</version>
39 </dependency>
40 <dependency>
41 <groupId>com.querydsl</groupId>
42 <artifactId>querydsl-jpa</artifactId>
43 <version>${querydsl.version}</version>
44 <classifier>jakarta</classifier>
45 </dependency>
46 <dependency>
47 <groupId>com.querydsl</groupId>
48 <artifactId>querydsl-apt</artifactId>
49 <version>${querydsl.version}</version>
50 <classifier>jakarta</classifier>
51 </dependency>
52 </dependencies>
53 <properties>
54 <java.version>21</java.version>
55 <start-class>com.mooo.bitarus.chucknorris.Application</start-class>
56 <querydsl.version>5.1.0</querydsl.version>
57 <mapstruct.version>1.5.5.Final</mapstruct.version>
58 </properties>
59 <build>
60 <plugins>
61 <plugin>
62 <groupId>org.springframework.boot</groupId>
63 <artifactId>spring-boot-maven-plugin</artifactId>
64 </plugin>
65 <plugin>
66 <groupId>org.apache.maven.plugins</groupId>
67 <artifactId>maven-compiler-plugin</artifactId>
68 <version>3.11.0</version>
69 <configuration>
70 <annotationProcessorPaths>
71 <path>
72 <groupId>org.mapstruct</groupId>
73 <artifactId>mapstruct-processor</artifactId>
74 <version>${mapstruct.version}</version>
75 </path>
76 <path>
77 <groupId>com.querydsl</groupId>
78 <artifactId>querydsl-apt</artifactId>
79 <version>${querydsl.version}</version>
80 <classifier>jakarta</classifier>
81 </path>
82 <path>
83 <groupId>jakarta.persistence</groupId>
84 <artifactId>jakarta.persistence-api</artifactId>
85 <version>3.1.0</version>
86 </path>
87 </annotationProcessorPaths>
88 </configuration>
89 </plugin>
90 </plugins>
91 </build>
92 </project>
src/main/resources/application.yaml
1 server:
2 port: 8181
3 chucknorris:
4 url: https://api.chucknorris.io/jokes/random
5 springdoc:
6 swagger-ui:
7 enabled: true
8 # http://localhost:8181/swagger-ui/index.html
9 # http://localhost:8181/v3/api-docs.yaml
10 # https://docs.swagger.io/swagger-core/v2.0.0-RC3/apidocs/io/swagger/v3/oas/annotations/Operation.html
11 spring:
12 datasource:
13 url: jdbc:hsqldb:mem:mydb
14 username: sa
15 password: sa
src/main/java/com/mooo/bitarus/chucknorris/Application.java
1 package com.mooo.bitarus.chucknorris;
2
3 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
4 import org.springframework.boot.SpringApplication;
5 import org.springframework.context.annotation.ComponentScan;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8
9 @ComponentScan //scans for @Component beans
10 @EnableAutoConfiguration
11 public class Application {
12 private static Logger logger;
13
14 public static void main(String[] args) {
15 logger = LoggerFactory.getLogger(Application.class);
16 logger.info("Starting application");
17 SpringApplication.run(Application.class, args);
18 }
19 }
src/main/java/com/mooo/bitarus/chucknorris/ChuckNorrisController.java
1 package com.mooo.bitarus.chucknorris;
2
3 import org.springframework.stereotype.Controller;
4 import org.springframework.web.bind.annotation.GetMapping;
5 import org.springframework.ui.Model;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8 import org.springframework.web.bind.annotation.ResponseBody;
9
10 import java.io.BufferedReader;
11 import java.io.InputStreamReader;
12 import java.io.OutputStream;
13 import java.net.HttpURLConnection;
14 import java.net.URL;
15 import org.springframework.http.HttpMethod;
16 import com.fasterxml.jackson.databind.ObjectMapper;
17 import java.util.stream.Stream;
18 import java.util.stream.Collectors;
19 import java.util.List;
20 import java.util.ArrayList;
21 import org.springframework.beans.factory.annotation.Value;
22 import io.swagger.v3.oas.annotations.Operation;
23 import io.swagger.v3.oas.annotations.tags.Tag;
24 import com.querydsl.jpa.impl.JPAQueryFactory;
25
26 @Tag(name="CNJokeController")
27 @Controller
28 public class ChuckNorrisController{
29 private final Logger logger = LoggerFactory.getLogger(ChuckNorrisController.class);
30
31 @Value("${chucknorris.url}")
32 private String chuckNorrisURL;
33 private ObjectMapper objectMapper;
34 private JokeRepository repo;
35 private JPAQueryFactory queryFactory;
36 private JokeMapper jokeMapper;
37
38 public ChuckNorrisController(JokeRepository repo, JPAQueryFactory queryFactory,
39 JokeMapper jokeMapper){
40 logger.info("ChuckNorrisController created");
41 this.objectMapper = new ObjectMapper();
42 this.repo = repo;
43 this.queryFactory = queryFactory;
44 this.jokeMapper = jokeMapper;
45 }
46
47 @GetMapping("/chucknorris")
48 @ResponseBody
49 @Operation(summary="Gets joke", description="Gets a Chuck Norris random joke.")
50 // http://localhost:8080/chucknorris
51 public JokeResponse chucknorris() {
52 String ret = "";
53 Joke joke=null;
54
55 try {
56 URL url = new URL(chuckNorrisURL);
57 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
58 connection.connect();
59 logger.info( Integer.toString(connection.getResponseCode() ));
60
61 try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"))) {
62 StringBuilder response = new StringBuilder();
63 String responseLine = null;
64 while ((responseLine = br.readLine()) != null) {
65 response.append(responseLine.trim());
66 }
67
68 joke = this.objectMapper.readValue(response.toString(), Joke.class);
69 List<Joke> jokesList = new ArrayList<Joke>();
70 jokesList.add(joke);
71 List<Joke> jokesList2 = jokesList.stream().map( item -> {
72 item.setValue(item.getValue().toUpperCase());
73 return item;
74 } ).toList();
75 joke = jokesList2.get(0);
76 ret = joke.getValue();
77
78 JokeEntity je = new JokeEntity();
79 je.setJoke(joke.getValue());
80 JokeEntity saved = repo.saveAndFlush(je);
81 logger.info( repo.count() + " " + saved );
82 }
83
84 }
85 catch(Exception ex){
86 logger.error("error",ex);
87 }
88
89 return jokeMapper.jokeToJokeResponse(joke);
90 }
91
92 @GetMapping("/getsavedjokes")
93 @ResponseBody
94 @Operation(summary="Gets saved jokes", description="Gets saved Chuck Norris jokes.")
95 public List<JokeProjection> getSavedJokes() {
96 QJokeEntity jokeEntity = QJokeEntity.jokeEntity;
97 return queryFactory
98 .select( new QJokeProjection( jokeEntity.id , jokeEntity.joke ) )
99 .from(jokeEntity)
100 .fetch();
101 }
102
103 @GetMapping("/getsavedjokebyid")
104 @ResponseBody
105 @Operation(summary="Get saved joke by id",
106 description="Get saved Chuck Norris joke by id.")
107 public JokeProjection getSavedJokeById(Long jokeId) {
108 QJokeEntity jokeEntity = QJokeEntity.jokeEntity;
109 List<JokeProjection> ret = queryFactory
110 .select( new QJokeProjection( jokeEntity.id , jokeEntity.joke ) )
111 .from(jokeEntity)
112 .where(jokeEntity.id.eq(jokeId))
113 .fetch();
114 if(ret.size() > 0) return ret.get(0);
115 return null;
116 }
117 }
src/main/java/com/mooo/bitarus/chucknorris/JokeEntity.java
1 package com.mooo.bitarus.chucknorris;
2
3 import jakarta.persistence.Entity;
4 import jakarta.persistence.GeneratedValue;
5 import jakarta.persistence.GenerationType;
6 import jakarta.persistence.Id;
7
8 @Entity
9 public class JokeEntity{
10 @Id
11 @GeneratedValue(strategy=GenerationType.AUTO)
12 private Long id;
13 private String joke;
14
15 public JokeEntity(){
16 }
17
18 public String getJoke(){
19 return this.joke;
20 }
21
22 public void setJoke(String joke){
23 this.joke=joke;
24 }
25
26 public Long getId(){
27 return this.id;
28 }
29
30 public void setId(Long id){
31 this.id=id;
32 }
33
34 @Override
35 public String toString(){
36 return String.format("<Joke %d %s>", this.id, this.joke);
37 }
38 }
src/main/java/com/mooo/bitarus/chucknorris/Joke.java
1 package com.mooo.bitarus.chucknorris;
2 // import com.fasterxml.jackson.annotation.JsonProperty
3 // @JsonProperty("propInJSON")
4 public class Joke{
5 private String[] categories;
6 private String created_at;
7 private String icon_url;
8 private String id;
9 private String updated_at;
10 private String url;
11 private String value;
12
13 public String[] getCategories(){
14 return this.categories;
15 }
16
17 public String getCreated_at(){
18 return this.created_at;
19 }
20
21 public String getIcon_url(){
22 return this.icon_url;
23 }
24
25 public String getId(){
26 return this.id;
27 }
28
29 public String getUpdated_at(){
30 return this.updated_at;
31 }
32
33 public String getUrl(){
34 return this.url;
35 }
36
37 public String getValue(){
38 return this.value;
39 }
40
41 public void setCategories(String[] categories){
42 this.categories = categories;
43 }
44
45 public void setCreated_at(String createdAt){
46 this.created_at = created_at;
47 }
48
49 public void setIcon_url(String iconUrl){
50 this.icon_url=icon_url;
51 }
52
53 public void setId(String id){
54 this.id=id;
55 }
56
57 public void setUpdated_at(String updatedAt){
58 this.updated_at=updated_at;
59 }
60
61 public void setUrl(String url){
62 this.url=url;
63 }
64
65 public void setValue(String value){
66 this.value=value;
67 }
68 }
src/main/java/com/mooo/bitarus/chucknorris/JokeMapper.java
1 package com.mooo.bitarus.chucknorris;
2
3 import org.mapstruct.Mapper;
4 import org.mapstruct.Mapping;
5
6 @Mapper(componentModel = "spring")
7 public interface JokeMapper {
8
9 @Mapping(source = "value" , target = "response")
10 JokeResponse jokeToJokeResponse(Joke joke);
11
12 @Mapping(source = "response", target = "value")
13 Joke jokeResponseToJoke(JokeResponse jokeResponse);
14 }
src/main/java/com/mooo/bitarus/chucknorris/JokeProjection.java
src/main/java/com/mooo/bitarus/chucknorris/JokeRepository.java
src/main/java/com/mooo/bitarus/chucknorris/JokeResponse.java
1 package com.mooo.bitarus.chucknorris;
2
3 public class JokeResponse{
4 private String response;
5
6 public JokeResponse(){}
7
8 public JokeResponse(String response){
9 this.response = response;
10 }
11
12 public String getResponse(){
13 return this.response;
14 }
15
16 public void setResponse(String response){
17 this.response = response;
18 }
19 }
src/main/java/com/mooo/bitarus/chucknorris/QueryDSLConfiguration.java
1 package com.mooo.bitarus.chucknorris;
2
3 import org.springframework.context.annotation.Configuration;
4 import jakarta.persistence.PersistenceContext;
5 import org.springframework.context.annotation.Bean;
6 import com.querydsl.jpa.impl.JPAQueryFactory;
7 import jakarta.persistence.EntityManager;
8
9 @Configuration
10 class QueryDslConfiguration {
11
12 @PersistenceContext
13 private EntityManager em;
14
15 @Bean
16 public JPAQueryFactory jpaQueryFactory() {
17 return new JPAQueryFactory(em);
18 }
19 }