autocomit
This commit is contained in:
@@ -0,0 +1,55 @@
|
|||||||
|
# Story 8: External Dependencies - Deployment Notes
|
||||||
|
|
||||||
|
## Files Created
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
- Database migration: `009-create-external-dependency-tables.xml`
|
||||||
|
- Entities: `DependencyType.java`, `ExternalDependency.java`
|
||||||
|
- Repositories: `DependencyTypeRepository.java`, `ExternalDependencyRepository.java`
|
||||||
|
- Services: `DependencyTypeService.java`, `ExternalDependencyService.java`
|
||||||
|
- Controllers: `DependencyTypeController.java`, `ExternalDependencyController.java`
|
||||||
|
- DTOs: Request and Response classes for both entities
|
||||||
|
- Updated: `db.changelog-master.xml`
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
- Models: `dependency.model.ts`
|
||||||
|
- Service: `dependency.service.ts`
|
||||||
|
- Components:
|
||||||
|
- `application-dependencies` (tab in application detail)
|
||||||
|
- `dependency-list` (full list page)
|
||||||
|
- `dependency-form` (create/edit)
|
||||||
|
- `dependency-detail` (view details)
|
||||||
|
- `dependency-type-list` (admin catalog management)
|
||||||
|
|
||||||
|
## Deployment Steps
|
||||||
|
|
||||||
|
1. Copy all backend files to their respective locations
|
||||||
|
2. Run Liquibase migration: `mvn liquibase:update`
|
||||||
|
3. Build backend: `mvn clean package`
|
||||||
|
4. Copy frontend files
|
||||||
|
5. Install dependencies: `npm install` (if needed)
|
||||||
|
6. Build frontend: `ng build`
|
||||||
|
|
||||||
|
## Testing Checklist
|
||||||
|
|
||||||
|
- [ ] Default dependency types seeded
|
||||||
|
- [ ] Create custom dependency type (admin)
|
||||||
|
- [ ] Create external dependency
|
||||||
|
- [ ] Validate date logic
|
||||||
|
- [ ] Filter by type and status
|
||||||
|
- [ ] View expiring dependencies
|
||||||
|
- [ ] Update dependency
|
||||||
|
- [ ] Delete dependency
|
||||||
|
- [ ] Cannot delete type with dependencies
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
See Swagger UI at: http://localhost:8080/api/swagger-ui.html
|
||||||
|
|
||||||
|
Key endpoints:
|
||||||
|
- GET /api/dependency-types
|
||||||
|
- POST /api/dependency-types (admin)
|
||||||
|
- GET /api/dependencies
|
||||||
|
- POST /api/dependencies/for-application/{id}
|
||||||
|
- GET /api/dependencies/expiring?days=30
|
||||||
|
- GET /api/dependencies/expired
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.ldpv2.controller;
|
||||||
|
|
||||||
|
import com.ldpv2.dto.request.CreateDependencyTypeRequest;
|
||||||
|
import com.ldpv2.dto.request.UpdateDependencyTypeRequest;
|
||||||
|
import com.ldpv2.dto.response.DependencyTypeResponse;
|
||||||
|
import com.ldpv2.service.DependencyTypeService;
|
||||||
|
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.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/dependency-types")
|
||||||
|
@Tag(name = "Dependency Types", description = "Dependency type catalog management")
|
||||||
|
@SecurityRequirement(name = "bearerAuth")
|
||||||
|
public class DependencyTypeController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DependencyTypeService dependencyTypeService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
@Operation(summary = "Create dependency type", description = "Create custom dependency type (Admin only)")
|
||||||
|
public ResponseEntity<DependencyTypeResponse> create(@Valid @RequestBody CreateDependencyTypeRequest request) {
|
||||||
|
DependencyTypeResponse response = dependencyTypeService.create(request);
|
||||||
|
return new ResponseEntity<>(response, HttpStatus.CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
@Operation(summary = "Update dependency type", description = "Update dependency type (Admin only)")
|
||||||
|
public ResponseEntity<DependencyTypeResponse> update(
|
||||||
|
@PathVariable UUID id,
|
||||||
|
@Valid @RequestBody UpdateDependencyTypeRequest request) {
|
||||||
|
DependencyTypeResponse response = dependencyTypeService.update(id, request);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@Operation(summary = "Get dependency type", description = "Get dependency type by ID")
|
||||||
|
public ResponseEntity<DependencyTypeResponse> getById(@PathVariable UUID id) {
|
||||||
|
DependencyTypeResponse response = dependencyTypeService.findById(id);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@Operation(summary = "List dependency types", description = "Get all dependency types")
|
||||||
|
public ResponseEntity<List<DependencyTypeResponse>> getAll() {
|
||||||
|
List<DependencyTypeResponse> response = dependencyTypeService.findAll();
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
@Operation(summary = "Delete dependency type", description = "Delete dependency type (Admin only)")
|
||||||
|
public ResponseEntity<Void> delete(@PathVariable UUID id) {
|
||||||
|
dependencyTypeService.delete(id);
|
||||||
|
return ResponseEntity.noContent().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
package com.ldpv2.controller;
|
||||||
|
|
||||||
|
import com.ldpv2.dto.request.CreateExternalDependencyRequest;
|
||||||
|
import com.ldpv2.dto.request.UpdateExternalDependencyRequest;
|
||||||
|
import com.ldpv2.dto.response.ExternalDependencyResponse;
|
||||||
|
import com.ldpv2.service.ExternalDependencyService;
|
||||||
|
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.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/dependencies")
|
||||||
|
@Tag(name = "External Dependencies", description = "External dependency management")
|
||||||
|
@SecurityRequirement(name = "bearerAuth")
|
||||||
|
public class ExternalDependencyController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ExternalDependencyService externalDependencyService;
|
||||||
|
|
||||||
|
@PostMapping("/for-application/{applicationId}")
|
||||||
|
@Operation(summary = "Create dependency", description = "Create external dependency for application")
|
||||||
|
public ResponseEntity<ExternalDependencyResponse> create(
|
||||||
|
@PathVariable UUID applicationId,
|
||||||
|
@Valid @RequestBody CreateExternalDependencyRequest request) {
|
||||||
|
ExternalDependencyResponse response = externalDependencyService.create(applicationId, request);
|
||||||
|
return new ResponseEntity<>(response, HttpStatus.CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
@Operation(summary = "Update dependency", description = "Update external dependency")
|
||||||
|
public ResponseEntity<ExternalDependencyResponse> update(
|
||||||
|
@PathVariable UUID id,
|
||||||
|
@Valid @RequestBody UpdateExternalDependencyRequest request) {
|
||||||
|
ExternalDependencyResponse response = externalDependencyService.update(id, request);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@Operation(summary = "Get dependency", description = "Get external dependency by ID")
|
||||||
|
public ResponseEntity<ExternalDependencyResponse> getById(@PathVariable UUID id) {
|
||||||
|
ExternalDependencyResponse response = externalDependencyService.findById(id);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@Operation(summary = "List dependencies", description = "Get all dependencies with filters")
|
||||||
|
public ResponseEntity<Page<ExternalDependencyResponse>> getAll(
|
||||||
|
@RequestParam(required = false) UUID applicationId,
|
||||||
|
@RequestParam(required = false) UUID dependencyTypeId,
|
||||||
|
@RequestParam(required = false) String status,
|
||||||
|
@RequestParam(defaultValue = "0") int page,
|
||||||
|
@RequestParam(defaultValue = "20") int size,
|
||||||
|
@RequestParam(defaultValue = "name") String sortBy,
|
||||||
|
@RequestParam(defaultValue = "asc") String sortDirection) {
|
||||||
|
|
||||||
|
Sort sort = sortDirection.equalsIgnoreCase("desc")
|
||||||
|
? Sort.by(sortBy).descending()
|
||||||
|
: Sort.by(sortBy).ascending();
|
||||||
|
|
||||||
|
Pageable pageable = PageRequest.of(page, size, sort);
|
||||||
|
|
||||||
|
Page<ExternalDependencyResponse> response;
|
||||||
|
if (applicationId != null || dependencyTypeId != null || status != null) {
|
||||||
|
response = externalDependencyService.search(applicationId, dependencyTypeId, status, pageable);
|
||||||
|
} else {
|
||||||
|
response = externalDependencyService.findAll(pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/by-application/{applicationId}")
|
||||||
|
@Operation(summary = "Get dependencies by application", description = "Get all dependencies for an application")
|
||||||
|
public ResponseEntity<Page<ExternalDependencyResponse>> getByApplication(
|
||||||
|
@PathVariable UUID applicationId,
|
||||||
|
@RequestParam(defaultValue = "0") int page,
|
||||||
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
|
|
||||||
|
Pageable pageable = PageRequest.of(page, size, Sort.by("name").ascending());
|
||||||
|
Page<ExternalDependencyResponse> response = externalDependencyService.findByApplication(applicationId, pageable);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/expiring")
|
||||||
|
@Operation(summary = "Get expiring dependencies", description = "Get dependencies expiring within specified days")
|
||||||
|
public ResponseEntity<List<ExternalDependencyResponse>> getExpiring(
|
||||||
|
@RequestParam(defaultValue = "30") int days) {
|
||||||
|
List<ExternalDependencyResponse> response = externalDependencyService.findExpiring(days);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/expired")
|
||||||
|
@Operation(summary = "Get expired dependencies", description = "Get all expired dependencies")
|
||||||
|
public ResponseEntity<List<ExternalDependencyResponse>> getExpired() {
|
||||||
|
List<ExternalDependencyResponse> response = externalDependencyService.findExpired();
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@Operation(summary = "Delete dependency", description = "Delete external dependency")
|
||||||
|
public ResponseEntity<Void> delete(@PathVariable UUID id) {
|
||||||
|
externalDependencyService.delete(id);
|
||||||
|
return ResponseEntity.noContent().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.ldpv2.domain.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency Type entity - catalog of dependency types
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Entity
|
||||||
|
@Table(name = "dependency_type")
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class DependencyType extends BaseEntity {
|
||||||
|
|
||||||
|
@Column(name = "type_name", nullable = false, unique = true, length = 100)
|
||||||
|
private String typeName;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Column(name = "is_custom", nullable = false)
|
||||||
|
private Boolean isCustom = false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.ldpv2.domain.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* External Dependency entity
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Entity
|
||||||
|
@Table(name = "external_dependency")
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class ExternalDependency extends BaseEntity {
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "application_id", nullable = false)
|
||||||
|
private Application application;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "dependency_type_id", nullable = false)
|
||||||
|
private DependencyType dependencyType;
|
||||||
|
|
||||||
|
@Column(nullable = false, length = 255)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Column(name = "technical_documentation", columnDefinition = "TEXT")
|
||||||
|
private String technicalDocumentation;
|
||||||
|
|
||||||
|
@Column(name = "validity_start_date")
|
||||||
|
private LocalDate validityStartDate;
|
||||||
|
|
||||||
|
@Column(name = "validity_end_date")
|
||||||
|
private LocalDate validityEndDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.ldpv2.dto.request;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CreateDependencyTypeRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "Type name is required")
|
||||||
|
@Size(max = 100, message = "Type name must not exceed 100 characters")
|
||||||
|
private String typeName;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
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;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CreateExternalDependencyRequest {
|
||||||
|
|
||||||
|
@NotNull(message = "Dependency type is required")
|
||||||
|
private UUID dependencyTypeId;
|
||||||
|
|
||||||
|
@NotBlank(message = "Name is required")
|
||||||
|
@Size(max = 255, message = "Name must not exceed 255 characters")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private String technicalDocumentation;
|
||||||
|
|
||||||
|
private LocalDate validityStartDate;
|
||||||
|
|
||||||
|
private LocalDate validityEndDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.ldpv2.dto.request;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class UpdateDependencyTypeRequest {
|
||||||
|
|
||||||
|
@Size(max = 100, message = "Type name must not exceed 100 characters")
|
||||||
|
private String typeName;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.ldpv2.dto.request;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class UpdateExternalDependencyRequest {
|
||||||
|
|
||||||
|
private UUID dependencyTypeId;
|
||||||
|
|
||||||
|
@Size(max = 255, message = "Name must not exceed 255 characters")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private String technicalDocumentation;
|
||||||
|
|
||||||
|
private LocalDate validityStartDate;
|
||||||
|
|
||||||
|
private LocalDate validityEndDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
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 DependencyTypeResponse {
|
||||||
|
private UUID id;
|
||||||
|
private String typeName;
|
||||||
|
private String description;
|
||||||
|
private Boolean isCustom;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
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 ExternalDependencyResponse {
|
||||||
|
private UUID id;
|
||||||
|
private ApplicationSummaryResponse application;
|
||||||
|
private DependencyTypeResponse dependencyType;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String technicalDocumentation;
|
||||||
|
private LocalDate validityStartDate;
|
||||||
|
private LocalDate validityEndDate;
|
||||||
|
private Boolean isActive;
|
||||||
|
private Integer daysUntilExpiration;
|
||||||
|
private String status; // ACTIVE, EXPIRING, EXPIRED, NOT_YET_VALID
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.ldpv2.repository;
|
||||||
|
|
||||||
|
import com.ldpv2.domain.entity.DependencyType;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface DependencyTypeRepository extends JpaRepository<DependencyType, UUID> {
|
||||||
|
Optional<DependencyType> findByTypeName(String typeName);
|
||||||
|
boolean existsByTypeName(String typeName);
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package com.ldpv2.repository;
|
||||||
|
|
||||||
|
import com.ldpv2.domain.entity.ExternalDependency;
|
||||||
|
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.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ExternalDependencyRepository extends JpaRepository<ExternalDependency, UUID> {
|
||||||
|
|
||||||
|
Page<ExternalDependency> findByApplicationId(UUID applicationId, Pageable pageable);
|
||||||
|
|
||||||
|
Page<ExternalDependency> findByDependencyTypeId(UUID dependencyTypeId, Pageable pageable);
|
||||||
|
|
||||||
|
@Query("SELECT d FROM ExternalDependency d WHERE " +
|
||||||
|
"d.validityEndDate IS NOT NULL AND " +
|
||||||
|
"d.validityEndDate >= :now AND " +
|
||||||
|
"d.validityEndDate <= :expirationDate")
|
||||||
|
List<ExternalDependency> findExpiring(
|
||||||
|
@Param("now") LocalDate now,
|
||||||
|
@Param("expirationDate") LocalDate expirationDate
|
||||||
|
);
|
||||||
|
|
||||||
|
@Query("SELECT d FROM ExternalDependency d WHERE " +
|
||||||
|
"d.validityEndDate IS NOT NULL AND " +
|
||||||
|
"d.validityEndDate < :now")
|
||||||
|
List<ExternalDependency> findExpired(@Param("now") LocalDate now);
|
||||||
|
|
||||||
|
@Query("SELECT d FROM ExternalDependency d WHERE " +
|
||||||
|
"(:applicationId IS NULL OR d.application.id = :applicationId) AND " +
|
||||||
|
"(:dependencyTypeId IS NULL OR d.dependencyType.id = :dependencyTypeId) AND " +
|
||||||
|
"(:status IS NULL OR " +
|
||||||
|
" (:status = 'ACTIVE' AND (d.validityEndDate IS NULL OR d.validityEndDate >= :now) AND (d.validityStartDate IS NULL OR d.validityStartDate <= :now)) OR " +
|
||||||
|
" (:status = 'EXPIRING' AND d.validityEndDate IS NOT NULL AND d.validityEndDate >= :now AND d.validityEndDate <= :expiringDate) OR " +
|
||||||
|
" (:status = 'EXPIRED' AND d.validityEndDate IS NOT NULL AND d.validityEndDate < :now) OR " +
|
||||||
|
" (:status = 'NOT_YET_VALID' AND d.validityStartDate IS NOT NULL AND d.validityStartDate > :now)" +
|
||||||
|
")")
|
||||||
|
Page<ExternalDependency> search(
|
||||||
|
@Param("applicationId") UUID applicationId,
|
||||||
|
@Param("dependencyTypeId") UUID dependencyTypeId,
|
||||||
|
@Param("status") String status,
|
||||||
|
@Param("now") LocalDate now,
|
||||||
|
@Param("expiringDate") LocalDate expiringDate,
|
||||||
|
Pageable pageable
|
||||||
|
);
|
||||||
|
|
||||||
|
long countByDependencyTypeId(UUID dependencyTypeId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package com.ldpv2.service;
|
||||||
|
|
||||||
|
import com.ldpv2.domain.entity.DependencyType;
|
||||||
|
import com.ldpv2.dto.request.CreateDependencyTypeRequest;
|
||||||
|
import com.ldpv2.dto.request.UpdateDependencyTypeRequest;
|
||||||
|
import com.ldpv2.dto.response.DependencyTypeResponse;
|
||||||
|
import com.ldpv2.exception.BadRequestException;
|
||||||
|
import com.ldpv2.exception.ResourceNotFoundException;
|
||||||
|
import com.ldpv2.repository.DependencyTypeRepository;
|
||||||
|
import com.ldpv2.repository.ExternalDependencyRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DependencyTypeService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DependencyTypeRepository dependencyTypeRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ExternalDependencyRepository externalDependencyRepository;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public DependencyTypeResponse create(CreateDependencyTypeRequest request) {
|
||||||
|
if (dependencyTypeRepository.existsByTypeName(request.getTypeName())) {
|
||||||
|
throw new BadRequestException("Dependency type '" + request.getTypeName() + "' already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
DependencyType type = new DependencyType();
|
||||||
|
type.setTypeName(request.getTypeName());
|
||||||
|
type.setDescription(request.getDescription());
|
||||||
|
type.setIsCustom(true); // User-created types are custom
|
||||||
|
|
||||||
|
type = dependencyTypeRepository.save(type);
|
||||||
|
return mapToResponse(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public DependencyTypeResponse update(UUID id, UpdateDependencyTypeRequest request) {
|
||||||
|
DependencyType type = dependencyTypeRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("Dependency type not found with id: " + id));
|
||||||
|
|
||||||
|
if (request.getTypeName() != null && !request.getTypeName().equals(type.getTypeName())) {
|
||||||
|
if (dependencyTypeRepository.existsByTypeName(request.getTypeName())) {
|
||||||
|
throw new BadRequestException("Dependency type '" + request.getTypeName() + "' already exists");
|
||||||
|
}
|
||||||
|
type.setTypeName(request.getTypeName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.getDescription() != null) {
|
||||||
|
type.setDescription(request.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
type = dependencyTypeRepository.save(type);
|
||||||
|
return mapToResponse(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DependencyTypeResponse findById(UUID id) {
|
||||||
|
DependencyType type = dependencyTypeRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("Dependency type not found with id: " + id));
|
||||||
|
return mapToResponse(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DependencyTypeResponse> findAll() {
|
||||||
|
return dependencyTypeRepository.findAll().stream()
|
||||||
|
.map(this::mapToResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void delete(UUID id) {
|
||||||
|
DependencyType type = dependencyTypeRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("Dependency type not found with id: " + id));
|
||||||
|
|
||||||
|
// Check if type is being used
|
||||||
|
long count = externalDependencyRepository.countByDependencyTypeId(id);
|
||||||
|
if (count > 0) {
|
||||||
|
throw new BadRequestException("Cannot delete dependency type with " + count + " existing dependencies");
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyTypeRepository.delete(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DependencyTypeResponse mapToResponse(DependencyType type) {
|
||||||
|
return new DependencyTypeResponse(
|
||||||
|
type.getId(),
|
||||||
|
type.getTypeName(),
|
||||||
|
type.getDescription(),
|
||||||
|
type.getIsCustom(),
|
||||||
|
type.getCreatedAt(),
|
||||||
|
type.getUpdatedAt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
package com.ldpv2.service;
|
||||||
|
|
||||||
|
import com.ldpv2.domain.entity.Application;
|
||||||
|
import com.ldpv2.domain.entity.DependencyType;
|
||||||
|
import com.ldpv2.domain.entity.ExternalDependency;
|
||||||
|
import com.ldpv2.dto.request.CreateExternalDependencyRequest;
|
||||||
|
import com.ldpv2.dto.request.UpdateExternalDependencyRequest;
|
||||||
|
import com.ldpv2.dto.response.ApplicationSummaryResponse;
|
||||||
|
import com.ldpv2.dto.response.DependencyTypeResponse;
|
||||||
|
import com.ldpv2.dto.response.ExternalDependencyResponse;
|
||||||
|
import com.ldpv2.exception.BadRequestException;
|
||||||
|
import com.ldpv2.exception.ResourceNotFoundException;
|
||||||
|
import com.ldpv2.repository.ApplicationRepository;
|
||||||
|
import com.ldpv2.repository.DependencyTypeRepository;
|
||||||
|
import com.ldpv2.repository.ExternalDependencyRepository;
|
||||||
|
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.time.temporal.ChronoUnit;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ExternalDependencyService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ExternalDependencyRepository externalDependencyRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationRepository applicationRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DependencyTypeRepository dependencyTypeRepository;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public ExternalDependencyResponse create(UUID applicationId, CreateExternalDependencyRequest request) {
|
||||||
|
Application application = applicationRepository.findById(applicationId)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("Application not found with id: " + applicationId));
|
||||||
|
|
||||||
|
DependencyType dependencyType = dependencyTypeRepository.findById(request.getDependencyTypeId())
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException(
|
||||||
|
"Dependency type not found with id: " + request.getDependencyTypeId()));
|
||||||
|
|
||||||
|
// Validate dates
|
||||||
|
if (request.getValidityStartDate() != null && request.getValidityEndDate() != null) {
|
||||||
|
if (request.getValidityEndDate().isBefore(request.getValidityStartDate())) {
|
||||||
|
throw new BadRequestException("End date must be after or equal to start date");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalDependency dependency = new ExternalDependency();
|
||||||
|
dependency.setApplication(application);
|
||||||
|
dependency.setDependencyType(dependencyType);
|
||||||
|
dependency.setName(request.getName());
|
||||||
|
dependency.setDescription(request.getDescription());
|
||||||
|
dependency.setTechnicalDocumentation(request.getTechnicalDocumentation());
|
||||||
|
dependency.setValidityStartDate(request.getValidityStartDate());
|
||||||
|
dependency.setValidityEndDate(request.getValidityEndDate());
|
||||||
|
|
||||||
|
dependency = externalDependencyRepository.save(dependency);
|
||||||
|
return mapToResponse(dependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public ExternalDependencyResponse update(UUID id, UpdateExternalDependencyRequest request) {
|
||||||
|
ExternalDependency dependency = externalDependencyRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("External dependency not found with id: " + id));
|
||||||
|
|
||||||
|
if (request.getDependencyTypeId() != null) {
|
||||||
|
DependencyType dependencyType = dependencyTypeRepository.findById(request.getDependencyTypeId())
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException(
|
||||||
|
"Dependency type not found with id: " + request.getDependencyTypeId()));
|
||||||
|
dependency.setDependencyType(dependencyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.getName() != null) {
|
||||||
|
dependency.setName(request.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.getDescription() != null) {
|
||||||
|
dependency.setDescription(request.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.getTechnicalDocumentation() != null) {
|
||||||
|
dependency.setTechnicalDocumentation(request.getTechnicalDocumentation());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.getValidityStartDate() != null) {
|
||||||
|
dependency.setValidityStartDate(request.getValidityStartDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.getValidityEndDate() != null) {
|
||||||
|
dependency.setValidityEndDate(request.getValidityEndDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate dates after updates
|
||||||
|
if (dependency.getValidityStartDate() != null && dependency.getValidityEndDate() != null) {
|
||||||
|
if (dependency.getValidityEndDate().isBefore(dependency.getValidityStartDate())) {
|
||||||
|
throw new BadRequestException("End date must be after or equal to start date");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependency = externalDependencyRepository.save(dependency);
|
||||||
|
return mapToResponse(dependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExternalDependencyResponse findById(UUID id) {
|
||||||
|
ExternalDependency dependency = externalDependencyRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("External dependency not found with id: " + id));
|
||||||
|
return mapToResponse(dependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<ExternalDependencyResponse> findByApplication(UUID applicationId, Pageable pageable) {
|
||||||
|
if (!applicationRepository.existsById(applicationId)) {
|
||||||
|
throw new ResourceNotFoundException("Application not found with id: " + applicationId);
|
||||||
|
}
|
||||||
|
return externalDependencyRepository.findByApplicationId(applicationId, pageable)
|
||||||
|
.map(this::mapToResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<ExternalDependencyResponse> findAll(Pageable pageable) {
|
||||||
|
return externalDependencyRepository.findAll(pageable).map(this::mapToResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<ExternalDependencyResponse> search(
|
||||||
|
UUID applicationId,
|
||||||
|
UUID dependencyTypeId,
|
||||||
|
String status,
|
||||||
|
Pageable pageable) {
|
||||||
|
|
||||||
|
LocalDate now = LocalDate.now();
|
||||||
|
LocalDate expiringDate = now.plusDays(30);
|
||||||
|
|
||||||
|
return externalDependencyRepository.search(
|
||||||
|
applicationId, dependencyTypeId, status, now, expiringDate, pageable)
|
||||||
|
.map(this::mapToResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ExternalDependencyResponse> findExpiring(int days) {
|
||||||
|
LocalDate now = LocalDate.now();
|
||||||
|
LocalDate expirationDate = now.plusDays(days);
|
||||||
|
return externalDependencyRepository.findExpiring(now, expirationDate).stream()
|
||||||
|
.map(this::mapToResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ExternalDependencyResponse> findExpired() {
|
||||||
|
LocalDate now = LocalDate.now();
|
||||||
|
return externalDependencyRepository.findExpired(now).stream()
|
||||||
|
.map(this::mapToResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void delete(UUID id) {
|
||||||
|
if (!externalDependencyRepository.existsById(id)) {
|
||||||
|
throw new ResourceNotFoundException("External dependency not found with id: " + id);
|
||||||
|
}
|
||||||
|
externalDependencyRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExternalDependencyResponse mapToResponse(ExternalDependency dependency) {
|
||||||
|
ApplicationSummaryResponse appSummary = new ApplicationSummaryResponse(
|
||||||
|
dependency.getApplication().getId(),
|
||||||
|
dependency.getApplication().getName(),
|
||||||
|
dependency.getApplication().getStatus(),
|
||||||
|
dependency.getApplication().getBusinessUnit().getName()
|
||||||
|
);
|
||||||
|
|
||||||
|
DependencyTypeResponse typeResponse = new DependencyTypeResponse(
|
||||||
|
dependency.getDependencyType().getId(),
|
||||||
|
dependency.getDependencyType().getTypeName(),
|
||||||
|
dependency.getDependencyType().getDescription(),
|
||||||
|
dependency.getDependencyType().getIsCustom(),
|
||||||
|
dependency.getDependencyType().getCreatedAt(),
|
||||||
|
dependency.getDependencyType().getUpdatedAt()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compute status
|
||||||
|
String status = computeStatus(dependency);
|
||||||
|
Boolean isActive = "ACTIVE".equals(status) || "EXPIRING".equals(status);
|
||||||
|
Integer daysUntilExpiration = computeDaysUntilExpiration(dependency);
|
||||||
|
|
||||||
|
return new ExternalDependencyResponse(
|
||||||
|
dependency.getId(),
|
||||||
|
appSummary,
|
||||||
|
typeResponse,
|
||||||
|
dependency.getName(),
|
||||||
|
dependency.getDescription(),
|
||||||
|
dependency.getTechnicalDocumentation(),
|
||||||
|
dependency.getValidityStartDate(),
|
||||||
|
dependency.getValidityEndDate(),
|
||||||
|
isActive,
|
||||||
|
daysUntilExpiration,
|
||||||
|
status,
|
||||||
|
dependency.getCreatedAt(),
|
||||||
|
dependency.getUpdatedAt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String computeStatus(ExternalDependency dependency) {
|
||||||
|
LocalDate now = LocalDate.now();
|
||||||
|
|
||||||
|
if (dependency.getValidityStartDate() != null && now.isBefore(dependency.getValidityStartDate())) {
|
||||||
|
return "NOT_YET_VALID";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dependency.getValidityEndDate() == null) {
|
||||||
|
return "ACTIVE"; // No end date = indefinite
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now.isAfter(dependency.getValidityEndDate())) {
|
||||||
|
return "EXPIRED";
|
||||||
|
}
|
||||||
|
|
||||||
|
long daysUntilExpiration = ChronoUnit.DAYS.between(now, dependency.getValidityEndDate());
|
||||||
|
if (daysUntilExpiration <= 30) {
|
||||||
|
return "EXPIRING";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "ACTIVE";
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer computeDaysUntilExpiration(ExternalDependency dependency) {
|
||||||
|
if (dependency.getValidityEndDate() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDate now = LocalDate.now();
|
||||||
|
if (now.isAfter(dependency.getValidityEndDate())) {
|
||||||
|
return null; // Already expired
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) ChronoUnit.DAYS.between(now, dependency.getValidityEndDate());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,5 +14,6 @@
|
|||||||
<include file="db/changelog/v1.0/006-create-version-table.xml"/>
|
<include file="db/changelog/v1.0/006-create-version-table.xml"/>
|
||||||
<include file="db/changelog/v1.0/007-create-deployment-table.xml"/>
|
<include file="db/changelog/v1.0/007-create-deployment-table.xml"/>
|
||||||
<include file="db/changelog/v1.0/008-create-application-contact-table.xml"/>
|
<include file="db/changelog/v1.0/008-create-application-contact-table.xml"/>
|
||||||
|
<include file="db/changelog/v1.0/009-create-external-dependency-tables.xml"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
<?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="009-create-external-dependency-tables" author="ldpv2-team">
|
||||||
|
|
||||||
|
<!-- Dependency Types Catalog -->
|
||||||
|
<createTable tableName="dependency_type">
|
||||||
|
<column name="id" type="UUID" defaultValueComputed="uuid_generate_v4()">
|
||||||
|
<constraints primaryKey="true" nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="type_name" type="VARCHAR(100)">
|
||||||
|
<constraints nullable="false" unique="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="description" type="TEXT"/>
|
||||||
|
<column name="is_custom" type="BOOLEAN" defaultValueBoolean="false">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- External Dependencies -->
|
||||||
|
<createTable tableName="external_dependency">
|
||||||
|
<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_ext_dep_application"
|
||||||
|
references="application(id)"
|
||||||
|
deleteCascade="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="dependency_type_id" type="UUID">
|
||||||
|
<constraints nullable="false"
|
||||||
|
foreignKeyName="fk_ext_dep_type"
|
||||||
|
references="dependency_type(id)"/>
|
||||||
|
</column>
|
||||||
|
<column name="name" type="VARCHAR(255)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="description" type="TEXT"/>
|
||||||
|
<column name="technical_documentation" type="TEXT"/>
|
||||||
|
<column name="validity_start_date" type="DATE"/>
|
||||||
|
<column name="validity_end_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>
|
||||||
|
|
||||||
|
<!-- Add check constraint for validity dates -->
|
||||||
|
<sql>
|
||||||
|
ALTER TABLE external_dependency
|
||||||
|
ADD CONSTRAINT check_validity_dates
|
||||||
|
CHECK (validity_end_date IS NULL OR validity_start_date IS NULL OR validity_end_date >= validity_start_date);
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!-- Indexes -->
|
||||||
|
<createIndex tableName="dependency_type" indexName="idx_dep_type_name">
|
||||||
|
<column name="type_name"/>
|
||||||
|
</createIndex>
|
||||||
|
|
||||||
|
<createIndex tableName="external_dependency" indexName="idx_ext_dep_application">
|
||||||
|
<column name="application_id"/>
|
||||||
|
</createIndex>
|
||||||
|
|
||||||
|
<createIndex tableName="external_dependency" indexName="idx_ext_dep_type">
|
||||||
|
<column name="dependency_type_id"/>
|
||||||
|
</createIndex>
|
||||||
|
|
||||||
|
<createIndex tableName="external_dependency" indexName="idx_ext_dep_validity_end">
|
||||||
|
<column name="validity_end_date"/>
|
||||||
|
</createIndex>
|
||||||
|
|
||||||
|
<createIndex tableName="external_dependency" indexName="idx_ext_dep_name">
|
||||||
|
<column name="name"/>
|
||||||
|
</createIndex>
|
||||||
|
|
||||||
|
<!-- Insert default dependency types -->
|
||||||
|
<insert tableName="dependency_type">
|
||||||
|
<column name="type_name" value="WEB_SERVICE"/>
|
||||||
|
<column name="description" value="REST APIs, SOAP services, microservices"/>
|
||||||
|
<column name="is_custom" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<insert tableName="dependency_type">
|
||||||
|
<column name="type_name" value="DATABASE"/>
|
||||||
|
<column name="description" value="External database connections"/>
|
||||||
|
<column name="is_custom" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<insert tableName="dependency_type">
|
||||||
|
<column name="type_name" value="CERTIFICATE"/>
|
||||||
|
<column name="description" value="SSL/TLS certificates, authentication certificates"/>
|
||||||
|
<column name="is_custom" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<insert tableName="dependency_type">
|
||||||
|
<column name="type_name" value="NETWORK_FLOW"/>
|
||||||
|
<column name="description" value="Network connections, firewall rules, VPN tunnels"/>
|
||||||
|
<column name="is_custom" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- Sample data -->
|
||||||
|
<insert tableName="external_dependency">
|
||||||
|
<column name="application_id" valueComputed="(SELECT id FROM application WHERE name = 'Customer Portal' LIMIT 1)"/>
|
||||||
|
<column name="dependency_type_id" valueComputed="(SELECT id FROM dependency_type WHERE type_name = 'WEB_SERVICE' LIMIT 1)"/>
|
||||||
|
<column name="name" value="Payment Gateway API"/>
|
||||||
|
<column name="description" value="External payment processing service"/>
|
||||||
|
<column name="technical_documentation" value="Endpoint: https://api.payment.example.com/v2"/>
|
||||||
|
<column name="validity_start_date" value="2024-01-01"/>
|
||||||
|
<column name="validity_end_date" value="2027-12-31"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<insert tableName="external_dependency">
|
||||||
|
<column name="application_id" valueComputed="(SELECT id FROM application WHERE name = 'Internal CRM' LIMIT 1)"/>
|
||||||
|
<column name="dependency_type_id" valueComputed="(SELECT id FROM dependency_type WHERE type_name = 'DATABASE' LIMIT 1)"/>
|
||||||
|
<column name="name" value="Legacy Customer Database"/>
|
||||||
|
<column name="description" value="Read-only connection to legacy system"/>
|
||||||
|
<column name="technical_documentation" value="Server: legacy-db.internal:5432"/>
|
||||||
|
<column name="validity_start_date" value="2020-01-01"/>
|
||||||
|
<column name="validity_end_date" value="2026-06-30"/>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+81
@@ -0,0 +1,81 @@
|
|||||||
|
<div class="dependencies-container">
|
||||||
|
<div class="header">
|
||||||
|
<h3>External Dependencies</h3>
|
||||||
|
<button (click)="createNew()" class="btn-primary">Add Dependency</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filters">
|
||||||
|
<div class="filter-group">
|
||||||
|
<label>Filter by Type:</label>
|
||||||
|
<select [(ngModel)]="selectedTypeId" (ngModelChange)="onFilterChange()" class="filter-select">
|
||||||
|
<option value="">All Types</option>
|
||||||
|
<option *ngFor="let type of dependencyTypes" [value]="type.id">
|
||||||
|
{{ type.typeName }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filter-group">
|
||||||
|
<label>Filter by Status:</label>
|
||||||
|
<select [(ngModel)]="selectedStatus" (ngModelChange)="onFilterChange()" class="filter-select">
|
||||||
|
<option *ngFor="let opt of statusOptions" [value]="opt.value">
|
||||||
|
{{ opt.label }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="loading" class="loading">Loading dependencies...</div>
|
||||||
|
<div *ngIf="error" class="error">{{ error }}</div>
|
||||||
|
|
||||||
|
<div *ngIf="!loading && dependencies.length > 0" class="dependencies-table">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Validity Period</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let dep of dependencies">
|
||||||
|
<td><strong>{{ dep.name }}</strong></td>
|
||||||
|
<td>{{ dep.dependencyType.typeName }}</td>
|
||||||
|
<td>
|
||||||
|
<span class="status-badge" [ngClass]="getStatusClass(dep.status)">
|
||||||
|
{{ getStatusLabel(dep.status) }}
|
||||||
|
<span *ngIf="dep.daysUntilExpiration !== null && dep.daysUntilExpiration !== undefined">
|
||||||
|
({{ dep.daysUntilExpiration }} days)
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div *ngIf="dep.validityStartDate || dep.validityEndDate">
|
||||||
|
{{ dep.validityStartDate ? (dep.validityStartDate | date:'mediumDate') : '-' }}
|
||||||
|
→
|
||||||
|
{{ dep.validityEndDate ? (dep.validityEndDate | date:'mediumDate') : 'Indefinite' }}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!dep.validityStartDate && !dep.validityEndDate">-</div>
|
||||||
|
</td>
|
||||||
|
<td class="actions">
|
||||||
|
<button (click)="viewDetails(dep.id)" class="btn-sm">View</button>
|
||||||
|
<button (click)="edit(dep.id)" class="btn-sm">Edit</button>
|
||||||
|
<button (click)="delete(dep.id)" class="btn-sm btn-danger">Delete</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="!loading && dependencies.length === 0" class="empty">
|
||||||
|
No external dependencies found for this application.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="totalPages > 1" class="pagination">
|
||||||
|
<button (click)="previousPage()" [disabled]="page === 0">Previous</button>
|
||||||
|
<span>Page {{ page + 1 }} of {{ totalPages }}</span>
|
||||||
|
<button (click)="nextPage()" [disabled]="page >= totalPages - 1">Next</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
+181
@@ -0,0 +1,181 @@
|
|||||||
|
.dependencies-container {
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #3f51b5;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #303f9f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background: #f9f9f9;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.filter-group {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 200px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #3f51b5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading, .error, .empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #f44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dependencies-table {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:hover {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
&.status-active {
|
||||||
|
background-color: #e8f5e9;
|
||||||
|
color: #388e3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.status-expiring {
|
||||||
|
background-color: #fff3e0;
|
||||||
|
color: #f57c00;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.status-expired {
|
||||||
|
background-color: #ffebee;
|
||||||
|
color: #c62828;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.status-not-valid {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #616161;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #2196f3;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #1976d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-danger {
|
||||||
|
background-color: #f44336;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #d32f2f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: white;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
+152
@@ -0,0 +1,152 @@
|
|||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { DependencyService } from '../../dependencies/dependency.service';
|
||||||
|
import { ExternalDependency, DependencyType } from '../../../shared/models/dependency.model';
|
||||||
|
import { Page } from '../../../shared/models/environment.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-application-dependencies',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, FormsModule],
|
||||||
|
templateUrl: './application-dependencies.component.html',
|
||||||
|
styleUrls: ['./application-dependencies.component.scss']
|
||||||
|
})
|
||||||
|
export class ApplicationDependenciesComponent implements OnInit {
|
||||||
|
@Input() applicationId!: string;
|
||||||
|
@Input() applicationName!: string;
|
||||||
|
|
||||||
|
dependencies: ExternalDependency[] = [];
|
||||||
|
dependencyTypes: DependencyType[] = [];
|
||||||
|
loading = false;
|
||||||
|
error = '';
|
||||||
|
|
||||||
|
page = 0;
|
||||||
|
size = 10;
|
||||||
|
totalPages = 0;
|
||||||
|
|
||||||
|
selectedTypeId = '';
|
||||||
|
selectedStatus = '';
|
||||||
|
|
||||||
|
statusOptions = [
|
||||||
|
{ value: '', label: 'All Statuses' },
|
||||||
|
{ value: 'ACTIVE', label: 'Active' },
|
||||||
|
{ value: 'EXPIRING', label: 'Expiring Soon' },
|
||||||
|
{ value: 'EXPIRED', label: 'Expired' },
|
||||||
|
{ value: 'NOT_YET_VALID', label: 'Not Yet Valid' }
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private dependencyService: DependencyService,
|
||||||
|
private router: Router
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (this.applicationId) {
|
||||||
|
this.loadDependencyTypes();
|
||||||
|
this.loadDependencies();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDependencyTypes(): void {
|
||||||
|
this.dependencyService.getDependencyTypes().subscribe({
|
||||||
|
next: (types) => {
|
||||||
|
this.dependencyTypes = types;
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
console.error('Failed to load dependency types', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDependencies(): void {
|
||||||
|
this.loading = true;
|
||||||
|
const filters: any = { applicationId: this.applicationId };
|
||||||
|
|
||||||
|
if (this.selectedTypeId) {
|
||||||
|
filters.dependencyTypeId = this.selectedTypeId;
|
||||||
|
}
|
||||||
|
if (this.selectedStatus) {
|
||||||
|
filters.status = this.selectedStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dependencyService.getDependencies(filters, this.page, this.size).subscribe({
|
||||||
|
next: (data: Page<ExternalDependency>) => {
|
||||||
|
this.dependencies = data.content;
|
||||||
|
this.totalPages = data.totalPages;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
this.error = 'Failed to load dependencies';
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onFilterChange(): void {
|
||||||
|
this.page = 0;
|
||||||
|
this.loadDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
createNew(): void {
|
||||||
|
this.router.navigate(['/dependencies/new'], {
|
||||||
|
queryParams: { applicationId: this.applicationId }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
viewDetails(id: string): void {
|
||||||
|
this.router.navigate(['/dependencies', id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
edit(id: string): void {
|
||||||
|
this.router.navigate(['/dependencies', id, 'edit']);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(id: string): void {
|
||||||
|
if (confirm('Are you sure you want to delete this dependency?')) {
|
||||||
|
this.dependencyService.deleteDependency(id).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.loadDependencies();
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
this.error = 'Failed to delete dependency';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusClass(status: string): string {
|
||||||
|
const classes: Record<string, string> = {
|
||||||
|
'ACTIVE': 'status-active',
|
||||||
|
'EXPIRING': 'status-expiring',
|
||||||
|
'EXPIRED': 'status-expired',
|
||||||
|
'NOT_YET_VALID': 'status-not-valid'
|
||||||
|
};
|
||||||
|
return classes[status] || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusLabel(status: string): string {
|
||||||
|
const labels: Record<string, string> = {
|
||||||
|
'ACTIVE': 'Active',
|
||||||
|
'EXPIRING': 'Expiring Soon',
|
||||||
|
'EXPIRED': 'Expired',
|
||||||
|
'NOT_YET_VALID': 'Not Yet Valid'
|
||||||
|
};
|
||||||
|
return labels[status] || status;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextPage(): void {
|
||||||
|
if (this.page < this.totalPages - 1) {
|
||||||
|
this.page++;
|
||||||
|
this.loadDependencies();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousPage(): void {
|
||||||
|
if (this.page > 0) {
|
||||||
|
this.page--;
|
||||||
|
this.loadDependencies();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import {
|
||||||
|
ExternalDependency,
|
||||||
|
CreateExternalDependencyRequest,
|
||||||
|
UpdateExternalDependencyRequest,
|
||||||
|
DependencyType,
|
||||||
|
CreateDependencyTypeRequest,
|
||||||
|
UpdateDependencyTypeRequest
|
||||||
|
} from '../../shared/models/dependency.model';
|
||||||
|
import { Page } from '../../shared/models/environment.model';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class DependencyService {
|
||||||
|
private readonly API_URL = '/api/dependencies';
|
||||||
|
private readonly TYPE_API_URL = '/api/dependency-types';
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
// Dependency Types
|
||||||
|
getDependencyTypes(): Observable<DependencyType[]> {
|
||||||
|
return this.http.get<DependencyType[]>(this.TYPE_API_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependencyType(id: string): Observable<DependencyType> {
|
||||||
|
return this.http.get<DependencyType>(`${this.TYPE_API_URL}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
createDependencyType(data: CreateDependencyTypeRequest): Observable<DependencyType> {
|
||||||
|
return this.http.post<DependencyType>(this.TYPE_API_URL, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDependencyType(id: string, data: UpdateDependencyTypeRequest): Observable<DependencyType> {
|
||||||
|
return this.http.put<DependencyType>(`${this.TYPE_API_URL}/${id}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteDependencyType(id: string): Observable<void> {
|
||||||
|
return this.http.delete<void>(`${this.TYPE_API_URL}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// External Dependencies
|
||||||
|
getDependencies(
|
||||||
|
filters?: {
|
||||||
|
applicationId?: string;
|
||||||
|
dependencyTypeId?: string;
|
||||||
|
status?: string;
|
||||||
|
},
|
||||||
|
page: number = 0,
|
||||||
|
size: number = 20,
|
||||||
|
sortBy: string = 'name',
|
||||||
|
sortDirection: string = 'asc'
|
||||||
|
): Observable<Page<ExternalDependency>> {
|
||||||
|
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?.dependencyTypeId) {
|
||||||
|
params = params.set('dependencyTypeId', filters.dependencyTypeId);
|
||||||
|
}
|
||||||
|
if (filters?.status) {
|
||||||
|
params = params.set('status', filters.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http.get<Page<ExternalDependency>>(this.API_URL, { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependency(id: string): Observable<ExternalDependency> {
|
||||||
|
return this.http.get<ExternalDependency>(`${this.API_URL}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependenciesByApplication(
|
||||||
|
applicationId: string,
|
||||||
|
page: number = 0,
|
||||||
|
size: number = 20
|
||||||
|
): Observable<Page<ExternalDependency>> {
|
||||||
|
const params = new HttpParams()
|
||||||
|
.set('page', page.toString())
|
||||||
|
.set('size', size.toString());
|
||||||
|
|
||||||
|
return this.http.get<Page<ExternalDependency>>(
|
||||||
|
`${this.API_URL}/by-application/${applicationId}`,
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createDependency(
|
||||||
|
applicationId: string,
|
||||||
|
data: CreateExternalDependencyRequest
|
||||||
|
): Observable<ExternalDependency> {
|
||||||
|
return this.http.post<ExternalDependency>(
|
||||||
|
`${this.API_URL}/for-application/${applicationId}`,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDependency(
|
||||||
|
id: string,
|
||||||
|
data: UpdateExternalDependencyRequest
|
||||||
|
): Observable<ExternalDependency> {
|
||||||
|
return this.http.put<ExternalDependency>(`${this.API_URL}/${id}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteDependency(id: string): Observable<void> {
|
||||||
|
return this.http.delete<void>(`${this.API_URL}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getExpiringDependencies(days: number = 30): Observable<ExternalDependency[]> {
|
||||||
|
const params = new HttpParams().set('days', days.toString());
|
||||||
|
return this.http.get<ExternalDependency[]>(`${this.API_URL}/expiring`, { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
getExpiredDependencies(): Observable<ExternalDependency[]> {
|
||||||
|
return this.http.get<ExternalDependency[]>(`${this.API_URL}/expired`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
export interface DependencyType {
|
||||||
|
id: string;
|
||||||
|
typeName: string;
|
||||||
|
description?: string;
|
||||||
|
isCustom: boolean;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExternalDependency {
|
||||||
|
id: string;
|
||||||
|
application: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
dependencyType: DependencyType;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
technicalDocumentation?: string;
|
||||||
|
validityStartDate?: Date;
|
||||||
|
validityEndDate?: Date;
|
||||||
|
isActive: boolean;
|
||||||
|
daysUntilExpiration?: number;
|
||||||
|
status: 'ACTIVE' | 'EXPIRING' | 'EXPIRED' | 'NOT_YET_VALID';
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateDependencyTypeRequest {
|
||||||
|
typeName: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateDependencyTypeRequest {
|
||||||
|
typeName?: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateExternalDependencyRequest {
|
||||||
|
dependencyTypeId: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
technicalDocumentation?: string;
|
||||||
|
validityStartDate?: Date;
|
||||||
|
validityEndDate?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateExternalDependencyRequest {
|
||||||
|
dependencyTypeId?: string;
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
technicalDocumentation?: string;
|
||||||
|
validityStartDate?: Date;
|
||||||
|
validityEndDate?: Date;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user