Refactor PrestashopProxyController to enhance request forwarding and error handling
This commit is contained in:
@@ -1,101 +1,141 @@
|
|||||||
package fr.gameovergne.api.controller.prestashop;
|
package fr.gameovergne.api.controller.prestashop;
|
||||||
|
|
||||||
import fr.gameovergne.api.service.prestashop.PrestashopClient;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.springframework.http.MediaType;
|
import org.slf4j.Logger;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.*;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.client.HttpStatusCodeException;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/ps")
|
@RequestMapping("/api/ps")
|
||||||
public class PrestashopProxyController {
|
public class PrestashopProxyController {
|
||||||
|
|
||||||
private final PrestashopClient prestashopClient;
|
private static final Logger log = LoggerFactory.getLogger(PrestashopProxyController.class);
|
||||||
|
|
||||||
public PrestashopProxyController(PrestashopClient prestashopClient) {
|
private final RestTemplate restTemplate = new RestTemplate();
|
||||||
this.prestashopClient = prestashopClient;
|
|
||||||
|
private final String baseUrl; // ex : https://shop.gameovergne.fr
|
||||||
|
private final String basicAuth; // base64 SANS le "Basic "
|
||||||
|
|
||||||
|
public PrestashopProxyController(
|
||||||
|
@Value("${prestashop.base-url}") String baseUrl,
|
||||||
|
@Value("${prestashop.basic-auth}") String basicAuth
|
||||||
|
) {
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
this.basicAuth = basicAuth;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extractPath(HttpServletRequest request) {
|
// =============== Helpers ===============
|
||||||
String fullPath = request.getRequestURI(); // ex: /api/ps/products/446
|
|
||||||
String contextPath = request.getContextPath(); // souvent ""
|
|
||||||
String relative = fullPath.substring(contextPath.length()); // /api/ps/products/446
|
|
||||||
|
|
||||||
// On enlève le préfixe /api/ps => /products/446
|
private String extractPath(HttpServletRequest request) {
|
||||||
|
// /api/ps/products/446 -> /products/446
|
||||||
|
String fullPath = request.getRequestURI();
|
||||||
|
String contextPath = request.getContextPath(); // souvent ""
|
||||||
|
String relative = fullPath.substring(contextPath.length());
|
||||||
return relative.replaceFirst("^/api/ps", "");
|
return relative.replaceFirst("^/api/ps", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===========================
|
private String readBody(HttpServletRequest request) throws IOException {
|
||||||
* GET
|
try (BufferedReader reader = request.getReader()) {
|
||||||
* =========================== */
|
return reader.lines().collect(Collectors.joining(System.lineSeparator()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<String> forward(HttpServletRequest request,
|
||||||
|
HttpMethod method,
|
||||||
|
String bodyIfAny) {
|
||||||
|
String path = extractPath(request);
|
||||||
|
String rawQuery = request.getQueryString();
|
||||||
|
|
||||||
|
UriComponentsBuilder builder = UriComponentsBuilder
|
||||||
|
.fromHttpUrl(baseUrl)
|
||||||
|
.path("/api")
|
||||||
|
.path(path);
|
||||||
|
|
||||||
|
if (rawQuery != null && !rawQuery.isBlank()) {
|
||||||
|
builder.query(rawQuery); // on garde EXACTEMENT la query envoyée par Angular
|
||||||
|
}
|
||||||
|
|
||||||
|
String url = builder.build(true).toUriString();
|
||||||
|
log.info("[PrestaProxy] {} {}", method.name(), url);
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
|
||||||
|
// Auth Presta (cachée côté backend)
|
||||||
|
headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth);
|
||||||
|
|
||||||
|
// On propage Accept/Content-Type du front pour garder le même comportement qu’en dev
|
||||||
|
String accept = request.getHeader(HttpHeaders.ACCEPT);
|
||||||
|
if (accept != null) {
|
||||||
|
headers.set(HttpHeaders.ACCEPT, accept);
|
||||||
|
}
|
||||||
|
|
||||||
|
String contentType = request.getHeader(HttpHeaders.CONTENT_TYPE);
|
||||||
|
if (contentType != null) {
|
||||||
|
headers.set(HttpHeaders.CONTENT_TYPE, contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpEntity<String> entity = new HttpEntity<>(bodyIfAny, headers);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
entity,
|
||||||
|
String.class
|
||||||
|
);
|
||||||
|
|
||||||
|
// On renvoie EXACTEMENT le status + headers + body de Presta au front
|
||||||
|
return ResponseEntity
|
||||||
|
.status(response.getStatusCode())
|
||||||
|
.headers(response.getHeaders())
|
||||||
|
.body(response.getBody());
|
||||||
|
|
||||||
|
} catch (HttpStatusCodeException ex) {
|
||||||
|
// On propage aussi les erreurs Presta (4xx/5xx) sans les transformer en 500 Spring
|
||||||
|
HttpHeaders respHeaders = ex.getResponseHeaders() != null
|
||||||
|
? ex.getResponseHeaders()
|
||||||
|
: new HttpHeaders();
|
||||||
|
|
||||||
|
log.error("[PrestaProxy] {} {} -> HTTP {}",
|
||||||
|
method.name(), url, ex.getStatusCode().value());
|
||||||
|
|
||||||
|
return ResponseEntity
|
||||||
|
.status(ex.getStatusCode())
|
||||||
|
.headers(respHeaders)
|
||||||
|
.body(ex.getResponseBodyAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============== Routes ===============
|
||||||
|
|
||||||
@GetMapping("/**")
|
@GetMapping("/**")
|
||||||
public ResponseEntity<String> proxyGet(HttpServletRequest request) {
|
public ResponseEntity<String> proxyGet(HttpServletRequest request) {
|
||||||
String path = extractPath(request);
|
return forward(request, HttpMethod.GET, null);
|
||||||
String rawQuery = request.getQueryString(); // on laisse Angular décider des filtres
|
|
||||||
|
|
||||||
String body = prestashopClient.get(path, rawQuery);
|
|
||||||
|
|
||||||
return ResponseEntity
|
|
||||||
.ok()
|
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
|
||||||
.body(body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===========================
|
|
||||||
* POST
|
|
||||||
* =========================== */
|
|
||||||
|
|
||||||
@PostMapping("/**")
|
|
||||||
public ResponseEntity<String> proxyPost(
|
|
||||||
HttpServletRequest request,
|
|
||||||
@RequestBody String rawBody
|
|
||||||
) {
|
|
||||||
String path = extractPath(request);
|
|
||||||
String rawQuery = request.getQueryString();
|
|
||||||
|
|
||||||
String body = prestashopClient.post(path, rawQuery, rawBody);
|
|
||||||
|
|
||||||
return ResponseEntity
|
|
||||||
.ok()
|
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
|
||||||
.body(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===========================
|
|
||||||
* PUT
|
|
||||||
* =========================== */
|
|
||||||
|
|
||||||
@PutMapping("/**")
|
|
||||||
public ResponseEntity<String> proxyPut(
|
|
||||||
HttpServletRequest request,
|
|
||||||
@RequestBody String rawBody
|
|
||||||
) {
|
|
||||||
String path = extractPath(request);
|
|
||||||
String rawQuery = request.getQueryString();
|
|
||||||
|
|
||||||
String body = prestashopClient.put(path, rawQuery, rawBody);
|
|
||||||
|
|
||||||
return ResponseEntity
|
|
||||||
.ok()
|
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
|
||||||
.body(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===========================
|
|
||||||
* DELETE
|
|
||||||
* =========================== */
|
|
||||||
|
|
||||||
@DeleteMapping("/**")
|
@DeleteMapping("/**")
|
||||||
public ResponseEntity<String> proxyDelete(HttpServletRequest request) {
|
public ResponseEntity<String> proxyDelete(HttpServletRequest request) {
|
||||||
String path = extractPath(request);
|
return forward(request, HttpMethod.DELETE, null);
|
||||||
String rawQuery = request.getQueryString();
|
}
|
||||||
|
|
||||||
String body = prestashopClient.delete(path, rawQuery);
|
@PostMapping("/**")
|
||||||
|
public ResponseEntity<String> proxyPost(HttpServletRequest request) throws IOException {
|
||||||
|
String body = readBody(request); // XML ou JSON, on ne touche à rien
|
||||||
|
return forward(request, HttpMethod.POST, body);
|
||||||
|
}
|
||||||
|
|
||||||
return ResponseEntity
|
@PutMapping("/**")
|
||||||
.ok()
|
public ResponseEntity<String> proxyPut(HttpServletRequest request) throws IOException {
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
String body = readBody(request);
|
||||||
.body(body);
|
return forward(request, HttpMethod.PUT, body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
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.lang.Nullable;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.web.client.RestClientException;
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class PrestashopClient {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(PrestashopClient.class);
|
|
||||||
|
|
||||||
private final RestTemplate restTemplate = new RestTemplate();
|
|
||||||
|
|
||||||
private final String baseUrl;
|
|
||||||
private final String basicAuth; // base64 SANS le "Basic "
|
|
||||||
|
|
||||||
public PrestashopClient(
|
|
||||||
@Value("${prestashop.base-url}") String baseUrl,
|
|
||||||
@Value("${prestashop.basic-auth}") String basicAuth
|
|
||||||
) {
|
|
||||||
this.baseUrl = baseUrl;
|
|
||||||
this.basicAuth = basicAuth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===========================
|
|
||||||
* GET (proxy)
|
|
||||||
* =========================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GET générique vers PrestaShop.
|
|
||||||
*
|
|
||||||
* - On part de la query envoyée par le front (filters, price[use_tax], ...).
|
|
||||||
* - On force/se remplace uniquement output_format=JSON et display=full.
|
|
||||||
*/
|
|
||||||
public String get(String path, @Nullable String rawQuery) {
|
|
||||||
UriComponentsBuilder builder = UriComponentsBuilder
|
|
||||||
.fromHttpUrl(baseUrl)
|
|
||||||
.path("/api")
|
|
||||||
.path(path);
|
|
||||||
|
|
||||||
// On remet la query EXACTEMENT comme envoyée par Angular
|
|
||||||
if (rawQuery != null && !rawQuery.isBlank()) {
|
|
||||||
builder.query(rawQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
// On force juste ces deux paramètres
|
|
||||||
builder.replaceQueryParam("output_format", "JSON");
|
|
||||||
builder.replaceQueryParam("display", "full");
|
|
||||||
|
|
||||||
String url = builder.build(true).toUriString();
|
|
||||||
|
|
||||||
log.info("[PrestaShop] GET {}", url);
|
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth);
|
|
||||||
|
|
||||||
HttpEntity<Void> entity = new HttpEntity<>(headers);
|
|
||||||
|
|
||||||
try {
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
|
||||||
url,
|
|
||||||
HttpMethod.GET,
|
|
||||||
entity,
|
|
||||||
String.class
|
|
||||||
);
|
|
||||||
|
|
||||||
log.info("[PrestaShop] Réponse HTTP {} pour {}", response.getStatusCode(), url);
|
|
||||||
|
|
||||||
if (!response.getStatusCode().is2xxSuccessful()) {
|
|
||||||
throw new RuntimeException("PrestaShop returned non-2xx status: "
|
|
||||||
+ response.getStatusCode() + " for URL " + url);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.getBody();
|
|
||||||
} catch (RestClientException e) {
|
|
||||||
log.error("[PrestaShop] Erreur GET {}", url, e);
|
|
||||||
throw new RuntimeException("Erreur GET PrestaShop", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===========================
|
|
||||||
* POST
|
|
||||||
* =========================== */
|
|
||||||
|
|
||||||
public String post(String path, @Nullable String rawQuery, String body) {
|
|
||||||
UriComponentsBuilder builder = UriComponentsBuilder
|
|
||||||
.fromHttpUrl(baseUrl)
|
|
||||||
.path("/api")
|
|
||||||
.path(path);
|
|
||||||
|
|
||||||
if (rawQuery != null && !rawQuery.isBlank()) {
|
|
||||||
builder.query(rawQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pour POST, tu peux choisir de forcer output_format si tu veux la réponse en JSON
|
|
||||||
builder.replaceQueryParam("output_format", "JSON");
|
|
||||||
|
|
||||||
String url = builder.build(true).toUriString();
|
|
||||||
|
|
||||||
log.info("[PrestaShop] POST {}", url);
|
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth);
|
|
||||||
headers.setContentType(MediaType.APPLICATION_XML); // Presta attend du XML
|
|
||||||
|
|
||||||
HttpEntity<String> entity = new HttpEntity<>(body, headers);
|
|
||||||
|
|
||||||
try {
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
|
||||||
url,
|
|
||||||
HttpMethod.POST,
|
|
||||||
entity,
|
|
||||||
String.class
|
|
||||||
);
|
|
||||||
|
|
||||||
log.info("[PrestaShop] Réponse HTTP {} pour {}", response.getStatusCode(), url);
|
|
||||||
|
|
||||||
if (!response.getStatusCode().is2xxSuccessful()) {
|
|
||||||
throw new RuntimeException("PrestaShop returned non-2xx status: "
|
|
||||||
+ response.getStatusCode() + " for URL " + url);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.getBody();
|
|
||||||
} catch (RestClientException e) {
|
|
||||||
log.error("[PrestaShop] Erreur POST {}", url, e);
|
|
||||||
throw new RuntimeException("Erreur POST PrestaShop", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===========================
|
|
||||||
* PUT
|
|
||||||
* =========================== */
|
|
||||||
|
|
||||||
public String put(String path, @Nullable String rawQuery, String body) {
|
|
||||||
UriComponentsBuilder builder = UriComponentsBuilder
|
|
||||||
.fromHttpUrl(baseUrl)
|
|
||||||
.path("/api")
|
|
||||||
.path(path);
|
|
||||||
|
|
||||||
if (rawQuery != null && !rawQuery.isBlank()) {
|
|
||||||
builder.query(rawQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.replaceQueryParam("output_format", "JSON");
|
|
||||||
|
|
||||||
String url = builder.build(true).toUriString();
|
|
||||||
|
|
||||||
log.info("[PrestaShop] PUT {}", url);
|
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth);
|
|
||||||
headers.setContentType(MediaType.APPLICATION_XML);
|
|
||||||
|
|
||||||
HttpEntity<String> entity = new HttpEntity<>(body, headers);
|
|
||||||
|
|
||||||
try {
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
|
||||||
url,
|
|
||||||
HttpMethod.PUT,
|
|
||||||
entity,
|
|
||||||
String.class
|
|
||||||
);
|
|
||||||
|
|
||||||
log.info("[PrestaShop] Réponse HTTP {} pour {}", response.getStatusCode(), url);
|
|
||||||
|
|
||||||
if (!response.getStatusCode().is2xxSuccessful()) {
|
|
||||||
throw new RuntimeException("PrestaShop returned non-2xx status: "
|
|
||||||
+ response.getStatusCode() + " for URL " + url);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.getBody();
|
|
||||||
} catch (RestClientException e) {
|
|
||||||
log.error("[PrestaShop] Erreur PUT {}", url, e);
|
|
||||||
throw new RuntimeException("Erreur PUT PrestaShop", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===========================
|
|
||||||
* DELETE
|
|
||||||
* =========================== */
|
|
||||||
|
|
||||||
public String delete(String path, @Nullable String rawQuery) {
|
|
||||||
UriComponentsBuilder builder = UriComponentsBuilder
|
|
||||||
.fromHttpUrl(baseUrl)
|
|
||||||
.path("/api")
|
|
||||||
.path(path);
|
|
||||||
|
|
||||||
if (rawQuery != null && !rawQuery.isBlank()) {
|
|
||||||
builder.query(rawQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.replaceQueryParam("output_format", "JSON");
|
|
||||||
|
|
||||||
String url = builder.build(true).toUriString();
|
|
||||||
|
|
||||||
log.info("[PrestaShop] DELETE {}", url);
|
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth);
|
|
||||||
|
|
||||||
HttpEntity<Void> entity = new HttpEntity<>(headers);
|
|
||||||
|
|
||||||
try {
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
|
||||||
url,
|
|
||||||
HttpMethod.DELETE,
|
|
||||||
entity,
|
|
||||||
String.class
|
|
||||||
);
|
|
||||||
|
|
||||||
log.info("[PrestaShop] Réponse HTTP {} pour {}", response.getStatusCode(), url);
|
|
||||||
|
|
||||||
if (!response.getStatusCode().is2xxSuccessful()) {
|
|
||||||
throw new RuntimeException("PrestaShop returned non-2xx status: "
|
|
||||||
+ response.getStatusCode() + " for URL " + url);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.getBody();
|
|
||||||
} catch (RestClientException e) {
|
|
||||||
log.error("[PrestaShop] Erreur DELETE {}", url, e);
|
|
||||||
throw new RuntimeException("Erreur DELETE PrestaShop", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: false,
|
||||||
apiUrl: 'http://localhost:3000/api',
|
apiUrl: 'http://localhost:3000/api',
|
||||||
psUrl: '/ps'
|
psUrl: 'http://localhost:3000/api/ps'
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user