Compare commits

...

2 Commits

3 changed files with 158 additions and 146 deletions

View File

@@ -16,65 +16,92 @@ public class PrestashopProxyController {
this.prestashopClient = prestashopClient; this.prestashopClient = prestashopClient;
} }
// Utilitaire pour extraire /products, /products/446, etc. /**
* Extrait le path Presta à partir de la requête entrante.
* Exemple :
* - requestURI = /api/ps/products/446
* - retourne /products/446
*/
private String extractPath(HttpServletRequest request) { private String extractPath(HttpServletRequest request) {
String fullPath = request.getRequestURI(); // ex: /api/ps/products/446 String fullPath = request.getRequestURI(); // ex: /api/ps/products/446
String contextPath = request.getContextPath(); // souvent "" String contextPath = request.getContextPath(); // souvent ""
String relative = fullPath.substring(contextPath.length()); // /api/ps/products/446 String relative = fullPath.substring(contextPath.length()); // /api/ps/products/446
return relative.replaceFirst("^/api/ps", ""); // => /products/446
// On enlève le préfixe /api/ps => /products/446
return relative.replaceFirst("^/api/ps", "");
} }
// ---------- GET ----------
@GetMapping("/**") @GetMapping("/**")
public ResponseEntity<String> proxyGet(HttpServletRequest request) { public ResponseEntity<String> proxyGet(HttpServletRequest request) {
String path = extractPath(request); String path = extractPath(request);
String query = request.getQueryString(); String query = request.getQueryString(); // ex: display=full&filter[id_product]=123
String body = prestashopClient.get(path, query); String body = prestashopClient.get(path, query);
// On sait qu'on force output_format=JSON côté client.get(...)
return ResponseEntity return ResponseEntity
.ok() .ok()
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.body(body); .body(body);
} }
@PutMapping("/**") // ---------- POST ----------
public ResponseEntity<String> proxyPut(HttpServletRequest request,
@RequestBody String body) {
String path = extractPath(request);
String query = request.getQueryString();
String responseBody = prestashopClient.put(path, query, body);
return ResponseEntity
.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(responseBody);
}
@PostMapping("/**") @PostMapping("/**")
public ResponseEntity<String> proxyPost(HttpServletRequest request, public ResponseEntity<String> proxyPost(
@RequestBody String body) { HttpServletRequest request,
@RequestBody String bodyFromFront
) {
String path = extractPath(request); String path = extractPath(request);
String query = request.getQueryString(); String query = request.getQueryString();
String responseBody = prestashopClient.post(path, query, body); String body = prestashopClient.post(path, query, bodyFromFront);
// Si Angular demande output_format=JSON dans la query, on peut renvoyer JSON,
// sinon ce sera typiquement du XML. Dans tous les cas on renvoie le raw body.
MediaType mediaType = (query != null && query.contains("output_format=JSON"))
? MediaType.APPLICATION_JSON
: MediaType.APPLICATION_XML;
return ResponseEntity return ResponseEntity
.ok() .ok()
.contentType(MediaType.APPLICATION_JSON) .contentType(mediaType)
.body(responseBody); .body(body);
} }
// ---------- PUT ----------
@PutMapping("/**")
public ResponseEntity<String> proxyPut(
HttpServletRequest request,
@RequestBody String bodyFromFront
) {
String path = extractPath(request);
String query = request.getQueryString();
String body = prestashopClient.put(path, query, bodyFromFront);
MediaType mediaType = (query != null && query.contains("output_format=JSON"))
? MediaType.APPLICATION_JSON
: MediaType.APPLICATION_XML;
return ResponseEntity
.ok()
.contentType(mediaType)
.body(body);
}
// ---------- DELETE ----------
@DeleteMapping("/**") @DeleteMapping("/**")
public ResponseEntity<String> proxyDelete(HttpServletRequest request) { public ResponseEntity<Void> proxyDelete(HttpServletRequest request) {
String path = extractPath(request); String path = extractPath(request);
String query = request.getQueryString(); String query = request.getQueryString();
String responseBody = prestashopClient.delete(path, query); prestashopClient.delete(path, query);
return ResponseEntity return ResponseEntity.noContent().build();
.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(responseBody);
} }
} }

View File

