Skip to content

Feature/core functions #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
454 changes: 454 additions & 0 deletions docs/spring_boot_coding_test.postman_collection.json

Large diffs are not rendered by default.

38 changes: 35 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,53 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>


</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>15</source>
<target>15</target>
</configuration>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
package com.accenture.codingtest.springbootcodingtest;

import com.accenture.codingtest.springbootcodingtest.entity.Project;
import com.accenture.codingtest.springbootcodingtest.service.ProjectService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;


@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);



}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.accenture.codingtest.springbootcodingtest.config;

import com.accenture.codingtest.springbootcodingtest.model.Role;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.builders.WebSecurity;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

private final UserDetailsService userDetailsService;

public SecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/v1/users").hasRole(Role.ADMIN.name())
.antMatchers(HttpMethod.GET, "/api/v1/users/{user_id}").hasRole(Role.ADMIN.name())
.antMatchers(HttpMethod.POST, "/api/v1/users").hasRole(Role.ADMIN.name())
.antMatchers(HttpMethod.PUT, "/api/v1/users/{user_id}").hasRole(Role.ADMIN.name())
.antMatchers(HttpMethod.PATCH, "/api/v1/users/{user_id}").hasRole(Role.ADMIN.name())
.antMatchers(HttpMethod.DELETE, "/api/v1/users/{user_id}").hasRole(Role.ADMIN.name())

.antMatchers(HttpMethod.GET, "/api/v1/projects").hasAnyRole(Role.ADMIN.name(),Role.PROJECT_OWNER.name())
.antMatchers(HttpMethod.GET, "/api/v1/projects/{project_id}").hasAnyRole(Role.ADMIN.name(),Role.PROJECT_OWNER.name())
.antMatchers(HttpMethod.POST, "/api/v1/projects").hasRole(Role.PROJECT_OWNER.name())
.antMatchers(HttpMethod.PUT, "/api/v1/projects/{project_id}").hasAnyRole(Role.ADMIN.name(),Role.PROJECT_OWNER.name())
.antMatchers(HttpMethod.DELETE, "/api/v1/projects/{project_id}").hasRole(Role.ADMIN.name())

.antMatchers(HttpMethod.GET, "/api/v1/tasks").hasAnyRole(Role.ADMIN.name(),Role.PROJECT_OWNER.name())
.antMatchers(HttpMethod.GET, "/api/v1/tasks/{task_id}").hasAnyRole(Role.ADMIN.name(),Role.PROJECT_OWNER.name())
.antMatchers(HttpMethod.POST, "/api/v1/tasks").hasRole(Role.PROJECT_OWNER.name())
.antMatchers(HttpMethod.PUT, "/api/v1/tasks/{task_id}").hasRole(Role.PROJECT_OWNER.name())
.antMatchers(HttpMethod.PATCH, "/api/v1/tasks/{task_id}").hasRole(Role.PROJECT_OWNER.name())
.antMatchers(HttpMethod.DELETE, "/api/v1/tasks/{task_id}").hasAnyRole(Role.ADMIN.name(),Role.PROJECT_OWNER.name())
.antMatchers(HttpMethod.PATCH, "/api/v1/tasks/{task_id}/status").hasRole(Role.TEAM_MEMBER.name())

.anyRequest().authenticated()
.and()
.httpBasic();
}


@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/h2-console/**");
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.accenture.codingtest.springbootcodingtest.config;

import com.accenture.codingtest.springbootcodingtest.entity.User;
import com.accenture.codingtest.springbootcodingtest.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Set;
import java.util.stream.Collectors;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;

@Autowired
public UserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));


Set<GrantedAuthority> authorities = user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getAuthority()))
.collect(Collectors.toSet());

return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
authorities
);
}

}


Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.accenture.codingtest.springbootcodingtest.controller;

import com.accenture.codingtest.springbootcodingtest.entity.Project;
import com.accenture.codingtest.springbootcodingtest.service.ProjectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping(value = "api/v1/projects")
public class ProjectController {

private final ProjectService projectService;

@Autowired
public ProjectController(ProjectService projectService) {
this.projectService = projectService;
}


@GetMapping
public ResponseEntity<Page<Project>> getAllProjects(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "5") int size,
@RequestParam(defaultValue = "name") String sortBy
) {
Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
Page<Project> projects = projectService.getAllProjects(pageable);
return ResponseEntity.ok(projects);
}


@GetMapping("/{project_id}")
public ResponseEntity<Project> getProjectById (@PathVariable("project_id") UUID projectId){
Project project = projectService.findById(projectId);
return ResponseEntity.ok(project);
}

@PostMapping
public ResponseEntity<Project> createProject (@RequestBody Project project){
Project savedProject = projectService.save(project);
return ResponseEntity.status(HttpStatus.CREATED).body(savedProject);
}

@PutMapping("/{project_id}")
public ResponseEntity<Project> updateProjectById (@PathVariable("project_id") UUID projectId,
@RequestBody Project project){
Project updated = projectService.updateProject(projectId, project);

return ResponseEntity.ok(updated);
}

@DeleteMapping("/{project_id}")
public ResponseEntity<Project> deleteProject (@PathVariable("project_id") UUID projectId){
projectService.delete(projectId);
return ResponseEntity.noContent().build();
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.accenture.codingtest.springbootcodingtest.controller;

import com.accenture.codingtest.springbootcodingtest.entity.Task;
import com.accenture.codingtest.springbootcodingtest.model.TaskStatus;
import com.accenture.codingtest.springbootcodingtest.service.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping(value = "/api/v1/tasks")
public class TaskController {
private final TaskService taskService;

@Autowired
public TaskController(TaskService taskService) {
this.taskService = taskService;
}

@GetMapping
public ResponseEntity<List<Task>> getAllTasks() {
List<Task> taskList = taskService.findAll();
return ResponseEntity.ok(taskList);
}

@GetMapping("/{task_id}")
public ResponseEntity<Task> getTaskById(@PathVariable("task_id") UUID taskId) {
Task task = taskService.findById(taskId);
return ResponseEntity.ok(task);
}

@PostMapping
public ResponseEntity<Task> createTask(@RequestBody Task task) {
task.setStatus(TaskStatus.NOT_STARTED);
Task savedTask = taskService.save(task);
return ResponseEntity.status(HttpStatus.CREATED).body(savedTask);
}

@PutMapping("/{task_id}")
public ResponseEntity<Task> updateTask(@PathVariable("task_id") UUID taskId,
@RequestBody Task updatedTask) {
Task task = taskService.updateTask(taskId,updatedTask);
return ResponseEntity.ok(task);
}


@PatchMapping("/{task_id}")
public ResponseEntity<Task> patchTask(@PathVariable("task_id") UUID taskId,
@RequestBody Task updatedTask) {
Task task = taskService.patchTask(taskId,updatedTask);

return ResponseEntity.ok(task);
}

@DeleteMapping("/{task_id}")
public ResponseEntity<Void> deleteTask(@PathVariable("task_id") UUID taskId) {

taskService.delete(taskId);
return ResponseEntity.noContent().build();
}


@PatchMapping("/{task_id}/status")
public ResponseEntity<Task> updateTaskStatus(@PathVariable("task_id") UUID taskId,
@RequestParam("status") TaskStatus status) {
Task task = taskService.updateTaskStatus(taskId,status);
return ResponseEntity.ok(task);
}




}

Loading