feat: add deployment controller with deploy/stop/restart endpoints
Add DeploymentResponse DTO, DeploymentController at /api/apps/{appId} with POST /deploy (202), GET /deployments, GET /deployments/{id}, POST /stop, POST /restart (202), and integration tests covering empty list, 404, and 401 cases.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
package net.siegeln.cameleer.saas.deployment;
|
||||
|
||||
import net.siegeln.cameleer.saas.deployment.dto.DeploymentResponse;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/apps/{appId}")
|
||||
public class DeploymentController {
|
||||
|
||||
private final DeploymentService deploymentService;
|
||||
|
||||
public DeploymentController(DeploymentService deploymentService) {
|
||||
this.deploymentService = deploymentService;
|
||||
}
|
||||
|
||||
@PostMapping("/deploy")
|
||||
public ResponseEntity<DeploymentResponse> deploy(
|
||||
@PathVariable UUID appId,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
UUID actorId = resolveActorId(authentication);
|
||||
var entity = deploymentService.deploy(appId, actorId);
|
||||
return ResponseEntity.status(HttpStatus.ACCEPTED).body(toResponse(entity));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.notFound().build();
|
||||
} catch (IllegalStateException e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/deployments")
|
||||
public ResponseEntity<List<DeploymentResponse>> listDeployments(@PathVariable UUID appId) {
|
||||
var deployments = deploymentService.listByAppId(appId)
|
||||
.stream()
|
||||
.map(this::toResponse)
|
||||
.toList();
|
||||
return ResponseEntity.ok(deployments);
|
||||
}
|
||||
|
||||
@GetMapping("/deployments/{deploymentId}")
|
||||
public ResponseEntity<DeploymentResponse> getDeployment(
|
||||
@PathVariable UUID appId,
|
||||
@PathVariable UUID deploymentId) {
|
||||
return deploymentService.getById(deploymentId)
|
||||
.map(entity -> ResponseEntity.ok(toResponse(entity)))
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PostMapping("/stop")
|
||||
public ResponseEntity<DeploymentResponse> stop(
|
||||
@PathVariable UUID appId,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
UUID actorId = resolveActorId(authentication);
|
||||
var entity = deploymentService.stop(appId, actorId);
|
||||
return ResponseEntity.ok(toResponse(entity));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.notFound().build();
|
||||
} catch (IllegalStateException e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/restart")
|
||||
public ResponseEntity<DeploymentResponse> restart(
|
||||
@PathVariable UUID appId,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
UUID actorId = resolveActorId(authentication);
|
||||
var entity = deploymentService.restart(appId, actorId);
|
||||
return ResponseEntity.status(HttpStatus.ACCEPTED).body(toResponse(entity));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.notFound().build();
|
||||
} catch (IllegalStateException e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
private UUID resolveActorId(Authentication authentication) {
|
||||
String sub = authentication.getName();
|
||||
try {
|
||||
return UUID.fromString(sub);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return UUID.nameUUIDFromBytes(sub.getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
private DeploymentResponse toResponse(DeploymentEntity entity) {
|
||||
return new DeploymentResponse(
|
||||
entity.getId(),
|
||||
entity.getAppId(),
|
||||
entity.getVersion(),
|
||||
entity.getImageRef(),
|
||||
entity.getDesiredStatus().name(),
|
||||
entity.getObservedStatus().name(),
|
||||
entity.getErrorMessage(),
|
||||
entity.getOrchestratorMetadata(),
|
||||
entity.getDeployedAt(),
|
||||
entity.getStoppedAt(),
|
||||
entity.getCreatedAt()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.siegeln.cameleer.saas.deployment.dto;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public record DeploymentResponse(
|
||||
UUID id, UUID appId, int version, String imageRef,
|
||||
String desiredStatus, String observedStatus, String errorMessage,
|
||||
Map<String, Object> orchestratorMetadata,
|
||||
Instant deployedAt, Instant stoppedAt, Instant createdAt
|
||||
) {}
|
||||
Reference in New Issue
Block a user