@@ -5,7 +5,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*; import org.springframework.http.*;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
@@ -30,18 +29,45 @@ public class PrestashopClient {
this.basicAuth = basicAuth; this.basicAuth = basicAuth;
} }
// ========================= /**
// GET : on force JSON + full * Construit l'URL finale vers PrestaShop en propageant la query d'origine.
// ========================= *
public String get(String path, String ignoredQuery) { * @param path ex: "/products", "/products/446"
* @param query queryString brute reçue côté API (peut être null)
* @param addDefaultFormatAndDisplay si true, ajoute output_format=JSON et display=full
*/
private String buildUrl(String path, String query, boolean addDefaultFormatAndDisplay) {
UriComponentsBuilder builder = UriComponentsBuilder UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(baseUrl) .fromHttpUrl(baseUrl)
.path("/api") .path("/api")
.path(path) .path(path);
.queryParam("output_format", "JSON")
.queryParam("display", "full");
String url = builder.build(true).toUriString(); // On propage TOUT ce que le front a mis (display, filter[...], etc.)
if (query != null && !query.isBlank()) {
builder.query(query);
}
if (addDefaultFormatAndDisplay) {
// Si le front n'a pas mis output_format, on force JSON
if (query == null || !query.contains("output_format=")) {
builder.queryParam("output_format", "JSON");
}
// Si le front n'a pas mis display, on force full
if (query == null || !query.contains("display=")) {
builder.queryParam("display", "full");
}
}
return builder.build(true).toUriString();
}
/**
* GET générique vers PrestaShop.
* - Propage la queryString Angular (display, filter[...], etc.)
* - Ajoute output_format=JSON & display=full si absents.
*/
public String get(String path, String query) {
String url = buildUrl(path, query, true);
log.info("[PrestaShop] GET {}", url); log.info("[PrestaShop] GET {}", url);
@@ -59,10 +85,8 @@ public class PrestashopClient {
String.class String.class
); );
log.info("[PrestaShop] Réponse GET {} pour {}", response.getStatusCode(), url);
if (!response.getStatusCode().is2xxSuccessful()) { if (!response.getStatusCode().is2xxSuccessful()) {
throw new RuntimeException("PrestaShop returned non-2xx status: " throw new RuntimeException("PrestaShop returned non-2xx status on GET: "
+ response.getStatusCode() + " for URL " + url); + response.getStatusCode() + " for URL " + url);
} }
@@ -73,79 +97,20 @@ public class PrestashopClient {
} }
} }
// ========================= /**
// PUT : on respecte la query du front * POST générique vers PrestaShop.
// ========================= * On propage la query telle quelle (si Angular met output_format=JSON, etc.).
public String put(String path, String query, String body) { * Le body est du XML (templates construits côté Angular).
UriComponentsBuilder builder = UriComponentsBuilder */
.fromHttpUrl(baseUrl)
.path("/api")
.path(path);
if (query != null && !query.isBlank()) {
// on laisse Angular décider (ex: output_format=JSON)
builder.query(query);
}
String url = builder.build(true).toUriString();
log.info("[PrestaShop] PUT {}", url);
log.debug("[PrestaShop] PUT body = {}", body);
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth);
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<>(body, headers);
try {
ResponseEntity<String> response = restTemplate.exchange(
url,
HttpMethod.PUT,
entity,
String.class
);
log.info("[PrestaShop] Réponse PUT {} 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 (HttpStatusCodeException e) {
// Ici on log le body d'erreur de Presta !
log.error("[PrestaShop] Erreur PUT {} status={} body={}",
url, e.getStatusCode(), e.getResponseBodyAsString(), e);
throw new RuntimeException("Erreur PUT PrestaShop", e);
} catch (RestClientException e) {
log.error("[PrestaShop] Erreur PUT {}", url, e);
throw new RuntimeException("Erreur PUT PrestaShop", e);
}
}
// =========================
// POST : création
// =========================
public String post(String path, String query, String body) { public String post(String path, String query, String body) {
UriComponentsBuilder builder = UriComponentsBuilder String url = buildUrl(path, query, false);
.fromHttpUrl(baseUrl)
.path("/api")
.path(path);
if (query != null && !query.isBlank()) {
builder.query(query);
}
String url = builder.build(true).toUriString();
log.info("[PrestaShop] POST {}", url); log.info("[PrestaShop] POST {}", url);
log.debug("[PrestaShop] POST body = {}", body);
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth);
headers.setContentType(MediaType.APPLICATION_JSON); headers.setContentType(MediaType.APPLICATION_XML);
headers.setAccept(List.of(MediaType.APPLICATION_JSON)); headers.setAccept(List.of(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML));
HttpEntity<String> entity = new HttpEntity<>(body, headers); HttpEntity<String> entity = new HttpEntity<>(body, headers);
@@ -157,43 +122,66 @@ public class PrestashopClient {
String.class String.class
); );
log.info("[PrestaShop] Réponse POST {} pour {}", response.getStatusCode(), url);
if (!response.getStatusCode().is2xxSuccessful()) { if (!response.getStatusCode().is2xxSuccessful()) {
throw new RuntimeException("PrestaShop returned non-2xx status: " throw new RuntimeException("PrestaShop returned non-2xx status on POST: "
+ response.getStatusCode() + " for URL " + url); + response.getStatusCode() + " for URL " + url
+ " - response: " + response.getBody());
} }
return response.getBody(); return response.getBody();
} catch (HttpStatusCodeException e) {
log.error("[PrestaShop] Erreur POST {} status={} body={}",
url, e.getStatusCode(), e.getResponseBodyAsString(), e);
throw new RuntimeException("Erreur POST PrestaShop", e);
} catch (RestClientException e) { } catch (RestClientException e) {
log.error("[PrestaShop] Erreur POST {}", url, e); log.error("[PrestaShop] Erreur POST {}", url, e);
throw new RuntimeException("Erreur POST PrestaShop", e); throw new RuntimeException("Erreur POST PrestaShop", e);
} }
} }
// ========================= /**
// DELETE : suppression * PUT générique vers PrestaShop.
// ========================= * Le body est du XML (templates construits côté Angular).
public String delete(String path, String query) { */
UriComponentsBuilder builder = UriComponentsBuilder public String put(String path, String query, String body) {
.fromHttpUrl(baseUrl) String url = buildUrl(path, query, false);
.path("/api")
.path(path);
if (query != null && !query.isBlank()) { log.info("[PrestaShop] PUT {}", url);
builder.query(query);
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth);
headers.setContentType(MediaType.APPLICATION_XML);
headers.setAccept(List.of(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML));
HttpEntity<String> entity = new HttpEntity<>(body, headers);
try {
ResponseEntity<String> response = restTemplate.exchange(
url,
HttpMethod.PUT,
entity,
String.class
);
if (!response.getStatusCode().is2xxSuccessful()) {
throw new RuntimeException("PrestaShop returned non-2xx status on PUT: "
+ response.getStatusCode() + " for URL " + url
+ " - response: " + response.getBody());
} }
String url = builder.build(true).toUriString(); return response.getBody();
} catch (RestClientException e) {
log.error("[PrestaShop] Erreur PUT {}", url, e);
throw new RuntimeException("Erreur PUT PrestaShop", e);
}
}
/**
* DELETE générique vers PrestaShop.
*/
public void delete(String path, String query) {
String url = buildUrl(path, query, false);
log.info("[PrestaShop] DELETE {}", url); log.info("[PrestaShop] DELETE {}", url);
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
HttpEntity<Void> entity = new HttpEntity<>(headers); HttpEntity<Void> entity = new HttpEntity<>(headers);
@@ -205,18 +193,11 @@ public class PrestashopClient {
String.class String.class
); );
log.info("[PrestaShop] Réponse DELETE {} pour {}", response.getStatusCode(), url);
if (!response.getStatusCode().is2xxSuccessful()) { if (!response.getStatusCode().is2xxSuccessful()) {
throw new RuntimeException("PrestaShop returned non-2xx status: " throw new RuntimeException("PrestaShop returned non-2xx status on DELETE: "
+ response.getStatusCode() + " for URL " + url); + response.getStatusCode() + " for URL " + url
+ " - response: " + response.getBody());
} }
return response.getBody();
} catch (HttpStatusCodeException e) {
log.error("[PrestaShop] Erreur DELETE {} status={} body={}",
url, e.getStatusCode(), e.getResponseBodyAsString(), e);
throw new RuntimeException("Erreur DELETE PrestaShop", e);
} catch (RestClientException e) { } catch (RestClientException e) {
log.error("[PrestaShop] Erreur DELETE {}", url, e); log.error("[PrestaShop] Erreur DELETE {}", url, e);
throw new RuntimeException("Erreur DELETE PrestaShop", e); throw new RuntimeException("Erreur DELETE PrestaShop", e);

View File

@@ -18,14 +18,16 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
flex: 0 1 auto; flex: 1 1 auto;
} }
.nav-actions { .nav-actions {
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
align-items: center; align-items: center;
flex: 0 0 auto; flex: 0 1 auto;
justify-content: flex-end;
flex-wrap: nowrap;
} }
.mat-menu-item mat-icon { .mat-menu-item mat-icon {
@@ -53,15 +55,17 @@
gap: 8px; gap: 8px;
overflow-x: auto; overflow-x: auto;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
}
button { .nav-actions button {
padding: 6px 8px; padding: 6px 8px;
font-size: 0.95rem; font-size: 0.95rem;
min-width: 0; min-width: 0;
white-space: nowrap;
} }
mat-icon { .nav-actions mat-icon {
font-size: 20px; font-size: 20px;
} line-height: 1;
} }
} }