autocomit
This commit is contained in:
@@ -0,0 +1,110 @@
|
|||||||
|
package com.ldpv2.controller;
|
||||||
|
|
||||||
|
import com.ldpv2.dto.request.RecordDeploymentRequest;
|
||||||
|
import com.ldpv2.dto.response.CurrentDeploymentStateResponse;
|
||||||
|
import com.ldpv2.dto.response.DeploymentResponse;
|
||||||
|
import com.ldpv2.service.DeploymentService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/deployments")
|
||||||
|
@Tag(name = "Deployments", description = "Deployment tracking endpoints")
|
||||||
|
@SecurityRequirement(name = "bearerAuth")
|
||||||
|
public class DeploymentController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DeploymentService deploymentService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@Operation(summary = "Record deployment", description = "Record a new deployment")
|
||||||
|
public ResponseEntity<DeploymentResponse> recordDeployment(
|
||||||
|
@Valid @RequestBody RecordDeploymentRequest request) {
|
||||||
|
DeploymentResponse response = deploymentService.recordDeployment(request);
|
||||||
|
return new ResponseEntity<>(response, HttpStatus.CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@Operation(summary = "Get deployment", description = "Get deployment by ID")
|
||||||
|
public ResponseEntity<DeploymentResponse> getById(@PathVariable UUID id) {
|
||||||
|
DeploymentResponse response = deploymentService.findById(id);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@Operation(summary = "List deployments", description = "Get paginated list of deployments with optional filters")
|
||||||
|
public ResponseEntity<Page<DeploymentResponse>> getAll(
|
||||||
|
@RequestParam(required = false) UUID applicationId,
|
||||||
|
@RequestParam(required = false) UUID environmentId,
|
||||||
|
@RequestParam(required = false) UUID versionId,
|
||||||
|
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime dateFrom,
|
||||||
|
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime dateTo,
|
||||||
|
@RequestParam(defaultValue = "0") int page,
|
||||||
|
@RequestParam(defaultValue = "20") int size,
|
||||||
|
@RequestParam(defaultValue = "deploymentDate") String sortBy,
|
||||||
|
@RequestParam(defaultValue = "desc") String sortDirection) {
|
||||||
|
|
||||||
|
Sort sort = sortDirection.equalsIgnoreCase("desc")
|
||||||
|
? Sort.by(sortBy).descending()
|
||||||
|
: Sort.by(sortBy).ascending();
|
||||||
|
|
||||||
|
Pageable pageable = PageRequest.of(page, size, sort);
|
||||||
|
|
||||||
|
Page<DeploymentResponse> response;
|
||||||
|
if (applicationId != null || environmentId != null || versionId != null || dateFrom != null || dateTo != null) {
|
||||||
|
response = deploymentService.search(applicationId, environmentId, versionId, dateFrom, dateTo, pageable);
|
||||||
|
} else {
|
||||||
|
response = deploymentService.findAll(pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/current")
|
||||||
|
@Operation(summary = "Get current state", description = "Get current deployment state across environments")
|
||||||
|
public ResponseEntity<List<CurrentDeploymentStateResponse>> getCurrentState(
|
||||||
|
@RequestParam(required = false) UUID applicationId,
|
||||||
|
@RequestParam(required = false) UUID environmentId) {
|
||||||
|
List<CurrentDeploymentStateResponse> response = deploymentService.getCurrentState(applicationId, environmentId);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/by-application/{applicationId}")
|
||||||
|
@Operation(summary = "Get deployments by application", description = "Get deployment history for an application")
|
||||||
|
public ResponseEntity<Page<DeploymentResponse>> getByApplication(
|
||||||
|
@PathVariable UUID applicationId,
|
||||||
|
@RequestParam(defaultValue = "0") int page,
|
||||||
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
|
|
||||||
|
Pageable pageable = PageRequest.of(page, size, Sort.by("deploymentDate").descending());
|
||||||
|
Page<DeploymentResponse> response = deploymentService.findByApplication(applicationId, pageable);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/by-environment/{environmentId}")
|
||||||
|
@Operation(summary = "Get deployments by environment", description = "Get all deployments to an environment")
|
||||||
|
public ResponseEntity<Page<DeploymentResponse>> getByEnvironment(
|
||||||
|
@PathVariable UUID environmentId,
|
||||||
|
@RequestParam(defaultValue = "0") int page,
|
||||||
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
|
|
||||||
|
Pageable pageable = PageRequest.of(page, size, Sort.by("deploymentDate").descending());
|
||||||
|
Page<DeploymentResponse> response = deploymentService.findByEnvironment(environmentId, pageable);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.ldpv2.controller;
|
||||||
|
|
||||||
|
import com.ldpv2.dto.request.CreateVersionRequest;
|
||||||
|
import com.ldpv2.dto.request.UpdateVersionRequest;
|
||||||
|
import com.ldpv2.dto.response.VersionResponse;
|
||||||
|
import com.ldpv2.service.VersionService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/applications/{applicationId}/versions")
|
||||||
|
@Tag(name = "Versions", description = "Version management endpoints")
|
||||||
|
@SecurityRequirement(name = "bearerAuth")
|
||||||
|
public class VersionController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private VersionService versionService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@Operation(summary = "Create version", description = "Create a new version for an application")
|
||||||
|
public ResponseEntity<VersionResponse> create(
|
||||||
|
@PathVariable UUID applicationId,
|
||||||
|
@Valid @RequestBody CreateVersionRequest request) {
|
||||||
|
VersionResponse response = versionService.create(applicationId, request);
|
||||||
|
return new ResponseEntity<>(response, HttpStatus.CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
@Operation(summary = "Update version", description = "Update an existing version")
|
||||||
|
public ResponseEntity<VersionResponse> update(
|
||||||
|
@PathVariable UUID applicationId,
|
||||||
|
@PathVariable UUID id,
|
||||||
|
@Valid @RequestBody UpdateVersionRequest request) {
|
||||||
|
VersionResponse response = versionService.update(id, request);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@Operation(summary = "Get version", description = "Get version by ID")
|
||||||
|
public ResponseEntity<VersionResponse> getById(
|
||||||
|
@PathVariable UUID applicationId,
|
||||||
|
@PathVariable UUID id) {
|
||||||
|
VersionResponse response = versionService.findById(id);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@Operation(summary = "List versions", description = "Get all versions for an application")
|
||||||
|
public ResponseEntity<Page<VersionResponse>> getAll(
|
||||||
|
@PathVariable UUID applicationId,
|
||||||
|
@RequestParam(defaultValue = "0") int page,
|
||||||
|
@RequestParam(defaultValue = "20") int size,
|
||||||
|
@RequestParam(defaultValue = "releaseDate") String sortBy,
|
||||||
|
@RequestParam(defaultValue = "desc") String sortDirection) {
|
||||||
|
|
||||||
|
Sort sort = sortDirection.equalsIgnoreCase("desc")
|
||||||
|
? Sort.by(sortBy).descending()
|
||||||
|
: Sort.by(sortBy).ascending();
|
||||||
|
|
||||||
|
Pageable pageable = PageRequest.of(page, size, sort);
|
||||||
|
Page<VersionResponse> response = versionService.findByApplication(applicationId, pageable);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/latest")
|
||||||
|
@Operation(summary = "Get latest version", description = "Get the most recent version for an application")
|
||||||
|
public ResponseEntity<VersionResponse> getLatest(@PathVariable UUID applicationId) {
|
||||||
|
Optional<VersionResponse> response = versionService.findLatestByApplication(applicationId);
|
||||||
|
return response.map(ResponseEntity::ok)
|
||||||
|
.orElse(ResponseEntity.notFound().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@Operation(summary = "Delete version", description = "Delete a version")
|
||||||
|
public ResponseEntity<Void> delete(
|
||||||
|
@PathVariable UUID applicationId,
|
||||||
|
@PathVariable UUID id) {
|
||||||
|
versionService.delete(id);
|
||||||
|
return ResponseEntity.noContent().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.ldpv2.domain.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deployment entity - immutable record of deployments
|
||||||
|
* Tracks which version of which application is deployed to which environment
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Entity
|
||||||
|
@Table(name = "deployment")
|
||||||
|
@NoArgsConstructor @AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class Deployment extends BaseEntity {
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "application_id", nullable = false)
|
||||||
|
private Application application;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "version_id", nullable = false)
|
||||||
|
private Version version;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "environment_id", nullable = false)
|
||||||
|
private Environment environment;
|
||||||
|
|
||||||
|
@Column(name = "deployment_date", nullable = false)
|
||||||
|
private LocalDateTime deploymentDate;
|
||||||
|
|
||||||
|
@Column(name = "deployed_by", length = 255)
|
||||||
|
private String deployedBy;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
private String notes;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.ldpv2.domain.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version entity representing application releases
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Entity
|
||||||
|
@Table(name = "version", uniqueConstraints = {
|
||||||
|
@UniqueConstraint(name = "uk_version_app_identifier",
|
||||||
|
columnNames = {"application_id", "version_identifier"})
|
||||||
|
})
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class Version extends BaseEntity {
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "application_id", nullable = false)
|
||||||
|
private Application application;
|
||||||
|
|
||||||
|
@Column(name = "version_identifier", nullable = false, length = 100)
|
||||||
|
private String versionIdentifier;
|
||||||
|
|
||||||
|
@Column(name = "external_reference", length = 500)
|
||||||
|
private String externalReference;
|
||||||
|
|
||||||
|
@Column(name = "release_date", nullable = false)
|
||||||
|
private LocalDate releaseDate;
|
||||||
|
|
||||||
|
@Column(name = "end_of_life_date")
|
||||||
|
private LocalDate endOfLifeDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.ldpv2.dto.request;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CreateVersionRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "Version identifier is required")
|
||||||
|
@Size(max = 100, message = "Version identifier must not exceed 100 characters")
|
||||||
|
private String versionIdentifier;
|
||||||
|
|
||||||
|
@Size(max = 500, message = "External reference must not exceed 500 characters")
|
||||||
|
private String externalReference;
|
||||||
|
|
||||||
|
@NotNull(message = "Release date is required")
|
||||||
|
private LocalDate releaseDate;
|
||||||
|
|
||||||
|
private LocalDate endOfLifeDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.ldpv2.dto.request;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class RecordDeploymentRequest {
|
||||||
|
|
||||||
|
@NotNull(message = "Application ID is required")
|
||||||
|
private UUID applicationId;
|
||||||
|
|
||||||
|
@NotNull(message = "Version ID is required")
|
||||||
|
private UUID versionId;
|
||||||
|
|
||||||
|
@NotNull(message = "Environment ID is required")
|
||||||
|
private UUID environmentId;
|
||||||
|
|
||||||
|
@NotNull(message = "Deployment date is required")
|
||||||
|
private LocalDateTime deploymentDate;
|
||||||
|
|
||||||
|
private String deployedBy;
|
||||||
|
|
||||||
|
private String notes;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.ldpv2.dto.request;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class UpdateVersionRequest {
|
||||||
|
|
||||||
|
@Size(max = 100, message = "Version identifier must not exceed 100 characters")
|
||||||
|
private String versionIdentifier;
|
||||||
|
|
||||||
|
@Size(max = 500, message = "External reference must not exceed 500 characters")
|
||||||
|
private String externalReference;
|
||||||
|
|
||||||
|
private LocalDate releaseDate;
|
||||||
|
|
||||||
|
private LocalDate endOfLifeDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.ldpv2.dto.response;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CurrentDeploymentStateResponse {
|
||||||
|
private ApplicationSummaryResponse application;
|
||||||
|
private EnvironmentSummaryResponse environment;
|
||||||
|
private VersionSummaryResponse version;
|
||||||
|
private LocalDateTime deploymentDate;
|
||||||
|
private String deployedBy;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.ldpv2.dto.response;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class DeploymentResponse {
|
||||||
|
private UUID id;
|
||||||
|
private ApplicationSummaryResponse application;
|
||||||
|
private VersionSummaryResponse version;
|
||||||
|
private EnvironmentSummaryResponse environment;
|
||||||
|
private LocalDateTime deploymentDate;
|
||||||
|
private String deployedBy;
|
||||||
|
private String notes;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.ldpv2.dto.response;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class EnvironmentSummaryResponse {
|
||||||
|
private UUID id;
|
||||||
|
private String name;
|
||||||
|
private boolean isProduction;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.ldpv2.dto.response;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class VersionResponse {
|
||||||
|
private UUID id;
|
||||||
|
private UUID applicationId;
|
||||||
|
private String applicationName;
|
||||||
|
private String versionIdentifier;
|
||||||
|
private String externalReference;
|
||||||
|
private LocalDate releaseDate;
|
||||||
|
private LocalDate endOfLifeDate;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.ldpv2.dto.response;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class VersionSummaryResponse {
|
||||||
|
private UUID id;
|
||||||
|
private String versionIdentifier;
|
||||||
|
private LocalDate releaseDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.ldpv2.repository;
|
||||||
|
|
||||||
|
import com.ldpv2.domain.entity.Deployment;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface DeploymentRepository extends JpaRepository<Deployment, UUID> {
|
||||||
|
|
||||||
|
Page<Deployment> findByApplicationId(UUID applicationId, Pageable pageable);
|
||||||
|
|
||||||
|
Page<Deployment> findByEnvironmentId(UUID environmentId, Pageable pageable);
|
||||||
|
|
||||||
|
Page<Deployment> findByApplicationIdAndEnvironmentId(UUID applicationId, UUID environmentId, Pageable pageable);
|
||||||
|
|
||||||
|
@Query("SELECT d FROM Deployment d WHERE " +
|
||||||
|
"(:applicationId IS NULL OR d.application.id = :applicationId) AND " +
|
||||||
|
"(:environmentId IS NULL OR d.environment.id = :environmentId) AND " +
|
||||||
|
"(:versionId IS NULL OR d.version.id = :versionId) AND " +
|
||||||
|
"(:dateFrom IS NULL OR d.deploymentDate >= :dateFrom) AND " +
|
||||||
|
"(:dateTo IS NULL OR d.deploymentDate <= :dateTo)")
|
||||||
|
Page<Deployment> search(
|
||||||
|
@Param("applicationId") UUID applicationId,
|
||||||
|
@Param("environmentId") UUID environmentId,
|
||||||
|
@Param("versionId") UUID versionId,
|
||||||
|
@Param("dateFrom") LocalDateTime dateFrom,
|
||||||
|
@Param("dateTo") LocalDateTime dateTo,
|
||||||
|
Pageable pageable
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current deployment state - most recent deployment per application/environment
|
||||||
|
*/
|
||||||
|
@Query("SELECT d FROM Deployment d WHERE d.id IN (" +
|
||||||
|
" SELECT MAX(d2.id) FROM Deployment d2 " +
|
||||||
|
" WHERE (:applicationId IS NULL OR d2.application.id = :applicationId) AND " +
|
||||||
|
" (:environmentId IS NULL OR d2.environment.id = :environmentId) " +
|
||||||
|
" GROUP BY d2.application.id, d2.environment.id" +
|
||||||
|
")")
|
||||||
|
List<Deployment> findCurrentState(
|
||||||
|
@Param("applicationId") UUID applicationId,
|
||||||
|
@Param("environmentId") UUID environmentId
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current deployment for specific application in specific environment
|
||||||
|
*/
|
||||||
|
@Query("SELECT d FROM Deployment d " +
|
||||||
|
"WHERE d.application.id = :applicationId AND d.environment.id = :environmentId " +
|
||||||
|
"ORDER BY d.deploymentDate DESC LIMIT 1")
|
||||||
|
Optional<Deployment> findCurrentForApplicationInEnvironment(
|
||||||
|
@Param("applicationId") UUID applicationId,
|
||||||
|
@Param("environmentId") UUID environmentId
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.ldpv2.repository;
|
||||||
|
|
||||||
|
import com.ldpv2.domain.entity.Version;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface VersionRepository extends JpaRepository<Version, UUID> {
|
||||||
|
|
||||||
|
Page<Version> findByApplicationId(UUID applicationId, Pageable pageable);
|
||||||
|
|
||||||
|
Optional<Version> findByApplicationIdAndVersionIdentifier(UUID applicationId, String versionIdentifier);
|
||||||
|
|
||||||
|
boolean existsByApplicationIdAndVersionIdentifier(UUID applicationId, String versionIdentifier);
|
||||||
|
|
||||||
|
@Query("SELECT v FROM Version v WHERE v.application.id = :applicationId ORDER BY v.releaseDate DESC LIMIT 1")
|
||||||
|
Optional<Version> findLatestByApplicationId(@Param("applicationId") UUID applicationId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
package com.ldpv2.service;
|
||||||
|
|
||||||
|
import com.ldpv2.domain.entity.Application;
|
||||||
|
import com.ldpv2.domain.entity.Deployment;
|
||||||
|
import com.ldpv2.domain.entity.Environment;
|
||||||
|
import com.ldpv2.domain.entity.Version;
|
||||||
|
import com.ldpv2.dto.request.RecordDeploymentRequest;
|
||||||
|
import com.ldpv2.dto.response.*;
|
||||||
|
import com.ldpv2.exception.BadRequestException;
|
||||||
|
import com.ldpv2.exception.ResourceNotFoundException;
|
||||||
|
import com.ldpv2.repository.ApplicationRepository;
|
||||||
|
import com.ldpv2.repository.DeploymentRepository;
|
||||||
|
import com.ldpv2.repository.EnvironmentRepository;
|
||||||
|
import com.ldpv2.repository.VersionRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DeploymentService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DeploymentRepository deploymentRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationRepository applicationRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private VersionRepository versionRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EnvironmentRepository environmentRepository;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public DeploymentResponse recordDeployment(RecordDeploymentRequest request) {
|
||||||
|
// Validate application exists
|
||||||
|
Application application = applicationRepository.findById(request.getApplicationId())
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException(
|
||||||
|
"Application not found with id: " + request.getApplicationId()));
|
||||||
|
|
||||||
|
// Validate version exists
|
||||||
|
Version version = versionRepository.findById(request.getVersionId())
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException(
|
||||||
|
"Version not found with id: " + request.getVersionId()));
|
||||||
|
|
||||||
|
// Validate environment exists
|
||||||
|
Environment environment = environmentRepository.findById(request.getEnvironmentId())
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException(
|
||||||
|
"Environment not found with id: " + request.getEnvironmentId()));
|
||||||
|
|
||||||
|
// Validate that version belongs to the application
|
||||||
|
if (!version.getApplication().getId().equals(request.getApplicationId())) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
"Version does not belong to the specified application");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate deployment date is not in future
|
||||||
|
if (request.getDeploymentDate().isAfter(LocalDateTime.now())) {
|
||||||
|
throw new BadRequestException("Deployment date cannot be in the future");
|
||||||
|
}
|
||||||
|
|
||||||
|
Deployment deployment = new Deployment();
|
||||||
|
deployment.setApplication(application);
|
||||||
|
deployment.setVersion(version);
|
||||||
|
deployment.setEnvironment(environment);
|
||||||
|
deployment.setDeploymentDate(request.getDeploymentDate());
|
||||||
|
deployment.setDeployedBy(request.getDeployedBy());
|
||||||
|
deployment.setNotes(request.getNotes());
|
||||||
|
|
||||||
|
deployment = deploymentRepository.save(deployment);
|
||||||
|
return mapToResponse(deployment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeploymentResponse findById(UUID id) {
|
||||||
|
Deployment deployment = deploymentRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException(
|
||||||
|
"Deployment not found with id: " + id));
|
||||||
|
return mapToResponse(deployment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<DeploymentResponse> findAll(Pageable pageable) {
|
||||||
|
return deploymentRepository.findAll(pageable).map(this::mapToResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<DeploymentResponse> findByApplication(UUID applicationId, Pageable pageable) {
|
||||||
|
if (!applicationRepository.existsById(applicationId)) {
|
||||||
|
throw new ResourceNotFoundException(
|
||||||
|
"Application not found with id: " + applicationId);
|
||||||
|
}
|
||||||
|
return deploymentRepository.findByApplicationId(applicationId, pageable)
|
||||||
|
.map(this::mapToResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<DeploymentResponse> findByEnvironment(UUID environmentId, Pageable pageable) {
|
||||||
|
if (!environmentRepository.existsById(environmentId)) {
|
||||||
|
throw new ResourceNotFoundException(
|
||||||
|
"Environment not found with id: " + environmentId);
|
||||||
|
}
|
||||||
|
return deploymentRepository.findByEnvironmentId(environmentId, pageable)
|
||||||
|
.map(this::mapToResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<DeploymentResponse> search(
|
||||||
|
UUID applicationId,
|
||||||
|
UUID environmentId,
|
||||||
|
UUID versionId,
|
||||||
|
LocalDateTime dateFrom,
|
||||||
|
LocalDateTime dateTo,
|
||||||
|
Pageable pageable) {
|
||||||
|
return deploymentRepository.search(
|
||||||
|
applicationId, environmentId, versionId, dateFrom, dateTo, pageable)
|
||||||
|
.map(this::mapToResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CurrentDeploymentStateResponse> getCurrentState(UUID applicationId, UUID environmentId) {
|
||||||
|
List<Deployment> deployments = deploymentRepository.findCurrentState(applicationId, environmentId);
|
||||||
|
return deployments.stream()
|
||||||
|
.map(this::mapToCurrentStateResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeploymentResponse mapToResponse(Deployment deployment) {
|
||||||
|
ApplicationSummaryResponse appSummary = new ApplicationSummaryResponse(
|
||||||
|
deployment.getApplication().getId(),
|
||||||
|
deployment.getApplication().getName(),
|
||||||
|
deployment.getApplication().getStatus(),
|
||||||
|
deployment.getApplication().getBusinessUnit().getName()
|
||||||
|
);
|
||||||
|
|
||||||
|
VersionSummaryResponse versionSummary = new VersionSummaryResponse(
|
||||||
|
deployment.getVersion().getId(),
|
||||||
|
deployment.getVersion().getVersionIdentifier(),
|
||||||
|
deployment.getVersion().getReleaseDate()
|
||||||
|
);
|
||||||
|
|
||||||
|
EnvironmentSummaryResponse envSummary = new EnvironmentSummaryResponse(
|
||||||
|
deployment.getEnvironment().getId(),
|
||||||
|
deployment.getEnvironment().getName(),
|
||||||
|
deployment.getEnvironment().getIsProduction()
|
||||||
|
);
|
||||||
|
|
||||||
|
return new DeploymentResponse(
|
||||||
|
deployment.getId(),
|
||||||
|
appSummary,
|
||||||
|
versionSummary,
|
||||||
|
envSummary,
|
||||||
|
deployment.getDeploymentDate(),
|
||||||
|
deployment.getDeployedBy(),
|
||||||
|
deployment.getNotes(),
|
||||||
|
deployment.getCreatedAt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CurrentDeploymentStateResponse mapToCurrentStateResponse(Deployment deployment) {
|
||||||
|
ApplicationSummaryResponse appSummary = new ApplicationSummaryResponse(
|
||||||
|
deployment.getApplication().getId(),
|
||||||
|
deployment.getApplication().getName(),
|
||||||
|
deployment.getApplication().getStatus(),
|
||||||
|
deployment.getApplication().getBusinessUnit().getName()
|
||||||
|
);
|
||||||
|
|
||||||
|
VersionSummaryResponse versionSummary = new VersionSummaryResponse(
|
||||||
|
deployment.getVersion().getId(),
|
||||||
|
deployment.getVersion().getVersionIdentifier(),
|
||||||
|
deployment.getVersion().getReleaseDate()
|
||||||
|
);
|
||||||
|
|
||||||
|
EnvironmentSummaryResponse envSummary = new EnvironmentSummaryResponse(
|
||||||
|
deployment.getEnvironment().getId(),
|
||||||
|
deployment.getEnvironment().getName(),
|
||||||
|
deployment.getEnvironment().getIsProduction()
|
||||||
|
);
|
||||||
|
|
||||||
|
return new CurrentDeploymentStateResponse(
|
||||||
|
appSummary,
|
||||||
|
envSummary,
|
||||||
|
versionSummary,
|
||||||
|
deployment.getDeploymentDate(),
|
||||||
|
deployment.getDeployedBy()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
package com.ldpv2.service;
|
||||||
|
|
||||||
|
import com.ldpv2.domain.entity.Application;
|
||||||
|
import com.ldpv2.domain.entity.Version;
|
||||||
|
import com.ldpv2.dto.request.CreateVersionRequest;
|
||||||
|
import com.ldpv2.dto.request.UpdateVersionRequest;
|
||||||
|
import com.ldpv2.dto.response.VersionResponse;
|
||||||
|
import com.ldpv2.exception.BadRequestException;
|
||||||
|
import com.ldpv2.exception.ResourceNotFoundException;
|
||||||
|
import com.ldpv2.repository.ApplicationRepository;
|
||||||
|
import com.ldpv2.repository.VersionRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class VersionService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private VersionRepository versionRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationRepository applicationRepository;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public VersionResponse create(UUID applicationId, CreateVersionRequest request) {
|
||||||
|
Application application = applicationRepository.findById(applicationId)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException(
|
||||||
|
"Application not found with id: " + applicationId));
|
||||||
|
|
||||||
|
// Check if version identifier already exists for this application
|
||||||
|
if (versionRepository.existsByApplicationIdAndVersionIdentifier(
|
||||||
|
applicationId, request.getVersionIdentifier())) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
"Version '" + request.getVersionIdentifier() +
|
||||||
|
"' already exists for this application");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate dates
|
||||||
|
if (request.getEndOfLifeDate() != null &&
|
||||||
|
request.getReleaseDate().isAfter(request.getEndOfLifeDate())) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
"End of life date must be after release date");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate release date is not in future
|
||||||
|
if (request.getReleaseDate().isAfter(LocalDate.now())) {
|
||||||
|
throw new BadRequestException("Release date cannot be in the future");
|
||||||
|
}
|
||||||
|
|
||||||
|
Version version = new Version();
|
||||||
|
version.setApplication(application);
|
||||||
|
version.setVersionIdentifier(request.getVersionIdentifier());
|
||||||
|
version.setExternalReference(request.getExternalReference());
|
||||||
|
version.setReleaseDate(request.getReleaseDate());
|
||||||
|
version.setEndOfLifeDate(request.getEndOfLifeDate());
|
||||||
|
|
||||||
|
version = versionRepository.save(version);
|
||||||
|
return mapToResponse(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public VersionResponse update(UUID id, UpdateVersionRequest request) {
|
||||||
|
Version version = versionRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException(
|
||||||
|
"Version not found with id: " + id));
|
||||||
|
|
||||||
|
if (request.getVersionIdentifier() != null) {
|
||||||
|
// Check if new version identifier already exists for this application
|
||||||
|
if (!request.getVersionIdentifier().equals(version.getVersionIdentifier()) &&
|
||||||
|
versionRepository.existsByApplicationIdAndVersionIdentifier(
|
||||||
|
version.getApplication().getId(), request.getVersionIdentifier())) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
"Version '" + request.getVersionIdentifier() +
|
||||||
|
"' already exists for this application");
|
||||||
|
}
|
||||||
|
version.setVersionIdentifier(request.getVersionIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.getExternalReference() != null) {
|
||||||
|
version.setExternalReference(request.getExternalReference());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.getReleaseDate() != null) {
|
||||||
|
if (request.getReleaseDate().isAfter(LocalDate.now())) {
|
||||||
|
throw new BadRequestException("Release date cannot be in the future");
|
||||||
|
}
|
||||||
|
version.setReleaseDate(request.getReleaseDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.getEndOfLifeDate() != null) {
|
||||||
|
version.setEndOfLifeDate(request.getEndOfLifeDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate dates
|
||||||
|
if (version.getEndOfLifeDate() != null &&
|
||||||
|
version.getReleaseDate().isAfter(version.getEndOfLifeDate())) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
"End of life date must be after release date");
|
||||||
|
}
|
||||||
|
|
||||||
|
version = versionRepository.save(version);
|
||||||
|
return mapToResponse(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VersionResponse findById(UUID id) {
|
||||||
|
Version version = versionRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException(
|
||||||
|
"Version not found with id: " + id));
|
||||||
|
return mapToResponse(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<VersionResponse> findByApplication(UUID applicationId, Pageable pageable) {
|
||||||
|
if (!applicationRepository.existsById(applicationId)) {
|
||||||
|
throw new ResourceNotFoundException(
|
||||||
|
"Application not found with id: " + applicationId);
|
||||||
|
}
|
||||||
|
return versionRepository.findByApplicationId(applicationId, pageable)
|
||||||
|
.map(this::mapToResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<VersionResponse> findLatestByApplication(UUID applicationId) {
|
||||||
|
if (!applicationRepository.existsById(applicationId)) {
|
||||||
|
throw new ResourceNotFoundException(
|
||||||
|
"Application not found with id: " + applicationId);
|
||||||
|
}
|
||||||
|
return versionRepository.findLatestByApplicationId(applicationId)
|
||||||
|
.map(this::mapToResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void delete(UUID id) {
|
||||||
|
Version version = versionRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException(
|
||||||
|
"Version not found with id: " + id));
|
||||||
|
|
||||||
|
// Note: In production, check if version is deployed anywhere before deletion
|
||||||
|
versionRepository.delete(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VersionResponse mapToResponse(Version version) {
|
||||||
|
return new VersionResponse(
|
||||||
|
version.getId(),
|
||||||
|
version.getApplication().getId(),
|
||||||
|
version.getApplication().getName(),
|
||||||
|
version.getVersionIdentifier(),
|
||||||
|
version.getExternalReference(),
|
||||||
|
version.getReleaseDate(),
|
||||||
|
version.getEndOfLifeDate(),
|
||||||
|
version.getCreatedAt(),
|
||||||
|
version.getUpdatedAt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,18 +5,13 @@
|
|||||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
||||||
|
|
||||||
<!-- Story 0: Foundation -->
|
|
||||||
<include file="db/changelog/v1.0/001-create-user-table.xml"/>
|
<include file="db/changelog/v1.0/001-create-user-table.xml"/>
|
||||||
<include file="db/changelog/v1.0/002-create-environment-table.xml"/>
|
<include file="db/changelog/v1.0/002-create-environment-table.xml"/>
|
||||||
<include file="db/changelog/data/initial-data.xml"/>
|
<include file="db/changelog/data/initial-data.xml"/>
|
||||||
|
|
||||||
<!-- Story 1: Business Units -->
|
|
||||||
<include file="db/changelog/v1.0/003-create-business-unit-table.xml"/>
|
<include file="db/changelog/v1.0/003-create-business-unit-table.xml"/>
|
||||||
|
|
||||||
<!-- Story 2: Applications -->
|
|
||||||
<include file="db/changelog/v1.0/004-create-application-table.xml"/>
|
<include file="db/changelog/v1.0/004-create-application-table.xml"/>
|
||||||
|
|
||||||
<!-- Story 3: Contacts -->
|
|
||||||
<include file="db/changelog/v1.0/005-create-contact-tables.xml"/>
|
<include file="db/changelog/v1.0/005-create-contact-tables.xml"/>
|
||||||
|
<include file="db/changelog/v1.0/006-create-version-table.xml"/>
|
||||||
|
<include file="db/changelog/v1.0/007-create-deployment-table.xml"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<databaseChangeLog
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
||||||
|
|
||||||
|
<changeSet id="006-create-version-table" author="ldpv2-team">
|
||||||
|
|
||||||
|
<createTable tableName="version">
|
||||||
|
<column name="id" type="UUID" defaultValueComputed="uuid_generate_v4()">
|
||||||
|
<constraints primaryKey="true" nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="application_id" type="UUID">
|
||||||
|
<constraints nullable="false"
|
||||||
|
foreignKeyName="fk_version_application"
|
||||||
|
references="application(id)"
|
||||||
|
deleteCascade="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="version_identifier" type="VARCHAR(100)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="external_reference" type="VARCHAR(500)"/>
|
||||||
|
<column name="release_date" type="DATE">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="end_of_life_date" type="DATE"/>
|
||||||
|
<column name="created_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="updated_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
|
||||||
|
<addUniqueConstraint
|
||||||
|
tableName="version"
|
||||||
|
columnNames="application_id, version_identifier"
|
||||||
|
constraintName="uk_version_app_identifier"/>
|
||||||
|
|
||||||
|
<createIndex tableName="version" indexName="idx_version_application">
|
||||||
|
<column name="application_id"/>
|
||||||
|
</createIndex>
|
||||||
|
|
||||||
|
<createIndex tableName="version" indexName="idx_version_release_date">
|
||||||
|
<column name="release_date"/>
|
||||||
|
</createIndex>
|
||||||
|
|
||||||
|
<!-- Sample data -->
|
||||||
|
<insert tableName="version">
|
||||||
|
<column name="application_id" valueComputed="(SELECT id FROM application WHERE name = 'Customer Portal' LIMIT 1)"/>
|
||||||
|
<column name="version_identifier" value="1.0.0"/>
|
||||||
|
<column name="external_reference" value="https://github.com/org/customer-portal/releases/tag/v1.0.0"/>
|
||||||
|
<column name="release_date" value="2024-01-15"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<insert tableName="version">
|
||||||
|
<column name="application_id" valueComputed="(SELECT id FROM application WHERE name = 'Customer Portal' LIMIT 1)"/>
|
||||||
|
<column name="version_identifier" value="1.1.0"/>
|
||||||
|
<column name="release_date" value="2024-06-20"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<insert tableName="version">
|
||||||
|
<column name="application_id" valueComputed="(SELECT id FROM application WHERE name = 'Customer Portal' LIMIT 1)"/>
|
||||||
|
<column name="version_identifier" value="1.2.0"/>
|
||||||
|
<column name="release_date" value="2025-01-10"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<insert tableName="version">
|
||||||
|
<column name="application_id" valueComputed="(SELECT id FROM application WHERE name = 'Internal CRM' LIMIT 1)"/>
|
||||||
|
<column name="version_identifier" value="2.0.0"/>
|
||||||
|
<column name="release_date" value="2024-03-01"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<insert tableName="version">
|
||||||
|
<column name="application_id" valueComputed="(SELECT id FROM application WHERE name = 'Internal CRM' LIMIT 1)"/>
|
||||||
|
<column name="version_identifier" value="2.1.0"/>
|
||||||
|
<column name="release_date" value="2024-09-15"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<databaseChangeLog
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
||||||
|
|
||||||
|
<changeSet id="007-create-deployment-table" author="ldpv2-team">
|
||||||
|
|
||||||
|
<createTable tableName="deployment">
|
||||||
|
<column name="id" type="UUID" defaultValueComputed="uuid_generate_v4()">
|
||||||
|
<constraints primaryKey="true" nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="application_id" type="UUID">
|
||||||
|
<constraints nullable="false"
|
||||||
|
foreignKeyName="fk_deployment_application"
|
||||||
|
references="application(id)"/>
|
||||||
|
</column>
|
||||||
|
<column name="version_id" type="UUID">
|
||||||
|
<constraints nullable="false"
|
||||||
|
foreignKeyName="fk_deployment_version"
|
||||||
|
references="version(id)"/>
|
||||||
|
</column>
|
||||||
|
<column name="environment_id" type="UUID">
|
||||||
|
<constraints nullable="false"
|
||||||
|
foreignKeyName="fk_deployment_environment"
|
||||||
|
references="environment(id)"/>
|
||||||
|
</column>
|
||||||
|
<column name="deployment_date" type="TIMESTAMP">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="deployed_by" type="VARCHAR(255)"/>
|
||||||
|
<column name="notes" type="TEXT"/>
|
||||||
|
<column name="created_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
|
||||||
|
<createIndex tableName="deployment" indexName="idx_deployment_application">
|
||||||
|
<column name="application_id"/>
|
||||||
|
</createIndex>
|
||||||
|
|
||||||
|
<createIndex tableName="deployment" indexName="idx_deployment_environment">
|
||||||
|
<column name="environment_id"/>
|
||||||
|
</createIndex>
|
||||||
|
|
||||||
|
<createIndex tableName="deployment" indexName="idx_deployment_date">
|
||||||
|
<column name="deployment_date" descending="true"/>
|
||||||
|
</createIndex>
|
||||||
|
|
||||||
|
<createIndex tableName="deployment" indexName="idx_deployment_app_env">
|
||||||
|
<column name="application_id"/>
|
||||||
|
<column name="environment_id"/>
|
||||||
|
</createIndex>
|
||||||
|
|
||||||
|
<!-- Sample deployments -->
|
||||||
|
<insert tableName="deployment">
|
||||||
|
<column name="application_id" valueComputed="(SELECT id FROM application WHERE name = 'Customer Portal' LIMIT 1)"/>
|
||||||
|
<column name="version_id" valueComputed="(SELECT id FROM version WHERE version_identifier = '1.2.0' LIMIT 1)"/>
|
||||||
|
<column name="environment_id" valueComputed="(SELECT id FROM environment WHERE name = 'PROD-EU' LIMIT 1)"/>
|
||||||
|
<column name="deployment_date" value="2025-01-15T10:30:00"/>
|
||||||
|
<column name="deployed_by" value="admin"/>
|
||||||
|
<column name="notes" value="Production deployment"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<insert tableName="deployment">
|
||||||
|
<column name="application_id" valueComputed="(SELECT id FROM application WHERE name = 'Internal CRM' LIMIT 1)"/>
|
||||||
|
<column name="version_id" valueComputed="(SELECT id FROM version WHERE version_identifier = '2.1.0' LIMIT 1)"/>
|
||||||
|
<column name="environment_id" valueComputed="(SELECT id FROM environment WHERE name = 'PROD-EU' LIMIT 1)"/>
|
||||||
|
<column name="deployment_date" value="2024-10-01T11:00:00"/>
|
||||||
|
<column name="deployed_by" value="admin"/>
|
||||||
|
<column name="notes" value="Major update"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { Deployment, RecordDeploymentRequest, CurrentDeploymentState } from '../../shared/models/deployment.model';
|
||||||
|
import { Page } from '../../shared/models/environment.model';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class DeploymentService {
|
||||||
|
private readonly API_URL = '/api/deployments';
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
getDeployments(
|
||||||
|
filters?: {
|
||||||
|
applicationId?: string;
|
||||||
|
environmentId?: string;
|
||||||
|
versionId?: string;
|
||||||
|
dateFrom?: Date;
|
||||||
|
dateTo?: Date;
|
||||||
|
},
|
||||||
|
page: number = 0,
|
||||||
|
size: number = 20,
|
||||||
|
sortBy: string = 'deploymentDate',
|
||||||
|
sortDirection: string = 'desc'
|
||||||
|
): Observable<Page<Deployment>> {
|
||||||
|
let params = new HttpParams()
|
||||||
|
.set('page', page.toString())
|
||||||
|
.set('size', size.toString())
|
||||||
|
.set('sortBy', sortBy)
|
||||||
|
.set('sortDirection', sortDirection);
|
||||||
|
|
||||||
|
if (filters?.applicationId) {
|
||||||
|
params = params.set('applicationId', filters.applicationId);
|
||||||
|
}
|
||||||
|
if (filters?.environmentId) {
|
||||||
|
params = params.set('environmentId', filters.environmentId);
|
||||||
|
}
|
||||||
|
if (filters?.versionId) {
|
||||||
|
params = params.set('versionId', filters.versionId);
|
||||||
|
}
|
||||||
|
if (filters?.dateFrom) {
|
||||||
|
params = params.set('dateFrom', filters.dateFrom.toISOString());
|
||||||
|
}
|
||||||
|
if (filters?.dateTo) {
|
||||||
|
params = params.set('dateTo', filters.dateTo.toISOString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http.get<Page<Deployment>>(this.API_URL, { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
getDeployment(id: string): Observable<Deployment> {
|
||||||
|
return this.http.get<Deployment>(`${this.API_URL}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDeploymentsByApplication(applicationId: string, page: number = 0, size: number = 20): Observable<Page<Deployment>> {
|
||||||
|
const params = new HttpParams()
|
||||||
|
.set('page', page.toString())
|
||||||
|
.set('size', size.toString());
|
||||||
|
|
||||||
|
return this.http.get<Page<Deployment>>(
|
||||||
|
`${this.API_URL}/by-application/${applicationId}`,
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDeploymentsByEnvironment(environmentId: string, page: number = 0, size: number = 20): Observable<Page<Deployment>> {
|
||||||
|
const params = new HttpParams()
|
||||||
|
.set('page', page.toString())
|
||||||
|
.set('size', size.toString());
|
||||||
|
|
||||||
|
return this.http.get<Page<Deployment>>(
|
||||||
|
`${this.API_URL}/by-environment/${environmentId}`,
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentState(applicationId?: string, environmentId?: string): Observable<CurrentDeploymentState[]> {
|
||||||
|
let params = new HttpParams();
|
||||||
|
if (applicationId) {
|
||||||
|
params = params.set('applicationId', applicationId);
|
||||||
|
}
|
||||||
|
if (environmentId) {
|
||||||
|
params = params.set('environmentId', environmentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http.get<CurrentDeploymentState[]>(
|
||||||
|
`${this.API_URL}/current`,
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
recordDeployment(data: RecordDeploymentRequest): Observable<Deployment> {
|
||||||
|
return this.http.post<Deployment>(this.API_URL, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { Version, CreateVersionRequest, UpdateVersionRequest } from '../../shared/models/version.model';
|
||||||
|
import { Page } from '../../shared/models/environment.model';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class VersionService {
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
getVersions(
|
||||||
|
applicationId: string,
|
||||||
|
page: number = 0,
|
||||||
|
size: number = 20,
|
||||||
|
sortBy: string = 'releaseDate',
|
||||||
|
sortDirection: string = 'desc'
|
||||||
|
): Observable<Page<Version>> {
|
||||||
|
const params = new HttpParams()
|
||||||
|
.set('page', page.toString())
|
||||||
|
.set('size', size.toString())
|
||||||
|
.set('sortBy', sortBy)
|
||||||
|
.set('sortDirection', sortDirection);
|
||||||
|
|
||||||
|
return this.http.get<Page<Version>>(
|
||||||
|
`/api/applications/${applicationId}/versions`,
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getVersion(applicationId: string, versionId: string): Observable<Version> {
|
||||||
|
return this.http.get<Version>(
|
||||||
|
`/api/applications/${applicationId}/versions/${versionId}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLatestVersion(applicationId: string): Observable<Version> {
|
||||||
|
return this.http.get<Version>(
|
||||||
|
`/api/applications/${applicationId}/versions/latest`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createVersion(applicationId: string, data: CreateVersionRequest): Observable<Version> {
|
||||||
|
return this.http.post<Version>(
|
||||||
|
`/api/applications/${applicationId}/versions`,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVersion(applicationId: string, versionId: string, data: UpdateVersionRequest): Observable<Version> {
|
||||||
|
return this.http.put<Version>(
|
||||||
|
`/api/applications/${applicationId}/versions/${versionId}`,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteVersion(applicationId: string, versionId: string): Observable<void> {
|
||||||
|
return this.http.delete<void>(
|
||||||
|
`/api/applications/${applicationId}/versions/${versionId}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { ApplicationStatus } from './application.model';
|
||||||
|
|
||||||
|
export interface Deployment {
|
||||||
|
id: string;
|
||||||
|
application: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
status: ApplicationStatus;
|
||||||
|
businessUnitName: string;
|
||||||
|
};
|
||||||
|
version: {
|
||||||
|
id: string;
|
||||||
|
versionIdentifier: string;
|
||||||
|
releaseDate: Date;
|
||||||
|
};
|
||||||
|
environment: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
isProduction: boolean;
|
||||||
|
};
|
||||||
|
deploymentDate: Date;
|
||||||
|
deployedBy?: string;
|
||||||
|
notes?: string;
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RecordDeploymentRequest {
|
||||||
|
applicationId: string;
|
||||||
|
versionId: string;
|
||||||
|
environmentId: string;
|
||||||
|
deploymentDate: Date;
|
||||||
|
deployedBy?: string;
|
||||||
|
notes?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CurrentDeploymentState {
|
||||||
|
application: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
status: ApplicationStatus;
|
||||||
|
businessUnitName: string;
|
||||||
|
};
|
||||||
|
environment: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
isProduction: boolean;
|
||||||
|
};
|
||||||
|
version: {
|
||||||
|
id: string;
|
||||||
|
versionIdentifier: string;
|
||||||
|
releaseDate: Date;
|
||||||
|
};
|
||||||
|
deploymentDate: Date;
|
||||||
|
deployedBy?: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
export interface Version {
|
||||||
|
id: string;
|
||||||
|
applicationId: string;
|
||||||
|
applicationName: string;
|
||||||
|
versionIdentifier: string;
|
||||||
|
externalReference?: string;
|
||||||
|
releaseDate: Date;
|
||||||
|
endOfLifeDate?: Date;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VersionSummary {
|
||||||
|
id: string;
|
||||||
|
versionIdentifier: string;
|
||||||
|
releaseDate: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateVersionRequest {
|
||||||
|
versionIdentifier: string;
|
||||||
|
externalReference?: string;
|
||||||
|
releaseDate: Date;
|
||||||
|
endOfLifeDate?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateVersionRequest {
|
||||||
|
versionIdentifier?: string;
|
||||||
|
externalReference?: string;
|
||||||
|
releaseDate?: Date;
|
||||||
|
endOfLifeDate?: Date;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user