Skip to content

Commit 26a8999

Browse files
committed
Implemented Deletion Feature with filter on X-user
1 parent 27b7b82 commit 26a8999

File tree

10 files changed

+107
-7
lines changed

10 files changed

+107
-7
lines changed

application/src/main/java/com/qa/blog/application/ApplicationConfig.java

+12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.qa.blog.application;
22

33
import com.qa.blog.core.*;
4+
import io.swagger.v3.oas.models.OpenAPI;
5+
import io.swagger.v3.oas.models.info.Info;
46
import org.springframework.context.annotation.Bean;
57
import org.springframework.context.annotation.Configuration;
68

@@ -26,4 +28,14 @@ public FindPostById findPost(PostRepository postRepository) {
2628
public SearchPosts searchPosts(PostRepository postRepository) {
2729
return new SearchPostsDefault(postRepository);
2830
}
31+
32+
@Bean
33+
public DeletePostById deletePostById(PostRepository postRepository) {
34+
return new DeletePostByIdDefault(postRepository);
35+
}
36+
37+
@Bean
38+
public OpenAPI customOpenAPI() {
39+
return new OpenAPI().info(new Info().title("QA Blog").version("undefined"));
40+
}
2941
}

application/src/test/java/com/qa/blog/application/steps/PostSteps.java

+22
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@
2020
import java.util.Set;
2121

2222
import static org.hamcrest.Matchers.*;
23+
import static org.junit.jupiter.api.Assertions.assertFalse;
2324

