Add PrestashopAdminController and PrestashopAdminService for managing admin resources
This commit is contained in:
@@ -0,0 +1,78 @@
|
|||||||
|
package fr.gameovergne.api.controller;
|
||||||
|
|
||||||
|
import fr.gameovergne.api.dto.ps.*;
|
||||||
|
import fr.gameovergne.api.model.ps.SimpleResource;
|
||||||
|
import fr.gameovergne.api.service.PrestashopAdminService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/ps-admin")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class PrestashopAdminController {
|
||||||
|
|
||||||
|
private final PrestashopAdminService service;
|
||||||
|
|
||||||
|
// --- Simple resources ---
|
||||||
|
|
||||||
|
@GetMapping("/{resource}")
|
||||||
|
public List<PsItemDto> listSimple(@PathVariable SimpleResource resource) {
|
||||||
|
return service.listSimple(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{resource}")
|
||||||
|
public long createSimple(@PathVariable SimpleResource resource,
|
||||||
|
@RequestParam String name) {
|
||||||
|
return service.createSimple(resource, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{resource}/{id}")
|
||||||
|
public void updateSimple(@PathVariable SimpleResource resource,
|
||||||
|
@PathVariable long id,
|
||||||
|
@RequestParam String name) {
|
||||||
|
service.updateSimple(resource, id, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{resource}/{id}")
|
||||||
|
public void deleteSimple(@PathVariable SimpleResource resource,
|
||||||
|
@PathVariable long id) {
|
||||||
|
service.deleteSimple(resource, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Produits liste + flags + condition values ---
|
||||||
|
|
||||||
|
@GetMapping("/products")
|
||||||
|
public List<ProductListItemDto> listProducts(@RequestParam(required = false) String q) {
|
||||||
|
return service.listProducts(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/products/{id}/flags")
|
||||||
|
public ProductFlagsDto getProductFlags(@PathVariable long id) {
|
||||||
|
return service.getProductFlags(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/meta/condition-values")
|
||||||
|
public List<String> getConditionValues() {
|
||||||
|
return service.getConditionValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Create / update / delete produit ---
|
||||||
|
|
||||||
|
@PostMapping("/products")
|
||||||
|
public long createProduct(@RequestBody PsProductDto dto) {
|
||||||
|
return service.createProduct(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/products/{id}")
|
||||||
|
public void updateProduct(@PathVariable long id,
|
||||||
|
@RequestBody PsProductDto dto) {
|
||||||
|
service.updateProduct(id, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/products/{id}")
|
||||||
|
public void deleteProduct(@PathVariable long id) {
|
||||||
|
service.deleteProduct(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package fr.gameovergne.api.dto.ps;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@Builder
|
||||||
|
public class ProductFlagsDto {
|
||||||
|
boolean complete;
|
||||||
|
boolean hasManual;
|
||||||
|
String conditionLabel;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package fr.gameovergne.api.dto.ps;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@Builder
|
||||||
|
public class ProductListItemDto {
|
||||||
|
Long id;
|
||||||
|
String name;
|
||||||
|
Long manufacturerId;
|
||||||
|
Long supplierId;
|
||||||
|
Long categoryId;
|
||||||
|
Double priceHt;
|
||||||
|
Integer quantity;
|
||||||
|
}
|
||||||
12
api/src/main/java/fr/gameovergne/api/dto/ps/PsItemDto.java
Normal file
12
api/src/main/java/fr/gameovergne/api/dto/ps/PsItemDto.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package fr.gameovergne.api.dto.ps;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@Builder
|
||||||
|
public class PsItemDto {
|
||||||
|
Long id;
|
||||||
|
String name;
|
||||||
|
Boolean active;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package fr.gameovergne.api.dto.ps;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@Builder
|
||||||
|
public class PsProductDto {
|
||||||
|
Long id; // optionnel pour update
|
||||||
|
String name;
|
||||||
|
|
||||||
|
Long manufacturerId;
|
||||||
|
Long supplierId;
|
||||||
|
Long categoryId;
|
||||||
|
|
||||||
|
Double priceTtc;
|
||||||
|
Double vatRate;
|
||||||
|
|
||||||
|
Integer quantity;
|
||||||
|
|
||||||
|
Boolean complete; // Complet: Oui/Non
|
||||||
|
Boolean hasManual; // Notice: Avec/Sans
|
||||||
|
String conditionLabel; // État: libellé
|
||||||
|
|
||||||
|
String description; // description libre
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package fr.gameovergne.api.model.ps;
|
||||||
|
|
||||||
|
public enum SimpleResource {
|
||||||
|
categories("categories", "category", true, true),
|
||||||
|
manufacturers("manufacturers", "manufacturer", false, false),
|
||||||
|
suppliers("suppliers", "supplier", false, false);
|
||||||
|
|
||||||
|
public final String path;
|
||||||
|
public final String root;
|
||||||
|
public final boolean needsDefaultLang;
|
||||||
|
public final boolean nameIsMultilang;
|
||||||
|
|
||||||
|
SimpleResource(String path, String root, boolean needsDefaultLang, boolean nameIsMultilang) {
|
||||||
|
this.path = path;
|
||||||
|
this.root = root;
|
||||||
|
this.needsDefaultLang = needsDefaultLang;
|
||||||
|
this.nameIsMultilang = nameIsMultilang;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,79 +1,97 @@
|
|||||||
package fr.gameovergne.api.service;
|
package fr.gameovergne.api.service;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
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.RestClientException;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestClient;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@Slf4j
|
||||||
public class PrestashopClient {
|
public class PrestashopClient {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(PrestashopClient.class);
|
private final RestClient client;
|
||||||
|
|
||||||
private final String baseUrl;
|
private final String baseUrl;
|
||||||
private final String apiKey;
|
|
||||||
private final RestTemplate restTemplate = new RestTemplate();
|
|
||||||
|
|
||||||
public PrestashopClient(
|
public PrestashopClient(
|
||||||
@Value("${prestashop.api.base-url}") String baseUrl,
|
@Value("${prestashop.base-url}") String baseUrl,
|
||||||
@Value("${prestashop.api.key}") String apiKey
|
@Value("${prestashop.api-key}") String apiKey
|
||||||
) {
|
) {
|
||||||
this.baseUrl = baseUrl; // ex: https://shop.gameovergne.fr/api
|
this.baseUrl = baseUrl;
|
||||||
this.apiKey = apiKey;
|
|
||||||
|
String basicAuth = Base64.getEncoder()
|
||||||
|
.encodeToString((apiKey + ":").getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
this.client = RestClient.builder()
|
||||||
|
.defaultHeader(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth)
|
||||||
|
.build();
|
||||||
|
|
||||||
log.info("[PrestaShop] Base URL = {}", baseUrl);
|
log.info("[PrestaShop] Base URL = {}", baseUrl);
|
||||||
log.info("[PrestaShop] API key length = {}", apiKey != null ? apiKey.length() : 0);
|
log.info("[PrestaShop] API key length = {}", apiKey.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResponseEntity<String> getWithRawQuery(String relativePath, String rawQuery) {
|
private String buildUri(String path, MultiValueMap<String, String> params) {
|
||||||
// Normalisation du path
|
UriComponentsBuilder builder = UriComponentsBuilder
|
||||||
String path = (relativePath == null) ? "" : relativePath;
|
.fromHttpUrl(baseUrl + path);
|
||||||
if (!path.startsWith("/")) {
|
if (params != null && !params.isEmpty()) {
|
||||||
path = "/" + path;
|
builder.queryParams(params);
|
||||||
|
}
|
||||||
|
return builder.build(true).toUriString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construction manuelle de l’URL
|
public String getJson(String path, MultiValueMap<String, String> params) {
|
||||||
StringBuilder urlBuilder = new StringBuilder();
|
String uri = buildUri(path, params);
|
||||||
urlBuilder.append(baseUrl);
|
log.info("[PrestaShop] GET JSON {}", uri);
|
||||||
urlBuilder.append(path);
|
return client.get()
|
||||||
|
.uri(uri)
|
||||||
if (rawQuery != null && !rawQuery.isBlank()) {
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
urlBuilder.append('?').append(rawQuery);
|
.retrieve()
|
||||||
|
.body(String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String urlString = urlBuilder.toString();
|
public String getXml(String path, MultiValueMap<String, String> params) {
|
||||||
log.info("[PrestaShop] GET {}", urlString);
|
String uri = buildUri(path, params);
|
||||||
|
log.info("[PrestaShop] GET XML {}", uri);
|
||||||
URI uri = URI.create(urlString);
|
return client.get()
|
||||||
|
.uri(uri)
|
||||||
HttpHeaders headers = new HttpHeaders();
|
.accept(MediaType.APPLICATION_XML)
|
||||||
headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
|
.retrieve()
|
||||||
// Presta: Basic Auth avec apiKey comme user et mot de passe vide
|
.body(String.class);
|
||||||
headers.setBasicAuth(apiKey, "");
|
|
||||||
|
|
||||||
HttpEntity<Void> entity = new HttpEntity<>(headers);
|
|
||||||
|
|
||||||
try {
|
|
||||||
ResponseEntity<String> response =
|
|
||||||
restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
|
|
||||||
|
|
||||||
log.info("[PrestaShop] Response {} {}", response.getStatusCode().value(),
|
|
||||||
response.getBody());
|
|
||||||
|
|
||||||
return response;
|
|
||||||
} catch (RestClientException ex) {
|
|
||||||
log.error("[PrestaShop] Error calling {} : {}", urlString, ex.toString(), ex);
|
|
||||||
// On renvoie quelque chose de propre au client Angular
|
|
||||||
throw new ResponseStatusException(
|
|
||||||
HttpStatus.BAD_GATEWAY,
|
|
||||||
"Error while calling PrestaShop API",
|
|
||||||
ex
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String postXml(String path, MultiValueMap<String, String> params, String xmlBody) {
|
||||||
|
String uri = buildUri(path, params);
|
||||||
|
log.info("[PrestaShop] POST XML {}", uri);
|
||||||
|
return client.post()
|
||||||
|
.uri(uri)
|
||||||
|
.contentType(MediaType.APPLICATION_XML)
|
||||||
|
.body(xmlBody)
|
||||||
|
.retrieve()
|
||||||
|
.body(String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String putXml(String path, MultiValueMap<String, String> params, String xmlBody) {
|
||||||
|
String uri = buildUri(path, params);
|
||||||
|
log.info("[PrestaShop] PUT XML {}", uri);
|
||||||
|
return client.put()
|
||||||
|
.uri(uri)
|
||||||
|
.contentType(MediaType.APPLICATION_XML)
|
||||||
|
.body(xmlBody)
|
||||||
|
.retrieve()
|
||||||
|
.body(String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(String path, MultiValueMap<String, String> params) {
|
||||||
|
String uri = buildUri(path, params);
|
||||||
|
log.info("[PrestaShop] DELETE {}", uri);
|
||||||
|
client.delete()
|
||||||
|
.uri(uri)
|
||||||
|
.retrieve()
|
||||||
|
.toBodilessEntity();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user