= ChuckNorris =
* Test app that invokes a ChuckNorris random joke API
== Structure ==
{{{#!highlight bash
.
├── pom.xml
├── src
│ └── main
│ └── java
│ ├── chucknorris
│ │ └── bitarus
│ │ └── allowed
│ │ └── org
│ │ ├── Application.java
│ │ ├── ChuckNorrisController.java
│ │ ├── Joke.java
│ │ └── JokeResponse.java
}}}
=== pom.xml ===
{{{#!highlight xml
4.0.0
bitarus.allowed.org
chucknorris
0.1.0
org.springframework.boot
spring-boot-starter-parent
2.1.6.RELEASE
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
com.google.code.gson
gson
2.8.6
compile
chucknorris.bitarus.allowed.org.Application
org.springframework.boot
spring-boot-maven-plugin
spring-milestone
http://repo.spring.io/libs-release
spring-milestone
http://repo.spring.io/libs-release
}}}
=== Application.java ===
{{{#!highlight java
package chucknorris.bitarus.allowed.org;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ComponentScan //scans for @Component beans
@EnableAutoConfiguration
public class Application {
private static Logger logger;
public static void main(String[] args) {
logger = LoggerFactory.getLogger(Application.class);
logger.info("Starting application");
SpringApplication.run(Application.class, args);
}
}
}}}
=== ChuckNorrisController.java ===
{{{#!highlight java
package chucknorris.bitarus.allowed.org;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.ui.Model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.springframework.http.HttpMethod;
import com.google.gson.Gson;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.List;
import java.util.ArrayList;
@Controller
public class ChuckNorrisController{
private final Logger logger = LoggerFactory.getLogger(ChuckNorrisController.class);
public ChuckNorrisController(){
logger.info("ChuckNorrisController created");
}
@RequestMapping("/chucknorris")
@ResponseBody
// http://localhost:8080/chucknorris
public JokeResponse chucknorris() {
String ret = "";
Gson gson = new Gson();
try {
URL url = new URL("https://api.chucknorris.io/jokes/random");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
logger.info( Integer.toString(connection.getResponseCode() ));
try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"))) {
StringBuilder response = new StringBuilder();
String responseLine = null;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
Joke joke = gson.fromJson(response.toString(), Joke.class);
List jokesList = new ArrayList();
jokesList.add(joke);
List jokesList2 = jokesList.stream().map( item ->{item.setValue(item.getValue().toUpperCase()); return item;} ).collect(Collectors.toList());
joke = jokesList2.get(0);
ret = joke.getValue();
}
}
catch(Exception ex){
logger.error("error",ex);
}
JokeResponse jr = new JokeResponse();
jr.setResponse(ret);
return jr;
}
}
}}}
=== Joke.java ===
{{{#!highlight java
package chucknorris.bitarus.allowed.org;
import com.google.gson.annotations.SerializedName;
public class Joke{
@SerializedName("categories")
private String[] categories;
@SerializedName("created_at")
private String createdAt;
@SerializedName("icon_url")
private String iconUrl;
@SerializedName("id")
private String id;
@SerializedName("updated_at")
private String updatedAt;
@SerializedName("url")
private String url;
@SerializedName("value")
private String value;
public String[] getCategories(){
return this.categories;
}
public String getCreatedAt(){
return this.createdAt;
}
public String getIconUrl(){
return this.iconUrl;
}
public String getId(){
return this.id;
}
public String getUpdatedAt(){
return this.updatedAt;
}
public String getUrl(){
return this.url;
}
public String getValue(){
return this.value;
}
public void setCategories(String[] categories){
this.categories = categories;
}
public void setCreatedAt(String createdAt ){
this.createdAt = createdAt;
}
public void setIconUrl(String iconUrl){
this.iconUrl=iconUrl;
}
public void setId(String id){
this.id=id;
}
public void setUpdatedAt(String updatedAt){
this.updatedAt=updatedAt;
}
public void setUrl(String url){
this.url=url;
}
public void setValue(String value){
this.value=value;
}
}
}}}
=== JokeResponse.java ===
{{{#!highlight java
package chucknorris.bitarus.allowed.org;
import com.google.gson.annotations.SerializedName;
public class JokeResponse{
@SerializedName("response")
private String response;
public String getResponse(){
return this.response;
}
public void setResponse(String response){
this.response = response;
}
}
}}}
== curl call ==
{{{#!highlight bash
curl https://api.chucknorris.io/jokes/random 2>/dev/null | json_reformat
{
"categories": [
],
"created_at": "2020-01-05 13:42:26.991637",
"icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
"id": "H-fvHao6QmiQxunoX2llBg",
"updated_at": "2020-01-05 13:42:26.991637",
"url": "https://api.chucknorris.io/jokes/H-fvHao6QmiQxunoX2llBg",
"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."
}
}}}
== Example 0.2 ==
=== Structure ===
{{{#!highlight sh
.
├── pom.xml
├── src
│ └── main
│ ├── java
│ │ └── com
│ │ └── mooo
│ │ └── bitarus
│ │ └── chucknorris
│ │ ├── Application.java
│ │ ├── ChuckNorrisController.java
│ │ ├── JokeEntity.java
│ │ ├── Joke.java
│ │ ├── JokeMapper.java
│ │ ├── JokeProjection.java
│ │ ├── JokeRepository.java
│ │ ├── JokeResponse.java
│ │ └── QueryDSLConfiguration.java
│ └── resources
│ └── application.yaml
}}}
=== pom.xml ===
{{{#!highlight xml
4.0.0
com.mooo.bitarus
chucknorris
0.2.0
org.springframework.boot
spring-boot-starter-parent
3.2.5
org.mapstruct
mapstruct
${mapstruct.version}
org.springdoc
springdoc-openapi-starter-webmvc-ui
2.5.0
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-jpa
org.hsqldb
hsqldb
2.7.2
com.querydsl
querydsl-jpa
${querydsl.version}
jakarta
com.querydsl
querydsl-apt
${querydsl.version}
jakarta
21
com.mooo.bitarus.chucknorris.Application
5.1.0
1.5.5.Final
org.springframework.boot
spring-boot-maven-plugin
org.apache.maven.plugins
maven-compiler-plugin
3.11.0
org.mapstruct
mapstruct-processor
${mapstruct.version}
com.querydsl
querydsl-apt
${querydsl.version}
jakarta
jakarta.persistence
jakarta.persistence-api
3.1.0
}}}
=== src/main/resources/application.yaml ===
{{{#!highlight yaml
server:
port: 8181
chucknorris:
url: https://api.chucknorris.io/jokes/random
springdoc:
swagger-ui:
enabled: true
# http://localhost:8181/swagger-ui/index.html
# http://localhost:8181/v3/api-docs.yaml
# https://docs.swagger.io/swagger-core/v2.0.0-RC3/apidocs/io/swagger/v3/oas/annotations/Operation.html
spring:
datasource:
url: jdbc:hsqldb:mem:mydb
username: sa
password: sa
}}}
=== src/main/java/com/mooo/bitarus/chucknorris/Application.java ===
{{{#!highlight java
package com.mooo.bitarus.chucknorris;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ComponentScan //scans for @Component beans
@EnableAutoConfiguration
public class Application {
private static Logger logger;
public static void main(String[] args) {
logger = LoggerFactory.getLogger(Application.class);
logger.info("Starting application");
SpringApplication.run(Application.class, args);
}
}
}}}
=== src/main/java/com/mooo/bitarus/chucknorris/ChuckNorrisController.java ===
{{{#!highlight java
package com.mooo.bitarus.chucknorris;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.ui.Model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.springframework.http.HttpMethod;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.List;
import java.util.ArrayList;
import org.springframework.beans.factory.annotation.Value;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import com.querydsl.jpa.impl.JPAQueryFactory;
@Tag(name="CNJokeController")
@Controller
public class ChuckNorrisController{
private final Logger logger = LoggerFactory.getLogger(ChuckNorrisController.class);
@Value("${chucknorris.url}")
private String chuckNorrisURL;
private ObjectMapper objectMapper;
private JokeRepository repo;
private JPAQueryFactory queryFactory;
private JokeMapper jokeMapper;
public ChuckNorrisController(JokeRepository repo, JPAQueryFactory queryFactory,
JokeMapper jokeMapper){
logger.info("ChuckNorrisController created");
this.objectMapper = new ObjectMapper();
this.repo = repo;
this.queryFactory = queryFactory;
this.jokeMapper = jokeMapper;
}
@GetMapping("/chucknorris")
@ResponseBody
@Operation(summary="Gets joke", description="Gets a Chuck Norris random joke.")
// http://localhost:8080/chucknorris
public JokeResponse chucknorris() {
String ret = "";
Joke joke=null;
try {
URL url = new URL(chuckNorrisURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
logger.info( Integer.toString(connection.getResponseCode() ));
try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"))) {
StringBuilder response = new StringBuilder();
String responseLine = null;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
joke = this.objectMapper.readValue(response.toString(), Joke.class);
List jokesList = new ArrayList();
jokesList.add(joke);
List jokesList2 = jokesList.stream().map( item -> {
item.setValue(item.getValue().toUpperCase());
return item;
} ).toList();
joke = jokesList2.get(0);
ret = joke.getValue();
JokeEntity je = new JokeEntity();
je.setJoke(joke.getValue());
JokeEntity saved = repo.saveAndFlush(je);
logger.info( repo.count() + " " + saved );
}
}
catch(Exception ex){
logger.error("error",ex);
}
return jokeMapper.jokeToJokeResponse(joke);
}
@GetMapping("/getsavedjokes")
@ResponseBody
@Operation(summary="Gets saved jokes", description="Gets saved Chuck Norris jokes.")
public List getSavedJokes() {
QJokeEntity jokeEntity = QJokeEntity.jokeEntity;
return queryFactory
.select( new QJokeProjection( jokeEntity.id , jokeEntity.joke ) )
.from(jokeEntity)
.fetch();
}
@GetMapping("/getsavedjokebyid")
@ResponseBody
@Operation(summary="Get saved joke by id",
description="Get saved Chuck Norris joke by id.")
public JokeProjection getSavedJokeById(Long jokeId) {
QJokeEntity jokeEntity = QJokeEntity.jokeEntity;
List ret = queryFactory
.select( new QJokeProjection( jokeEntity.id , jokeEntity.joke ) )
.from(jokeEntity)
.where(jokeEntity.id.eq(jokeId))
.fetch();
if(ret.size() > 0) return ret.get(0);
return null;
}
}
}}}
=== src/main/java/com/mooo/bitarus/chucknorris/JokeEntity.java ===
{{{#!highlight java
package com.mooo.bitarus.chucknorris;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class JokeEntity{
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String joke;
public JokeEntity(){
}
public String getJoke(){
return this.joke;
}
public void setJoke(String joke){
this.joke=joke;
}
public Long getId(){
return this.id;
}
public void setId(Long id){
this.id=id;
}
@Override
public String toString(){
return String.format("", this.id, this.joke);
}
}
}}}
=== src/main/java/com/mooo/bitarus/chucknorris/Joke.java ===
{{{#!highlight java
package com.mooo.bitarus.chucknorris;
// import com.fasterxml.jackson.annotation.JsonProperty
// @JsonProperty("propInJSON")
public class Joke{
private String[] categories;
private String created_at;
private String icon_url;
private String id;
private String updated_at;
private String url;
private String value;
public String[] getCategories(){
return this.categories;
}
public String getCreated_at(){
return this.created_at;
}
public String getIcon_url(){
return this.icon_url;
}
public String getId(){
return this.id;
}
public String getUpdated_at(){
return this.updated_at;
}
public String getUrl(){
return this.url;
}
public String getValue(){
return this.value;
}
public void setCategories(String[] categories){
this.categories = categories;
}
public void setCreated_at(String createdAt){
this.created_at = created_at;
}
public void setIcon_url(String iconUrl){
this.icon_url=icon_url;
}
public void setId(String id){
this.id=id;
}
public void setUpdated_at(String updatedAt){
this.updated_at=updated_at;
}
public void setUrl(String url){
this.url=url;
}
public void setValue(String value){
this.value=value;
}
}
}}}
=== src/main/java/com/mooo/bitarus/chucknorris/JokeMapper.java ===
{{{#!highlight java
package com.mooo.bitarus.chucknorris;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(componentModel = "spring")
public interface JokeMapper {
@Mapping(source = "value" , target = "response")
JokeResponse jokeToJokeResponse(Joke joke);
@Mapping(source = "response", target = "value")
Joke jokeResponseToJoke(JokeResponse jokeResponse);
}
}}}
=== src/main/java/com/mooo/bitarus/chucknorris/JokeProjection.java ===
{{{#!highlight java
package com.mooo.bitarus.chucknorris;
import com.querydsl.core.annotations.QueryProjection;
public record JokeProjection(Long id, String joke) {
@QueryProjection
public JokeProjection {
}
}
}}}
=== src/main/java/com/mooo/bitarus/chucknorris/JokeRepository.java ===
{{{#!highlight java
package com.mooo.bitarus.chucknorris;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface JokeRepository extends JpaRepository{
}
}}}
=== src/main/java/com/mooo/bitarus/chucknorris/JokeResponse.java ===
{{{#!highlight java
package com.mooo.bitarus.chucknorris;
public class JokeResponse{
private String response;
public JokeResponse(){}
public JokeResponse(String response){
this.response = response;
}
public String getResponse(){
return this.response;
}
public void setResponse(String response){
this.response = response;
}
}
}}}
=== src/main/java/com/mooo/bitarus/chucknorris/QueryDSLConfiguration.java ===
{{{#!highlight java
package com.mooo.bitarus.chucknorris;
import org.springframework.context.annotation.Configuration;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Bean;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
@Configuration
class QueryDslConfiguration {
@PersistenceContext
private EntityManager em;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em);
}
}
}}}