OrderProxyController.java
package com.order.api01authgateway.controller;
import com.order.api01authgateway.dto.OrderDTO;
import com.order.api01authgateway.dto.OrderItemDTO;
import com.order.api01authgateway.dto.PageOrderDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.util.UUID;
@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
@Tag(name = "Orders", description = "Proxy para gerenciamento de pedidos e itens (API-02)")
@SecurityRequirement(name = "bearerAuth")
public class OrderProxyController {
private final WebClient webClient;
// ─── Orders ───────────────────────────────────────────────────────────
@GetMapping
@Operation(summary = "Listar pedidos", description = "Retorna uma lista paginada de todos os pedidos")
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "Sucesso",
content = @Content(schema = @Schema(implementation = PageOrderDTO.class))
),
@ApiResponse(responseCode = "401", description = "Não autorizado")
})
public Mono<ResponseEntity<byte[]>> findAll(@ParameterObject Pageable pageable, HttpServletRequest request) {
return proxy(request, null, request.getRequestURI(), true);
}
@GetMapping("/v3/api-docs")
public Mono<ResponseEntity<byte[]>> apiDocs(HttpServletRequest request) {
return proxy(request, null, "/v3/api-docs", false);
}
@GetMapping("/{id}")
@Operation(summary = "Buscar pedido por ID", description = "Retorna os detalhes de um pedido específico")
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "Sucesso",
content = @Content(schema = @Schema(implementation = OrderDTO.class))
),
@ApiResponse(responseCode = "401", description = "Não autorizado"),
@ApiResponse(responseCode = "404", description = "Pedido não encontrado")
})
public Mono<ResponseEntity<byte[]>> findById(
@Parameter(description = "ID do pedido") @PathVariable UUID id,
HttpServletRequest request) {
return proxy(request, null, request.getRequestURI(), true);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@Operation(summary = "Criar novo pedido", description = "Cria um novo pedido no sistema")
@ApiResponses({
@ApiResponse(
responseCode = "201",
description = "Criado com sucesso",
content = @Content(schema = @Schema(implementation = OrderDTO.class))
),
@ApiResponse(responseCode = "400", description = "Erro de validação"),
@ApiResponse(responseCode = "401", description = "Não autorizado")
})
public Mono<ResponseEntity<byte[]>> save(
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Dados do pedido") @Valid @RequestBody OrderDTO dto,
HttpServletRequest request) {
return proxy(request, dto, request.getRequestURI(), true);
}
@PutMapping("/{id}")
@Operation(summary = "Atualizar pedido", description = "Atualiza os dados de um pedido existente")
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "Atualizado com sucesso",
content = @Content(schema = @Schema(implementation = OrderDTO.class))
),
@ApiResponse(responseCode = "400", description = "Erro de validação"),
@ApiResponse(responseCode = "401", description = "Não autorizado"),
@ApiResponse(responseCode = "404", description = "Pedido não encontrado")
})
public Mono<ResponseEntity<byte[]>> update(
@Parameter(description = "ID do pedido") @PathVariable UUID id,
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Dados atualizados do pedido") @Valid @RequestBody OrderDTO dto,
HttpServletRequest request) {
return proxy(request, dto, request.getRequestURI(), true);
}
@DeleteMapping("/{id}")
@Operation(summary = "Excluir pedido", description = "Remove um pedido pelo seu ID")
@ApiResponses({
@ApiResponse(responseCode = "204", description = "Excluído com sucesso"),
@ApiResponse(responseCode = "401", description = "Não autorizado"),
@ApiResponse(responseCode = "404", description = "Pedido não encontrado")
})
public Mono<ResponseEntity<byte[]>> delete(
@Parameter(description = "ID do pedido") @PathVariable UUID id,
HttpServletRequest request) {
return proxy(request, null, request.getRequestURI(), true);
}
// ─── Order Items ──────────────────────────────────────────────────────
@GetMapping("/{orderId}/items")
@Operation(summary = "Listar itens do pedido", description = "Retorna a lista de itens associados a um pedido")
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "Sucesso",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = OrderItemDTO.class)))
),
@ApiResponse(responseCode = "401", description = "Não autorizado"),
@ApiResponse(responseCode = "404", description = "Pedido não encontrado")
})
public Mono<ResponseEntity<byte[]>> findItemsByOrderId(
@Parameter(description = "ID do pedido") @PathVariable UUID orderId,
HttpServletRequest request) {
return proxy(request, null, request.getRequestURI(), true);
}
@PostMapping("/{orderId}/items")
@ResponseStatus(HttpStatus.CREATED)
@Operation(summary = "Adicionar item ao pedido", description = "Adiciona um novo item ao pedido especificado")
@ApiResponses({
@ApiResponse(
responseCode = "201",
description = "Adicionado com sucesso",
content = @Content(schema = @Schema(implementation = OrderItemDTO.class))
),
@ApiResponse(responseCode = "400", description = "Erro de validação"),
@ApiResponse(responseCode = "401", description = "Não autorizado"),
@ApiResponse(responseCode = "404", description = "Pedido não encontrado")
})
public Mono<ResponseEntity<byte[]>> addItem(
@Parameter(description = "ID do pedido") @PathVariable UUID orderId,
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Dados do item") @Valid @RequestBody OrderItemDTO dto,
HttpServletRequest request) {
return proxy(request, dto, request.getRequestURI(), true);
}
// ─── Internal proxy helper ────────────────────────────────────────────
private Mono<ResponseEntity<byte[]>> proxy(HttpServletRequest request, Object body) {
return proxy(request, body, request.getRequestURI(), true);
}
private Mono<ResponseEntity<byte[]>> proxy(HttpServletRequest request, Object body, String targetPath, boolean propagateAuthorization) {
String path = targetPath;
String query = request.getQueryString() != null ? "?" + request.getQueryString() : "";
HttpHeaders forwardHeaders = buildForwardHeaders(request, propagateAuthorization);
WebClient.RequestBodySpec bodySpec = webClient
.method(HttpMethod.valueOf(request.getMethod()))
.uri(path + query)
.headers(h -> h.addAll(forwardHeaders));
if (body != null) {
return bodySpec.bodyValue(body).exchangeToMono(response -> response.toEntity(byte[].class));
}
return bodySpec.exchangeToMono(response -> response.toEntity(byte[].class));
}
private HttpHeaders buildForwardHeaders(HttpServletRequest request, boolean propagateAuthorization) {
HttpHeaders headers = new HttpHeaders();
if (propagateAuthorization) {
String token = extractBearerToken(request);
if (StringUtils.hasText(token)) {
headers.setBearerAuth(token);
}
}
if (StringUtils.hasText(request.getContentType())) {
headers.setContentType(MediaType.parseMediaType(request.getContentType()));
}
String accept = request.getHeader(HttpHeaders.ACCEPT);
if (StringUtils.hasText(accept)) {
headers.add(HttpHeaders.ACCEPT, accept);
}
return headers;
}
private String extractBearerToken(HttpServletRequest request) {
String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
if (!StringUtils.hasText(authorization) || !authorization.startsWith("Bearer ")) {
return null;
}
return authorization.substring(7);
}
}