feat(deploy): cascade createdBy through Deployment record + service + repo

Appends String createdBy to the Deployment record (after createdAt), updates
both with-er methods to pass it through, threads the parameter through
DeploymentRepository.create, DeploymentService.createDeployment/promote, and
PostgresDeploymentRepository (INSERT + SELECT_COLS + mapRow). DeploymentController
passes null as placeholder (Task 4 will resolve from SecurityContextHolder).
Covers with PostgresDeploymentRepositoryCreatedByIT verifying round-trip via
both createDeployment and promote.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-23 12:04:15 +02:00
parent 15d00f039c
commit a141e99a07
8 changed files with 91 additions and 22 deletions

View File

@@ -22,19 +22,20 @@ public record Deployment(
DeploymentConfigSnapshot deployedConfigSnapshot,
Instant deployedAt,
Instant stoppedAt,
Instant createdAt
Instant createdAt,
String createdBy
) {
public Deployment withStatus(DeploymentStatus newStatus) {
return new Deployment(id, appId, appVersionId, environmentId, newStatus,
targetState, deploymentStrategy, replicaStates, deployStage,
containerId, containerName, errorMessage, resolvedConfig,
deployedConfigSnapshot, deployedAt, stoppedAt, createdAt);
deployedConfigSnapshot, deployedAt, stoppedAt, createdAt, createdBy);
}
public Deployment withDeployedConfigSnapshot(DeploymentConfigSnapshot snapshot) {
return new Deployment(id, appId, appVersionId, environmentId, status,
targetState, deploymentStrategy, replicaStates, deployStage,
containerId, containerName, errorMessage, resolvedConfig,
snapshot, deployedAt, stoppedAt, createdAt);
snapshot, deployedAt, stoppedAt, createdAt, createdBy);
}
}

View File

@@ -10,7 +10,7 @@ public interface DeploymentRepository {
Optional<Deployment> findById(UUID id);
Optional<Deployment> findActiveByAppIdAndEnvironmentId(UUID appId, UUID environmentId);
Optional<Deployment> findActiveByAppIdAndEnvironmentIdExcluding(UUID appId, UUID environmentId, UUID excludeDeploymentId);
UUID create(UUID appId, UUID appVersionId, UUID environmentId, String containerName);
UUID create(UUID appId, UUID appVersionId, UUID environmentId, String containerName, String createdBy);
void updateStatus(UUID id, DeploymentStatus status, String containerId, String errorMessage);
void markDeployed(UUID id);
void markStopped(UUID id);

View File

@@ -23,19 +23,19 @@ public class DeploymentService {
public Deployment getById(UUID id) { return deployRepo.findById(id).orElseThrow(() -> new IllegalArgumentException("Deployment not found: " + id)); }
/** Create a deployment record. Actual container start is handled by DeploymentExecutor (async). */
public Deployment createDeployment(UUID appId, UUID appVersionId, UUID environmentId) {
public Deployment createDeployment(UUID appId, UUID appVersionId, UUID environmentId, String createdBy) {
App app = appService.getById(appId);
Environment env = envService.getById(environmentId);
String containerName = env.slug() + "-" + app.slug();
deployRepo.deleteFailedByAppAndEnvironment(appId, environmentId);
UUID deploymentId = deployRepo.create(appId, appVersionId, environmentId, containerName);
UUID deploymentId = deployRepo.create(appId, appVersionId, environmentId, containerName, createdBy);
return deployRepo.findById(deploymentId).orElseThrow();
}
/** Promote: deploy the same app version to a different environment. */
public Deployment promote(UUID appId, UUID appVersionId, UUID targetEnvironmentId) {
return createDeployment(appId, appVersionId, targetEnvironmentId);
public Deployment promote(UUID appId, UUID appVersionId, UUID targetEnvironmentId, String createdBy) {
return createDeployment(appId, appVersionId, targetEnvironmentId, createdBy);
}
public void markRunning(UUID deploymentId, String containerId) {