2425
@AutoConfigureWebTestClient
2526
public class PostSteps extends BaseSteps {
27+
private String userType;
28+
2629
@After
2730
public void afterEachScenario() {
2831
deleteDatabase();
@@ -231,6 +234,25 @@ public void theResponseShouldContainResults(int postsQuantity) {
231234
.jsonPath("$.length()").isEqualTo(postsQuantity);
232235
}
233236

237+
@Given("I am an {string}")
238+
public void iAmAn(String userType) {
239+
this.userType = userType;
240+
}
241+
242+
@When("I delete the blog post")
243+
public void iDeleteTheBlogPost() {
244+
resultComponent.actualResponse = webTestClient.delete()
245+
.uri("/post/" + resultComponent.post.getId())
246+
.header("X-user", userType)
247+
.exchange();
248+
}
249+
250+
@Then("the blog post should no longer exist")
251+
public void theBlogPostShouldNoLongerExist() {
252+
boolean postExists = jpaPostRepository.existsById(resultComponent.post.getId());
253+
assertFalse(postExists, "The blog post should no longer exist");
254+
}
255+
234256
public record PostRequest(String title, String content, String author, String image, String category,
235257
List<String> tags) {
236258
}

application/src/test/resources/features/blog/DeletePost.feature

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ Feature: Delete a blog post
33
I want to delete a blog post
44

55
Background:
6-
Given a blog with the following blog post
6+
Given the following blog posts exist:
77
| title | content | author | image | category | tags |
8-
| My Post | some content | Mario Fusco | http://example.com/image.jpg | Technology | tag1, tag2 |
8+
| My Post | some content | Mario Fusco | http://example.com/image.jpg | Technology | tag1,tag2 |
99

1010
Scenario: Successfully delete a blog post
1111
Given I am an "admin"
1212
When I delete the blog post
13-
Then the response status should be 204
13+
Then the response status code should be 204
1414
And the blog post should no longer exist
1515

1616
Scenario: Fail to delete a blog post as a regular user
17-
Given I am a "user"
17+
Given I am an "user"
1818
When I delete the blog post
19-
Then the response status should be 403
19+
Then the response status code should be 403
2020
And the response should contain an error message "Only admins can delete blog posts"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.qa.blog.core;
2+
3+
public interface DeletePostById {
4+
void execute(Long id);
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.qa.blog.core;
2+
3+
public class DeletePostByIdDefault implements DeletePostById {
4+
private final PostRepository postRepository;
5+
6+
public DeletePostByIdDefault(PostRepository postRepository) {
7+
this.postRepository = postRepository;
8+
}
9+
10+
@Override
11+
public void execute(Long id) {
12+
postRepository.deleteById(id);
13+
}
14+
}

core/src/main/java/com/qa/blog/core/PostRepository.java

+1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ public interface PostRepository {
66
Post save(Post post);
77
Post findById(Long id);
88
List<Post> findByTitleAndCategoryAndTags (String title, String category, List<String> tags);
9+
void deleteById(Long id);
910
}

mariadb/src/main/java/com/qa/blog/mariadb/PostRepositoryDefault.java

+5
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,9 @@ public List<Post> findByTitleAndCategoryAndTags(String title, String category, L
4141
return postEntities.stream().map(postEntityMapper::toDomain).toList();
4242
}
4343

44+
@Override
45+
public void deleteById(Long id) {
46+
jpaPostRepository.deleteById(id);
47+
}
48+
4449
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.qa.blog.springweb;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.qa.blog.springweb.exception.ErrorDetails;
5+
import jakarta.servlet.*;
6+
import jakarta.servlet.http.HttpServletRequest;
7+
import jakarta.servlet.http.HttpServletResponse;
8+
import org.springframework.stereotype.Component;
9+
10+
import java.io.IOException;
11+
12+
@Component
13+
public class DeleteActionFilter implements Filter {
14+
@Override
15+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
16+
HttpServletRequest httpRequest = (HttpServletRequest) request;
17+
HttpServletResponse httpResponse = (HttpServletResponse) response;
18+
19+
if ("DELETE".equalsIgnoreCase(httpRequest.getMethod())) {
20+
String userHeader = httpRequest.getHeader("X-user");
21+
if (!"admin".equals(userHeader)) {
22+
httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
23+
ErrorDetails errorDetails = new ErrorDetails("403", "Only admins can delete blog posts");
24+
ObjectMapper objectMapper = new ObjectMapper();
25+
String jsonResponse = objectMapper.writeValueAsString(errorDetails);
26+
httpResponse.setContentType("application/json");
27+
httpResponse.getWriter().write(jsonResponse);
28+
return;
29+
}
30+
}
31+
chain.doFilter(request, response);
32+
}
33+
}

spring-web/src/main/java/com/qa/blog/springweb/PostController.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,16 @@ public class PostController {
2121
private final UpdatePost updatePost;
2222
private final FindPostById findPostById;
2323
private final SearchPosts searchPosts;
24+
private final DeletePostById deletePostById;
2425
private final PostWebMapper postWebMapper;
2526
private final ObjectMapper objectMapper;
2627

27-
public PostController(CreatePost createPost, UpdatePost updatePost, FindPostById findPostById, SearchPosts searchPosts, PostWebMapper postWebMapper, ObjectMapper objectMapper) {
28+
public PostController(CreatePost createPost, UpdatePost updatePost, FindPostById findPostById, SearchPosts searchPosts, DeletePostById deletePostById, PostWebMapper postWebMapper, ObjectMapper objectMapper) {
2829
this.createPost = createPost;
2930
this.updatePost = updatePost;
3031
this.findPostById = findPostById;
3132
this.searchPosts = searchPosts;
33+
this.deletePostById = deletePostById;
3234
this.postWebMapper = postWebMapper;
3335
this.objectMapper = objectMapper;
3436
}
@@ -72,6 +74,12 @@ public ResponseEntity<List<PostDTO>> searchPosts(
7274
return ResponseEntity.ok(postDTOs);
7375
}
7476

77+
@DeleteMapping("/{id}")
78+
public ResponseEntity deletePost(@PathVariable("id") Long id) {
79+
deletePostById.execute(id);
80+
return ResponseEntity.noContent().build();
81+
}
82+
7583
private Post getPatchedPost(JsonNode patch, Post existingPost) throws JsonProcessingException {
7684
PostDTO existingPostToPatch = postWebMapper.toDTO(existingPost);
7785
JsonNode postNode = objectMapper.valueToTree(existingPostToPatch);

spring-web/src/main/java/com/qa/blog/springweb/exception/GlobalExceptionHandler.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ public ResponseEntity<ErrorDetails> handleApiException(BlogException blogExcepti
2222
@ExceptionHandler(PostNotFoundException.class)
2323
public ResponseEntity<ErrorDetails> handleNotFoundException(PostNotFoundException postNotFoundException) {
2424
logger.atInfo().log(postNotFoundException.getMessage());
25-
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ErrorDetails("404", postNotFoundException.getMessage()));
25+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorDetails("404", postNotFoundException.getMessage()));
2626
}
2727
}

0 commit comments

Comments
 (0)