diff --git a/spring-boot-groovy/pom.xml b/spring-boot-groovy/pom.xml
new file mode 100644
index 0000000000..f61398c5d6
--- /dev/null
+++ b/spring-boot-groovy/pom.xml
@@ -0,0 +1,83 @@
+
+
+
+ 4.0.0
+ com.baeldung.app
+ spring-boot-groovy
+ spring-boot-groovy
+ war
+ Spring Boot Todo Application with Groovy
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.codehaus.groovy
+ groovy
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ com.h2database
+ h2
+ runtime
+
+
+ net.bytebuddy
+ byte-buddy-dep
+ 1.10.9
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.codehaus.gmavenplus
+ gmavenplus-plugin
+ 1.9.0
+
+
+
+ addSources
+ addTestSources
+ generateStubs
+ compile
+ generateTestStubs
+ compileTests
+ removeStubs
+ removeTestStubs
+
+
+
+
+
+
+
+
+ com.baeldung.app.SpringBootGroovyApplication
+
+
+
diff --git a/spring-boot-groovy/src/main/groovy/com/baeldung/app/SpringBootGroovyApplication.groovy b/spring-boot-groovy/src/main/groovy/com/baeldung/app/SpringBootGroovyApplication.groovy
new file mode 100644
index 0000000000..226a2ff53d
--- /dev/null
+++ b/spring-boot-groovy/src/main/groovy/com/baeldung/app/SpringBootGroovyApplication.groovy
@@ -0,0 +1,13 @@
+package com.baeldung.app
+
+import org.springframework.boot.SpringApplication
+import org.springframework.boot.autoconfigure.SpringBootApplication
+
+import com.baeldung.app.SpringBootGroovyApplication
+
+@SpringBootApplication
+class SpringBootGroovyApplication {
+ static void main(String[] args) {
+ SpringApplication.run SpringBootGroovyApplication, args
+ }
+}
diff --git a/spring-boot-groovy/src/main/groovy/com/baeldung/app/controller/TodoController.groovy b/spring-boot-groovy/src/main/groovy/com/baeldung/app/controller/TodoController.groovy
new file mode 100644
index 0000000000..02f6d0223b
--- /dev/null
+++ b/spring-boot-groovy/src/main/groovy/com/baeldung/app/controller/TodoController.groovy
@@ -0,0 +1,48 @@
+package com.baeldung.app.controller
+
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.web.bind.annotation.DeleteMapping
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.PathVariable
+import org.springframework.web.bind.annotation.PostMapping
+import org.springframework.web.bind.annotation.PutMapping
+import org.springframework.web.bind.annotation.RequestBody
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RequestMethod
+import org.springframework.web.bind.annotation.RestController
+
+import com.baeldung.app.entity.Todo
+import com.baeldung.app.service.TodoService
+
+@RestController
+@RequestMapping('todo')
+public class TodoController {
+
+ @Autowired
+ TodoService todoService
+
+ @GetMapping
+ List getAllTodoList(){
+ todoService.findAll()
+ }
+
+ @PostMapping
+ Todo saveTodo(@RequestBody Todo todo){
+ todoService.saveTodo todo
+ }
+
+ @PutMapping
+ Todo updateTodo(@RequestBody Todo todo){
+ todoService.updateTodo todo
+ }
+
+ @DeleteMapping('/{todoId}')
+ deleteTodo(@PathVariable Integer todoId){
+ todoService.deleteTodo todoId
+ }
+
+ @GetMapping('/{todoId}')
+ Todo getTodoById(@PathVariable Integer todoId){
+ todoService.findById todoId
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-groovy/src/main/groovy/com/baeldung/app/entity/Todo.groovy b/spring-boot-groovy/src/main/groovy/com/baeldung/app/entity/Todo.groovy
new file mode 100644
index 0000000000..9f1253c5b3
--- /dev/null
+++ b/spring-boot-groovy/src/main/groovy/com/baeldung/app/entity/Todo.groovy
@@ -0,0 +1,23 @@
+package com.baeldung.app.entity
+
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.GeneratedValue
+import javax.persistence.GenerationType
+import javax.persistence.Id
+import javax.persistence.Table
+
+@Entity
+@Table(name = 'todo')
+class Todo {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ Integer id
+
+ @Column
+ String task
+
+ @Column
+ Boolean isCompleted
+
+}
diff --git a/spring-boot-groovy/src/main/groovy/com/baeldung/app/repository/TodoRepository.groovy b/spring-boot-groovy/src/main/groovy/com/baeldung/app/repository/TodoRepository.groovy
new file mode 100644
index 0000000000..c0b35cc37d
--- /dev/null
+++ b/spring-boot-groovy/src/main/groovy/com/baeldung/app/repository/TodoRepository.groovy
@@ -0,0 +1,9 @@
+package com.baeldung.app.repository
+
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.stereotype.Repository
+
+import com.baeldung.app.entity.Todo
+
+@Repository
+interface TodoRepository extends JpaRepository {}
\ No newline at end of file
diff --git a/spring-boot-groovy/src/main/groovy/com/baeldung/app/service/TodoService.groovy b/spring-boot-groovy/src/main/groovy/com/baeldung/app/service/TodoService.groovy
new file mode 100644
index 0000000000..0a59d93330
--- /dev/null
+++ b/spring-boot-groovy/src/main/groovy/com/baeldung/app/service/TodoService.groovy
@@ -0,0 +1,16 @@
+package com.baeldung.app.service
+
+import com.baeldung.app.entity.Todo
+
+interface TodoService {
+
+ List findAll()
+
+ Todo findById(Integer todoId)
+
+ Todo saveTodo(Todo todo)
+
+ Todo updateTodo(Todo todo)
+
+ Todo deleteTodo(Integer todoId)
+}
diff --git a/spring-boot-groovy/src/main/groovy/com/baeldung/app/service/impl/TodoServiceImpl.groovy b/spring-boot-groovy/src/main/groovy/com/baeldung/app/service/impl/TodoServiceImpl.groovy
new file mode 100644
index 0000000000..6d0ee03a9f
--- /dev/null
+++ b/spring-boot-groovy/src/main/groovy/com/baeldung/app/service/impl/TodoServiceImpl.groovy
@@ -0,0 +1,40 @@
+package com.baeldung.app.service.impl
+
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Service
+
+import com.baeldung.app.entity.Todo
+import com.baeldung.app.repository.TodoRepository
+import com.baeldung.app.service.TodoService
+
+@Service
+class TodoServiceImpl implements TodoService {
+
+ @Autowired
+ TodoRepository todoRepository
+
+ @Override
+ List findAll() {
+ todoRepository.findAll()
+ }
+
+ @Override
+ Todo findById(Integer todoId) {
+ todoRepository.findById todoId get()
+ }
+
+ @Override
+ Todo saveTodo(Todo todo){
+ todoRepository.save todo
+ }
+
+ @Override
+ Todo updateTodo(Todo todo){
+ todoRepository.save todo
+ }
+
+ @Override
+ Todo deleteTodo(Integer todoId){
+ todoRepository.deleteById todoId
+ }
+}
diff --git a/spring-boot-groovy/src/main/resources/application.properties b/spring-boot-groovy/src/main/resources/application.properties
new file mode 100644
index 0000000000..8d53a190bb
--- /dev/null
+++ b/spring-boot-groovy/src/main/resources/application.properties
@@ -0,0 +1,5 @@
+spring.datasource.url=jdbc:h2:mem:todo
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=sa
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
\ No newline at end of file
diff --git a/spring-boot-groovy/src/test/groovy/com/baeldung/app/TodoAppUnitTest.groovy b/spring-boot-groovy/src/test/groovy/com/baeldung/app/TodoAppUnitTest.groovy
new file mode 100644
index 0000000000..faf2d64ba7
--- /dev/null
+++ b/spring-boot-groovy/src/test/groovy/com/baeldung/app/TodoAppUnitTest.groovy
@@ -0,0 +1,97 @@
+package com.baeldung.app
+
+import static org.junit.jupiter.api.Assertions.assertEquals
+import static org.junit.jupiter.api.Assertions.assertTrue
+
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.http.HttpStatus
+import org.springframework.http.MediaType
+import org.springframework.test.context.event.annotation.BeforeTestClass
+import org.springframework.test.context.junit4.SpringRunner
+
+import com.baeldung.app.entity.Todo
+
+import io.restassured.RestAssured
+import io.restassured.response.Response
+
+class TodoAppUnitTest {
+ static API_ROOT = 'http://localhost:8081/todo'
+ static readingTodoId
+ static writingTodoId
+
+ @BeforeClass
+ static void populateDummyData() {
+ Todo readingTodo = new Todo(task: 'Reading', isCompleted: false)
+ Todo writingTodo = new Todo(task: 'Writing', isCompleted: false)
+
+ final Response readingResponse =
+ RestAssured.given()
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .body(readingTodo).post(API_ROOT)
+
+ Todo cookingTodoResponse = readingResponse.as Todo.class
+ readingTodoId = cookingTodoResponse.getId()
+
+ final Response writingResponse =
+ RestAssured.given()
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .body(writingTodo).post(API_ROOT)
+
+ Todo writingTodoResponse = writingResponse.as Todo.class
+ writingTodoId = writingTodoResponse.getId()
+ }
+
+ @Test
+ void whenGetAllTodoList_thenOk(){
+ final Response response = RestAssured.get(API_ROOT)
+
+ assertEquals HttpStatus.OK.value(),response.getStatusCode()
+ assertTrue response.as(List.class).size() > 0
+ }
+
+ @Test
+ void whenGetTodoById_thenOk(){
+ final Response response =
+ RestAssured.get("$API_ROOT/$readingTodoId")
+
+ assertEquals HttpStatus.OK.value(),response.getStatusCode()
+ Todo todoResponse = response.as Todo.class
+ assertEquals readingTodoId,todoResponse.getId()
+ }
+
+ @Test
+ void whenUpdateTodoById_thenOk(){
+ Todo todo = new Todo(id:readingTodoId, isCompleted: true)
+ final Response response =
+ RestAssured.given()
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .body(todo).put(API_ROOT)
+
+ assertEquals HttpStatus.OK.value(),response.getStatusCode()
+ Todo todoResponse = response.as Todo.class
+ assertTrue todoResponse.getIsCompleted()
+ }
+
+ @Test
+ void whenDeleteTodoById_thenOk(){
+ final Response response =
+ RestAssured.given()
+ .delete("$API_ROOT/$writingTodoId")
+
+ assertEquals HttpStatus.OK.value(),response.getStatusCode()
+ }
+
+ @Test
+ void whenSaveTodo_thenOk(){
+ Todo todo = new Todo(task: 'Blogging', isCompleted: false)
+ final Response response =
+ RestAssured.given()
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .body(todo).post(API_ROOT)
+
+ assertEquals HttpStatus.OK.value(),response.getStatusCode()
+ }
+}
\ No newline at end of file