From d70d777d391935140d3922dca47ff8fe40f99563 Mon Sep 17 00:00:00 2001 From: Kasun Nadeeshana Date: Wed, 3 May 2023 21:36:13 +0530 Subject: [PATCH 1/6] Create entities --- pom.xml | 80 +++++++++++-------- .../springbootcodingtest/entity/Project.java | 17 ++++ .../springbootcodingtest/entity/Task.java | 24 ++++++ .../springbootcodingtest/entity/User.java | 19 +++++ 4 files changed, 105 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/entity/Project.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/entity/Task.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/entity/User.java diff --git a/pom.xml b/pom.xml index b0a6389..f545292 100644 --- a/pom.xml +++ b/pom.xml @@ -1,41 +1,51 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.6.3 - - - com.accenture.codingtest - spring-boot-coding-test - 0.0.1-SNAPSHOT - spring-boot-coding-test - Demo project for Spring Boot - - 11 - - - - org.springframework.boot - spring-boot-starter - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.3 + + + com.accenture.codingtest + spring-boot-coding-test + 0.0.1-SNAPSHOT + spring-boot-coding-test + Demo project for Spring Boot + + 11 + + + + org.springframework.boot + spring-boot-starter + - - org.springframework.boot - spring-boot-starter-test - test - - + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.projectlombok + lombok + 1.18.26 + compile + + - - - - org.springframework.boot - spring-boot-maven-plugin - - - + + + + org.springframework.boot + spring-boot-maven-plugin + + + diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/entity/Project.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/entity/Project.java new file mode 100644 index 0000000..c1d4c6b --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/entity/Project.java @@ -0,0 +1,17 @@ +package com.accenture.codingtest.springbootcodingtest.entity; + +import lombok.Data; + +import javax.persistence.*; +import java.util.UUID; + +@Entity +@Data +@Table +public class Project { + @Id + @GeneratedValue + private UUID id; + @Column(nullable = false) + private String name; +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/entity/Task.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/entity/Task.java new file mode 100644 index 0000000..79766f9 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/entity/Task.java @@ -0,0 +1,24 @@ +package com.accenture.codingtest.springbootcodingtest.entity; + +import lombok.Data; + +import javax.persistence.*; +import java.util.UUID; + +@Data +@Entity +@Table +public class Task { + @Id + @GeneratedValue + private UUID id; + @Column(nullable = false) + private String title; + private String description; + @Column(nullable = false) + private String status; + @Column(nullable = false) + private UUID projectId; + @Column(nullable = false) + private UUID userId; +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/entity/User.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/entity/User.java new file mode 100644 index 0000000..4bfb7ef --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/entity/User.java @@ -0,0 +1,19 @@ +package com.accenture.codingtest.springbootcodingtest.entity; + +import lombok.Data; + +import javax.persistence.*; +import java.util.UUID; + +@Entity +@Data +@Table +public class User { + @Id + @GeneratedValue + private UUID id; + @Column(nullable = false, unique = true) + private String username ; + @Column(nullable = false) + private String password ; +} From a64a374ee90ee698f839ea492e63bb7e31d19fc7 Mon Sep 17 00:00:00 2001 From: Kasun Nadeeshana Date: Wed, 3 May 2023 23:28:47 +0530 Subject: [PATCH 2/6] Create REST APIs --- pom.xml | 10 ++- .../controller/ProjectController.java | 50 +++++++++++++++ .../controller/TaskController.java | 50 +++++++++++++++ .../controller/UserController.java | 51 +++++++++++++++ .../model/ProjectDto.java | 13 ++++ .../springbootcodingtest/model/TaskDto.java | 15 +++++ .../springbootcodingtest/model/UserDto.java | 15 +++++ .../repository/ProjectRepository.java | 10 +++ .../repository/TaskRepository.java | 10 +++ .../repository/UserRepository.java | 9 +++ .../service/ProjectService.java | 62 ++++++++++++++++++ .../service/TaskService.java | 62 ++++++++++++++++++ .../service/UserService.java | 63 +++++++++++++++++++ .../service/UserServiceTest.java | 56 +++++++++++++++++ 14 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/controller/ProjectController.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/controller/TaskController.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/controller/UserController.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/model/ProjectDto.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/model/TaskDto.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/model/UserDto.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/repository/ProjectRepository.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/repository/TaskRepository.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/repository/UserRepository.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/service/ProjectService.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/service/TaskService.java create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/service/UserService.java create mode 100644 src/test/java/com/accenture/codingtest/springbootcodingtest/service/UserServiceTest.java diff --git a/pom.xml b/pom.xml index f545292..9252099 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,10 @@ org.springframework.boot spring-boot-starter - + + org.springframework.boot + spring-boot-starter-web + org.springframework.boot spring-boot-starter-test @@ -37,6 +40,11 @@ 1.18.26 compile + + org.modelmapper + modelmapper + 3.1.1 + diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/ProjectController.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/ProjectController.java new file mode 100644 index 0000000..8efa375 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/ProjectController.java @@ -0,0 +1,50 @@ +package com.accenture.codingtest.springbootcodingtest.controller; + +import com.accenture.codingtest.springbootcodingtest.model.ProjectDto; +import com.accenture.codingtest.springbootcodingtest.service.ProjectService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Collection; +import java.util.UUID; + +@RestController +@RequestMapping("/api/v1/projects") +@RequiredArgsConstructor +public class ProjectController { + private final ProjectService projectService; + + @GetMapping + public Collection findAll() { + return projectService.findAll(); + } + + @GetMapping("/{id}") + public ProjectDto findById(@PathVariable("id") String id) { + return projectService.findById(UUID.fromString(id)); + } + + @PostMapping("/") + public ProjectDto create(@RequestBody ProjectDto projectDto) { + return projectService.create(projectDto); + } + + @PutMapping("/{id}") + public ProjectDto update(@PathVariable("id") String id, @RequestBody ProjectDto projectDto) { + return projectService.update(UUID.fromString(id), projectDto); + } + + @PatchMapping("/{id}") + public ProjectDto updatePartially(@PathVariable("id") String id, @RequestBody ProjectDto projectDto) { + return projectService.updatePartially(UUID.fromString(id), projectDto); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteById(@PathVariable("id") String id) { + projectService.deleteById(UUID.fromString(id)); + return ResponseEntity.ok().build(); + } + + +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/TaskController.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/TaskController.java new file mode 100644 index 0000000..5e483c8 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/TaskController.java @@ -0,0 +1,50 @@ +package com.accenture.codingtest.springbootcodingtest.controller; + +import com.accenture.codingtest.springbootcodingtest.model.TaskDto; +import com.accenture.codingtest.springbootcodingtest.service.TaskService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Collection; +import java.util.UUID; + +@RestController +@RequestMapping("/api/v1/tasks") +@RequiredArgsConstructor +public class TaskController { + private final TaskService taskService; + + @GetMapping + public Collection findAll() { + return taskService.findAll(); + } + + @GetMapping("/{id}") + public TaskDto findById(@PathVariable("id") String id) { + return taskService.findById(UUID.fromString(id)); + } + + @PostMapping("/") + public TaskDto create(@RequestBody TaskDto taskDto) { + return taskService.create(taskDto); + } + + @PutMapping("/{id}") + public TaskDto update(@PathVariable("id") String id, @RequestBody TaskDto taskDto) { + return taskService.update(UUID.fromString(id), taskDto); + } + + @PatchMapping("/{id}") + public TaskDto updatePartially(@PathVariable("id") String id, @RequestBody TaskDto taskDto) { + return taskService.updatePartially(UUID.fromString(id), taskDto); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteById(@PathVariable("id") String id) { + taskService.deleteById(UUID.fromString(id)); + return ResponseEntity.ok().build(); + } + + +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/UserController.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/UserController.java new file mode 100644 index 0000000..ac2b618 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/UserController.java @@ -0,0 +1,51 @@ +package com.accenture.codingtest.springbootcodingtest.controller; + +import com.accenture.codingtest.springbootcodingtest.model.UserDto; +import com.accenture.codingtest.springbootcodingtest.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import java.util.Collection; +import java.util.UUID; + +@RestController +@RequestMapping("/api/v1/users") +@RequiredArgsConstructor +public class UserController { + private final UserService userService; + + @GetMapping + public Collection findAll() { + return userService.findAll(); + } + + @GetMapping("/{id}") + public UserDto findById(@PathVariable("id") String id) { + return userService.findById(UUID.fromString(id)); + } + + @PostMapping("/") + public UserDto create(@RequestBody UserDto userDto) { + return userService.create(userDto); + } + + @PutMapping("/{id}") + public UserDto update(@PathVariable("id") String id, @RequestBody UserDto userDto) { + return userService.update(UUID.fromString(id), userDto); + } + + @PatchMapping("/{id}") + public UserDto updatePartially(@PathVariable("id") String id, @RequestBody UserDto userDto) { + return userService.updatePartially(UUID.fromString(id), userDto); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteById(@PathVariable("id") String id) { + userService.deleteById(UUID.fromString(id)); + return ResponseEntity.ok().build(); + } + + +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/model/ProjectDto.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/model/ProjectDto.java new file mode 100644 index 0000000..8ba7cf8 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/model/ProjectDto.java @@ -0,0 +1,13 @@ +package com.accenture.codingtest.springbootcodingtest.model; + +import lombok.Data; + +import javax.persistence.*; +import java.util.UUID; + + +@Data +public class ProjectDto { + private UUID id; + private String name; +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/model/TaskDto.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/model/TaskDto.java new file mode 100644 index 0000000..1bc70c3 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/model/TaskDto.java @@ -0,0 +1,15 @@ +package com.accenture.codingtest.springbootcodingtest.model; + +import lombok.Data; + +import java.util.UUID; + +@Data +public class TaskDto { + private UUID id; + private String title; + private String description; + private String status; + private UUID projectId; + private UUID userId; +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/model/UserDto.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/model/UserDto.java new file mode 100644 index 0000000..4f6df19 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/model/UserDto.java @@ -0,0 +1,15 @@ +package com.accenture.codingtest.springbootcodingtest.model; + +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import java.util.UUID; + +@Data +public class UserDto { + private UUID id; + private String username; + private String password; +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/repository/ProjectRepository.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/repository/ProjectRepository.java new file mode 100644 index 0000000..40c7f00 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/repository/ProjectRepository.java @@ -0,0 +1,10 @@ +package com.accenture.codingtest.springbootcodingtest.repository; + +import com.accenture.codingtest.springbootcodingtest.entity.Project; +import com.accenture.codingtest.springbootcodingtest.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface ProjectRepository extends JpaRepository { +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/repository/TaskRepository.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/repository/TaskRepository.java new file mode 100644 index 0000000..1297238 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/repository/TaskRepository.java @@ -0,0 +1,10 @@ +package com.accenture.codingtest.springbootcodingtest.repository; + +import com.accenture.codingtest.springbootcodingtest.entity.Project; +import com.accenture.codingtest.springbootcodingtest.entity.Task; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface TaskRepository extends JpaRepository { +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/repository/UserRepository.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/repository/UserRepository.java new file mode 100644 index 0000000..654bbd3 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/repository/UserRepository.java @@ -0,0 +1,9 @@ +package com.accenture.codingtest.springbootcodingtest.repository; + +import com.accenture.codingtest.springbootcodingtest.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface UserRepository extends JpaRepository { +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/service/ProjectService.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/service/ProjectService.java new file mode 100644 index 0000000..7b7e41c --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/service/ProjectService.java @@ -0,0 +1,62 @@ +package com.accenture.codingtest.springbootcodingtest.service; + +import com.accenture.codingtest.springbootcodingtest.entity.Project; +import com.accenture.codingtest.springbootcodingtest.model.ProjectDto; +import com.accenture.codingtest.springbootcodingtest.repository.ProjectRepository; +import lombok.RequiredArgsConstructor; +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; + +import javax.persistence.EntityNotFoundException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class ProjectService { + private final ProjectRepository projectRepository; + private final ModelMapper modelMapper; + + public Collection findAll() { + Collection all = new ArrayList<>(); + projectRepository.findAll().forEach(obj -> { + all.add(modelMapper.map(obj, ProjectDto.class)); + }); + return all; + } + + public ProjectDto findById(UUID id) { + Project project = projectRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Project not found. Id: " + id)); + return modelMapper.map(project, ProjectDto.class); + } + + public ProjectDto create(ProjectDto projectDto) { + Project entity = modelMapper.map(projectDto, Project.class); + Project createdEntity = projectRepository.save(entity); + return modelMapper.map(createdEntity, ProjectDto.class); + } + + public ProjectDto update(UUID id, ProjectDto projectDto) { + boolean existsById = projectRepository.existsById(id); + if (existsById) { + Project entityProject = modelMapper.map(projectDto, Project.class); + Project updatedEntity = projectRepository.save(entityProject); + return modelMapper.map(updatedEntity, ProjectDto.class); + } + throw new EntityNotFoundException("Project not found. Id: " + id); + } + + public ProjectDto updatePartially(UUID id, ProjectDto projectDto) { + ProjectDto target = findById(id); + modelMapper.map(projectDto, target); + Project targetEntity = modelMapper.map(target, Project.class); + projectRepository.save(targetEntity); + return target; + } + + public void deleteById(UUID id) { + projectRepository.deleteById(id); + } +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/service/TaskService.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/service/TaskService.java new file mode 100644 index 0000000..5739978 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/service/TaskService.java @@ -0,0 +1,62 @@ +package com.accenture.codingtest.springbootcodingtest.service; + +import com.accenture.codingtest.springbootcodingtest.entity.Task; +import com.accenture.codingtest.springbootcodingtest.model.TaskDto; +import com.accenture.codingtest.springbootcodingtest.repository.TaskRepository; +import lombok.RequiredArgsConstructor; +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; + +import javax.persistence.EntityNotFoundException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class TaskService { + private final TaskRepository taskRepository; + private final ModelMapper modelMapper; + + public Collection findAll() { + Collection all = new ArrayList<>(); + taskRepository.findAll().forEach(obj -> { + all.add(modelMapper.map(obj, TaskDto.class)); + }); + return all; + } + + public TaskDto findById(UUID id) { + Task task = taskRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Task not found. Id: " + id)); + return modelMapper.map(task, TaskDto.class); + } + + public TaskDto create(TaskDto taskDto) { + Task entity = modelMapper.map(taskDto, Task.class); + Task createdEntity = taskRepository.save(entity); + return modelMapper.map(createdEntity, TaskDto.class); + } + + public TaskDto update(UUID id, TaskDto taskDto) { + boolean existsById = taskRepository.existsById(id); + if (existsById) { + Task entityTask = modelMapper.map(taskDto, Task.class); + Task updatedEntity = taskRepository.save(entityTask); + return modelMapper.map(updatedEntity, TaskDto.class); + } + throw new EntityNotFoundException("Task not found. Id: " + id); + } + + public TaskDto updatePartially(UUID id, TaskDto taskDto) { + TaskDto target = findById(id); + modelMapper.map(taskDto, target); + Task targetEntity = modelMapper.map(target, Task.class); + taskRepository.save(targetEntity); + return target; + } + + public void deleteById(UUID id) { + taskRepository.deleteById(id); + } +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/service/UserService.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/service/UserService.java new file mode 100644 index 0000000..a3e7360 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/service/UserService.java @@ -0,0 +1,63 @@ +package com.accenture.codingtest.springbootcodingtest.service; + +import com.accenture.codingtest.springbootcodingtest.entity.User; +import com.accenture.codingtest.springbootcodingtest.model.UserDto; +import com.accenture.codingtest.springbootcodingtest.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; + +import javax.persistence.EntityNotFoundException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Optional; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class UserService { + private final UserRepository userRepository; + private final ModelMapper modelMapper; + + public Collection findAll() { + Collection all = new ArrayList<>(); + userRepository.findAll().forEach(obj -> { + all.add(modelMapper.map(obj, UserDto.class)); + }); + return all; + } + + public UserDto findById(UUID id) { + User user = userRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("User not found. Id: " + id)); + return modelMapper.map(user, UserDto.class); + } + + public UserDto create(UserDto userDto) { + User entityUser = modelMapper.map(userDto, User.class); + User createdEntity = userRepository.save(entityUser); + return modelMapper.map(createdEntity, UserDto.class); + } + + public UserDto update(UUID id, UserDto userDto) { + boolean existsById = userRepository.existsById(id); + if (existsById) { + User entityUser = modelMapper.map(userDto, User.class); + User updatedEntity = userRepository.save(entityUser); + return modelMapper.map(updatedEntity, UserDto.class); + } + throw new EntityNotFoundException("User not found. Id: " + id); + } + + public UserDto updatePartially(UUID id, UserDto userDto) { + UserDto target = findById(id); + modelMapper.map(userDto, target); + User targetEntity = modelMapper.map(target, User.class); + userRepository.save(targetEntity); + return target; + } + + public void deleteById(UUID id) { + userRepository.deleteById(id); + } +} diff --git a/src/test/java/com/accenture/codingtest/springbootcodingtest/service/UserServiceTest.java b/src/test/java/com/accenture/codingtest/springbootcodingtest/service/UserServiceTest.java new file mode 100644 index 0000000..170f5c6 --- /dev/null +++ b/src/test/java/com/accenture/codingtest/springbootcodingtest/service/UserServiceTest.java @@ -0,0 +1,56 @@ +package com.accenture.codingtest.springbootcodingtest.service; + +import com.accenture.codingtest.springbootcodingtest.entity.User; +import com.accenture.codingtest.springbootcodingtest.model.UserDto; +import com.accenture.codingtest.springbootcodingtest.repository.UserRepository; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; +import org.modelmapper.ModelMapper; + +import java.util.Optional; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + + +@ExtendWith(MockitoExtension.class) +class UserServiceTest { + + @Mock + private UserRepository userRepository; + + private ModelMapper modelMapper; + + private UUID id; + @Mock + private User getUser; + + @Mock + private UserDto updteUser; + + private UserService userService; + + @Test + void updateByPartially() { + modelMapper = new ModelMapper(); + userService = new UserService(userRepository, modelMapper); + id = UUID.randomUUID(); + + when(getUser.getId()).thenReturn(id); + when(getUser.getUsername()).thenReturn("user1"); + when(getUser.getPassword()).thenReturn("password"); + + when(updteUser.getId()).thenReturn(id); + when(updteUser.getUsername()).thenReturn("user1"); + when(updteUser.getPassword()).thenReturn("newPass"); + + when(userRepository.findById(id)).thenReturn(Optional.of(getUser)); + userService.updateByPartially(id, updteUser); + + } +} \ No newline at end of file From 1116c9ab4d7405a2960f35250fb1133f5af81526 Mon Sep 17 00:00:00 2001 From: Kasun Nadeeshana Date: Wed, 3 May 2023 23:29:07 +0530 Subject: [PATCH 3/6] Create REST APIs --- .../service/UserServiceTest.java | 56 ------------------- 1 file changed, 56 deletions(-) delete mode 100644 src/test/java/com/accenture/codingtest/springbootcodingtest/service/UserServiceTest.java diff --git a/src/test/java/com/accenture/codingtest/springbootcodingtest/service/UserServiceTest.java b/src/test/java/com/accenture/codingtest/springbootcodingtest/service/UserServiceTest.java deleted file mode 100644 index 170f5c6..0000000 --- a/src/test/java/com/accenture/codingtest/springbootcodingtest/service/UserServiceTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.accenture.codingtest.springbootcodingtest.service; - -import com.accenture.codingtest.springbootcodingtest.entity.User; -import com.accenture.codingtest.springbootcodingtest.model.UserDto; -import com.accenture.codingtest.springbootcodingtest.repository.UserRepository; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.junit.jupiter.MockitoExtension; -import org.modelmapper.ModelMapper; - -import java.util.Optional; -import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; - - -@ExtendWith(MockitoExtension.class) -class UserServiceTest { - - @Mock - private UserRepository userRepository; - - private ModelMapper modelMapper; - - private UUID id; - @Mock - private User getUser; - - @Mock - private UserDto updteUser; - - private UserService userService; - - @Test - void updateByPartially() { - modelMapper = new ModelMapper(); - userService = new UserService(userRepository, modelMapper); - id = UUID.randomUUID(); - - when(getUser.getId()).thenReturn(id); - when(getUser.getUsername()).thenReturn("user1"); - when(getUser.getPassword()).thenReturn("password"); - - when(updteUser.getId()).thenReturn(id); - when(updteUser.getUsername()).thenReturn("user1"); - when(updteUser.getPassword()).thenReturn("newPass"); - - when(userRepository.findById(id)).thenReturn(Optional.of(getUser)); - userService.updateByPartially(id, updteUser); - - } -} \ No newline at end of file From 139947b7b8e58ca2e99bd1ea84825754a48720a5 Mon Sep 17 00:00:00 2001 From: Kasun Nadeeshana Date: Wed, 3 May 2023 23:48:38 +0530 Subject: [PATCH 4/6] Implement features --- pom.xml | 4 ++++ .../SpringBootCodingTestApplication.java | 13 ++++++++++--- .../controller/ProjectController.java | 3 +++ .../controller/TaskController.java | 3 +++ .../controller/UserController.java | 4 ++++ .../springbootcodingtest/model/Status.java | 10 ++++++++++ .../springbootcodingtest/service/TaskService.java | 3 +++ 7 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/model/Status.java diff --git a/pom.xml b/pom.xml index 9252099..f8f5609 100644 --- a/pom.xml +++ b/pom.xml @@ -45,6 +45,10 @@ modelmapper 3.1.1 + + org.springframework.boot + spring-boot-starter-security + diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/SpringBootCodingTestApplication.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/SpringBootCodingTestApplication.java index 14e2bbf..7c974c7 100644 --- a/src/main/java/com/accenture/codingtest/springbootcodingtest/SpringBootCodingTestApplication.java +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/SpringBootCodingTestApplication.java @@ -1,13 +1,20 @@ package com.accenture.codingtest.springbootcodingtest; +import org.modelmapper.ModelMapper; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; @SpringBootApplication public class SpringBootCodingTestApplication { - public static void main(String[] args) { - SpringApplication.run(SpringBootCodingTestApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(SpringBootCodingTestApplication.class, args); + } + + @Bean + public ModelMapper modelMapper() { + return new ModelMapper(); + } } diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/ProjectController.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/ProjectController.java index 8efa375..37d3ead 100644 --- a/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/ProjectController.java +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/ProjectController.java @@ -4,6 +4,8 @@ import com.accenture.codingtest.springbootcodingtest.service.ProjectService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.Collection; @@ -26,6 +28,7 @@ public ProjectDto findById(@PathVariable("id") String id) { } @PostMapping("/") + @PreAuthorize("hasAuthority('ROLE_PRODUCT_OWNER')") public ProjectDto create(@RequestBody ProjectDto projectDto) { return projectService.create(projectDto); } diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/TaskController.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/TaskController.java index 5e483c8..2aad6ba 100644 --- a/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/TaskController.java +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/TaskController.java @@ -4,6 +4,7 @@ import com.accenture.codingtest.springbootcodingtest.service.TaskService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.Collection; @@ -26,6 +27,7 @@ public TaskDto findById(@PathVariable("id") String id) { } @PostMapping("/") + @PreAuthorize("hasAuthority('ROLE_PRODUCT_OWNER')") public TaskDto create(@RequestBody TaskDto taskDto) { return taskService.create(taskDto); } @@ -36,6 +38,7 @@ public TaskDto update(@PathVariable("id") String id, @RequestBody TaskDto taskDt } @PatchMapping("/{id}") + @PreAuthorize("hasAuthority('ROLE_PRODUCT_OWNER')") public TaskDto updatePartially(@PathVariable("id") String id, @RequestBody TaskDto taskDto) { return taskService.updatePartially(UUID.fromString(id), taskDto); } diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/UserController.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/UserController.java index ac2b618..8bffbcb 100644 --- a/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/UserController.java +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/controller/UserController.java @@ -4,6 +4,8 @@ import com.accenture.codingtest.springbootcodingtest.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @@ -13,6 +15,8 @@ @RestController @RequestMapping("/api/v1/users") @RequiredArgsConstructor +@PreAuthorize("hasAuthority('ROLE_ADMIN')") +@Secured("ROLE_ADMIN") public class UserController { private final UserService userService; diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/model/Status.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/model/Status.java new file mode 100644 index 0000000..cb61146 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/model/Status.java @@ -0,0 +1,10 @@ +package com.accenture.codingtest.springbootcodingtest.model; + +public enum Status { + NOT_STARTED, + IN_PROGRESS, + READY_FOR_TEST, + COMPLETED + + +} diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/service/TaskService.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/service/TaskService.java index 5739978..e293324 100644 --- a/src/main/java/com/accenture/codingtest/springbootcodingtest/service/TaskService.java +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/service/TaskService.java @@ -12,6 +12,8 @@ import java.util.Collection; import java.util.UUID; +import static com.accenture.codingtest.springbootcodingtest.model.Status.NOT_STARTED; + @Service @RequiredArgsConstructor public class TaskService { @@ -34,6 +36,7 @@ public TaskDto findById(UUID id) { public TaskDto create(TaskDto taskDto) { Task entity = modelMapper.map(taskDto, Task.class); + entity.setStatus(NOT_STARTED.name()); Task createdEntity = taskRepository.save(entity); return modelMapper.map(createdEntity, TaskDto.class); } From fc08fe261b6fb4295f31583eaa8e4af57e2711c6 Mon Sep 17 00:00:00 2001 From: Kasun Nadeeshana Date: Thu, 4 May 2023 00:02:19 +0530 Subject: [PATCH 5/6] Implement features --- pom.xml | 17 +++++ .../config/WebSecurityConfiguration.java | 73 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/main/java/com/accenture/codingtest/springbootcodingtest/config/WebSecurityConfiguration.java diff --git a/pom.xml b/pom.xml index f8f5609..d72f7b3 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ Demo project for Spring Boot 11 + Hoxton.SR6 @@ -49,8 +50,24 @@ org.springframework.boot spring-boot-starter-security + + org.springframework.cloud + spring-cloud-starter-oauth2 + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + diff --git a/src/main/java/com/accenture/codingtest/springbootcodingtest/config/WebSecurityConfiguration.java b/src/main/java/com/accenture/codingtest/springbootcodingtest/config/WebSecurityConfiguration.java new file mode 100644 index 0000000..2223b17 --- /dev/null +++ b/src/main/java/com/accenture/codingtest/springbootcodingtest/config/WebSecurityConfiguration.java @@ -0,0 +1,73 @@ +package com.accenture.codingtest.springbootcodingtest.config; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + + +@Configuration +@EnableWebSecurity +public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { + + @Autowired + private UserDetailsService userDetailsService; + + @Bean + protected AuthenticationManager getAuthenticationManager() throws Exception { + return super.authenticationManager(); + } + + @Bean + PasswordEncoder passwordEncoder() { + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOrigin("*"); + config.addAllowedHeader("*"); + config.addAllowedMethod("OPTIONS"); + config.addAllowedMethod("GET"); + config.addAllowedMethod("POST"); + config.addAllowedMethod("PUT"); + config.addAllowedMethod("PATCH"); + config.addAllowedMethod("DELETE"); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } + + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); + + } + + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable() + .authorizeRequests().antMatchers("/v1/**") + .permitAll() + .antMatchers("/v1/User/**").access("hasRole('ROLE_ADMIN')") + .antMatchers("/v1/Task/**", "/v1/Project/**").access("hasRole('ROLE_PROJECT_OWNER')"); + } + +} From 6bebdf157b936003be2c679300e47d2ee00a8622 Mon Sep 17 00:00:00 2001 From: Kasun Nadeeshana Date: Thu, 4 May 2023 00:10:14 +0530 Subject: [PATCH 6/6] Write test --- .../TestRestTemplate.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/test/java/com/accenture/codingtest/springbootcodingtest/TestRestTemplate.java diff --git a/src/test/java/com/accenture/codingtest/springbootcodingtest/TestRestTemplate.java b/src/test/java/com/accenture/codingtest/springbootcodingtest/TestRestTemplate.java new file mode 100644 index 0000000..59a10b1 --- /dev/null +++ b/src/test/java/com/accenture/codingtest/springbootcodingtest/TestRestTemplate.java @@ -0,0 +1,33 @@ +package com.accenture.codingtest.springbootcodingtest; + +import com.accenture.codingtest.springbootcodingtest.model.ProjectDto; +import com.accenture.codingtest.springbootcodingtest.model.TaskDto; +import com.accenture.codingtest.springbootcodingtest.model.UserDto; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.web.client.RestTemplate; + + +public class TestRestTemplate extends SpringBootCodingTestApplication { + public static final String BASE_URL = "http://localhost:8080"; + RestTemplate restTemplate; + + @BeforeEach + void setUp() { + restTemplate = new RestTemplate(); + } + + @Test + public void testcase(){ + UserDto userDto =new UserDto(); + userDto.setUsername("user1"); + userDto.setPassword("password"); + restTemplate.postForEntity(BASE_URL+"/api/v1/users",userDto, UserDto.class); + + ProjectDto projectDto =new ProjectDto(); + projectDto.setName("project1"); + restTemplate.postForEntity(BASE_URL+"/api/v1/projects",projectDto, UserDto.class); + + } +}