Refactor PrestashopProxyController to improve path extraction and enhance logging for proxy requests
This commit is contained in:
@@ -2,134 +2,142 @@ package fr.gameovergne.api.controller.prestashop;
|
|||||||
|
|
||||||
import fr.gameovergne.api.service.prestashop.PrestashopClient;
|
import fr.gameovergne.api.service.prestashop.PrestashopClient;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import org.slf4j.Logger;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.util.AntPathMatcher;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.springframework.web.servlet.HandlerMapping;
|
||||||
|
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/ps") // <-- IMPORTANT : préfixe unique pour tout le proxy Presta
|
@RequestMapping("/api/ps")
|
||||||
@RequiredArgsConstructor
|
|
||||||
@Slf4j
|
|
||||||
public class PrestashopProxyController {
|
public class PrestashopProxyController {
|
||||||
|
|
||||||
|
Logger log = LoggerFactory.getLogger(PrestashopProxyController.class);
|
||||||
|
|
||||||
private final PrestashopClient prestashopClient;
|
private final PrestashopClient prestashopClient;
|
||||||
|
|
||||||
/**
|
public PrestashopProxyController(PrestashopClient prestashopClient) {
|
||||||
* Extrait le path Presta à partir de l'URL appelée côté front.
|
this.prestashopClient = prestashopClient;
|
||||||
*
|
|
||||||
* Exemple :
|
|
||||||
* contextPath = /gameovergne-api
|
|
||||||
* requestURI = /gameovergne-api/api/ps/categories
|
|
||||||
* prefixToStrip = /gameovergne-api/api/ps
|
|
||||||
* => pathForwarded = /categories
|
|
||||||
*/
|
|
||||||
private String extractPrestaPath(HttpServletRequest request) {
|
|
||||||
String requestUri = request.getRequestURI();
|
|
||||||
String prefix = request.getContextPath() + "/api/ps";
|
|
||||||
if (!requestUri.startsWith(prefix)) {
|
|
||||||
// Sécurité, mais normalement ça ne doit jamais arriver
|
|
||||||
log.warn("extractPrestaPath: URI {} ne commence pas par prefix {}", requestUri, prefix);
|
|
||||||
return requestUri;
|
|
||||||
}
|
|
||||||
String path = requestUri.substring(prefix.length()); // garde le '/' de début
|
|
||||||
if (path.isEmpty()) {
|
|
||||||
path = "/"; // Cas extrême, normalement on ne l’utilise pas
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- GET générique /api/ps/** ----------
|
// --- Helpers communs ---
|
||||||
|
|
||||||
@GetMapping(path = "/**", produces = MediaType.APPLICATION_JSON_VALUE)
|
private String extractPath(HttpServletRequest request) {
|
||||||
|
String fullPath = (String) request.getAttribute(
|
||||||
|
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
|
||||||
|
);
|
||||||
|
String bestMatchPattern = (String) request.getAttribute(
|
||||||
|
HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE
|
||||||
|
);
|
||||||
|
|
||||||
|
String relativePath = new AntPathMatcher()
|
||||||
|
.extractPathWithinPattern(bestMatchPattern, fullPath);
|
||||||
|
|
||||||
|
// On renvoie toujours avec un "/" devant
|
||||||
|
return "/" + relativePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractDecodedQuery(HttpServletRequest request) {
|
||||||
|
String rawQuery = request.getQueryString();
|
||||||
|
if (rawQuery != null) {
|
||||||
|
rawQuery = URLDecoder.decode(rawQuery, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
return rawQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- GET ----------
|
||||||
|
@GetMapping("/**")
|
||||||
public ResponseEntity<String> proxyGet(HttpServletRequest request) {
|
public ResponseEntity<String> proxyGet(HttpServletRequest request) {
|
||||||
String path = extractPrestaPath(request); // ex: /categories
|
String path = extractPath(request);
|
||||||
String rawQuery = request.getQueryString(); // ex: display=[id,name,active]&output_format=JSON
|
String rawQuery = extractDecodedQuery(request);
|
||||||
|
|
||||||
log.info("[Proxy] GET {}", path);
|
ResponseEntity<String> prestaResponse =
|
||||||
|
prestashopClient.getWithRawQuery(path, rawQuery);
|
||||||
|
|
||||||
try {
|
return ResponseEntity
|
||||||
return prestashopClient.getWithRawQuery(path, rawQuery);
|
.status(prestaResponse.getStatusCode())
|
||||||
} catch (Exception ex) {
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
log.error("[Proxy] GET error on {}?{} ", path, rawQuery, ex);
|
.body(prestaResponse.getBody());
|
||||||
// On renvoie une 502 propre plutôt que laisser Spring balancer un stacktrace
|
|
||||||
return ResponseEntity
|
|
||||||
.status(502)
|
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
|
||||||
.body("""
|
|
||||||
{"error":"Error while calling PrestaShop WebService"}
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- POST générique XML /api/ps/** (catégories, produits, etc.) ----------
|
// ---------- POST ----------
|
||||||
|
@PostMapping("/**")
|
||||||
@PostMapping(
|
|
||||||
path = "/**",
|
|
||||||
consumes = MediaType.APPLICATION_XML_VALUE,
|
|
||||||
produces = MediaType.APPLICATION_XML_VALUE
|
|
||||||
)
|
|
||||||
public ResponseEntity<String> proxyPost(HttpServletRequest request,
|
public ResponseEntity<String> proxyPost(HttpServletRequest request,
|
||||||
@RequestBody String xmlBody) {
|
@RequestBody String xmlBody) {
|
||||||
|
String path = extractPath(request);
|
||||||
|
String rawQuery = extractDecodedQuery(request);
|
||||||
|
|
||||||
String path = extractPrestaPath(request); // ex: /categories
|
log.info("XML envoyé à Presta:\n{}", xmlBody);
|
||||||
String rawQuery = request.getQueryString();
|
|
||||||
|
|
||||||
log.info("XML envoyé à Presta (POST {}):\n{}", path, xmlBody);
|
ResponseEntity<String> prestaResponse =
|
||||||
|
prestashopClient.postWithRawQuery(path, rawQuery, xmlBody);
|
||||||
|
|
||||||
return prestashopClient.postWithRawQuery(path, rawQuery, xmlBody);
|
return ResponseEntity
|
||||||
|
.status(prestaResponse.getStatusCode())
|
||||||
|
.contentType(MediaType.APPLICATION_XML)
|
||||||
|
.body(prestaResponse.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- PUT générique XML /api/ps/** ----------
|
// ---------- PUT ----------
|
||||||
|
@PutMapping("/**")
|
||||||
@PutMapping(
|
|
||||||
path = "/**",
|
|
||||||
consumes = MediaType.APPLICATION_XML_VALUE,
|
|
||||||
produces = MediaType.APPLICATION_XML_VALUE
|
|
||||||
)
|
|
||||||
public ResponseEntity<String> proxyPut(HttpServletRequest request,
|
public ResponseEntity<String> proxyPut(HttpServletRequest request,
|
||||||
@RequestBody String xmlBody) {
|
@RequestBody String xmlBody) {
|
||||||
|
String path = extractPath(request);
|
||||||
|
String rawQuery = extractDecodedQuery(request);
|
||||||
|
|
||||||
String path = extractPrestaPath(request); // ex: /products/446
|
log.info("XML envoyé à Presta:\n{}", xmlBody);
|
||||||
String rawQuery = request.getQueryString();
|
|
||||||
|
|
||||||
log.info("XML envoyé (proxy PUT {}):\n{}", path, xmlBody);
|
ResponseEntity<String> prestaResponse =
|
||||||
|
prestashopClient.putWithRawQuery(path, rawQuery, xmlBody);
|
||||||
|
|
||||||
return prestashopClient.putWithRawQuery(path, rawQuery, xmlBody);
|
return ResponseEntity
|
||||||
|
.status(prestaResponse.getStatusCode())
|
||||||
|
.contentType(MediaType.APPLICATION_XML)
|
||||||
|
.body(prestaResponse.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- DELETE générique /api/ps/** ----------
|
// ---------- DELETE ----------
|
||||||
|
@DeleteMapping("/**")
|
||||||
@DeleteMapping(path = "/**", produces = MediaType.APPLICATION_XML_VALUE)
|
|
||||||
public ResponseEntity<String> proxyDelete(HttpServletRequest request) {
|
public ResponseEntity<String> proxyDelete(HttpServletRequest request) {
|
||||||
|
String path = extractPath(request);
|
||||||
|
String rawQuery = extractDecodedQuery(request);
|
||||||
|
|
||||||
String path = extractPrestaPath(request); // ex: /categories/10
|
ResponseEntity<String> prestaResponse =
|
||||||
String rawQuery = request.getQueryString();
|
prestashopClient.deleteWithRawQuery(path, rawQuery);
|
||||||
|
|
||||||
log.info("[Proxy] DELETE {}", path);
|
return ResponseEntity
|
||||||
|
.status(prestaResponse.getStatusCode())
|
||||||
return prestashopClient.deleteWithRawQuery(path, rawQuery);
|
.contentType(MediaType.APPLICATION_XML)
|
||||||
|
.body(prestaResponse.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- UPLOAD IMAGE PRODUIT /api/ps/images/products/{productId} ----------
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload d'une image produit :
|
||||||
|
* Front → (multipart/form-data) → /api/ps/images/products/{productId}
|
||||||
|
* Backend → (bytes) → https://shop.gameovergne.fr/api/images/products/{productId}
|
||||||
|
*/
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
path = "/images/products/{productId}",
|
path = "/api/ps/images/products/{productId}",
|
||||||
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
|
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
|
||||||
produces = MediaType.APPLICATION_XML_VALUE
|
produces = MediaType.APPLICATION_XML_VALUE
|
||||||
)
|
)
|
||||||
public ResponseEntity<String> uploadProductImage(HttpServletRequest request,
|
public ResponseEntity<String> uploadProductImage(
|
||||||
@PathVariable("productId") String productId,
|
jakarta.servlet.http.HttpServletRequest request,
|
||||||
@RequestPart("image") MultipartFile imageFile) {
|
@PathVariable("productId") String productId,
|
||||||
|
@RequestPart("image") MultipartFile imageFile
|
||||||
String rawQuery = request.getQueryString();
|
) {
|
||||||
|
String rawQuery = request.getQueryString(); // si jamais tu ajoutes des options côté Presta
|
||||||
|
|
||||||
log.info("[Proxy] Upload image produit {} (size={} bytes, ct={})",
|
log.info("[Proxy] Upload image produit {} (size={} bytes, ct={})",
|
||||||
productId, imageFile.getSize(), imageFile.getContentType());
|
productId, imageFile.getSize(), imageFile.getContentType());
|
||||||
|
|
||||||
// Là on ne passe PAS par extractPrestaPath : on a un endpoint dédié côté PrestashopClient
|
|
||||||
return prestashopClient.uploadProductImage(productId, rawQuery, imageFile);
|
return prestashopClient.uploadProductImage(productId, rawQuery, imageFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user