diff --git a/api/pom.xml b/api/pom.xml index bb77c35..1377aa6 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -100,15 +100,6 @@ spring-boot-starter-test test - - org.springframework.boot - spring-boot-starter-webflux - - - io.projectreactor - reactor-test - test - diff --git a/api/src/main/java/fr/gameovergne/api/config/AppConfig.java b/api/src/main/java/fr/gameovergne/api/config/AppConfig.java deleted file mode 100644 index 7702bdb..0000000 --- a/api/src/main/java/fr/gameovergne/api/config/AppConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package fr.gameovergne.api.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.reactive.function.client.WebClient; - -@Configuration -public class AppConfig { - - @Bean - public WebClient prestashopWebClient(WebClient.Builder builder) { - return builder.build(); - } -} \ No newline at end of file diff --git a/api/src/main/java/fr/gameovergne/api/controller/prestashop/PrestashopProxyController.java b/api/src/main/java/fr/gameovergne/api/controller/prestashop/PrestashopProxyController.java index d0fa1ee..a6dfcd0 100644 --- a/api/src/main/java/fr/gameovergne/api/controller/prestashop/PrestashopProxyController.java +++ b/api/src/main/java/fr/gameovergne/api/controller/prestashop/PrestashopProxyController.java @@ -1,69 +1,32 @@ package fr.gameovergne.api.controller.prestashop; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; +import fr.gameovergne.api.service.prestashop.PrestashopClient; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.ResponseEntity; -import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.*; -import org.springframework.web.reactive.function.client.WebClient; -import org.springframework.web.util.UriComponentsBuilder; -import reactor.core.publisher.Mono; + @RestController @RequestMapping("/api/ps") public class PrestashopProxyController { - private final WebClient webClient; + private final PrestashopClient prestashopClient; - @Value("${prestashop.api.base-url}") - private String prestaBaseUrl; - - @Value("${prestashop.api.key}") - private String prestaApiKey; // ta clé déjà encodée en Base64 - - public PrestashopProxyController(WebClient prestashopWebClient) { - this.webClient = prestashopWebClient; + public PrestashopProxyController(PrestashopClient prestashopClient) { + this.prestashopClient = prestashopClient; } - // ----------- SUPPLIERS ----------- - @GetMapping("/suppliers") - public ResponseEntity getSuppliers(@RequestParam MultiValueMap params) { - return forwardGet("/suppliers", params); - } + @GetMapping("/**") + public ResponseEntity proxyGet(HttpServletRequest request) { + String fullPath = request.getRequestURI(); // ex: /api/ps/suppliers + String contextPath = request.getContextPath(); // souvent "" + String relative = fullPath.substring(contextPath.length()); // /api/ps/suppliers - // ----------- CATEGORIES ----------- - @GetMapping("/categories") - public ResponseEntity getCategories(@RequestParam MultiValueMap params) { - return forwardGet("/categories", params); - } + // On enlève le préfixe /api/ps -> /suppliers + String path = relative.replaceFirst("^/api/ps", ""); - // ----------- MANUFACTURERS ----------- - @GetMapping("/manufacturers") - public ResponseEntity getManufacturers(@RequestParam MultiValueMap params) { - return forwardGet("/manufacturers", params); - } + String query = request.getQueryString(); // ex: display=%5Bid,name,active%5D&output_format=JSON - // ----------- Méthode commune de forward ----------- - - private ResponseEntity forwardGet(String resourcePath, MultiValueMap params) { - // IMPORTANT : build(false) => ne PAS ré-encoder les crochets - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(prestaBaseUrl + resourcePath); - - params.forEach((key, values) -> values.forEach(v -> builder.queryParam(key, v))); - - String targetUrl = builder.build(false).toUriString(); // false => pas de double encodage - - Mono> monoResponse = webClient - .get() - .uri(targetUrl) - .header(HttpHeaders.AUTHORIZATION, "Basic " + prestaApiKey) - .accept(MediaType.APPLICATION_JSON) - .retrieve() - .toEntity(String.class); - - // On bloque car ton contrôleur est synchrone (MVC) - return monoResponse.block(); + return prestashopClient.get(path, query); } } \ No newline at end of file diff --git a/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java b/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java index c6c7ae5..8e9645d 100644 --- a/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java +++ b/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java @@ -1,72 +1,94 @@ -// FILE: api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java package fr.gameovergne.api.service.prestashop; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.*; -import org.springframework.stereotype.Component; -import org.springframework.web.client.HttpStatusCodeException; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; -@Component +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.List; +import java.util.Map; + +@Service public class PrestashopClient { - private static final Logger log = LoggerFactory.getLogger(PrestashopClient.class); + private final HttpClient httpClient = HttpClient.newHttpClient(); - private final RestTemplate restTemplate = new RestTemplate(); + private final String baseUrl; + private final String basicAuth; // valeur déjà encodée Base64 (sans le "Basic ") - @Value("${prestashop.base-url}") - private String baseUrl; - - @Value("${prestashop.basic-auth}") - private String basicAuth; + public PrestashopClient( + @Value("${prestashop.base-url}") String baseUrl, + @Value("${prestashop.basic-auth}") String basicAuth + ) { + this.baseUrl = baseUrl; + this.basicAuth = basicAuth; + } public ResponseEntity get(String path, String query) { + // path : ex "/suppliers" + // query : ex "display=%5Bid,name,active%5D&output_format=JSON" + String url = buildUrl(path, query); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Authorization", "Basic " + basicAuth) + .GET() + .build(); + try { - String url = baseUrl + "/api" + path; + HttpResponse response = + httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(url); - - if (query != null && !query.isBlank()) { - // on réinjecte TELS QUELS les query params venant du front - builder.query(query); + // On reconstruit une ResponseEntity en gardant le status PrestaShop + HttpHeaders springHeaders = new HttpHeaders(); + for (Map.Entry> entry : response.headers().map().entrySet()) { + springHeaders.put(entry.getKey(), entry.getValue()); } - String finalUrl = builder.build(true).toUriString(); + return new ResponseEntity<>(response.body(), springHeaders, + HttpStatus.valueOf(response.statusCode())); - HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", basicAuth); - headers.setAccept(MediaType.parseMediaTypes("application/json")); - - HttpEntity entity = new HttpEntity<>(headers); - - log.debug("[Presta] GET {}", finalUrl); - - ResponseEntity response = restTemplate.exchange( - finalUrl, - HttpMethod.GET, - entity, - String.class - ); - - log.debug("[Presta] status={} body={}", response.getStatusCode(), response.getBody()); - return ResponseEntity.status(response.getStatusCode()).body(response.getBody()); - - } catch (HttpStatusCodeException e) { - // <-- ICI on garde le vrai status + body de Prestashop - log.error("[Presta] HTTP error {} body={}", e.getStatusCode(), e.getResponseBodyAsString()); - return ResponseEntity - .status(e.getStatusCode()) - .body(e.getResponseBodyAsString()); - } catch (Exception e) { - log.error("[Presta] Unexpected error while calling Presta", e); - return ResponseEntity - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .body("{\"error\":\"Unexpected error while calling Prestashop\"}"); + } catch (IOException | InterruptedException e) { + // En prod tu pourrais logger proprement + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body("{\"error\":\"Failed to call PrestaShop Webservice\"}"); } } + + private String buildUrl(String path, String query) { + StringBuilder sb = new StringBuilder(); + + // baseUrl, ex: https://shop.gameovergne.fr + sb.append(baseUrl); + if (baseUrl.endsWith("/")) { + sb.setLength(sb.length() - 1); // on enlève le / final si présent + } + + // /api + sb.append("/api"); + + // path, ex: "/suppliers" + if (path != null && !path.isBlank()) { + if (!path.startsWith("/")) { + sb.append('/'); + } + sb.append(path); + } + + // query déjà encodée par le navigateur : NE PAS la ré-encoder ! + if (query != null && !query.isBlank()) { + sb.append('?').append(query); + } + + return sb.toString(); + } } \ No newline at end of file