-
Notifications
You must be signed in to change notification settings - Fork 0
Fix 403 Forbidden and ~31s response time for MediaPage (tvserie) query #38
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -211,7 +211,7 @@ | |
| @Transactional | ||
| public Optional<MediaModel> findByIdEager(Integer id) { | ||
| var fieldList = new ArrayList<Field>(); | ||
| MediaModel media = (MediaModel) mediaRepository.findById(id).orElseGet(null); | ||
|
Check warning on line 214 in src/main/java/com/espacogeek/geek/services/impl/MediaServiceImpl.java
|
||
|
|
||
| if (media == null) | ||
| return Optional.empty(); | ||
|
|
@@ -366,27 +366,7 @@ | |
| response.setNumber(medias.getNumber()); | ||
| response.setSize(medias.getSize()); | ||
|
|
||
| var mediasList = medias.getContent(); | ||
| for (MediaModel mediaModel : mediasList) { | ||
| if (MediaUtils.updateMediaWhenLastTimeUpdateMoreThanOneDay(mediaModel)) { | ||
| switch (mediaModel.getMediaCategory().getId()) { | ||
| case 5: | ||
| case 1: | ||
| serieController.updateArtworks(mediaModel, null); | ||
| break; | ||
| case 7: | ||
| case 4: | ||
| genericMediaDataController.updateArtworks(mediaModel, null, typeReferenceService.findById(MediaDataController.ExternalReferenceType.TMDB.getId()).get(), movieAPI); | ||
| break; | ||
| case 2: | ||
| case 3: | ||
| genericMediaDataController.updateArtworks(mediaModel, null, typeReferenceService.findById(MediaDataController.ExternalReferenceType.IGDB.getId()).get(), gamesAndVNsAPI); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| response.setContent(MediaSimplefied.fromMediaModelList(mediasList)); | ||
| response.setContent(MediaSimplefied.fromMediaModelList(medias.getContent())); | ||
|
|
||
| return response; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,9 +17,13 @@ | |
| import com.espacogeek.geek.config.JwtConfig; | ||
| import com.espacogeek.geek.config.SecurityConfig; | ||
| import com.espacogeek.geek.controllers.DailyQuoteArtworkController; | ||
| import com.espacogeek.geek.controllers.MediaController; | ||
| import com.espacogeek.geek.models.DailyQuoteArtworkModel; | ||
| import com.espacogeek.geek.services.DailyQuoteArtworkService; | ||
| import com.espacogeek.geek.services.MediaService; | ||
| import com.espacogeek.geek.services.impl.UserDetailsServiceImpl; | ||
| import com.espacogeek.geek.types.MediaPage; | ||
| import com.espacogeek.geek.types.MediaSimplefied; | ||
| import com.espacogeek.geek.types.Scalars; | ||
| import com.espacogeek.geek.utils.TokenUtils; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
|
@@ -62,6 +66,7 @@ class BrowserCorsRequestTest { | |
| @SpringBootApplication | ||
| @Import({ | ||
| DailyQuoteArtworkController.class, | ||
| MediaController.class, | ||
| SecurityConfig.class, | ||
| JwtConfig.class, | ||
| JwtAuthenticationFilter.class, | ||
|
|
@@ -80,6 +85,9 @@ RuntimeWiringConfigurer dateScalarConfigurer() { | |
| @MockitoBean | ||
| private DailyQuoteArtworkService dailyQuoteArtworkService; | ||
|
|
||
| @MockitoBean | ||
| private MediaService mediaService; | ||
|
|
||
| @MockitoBean | ||
| private UserDetailsServiceImpl userDetailsService; | ||
|
|
||
|
|
@@ -125,6 +133,39 @@ private String graphqlPayload() throws Exception { | |
| return objectMapper.writeValueAsString(body); | ||
| } | ||
|
|
||
| private MediaPage stubTvSerieMediaPage() { | ||
| MediaSimplefied item = new MediaSimplefied(); | ||
| item.setId(1); | ||
| item.setName("Stranger Things"); | ||
| item.setCover("https://example.com/stranger-things.jpg"); | ||
|
|
||
| MediaPage page = new MediaPage(); | ||
| page.setContent(java.util.List.of(item)); | ||
| page.setTotalElements(1); | ||
| page.setTotalPages(1); | ||
| return page; | ||
| } | ||
|
|
||
| private String tvSeriePayload() throws Exception { | ||
| Map<String, Object> body = Map.of( | ||
| "operationName", "MediaPage", | ||
| "variables", Map.of("name", "stranger"), | ||
| "query", """ | ||
| query MediaPage($name: String) { | ||
| tvserie(name: $name) { | ||
| content { | ||
| id | ||
| name | ||
| cover | ||
| } | ||
| totalElements | ||
| totalPages | ||
| } | ||
| }""" | ||
| ); | ||
| return objectMapper.writeValueAsString(body); | ||
| } | ||
|
|
||
| // ---- CORS preflight tests ---- | ||
|
|
||
| @Test | ||
|
|
@@ -239,4 +280,77 @@ void browserPost_AllowedOrigin_ResponseShouldExposeCorrectHeaders() throws Excep | |
| assertThat(exposeHeaders).contains("Content-Type"); | ||
| assertThat(exposeHeaders).contains("Set-Cookie"); | ||
| } | ||
|
|
||
| // ---- tvserie MediaPage query tests ---- | ||
|
|
||
| @Test | ||
| void tvSerieMediaPage_AllowedOrigin_ShouldReturn200WithCorsHeaders() throws Exception { | ||
| when(mediaService.findSerieByIdOrName( | ||
| org.mockito.ArgumentMatchers.isNull(), | ||
| org.mockito.ArgumentMatchers.eq("stranger"), | ||
| org.mockito.ArgumentMatchers.any())) | ||
| .thenReturn(stubTvSerieMediaPage()); | ||
|
|
||
| // Reproduce the exact browser request from the issue report | ||
| MvcResult mvcResult = mockMvc.perform(post("/") | ||
| .contentType(MediaType.APPLICATION_JSON) | ||
| .header(HttpHeaders.ORIGIN, ALLOWED_ORIGIN) | ||
| .header(HttpHeaders.REFERER, ALLOWED_ORIGIN + "/") | ||
| .header(HttpHeaders.ACCEPT, "*/*") | ||
| .header("Sec-Fetch-Dest", "empty") | ||
| .header("Sec-Fetch-Mode", "cors") | ||
| .header("Sec-Fetch-Site", "same-site") | ||
| .content(tvSeriePayload())) | ||
| .andReturn(); | ||
|
|
||
| MvcResult result = resolveResult(mvcResult); | ||
| assertThat(result.getResponse().getStatus()).isEqualTo(200); | ||
| assertThat(result.getResponse().getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)) | ||
| .isEqualTo(ALLOWED_ORIGIN); | ||
| assertThat(result.getResponse().getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)) | ||
| .isEqualTo("true"); | ||
|
|
||
| String body = result.getResponse().getContentAsString(StandardCharsets.UTF_8); | ||
| var json = objectMapper.readTree(body); | ||
| assertThat(json.path("data").path("tvserie").path("content").get(0).path("name").asText()) | ||
| .isEqualTo("Stranger Things"); | ||
| assertThat(json.path("data").path("tvserie").path("totalElements").asLong()) | ||
| .isEqualTo(1); | ||
| } | ||
|
|
||
| @Test | ||
| void tvSerieMediaPage_NoOriginHeader_ShouldReturn200() throws Exception { | ||
| when(mediaService.findSerieByIdOrName( | ||
| org.mockito.ArgumentMatchers.isNull(), | ||
| org.mockito.ArgumentMatchers.eq("stranger"), | ||
| org.mockito.ArgumentMatchers.any())) | ||
| .thenReturn(stubTvSerieMediaPage()); | ||
|
|
||
| // A request without Origin header (like Postman/curl) should work — not return 403 | ||
| MvcResult mvcResult = mockMvc.perform(post("/") | ||
| .contentType(MediaType.APPLICATION_JSON) | ||
| .content(tvSeriePayload())) | ||
| .andReturn(); | ||
|
|
||
| MvcResult result = resolveResult(mvcResult); | ||
| assertThat(result.getResponse().getStatus()).isEqualTo(200); | ||
|
|
||
| String body = result.getResponse().getContentAsString(StandardCharsets.UTF_8); | ||
| var json = objectMapper.readTree(body); | ||
| assertThat(json.path("data").path("tvserie").path("content").get(0).path("name").asText()) | ||
| .isEqualTo("Stranger Things"); | ||
|
Comment on lines
+338
to
+341
|
||
| } | ||
|
|
||
| @Test | ||
| void tvSerieMediaPage_DisallowedOrigin_ShouldReturn403() throws Exception { | ||
| // A browser request from a disallowed origin should be rejected even for tvserie | ||
| mockMvc.perform(post("/") | ||
| .contentType(MediaType.APPLICATION_JSON) | ||
| .header(HttpHeaders.ORIGIN, DISALLOWED_ORIGIN) | ||
| .header("Sec-Fetch-Dest", "empty") | ||
| .header("Sec-Fetch-Mode", "cors") | ||
| .header("Sec-Fetch-Site", "cross-site") | ||
| .content(tvSeriePayload())) | ||
| .andExpect(status().isForbidden()); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -3,22 +3,21 @@ | |||
| import static org.assertj.core.api.Assertions.assertThat; | ||||
| import static org.mockito.ArgumentMatchers.any; | ||||
| import static org.mockito.ArgumentMatchers.anyString; | ||||
|
||||
| import static org.mockito.ArgumentMatchers.anyString; |
Copilot
AI
Mar 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test name says it “ShouldNotReturn403”, but this is a @GraphQlTest using GraphQlTester and it only asserts that there are no GraphQL errors; it doesn’t (and can’t) assert an HTTP 403. Consider renaming the test to match what it actually verifies (e.g., executes without GraphQL errors) to avoid confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The JSON assertion uses
...path("content").get(0)...;JsonNode#get(int)can return null when the array is missing/empty, which can cause a NullPointerException and hide the real test failure. Preferpath(0)(which returns a missing node) and/or assert the array exists and has at least one element before indexing.