Skip to content

Commit

Permalink
feat: adding redis and todo scaffolding
Browse files Browse the repository at this point in the history
  • Loading branch information
wjohnsto committed Jan 24, 2025
1 parent 1c05985 commit c5d5b4b
Show file tree
Hide file tree
Showing 20 changed files with 459 additions and 38 deletions.
20 changes: 20 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
README.md
*.DS_Store
*.sw?
.#*
*#
*~
.classpath
.project
.settings
bin
build
target
dependency-reduced-pom.xml
*.sublime-*
/scratch
.gradle
README.html
*.iml
.idea
.vscode
1 change: 1 addition & 0 deletions .env.docker
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
REDIS_URL="redis://redis:6379"
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
REDIS_URL="redis://localhost:6379"
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# env files (can opt-in for commiting if needed)
.env*
!.env.example
!.env.docker
README.md
*.DS_Store
*.sw?
.#*
Expand All @@ -16,3 +21,4 @@ dependency-reduced-pom.xml
README.html
*.iml
.idea
.vscode
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Build stage
FROM maven:3.9.9-amazoncorretto-23-alpine AS build
WORKDIR /home/app
COPY . .
RUN mvn -f ./pom.xml clean test package

# Package stage
FROM amazoncorretto:23-alpine3.20 AS release
WORKDIR /home/app
COPY --from=build /home/app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
28 changes: 28 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: "redis-starter-java"
services:
redis:
container_name: redis
image: "redis:8.0-M02"
ports:
- 6379:6379
deploy:
replicas: 1
restart_policy:
condition: on-failure
volumes:
- redis-data:/data

server:
container_name: server
build: .
ports:
- 8080:8080
env_file:
- ./.env
- ./.env.docker
restart: always
depends_on:
- redis

volumes:
redis-data:
20 changes: 17 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath/> <!-- lookup parent from repository -->
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>io.redis</groupId>
<artifactId>redis-starter-java</artifactId>
Expand All @@ -15,6 +16,7 @@
<description>A starter project for working with Redis and Java</description>
<properties>
<java.version>17</java.version>
<jedis.version>5.2.0</jedis.version>
</properties>
<dependencies>
<dependency>
Expand All @@ -27,6 +29,18 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>

<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>dotenv-java</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>

<build>
Expand All @@ -38,4 +52,4 @@
</plugins>
</build>

</project>
</project>
3 changes: 0 additions & 3 deletions src/main/java/io/redis/restservice/Greeting.java

This file was deleted.

19 changes: 0 additions & 19 deletions src/main/java/io/redis/restservice/GreetingController.java

This file was deleted.

13 changes: 0 additions & 13 deletions src/main/java/io/redis/restservice/RestServiceApplication.java

This file was deleted.

18 changes: 18 additions & 0 deletions src/main/java/io/redis/todoapp/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.redis.todoapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import io.github.cdimascio.dotenv.Dotenv;

@SpringBootApplication
public class App {

public static void main(String[] args) {
Dotenv dotenv = Dotenv.configure().load();
dotenv.entries().forEach(e -> System.setProperty(e.getKey(), e.getValue()));

SpringApplication.run(App.class, args);
}

}
23 changes: 23 additions & 0 deletions src/main/java/io/redis/todoapp/GsonConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.redis.todoapp;

import java.time.Instant;

import org.springframework.boot.autoconfigure.gson.GsonBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import io.redis.todoapp.util.Gson_InstantTypeAdapter;

@Configuration
public class GsonConfiguration {
@Primary
@Bean
public GsonBuilderCustomizer typeAdapterRegistration() {
return builder -> {
builder
.registerTypeAdapter(Instant.class, new Gson_InstantTypeAdapter())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'");
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.redis.todoapp.components.todos;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/todos")
public class TodoController {
private static final Logger logger = LoggerFactory.getLogger(TodoController.class);

@Autowired
private TodoRepository repository;

public TodoController() {
}

@GetMapping("")
public ResponseEntity<?> all() {
logger.info("GET request access '/api/todos' path.");
try {
var todos = repository.all();
return new ResponseEntity<>(todos, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(String.format("Error getting all todos %s", e), HttpStatus.INTERNAL_SERVER_ERROR);
}
}

@GetMapping("/")
public ResponseEntity<?> allTrailing() {
return all();
}

@GetMapping("/{id}")
public ResponseEntity<?> one(@PathVariable String id) {
return new ResponseEntity<>(repository.one(id), HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package io.redis.todoapp.components.todos;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.gson.GsonBuilderCustomizer;
import org.springframework.stereotype.Repository;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import io.redis.todoapp.components.todos.models.Todo;
import io.redis.todoapp.components.todos.models.TodoDocument;
import io.redis.todoapp.components.todos.models.TodoDocuments;
import jakarta.annotation.PostConstruct;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.search.FTCreateParams;
import redis.clients.jedis.search.IndexDataType;
import redis.clients.jedis.search.schemafields.SchemaField;
import redis.clients.jedis.search.schemafields.TextField;

@Repository
public class TodoRepository {

public static final String TODOS_INDEX = "todos-idx";
public static final String TODOS_PREFIX = "todos:";

@Autowired
private GsonBuilderCustomizer gsonCustomizer;

private Gson gson;

@Autowired
private UnifiedJedis redis;

@PostConstruct
public void init() {
var gsonBuilder = new GsonBuilder();
gsonCustomizer.customize(gsonBuilder);
gson = gsonBuilder.create();

this.createIndexIfNotExists();
}

private boolean haveIndex() {
var indexes = redis.ftList();

return indexes.contains(TODOS_INDEX);
}

public void createIndexIfNotExists() {
if (haveIndex()) {
return;
}

SchemaField[] schema = {
TextField.of("$.name").as("name"),
TextField.of("$.status").as("status")
};
redis.ftCreate(TODOS_INDEX,
FTCreateParams.createParams()
.on(IndexDataType.JSON)
.addPrefix(TODOS_PREFIX),
schema
);
}

public TodoDocuments all() {
var result = redis.ftSearch(TODOS_INDEX, "*");
var total = result.getTotalResults();
var documents = result.getDocuments();
List<TodoDocument> todos = new ArrayList<>();

for (var doc : documents) {
var name = doc.getString("name");
var status = doc.getString("status");
var createdDate = doc.getString("created_date");
var updatedDate = doc.getString("updated_date");
var todo = new Todo(name, status, createdDate, updatedDate);
todos.add(new TodoDocument(doc.getId(), todo));
}

return new TodoDocuments(total, todos);
}

public Todo one(String id) {
var result = redis.jsonGet(id);

return jsonToTodo(result);
}

private Todo jsonToTodo(Object obj) {
var json = gson.toJson(obj);
return gson.fromJson(json, Todo.class);
}
}
Loading

0 comments on commit c5d5b4b

Please sign in to comment.