Spaces:
Build error
Build error
Ajay Yadav
commited on
Commit
·
623efaa
1
Parent(s):
0391561
Initial deployment of da-catalog-dev
Browse files- Dockerfile +27 -0
- README.md +33 -5
- build.gradle.kts +12 -0
- src/main/docker/Dockerfile +23 -0
- src/main/docker/Dockerfile.alpine-jlink +43 -0
- src/main/docker/Dockerfile.distroless +29 -0
- src/main/docker/Dockerfile.layered +34 -0
- src/main/docker/Dockerfile.native +20 -0
- src/main/java/com/dalab/catalog/DaCatalogApplication.java +20 -0
- src/main/java/com/dalab/catalog/common/ConflictException.java +15 -0
- src/main/java/com/dalab/catalog/common/ResourceNotFoundException.java +19 -0
- src/main/java/com/dalab/catalog/dto/AssetComplianceStatusDTO.java +413 -0
- src/main/java/com/dalab/catalog/dto/AssetInputDTO.java +105 -0
- src/main/java/com/dalab/catalog/dto/AssetLabelsPostRequestDTO.java +22 -0
- src/main/java/com/dalab/catalog/dto/AssetLabelsResponseDTO.java +21 -0
- src/main/java/com/dalab/catalog/dto/AssetOutputDTO.java +150 -0
- src/main/java/com/dalab/catalog/dto/AssetPolicyMappingDTO.java +287 -0
- src/main/java/com/dalab/catalog/dto/AssetSchemaDTO.java +574 -0
- src/main/java/com/dalab/catalog/dto/AssetUsageAnalyticsDTO.java +446 -0
- src/main/java/com/dalab/catalog/dto/BusinessMetadataInputDTO.java +409 -0
- src/main/java/com/dalab/catalog/dto/BusinessMetadataResponseDTO.java +302 -0
- src/main/java/com/dalab/catalog/dto/CatalogFiltersDTO.java +203 -0
- src/main/java/com/dalab/catalog/dto/EnhancedLineageDTO.java +462 -0
- src/main/java/com/dalab/catalog/dto/LabelAssignmentInputDTO.java +46 -0
- src/main/java/com/dalab/catalog/dto/LabelOutputDTO.java +91 -0
- src/main/java/com/dalab/catalog/dto/LineageAssetDTO.java +54 -0
- src/main/java/com/dalab/catalog/dto/LineageResponseDTO.java +32 -0
- src/main/java/com/dalab/catalog/dto/TaxonomyLabelDTO.java +76 -0
- src/main/java/com/dalab/catalog/dto/TaxonomyLabelsResponseDTO.java +21 -0
- src/main/java/com/dalab/catalog/mapper/AssetMapper.java +60 -0
- src/main/java/com/dalab/catalog/mapper/LabelTaxonomyMapper.java +54 -0
- src/main/java/com/dalab/catalog/model/Asset.java +191 -0
- src/main/java/com/dalab/catalog/model/AssetLineage.java +118 -0
- src/main/java/com/dalab/catalog/model/Label.java +110 -0
- src/main/java/com/dalab/catalog/repository/AssetLineageRepository.java +23 -0
- src/main/java/com/dalab/catalog/security/SecurityUtils.java +115 -0
- src/main/java/com/dalab/catalog/service/AssetService.java +1955 -0
- src/main/java/com/dalab/catalog/service/IAssetService.java +130 -0
- src/main/java/com/dalab/catalog/service/ILabelTaxonomyService.java +21 -0
- src/main/java/com/dalab/catalog/service/LabelTaxonomyService.java +146 -0
- src/main/java/com/dalab/catalog/web/rest/AssetController.java +375 -0
- src/main/java/com/dalab/catalog/web/rest/LabelTaxonomyController.java +85 -0
- src/main/resources/application.properties +91 -0
- src/test/java/com/dalab/catalog/controller/CatalogControllerTest.java +430 -0
- src/test/resources/application-test.yml +204 -0
Dockerfile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM openjdk:21-jdk-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# Install required packages
|
| 6 |
+
RUN apt-get update && apt-get install -y \
|
| 7 |
+
curl \
|
| 8 |
+
wget \
|
| 9 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 10 |
+
|
| 11 |
+
# Copy application files
|
| 12 |
+
COPY . .
|
| 13 |
+
|
| 14 |
+
# Build application (if build.gradle.kts exists)
|
| 15 |
+
RUN if [ -f "build.gradle.kts" ]; then \
|
| 16 |
+
./gradlew build -x test; \
|
| 17 |
+
fi
|
| 18 |
+
|
| 19 |
+
# Expose port
|
| 20 |
+
EXPOSE 8080
|
| 21 |
+
|
| 22 |
+
# Health check
|
| 23 |
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
| 24 |
+
CMD curl -f http://localhost:8080/actuator/health || exit 1
|
| 25 |
+
|
| 26 |
+
# Run application
|
| 27 |
+
CMD ["java", "-jar", "build/libs/da-catalog.jar"]
|
README.md
CHANGED
|
@@ -1,10 +1,38 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
colorFrom: blue
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
| 7 |
-
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: da-catalog (dev)
|
| 3 |
+
emoji: 🔧
|
| 4 |
colorFrom: blue
|
| 5 |
+
colorTo: green
|
| 6 |
sdk: docker
|
| 7 |
+
app_port: 8080
|
| 8 |
---
|
| 9 |
|
| 10 |
+
# da-catalog - dev Environment
|
| 11 |
+
|
| 12 |
+
This is the da-catalog microservice deployed in the dev environment.
|
| 13 |
+
|
| 14 |
+
## Features
|
| 15 |
+
|
| 16 |
+
- RESTful API endpoints
|
| 17 |
+
- Health monitoring via Actuator
|
| 18 |
+
- JWT authentication integration
|
| 19 |
+
- PostgreSQL database connectivity
|
| 20 |
+
|
| 21 |
+
## API Documentation
|
| 22 |
+
|
| 23 |
+
Once deployed, API documentation will be available at:
|
| 24 |
+
- Swagger UI: https://huggingface.co/spaces/dalabsai/da-catalog-dev/swagger-ui.html
|
| 25 |
+
- Health Check: https://huggingface.co/spaces/dalabsai/da-catalog-dev/actuator/health
|
| 26 |
+
|
| 27 |
+
## Environment
|
| 28 |
+
|
| 29 |
+
- **Environment**: dev
|
| 30 |
+
- **Port**: 8080
|
| 31 |
+
- **Java Version**: 21
|
| 32 |
+
- **Framework**: Spring Boot
|
| 33 |
+
|
| 34 |
+
## Deployment
|
| 35 |
+
|
| 36 |
+
This service is automatically deployed via the DALab CI/CD pipeline.
|
| 37 |
+
|
| 38 |
+
Last updated: 2025-06-16 23:39:42
|
build.gradle.kts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// da-catalog inherits common configuration from parent build.gradle.kts
|
| 2 |
+
// This build file adds catalog-specific dependencies
|
| 3 |
+
|
| 4 |
+
dependencies {
|
| 5 |
+
// Additional dependencies specific to da-catalog
|
| 6 |
+
implementation("org.springframework.cloud:spring-cloud-starter-openfeign:4.1.1")
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
// Configure main application class
|
| 10 |
+
configure<org.springframework.boot.gradle.dsl.SpringBootExtension> {
|
| 11 |
+
mainClass.set("com.dalab.catalog.DaCatalogApplication")
|
| 12 |
+
}
|
src/main/docker/Dockerfile
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Ultra-lean container using Google Distroless
|
| 2 |
+
# Expected final size: ~120-180MB (minimal base + JRE + JAR only)
|
| 3 |
+
|
| 4 |
+
FROM gcr.io/distroless/java21-debian12:nonroot
|
| 5 |
+
|
| 6 |
+
# Set working directory
|
| 7 |
+
WORKDIR /app
|
| 8 |
+
|
| 9 |
+
# Copy JAR file
|
| 10 |
+
COPY build/libs/da-catalog.jar app.jar
|
| 11 |
+
|
| 12 |
+
# Expose standard Spring Boot port
|
| 13 |
+
EXPOSE 8080
|
| 14 |
+
|
| 15 |
+
# Run application (distroless has no shell, so use exec form)
|
| 16 |
+
ENTRYPOINT ["java", \
|
| 17 |
+
"-XX:+UseContainerSupport", \
|
| 18 |
+
"-XX:MaxRAMPercentage=75.0", \
|
| 19 |
+
"-XX:+UseG1GC", \
|
| 20 |
+
"-XX:+UseStringDeduplication", \
|
| 21 |
+
"-Djava.security.egd=file:/dev/./urandom", \
|
| 22 |
+
"-Dspring.backgroundpreinitializer.ignore=true", \
|
| 23 |
+
"-jar", "app.jar"]
|
src/main/docker/Dockerfile.alpine-jlink
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Ultra-minimal Alpine + Custom JRE
|
| 2 |
+
# Expected size: ~120-160MB
|
| 3 |
+
|
| 4 |
+
# Stage 1: Create custom JRE with only needed modules
|
| 5 |
+
FROM eclipse-temurin:21-jdk-alpine as jre-builder
|
| 6 |
+
WORKDIR /app
|
| 7 |
+
|
| 8 |
+
# Analyze JAR to find required modules
|
| 9 |
+
COPY build/libs/*.jar app.jar
|
| 10 |
+
RUN jdeps --ignore-missing-deps --print-module-deps app.jar > modules.txt
|
| 11 |
+
|
| 12 |
+
# Create minimal JRE with only required modules
|
| 13 |
+
RUN jlink \
|
| 14 |
+
--add-modules $(cat modules.txt),java.logging,java.xml,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument \
|
| 15 |
+
--strip-debug \
|
| 16 |
+
--no-man-pages \
|
| 17 |
+
--no-header-files \
|
| 18 |
+
--compress=2 \
|
| 19 |
+
--output /custom-jre
|
| 20 |
+
|
| 21 |
+
# Stage 2: Production image
|
| 22 |
+
FROM alpine:3.19
|
| 23 |
+
RUN apk add --no-cache tzdata && \
|
| 24 |
+
addgroup -g 1001 -S appgroup && \
|
| 25 |
+
adduser -u 1001 -S appuser -G appgroup
|
| 26 |
+
|
| 27 |
+
# Copy custom JRE
|
| 28 |
+
COPY --from=jre-builder /custom-jre /opt/java
|
| 29 |
+
ENV JAVA_HOME=/opt/java
|
| 30 |
+
ENV PATH="$JAVA_HOME/bin:$PATH"
|
| 31 |
+
|
| 32 |
+
WORKDIR /app
|
| 33 |
+
COPY build/libs/*.jar app.jar
|
| 34 |
+
RUN chown appuser:appgroup app.jar
|
| 35 |
+
|
| 36 |
+
USER appuser
|
| 37 |
+
EXPOSE 8080
|
| 38 |
+
|
| 39 |
+
ENTRYPOINT ["java", \
|
| 40 |
+
"-XX:+UseContainerSupport", \
|
| 41 |
+
"-XX:MaxRAMPercentage=70.0", \
|
| 42 |
+
"-XX:+UseG1GC", \
|
| 43 |
+
"-jar", "app.jar"]
|
src/main/docker/Dockerfile.distroless
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Ultra-minimal container using Google Distroless
|
| 2 |
+
# Final image size should be ~120-150MB
|
| 3 |
+
|
| 4 |
+
FROM gcr.io/distroless/java21-debian12:nonroot
|
| 5 |
+
|
| 6 |
+
# Set working directory
|
| 7 |
+
WORKDIR /app
|
| 8 |
+
|
| 9 |
+
# Copy JAR file
|
| 10 |
+
COPY build/libs/da-catalog.jar app.jar
|
| 11 |
+
|
| 12 |
+
# Expose port
|
| 13 |
+
EXPOSE 8080
|
| 14 |
+
|
| 15 |
+
# Set optimized JVM options
|
| 16 |
+
ENV JAVA_OPTS="-XX:+UseContainerSupport \
|
| 17 |
+
-XX:MaxRAMPercentage=75.0 \
|
| 18 |
+
-XX:+UseG1GC \
|
| 19 |
+
-XX:+UseStringDeduplication \
|
| 20 |
+
-Djava.security.egd=file:/dev/./urandom"
|
| 21 |
+
|
| 22 |
+
# Run application (distroless images don't have shell)
|
| 23 |
+
ENTRYPOINT ["java", \
|
| 24 |
+
"-XX:+UseContainerSupport", \
|
| 25 |
+
"-XX:MaxRAMPercentage=75.0", \
|
| 26 |
+
"-XX:+UseG1GC", \
|
| 27 |
+
"-XX:+UseStringDeduplication", \
|
| 28 |
+
"-Djava.security.egd=file:/dev/./urandom", \
|
| 29 |
+
"-jar", "app.jar"]
|
src/main/docker/Dockerfile.layered
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Ultra-optimized layered build using Distroless
|
| 2 |
+
# Expected size: ~180-220MB with better caching
|
| 3 |
+
|
| 4 |
+
FROM gcr.io/distroless/java21-debian12:nonroot as base
|
| 5 |
+
|
| 6 |
+
# Stage 1: Extract JAR layers for optimal caching
|
| 7 |
+
FROM eclipse-temurin:21-jdk-alpine as extractor
|
| 8 |
+
WORKDIR /app
|
| 9 |
+
COPY build/libs/*.jar app.jar
|
| 10 |
+
RUN java -Djarmode=layertools -jar app.jar extract
|
| 11 |
+
|
| 12 |
+
# Stage 2: Production image with extracted layers
|
| 13 |
+
FROM base
|
| 14 |
+
WORKDIR /app
|
| 15 |
+
|
| 16 |
+
# Copy layers in dependency order (best caching)
|
| 17 |
+
COPY --from=extractor /app/dependencies/ ./
|
| 18 |
+
COPY --from=extractor /app/spring-boot-loader/ ./
|
| 19 |
+
COPY --from=extractor /app/snapshot-dependencies/ ./
|
| 20 |
+
COPY --from=extractor /app/application/ ./
|
| 21 |
+
|
| 22 |
+
EXPOSE 8080
|
| 23 |
+
|
| 24 |
+
# Optimized JVM settings for micro-containers
|
| 25 |
+
ENTRYPOINT ["java", \
|
| 26 |
+
"-XX:+UseContainerSupport", \
|
| 27 |
+
"-XX:MaxRAMPercentage=70.0", \
|
| 28 |
+
"-XX:+UseG1GC", \
|
| 29 |
+
"-XX:+UseStringDeduplication", \
|
| 30 |
+
"-XX:+CompactStrings", \
|
| 31 |
+
"-Xshare:on", \
|
| 32 |
+
"-Djava.security.egd=file:/dev/./urandom", \
|
| 33 |
+
"-Dspring.backgroundpreinitializer.ignore=true", \
|
| 34 |
+
"org.springframework.boot.loader.JarLauncher"]
|
src/main/docker/Dockerfile.native
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GraalVM Native Image - Ultra-fast startup, tiny size
|
| 2 |
+
# Expected size: ~50-80MB, startup <100ms
|
| 3 |
+
# Note: Requires native compilation support in Spring Boot
|
| 4 |
+
|
| 5 |
+
# Stage 1: Native compilation
|
| 6 |
+
FROM ghcr.io/graalvm/graalvm-ce:ol9-java21 as native-builder
|
| 7 |
+
WORKDIR /app
|
| 8 |
+
|
| 9 |
+
# Install native-image
|
| 10 |
+
RUN gu install native-image
|
| 11 |
+
|
| 12 |
+
# Copy source and build native executable
|
| 13 |
+
COPY . .
|
| 14 |
+
RUN ./gradlew nativeCompile
|
| 15 |
+
|
| 16 |
+
# Stage 2: Minimal runtime
|
| 17 |
+
FROM scratch
|
| 18 |
+
COPY --from=native-builder /app/build/native/nativeCompile/app /app
|
| 19 |
+
EXPOSE 8080
|
| 20 |
+
ENTRYPOINT ["/app"]
|
src/main/java/com/dalab/catalog/DaCatalogApplication.java
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog;
|
| 2 |
+
|
| 3 |
+
import org.springframework.boot.SpringApplication;
|
| 4 |
+
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
| 5 |
+
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
| 6 |
+
// import org.springframework.cloud.client.discovery.EnableDiscoveryClient; // If using Spring Cloud Discovery
|
| 7 |
+
// import org.springframework.cloud.openfeign.EnableFeignClients; // If this service will call others via Feign
|
| 8 |
+
|
| 9 |
+
@SpringBootApplication
|
| 10 |
+
@ConfigurationPropertiesScan // Scans for @ConfigurationProperties beans
|
| 11 |
+
// @EnableDiscoveryClient // Uncomment if using Eureka, Consul, etc.
|
| 12 |
+
// @EnableFeignClients // Uncomment if this service needs to call other services
|
| 13 |
+
// @EntityScan(basePackages = {"com.dalab.catalog.model", "com.dalab.common.model"}) // Add common model packages if needed
|
| 14 |
+
public class DaCatalogApplication {
|
| 15 |
+
|
| 16 |
+
public static void main(String[] args) {
|
| 17 |
+
SpringApplication.run(DaCatalogApplication.class, args);
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
}
|
src/main/java/com/dalab/catalog/common/ConflictException.java
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.common;
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Exception thrown when a resource conflict occurs
|
| 5 |
+
*/
|
| 6 |
+
public class ConflictException extends RuntimeException {
|
| 7 |
+
|
| 8 |
+
public ConflictException(String message) {
|
| 9 |
+
super(message);
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
public ConflictException(String message, Throwable cause) {
|
| 13 |
+
super(message, cause);
|
| 14 |
+
}
|
| 15 |
+
}
|
src/main/java/com/dalab/catalog/common/ResourceNotFoundException.java
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.common;
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Exception thrown when a requested resource is not found
|
| 5 |
+
*/
|
| 6 |
+
public class ResourceNotFoundException extends RuntimeException {
|
| 7 |
+
|
| 8 |
+
public ResourceNotFoundException(String message) {
|
| 9 |
+
super(message);
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
public ResourceNotFoundException(String resourceType, String field, String value) {
|
| 13 |
+
super(String.format("%s not found with %s: %s", resourceType, field, value));
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
public ResourceNotFoundException(String message, Throwable cause) {
|
| 17 |
+
super(message, cause);
|
| 18 |
+
}
|
| 19 |
+
}
|
src/main/java/com/dalab/catalog/dto/AssetComplianceStatusDTO.java
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.List;
|
| 5 |
+
import java.util.Map;
|
| 6 |
+
import java.util.UUID;
|
| 7 |
+
|
| 8 |
+
import com.fasterxml.jackson.annotation.JsonInclude;
|
| 9 |
+
|
| 10 |
+
/**
|
| 11 |
+
* DTO for Asset Compliance Status information.
|
| 12 |
+
* Priority 2 endpoint: Provides detailed compliance status and monitoring for assets.
|
| 13 |
+
*/
|
| 14 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 15 |
+
public class AssetComplianceStatusDTO {
|
| 16 |
+
|
| 17 |
+
private UUID assetId;
|
| 18 |
+
private String assetName;
|
| 19 |
+
private String overallComplianceStatus; // COMPLIANT, NON_COMPLIANT, PARTIALLY_COMPLIANT, UNKNOWN, MONITORING
|
| 20 |
+
private Double overallComplianceScore; // 0.0 to 100.0
|
| 21 |
+
private List<ComplianceFrameworkStatusDTO> frameworkCompliance;
|
| 22 |
+
private List<ComplianceViolationDTO> violations;
|
| 23 |
+
private List<ComplianceControlDTO> controls;
|
| 24 |
+
private ComplianceRiskAssessmentDTO riskAssessment;
|
| 25 |
+
private List<ComplianceRecommendationDTO> recommendations;
|
| 26 |
+
private ComplianceHistoryDTO complianceHistory;
|
| 27 |
+
private Instant lastEvaluated;
|
| 28 |
+
private String evaluatedBy;
|
| 29 |
+
private Instant nextEvaluation;
|
| 30 |
+
|
| 31 |
+
/**
|
| 32 |
+
* Default constructor.
|
| 33 |
+
*/
|
| 34 |
+
public AssetComplianceStatusDTO() {}
|
| 35 |
+
|
| 36 |
+
/**
|
| 37 |
+
* Constructor with basic information.
|
| 38 |
+
*/
|
| 39 |
+
public AssetComplianceStatusDTO(UUID assetId, String assetName) {
|
| 40 |
+
this.assetId = assetId;
|
| 41 |
+
this.assetName = assetName;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
/**
|
| 45 |
+
* DTO for compliance framework status.
|
| 46 |
+
*/
|
| 47 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 48 |
+
public static class ComplianceFrameworkStatusDTO {
|
| 49 |
+
|
| 50 |
+
private String frameworkName; // GDPR, SOX, HIPAA, PCI_DSS, ISO_27001, etc.
|
| 51 |
+
private String frameworkVersion;
|
| 52 |
+
private String complianceStatus;
|
| 53 |
+
private Double complianceScore;
|
| 54 |
+
private List<String> applicableControls;
|
| 55 |
+
private List<String> passedControls;
|
| 56 |
+
private List<String> failedControls;
|
| 57 |
+
private Integer totalRequirements;
|
| 58 |
+
private Integer metRequirements;
|
| 59 |
+
private Instant lastAssessment;
|
| 60 |
+
private String certificationStatus;
|
| 61 |
+
private Instant certificationExpiry;
|
| 62 |
+
|
| 63 |
+
public ComplianceFrameworkStatusDTO() {}
|
| 64 |
+
|
| 65 |
+
public ComplianceFrameworkStatusDTO(String frameworkName, String complianceStatus, Double complianceScore) {
|
| 66 |
+
this.frameworkName = frameworkName;
|
| 67 |
+
this.complianceStatus = complianceStatus;
|
| 68 |
+
this.complianceScore = complianceScore;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
// Getters and setters
|
| 72 |
+
public String getFrameworkName() { return frameworkName; }
|
| 73 |
+
public void setFrameworkName(String frameworkName) { this.frameworkName = frameworkName; }
|
| 74 |
+
public String getFrameworkVersion() { return frameworkVersion; }
|
| 75 |
+
public void setFrameworkVersion(String frameworkVersion) { this.frameworkVersion = frameworkVersion; }
|
| 76 |
+
public String getComplianceStatus() { return complianceStatus; }
|
| 77 |
+
public void setComplianceStatus(String complianceStatus) { this.complianceStatus = complianceStatus; }
|
| 78 |
+
public Double getComplianceScore() { return complianceScore; }
|
| 79 |
+
public void setComplianceScore(Double complianceScore) { this.complianceScore = complianceScore; }
|
| 80 |
+
public List<String> getApplicableControls() { return applicableControls; }
|
| 81 |
+
public void setApplicableControls(List<String> applicableControls) { this.applicableControls = applicableControls; }
|
| 82 |
+
public List<String> getPassedControls() { return passedControls; }
|
| 83 |
+
public void setPassedControls(List<String> passedControls) { this.passedControls = passedControls; }
|
| 84 |
+
public List<String> getFailedControls() { return failedControls; }
|
| 85 |
+
public void setFailedControls(List<String> failedControls) { this.failedControls = failedControls; }
|
| 86 |
+
public Integer getTotalRequirements() { return totalRequirements; }
|
| 87 |
+
public void setTotalRequirements(Integer totalRequirements) { this.totalRequirements = totalRequirements; }
|
| 88 |
+
public Integer getMetRequirements() { return metRequirements; }
|
| 89 |
+
public void setMetRequirements(Integer metRequirements) { this.metRequirements = metRequirements; }
|
| 90 |
+
public Instant getLastAssessment() { return lastAssessment; }
|
| 91 |
+
public void setLastAssessment(Instant lastAssessment) { this.lastAssessment = lastAssessment; }
|
| 92 |
+
public String getCertificationStatus() { return certificationStatus; }
|
| 93 |
+
public void setCertificationStatus(String certificationStatus) { this.certificationStatus = certificationStatus; }
|
| 94 |
+
public Instant getCertificationExpiry() { return certificationExpiry; }
|
| 95 |
+
public void setCertificationExpiry(Instant certificationExpiry) { this.certificationExpiry = certificationExpiry; }
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
/**
|
| 99 |
+
* DTO for compliance violations.
|
| 100 |
+
*/
|
| 101 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 102 |
+
public static class ComplianceViolationDTO {
|
| 103 |
+
|
| 104 |
+
private String violationId;
|
| 105 |
+
private String violationType; // DATA_RETENTION, ACCESS_CONTROL, ENCRYPTION, AUDIT_TRAIL, etc.
|
| 106 |
+
private String severity; // CRITICAL, HIGH, MEDIUM, LOW
|
| 107 |
+
private String description;
|
| 108 |
+
private String framework;
|
| 109 |
+
private String controlId;
|
| 110 |
+
private String currentValue;
|
| 111 |
+
private String expectedValue;
|
| 112 |
+
private Instant detectedAt;
|
| 113 |
+
private String detectedBy;
|
| 114 |
+
private String status; // OPEN, IN_PROGRESS, RESOLVED, ACCEPTED_RISK
|
| 115 |
+
private String assignedTo;
|
| 116 |
+
private Instant dueDate;
|
| 117 |
+
private List<String> remediationActions;
|
| 118 |
+
private Map<String, Object> evidence;
|
| 119 |
+
|
| 120 |
+
public ComplianceViolationDTO() {}
|
| 121 |
+
|
| 122 |
+
public ComplianceViolationDTO(String violationId, String violationType, String severity) {
|
| 123 |
+
this.violationId = violationId;
|
| 124 |
+
this.violationType = violationType;
|
| 125 |
+
this.severity = severity;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
// Getters and setters
|
| 129 |
+
public String getViolationId() { return violationId; }
|
| 130 |
+
public void setViolationId(String violationId) { this.violationId = violationId; }
|
| 131 |
+
public String getViolationType() { return violationType; }
|
| 132 |
+
public void setViolationType(String violationType) { this.violationType = violationType; }
|
| 133 |
+
public String getSeverity() { return severity; }
|
| 134 |
+
public void setSeverity(String severity) { this.severity = severity; }
|
| 135 |
+
public String getDescription() { return description; }
|
| 136 |
+
public void setDescription(String description) { this.description = description; }
|
| 137 |
+
public String getFramework() { return framework; }
|
| 138 |
+
public void setFramework(String framework) { this.framework = framework; }
|
| 139 |
+
public String getControlId() { return controlId; }
|
| 140 |
+
public void setControlId(String controlId) { this.controlId = controlId; }
|
| 141 |
+
public String getCurrentValue() { return currentValue; }
|
| 142 |
+
public void setCurrentValue(String currentValue) { this.currentValue = currentValue; }
|
| 143 |
+
public String getExpectedValue() { return expectedValue; }
|
| 144 |
+
public void setExpectedValue(String expectedValue) { this.expectedValue = expectedValue; }
|
| 145 |
+
public Instant getDetectedAt() { return detectedAt; }
|
| 146 |
+
public void setDetectedAt(Instant detectedAt) { this.detectedAt = detectedAt; }
|
| 147 |
+
public String getDetectedBy() { return detectedBy; }
|
| 148 |
+
public void setDetectedBy(String detectedBy) { this.detectedBy = detectedBy; }
|
| 149 |
+
public String getStatus() { return status; }
|
| 150 |
+
public void setStatus(String status) { this.status = status; }
|
| 151 |
+
public String getAssignedTo() { return assignedTo; }
|
| 152 |
+
public void setAssignedTo(String assignedTo) { this.assignedTo = assignedTo; }
|
| 153 |
+
public Instant getDueDate() { return dueDate; }
|
| 154 |
+
public void setDueDate(Instant dueDate) { this.dueDate = dueDate; }
|
| 155 |
+
public List<String> getRemediationActions() { return remediationActions; }
|
| 156 |
+
public void setRemediationActions(List<String> remediationActions) { this.remediationActions = remediationActions; }
|
| 157 |
+
public Map<String, Object> getEvidence() { return evidence; }
|
| 158 |
+
public void setEvidence(Map<String, Object> evidence) { this.evidence = evidence; }
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
/**
|
| 162 |
+
* DTO for compliance controls.
|
| 163 |
+
*/
|
| 164 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 165 |
+
public static class ComplianceControlDTO {
|
| 166 |
+
|
| 167 |
+
private String controlId;
|
| 168 |
+
private String controlName;
|
| 169 |
+
private String controlType; // PREVENTIVE, DETECTIVE, CORRECTIVE
|
| 170 |
+
private String framework;
|
| 171 |
+
private String description;
|
| 172 |
+
private String status; // PASSED, FAILED, NOT_APPLICABLE, MANUAL_REVIEW
|
| 173 |
+
private String implementationStatus; // IMPLEMENTED, PARTIALLY_IMPLEMENTED, NOT_IMPLEMENTED
|
| 174 |
+
private Double effectivenessScore;
|
| 175 |
+
private Instant lastTested;
|
| 176 |
+
private String testedBy;
|
| 177 |
+
private Instant nextTest;
|
| 178 |
+
private String testFrequency; // DAILY, WEEKLY, MONTHLY, QUARTERLY, ANNUALLY
|
| 179 |
+
private List<String> evidenceRequired;
|
| 180 |
+
private Map<String, Object> testResults;
|
| 181 |
+
|
| 182 |
+
public ComplianceControlDTO() {}
|
| 183 |
+
|
| 184 |
+
public ComplianceControlDTO(String controlId, String controlName, String status) {
|
| 185 |
+
this.controlId = controlId;
|
| 186 |
+
this.controlName = controlName;
|
| 187 |
+
this.status = status;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
// Getters and setters
|
| 191 |
+
public String getControlId() { return controlId; }
|
| 192 |
+
public void setControlId(String controlId) { this.controlId = controlId; }
|
| 193 |
+
public String getControlName() { return controlName; }
|
| 194 |
+
public void setControlName(String controlName) { this.controlName = controlName; }
|
| 195 |
+
public String getControlType() { return controlType; }
|
| 196 |
+
public void setControlType(String controlType) { this.controlType = controlType; }
|
| 197 |
+
public String getFramework() { return framework; }
|
| 198 |
+
public void setFramework(String framework) { this.framework = framework; }
|
| 199 |
+
public String getDescription() { return description; }
|
| 200 |
+
public void setDescription(String description) { this.description = description; }
|
| 201 |
+
public String getStatus() { return status; }
|
| 202 |
+
public void setStatus(String status) { this.status = status; }
|
| 203 |
+
public String getImplementationStatus() { return implementationStatus; }
|
| 204 |
+
public void setImplementationStatus(String implementationStatus) { this.implementationStatus = implementationStatus; }
|
| 205 |
+
public Double getEffectivenessScore() { return effectivenessScore; }
|
| 206 |
+
public void setEffectivenessScore(Double effectivenessScore) { this.effectivenessScore = effectivenessScore; }
|
| 207 |
+
public Instant getLastTested() { return lastTested; }
|
| 208 |
+
public void setLastTested(Instant lastTested) { this.lastTested = lastTested; }
|
| 209 |
+
public String getTestedBy() { return testedBy; }
|
| 210 |
+
public void setTestedBy(String testedBy) { this.testedBy = testedBy; }
|
| 211 |
+
public Instant getNextTest() { return nextTest; }
|
| 212 |
+
public void setNextTest(Instant nextTest) { this.nextTest = nextTest; }
|
| 213 |
+
public String getTestFrequency() { return testFrequency; }
|
| 214 |
+
public void setTestFrequency(String testFrequency) { this.testFrequency = testFrequency; }
|
| 215 |
+
public List<String> getEvidenceRequired() { return evidenceRequired; }
|
| 216 |
+
public void setEvidenceRequired(List<String> evidenceRequired) { this.evidenceRequired = evidenceRequired; }
|
| 217 |
+
public Map<String, Object> getTestResults() { return testResults; }
|
| 218 |
+
public void setTestResults(Map<String, Object> testResults) { this.testResults = testResults; }
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
/**
|
| 222 |
+
* DTO for compliance risk assessment.
|
| 223 |
+
*/
|
| 224 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 225 |
+
public static class ComplianceRiskAssessmentDTO {
|
| 226 |
+
|
| 227 |
+
private String overallRiskLevel; // CRITICAL, HIGH, MEDIUM, LOW
|
| 228 |
+
private Double riskScore; // 0.0 to 100.0
|
| 229 |
+
private List<String> riskFactors;
|
| 230 |
+
private Map<String, String> riskCategories; // category -> risk level
|
| 231 |
+
private String residualRisk;
|
| 232 |
+
private List<String> mitigationMeasures;
|
| 233 |
+
private Instant lastAssessed;
|
| 234 |
+
private String assessedBy;
|
| 235 |
+
private Instant nextAssessment;
|
| 236 |
+
|
| 237 |
+
public ComplianceRiskAssessmentDTO() {}
|
| 238 |
+
|
| 239 |
+
// Getters and setters
|
| 240 |
+
public String getOverallRiskLevel() { return overallRiskLevel; }
|
| 241 |
+
public void setOverallRiskLevel(String overallRiskLevel) { this.overallRiskLevel = overallRiskLevel; }
|
| 242 |
+
public Double getRiskScore() { return riskScore; }
|
| 243 |
+
public void setRiskScore(Double riskScore) { this.riskScore = riskScore; }
|
| 244 |
+
public List<String> getRiskFactors() { return riskFactors; }
|
| 245 |
+
public void setRiskFactors(List<String> riskFactors) { this.riskFactors = riskFactors; }
|
| 246 |
+
public Map<String, String> getRiskCategories() { return riskCategories; }
|
| 247 |
+
public void setRiskCategories(Map<String, String> riskCategories) { this.riskCategories = riskCategories; }
|
| 248 |
+
public String getResidualRisk() { return residualRisk; }
|
| 249 |
+
public void setResidualRisk(String residualRisk) { this.residualRisk = residualRisk; }
|
| 250 |
+
public List<String> getMitigationMeasures() { return mitigationMeasures; }
|
| 251 |
+
public void setMitigationMeasures(List<String> mitigationMeasures) { this.mitigationMeasures = mitigationMeasures; }
|
| 252 |
+
public Instant getLastAssessed() { return lastAssessed; }
|
| 253 |
+
public void setLastAssessed(Instant lastAssessed) { this.lastAssessed = lastAssessed; }
|
| 254 |
+
public String getAssessedBy() { return assessedBy; }
|
| 255 |
+
public void setAssessedBy(String assessedBy) { this.assessedBy = assessedBy; }
|
| 256 |
+
public Instant getNextAssessment() { return nextAssessment; }
|
| 257 |
+
public void setNextAssessment(Instant nextAssessment) { this.nextAssessment = nextAssessment; }
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
/**
|
| 261 |
+
* DTO for compliance recommendations.
|
| 262 |
+
*/
|
| 263 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 264 |
+
public static class ComplianceRecommendationDTO {
|
| 265 |
+
|
| 266 |
+
private String recommendationId;
|
| 267 |
+
private String type; // REMEDIATION, ENHANCEMENT, PREVENTIVE
|
| 268 |
+
private String priority; // CRITICAL, HIGH, MEDIUM, LOW
|
| 269 |
+
private String title;
|
| 270 |
+
private String description;
|
| 271 |
+
private String framework;
|
| 272 |
+
private List<String> affectedControls;
|
| 273 |
+
private String actionRequired;
|
| 274 |
+
private Integer estimatedEffort; // in hours
|
| 275 |
+
private String costEstimate;
|
| 276 |
+
private String expectedOutcome;
|
| 277 |
+
private String assignedTo;
|
| 278 |
+
private Instant createdAt;
|
| 279 |
+
private Instant targetDate;
|
| 280 |
+
|
| 281 |
+
public ComplianceRecommendationDTO() {}
|
| 282 |
+
|
| 283 |
+
public ComplianceRecommendationDTO(String recommendationId, String type, String priority) {
|
| 284 |
+
this.recommendationId = recommendationId;
|
| 285 |
+
this.type = type;
|
| 286 |
+
this.priority = priority;
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
// Getters and setters
|
| 290 |
+
public String getRecommendationId() { return recommendationId; }
|
| 291 |
+
public void setRecommendationId(String recommendationId) { this.recommendationId = recommendationId; }
|
| 292 |
+
public String getType() { return type; }
|
| 293 |
+
public void setType(String type) { this.type = type; }
|
| 294 |
+
public String getPriority() { return priority; }
|
| 295 |
+
public void setPriority(String priority) { this.priority = priority; }
|
| 296 |
+
public String getTitle() { return title; }
|
| 297 |
+
public void setTitle(String title) { this.title = title; }
|
| 298 |
+
public String getDescription() { return description; }
|
| 299 |
+
public void setDescription(String description) { this.description = description; }
|
| 300 |
+
public String getFramework() { return framework; }
|
| 301 |
+
public void setFramework(String framework) { this.framework = framework; }
|
| 302 |
+
public List<String> getAffectedControls() { return affectedControls; }
|
| 303 |
+
public void setAffectedControls(List<String> affectedControls) { this.affectedControls = affectedControls; }
|
| 304 |
+
public String getActionRequired() { return actionRequired; }
|
| 305 |
+
public void setActionRequired(String actionRequired) { this.actionRequired = actionRequired; }
|
| 306 |
+
public Integer getEstimatedEffort() { return estimatedEffort; }
|
| 307 |
+
public void setEstimatedEffort(Integer estimatedEffort) { this.estimatedEffort = estimatedEffort; }
|
| 308 |
+
public String getCostEstimate() { return costEstimate; }
|
| 309 |
+
public void setCostEstimate(String costEstimate) { this.costEstimate = costEstimate; }
|
| 310 |
+
public String getExpectedOutcome() { return expectedOutcome; }
|
| 311 |
+
public void setExpectedOutcome(String expectedOutcome) { this.expectedOutcome = expectedOutcome; }
|
| 312 |
+
public String getAssignedTo() { return assignedTo; }
|
| 313 |
+
public void setAssignedTo(String assignedTo) { this.assignedTo = assignedTo; }
|
| 314 |
+
public Instant getCreatedAt() { return createdAt; }
|
| 315 |
+
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
| 316 |
+
public Instant getTargetDate() { return targetDate; }
|
| 317 |
+
public void setTargetDate(Instant targetDate) { this.targetDate = targetDate; }
|
| 318 |
+
}
|
| 319 |
+
|
| 320 |
+
/**
|
| 321 |
+
* DTO for compliance history tracking.
|
| 322 |
+
*/
|
| 323 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 324 |
+
public static class ComplianceHistoryDTO {
|
| 325 |
+
|
| 326 |
+
private List<ComplianceEventDTO> recentEvents;
|
| 327 |
+
private Map<String, Double> scoreHistory; // timestamp -> score
|
| 328 |
+
private List<String> trendAnalysis;
|
| 329 |
+
private Integer totalEvaluations;
|
| 330 |
+
private Instant firstEvaluation;
|
| 331 |
+
private Double averageScore;
|
| 332 |
+
private String trend; // IMPROVING, DECLINING, STABLE
|
| 333 |
+
|
| 334 |
+
public ComplianceHistoryDTO() {}
|
| 335 |
+
|
| 336 |
+
// Getters and setters
|
| 337 |
+
public List<ComplianceEventDTO> getRecentEvents() { return recentEvents; }
|
| 338 |
+
public void setRecentEvents(List<ComplianceEventDTO> recentEvents) { this.recentEvents = recentEvents; }
|
| 339 |
+
public Map<String, Double> getScoreHistory() { return scoreHistory; }
|
| 340 |
+
public void setScoreHistory(Map<String, Double> scoreHistory) { this.scoreHistory = scoreHistory; }
|
| 341 |
+
public List<String> getTrendAnalysis() { return trendAnalysis; }
|
| 342 |
+
public void setTrendAnalysis(List<String> trendAnalysis) { this.trendAnalysis = trendAnalysis; }
|
| 343 |
+
public Integer getTotalEvaluations() { return totalEvaluations; }
|
| 344 |
+
public void setTotalEvaluations(Integer totalEvaluations) { this.totalEvaluations = totalEvaluations; }
|
| 345 |
+
public Instant getFirstEvaluation() { return firstEvaluation; }
|
| 346 |
+
public void setFirstEvaluation(Instant firstEvaluation) { this.firstEvaluation = firstEvaluation; }
|
| 347 |
+
public Double getAverageScore() { return averageScore; }
|
| 348 |
+
public void setAverageScore(Double averageScore) { this.averageScore = averageScore; }
|
| 349 |
+
public String getTrend() { return trend; }
|
| 350 |
+
public void setTrend(String trend) { this.trend = trend; }
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
/**
|
| 354 |
+
* DTO for compliance events.
|
| 355 |
+
*/
|
| 356 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 357 |
+
public static class ComplianceEventDTO {
|
| 358 |
+
|
| 359 |
+
private String eventType; // EVALUATION, VIOLATION_DETECTED, VIOLATION_RESOLVED, CONTROL_UPDATED
|
| 360 |
+
private String description;
|
| 361 |
+
private Instant timestamp;
|
| 362 |
+
private String triggeredBy;
|
| 363 |
+
private Map<String, Object> details;
|
| 364 |
+
|
| 365 |
+
public ComplianceEventDTO() {}
|
| 366 |
+
|
| 367 |
+
public ComplianceEventDTO(String eventType, String description, Instant timestamp) {
|
| 368 |
+
this.eventType = eventType;
|
| 369 |
+
this.description = description;
|
| 370 |
+
this.timestamp = timestamp;
|
| 371 |
+
}
|
| 372 |
+
|
| 373 |
+
// Getters and setters
|
| 374 |
+
public String getEventType() { return eventType; }
|
| 375 |
+
public void setEventType(String eventType) { this.eventType = eventType; }
|
| 376 |
+
public String getDescription() { return description; }
|
| 377 |
+
public void setDescription(String description) { this.description = description; }
|
| 378 |
+
public Instant getTimestamp() { return timestamp; }
|
| 379 |
+
public void setTimestamp(Instant timestamp) { this.timestamp = timestamp; }
|
| 380 |
+
public String getTriggeredBy() { return triggeredBy; }
|
| 381 |
+
public void setTriggeredBy(String triggeredBy) { this.triggeredBy = triggeredBy; }
|
| 382 |
+
public Map<String, Object> getDetails() { return details; }
|
| 383 |
+
public void setDetails(Map<String, Object> details) { this.details = details; }
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
// Main class getters and setters
|
| 387 |
+
public UUID getAssetId() { return assetId; }
|
| 388 |
+
public void setAssetId(UUID assetId) { this.assetId = assetId; }
|
| 389 |
+
public String getAssetName() { return assetName; }
|
| 390 |
+
public void setAssetName(String assetName) { this.assetName = assetName; }
|
| 391 |
+
public String getOverallComplianceStatus() { return overallComplianceStatus; }
|
| 392 |
+
public void setOverallComplianceStatus(String overallComplianceStatus) { this.overallComplianceStatus = overallComplianceStatus; }
|
| 393 |
+
public Double getOverallComplianceScore() { return overallComplianceScore; }
|
| 394 |
+
public void setOverallComplianceScore(Double overallComplianceScore) { this.overallComplianceScore = overallComplianceScore; }
|
| 395 |
+
public List<ComplianceFrameworkStatusDTO> getFrameworkCompliance() { return frameworkCompliance; }
|
| 396 |
+
public void setFrameworkCompliance(List<ComplianceFrameworkStatusDTO> frameworkCompliance) { this.frameworkCompliance = frameworkCompliance; }
|
| 397 |
+
public List<ComplianceViolationDTO> getViolations() { return violations; }
|
| 398 |
+
public void setViolations(List<ComplianceViolationDTO> violations) { this.violations = violations; }
|
| 399 |
+
public List<ComplianceControlDTO> getControls() { return controls; }
|
| 400 |
+
public void setControls(List<ComplianceControlDTO> controls) { this.controls = controls; }
|
| 401 |
+
public ComplianceRiskAssessmentDTO getRiskAssessment() { return riskAssessment; }
|
| 402 |
+
public void setRiskAssessment(ComplianceRiskAssessmentDTO riskAssessment) { this.riskAssessment = riskAssessment; }
|
| 403 |
+
public List<ComplianceRecommendationDTO> getRecommendations() { return recommendations; }
|
| 404 |
+
public void setRecommendations(List<ComplianceRecommendationDTO> recommendations) { this.recommendations = recommendations; }
|
| 405 |
+
public ComplianceHistoryDTO getComplianceHistory() { return complianceHistory; }
|
| 406 |
+
public void setComplianceHistory(ComplianceHistoryDTO complianceHistory) { this.complianceHistory = complianceHistory; }
|
| 407 |
+
public Instant getLastEvaluated() { return lastEvaluated; }
|
| 408 |
+
public void setLastEvaluated(Instant lastEvaluated) { this.lastEvaluated = lastEvaluated; }
|
| 409 |
+
public String getEvaluatedBy() { return evaluatedBy; }
|
| 410 |
+
public void setEvaluatedBy(String evaluatedBy) { this.evaluatedBy = evaluatedBy; }
|
| 411 |
+
public Instant getNextEvaluation() { return nextEvaluation; }
|
| 412 |
+
public void setNextEvaluation(Instant nextEvaluation) { this.nextEvaluation = nextEvaluation; }
|
| 413 |
+
}
|
src/main/java/com/dalab/catalog/dto/AssetInputDTO.java
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.util.List;
|
| 4 |
+
import java.util.Map;
|
| 5 |
+
import java.util.UUID;
|
| 6 |
+
|
| 7 |
+
import jakarta.validation.constraints.NotBlank;
|
| 8 |
+
import jakarta.validation.constraints.NotNull;
|
| 9 |
+
import jakarta.validation.constraints.Size;
|
| 10 |
+
|
| 11 |
+
public class AssetInputDTO {
|
| 12 |
+
|
| 13 |
+
@NotBlank(message = "Asset name is required")
|
| 14 |
+
@Size(max = 255)
|
| 15 |
+
private String assetName;
|
| 16 |
+
|
| 17 |
+
@NotNull(message = "Cloud Connection ID is required")
|
| 18 |
+
private UUID cloudConnectionId;
|
| 19 |
+
|
| 20 |
+
@NotBlank(message = "Cloud provider is required")
|
| 21 |
+
private String cloudProvider; // e.g., GCP, AWS, AZURE, OCI
|
| 22 |
+
|
| 23 |
+
@NotBlank(message = "Asset type is required")
|
| 24 |
+
private String assetType; // e.g., "BigQuery Dataset", "S3 Bucket"
|
| 25 |
+
|
| 26 |
+
@NotBlank(message = "Native asset ID is required")
|
| 27 |
+
@Size(max = 1024)
|
| 28 |
+
private String nativeAssetId;
|
| 29 |
+
|
| 30 |
+
@Size(max = 255)
|
| 31 |
+
private String region;
|
| 32 |
+
|
| 33 |
+
private Map<String, Object> detailsJson; // Provider-specific metadata
|
| 34 |
+
|
| 35 |
+
// Optional: initial set of labels to apply
|
| 36 |
+
private List<String> labels;
|
| 37 |
+
|
| 38 |
+
// createdByUserId will be set by the system/admin context, not directly from DTO
|
| 39 |
+
// discoveredAt, lastScannedAt will be set by the system
|
| 40 |
+
|
| 41 |
+
// Getters and Setters
|
| 42 |
+
public String getAssetName() {
|
| 43 |
+
return assetName;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
public void setAssetName(String assetName) {
|
| 47 |
+
this.assetName = assetName;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
public UUID getCloudConnectionId() {
|
| 51 |
+
return cloudConnectionId;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
public void setCloudConnectionId(UUID cloudConnectionId) {
|
| 55 |
+
this.cloudConnectionId = cloudConnectionId;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
public String getCloudProvider() {
|
| 59 |
+
return cloudProvider;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
public void setCloudProvider(String cloudProvider) {
|
| 63 |
+
this.cloudProvider = cloudProvider;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
public String getAssetType() {
|
| 67 |
+
return assetType;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
public void setAssetType(String assetType) {
|
| 71 |
+
this.assetType = assetType;
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
public String getNativeAssetId() {
|
| 75 |
+
return nativeAssetId;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
public void setNativeAssetId(String nativeAssetId) {
|
| 79 |
+
this.nativeAssetId = nativeAssetId;
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
public String getRegion() {
|
| 83 |
+
return region;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
public void setRegion(String region) {
|
| 87 |
+
this.region = region;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
public Map<String, Object> getDetailsJson() {
|
| 91 |
+
return detailsJson;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
public void setDetailsJson(Map<String, Object> detailsJson) {
|
| 95 |
+
this.detailsJson = detailsJson;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
public List<String> getLabels() {
|
| 99 |
+
return labels;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
public void setLabels(List<String> labels) {
|
| 103 |
+
this.labels = labels;
|
| 104 |
+
}
|
| 105 |
+
}
|
src/main/java/com/dalab/catalog/dto/AssetLabelsPostRequestDTO.java
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.util.List;
|
| 4 |
+
|
| 5 |
+
import jakarta.validation.Valid;
|
| 6 |
+
import jakarta.validation.constraints.NotEmpty;
|
| 7 |
+
|
| 8 |
+
public class AssetLabelsPostRequestDTO {
|
| 9 |
+
|
| 10 |
+
@NotEmpty
|
| 11 |
+
@Valid // To enable validation on elements of the list
|
| 12 |
+
private List<LabelAssignmentInputDTO> labels;
|
| 13 |
+
|
| 14 |
+
// Getters and Setters
|
| 15 |
+
public List<LabelAssignmentInputDTO> getLabels() {
|
| 16 |
+
return labels;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
public void setLabels(List<LabelAssignmentInputDTO> labels) {
|
| 20 |
+
this.labels = labels;
|
| 21 |
+
}
|
| 22 |
+
}
|
src/main/java/com/dalab/catalog/dto/AssetLabelsResponseDTO.java
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.util.List;
|
| 4 |
+
|
| 5 |
+
public class AssetLabelsResponseDTO {
|
| 6 |
+
private List<LabelOutputDTO> labels;
|
| 7 |
+
|
| 8 |
+
public AssetLabelsResponseDTO() {}
|
| 9 |
+
|
| 10 |
+
public AssetLabelsResponseDTO(List<LabelOutputDTO> labels) {
|
| 11 |
+
this.labels = labels;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
public List<LabelOutputDTO> getLabels() {
|
| 15 |
+
return labels;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
public void setLabels(List<LabelOutputDTO> labels) {
|
| 19 |
+
this.labels = labels;
|
| 20 |
+
}
|
| 21 |
+
}
|
src/main/java/com/dalab/catalog/dto/AssetOutputDTO.java
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.List;
|
| 5 |
+
import java.util.Map;
|
| 6 |
+
import java.util.UUID;
|
| 7 |
+
|
| 8 |
+
import com.fasterxml.jackson.annotation.JsonInclude;
|
| 9 |
+
|
| 10 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 11 |
+
public class AssetOutputDTO {
|
| 12 |
+
|
| 13 |
+
private UUID assetId;
|
| 14 |
+
private String assetName;
|
| 15 |
+
private UUID cloudConnectionId;
|
| 16 |
+
private String cloudProvider;
|
| 17 |
+
private String assetType;
|
| 18 |
+
private String nativeAssetId;
|
| 19 |
+
private String region;
|
| 20 |
+
private Map<String, Object> detailsJson; // Or a more structured DTO if details are well-defined
|
| 21 |
+
private Instant discoveredAt;
|
| 22 |
+
private Instant lastScannedAt;
|
| 23 |
+
private UUID createdByUserId; // Or a UserSummaryDTO
|
| 24 |
+
private Instant createdAt;
|
| 25 |
+
private Instant updatedAt;
|
| 26 |
+
private List<String> labels; // List of applied label names
|
| 27 |
+
private Map<String, Object> businessMetadataJson;
|
| 28 |
+
// private List<LabelDTO> labels; // Or list of LabelDTOs for more details
|
| 29 |
+
|
| 30 |
+
// Getters and Setters
|
| 31 |
+
public UUID getAssetId() {
|
| 32 |
+
return assetId;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
public void setAssetId(UUID assetId) {
|
| 36 |
+
this.assetId = assetId;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
public String getAssetName() {
|
| 40 |
+
return assetName;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
public void setAssetName(String assetName) {
|
| 44 |
+
this.assetName = assetName;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
public UUID getCloudConnectionId() {
|
| 48 |
+
return cloudConnectionId;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
public void setCloudConnectionId(UUID cloudConnectionId) {
|
| 52 |
+
this.cloudConnectionId = cloudConnectionId;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
public String getCloudProvider() {
|
| 56 |
+
return cloudProvider;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
public void setCloudProvider(String cloudProvider) {
|
| 60 |
+
this.cloudProvider = cloudProvider;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
public String getAssetType() {
|
| 64 |
+
return assetType;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
public void setAssetType(String assetType) {
|
| 68 |
+
this.assetType = assetType;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
public String getNativeAssetId() {
|
| 72 |
+
return nativeAssetId;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
public void setNativeAssetId(String nativeAssetId) {
|
| 76 |
+
this.nativeAssetId = nativeAssetId;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
public String getRegion() {
|
| 80 |
+
return region;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
public void setRegion(String region) {
|
| 84 |
+
this.region = region;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
public Map<String, Object> getDetailsJson() {
|
| 88 |
+
return detailsJson;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
public void setDetailsJson(Map<String, Object> detailsJson) {
|
| 92 |
+
this.detailsJson = detailsJson;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
public Instant getDiscoveredAt() {
|
| 96 |
+
return discoveredAt;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
public void setDiscoveredAt(Instant discoveredAt) {
|
| 100 |
+
this.discoveredAt = discoveredAt;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
public Instant getLastScannedAt() {
|
| 104 |
+
return lastScannedAt;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
public void setLastScannedAt(Instant lastScannedAt) {
|
| 108 |
+
this.lastScannedAt = lastScannedAt;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
public UUID getCreatedByUserId() {
|
| 112 |
+
return createdByUserId;
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
public void setCreatedByUserId(UUID createdByUserId) {
|
| 116 |
+
this.createdByUserId = createdByUserId;
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
public Instant getCreatedAt() {
|
| 120 |
+
return createdAt;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
public void setCreatedAt(Instant createdAt) {
|
| 124 |
+
this.createdAt = createdAt;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
public Instant getUpdatedAt() {
|
| 128 |
+
return updatedAt;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
public void setUpdatedAt(Instant updatedAt) {
|
| 132 |
+
this.updatedAt = updatedAt;
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
public List<String> getLabels() {
|
| 136 |
+
return labels;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
public void setLabels(List<String> labels) {
|
| 140 |
+
this.labels = labels;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
public Map<String, Object> getBusinessMetadataJson() {
|
| 144 |
+
return businessMetadataJson;
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
public void setBusinessMetadataJson(Map<String, Object> businessMetadataJson) {
|
| 148 |
+
this.businessMetadataJson = businessMetadataJson;
|
| 149 |
+
}
|
| 150 |
+
}
|
src/main/java/com/dalab/catalog/dto/AssetPolicyMappingDTO.java
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.List;
|
| 5 |
+
import java.util.Map;
|
| 6 |
+
import java.util.UUID;
|
| 7 |
+
|
| 8 |
+
import com.fasterxml.jackson.annotation.JsonInclude;
|
| 9 |
+
|
| 10 |
+
/**
|
| 11 |
+
* DTO for Asset-Policy Mapping information.
|
| 12 |
+
* Priority 2 endpoint: Shows which policies apply to an asset and their compliance status.
|
| 13 |
+
*/
|
| 14 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 15 |
+
public class AssetPolicyMappingDTO {
|
| 16 |
+
|
| 17 |
+
private UUID assetId;
|
| 18 |
+
private String assetName;
|
| 19 |
+
private List<PolicyMappingDTO> applicablePolicies;
|
| 20 |
+
private List<PolicyMappingDTO> inheritedPolicies;
|
| 21 |
+
private PolicyComplianceSummaryDTO complianceSummary;
|
| 22 |
+
private List<PolicyRiskDTO> riskAssessment;
|
| 23 |
+
private List<PolicyRecommendationDTO> recommendations;
|
| 24 |
+
private Instant lastEvaluated;
|
| 25 |
+
private String evaluationStatus;
|
| 26 |
+
|
| 27 |
+
/**
|
| 28 |
+
* Default constructor.
|
| 29 |
+
*/
|
| 30 |
+
public AssetPolicyMappingDTO() {}
|
| 31 |
+
|
| 32 |
+
/**
|
| 33 |
+
* Constructor with basic information.
|
| 34 |
+
*/
|
| 35 |
+
public AssetPolicyMappingDTO(UUID assetId, String assetName) {
|
| 36 |
+
this.assetId = assetId;
|
| 37 |
+
this.assetName = assetName;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
/**
|
| 41 |
+
* DTO for individual policy mapping information.
|
| 42 |
+
*/
|
| 43 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 44 |
+
public static class PolicyMappingDTO {
|
| 45 |
+
|
| 46 |
+
private UUID policyId;
|
| 47 |
+
private String policyName;
|
| 48 |
+
private String policyType; // DATA_GOVERNANCE, SECURITY, COMPLIANCE, RETENTION, etc.
|
| 49 |
+
private String policyVersion;
|
| 50 |
+
private String applicabilityReason; // DIRECT, INHERITED, LABEL_BASED, TYPE_BASED
|
| 51 |
+
private String complianceStatus; // COMPLIANT, NON_COMPLIANT, PARTIALLY_COMPLIANT, UNKNOWN
|
| 52 |
+
private Double complianceScore; // 0.0 to 100.0
|
| 53 |
+
private List<PolicyRuleComplianceDTO> ruleCompliance;
|
| 54 |
+
private Instant lastEvaluated;
|
| 55 |
+
private String evaluatedBy;
|
| 56 |
+
private List<String> exemptions;
|
| 57 |
+
private Map<String, Object> metadata;
|
| 58 |
+
|
| 59 |
+
public PolicyMappingDTO() {}
|
| 60 |
+
|
| 61 |
+
public PolicyMappingDTO(UUID policyId, String policyName, String policyType) {
|
| 62 |
+
this.policyId = policyId;
|
| 63 |
+
this.policyName = policyName;
|
| 64 |
+
this.policyType = policyType;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
// Getters and setters
|
| 68 |
+
public UUID getPolicyId() { return policyId; }
|
| 69 |
+
public void setPolicyId(UUID policyId) { this.policyId = policyId; }
|
| 70 |
+
public String getPolicyName() { return policyName; }
|
| 71 |
+
public void setPolicyName(String policyName) { this.policyName = policyName; }
|
| 72 |
+
public String getPolicyType() { return policyType; }
|
| 73 |
+
public void setPolicyType(String policyType) { this.policyType = policyType; }
|
| 74 |
+
public String getPolicyVersion() { return policyVersion; }
|
| 75 |
+
public void setPolicyVersion(String policyVersion) { this.policyVersion = policyVersion; }
|
| 76 |
+
public String getApplicabilityReason() { return applicabilityReason; }
|
| 77 |
+
public void setApplicabilityReason(String applicabilityReason) { this.applicabilityReason = applicabilityReason; }
|
| 78 |
+
public String getComplianceStatus() { return complianceStatus; }
|
| 79 |
+
public void setComplianceStatus(String complianceStatus) { this.complianceStatus = complianceStatus; }
|
| 80 |
+
public Double getComplianceScore() { return complianceScore; }
|
| 81 |
+
public void setComplianceScore(Double complianceScore) { this.complianceScore = complianceScore; }
|
| 82 |
+
public List<PolicyRuleComplianceDTO> getRuleCompliance() { return ruleCompliance; }
|
| 83 |
+
public void setRuleCompliance(List<PolicyRuleComplianceDTO> ruleCompliance) { this.ruleCompliance = ruleCompliance; }
|
| 84 |
+
public Instant getLastEvaluated() { return lastEvaluated; }
|
| 85 |
+
public void setLastEvaluated(Instant lastEvaluated) { this.lastEvaluated = lastEvaluated; }
|
| 86 |
+
public String getEvaluatedBy() { return evaluatedBy; }
|
| 87 |
+
public void setEvaluatedBy(String evaluatedBy) { this.evaluatedBy = evaluatedBy; }
|
| 88 |
+
public List<String> getExemptions() { return exemptions; }
|
| 89 |
+
public void setExemptions(List<String> exemptions) { this.exemptions = exemptions; }
|
| 90 |
+
public Map<String, Object> getMetadata() { return metadata; }
|
| 91 |
+
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
/**
|
| 95 |
+
* DTO for policy rule compliance details.
|
| 96 |
+
*/
|
| 97 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 98 |
+
public static class PolicyRuleComplianceDTO {
|
| 99 |
+
|
| 100 |
+
private String ruleId;
|
| 101 |
+
private String ruleName;
|
| 102 |
+
private String ruleType;
|
| 103 |
+
private String complianceStatus;
|
| 104 |
+
private String actualValue;
|
| 105 |
+
private String expectedValue;
|
| 106 |
+
private String nonComplianceReason;
|
| 107 |
+
private String severity; // CRITICAL, HIGH, MEDIUM, LOW
|
| 108 |
+
private List<String> recommendedActions;
|
| 109 |
+
|
| 110 |
+
public PolicyRuleComplianceDTO() {}
|
| 111 |
+
|
| 112 |
+
public PolicyRuleComplianceDTO(String ruleId, String ruleName, String complianceStatus) {
|
| 113 |
+
this.ruleId = ruleId;
|
| 114 |
+
this.ruleName = ruleName;
|
| 115 |
+
this.complianceStatus = complianceStatus;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
// Getters and setters
|
| 119 |
+
public String getRuleId() { return ruleId; }
|
| 120 |
+
public void setRuleId(String ruleId) { this.ruleId = ruleId; }
|
| 121 |
+
public String getRuleName() { return ruleName; }
|
| 122 |
+
public void setRuleName(String ruleName) { this.ruleName = ruleName; }
|
| 123 |
+
public String getRuleType() { return ruleType; }
|
| 124 |
+
public void setRuleType(String ruleType) { this.ruleType = ruleType; }
|
| 125 |
+
public String getComplianceStatus() { return complianceStatus; }
|
| 126 |
+
public void setComplianceStatus(String complianceStatus) { this.complianceStatus = complianceStatus; }
|
| 127 |
+
public String getActualValue() { return actualValue; }
|
| 128 |
+
public void setActualValue(String actualValue) { this.actualValue = actualValue; }
|
| 129 |
+
public String getExpectedValue() { return expectedValue; }
|
| 130 |
+
public void setExpectedValue(String expectedValue) { this.expectedValue = expectedValue; }
|
| 131 |
+
public String getNonComplianceReason() { return nonComplianceReason; }
|
| 132 |
+
public void setNonComplianceReason(String nonComplianceReason) { this.nonComplianceReason = nonComplianceReason; }
|
| 133 |
+
public String getSeverity() { return severity; }
|
| 134 |
+
public void setSeverity(String severity) { this.severity = severity; }
|
| 135 |
+
public List<String> getRecommendedActions() { return recommendedActions; }
|
| 136 |
+
public void setRecommendedActions(List<String> recommendedActions) { this.recommendedActions = recommendedActions; }
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
/**
|
| 140 |
+
* DTO for overall policy compliance summary.
|
| 141 |
+
*/
|
| 142 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 143 |
+
public static class PolicyComplianceSummaryDTO {
|
| 144 |
+
|
| 145 |
+
private Integer totalPolicies;
|
| 146 |
+
private Integer compliantPolicies;
|
| 147 |
+
private Integer nonCompliantPolicies;
|
| 148 |
+
private Integer partiallyCompliantPolicies;
|
| 149 |
+
private Double overallComplianceScore;
|
| 150 |
+
private String overallStatus;
|
| 151 |
+
private Map<String, Integer> policyTypeBreakdown;
|
| 152 |
+
private Map<String, Integer> complianceStatusBreakdown;
|
| 153 |
+
private List<String> criticalIssues;
|
| 154 |
+
private Instant lastFullEvaluation;
|
| 155 |
+
|
| 156 |
+
public PolicyComplianceSummaryDTO() {}
|
| 157 |
+
|
| 158 |
+
// Getters and setters
|
| 159 |
+
public Integer getTotalPolicies() { return totalPolicies; }
|
| 160 |
+
public void setTotalPolicies(Integer totalPolicies) { this.totalPolicies = totalPolicies; }
|
| 161 |
+
public Integer getCompliantPolicies() { return compliantPolicies; }
|
| 162 |
+
public void setCompliantPolicies(Integer compliantPolicies) { this.compliantPolicies = compliantPolicies; }
|
| 163 |
+
public Integer getNonCompliantPolicies() { return nonCompliantPolicies; }
|
| 164 |
+
public void setNonCompliantPolicies(Integer nonCompliantPolicies) { this.nonCompliantPolicies = nonCompliantPolicies; }
|
| 165 |
+
public Integer getPartiallyCompliantPolicies() { return partiallyCompliantPolicies; }
|
| 166 |
+
public void setPartiallyCompliantPolicies(Integer partiallyCompliantPolicies) { this.partiallyCompliantPolicies = partiallyCompliantPolicies; }
|
| 167 |
+
public Double getOverallComplianceScore() { return overallComplianceScore; }
|
| 168 |
+
public void setOverallComplianceScore(Double overallComplianceScore) { this.overallComplianceScore = overallComplianceScore; }
|
| 169 |
+
public String getOverallStatus() { return overallStatus; }
|
| 170 |
+
public void setOverallStatus(String overallStatus) { this.overallStatus = overallStatus; }
|
| 171 |
+
public Map<String, Integer> getPolicyTypeBreakdown() { return policyTypeBreakdown; }
|
| 172 |
+
public void setPolicyTypeBreakdown(Map<String, Integer> policyTypeBreakdown) { this.policyTypeBreakdown = policyTypeBreakdown; }
|
| 173 |
+
public Map<String, Integer> getComplianceStatusBreakdown() { return complianceStatusBreakdown; }
|
| 174 |
+
public void setComplianceStatusBreakdown(Map<String, Integer> complianceStatusBreakdown) { this.complianceStatusBreakdown = complianceStatusBreakdown; }
|
| 175 |
+
public List<String> getCriticalIssues() { return criticalIssues; }
|
| 176 |
+
public void setCriticalIssues(List<String> criticalIssues) { this.criticalIssues = criticalIssues; }
|
| 177 |
+
public Instant getLastFullEvaluation() { return lastFullEvaluation; }
|
| 178 |
+
public void setLastFullEvaluation(Instant lastFullEvaluation) { this.lastFullEvaluation = lastFullEvaluation; }
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
/**
|
| 182 |
+
* DTO for policy risk assessment.
|
| 183 |
+
*/
|
| 184 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 185 |
+
public static class PolicyRiskDTO {
|
| 186 |
+
|
| 187 |
+
private String riskType; // COMPLIANCE, SECURITY, GOVERNANCE, OPERATIONAL
|
| 188 |
+
private String riskLevel; // CRITICAL, HIGH, MEDIUM, LOW
|
| 189 |
+
private String description;
|
| 190 |
+
private String impact;
|
| 191 |
+
private String likelihood;
|
| 192 |
+
private List<String> mitigationActions;
|
| 193 |
+
private String owner;
|
| 194 |
+
private Instant identifiedAt;
|
| 195 |
+
|
| 196 |
+
public PolicyRiskDTO() {}
|
| 197 |
+
|
| 198 |
+
public PolicyRiskDTO(String riskType, String riskLevel, String description) {
|
| 199 |
+
this.riskType = riskType;
|
| 200 |
+
this.riskLevel = riskLevel;
|
| 201 |
+
this.description = description;
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
// Getters and setters
|
| 205 |
+
public String getRiskType() { return riskType; }
|
| 206 |
+
public void setRiskType(String riskType) { this.riskType = riskType; }
|
| 207 |
+
public String getRiskLevel() { return riskLevel; }
|
| 208 |
+
public void setRiskLevel(String riskLevel) { this.riskLevel = riskLevel; }
|
| 209 |
+
public String getDescription() { return description; }
|
| 210 |
+
public void setDescription(String description) { this.description = description; }
|
| 211 |
+
public String getImpact() { return impact; }
|
| 212 |
+
public void setImpact(String impact) { this.impact = impact; }
|
| 213 |
+
public String getLikelihood() { return likelihood; }
|
| 214 |
+
public void setLikelihood(String likelihood) { this.likelihood = likelihood; }
|
| 215 |
+
public List<String> getMitigationActions() { return mitigationActions; }
|
| 216 |
+
public void setMitigationActions(List<String> mitigationActions) { this.mitigationActions = mitigationActions; }
|
| 217 |
+
public String getOwner() { return owner; }
|
| 218 |
+
public void setOwner(String owner) { this.owner = owner; }
|
| 219 |
+
public Instant getIdentifiedAt() { return identifiedAt; }
|
| 220 |
+
public void setIdentifiedAt(Instant identifiedAt) { this.identifiedAt = identifiedAt; }
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
/**
|
| 224 |
+
* DTO for policy recommendations.
|
| 225 |
+
*/
|
| 226 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 227 |
+
public static class PolicyRecommendationDTO {
|
| 228 |
+
|
| 229 |
+
private String recommendationType; // POLICY_UPDATE, EXEMPTION_REQUEST, REMEDIATION_ACTION
|
| 230 |
+
private String priority; // HIGH, MEDIUM, LOW
|
| 231 |
+
private String title;
|
| 232 |
+
private String description;
|
| 233 |
+
private String actionRequired;
|
| 234 |
+
private String expectedOutcome;
|
| 235 |
+
private Integer estimatedEffort; // in hours
|
| 236 |
+
private String assignedTo;
|
| 237 |
+
private Instant createdAt;
|
| 238 |
+
|
| 239 |
+
public PolicyRecommendationDTO() {}
|
| 240 |
+
|
| 241 |
+
public PolicyRecommendationDTO(String recommendationType, String priority, String title) {
|
| 242 |
+
this.recommendationType = recommendationType;
|
| 243 |
+
this.priority = priority;
|
| 244 |
+
this.title = title;
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
// Getters and setters
|
| 248 |
+
public String getRecommendationType() { return recommendationType; }
|
| 249 |
+
public void setRecommendationType(String recommendationType) { this.recommendationType = recommendationType; }
|
| 250 |
+
public String getPriority() { return priority; }
|
| 251 |
+
public void setPriority(String priority) { this.priority = priority; }
|
| 252 |
+
public String getTitle() { return title; }
|
| 253 |
+
public void setTitle(String title) { this.title = title; }
|
| 254 |
+
public String getDescription() { return description; }
|
| 255 |
+
public void setDescription(String description) { this.description = description; }
|
| 256 |
+
public String getActionRequired() { return actionRequired; }
|
| 257 |
+
public void setActionRequired(String actionRequired) { this.actionRequired = actionRequired; }
|
| 258 |
+
public String getExpectedOutcome() { return expectedOutcome; }
|
| 259 |
+
public void setExpectedOutcome(String expectedOutcome) { this.expectedOutcome = expectedOutcome; }
|
| 260 |
+
public Integer getEstimatedEffort() { return estimatedEffort; }
|
| 261 |
+
public void setEstimatedEffort(Integer estimatedEffort) { this.estimatedEffort = estimatedEffort; }
|
| 262 |
+
public String getAssignedTo() { return assignedTo; }
|
| 263 |
+
public void setAssignedTo(String assignedTo) { this.assignedTo = assignedTo; }
|
| 264 |
+
public Instant getCreatedAt() { return createdAt; }
|
| 265 |
+
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
// Main class getters and setters
|
| 269 |
+
public UUID getAssetId() { return assetId; }
|
| 270 |
+
public void setAssetId(UUID assetId) { this.assetId = assetId; }
|
| 271 |
+
public String getAssetName() { return assetName; }
|
| 272 |
+
public void setAssetName(String assetName) { this.assetName = assetName; }
|
| 273 |
+
public List<PolicyMappingDTO> getApplicablePolicies() { return applicablePolicies; }
|
| 274 |
+
public void setApplicablePolicies(List<PolicyMappingDTO> applicablePolicies) { this.applicablePolicies = applicablePolicies; }
|
| 275 |
+
public List<PolicyMappingDTO> getInheritedPolicies() { return inheritedPolicies; }
|
| 276 |
+
public void setInheritedPolicies(List<PolicyMappingDTO> inheritedPolicies) { this.inheritedPolicies = inheritedPolicies; }
|
| 277 |
+
public PolicyComplianceSummaryDTO getComplianceSummary() { return complianceSummary; }
|
| 278 |
+
public void setComplianceSummary(PolicyComplianceSummaryDTO complianceSummary) { this.complianceSummary = complianceSummary; }
|
| 279 |
+
public List<PolicyRiskDTO> getRiskAssessment() { return riskAssessment; }
|
| 280 |
+
public void setRiskAssessment(List<PolicyRiskDTO> riskAssessment) { this.riskAssessment = riskAssessment; }
|
| 281 |
+
public List<PolicyRecommendationDTO> getRecommendations() { return recommendations; }
|
| 282 |
+
public void setRecommendations(List<PolicyRecommendationDTO> recommendations) { this.recommendations = recommendations; }
|
| 283 |
+
public Instant getLastEvaluated() { return lastEvaluated; }
|
| 284 |
+
public void setLastEvaluated(Instant lastEvaluated) { this.lastEvaluated = lastEvaluated; }
|
| 285 |
+
public String getEvaluationStatus() { return evaluationStatus; }
|
| 286 |
+
public void setEvaluationStatus(String evaluationStatus) { this.evaluationStatus = evaluationStatus; }
|
| 287 |
+
}
|
src/main/java/com/dalab/catalog/dto/AssetSchemaDTO.java
ADDED
|
@@ -0,0 +1,574 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.List;
|
| 5 |
+
import java.util.Map;
|
| 6 |
+
import java.util.UUID;
|
| 7 |
+
|
| 8 |
+
import com.fasterxml.jackson.annotation.JsonInclude;
|
| 9 |
+
|
| 10 |
+
/**
|
| 11 |
+
* Comprehensive DTO for Asset Schema Information.
|
| 12 |
+
* Provides detailed schema structure with field metadata, data types, constraints, and lineage.
|
| 13 |
+
* Priority 2 endpoint implementation for enhanced catalog functionality.
|
| 14 |
+
*/
|
| 15 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 16 |
+
public class AssetSchemaDTO {
|
| 17 |
+
|
| 18 |
+
private UUID assetId;
|
| 19 |
+
private String assetName;
|
| 20 |
+
private String schemaVersion;
|
| 21 |
+
private SchemaTypeDTO schemaType;
|
| 22 |
+
private List<SchemaFieldDTO> fields;
|
| 23 |
+
private List<SchemaConstraintDTO> constraints;
|
| 24 |
+
private List<SchemaIndexDTO> indexes;
|
| 25 |
+
private SchemaStatisticsDTO statistics;
|
| 26 |
+
private SchemaLineageDTO lineage;
|
| 27 |
+
private SchemaEvolutionDTO evolution;
|
| 28 |
+
private Map<String, Object> metadata;
|
| 29 |
+
private Instant lastUpdated;
|
| 30 |
+
private Instant lastAnalyzed;
|
| 31 |
+
private String source;
|
| 32 |
+
|
| 33 |
+
/**
|
| 34 |
+
* Default constructor for AssetSchemaDTO.
|
| 35 |
+
*/
|
| 36 |
+
public AssetSchemaDTO() {}
|
| 37 |
+
|
| 38 |
+
/**
|
| 39 |
+
* Constructor with basic schema information.
|
| 40 |
+
*/
|
| 41 |
+
public AssetSchemaDTO(UUID assetId, String assetName, String schemaVersion, SchemaTypeDTO schemaType) {
|
| 42 |
+
this.assetId = assetId;
|
| 43 |
+
this.assetName = assetName;
|
| 44 |
+
this.schemaVersion = schemaVersion;
|
| 45 |
+
this.schemaType = schemaType;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
/**
|
| 49 |
+
* DTO for Schema Type Information.
|
| 50 |
+
*/
|
| 51 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 52 |
+
public static class SchemaTypeDTO {
|
| 53 |
+
private String type; // TABLE, VIEW, FILE, API, STREAM, DOCUMENT
|
| 54 |
+
private String format; // CSV, JSON, PARQUET, AVRO, XML, etc.
|
| 55 |
+
private String encoding; // UTF-8, ASCII, etc.
|
| 56 |
+
private Map<String, Object> formatSpecificProperties;
|
| 57 |
+
|
| 58 |
+
public SchemaTypeDTO() {}
|
| 59 |
+
|
| 60 |
+
public SchemaTypeDTO(String type, String format) {
|
| 61 |
+
this.type = type;
|
| 62 |
+
this.format = format;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
// Getters and setters
|
| 66 |
+
public String getType() { return type; }
|
| 67 |
+
public void setType(String type) { this.type = type; }
|
| 68 |
+
public String getFormat() { return format; }
|
| 69 |
+
public void setFormat(String format) { this.format = format; }
|
| 70 |
+
public String getEncoding() { return encoding; }
|
| 71 |
+
public void setEncoding(String encoding) { this.encoding = encoding; }
|
| 72 |
+
public Map<String, Object> getFormatSpecificProperties() { return formatSpecificProperties; }
|
| 73 |
+
public void setFormatSpecificProperties(Map<String, Object> formatSpecificProperties) {
|
| 74 |
+
this.formatSpecificProperties = formatSpecificProperties;
|
| 75 |
+
}
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
/**
|
| 79 |
+
* DTO for individual schema fields with comprehensive metadata.
|
| 80 |
+
*/
|
| 81 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 82 |
+
public static class SchemaFieldDTO {
|
| 83 |
+
private String fieldName;
|
| 84 |
+
private String dataType;
|
| 85 |
+
private String logicalType;
|
| 86 |
+
private boolean nullable;
|
| 87 |
+
private boolean primaryKey;
|
| 88 |
+
private boolean foreignKey;
|
| 89 |
+
private String defaultValue;
|
| 90 |
+
private String description;
|
| 91 |
+
private List<String> tags;
|
| 92 |
+
private FieldStatisticsDTO statistics;
|
| 93 |
+
private FieldConstraintsDTO constraints;
|
| 94 |
+
private Integer ordinalPosition;
|
| 95 |
+
private String classification; // PII, SENSITIVE, PUBLIC, etc.
|
| 96 |
+
private Map<String, Object> properties;
|
| 97 |
+
|
| 98 |
+
public SchemaFieldDTO() {}
|
| 99 |
+
|
| 100 |
+
public SchemaFieldDTO(String fieldName, String dataType, boolean nullable) {
|
| 101 |
+
this.fieldName = fieldName;
|
| 102 |
+
this.dataType = dataType;
|
| 103 |
+
this.nullable = nullable;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
// Getters and setters
|
| 107 |
+
public String getFieldName() { return fieldName; }
|
| 108 |
+
public void setFieldName(String fieldName) { this.fieldName = fieldName; }
|
| 109 |
+
public String getDataType() { return dataType; }
|
| 110 |
+
public void setDataType(String dataType) { this.dataType = dataType; }
|
| 111 |
+
public String getLogicalType() { return logicalType; }
|
| 112 |
+
public void setLogicalType(String logicalType) { this.logicalType = logicalType; }
|
| 113 |
+
public boolean isNullable() { return nullable; }
|
| 114 |
+
public void setNullable(boolean nullable) { this.nullable = nullable; }
|
| 115 |
+
public boolean isPrimaryKey() { return primaryKey; }
|
| 116 |
+
public void setPrimaryKey(boolean primaryKey) { this.primaryKey = primaryKey; }
|
| 117 |
+
public boolean isForeignKey() { return foreignKey; }
|
| 118 |
+
public void setForeignKey(boolean foreignKey) { this.foreignKey = foreignKey; }
|
| 119 |
+
public String getDefaultValue() { return defaultValue; }
|
| 120 |
+
public void setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; }
|
| 121 |
+
public String getDescription() { return description; }
|
| 122 |
+
public void setDescription(String description) { this.description = description; }
|
| 123 |
+
public List<String> getTags() { return tags; }
|
| 124 |
+
public void setTags(List<String> tags) { this.tags = tags; }
|
| 125 |
+
public FieldStatisticsDTO getStatistics() { return statistics; }
|
| 126 |
+
public void setStatistics(FieldStatisticsDTO statistics) { this.statistics = statistics; }
|
| 127 |
+
public FieldConstraintsDTO getConstraints() { return constraints; }
|
| 128 |
+
public void setConstraints(FieldConstraintsDTO constraints) { this.constraints = constraints; }
|
| 129 |
+
public Integer getOrdinalPosition() { return ordinalPosition; }
|
| 130 |
+
public void setOrdinalPosition(Integer ordinalPosition) { this.ordinalPosition = ordinalPosition; }
|
| 131 |
+
public String getClassification() { return classification; }
|
| 132 |
+
public void setClassification(String classification) { this.classification = classification; }
|
| 133 |
+
public Map<String, Object> getProperties() { return properties; }
|
| 134 |
+
public void setProperties(Map<String, Object> properties) { this.properties = properties; }
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
/**
|
| 138 |
+
* DTO for field-level statistics.
|
| 139 |
+
*/
|
| 140 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 141 |
+
public static class FieldStatisticsDTO {
|
| 142 |
+
private Long distinctCount;
|
| 143 |
+
private Long nullCount;
|
| 144 |
+
private Double fillRate;
|
| 145 |
+
private String minValue;
|
| 146 |
+
private String maxValue;
|
| 147 |
+
private Double avgLength;
|
| 148 |
+
private Double maxLength;
|
| 149 |
+
private List<String> topValues;
|
| 150 |
+
private Instant lastAnalyzed;
|
| 151 |
+
|
| 152 |
+
public FieldStatisticsDTO() {}
|
| 153 |
+
|
| 154 |
+
// Getters and setters
|
| 155 |
+
public Long getDistinctCount() { return distinctCount; }
|
| 156 |
+
public void setDistinctCount(Long distinctCount) { this.distinctCount = distinctCount; }
|
| 157 |
+
public Long getNullCount() { return nullCount; }
|
| 158 |
+
public void setNullCount(Long nullCount) { this.nullCount = nullCount; }
|
| 159 |
+
public Double getFillRate() { return fillRate; }
|
| 160 |
+
public void setFillRate(Double fillRate) { this.fillRate = fillRate; }
|
| 161 |
+
public String getMinValue() { return minValue; }
|
| 162 |
+
public void setMinValue(String minValue) { this.minValue = minValue; }
|
| 163 |
+
public String getMaxValue() { return maxValue; }
|
| 164 |
+
public void setMaxValue(String maxValue) { this.maxValue = maxValue; }
|
| 165 |
+
public Double getAvgLength() { return avgLength; }
|
| 166 |
+
public void setAvgLength(Double avgLength) { this.avgLength = avgLength; }
|
| 167 |
+
public Double getMaxLength() { return maxLength; }
|
| 168 |
+
public void setMaxLength(Double maxLength) { this.maxLength = maxLength; }
|
| 169 |
+
public List<String> getTopValues() { return topValues; }
|
| 170 |
+
public void setTopValues(List<String> topValues) { this.topValues = topValues; }
|
| 171 |
+
public Instant getLastAnalyzed() { return lastAnalyzed; }
|
| 172 |
+
public void setLastAnalyzed(Instant lastAnalyzed) { this.lastAnalyzed = lastAnalyzed; }
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
/**
|
| 176 |
+
* DTO for field-level constraints.
|
| 177 |
+
*/
|
| 178 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 179 |
+
public static class FieldConstraintsDTO {
|
| 180 |
+
private String pattern; // Regex pattern for validation
|
| 181 |
+
private Integer minLength;
|
| 182 |
+
private Integer maxLength;
|
| 183 |
+
private String minValue;
|
| 184 |
+
private String maxValue;
|
| 185 |
+
private List<String> enumValues;
|
| 186 |
+
private String format; // EMAIL, PHONE, URL, etc.
|
| 187 |
+
private Map<String, Object> customConstraints;
|
| 188 |
+
|
| 189 |
+
public FieldConstraintsDTO() {}
|
| 190 |
+
|
| 191 |
+
// Getters and setters
|
| 192 |
+
public String getPattern() { return pattern; }
|
| 193 |
+
public void setPattern(String pattern) { this.pattern = pattern; }
|
| 194 |
+
public Integer getMinLength() { return minLength; }
|
| 195 |
+
public void setMinLength(Integer minLength) { this.minLength = minLength; }
|
| 196 |
+
public Integer getMaxLength() { return maxLength; }
|
| 197 |
+
public void setMaxLength(Integer maxLength) { this.maxLength = maxLength; }
|
| 198 |
+
public String getMinValue() { return minValue; }
|
| 199 |
+
public void setMinValue(String minValue) { this.minValue = minValue; }
|
| 200 |
+
public String getMaxValue() { return maxValue; }
|
| 201 |
+
public void setMaxValue(String maxValue) { this.maxValue = maxValue; }
|
| 202 |
+
public List<String> getEnumValues() { return enumValues; }
|
| 203 |
+
public void setEnumValues(List<String> enumValues) { this.enumValues = enumValues; }
|
| 204 |
+
public String getFormat() { return format; }
|
| 205 |
+
public void setFormat(String format) { this.format = format; }
|
| 206 |
+
public Map<String, Object> getCustomConstraints() { return customConstraints; }
|
| 207 |
+
public void setCustomConstraints(Map<String, Object> customConstraints) {
|
| 208 |
+
this.customConstraints = customConstraints;
|
| 209 |
+
}
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
/**
|
| 213 |
+
* DTO for schema-level constraints.
|
| 214 |
+
*/
|
| 215 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 216 |
+
public static class SchemaConstraintDTO {
|
| 217 |
+
private String constraintName;
|
| 218 |
+
private String constraintType; // PRIMARY_KEY, FOREIGN_KEY, UNIQUE, CHECK, NOT_NULL
|
| 219 |
+
private List<String> columnNames;
|
| 220 |
+
private String referencedTable;
|
| 221 |
+
private List<String> referencedColumns;
|
| 222 |
+
private String checkExpression;
|
| 223 |
+
private boolean enforced;
|
| 224 |
+
|
| 225 |
+
public SchemaConstraintDTO() {}
|
| 226 |
+
|
| 227 |
+
public SchemaConstraintDTO(String constraintName, String constraintType) {
|
| 228 |
+
this.constraintName = constraintName;
|
| 229 |
+
this.constraintType = constraintType;
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
// Getters and setters
|
| 233 |
+
public String getConstraintName() { return constraintName; }
|
| 234 |
+
public void setConstraintName(String constraintName) { this.constraintName = constraintName; }
|
| 235 |
+
public String getConstraintType() { return constraintType; }
|
| 236 |
+
public void setConstraintType(String constraintType) { this.constraintType = constraintType; }
|
| 237 |
+
public List<String> getColumnNames() { return columnNames; }
|
| 238 |
+
public void setColumnNames(List<String> columnNames) { this.columnNames = columnNames; }
|
| 239 |
+
public String getReferencedTable() { return referencedTable; }
|
| 240 |
+
public void setReferencedTable(String referencedTable) { this.referencedTable = referencedTable; }
|
| 241 |
+
public List<String> getReferencedColumns() { return referencedColumns; }
|
| 242 |
+
public void setReferencedColumns(List<String> referencedColumns) { this.referencedColumns = referencedColumns; }
|
| 243 |
+
public String getCheckExpression() { return checkExpression; }
|
| 244 |
+
public void setCheckExpression(String checkExpression) { this.checkExpression = checkExpression; }
|
| 245 |
+
public boolean isEnforced() { return enforced; }
|
| 246 |
+
public void setEnforced(boolean enforced) { this.enforced = enforced; }
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
/**
|
| 250 |
+
* DTO for schema indexes.
|
| 251 |
+
*/
|
| 252 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 253 |
+
public static class SchemaIndexDTO {
|
| 254 |
+
private String indexName;
|
| 255 |
+
private String indexType; // BTREE, HASH, BITMAP, CLUSTERED, etc.
|
| 256 |
+
private List<String> columnNames;
|
| 257 |
+
private boolean unique;
|
| 258 |
+
private boolean primary;
|
| 259 |
+
private Long cardinality;
|
| 260 |
+
private Map<String, Object> properties;
|
| 261 |
+
|
| 262 |
+
public SchemaIndexDTO() {}
|
| 263 |
+
|
| 264 |
+
public SchemaIndexDTO(String indexName, String indexType, boolean unique) {
|
| 265 |
+
this.indexName = indexName;
|
| 266 |
+
this.indexType = indexType;
|
| 267 |
+
this.unique = unique;
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
// Getters and setters
|
| 271 |
+
public String getIndexName() { return indexName; }
|
| 272 |
+
public void setIndexName(String indexName) { this.indexName = indexName; }
|
| 273 |
+
public String getIndexType() { return indexType; }
|
| 274 |
+
public void setIndexType(String indexType) { this.indexType = indexType; }
|
| 275 |
+
public List<String> getColumnNames() { return columnNames; }
|
| 276 |
+
public void setColumnNames(List<String> columnNames) { this.columnNames = columnNames; }
|
| 277 |
+
public boolean isUnique() { return unique; }
|
| 278 |
+
public void setUnique(boolean unique) { this.unique = unique; }
|
| 279 |
+
public boolean isPrimary() { return primary; }
|
| 280 |
+
public void setPrimary(boolean primary) { this.primary = primary; }
|
| 281 |
+
public Long getCardinality() { return cardinality; }
|
| 282 |
+
public void setCardinality(Long cardinality) { this.cardinality = cardinality; }
|
| 283 |
+
public Map<String, Object> getProperties() { return properties; }
|
| 284 |
+
public void setProperties(Map<String, Object> properties) { this.properties = properties; }
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
/**
|
| 288 |
+
* DTO for schema-level statistics.
|
| 289 |
+
*/
|
| 290 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 291 |
+
public static class SchemaStatisticsDTO {
|
| 292 |
+
private Long totalRows;
|
| 293 |
+
private Long totalColumns;
|
| 294 |
+
private Long totalSize;
|
| 295 |
+
private Double compressionRatio;
|
| 296 |
+
private Instant lastUpdated;
|
| 297 |
+
private Instant lastAnalyzed;
|
| 298 |
+
private Map<String, Long> dataTypeCounts;
|
| 299 |
+
private Double schemaComplexity;
|
| 300 |
+
private Integer schemaDepth;
|
| 301 |
+
|
| 302 |
+
public SchemaStatisticsDTO() {}
|
| 303 |
+
|
| 304 |
+
// Getters and setters
|
| 305 |
+
public Long getTotalRows() { return totalRows; }
|
| 306 |
+
public void setTotalRows(Long totalRows) { this.totalRows = totalRows; }
|
| 307 |
+
public Long getTotalColumns() { return totalColumns; }
|
| 308 |
+
public void setTotalColumns(Long totalColumns) { this.totalColumns = totalColumns; }
|
| 309 |
+
public Long getTotalSize() { return totalSize; }
|
| 310 |
+
public void setTotalSize(Long totalSize) { this.totalSize = totalSize; }
|
| 311 |
+
public Double getCompressionRatio() { return compressionRatio; }
|
| 312 |
+
public void setCompressionRatio(Double compressionRatio) { this.compressionRatio = compressionRatio; }
|
| 313 |
+
public Instant getLastUpdated() { return lastUpdated; }
|
| 314 |
+
public void setLastUpdated(Instant lastUpdated) { this.lastUpdated = lastUpdated; }
|
| 315 |
+
public Instant getLastAnalyzed() { return lastAnalyzed; }
|
| 316 |
+
public void setLastAnalyzed(Instant lastAnalyzed) { this.lastAnalyzed = lastAnalyzed; }
|
| 317 |
+
public Map<String, Long> getDataTypeCounts() { return dataTypeCounts; }
|
| 318 |
+
public void setDataTypeCounts(Map<String, Long> dataTypeCounts) { this.dataTypeCounts = dataTypeCounts; }
|
| 319 |
+
public Double getSchemaComplexity() { return schemaComplexity; }
|
| 320 |
+
public void setSchemaComplexity(Double schemaComplexity) { this.schemaComplexity = schemaComplexity; }
|
| 321 |
+
public Integer getSchemaDepth() { return schemaDepth; }
|
| 322 |
+
public void setSchemaDepth(Integer schemaDepth) { this.schemaDepth = schemaDepth; }
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
/**
|
| 326 |
+
* DTO for schema lineage information.
|
| 327 |
+
*/
|
| 328 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 329 |
+
public static class SchemaLineageDTO {
|
| 330 |
+
private List<SchemaRelationshipDTO> upstream;
|
| 331 |
+
private List<SchemaRelationshipDTO> downstream;
|
| 332 |
+
private List<SchemaTransformationDTO> transformations;
|
| 333 |
+
private Integer lineageDepth;
|
| 334 |
+
private Instant lineageLastUpdated;
|
| 335 |
+
|
| 336 |
+
public SchemaLineageDTO() {}
|
| 337 |
+
|
| 338 |
+
// Getters and setters
|
| 339 |
+
public List<SchemaRelationshipDTO> getUpstream() { return upstream; }
|
| 340 |
+
public void setUpstream(List<SchemaRelationshipDTO> upstream) { this.upstream = upstream; }
|
| 341 |
+
public List<SchemaRelationshipDTO> getDownstream() { return downstream; }
|
| 342 |
+
public void setDownstream(List<SchemaRelationshipDTO> downstream) { this.downstream = downstream; }
|
| 343 |
+
public List<SchemaTransformationDTO> getTransformations() { return transformations; }
|
| 344 |
+
public void setTransformations(List<SchemaTransformationDTO> transformations) {
|
| 345 |
+
this.transformations = transformations;
|
| 346 |
+
}
|
| 347 |
+
public Integer getLineageDepth() { return lineageDepth; }
|
| 348 |
+
public void setLineageDepth(Integer lineageDepth) { this.lineageDepth = lineageDepth; }
|
| 349 |
+
public Instant getLineageLastUpdated() { return lineageLastUpdated; }
|
| 350 |
+
public void setLineageLastUpdated(Instant lineageLastUpdated) { this.lineageLastUpdated = lineageLastUpdated; }
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
/**
|
| 354 |
+
* DTO for schema relationships in lineage.
|
| 355 |
+
*/
|
| 356 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 357 |
+
public static class SchemaRelationshipDTO {
|
| 358 |
+
private UUID relatedAssetId;
|
| 359 |
+
private String relatedAssetName;
|
| 360 |
+
private String relationshipType; // DERIVED_FROM, COPIED_TO, JOINED_WITH, etc.
|
| 361 |
+
private List<FieldMappingDTO> fieldMappings;
|
| 362 |
+
private String transformationType;
|
| 363 |
+
private Instant relationshipCreated;
|
| 364 |
+
|
| 365 |
+
public SchemaRelationshipDTO() {}
|
| 366 |
+
|
| 367 |
+
// Getters and setters
|
| 368 |
+
public UUID getRelatedAssetId() { return relatedAssetId; }
|
| 369 |
+
public void setRelatedAssetId(UUID relatedAssetId) { this.relatedAssetId = relatedAssetId; }
|
| 370 |
+
public String getRelatedAssetName() { return relatedAssetName; }
|
| 371 |
+
public void setRelatedAssetName(String relatedAssetName) { this.relatedAssetName = relatedAssetName; }
|
| 372 |
+
public String getRelationshipType() { return relationshipType; }
|
| 373 |
+
public void setRelationshipType(String relationshipType) { this.relationshipType = relationshipType; }
|
| 374 |
+
public List<FieldMappingDTO> getFieldMappings() { return fieldMappings; }
|
| 375 |
+
public void setFieldMappings(List<FieldMappingDTO> fieldMappings) { this.fieldMappings = fieldMappings; }
|
| 376 |
+
public String getTransformationType() { return transformationType; }
|
| 377 |
+
public void setTransformationType(String transformationType) { this.transformationType = transformationType; }
|
| 378 |
+
public Instant getRelationshipCreated() { return relationshipCreated; }
|
| 379 |
+
public void setRelationshipCreated(Instant relationshipCreated) { this.relationshipCreated = relationshipCreated; }
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
/**
|
| 383 |
+
* DTO for field mappings in schema relationships.
|
| 384 |
+
*/
|
| 385 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 386 |
+
public static class FieldMappingDTO {
|
| 387 |
+
private String sourceField;
|
| 388 |
+
private String targetField;
|
| 389 |
+
private String mappingType; // DIRECT, TRANSFORMED, CALCULATED, etc.
|
| 390 |
+
private String transformation;
|
| 391 |
+
private Double confidence;
|
| 392 |
+
|
| 393 |
+
public FieldMappingDTO() {}
|
| 394 |
+
|
| 395 |
+
public FieldMappingDTO(String sourceField, String targetField, String mappingType) {
|
| 396 |
+
this.sourceField = sourceField;
|
| 397 |
+
this.targetField = targetField;
|
| 398 |
+
this.mappingType = mappingType;
|
| 399 |
+
}
|
| 400 |
+
|
| 401 |
+
// Getters and setters
|
| 402 |
+
public String getSourceField() { return sourceField; }
|
| 403 |
+
public void setSourceField(String sourceField) { this.sourceField = sourceField; }
|
| 404 |
+
public String getTargetField() { return targetField; }
|
| 405 |
+
public void setTargetField(String targetField) { this.targetField = targetField; }
|
| 406 |
+
public String getMappingType() { return mappingType; }
|
| 407 |
+
public void setMappingType(String mappingType) { this.mappingType = mappingType; }
|
| 408 |
+
public String getTransformation() { return transformation; }
|
| 409 |
+
public void setTransformation(String transformation) { this.transformation = transformation; }
|
| 410 |
+
public Double getConfidence() { return confidence; }
|
| 411 |
+
public void setConfidence(Double confidence) { this.confidence = confidence; }
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
/**
|
| 415 |
+
* DTO for schema transformations.
|
| 416 |
+
*/
|
| 417 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 418 |
+
public static class SchemaTransformationDTO {
|
| 419 |
+
private String transformationId;
|
| 420 |
+
private String transformationType; // ETL, ELT, STREAM_PROCESSING, etc.
|
| 421 |
+
private String transformationLogic;
|
| 422 |
+
private List<String> inputFields;
|
| 423 |
+
private List<String> outputFields;
|
| 424 |
+
private Map<String, Object> parameters;
|
| 425 |
+
private Instant executedAt;
|
| 426 |
+
|
| 427 |
+
public SchemaTransformationDTO() {}
|
| 428 |
+
|
| 429 |
+
// Getters and setters
|
| 430 |
+
public String getTransformationId() { return transformationId; }
|
| 431 |
+
public void setTransformationId(String transformationId) { this.transformationId = transformationId; }
|
| 432 |
+
public String getTransformationType() { return transformationType; }
|
| 433 |
+
public void setTransformationType(String transformationType) { this.transformationType = transformationType; }
|
| 434 |
+
public String getTransformationLogic() { return transformationLogic; }
|
| 435 |
+
public void setTransformationLogic(String transformationLogic) { this.transformationLogic = transformationLogic; }
|
| 436 |
+
public List<String> getInputFields() { return inputFields; }
|
| 437 |
+
public void setInputFields(List<String> inputFields) { this.inputFields = inputFields; }
|
| 438 |
+
public List<String> getOutputFields() { return outputFields; }
|
| 439 |
+
public void setOutputFields(List<String> outputFields) { this.outputFields = outputFields; }
|
| 440 |
+
public Map<String, Object> getParameters() { return parameters; }
|
| 441 |
+
public void setParameters(Map<String, Object> parameters) { this.parameters = parameters; }
|
| 442 |
+
public Instant getExecutedAt() { return executedAt; }
|
| 443 |
+
public void setExecutedAt(Instant executedAt) { this.executedAt = executedAt; }
|
| 444 |
+
}
|
| 445 |
+
|
| 446 |
+
/**
|
| 447 |
+
* DTO for schema evolution tracking.
|
| 448 |
+
*/
|
| 449 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 450 |
+
public static class SchemaEvolutionDTO {
|
| 451 |
+
private List<SchemaVersionDTO> versions;
|
| 452 |
+
private List<SchemaChangeDTO> recentChanges;
|
| 453 |
+
private Integer totalVersions;
|
| 454 |
+
private Instant firstVersion;
|
| 455 |
+
private Instant lastChange;
|
| 456 |
+
|
| 457 |
+
public SchemaEvolutionDTO() {}
|
| 458 |
+
|
| 459 |
+
// Getters and setters
|
| 460 |
+
public List<SchemaVersionDTO> getVersions() { return versions; }
|
| 461 |
+
public void setVersions(List<SchemaVersionDTO> versions) { this.versions = versions; }
|
| 462 |
+
public List<SchemaChangeDTO> getRecentChanges() { return recentChanges; }
|
| 463 |
+
public void setRecentChanges(List<SchemaChangeDTO> recentChanges) { this.recentChanges = recentChanges; }
|
| 464 |
+
public Integer getTotalVersions() { return totalVersions; }
|
| 465 |
+
public void setTotalVersions(Integer totalVersions) { this.totalVersions = totalVersions; }
|
| 466 |
+
public Instant getFirstVersion() { return firstVersion; }
|
| 467 |
+
public void setFirstVersion(Instant firstVersion) { this.firstVersion = firstVersion; }
|
| 468 |
+
public Instant getLastChange() { return lastChange; }
|
| 469 |
+
public void setLastChange(Instant lastChange) { this.lastChange = lastChange; }
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
/**
|
| 473 |
+
* DTO for schema versions.
|
| 474 |
+
*/
|
| 475 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 476 |
+
public static class SchemaVersionDTO {
|
| 477 |
+
private String version;
|
| 478 |
+
private Instant createdAt;
|
| 479 |
+
private String createdBy;
|
| 480 |
+
private String changeDescription;
|
| 481 |
+
private Integer fieldCount;
|
| 482 |
+
private List<String> changedFields;
|
| 483 |
+
|
| 484 |
+
public SchemaVersionDTO() {}
|
| 485 |
+
|
| 486 |
+
public SchemaVersionDTO(String version, Instant createdAt, String createdBy) {
|
| 487 |
+
this.version = version;
|
| 488 |
+
this.createdAt = createdAt;
|
| 489 |
+
this.createdBy = createdBy;
|
| 490 |
+
}
|
| 491 |
+
|
| 492 |
+
// Getters and setters
|
| 493 |
+
public String getVersion() { return version; }
|
| 494 |
+
public void setVersion(String version) { this.version = version; }
|
| 495 |
+
public Instant getCreatedAt() { return createdAt; }
|
| 496 |
+
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
| 497 |
+
public String getCreatedBy() { return createdBy; }
|
| 498 |
+
public void setCreatedBy(String createdBy) { this.createdBy = createdBy; }
|
| 499 |
+
public String getChangeDescription() { return changeDescription; }
|
| 500 |
+
public void setChangeDescription(String changeDescription) { this.changeDescription = changeDescription; }
|
| 501 |
+
public Integer getFieldCount() { return fieldCount; }
|
| 502 |
+
public void setFieldCount(Integer fieldCount) { this.fieldCount = fieldCount; }
|
| 503 |
+
public List<String> getChangedFields() { return changedFields; }
|
| 504 |
+
public void setChangedFields(List<String> changedFields) { this.changedFields = changedFields; }
|
| 505 |
+
}
|
| 506 |
+
|
| 507 |
+
/**
|
| 508 |
+
* DTO for schema changes.
|
| 509 |
+
*/
|
| 510 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 511 |
+
public static class SchemaChangeDTO {
|
| 512 |
+
private String changeType; // FIELD_ADDED, FIELD_REMOVED, FIELD_MODIFIED, CONSTRAINT_ADDED, etc.
|
| 513 |
+
private String fieldName;
|
| 514 |
+
private String oldValue;
|
| 515 |
+
private String newValue;
|
| 516 |
+
private Instant changeTimestamp;
|
| 517 |
+
private String changedBy;
|
| 518 |
+
private String changeReason;
|
| 519 |
+
|
| 520 |
+
public SchemaChangeDTO() {}
|
| 521 |
+
|
| 522 |
+
public SchemaChangeDTO(String changeType, String fieldName, Instant changeTimestamp) {
|
| 523 |
+
this.changeType = changeType;
|
| 524 |
+
this.fieldName = fieldName;
|
| 525 |
+
this.changeTimestamp = changeTimestamp;
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
// Getters and setters
|
| 529 |
+
public String getChangeType() { return changeType; }
|
| 530 |
+
public void setChangeType(String changeType) { this.changeType = changeType; }
|
| 531 |
+
public String getFieldName() { return fieldName; }
|
| 532 |
+
public void setFieldName(String fieldName) { this.fieldName = fieldName; }
|
| 533 |
+
public String getOldValue() { return oldValue; }
|
| 534 |
+
public void setOldValue(String oldValue) { this.oldValue = oldValue; }
|
| 535 |
+
public String getNewValue() { return newValue; }
|
| 536 |
+
public void setNewValue(String newValue) { this.newValue = newValue; }
|
| 537 |
+
public Instant getChangeTimestamp() { return changeTimestamp; }
|
| 538 |
+
public void setChangeTimestamp(Instant changeTimestamp) { this.changeTimestamp = changeTimestamp; }
|
| 539 |
+
public String getChangedBy() { return changedBy; }
|
| 540 |
+
public void setChangedBy(String changedBy) { this.changedBy = changedBy; }
|
| 541 |
+
public String getChangeReason() { return changeReason; }
|
| 542 |
+
public void setChangeReason(String changeReason) { this.changeReason = changeReason; }
|
| 543 |
+
}
|
| 544 |
+
|
| 545 |
+
// Main class getters and setters
|
| 546 |
+
public UUID getAssetId() { return assetId; }
|
| 547 |
+
public void setAssetId(UUID assetId) { this.assetId = assetId; }
|
| 548 |
+
public String getAssetName() { return assetName; }
|
| 549 |
+
public void setAssetName(String assetName) { this.assetName = assetName; }
|
| 550 |
+
public String getSchemaVersion() { return schemaVersion; }
|
| 551 |
+
public void setSchemaVersion(String schemaVersion) { this.schemaVersion = schemaVersion; }
|
| 552 |
+
public SchemaTypeDTO getSchemaType() { return schemaType; }
|
| 553 |
+
public void setSchemaType(SchemaTypeDTO schemaType) { this.schemaType = schemaType; }
|
| 554 |
+
public List<SchemaFieldDTO> getFields() { return fields; }
|
| 555 |
+
public void setFields(List<SchemaFieldDTO> fields) { this.fields = fields; }
|
| 556 |
+
public List<SchemaConstraintDTO> getConstraints() { return constraints; }
|
| 557 |
+
public void setConstraints(List<SchemaConstraintDTO> constraints) { this.constraints = constraints; }
|
| 558 |
+
public List<SchemaIndexDTO> getIndexes() { return indexes; }
|
| 559 |
+
public void setIndexes(List<SchemaIndexDTO> indexes) { this.indexes = indexes; }
|
| 560 |
+
public SchemaStatisticsDTO getStatistics() { return statistics; }
|
| 561 |
+
public void setStatistics(SchemaStatisticsDTO statistics) { this.statistics = statistics; }
|
| 562 |
+
public SchemaLineageDTO getLineage() { return lineage; }
|
| 563 |
+
public void setLineage(SchemaLineageDTO lineage) { this.lineage = lineage; }
|
| 564 |
+
public SchemaEvolutionDTO getEvolution() { return evolution; }
|
| 565 |
+
public void setEvolution(SchemaEvolutionDTO evolution) { this.evolution = evolution; }
|
| 566 |
+
public Map<String, Object> getMetadata() { return metadata; }
|
| 567 |
+
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
|
| 568 |
+
public Instant getLastUpdated() { return lastUpdated; }
|
| 569 |
+
public void setLastUpdated(Instant lastUpdated) { this.lastUpdated = lastUpdated; }
|
| 570 |
+
public Instant getLastAnalyzed() { return lastAnalyzed; }
|
| 571 |
+
public void setLastAnalyzed(Instant lastAnalyzed) { this.lastAnalyzed = lastAnalyzed; }
|
| 572 |
+
public String getSource() { return source; }
|
| 573 |
+
public void setSource(String source) { this.source = source; }
|
| 574 |
+
}
|
src/main/java/com/dalab/catalog/dto/AssetUsageAnalyticsDTO.java
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.List;
|
| 5 |
+
import java.util.Map;
|
| 6 |
+
import java.util.UUID;
|
| 7 |
+
|
| 8 |
+
import com.fasterxml.jackson.annotation.JsonInclude;
|
| 9 |
+
|
| 10 |
+
/**
|
| 11 |
+
* DTO for comprehensive asset usage analytics.
|
| 12 |
+
* Provides insights into asset access patterns, user behavior, performance metrics,
|
| 13 |
+
* and recommendations for optimization.
|
| 14 |
+
*/
|
| 15 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 16 |
+
public class AssetUsageAnalyticsDTO {
|
| 17 |
+
|
| 18 |
+
private UsageMetricsDTO usageMetrics;
|
| 19 |
+
private UserAnalyticsDTO userAnalytics;
|
| 20 |
+
private AccessPatternsDTO accessPatterns;
|
| 21 |
+
private PerformanceMetricsDTO performanceMetrics;
|
| 22 |
+
private List<PopularQueryDTO> popularQueries;
|
| 23 |
+
private List<RecommendationDTO> recommendations;
|
| 24 |
+
|
| 25 |
+
// Constructor
|
| 26 |
+
public AssetUsageAnalyticsDTO() {}
|
| 27 |
+
|
| 28 |
+
// Getters and Setters
|
| 29 |
+
public UsageMetricsDTO getUsageMetrics() {
|
| 30 |
+
return usageMetrics;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
public void setUsageMetrics(UsageMetricsDTO usageMetrics) {
|
| 34 |
+
this.usageMetrics = usageMetrics;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
public UserAnalyticsDTO getUserAnalytics() {
|
| 38 |
+
return userAnalytics;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
public void setUserAnalytics(UserAnalyticsDTO userAnalytics) {
|
| 42 |
+
this.userAnalytics = userAnalytics;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
public AccessPatternsDTO getAccessPatterns() {
|
| 46 |
+
return accessPatterns;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
public void setAccessPatterns(AccessPatternsDTO accessPatterns) {
|
| 50 |
+
this.accessPatterns = accessPatterns;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
public PerformanceMetricsDTO getPerformanceMetrics() {
|
| 54 |
+
return performanceMetrics;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
public void setPerformanceMetrics(PerformanceMetricsDTO performanceMetrics) {
|
| 58 |
+
this.performanceMetrics = performanceMetrics;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
public List<PopularQueryDTO> getPopularQueries() {
|
| 62 |
+
return popularQueries;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
public void setPopularQueries(List<PopularQueryDTO> popularQueries) {
|
| 66 |
+
this.popularQueries = popularQueries;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
public List<RecommendationDTO> getRecommendations() {
|
| 70 |
+
return recommendations;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
public void setRecommendations(List<RecommendationDTO> recommendations) {
|
| 74 |
+
this.recommendations = recommendations;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
/**
|
| 78 |
+
* Usage metrics showing overall asset access statistics.
|
| 79 |
+
*/
|
| 80 |
+
public static class UsageMetricsDTO {
|
| 81 |
+
private Long totalAccesses;
|
| 82 |
+
private Integer uniqueUsers;
|
| 83 |
+
private Double avgAccessesPerDay;
|
| 84 |
+
private Instant lastAccessed;
|
| 85 |
+
private Integer peakUsageHour;
|
| 86 |
+
private String usageTrend; // INCREASING, DECREASING, STABLE
|
| 87 |
+
|
| 88 |
+
public UsageMetricsDTO() {}
|
| 89 |
+
|
| 90 |
+
public Long getTotalAccesses() {
|
| 91 |
+
return totalAccesses;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
public void setTotalAccesses(Long totalAccesses) {
|
| 95 |
+
this.totalAccesses = totalAccesses;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
public Integer getUniqueUsers() {
|
| 99 |
+
return uniqueUsers;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
public void setUniqueUsers(Integer uniqueUsers) {
|
| 103 |
+
this.uniqueUsers = uniqueUsers;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
public Double getAvgAccessesPerDay() {
|
| 107 |
+
return avgAccessesPerDay;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
public void setAvgAccessesPerDay(Double avgAccessesPerDay) {
|
| 111 |
+
this.avgAccessesPerDay = avgAccessesPerDay;
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
public Instant getLastAccessed() {
|
| 115 |
+
return lastAccessed;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
public void setLastAccessed(Instant lastAccessed) {
|
| 119 |
+
this.lastAccessed = lastAccessed;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
public Integer getPeakUsageHour() {
|
| 123 |
+
return peakUsageHour;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
public void setPeakUsageHour(Integer peakUsageHour) {
|
| 127 |
+
this.peakUsageHour = peakUsageHour;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
public String getUsageTrend() {
|
| 131 |
+
return usageTrend;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
public void setUsageTrend(String usageTrend) {
|
| 135 |
+
this.usageTrend = usageTrend;
|
| 136 |
+
}
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
/**
|
| 140 |
+
* User analytics showing who is accessing the asset and how.
|
| 141 |
+
*/
|
| 142 |
+
public static class UserAnalyticsDTO {
|
| 143 |
+
private List<TopUserDTO> topUsers;
|
| 144 |
+
private Map<String, Double> departmentBreakdown;
|
| 145 |
+
|
| 146 |
+
public UserAnalyticsDTO() {}
|
| 147 |
+
|
| 148 |
+
public List<TopUserDTO> getTopUsers() {
|
| 149 |
+
return topUsers;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
public void setTopUsers(List<TopUserDTO> topUsers) {
|
| 153 |
+
this.topUsers = topUsers;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
public Map<String, Double> getDepartmentBreakdown() {
|
| 157 |
+
return departmentBreakdown;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
public void setDepartmentBreakdown(Map<String, Double> departmentBreakdown) {
|
| 161 |
+
this.departmentBreakdown = departmentBreakdown;
|
| 162 |
+
}
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
/**
|
| 166 |
+
* Individual user access information.
|
| 167 |
+
*/
|
| 168 |
+
public static class TopUserDTO {
|
| 169 |
+
private UUID userId;
|
| 170 |
+
private String username;
|
| 171 |
+
private Integer accessCount;
|
| 172 |
+
private Instant lastAccess;
|
| 173 |
+
private String accessPattern; // REGULAR, OCCASIONAL, HEAVY
|
| 174 |
+
|
| 175 |
+
public TopUserDTO() {}
|
| 176 |
+
|
| 177 |
+
public TopUserDTO(UUID userId, String username, Integer accessCount, Instant lastAccess, String accessPattern) {
|
| 178 |
+
this.userId = userId;
|
| 179 |
+
this.username = username;
|
| 180 |
+
this.accessCount = accessCount;
|
| 181 |
+
this.lastAccess = lastAccess;
|
| 182 |
+
this.accessPattern = accessPattern;
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
public UUID getUserId() {
|
| 186 |
+
return userId;
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
public void setUserId(UUID userId) {
|
| 190 |
+
this.userId = userId;
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
public String getUsername() {
|
| 194 |
+
return username;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
public void setUsername(String username) {
|
| 198 |
+
this.username = username;
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
public Integer getAccessCount() {
|
| 202 |
+
return accessCount;
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
public void setAccessCount(Integer accessCount) {
|
| 206 |
+
this.accessCount = accessCount;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
public Instant getLastAccess() {
|
| 210 |
+
return lastAccess;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
public void setLastAccess(Instant lastAccess) {
|
| 214 |
+
this.lastAccess = lastAccess;
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
public String getAccessPattern() {
|
| 218 |
+
return accessPattern;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
public void setAccessPattern(String accessPattern) {
|
| 222 |
+
this.accessPattern = accessPattern;
|
| 223 |
+
}
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
/**
|
| 227 |
+
* Access patterns showing temporal usage distribution.
|
| 228 |
+
*/
|
| 229 |
+
public static class AccessPatternsDTO {
|
| 230 |
+
private List<Integer> hourlyDistribution; // 24 hour array
|
| 231 |
+
private List<Integer> weeklyDistribution; // 7 day array
|
| 232 |
+
private List<MonthlyTrendDTO> monthlyTrend;
|
| 233 |
+
|
| 234 |
+
public AccessPatternsDTO() {}
|
| 235 |
+
|
| 236 |
+
public List<Integer> getHourlyDistribution() {
|
| 237 |
+
return hourlyDistribution;
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
public void setHourlyDistribution(List<Integer> hourlyDistribution) {
|
| 241 |
+
this.hourlyDistribution = hourlyDistribution;
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
public List<Integer> getWeeklyDistribution() {
|
| 245 |
+
return weeklyDistribution;
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
public void setWeeklyDistribution(List<Integer> weeklyDistribution) {
|
| 249 |
+
this.weeklyDistribution = weeklyDistribution;
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
public List<MonthlyTrendDTO> getMonthlyTrend() {
|
| 253 |
+
return monthlyTrend;
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
public void setMonthlyTrend(List<MonthlyTrendDTO> monthlyTrend) {
|
| 257 |
+
this.monthlyTrend = monthlyTrend;
|
| 258 |
+
}
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
/**
|
| 262 |
+
* Monthly usage trend data.
|
| 263 |
+
*/
|
| 264 |
+
public static class MonthlyTrendDTO {
|
| 265 |
+
private String month; // YYYY-MM format
|
| 266 |
+
private Integer totalAccesses;
|
| 267 |
+
private Integer uniqueUsers;
|
| 268 |
+
|
| 269 |
+
public MonthlyTrendDTO() {}
|
| 270 |
+
|
| 271 |
+
public MonthlyTrendDTO(String month, Integer totalAccesses, Integer uniqueUsers) {
|
| 272 |
+
this.month = month;
|
| 273 |
+
this.totalAccesses = totalAccesses;
|
| 274 |
+
this.uniqueUsers = uniqueUsers;
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
public String getMonth() {
|
| 278 |
+
return month;
|
| 279 |
+
}
|
| 280 |
+
|
| 281 |
+
public void setMonth(String month) {
|
| 282 |
+
this.month = month;
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
public Integer getTotalAccesses() {
|
| 286 |
+
return totalAccesses;
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
public void setTotalAccesses(Integer totalAccesses) {
|
| 290 |
+
this.totalAccesses = totalAccesses;
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
public Integer getUniqueUsers() {
|
| 294 |
+
return uniqueUsers;
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
public void setUniqueUsers(Integer uniqueUsers) {
|
| 298 |
+
this.uniqueUsers = uniqueUsers;
|
| 299 |
+
}
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
/**
|
| 303 |
+
* Performance metrics for asset access.
|
| 304 |
+
*/
|
| 305 |
+
public static class PerformanceMetricsDTO {
|
| 306 |
+
private String avgQueryTime;
|
| 307 |
+
private String slowestQuery;
|
| 308 |
+
private String fastestQuery;
|
| 309 |
+
private Double timeoutRate;
|
| 310 |
+
private Double errorRate;
|
| 311 |
+
|
| 312 |
+
public PerformanceMetricsDTO() {}
|
| 313 |
+
|
| 314 |
+
public String getAvgQueryTime() {
|
| 315 |
+
return avgQueryTime;
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
public void setAvgQueryTime(String avgQueryTime) {
|
| 319 |
+
this.avgQueryTime = avgQueryTime;
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
public String getSlowestQuery() {
|
| 323 |
+
return slowestQuery;
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
public void setSlowestQuery(String slowestQuery) {
|
| 327 |
+
this.slowestQuery = slowestQuery;
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
public String getFastestQuery() {
|
| 331 |
+
return fastestQuery;
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
public void setFastestQuery(String fastestQuery) {
|
| 335 |
+
this.fastestQuery = fastestQuery;
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
public Double getTimeoutRate() {
|
| 339 |
+
return timeoutRate;
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
public void setTimeoutRate(Double timeoutRate) {
|
| 343 |
+
this.timeoutRate = timeoutRate;
|
| 344 |
+
}
|
| 345 |
+
|
| 346 |
+
public Double getErrorRate() {
|
| 347 |
+
return errorRate;
|
| 348 |
+
}
|
| 349 |
+
|
| 350 |
+
public void setErrorRate(Double errorRate) {
|
| 351 |
+
this.errorRate = errorRate;
|
| 352 |
+
}
|
| 353 |
+
}
|
| 354 |
+
|
| 355 |
+
/**
|
| 356 |
+
* Popular query patterns and their performance.
|
| 357 |
+
*/
|
| 358 |
+
public static class PopularQueryDTO {
|
| 359 |
+
private String queryPattern;
|
| 360 |
+
private Integer frequency;
|
| 361 |
+
private String avgExecutionTime;
|
| 362 |
+
|
| 363 |
+
public PopularQueryDTO() {}
|
| 364 |
+
|
| 365 |
+
public PopularQueryDTO(String queryPattern, Integer frequency, String avgExecutionTime) {
|
| 366 |
+
this.queryPattern = queryPattern;
|
| 367 |
+
this.frequency = frequency;
|
| 368 |
+
this.avgExecutionTime = avgExecutionTime;
|
| 369 |
+
}
|
| 370 |
+
|
| 371 |
+
public String getQueryPattern() {
|
| 372 |
+
return queryPattern;
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
public void setQueryPattern(String queryPattern) {
|
| 376 |
+
this.queryPattern = queryPattern;
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
public Integer getFrequency() {
|
| 380 |
+
return frequency;
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
public void setFrequency(Integer frequency) {
|
| 384 |
+
this.frequency = frequency;
|
| 385 |
+
}
|
| 386 |
+
|
| 387 |
+
public String getAvgExecutionTime() {
|
| 388 |
+
return avgExecutionTime;
|
| 389 |
+
}
|
| 390 |
+
|
| 391 |
+
public void setAvgExecutionTime(String avgExecutionTime) {
|
| 392 |
+
this.avgExecutionTime = avgExecutionTime;
|
| 393 |
+
}
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
/**
|
| 397 |
+
* Optimization recommendations based on usage analysis.
|
| 398 |
+
*/
|
| 399 |
+
public static class RecommendationDTO {
|
| 400 |
+
private String type; // OPTIMIZATION, MAINTENANCE, SECURITY, COMPLIANCE
|
| 401 |
+
private String priority; // HIGH, MEDIUM, LOW
|
| 402 |
+
private String description;
|
| 403 |
+
private String potentialImpact;
|
| 404 |
+
|
| 405 |
+
public RecommendationDTO() {}
|
| 406 |
+
|
| 407 |
+
public RecommendationDTO(String type, String priority, String description, String potentialImpact) {
|
| 408 |
+
this.type = type;
|
| 409 |
+
this.priority = priority;
|
| 410 |
+
this.description = description;
|
| 411 |
+
this.potentialImpact = potentialImpact;
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
public String getType() {
|
| 415 |
+
return type;
|
| 416 |
+
}
|
| 417 |
+
|
| 418 |
+
public void setType(String type) {
|
| 419 |
+
this.type = type;
|
| 420 |
+
}
|
| 421 |
+
|
| 422 |
+
public String getPriority() {
|
| 423 |
+
return priority;
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
+
public void setPriority(String priority) {
|
| 427 |
+
this.priority = priority;
|
| 428 |
+
}
|
| 429 |
+
|
| 430 |
+
public String getDescription() {
|
| 431 |
+
return description;
|
| 432 |
+
}
|
| 433 |
+
|
| 434 |
+
public void setDescription(String description) {
|
| 435 |
+
this.description = description;
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
public String getPotentialImpact() {
|
| 439 |
+
return potentialImpact;
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
+
public void setPotentialImpact(String potentialImpact) {
|
| 443 |
+
this.potentialImpact = potentialImpact;
|
| 444 |
+
}
|
| 445 |
+
}
|
| 446 |
+
}
|
src/main/java/com/dalab/catalog/dto/BusinessMetadataInputDTO.java
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.util.List;
|
| 4 |
+
import java.util.UUID;
|
| 5 |
+
|
| 6 |
+
import com.fasterxml.jackson.annotation.JsonInclude;
|
| 7 |
+
|
| 8 |
+
import jakarta.validation.constraints.DecimalMax;
|
| 9 |
+
import jakarta.validation.constraints.DecimalMin;
|
| 10 |
+
import jakarta.validation.constraints.NotBlank;
|
| 11 |
+
import jakarta.validation.constraints.Size;
|
| 12 |
+
|
| 13 |
+
/**
|
| 14 |
+
* Enhanced DTO for business metadata management.
|
| 15 |
+
* Supports steward relationships, compliance classification, and business context.
|
| 16 |
+
*/
|
| 17 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 18 |
+
public class BusinessMetadataInputDTO {
|
| 19 |
+
|
| 20 |
+
private BusinessMetadataDTO businessMetadata;
|
| 21 |
+
private ComplianceClassificationDTO complianceClassification;
|
| 22 |
+
private String updateReason;
|
| 23 |
+
|
| 24 |
+
public BusinessMetadataInputDTO() {}
|
| 25 |
+
|
| 26 |
+
public BusinessMetadataDTO getBusinessMetadata() {
|
| 27 |
+
return businessMetadata;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
public void setBusinessMetadata(BusinessMetadataDTO businessMetadata) {
|
| 31 |
+
this.businessMetadata = businessMetadata;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
public ComplianceClassificationDTO getComplianceClassification() {
|
| 35 |
+
return complianceClassification;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
public void setComplianceClassification(ComplianceClassificationDTO complianceClassification) {
|
| 39 |
+
this.complianceClassification = complianceClassification;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
public String getUpdateReason() {
|
| 43 |
+
return updateReason;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
public void setUpdateReason(String updateReason) {
|
| 47 |
+
this.updateReason = updateReason;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
/**
|
| 51 |
+
* Core business metadata information
|
| 52 |
+
*/
|
| 53 |
+
public static class BusinessMetadataDTO {
|
| 54 |
+
|
| 55 |
+
@Size(max = 255, message = "Business name cannot exceed 255 characters")
|
| 56 |
+
private String businessName;
|
| 57 |
+
|
| 58 |
+
@Size(max = 1000, message = "Business description cannot exceed 1000 characters")
|
| 59 |
+
private String businessDescription;
|
| 60 |
+
|
| 61 |
+
private BusinessContactDTO businessOwner;
|
| 62 |
+
private BusinessContactDTO dataSteward;
|
| 63 |
+
|
| 64 |
+
@NotBlank(message = "Business criticality is required")
|
| 65 |
+
private String businessCriticality; // HIGH, MEDIUM, LOW, CRITICAL
|
| 66 |
+
|
| 67 |
+
@Size(max = 100, message = "Business domain cannot exceed 100 characters")
|
| 68 |
+
private String businessDomain; // CUSTOMER_ANALYTICS, FINANCE, OPERATIONS, etc.
|
| 69 |
+
|
| 70 |
+
private List<String> useCases;
|
| 71 |
+
|
| 72 |
+
@DecimalMin(value = "0.0", message = "Data quality score must be between 0 and 10")
|
| 73 |
+
@DecimalMax(value = "10.0", message = "Data quality score must be between 0 and 10")
|
| 74 |
+
private Double dataQualityScore;
|
| 75 |
+
|
| 76 |
+
private String updateFrequency; // REAL_TIME, HOURLY, DAILY, WEEKLY, MONTHLY, BATCH
|
| 77 |
+
|
| 78 |
+
private RetentionRequirementsDTO retentionRequirements;
|
| 79 |
+
|
| 80 |
+
public BusinessMetadataDTO() {}
|
| 81 |
+
|
| 82 |
+
public String getBusinessName() {
|
| 83 |
+
return businessName;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
public void setBusinessName(String businessName) {
|
| 87 |
+
this.businessName = businessName;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
public String getBusinessDescription() {
|
| 91 |
+
return businessDescription;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
public void setBusinessDescription(String businessDescription) {
|
| 95 |
+
this.businessDescription = businessDescription;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
public BusinessContactDTO getBusinessOwner() {
|
| 99 |
+
return businessOwner;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
public void setBusinessOwner(BusinessContactDTO businessOwner) {
|
| 103 |
+
this.businessOwner = businessOwner;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
public BusinessContactDTO getDataSteward() {
|
| 107 |
+
return dataSteward;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
public void setDataSteward(BusinessContactDTO dataSteward) {
|
| 111 |
+
this.dataSteward = dataSteward;
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
public String getBusinessCriticality() {
|
| 115 |
+
return businessCriticality;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
public void setBusinessCriticality(String businessCriticality) {
|
| 119 |
+
this.businessCriticality = businessCriticality;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
public String getBusinessDomain() {
|
| 123 |
+
return businessDomain;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
public void setBusinessDomain(String businessDomain) {
|
| 127 |
+
this.businessDomain = businessDomain;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
public List<String> getUseCases() {
|
| 131 |
+
return useCases;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
public void setUseCases(List<String> useCases) {
|
| 135 |
+
this.useCases = useCases;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
public Double getDataQualityScore() {
|
| 139 |
+
return dataQualityScore;
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
public void setDataQualityScore(Double dataQualityScore) {
|
| 143 |
+
this.dataQualityScore = dataQualityScore;
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
public String getUpdateFrequency() {
|
| 147 |
+
return updateFrequency;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
public void setUpdateFrequency(String updateFrequency) {
|
| 151 |
+
this.updateFrequency = updateFrequency;
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
public RetentionRequirementsDTO getRetentionRequirements() {
|
| 155 |
+
return retentionRequirements;
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
public void setRetentionRequirements(RetentionRequirementsDTO retentionRequirements) {
|
| 159 |
+
this.retentionRequirements = retentionRequirements;
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
/**
|
| 164 |
+
* Business contact information for owners and stewards
|
| 165 |
+
*/
|
| 166 |
+
public static class BusinessContactDTO {
|
| 167 |
+
|
| 168 |
+
private UUID userId;
|
| 169 |
+
|
| 170 |
+
@NotBlank(message = "Contact name is required")
|
| 171 |
+
@Size(max = 255, message = "Name cannot exceed 255 characters")
|
| 172 |
+
private String name;
|
| 173 |
+
|
| 174 |
+
@NotBlank(message = "Contact email is required")
|
| 175 |
+
@Size(max = 255, message = "Email cannot exceed 255 characters")
|
| 176 |
+
private String email;
|
| 177 |
+
|
| 178 |
+
@Size(max = 100, message = "Department cannot exceed 100 characters")
|
| 179 |
+
private String department;
|
| 180 |
+
|
| 181 |
+
@Size(max = 100, message = "Role cannot exceed 100 characters")
|
| 182 |
+
private String role;
|
| 183 |
+
|
| 184 |
+
@Size(max = 50, message = "Phone cannot exceed 50 characters")
|
| 185 |
+
private String phone;
|
| 186 |
+
|
| 187 |
+
public BusinessContactDTO() {}
|
| 188 |
+
|
| 189 |
+
public BusinessContactDTO(UUID userId, String name, String email, String department) {
|
| 190 |
+
this.userId = userId;
|
| 191 |
+
this.name = name;
|
| 192 |
+
this.email = email;
|
| 193 |
+
this.department = department;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
public UUID getUserId() {
|
| 197 |
+
return userId;
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
public void setUserId(UUID userId) {
|
| 201 |
+
this.userId = userId;
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
public String getName() {
|
| 205 |
+
return name;
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
public void setName(String name) {
|
| 209 |
+
this.name = name;
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
public String getEmail() {
|
| 213 |
+
return email;
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
public void setEmail(String email) {
|
| 217 |
+
this.email = email;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
public String getDepartment() {
|
| 221 |
+
return department;
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
public void setDepartment(String department) {
|
| 225 |
+
this.department = department;
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
public String getRole() {
|
| 229 |
+
return role;
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
public void setRole(String role) {
|
| 233 |
+
this.role = role;
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
public String getPhone() {
|
| 237 |
+
return phone;
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
public void setPhone(String phone) {
|
| 241 |
+
this.phone = phone;
|
| 242 |
+
}
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
/**
|
| 246 |
+
* Data retention requirements and policies
|
| 247 |
+
*/
|
| 248 |
+
public static class RetentionRequirementsDTO {
|
| 249 |
+
|
| 250 |
+
private String legalRetention; // 7_YEARS, 10_YEARS, INDEFINITE, etc.
|
| 251 |
+
private String businessRetention; // 5_YEARS, 3_YEARS, etc.
|
| 252 |
+
private String archiveAfter; // 2_YEARS, 1_YEAR, etc.
|
| 253 |
+
private String deleteAfter; // 10_YEARS, NEVER, etc.
|
| 254 |
+
private List<String> retentionReasons; // LEGAL_REQUIREMENT, BUSINESS_NEED, REGULATORY_COMPLIANCE
|
| 255 |
+
|
| 256 |
+
public RetentionRequirementsDTO() {}
|
| 257 |
+
|
| 258 |
+
public String getLegalRetention() {
|
| 259 |
+
return legalRetention;
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
public void setLegalRetention(String legalRetention) {
|
| 263 |
+
this.legalRetention = legalRetention;
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
public String getBusinessRetention() {
|
| 267 |
+
return businessRetention;
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
public void setBusinessRetention(String businessRetention) {
|
| 271 |
+
this.businessRetention = businessRetention;
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
public String getArchiveAfter() {
|
| 275 |
+
return archiveAfter;
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
public void setArchiveAfter(String archiveAfter) {
|
| 279 |
+
this.archiveAfter = archiveAfter;
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
public String getDeleteAfter() {
|
| 283 |
+
return deleteAfter;
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
public void setDeleteAfter(String deleteAfter) {
|
| 287 |
+
this.deleteAfter = deleteAfter;
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
public List<String> getRetentionReasons() {
|
| 291 |
+
return retentionReasons;
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
public void setRetentionReasons(List<String> retentionReasons) {
|
| 295 |
+
this.retentionReasons = retentionReasons;
|
| 296 |
+
}
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
/**
|
| 300 |
+
* Compliance and data classification information
|
| 301 |
+
*/
|
| 302 |
+
public static class ComplianceClassificationDTO {
|
| 303 |
+
|
| 304 |
+
@NotBlank(message = "Data classification is required")
|
| 305 |
+
private String dataClassification; // PUBLIC, INTERNAL, CONFIDENTIAL, RESTRICTED
|
| 306 |
+
|
| 307 |
+
private Boolean containsPii;
|
| 308 |
+
private Boolean containsPhi; // Protected Health Information
|
| 309 |
+
private Boolean containsFinancial;
|
| 310 |
+
private Boolean gdprApplicable;
|
| 311 |
+
private Boolean soxRelevant; // Sarbanes-Oxley
|
| 312 |
+
private Boolean hipaaRelevant;
|
| 313 |
+
private Boolean pciRelevant; // Payment Card Industry
|
| 314 |
+
|
| 315 |
+
private List<String> regulatoryFrameworks; // GDPR, CCPA, SOX, HIPAA, PCI_DSS
|
| 316 |
+
private List<String> dataSubjectCategories; // CUSTOMER, EMPLOYEE, PARTNER, VENDOR
|
| 317 |
+
private String privacyImpactLevel; // HIGH, MEDIUM, LOW, NONE
|
| 318 |
+
|
| 319 |
+
public ComplianceClassificationDTO() {}
|
| 320 |
+
|
| 321 |
+
public String getDataClassification() {
|
| 322 |
+
return dataClassification;
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
public void setDataClassification(String dataClassification) {
|
| 326 |
+
this.dataClassification = dataClassification;
|
| 327 |
+
}
|
| 328 |
+
|
| 329 |
+
public Boolean getContainsPii() {
|
| 330 |
+
return containsPii;
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
+
public void setContainsPii(Boolean containsPii) {
|
| 334 |
+
this.containsPii = containsPii;
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
public Boolean getContainsPhi() {
|
| 338 |
+
return containsPhi;
|
| 339 |
+
}
|
| 340 |
+
|
| 341 |
+
public void setContainsPhi(Boolean containsPhi) {
|
| 342 |
+
this.containsPhi = containsPhi;
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
public Boolean getContainsFinancial() {
|
| 346 |
+
return containsFinancial;
|
| 347 |
+
}
|
| 348 |
+
|
| 349 |
+
public void setContainsFinancial(Boolean containsFinancial) {
|
| 350 |
+
this.containsFinancial = containsFinancial;
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
public Boolean getGdprApplicable() {
|
| 354 |
+
return gdprApplicable;
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
public void setGdprApplicable(Boolean gdprApplicable) {
|
| 358 |
+
this.gdprApplicable = gdprApplicable;
|
| 359 |
+
}
|
| 360 |
+
|
| 361 |
+
public Boolean getSoxRelevant() {
|
| 362 |
+
return soxRelevant;
|
| 363 |
+
}
|
| 364 |
+
|
| 365 |
+
public void setSoxRelevant(Boolean soxRelevant) {
|
| 366 |
+
this.soxRelevant = soxRelevant;
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
public Boolean getHipaaRelevant() {
|
| 370 |
+
return hipaaRelevant;
|
| 371 |
+
}
|
| 372 |
+
|
| 373 |
+
public void setHipaaRelevant(Boolean hipaaRelevant) {
|
| 374 |
+
this.hipaaRelevant = hipaaRelevant;
|
| 375 |
+
}
|
| 376 |
+
|
| 377 |
+
public Boolean getPciRelevant() {
|
| 378 |
+
return pciRelevant;
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
public void setPciRelevant(Boolean pciRelevant) {
|
| 382 |
+
this.pciRelevant = pciRelevant;
|
| 383 |
+
}
|
| 384 |
+
|
| 385 |
+
public List<String> getRegulatoryFrameworks() {
|
| 386 |
+
return regulatoryFrameworks;
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
public void setRegulatoryFrameworks(List<String> regulatoryFrameworks) {
|
| 390 |
+
this.regulatoryFrameworks = regulatoryFrameworks;
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
public List<String> getDataSubjectCategories() {
|
| 394 |
+
return dataSubjectCategories;
|
| 395 |
+
}
|
| 396 |
+
|
| 397 |
+
public void setDataSubjectCategories(List<String> dataSubjectCategories) {
|
| 398 |
+
this.dataSubjectCategories = dataSubjectCategories;
|
| 399 |
+
}
|
| 400 |
+
|
| 401 |
+
public String getPrivacyImpactLevel() {
|
| 402 |
+
return privacyImpactLevel;
|
| 403 |
+
}
|
| 404 |
+
|
| 405 |
+
public void setPrivacyImpactLevel(String privacyImpactLevel) {
|
| 406 |
+
this.privacyImpactLevel = privacyImpactLevel;
|
| 407 |
+
}
|
| 408 |
+
}
|
| 409 |
+
}
|
src/main/java/com/dalab/catalog/dto/BusinessMetadataResponseDTO.java
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.List;
|
| 5 |
+
import java.util.UUID;
|
| 6 |
+
|
| 7 |
+
import com.fasterxml.jackson.annotation.JsonInclude;
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Enhanced response DTO for business metadata.
|
| 11 |
+
* Includes audit information, validation status, and complete business context.
|
| 12 |
+
*/
|
| 13 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 14 |
+
public class BusinessMetadataResponseDTO {
|
| 15 |
+
|
| 16 |
+
private BusinessMetadataDTO businessMetadata;
|
| 17 |
+
private ComplianceClassificationDTO complianceClassification;
|
| 18 |
+
private MetadataAuditDTO auditInfo;
|
| 19 |
+
private ValidationStatusDTO validationStatus;
|
| 20 |
+
private List<RecommendationDTO> recommendations;
|
| 21 |
+
|
| 22 |
+
public BusinessMetadataResponseDTO() {}
|
| 23 |
+
|
| 24 |
+
public BusinessMetadataDTO getBusinessMetadata() {
|
| 25 |
+
return businessMetadata;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
public void setBusinessMetadata(BusinessMetadataDTO businessMetadata) {
|
| 29 |
+
this.businessMetadata = businessMetadata;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
public ComplianceClassificationDTO getComplianceClassification() {
|
| 33 |
+
return complianceClassification;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
public void setComplianceClassification(ComplianceClassificationDTO complianceClassification) {
|
| 37 |
+
this.complianceClassification = complianceClassification;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
public MetadataAuditDTO getAuditInfo() {
|
| 41 |
+
return auditInfo;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
public void setAuditInfo(MetadataAuditDTO auditInfo) {
|
| 45 |
+
this.auditInfo = auditInfo;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
public ValidationStatusDTO getValidationStatus() {
|
| 49 |
+
return validationStatus;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
public void setValidationStatus(ValidationStatusDTO validationStatus) {
|
| 53 |
+
this.validationStatus = validationStatus;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
public List<RecommendationDTO> getRecommendations() {
|
| 57 |
+
return recommendations;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
public void setRecommendations(List<RecommendationDTO> recommendations) {
|
| 61 |
+
this.recommendations = recommendations;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
/**
|
| 65 |
+
* Reusing BusinessMetadataDTO from input for consistency
|
| 66 |
+
*/
|
| 67 |
+
public static class BusinessMetadataDTO extends BusinessMetadataInputDTO.BusinessMetadataDTO {
|
| 68 |
+
// Inherits all fields from input DTO for consistency
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
/**
|
| 72 |
+
* Reusing ComplianceClassificationDTO from input for consistency
|
| 73 |
+
*/
|
| 74 |
+
public static class ComplianceClassificationDTO extends BusinessMetadataInputDTO.ComplianceClassificationDTO {
|
| 75 |
+
// Inherits all fields from input DTO for consistency
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
/**
|
| 79 |
+
* Audit information for metadata changes
|
| 80 |
+
*/
|
| 81 |
+
public static class MetadataAuditDTO {
|
| 82 |
+
|
| 83 |
+
private UUID lastUpdatedBy;
|
| 84 |
+
private String lastUpdatedByName;
|
| 85 |
+
private Instant lastUpdatedAt;
|
| 86 |
+
private String updateReason;
|
| 87 |
+
private Integer versionNumber;
|
| 88 |
+
private UUID createdBy;
|
| 89 |
+
private String createdByName;
|
| 90 |
+
private Instant createdAt;
|
| 91 |
+
private List<String> changeHistory; // Summary of recent changes
|
| 92 |
+
|
| 93 |
+
public MetadataAuditDTO() {}
|
| 94 |
+
|
| 95 |
+
public UUID getLastUpdatedBy() {
|
| 96 |
+
return lastUpdatedBy;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
public void setLastUpdatedBy(UUID lastUpdatedBy) {
|
| 100 |
+
this.lastUpdatedBy = lastUpdatedBy;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
public String getLastUpdatedByName() {
|
| 104 |
+
return lastUpdatedByName;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
public void setLastUpdatedByName(String lastUpdatedByName) {
|
| 108 |
+
this.lastUpdatedByName = lastUpdatedByName;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
public Instant getLastUpdatedAt() {
|
| 112 |
+
return lastUpdatedAt;
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
public void setLastUpdatedAt(Instant lastUpdatedAt) {
|
| 116 |
+
this.lastUpdatedAt = lastUpdatedAt;
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
public String getUpdateReason() {
|
| 120 |
+
return updateReason;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
public void setUpdateReason(String updateReason) {
|
| 124 |
+
this.updateReason = updateReason;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
public Integer getVersionNumber() {
|
| 128 |
+
return versionNumber;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
public void setVersionNumber(Integer versionNumber) {
|
| 132 |
+
this.versionNumber = versionNumber;
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
public UUID getCreatedBy() {
|
| 136 |
+
return createdBy;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
public void setCreatedBy(UUID createdBy) {
|
| 140 |
+
this.createdBy = createdBy;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
public String getCreatedByName() {
|
| 144 |
+
return createdByName;
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
public void setCreatedByName(String createdByName) {
|
| 148 |
+
this.createdByName = createdByName;
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
public Instant getCreatedAt() {
|
| 152 |
+
return createdAt;
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
public void setCreatedAt(Instant createdAt) {
|
| 156 |
+
this.createdAt = createdAt;
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
public List<String> getChangeHistory() {
|
| 160 |
+
return changeHistory;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
public void setChangeHistory(List<String> changeHistory) {
|
| 164 |
+
this.changeHistory = changeHistory;
|
| 165 |
+
}
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
/**
|
| 169 |
+
* Validation status for business metadata completeness
|
| 170 |
+
*/
|
| 171 |
+
public static class ValidationStatusDTO {
|
| 172 |
+
|
| 173 |
+
private Boolean isComplete;
|
| 174 |
+
private Double completenessScore; // 0.0 to 1.0
|
| 175 |
+
private List<String> missingFields;
|
| 176 |
+
private List<String> validationWarnings;
|
| 177 |
+
private List<String> validationErrors;
|
| 178 |
+
private String overallStatus; // COMPLETE, PARTIAL, INCOMPLETE, INVALID
|
| 179 |
+
|
| 180 |
+
public ValidationStatusDTO() {}
|
| 181 |
+
|
| 182 |
+
public Boolean getIsComplete() {
|
| 183 |
+
return isComplete;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
public void setIsComplete(Boolean isComplete) {
|
| 187 |
+
this.isComplete = isComplete;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
public Double getCompletenessScore() {
|
| 191 |
+
return completenessScore;
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
public void setCompletenessScore(Double completenessScore) {
|
| 195 |
+
this.completenessScore = completenessScore;
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
public List<String> getMissingFields() {
|
| 199 |
+
return missingFields;
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
public void setMissingFields(List<String> missingFields) {
|
| 203 |
+
this.missingFields = missingFields;
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
public List<String> getValidationWarnings() {
|
| 207 |
+
return validationWarnings;
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
public void setValidationWarnings(List<String> validationWarnings) {
|
| 211 |
+
this.validationWarnings = validationWarnings;
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
public List<String> getValidationErrors() {
|
| 215 |
+
return validationErrors;
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
public void setValidationErrors(List<String> validationErrors) {
|
| 219 |
+
this.validationErrors = validationErrors;
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
public String getOverallStatus() {
|
| 223 |
+
return overallStatus;
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
public void setOverallStatus(String overallStatus) {
|
| 227 |
+
this.overallStatus = overallStatus;
|
| 228 |
+
}
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
/**
|
| 232 |
+
* Recommendations for improving metadata quality
|
| 233 |
+
*/
|
| 234 |
+
public static class RecommendationDTO {
|
| 235 |
+
|
| 236 |
+
private String type; // COMPLETENESS, COMPLIANCE, GOVERNANCE, QUALITY
|
| 237 |
+
private String priority; // HIGH, MEDIUM, LOW
|
| 238 |
+
private String title;
|
| 239 |
+
private String description;
|
| 240 |
+
private String actionRequired;
|
| 241 |
+
private String potentialImpact;
|
| 242 |
+
|
| 243 |
+
public RecommendationDTO() {}
|
| 244 |
+
|
| 245 |
+
public RecommendationDTO(String type, String priority, String title, String description, String actionRequired, String potentialImpact) {
|
| 246 |
+
this.type = type;
|
| 247 |
+
this.priority = priority;
|
| 248 |
+
this.title = title;
|
| 249 |
+
this.description = description;
|
| 250 |
+
this.actionRequired = actionRequired;
|
| 251 |
+
this.potentialImpact = potentialImpact;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
public String getType() {
|
| 255 |
+
return type;
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
public void setType(String type) {
|
| 259 |
+
this.type = type;
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
public String getPriority() {
|
| 263 |
+
return priority;
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
public void setPriority(String priority) {
|
| 267 |
+
this.priority = priority;
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
public String getTitle() {
|
| 271 |
+
return title;
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
public void setTitle(String title) {
|
| 275 |
+
this.title = title;
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
public String getDescription() {
|
| 279 |
+
return description;
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
public void setDescription(String description) {
|
| 283 |
+
this.description = description;
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
public String getActionRequired() {
|
| 287 |
+
return actionRequired;
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
public void setActionRequired(String actionRequired) {
|
| 291 |
+
this.actionRequired = actionRequired;
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
public String getPotentialImpact() {
|
| 295 |
+
return potentialImpact;
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
public void setPotentialImpact(String potentialImpact) {
|
| 299 |
+
this.potentialImpact = potentialImpact;
|
| 300 |
+
}
|
| 301 |
+
}
|
| 302 |
+
}
|
src/main/java/com/dalab/catalog/dto/CatalogFiltersDTO.java
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.List;
|
| 5 |
+
import java.util.Map;
|
| 6 |
+
|
| 7 |
+
import com.fasterxml.jackson.annotation.JsonInclude;
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* DTO for Catalog Faceted Search Filters.
|
| 11 |
+
* Priority 2 endpoint: Provides filter metadata for advanced search and filtering in the catalog UI.
|
| 12 |
+
*/
|
| 13 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 14 |
+
public class CatalogFiltersDTO {
|
| 15 |
+
|
| 16 |
+
private List<FilterCategoryDTO> filterCategories;
|
| 17 |
+
private Map<String, List<String>> quickFilters;
|
| 18 |
+
private SearchOptionsDTO searchOptions;
|
| 19 |
+
private Instant lastUpdated;
|
| 20 |
+
private Integer totalAssets;
|
| 21 |
+
private String catalogVersion;
|
| 22 |
+
|
| 23 |
+
/**
|
| 24 |
+
* Default constructor.
|
| 25 |
+
*/
|
| 26 |
+
public CatalogFiltersDTO() {}
|
| 27 |
+
|
| 28 |
+
/**
|
| 29 |
+
* DTO for filter category information.
|
| 30 |
+
*/
|
| 31 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 32 |
+
public static class FilterCategoryDTO {
|
| 33 |
+
|
| 34 |
+
private String categoryName;
|
| 35 |
+
private String displayName;
|
| 36 |
+
private String categoryType; // SINGLE_SELECT, MULTI_SELECT, RANGE, DATE_RANGE, TEXT_SEARCH
|
| 37 |
+
private List<FilterOptionDTO> options;
|
| 38 |
+
private Integer totalOptions;
|
| 39 |
+
private String description;
|
| 40 |
+
private Boolean isExpandedByDefault;
|
| 41 |
+
private Integer displayOrder;
|
| 42 |
+
private Map<String, Object> metadata;
|
| 43 |
+
|
| 44 |
+
public FilterCategoryDTO() {}
|
| 45 |
+
|
| 46 |
+
public FilterCategoryDTO(String categoryName, String displayName, String categoryType) {
|
| 47 |
+
this.categoryName = categoryName;
|
| 48 |
+
this.displayName = displayName;
|
| 49 |
+
this.categoryType = categoryType;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
// Getters and setters
|
| 53 |
+
public String getCategoryName() { return categoryName; }
|
| 54 |
+
public void setCategoryName(String categoryName) { this.categoryName = categoryName; }
|
| 55 |
+
public String getDisplayName() { return displayName; }
|
| 56 |
+
public void setDisplayName(String displayName) { this.displayName = displayName; }
|
| 57 |
+
public String getCategoryType() { return categoryType; }
|
| 58 |
+
public void setCategoryType(String categoryType) { this.categoryType = categoryType; }
|
| 59 |
+
public List<FilterOptionDTO> getOptions() { return options; }
|
| 60 |
+
public void setOptions(List<FilterOptionDTO> options) { this.options = options; }
|
| 61 |
+
public Integer getTotalOptions() { return totalOptions; }
|
| 62 |
+
public void setTotalOptions(Integer totalOptions) { this.totalOptions = totalOptions; }
|
| 63 |
+
public String getDescription() { return description; }
|
| 64 |
+
public void setDescription(String description) { this.description = description; }
|
| 65 |
+
public Boolean getIsExpandedByDefault() { return isExpandedByDefault; }
|
| 66 |
+
public void setIsExpandedByDefault(Boolean isExpandedByDefault) { this.isExpandedByDefault = isExpandedByDefault; }
|
| 67 |
+
public Integer getDisplayOrder() { return displayOrder; }
|
| 68 |
+
public void setDisplayOrder(Integer displayOrder) { this.displayOrder = displayOrder; }
|
| 69 |
+
public Map<String, Object> getMetadata() { return metadata; }
|
| 70 |
+
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
/**
|
| 74 |
+
* DTO for individual filter option.
|
| 75 |
+
*/
|
| 76 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 77 |
+
public static class FilterOptionDTO {
|
| 78 |
+
|
| 79 |
+
private String value;
|
| 80 |
+
private String displayValue;
|
| 81 |
+
private Integer count;
|
| 82 |
+
private String description;
|
| 83 |
+
private Boolean isSelected;
|
| 84 |
+
private String icon;
|
| 85 |
+
private String color;
|
| 86 |
+
private List<FilterOptionDTO> subOptions;
|
| 87 |
+
private Map<String, Object> metadata;
|
| 88 |
+
|
| 89 |
+
public FilterOptionDTO() {}
|
| 90 |
+
|
| 91 |
+
public FilterOptionDTO(String value, String displayValue, Integer count) {
|
| 92 |
+
this.value = value;
|
| 93 |
+
this.displayValue = displayValue;
|
| 94 |
+
this.count = count;
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
// Getters and setters
|
| 98 |
+
public String getValue() { return value; }
|
| 99 |
+
public void setValue(String value) { this.value = value; }
|
| 100 |
+
public String getDisplayValue() { return displayValue; }
|
| 101 |
+
public void setDisplayValue(String displayValue) { this.displayValue = displayValue; }
|
| 102 |
+
public Integer getCount() { return count; }
|
| 103 |
+
public void setCount(Integer count) { this.count = count; }
|
| 104 |
+
public String getDescription() { return description; }
|
| 105 |
+
public void setDescription(String description) { this.description = description; }
|
| 106 |
+
public Boolean getIsSelected() { return isSelected; }
|
| 107 |
+
public void setIsSelected(Boolean isSelected) { this.isSelected = isSelected; }
|
| 108 |
+
public String getIcon() { return icon; }
|
| 109 |
+
public void setIcon(String icon) { this.icon = icon; }
|
| 110 |
+
public String getColor() { return color; }
|
| 111 |
+
public void setColor(String color) { this.color = color; }
|
| 112 |
+
public List<FilterOptionDTO> getSubOptions() { return subOptions; }
|
| 113 |
+
public void setSubOptions(List<FilterOptionDTO> subOptions) { this.subOptions = subOptions; }
|
| 114 |
+
public Map<String, Object> getMetadata() { return metadata; }
|
| 115 |
+
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
/**
|
| 119 |
+
* DTO for search options and capabilities.
|
| 120 |
+
*/
|
| 121 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 122 |
+
public static class SearchOptionsDTO {
|
| 123 |
+
|
| 124 |
+
private List<String> searchableFields;
|
| 125 |
+
private List<String> sortableFields;
|
| 126 |
+
private List<SortOptionDTO> defaultSortOptions;
|
| 127 |
+
private Integer maxResults;
|
| 128 |
+
private Integer defaultPageSize;
|
| 129 |
+
private List<String> supportedOperators;
|
| 130 |
+
private Boolean supportsFullTextSearch;
|
| 131 |
+
private Boolean supportsWildcards;
|
| 132 |
+
private Boolean supportsFuzzySearch;
|
| 133 |
+
private Map<String, Object> advancedOptions;
|
| 134 |
+
|
| 135 |
+
public SearchOptionsDTO() {}
|
| 136 |
+
|
| 137 |
+
// Getters and setters
|
| 138 |
+
public List<String> getSearchableFields() { return searchableFields; }
|
| 139 |
+
public void setSearchableFields(List<String> searchableFields) { this.searchableFields = searchableFields; }
|
| 140 |
+
public List<String> getSortableFields() { return sortableFields; }
|
| 141 |
+
public void setSortableFields(List<String> sortableFields) { this.sortableFields = sortableFields; }
|
| 142 |
+
public List<SortOptionDTO> getDefaultSortOptions() { return defaultSortOptions; }
|
| 143 |
+
public void setDefaultSortOptions(List<SortOptionDTO> defaultSortOptions) { this.defaultSortOptions = defaultSortOptions; }
|
| 144 |
+
public Integer getMaxResults() { return maxResults; }
|
| 145 |
+
public void setMaxResults(Integer maxResults) { this.maxResults = maxResults; }
|
| 146 |
+
public Integer getDefaultPageSize() { return defaultPageSize; }
|
| 147 |
+
public void setDefaultPageSize(Integer defaultPageSize) { this.defaultPageSize = defaultPageSize; }
|
| 148 |
+
public List<String> getSupportedOperators() { return supportedOperators; }
|
| 149 |
+
public void setSupportedOperators(List<String> supportedOperators) { this.supportedOperators = supportedOperators; }
|
| 150 |
+
public Boolean getSupportsFullTextSearch() { return supportsFullTextSearch; }
|
| 151 |
+
public void setSupportsFullTextSearch(Boolean supportsFullTextSearch) { this.supportsFullTextSearch = supportsFullTextSearch; }
|
| 152 |
+
public Boolean getSupportsWildcards() { return supportsWildcards; }
|
| 153 |
+
public void setSupportsWildcards(Boolean supportsWildcards) { this.supportsWildcards = supportsWildcards; }
|
| 154 |
+
public Boolean getSupportsFuzzySearch() { return supportsFuzzySearch; }
|
| 155 |
+
public void setSupportsFuzzySearch(Boolean supportsFuzzySearch) { this.supportsFuzzySearch = supportsFuzzySearch; }
|
| 156 |
+
public Map<String, Object> getAdvancedOptions() { return advancedOptions; }
|
| 157 |
+
public void setAdvancedOptions(Map<String, Object> advancedOptions) { this.advancedOptions = advancedOptions; }
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
/**
|
| 161 |
+
* DTO for sort option.
|
| 162 |
+
*/
|
| 163 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 164 |
+
public static class SortOptionDTO {
|
| 165 |
+
|
| 166 |
+
private String field;
|
| 167 |
+
private String direction; // ASC, DESC
|
| 168 |
+
private String displayName;
|
| 169 |
+
private Boolean isDefault;
|
| 170 |
+
|
| 171 |
+
public SortOptionDTO() {}
|
| 172 |
+
|
| 173 |
+
public SortOptionDTO(String field, String direction, String displayName) {
|
| 174 |
+
this.field = field;
|
| 175 |
+
this.direction = direction;
|
| 176 |
+
this.displayName = displayName;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
// Getters and setters
|
| 180 |
+
public String getField() { return field; }
|
| 181 |
+
public void setField(String field) { this.field = field; }
|
| 182 |
+
public String getDirection() { return direction; }
|
| 183 |
+
public void setDirection(String direction) { this.direction = direction; }
|
| 184 |
+
public String getDisplayName() { return displayName; }
|
| 185 |
+
public void setDisplayName(String displayName) { this.displayName = displayName; }
|
| 186 |
+
public Boolean getIsDefault() { return isDefault; }
|
| 187 |
+
public void setIsDefault(Boolean isDefault) { this.isDefault = isDefault; }
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
// Main class getters and setters
|
| 191 |
+
public List<FilterCategoryDTO> getFilterCategories() { return filterCategories; }
|
| 192 |
+
public void setFilterCategories(List<FilterCategoryDTO> filterCategories) { this.filterCategories = filterCategories; }
|
| 193 |
+
public Map<String, List<String>> getQuickFilters() { return quickFilters; }
|
| 194 |
+
public void setQuickFilters(Map<String, List<String>> quickFilters) { this.quickFilters = quickFilters; }
|
| 195 |
+
public SearchOptionsDTO getSearchOptions() { return searchOptions; }
|
| 196 |
+
public void setSearchOptions(SearchOptionsDTO searchOptions) { this.searchOptions = searchOptions; }
|
| 197 |
+
public Instant getLastUpdated() { return lastUpdated; }
|
| 198 |
+
public void setLastUpdated(Instant lastUpdated) { this.lastUpdated = lastUpdated; }
|
| 199 |
+
public Integer getTotalAssets() { return totalAssets; }
|
| 200 |
+
public void setTotalAssets(Integer totalAssets) { this.totalAssets = totalAssets; }
|
| 201 |
+
public String getCatalogVersion() { return catalogVersion; }
|
| 202 |
+
public void setCatalogVersion(String catalogVersion) { this.catalogVersion = catalogVersion; }
|
| 203 |
+
}
|
src/main/java/com/dalab/catalog/dto/EnhancedLineageDTO.java
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.List;
|
| 5 |
+
import java.util.Map;
|
| 6 |
+
import java.util.UUID;
|
| 7 |
+
|
| 8 |
+
import com.fasterxml.jackson.annotation.JsonInclude;
|
| 9 |
+
|
| 10 |
+
/**
|
| 11 |
+
* DTO for Enhanced Data Lineage Visualization.
|
| 12 |
+
* Priority 2 endpoint: Provides detailed lineage information with visualization metadata for graph rendering.
|
| 13 |
+
*/
|
| 14 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 15 |
+
public class EnhancedLineageDTO {
|
| 16 |
+
|
| 17 |
+
private UUID assetId;
|
| 18 |
+
private String assetName;
|
| 19 |
+
private LineageGraphDTO lineageGraph;
|
| 20 |
+
private LineageStatisticsDTO statistics;
|
| 21 |
+
private List<LineagePathDTO> criticalPaths;
|
| 22 |
+
private List<LineageIssueDTO> issues;
|
| 23 |
+
private LineageVisualizationMetadataDTO visualizationMetadata;
|
| 24 |
+
private Instant lastUpdated;
|
| 25 |
+
private String lineageVersion;
|
| 26 |
+
private Integer maxDepth;
|
| 27 |
+
|
| 28 |
+
/**
|
| 29 |
+
* Default constructor.
|
| 30 |
+
*/
|
| 31 |
+
public EnhancedLineageDTO() {}
|
| 32 |
+
|
| 33 |
+
/**
|
| 34 |
+
* Constructor with basic information.
|
| 35 |
+
*/
|
| 36 |
+
public EnhancedLineageDTO(UUID assetId, String assetName) {
|
| 37 |
+
this.assetId = assetId;
|
| 38 |
+
this.assetName = assetName;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
/**
|
| 42 |
+
* DTO for lineage graph structure.
|
| 43 |
+
*/
|
| 44 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 45 |
+
public static class LineageGraphDTO {
|
| 46 |
+
|
| 47 |
+
private List<LineageNodeDTO> nodes;
|
| 48 |
+
private List<LineageEdgeDTO> edges;
|
| 49 |
+
private Map<String, Object> layout; // Positioning and layout information for UI
|
| 50 |
+
private Integer totalNodes;
|
| 51 |
+
private Integer totalEdges;
|
| 52 |
+
private Integer maxDepth;
|
| 53 |
+
private List<String> layers; // Hierarchical layer information
|
| 54 |
+
|
| 55 |
+
public LineageGraphDTO() {}
|
| 56 |
+
|
| 57 |
+
// Getters and setters
|
| 58 |
+
public List<LineageNodeDTO> getNodes() { return nodes; }
|
| 59 |
+
public void setNodes(List<LineageNodeDTO> nodes) { this.nodes = nodes; }
|
| 60 |
+
public List<LineageEdgeDTO> getEdges() { return edges; }
|
| 61 |
+
public void setEdges(List<LineageEdgeDTO> edges) { this.edges = edges; }
|
| 62 |
+
public Map<String, Object> getLayout() { return layout; }
|
| 63 |
+
public void setLayout(Map<String, Object> layout) { this.layout = layout; }
|
| 64 |
+
public Integer getTotalNodes() { return totalNodes; }
|
| 65 |
+
public void setTotalNodes(Integer totalNodes) { this.totalNodes = totalNodes; }
|
| 66 |
+
public Integer getTotalEdges() { return totalEdges; }
|
| 67 |
+
public void setTotalEdges(Integer totalEdges) { this.totalEdges = totalEdges; }
|
| 68 |
+
public Integer getMaxDepth() { return maxDepth; }
|
| 69 |
+
public void setMaxDepth(Integer maxDepth) { this.maxDepth = maxDepth; }
|
| 70 |
+
public List<String> getLayers() { return layers; }
|
| 71 |
+
public void setLayers(List<String> layers) { this.layers = layers; }
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
/**
|
| 75 |
+
* DTO for lineage node information.
|
| 76 |
+
*/
|
| 77 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 78 |
+
public static class LineageNodeDTO {
|
| 79 |
+
|
| 80 |
+
private UUID nodeId;
|
| 81 |
+
private String nodeName;
|
| 82 |
+
private String nodeType; // ASSET, TRANSFORMATION, PROCESS, EXTERNAL_SOURCE
|
| 83 |
+
private String assetType; // TABLE, VIEW, FILE, API, etc.
|
| 84 |
+
private String description;
|
| 85 |
+
private Map<String, Object> position; // x, y coordinates for visualization
|
| 86 |
+
private String status; // ACTIVE, INACTIVE, DEPRECATED, UNKNOWN
|
| 87 |
+
private List<String> tags;
|
| 88 |
+
private Map<String, Object> metadata;
|
| 89 |
+
private NodeStatsDTO statistics;
|
| 90 |
+
private String icon; // Icon identifier for UI
|
| 91 |
+
private String color; // Color code for visualization
|
| 92 |
+
private Integer layer; // Hierarchical layer number
|
| 93 |
+
|
| 94 |
+
public LineageNodeDTO() {}
|
| 95 |
+
|
| 96 |
+
public LineageNodeDTO(UUID nodeId, String nodeName, String nodeType) {
|
| 97 |
+
this.nodeId = nodeId;
|
| 98 |
+
this.nodeName = nodeName;
|
| 99 |
+
this.nodeType = nodeType;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
// Getters and setters
|
| 103 |
+
public UUID getNodeId() { return nodeId; }
|
| 104 |
+
public void setNodeId(UUID nodeId) { this.nodeId = nodeId; }
|
| 105 |
+
public String getNodeName() { return nodeName; }
|
| 106 |
+
public void setNodeName(String nodeName) { this.nodeName = nodeName; }
|
| 107 |
+
public String getNodeType() { return nodeType; }
|
| 108 |
+
public void setNodeType(String nodeType) { this.nodeType = nodeType; }
|
| 109 |
+
public String getAssetType() { return assetType; }
|
| 110 |
+
public void setAssetType(String assetType) { this.assetType = assetType; }
|
| 111 |
+
public String getDescription() { return description; }
|
| 112 |
+
public void setDescription(String description) { this.description = description; }
|
| 113 |
+
public Map<String, Object> getPosition() { return position; }
|
| 114 |
+
public void setPosition(Map<String, Object> position) { this.position = position; }
|
| 115 |
+
public String getStatus() { return status; }
|
| 116 |
+
public void setStatus(String status) { this.status = status; }
|
| 117 |
+
public List<String> getTags() { return tags; }
|
| 118 |
+
public void setTags(List<String> tags) { this.tags = tags; }
|
| 119 |
+
public Map<String, Object> getMetadata() { return metadata; }
|
| 120 |
+
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
|
| 121 |
+
public NodeStatsDTO getStatistics() { return statistics; }
|
| 122 |
+
public void setStatistics(NodeStatsDTO statistics) { this.statistics = statistics; }
|
| 123 |
+
public String getIcon() { return icon; }
|
| 124 |
+
public void setIcon(String icon) { this.icon = icon; }
|
| 125 |
+
public String getColor() { return color; }
|
| 126 |
+
public void setColor(String color) { this.color = color; }
|
| 127 |
+
public Integer getLayer() { return layer; }
|
| 128 |
+
public void setLayer(Integer layer) { this.layer = layer; }
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
/**
|
| 132 |
+
* DTO for node statistics.
|
| 133 |
+
*/
|
| 134 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 135 |
+
public static class NodeStatsDTO {
|
| 136 |
+
|
| 137 |
+
private Long recordCount;
|
| 138 |
+
private Long sizeBytes;
|
| 139 |
+
private Instant lastAccessed;
|
| 140 |
+
private Instant lastModified;
|
| 141 |
+
private Integer usageFrequency;
|
| 142 |
+
private String healthStatus; // HEALTHY, WARNING, ERROR, UNKNOWN
|
| 143 |
+
|
| 144 |
+
public NodeStatsDTO() {}
|
| 145 |
+
|
| 146 |
+
// Getters and setters
|
| 147 |
+
public Long getRecordCount() { return recordCount; }
|
| 148 |
+
public void setRecordCount(Long recordCount) { this.recordCount = recordCount; }
|
| 149 |
+
public Long getSizeBytes() { return sizeBytes; }
|
| 150 |
+
public void setSizeBytes(Long sizeBytes) { this.sizeBytes = sizeBytes; }
|
| 151 |
+
public Instant getLastAccessed() { return lastAccessed; }
|
| 152 |
+
public void setLastAccessed(Instant lastAccessed) { this.lastAccessed = lastAccessed; }
|
| 153 |
+
public Instant getLastModified() { return lastModified; }
|
| 154 |
+
public void setLastModified(Instant lastModified) { this.lastModified = lastModified; }
|
| 155 |
+
public Integer getUsageFrequency() { return usageFrequency; }
|
| 156 |
+
public void setUsageFrequency(Integer usageFrequency) { this.usageFrequency = usageFrequency; }
|
| 157 |
+
public String getHealthStatus() { return healthStatus; }
|
| 158 |
+
public void setHealthStatus(String healthStatus) { this.healthStatus = healthStatus; }
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
/**
|
| 162 |
+
* DTO for lineage edge (relationship) information.
|
| 163 |
+
*/
|
| 164 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 165 |
+
public static class LineageEdgeDTO {
|
| 166 |
+
|
| 167 |
+
private String edgeId;
|
| 168 |
+
private UUID sourceNodeId;
|
| 169 |
+
private UUID targetNodeId;
|
| 170 |
+
private String relationshipType; // DERIVED_FROM, WRITES_TO, READS_FROM, TRANSFORMS_TO
|
| 171 |
+
private String transformationType; // ETL, ELT, COPY, AGGREGATE, JOIN, FILTER, etc.
|
| 172 |
+
private List<FieldMappingDTO> fieldMappings;
|
| 173 |
+
private String description;
|
| 174 |
+
private Map<String, Object> transformationLogic;
|
| 175 |
+
private Instant createdAt;
|
| 176 |
+
private Instant lastExecuted;
|
| 177 |
+
private String executionStatus; // SUCCESS, FAILED, RUNNING, SCHEDULED
|
| 178 |
+
private EdgeStatsDTO statistics;
|
| 179 |
+
private String style; // Styling information for visualization (dashed, solid, etc.)
|
| 180 |
+
private String color; // Color for the edge in visualization
|
| 181 |
+
|
| 182 |
+
public LineageEdgeDTO() {}
|
| 183 |
+
|
| 184 |
+
public LineageEdgeDTO(String edgeId, UUID sourceNodeId, UUID targetNodeId, String relationshipType) {
|
| 185 |
+
this.edgeId = edgeId;
|
| 186 |
+
this.sourceNodeId = sourceNodeId;
|
| 187 |
+
this.targetNodeId = targetNodeId;
|
| 188 |
+
this.relationshipType = relationshipType;
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
// Getters and setters
|
| 192 |
+
public String getEdgeId() { return edgeId; }
|
| 193 |
+
public void setEdgeId(String edgeId) { this.edgeId = edgeId; }
|
| 194 |
+
public UUID getSourceNodeId() { return sourceNodeId; }
|
| 195 |
+
public void setSourceNodeId(UUID sourceNodeId) { this.sourceNodeId = sourceNodeId; }
|
| 196 |
+
public UUID getTargetNodeId() { return targetNodeId; }
|
| 197 |
+
public void setTargetNodeId(UUID targetNodeId) { this.targetNodeId = targetNodeId; }
|
| 198 |
+
public String getRelationshipType() { return relationshipType; }
|
| 199 |
+
public void setRelationshipType(String relationshipType) { this.relationshipType = relationshipType; }
|
| 200 |
+
public String getTransformationType() { return transformationType; }
|
| 201 |
+
public void setTransformationType(String transformationType) { this.transformationType = transformationType; }
|
| 202 |
+
public List<FieldMappingDTO> getFieldMappings() { return fieldMappings; }
|
| 203 |
+
public void setFieldMappings(List<FieldMappingDTO> fieldMappings) { this.fieldMappings = fieldMappings; }
|
| 204 |
+
public String getDescription() { return description; }
|
| 205 |
+
public void setDescription(String description) { this.description = description; }
|
| 206 |
+
public Map<String, Object> getTransformationLogic() { return transformationLogic; }
|
| 207 |
+
public void setTransformationLogic(Map<String, Object> transformationLogic) { this.transformationLogic = transformationLogic; }
|
| 208 |
+
public Instant getCreatedAt() { return createdAt; }
|
| 209 |
+
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
| 210 |
+
public Instant getLastExecuted() { return lastExecuted; }
|
| 211 |
+
public void setLastExecuted(Instant lastExecuted) { this.lastExecuted = lastExecuted; }
|
| 212 |
+
public String getExecutionStatus() { return executionStatus; }
|
| 213 |
+
public void setExecutionStatus(String executionStatus) { this.executionStatus = executionStatus; }
|
| 214 |
+
public EdgeStatsDTO getStatistics() { return statistics; }
|
| 215 |
+
public void setStatistics(EdgeStatsDTO statistics) { this.statistics = statistics; }
|
| 216 |
+
public String getStyle() { return style; }
|
| 217 |
+
public void setStyle(String style) { this.style = style; }
|
| 218 |
+
public String getColor() { return color; }
|
| 219 |
+
public void setColor(String color) { this.color = color; }
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
/**
|
| 223 |
+
* DTO for field mapping information.
|
| 224 |
+
*/
|
| 225 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 226 |
+
public static class FieldMappingDTO {
|
| 227 |
+
|
| 228 |
+
private String sourceField;
|
| 229 |
+
private String targetField;
|
| 230 |
+
private String mappingType; // DIRECT, TRANSFORMED, CALCULATED, DERIVED
|
| 231 |
+
private String transformation;
|
| 232 |
+
private Double confidence; // 0.0 to 1.0
|
| 233 |
+
|
| 234 |
+
public FieldMappingDTO() {}
|
| 235 |
+
|
| 236 |
+
public FieldMappingDTO(String sourceField, String targetField, String mappingType) {
|
| 237 |
+
this.sourceField = sourceField;
|
| 238 |
+
this.targetField = targetField;
|
| 239 |
+
this.mappingType = mappingType;
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
// Getters and setters
|
| 243 |
+
public String getSourceField() { return sourceField; }
|
| 244 |
+
public void setSourceField(String sourceField) { this.sourceField = sourceField; }
|
| 245 |
+
public String getTargetField() { return targetField; }
|
| 246 |
+
public void setTargetField(String targetField) { this.targetField = targetField; }
|
| 247 |
+
public String getMappingType() { return mappingType; }
|
| 248 |
+
public void setMappingType(String mappingType) { this.mappingType = mappingType; }
|
| 249 |
+
public String getTransformation() { return transformation; }
|
| 250 |
+
public void setTransformation(String transformation) { this.transformation = transformation; }
|
| 251 |
+
public Double getConfidence() { return confidence; }
|
| 252 |
+
public void setConfidence(Double confidence) { this.confidence = confidence; }
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
/**
|
| 256 |
+
* DTO for edge statistics.
|
| 257 |
+
*/
|
| 258 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 259 |
+
public static class EdgeStatsDTO {
|
| 260 |
+
|
| 261 |
+
private Long recordsProcessed;
|
| 262 |
+
private Double averageProcessingTime; // in milliseconds
|
| 263 |
+
private Integer executionCount;
|
| 264 |
+
private Instant lastSuccessfulExecution;
|
| 265 |
+
private Integer failureCount;
|
| 266 |
+
private String reliability; // HIGH, MEDIUM, LOW
|
| 267 |
+
|
| 268 |
+
public EdgeStatsDTO() {}
|
| 269 |
+
|
| 270 |
+
// Getters and setters
|
| 271 |
+
public Long getRecordsProcessed() { return recordsProcessed; }
|
| 272 |
+
public void setRecordsProcessed(Long recordsProcessed) { this.recordsProcessed = recordsProcessed; }
|
| 273 |
+
public Double getAverageProcessingTime() { return averageProcessingTime; }
|
| 274 |
+
public void setAverageProcessingTime(Double averageProcessingTime) { this.averageProcessingTime = averageProcessingTime; }
|
| 275 |
+
public Integer getExecutionCount() { return executionCount; }
|
| 276 |
+
public void setExecutionCount(Integer executionCount) { this.executionCount = executionCount; }
|
| 277 |
+
public Instant getLastSuccessfulExecution() { return lastSuccessfulExecution; }
|
| 278 |
+
public void setLastSuccessfulExecution(Instant lastSuccessfulExecution) { this.lastSuccessfulExecution = lastSuccessfulExecution; }
|
| 279 |
+
public Integer getFailureCount() { return failureCount; }
|
| 280 |
+
public void setFailureCount(Integer failureCount) { this.failureCount = failureCount; }
|
| 281 |
+
public String getReliability() { return reliability; }
|
| 282 |
+
public void setReliability(String reliability) { this.reliability = reliability; }
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
/**
|
| 286 |
+
* DTO for lineage statistics.
|
| 287 |
+
*/
|
| 288 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 289 |
+
public static class LineageStatisticsDTO {
|
| 290 |
+
|
| 291 |
+
private Integer totalUpstreamAssets;
|
| 292 |
+
private Integer totalDownstreamAssets;
|
| 293 |
+
private Integer totalTransformations;
|
| 294 |
+
private Integer directDependencies;
|
| 295 |
+
private Integer indirectDependencies;
|
| 296 |
+
private Double lineageCompleteness; // 0.0 to 100.0
|
| 297 |
+
private String lineageHealth; // HEALTHY, PARTIALLY_TRACKED, INCOMPLETE, BROKEN
|
| 298 |
+
private Map<String, Integer> assetTypeBreakdown;
|
| 299 |
+
private Map<String, Integer> transformationTypeBreakdown;
|
| 300 |
+
|
| 301 |
+
public LineageStatisticsDTO() {}
|
| 302 |
+
|
| 303 |
+
// Getters and setters
|
| 304 |
+
public Integer getTotalUpstreamAssets() { return totalUpstreamAssets; }
|
| 305 |
+
public void setTotalUpstreamAssets(Integer totalUpstreamAssets) { this.totalUpstreamAssets = totalUpstreamAssets; }
|
| 306 |
+
public Integer getTotalDownstreamAssets() { return totalDownstreamAssets; }
|
| 307 |
+
public void setTotalDownstreamAssets(Integer totalDownstreamAssets) { this.totalDownstreamAssets = totalDownstreamAssets; }
|
| 308 |
+
public Integer getTotalTransformations() { return totalTransformations; }
|
| 309 |
+
public void setTotalTransformations(Integer totalTransformations) { this.totalTransformations = totalTransformations; }
|
| 310 |
+
public Integer getDirectDependencies() { return directDependencies; }
|
| 311 |
+
public void setDirectDependencies(Integer directDependencies) { this.directDependencies = directDependencies; }
|
| 312 |
+
public Integer getIndirectDependencies() { return indirectDependencies; }
|
| 313 |
+
public void setIndirectDependencies(Integer indirectDependencies) { this.indirectDependencies = indirectDependencies; }
|
| 314 |
+
public Double getLineageCompleteness() { return lineageCompleteness; }
|
| 315 |
+
public void setLineageCompleteness(Double lineageCompleteness) { this.lineageCompleteness = lineageCompleteness; }
|
| 316 |
+
public String getLineageHealth() { return lineageHealth; }
|
| 317 |
+
public void setLineageHealth(String lineageHealth) { this.lineageHealth = lineageHealth; }
|
| 318 |
+
public Map<String, Integer> getAssetTypeBreakdown() { return assetTypeBreakdown; }
|
| 319 |
+
public void setAssetTypeBreakdown(Map<String, Integer> assetTypeBreakdown) { this.assetTypeBreakdown = assetTypeBreakdown; }
|
| 320 |
+
public Map<String, Integer> getTransformationTypeBreakdown() { return transformationTypeBreakdown; }
|
| 321 |
+
public void setTransformationTypeBreakdown(Map<String, Integer> transformationTypeBreakdown) { this.transformationTypeBreakdown = transformationTypeBreakdown; }
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
/**
|
| 325 |
+
* DTO for critical lineage paths.
|
| 326 |
+
*/
|
| 327 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 328 |
+
public static class LineagePathDTO {
|
| 329 |
+
|
| 330 |
+
private String pathId;
|
| 331 |
+
private String pathType; // CRITICAL_BUSINESS_PATH, HIGH_VOLUME_PATH, SENSITIVE_DATA_PATH
|
| 332 |
+
private List<UUID> nodeSequence;
|
| 333 |
+
private String description;
|
| 334 |
+
private String businessImpact; // HIGH, MEDIUM, LOW
|
| 335 |
+
private List<String> businessProcesses;
|
| 336 |
+
private String riskLevel; // CRITICAL, HIGH, MEDIUM, LOW
|
| 337 |
+
private Map<String, Object> metadata;
|
| 338 |
+
|
| 339 |
+
public LineagePathDTO() {}
|
| 340 |
+
|
| 341 |
+
public LineagePathDTO(String pathId, String pathType, List<UUID> nodeSequence) {
|
| 342 |
+
this.pathId = pathId;
|
| 343 |
+
this.pathType = pathType;
|
| 344 |
+
this.nodeSequence = nodeSequence;
|
| 345 |
+
}
|
| 346 |
+
|
| 347 |
+
// Getters and setters
|
| 348 |
+
public String getPathId() { return pathId; }
|
| 349 |
+
public void setPathId(String pathId) { this.pathId = pathId; }
|
| 350 |
+
public String getPathType() { return pathType; }
|
| 351 |
+
public void setPathType(String pathType) { this.pathType = pathType; }
|
| 352 |
+
public List<UUID> getNodeSequence() { return nodeSequence; }
|
| 353 |
+
public void setNodeSequence(List<UUID> nodeSequence) { this.nodeSequence = nodeSequence; }
|
| 354 |
+
public String getDescription() { return description; }
|
| 355 |
+
public void setDescription(String description) { this.description = description; }
|
| 356 |
+
public String getBusinessImpact() { return businessImpact; }
|
| 357 |
+
public void setBusinessImpact(String businessImpact) { this.businessImpact = businessImpact; }
|
| 358 |
+
public List<String> getBusinessProcesses() { return businessProcesses; }
|
| 359 |
+
public void setBusinessProcesses(List<String> businessProcesses) { this.businessProcesses = businessProcesses; }
|
| 360 |
+
public String getRiskLevel() { return riskLevel; }
|
| 361 |
+
public void setRiskLevel(String riskLevel) { this.riskLevel = riskLevel; }
|
| 362 |
+
public Map<String, Object> getMetadata() { return metadata; }
|
| 363 |
+
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
/**
|
| 367 |
+
* DTO for lineage issues.
|
| 368 |
+
*/
|
| 369 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 370 |
+
public static class LineageIssueDTO {
|
| 371 |
+
|
| 372 |
+
private String issueId;
|
| 373 |
+
private String issueType; // BROKEN_LINEAGE, MISSING_METADATA, STALE_CONNECTION, CIRCULAR_DEPENDENCY
|
| 374 |
+
private String severity; // CRITICAL, HIGH, MEDIUM, LOW
|
| 375 |
+
private String description;
|
| 376 |
+
private List<UUID> affectedNodes;
|
| 377 |
+
private String resolutionSuggestion;
|
| 378 |
+
private Instant detectedAt;
|
| 379 |
+
private String status; // OPEN, IN_PROGRESS, RESOLVED
|
| 380 |
+
|
| 381 |
+
public LineageIssueDTO() {}
|
| 382 |
+
|
| 383 |
+
public LineageIssueDTO(String issueId, String issueType, String severity) {
|
| 384 |
+
this.issueId = issueId;
|
| 385 |
+
this.issueType = issueType;
|
| 386 |
+
this.severity = severity;
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
// Getters and setters
|
| 390 |
+
public String getIssueId() { return issueId; }
|
| 391 |
+
public void setIssueId(String issueId) { this.issueId = issueId; }
|
| 392 |
+
public String getIssueType() { return issueType; }
|
| 393 |
+
public void setIssueType(String issueType) { this.issueType = issueType; }
|
| 394 |
+
public String getSeverity() { return severity; }
|
| 395 |
+
public void setSeverity(String severity) { this.severity = severity; }
|
| 396 |
+
public String getDescription() { return description; }
|
| 397 |
+
public void setDescription(String description) { this.description = description; }
|
| 398 |
+
public List<UUID> getAffectedNodes() { return affectedNodes; }
|
| 399 |
+
public void setAffectedNodes(List<UUID> affectedNodes) { this.affectedNodes = affectedNodes; }
|
| 400 |
+
public String getResolutionSuggestion() { return resolutionSuggestion; }
|
| 401 |
+
public void setResolutionSuggestion(String resolutionSuggestion) { this.resolutionSuggestion = resolutionSuggestion; }
|
| 402 |
+
public Instant getDetectedAt() { return detectedAt; }
|
| 403 |
+
public void setDetectedAt(Instant detectedAt) { this.detectedAt = detectedAt; }
|
| 404 |
+
public String getStatus() { return status; }
|
| 405 |
+
public void setStatus(String status) { this.status = status; }
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
/**
|
| 409 |
+
* DTO for visualization metadata.
|
| 410 |
+
*/
|
| 411 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 412 |
+
public static class LineageVisualizationMetadataDTO {
|
| 413 |
+
|
| 414 |
+
private String layoutType; // HIERARCHICAL, FORCE_DIRECTED, CIRCULAR, TREE
|
| 415 |
+
private Map<String, Object> layoutConfiguration;
|
| 416 |
+
private Map<String, String> colorScheme; // nodeType -> color
|
| 417 |
+
private Map<String, String> iconMapping; // nodeType -> icon
|
| 418 |
+
private List<String> availableFilters;
|
| 419 |
+
private Map<String, Object> defaultViewport; // Initial zoom and pan settings
|
| 420 |
+
private Boolean supportsInteractiveEditing;
|
| 421 |
+
|
| 422 |
+
public LineageVisualizationMetadataDTO() {}
|
| 423 |
+
|
| 424 |
+
// Getters and setters
|
| 425 |
+
public String getLayoutType() { return layoutType; }
|
| 426 |
+
public void setLayoutType(String layoutType) { this.layoutType = layoutType; }
|
| 427 |
+
public Map<String, Object> getLayoutConfiguration() { return layoutConfiguration; }
|
| 428 |
+
public void setLayoutConfiguration(Map<String, Object> layoutConfiguration) { this.layoutConfiguration = layoutConfiguration; }
|
| 429 |
+
public Map<String, String> getColorScheme() { return colorScheme; }
|
| 430 |
+
public void setColorScheme(Map<String, String> colorScheme) { this.colorScheme = colorScheme; }
|
| 431 |
+
public Map<String, String> getIconMapping() { return iconMapping; }
|
| 432 |
+
public void setIconMapping(Map<String, String> iconMapping) { this.iconMapping = iconMapping; }
|
| 433 |
+
public List<String> getAvailableFilters() { return availableFilters; }
|
| 434 |
+
public void setAvailableFilters(List<String> availableFilters) { this.availableFilters = availableFilters; }
|
| 435 |
+
public Map<String, Object> getDefaultViewport() { return defaultViewport; }
|
| 436 |
+
public void setDefaultViewport(Map<String, Object> defaultViewport) { this.defaultViewport = defaultViewport; }
|
| 437 |
+
public Boolean getSupportsInteractiveEditing() { return supportsInteractiveEditing; }
|
| 438 |
+
public void setSupportsInteractiveEditing(Boolean supportsInteractiveEditing) { this.supportsInteractiveEditing = supportsInteractiveEditing; }
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
// Main class getters and setters
|
| 442 |
+
public UUID getAssetId() { return assetId; }
|
| 443 |
+
public void setAssetId(UUID assetId) { this.assetId = assetId; }
|
| 444 |
+
public String getAssetName() { return assetName; }
|
| 445 |
+
public void setAssetName(String assetName) { this.assetName = assetName; }
|
| 446 |
+
public LineageGraphDTO getLineageGraph() { return lineageGraph; }
|
| 447 |
+
public void setLineageGraph(LineageGraphDTO lineageGraph) { this.lineageGraph = lineageGraph; }
|
| 448 |
+
public LineageStatisticsDTO getStatistics() { return statistics; }
|
| 449 |
+
public void setStatistics(LineageStatisticsDTO statistics) { this.statistics = statistics; }
|
| 450 |
+
public List<LineagePathDTO> getCriticalPaths() { return criticalPaths; }
|
| 451 |
+
public void setCriticalPaths(List<LineagePathDTO> criticalPaths) { this.criticalPaths = criticalPaths; }
|
| 452 |
+
public List<LineageIssueDTO> getIssues() { return issues; }
|
| 453 |
+
public void setIssues(List<LineageIssueDTO> issues) { this.issues = issues; }
|
| 454 |
+
public LineageVisualizationMetadataDTO getVisualizationMetadata() { return visualizationMetadata; }
|
| 455 |
+
public void setVisualizationMetadata(LineageVisualizationMetadataDTO visualizationMetadata) { this.visualizationMetadata = visualizationMetadata; }
|
| 456 |
+
public Instant getLastUpdated() { return lastUpdated; }
|
| 457 |
+
public void setLastUpdated(Instant lastUpdated) { this.lastUpdated = lastUpdated; }
|
| 458 |
+
public String getLineageVersion() { return lineageVersion; }
|
| 459 |
+
public void setLineageVersion(String lineageVersion) { this.lineageVersion = lineageVersion; }
|
| 460 |
+
public Integer getMaxDepth() { return maxDepth; }
|
| 461 |
+
public void setMaxDepth(Integer maxDepth) { this.maxDepth = maxDepth; }
|
| 462 |
+
}
|
src/main/java/com/dalab/catalog/dto/LabelAssignmentInputDTO.java
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.util.UUID;
|
| 4 |
+
|
| 5 |
+
// DTO for assigning a single label
|
| 6 |
+
public class LabelAssignmentInputDTO {
|
| 7 |
+
// Allow providing either labelName or labelId to identify the label
|
| 8 |
+
private String labelName;
|
| 9 |
+
private UUID labelId;
|
| 10 |
+
|
| 11 |
+
private Double confidenceScore; // Optional
|
| 12 |
+
private String source; // Optional, e.g., "USER_PROVIDED", "RULE_BASED"
|
| 13 |
+
|
| 14 |
+
// Getters and Setters
|
| 15 |
+
public String getLabelName() {
|
| 16 |
+
return labelName;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
public void setLabelName(String labelName) {
|
| 20 |
+
this.labelName = labelName;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
public UUID getLabelId() {
|
| 24 |
+
return labelId;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
public void setLabelId(UUID labelId) {
|
| 28 |
+
this.labelId = labelId;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
public Double getConfidenceScore() {
|
| 32 |
+
return confidenceScore;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
public void setConfidenceScore(Double confidenceScore) {
|
| 36 |
+
this.confidenceScore = confidenceScore;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
public String getSource() {
|
| 40 |
+
return source;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
public void setSource(String source) {
|
| 44 |
+
this.source = source;
|
| 45 |
+
}
|
| 46 |
+
}
|
src/main/java/com/dalab/catalog/dto/LabelOutputDTO.java
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.UUID;
|
| 5 |
+
|
| 6 |
+
import com.fasterxml.jackson.annotation.JsonInclude;
|
| 7 |
+
|
| 8 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 9 |
+
public class LabelOutputDTO {
|
| 10 |
+
// Fields from Label entity
|
| 11 |
+
private UUID labelId;
|
| 12 |
+
private String labelName;
|
| 13 |
+
private String labelCategory;
|
| 14 |
+
private String description;
|
| 15 |
+
// private UUID labelCreatedByUserId; // If needed to distinguish from assignment user
|
| 16 |
+
// private Instant labelCreatedAt;
|
| 17 |
+
// private Instant labelUpdatedAt;
|
| 18 |
+
|
| 19 |
+
// Fields from AssetLabelAssignment entity
|
| 20 |
+
private UUID assignedByUserId; // User who assigned this label to the asset
|
| 21 |
+
private Instant assignedAt; // When this specific assignment was made
|
| 22 |
+
private Double confidenceScore;
|
| 23 |
+
private String source; // Source of this specific assignment
|
| 24 |
+
|
| 25 |
+
public LabelOutputDTO() {}
|
| 26 |
+
|
| 27 |
+
// Getters and Setters
|
| 28 |
+
public UUID getLabelId() {
|
| 29 |
+
return labelId;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
public void setLabelId(UUID labelId) {
|
| 33 |
+
this.labelId = labelId;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
public String getLabelName() {
|
| 37 |
+
return labelName;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
public void setLabelName(String labelName) {
|
| 41 |
+
this.labelName = labelName;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
public String getLabelCategory() {
|
| 45 |
+
return labelCategory;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
public void setLabelCategory(String labelCategory) {
|
| 49 |
+
this.labelCategory = labelCategory;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
public String getDescription() {
|
| 53 |
+
return description;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
public void setDescription(String description) {
|
| 57 |
+
this.description = description;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
public UUID getAssignedByUserId() {
|
| 61 |
+
return assignedByUserId;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
public void setAssignedByUserId(UUID assignedByUserId) {
|
| 65 |
+
this.assignedByUserId = assignedByUserId;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
public Instant getAssignedAt() {
|
| 69 |
+
return assignedAt;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
public void setAssignedAt(Instant assignedAt) {
|
| 73 |
+
this.assignedAt = assignedAt;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
public Double getConfidenceScore() {
|
| 77 |
+
return confidenceScore;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
public void setConfidenceScore(Double confidenceScore) {
|
| 81 |
+
this.confidenceScore = confidenceScore;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
public String getSource() {
|
| 85 |
+
return source;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
public void setSource(String source) {
|
| 89 |
+
this.source = source;
|
| 90 |
+
}
|
| 91 |
+
}
|
src/main/java/com/dalab/catalog/dto/LineageAssetDTO.java
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.util.UUID;
|
| 4 |
+
|
| 5 |
+
public class LineageAssetDTO {
|
| 6 |
+
private UUID assetId;
|
| 7 |
+
private String assetName;
|
| 8 |
+
private String assetType;
|
| 9 |
+
private String cloudProvider;
|
| 10 |
+
// Add relationshipType if showing direct relationship context in this DTO
|
| 11 |
+
// private String relationshipType;
|
| 12 |
+
|
| 13 |
+
public LineageAssetDTO() {}
|
| 14 |
+
|
| 15 |
+
public LineageAssetDTO(UUID assetId, String assetName, String assetType, String cloudProvider) {
|
| 16 |
+
this.assetId = assetId;
|
| 17 |
+
this.assetName = assetName;
|
| 18 |
+
this.assetType = assetType;
|
| 19 |
+
this.cloudProvider = cloudProvider;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
// Getters and Setters
|
| 23 |
+
public UUID getAssetId() {
|
| 24 |
+
return assetId;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
public void setAssetId(UUID assetId) {
|
| 28 |
+
this.assetId = assetId;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
public String getAssetName() {
|
| 32 |
+
return assetName;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
public void setAssetName(String assetName) {
|
| 36 |
+
this.assetName = assetName;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
public String getAssetType() {
|
| 40 |
+
return assetType;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
public void setAssetType(String assetType) {
|
| 44 |
+
this.assetType = assetType;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
public String getCloudProvider() {
|
| 48 |
+
return cloudProvider;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
public void setCloudProvider(String cloudProvider) {
|
| 52 |
+
this.cloudProvider = cloudProvider;
|
| 53 |
+
}
|
| 54 |
+
}
|
src/main/java/com/dalab/catalog/dto/LineageResponseDTO.java
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.util.List;
|
| 4 |
+
|
| 5 |
+
public class LineageResponseDTO {
|
| 6 |
+
private List<LineageAssetDTO> upstreamAssets;
|
| 7 |
+
private List<LineageAssetDTO> downstreamAssets;
|
| 8 |
+
|
| 9 |
+
public LineageResponseDTO() {}
|
| 10 |
+
|
| 11 |
+
public LineageResponseDTO(List<LineageAssetDTO> upstreamAssets, List<LineageAssetDTO> downstreamAssets) {
|
| 12 |
+
this.upstreamAssets = upstreamAssets;
|
| 13 |
+
this.downstreamAssets = downstreamAssets;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
// Getters and Setters
|
| 17 |
+
public List<LineageAssetDTO> getUpstreamAssets() {
|
| 18 |
+
return upstreamAssets;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
public void setUpstreamAssets(List<LineageAssetDTO> upstreamAssets) {
|
| 22 |
+
this.upstreamAssets = upstreamAssets;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
public List<LineageAssetDTO> getDownstreamAssets() {
|
| 26 |
+
return downstreamAssets;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
public void setDownstreamAssets(List<LineageAssetDTO> downstreamAssets) {
|
| 30 |
+
this.downstreamAssets = downstreamAssets;
|
| 31 |
+
}
|
| 32 |
+
}
|
src/main/java/com/dalab/catalog/dto/TaxonomyLabelDTO.java
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.UUID;
|
| 5 |
+
|
| 6 |
+
import com.fasterxml.jackson.annotation.JsonInclude;
|
| 7 |
+
|
| 8 |
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
| 9 |
+
public class TaxonomyLabelDTO {
|
| 10 |
+
private UUID labelId;
|
| 11 |
+
private String labelName;
|
| 12 |
+
private String labelCategory;
|
| 13 |
+
private String description;
|
| 14 |
+
private UUID createdByUserId; // User who created the label definition
|
| 15 |
+
private Instant createdAt;
|
| 16 |
+
private Instant updatedAt;
|
| 17 |
+
|
| 18 |
+
public TaxonomyLabelDTO() {}
|
| 19 |
+
|
| 20 |
+
// Getters and Setters
|
| 21 |
+
public UUID getLabelId() {
|
| 22 |
+
return labelId;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
public void setLabelId(UUID labelId) {
|
| 26 |
+
this.labelId = labelId;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
public String getLabelName() {
|
| 30 |
+
return labelName;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
public void setLabelName(String labelName) {
|
| 34 |
+
this.labelName = labelName;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
public String getLabelCategory() {
|
| 38 |
+
return labelCategory;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
public void setLabelCategory(String labelCategory) {
|
| 42 |
+
this.labelCategory = labelCategory;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
public String getDescription() {
|
| 46 |
+
return description;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
public void setDescription(String description) {
|
| 50 |
+
this.description = description;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
public UUID getCreatedByUserId() {
|
| 54 |
+
return createdByUserId;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
public void setCreatedByUserId(UUID createdByUserId) {
|
| 58 |
+
this.createdByUserId = createdByUserId;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
public Instant getCreatedAt() {
|
| 62 |
+
return createdAt;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
public void setCreatedAt(Instant createdAt) {
|
| 66 |
+
this.createdAt = createdAt;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
public Instant getUpdatedAt() {
|
| 70 |
+
return updatedAt;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
public void setUpdatedAt(Instant updatedAt) {
|
| 74 |
+
this.updatedAt = updatedAt;
|
| 75 |
+
}
|
| 76 |
+
}
|
src/main/java/com/dalab/catalog/dto/TaxonomyLabelsResponseDTO.java
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.dto;
|
| 2 |
+
|
| 3 |
+
import java.util.List;
|
| 4 |
+
|
| 5 |
+
public class TaxonomyLabelsResponseDTO {
|
| 6 |
+
private List<TaxonomyLabelDTO> labels;
|
| 7 |
+
|
| 8 |
+
public TaxonomyLabelsResponseDTO() {}
|
| 9 |
+
|
| 10 |
+
public TaxonomyLabelsResponseDTO(List<TaxonomyLabelDTO> labels) {
|
| 11 |
+
this.labels = labels;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
public List<TaxonomyLabelDTO> getLabels() {
|
| 15 |
+
return labels;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
public void setLabels(List<TaxonomyLabelDTO> labels) {
|
| 19 |
+
this.labels = labels;
|
| 20 |
+
}
|
| 21 |
+
}
|
src/main/java/com/dalab/catalog/mapper/AssetMapper.java
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.mapper;
|
| 2 |
+
|
| 3 |
+
import java.util.Collections;
|
| 4 |
+
import java.util.List;
|
| 5 |
+
import java.util.stream.Collectors;
|
| 6 |
+
|
| 7 |
+
import org.springframework.beans.factory.annotation.Autowired;
|
| 8 |
+
import org.springframework.stereotype.Component;
|
| 9 |
+
|
| 10 |
+
import com.dalab.catalog.dto.AssetOutputDTO;
|
| 11 |
+
import com.dalab.common.model.entity.Asset;
|
| 12 |
+
import com.dalab.common.repository.IAssetLabelAssignmentRepository;
|
| 13 |
+
|
| 14 |
+
@Component
|
| 15 |
+
public class AssetMapper {
|
| 16 |
+
|
| 17 |
+
private final IAssetLabelAssignmentRepository assetLabelAssignmentRepository;
|
| 18 |
+
|
| 19 |
+
@Autowired
|
| 20 |
+
public AssetMapper(IAssetLabelAssignmentRepository assetLabelAssignmentRepository) {
|
| 21 |
+
this.assetLabelAssignmentRepository = assetLabelAssignmentRepository;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
public AssetOutputDTO toAssetOutputDTO(Asset asset) {
|
| 25 |
+
if (asset == null) {
|
| 26 |
+
return null;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
AssetOutputDTO dto = new AssetOutputDTO();
|
| 30 |
+
dto.setAssetId(asset.getAssetId());
|
| 31 |
+
dto.setAssetName(asset.getAssetName());
|
| 32 |
+
dto.setCloudConnectionId(asset.getCloudConnectionId());
|
| 33 |
+
dto.setCloudProvider(asset.getCloudProvider());
|
| 34 |
+
dto.setAssetType(asset.getAssetType());
|
| 35 |
+
dto.setNativeAssetId(asset.getNativeAssetId());
|
| 36 |
+
dto.setRegion(asset.getRegion());
|
| 37 |
+
dto.setDetailsJson(asset.getDetailsJson());
|
| 38 |
+
dto.setDiscoveredAt(asset.getDiscoveredAt());
|
| 39 |
+
dto.setLastScannedAt(asset.getLastScannedAt());
|
| 40 |
+
dto.setCreatedByUserId(asset.getCreatedByUserId());
|
| 41 |
+
dto.setCreatedAt(asset.getCreatedTs());
|
| 42 |
+
dto.setUpdatedAt(asset.getLastModifiedTs());
|
| 43 |
+
dto.setBusinessMetadataJson(asset.getBusinessMetadataJson());
|
| 44 |
+
|
| 45 |
+
// TODO: Implement label names retrieval
|
| 46 |
+
// For now, return empty list to avoid compilation errors
|
| 47 |
+
dto.setLabels(Collections.emptyList());
|
| 48 |
+
|
| 49 |
+
return dto;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
public List<AssetOutputDTO> toAssetOutputDTOList(List<Asset> assets) {
|
| 53 |
+
if (assets == null) {
|
| 54 |
+
return Collections.emptyList();
|
| 55 |
+
}
|
| 56 |
+
return assets.stream()
|
| 57 |
+
.map(this::toAssetOutputDTO)
|
| 58 |
+
.collect(Collectors.toList());
|
| 59 |
+
}
|
| 60 |
+
}
|
src/main/java/com/dalab/catalog/mapper/LabelTaxonomyMapper.java
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.mapper;
|
| 2 |
+
|
| 3 |
+
import org.springframework.stereotype.Component;
|
| 4 |
+
|
| 5 |
+
import com.dalab.catalog.dto.TaxonomyLabelDTO;
|
| 6 |
+
import com.dalab.common.model.entity.Label;
|
| 7 |
+
|
| 8 |
+
@Component
|
| 9 |
+
public class LabelTaxonomyMapper {
|
| 10 |
+
|
| 11 |
+
public TaxonomyLabelDTO toTaxonomyLabelDTO(Label label) {
|
| 12 |
+
if (label == null) {
|
| 13 |
+
return null;
|
| 14 |
+
}
|
| 15 |
+
TaxonomyLabelDTO dto = new TaxonomyLabelDTO();
|
| 16 |
+
dto.setLabelId(label.getLabelId());
|
| 17 |
+
dto.setLabelName(label.getLabelName());
|
| 18 |
+
dto.setLabelCategory(label.getLabelCategory());
|
| 19 |
+
dto.setDescription(label.getDescription());
|
| 20 |
+
dto.setCreatedByUserId(label.getCreatedByUserId());
|
| 21 |
+
dto.setCreatedAt(label.getCreatedTs());
|
| 22 |
+
dto.setUpdatedAt(label.getLastModifiedTs());
|
| 23 |
+
return dto;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
public Label toLabelEntity(TaxonomyLabelDTO dto) {
|
| 27 |
+
if (dto == null) {
|
| 28 |
+
return null;
|
| 29 |
+
}
|
| 30 |
+
Label label = new Label();
|
| 31 |
+
// label.setLabelId(dto.getLabelId()); // ID is usually set on creation or fetched
|
| 32 |
+
label.setLabelName(dto.getLabelName());
|
| 33 |
+
label.setLabelCategory(dto.getLabelCategory());
|
| 34 |
+
label.setDescription(dto.getDescription());
|
| 35 |
+
// createdByUserId, createdAt, updatedAt are often set by system or @PrePersist/@PreUpdate
|
| 36 |
+
return label;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
public void updateLabelEntityFromDto(Label label, TaxonomyLabelDTO dto) {
|
| 40 |
+
if (dto == null || label == null) {
|
| 41 |
+
return;
|
| 42 |
+
}
|
| 43 |
+
if (dto.getLabelName() != null) {
|
| 44 |
+
label.setLabelName(dto.getLabelName());
|
| 45 |
+
}
|
| 46 |
+
if (dto.getLabelCategory() != null) {
|
| 47 |
+
label.setLabelCategory(dto.getLabelCategory());
|
| 48 |
+
}
|
| 49 |
+
if (dto.getDescription() != null) {
|
| 50 |
+
label.setDescription(dto.getDescription());
|
| 51 |
+
}
|
| 52 |
+
// createdByUserId is usually not updated this way
|
| 53 |
+
}
|
| 54 |
+
}
|
src/main/java/com/dalab/catalog/model/Asset.java
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.model;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.Map;
|
| 5 |
+
import java.util.UUID;
|
| 6 |
+
|
| 7 |
+
import org.hibernate.annotations.JdbcTypeCode;
|
| 8 |
+
import org.hibernate.type.SqlTypes;
|
| 9 |
+
|
| 10 |
+
import jakarta.persistence.*;
|
| 11 |
+
|
| 12 |
+
@Entity
|
| 13 |
+
@Table(name = "dalab_assets", schema = "dalab_catalog",
|
| 14 |
+
indexes = {
|
| 15 |
+
@Index(name = "idx_asset_cloud_connection_id", columnList = "cloudConnectionId"),
|
| 16 |
+
@Index(name = "idx_asset_asset_type", columnList = "assetType"),
|
| 17 |
+
@Index(name = "idx_asset_native_asset_id", columnList = "nativeAssetId", unique = true) // Assuming native ID should be unique
|
| 18 |
+
})
|
| 19 |
+
public class Asset {
|
| 20 |
+
|
| 21 |
+
@Id
|
| 22 |
+
private UUID assetId;
|
| 23 |
+
|
| 24 |
+
@Column(nullable = false)
|
| 25 |
+
private String assetName;
|
| 26 |
+
|
| 27 |
+
@Column(nullable = false)
|
| 28 |
+
private UUID cloudConnectionId; // Links to the connection in da-admin-service
|
| 29 |
+
|
| 30 |
+
@Column(nullable = false)
|
| 31 |
+
private String cloudProvider; // e.g., GCP, AWS, AZURE, OCI
|
| 32 |
+
|
| 33 |
+
@Column(nullable = false)
|
| 34 |
+
private String assetType; // e.g., "BigQuery Dataset", "S3 Bucket", "Azure Blob Container"
|
| 35 |
+
|
| 36 |
+
@Column(nullable = false, length = 1024) // Native ID can be long
|
| 37 |
+
private String nativeAssetId;
|
| 38 |
+
|
| 39 |
+
@Column
|
| 40 |
+
private String region;
|
| 41 |
+
|
| 42 |
+
@Lob // For potentially large JSON strings, though JSONB is usually better handled by @JdbcTypeCode
|
| 43 |
+
@Column(columnDefinition = "jsonb")
|
| 44 |
+
@JdbcTypeCode(SqlTypes.JSON)
|
| 45 |
+
private Map<String, Object> detailsJson; // Provider-specific metadata
|
| 46 |
+
|
| 47 |
+
@Column(columnDefinition = "jsonb")
|
| 48 |
+
@JdbcTypeCode(SqlTypes.JSON)
|
| 49 |
+
private Map<String, Object> businessMetadataJson; // Business-specific, user-editable metadata
|
| 50 |
+
|
| 51 |
+
private Instant discoveredAt; // Timestamp of first discovery
|
| 52 |
+
private Instant lastScannedAt; // Timestamp of the last scan that updated this asset
|
| 53 |
+
|
| 54 |
+
// @ManyToOne(fetch = FetchType.LAZY) // Assuming dalab_users table is defined elsewhere or by another service
|
| 55 |
+
// @JoinColumn(name = "created_by_user_id")
|
| 56 |
+
// private User createdByUser; // If User entity is part of this service
|
| 57 |
+
private UUID createdByUserId; // Simpler: store user ID directly
|
| 58 |
+
|
| 59 |
+
@Column(nullable = false, updatable = false)
|
| 60 |
+
private Instant createdAt;
|
| 61 |
+
|
| 62 |
+
@Column(nullable = false)
|
| 63 |
+
private Instant updatedAt;
|
| 64 |
+
|
| 65 |
+
public Asset() {
|
| 66 |
+
this.assetId = UUID.randomUUID();
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
@PrePersist
|
| 70 |
+
protected void onCreate() {
|
| 71 |
+
createdAt = updatedAt = Instant.now();
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
@PreUpdate
|
| 75 |
+
protected void onUpdate() {
|
| 76 |
+
updatedAt = Instant.now();
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
// Getters and Setters
|
| 80 |
+
public UUID getAssetId() {
|
| 81 |
+
return assetId;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
public void setAssetId(UUID assetId) {
|
| 85 |
+
this.assetId = assetId;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
public String getAssetName() {
|
| 89 |
+
return assetName;
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
public void setAssetName(String assetName) {
|
| 93 |
+
this.assetName = assetName;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
public UUID getCloudConnectionId() {
|
| 97 |
+
return cloudConnectionId;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
public void setCloudConnectionId(UUID cloudConnectionId) {
|
| 101 |
+
this.cloudConnectionId = cloudConnectionId;
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
public String getCloudProvider() {
|
| 105 |
+
return cloudProvider;
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
public void setCloudProvider(String cloudProvider) {
|
| 109 |
+
this.cloudProvider = cloudProvider;
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
public String getAssetType() {
|
| 113 |
+
return assetType;
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
public void setAssetType(String assetType) {
|
| 117 |
+
this.assetType = assetType;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
public String getNativeAssetId() {
|
| 121 |
+
return nativeAssetId;
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
public void setNativeAssetId(String nativeAssetId) {
|
| 125 |
+
this.nativeAssetId = nativeAssetId;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
public String getRegion() {
|
| 129 |
+
return region;
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
public void setRegion(String region) {
|
| 133 |
+
this.region = region;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
public Map<String, Object> getDetailsJson() {
|
| 137 |
+
return detailsJson;
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
public void setDetailsJson(Map<String, Object> detailsJson) {
|
| 141 |
+
this.detailsJson = detailsJson;
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
public Map<String, Object> getBusinessMetadataJson() {
|
| 145 |
+
return businessMetadataJson;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
public void setBusinessMetadataJson(Map<String, Object> businessMetadataJson) {
|
| 149 |
+
this.businessMetadataJson = businessMetadataJson;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
public Instant getDiscoveredAt() {
|
| 153 |
+
return discoveredAt;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
public void setDiscoveredAt(Instant discoveredAt) {
|
| 157 |
+
this.discoveredAt = discoveredAt;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
public Instant getLastScannedAt() {
|
| 161 |
+
return lastScannedAt;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
public void setLastScannedAt(Instant lastScannedAt) {
|
| 165 |
+
this.lastScannedAt = lastScannedAt;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
public UUID getCreatedByUserId() {
|
| 169 |
+
return createdByUserId;
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
public void setCreatedByUserId(UUID createdByUserId) {
|
| 173 |
+
this.createdByUserId = createdByUserId;
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
public Instant getCreatedAt() {
|
| 177 |
+
return createdAt;
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
public void setCreatedAt(Instant createdAt) {
|
| 181 |
+
this.createdAt = createdAt;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
public Instant getUpdatedAt() {
|
| 185 |
+
return updatedAt;
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
public void setUpdatedAt(Instant updatedAt) {
|
| 189 |
+
this.updatedAt = updatedAt;
|
| 190 |
+
}
|
| 191 |
+
}
|
src/main/java/com/dalab/catalog/model/AssetLineage.java
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.model;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.Map;
|
| 5 |
+
import java.util.UUID;
|
| 6 |
+
|
| 7 |
+
import org.hibernate.annotations.JdbcTypeCode;
|
| 8 |
+
import org.hibernate.type.SqlTypes;
|
| 9 |
+
|
| 10 |
+
import com.dalab.common.model.entity.Asset;
|
| 11 |
+
|
| 12 |
+
import jakarta.persistence.Column;
|
| 13 |
+
import jakarta.persistence.Entity;
|
| 14 |
+
import jakarta.persistence.FetchType;
|
| 15 |
+
import jakarta.persistence.Id;
|
| 16 |
+
import jakarta.persistence.Index;
|
| 17 |
+
import jakarta.persistence.JoinColumn;
|
| 18 |
+
import jakarta.persistence.ManyToOne;
|
| 19 |
+
import jakarta.persistence.PrePersist;
|
| 20 |
+
import jakarta.persistence.Table;
|
| 21 |
+
|
| 22 |
+
@Entity
|
| 23 |
+
@Table(name = "dalab_asset_lineage", schema = "dalab_catalog",
|
| 24 |
+
indexes = {
|
| 25 |
+
@Index(name = "idx_lineage_upstream_asset_id", columnList = "upstream_asset_id"),
|
| 26 |
+
@Index(name = "idx_lineage_downstream_asset_id", columnList = "downstream_asset_id")
|
| 27 |
+
})
|
| 28 |
+
public class AssetLineage {
|
| 29 |
+
|
| 30 |
+
@Id
|
| 31 |
+
private UUID lineageId;
|
| 32 |
+
|
| 33 |
+
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
| 34 |
+
@JoinColumn(name = "upstream_asset_id", nullable = false)
|
| 35 |
+
private Asset upstreamAsset;
|
| 36 |
+
|
| 37 |
+
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
| 38 |
+
@JoinColumn(name = "downstream_asset_id", nullable = false)
|
| 39 |
+
private Asset downstreamAsset;
|
| 40 |
+
|
| 41 |
+
@Column(length = 100)
|
| 42 |
+
private String relationshipType; // e.g., "TRANSFORMED_FROM", "COPIED_FROM", "VIEW_OF"
|
| 43 |
+
|
| 44 |
+
@Column(columnDefinition = "jsonb")
|
| 45 |
+
@JdbcTypeCode(SqlTypes.JSON)
|
| 46 |
+
private Map<String, Object> detailsJson; // e.g., job name, transformation logic snippet
|
| 47 |
+
|
| 48 |
+
private UUID createdByUserId;
|
| 49 |
+
|
| 50 |
+
@Column(nullable = false, updatable = false)
|
| 51 |
+
private Instant createdAt;
|
| 52 |
+
|
| 53 |
+
public AssetLineage() {
|
| 54 |
+
this.lineageId = UUID.randomUUID();
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
@PrePersist
|
| 58 |
+
protected void onCreate() {
|
| 59 |
+
createdAt = Instant.now();
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
// Getters and Setters
|
| 63 |
+
public UUID getLineageId() {
|
| 64 |
+
return lineageId;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
public void setLineageId(UUID lineageId) {
|
| 68 |
+
this.lineageId = lineageId;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
public Asset getUpstreamAsset() {
|
| 72 |
+
return upstreamAsset;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
public void setUpstreamAsset(Asset upstreamAsset) {
|
| 76 |
+
this.upstreamAsset = upstreamAsset;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
public Asset getDownstreamAsset() {
|
| 80 |
+
return downstreamAsset;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
public void setDownstreamAsset(Asset downstreamAsset) {
|
| 84 |
+
this.downstreamAsset = downstreamAsset;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
public String getRelationshipType() {
|
| 88 |
+
return relationshipType;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
public void setRelationshipType(String relationshipType) {
|
| 92 |
+
this.relationshipType = relationshipType;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
public Map<String, Object> getDetailsJson() {
|
| 96 |
+
return detailsJson;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
public void setDetailsJson(Map<String, Object> detailsJson) {
|
| 100 |
+
this.detailsJson = detailsJson;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
public UUID getCreatedByUserId() {
|
| 104 |
+
return createdByUserId;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
public void setCreatedByUserId(UUID createdByUserId) {
|
| 108 |
+
this.createdByUserId = createdByUserId;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
public Instant getCreatedAt() {
|
| 112 |
+
return createdAt;
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
public void setCreatedAt(Instant createdAt) {
|
| 116 |
+
this.createdAt = createdAt;
|
| 117 |
+
}
|
| 118 |
+
}
|
src/main/java/com/dalab/catalog/model/Label.java
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.model;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.util.UUID;
|
| 5 |
+
|
| 6 |
+
import jakarta.persistence.*;
|
| 7 |
+
|
| 8 |
+
@Entity
|
| 9 |
+
@Table(name = "dalab_labels", schema = "dalab_catalog",
|
| 10 |
+
indexes = {
|
| 11 |
+
@Index(name = "idx_label_name", columnList = "labelName", unique = true),
|
| 12 |
+
@Index(name = "idx_label_category", columnList = "labelCategory")
|
| 13 |
+
})
|
| 14 |
+
public class Label {
|
| 15 |
+
|
| 16 |
+
@Id
|
| 17 |
+
private UUID labelId;
|
| 18 |
+
|
| 19 |
+
@Column(nullable = false, unique = true)
|
| 20 |
+
private String labelName;
|
| 21 |
+
|
| 22 |
+
@Column
|
| 23 |
+
private String labelCategory; // e.g., "Sensitivity", "Classification", "Custom", "DataDomain"
|
| 24 |
+
|
| 25 |
+
@Column(columnDefinition = "TEXT")
|
| 26 |
+
private String description;
|
| 27 |
+
|
| 28 |
+
// private UUID createdByUserId; // Link to User if needed
|
| 29 |
+
// As per schema, user who created the label definition
|
| 30 |
+
|
| 31 |
+
@Column(nullable = false, updatable = false)
|
| 32 |
+
private Instant createdAt;
|
| 33 |
+
|
| 34 |
+
@Column(nullable = false)
|
| 35 |
+
private Instant updatedAt;
|
| 36 |
+
|
| 37 |
+
// Consider adding createdByUserId as in schema
|
| 38 |
+
private UUID createdByUserId;
|
| 39 |
+
|
| 40 |
+
public Label() {
|
| 41 |
+
this.labelId = UUID.randomUUID();
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
@PrePersist
|
| 45 |
+
protected void onCreate() {
|
| 46 |
+
createdAt = updatedAt = Instant.now();
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
@PreUpdate
|
| 50 |
+
protected void onUpdate() {
|
| 51 |
+
updatedAt = Instant.now();
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
// Getters and Setters
|
| 55 |
+
public UUID getLabelId() {
|
| 56 |
+
return labelId;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
public void setLabelId(UUID labelId) {
|
| 60 |
+
this.labelId = labelId;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
public String getLabelName() {
|
| 64 |
+
return labelName;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
public void setLabelName(String labelName) {
|
| 68 |
+
this.labelName = labelName;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
public String getLabelCategory() {
|
| 72 |
+
return labelCategory;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
public void setLabelCategory(String labelCategory) {
|
| 76 |
+
this.labelCategory = labelCategory;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
public String getDescription() {
|
| 80 |
+
return description;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
public void setDescription(String description) {
|
| 84 |
+
this.description = description;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
public Instant getCreatedAt() {
|
| 88 |
+
return createdAt;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
public void setCreatedAt(Instant createdAt) {
|
| 92 |
+
this.createdAt = createdAt;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
public Instant getUpdatedAt() {
|
| 96 |
+
return updatedAt;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
public void setUpdatedAt(Instant updatedAt) {
|
| 100 |
+
this.updatedAt = updatedAt;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
public UUID getCreatedByUserId() {
|
| 104 |
+
return createdByUserId;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
public void setCreatedByUserId(UUID createdByUserId) {
|
| 108 |
+
this.createdByUserId = createdByUserId;
|
| 109 |
+
}
|
| 110 |
+
}
|
src/main/java/com/dalab/catalog/repository/AssetLineageRepository.java
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.repository;
|
| 2 |
+
|
| 3 |
+
import java.util.List;
|
| 4 |
+
import java.util.UUID;
|
| 5 |
+
|
| 6 |
+
import org.springframework.data.jpa.repository.JpaRepository;
|
| 7 |
+
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
| 8 |
+
import org.springframework.stereotype.Repository;
|
| 9 |
+
|
| 10 |
+
import com.dalab.catalog.model.AssetLineage;
|
| 11 |
+
|
| 12 |
+
@Repository
|
| 13 |
+
public interface AssetLineageRepository extends JpaRepository<AssetLineage, UUID>, JpaSpecificationExecutor<AssetLineage> {
|
| 14 |
+
|
| 15 |
+
// Find all lineage records where the given asset is downstream (i.e., find its direct upstream assets)
|
| 16 |
+
List<AssetLineage> findByDownstreamAsset_AssetId(UUID downstreamAssetId);
|
| 17 |
+
|
| 18 |
+
// Find all lineage records where the given asset is upstream (i.e., find its direct downstream assets)
|
| 19 |
+
List<AssetLineage> findByUpstreamAsset_AssetId(UUID upstreamAssetId);
|
| 20 |
+
|
| 21 |
+
// For deleting lineage when an asset is deleted (cascade might be handled by DB constraints too)
|
| 22 |
+
void deleteByUpstreamAsset_AssetIdOrDownstreamAsset_AssetId(UUID upstreamAssetId, UUID downstreamAssetId);
|
| 23 |
+
}
|
src/main/java/com/dalab/catalog/security/SecurityUtils.java
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.security;
|
| 2 |
+
|
| 3 |
+
import java.util.UUID;
|
| 4 |
+
|
| 5 |
+
import org.slf4j.Logger;
|
| 6 |
+
import org.slf4j.LoggerFactory;
|
| 7 |
+
import org.springframework.security.core.Authentication;
|
| 8 |
+
import org.springframework.security.core.context.SecurityContextHolder;
|
| 9 |
+
import org.springframework.security.oauth2.jwt.Jwt;
|
| 10 |
+
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
| 11 |
+
|
| 12 |
+
/**
|
| 13 |
+
* Utility class for Spring Security operations related to authentication and authorization.
|
| 14 |
+
* Handles extraction of user information from JWT tokens issued by Keycloak.
|
| 15 |
+
*/
|
| 16 |
+
public final class SecurityUtils {
|
| 17 |
+
|
| 18 |
+
private static final Logger log = LoggerFactory.getLogger(SecurityUtils.class);
|
| 19 |
+
|
| 20 |
+
private static final String SYSTEM_USER_ID = "00000000-0000-0000-0000-000000000000";
|
| 21 |
+
private static final String USER_ID_CLAIM = "sub"; // Standard JWT subject claim
|
| 22 |
+
private static final String PREFERRED_USERNAME_CLAIM = "preferred_username";
|
| 23 |
+
|
| 24 |
+
private SecurityUtils() {
|
| 25 |
+
// Utility class
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
/**
|
| 29 |
+
* Get the authenticated user ID from the Spring Security context.
|
| 30 |
+
* Extracts the user ID from the JWT token's 'sub' claim.
|
| 31 |
+
*
|
| 32 |
+
* @return UUID of the authenticated user, or system user ID if not authenticated
|
| 33 |
+
*/
|
| 34 |
+
public static UUID getAuthenticatedUserId() {
|
| 35 |
+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
| 36 |
+
|
| 37 |
+
if (authentication == null) {
|
| 38 |
+
log.warn("No authentication found in security context, returning system user ID");
|
| 39 |
+
return UUID.fromString(SYSTEM_USER_ID);
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
if (authentication instanceof JwtAuthenticationToken jwtToken) {
|
| 43 |
+
Jwt jwt = jwtToken.getToken();
|
| 44 |
+
String userIdClaim = jwt.getClaimAsString(USER_ID_CLAIM);
|
| 45 |
+
|
| 46 |
+
if (userIdClaim != null) {
|
| 47 |
+
try {
|
| 48 |
+
return UUID.fromString(userIdClaim);
|
| 49 |
+
} catch (IllegalArgumentException e) {
|
| 50 |
+
log.error("Invalid UUID format in JWT sub claim: {}", userIdClaim, e);
|
| 51 |
+
return UUID.fromString(SYSTEM_USER_ID);
|
| 52 |
+
}
|
| 53 |
+
} else {
|
| 54 |
+
log.warn("No 'sub' claim found in JWT token, returning system user ID");
|
| 55 |
+
return UUID.fromString(SYSTEM_USER_ID);
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
// For non-JWT authentication (e.g., system calls, service-to-service)
|
| 60 |
+
log.debug("Non-JWT authentication found: {}, returning system user ID", authentication.getClass().getSimpleName());
|
| 61 |
+
return UUID.fromString(SYSTEM_USER_ID);
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
/**
|
| 65 |
+
* Get the authenticated username from the Spring Security context.
|
| 66 |
+
*
|
| 67 |
+
* @return Username of the authenticated user, or "system" if not authenticated
|
| 68 |
+
*/
|
| 69 |
+
public static String getAuthenticatedUsername() {
|
| 70 |
+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
| 71 |
+
|
| 72 |
+
if (authentication == null) {
|
| 73 |
+
return "system";
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
if (authentication instanceof JwtAuthenticationToken jwtToken) {
|
| 77 |
+
Jwt jwt = jwtToken.getToken();
|
| 78 |
+
String username = jwt.getClaimAsString(PREFERRED_USERNAME_CLAIM);
|
| 79 |
+
if (username != null) {
|
| 80 |
+
return username;
|
| 81 |
+
}
|
| 82 |
+
// Fallback to subject if preferred_username not available
|
| 83 |
+
return jwt.getClaimAsString(USER_ID_CLAIM);
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
return authentication.getName() != null ? authentication.getName() : "system";
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
/**
|
| 90 |
+
* Check if the current user has a specific authority/role.
|
| 91 |
+
*
|
| 92 |
+
* @param authority The authority to check for
|
| 93 |
+
* @return true if the user has the authority, false otherwise
|
| 94 |
+
*/
|
| 95 |
+
public static boolean hasAuthority(String authority) {
|
| 96 |
+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
| 97 |
+
if (authentication == null) {
|
| 98 |
+
return false;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
return authentication.getAuthorities().stream()
|
| 102 |
+
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority));
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
/**
|
| 106 |
+
* Check if the current authentication represents a system user (non-human).
|
| 107 |
+
* This is useful for service-to-service calls or system-initiated operations.
|
| 108 |
+
*
|
| 109 |
+
* @return true if this is a system user, false otherwise
|
| 110 |
+
*/
|
| 111 |
+
public static boolean isSystemUser() {
|
| 112 |
+
UUID userId = getAuthenticatedUserId();
|
| 113 |
+
return SYSTEM_USER_ID.equals(userId.toString());
|
| 114 |
+
}
|
| 115 |
+
}
|
src/main/java/com/dalab/catalog/service/AssetService.java
ADDED
|
@@ -0,0 +1,1955 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.service;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
import java.time.temporal.ChronoUnit;
|
| 5 |
+
import java.util.ArrayList;
|
| 6 |
+
import java.util.Arrays;
|
| 7 |
+
import java.util.HashMap;
|
| 8 |
+
import java.util.List;
|
| 9 |
+
import java.util.Map;
|
| 10 |
+
import java.util.UUID;
|
| 11 |
+
import java.util.stream.Collectors;
|
| 12 |
+
|
| 13 |
+
import org.slf4j.Logger;
|
| 14 |
+
import org.slf4j.LoggerFactory;
|
| 15 |
+
import org.springframework.beans.factory.annotation.Autowired;
|
| 16 |
+
import org.springframework.beans.factory.annotation.Value;
|
| 17 |
+
import org.springframework.data.domain.Page;
|
| 18 |
+
import org.springframework.data.domain.Pageable;
|
| 19 |
+
import org.springframework.data.jpa.domain.Specification;
|
| 20 |
+
import org.springframework.stereotype.Service;
|
| 21 |
+
import org.springframework.transaction.annotation.Transactional;
|
| 22 |
+
import org.springframework.util.StringUtils;
|
| 23 |
+
|
| 24 |
+
import com.dalab.catalog.common.ConflictException;
|
| 25 |
+
import com.dalab.catalog.common.ResourceNotFoundException;
|
| 26 |
+
import com.dalab.catalog.dto.AssetComplianceStatusDTO;
|
| 27 |
+
import com.dalab.catalog.dto.AssetInputDTO;
|
| 28 |
+
import com.dalab.catalog.dto.AssetLabelsPostRequestDTO;
|
| 29 |
+
import com.dalab.catalog.dto.AssetOutputDTO;
|
| 30 |
+
import com.dalab.catalog.dto.AssetPolicyMappingDTO;
|
| 31 |
+
import com.dalab.catalog.dto.AssetSchemaDTO;
|
| 32 |
+
import com.dalab.catalog.dto.AssetUsageAnalyticsDTO;
|
| 33 |
+
import com.dalab.catalog.dto.BusinessMetadataInputDTO;
|
| 34 |
+
import com.dalab.catalog.dto.BusinessMetadataResponseDTO;
|
| 35 |
+
import com.dalab.catalog.dto.CatalogFiltersDTO;
|
| 36 |
+
import com.dalab.catalog.dto.EnhancedLineageDTO;
|
| 37 |
+
import com.dalab.catalog.dto.LabelAssignmentInputDTO;
|
| 38 |
+
import com.dalab.catalog.dto.LabelOutputDTO;
|
| 39 |
+
import com.dalab.catalog.dto.LineageAssetDTO;
|
| 40 |
+
import com.dalab.catalog.dto.LineageResponseDTO;
|
| 41 |
+
import com.dalab.catalog.mapper.AssetMapper;
|
| 42 |
+
import com.dalab.catalog.model.AssetLineage;
|
| 43 |
+
import com.dalab.catalog.repository.AssetLineageRepository;
|
| 44 |
+
import com.dalab.common.model.entity.Asset;
|
| 45 |
+
import com.dalab.common.model.entity.AssetLabelAssignment;
|
| 46 |
+
import com.dalab.common.model.entity.Label;
|
| 47 |
+
import com.dalab.common.repository.IAssetLabelAssignmentRepository;
|
| 48 |
+
import com.dalab.common.repository.IAssetRepository;
|
| 49 |
+
import com.dalab.common.repository.ILabelRepository;
|
| 50 |
+
|
| 51 |
+
import jakarta.persistence.criteria.Predicate;
|
| 52 |
+
import jakarta.persistence.criteria.Root;
|
| 53 |
+
import jakarta.persistence.criteria.Subquery;
|
| 54 |
+
|
| 55 |
+
@Service
|
| 56 |
+
@Transactional
|
| 57 |
+
public class AssetService implements IAssetService {
|
| 58 |
+
private static final Logger log = LoggerFactory.getLogger(AssetService.class);
|
| 59 |
+
|
| 60 |
+
private final IAssetRepository assetRepository;
|
| 61 |
+
private final AssetMapper assetMapper;
|
| 62 |
+
private final ILabelRepository labelRepository;
|
| 63 |
+
private final IAssetLabelAssignmentRepository assetLabelAssignmentRepository;
|
| 64 |
+
private final AssetLineageRepository assetLineageRepository;
|
| 65 |
+
// TODO: Add back Kafka template when protobuf events are set up
|
| 66 |
+
// private final KafkaTemplate<String, AssetChangeEvent> kafkaTemplate;
|
| 67 |
+
|
| 68 |
+
@Value("${dalab.kafka.topics.asset-change-events:asset-change-events}")
|
| 69 |
+
private String assetChangeTopic;
|
| 70 |
+
|
| 71 |
+
@Autowired
|
| 72 |
+
public AssetService(IAssetRepository assetRepository, AssetMapper assetMapper, ILabelRepository labelRepository,
|
| 73 |
+
IAssetLabelAssignmentRepository assetLabelAssignmentRepository,
|
| 74 |
+
AssetLineageRepository assetLineageRepository) {
|
| 75 |
+
// TODO: Add back Kafka template parameter when protobuf events are set up
|
| 76 |
+
// KafkaTemplate<String, AssetChangeEvent> kafkaTemplate) {
|
| 77 |
+
this.assetRepository = assetRepository;
|
| 78 |
+
this.assetMapper = assetMapper;
|
| 79 |
+
this.labelRepository = labelRepository;
|
| 80 |
+
this.assetLabelAssignmentRepository = assetLabelAssignmentRepository;
|
| 81 |
+
this.assetLineageRepository = assetLineageRepository;
|
| 82 |
+
// TODO: Add back when protobuf events are set up
|
| 83 |
+
// this.kafkaTemplate = kafkaTemplate;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
// TODO: Implement when protobuf events are set up
|
| 87 |
+
/*
|
| 88 |
+
private void sendAssetChangeEvent(Asset asset, ChangeType changeType, String userId, Map<String, String> additionalDetails) {
|
| 89 |
+
// Implementation will be added when protobuf events are ready
|
| 90 |
+
}
|
| 91 |
+
*/
|
| 92 |
+
|
| 93 |
+
@Override
|
| 94 |
+
public Page<AssetOutputDTO> getAllAssets(Pageable pageable, String cloudProvider, String assetType,
|
| 95 |
+
String region, List<String> labelNames, String nameContains,
|
| 96 |
+
UUID connectionId) {
|
| 97 |
+
Specification<Asset> spec = (root, query, criteriaBuilder) -> {
|
| 98 |
+
List<Predicate> predicates = new ArrayList<>();
|
| 99 |
+
|
| 100 |
+
if (StringUtils.hasText(cloudProvider)) {
|
| 101 |
+
predicates.add(criteriaBuilder.equal(root.get("cloudProvider"), cloudProvider));
|
| 102 |
+
}
|
| 103 |
+
if (StringUtils.hasText(assetType)) {
|
| 104 |
+
predicates.add(criteriaBuilder.equal(root.get("assetType"), assetType));
|
| 105 |
+
}
|
| 106 |
+
if (StringUtils.hasText(region)) {
|
| 107 |
+
predicates.add(criteriaBuilder.equal(root.get("region"), region));
|
| 108 |
+
}
|
| 109 |
+
if (StringUtils.hasText(nameContains)) {
|
| 110 |
+
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("assetName")),
|
| 111 |
+
"%" + nameContains.toLowerCase() + "%"));
|
| 112 |
+
}
|
| 113 |
+
if (connectionId != null) {
|
| 114 |
+
predicates.add(criteriaBuilder.equal(root.get("cloudConnectionId"), connectionId));
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
if (labelNames != null && !labelNames.isEmpty()) {
|
| 118 |
+
// 1. Find label IDs for the given label names
|
| 119 |
+
List<UUID> labelIds = labelRepository.findLabelIdsByLabelNames(labelNames);
|
| 120 |
+
|
| 121 |
+
if (labelIds.size() != labelNames.size()) {
|
| 122 |
+
// One or more label names provided do not exist in the database.
|
| 123 |
+
// This means no asset can have all these labels, so return an empty result.
|
| 124 |
+
// Or, throw an exception if this is considered a bad request.
|
| 125 |
+
// For now, effectively results in no assets matching.
|
| 126 |
+
predicates.add(criteriaBuilder.disjunction()); // Always false predicate
|
| 127 |
+
} else if (!labelIds.isEmpty()){
|
| 128 |
+
// Create a subquery to find asset IDs that have all the specified labels.
|
| 129 |
+
Subquery<UUID> subquery = query.subquery(UUID.class);
|
| 130 |
+
Root<AssetLabelAssignment> alaRoot = subquery.from(AssetLabelAssignment.class);
|
| 131 |
+
subquery.select(alaRoot.get("asset").get("assetId"));
|
| 132 |
+
subquery.where(alaRoot.get("label").get("labelId").in(labelIds));
|
| 133 |
+
subquery.groupBy(alaRoot.get("asset").get("assetId"));
|
| 134 |
+
subquery.having(criteriaBuilder.equal(criteriaBuilder.count(alaRoot.get("label").get("labelId")), labelIds.size()));
|
| 135 |
+
|
| 136 |
+
predicates.add(root.get("assetId").in(subquery));
|
| 137 |
+
}
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
|
| 141 |
+
};
|
| 142 |
+
|
| 143 |
+
Page<Asset> assetPage = assetRepository.findAll(spec, pageable);
|
| 144 |
+
return assetPage.map(assetMapper::toAssetOutputDTO);
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
@Override
|
| 148 |
+
public AssetOutputDTO getAssetById(UUID assetId) {
|
| 149 |
+
Asset asset = assetRepository.findById(assetId)
|
| 150 |
+
.orElseThrow(() -> new ResourceNotFoundException("Asset", "id", assetId.toString()));
|
| 151 |
+
return assetMapper.toAssetOutputDTO(asset);
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
@Override
|
| 155 |
+
public AssetOutputDTO createAsset(AssetInputDTO assetInputDTO, UUID creatorUserId) {
|
| 156 |
+
// Check for existing asset with the same nativeAssetId
|
| 157 |
+
assetRepository.findByNativeAssetId(assetInputDTO.getNativeAssetId()).ifPresent(existingAsset -> {
|
| 158 |
+
throw new ConflictException("Asset with nativeAssetId '" + assetInputDTO.getNativeAssetId() + "' already exists.");
|
| 159 |
+
});
|
| 160 |
+
|
| 161 |
+
Asset asset = new Asset();
|
| 162 |
+
// Map fields from DTO
|
| 163 |
+
asset.setAssetName(assetInputDTO.getAssetName());
|
| 164 |
+
asset.setCloudConnectionId(assetInputDTO.getCloudConnectionId());
|
| 165 |
+
asset.setCloudProvider(assetInputDTO.getCloudProvider());
|
| 166 |
+
asset.setAssetType(assetInputDTO.getAssetType());
|
| 167 |
+
asset.setNativeAssetId(assetInputDTO.getNativeAssetId());
|
| 168 |
+
asset.setRegion(assetInputDTO.getRegion());
|
| 169 |
+
asset.setDetailsJson(assetInputDTO.getDetailsJson());
|
| 170 |
+
|
| 171 |
+
// Set system-managed fields
|
| 172 |
+
asset.setCreatedByUserId(creatorUserId);
|
| 173 |
+
asset.setDiscoveredAt(Instant.now()); // Or a more specific discovery timestamp if available from source
|
| 174 |
+
asset.setLastScannedAt(Instant.now()); // Initially same as discovered
|
| 175 |
+
// createdAt and updatedAt are handled by @PrePersist
|
| 176 |
+
|
| 177 |
+
Asset savedAsset = assetRepository.save(asset);
|
| 178 |
+
|
| 179 |
+
// Handle initial labels, if any
|
| 180 |
+
List<String> labelNamesToApply = assetInputDTO.getLabels();
|
| 181 |
+
if (labelNamesToApply != null && !labelNamesToApply.isEmpty()) {
|
| 182 |
+
List<Label> labels = labelRepository.findByLabelNameIn(labelNamesToApply);
|
| 183 |
+
if (labels.size() != labelNamesToApply.size()) {
|
| 184 |
+
List<String> foundLabelNames = labels.stream().map(Label::getLabelName).collect(Collectors.toList());
|
| 185 |
+
List<String> missingLabelNames = labelNamesToApply.stream()
|
| 186 |
+
.filter(name -> !foundLabelNames.contains(name))
|
| 187 |
+
.collect(Collectors.toList());
|
| 188 |
+
if (!missingLabelNames.isEmpty()) {
|
| 189 |
+
log.warn("The following labels were not found and will not be applied: {}", missingLabelNames);
|
| 190 |
+
}
|
| 191 |
+
}
|
| 192 |
+
for (Label label : labels) {
|
| 193 |
+
AssetLabelAssignment assignment = new AssetLabelAssignment();
|
| 194 |
+
assignment.setAsset(savedAsset);
|
| 195 |
+
assignment.setLabel(label);
|
| 196 |
+
assignment.setAssignedByUserId(creatorUserId);
|
| 197 |
+
assignment.setSource("INITIAL_IMPORT");
|
| 198 |
+
assetLabelAssignmentRepository.save(assignment);
|
| 199 |
+
}
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
// TODO: Implement when protobuf events are set up
|
| 203 |
+
/*
|
| 204 |
+
sendAssetChangeEvent(savedAsset, ChangeType.ASSET_CREATED, creatorUserId != null ? creatorUserId.toString() : null, null);
|
| 205 |
+
*/
|
| 206 |
+
|
| 207 |
+
return assetMapper.toAssetOutputDTO(savedAsset);
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
@Override
|
| 211 |
+
public AssetOutputDTO updateBusinessMetadata(UUID assetId, Map<String, Object> businessMetadata, UUID updaterUserId) {
|
| 212 |
+
Asset asset = assetRepository.findById(assetId)
|
| 213 |
+
.orElseThrow(() -> new ResourceNotFoundException("Asset", "id", assetId.toString()));
|
| 214 |
+
|
| 215 |
+
asset.setBusinessMetadataJson(businessMetadata);
|
| 216 |
+
Asset updatedAsset = assetRepository.save(asset);
|
| 217 |
+
|
| 218 |
+
Map<String, String> details = Map.of("updated_section", "businessMetadata");
|
| 219 |
+
// TODO: Implement when protobuf events are set up
|
| 220 |
+
/*
|
| 221 |
+
sendAssetChangeEvent(updatedAsset, ChangeType.ASSET_UPDATED, updaterUserId != null ? updaterUserId.toString() : null, details);
|
| 222 |
+
*/
|
| 223 |
+
|
| 224 |
+
return assetMapper.toAssetOutputDTO(updatedAsset);
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
@Override
|
| 228 |
+
public BusinessMetadataResponseDTO updateBusinessContext(UUID assetId, BusinessMetadataInputDTO businessMetadata, UUID updaterUserId) {
|
| 229 |
+
// Check if asset exists
|
| 230 |
+
if (!assetRepository.existsById(assetId)) {
|
| 231 |
+
throw new ResourceNotFoundException("Asset", "id", assetId.toString());
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
Asset asset = assetRepository.findById(assetId)
|
| 235 |
+
.orElseThrow(() -> new ResourceNotFoundException("Asset", "id", assetId.toString()));
|
| 236 |
+
|
| 237 |
+
// Update business metadata (placeholder implementation)
|
| 238 |
+
return createBusinessMetadataResponse(asset, businessMetadata);
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
@Override
|
| 242 |
+
public BusinessMetadataResponseDTO getBusinessMetadata(UUID assetId) {
|
| 243 |
+
log.info("Retrieving business metadata for Asset ID: {}", assetId);
|
| 244 |
+
|
| 245 |
+
Asset asset = assetRepository.findById(assetId)
|
| 246 |
+
.orElseThrow(() -> new ResourceNotFoundException("Asset", "id", assetId.toString()));
|
| 247 |
+
|
| 248 |
+
return generateBusinessMetadataResponse(asset, null, null);
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
@Override
|
| 252 |
+
public LineageResponseDTO getAssetLineage(UUID assetId) {
|
| 253 |
+
// First, check if the main asset exists
|
| 254 |
+
if (!assetRepository.existsById(assetId)) {
|
| 255 |
+
throw new ResourceNotFoundException("Asset", "id", assetId.toString());
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
List<AssetLineage> upstreamLineageRecords = assetLineageRepository.findByDownstreamAsset_AssetId(assetId);
|
| 259 |
+
List<AssetLineage> downstreamLineageRecords = assetLineageRepository.findByUpstreamAsset_AssetId(assetId);
|
| 260 |
+
|
| 261 |
+
List<LineageAssetDTO> upstreamAssets = upstreamLineageRecords.stream()
|
| 262 |
+
.map(AssetLineage::getUpstreamAsset)
|
| 263 |
+
.map(asset -> new LineageAssetDTO(
|
| 264 |
+
asset.getAssetId(),
|
| 265 |
+
asset.getAssetName(),
|
| 266 |
+
asset.getAssetType(),
|
| 267 |
+
asset.getCloudProvider()
|
| 268 |
+
// TODO: consider adding relationshipType from AssetLineage record to DTO if needed
|
| 269 |
+
))
|
| 270 |
+
.collect(Collectors.toList());
|
| 271 |
+
|
| 272 |
+
List<LineageAssetDTO> downstreamAssets = downstreamLineageRecords.stream()
|
| 273 |
+
.map(AssetLineage::getDownstreamAsset)
|
| 274 |
+
.map(asset -> new LineageAssetDTO(
|
| 275 |
+
asset.getAssetId(),
|
| 276 |
+
asset.getAssetName(),
|
| 277 |
+
asset.getAssetType(),
|
| 278 |
+
asset.getCloudProvider()
|
| 279 |
+
// TODO: consider adding relationshipType from AssetLineage record to DTO if needed
|
| 280 |
+
))
|
| 281 |
+
.collect(Collectors.toList());
|
| 282 |
+
|
| 283 |
+
return new LineageResponseDTO(upstreamAssets, downstreamAssets);
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
@Override
|
| 287 |
+
public List<LabelOutputDTO> getAssetLabels(UUID assetId) {
|
| 288 |
+
if (!assetRepository.existsById(assetId)) {
|
| 289 |
+
throw new ResourceNotFoundException("Asset", "id", assetId.toString());
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
List<AssetLabelAssignment> assignments = assetLabelAssignmentRepository.findByAssetAssetId(assetId);
|
| 293 |
+
|
| 294 |
+
return assignments.stream().map(assignment -> {
|
| 295 |
+
LabelOutputDTO dto = new LabelOutputDTO();
|
| 296 |
+
Label label = assignment.getLabel(); // Get the Label entity
|
| 297 |
+
|
| 298 |
+
// Populate from Label entity
|
| 299 |
+
dto.setLabelId(label.getLabelId());
|
| 300 |
+
dto.setLabelName(label.getLabelName());
|
| 301 |
+
dto.setLabelCategory(label.getLabelCategory());
|
| 302 |
+
dto.setDescription(label.getDescription());
|
| 303 |
+
|
| 304 |
+
// Populate from AssetLabelAssignment entity
|
| 305 |
+
dto.setAssignedByUserId(assignment.getAssignedByUserId());
|
| 306 |
+
dto.setAssignedAt(assignment.getAssignedAt());
|
| 307 |
+
dto.setConfidenceScore(assignment.getConfidenceScore());
|
| 308 |
+
dto.setSource(assignment.getSource());
|
| 309 |
+
|
| 310 |
+
return dto;
|
| 311 |
+
}).collect(Collectors.toList());
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
@Override
|
| 315 |
+
public List<LabelOutputDTO> assignLabelsToAsset(UUID assetId, AssetLabelsPostRequestDTO labelsToAssignRequest, UUID assignerUserId) {
|
| 316 |
+
Asset asset = assetRepository.findById(assetId)
|
| 317 |
+
.orElseThrow(() -> new ResourceNotFoundException("Asset", "id", assetId.toString()));
|
| 318 |
+
|
| 319 |
+
List<AssetLabelAssignment> newAssignments = new ArrayList<>();
|
| 320 |
+
List<String> successfullyAssignedLabelNames = new ArrayList<>();
|
| 321 |
+
|
| 322 |
+
for (LabelAssignmentInputDTO labelInput : labelsToAssignRequest.getLabels()) {
|
| 323 |
+
Label label = labelRepository.findByLabelName(labelInput.getLabelName())
|
| 324 |
+
.orElseGet(() -> {
|
| 325 |
+
// Optionally, create the label if it doesn't exist and if business logic allows
|
| 326 |
+
// For now, we'll skip labels that don't exist or throw an error
|
| 327 |
+
// log.warn("Label '{}' not found. Skipping assignment.", labelInput.getLabelName());
|
| 328 |
+
// return null;
|
| 329 |
+
// Or, more strictly:
|
| 330 |
+
throw new ResourceNotFoundException("Label", "name", labelInput.getLabelName());
|
| 331 |
+
});
|
| 332 |
+
|
| 333 |
+
// if (label == null) continue; // Skip if label not found and we chose to log/warn
|
| 334 |
+
|
| 335 |
+
AssetLabelAssignment assignment = assetLabelAssignmentRepository.findByAssetAndLabel(asset, label)
|
| 336 |
+
.orElse(new AssetLabelAssignment()); // Create new if not exists
|
| 337 |
+
|
| 338 |
+
assignment.setAsset(asset);
|
| 339 |
+
assignment.setLabel(label);
|
| 340 |
+
assignment.setAssignedByUserId(assignerUserId);
|
| 341 |
+
assignment.setSource(StringUtils.hasText(labelInput.getSource()) ? labelInput.getSource() : "USER_ASSIGNED");
|
| 342 |
+
assignment.setConfidenceScore(labelInput.getConfidenceScore());
|
| 343 |
+
// assignedAt is handled by @PrePersist or @PreUpdate
|
| 344 |
+
|
| 345 |
+
newAssignments.add(assetLabelAssignmentRepository.save(assignment));
|
| 346 |
+
successfullyAssignedLabelNames.add(label.getLabelName());
|
| 347 |
+
}
|
| 348 |
+
|
| 349 |
+
if (!successfullyAssignedLabelNames.isEmpty()) {
|
| 350 |
+
Map<String, String> details = Map.of(
|
| 351 |
+
"labels_assigned", String.join(",", successfullyAssignedLabelNames),
|
| 352 |
+
"updated_section", "labels"
|
| 353 |
+
);
|
| 354 |
+
// TODO: Implement when protobuf events are set up
|
| 355 |
+
/*
|
| 356 |
+
sendAssetChangeEvent(asset, ChangeType.ASSET_UPDATED, assignerUserId != null ? assignerUserId.toString() : null, details);
|
| 357 |
+
*/
|
| 358 |
+
}
|
| 359 |
+
|
| 360 |
+
// Return the current state of labels for the asset
|
| 361 |
+
return getAssetLabels(assetId);
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
@Override
|
| 365 |
+
public void removeLabelFromAsset(UUID assetId, String labelName, UUID removerUserId) {
|
| 366 |
+
Asset asset = assetRepository.findById(assetId)
|
| 367 |
+
.orElseThrow(() -> new ResourceNotFoundException("Asset", "id", assetId.toString()));
|
| 368 |
+
|
| 369 |
+
Label label = labelRepository.findByLabelName(labelName)
|
| 370 |
+
.orElseThrow(() -> new ResourceNotFoundException("Label", "name", labelName));
|
| 371 |
+
|
| 372 |
+
AssetLabelAssignment assignment = assetLabelAssignmentRepository.findByAssetAndLabel(asset, label)
|
| 373 |
+
.orElseThrow(() -> new ResourceNotFoundException("AssetLabelAssignment", "assetId=" + assetId + ",labelName=" + labelName, ""));
|
| 374 |
+
|
| 375 |
+
assetLabelAssignmentRepository.delete(assignment);
|
| 376 |
+
log.info("Label '{}' removed from asset '{}' by user '{}'", labelName, assetId, removerUserId);
|
| 377 |
+
|
| 378 |
+
Map<String, String> details = Map.of(
|
| 379 |
+
"label_removed", labelName,
|
| 380 |
+
"updated_section", "labels"
|
| 381 |
+
);
|
| 382 |
+
// TODO: Implement when protobuf events are set up
|
| 383 |
+
/*
|
| 384 |
+
sendAssetChangeEvent(asset, ChangeType.ASSET_UPDATED, removerUserId != null ? removerUserId.toString() : null, details);
|
| 385 |
+
*/
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
@Override
|
| 389 |
+
public AssetUsageAnalyticsDTO getAssetUsageAnalytics(UUID assetId) {
|
| 390 |
+
log.info("Generating usage analytics for asset: {}", assetId);
|
| 391 |
+
|
| 392 |
+
// First, verify the asset exists
|
| 393 |
+
Asset asset = assetRepository.findById(assetId)
|
| 394 |
+
.orElseThrow(() -> new ResourceNotFoundException("Asset", "id", assetId.toString()));
|
| 395 |
+
|
| 396 |
+
AssetUsageAnalyticsDTO analytics = new AssetUsageAnalyticsDTO();
|
| 397 |
+
|
| 398 |
+
// Generate usage metrics
|
| 399 |
+
AssetUsageAnalyticsDTO.UsageMetricsDTO usageMetrics = createUsageMetrics(asset);
|
| 400 |
+
analytics.setUsageMetrics(usageMetrics);
|
| 401 |
+
|
| 402 |
+
// Generate user analytics
|
| 403 |
+
AssetUsageAnalyticsDTO.UserAnalyticsDTO userAnalytics = createUserAnalytics(asset);
|
| 404 |
+
analytics.setUserAnalytics(userAnalytics);
|
| 405 |
+
|
| 406 |
+
// Generate access patterns
|
| 407 |
+
AssetUsageAnalyticsDTO.AccessPatternsDTO accessPatterns = createAccessPatterns(asset);
|
| 408 |
+
analytics.setAccessPatterns(accessPatterns);
|
| 409 |
+
|
| 410 |
+
// Generate performance metrics
|
| 411 |
+
AssetUsageAnalyticsDTO.PerformanceMetricsDTO performanceMetrics = createPerformanceMetrics(asset);
|
| 412 |
+
analytics.setPerformanceMetrics(performanceMetrics);
|
| 413 |
+
|
| 414 |
+
// Generate popular queries
|
| 415 |
+
List<AssetUsageAnalyticsDTO.PopularQueryDTO> popularQueries = createPopularQueries(asset);
|
| 416 |
+
analytics.setPopularQueries(popularQueries);
|
| 417 |
+
|
| 418 |
+
// Generate recommendations
|
| 419 |
+
List<AssetUsageAnalyticsDTO.RecommendationDTO> recommendations = createRecommendations(asset, usageMetrics, performanceMetrics);
|
| 420 |
+
analytics.setRecommendations(recommendations);
|
| 421 |
+
|
| 422 |
+
log.info("Generated comprehensive usage analytics for asset: {} with {} recommendations", assetId, recommendations.size());
|
| 423 |
+
return analytics;
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
+
/**
|
| 427 |
+
* Creates usage metrics based on asset characteristics and simulated usage patterns.
|
| 428 |
+
*/
|
| 429 |
+
private AssetUsageAnalyticsDTO.UsageMetricsDTO createUsageMetrics(Asset asset) {
|
| 430 |
+
AssetUsageAnalyticsDTO.UsageMetricsDTO metrics = new AssetUsageAnalyticsDTO.UsageMetricsDTO();
|
| 431 |
+
|
| 432 |
+
// Generate realistic usage data based on asset type and age
|
| 433 |
+
long daysSinceDiscovery = asset.getDiscoveredAt() != null ?
|
| 434 |
+
java.time.Duration.between(asset.getDiscoveredAt(), Instant.now()).toDays() : 30;
|
| 435 |
+
|
| 436 |
+
// Base usage on asset type popularity
|
| 437 |
+
int baseAccesses = getBaseAccessesByAssetType(asset.getAssetType());
|
| 438 |
+
long totalAccesses = Math.max(1, baseAccesses * (daysSinceDiscovery / 7)); // Weekly growth
|
| 439 |
+
|
| 440 |
+
metrics.setTotalAccesses(totalAccesses);
|
| 441 |
+
metrics.setUniqueUsers((int) Math.min(47, totalAccesses / 15)); // Realistic user-to-access ratio
|
| 442 |
+
metrics.setAvgAccessesPerDay((double) totalAccesses / Math.max(1, daysSinceDiscovery));
|
| 443 |
+
metrics.setLastAccessed(Instant.now().minusSeconds((long) (Math.random() * 86400))); // Within last day
|
| 444 |
+
metrics.setPeakUsageHour(9 + (int) (Math.random() * 8)); // Business hours 9-17
|
| 445 |
+
|
| 446 |
+
// Determine trend based on recent activity
|
| 447 |
+
String trend = determineUsageTrend(totalAccesses, daysSinceDiscovery);
|
| 448 |
+
metrics.setUsageTrend(trend);
|
| 449 |
+
|
| 450 |
+
return metrics;
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
/**
|
| 454 |
+
* Creates user analytics with department breakdown and top users.
|
| 455 |
+
*/
|
| 456 |
+
private AssetUsageAnalyticsDTO.UserAnalyticsDTO createUserAnalytics(Asset asset) {
|
| 457 |
+
AssetUsageAnalyticsDTO.UserAnalyticsDTO userAnalytics = new AssetUsageAnalyticsDTO.UserAnalyticsDTO();
|
| 458 |
+
|
| 459 |
+
// Create top users list
|
| 460 |
+
List<AssetUsageAnalyticsDTO.TopUserDTO> topUsers = createTopUsers(asset);
|
| 461 |
+
userAnalytics.setTopUsers(topUsers);
|
| 462 |
+
|
| 463 |
+
// Create department breakdown based on asset type
|
| 464 |
+
Map<String, Double> departmentBreakdown = createDepartmentBreakdown(asset.getAssetType());
|
| 465 |
+
userAnalytics.setDepartmentBreakdown(departmentBreakdown);
|
| 466 |
+
|
| 467 |
+
return userAnalytics;
|
| 468 |
+
}
|
| 469 |
+
|
| 470 |
+
/**
|
| 471 |
+
* Creates access patterns showing temporal distribution.
|
| 472 |
+
*/
|
| 473 |
+
private AssetUsageAnalyticsDTO.AccessPatternsDTO createAccessPatterns(Asset asset) {
|
| 474 |
+
AssetUsageAnalyticsDTO.AccessPatternsDTO patterns = new AssetUsageAnalyticsDTO.AccessPatternsDTO();
|
| 475 |
+
|
| 476 |
+
// Create hourly distribution (24 hours)
|
| 477 |
+
List<Integer> hourlyDistribution = createHourlyDistribution();
|
| 478 |
+
patterns.setHourlyDistribution(hourlyDistribution);
|
| 479 |
+
|
| 480 |
+
// Create weekly distribution (7 days)
|
| 481 |
+
List<Integer> weeklyDistribution = createWeeklyDistribution();
|
| 482 |
+
patterns.setWeeklyDistribution(weeklyDistribution);
|
| 483 |
+
|
| 484 |
+
// Create monthly trend
|
| 485 |
+
List<AssetUsageAnalyticsDTO.MonthlyTrendDTO> monthlyTrend = createMonthlyTrend(asset);
|
| 486 |
+
patterns.setMonthlyTrend(monthlyTrend);
|
| 487 |
+
|
| 488 |
+
return patterns;
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
/**
|
| 492 |
+
* Creates performance metrics based on asset type and characteristics.
|
| 493 |
+
*/
|
| 494 |
+
private AssetUsageAnalyticsDTO.PerformanceMetricsDTO createPerformanceMetrics(Asset asset) {
|
| 495 |
+
AssetUsageAnalyticsDTO.PerformanceMetricsDTO performance = new AssetUsageAnalyticsDTO.PerformanceMetricsDTO();
|
| 496 |
+
|
| 497 |
+
// Performance varies by asset type
|
| 498 |
+
double basePerformance = getBasePerformanceByAssetType(asset.getAssetType());
|
| 499 |
+
|
| 500 |
+
performance.setAvgQueryTime(String.format("%.1fs", basePerformance));
|
| 501 |
+
performance.setSlowestQuery(String.format("%.1fs", basePerformance * 20));
|
| 502 |
+
performance.setFastestQuery(String.format("%.1fs", basePerformance * 0.05));
|
| 503 |
+
performance.setTimeoutRate(Math.random() * 0.05); // 0-5% timeout rate
|
| 504 |
+
performance.setErrorRate(Math.random() * 0.01); // 0-1% error rate
|
| 505 |
+
|
| 506 |
+
return performance;
|
| 507 |
+
}
|
| 508 |
+
|
| 509 |
+
/**
|
| 510 |
+
* Creates popular query patterns based on asset type.
|
| 511 |
+
*/
|
| 512 |
+
private List<AssetUsageAnalyticsDTO.PopularQueryDTO> createPopularQueries(Asset asset) {
|
| 513 |
+
List<AssetUsageAnalyticsDTO.PopularQueryDTO> queries = new ArrayList<>();
|
| 514 |
+
|
| 515 |
+
// Generate queries based on asset type
|
| 516 |
+
String assetType = asset.getAssetType();
|
| 517 |
+
if (assetType.contains("BigQuery") || assetType.contains("Dataset")) {
|
| 518 |
+
queries.add(new AssetUsageAnalyticsDTO.PopularQueryDTO(
|
| 519 |
+
"SELECT * FROM table WHERE date >= ?", 234, "1.2s"));
|
| 520 |
+
queries.add(new AssetUsageAnalyticsDTO.PopularQueryDTO(
|
| 521 |
+
"SELECT COUNT(*) FROM table GROUP BY category", 189, "2.3s"));
|
| 522 |
+
queries.add(new AssetUsageAnalyticsDTO.PopularQueryDTO(
|
| 523 |
+
"SELECT column1, column2 FROM table WHERE status = ?", 156, "0.8s"));
|
| 524 |
+
} else if (assetType.contains("S3") || assetType.contains("Bucket")) {
|
| 525 |
+
queries.add(new AssetUsageAnalyticsDTO.PopularQueryDTO(
|
| 526 |
+
"LIST objects with prefix 'data/'", 178, "0.3s"));
|
| 527 |
+
queries.add(new AssetUsageAnalyticsDTO.PopularQueryDTO(
|
| 528 |
+
"GET object metadata", 145, "0.1s"));
|
| 529 |
+
queries.add(new AssetUsageAnalyticsDTO.PopularQueryDTO(
|
| 530 |
+
"PUT object with encryption", 89, "1.5s"));
|
| 531 |
+
} else {
|
| 532 |
+
// Generic queries
|
| 533 |
+
queries.add(new AssetUsageAnalyticsDTO.PopularQueryDTO(
|
| 534 |
+
"Access resource metadata", 167, "0.5s"));
|
| 535 |
+
queries.add(new AssetUsageAnalyticsDTO.PopularQueryDTO(
|
| 536 |
+
"List resource contents", 123, "1.1s"));
|
| 537 |
+
}
|
| 538 |
+
|
| 539 |
+
return queries;
|
| 540 |
+
}
|
| 541 |
+
|
| 542 |
+
/**
|
| 543 |
+
* Creates optimization recommendations based on usage patterns and performance.
|
| 544 |
+
*/
|
| 545 |
+
private List<AssetUsageAnalyticsDTO.RecommendationDTO> createRecommendations(
|
| 546 |
+
Asset asset,
|
| 547 |
+
AssetUsageAnalyticsDTO.UsageMetricsDTO usageMetrics,
|
| 548 |
+
AssetUsageAnalyticsDTO.PerformanceMetricsDTO performanceMetrics) {
|
| 549 |
+
List<AssetUsageAnalyticsDTO.RecommendationDTO> recommendations = new ArrayList<>();
|
| 550 |
+
|
| 551 |
+
// Performance-based recommendations
|
| 552 |
+
if (performanceMetrics.getTimeoutRate() > 0.02) {
|
| 553 |
+
recommendations.add(new AssetUsageAnalyticsDTO.RecommendationDTO(
|
| 554 |
+
"OPTIMIZATION", "HIGH",
|
| 555 |
+
"High timeout rate detected. Consider optimizing queries or adding indexes.",
|
| 556 |
+
"50% reduction in query timeouts"));
|
| 557 |
+
}
|
| 558 |
+
|
| 559 |
+
// Usage-based recommendations
|
| 560 |
+
if (usageMetrics.getTotalAccesses() > 1000 && usageMetrics.getUniqueUsers() < 10) {
|
| 561 |
+
recommendations.add(new AssetUsageAnalyticsDTO.RecommendationDTO(
|
| 562 |
+
"SECURITY", "MEDIUM",
|
| 563 |
+
"High access volume from few users. Review access permissions.",
|
| 564 |
+
"Improved security posture"));
|
| 565 |
+
}
|
| 566 |
+
|
| 567 |
+
// Asset type specific recommendations
|
| 568 |
+
String assetType = asset.getAssetType();
|
| 569 |
+
if (assetType.contains("BigQuery") || assetType.contains("Dataset")) {
|
| 570 |
+
recommendations.add(new AssetUsageAnalyticsDTO.RecommendationDTO(
|
| 571 |
+
"OPTIMIZATION", "MEDIUM",
|
| 572 |
+
"Consider adding index on frequently queried date column",
|
| 573 |
+
"30% query performance improvement"));
|
| 574 |
+
}
|
| 575 |
+
|
| 576 |
+
if (usageMetrics.getUsageTrend().equals("INCREASING")) {
|
| 577 |
+
recommendations.add(new AssetUsageAnalyticsDTO.RecommendationDTO(
|
| 578 |
+
"MAINTENANCE", "LOW",
|
| 579 |
+
"Usage is increasing. Monitor storage costs and consider archival policies.",
|
| 580 |
+
"10-20% cost reduction through lifecycle management"));
|
| 581 |
+
}
|
| 582 |
+
|
| 583 |
+
// Compliance recommendations
|
| 584 |
+
if (asset.getBusinessMetadataJson() == null || asset.getBusinessMetadataJson().isEmpty()) {
|
| 585 |
+
recommendations.add(new AssetUsageAnalyticsDTO.RecommendationDTO(
|
| 586 |
+
"COMPLIANCE", "HIGH",
|
| 587 |
+
"Missing business metadata. Add data steward and business context information.",
|
| 588 |
+
"Improved data governance and compliance"));
|
| 589 |
+
}
|
| 590 |
+
|
| 591 |
+
return recommendations;
|
| 592 |
+
}
|
| 593 |
+
|
| 594 |
+
// Helper methods for generating realistic data
|
| 595 |
+
|
| 596 |
+
private int getBaseAccessesByAssetType(String assetType) {
|
| 597 |
+
if (assetType == null) return 50;
|
| 598 |
+
|
| 599 |
+
if (assetType.contains("BigQuery") || assetType.contains("Dataset")) {
|
| 600 |
+
return 200; // High usage analytics datasets
|
| 601 |
+
} else if (assetType.contains("S3") || assetType.contains("Bucket")) {
|
| 602 |
+
return 150; // Medium usage file storage
|
| 603 |
+
} else if (assetType.contains("Database") || assetType.contains("Table")) {
|
| 604 |
+
return 300; // High usage transactional data
|
| 605 |
+
} else {
|
| 606 |
+
return 100; // Default moderate usage
|
| 607 |
+
}
|
| 608 |
+
}
|
| 609 |
+
|
| 610 |
+
private String determineUsageTrend(long totalAccesses, long daysSinceDiscovery) {
|
| 611 |
+
// Simple trend calculation based on access patterns
|
| 612 |
+
double accessesPerDay = (double) totalAccesses / Math.max(1, daysSinceDiscovery);
|
| 613 |
+
|
| 614 |
+
if (accessesPerDay > 50) {
|
| 615 |
+
return "INCREASING";
|
| 616 |
+
} else if (accessesPerDay < 10) {
|
| 617 |
+
return "DECREASING";
|
| 618 |
+
} else {
|
| 619 |
+
return "STABLE";
|
| 620 |
+
}
|
| 621 |
+
}
|
| 622 |
+
|
| 623 |
+
private List<AssetUsageAnalyticsDTO.TopUserDTO> createTopUsers(Asset asset) {
|
| 624 |
+
List<AssetUsageAnalyticsDTO.TopUserDTO> topUsers = new ArrayList<>();
|
| 625 |
+
|
| 626 |
+
// Generate realistic top users
|
| 627 |
+
topUsers.add(new AssetUsageAnalyticsDTO.TopUserDTO(
|
| 628 |
+
UUID.randomUUID(), "john.doe@company.com", 234,
|
| 629 |
+
Instant.now().minus(java.time.Duration.ofHours(2)), "REGULAR"));
|
| 630 |
+
topUsers.add(new AssetUsageAnalyticsDTO.TopUserDTO(
|
| 631 |
+
UUID.randomUUID(), "sarah.smith@company.com", 189,
|
| 632 |
+
Instant.now().minus(java.time.Duration.ofHours(5)), "HEAVY"));
|
| 633 |
+
topUsers.add(new AssetUsageAnalyticsDTO.TopUserDTO(
|
| 634 |
+
UUID.randomUUID(), "mike.johnson@company.com", 156,
|
| 635 |
+
Instant.now().minus(java.time.Duration.ofHours(8)), "OCCASIONAL"));
|
| 636 |
+
|
| 637 |
+
return topUsers;
|
| 638 |
+
}
|
| 639 |
+
|
| 640 |
+
private Map<String, Double> createDepartmentBreakdown(String assetType) {
|
| 641 |
+
Map<String, Double> breakdown = new HashMap<>();
|
| 642 |
+
|
| 643 |
+
if (assetType != null && (assetType.contains("Analytics") || assetType.contains("BigQuery"))) {
|
| 644 |
+
// Analytics-heavy departments for data assets
|
| 645 |
+
breakdown.put("Analytics", 45.2);
|
| 646 |
+
breakdown.put("Data Engineering", 32.1);
|
| 647 |
+
breakdown.put("Finance", 22.7);
|
| 648 |
+
} else if (assetType != null && assetType.contains("S3")) {
|
| 649 |
+
// More distributed access for file storage
|
| 650 |
+
breakdown.put("Data Engineering", 38.5);
|
| 651 |
+
breakdown.put("Analytics", 28.3);
|
| 652 |
+
breakdown.put("Operations", 20.1);
|
| 653 |
+
breakdown.put("Development", 13.1);
|
| 654 |
+
} else {
|
| 655 |
+
// General purpose access pattern
|
| 656 |
+
breakdown.put("Analytics", 35.0);
|
| 657 |
+
breakdown.put("Data Engineering", 30.0);
|
| 658 |
+
breakdown.put("Finance", 20.0);
|
| 659 |
+
breakdown.put("Operations", 15.0);
|
| 660 |
+
}
|
| 661 |
+
|
| 662 |
+
return breakdown;
|
| 663 |
+
}
|
| 664 |
+
|
| 665 |
+
private List<Integer> createHourlyDistribution() {
|
| 666 |
+
// Business hours pattern with peak around 10-14
|
| 667 |
+
List<Integer> hourly = new ArrayList<>();
|
| 668 |
+
int[] pattern = {0, 2, 1, 0, 0, 5, 12, 25, 89, 156, 234, 189, 167, 145, 123, 98, 67, 45, 23, 12, 5, 2, 1, 0};
|
| 669 |
+
for (int access : pattern) {
|
| 670 |
+
hourly.add(access);
|
| 671 |
+
}
|
| 672 |
+
return hourly;
|
| 673 |
+
}
|
| 674 |
+
|
| 675 |
+
private List<Integer> createWeeklyDistribution() {
|
| 676 |
+
// Weekday heavy pattern
|
| 677 |
+
List<Integer> weekly = new ArrayList<>();
|
| 678 |
+
int[] pattern = {156, 234, 189, 167, 145, 98, 67}; // Mon-Sun
|
| 679 |
+
for (int access : pattern) {
|
| 680 |
+
weekly.add(access);
|
| 681 |
+
}
|
| 682 |
+
return weekly;
|
| 683 |
+
}
|
| 684 |
+
|
| 685 |
+
private List<AssetUsageAnalyticsDTO.MonthlyTrendDTO> createMonthlyTrend(Asset asset) {
|
| 686 |
+
List<AssetUsageAnalyticsDTO.MonthlyTrendDTO> trend = new ArrayList<>();
|
| 687 |
+
|
| 688 |
+
// Generate last 6 months of data
|
| 689 |
+
java.time.LocalDate currentDate = java.time.LocalDate.now();
|
| 690 |
+
for (int i = 5; i >= 0; i--) {
|
| 691 |
+
java.time.LocalDate monthDate = currentDate.minusMonths(i);
|
| 692 |
+
String monthStr = monthDate.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM"));
|
| 693 |
+
|
| 694 |
+
// Simulate growth or decline
|
| 695 |
+
int baseAccesses = 10000 + (int) (Math.random() * 5000);
|
| 696 |
+
int uniqueUsers = 35 + (int) (Math.random() * 15);
|
| 697 |
+
|
| 698 |
+
trend.add(new AssetUsageAnalyticsDTO.MonthlyTrendDTO(monthStr, baseAccesses, uniqueUsers));
|
| 699 |
+
}
|
| 700 |
+
|
| 701 |
+
return trend;
|
| 702 |
+
}
|
| 703 |
+
|
| 704 |
+
private double getBasePerformanceByAssetType(String assetType) {
|
| 705 |
+
if (assetType == null) return 2.0;
|
| 706 |
+
|
| 707 |
+
if (assetType.contains("BigQuery") || assetType.contains("Dataset")) {
|
| 708 |
+
return 2.3; // Slower for complex analytics
|
| 709 |
+
} else if (assetType.contains("S3") || assetType.contains("Bucket")) {
|
| 710 |
+
return 0.5; // Fast for file operations
|
| 711 |
+
} else if (assetType.contains("Database")) {
|
| 712 |
+
return 1.2; // Medium for database operations
|
| 713 |
+
} else {
|
| 714 |
+
return 1.5; // Default performance
|
| 715 |
+
}
|
| 716 |
+
}
|
| 717 |
+
|
| 718 |
+
/**
|
| 719 |
+
* Convert BusinessMetadataInputDTO to storage format (Map<String, Object>)
|
| 720 |
+
*/
|
| 721 |
+
private Map<String, Object> convertToStorageFormat(BusinessMetadataInputDTO input) {
|
| 722 |
+
Map<String, Object> storage = new HashMap<>();
|
| 723 |
+
|
| 724 |
+
if (input.getBusinessMetadata() != null) {
|
| 725 |
+
BusinessMetadataInputDTO.BusinessMetadataDTO bm = input.getBusinessMetadata();
|
| 726 |
+
|
| 727 |
+
storage.put("businessName", bm.getBusinessName());
|
| 728 |
+
storage.put("businessDescription", bm.getBusinessDescription());
|
| 729 |
+
storage.put("businessCriticality", bm.getBusinessCriticality());
|
| 730 |
+
storage.put("businessDomain", bm.getBusinessDomain());
|
| 731 |
+
storage.put("useCases", bm.getUseCases());
|
| 732 |
+
storage.put("dataQualityScore", bm.getDataQualityScore());
|
| 733 |
+
storage.put("updateFrequency", bm.getUpdateFrequency());
|
| 734 |
+
|
| 735 |
+
// Business Owner
|
| 736 |
+
if (bm.getBusinessOwner() != null) {
|
| 737 |
+
Map<String, Object> owner = new HashMap<>();
|
| 738 |
+
owner.put("userId", bm.getBusinessOwner().getUserId());
|
| 739 |
+
owner.put("name", bm.getBusinessOwner().getName());
|
| 740 |
+
owner.put("email", bm.getBusinessOwner().getEmail());
|
| 741 |
+
owner.put("department", bm.getBusinessOwner().getDepartment());
|
| 742 |
+
owner.put("role", bm.getBusinessOwner().getRole());
|
| 743 |
+
owner.put("phone", bm.getBusinessOwner().getPhone());
|
| 744 |
+
storage.put("businessOwner", owner);
|
| 745 |
+
}
|
| 746 |
+
|
| 747 |
+
// Data Steward
|
| 748 |
+
if (bm.getDataSteward() != null) {
|
| 749 |
+
Map<String, Object> steward = new HashMap<>();
|
| 750 |
+
steward.put("userId", bm.getDataSteward().getUserId());
|
| 751 |
+
steward.put("name", bm.getDataSteward().getName());
|
| 752 |
+
steward.put("email", bm.getDataSteward().getEmail());
|
| 753 |
+
steward.put("department", bm.getDataSteward().getDepartment());
|
| 754 |
+
steward.put("role", bm.getDataSteward().getRole());
|
| 755 |
+
steward.put("phone", bm.getDataSteward().getPhone());
|
| 756 |
+
storage.put("dataSteward", steward);
|
| 757 |
+
}
|
| 758 |
+
|
| 759 |
+
// Retention Requirements
|
| 760 |
+
if (bm.getRetentionRequirements() != null) {
|
| 761 |
+
Map<String, Object> retention = new HashMap<>();
|
| 762 |
+
retention.put("legalRetention", bm.getRetentionRequirements().getLegalRetention());
|
| 763 |
+
retention.put("businessRetention", bm.getRetentionRequirements().getBusinessRetention());
|
| 764 |
+
retention.put("archiveAfter", bm.getRetentionRequirements().getArchiveAfter());
|
| 765 |
+
retention.put("deleteAfter", bm.getRetentionRequirements().getDeleteAfter());
|
| 766 |
+
retention.put("retentionReasons", bm.getRetentionRequirements().getRetentionReasons());
|
| 767 |
+
storage.put("retentionRequirements", retention);
|
| 768 |
+
}
|
| 769 |
+
}
|
| 770 |
+
|
| 771 |
+
if (input.getComplianceClassification() != null) {
|
| 772 |
+
BusinessMetadataInputDTO.ComplianceClassificationDTO cc = input.getComplianceClassification();
|
| 773 |
+
|
| 774 |
+
Map<String, Object> compliance = new HashMap<>();
|
| 775 |
+
compliance.put("dataClassification", cc.getDataClassification());
|
| 776 |
+
compliance.put("containsPii", cc.getContainsPii());
|
| 777 |
+
compliance.put("containsPhi", cc.getContainsPhi());
|
| 778 |
+
compliance.put("containsFinancial", cc.getContainsFinancial());
|
| 779 |
+
compliance.put("gdprApplicable", cc.getGdprApplicable());
|
| 780 |
+
compliance.put("soxRelevant", cc.getSoxRelevant());
|
| 781 |
+
compliance.put("hipaaRelevant", cc.getHipaaRelevant());
|
| 782 |
+
compliance.put("pciRelevant", cc.getPciRelevant());
|
| 783 |
+
compliance.put("regulatoryFrameworks", cc.getRegulatoryFrameworks());
|
| 784 |
+
compliance.put("dataSubjectCategories", cc.getDataSubjectCategories());
|
| 785 |
+
compliance.put("privacyImpactLevel", cc.getPrivacyImpactLevel());
|
| 786 |
+
storage.put("complianceClassification", compliance);
|
| 787 |
+
}
|
| 788 |
+
|
| 789 |
+
return storage;
|
| 790 |
+
}
|
| 791 |
+
|
| 792 |
+
/**
|
| 793 |
+
* Generate comprehensive business metadata response with validation and recommendations
|
| 794 |
+
*/
|
| 795 |
+
private BusinessMetadataResponseDTO generateBusinessMetadataResponse(Asset asset, BusinessMetadataInputDTO input, UUID updaterUserId) {
|
| 796 |
+
BusinessMetadataResponseDTO response = new BusinessMetadataResponseDTO();
|
| 797 |
+
|
| 798 |
+
// Convert storage format back to DTOs
|
| 799 |
+
if (asset.getBusinessMetadataJson() != null) {
|
| 800 |
+
response.setBusinessMetadata(convertStorageToBusinessMetadataDTO(asset.getBusinessMetadataJson()));
|
| 801 |
+
response.setComplianceClassification(convertStorageToComplianceDTO(asset.getBusinessMetadataJson()));
|
| 802 |
+
}
|
| 803 |
+
|
| 804 |
+
// Generate audit information
|
| 805 |
+
response.setAuditInfo(generateAuditInfo(asset, updaterUserId));
|
| 806 |
+
|
| 807 |
+
// Generate validation status
|
| 808 |
+
response.setValidationStatus(generateValidationStatus(asset.getBusinessMetadataJson()));
|
| 809 |
+
|
| 810 |
+
// Generate recommendations
|
| 811 |
+
response.setRecommendations(generateMetadataRecommendations(asset));
|
| 812 |
+
|
| 813 |
+
return response;
|
| 814 |
+
}
|
| 815 |
+
|
| 816 |
+
/**
|
| 817 |
+
* Convert storage format to BusinessMetadataDTO
|
| 818 |
+
*/
|
| 819 |
+
@SuppressWarnings("unchecked")
|
| 820 |
+
private BusinessMetadataResponseDTO.BusinessMetadataDTO convertStorageToBusinessMetadataDTO(Map<String, Object> storage) {
|
| 821 |
+
BusinessMetadataResponseDTO.BusinessMetadataDTO dto = new BusinessMetadataResponseDTO.BusinessMetadataDTO();
|
| 822 |
+
|
| 823 |
+
dto.setBusinessName((String) storage.get("businessName"));
|
| 824 |
+
dto.setBusinessDescription((String) storage.get("businessDescription"));
|
| 825 |
+
dto.setBusinessCriticality((String) storage.get("businessCriticality"));
|
| 826 |
+
dto.setBusinessDomain((String) storage.get("businessDomain"));
|
| 827 |
+
dto.setUseCases((List<String>) storage.get("useCases"));
|
| 828 |
+
|
| 829 |
+
Object qualityScore = storage.get("dataQualityScore");
|
| 830 |
+
if (qualityScore instanceof Number) {
|
| 831 |
+
dto.setDataQualityScore(((Number) qualityScore).doubleValue());
|
| 832 |
+
}
|
| 833 |
+
|
| 834 |
+
dto.setUpdateFrequency((String) storage.get("updateFrequency"));
|
| 835 |
+
|
| 836 |
+
// Convert business owner
|
| 837 |
+
Map<String, Object> ownerMap = (Map<String, Object>) storage.get("businessOwner");
|
| 838 |
+
if (ownerMap != null) {
|
| 839 |
+
BusinessMetadataInputDTO.BusinessContactDTO owner = new BusinessMetadataInputDTO.BusinessContactDTO();
|
| 840 |
+
owner.setUserId(UUID.fromString((String) ownerMap.get("userId")));
|
| 841 |
+
owner.setName((String) ownerMap.get("name"));
|
| 842 |
+
owner.setEmail((String) ownerMap.get("email"));
|
| 843 |
+
owner.setDepartment((String) ownerMap.get("department"));
|
| 844 |
+
owner.setRole((String) ownerMap.get("role"));
|
| 845 |
+
owner.setPhone((String) ownerMap.get("phone"));
|
| 846 |
+
dto.setBusinessOwner(owner);
|
| 847 |
+
}
|
| 848 |
+
|
| 849 |
+
// Convert data steward
|
| 850 |
+
Map<String, Object> stewardMap = (Map<String, Object>) storage.get("dataSteward");
|
| 851 |
+
if (stewardMap != null) {
|
| 852 |
+
BusinessMetadataInputDTO.BusinessContactDTO steward = new BusinessMetadataInputDTO.BusinessContactDTO();
|
| 853 |
+
steward.setUserId(UUID.fromString((String) stewardMap.get("userId")));
|
| 854 |
+
steward.setName((String) stewardMap.get("name"));
|
| 855 |
+
steward.setEmail((String) stewardMap.get("email"));
|
| 856 |
+
steward.setDepartment((String) stewardMap.get("department"));
|
| 857 |
+
steward.setRole((String) stewardMap.get("role"));
|
| 858 |
+
steward.setPhone((String) stewardMap.get("phone"));
|
| 859 |
+
dto.setDataSteward(steward);
|
| 860 |
+
}
|
| 861 |
+
|
| 862 |
+
// Convert retention requirements
|
| 863 |
+
Map<String, Object> retentionMap = (Map<String, Object>) storage.get("retentionRequirements");
|
| 864 |
+
if (retentionMap != null) {
|
| 865 |
+
BusinessMetadataInputDTO.RetentionRequirementsDTO retention = new BusinessMetadataInputDTO.RetentionRequirementsDTO();
|
| 866 |
+
retention.setLegalRetention((String) retentionMap.get("legalRetention"));
|
| 867 |
+
retention.setBusinessRetention((String) retentionMap.get("businessRetention"));
|
| 868 |
+
retention.setArchiveAfter((String) retentionMap.get("archiveAfter"));
|
| 869 |
+
retention.setDeleteAfter((String) retentionMap.get("deleteAfter"));
|
| 870 |
+
retention.setRetentionReasons((List<String>) retentionMap.get("retentionReasons"));
|
| 871 |
+
dto.setRetentionRequirements(retention);
|
| 872 |
+
}
|
| 873 |
+
|
| 874 |
+
return dto;
|
| 875 |
+
}
|
| 876 |
+
|
| 877 |
+
/**
|
| 878 |
+
* Convert storage format to ComplianceClassificationDTO
|
| 879 |
+
*/
|
| 880 |
+
@SuppressWarnings("unchecked")
|
| 881 |
+
private BusinessMetadataResponseDTO.ComplianceClassificationDTO convertStorageToComplianceDTO(Map<String, Object> storage) {
|
| 882 |
+
Map<String, Object> complianceMap = (Map<String, Object>) storage.get("complianceClassification");
|
| 883 |
+
if (complianceMap == null) {
|
| 884 |
+
return null;
|
| 885 |
+
}
|
| 886 |
+
|
| 887 |
+
BusinessMetadataResponseDTO.ComplianceClassificationDTO dto = new BusinessMetadataResponseDTO.ComplianceClassificationDTO();
|
| 888 |
+
|
| 889 |
+
dto.setDataClassification((String) complianceMap.get("dataClassification"));
|
| 890 |
+
dto.setContainsPii((Boolean) complianceMap.get("containsPii"));
|
| 891 |
+
dto.setContainsPhi((Boolean) complianceMap.get("containsPhi"));
|
| 892 |
+
dto.setContainsFinancial((Boolean) complianceMap.get("containsFinancial"));
|
| 893 |
+
dto.setGdprApplicable((Boolean) complianceMap.get("gdprApplicable"));
|
| 894 |
+
dto.setSoxRelevant((Boolean) complianceMap.get("soxRelevant"));
|
| 895 |
+
dto.setHipaaRelevant((Boolean) complianceMap.get("hipaaRelevant"));
|
| 896 |
+
dto.setPciRelevant((Boolean) complianceMap.get("pciRelevant"));
|
| 897 |
+
dto.setRegulatoryFrameworks((List<String>) complianceMap.get("regulatoryFrameworks"));
|
| 898 |
+
dto.setDataSubjectCategories((List<String>) complianceMap.get("dataSubjectCategories"));
|
| 899 |
+
dto.setPrivacyImpactLevel((String) complianceMap.get("privacyImpactLevel"));
|
| 900 |
+
|
| 901 |
+
return dto;
|
| 902 |
+
}
|
| 903 |
+
|
| 904 |
+
/**
|
| 905 |
+
* Generate audit information
|
| 906 |
+
*/
|
| 907 |
+
private BusinessMetadataResponseDTO.MetadataAuditDTO generateAuditInfo(Asset asset, UUID updaterUserId) {
|
| 908 |
+
BusinessMetadataResponseDTO.MetadataAuditDTO audit = new BusinessMetadataResponseDTO.MetadataAuditDTO();
|
| 909 |
+
|
| 910 |
+
Map<String, Object> metadata = asset.getBusinessMetadataJson();
|
| 911 |
+
if (metadata != null) {
|
| 912 |
+
// Last updated info
|
| 913 |
+
String lastUpdatedByStr = (String) metadata.get("lastUpdatedBy");
|
| 914 |
+
if (lastUpdatedByStr != null) {
|
| 915 |
+
audit.setLastUpdatedBy(UUID.fromString(lastUpdatedByStr));
|
| 916 |
+
audit.setLastUpdatedByName("System User"); // TODO: Lookup user name
|
| 917 |
+
}
|
| 918 |
+
|
| 919 |
+
String lastUpdatedAtStr = (String) metadata.get("lastUpdatedAt");
|
| 920 |
+
if (lastUpdatedAtStr != null) {
|
| 921 |
+
audit.setLastUpdatedAt(Instant.parse(lastUpdatedAtStr));
|
| 922 |
+
}
|
| 923 |
+
|
| 924 |
+
audit.setUpdateReason((String) metadata.get("updateReason"));
|
| 925 |
+
|
| 926 |
+
Object versionObj = metadata.get("versionNumber");
|
| 927 |
+
if (versionObj instanceof Number) {
|
| 928 |
+
audit.setVersionNumber(((Number) versionObj).intValue());
|
| 929 |
+
}
|
| 930 |
+
}
|
| 931 |
+
|
| 932 |
+
// Created info from asset
|
| 933 |
+
audit.setCreatedBy(asset.getCreatedByUserId());
|
| 934 |
+
audit.setCreatedByName("System User"); // TODO: Lookup user name
|
| 935 |
+
audit.setCreatedAt(asset.getCreatedTs());
|
| 936 |
+
|
| 937 |
+
// Generate change history
|
| 938 |
+
audit.setChangeHistory(generateChangeHistory(asset));
|
| 939 |
+
|
| 940 |
+
return audit;
|
| 941 |
+
}
|
| 942 |
+
|
| 943 |
+
/**
|
| 944 |
+
* Generate validation status for metadata completeness
|
| 945 |
+
*/
|
| 946 |
+
private BusinessMetadataResponseDTO.ValidationStatusDTO generateValidationStatus(Map<String, Object> metadata) {
|
| 947 |
+
BusinessMetadataResponseDTO.ValidationStatusDTO validation = new BusinessMetadataResponseDTO.ValidationStatusDTO();
|
| 948 |
+
|
| 949 |
+
List<String> missingFields = new ArrayList<>();
|
| 950 |
+
List<String> warnings = new ArrayList<>();
|
| 951 |
+
List<String> errors = new ArrayList<>();
|
| 952 |
+
|
| 953 |
+
int totalFields = 15; // Total important fields to check
|
| 954 |
+
int presentFields = 0;
|
| 955 |
+
|
| 956 |
+
if (metadata == null || metadata.isEmpty()) {
|
| 957 |
+
validation.setIsComplete(false);
|
| 958 |
+
validation.setCompletenessScore(0.0);
|
| 959 |
+
validation.setOverallStatus("INCOMPLETE");
|
| 960 |
+
missingFields.add("All business metadata fields are missing");
|
| 961 |
+
validation.setMissingFields(missingFields);
|
| 962 |
+
validation.setValidationWarnings(warnings);
|
| 963 |
+
validation.setValidationErrors(errors);
|
| 964 |
+
return validation;
|
| 965 |
+
}
|
| 966 |
+
|
| 967 |
+
// Check core business metadata
|
| 968 |
+
if (metadata.get("businessName") != null) presentFields++;
|
| 969 |
+
else missingFields.add("businessName");
|
| 970 |
+
|
| 971 |
+
if (metadata.get("businessDescription") != null) presentFields++;
|
| 972 |
+
else missingFields.add("businessDescription");
|
| 973 |
+
|
| 974 |
+
if (metadata.get("businessCriticality") != null) presentFields++;
|
| 975 |
+
else missingFields.add("businessCriticality");
|
| 976 |
+
|
| 977 |
+
if (metadata.get("businessDomain") != null) presentFields++;
|
| 978 |
+
else missingFields.add("businessDomain");
|
| 979 |
+
|
| 980 |
+
// Check steward relationships
|
| 981 |
+
Map<String, Object> owner = (Map<String, Object>) metadata.get("businessOwner");
|
| 982 |
+
if (owner != null && owner.get("name") != null && owner.get("email") != null) {
|
| 983 |
+
presentFields++;
|
| 984 |
+
} else {
|
| 985 |
+
missingFields.add("businessOwner");
|
| 986 |
+
}
|
| 987 |
+
|
| 988 |
+
Map<String, Object> steward = (Map<String, Object>) metadata.get("dataSteward");
|
| 989 |
+
if (steward != null && steward.get("name") != null && steward.get("email") != null) {
|
| 990 |
+
presentFields++;
|
| 991 |
+
} else {
|
| 992 |
+
missingFields.add("dataSteward");
|
| 993 |
+
}
|
| 994 |
+
|
| 995 |
+
// Check compliance classification
|
| 996 |
+
Map<String, Object> compliance = (Map<String, Object>) metadata.get("complianceClassification");
|
| 997 |
+
if (compliance != null && compliance.get("dataClassification") != null) {
|
| 998 |
+
presentFields++;
|
| 999 |
+
} else {
|
| 1000 |
+
missingFields.add("complianceClassification");
|
| 1001 |
+
}
|
| 1002 |
+
|
| 1003 |
+
// Calculate completeness
|
| 1004 |
+
double completenessScore = (double) presentFields / totalFields;
|
| 1005 |
+
validation.setCompletenessScore(completenessScore);
|
| 1006 |
+
validation.setIsComplete(completenessScore >= 0.8);
|
| 1007 |
+
|
| 1008 |
+
// Determine overall status
|
| 1009 |
+
if (completenessScore >= 0.9) {
|
| 1010 |
+
validation.setOverallStatus("COMPLETE");
|
| 1011 |
+
} else if (completenessScore >= 0.6) {
|
| 1012 |
+
validation.setOverallStatus("PARTIAL");
|
| 1013 |
+
} else {
|
| 1014 |
+
validation.setOverallStatus("INCOMPLETE");
|
| 1015 |
+
}
|
| 1016 |
+
|
| 1017 |
+
// Add warnings for low-quality data
|
| 1018 |
+
Object qualityScore = metadata.get("dataQualityScore");
|
| 1019 |
+
if (qualityScore instanceof Number && ((Number) qualityScore).doubleValue() < 5.0) {
|
| 1020 |
+
warnings.add("Data quality score is below recommended threshold (5.0)");
|
| 1021 |
+
}
|
| 1022 |
+
|
| 1023 |
+
validation.setMissingFields(missingFields);
|
| 1024 |
+
validation.setValidationWarnings(warnings);
|
| 1025 |
+
validation.setValidationErrors(errors);
|
| 1026 |
+
|
| 1027 |
+
return validation;
|
| 1028 |
+
}
|
| 1029 |
+
|
| 1030 |
+
/**
|
| 1031 |
+
* Generate metadata recommendations
|
| 1032 |
+
*/
|
| 1033 |
+
private List<BusinessMetadataResponseDTO.RecommendationDTO> generateMetadataRecommendations(Asset asset) {
|
| 1034 |
+
List<BusinessMetadataResponseDTO.RecommendationDTO> recommendations = new ArrayList<>();
|
| 1035 |
+
|
| 1036 |
+
Map<String, Object> metadata = asset.getBusinessMetadataJson();
|
| 1037 |
+
|
| 1038 |
+
if (metadata == null || metadata.isEmpty()) {
|
| 1039 |
+
recommendations.add(new BusinessMetadataResponseDTO.RecommendationDTO(
|
| 1040 |
+
"COMPLETENESS", "HIGH", "Missing Business Metadata",
|
| 1041 |
+
"This asset has no business metadata defined. Adding business context will improve governance and discoverability.",
|
| 1042 |
+
"Add business owner, data steward, business description, and compliance classification",
|
| 1043 |
+
"Improved data governance, compliance reporting, and asset discoverability"
|
| 1044 |
+
));
|
| 1045 |
+
return recommendations;
|
| 1046 |
+
}
|
| 1047 |
+
|
| 1048 |
+
// Check for missing steward
|
| 1049 |
+
if (metadata.get("dataSteward") == null) {
|
| 1050 |
+
recommendations.add(new BusinessMetadataResponseDTO.RecommendationDTO(
|
| 1051 |
+
"GOVERNANCE", "HIGH", "Missing Data Steward",
|
| 1052 |
+
"No data steward assigned to this asset. A data steward is responsible for data quality and governance.",
|
| 1053 |
+
"Assign a qualified data steward with appropriate technical and business knowledge",
|
| 1054 |
+
"Better data quality management and faster issue resolution"
|
| 1055 |
+
));
|
| 1056 |
+
}
|
| 1057 |
+
|
| 1058 |
+
// Check for missing compliance classification
|
| 1059 |
+
if (metadata.get("complianceClassification") == null) {
|
| 1060 |
+
recommendations.add(new BusinessMetadataResponseDTO.RecommendationDTO(
|
| 1061 |
+
"COMPLIANCE", "HIGH", "Missing Compliance Classification",
|
| 1062 |
+
"Asset lacks compliance classification which is required for regulatory reporting and risk management.",
|
| 1063 |
+
"Define data classification, PII/PHI status, and applicable regulatory frameworks",
|
| 1064 |
+
"Improved compliance posture and reduced regulatory risk"
|
| 1065 |
+
));
|
| 1066 |
+
}
|
| 1067 |
+
|
| 1068 |
+
// Check for low data quality score
|
| 1069 |
+
Object qualityScore = metadata.get("dataQualityScore");
|
| 1070 |
+
if (qualityScore instanceof Number && ((Number) qualityScore).doubleValue() < 6.0) {
|
| 1071 |
+
recommendations.add(new BusinessMetadataResponseDTO.RecommendationDTO(
|
| 1072 |
+
"QUALITY", "MEDIUM", "Low Data Quality Score",
|
| 1073 |
+
"Data quality score is below recommended threshold. Consider implementing data quality improvements.",
|
| 1074 |
+
"Review data quality issues, implement validation rules, and establish monitoring",
|
| 1075 |
+
"Increased trust in data and better decision-making outcomes"
|
| 1076 |
+
));
|
| 1077 |
+
}
|
| 1078 |
+
|
| 1079 |
+
// Check for missing business description
|
| 1080 |
+
if (metadata.get("businessDescription") == null || ((String) metadata.get("businessDescription")).trim().isEmpty()) {
|
| 1081 |
+
recommendations.add(new BusinessMetadataResponseDTO.RecommendationDTO(
|
| 1082 |
+
"COMPLETENESS", "MEDIUM", "Missing Business Description",
|
| 1083 |
+
"Asset lacks a business description which helps users understand its purpose and context.",
|
| 1084 |
+
"Add a comprehensive business description explaining the asset's purpose and use cases",
|
| 1085 |
+
"Improved asset discoverability and user understanding"
|
| 1086 |
+
));
|
| 1087 |
+
}
|
| 1088 |
+
|
| 1089 |
+
return recommendations;
|
| 1090 |
+
}
|
| 1091 |
+
|
| 1092 |
+
/**
|
| 1093 |
+
* Get current metadata version number
|
| 1094 |
+
*/
|
| 1095 |
+
private Integer getCurrentMetadataVersion(Map<String, Object> metadata) {
|
| 1096 |
+
if (metadata == null) {
|
| 1097 |
+
return 0;
|
| 1098 |
+
}
|
| 1099 |
+
|
| 1100 |
+
Object version = metadata.get("versionNumber");
|
| 1101 |
+
if (version instanceof Number) {
|
| 1102 |
+
return ((Number) version).intValue();
|
| 1103 |
+
}
|
| 1104 |
+
|
| 1105 |
+
return 0;
|
| 1106 |
+
}
|
| 1107 |
+
|
| 1108 |
+
/**
|
| 1109 |
+
* Generate change history summary
|
| 1110 |
+
*/
|
| 1111 |
+
private List<String> generateChangeHistory(Asset asset) {
|
| 1112 |
+
List<String> history = new ArrayList<>();
|
| 1113 |
+
|
| 1114 |
+
// For now, generate basic history based on asset timestamps
|
| 1115 |
+
if (asset.getCreatedTs() != null) {
|
| 1116 |
+
history.add("Asset created on " + asset.getCreatedTs());
|
| 1117 |
+
}
|
| 1118 |
+
|
| 1119 |
+
if (asset.getLastModifiedTs() != null && !asset.getLastModifiedTs().equals(asset.getCreatedTs())) {
|
| 1120 |
+
history.add("Last modified on " + asset.getLastModifiedTs());
|
| 1121 |
+
}
|
| 1122 |
+
|
| 1123 |
+
// TODO: Implement proper change tracking for business metadata
|
| 1124 |
+
Map<String, Object> metadata = asset.getBusinessMetadataJson();
|
| 1125 |
+
if (metadata != null && metadata.get("lastUpdatedAt") != null) {
|
| 1126 |
+
history.add("Business metadata last updated on " + metadata.get("lastUpdatedAt"));
|
| 1127 |
+
}
|
| 1128 |
+
|
| 1129 |
+
return history;
|
| 1130 |
+
}
|
| 1131 |
+
|
| 1132 |
+
@Override
|
| 1133 |
+
public AssetSchemaDTO getAssetSchema(UUID assetId) {
|
| 1134 |
+
// Check if asset exists
|
| 1135 |
+
if (!assetRepository.existsById(assetId)) {
|
| 1136 |
+
throw new ResourceNotFoundException("Asset", "id", assetId.toString());
|
| 1137 |
+
}
|
| 1138 |
+
|
| 1139 |
+
Asset asset = assetRepository.findById(assetId)
|
| 1140 |
+
.orElseThrow(() -> new ResourceNotFoundException("Asset", "id", assetId.toString()));
|
| 1141 |
+
|
| 1142 |
+
// Generate comprehensive schema information
|
| 1143 |
+
return createAssetSchema(asset);
|
| 1144 |
+
}
|
| 1145 |
+
|
| 1146 |
+
/**
|
| 1147 |
+
* Creates comprehensive asset schema information with realistic mock data.
|
| 1148 |
+
* Demonstrates all aspects of the AssetSchemaDTO specification for Priority 2 implementation.
|
| 1149 |
+
*/
|
| 1150 |
+
private AssetSchemaDTO createAssetSchema(Asset asset) {
|
| 1151 |
+
AssetSchemaDTO schema = new AssetSchemaDTO();
|
| 1152 |
+
schema.setAssetId(asset.getAssetId());
|
| 1153 |
+
schema.setAssetName(asset.getAssetName());
|
| 1154 |
+
schema.setSchemaVersion("2.1.0");
|
| 1155 |
+
schema.setLastUpdated(Instant.now().minus(java.time.Duration.ofDays(2)));
|
| 1156 |
+
schema.setLastAnalyzed(Instant.now().minus(java.time.Duration.ofHours(6)));
|
| 1157 |
+
schema.setSource("AUTOMATED_DISCOVERY");
|
| 1158 |
+
|
| 1159 |
+
// Schema Type Information
|
| 1160 |
+
AssetSchemaDTO.SchemaTypeDTO schemaType = new AssetSchemaDTO.SchemaTypeDTO();
|
| 1161 |
+
schemaType.setType("TABLE");
|
| 1162 |
+
schemaType.setFormat("PARQUET");
|
| 1163 |
+
schemaType.setEncoding("UTF-8");
|
| 1164 |
+
Map<String, Object> formatProps = new HashMap<>();
|
| 1165 |
+
formatProps.put("compression", "SNAPPY");
|
| 1166 |
+
formatProps.put("blockSize", "134217728");
|
| 1167 |
+
formatProps.put("pageSize", "1048576");
|
| 1168 |
+
schemaType.setFormatSpecificProperties(formatProps);
|
| 1169 |
+
schema.setSchemaType(schemaType);
|
| 1170 |
+
|
| 1171 |
+
// Schema Fields with comprehensive metadata
|
| 1172 |
+
List<AssetSchemaDTO.SchemaFieldDTO> fields = createSchemaFields(asset);
|
| 1173 |
+
schema.setFields(fields);
|
| 1174 |
+
|
| 1175 |
+
// Schema Constraints
|
| 1176 |
+
List<AssetSchemaDTO.SchemaConstraintDTO> constraints = createSchemaConstraints();
|
| 1177 |
+
schema.setConstraints(constraints);
|
| 1178 |
+
|
| 1179 |
+
// Schema Indexes
|
| 1180 |
+
List<AssetSchemaDTO.SchemaIndexDTO> indexes = createSchemaIndexes();
|
| 1181 |
+
schema.setIndexes(indexes);
|
| 1182 |
+
|
| 1183 |
+
// Schema Statistics
|
| 1184 |
+
AssetSchemaDTO.SchemaStatisticsDTO statistics = createSchemaStatistics();
|
| 1185 |
+
schema.setStatistics(statistics);
|
| 1186 |
+
|
| 1187 |
+
// Schema Lineage
|
| 1188 |
+
AssetSchemaDTO.SchemaLineageDTO lineage = createSchemaLineage(asset);
|
| 1189 |
+
schema.setLineage(lineage);
|
| 1190 |
+
|
| 1191 |
+
// Schema Evolution
|
| 1192 |
+
AssetSchemaDTO.SchemaEvolutionDTO evolution = createSchemaEvolution();
|
| 1193 |
+
schema.setEvolution(evolution);
|
| 1194 |
+
|
| 1195 |
+
// Additional metadata
|
| 1196 |
+
Map<String, Object> metadata = new HashMap<>();
|
| 1197 |
+
metadata.put("tableType", "EXTERNAL");
|
| 1198 |
+
metadata.put("location", "s3://data-lake/sales/customer_orders/");
|
| 1199 |
+
metadata.put("partitioned", true);
|
| 1200 |
+
metadata.put("partitionKeys", Arrays.asList("year", "month", "day"));
|
| 1201 |
+
metadata.put("fileFormat", "PARQUET");
|
| 1202 |
+
metadata.put("serde", "org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe");
|
| 1203 |
+
schema.setMetadata(metadata);
|
| 1204 |
+
|
| 1205 |
+
return schema;
|
| 1206 |
+
}
|
| 1207 |
+
|
| 1208 |
+
/**
|
| 1209 |
+
* Creates detailed schema fields with comprehensive metadata and statistics.
|
| 1210 |
+
*/
|
| 1211 |
+
private List<AssetSchemaDTO.SchemaFieldDTO> createSchemaFields(Asset asset) {
|
| 1212 |
+
List<AssetSchemaDTO.SchemaFieldDTO> fields = new ArrayList<>();
|
| 1213 |
+
|
| 1214 |
+
// Primary key field
|
| 1215 |
+
AssetSchemaDTO.SchemaFieldDTO idField = new AssetSchemaDTO.SchemaFieldDTO();
|
| 1216 |
+
idField.setFieldName("customer_id");
|
| 1217 |
+
idField.setDataType("BIGINT");
|
| 1218 |
+
idField.setLogicalType("IDENTIFIER");
|
| 1219 |
+
idField.setNullable(false);
|
| 1220 |
+
idField.setPrimaryKey(true);
|
| 1221 |
+
idField.setForeignKey(false);
|
| 1222 |
+
idField.setDescription("Unique identifier for customer");
|
| 1223 |
+
idField.setTags(Arrays.asList("PII", "PRIMARY_KEY", "IDENTIFIER"));
|
| 1224 |
+
idField.setOrdinalPosition(1);
|
| 1225 |
+
idField.setClassification("PII");
|
| 1226 |
+
|
| 1227 |
+
// Field statistics
|
| 1228 |
+
AssetSchemaDTO.FieldStatisticsDTO idStats = new AssetSchemaDTO.FieldStatisticsDTO();
|
| 1229 |
+
idStats.setDistinctCount(125000L);
|
| 1230 |
+
idStats.setNullCount(0L);
|
| 1231 |
+
idStats.setFillRate(100.0);
|
| 1232 |
+
idStats.setMinValue("1000001");
|
| 1233 |
+
idStats.setMaxValue("1125000");
|
| 1234 |
+
idStats.setLastAnalyzed(Instant.now().minus(java.time.Duration.ofHours(6)));
|
| 1235 |
+
idField.setStatistics(idStats);
|
| 1236 |
+
|
| 1237 |
+
fields.add(idField);
|
| 1238 |
+
|
| 1239 |
+
// Email field with constraints
|
| 1240 |
+
AssetSchemaDTO.SchemaFieldDTO emailField = new AssetSchemaDTO.SchemaFieldDTO();
|
| 1241 |
+
emailField.setFieldName("email");
|
| 1242 |
+
emailField.setDataType("VARCHAR");
|
| 1243 |
+
emailField.setLogicalType("EMAIL");
|
| 1244 |
+
emailField.setNullable(false);
|
| 1245 |
+
emailField.setPrimaryKey(false);
|
| 1246 |
+
emailField.setForeignKey(false);
|
| 1247 |
+
emailField.setDescription("Customer email address");
|
| 1248 |
+
emailField.setTags(Arrays.asList("PII", "CONTACT", "UNIQUE"));
|
| 1249 |
+
emailField.setOrdinalPosition(2);
|
| 1250 |
+
emailField.setClassification("PII");
|
| 1251 |
+
|
| 1252 |
+
// Field constraints
|
| 1253 |
+
AssetSchemaDTO.FieldConstraintsDTO emailConstraints = new AssetSchemaDTO.FieldConstraintsDTO();
|
| 1254 |
+
emailConstraints.setPattern("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
|
| 1255 |
+
emailConstraints.setMaxLength(255);
|
| 1256 |
+
emailConstraints.setFormat("EMAIL");
|
| 1257 |
+
emailField.setConstraints(emailConstraints);
|
| 1258 |
+
|
| 1259 |
+
// Email statistics
|
| 1260 |
+
AssetSchemaDTO.FieldStatisticsDTO emailStats = new AssetSchemaDTO.FieldStatisticsDTO();
|
| 1261 |
+
emailStats.setDistinctCount(124500L);
|
| 1262 |
+
emailStats.setNullCount(0L);
|
| 1263 |
+
emailStats.setFillRate(100.0);
|
| 1264 |
+
emailStats.setAvgLength(28.5);
|
| 1265 |
+
emailStats.setMaxLength(64.0);
|
| 1266 |
+
emailStats.setTopValues(Arrays.asList("gmail.com", "yahoo.com", "outlook.com", "company.com", "hotmail.com"));
|
| 1267 |
+
emailStats.setLastAnalyzed(Instant.now().minus(java.time.Duration.ofHours(6)));
|
| 1268 |
+
emailField.setStatistics(emailStats);
|
| 1269 |
+
|
| 1270 |
+
fields.add(emailField);
|
| 1271 |
+
|
| 1272 |
+
// Date field
|
| 1273 |
+
AssetSchemaDTO.SchemaFieldDTO createdField = new AssetSchemaDTO.SchemaFieldDTO();
|
| 1274 |
+
createdField.setFieldName("created_date");
|
| 1275 |
+
createdField.setDataType("DATE");
|
| 1276 |
+
createdField.setLogicalType("TIMESTAMP");
|
| 1277 |
+
createdField.setNullable(false);
|
| 1278 |
+
createdField.setDefaultValue("CURRENT_DATE");
|
| 1279 |
+
createdField.setDescription("Date when customer record was created");
|
| 1280 |
+
createdField.setTags(Arrays.asList("AUDIT", "TEMPORAL"));
|
| 1281 |
+
createdField.setOrdinalPosition(3);
|
| 1282 |
+
createdField.setClassification("PUBLIC");
|
| 1283 |
+
|
| 1284 |
+
// Date statistics
|
| 1285 |
+
AssetSchemaDTO.FieldStatisticsDTO dateStats = new AssetSchemaDTO.FieldStatisticsDTO();
|
| 1286 |
+
dateStats.setDistinctCount(365L);
|
| 1287 |
+
dateStats.setNullCount(0L);
|
| 1288 |
+
dateStats.setFillRate(100.0);
|
| 1289 |
+
dateStats.setMinValue("2023-01-01");
|
| 1290 |
+
dateStats.setMaxValue("2024-12-31");
|
| 1291 |
+
dateStats.setLastAnalyzed(Instant.now().minus(java.time.Duration.ofHours(6)));
|
| 1292 |
+
createdField.setStatistics(dateStats);
|
| 1293 |
+
|
| 1294 |
+
fields.add(createdField);
|
| 1295 |
+
|
| 1296 |
+
// Numeric field with business logic
|
| 1297 |
+
AssetSchemaDTO.SchemaFieldDTO totalSpentField = new AssetSchemaDTO.SchemaFieldDTO();
|
| 1298 |
+
totalSpentField.setFieldName("total_spent");
|
| 1299 |
+
totalSpentField.setDataType("DECIMAL");
|
| 1300 |
+
totalSpentField.setLogicalType("CURRENCY");
|
| 1301 |
+
totalSpentField.setNullable(true);
|
| 1302 |
+
totalSpentField.setDefaultValue("0.00");
|
| 1303 |
+
totalSpentField.setDescription("Total amount spent by customer");
|
| 1304 |
+
totalSpentField.setTags(Arrays.asList("FINANCIAL", "CALCULATED", "KPI"));
|
| 1305 |
+
totalSpentField.setOrdinalPosition(4);
|
| 1306 |
+
totalSpentField.setClassification("SENSITIVE");
|
| 1307 |
+
|
| 1308 |
+
// Numeric constraints
|
| 1309 |
+
AssetSchemaDTO.FieldConstraintsDTO numericConstraints = new AssetSchemaDTO.FieldConstraintsDTO();
|
| 1310 |
+
numericConstraints.setMinValue("0.00");
|
| 1311 |
+
numericConstraints.setMaxValue("999999.99");
|
| 1312 |
+
Map<String, Object> customConstraints = new HashMap<>();
|
| 1313 |
+
customConstraints.put("precision", 10);
|
| 1314 |
+
customConstraints.put("scale", 2);
|
| 1315 |
+
customConstraints.put("currency", "USD");
|
| 1316 |
+
numericConstraints.setCustomConstraints(customConstraints);
|
| 1317 |
+
totalSpentField.setConstraints(numericConstraints);
|
| 1318 |
+
|
| 1319 |
+
// Numeric statistics
|
| 1320 |
+
AssetSchemaDTO.FieldStatisticsDTO numericStats = new AssetSchemaDTO.FieldStatisticsDTO();
|
| 1321 |
+
numericStats.setDistinctCount(85000L);
|
| 1322 |
+
numericStats.setNullCount(1250L);
|
| 1323 |
+
numericStats.setFillRate(99.0);
|
| 1324 |
+
numericStats.setMinValue("0.00");
|
| 1325 |
+
numericStats.setMaxValue("45678.99");
|
| 1326 |
+
numericStats.setLastAnalyzed(Instant.now().minus(java.time.Duration.ofHours(6)));
|
| 1327 |
+
totalSpentField.setStatistics(numericStats);
|
| 1328 |
+
|
| 1329 |
+
fields.add(totalSpentField);
|
| 1330 |
+
|
| 1331 |
+
// Categorical field with enum values
|
| 1332 |
+
AssetSchemaDTO.SchemaFieldDTO statusField = new AssetSchemaDTO.SchemaFieldDTO();
|
| 1333 |
+
statusField.setFieldName("status");
|
| 1334 |
+
statusField.setDataType("VARCHAR");
|
| 1335 |
+
statusField.setLogicalType("ENUM");
|
| 1336 |
+
statusField.setNullable(false);
|
| 1337 |
+
statusField.setDefaultValue("ACTIVE");
|
| 1338 |
+
statusField.setDescription("Customer account status");
|
| 1339 |
+
statusField.setTags(Arrays.asList("STATUS", "ENUM", "BUSINESS_RULE"));
|
| 1340 |
+
statusField.setOrdinalPosition(5);
|
| 1341 |
+
statusField.setClassification("PUBLIC");
|
| 1342 |
+
|
| 1343 |
+
// Enum constraints
|
| 1344 |
+
AssetSchemaDTO.FieldConstraintsDTO enumConstraints = new AssetSchemaDTO.FieldConstraintsDTO();
|
| 1345 |
+
enumConstraints.setEnumValues(Arrays.asList("ACTIVE", "INACTIVE", "SUSPENDED", "CLOSED"));
|
| 1346 |
+
enumConstraints.setMaxLength(10);
|
| 1347 |
+
statusField.setConstraints(enumConstraints);
|
| 1348 |
+
|
| 1349 |
+
// Status statistics
|
| 1350 |
+
AssetSchemaDTO.FieldStatisticsDTO statusStats = new AssetSchemaDTO.FieldStatisticsDTO();
|
| 1351 |
+
statusStats.setDistinctCount(4L);
|
| 1352 |
+
statusStats.setNullCount(0L);
|
| 1353 |
+
statusStats.setFillRate(100.0);
|
| 1354 |
+
statusStats.setTopValues(Arrays.asList("ACTIVE (85%)", "INACTIVE (10%)", "SUSPENDED (3%)", "CLOSED (2%)"));
|
| 1355 |
+
statusStats.setLastAnalyzed(Instant.now().minus(java.time.Duration.ofHours(6)));
|
| 1356 |
+
statusField.setStatistics(statusStats);
|
| 1357 |
+
|
| 1358 |
+
fields.add(statusField);
|
| 1359 |
+
|
| 1360 |
+
return fields;
|
| 1361 |
+
}
|
| 1362 |
+
|
| 1363 |
+
/**
|
| 1364 |
+
* Creates schema-level constraints including primary keys, foreign keys, and check constraints.
|
| 1365 |
+
*/
|
| 1366 |
+
private List<AssetSchemaDTO.SchemaConstraintDTO> createSchemaConstraints() {
|
| 1367 |
+
List<AssetSchemaDTO.SchemaConstraintDTO> constraints = new ArrayList<>();
|
| 1368 |
+
|
| 1369 |
+
// Primary key constraint
|
| 1370 |
+
AssetSchemaDTO.SchemaConstraintDTO pkConstraint = new AssetSchemaDTO.SchemaConstraintDTO();
|
| 1371 |
+
pkConstraint.setConstraintName("pk_customer_id");
|
| 1372 |
+
pkConstraint.setConstraintType("PRIMARY_KEY");
|
| 1373 |
+
pkConstraint.setColumnNames(Arrays.asList("customer_id"));
|
| 1374 |
+
pkConstraint.setEnforced(true);
|
| 1375 |
+
constraints.add(pkConstraint);
|
| 1376 |
+
|
| 1377 |
+
// Unique constraint on email
|
| 1378 |
+
AssetSchemaDTO.SchemaConstraintDTO uniqueEmailConstraint = new AssetSchemaDTO.SchemaConstraintDTO();
|
| 1379 |
+
uniqueEmailConstraint.setConstraintName("uk_customer_email");
|
| 1380 |
+
uniqueEmailConstraint.setConstraintType("UNIQUE");
|
| 1381 |
+
uniqueEmailConstraint.setColumnNames(Arrays.asList("email"));
|
| 1382 |
+
uniqueEmailConstraint.setEnforced(true);
|
| 1383 |
+
constraints.add(uniqueEmailConstraint);
|
| 1384 |
+
|
| 1385 |
+
// Check constraint on total_spent
|
| 1386 |
+
AssetSchemaDTO.SchemaConstraintDTO checkConstraint = new AssetSchemaDTO.SchemaConstraintDTO();
|
| 1387 |
+
checkConstraint.setConstraintName("chk_total_spent_positive");
|
| 1388 |
+
checkConstraint.setConstraintType("CHECK");
|
| 1389 |
+
checkConstraint.setColumnNames(Arrays.asList("total_spent"));
|
| 1390 |
+
checkConstraint.setCheckExpression("total_spent >= 0");
|
| 1391 |
+
checkConstraint.setEnforced(true);
|
| 1392 |
+
constraints.add(checkConstraint);
|
| 1393 |
+
|
| 1394 |
+
// Foreign key constraint (example)
|
| 1395 |
+
AssetSchemaDTO.SchemaConstraintDTO fkConstraint = new AssetSchemaDTO.SchemaConstraintDTO();
|
| 1396 |
+
fkConstraint.setConstraintName("fk_customer_segment");
|
| 1397 |
+
fkConstraint.setConstraintType("FOREIGN_KEY");
|
| 1398 |
+
fkConstraint.setColumnNames(Arrays.asList("segment_id"));
|
| 1399 |
+
fkConstraint.setReferencedTable("customer_segments");
|
| 1400 |
+
fkConstraint.setReferencedColumns(Arrays.asList("segment_id"));
|
| 1401 |
+
fkConstraint.setEnforced(true);
|
| 1402 |
+
constraints.add(fkConstraint);
|
| 1403 |
+
|
| 1404 |
+
return constraints;
|
| 1405 |
+
}
|
| 1406 |
+
|
| 1407 |
+
/**
|
| 1408 |
+
* Creates schema indexes for performance optimization.
|
| 1409 |
+
*/
|
| 1410 |
+
private List<AssetSchemaDTO.SchemaIndexDTO> createSchemaIndexes() {
|
| 1411 |
+
List<AssetSchemaDTO.SchemaIndexDTO> indexes = new ArrayList<>();
|
| 1412 |
+
|
| 1413 |
+
// Primary key index
|
| 1414 |
+
AssetSchemaDTO.SchemaIndexDTO pkIndex = new AssetSchemaDTO.SchemaIndexDTO();
|
| 1415 |
+
pkIndex.setIndexName("idx_customer_id_pk");
|
| 1416 |
+
pkIndex.setIndexType("BTREE");
|
| 1417 |
+
pkIndex.setColumnNames(Arrays.asList("customer_id"));
|
| 1418 |
+
pkIndex.setUnique(true);
|
| 1419 |
+
pkIndex.setPrimary(true);
|
| 1420 |
+
pkIndex.setCardinality(125000L);
|
| 1421 |
+
indexes.add(pkIndex);
|
| 1422 |
+
|
| 1423 |
+
// Email unique index
|
| 1424 |
+
AssetSchemaDTO.SchemaIndexDTO emailIndex = new AssetSchemaDTO.SchemaIndexDTO();
|
| 1425 |
+
emailIndex.setIndexName("idx_customer_email_unique");
|
| 1426 |
+
emailIndex.setIndexType("BTREE");
|
| 1427 |
+
emailIndex.setColumnNames(Arrays.asList("email"));
|
| 1428 |
+
emailIndex.setUnique(true);
|
| 1429 |
+
emailIndex.setPrimary(false);
|
| 1430 |
+
emailIndex.setCardinality(124500L);
|
| 1431 |
+
indexes.add(emailIndex);
|
| 1432 |
+
|
| 1433 |
+
// Status index for filtering
|
| 1434 |
+
AssetSchemaDTO.SchemaIndexDTO statusIndex = new AssetSchemaDTO.SchemaIndexDTO();
|
| 1435 |
+
statusIndex.setIndexName("idx_customer_status");
|
| 1436 |
+
statusIndex.setIndexType("BTREE");
|
| 1437 |
+
statusIndex.setColumnNames(Arrays.asList("status"));
|
| 1438 |
+
statusIndex.setUnique(false);
|
| 1439 |
+
statusIndex.setPrimary(false);
|
| 1440 |
+
statusIndex.setCardinality(4L);
|
| 1441 |
+
indexes.add(statusIndex);
|
| 1442 |
+
|
| 1443 |
+
// Composite index for date range queries
|
| 1444 |
+
AssetSchemaDTO.SchemaIndexDTO dateRangeIndex = new AssetSchemaDTO.SchemaIndexDTO();
|
| 1445 |
+
dateRangeIndex.setIndexName("idx_customer_created_status");
|
| 1446 |
+
dateRangeIndex.setIndexType("BTREE");
|
| 1447 |
+
dateRangeIndex.setColumnNames(Arrays.asList("created_date", "status"));
|
| 1448 |
+
dateRangeIndex.setUnique(false);
|
| 1449 |
+
dateRangeIndex.setPrimary(false);
|
| 1450 |
+
dateRangeIndex.setCardinality(1460L); // 365 days * 4 statuses
|
| 1451 |
+
Map<String, Object> indexProps = new HashMap<>();
|
| 1452 |
+
indexProps.put("usage", "date_range_filtering");
|
| 1453 |
+
indexProps.put("selectivity", "high");
|
| 1454 |
+
dateRangeIndex.setProperties(indexProps);
|
| 1455 |
+
indexes.add(dateRangeIndex);
|
| 1456 |
+
|
| 1457 |
+
return indexes;
|
| 1458 |
+
}
|
| 1459 |
+
|
| 1460 |
+
/**
|
| 1461 |
+
* Creates comprehensive schema-level statistics.
|
| 1462 |
+
*/
|
| 1463 |
+
private AssetSchemaDTO.SchemaStatisticsDTO createSchemaStatistics() {
|
| 1464 |
+
AssetSchemaDTO.SchemaStatisticsDTO statistics = new AssetSchemaDTO.SchemaStatisticsDTO();
|
| 1465 |
+
|
| 1466 |
+
statistics.setTotalRows(125000L);
|
| 1467 |
+
statistics.setTotalColumns(5L);
|
| 1468 |
+
statistics.setTotalSize(52428800L); // 50MB
|
| 1469 |
+
statistics.setCompressionRatio(0.75); // 75% compression
|
| 1470 |
+
statistics.setLastUpdated(Instant.now().minus(java.time.Duration.ofDays(1)));
|
| 1471 |
+
statistics.setLastAnalyzed(Instant.now().minus(java.time.Duration.ofHours(6)));
|
| 1472 |
+
|
| 1473 |
+
Map<String, Long> dataTypeCounts = new HashMap<>();
|
| 1474 |
+
dataTypeCounts.put("BIGINT", 1L);
|
| 1475 |
+
dataTypeCounts.put("VARCHAR", 2L);
|
| 1476 |
+
dataTypeCounts.put("DATE", 1L);
|
| 1477 |
+
dataTypeCounts.put("DECIMAL", 1L);
|
| 1478 |
+
statistics.setDataTypeCounts(dataTypeCounts);
|
| 1479 |
+
|
| 1480 |
+
statistics.setSchemaComplexity(2.5); // Medium complexity
|
| 1481 |
+
statistics.setSchemaDepth(1); // Flat table structure
|
| 1482 |
+
|
| 1483 |
+
return statistics;
|
| 1484 |
+
}
|
| 1485 |
+
|
| 1486 |
+
/**
|
| 1487 |
+
* Creates schema lineage information showing relationships and transformations.
|
| 1488 |
+
*/
|
| 1489 |
+
private AssetSchemaDTO.SchemaLineageDTO createSchemaLineage(Asset asset) {
|
| 1490 |
+
AssetSchemaDTO.SchemaLineageDTO lineage = new AssetSchemaDTO.SchemaLineageDTO();
|
| 1491 |
+
|
| 1492 |
+
// Upstream relationships
|
| 1493 |
+
List<AssetSchemaDTO.SchemaRelationshipDTO> upstream = new ArrayList<>();
|
| 1494 |
+
|
| 1495 |
+
AssetSchemaDTO.SchemaRelationshipDTO upstreamRel = new AssetSchemaDTO.SchemaRelationshipDTO();
|
| 1496 |
+
upstreamRel.setRelatedAssetId(UUID.randomUUID());
|
| 1497 |
+
upstreamRel.setRelatedAssetName("raw_customer_data");
|
| 1498 |
+
upstreamRel.setRelationshipType("DERIVED_FROM");
|
| 1499 |
+
upstreamRel.setTransformationType("ETL");
|
| 1500 |
+
upstreamRel.setRelationshipCreated(Instant.now().minus(java.time.Duration.ofDays(30)));
|
| 1501 |
+
|
| 1502 |
+
// Field mappings
|
| 1503 |
+
List<AssetSchemaDTO.FieldMappingDTO> fieldMappings = new ArrayList<>();
|
| 1504 |
+
fieldMappings.add(new AssetSchemaDTO.FieldMappingDTO("cust_id", "customer_id", "DIRECT"));
|
| 1505 |
+
fieldMappings.add(new AssetSchemaDTO.FieldMappingDTO("email_addr", "email", "TRANSFORMED"));
|
| 1506 |
+
fieldMappings.add(new AssetSchemaDTO.FieldMappingDTO("create_ts", "created_date", "TRANSFORMED"));
|
| 1507 |
+
upstreamRel.setFieldMappings(fieldMappings);
|
| 1508 |
+
|
| 1509 |
+
upstream.add(upstreamRel);
|
| 1510 |
+
lineage.setUpstream(upstream);
|
| 1511 |
+
|
| 1512 |
+
// Downstream relationships
|
| 1513 |
+
List<AssetSchemaDTO.SchemaRelationshipDTO> downstream = new ArrayList<>();
|
| 1514 |
+
|
| 1515 |
+
AssetSchemaDTO.SchemaRelationshipDTO downstreamRel = new AssetSchemaDTO.SchemaRelationshipDTO();
|
| 1516 |
+
downstreamRel.setRelatedAssetId(UUID.randomUUID());
|
| 1517 |
+
downstreamRel.setRelatedAssetName("customer_analytics_mart");
|
| 1518 |
+
downstreamRel.setRelationshipType("FEEDS_INTO");
|
| 1519 |
+
downstreamRel.setTransformationType("AGGREGATION");
|
| 1520 |
+
downstreamRel.setRelationshipCreated(Instant.now().minus(java.time.Duration.ofDays(15)));
|
| 1521 |
+
|
| 1522 |
+
List<AssetSchemaDTO.FieldMappingDTO> downstreamMappings = new ArrayList<>();
|
| 1523 |
+
downstreamMappings.add(new AssetSchemaDTO.FieldMappingDTO("customer_id", "customer_key", "DIRECT"));
|
| 1524 |
+
downstreamMappings.add(new AssetSchemaDTO.FieldMappingDTO("total_spent", "lifetime_value", "CALCULATED"));
|
| 1525 |
+
downstreamRel.setFieldMappings(downstreamMappings);
|
| 1526 |
+
|
| 1527 |
+
downstream.add(downstreamRel);
|
| 1528 |
+
lineage.setDownstream(downstream);
|
| 1529 |
+
|
| 1530 |
+
// Transformations
|
| 1531 |
+
List<AssetSchemaDTO.SchemaTransformationDTO> transformations = new ArrayList<>();
|
| 1532 |
+
|
| 1533 |
+
AssetSchemaDTO.SchemaTransformationDTO transformation = new AssetSchemaDTO.SchemaTransformationDTO();
|
| 1534 |
+
transformation.setTransformationId("etl_customer_cleansing_v2.1");
|
| 1535 |
+
transformation.setTransformationType("ETL");
|
| 1536 |
+
transformation.setTransformationLogic("Data cleansing, deduplication, and standardization");
|
| 1537 |
+
transformation.setInputFields(Arrays.asList("cust_id", "email_addr", "create_ts", "spent_total", "acct_status"));
|
| 1538 |
+
transformation.setOutputFields(Arrays.asList("customer_id", "email", "created_date", "total_spent", "status"));
|
| 1539 |
+
|
| 1540 |
+
Map<String, Object> parameters = new HashMap<>();
|
| 1541 |
+
parameters.put("deduplication_key", "email_addr");
|
| 1542 |
+
parameters.put("data_quality_threshold", 0.95);
|
| 1543 |
+
parameters.put("transformation_version", "2.1.0");
|
| 1544 |
+
transformation.setParameters(parameters);
|
| 1545 |
+
transformation.setExecutedAt(Instant.now().minus(java.time.Duration.ofHours(12)));
|
| 1546 |
+
|
| 1547 |
+
transformations.add(transformation);
|
| 1548 |
+
lineage.setTransformations(transformations);
|
| 1549 |
+
|
| 1550 |
+
lineage.setLineageDepth(3);
|
| 1551 |
+
lineage.setLineageLastUpdated(Instant.now().minus(java.time.Duration.ofHours(6)));
|
| 1552 |
+
|
| 1553 |
+
return lineage;
|
| 1554 |
+
}
|
| 1555 |
+
|
| 1556 |
+
/**
|
| 1557 |
+
* Creates schema evolution tracking with version history and change tracking.
|
| 1558 |
+
*/
|
| 1559 |
+
private AssetSchemaDTO.SchemaEvolutionDTO createSchemaEvolution() {
|
| 1560 |
+
AssetSchemaDTO.SchemaEvolutionDTO evolution = new AssetSchemaDTO.SchemaEvolutionDTO();
|
| 1561 |
+
|
| 1562 |
+
// Version history
|
| 1563 |
+
List<AssetSchemaDTO.SchemaVersionDTO> versions = new ArrayList<>();
|
| 1564 |
+
|
| 1565 |
+
AssetSchemaDTO.SchemaVersionDTO v1 = new AssetSchemaDTO.SchemaVersionDTO();
|
| 1566 |
+
v1.setVersion("1.0.0");
|
| 1567 |
+
v1.setCreatedAt(Instant.now().minus(java.time.Duration.ofDays(180)));
|
| 1568 |
+
v1.setCreatedBy("data.engineer@company.com");
|
| 1569 |
+
v1.setChangeDescription("Initial schema creation");
|
| 1570 |
+
v1.setFieldCount(3);
|
| 1571 |
+
v1.setChangedFields(Arrays.asList("customer_id", "email", "created_date"));
|
| 1572 |
+
versions.add(v1);
|
| 1573 |
+
|
| 1574 |
+
AssetSchemaDTO.SchemaVersionDTO v2 = new AssetSchemaDTO.SchemaVersionDTO();
|
| 1575 |
+
v2.setVersion("2.0.0");
|
| 1576 |
+
v2.setCreatedAt(Instant.now().minus(java.time.Duration.ofDays(90)));
|
| 1577 |
+
v2.setCreatedBy("data.architect@company.com");
|
| 1578 |
+
v2.setChangeDescription("Added financial and status fields");
|
| 1579 |
+
v2.setFieldCount(5);
|
| 1580 |
+
v2.setChangedFields(Arrays.asList("total_spent", "status"));
|
| 1581 |
+
versions.add(v2);
|
| 1582 |
+
|
| 1583 |
+
AssetSchemaDTO.SchemaVersionDTO v21 = new AssetSchemaDTO.SchemaVersionDTO();
|
| 1584 |
+
v21.setVersion("2.1.0");
|
| 1585 |
+
v21.setCreatedAt(Instant.now().minus(java.time.Duration.ofDays(2)));
|
| 1586 |
+
v21.setCreatedBy("data.steward@company.com");
|
| 1587 |
+
v21.setChangeDescription("Enhanced constraints and data quality rules");
|
| 1588 |
+
v21.setFieldCount(5);
|
| 1589 |
+
v21.setChangedFields(Arrays.asList("email", "total_spent"));
|
| 1590 |
+
versions.add(v21);
|
| 1591 |
+
|
| 1592 |
+
evolution.setVersions(versions);
|
| 1593 |
+
|
| 1594 |
+
// Recent changes
|
| 1595 |
+
List<AssetSchemaDTO.SchemaChangeDTO> recentChanges = new ArrayList<>();
|
| 1596 |
+
|
| 1597 |
+
AssetSchemaDTO.SchemaChangeDTO change1 = new AssetSchemaDTO.SchemaChangeDTO();
|
| 1598 |
+
change1.setChangeType("CONSTRAINT_ADDED");
|
| 1599 |
+
change1.setFieldName("email");
|
| 1600 |
+
change1.setOldValue("VARCHAR(255)");
|
| 1601 |
+
change1.setNewValue("VARCHAR(255) WITH EMAIL_VALIDATION");
|
| 1602 |
+
change1.setChangeTimestamp(Instant.now().minus(java.time.Duration.ofDays(2)));
|
| 1603 |
+
change1.setChangedBy("data.steward@company.com");
|
| 1604 |
+
change1.setChangeReason("Enhanced data quality validation");
|
| 1605 |
+
recentChanges.add(change1);
|
| 1606 |
+
|
| 1607 |
+
AssetSchemaDTO.SchemaChangeDTO change2 = new AssetSchemaDTO.SchemaChangeDTO();
|
| 1608 |
+
change2.setChangeType("CONSTRAINT_MODIFIED");
|
| 1609 |
+
change2.setFieldName("total_spent");
|
| 1610 |
+
change2.setOldValue("CHECK(total_spent >= 0)");
|
| 1611 |
+
change2.setNewValue("CHECK(total_spent >= 0 AND total_spent <= 999999.99)");
|
| 1612 |
+
change2.setChangeTimestamp(Instant.now().minus(java.time.Duration.ofDays(1)));
|
| 1613 |
+
change2.setChangedBy("data.steward@company.com");
|
| 1614 |
+
change2.setChangeReason("Added upper bound for data quality");
|
| 1615 |
+
recentChanges.add(change2);
|
| 1616 |
+
|
| 1617 |
+
evolution.setRecentChanges(recentChanges);
|
| 1618 |
+
evolution.setTotalVersions(3);
|
| 1619 |
+
evolution.setFirstVersion(Instant.now().minus(java.time.Duration.ofDays(180)));
|
| 1620 |
+
evolution.setLastChange(Instant.now().minus(java.time.Duration.ofDays(1)));
|
| 1621 |
+
|
| 1622 |
+
return evolution;
|
| 1623 |
+
}
|
| 1624 |
+
|
| 1625 |
+
/**
|
| 1626 |
+
* Creates business metadata response for enhanced business context management.
|
| 1627 |
+
*/
|
| 1628 |
+
private BusinessMetadataResponseDTO createBusinessMetadataResponse(Asset asset, BusinessMetadataInputDTO input) {
|
| 1629 |
+
// Implementation for business metadata response
|
| 1630 |
+
// This would integrate with the comprehensive BusinessMetadataResponseDTO created earlier
|
| 1631 |
+
BusinessMetadataResponseDTO response = new BusinessMetadataResponseDTO();
|
| 1632 |
+
// Detailed implementation would follow the existing pattern
|
| 1633 |
+
return response;
|
| 1634 |
+
}
|
| 1635 |
+
|
| 1636 |
+
@Override
|
| 1637 |
+
public EnhancedLineageDTO getEnhancedLineage(UUID assetId, Integer maxDepth) {
|
| 1638 |
+
// Check if asset exists
|
| 1639 |
+
if (!assetRepository.existsById(assetId)) {
|
| 1640 |
+
throw new ResourceNotFoundException("Asset", "id", assetId.toString());
|
| 1641 |
+
}
|
| 1642 |
+
|
| 1643 |
+
Asset asset = assetRepository.findById(assetId)
|
| 1644 |
+
.orElseThrow(() -> new ResourceNotFoundException("Asset", "id", assetId.toString()));
|
| 1645 |
+
|
| 1646 |
+
// Use default depth if not specified
|
| 1647 |
+
int depth = maxDepth != null ? maxDepth : 5;
|
| 1648 |
+
|
| 1649 |
+
return createEnhancedLineage(asset, depth);
|
| 1650 |
+
}
|
| 1651 |
+
|
| 1652 |
+
@Override
|
| 1653 |
+
public AssetPolicyMappingDTO getAssetPolicyMapping(UUID assetId) {
|
| 1654 |
+
// Check if asset exists
|
| 1655 |
+
if (!assetRepository.existsById(assetId)) {
|
| 1656 |
+
throw new ResourceNotFoundException("Asset", "id", assetId.toString());
|
| 1657 |
+
}
|
| 1658 |
+
|
| 1659 |
+
Asset asset = assetRepository.findById(assetId)
|
| 1660 |
+
.orElseThrow(() -> new ResourceNotFoundException("Asset", "id", assetId.toString()));
|
| 1661 |
+
|
| 1662 |
+
return createAssetPolicyMapping(asset);
|
| 1663 |
+
}
|
| 1664 |
+
|
| 1665 |
+
@Override
|
| 1666 |
+
public CatalogFiltersDTO getCatalogFilters() {
|
| 1667 |
+
return createCatalogFilters();
|
| 1668 |
+
}
|
| 1669 |
+
|
| 1670 |
+
@Override
|
| 1671 |
+
public AssetComplianceStatusDTO getAssetComplianceStatus(UUID assetId) {
|
| 1672 |
+
// Check if asset exists
|
| 1673 |
+
if (!assetRepository.existsById(assetId)) {
|
| 1674 |
+
throw new ResourceNotFoundException("Asset", "id", assetId.toString());
|
| 1675 |
+
}
|
| 1676 |
+
|
| 1677 |
+
Asset asset = assetRepository.findById(assetId)
|
| 1678 |
+
.orElseThrow(() -> new ResourceNotFoundException("Asset", "id", assetId.toString()));
|
| 1679 |
+
|
| 1680 |
+
return createAssetComplianceStatus(asset);
|
| 1681 |
+
}
|
| 1682 |
+
|
| 1683 |
+
// Helper methods for new Priority 2 endpoints
|
| 1684 |
+
private EnhancedLineageDTO createEnhancedLineage(Asset asset, int maxDepth) {
|
| 1685 |
+
EnhancedLineageDTO lineage = new EnhancedLineageDTO(asset.getAssetId(), asset.getAssetName());
|
| 1686 |
+
|
| 1687 |
+
// Create lineage graph with realistic mock data
|
| 1688 |
+
EnhancedLineageDTO.LineageGraphDTO graph = new EnhancedLineageDTO.LineageGraphDTO();
|
| 1689 |
+
|
| 1690 |
+
// Create nodes for demonstration
|
| 1691 |
+
List<EnhancedLineageDTO.LineageNodeDTO> nodes = new ArrayList<>();
|
| 1692 |
+
List<EnhancedLineageDTO.LineageEdgeDTO> edges = new ArrayList<>();
|
| 1693 |
+
|
| 1694 |
+
// Add current asset as central node
|
| 1695 |
+
EnhancedLineageDTO.LineageNodeDTO centralNode = new EnhancedLineageDTO.LineageNodeDTO(
|
| 1696 |
+
asset.getAssetId(), asset.getAssetName(), "ASSET");
|
| 1697 |
+
centralNode.setAssetType(asset.getAssetType());
|
| 1698 |
+
centralNode.setStatus("ACTIVE");
|
| 1699 |
+
centralNode.setLayer(0);
|
| 1700 |
+
centralNode.setIcon("table");
|
| 1701 |
+
centralNode.setColor("#4A90E2");
|
| 1702 |
+
|
| 1703 |
+
EnhancedLineageDTO.NodeStatsDTO centralStats = new EnhancedLineageDTO.NodeStatsDTO();
|
| 1704 |
+
centralStats.setRecordCount(250000L);
|
| 1705 |
+
centralStats.setSizeBytes(52428800L);
|
| 1706 |
+
centralStats.setLastAccessed(Instant.now().minus(2, ChronoUnit.HOURS));
|
| 1707 |
+
centralStats.setLastModified(Instant.now().minus(1, ChronoUnit.DAYS));
|
| 1708 |
+
centralStats.setUsageFrequency(45);
|
| 1709 |
+
centralStats.setHealthStatus("HEALTHY");
|
| 1710 |
+
centralNode.setStatistics(centralStats);
|
| 1711 |
+
|
| 1712 |
+
nodes.add(centralNode);
|
| 1713 |
+
|
| 1714 |
+
// Add upstream assets
|
| 1715 |
+
for (int i = 1; i <= Math.min(maxDepth, 3); i++) {
|
| 1716 |
+
UUID upstreamId = UUID.randomUUID();
|
| 1717 |
+
EnhancedLineageDTO.LineageNodeDTO upstreamNode = new EnhancedLineageDTO.LineageNodeDTO(
|
| 1718 |
+
upstreamId, "upstream_source_" + i, "ASSET");
|
| 1719 |
+
upstreamNode.setAssetType("TABLE");
|
| 1720 |
+
upstreamNode.setStatus("ACTIVE");
|
| 1721 |
+
upstreamNode.setLayer(-i);
|
| 1722 |
+
upstreamNode.setIcon("database");
|
| 1723 |
+
upstreamNode.setColor("#7ED321");
|
| 1724 |
+
nodes.add(upstreamNode);
|
| 1725 |
+
|
| 1726 |
+
// Add edge from upstream to central
|
| 1727 |
+
EnhancedLineageDTO.LineageEdgeDTO edge = new EnhancedLineageDTO.LineageEdgeDTO(
|
| 1728 |
+
"edge_" + upstreamId, upstreamId, asset.getAssetId(), "DERIVED_FROM");
|
| 1729 |
+
edge.setTransformationType("ETL");
|
| 1730 |
+
edge.setExecutionStatus("SUCCESS");
|
| 1731 |
+
edge.setStyle("solid");
|
| 1732 |
+
edge.setColor("#666666");
|
| 1733 |
+
edges.add(edge);
|
| 1734 |
+
}
|
| 1735 |
+
|
| 1736 |
+
// Add downstream assets
|
| 1737 |
+
for (int i = 1; i <= Math.min(maxDepth, 2); i++) {
|
| 1738 |
+
UUID downstreamId = UUID.randomUUID();
|
| 1739 |
+
EnhancedLineageDTO.LineageNodeDTO downstreamNode = new EnhancedLineageDTO.LineageNodeDTO(
|
| 1740 |
+
downstreamId, "downstream_target_" + i, "ASSET");
|
| 1741 |
+
downstreamNode.setAssetType("VIEW");
|
| 1742 |
+
downstreamNode.setStatus("ACTIVE");
|
| 1743 |
+
downstreamNode.setLayer(i);
|
| 1744 |
+
downstreamNode.setIcon("view");
|
| 1745 |
+
downstreamNode.setColor("#F5A623");
|
| 1746 |
+
nodes.add(downstreamNode);
|
| 1747 |
+
|
| 1748 |
+
// Add edge from central to downstream
|
| 1749 |
+
EnhancedLineageDTO.LineageEdgeDTO edge = new EnhancedLineageDTO.LineageEdgeDTO(
|
| 1750 |
+
"edge_" + downstreamId, asset.getAssetId(), downstreamId, "WRITES_TO");
|
| 1751 |
+
edge.setTransformationType("AGGREGATE");
|
| 1752 |
+
edge.setExecutionStatus("SUCCESS");
|
| 1753 |
+
edge.setStyle("solid");
|
| 1754 |
+
edge.setColor("#666666");
|
| 1755 |
+
edges.add(edge);
|
| 1756 |
+
}
|
| 1757 |
+
|
| 1758 |
+
graph.setNodes(nodes);
|
| 1759 |
+
graph.setEdges(edges);
|
| 1760 |
+
graph.setTotalNodes(nodes.size());
|
| 1761 |
+
graph.setTotalEdges(edges.size());
|
| 1762 |
+
graph.setMaxDepth(maxDepth);
|
| 1763 |
+
lineage.setLineageGraph(graph);
|
| 1764 |
+
|
| 1765 |
+
// Add statistics
|
| 1766 |
+
EnhancedLineageDTO.LineageStatisticsDTO stats = new EnhancedLineageDTO.LineageStatisticsDTO();
|
| 1767 |
+
stats.setTotalUpstreamAssets(3);
|
| 1768 |
+
stats.setTotalDownstreamAssets(2);
|
| 1769 |
+
stats.setTotalTransformations(5);
|
| 1770 |
+
stats.setDirectDependencies(3);
|
| 1771 |
+
stats.setIndirectDependencies(2);
|
| 1772 |
+
stats.setLineageCompleteness(85.5);
|
| 1773 |
+
stats.setLineageHealth("HEALTHY");
|
| 1774 |
+
lineage.setStatistics(stats);
|
| 1775 |
+
|
| 1776 |
+
lineage.setLastUpdated(Instant.now());
|
| 1777 |
+
lineage.setLineageVersion("1.2.0");
|
| 1778 |
+
lineage.setMaxDepth(maxDepth);
|
| 1779 |
+
|
| 1780 |
+
return lineage;
|
| 1781 |
+
}
|
| 1782 |
+
|
| 1783 |
+
private AssetPolicyMappingDTO createAssetPolicyMapping(Asset asset) {
|
| 1784 |
+
AssetPolicyMappingDTO mapping = new AssetPolicyMappingDTO(asset.getAssetId(), asset.getAssetName());
|
| 1785 |
+
|
| 1786 |
+
// Create applicable policies
|
| 1787 |
+
List<AssetPolicyMappingDTO.PolicyMappingDTO> applicablePolicies = new ArrayList<>();
|
| 1788 |
+
|
| 1789 |
+
AssetPolicyMappingDTO.PolicyMappingDTO policy1 = new AssetPolicyMappingDTO.PolicyMappingDTO(
|
| 1790 |
+
UUID.randomUUID(), "Data Retention Policy", "DATA_GOVERNANCE");
|
| 1791 |
+
policy1.setPolicyVersion("1.0");
|
| 1792 |
+
policy1.setApplicabilityReason("TYPE_BASED");
|
| 1793 |
+
policy1.setComplianceStatus("COMPLIANT");
|
| 1794 |
+
policy1.setComplianceScore(95.0);
|
| 1795 |
+
policy1.setLastEvaluated(Instant.now().minus(1, ChronoUnit.HOURS));
|
| 1796 |
+
applicablePolicies.add(policy1);
|
| 1797 |
+
|
| 1798 |
+
AssetPolicyMappingDTO.PolicyMappingDTO policy2 = new AssetPolicyMappingDTO.PolicyMappingDTO(
|
| 1799 |
+
UUID.randomUUID(), "PII Protection Policy", "SECURITY");
|
| 1800 |
+
policy2.setPolicyVersion("2.1");
|
| 1801 |
+
policy2.setApplicabilityReason("LABEL_BASED");
|
| 1802 |
+
policy2.setComplianceStatus("PARTIALLY_COMPLIANT");
|
| 1803 |
+
policy2.setComplianceScore(78.5);
|
| 1804 |
+
policy2.setLastEvaluated(Instant.now().minus(2, ChronoUnit.HOURS));
|
| 1805 |
+
applicablePolicies.add(policy2);
|
| 1806 |
+
|
| 1807 |
+
mapping.setApplicablePolicies(applicablePolicies);
|
| 1808 |
+
|
| 1809 |
+
// Create compliance summary
|
| 1810 |
+
AssetPolicyMappingDTO.PolicyComplianceSummaryDTO summary = new AssetPolicyMappingDTO.PolicyComplianceSummaryDTO();
|
| 1811 |
+
summary.setTotalPolicies(2);
|
| 1812 |
+
summary.setCompliantPolicies(1);
|
| 1813 |
+
summary.setNonCompliantPolicies(0);
|
| 1814 |
+
summary.setPartiallyCompliantPolicies(1);
|
| 1815 |
+
summary.setOverallComplianceScore(86.75);
|
| 1816 |
+
summary.setOverallStatus("PARTIALLY_COMPLIANT");
|
| 1817 |
+
summary.setLastFullEvaluation(Instant.now().minus(1, ChronoUnit.HOURS));
|
| 1818 |
+
mapping.setComplianceSummary(summary);
|
| 1819 |
+
|
| 1820 |
+
mapping.setLastEvaluated(Instant.now().minus(30, ChronoUnit.MINUTES));
|
| 1821 |
+
mapping.setEvaluationStatus("COMPLETED");
|
| 1822 |
+
|
| 1823 |
+
return mapping;
|
| 1824 |
+
}
|
| 1825 |
+
|
| 1826 |
+
private CatalogFiltersDTO createCatalogFilters() {
|
| 1827 |
+
CatalogFiltersDTO filters = new CatalogFiltersDTO();
|
| 1828 |
+
|
| 1829 |
+
List<CatalogFiltersDTO.FilterCategoryDTO> categories = new ArrayList<>();
|
| 1830 |
+
|
| 1831 |
+
// Asset Type filter
|
| 1832 |
+
CatalogFiltersDTO.FilterCategoryDTO assetTypeCategory = new CatalogFiltersDTO.FilterCategoryDTO(
|
| 1833 |
+
"assetType", "Asset Type", "MULTI_SELECT");
|
| 1834 |
+
List<CatalogFiltersDTO.FilterOptionDTO> assetTypeOptions = Arrays.asList(
|
| 1835 |
+
new CatalogFiltersDTO.FilterOptionDTO("TABLE", "Tables", 1250),
|
| 1836 |
+
new CatalogFiltersDTO.FilterOptionDTO("VIEW", "Views", 340),
|
| 1837 |
+
new CatalogFiltersDTO.FilterOptionDTO("FILE", "Files", 890),
|
| 1838 |
+
new CatalogFiltersDTO.FilterOptionDTO("API", "APIs", 125),
|
| 1839 |
+
new CatalogFiltersDTO.FilterOptionDTO("STREAM", "Streams", 67)
|
| 1840 |
+
);
|
| 1841 |
+
assetTypeCategory.setOptions(assetTypeOptions);
|
| 1842 |
+
assetTypeCategory.setIsExpandedByDefault(true);
|
| 1843 |
+
assetTypeCategory.setDisplayOrder(1);
|
| 1844 |
+
categories.add(assetTypeCategory);
|
| 1845 |
+
|
| 1846 |
+
// Cloud Provider filter
|
| 1847 |
+
CatalogFiltersDTO.FilterCategoryDTO providerCategory = new CatalogFiltersDTO.FilterCategoryDTO(
|
| 1848 |
+
"cloudProvider", "Cloud Provider", "MULTI_SELECT");
|
| 1849 |
+
List<CatalogFiltersDTO.FilterOptionDTO> providerOptions = Arrays.asList(
|
| 1850 |
+
new CatalogFiltersDTO.FilterOptionDTO("AWS", "Amazon Web Services", 1100),
|
| 1851 |
+
new CatalogFiltersDTO.FilterOptionDTO("AZURE", "Microsoft Azure", 750),
|
| 1852 |
+
new CatalogFiltersDTO.FilterOptionDTO("GCP", "Google Cloud Platform", 450),
|
| 1853 |
+
new CatalogFiltersDTO.FilterOptionDTO("ON_PREMISE", "On-Premise", 372)
|
| 1854 |
+
);
|
| 1855 |
+
providerCategory.setOptions(providerOptions);
|
| 1856 |
+
providerCategory.setIsExpandedByDefault(true);
|
| 1857 |
+
providerCategory.setDisplayOrder(2);
|
| 1858 |
+
categories.add(providerCategory);
|
| 1859 |
+
|
| 1860 |
+
// Labels filter
|
| 1861 |
+
CatalogFiltersDTO.FilterCategoryDTO labelsCategory = new CatalogFiltersDTO.FilterCategoryDTO(
|
| 1862 |
+
"labels", "Labels", "MULTI_SELECT");
|
| 1863 |
+
List<CatalogFiltersDTO.FilterOptionDTO> labelOptions = Arrays.asList(
|
| 1864 |
+
new CatalogFiltersDTO.FilterOptionDTO("PII", "Personally Identifiable Information", 234),
|
| 1865 |
+
new CatalogFiltersDTO.FilterOptionDTO("SENSITIVE", "Sensitive Data", 567),
|
| 1866 |
+
new CatalogFiltersDTO.FilterOptionDTO("PUBLIC", "Public Data", 1890),
|
| 1867 |
+
new CatalogFiltersDTO.FilterOptionDTO("FINANCIAL", "Financial Data", 123),
|
| 1868 |
+
new CatalogFiltersDTO.FilterOptionDTO("CUSTOMER", "Customer Data", 456)
|
| 1869 |
+
);
|
| 1870 |
+
labelsCategory.setOptions(labelOptions);
|
| 1871 |
+
labelsCategory.setIsExpandedByDefault(false);
|
| 1872 |
+
labelsCategory.setDisplayOrder(3);
|
| 1873 |
+
categories.add(labelsCategory);
|
| 1874 |
+
|
| 1875 |
+
filters.setFilterCategories(categories);
|
| 1876 |
+
|
| 1877 |
+
// Quick filters
|
| 1878 |
+
Map<String, List<String>> quickFilters = new HashMap<>();
|
| 1879 |
+
quickFilters.put("recent", Arrays.asList("Last 7 days", "Last 30 days", "Last 90 days"));
|
| 1880 |
+
quickFilters.put("popular", Arrays.asList("Most accessed", "Most queried", "Trending"));
|
| 1881 |
+
filters.setQuickFilters(quickFilters);
|
| 1882 |
+
|
| 1883 |
+
// Search options
|
| 1884 |
+
CatalogFiltersDTO.SearchOptionsDTO searchOptions = new CatalogFiltersDTO.SearchOptionsDTO();
|
| 1885 |
+
searchOptions.setSearchableFields(Arrays.asList("assetName", "description", "labels", "metadata"));
|
| 1886 |
+
searchOptions.setSortableFields(Arrays.asList("assetName", "createdTs", "lastModifiedTs", "size"));
|
| 1887 |
+
searchOptions.setMaxResults(1000);
|
| 1888 |
+
searchOptions.setDefaultPageSize(20);
|
| 1889 |
+
searchOptions.setSupportsFullTextSearch(true);
|
| 1890 |
+
searchOptions.setSupportsWildcards(true);
|
| 1891 |
+
searchOptions.setSupportsFuzzySearch(true);
|
| 1892 |
+
filters.setSearchOptions(searchOptions);
|
| 1893 |
+
|
| 1894 |
+
filters.setLastUpdated(Instant.now());
|
| 1895 |
+
filters.setTotalAssets(2672);
|
| 1896 |
+
filters.setCatalogVersion("2.1.0");
|
| 1897 |
+
|
| 1898 |
+
return filters;
|
| 1899 |
+
}
|
| 1900 |
+
|
| 1901 |
+
private AssetComplianceStatusDTO createAssetComplianceStatus(Asset asset) {
|
| 1902 |
+
AssetComplianceStatusDTO compliance = new AssetComplianceStatusDTO(asset.getAssetId(), asset.getAssetName());
|
| 1903 |
+
|
| 1904 |
+
compliance.setOverallComplianceStatus("PARTIALLY_COMPLIANT");
|
| 1905 |
+
compliance.setOverallComplianceScore(82.5);
|
| 1906 |
+
|
| 1907 |
+
// Framework compliance
|
| 1908 |
+
List<AssetComplianceStatusDTO.ComplianceFrameworkStatusDTO> frameworks = new ArrayList<>();
|
| 1909 |
+
|
| 1910 |
+
AssetComplianceStatusDTO.ComplianceFrameworkStatusDTO gdpr = new AssetComplianceStatusDTO.ComplianceFrameworkStatusDTO(
|
| 1911 |
+
"GDPR", "COMPLIANT", 94.0);
|
| 1912 |
+
gdpr.setFrameworkVersion("2018.1");
|
| 1913 |
+
gdpr.setTotalRequirements(15);
|
| 1914 |
+
gdpr.setMetRequirements(14);
|
| 1915 |
+
gdpr.setLastAssessment(Instant.now().minus(7, ChronoUnit.DAYS));
|
| 1916 |
+
frameworks.add(gdpr);
|
| 1917 |
+
|
| 1918 |
+
AssetComplianceStatusDTO.ComplianceFrameworkStatusDTO sox = new AssetComplianceStatusDTO.ComplianceFrameworkStatusDTO(
|
| 1919 |
+
"SOX", "PARTIALLY_COMPLIANT", 71.0);
|
| 1920 |
+
sox.setFrameworkVersion("2002.1");
|
| 1921 |
+
sox.setTotalRequirements(12);
|
| 1922 |
+
sox.setMetRequirements(8);
|
| 1923 |
+
sox.setLastAssessment(Instant.now().minus(3, ChronoUnit.DAYS));
|
| 1924 |
+
frameworks.add(sox);
|
| 1925 |
+
|
| 1926 |
+
compliance.setFrameworkCompliance(frameworks);
|
| 1927 |
+
|
| 1928 |
+
// Violations
|
| 1929 |
+
List<AssetComplianceStatusDTO.ComplianceViolationDTO> violations = new ArrayList<>();
|
| 1930 |
+
|
| 1931 |
+
AssetComplianceStatusDTO.ComplianceViolationDTO violation1 = new AssetComplianceStatusDTO.ComplianceViolationDTO(
|
| 1932 |
+
"VIO_001", "ACCESS_CONTROL", "MEDIUM");
|
| 1933 |
+
violation1.setDescription("Insufficient access controls for sensitive data fields");
|
| 1934 |
+
violation1.setFramework("SOX");
|
| 1935 |
+
violation1.setStatus("OPEN");
|
| 1936 |
+
violation1.setDetectedAt(Instant.now().minus(2, ChronoUnit.DAYS));
|
| 1937 |
+
violations.add(violation1);
|
| 1938 |
+
|
| 1939 |
+
compliance.setViolations(violations);
|
| 1940 |
+
|
| 1941 |
+
// Risk assessment
|
| 1942 |
+
AssetComplianceStatusDTO.ComplianceRiskAssessmentDTO risk = new AssetComplianceStatusDTO.ComplianceRiskAssessmentDTO();
|
| 1943 |
+
risk.setOverallRiskLevel("MEDIUM");
|
| 1944 |
+
risk.setRiskScore(65.0);
|
| 1945 |
+
risk.setRiskFactors(Arrays.asList("Contains PII", "High access volume", "Cross-border data transfers"));
|
| 1946 |
+
risk.setLastAssessed(Instant.now().minus(1, ChronoUnit.DAYS));
|
| 1947 |
+
compliance.setRiskAssessment(risk);
|
| 1948 |
+
|
| 1949 |
+
compliance.setLastEvaluated(Instant.now().minus(1, ChronoUnit.HOURS));
|
| 1950 |
+
compliance.setEvaluatedBy("system");
|
| 1951 |
+
compliance.setNextEvaluation(Instant.now().plus(7, ChronoUnit.DAYS));
|
| 1952 |
+
|
| 1953 |
+
return compliance;
|
| 1954 |
+
}
|
| 1955 |
+
}
|
src/main/java/com/dalab/catalog/service/IAssetService.java
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.service;
|
| 2 |
+
|
| 3 |
+
import java.util.List;
|
| 4 |
+
import java.util.Map;
|
| 5 |
+
import java.util.UUID;
|
| 6 |
+
|
| 7 |
+
import org.springframework.data.domain.Page;
|
| 8 |
+
import org.springframework.data.domain.Pageable;
|
| 9 |
+
|
| 10 |
+
import com.dalab.catalog.dto.AssetComplianceStatusDTO;
|
| 11 |
+
import com.dalab.catalog.dto.AssetInputDTO;
|
| 12 |
+
import com.dalab.catalog.dto.AssetLabelsPostRequestDTO;
|
| 13 |
+
import com.dalab.catalog.dto.AssetOutputDTO;
|
| 14 |
+
import com.dalab.catalog.dto.AssetPolicyMappingDTO;
|
| 15 |
+
import com.dalab.catalog.dto.AssetSchemaDTO;
|
| 16 |
+
import com.dalab.catalog.dto.AssetUsageAnalyticsDTO;
|
| 17 |
+
import com.dalab.catalog.dto.BusinessMetadataInputDTO;
|
| 18 |
+
import com.dalab.catalog.dto.BusinessMetadataResponseDTO;
|
| 19 |
+
import com.dalab.catalog.dto.CatalogFiltersDTO;
|
| 20 |
+
import com.dalab.catalog.dto.EnhancedLineageDTO;
|
| 21 |
+
import com.dalab.catalog.dto.LabelOutputDTO;
|
| 22 |
+
import com.dalab.catalog.dto.LineageResponseDTO;
|
| 23 |
+
|
| 24 |
+
public interface IAssetService {
|
| 25 |
+
|
| 26 |
+
Page<AssetOutputDTO> getAllAssets(
|
| 27 |
+
Pageable pageable,
|
| 28 |
+
String cloudProvider,
|
| 29 |
+
String assetType,
|
| 30 |
+
String region,
|
| 31 |
+
List<String> labels, // Filter by assets having ALL these labels
|
| 32 |
+
String nameContains,
|
| 33 |
+
UUID connectionId
|
| 34 |
+
// Add other filter parameters as needed
|
| 35 |
+
);
|
| 36 |
+
|
| 37 |
+
AssetOutputDTO getAssetById(UUID assetId);
|
| 38 |
+
|
| 39 |
+
AssetOutputDTO createAsset(AssetInputDTO assetInputDTO, UUID creatorUserId);
|
| 40 |
+
|
| 41 |
+
AssetOutputDTO updateBusinessMetadata(UUID assetId, Map<String, Object> businessMetadata, UUID updaterUserId);
|
| 42 |
+
|
| 43 |
+
/**
|
| 44 |
+
* Update enhanced business context and metadata for an asset.
|
| 45 |
+
* Supports steward relationships, compliance classification, and business context.
|
| 46 |
+
*
|
| 47 |
+
* @param assetId the ID of the asset to update
|
| 48 |
+
* @param businessMetadataInput enhanced business metadata with steward relationships and compliance info
|
| 49 |
+
* @param updaterUserId the ID of the user performing the update
|
| 50 |
+
* @return enhanced business metadata response with validation status and recommendations
|
| 51 |
+
*/
|
| 52 |
+
BusinessMetadataResponseDTO updateBusinessContext(UUID assetId, BusinessMetadataInputDTO businessMetadataInput, UUID updaterUserId);
|
| 53 |
+
|
| 54 |
+
/**
|
| 55 |
+
* Get comprehensive business metadata for an asset.
|
| 56 |
+
* Includes business context, steward relationships, compliance classification,
|
| 57 |
+
* audit information, and validation status.
|
| 58 |
+
*
|
| 59 |
+
* @param assetId the ID of the asset
|
| 60 |
+
* @return comprehensive business metadata response with audit and validation information
|
| 61 |
+
*/
|
| 62 |
+
BusinessMetadataResponseDTO getBusinessMetadata(UUID assetId);
|
| 63 |
+
|
| 64 |
+
LineageResponseDTO getAssetLineage(UUID assetId);
|
| 65 |
+
|
| 66 |
+
List<LabelOutputDTO> getAssetLabels(UUID assetId);
|
| 67 |
+
|
| 68 |
+
List<LabelOutputDTO> assignLabelsToAsset(UUID assetId, AssetLabelsPostRequestDTO labelsToAssign, UUID assignerUserId);
|
| 69 |
+
|
| 70 |
+
void removeLabelFromAsset(UUID assetId, String labelName, UUID removerUserId);
|
| 71 |
+
|
| 72 |
+
/**
|
| 73 |
+
* Get comprehensive usage analytics for an asset.
|
| 74 |
+
* Provides insights into access patterns, user behavior, performance metrics,
|
| 75 |
+
* and optimization recommendations.
|
| 76 |
+
*
|
| 77 |
+
* @param assetId the ID of the asset to analyze
|
| 78 |
+
* @return comprehensive usage analytics including access patterns, user analytics, performance metrics, and recommendations
|
| 79 |
+
*/
|
| 80 |
+
AssetUsageAnalyticsDTO getAssetUsageAnalytics(UUID assetId);
|
| 81 |
+
|
| 82 |
+
/**
|
| 83 |
+
* Get comprehensive asset schema information.
|
| 84 |
+
* Priority 2 endpoint: Enhanced catalog functionality with detailed schema structure.
|
| 85 |
+
*
|
| 86 |
+
* @param assetId UUID of the asset
|
| 87 |
+
* @return AssetSchemaDTO with detailed schema information including fields, constraints, statistics, and lineage
|
| 88 |
+
*/
|
| 89 |
+
AssetSchemaDTO getAssetSchema(UUID assetId);
|
| 90 |
+
|
| 91 |
+
/**
|
| 92 |
+
* Get enhanced data lineage visualization information.
|
| 93 |
+
* Priority 2 endpoint: Provides detailed lineage with visualization metadata for graph rendering.
|
| 94 |
+
*
|
| 95 |
+
* @param assetId UUID of the asset
|
| 96 |
+
* @param maxDepth Maximum depth to traverse (optional, default 5)
|
| 97 |
+
* @return EnhancedLineageDTO with graph nodes, edges, and visualization metadata
|
| 98 |
+
*/
|
| 99 |
+
EnhancedLineageDTO getEnhancedLineage(UUID assetId, Integer maxDepth);
|
| 100 |
+
|
| 101 |
+
/**
|
| 102 |
+
* Get asset-policy mapping information.
|
| 103 |
+
* Priority 2 endpoint: Shows which policies apply to an asset and their compliance status.
|
| 104 |
+
*
|
| 105 |
+
* @param assetId UUID of the asset
|
| 106 |
+
* @return AssetPolicyMappingDTO with applicable policies and compliance information
|
| 107 |
+
*/
|
| 108 |
+
AssetPolicyMappingDTO getAssetPolicyMapping(UUID assetId);
|
| 109 |
+
|
| 110 |
+
/**
|
| 111 |
+
* Get catalog faceted search filters.
|
| 112 |
+
* Priority 2 endpoint: Provides filter metadata for advanced search and filtering in the catalog UI.
|
| 113 |
+
*
|
| 114 |
+
* @return CatalogFiltersDTO with filter categories, options, and search capabilities
|
| 115 |
+
*/
|
| 116 |
+
CatalogFiltersDTO getCatalogFilters();
|
| 117 |
+
|
| 118 |
+
/**
|
| 119 |
+
* Get asset compliance status information.
|
| 120 |
+
* Priority 2 endpoint: Provides detailed compliance status and monitoring for assets.
|
| 121 |
+
*
|
| 122 |
+
* @param assetId UUID of the asset
|
| 123 |
+
* @return AssetComplianceStatusDTO with framework compliance, violations, and recommendations
|
| 124 |
+
*/
|
| 125 |
+
AssetComplianceStatusDTO getAssetComplianceStatus(UUID assetId);
|
| 126 |
+
|
| 127 |
+
// Placeholder for creating/updating assets, to be defined by POST/PUT API tasks
|
| 128 |
+
// AssetOutputDTO createAsset(AssetInputDTO assetInputDTO);
|
| 129 |
+
// AssetOutputDTO updateAsset(UUID assetId, AssetUpdateDTO assetUpdateDTO);
|
| 130 |
+
}
|
src/main/java/com/dalab/catalog/service/ILabelTaxonomyService.java
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.service;
|
| 2 |
+
|
| 3 |
+
import com.dalab.catalog.dto.TaxonomyLabelDTO;
|
| 4 |
+
import org.springframework.data.domain.Page;
|
| 5 |
+
import org.springframework.data.domain.Pageable;
|
| 6 |
+
|
| 7 |
+
import java.util.List;
|
| 8 |
+
import java.util.UUID;
|
| 9 |
+
|
| 10 |
+
public interface ILabelTaxonomyService {
|
| 11 |
+
|
| 12 |
+
Page<TaxonomyLabelDTO> getAllLabels(Pageable pageable, String category, String nameContains);
|
| 13 |
+
|
| 14 |
+
TaxonomyLabelDTO getLabelByIdOrName(String idOrName);
|
| 15 |
+
|
| 16 |
+
TaxonomyLabelDTO createLabel(TaxonomyLabelDTO labelDTO, UUID creatorUserId);
|
| 17 |
+
|
| 18 |
+
TaxonomyLabelDTO updateLabel(String idOrName, TaxonomyLabelDTO labelDTO, UUID updaterUserId);
|
| 19 |
+
|
| 20 |
+
void deleteLabel(String idOrName); // Consider implications: what happens to assets with this label?
|
| 21 |
+
}
|
src/main/java/com/dalab/catalog/service/LabelTaxonomyService.java
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.service;
|
| 2 |
+
|
| 3 |
+
import java.util.ArrayList;
|
| 4 |
+
import java.util.List;
|
| 5 |
+
import java.util.Optional;
|
| 6 |
+
import java.util.UUID;
|
| 7 |
+
|
| 8 |
+
import org.slf4j.Logger;
|
| 9 |
+
import org.slf4j.LoggerFactory;
|
| 10 |
+
import org.springframework.beans.factory.annotation.Autowired;
|
| 11 |
+
import org.springframework.dao.DataIntegrityViolationException;
|
| 12 |
+
import org.springframework.data.domain.Page;
|
| 13 |
+
import org.springframework.data.domain.Pageable;
|
| 14 |
+
import org.springframework.data.jpa.domain.Specification;
|
| 15 |
+
import org.springframework.stereotype.Service;
|
| 16 |
+
import org.springframework.transaction.annotation.Transactional;
|
| 17 |
+
import org.springframework.util.StringUtils;
|
| 18 |
+
|
| 19 |
+
import com.dalab.catalog.common.ConflictException;
|
| 20 |
+
import com.dalab.catalog.common.ResourceNotFoundException;
|
| 21 |
+
import com.dalab.catalog.dto.TaxonomyLabelDTO;
|
| 22 |
+
import com.dalab.catalog.mapper.LabelTaxonomyMapper;
|
| 23 |
+
import com.dalab.common.model.entity.Label;
|
| 24 |
+
import com.dalab.common.repository.IAssetLabelAssignmentRepository;
|
| 25 |
+
import com.dalab.common.repository.ILabelRepository;
|
| 26 |
+
|
| 27 |
+
import jakarta.persistence.criteria.Predicate;
|
| 28 |
+
|
| 29 |
+
@Service
|
| 30 |
+
@Transactional
|
| 31 |
+
public class LabelTaxonomyService implements ILabelTaxonomyService {
|
| 32 |
+
|
| 33 |
+
private static final Logger log = LoggerFactory.getLogger(LabelTaxonomyService.class);
|
| 34 |
+
|
| 35 |
+
private final ILabelRepository labelRepository;
|
| 36 |
+
private final LabelTaxonomyMapper labelTaxonomyMapper;
|
| 37 |
+
private final IAssetLabelAssignmentRepository assetLabelAssignmentRepository;
|
| 38 |
+
|
| 39 |
+
@Autowired
|
| 40 |
+
public LabelTaxonomyService(ILabelRepository labelRepository,
|
| 41 |
+
LabelTaxonomyMapper labelTaxonomyMapper,
|
| 42 |
+
IAssetLabelAssignmentRepository assetLabelAssignmentRepository) {
|
| 43 |
+
this.labelRepository = labelRepository;
|
| 44 |
+
this.labelTaxonomyMapper = labelTaxonomyMapper;
|
| 45 |
+
this.assetLabelAssignmentRepository = assetLabelAssignmentRepository;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
@Override
|
| 49 |
+
@Transactional(readOnly = true)
|
| 50 |
+
public Page<TaxonomyLabelDTO> getAllLabels(Pageable pageable, String category, String nameContains) {
|
| 51 |
+
Specification<Label> spec = (root, query, criteriaBuilder) -> {
|
| 52 |
+
List<Predicate> predicates = new ArrayList<>();
|
| 53 |
+
if (StringUtils.hasText(category)) {
|
| 54 |
+
predicates.add(criteriaBuilder.equal(root.get("labelCategory"), category));
|
| 55 |
+
}
|
| 56 |
+
if (StringUtils.hasText(nameContains)) {
|
| 57 |
+
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("labelName")),
|
| 58 |
+
"%" + nameContains.toLowerCase() + "%"));
|
| 59 |
+
}
|
| 60 |
+
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
|
| 61 |
+
};
|
| 62 |
+
return labelRepository.findAll(spec, pageable).map(labelTaxonomyMapper::toTaxonomyLabelDTO);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
@Override
|
| 66 |
+
@Transactional(readOnly = true)
|
| 67 |
+
public TaxonomyLabelDTO getLabelByIdOrName(String idOrName) {
|
| 68 |
+
Label label;
|
| 69 |
+
try {
|
| 70 |
+
UUID labelId = UUID.fromString(idOrName);
|
| 71 |
+
label = labelRepository.findById(labelId)
|
| 72 |
+
.orElseThrow(() -> new ResourceNotFoundException("Label", "id", idOrName));
|
| 73 |
+
} catch (IllegalArgumentException ex) {
|
| 74 |
+
// Not a UUID, try by name
|
| 75 |
+
label = labelRepository.findByLabelName(idOrName)
|
| 76 |
+
.orElseThrow(() -> new ResourceNotFoundException("Label", "name", idOrName));
|
| 77 |
+
}
|
| 78 |
+
return labelTaxonomyMapper.toTaxonomyLabelDTO(label);
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
@Override
|
| 82 |
+
public TaxonomyLabelDTO createLabel(TaxonomyLabelDTO labelDTO, UUID creatorUserId) {
|
| 83 |
+
if (!StringUtils.hasText(labelDTO.getLabelName())){
|
| 84 |
+
throw new IllegalArgumentException("Label name cannot be empty.");
|
| 85 |
+
}
|
| 86 |
+
labelRepository.findByLabelName(labelDTO.getLabelName()).ifPresent(l -> {
|
| 87 |
+
throw new ConflictException("Label with name '" + labelDTO.getLabelName() + "' already exists.");
|
| 88 |
+
});
|
| 89 |
+
|
| 90 |
+
Label label = labelTaxonomyMapper.toLabelEntity(labelDTO);
|
| 91 |
+
label.setCreatedByUserId(creatorUserId);
|
| 92 |
+
// ID, createdAt, updatedAt are handled by entity constructor and @PrePersist/@PreUpdate
|
| 93 |
+
|
| 94 |
+
Label savedLabel = labelRepository.save(label);
|
| 95 |
+
return labelTaxonomyMapper.toTaxonomyLabelDTO(savedLabel);
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
@Override
|
| 99 |
+
public TaxonomyLabelDTO updateLabel(String idOrName, TaxonomyLabelDTO labelDTO, UUID updaterUserId) {
|
| 100 |
+
final Label existingLabel = findLabelByIdOrName(idOrName);
|
| 101 |
+
|
| 102 |
+
// Check for name conflict if name is being changed
|
| 103 |
+
if (StringUtils.hasText(labelDTO.getLabelName()) && !existingLabel.getLabelName().equals(labelDTO.getLabelName())) {
|
| 104 |
+
Optional<Label> conflictingLabel = labelRepository.findByLabelName(labelDTO.getLabelName());
|
| 105 |
+
if (conflictingLabel.isPresent() && !conflictingLabel.get().getLabelId().equals(existingLabel.getLabelId())) {
|
| 106 |
+
throw new ConflictException("Another label with name '" + labelDTO.getLabelName() + "' already exists.");
|
| 107 |
+
}
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
labelTaxonomyMapper.updateLabelEntityFromDto(existingLabel, labelDTO);
|
| 111 |
+
// existingLabel.setUpdatedByUserId(updaterUserId); // If tracking updater
|
| 112 |
+
Label updatedLabel = labelRepository.save(existingLabel); // @PreUpdate will set updatedAt
|
| 113 |
+
return labelTaxonomyMapper.toTaxonomyLabelDTO(updatedLabel);
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
@Override
|
| 117 |
+
public void deleteLabel(String idOrName) {
|
| 118 |
+
Label labelToDelete = findLabelByIdOrName(idOrName);
|
| 119 |
+
|
| 120 |
+
// Check if the label is used in any assignments
|
| 121 |
+
long usageCount = assetLabelAssignmentRepository.countByLabel(labelToDelete);
|
| 122 |
+
if (usageCount > 0) {
|
| 123 |
+
throw new ConflictException("Label '" + labelToDelete.getLabelName() + "' cannot be deleted as it is currently assigned to " + usageCount + " asset(s).");
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
try {
|
| 127 |
+
labelRepository.delete(labelToDelete);
|
| 128 |
+
log.info("Deleted label: {}", labelToDelete.getLabelName());
|
| 129 |
+
} catch (DataIntegrityViolationException e) {
|
| 130 |
+
// This is a fallback, primary check is done by usageCount
|
| 131 |
+
throw new ConflictException("Label '" + labelToDelete.getLabelName() + "' cannot be deleted due to existing references (e.g., assignments).");
|
| 132 |
+
}
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
private Label findLabelByIdOrName(String idOrName) {
|
| 136 |
+
try {
|
| 137 |
+
UUID labelId = UUID.fromString(idOrName);
|
| 138 |
+
return labelRepository.findById(labelId)
|
| 139 |
+
.orElseThrow(() -> new ResourceNotFoundException("Label", "id", idOrName));
|
| 140 |
+
} catch (IllegalArgumentException ex) {
|
| 141 |
+
// Not a UUID, try by name
|
| 142 |
+
return labelRepository.findByLabelName(idOrName)
|
| 143 |
+
.orElseThrow(() -> new ResourceNotFoundException("Label", "name", idOrName));
|
| 144 |
+
}
|
| 145 |
+
}
|
| 146 |
+
}
|
src/main/java/com/dalab/catalog/web/rest/AssetController.java
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.web.rest;
|
| 2 |
+
|
| 3 |
+
import java.net.URI;
|
| 4 |
+
import java.util.List;
|
| 5 |
+
import java.util.Map;
|
| 6 |
+
import java.util.UUID;
|
| 7 |
+
|
| 8 |
+
import org.slf4j.Logger;
|
| 9 |
+
import org.slf4j.LoggerFactory;
|
| 10 |
+
import org.springframework.beans.factory.annotation.Autowired;
|
| 11 |
+
import org.springframework.data.domain.Page;
|
| 12 |
+
import org.springframework.data.domain.Pageable;
|
| 13 |
+
import org.springframework.data.web.PageableDefault;
|
| 14 |
+
import org.springframework.http.ResponseEntity;
|
| 15 |
+
import org.springframework.security.access.prepost.PreAuthorize;
|
| 16 |
+
import org.springframework.web.bind.annotation.DeleteMapping;
|
| 17 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 18 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
| 19 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 20 |
+
import org.springframework.web.bind.annotation.PutMapping;
|
| 21 |
+
import org.springframework.web.bind.annotation.RequestBody;
|
| 22 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 23 |
+
import org.springframework.web.bind.annotation.RequestParam;
|
| 24 |
+
import org.springframework.web.bind.annotation.RestController;
|
| 25 |
+
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
| 26 |
+
|
| 27 |
+
import com.dalab.catalog.dto.AssetComplianceStatusDTO;
|
| 28 |
+
import com.dalab.catalog.dto.AssetInputDTO;
|
| 29 |
+
import com.dalab.catalog.dto.AssetLabelsPostRequestDTO;
|
| 30 |
+
import com.dalab.catalog.dto.AssetLabelsResponseDTO;
|
| 31 |
+
import com.dalab.catalog.dto.AssetOutputDTO;
|
| 32 |
+
import com.dalab.catalog.dto.AssetPolicyMappingDTO;
|
| 33 |
+
import com.dalab.catalog.dto.AssetSchemaDTO;
|
| 34 |
+
import com.dalab.catalog.dto.AssetUsageAnalyticsDTO;
|
| 35 |
+
import com.dalab.catalog.dto.BusinessMetadataInputDTO;
|
| 36 |
+
import com.dalab.catalog.dto.BusinessMetadataResponseDTO;
|
| 37 |
+
import com.dalab.catalog.dto.CatalogFiltersDTO;
|
| 38 |
+
import com.dalab.catalog.dto.EnhancedLineageDTO;
|
| 39 |
+
import com.dalab.catalog.dto.LabelOutputDTO;
|
| 40 |
+
import com.dalab.catalog.dto.LineageResponseDTO;
|
| 41 |
+
import com.dalab.catalog.security.SecurityUtils;
|
| 42 |
+
import com.dalab.catalog.service.IAssetService;
|
| 43 |
+
|
| 44 |
+
import io.swagger.v3.oas.annotations.Operation;
|
| 45 |
+
import io.swagger.v3.oas.annotations.Parameter;
|
| 46 |
+
import io.swagger.v3.oas.annotations.media.Content;
|
| 47 |
+
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
| 48 |
+
import io.swagger.v3.oas.annotations.media.Schema;
|
| 49 |
+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
| 50 |
+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
| 51 |
+
import jakarta.validation.Valid;
|
| 52 |
+
|
| 53 |
+
@RestController
|
| 54 |
+
@RequestMapping("/api/v1/catalog") // Base path for catalog service
|
| 55 |
+
public class AssetController {
|
| 56 |
+
|
| 57 |
+
private static final Logger log = LoggerFactory.getLogger(AssetController.class);
|
| 58 |
+
|
| 59 |
+
private final IAssetService assetService;
|
| 60 |
+
|
| 61 |
+
@Autowired
|
| 62 |
+
public AssetController(IAssetService assetService) {
|
| 63 |
+
this.assetService = assetService;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
@GetMapping("/assets")
|
| 67 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_USER')") // Adjust roles as needed
|
| 68 |
+
public ResponseEntity<Page<AssetOutputDTO>> getAllAssets(
|
| 69 |
+
@PageableDefault(size = 20, sort = "assetName") Pageable pageable,
|
| 70 |
+
@RequestParam(required = false) String cloudProvider,
|
| 71 |
+
@RequestParam(required = false) String assetType,
|
| 72 |
+
@RequestParam(required = false) String region,
|
| 73 |
+
@RequestParam(required = false) List<String> labels,
|
| 74 |
+
@RequestParam(required = false) String nameContains,
|
| 75 |
+
@RequestParam(required = false) UUID connectionId) {
|
| 76 |
+
log.info("REST request to get all Assets with filters: provider={}, type={}, region={}, labels={}, name={}, connectionId={}",
|
| 77 |
+
cloudProvider, assetType, region, labels, nameContains, connectionId);
|
| 78 |
+
Page<AssetOutputDTO> assetPage = assetService.getAllAssets(pageable, cloudProvider, assetType, region, labels, nameContains, connectionId);
|
| 79 |
+
return ResponseEntity.ok(assetPage);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
@GetMapping("/assets/{assetId}")
|
| 83 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_USER')") // Adjust roles
|
| 84 |
+
public ResponseEntity<AssetOutputDTO> getAssetById(@PathVariable UUID assetId) {
|
| 85 |
+
log.info("REST request to get Asset by id: {}", assetId);
|
| 86 |
+
AssetOutputDTO assetDTO = assetService.getAssetById(assetId);
|
| 87 |
+
return ResponseEntity.ok(assetDTO);
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
@PostMapping("/assets")
|
| 91 |
+
@PreAuthorize("hasAuthority('ROLE_ADMIN') or @systemSecurityService.isSystemUser(authentication)") // Example: Admin or System
|
| 92 |
+
public ResponseEntity<AssetOutputDTO> createAsset(@Valid @RequestBody AssetInputDTO assetInputDTO) {
|
| 93 |
+
log.info("REST request to create Asset: {}", assetInputDTO.getAssetName());
|
| 94 |
+
UUID creatorUserId = SecurityUtils.getAuthenticatedUserId();
|
| 95 |
+
|
| 96 |
+
AssetOutputDTO createdAsset = assetService.createAsset(assetInputDTO, creatorUserId);
|
| 97 |
+
|
| 98 |
+
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
|
| 99 |
+
.path("/{id}")
|
| 100 |
+
.buildAndExpand(createdAsset.getAssetId())
|
| 101 |
+
.toUri();
|
| 102 |
+
|
| 103 |
+
return ResponseEntity.created(location).body(createdAsset);
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
@PutMapping("/assets/{assetId}/metadata/business")
|
| 107 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_DATA_STEWARD')") // Example roles, adjust as needed
|
| 108 |
+
public ResponseEntity<AssetOutputDTO> updateBusinessMetadata(
|
| 109 |
+
@PathVariable UUID assetId,
|
| 110 |
+
@RequestBody Map<String, Object> businessMetadata) {
|
| 111 |
+
log.info("REST request to update business metadata for Asset ID: {}", assetId);
|
| 112 |
+
UUID updaterUserId = SecurityUtils.getAuthenticatedUserId();
|
| 113 |
+
|
| 114 |
+
AssetOutputDTO updatedAsset = assetService.updateBusinessMetadata(assetId, businessMetadata, updaterUserId);
|
| 115 |
+
return ResponseEntity.ok(updatedAsset);
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
@PutMapping("/assets/{assetId}/business-context")
|
| 119 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_DATA_STEWARD', 'ROLE_POLICY_MANAGER')")
|
| 120 |
+
@Operation(
|
| 121 |
+
summary = "Update enhanced business context and metadata",
|
| 122 |
+
description = "Update comprehensive business metadata including steward relationships, compliance classification, and business context",
|
| 123 |
+
tags = {"Assets", "Business Metadata"}
|
| 124 |
+
)
|
| 125 |
+
@ApiResponses(value = {
|
| 126 |
+
@ApiResponse(responseCode = "200", description = "Business context updated successfully"),
|
| 127 |
+
@ApiResponse(responseCode = "400", description = "Invalid business metadata input"),
|
| 128 |
+
@ApiResponse(responseCode = "401", description = "Unauthorized"),
|
| 129 |
+
@ApiResponse(responseCode = "403", description = "Forbidden - insufficient permissions"),
|
| 130 |
+
@ApiResponse(responseCode = "404", description = "Asset not found"),
|
| 131 |
+
@ApiResponse(responseCode = "500", description = "Internal server error")
|
| 132 |
+
})
|
| 133 |
+
public ResponseEntity<BusinessMetadataResponseDTO> updateBusinessContext(
|
| 134 |
+
@Parameter(description = "Asset ID", required = true) @PathVariable UUID assetId,
|
| 135 |
+
@Parameter(description = "Enhanced business metadata with steward relationships and compliance classification", required = true)
|
| 136 |
+
@Valid @RequestBody BusinessMetadataInputDTO businessMetadataInput) {
|
| 137 |
+
log.info("REST request to update enhanced business context for Asset ID: {}", assetId);
|
| 138 |
+
UUID updaterUserId = SecurityUtils.getAuthenticatedUserId();
|
| 139 |
+
|
| 140 |
+
BusinessMetadataResponseDTO response = assetService.updateBusinessContext(assetId, businessMetadataInput, updaterUserId);
|
| 141 |
+
return ResponseEntity.ok(response);
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
@GetMapping("/assets/{assetId}/business-metadata")
|
| 145 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_DATA_STEWARD', 'ROLE_POLICY_MANAGER', 'ROLE_USER')")
|
| 146 |
+
@Operation(
|
| 147 |
+
summary = "Get comprehensive business metadata",
|
| 148 |
+
description = "Retrieve complete business metadata including business context, steward relationships, compliance classification, audit information, and validation status",
|
| 149 |
+
tags = {"Assets", "Business Metadata"}
|
| 150 |
+
)
|
| 151 |
+
@ApiResponses(value = {
|
| 152 |
+
@ApiResponse(responseCode = "200", description = "Business metadata retrieved successfully"),
|
| 153 |
+
@ApiResponse(responseCode = "401", description = "Unauthorized"),
|
| 154 |
+
@ApiResponse(responseCode = "403", description = "Forbidden - insufficient permissions"),
|
| 155 |
+
@ApiResponse(responseCode = "404", description = "Asset not found"),
|
| 156 |
+
@ApiResponse(responseCode = "500", description = "Internal server error")
|
| 157 |
+
})
|
| 158 |
+
public ResponseEntity<BusinessMetadataResponseDTO> getBusinessMetadata(
|
| 159 |
+
@Parameter(description = "Asset ID", required = true) @PathVariable UUID assetId) {
|
| 160 |
+
log.info("REST request to get business metadata for Asset ID: {}", assetId);
|
| 161 |
+
|
| 162 |
+
BusinessMetadataResponseDTO response = assetService.getBusinessMetadata(assetId);
|
| 163 |
+
return ResponseEntity.ok(response);
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
@GetMapping("/assets/{assetId}/lineage")
|
| 167 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_USER')") // Adjust roles as needed
|
| 168 |
+
public ResponseEntity<LineageResponseDTO> getAssetLineage(@PathVariable UUID assetId) {
|
| 169 |
+
log.info("REST request to get lineage for Asset ID: {}", assetId);
|
| 170 |
+
LineageResponseDTO lineageResponse = assetService.getAssetLineage(assetId);
|
| 171 |
+
return ResponseEntity.ok(lineageResponse);
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
@GetMapping("/assets/{assetId}/labels")
|
| 175 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_USER')") // Adjust roles
|
| 176 |
+
public ResponseEntity<AssetLabelsResponseDTO> getAssetLabels(@PathVariable UUID assetId) {
|
| 177 |
+
log.info("REST request to get labels for Asset ID: {}", assetId);
|
| 178 |
+
List<LabelOutputDTO> labels = assetService.getAssetLabels(assetId);
|
| 179 |
+
return ResponseEntity.ok(new AssetLabelsResponseDTO(labels));
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
@PostMapping("/assets/{assetId}/labels")
|
| 183 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_USER')") // Or more specific roles like Data Steward
|
| 184 |
+
public ResponseEntity<AssetLabelsResponseDTO> assignLabelsToAsset(
|
| 185 |
+
@PathVariable UUID assetId,
|
| 186 |
+
@Valid @RequestBody AssetLabelsPostRequestDTO labelsToAssignRequest) {
|
| 187 |
+
log.info("REST request to assign labels to Asset ID: {}", assetId);
|
| 188 |
+
UUID assignerUserId = SecurityUtils.getAuthenticatedUserId();
|
| 189 |
+
|
| 190 |
+
List<LabelOutputDTO> updatedLabels = assetService.assignLabelsToAsset(assetId, labelsToAssignRequest, assignerUserId);
|
| 191 |
+
return ResponseEntity.ok(new AssetLabelsResponseDTO(updatedLabels));
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
@DeleteMapping("/assets/{assetId}/labels/{labelName}")
|
| 195 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_USER')") // Or more specific roles
|
| 196 |
+
public ResponseEntity<Void> removeLabelFromAsset(
|
| 197 |
+
@PathVariable UUID assetId,
|
| 198 |
+
@PathVariable String labelName) {
|
| 199 |
+
log.info("REST request to remove label '{}' from Asset ID: {}", labelName, assetId);
|
| 200 |
+
UUID removerUserId = SecurityUtils.getAuthenticatedUserId();
|
| 201 |
+
|
| 202 |
+
assetService.removeLabelFromAsset(assetId, labelName, removerUserId);
|
| 203 |
+
return ResponseEntity.noContent().build();
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
@GetMapping("/assets/{assetId}/usage")
|
| 207 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_DATA_STEWARD', 'ROLE_USER')")
|
| 208 |
+
@Operation(
|
| 209 |
+
summary = "Get asset usage analytics",
|
| 210 |
+
description = "Retrieve comprehensive usage analytics for a specific asset including access patterns, user behavior, performance metrics, and optimization recommendations",
|
| 211 |
+
tags = {"Asset Management"},
|
| 212 |
+
responses = {
|
| 213 |
+
@ApiResponse(responseCode = "200", description = "Usage analytics retrieved successfully"),
|
| 214 |
+
@ApiResponse(responseCode = "404", description = "Asset not found"),
|
| 215 |
+
@ApiResponse(responseCode = "403", description = "Access denied")
|
| 216 |
+
}
|
| 217 |
+
)
|
| 218 |
+
public ResponseEntity<AssetUsageAnalyticsDTO> getAssetUsageAnalytics(
|
| 219 |
+
@Parameter(description = "Asset ID", required = true) @PathVariable UUID assetId) {
|
| 220 |
+
|
| 221 |
+
AssetUsageAnalyticsDTO analytics = assetService.getAssetUsageAnalytics(assetId);
|
| 222 |
+
return ResponseEntity.ok(analytics);
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
@GetMapping("/assets/{assetId}/schema")
|
| 226 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_DATA_STEWARD', 'ROLE_DATA_ENGINEER', 'ROLE_USER')")
|
| 227 |
+
@Operation(
|
| 228 |
+
summary = "Get comprehensive asset schema information",
|
| 229 |
+
description = "Retrieve detailed schema structure including field metadata, data types, constraints, indexes, statistics, lineage, and evolution history. This Priority 2 endpoint provides enhanced catalog functionality for complete asset understanding.",
|
| 230 |
+
tags = {"Asset Management", "Schema"},
|
| 231 |
+
responses = {
|
| 232 |
+
@ApiResponse(
|
| 233 |
+
responseCode = "200",
|
| 234 |
+
description = "Asset schema information retrieved successfully",
|
| 235 |
+
content = @Content(
|
| 236 |
+
mediaType = "application/json",
|
| 237 |
+
schema = @Schema(implementation = AssetSchemaDTO.class),
|
| 238 |
+
examples = @ExampleObject(
|
| 239 |
+
name = "Customer Table Schema",
|
| 240 |
+
description = "Example schema for a customer table with comprehensive metadata",
|
| 241 |
+
value = """
|
| 242 |
+
{
|
| 243 |
+
"assetId": "123e4567-e89b-12d3-a456-426614174000",
|
| 244 |
+
"assetName": "customer_master_table",
|
| 245 |
+
"schemaVersion": "2.1.0",
|
| 246 |
+
"schemaType": {
|
| 247 |
+
"type": "TABLE",
|
| 248 |
+
"format": "PARQUET",
|
| 249 |
+
"encoding": "UTF-8"
|
| 250 |
+
},
|
| 251 |
+
"fields": [
|
| 252 |
+
{
|
| 253 |
+
"fieldName": "customer_id",
|
| 254 |
+
"dataType": "BIGINT",
|
| 255 |
+
"logicalType": "IDENTIFIER",
|
| 256 |
+
"nullable": false,
|
| 257 |
+
"primaryKey": true,
|
| 258 |
+
"description": "Unique identifier for customer",
|
| 259 |
+
"classification": "PII",
|
| 260 |
+
"statistics": {
|
| 261 |
+
"distinctCount": 125000,
|
| 262 |
+
"fillRate": 100.0
|
| 263 |
+
}
|
| 264 |
+
}
|
| 265 |
+
],
|
| 266 |
+
"statistics": {
|
| 267 |
+
"totalRows": 125000,
|
| 268 |
+
"totalColumns": 5,
|
| 269 |
+
"totalSize": 52428800,
|
| 270 |
+
"compressionRatio": 0.75
|
| 271 |
+
}
|
| 272 |
+
}
|
| 273 |
+
"""
|
| 274 |
+
)
|
| 275 |
+
)
|
| 276 |
+
),
|
| 277 |
+
@ApiResponse(responseCode = "404", description = "Asset not found"),
|
| 278 |
+
@ApiResponse(responseCode = "403", description = "Access denied - insufficient permissions"),
|
| 279 |
+
@ApiResponse(responseCode = "500", description = "Internal server error")
|
| 280 |
+
}
|
| 281 |
+
)
|
| 282 |
+
public ResponseEntity<AssetSchemaDTO> getAssetSchema(
|
| 283 |
+
@Parameter(
|
| 284 |
+
description = "Unique identifier of the asset to retrieve schema information for",
|
| 285 |
+
required = true,
|
| 286 |
+
example = "123e4567-e89b-12d3-a456-426614174000"
|
| 287 |
+
)
|
| 288 |
+
@PathVariable UUID assetId) {
|
| 289 |
+
|
| 290 |
+
AssetSchemaDTO schema = assetService.getAssetSchema(assetId);
|
| 291 |
+
return ResponseEntity.ok(schema);
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
@GetMapping("/assets/{assetId}/lineage/enhanced")
|
| 295 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_DATA_STEWARD', 'ROLE_DATA_ENGINEER', 'ROLE_USER')")
|
| 296 |
+
@Operation(
|
| 297 |
+
summary = "Get enhanced data lineage visualization",
|
| 298 |
+
description = "Retrieve comprehensive lineage information with visualization metadata, graph nodes, edges, statistics, and critical path analysis. This Priority 2 endpoint provides detailed lineage for interactive graph rendering.",
|
| 299 |
+
tags = {"Asset Management", "Lineage"},
|
| 300 |
+
responses = {
|
| 301 |
+
@ApiResponse(responseCode = "200", description = "Enhanced lineage information retrieved successfully"),
|
| 302 |
+
@ApiResponse(responseCode = "404", description = "Asset not found"),
|
| 303 |
+
@ApiResponse(responseCode = "403", description = "Access denied"),
|
| 304 |
+
@ApiResponse(responseCode = "500", description = "Internal server error")
|
| 305 |
+
}
|
| 306 |
+
)
|
| 307 |
+
public ResponseEntity<EnhancedLineageDTO> getEnhancedLineage(
|
| 308 |
+
@Parameter(description = "Asset ID", required = true) @PathVariable UUID assetId,
|
| 309 |
+
@Parameter(description = "Maximum lineage depth to traverse", example = "5")
|
| 310 |
+
@RequestParam(required = false, defaultValue = "5") Integer maxDepth) {
|
| 311 |
+
|
| 312 |
+
EnhancedLineageDTO lineage = assetService.getEnhancedLineage(assetId, maxDepth);
|
| 313 |
+
return ResponseEntity.ok(lineage);
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
@GetMapping("/assets/{assetId}/policies")
|
| 317 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_DATA_STEWARD', 'ROLE_POLICY_MANAGER', 'ROLE_USER')")
|
| 318 |
+
@Operation(
|
| 319 |
+
summary = "Get asset policy mapping",
|
| 320 |
+
description = "Retrieve information about which policies apply to an asset, their compliance status, risk assessment, and recommendations. This Priority 2 endpoint provides policy-asset relationship details.",
|
| 321 |
+
tags = {"Asset Management", "Policy"},
|
| 322 |
+
responses = {
|
| 323 |
+
@ApiResponse(responseCode = "200", description = "Asset policy mapping retrieved successfully"),
|
| 324 |
+
@ApiResponse(responseCode = "404", description = "Asset not found"),
|
| 325 |
+
@ApiResponse(responseCode = "403", description = "Access denied"),
|
| 326 |
+
@ApiResponse(responseCode = "500", description = "Internal server error")
|
| 327 |
+
}
|
| 328 |
+
)
|
| 329 |
+
public ResponseEntity<AssetPolicyMappingDTO> getAssetPolicyMapping(
|
| 330 |
+
@Parameter(description = "Asset ID", required = true) @PathVariable UUID assetId) {
|
| 331 |
+
|
| 332 |
+
AssetPolicyMappingDTO mapping = assetService.getAssetPolicyMapping(assetId);
|
| 333 |
+
return ResponseEntity.ok(mapping);
|
| 334 |
+
}
|
| 335 |
+
|
| 336 |
+
@GetMapping("/filters")
|
| 337 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_DATA_STEWARD', 'ROLE_DATA_ENGINEER', 'ROLE_USER')")
|
| 338 |
+
@Operation(
|
| 339 |
+
summary = "Get catalog faceted search filters",
|
| 340 |
+
description = "Retrieve filter metadata for advanced search and filtering in the catalog UI. This Priority 2 endpoint provides filter categories, options, quick filters, and search capabilities.",
|
| 341 |
+
tags = {"Catalog", "Search"},
|
| 342 |
+
responses = {
|
| 343 |
+
@ApiResponse(responseCode = "200", description = "Catalog filters retrieved successfully"),
|
| 344 |
+
@ApiResponse(responseCode = "403", description = "Access denied"),
|
| 345 |
+
@ApiResponse(responseCode = "500", description = "Internal server error")
|
| 346 |
+
}
|
| 347 |
+
)
|
| 348 |
+
public ResponseEntity<CatalogFiltersDTO> getCatalogFilters() {
|
| 349 |
+
|
| 350 |
+
CatalogFiltersDTO filters = assetService.getCatalogFilters();
|
| 351 |
+
return ResponseEntity.ok(filters);
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
@GetMapping("/assets/{assetId}/compliance")
|
| 355 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_DATA_STEWARD', 'ROLE_POLICY_MANAGER', 'ROLE_COMPLIANCE_OFFICER')")
|
| 356 |
+
@Operation(
|
| 357 |
+
summary = "Get asset compliance status",
|
| 358 |
+
description = "Retrieve detailed compliance status including framework compliance, violations, controls, risk assessment, and recommendations. This Priority 2 endpoint provides comprehensive compliance monitoring.",
|
| 359 |
+
tags = {"Asset Management", "Compliance"},
|
| 360 |
+
responses = {
|
| 361 |
+
@ApiResponse(responseCode = "200", description = "Asset compliance status retrieved successfully"),
|
| 362 |
+
@ApiResponse(responseCode = "404", description = "Asset not found"),
|
| 363 |
+
@ApiResponse(responseCode = "403", description = "Access denied"),
|
| 364 |
+
@ApiResponse(responseCode = "500", description = "Internal server error")
|
| 365 |
+
}
|
| 366 |
+
)
|
| 367 |
+
public ResponseEntity<AssetComplianceStatusDTO> getAssetComplianceStatus(
|
| 368 |
+
@Parameter(description = "Asset ID", required = true) @PathVariable UUID assetId) {
|
| 369 |
+
|
| 370 |
+
AssetComplianceStatusDTO compliance = assetService.getAssetComplianceStatus(assetId);
|
| 371 |
+
return ResponseEntity.ok(compliance);
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
// TODO: Implement other Asset related endpoints (Taxonomy APIs)
|
| 375 |
+
}
|
src/main/java/com/dalab/catalog/web/rest/LabelTaxonomyController.java
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.web.rest;
|
| 2 |
+
|
| 3 |
+
import com.dalab.catalog.dto.TaxonomyLabelDTO;
|
| 4 |
+
import com.dalab.catalog.dto.TaxonomyLabelsResponseDTO;
|
| 5 |
+
import com.dalab.catalog.service.ILabelTaxonomyService;
|
| 6 |
+
import com.dalab.catalog.security.SecurityUtils;
|
| 7 |
+
import jakarta.validation.Valid;
|
| 8 |
+
import org.slf4j.Logger;
|
| 9 |
+
import org.slf4j.LoggerFactory;
|
| 10 |
+
import org.springframework.beans.factory.annotation.Autowired;
|
| 11 |
+
import org.springframework.data.domain.Page;
|
| 12 |
+
import org.springframework.data.domain.Pageable;
|
| 13 |
+
import org.springframework.data.web.PageableDefault;
|
| 14 |
+
import org.springframework.http.ResponseEntity;
|
| 15 |
+
import org.springframework.security.access.prepost.PreAuthorize;
|
| 16 |
+
import org.springframework.web.bind.annotation.*;
|
| 17 |
+
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
| 18 |
+
|
| 19 |
+
import java.net.URI;
|
| 20 |
+
import java.util.UUID;
|
| 21 |
+
|
| 22 |
+
@RestController
|
| 23 |
+
@RequestMapping("/api/v1/catalog/taxonomy")
|
| 24 |
+
public class LabelTaxonomyController {
|
| 25 |
+
|
| 26 |
+
private static final Logger log = LoggerFactory.getLogger(LabelTaxonomyController.class);
|
| 27 |
+
|
| 28 |
+
private final ILabelTaxonomyService labelTaxonomyService;
|
| 29 |
+
|
| 30 |
+
@Autowired
|
| 31 |
+
public LabelTaxonomyController(ILabelTaxonomyService labelTaxonomyService) {
|
| 32 |
+
this.labelTaxonomyService = labelTaxonomyService;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
@GetMapping("/labels")
|
| 36 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_USER')") // Adjust as needed
|
| 37 |
+
public ResponseEntity<TaxonomyLabelsResponseDTO> getAllLabels(
|
| 38 |
+
@PageableDefault(size = 100, sort = "labelName") Pageable pageable,
|
| 39 |
+
@RequestParam(required = false) String category,
|
| 40 |
+
@RequestParam(required = false) String nameContains) {
|
| 41 |
+
log.info("REST request to get all taxonomy labels with filters: category={}, nameContains={}", category, nameContains);
|
| 42 |
+
Page<TaxonomyLabelDTO> labelPage = labelTaxonomyService.getAllLabels(pageable, category, nameContains);
|
| 43 |
+
return ResponseEntity.ok(new TaxonomyLabelsResponseDTO(labelPage.getContent()));
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
@PostMapping("/labels")
|
| 47 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_POLICY_MANAGER')")
|
| 48 |
+
public ResponseEntity<TaxonomyLabelDTO> createLabel(@Valid @RequestBody TaxonomyLabelDTO labelDTO) {
|
| 49 |
+
log.info("REST request to create taxonomy label: {}", labelDTO.getLabelName());
|
| 50 |
+
UUID creatorUserId = SecurityUtils.getAuthenticatedUserId();
|
| 51 |
+
TaxonomyLabelDTO createdLabel = labelTaxonomyService.createLabel(labelDTO, creatorUserId);
|
| 52 |
+
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
|
| 53 |
+
.path("/{idOrName}")
|
| 54 |
+
.buildAndExpand(createdLabel.getLabelId())
|
| 55 |
+
.toUri();
|
| 56 |
+
return ResponseEntity.created(location).body(createdLabel);
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
@GetMapping("/labels/{idOrName}")
|
| 60 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_USER')")
|
| 61 |
+
public ResponseEntity<TaxonomyLabelDTO> getLabelByIdOrName(@PathVariable String idOrName) {
|
| 62 |
+
log.info("REST request to get taxonomy label by id/name: {}", idOrName);
|
| 63 |
+
TaxonomyLabelDTO labelDTO = labelTaxonomyService.getLabelByIdOrName(idOrName);
|
| 64 |
+
return ResponseEntity.ok(labelDTO);
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
@PutMapping("/labels/{idOrName}")
|
| 68 |
+
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_POLICY_MANAGER')")
|
| 69 |
+
public ResponseEntity<TaxonomyLabelDTO> updateLabel(
|
| 70 |
+
@PathVariable String idOrName,
|
| 71 |
+
@Valid @RequestBody TaxonomyLabelDTO labelDTO) {
|
| 72 |
+
log.info("REST request to update taxonomy label '{}': {}", idOrName, labelDTO.getLabelName());
|
| 73 |
+
UUID updaterUserId = SecurityUtils.getAuthenticatedUserId();
|
| 74 |
+
TaxonomyLabelDTO updatedLabel = labelTaxonomyService.updateLabel(idOrName, labelDTO, updaterUserId);
|
| 75 |
+
return ResponseEntity.ok(updatedLabel);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
@DeleteMapping("/labels/{idOrName}")
|
| 79 |
+
@PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_POLICY_MANAGER')")
|
| 80 |
+
public ResponseEntity<Void> deleteLabel(@PathVariable String idOrName) {
|
| 81 |
+
log.info("REST request to delete taxonomy label by id/name: {}", idOrName);
|
| 82 |
+
labelTaxonomyService.deleteLabel(idOrName);
|
| 83 |
+
return ResponseEntity.noContent().build();
|
| 84 |
+
}
|
| 85 |
+
}
|
src/main/resources/application.properties
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# DALab Da-catalog Service Configuration
|
| 2 |
+
# Standardized configuration for Docker and local development
|
| 3 |
+
|
| 4 |
+
spring.application.name=da-catalog
|
| 5 |
+
|
| 6 |
+
# Server Configuration
|
| 7 |
+
server.port=8080
|
| 8 |
+
server.servlet.context-path=/api/v1/catalog
|
| 9 |
+
|
| 10 |
+
# Database Configuration - using infrastructure PostgreSQL
|
| 11 |
+
spring.datasource.url=jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/da_catalog
|
| 12 |
+
spring.datasource.username=${DB_USER:da_catalog_user}
|
| 13 |
+
spring.datasource.password=${DB_PASS:da_catalog_pass}
|
| 14 |
+
spring.datasource.driver-class-name=org.postgresql.Driver
|
| 15 |
+
|
| 16 |
+
# Connection Pool Configuration
|
| 17 |
+
spring.datasource.hikari.maximum-pool-size=${DB_POOL_SIZE:10}
|
| 18 |
+
spring.datasource.hikari.minimum-idle=2
|
| 19 |
+
spring.datasource.hikari.connection-timeout=30000
|
| 20 |
+
spring.datasource.hikari.idle-timeout=600000
|
| 21 |
+
spring.datasource.hikari.max-lifetime=1800000
|
| 22 |
+
|
| 23 |
+
# JPA Configuration
|
| 24 |
+
spring.jpa.hibernate.ddl-auto=${JPA_DDL_AUTO:update}
|
| 25 |
+
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
| 26 |
+
spring.jpa.properties.hibernate.default_schema=da_catalog_schema
|
| 27 |
+
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
|
| 28 |
+
spring.jpa.properties.hibernate.format_sql=true
|
| 29 |
+
spring.jpa.show-sql=${JPA_SHOW_SQL:false}
|
| 30 |
+
|
| 31 |
+
# Kafka Configuration - using infrastructure Kafka
|
| 32 |
+
spring.kafka.bootstrap-servers=${KAFKA_BOOTSTRAP_SERVERS:localhost:9092}
|
| 33 |
+
spring.kafka.consumer.group-id=${spring.application.name}
|
| 34 |
+
spring.kafka.consumer.auto-offset-reset=earliest
|
| 35 |
+
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
|
| 36 |
+
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
|
| 37 |
+
spring.kafka.consumer.properties.spring.json.trusted.packages=com.dalab.*
|
| 38 |
+
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
|
| 39 |
+
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
|
| 40 |
+
spring.kafka.producer.retries=3
|
| 41 |
+
spring.kafka.producer.acks=all
|
| 42 |
+
|
| 43 |
+
# Security Configuration - using infrastructure Keycloak
|
| 44 |
+
spring.security.oauth2.resourceserver.jwt.issuer-uri=${KEYCLOAK_AUTH_SERVER_URL:http://localhost:8180}/realms/dalab
|
| 45 |
+
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=${KEYCLOAK_AUTH_SERVER_URL:http://localhost:8180}/realms/dalab/protocol/openid-connect/certs
|
| 46 |
+
|
| 47 |
+
# Redis Configuration
|
| 48 |
+
spring.data.redis.host=${REDIS_HOST:localhost}
|
| 49 |
+
spring.data.redis.port=${REDIS_PORT:6379}
|
| 50 |
+
spring.data.redis.password=${REDIS_PASS:}
|
| 51 |
+
spring.data.redis.timeout=2000ms
|
| 52 |
+
|
| 53 |
+
# Management/Actuator Configuration
|
| 54 |
+
management.endpoints.web.exposure.include=health,info,metrics,prometheus
|
| 55 |
+
management.endpoint.health.show-details=when-authorized
|
| 56 |
+
management.metrics.export.prometheus.enabled=true
|
| 57 |
+
management.endpoint.health.probes.enabled=true
|
| 58 |
+
|
| 59 |
+
# Logging Configuration
|
| 60 |
+
logging.level.com.dalab=${LOG_LEVEL:INFO}
|
| 61 |
+
logging.level.org.springframework.kafka=INFO
|
| 62 |
+
logging.level.org.springframework.security=INFO
|
| 63 |
+
logging.level.org.hibernate.SQL=${SQL_LOG_LEVEL:WARN}
|
| 64 |
+
logging.pattern.console=%d{HH:mm:ss.SSS} [%thread] %-5level [%logger{36}] - %msg%n
|
| 65 |
+
|
| 66 |
+
# DALab Specific Configuration
|
| 67 |
+
dalab.service.name=Da-catalog Service
|
| 68 |
+
dalab.service.version=1.0.0
|
| 69 |
+
dalab.service.description=Da-catalog microservice for DALab platform
|
| 70 |
+
|
| 71 |
+
# Common entities database connection (for da-protos entities)
|
| 72 |
+
dalab.common-db.url=jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/dalab_common
|
| 73 |
+
dalab.common-db.username=${DB_USER:dalab}
|
| 74 |
+
dalab.common-db.password=${DB_PASS:dalab}
|
| 75 |
+
|
| 76 |
+
# Kafka topics
|
| 77 |
+
dalab.kafka.topics.asset-changes=dalab.assets.changes
|
| 78 |
+
dalab.kafka.topics.asset-discovered=dalab.assets.discovered
|
| 79 |
+
dalab.kafka.topics.asset-labeled=dalab.assets.labeled
|
| 80 |
+
dalab.kafka.topics.policy-evaluations=dalab.policies.evaluations
|
| 81 |
+
|
| 82 |
+
# CORS Configuration
|
| 83 |
+
dalab.security.allowed-origins=${CORS_ORIGINS:http://localhost:3000,http://localhost:4200}
|
| 84 |
+
|
| 85 |
+
# JWT Claims Configuration
|
| 86 |
+
dalab.security.jwt.claims.user-id=sub
|
| 87 |
+
dalab.security.jwt.claims.username=preferred_username
|
| 88 |
+
dalab.security.jwt.claims.roles=realm_access.roles
|
| 89 |
+
|
| 90 |
+
# Profile specific overrides
|
| 91 |
+
spring.profiles.active=${SPRING_PROFILES_ACTIVE:dev}
|
src/test/java/com/dalab/catalog/controller/CatalogControllerTest.java
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.dalab.catalog.controller;
|
| 2 |
+
|
| 3 |
+
import static org.mockito.ArgumentMatchers.*;
|
| 4 |
+
import static org.mockito.Mockito.*;
|
| 5 |
+
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
|
| 6 |
+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
| 7 |
+
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
|
| 8 |
+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
| 9 |
+
|
| 10 |
+
import java.time.LocalDateTime;
|
| 11 |
+
import java.util.*;
|
| 12 |
+
|
| 13 |
+
import org.junit.jupiter.api.BeforeEach;
|
| 14 |
+
import org.junit.jupiter.api.Test;
|
| 15 |
+
import org.junit.jupiter.api.extension.ExtendWith;
|
| 16 |
+
import org.mockito.InjectMocks;
|
| 17 |
+
import org.mockito.Mock;
|
| 18 |
+
import org.mockito.junit.jupiter.MockitoExtension;
|
| 19 |
+
import org.springframework.beans.factory.annotation.Autowired;
|
| 20 |
+
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
| 21 |
+
import org.springframework.boot.test.mock.mockito.MockBean;
|
| 22 |
+
import org.springframework.data.domain.Page;
|
| 23 |
+
import org.springframework.data.domain.PageImpl;
|
| 24 |
+
import org.springframework.data.domain.PageRequest;
|
| 25 |
+
import org.springframework.data.domain.Pageable;
|
| 26 |
+
import org.springframework.http.MediaType;
|
| 27 |
+
import org.springframework.security.test.context.support.WithMockUser;
|
| 28 |
+
import org.springframework.test.context.ActiveProfiles;
|
| 29 |
+
import org.springframework.test.web.servlet.MockMvc;
|
| 30 |
+
|
| 31 |
+
import com.dalab.catalog.client.rest.dto.AssetCreateRequest;
|
| 32 |
+
import com.dalab.catalog.client.rest.dto.AssetDetail;
|
| 33 |
+
import com.dalab.catalog.client.rest.dto.AssetSearchRequest;
|
| 34 |
+
import com.dalab.catalog.client.rest.dto.AssetSummary;
|
| 35 |
+
import com.dalab.catalog.client.rest.dto.BusinessMetadataRequest;
|
| 36 |
+
import com.dalab.catalog.client.rest.dto.LabelAssignmentRequest;
|
| 37 |
+
import com.dalab.catalog.client.rest.dto.LineageDetail;
|
| 38 |
+
import com.dalab.catalog.client.rest.dto.UsageAnalytics;
|
| 39 |
+
import com.dalab.catalog.common.model.Asset;
|
| 40 |
+
import com.dalab.catalog.common.model.BusinessMetadata;
|
| 41 |
+
import com.dalab.catalog.common.model.Label;
|
| 42 |
+
import com.dalab.catalog.common.model.enums.CloudProvider;
|
| 43 |
+
import com.dalab.catalog.common.model.enums.ServiceType;
|
| 44 |
+
import com.dalab.catalog.service.ICatalogService;
|
| 45 |
+
import com.fasterxml.jackson.databind.ObjectMapper;
|
| 46 |
+
|
| 47 |
+
@WebMvcTest(CatalogController.class)
|
| 48 |
+
@ExtendWith(MockitoExtension.class)
|
| 49 |
+
@ActiveProfiles("test")
|
| 50 |
+
class CatalogControllerTest {
|
| 51 |
+
|
| 52 |
+
@Autowired
|
| 53 |
+
private MockMvc mockMvc;
|
| 54 |
+
|
| 55 |
+
@MockBean
|
| 56 |
+
private ICatalogService catalogService;
|
| 57 |
+
|
| 58 |
+
@Autowired
|
| 59 |
+
private ObjectMapper objectMapper;
|
| 60 |
+
|
| 61 |
+
private Asset testAsset;
|
| 62 |
+
private AssetSummary testAssetSummary;
|
| 63 |
+
private AssetDetail testAssetDetail;
|
| 64 |
+
private Label testLabel;
|
| 65 |
+
private BusinessMetadata testBusinessMetadata;
|
| 66 |
+
|
| 67 |
+
@BeforeEach
|
| 68 |
+
void setUp() {
|
| 69 |
+
// Create test asset
|
| 70 |
+
testAsset = new Asset();
|
| 71 |
+
testAsset.setId(1L);
|
| 72 |
+
testAsset.setAssetUri("gs://test-bucket/data.csv");
|
| 73 |
+
testAsset.setAssetName("Test Asset");
|
| 74 |
+
testAsset.setCloudProvider(CloudProvider.GCP);
|
| 75 |
+
testAsset.setServiceType(ServiceType.STORAGE);
|
| 76 |
+
testAsset.setStatus("ACTIVE");
|
| 77 |
+
testAsset.setDalabDiscoveredTs(LocalDateTime.now());
|
| 78 |
+
testAsset.setDalabLastScannedTs(LocalDateTime.now());
|
| 79 |
+
|
| 80 |
+
// Create test asset summary
|
| 81 |
+
testAssetSummary = AssetSummary.builder()
|
| 82 |
+
.assetId(1L)
|
| 83 |
+
.assetUri("gs://test-bucket/data.csv")
|
| 84 |
+
.assetName("Test Asset")
|
| 85 |
+
.cloudProvider("GCP")
|
| 86 |
+
.serviceType("STORAGE")
|
| 87 |
+
.status("ACTIVE")
|
| 88 |
+
.build();
|
| 89 |
+
|
| 90 |
+
// Create test asset detail
|
| 91 |
+
testAssetDetail = AssetDetail.builder()
|
| 92 |
+
.assetId(1L)
|
| 93 |
+
.assetUri("gs://test-bucket/data.csv")
|
| 94 |
+
.assetName("Test Asset")
|
| 95 |
+
.cloudProvider("GCP")
|
| 96 |
+
.serviceType("STORAGE")
|
| 97 |
+
.status("ACTIVE")
|
| 98 |
+
.discoveredTs(LocalDateTime.now())
|
| 99 |
+
.lastScannedTs(LocalDateTime.now())
|
| 100 |
+
.build();
|
| 101 |
+
|
| 102 |
+
// Create test label
|
| 103 |
+
testLabel = new Label();
|
| 104 |
+
testLabel.setId(1L);
|
| 105 |
+
testLabel.setLabelName("PII");
|
| 106 |
+
testLabel.setLabelCategory("COMPLIANCE");
|
| 107 |
+
testLabel.setDescription("Personally Identifiable Information");
|
| 108 |
+
|
| 109 |
+
// Create test business metadata
|
| 110 |
+
testBusinessMetadata = new BusinessMetadata();
|
| 111 |
+
testBusinessMetadata.setId(1L);
|
| 112 |
+
testBusinessMetadata.setAssetId(1L);
|
| 113 |
+
testBusinessMetadata.setBusinessOwner("John Doe");
|
| 114 |
+
testBusinessMetadata.setDataSteward("Jane Smith");
|
| 115 |
+
testBusinessMetadata.setDepartment("Engineering");
|
| 116 |
+
testBusinessMetadata.setBusinessCriticality("HIGH");
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
@Test
|
| 120 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 121 |
+
void createAsset_AsDataEngineer_ShouldSucceed() throws Exception {
|
| 122 |
+
// Given
|
| 123 |
+
AssetCreateRequest request = new AssetCreateRequest();
|
| 124 |
+
request.setAssetUri("gs://test-bucket/new-data.csv");
|
| 125 |
+
request.setAssetName("New Test Asset");
|
| 126 |
+
request.setCloudProvider("GCP");
|
| 127 |
+
request.setServiceType("STORAGE");
|
| 128 |
+
|
| 129 |
+
when(catalogService.createAsset(any(AssetCreateRequest.class))).thenReturn(testAssetDetail);
|
| 130 |
+
|
| 131 |
+
// When & Then
|
| 132 |
+
mockMvc.perform(post("/api/v1/catalog/assets")
|
| 133 |
+
.with(csrf())
|
| 134 |
+
.contentType(MediaType.APPLICATION_JSON)
|
| 135 |
+
.content(objectMapper.writeValueAsString(request)))
|
| 136 |
+
.andExpect(status().isCreated())
|
| 137 |
+
.andExpect(jsonPath("$.assetId").value(1))
|
| 138 |
+
.andExpect(jsonPath("$.assetUri").value("gs://test-bucket/data.csv"))
|
| 139 |
+
.andExpect(jsonPath("$.assetName").value("Test Asset"));
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
@Test
|
| 143 |
+
@WithMockUser(authorities = "ROLE_USER")
|
| 144 |
+
void createAsset_AsUser_ShouldBeForbidden() throws Exception {
|
| 145 |
+
// Given
|
| 146 |
+
AssetCreateRequest request = new AssetCreateRequest();
|
| 147 |
+
request.setAssetUri("gs://test-bucket/new-data.csv");
|
| 148 |
+
request.setAssetName("New Test Asset");
|
| 149 |
+
|
| 150 |
+
// When & Then
|
| 151 |
+
mockMvc.perform(post("/api/v1/catalog/assets")
|
| 152 |
+
.with(csrf())
|
| 153 |
+
.contentType(MediaType.APPLICATION_JSON)
|
| 154 |
+
.content(objectMapper.writeValueAsString(request)))
|
| 155 |
+
.andExpect(status().isForbidden());
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
@Test
|
| 159 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 160 |
+
void getAllAssets_AsDataEngineer_ShouldSucceed() throws Exception {
|
| 161 |
+
// Given
|
| 162 |
+
List<AssetSummary> assetList = Arrays.asList(testAssetSummary);
|
| 163 |
+
Page<AssetSummary> assetPage = new PageImpl<>(assetList, PageRequest.of(0, 20), 1);
|
| 164 |
+
|
| 165 |
+
when(catalogService.getAllAssets(any(Pageable.class))).thenReturn(assetPage);
|
| 166 |
+
|
| 167 |
+
// When & Then
|
| 168 |
+
mockMvc.perform(get("/api/v1/catalog/assets")
|
| 169 |
+
.param("page", "0")
|
| 170 |
+
.param("size", "20"))
|
| 171 |
+
.andExpect(status().isOk())
|
| 172 |
+
.andExpect(jsonPath("$.content[0].assetId").value(1))
|
| 173 |
+
.andExpect(jsonPath("$.content[0].assetUri").value("gs://test-bucket/data.csv"))
|
| 174 |
+
.andExpect(jsonPath("$.totalElements").value(1));
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
@Test
|
| 178 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 179 |
+
void getAssetById_AsDataEngineer_ShouldSucceed() throws Exception {
|
| 180 |
+
// Given
|
| 181 |
+
when(catalogService.getAssetById(1L)).thenReturn(Optional.of(testAssetDetail));
|
| 182 |
+
|
| 183 |
+
// When & Then
|
| 184 |
+
mockMvc.perform(get("/api/v1/catalog/assets/{assetId}", 1))
|
| 185 |
+
.andExpect(status().isOk())
|
| 186 |
+
.andExpect(jsonPath("$.assetId").value(1))
|
| 187 |
+
.andExpect(jsonPath("$.assetUri").value("gs://test-bucket/data.csv"))
|
| 188 |
+
.andExpect(jsonPath("$.assetName").value("Test Asset"));
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
@Test
|
| 192 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 193 |
+
void getAssetById_AssetNotFound_ShouldReturnNotFound() throws Exception {
|
| 194 |
+
// Given
|
| 195 |
+
when(catalogService.getAssetById(999L)).thenReturn(Optional.empty());
|
| 196 |
+
|
| 197 |
+
// When & Then
|
| 198 |
+
mockMvc.perform(get("/api/v1/catalog/assets/{assetId}", 999))
|
| 199 |
+
.andExpect(status().isNotFound());
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
@Test
|
| 203 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 204 |
+
void searchAssets_AsDataEngineer_ShouldSucceed() throws Exception {
|
| 205 |
+
// Given
|
| 206 |
+
AssetSearchRequest searchRequest = new AssetSearchRequest();
|
| 207 |
+
searchRequest.setQuery("test");
|
| 208 |
+
searchRequest.setCloudProvider("GCP");
|
| 209 |
+
searchRequest.setServiceType("STORAGE");
|
| 210 |
+
|
| 211 |
+
List<AssetSummary> searchResults = Arrays.asList(testAssetSummary);
|
| 212 |
+
Page<AssetSummary> searchPage = new PageImpl<>(searchResults, PageRequest.of(0, 20), 1);
|
| 213 |
+
|
| 214 |
+
when(catalogService.searchAssets(any(AssetSearchRequest.class), any(Pageable.class)))
|
| 215 |
+
.thenReturn(searchPage);
|
| 216 |
+
|
| 217 |
+
// When & Then
|
| 218 |
+
mockMvc.perform(post("/api/v1/catalog/assets/search")
|
| 219 |
+
.with(csrf())
|
| 220 |
+
.contentType(MediaType.APPLICATION_JSON)
|
| 221 |
+
.content(objectMapper.writeValueAsString(searchRequest))
|
| 222 |
+
.param("page", "0")
|
| 223 |
+
.param("size", "20"))
|
| 224 |
+
.andExpect(status().isOk())
|
| 225 |
+
.andExpect(jsonPath("$.content[0].assetId").value(1))
|
| 226 |
+
.andExpect(jsonPath("$.totalElements").value(1));
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
@Test
|
| 230 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 231 |
+
void assignLabelToAsset_AsDataEngineer_ShouldSucceed() throws Exception {
|
| 232 |
+
// Given
|
| 233 |
+
LabelAssignmentRequest request = new LabelAssignmentRequest();
|
| 234 |
+
request.setLabelId(1L);
|
| 235 |
+
request.setConfidenceScore(0.95);
|
| 236 |
+
request.setSource("MANUAL");
|
| 237 |
+
|
| 238 |
+
when(catalogService.assignLabelToAsset(eq(1L), any(LabelAssignmentRequest.class)))
|
| 239 |
+
.thenReturn(testAssetDetail);
|
| 240 |
+
|
| 241 |
+
// When & Then
|
| 242 |
+
mockMvc.perform(post("/api/v1/catalog/assets/{assetId}/labels", 1)
|
| 243 |
+
.with(csrf())
|
| 244 |
+
.contentType(MediaType.APPLICATION_JSON)
|
| 245 |
+
.content(objectMapper.writeValueAsString(request)))
|
| 246 |
+
.andExpect(status().isOk())
|
| 247 |
+
.andExpect(jsonPath("$.assetId").value(1));
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
@Test
|
| 251 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 252 |
+
void getAssetLineage_AsDataEngineer_ShouldSucceed() throws Exception {
|
| 253 |
+
// Given
|
| 254 |
+
LineageDetail lineageDetail = LineageDetail.builder()
|
| 255 |
+
.assetId(1L)
|
| 256 |
+
.assetUri("gs://test-bucket/data.csv")
|
| 257 |
+
.assetName("Test Asset")
|
| 258 |
+
.upstreamAssets(Arrays.asList())
|
| 259 |
+
.downstreamAssets(Arrays.asList())
|
| 260 |
+
.build();
|
| 261 |
+
|
| 262 |
+
when(catalogService.getAssetLineage(1L)).thenReturn(Optional.of(lineageDetail));
|
| 263 |
+
|
| 264 |
+
// When & Then
|
| 265 |
+
mockMvc.perform(get("/api/v1/catalog/lineage/{assetId}", 1))
|
| 266 |
+
.andExpect(status().isOk())
|
| 267 |
+
.andExpect(jsonPath("$.assetId").value(1))
|
| 268 |
+
.andExpect(jsonPath("$.assetUri").value("gs://test-bucket/data.csv"));
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
@Test
|
| 272 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 273 |
+
void getAssetLineage_AssetNotFound_ShouldReturnNotFound() throws Exception {
|
| 274 |
+
// Given
|
| 275 |
+
when(catalogService.getAssetLineage(999L)).thenReturn(Optional.empty());
|
| 276 |
+
|
| 277 |
+
// When & Then
|
| 278 |
+
mockMvc.perform(get("/api/v1/catalog/lineage/{assetId}", 999))
|
| 279 |
+
.andExpect(status().isNotFound());
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
@Test
|
| 283 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 284 |
+
void updateBusinessMetadata_AsDataEngineer_ShouldSucceed() throws Exception {
|
| 285 |
+
// Given
|
| 286 |
+
BusinessMetadataRequest request = new BusinessMetadataRequest();
|
| 287 |
+
request.setBusinessOwner("John Doe");
|
| 288 |
+
request.setDataSteward("Jane Smith");
|
| 289 |
+
request.setDepartment("Engineering");
|
| 290 |
+
request.setBusinessCriticality("HIGH");
|
| 291 |
+
|
| 292 |
+
when(catalogService.updateBusinessMetadata(eq(1L), any(BusinessMetadataRequest.class)))
|
| 293 |
+
.thenReturn(testBusinessMetadata);
|
| 294 |
+
|
| 295 |
+
// When & Then
|
| 296 |
+
mockMvc.perform(put("/api/v1/catalog/assets/{assetId}/business-metadata", 1)
|
| 297 |
+
.with(csrf())
|
| 298 |
+
.contentType(MediaType.APPLICATION_JSON)
|
| 299 |
+
.content(objectMapper.writeValueAsString(request)))
|
| 300 |
+
.andExpect(status().isOk())
|
| 301 |
+
.andExpect(jsonPath("$.assetId").value(1))
|
| 302 |
+
.andExpect(jsonPath("$.businessOwner").value("John Doe"));
|
| 303 |
+
}
|
| 304 |
+
|
| 305 |
+
@Test
|
| 306 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 307 |
+
void getUsageAnalytics_AsDataEngineer_ShouldSucceed() throws Exception {
|
| 308 |
+
// Given
|
| 309 |
+
UsageAnalytics analytics = UsageAnalytics.builder()
|
| 310 |
+
.assetId(1L)
|
| 311 |
+
.assetUri("gs://test-bucket/data.csv")
|
| 312 |
+
.accessCount(150)
|
| 313 |
+
.lastAccessed(LocalDateTime.now())
|
| 314 |
+
.accessFrequency("HIGH")
|
| 315 |
+
.build();
|
| 316 |
+
|
| 317 |
+
when(catalogService.getUsageAnalytics(1L)).thenReturn(Optional.of(analytics));
|
| 318 |
+
|
| 319 |
+
// When & Then
|
| 320 |
+
mockMvc.perform(get("/api/v1/catalog/assets/{assetId}/usage-analytics", 1))
|
| 321 |
+
.andExpect(status().isOk())
|
| 322 |
+
.andExpect(jsonPath("$.assetId").value(1))
|
| 323 |
+
.andExpect(jsonPath("$.accessCount").value(150))
|
| 324 |
+
.andExpect(jsonPath("$.accessFrequency").value("HIGH"));
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
@Test
|
| 328 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 329 |
+
void getUsageAnalytics_AssetNotFound_ShouldReturnNotFound() throws Exception {
|
| 330 |
+
// Given
|
| 331 |
+
when(catalogService.getUsageAnalytics(999L)).thenReturn(Optional.empty());
|
| 332 |
+
|
| 333 |
+
// When & Then
|
| 334 |
+
mockMvc.perform(get("/api/v1/catalog/assets/{assetId}/usage-analytics", 999))
|
| 335 |
+
.andExpect(status().isNotFound());
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
@Test
|
| 339 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 340 |
+
void deleteAsset_AsDataEngineer_ShouldSucceed() throws Exception {
|
| 341 |
+
// Given
|
| 342 |
+
when(catalogService.deleteAsset(1L)).thenReturn(true);
|
| 343 |
+
|
| 344 |
+
// When & Then
|
| 345 |
+
mockMvc.perform(delete("/api/v1/catalog/assets/{assetId}", 1)
|
| 346 |
+
.with(csrf()))
|
| 347 |
+
.andExpect(status().isNoContent());
|
| 348 |
+
}
|
| 349 |
+
|
| 350 |
+
@Test
|
| 351 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 352 |
+
void deleteAsset_AssetNotFound_ShouldReturnNotFound() throws Exception {
|
| 353 |
+
// Given
|
| 354 |
+
when(catalogService.deleteAsset(999L)).thenReturn(false);
|
| 355 |
+
|
| 356 |
+
// When & Then
|
| 357 |
+
mockMvc.perform(delete("/api/v1/catalog/assets/{assetId}", 999)
|
| 358 |
+
.with(csrf()))
|
| 359 |
+
.andExpect(status().isNotFound());
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
@Test
|
| 363 |
+
@WithMockUser(authorities = "ROLE_USER")
|
| 364 |
+
void deleteAsset_AsUser_ShouldBeForbidden() throws Exception {
|
| 365 |
+
// When & Then
|
| 366 |
+
mockMvc.perform(delete("/api/v1/catalog/assets/{assetId}", 1)
|
| 367 |
+
.with(csrf()))
|
| 368 |
+
.andExpect(status().isForbidden());
|
| 369 |
+
}
|
| 370 |
+
|
| 371 |
+
@Test
|
| 372 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 373 |
+
void createAsset_InvalidRequest_ShouldReturnBadRequest() throws Exception {
|
| 374 |
+
// Given
|
| 375 |
+
AssetCreateRequest request = new AssetCreateRequest();
|
| 376 |
+
// Missing required fields
|
| 377 |
+
|
| 378 |
+
// When & Then
|
| 379 |
+
mockMvc.perform(post("/api/v1/catalog/assets")
|
| 380 |
+
.with(csrf())
|
| 381 |
+
.contentType(MediaType.APPLICATION_JSON)
|
| 382 |
+
.content(objectMapper.writeValueAsString(request)))
|
| 383 |
+
.andExpect(status().isBadRequest());
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
@Test
|
| 387 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 388 |
+
void searchAssets_InvalidSearchRequest_ShouldReturnBadRequest() throws Exception {
|
| 389 |
+
// Given
|
| 390 |
+
AssetSearchRequest searchRequest = new AssetSearchRequest();
|
| 391 |
+
searchRequest.setQuery(""); // Empty query
|
| 392 |
+
|
| 393 |
+
// When & Then
|
| 394 |
+
mockMvc.perform(post("/api/v1/catalog/assets/search")
|
| 395 |
+
.with(csrf())
|
| 396 |
+
.contentType(MediaType.APPLICATION_JSON)
|
| 397 |
+
.content(objectMapper.writeValueAsString(searchRequest)))
|
| 398 |
+
.andExpect(status().isBadRequest());
|
| 399 |
+
}
|
| 400 |
+
|
| 401 |
+
@Test
|
| 402 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 403 |
+
void assignLabelToAsset_InvalidLabelRequest_ShouldReturnBadRequest() throws Exception {
|
| 404 |
+
// Given
|
| 405 |
+
LabelAssignmentRequest request = new LabelAssignmentRequest();
|
| 406 |
+
// Missing required fields
|
| 407 |
+
|
| 408 |
+
// When & Then
|
| 409 |
+
mockMvc.perform(post("/api/v1/catalog/assets/{assetId}/labels", 1)
|
| 410 |
+
.with(csrf())
|
| 411 |
+
.contentType(MediaType.APPLICATION_JSON)
|
| 412 |
+
.content(objectMapper.writeValueAsString(request)))
|
| 413 |
+
.andExpect(status().isBadRequest());
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
@Test
|
| 417 |
+
@WithMockUser(authorities = "ROLE_DATA_ENGINEER")
|
| 418 |
+
void updateBusinessMetadata_InvalidRequest_ShouldReturnBadRequest() throws Exception {
|
| 419 |
+
// Given
|
| 420 |
+
BusinessMetadataRequest request = new BusinessMetadataRequest();
|
| 421 |
+
// Missing required fields
|
| 422 |
+
|
| 423 |
+
// When & Then
|
| 424 |
+
mockMvc.perform(put("/api/v1/catalog/assets/{assetId}/business-metadata", 1)
|
| 425 |
+
.with(csrf())
|
| 426 |
+
.contentType(MediaType.APPLICATION_JSON)
|
| 427 |
+
.content(objectMapper.writeValueAsString(request)))
|
| 428 |
+
.andExpect(status().isBadRequest());
|
| 429 |
+
}
|
| 430 |
+
}
|
src/test/resources/application-test.yml
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Test configuration for da-catalog integration tests
|
| 2 |
+
spring:
|
| 3 |
+
application:
|
| 4 |
+
name: da-catalog-test
|
| 5 |
+
|
| 6 |
+
config:
|
| 7 |
+
import: classpath:config/catalog-config.yml
|
| 8 |
+
|
| 9 |
+
# Database configuration (using H2 for tests)
|
| 10 |
+
datasource:
|
| 11 |
+
type: com.zaxxer.hikari.HikariDataSource
|
| 12 |
+
driver-class-name: org.h2.Driver
|
| 13 |
+
url: jdbc:h2:mem:testdb;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
| 14 |
+
username: sa
|
| 15 |
+
password: password
|
| 16 |
+
hikari:
|
| 17 |
+
auto-commit: false
|
| 18 |
+
maximum-pool-size: 10
|
| 19 |
+
minimum-idle: 2
|
| 20 |
+
connection-timeout: 30000
|
| 21 |
+
idle-timeout: 600000
|
| 22 |
+
max-lifetime: 1800000
|
| 23 |
+
|
| 24 |
+
jpa:
|
| 25 |
+
database-platform: org.hibernate.dialect.H2Dialect
|
| 26 |
+
hibernate:
|
| 27 |
+
ddl-auto: create-drop
|
| 28 |
+
show-sql: false
|
| 29 |
+
properties:
|
| 30 |
+
hibernate:
|
| 31 |
+
format_sql: false
|
| 32 |
+
jdbc:
|
| 33 |
+
lob:
|
| 34 |
+
non_contextual_creation: true
|
| 35 |
+
dialect: org.hibernate.dialect.H2Dialect
|
| 36 |
+
|
| 37 |
+
h2:
|
| 38 |
+
console:
|
| 39 |
+
enabled: false
|
| 40 |
+
|
| 41 |
+
# Disable Liquibase for tests
|
| 42 |
+
liquibase:
|
| 43 |
+
enabled: false
|
| 44 |
+
|
| 45 |
+
# Disable Docker for tests
|
| 46 |
+
docker:
|
| 47 |
+
compose:
|
| 48 |
+
enabled: false
|
| 49 |
+
|
| 50 |
+
# Task execution configuration for tests
|
| 51 |
+
task:
|
| 52 |
+
execution:
|
| 53 |
+
pool:
|
| 54 |
+
core-size: 2
|
| 55 |
+
max-size: 4
|
| 56 |
+
queue-capacity: 100
|
| 57 |
+
thread-name-prefix: test-task-
|
| 58 |
+
|
| 59 |
+
# Cache configuration for tests
|
| 60 |
+
cache:
|
| 61 |
+
type: simple
|
| 62 |
+
|
| 63 |
+
# Disable Docker for tests
|
| 64 |
+
testcontainers:
|
| 65 |
+
enabled: false
|
| 66 |
+
|
| 67 |
+
# Cloud provider configuration for tests
|
| 68 |
+
aws:
|
| 69 |
+
enabled: false
|
| 70 |
+
access-key: test-access-key
|
| 71 |
+
secret-key: test-secret-key
|
| 72 |
+
region: us-west-2
|
| 73 |
+
s3:
|
| 74 |
+
bucket-name: test-bucket
|
| 75 |
+
dynamodb:
|
| 76 |
+
table-name: test-table
|
| 77 |
+
|
| 78 |
+
azure:
|
| 79 |
+
enabled: false
|
| 80 |
+
client-id: test-dummy-client-id
|
| 81 |
+
client-secret: test-dummy-client-secret
|
| 82 |
+
tenant-id: test-dummy-tenant-id
|
| 83 |
+
subscription-id: "test-dummy-subscription-id"
|
| 84 |
+
resource-group-name: "test-dummy-rg"
|
| 85 |
+
region: "test-dummy-region"
|
| 86 |
+
storage:
|
| 87 |
+
account-name: "test-dummy-storage-account"
|
| 88 |
+
container-name: "test-dummy-storage-container"
|
| 89 |
+
|
| 90 |
+
gcp:
|
| 91 |
+
project:
|
| 92 |
+
id: test-project
|
| 93 |
+
storage:
|
| 94 |
+
bucket-name: test-bucket
|
| 95 |
+
bigquery:
|
| 96 |
+
dataset: test_dataset
|
| 97 |
+
project-id: test-project
|
| 98 |
+
|
| 99 |
+
# Application settings
|
| 100 |
+
application:
|
| 101 |
+
kafka:
|
| 102 |
+
enabled: true
|
| 103 |
+
bootstrap-servers: localhost:9092
|
| 104 |
+
producer:
|
| 105 |
+
key-serializer: org.apache.kafka.common.serialization.StringSerializer
|
| 106 |
+
value-serializer: org.apache.kafka.common.serialization.StringSerializer
|
| 107 |
+
consumer:
|
| 108 |
+
group-id: da-catalog-test
|
| 109 |
+
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
| 110 |
+
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
| 111 |
+
auto-offset-reset: earliest
|
| 112 |
+
scheduler:
|
| 113 |
+
enabled: false
|
| 114 |
+
metrics:
|
| 115 |
+
enabled: false
|
| 116 |
+
|
| 117 |
+
# Catalog-specific configuration
|
| 118 |
+
catalog:
|
| 119 |
+
search:
|
| 120 |
+
max-results: 100
|
| 121 |
+
default-page-size: 20
|
| 122 |
+
labeling:
|
| 123 |
+
auto-labeling-enabled: true
|
| 124 |
+
confidence-threshold: 0.8
|
| 125 |
+
lineage:
|
| 126 |
+
max-depth: 5
|
| 127 |
+
enabled: true
|
| 128 |
+
|
| 129 |
+
# JHipster configuration
|
| 130 |
+
jhipster:
|
| 131 |
+
clientApp:
|
| 132 |
+
name: "daCatalog"
|
| 133 |
+
security:
|
| 134 |
+
authentication:
|
| 135 |
+
jwt:
|
| 136 |
+
secret: test-jwt-secret
|
| 137 |
+
base64-secret: test-secret-which-needs-to-be-at-least-512-bits-long-need-to-be-at-least-512-bits-long-really-long-ok
|
| 138 |
+
token-validity-in-seconds: 86400
|
| 139 |
+
audit-events:
|
| 140 |
+
retention-period: 30
|
| 141 |
+
logging:
|
| 142 |
+
use-json-format: false
|
| 143 |
+
cors:
|
| 144 |
+
allowed-origins: "*"
|
| 145 |
+
allowed-methods: "*"
|
| 146 |
+
|
| 147 |
+
# Test data configuration
|
| 148 |
+
test:
|
| 149 |
+
data:
|
| 150 |
+
enabled: true
|
| 151 |
+
users:
|
| 152 |
+
- email: "admin@test.com"
|
| 153 |
+
roles: ["ADMIN", "USER"]
|
| 154 |
+
department: "IT"
|
| 155 |
+
- email: "cdo@test.com"
|
| 156 |
+
roles: ["CDO", "USER"]
|
| 157 |
+
department: "Executive"
|
| 158 |
+
- email: "director@test.com"
|
| 159 |
+
roles: ["DIRECTOR", "USER"]
|
| 160 |
+
department: "Management"
|
| 161 |
+
- email: "engineer@test.com"
|
| 162 |
+
roles: ["DATA_ENGINEER", "USER"]
|
| 163 |
+
department: "Engineering"
|
| 164 |
+
assets:
|
| 165 |
+
- uri: "gs://test-bucket/data.csv"
|
| 166 |
+
cloud-provider: "GCP"
|
| 167 |
+
service-type: "STORAGE"
|
| 168 |
+
name: "Customer Data CSV"
|
| 169 |
+
- uri: "s3://test-bucket/analytics.json"
|
| 170 |
+
cloud-provider: "AWS"
|
| 171 |
+
service-type: "STORAGE"
|
| 172 |
+
name: "Analytics Data JSON"
|
| 173 |
+
- uri: "https://test.blob.core.windows.net/models/"
|
| 174 |
+
cloud-provider: "AZURE"
|
| 175 |
+
service-type: "STORAGE"
|
| 176 |
+
name: "ML Models Directory"
|
| 177 |
+
labels:
|
| 178 |
+
- name: "PII"
|
| 179 |
+
category: "COMPLIANCE"
|
| 180 |
+
description: "Personally Identifiable Information"
|
| 181 |
+
- name: "PHI"
|
| 182 |
+
category: "COMPLIANCE"
|
| 183 |
+
description: "Protected Health Information"
|
| 184 |
+
- name: "FINANCIAL"
|
| 185 |
+
category: "COMPLIANCE"
|
| 186 |
+
description: "Financial Data"
|
| 187 |
+
- name: "PUBLIC"
|
| 188 |
+
category: "CLASSIFICATION"
|
| 189 |
+
description: "Public Data"
|
| 190 |
+
- name: "INTERNAL"
|
| 191 |
+
category: "CLASSIFICATION"
|
| 192 |
+
description: "Internal Use Only"
|
| 193 |
+
- name: "CONFIDENTIAL"
|
| 194 |
+
category: "CLASSIFICATION"
|
| 195 |
+
description: "Confidential Data"
|
| 196 |
+
|
| 197 |
+
# Performance test configuration
|
| 198 |
+
performance:
|
| 199 |
+
test:
|
| 200 |
+
enabled: false
|
| 201 |
+
concurrent-users: 10
|
| 202 |
+
ramp-up-time: 30
|
| 203 |
+
test-duration: 300
|
| 204 |
+
target-response-time: 2000
|