Spaces:
Build error
Build error
Ajay Yadav
commited on
Commit
·
d6afd6c
1
Parent(s):
3fc05e6
Initial deployment of da-admin-service-dev
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- Dockerfile +27 -0
- README.md +34 -6
- build.gradle.kts +70 -0
- src/main/docker/Dockerfile +23 -0
- src/main/docker/Dockerfile.alpine-jlink +43 -0
- src/main/docker/Dockerfile.layered +34 -0
- src/main/docker/Dockerfile.native +20 -0
- src/main/java/com/dalab/adminservice/DaAdminServiceApplication.java +15 -0
- src/main/java/com/dalab/adminservice/client/IAutoarchivalTaskApiClient.java +22 -0
- src/main/java/com/dalab/adminservice/client/IAutocomplianceJobApiClient.java +19 -0
- src/main/java/com/dalab/adminservice/client/IAutodeleteTaskApiClient.java +22 -0
- src/main/java/com/dalab/adminservice/client/IAutolabelJobApiClient.java +22 -0
- src/main/java/com/dalab/adminservice/client/IDiscoveryJobApiClient.java +23 -0
- src/main/java/com/dalab/adminservice/config/KeycloakAdminClientConfig.java +58 -0
- src/main/java/com/dalab/adminservice/config/SpringSecurityAuditorAware.java +1 -0
- src/main/java/com/dalab/adminservice/controller/CloudConnectionController.java +97 -0
- src/main/java/com/dalab/adminservice/controller/JobStatusController.java +35 -0
- src/main/java/com/dalab/adminservice/controller/RoleController.java +33 -0
- src/main/java/com/dalab/adminservice/controller/ServiceConfigController.java +76 -0
- src/main/java/com/dalab/adminservice/controller/UserController.java +126 -0
- src/main/java/com/dalab/adminservice/dto/AggregatedJobStatusDTO.java +22 -0
- src/main/java/com/dalab/adminservice/dto/CloudConnectionDTO.java +53 -0
- src/main/java/com/dalab/adminservice/dto/CloudConnectionTestResultDTO.java +24 -0
- src/main/java/com/dalab/adminservice/dto/JobStatusDTO.java +25 -0
- src/main/java/com/dalab/adminservice/dto/RoleDTO.java +21 -0
- src/main/java/com/dalab/adminservice/dto/ServiceConfigDTO.java +29 -0
- src/main/java/com/dalab/adminservice/dto/UserDTO.java +38 -0
- src/main/java/com/dalab/adminservice/exception/ConflictException.java +15 -0
- src/main/java/com/dalab/adminservice/exception/KeycloakAdminException.java +8 -0
- src/main/java/com/dalab/adminservice/exception/NotFoundException.java +11 -0
- src/main/java/com/dalab/adminservice/mapper/CloudConnectionMapper.java +53 -0
- src/main/java/com/dalab/adminservice/mapper/RoleMapper.java +19 -0
- src/main/java/com/dalab/adminservice/mapper/ServiceConfigMapper.java +29 -0
- src/main/java/com/dalab/adminservice/mapper/UserMapper.java +41 -0
- src/main/java/com/dalab/adminservice/model/AbstractAuditableEntity.java +41 -0
- src/main/java/com/dalab/adminservice/model/CloudConnectionEntity.java +52 -0
- src/main/java/com/dalab/adminservice/model/ServiceConfigEntity.java +42 -0
- src/main/java/com/dalab/adminservice/model/enums/CloudProviderType.java +11 -0
- src/main/java/com/dalab/adminservice/repository/CloudConnectionRepository.java +12 -0
- src/main/java/com/dalab/adminservice/repository/ServiceConfigRepository.java +23 -0
- src/main/java/com/dalab/adminservice/service/ICloudConnectionService.java +18 -0
- src/main/java/com/dalab/adminservice/service/IEncryptionService.java +22 -0
- src/main/java/com/dalab/adminservice/service/IJobStatusService.java +16 -0
- src/main/java/com/dalab/adminservice/service/IRoleService.java +18 -0
- src/main/java/com/dalab/adminservice/service/IServiceConfigService.java +14 -0
- src/main/java/com/dalab/adminservice/service/IUserService.java +19 -0
- src/main/java/com/dalab/adminservice/service/impl/BasicEncryptionServiceImpl.java +59 -0
- src/main/java/com/dalab/adminservice/service/impl/CloudConnectionServiceImpl.java +166 -0
- src/main/java/com/dalab/adminservice/service/impl/JobStatusServiceImpl.java +108 -0
- src/main/java/com/dalab/adminservice/service/impl/RoleServiceImpl.java +44 -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-admin-service.jar"]
|
README.md
CHANGED
@@ -1,10 +1,38 @@
|
|
1 |
---
|
2 |
-
title:
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: docker
|
7 |
-
|
8 |
---
|
9 |
|
10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
+
title: da-admin-service (dev)
|
3 |
+
emoji: 🔧
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: green
|
6 |
sdk: docker
|
7 |
+
app_port: 8080
|
8 |
---
|
9 |
|
10 |
+
# da-admin-service - dev Environment
|
11 |
+
|
12 |
+
This is the da-admin-service 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-admin-service-dev/swagger-ui.html
|
25 |
+
- Health Check: https://huggingface.co/spaces/dalabsai/da-admin-service-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:48
|
build.gradle.kts
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
plugins {
|
2 |
+
java
|
3 |
+
id("org.springframework.boot") version "3.2.5"
|
4 |
+
id("io.spring.dependency-management") version "1.1.4"
|
5 |
+
}
|
6 |
+
|
7 |
+
group = "com.dalab"
|
8 |
+
version = "0.0.1-SNAPSHOT"
|
9 |
+
|
10 |
+
java {
|
11 |
+
sourceCompatibility = JavaVersion.VERSION_21
|
12 |
+
}
|
13 |
+
|
14 |
+
configurations {
|
15 |
+
compileOnly {
|
16 |
+
extendsFrom(configurations.annotationProcessor.get())
|
17 |
+
}
|
18 |
+
}
|
19 |
+
|
20 |
+
repositories {
|
21 |
+
mavenCentral()
|
22 |
+
}
|
23 |
+
|
24 |
+
dependencies {
|
25 |
+
// da-protos common entities and utilities
|
26 |
+
implementation(project(":da-protos"))
|
27 |
+
|
28 |
+
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
29 |
+
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
30 |
+
implementation("org.springframework.boot:spring-boot-starter-security")
|
31 |
+
implementation("org.springframework.boot:spring-boot-starter-validation")
|
32 |
+
implementation("org.springframework.boot:spring-boot-starter-web")
|
33 |
+
implementation("org.springframework.kafka:spring-kafka")
|
34 |
+
implementation("org.springframework.cloud:spring-cloud-starter-openfeign:4.1.1") // For Feign clients
|
35 |
+
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0") // OpenAPI
|
36 |
+
|
37 |
+
// MapStruct for DTO mapping
|
38 |
+
implementation("org.mapstruct:mapstruct:1.5.5.Final")
|
39 |
+
annotationProcessor("org.mapstruct:mapstruct-processor:1.5.5.Final")
|
40 |
+
|
41 |
+
// Lombok for reduced boilerplate
|
42 |
+
compileOnly("org.projectlombok:lombok")
|
43 |
+
annotationProcessor("org.projectlombok:lombok")
|
44 |
+
annotationProcessor("org.projectlombok:lombok-mapstruct-binding:0.2.0") // If using Lombok with MapStruct
|
45 |
+
|
46 |
+
// Hypersistence Utils for JSONB and other advanced types
|
47 |
+
implementation("io.hypersistence:hypersistence-utils-hibernate-62:3.7.0") // For Hibernate 6.2.x used by Spring Boot 3.x
|
48 |
+
|
49 |
+
runtimeOnly("org.postgresql:postgresql")
|
50 |
+
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
51 |
+
testImplementation("org.springframework.kafka:spring-kafka-test")
|
52 |
+
testImplementation("org.springframework.security:spring-security-test")
|
53 |
+
|
54 |
+
// Keycloak Admin Client (if direct Keycloak interaction is needed for users/roles)
|
55 |
+
implementation("org.keycloak:keycloak-admin-client:24.0.4")
|
56 |
+
}
|
57 |
+
|
58 |
+
dependencyManagement {
|
59 |
+
imports {
|
60 |
+
mavenBom("org.springframework.cloud:spring-cloud-dependencies:2023.0.1")
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
tasks.withType<Test> {
|
65 |
+
useJUnitPlatform()
|
66 |
+
}
|
67 |
+
|
68 |
+
tasks.named<org.springframework.boot.gradle.tasks.bundling.BootJar>("bootJar") {
|
69 |
+
archiveFileName.set("${project.name}.jar")
|
70 |
+
}
|
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-admin-service.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.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/adminservice/DaAdminServiceApplication.java
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice;
|
2 |
+
|
3 |
+
import org.springframework.boot.SpringApplication;
|
4 |
+
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
5 |
+
import org.springframework.cloud.openfeign.EnableFeignClients;
|
6 |
+
|
7 |
+
@SpringBootApplication
|
8 |
+
@EnableFeignClients // Important for enabling Feign clients
|
9 |
+
public class DaAdminServiceApplication {
|
10 |
+
|
11 |
+
public static void main(String[] args) {
|
12 |
+
SpringApplication.run(DaAdminServiceApplication.class, args);
|
13 |
+
}
|
14 |
+
|
15 |
+
}
|
src/main/java/com/dalab/adminservice/client/IAutoarchivalTaskApiClient.java
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.client;
|
2 |
+
|
3 |
+
import java.util.List;
|
4 |
+
|
5 |
+
import org.springframework.cloud.openfeign.FeignClient;
|
6 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
7 |
+
|
8 |
+
import com.dalab.adminservice.dto.JobStatusDTO;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Feign client for Autoarchival Task API
|
12 |
+
*/
|
13 |
+
@FeignClient(name = "da-autoarchival", url = "${dalab.services.autoarchival.url:http://localhost:8086}")
|
14 |
+
public interface IAutoarchivalTaskApiClient {
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Get all archival tasks status
|
18 |
+
* @return List of job status DTOs
|
19 |
+
*/
|
20 |
+
@GetMapping("/api/v1/autoarchival/archival/tasks")
|
21 |
+
List<JobStatusDTO> getArchivalTasks();
|
22 |
+
}
|
src/main/java/com/dalab/adminservice/client/IAutocomplianceJobApiClient.java
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.client;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.JobStatusDTO;
|
4 |
+
import org.springframework.cloud.openfeign.FeignClient;
|
5 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
6 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
7 |
+
|
8 |
+
import java.util.List;
|
9 |
+
|
10 |
+
@FeignClient(name = "autocompliance-service", url = "${feign.client.config.autocompliance-service.url:/api/v1/compliance}") // Placeholder URL
|
11 |
+
public interface IAutocomplianceJobApiClient {
|
12 |
+
|
13 |
+
// Assuming an endpoint like /api/v1/compliance/reports/jobs exists or will be created
|
14 |
+
@GetMapping("/reports/jobs")
|
15 |
+
List<JobStatusDTO> getComplianceReportJobs();
|
16 |
+
|
17 |
+
@GetMapping("/reports/jobs/{jobId}")
|
18 |
+
JobStatusDTO getComplianceReportJobById(@PathVariable("jobId") String jobId);
|
19 |
+
}
|
src/main/java/com/dalab/adminservice/client/IAutodeleteTaskApiClient.java
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.client;
|
2 |
+
|
3 |
+
import java.util.List;
|
4 |
+
|
5 |
+
import org.springframework.cloud.openfeign.FeignClient;
|
6 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
7 |
+
|
8 |
+
import com.dalab.adminservice.dto.JobStatusDTO;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Feign client for Autodelete Task API
|
12 |
+
*/
|
13 |
+
@FeignClient(name = "da-autodelete", url = "${dalab.services.autodelete.url:http://localhost:8087}")
|
14 |
+
public interface IAutodeleteTaskApiClient {
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Get all deletion tasks status
|
18 |
+
* @return List of job status DTOs
|
19 |
+
*/
|
20 |
+
@GetMapping("/api/v1/autodelete/deletion/tasks")
|
21 |
+
List<JobStatusDTO> getDeletionTasks();
|
22 |
+
}
|
src/main/java/com/dalab/adminservice/client/IAutolabelJobApiClient.java
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.client;
|
2 |
+
|
3 |
+
import java.util.List;
|
4 |
+
|
5 |
+
import org.springframework.cloud.openfeign.FeignClient;
|
6 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
7 |
+
|
8 |
+
import com.dalab.adminservice.dto.JobStatusDTO;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Feign client for Autolabel Job API
|
12 |
+
*/
|
13 |
+
@FeignClient(name = "da-autolabel", url = "${dalab.services.autolabel.url:http://localhost:8085}")
|
14 |
+
public interface IAutolabelJobApiClient {
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Get all labeling jobs status
|
18 |
+
* @return List of job status DTOs
|
19 |
+
*/
|
20 |
+
@GetMapping("/api/v1/autolabel/labeling/jobs")
|
21 |
+
List<JobStatusDTO> getAutolabelJobs();
|
22 |
+
}
|
src/main/java/com/dalab/adminservice/client/IDiscoveryJobApiClient.java
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.client;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.JobStatusDTO;
|
4 |
+
import org.springframework.cloud.openfeign.FeignClient;
|
5 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
6 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
7 |
+
|
8 |
+
import java.util.List;
|
9 |
+
|
10 |
+
// The name "discovery-service" should match a Feign client configuration in application.properties
|
11 |
+
// e.g., feign.client.config.discovery-service.url=http://da-discovery-service-host:port/api/v1/discovery
|
12 |
+
// The path here is relative to the URL configured for the Feign client.
|
13 |
+
@FeignClient(name = "discovery-service", url = "${feign.client.config.discovery-service.url:/api/v1/discovery}") // Placeholder URL
|
14 |
+
public interface IDiscoveryJobApiClient {
|
15 |
+
|
16 |
+
// Assuming da-discovery exposes an endpoint like /api/v1/discovery/jobs that returns a list of its jobs
|
17 |
+
// and that its Job DTO is compatible or can be mapped to our common JobStatusDTO
|
18 |
+
@GetMapping("/jobs") // This path is relative to the Feign client's configured URL
|
19 |
+
List<JobStatusDTO> getDiscoveryJobs();
|
20 |
+
|
21 |
+
@GetMapping("/jobs/{jobId}")
|
22 |
+
JobStatusDTO getDiscoveryJobById(@PathVariable("jobId") String jobId);
|
23 |
+
}
|
src/main/java/com/dalab/adminservice/config/KeycloakAdminClientConfig.java
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.config;
|
2 |
+
|
3 |
+
import org.keycloak.admin.client.Keycloak;
|
4 |
+
import org.keycloak.admin.client.KeycloakBuilder;
|
5 |
+
import org.springframework.beans.factory.annotation.Value;
|
6 |
+
import org.springframework.context.annotation.Bean;
|
7 |
+
import org.springframework.context.annotation.Configuration;
|
8 |
+
|
9 |
+
@Configuration
|
10 |
+
public class KeycloakAdminClientConfig {
|
11 |
+
|
12 |
+
@Value("${keycloak.auth-server-url}")
|
13 |
+
private String serverUrl;
|
14 |
+
|
15 |
+
@Value("${keycloak.realm}")
|
16 |
+
private String realm;
|
17 |
+
|
18 |
+
@Value("${keycloak.client-id}")
|
19 |
+
private String clientId;
|
20 |
+
|
21 |
+
@Value("${keycloak.client-secret:#{null}}") // Default to null if not provided
|
22 |
+
private String clientSecret;
|
23 |
+
|
24 |
+
// Optional: If using password credentials grant type for admin client
|
25 |
+
@Value("${keycloak.admin.username:#{null}}")
|
26 |
+
private String adminUsername;
|
27 |
+
|
28 |
+
@Value("${keycloak.admin.password:#{null}}")
|
29 |
+
private String adminPassword;
|
30 |
+
|
31 |
+
@Bean
|
32 |
+
public Keycloak keycloakAdminClient() {
|
33 |
+
KeycloakBuilder builder = KeycloakBuilder.builder()
|
34 |
+
.serverUrl(serverUrl)
|
35 |
+
.realm(realm)
|
36 |
+
.clientId(clientId);
|
37 |
+
// ResteasyClient can be configured here if needed, e.g., for connection pooling
|
38 |
+
// .resteasyClient(ResteasyClientBuilder.newBuilder().build()); // Basic Resteasy client
|
39 |
+
|
40 |
+
if (clientSecret != null && !clientSecret.isEmpty()) {
|
41 |
+
builder.clientSecret(clientSecret);
|
42 |
+
// Ensure the client (e.g., admin-cli or your dedicated client) has "Service Accounts Enabled"
|
43 |
+
// and appropriate "Service Account Roles" (e.g., realm-management -> manage-users, view-users)
|
44 |
+
builder.grantType("client_credentials");
|
45 |
+
} else if (adminUsername != null && !adminUsername.isEmpty() && adminPassword != null && !adminPassword.isEmpty()){
|
46 |
+
// This is typically for direct admin user login, ensure this user has necessary permissions.
|
47 |
+
// The 'admin-cli' client itself might not allow password grant for other users.
|
48 |
+
// Prefer service account with client_credentials for backend services.
|
49 |
+
builder.username(adminUsername);
|
50 |
+
builder.password(adminPassword);
|
51 |
+
builder.grantType("password");
|
52 |
+
} else {
|
53 |
+
throw new IllegalStateException("Keycloak admin client not properly configured: Missing clientSecret or admin username/password.");
|
54 |
+
}
|
55 |
+
|
56 |
+
return builder.build();
|
57 |
+
}
|
58 |
+
}
|
src/main/java/com/dalab/adminservice/config/SpringSecurityAuditorAware.java
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
|
src/main/java/com/dalab/adminservice/controller/CloudConnectionController.java
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.controller;
|
2 |
+
|
3 |
+
import java.util.List;
|
4 |
+
import java.util.Optional;
|
5 |
+
import java.util.UUID;
|
6 |
+
|
7 |
+
import org.springframework.http.HttpStatus;
|
8 |
+
import org.springframework.http.ResponseEntity;
|
9 |
+
import org.springframework.security.access.prepost.PreAuthorize;
|
10 |
+
import org.springframework.web.bind.annotation.DeleteMapping;
|
11 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
12 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
13 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
14 |
+
import org.springframework.web.bind.annotation.PutMapping;
|
15 |
+
import org.springframework.web.bind.annotation.RequestBody;
|
16 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
17 |
+
import org.springframework.web.bind.annotation.RestController;
|
18 |
+
|
19 |
+
import com.dalab.adminservice.dto.CloudConnectionDTO;
|
20 |
+
import com.dalab.adminservice.dto.CloudConnectionTestResultDTO;
|
21 |
+
import com.dalab.adminservice.service.ICloudConnectionService;
|
22 |
+
|
23 |
+
import io.swagger.v3.oas.annotations.Operation;
|
24 |
+
import io.swagger.v3.oas.annotations.tags.Tag;
|
25 |
+
import jakarta.validation.Valid;
|
26 |
+
import lombok.RequiredArgsConstructor;
|
27 |
+
import lombok.extern.slf4j.Slf4j;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* REST controller for managing cloud connections
|
31 |
+
*/
|
32 |
+
@RestController
|
33 |
+
@RequestMapping("/api/v1/admin/cloud-connections")
|
34 |
+
@RequiredArgsConstructor
|
35 |
+
@Slf4j
|
36 |
+
@Tag(name = "Cloud Connection Management", description = "APIs for managing cloud connections")
|
37 |
+
public class CloudConnectionController {
|
38 |
+
|
39 |
+
private final ICloudConnectionService cloudConnectionService;
|
40 |
+
|
41 |
+
@GetMapping
|
42 |
+
@PreAuthorize("hasRole('ADMIN')")
|
43 |
+
@Operation(summary = "Get all cloud connections")
|
44 |
+
public ResponseEntity<List<CloudConnectionDTO>> getAllCloudConnections() {
|
45 |
+
log.debug("Getting all cloud connections");
|
46 |
+
List<CloudConnectionDTO> connections = cloudConnectionService.getAllCloudConnections();
|
47 |
+
return ResponseEntity.ok(connections);
|
48 |
+
}
|
49 |
+
|
50 |
+
@GetMapping("/{connectionId}")
|
51 |
+
@PreAuthorize("hasRole('ADMIN')")
|
52 |
+
@Operation(summary = "Get cloud connection by ID")
|
53 |
+
public ResponseEntity<CloudConnectionDTO> getCloudConnectionById(@PathVariable String connectionId) {
|
54 |
+
log.debug("Getting cloud connection with id: {}", connectionId);
|
55 |
+
Optional<CloudConnectionDTO> connection = cloudConnectionService.getCloudConnectionById(connectionId);
|
56 |
+
return connection.map(ResponseEntity::ok)
|
57 |
+
.orElse(ResponseEntity.notFound().build());
|
58 |
+
}
|
59 |
+
|
60 |
+
@PostMapping
|
61 |
+
@PreAuthorize("hasRole('ADMIN')")
|
62 |
+
@Operation(summary = "Create new cloud connection")
|
63 |
+
public ResponseEntity<CloudConnectionDTO> createCloudConnection(@Valid @RequestBody CloudConnectionDTO cloudConnectionDTO) {
|
64 |
+
log.debug("Creating new cloud connection: {}", cloudConnectionDTO.getName());
|
65 |
+
CloudConnectionDTO created = cloudConnectionService.createCloudConnection(cloudConnectionDTO);
|
66 |
+
return ResponseEntity.status(HttpStatus.CREATED).body(created);
|
67 |
+
}
|
68 |
+
|
69 |
+
@PutMapping("/{connectionId}")
|
70 |
+
@PreAuthorize("hasRole('ADMIN')")
|
71 |
+
@Operation(summary = "Update cloud connection")
|
72 |
+
public ResponseEntity<CloudConnectionDTO> updateCloudConnection(
|
73 |
+
@PathVariable String connectionId,
|
74 |
+
@Valid @RequestBody CloudConnectionDTO cloudConnectionDTO) {
|
75 |
+
log.debug("Updating cloud connection with id: {}", connectionId);
|
76 |
+
CloudConnectionDTO updated = cloudConnectionService.updateCloudConnection(connectionId, cloudConnectionDTO);
|
77 |
+
return ResponseEntity.ok(updated);
|
78 |
+
}
|
79 |
+
|
80 |
+
@DeleteMapping("/{connectionId}")
|
81 |
+
@PreAuthorize("hasRole('ADMIN')")
|
82 |
+
@Operation(summary = "Delete cloud connection")
|
83 |
+
public ResponseEntity<Void> deleteCloudConnection(@PathVariable String connectionId) {
|
84 |
+
log.debug("Deleting cloud connection with id: {}", connectionId);
|
85 |
+
cloudConnectionService.deleteCloudConnection(connectionId);
|
86 |
+
return ResponseEntity.noContent().build();
|
87 |
+
}
|
88 |
+
|
89 |
+
@PostMapping("/{connectionId}/test")
|
90 |
+
@PreAuthorize("hasRole('ADMIN')")
|
91 |
+
@Operation(summary = "Test cloud connection")
|
92 |
+
public ResponseEntity<CloudConnectionTestResultDTO> testCloudConnection(@PathVariable String connectionId) {
|
93 |
+
log.debug("Testing cloud connection with id: {}", connectionId);
|
94 |
+
CloudConnectionTestResultDTO result = cloudConnectionService.testCloudConnection(connectionId);
|
95 |
+
return ResponseEntity.ok(result);
|
96 |
+
}
|
97 |
+
}
|
src/main/java/com/dalab/adminservice/controller/JobStatusController.java
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.controller;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.AggregatedJobStatusDTO;
|
4 |
+
import com.dalab.adminservice.service.IJobStatusService;
|
5 |
+
import io.swagger.v3.oas.annotations.Operation;
|
6 |
+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
7 |
+
import io.swagger.v3.oas.annotations.tags.Tag;
|
8 |
+
import lombok.RequiredArgsConstructor;
|
9 |
+
import org.springframework.http.ResponseEntity;
|
10 |
+
import org.springframework.security.access.prepost.PreAuthorize;
|
11 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
12 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
13 |
+
import org.springframework.web.bind.annotation.RestController;
|
14 |
+
|
15 |
+
@RestController
|
16 |
+
@RequestMapping("/api/v1/admin/job-statuses")
|
17 |
+
@RequiredArgsConstructor
|
18 |
+
@Tag(name = "Job Status Management", description = "APIs for viewing aggregated job statuses across services.")
|
19 |
+
public class JobStatusController {
|
20 |
+
|
21 |
+
private final IJobStatusService jobStatusService;
|
22 |
+
|
23 |
+
@GetMapping
|
24 |
+
@PreAuthorize("hasAnyRole('ADMIN', 'VIEWER')") // Or more specific roles as needed
|
25 |
+
@Operation(summary = "Get Aggregated Job Statuses",
|
26 |
+
description = "Retrieves a list of all jobs from various microservices and their statuses.",
|
27 |
+
responses = {
|
28 |
+
@ApiResponse(responseCode = "200", description = "Successfully retrieved job statuses"),
|
29 |
+
@ApiResponse(responseCode = "500", description = "Internal server error while fetching statuses")
|
30 |
+
})
|
31 |
+
public ResponseEntity<AggregatedJobStatusDTO> getAggregatedJobStatuses() {
|
32 |
+
AggregatedJobStatusDTO aggregatedStatus = jobStatusService.getAggregatedJobStatuses();
|
33 |
+
return ResponseEntity.ok(aggregatedStatus);
|
34 |
+
}
|
35 |
+
}
|
src/main/java/com/dalab/adminservice/controller/RoleController.java
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.controller;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.RoleDTO;
|
4 |
+
import com.dalab.adminservice.service.IRoleService;
|
5 |
+
import io.swagger.v3.oas.annotations.Operation;
|
6 |
+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
7 |
+
import io.swagger.v3.oas.annotations.tags.Tag;
|
8 |
+
import lombok.RequiredArgsConstructor;
|
9 |
+
import org.springframework.http.ResponseEntity;
|
10 |
+
import org.springframework.security.access.prepost.PreAuthorize;
|
11 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
12 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
13 |
+
import org.springframework.web.bind.annotation.RestController;
|
14 |
+
|
15 |
+
import java.util.List;
|
16 |
+
|
17 |
+
@RestController
|
18 |
+
@RequestMapping("/api/v1/admin/roles")
|
19 |
+
@RequiredArgsConstructor
|
20 |
+
@Tag(name = "Role Management", description = "APIs for viewing available roles.")
|
21 |
+
@PreAuthorize("hasRole('ADMIN')")
|
22 |
+
public class RoleController {
|
23 |
+
|
24 |
+
private final IRoleService roleService;
|
25 |
+
|
26 |
+
@GetMapping
|
27 |
+
@Operation(summary = "Get all available realm roles",
|
28 |
+
description = "Retrieves a list of all available roles in the configured Keycloak realm.",
|
29 |
+
responses = @ApiResponse(responseCode = "200", description = "Successfully retrieved roles"))
|
30 |
+
public ResponseEntity<List<RoleDTO>> getAllRealmRoles() {
|
31 |
+
return ResponseEntity.ok(roleService.getAllRealmRoles());
|
32 |
+
}
|
33 |
+
}
|
src/main/java/com/dalab/adminservice/controller/ServiceConfigController.java
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.controller;
|
2 |
+
|
3 |
+
import java.util.List;
|
4 |
+
import java.util.Optional;
|
5 |
+
|
6 |
+
import org.springframework.http.HttpStatus;
|
7 |
+
import org.springframework.http.ResponseEntity;
|
8 |
+
import org.springframework.security.access.prepost.PreAuthorize;
|
9 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
10 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
11 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
12 |
+
import org.springframework.web.bind.annotation.PutMapping;
|
13 |
+
import org.springframework.web.bind.annotation.RequestBody;
|
14 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
15 |
+
import org.springframework.web.bind.annotation.RestController;
|
16 |
+
|
17 |
+
import com.dalab.adminservice.dto.ServiceConfigDTO;
|
18 |
+
import com.dalab.adminservice.service.IServiceConfigService;
|
19 |
+
|
20 |
+
import io.swagger.v3.oas.annotations.Operation;
|
21 |
+
import io.swagger.v3.oas.annotations.tags.Tag;
|
22 |
+
import jakarta.validation.Valid;
|
23 |
+
import lombok.RequiredArgsConstructor;
|
24 |
+
import lombok.extern.slf4j.Slf4j;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* REST controller for managing service configurations
|
28 |
+
*/
|
29 |
+
@RestController
|
30 |
+
@RequestMapping("/api/v1/admin/config/services")
|
31 |
+
@RequiredArgsConstructor
|
32 |
+
@Slf4j
|
33 |
+
@Tag(name = "Service Configuration Management", description = "APIs for managing service configurations")
|
34 |
+
public class ServiceConfigController {
|
35 |
+
|
36 |
+
private final IServiceConfigService serviceConfigService;
|
37 |
+
|
38 |
+
@GetMapping
|
39 |
+
@PreAuthorize("hasRole('ADMIN')")
|
40 |
+
@Operation(summary = "Get all service configurations")
|
41 |
+
public ResponseEntity<List<ServiceConfigDTO>> getAllServiceConfigs() {
|
42 |
+
log.debug("Getting all service configurations");
|
43 |
+
List<ServiceConfigDTO> configs = serviceConfigService.getAllServiceConfigs();
|
44 |
+
return ResponseEntity.ok(configs);
|
45 |
+
}
|
46 |
+
|
47 |
+
@GetMapping("/{serviceId}")
|
48 |
+
@PreAuthorize("hasRole('ADMIN')")
|
49 |
+
@Operation(summary = "Get service configuration by service ID")
|
50 |
+
public ResponseEntity<ServiceConfigDTO> getServiceConfigById(@PathVariable String serviceId) {
|
51 |
+
log.debug("Getting service configuration for service: {}", serviceId);
|
52 |
+
Optional<ServiceConfigDTO> config = serviceConfigService.getServiceConfigById(serviceId);
|
53 |
+
return config.map(ResponseEntity::ok)
|
54 |
+
.orElse(ResponseEntity.notFound().build());
|
55 |
+
}
|
56 |
+
|
57 |
+
@PostMapping
|
58 |
+
@PreAuthorize("hasRole('ADMIN')")
|
59 |
+
@Operation(summary = "Create new service configuration")
|
60 |
+
public ResponseEntity<ServiceConfigDTO> createServiceConfig(@Valid @RequestBody ServiceConfigDTO serviceConfigDTO) {
|
61 |
+
log.debug("Creating new service configuration for service: {}", serviceConfigDTO.getServiceId());
|
62 |
+
ServiceConfigDTO created = serviceConfigService.createServiceConfig(serviceConfigDTO);
|
63 |
+
return ResponseEntity.status(HttpStatus.CREATED).body(created);
|
64 |
+
}
|
65 |
+
|
66 |
+
@PutMapping("/{serviceId}")
|
67 |
+
@PreAuthorize("hasRole('ADMIN')")
|
68 |
+
@Operation(summary = "Update service configuration")
|
69 |
+
public ResponseEntity<ServiceConfigDTO> updateServiceConfig(
|
70 |
+
@PathVariable String serviceId,
|
71 |
+
@Valid @RequestBody ServiceConfigDTO serviceConfigDTO) {
|
72 |
+
log.debug("Updating service configuration for service: {}", serviceId);
|
73 |
+
ServiceConfigDTO updated = serviceConfigService.updateServiceConfig(serviceId, serviceConfigDTO);
|
74 |
+
return ResponseEntity.ok(updated);
|
75 |
+
}
|
76 |
+
}
|
src/main/java/com/dalab/adminservice/controller/UserController.java
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.controller;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.UserDTO;
|
4 |
+
import com.dalab.adminservice.service.IUserService;
|
5 |
+
import io.swagger.v3.oas.annotations.Operation;
|
6 |
+
import io.swagger.v3.oas.annotations.Parameter;
|
7 |
+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
8 |
+
import io.swagger.v3.oas.annotations.tags.Tag;
|
9 |
+
import jakarta.validation.Valid;
|
10 |
+
import lombok.RequiredArgsConstructor;
|
11 |
+
import org.keycloak.representations.idm.RoleRepresentation;
|
12 |
+
import org.springframework.http.HttpStatus;
|
13 |
+
import org.springframework.http.ResponseEntity;
|
14 |
+
import org.springframework.security.access.prepost.PreAuthorize;
|
15 |
+
import org.springframework.web.bind.annotation.*;
|
16 |
+
|
17 |
+
import java.util.List;
|
18 |
+
|
19 |
+
@RestController
|
20 |
+
@RequestMapping("/api/v1/admin/users")
|
21 |
+
@RequiredArgsConstructor
|
22 |
+
@Tag(name = "User Management", description = "APIs for managing user accounts.")
|
23 |
+
@PreAuthorize("hasRole('ADMIN')") // All user management operations require ADMIN role
|
24 |
+
public class UserController {
|
25 |
+
|
26 |
+
private final IUserService userService;
|
27 |
+
|
28 |
+
@GetMapping
|
29 |
+
@Operation(summary = "Get all users",
|
30 |
+
description = "Retrieves a paginated list of all users.",
|
31 |
+
responses = @ApiResponse(responseCode = "200", description = "Successfully retrieved users"))
|
32 |
+
public ResponseEntity<List<UserDTO>> getAllUsers(
|
33 |
+
@Parameter(description = "Offset for pagination") @RequestParam(required = false) Integer firstResult,
|
34 |
+
@Parameter(description = "Maximum number of results per page") @RequestParam(required = false) Integer maxResults) {
|
35 |
+
return ResponseEntity.ok(userService.getAllUsers(firstResult, maxResults));
|
36 |
+
}
|
37 |
+
|
38 |
+
@GetMapping("/{userId}")
|
39 |
+
@Operation(summary = "Get user by ID",
|
40 |
+
description = "Retrieves a specific user by their ID.",
|
41 |
+
responses = {
|
42 |
+
@ApiResponse(responseCode = "200", description = "Successfully retrieved user"),
|
43 |
+
@ApiResponse(responseCode = "404", description = "User not found")
|
44 |
+
})
|
45 |
+
public ResponseEntity<UserDTO> getUserById(
|
46 |
+
@Parameter(description = "ID of the user to retrieve", example = "f47ac10b-58cc-4372-a567-0e02b2c3d479")
|
47 |
+
@PathVariable String userId) {
|
48 |
+
return userService.getUserById(userId)
|
49 |
+
.map(ResponseEntity::ok)
|
50 |
+
.orElse(ResponseEntity.notFound().build());
|
51 |
+
}
|
52 |
+
|
53 |
+
@GetMapping("/username/{username}")
|
54 |
+
@Operation(summary = "Get user by username",
|
55 |
+
description = "Retrieves a specific user by their username.",
|
56 |
+
responses = {
|
57 |
+
@ApiResponse(responseCode = "200", description = "Successfully retrieved user"),
|
58 |
+
@ApiResponse(responseCode = "404", description = "User not found")
|
59 |
+
})
|
60 |
+
public ResponseEntity<UserDTO> getUserByUsername(
|
61 |
+
@Parameter(description = "Username of the user to retrieve", example = "johndoe")
|
62 |
+
@PathVariable String username) {
|
63 |
+
return userService.getUserByUsername(username)
|
64 |
+
.map(ResponseEntity::ok)
|
65 |
+
.orElse(ResponseEntity.notFound().build());
|
66 |
+
}
|
67 |
+
|
68 |
+
@PostMapping
|
69 |
+
@Operation(summary = "Create a new user",
|
70 |
+
description = "Creates a new user account.",
|
71 |
+
responses = {
|
72 |
+
@ApiResponse(responseCode = "201", description = "User created successfully"),
|
73 |
+
@ApiResponse(responseCode = "400", description = "Invalid input data"),
|
74 |
+
@ApiResponse(responseCode = "409", description = "User already exists")
|
75 |
+
})
|
76 |
+
public ResponseEntity<UserDTO> createUser(@Valid @RequestBody UserDTO userDTO) {
|
77 |
+
UserDTO createdUser = userService.createUser(userDTO);
|
78 |
+
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
|
79 |
+
}
|
80 |
+
|
81 |
+
@PutMapping("/{userId}")
|
82 |
+
@Operation(summary = "Update an existing user",
|
83 |
+
description = "Updates an existing user account.",
|
84 |
+
responses = {
|
85 |
+
@ApiResponse(responseCode = "200", description = "User updated successfully"),
|
86 |
+
@ApiResponse(responseCode = "400", description = "Invalid input data"),
|
87 |
+
@ApiResponse(responseCode = "404", description = "User not found")
|
88 |
+
})
|
89 |
+
public ResponseEntity<UserDTO> updateUser(
|
90 |
+
@Parameter(description = "ID of the user to update") @PathVariable String userId,
|
91 |
+
@Valid @RequestBody UserDTO userDTO) {
|
92 |
+
UserDTO updatedUser = userService.updateUser(userId, userDTO);
|
93 |
+
return ResponseEntity.ok(updatedUser);
|
94 |
+
}
|
95 |
+
|
96 |
+
@DeleteMapping("/{userId}")
|
97 |
+
@Operation(summary = "Delete a user",
|
98 |
+
description = "Deletes a user account by their ID.",
|
99 |
+
responses = {
|
100 |
+
@ApiResponse(responseCode = "204", description = "User deleted successfully"),
|
101 |
+
@ApiResponse(responseCode = "404", description = "User not found")
|
102 |
+
})
|
103 |
+
public ResponseEntity<Void> deleteUser(
|
104 |
+
@Parameter(description = "ID of the user to delete") @PathVariable String userId) {
|
105 |
+
userService.deleteUser(userId);
|
106 |
+
return ResponseEntity.noContent().build();
|
107 |
+
}
|
108 |
+
|
109 |
+
// Role Management specific to a user might be better here than a top-level /roles endpoint if it's always user-centric
|
110 |
+
@GetMapping("/{userId}/roles")
|
111 |
+
@Operation(summary = "Get user's realm roles",
|
112 |
+
description = "Retrieves the realm roles assigned to a specific user.")
|
113 |
+
public ResponseEntity<List<RoleRepresentation>> getUserRealmRoles(@PathVariable String userId) {
|
114 |
+
return ResponseEntity.ok(userService.getUserRealmRoles(userId));
|
115 |
+
}
|
116 |
+
|
117 |
+
@PostMapping("/{userId}/roles")
|
118 |
+
@Operation(summary = "Assign realm roles to user",
|
119 |
+
description = "Assigns a list of realm roles to a specific user. Replaces existing roles if not additive.")
|
120 |
+
@ResponseStatus(HttpStatus.NO_CONTENT)
|
121 |
+
public void assignRealmRolesToUser(
|
122 |
+
@PathVariable String userId,
|
123 |
+
@Parameter(description = "List of role names to assign") @RequestBody List<String> roleNames) {
|
124 |
+
userService.assignRealmRolesToUser(userId, roleNames);
|
125 |
+
}
|
126 |
+
}
|
src/main/java/com/dalab/adminservice/dto/AggregatedJobStatusDTO.java
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.dto;
|
2 |
+
|
3 |
+
import lombok.AllArgsConstructor;
|
4 |
+
import lombok.Builder;
|
5 |
+
import lombok.Data;
|
6 |
+
import lombok.NoArgsConstructor;
|
7 |
+
|
8 |
+
import java.util.List;
|
9 |
+
|
10 |
+
@Data
|
11 |
+
@Builder
|
12 |
+
@NoArgsConstructor
|
13 |
+
@AllArgsConstructor
|
14 |
+
public class AggregatedJobStatusDTO {
|
15 |
+
private List<JobStatusDTO> jobs;
|
16 |
+
private int totalJobs;
|
17 |
+
private int pendingJobs;
|
18 |
+
private int runningJobs;
|
19 |
+
private int completedSuccessJobs;
|
20 |
+
private int completedFailedJobs;
|
21 |
+
// Add any other summary fields as needed
|
22 |
+
}
|
src/main/java/com/dalab/adminservice/dto/CloudConnectionDTO.java
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.dto;
|
2 |
+
|
3 |
+
import java.util.Map;
|
4 |
+
|
5 |
+
import com.dalab.adminservice.model.enums.CloudProviderType;
|
6 |
+
|
7 |
+
import io.swagger.v3.oas.annotations.media.Schema;
|
8 |
+
import jakarta.validation.constraints.NotBlank;
|
9 |
+
import jakarta.validation.constraints.NotNull;
|
10 |
+
import jakarta.validation.constraints.Size;
|
11 |
+
import lombok.AllArgsConstructor;
|
12 |
+
import lombok.Builder;
|
13 |
+
import lombok.Data;
|
14 |
+
import lombok.NoArgsConstructor;
|
15 |
+
|
16 |
+
@Data
|
17 |
+
@Builder
|
18 |
+
@NoArgsConstructor
|
19 |
+
@AllArgsConstructor
|
20 |
+
@Schema(description = "Represents a cloud connection configuration.")
|
21 |
+
public class CloudConnectionDTO {
|
22 |
+
|
23 |
+
@Schema(description = "Unique identifier of the cloud connection, generated by the system.", example = "f47ac10b-58cc-4372-a567-0e02b2c3d479", accessMode = Schema.AccessMode.READ_ONLY)
|
24 |
+
private String id;
|
25 |
+
|
26 |
+
@NotBlank
|
27 |
+
@Size(max = 100)
|
28 |
+
@Schema(description = "User-defined name for this cloud connection.", example = "My Production GCP Connection")
|
29 |
+
private String name;
|
30 |
+
|
31 |
+
@Schema(description = "Optional description for the cloud connection.", example = "Main GCP project for production workloads")
|
32 |
+
private String description;
|
33 |
+
|
34 |
+
@NotNull
|
35 |
+
@Schema(description = "Type of the cloud provider.", example = "GCP")
|
36 |
+
private CloudProviderType providerType;
|
37 |
+
|
38 |
+
@Schema(description = "Key-value pairs for connection credentials and parameters (e.g., projectId, region, accessKey). Specific keys depend on the providerType. Sensitive values are write-only.")
|
39 |
+
private Map<String, String> connectionParameters; // e.g., { "projectId": "my-gcp-project", "region": "us-central1" }
|
40 |
+
|
41 |
+
@Schema(description = "Service Account Key JSON or other sensitive credential details. This field is write-only and will not be returned in GET requests.", accessMode = Schema.AccessMode.WRITE_ONLY)
|
42 |
+
private String sensitiveCredentials; // e.g., GCP Service Account JSON, AWS Secret Key
|
43 |
+
|
44 |
+
@Schema(description = "Indicates if the connection is currently enabled.", example = "true", accessMode = Schema.AccessMode.READ_ONLY)
|
45 |
+
@Builder.Default
|
46 |
+
private boolean enabled = true;
|
47 |
+
|
48 |
+
@Schema(description = "Timestamp of the last successful connection test.", accessMode = Schema.AccessMode.READ_ONLY)
|
49 |
+
private String lastConnectionTestAt; // Using String for simplicity, can be LocalDateTime
|
50 |
+
|
51 |
+
@Schema(description = "Status of the last connection test (e.g., SUCCESS, FAILED).", accessMode = Schema.AccessMode.READ_ONLY)
|
52 |
+
private String lastConnectionTestStatus;
|
53 |
+
}
|
src/main/java/com/dalab/adminservice/dto/CloudConnectionTestResultDTO.java
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.dto;
|
2 |
+
|
3 |
+
import io.swagger.v3.oas.annotations.media.Schema;
|
4 |
+
import lombok.AllArgsConstructor;
|
5 |
+
import lombok.Builder;
|
6 |
+
import lombok.Data;
|
7 |
+
import lombok.NoArgsConstructor;
|
8 |
+
|
9 |
+
@Data
|
10 |
+
@Builder
|
11 |
+
@NoArgsConstructor
|
12 |
+
@AllArgsConstructor
|
13 |
+
@Schema(description = "Result of a cloud connection test operation.")
|
14 |
+
public class CloudConnectionTestResultDTO {
|
15 |
+
|
16 |
+
@Schema(description = "Indicates whether the connection test was successful.", example = "true")
|
17 |
+
private boolean success;
|
18 |
+
|
19 |
+
@Schema(description = "A message detailing the test result, including error information if applicable.", example = "Connection to GCP project 'my-gcp-project' successful.")
|
20 |
+
private String message;
|
21 |
+
|
22 |
+
@Schema(description = "Timestamp of when the test was performed.")
|
23 |
+
private String testedAt; // Can be LocalDateTime
|
24 |
+
}
|
src/main/java/com/dalab/adminservice/dto/JobStatusDTO.java
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.dto;
|
2 |
+
|
3 |
+
import lombok.AllArgsConstructor;
|
4 |
+
import lombok.Builder;
|
5 |
+
import lombok.Data;
|
6 |
+
import lombok.NoArgsConstructor;
|
7 |
+
|
8 |
+
import java.time.LocalDateTime;
|
9 |
+
import java.util.Map;
|
10 |
+
|
11 |
+
@Data
|
12 |
+
@Builder
|
13 |
+
@NoArgsConstructor
|
14 |
+
@AllArgsConstructor
|
15 |
+
public class JobStatusDTO {
|
16 |
+
private String jobId;
|
17 |
+
private String serviceName; // Name of the service that owns the job
|
18 |
+
private String jobType; // e.g., "DISCOVERY_SCAN", "AUTOLABEL_TASK", "ARCHIVAL_JOB"
|
19 |
+
private String status; // e.g., "PENDING", "RUNNING", "COMPLETED_SUCCESS", "COMPLETED_FAILED", "UNKNOWN"
|
20 |
+
private LocalDateTime submittedAt;
|
21 |
+
private LocalDateTime startedAt;
|
22 |
+
private LocalDateTime completedAt;
|
23 |
+
private Map<String, Object> details; // Any service-specific details about the job
|
24 |
+
private String errorDetails; // Error message if the job failed
|
25 |
+
}
|
src/main/java/com/dalab/adminservice/dto/RoleDTO.java
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.dto;
|
2 |
+
|
3 |
+
import lombok.AllArgsConstructor;
|
4 |
+
import lombok.Builder;
|
5 |
+
import lombok.Data;
|
6 |
+
import lombok.NoArgsConstructor;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* DTO for role management operations.
|
10 |
+
*/
|
11 |
+
@Data
|
12 |
+
@Builder
|
13 |
+
@NoArgsConstructor
|
14 |
+
@AllArgsConstructor
|
15 |
+
public class RoleDTO {
|
16 |
+
|
17 |
+
private String id;
|
18 |
+
private String name;
|
19 |
+
private String description;
|
20 |
+
|
21 |
+
}
|
src/main/java/com/dalab/adminservice/dto/ServiceConfigDTO.java
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.dto;
|
2 |
+
|
3 |
+
import jakarta.validation.constraints.NotBlank;
|
4 |
+
import lombok.AllArgsConstructor;
|
5 |
+
import lombok.Builder;
|
6 |
+
import lombok.Data;
|
7 |
+
import lombok.NoArgsConstructor;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* DTO for service configuration management.
|
11 |
+
*/
|
12 |
+
@Data
|
13 |
+
@Builder
|
14 |
+
@NoArgsConstructor
|
15 |
+
@AllArgsConstructor
|
16 |
+
public class ServiceConfigDTO {
|
17 |
+
|
18 |
+
@NotBlank
|
19 |
+
private String serviceId;
|
20 |
+
|
21 |
+
private String displayName;
|
22 |
+
private String description;
|
23 |
+
private String version;
|
24 |
+
private String endpoint;
|
25 |
+
|
26 |
+
@Builder.Default
|
27 |
+
private boolean enabled = true;
|
28 |
+
|
29 |
+
}
|
src/main/java/com/dalab/adminservice/dto/UserDTO.java
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.dto;
|
2 |
+
|
3 |
+
import java.util.List;
|
4 |
+
|
5 |
+
import jakarta.validation.constraints.Email;
|
6 |
+
import jakarta.validation.constraints.NotBlank;
|
7 |
+
import lombok.AllArgsConstructor;
|
8 |
+
import lombok.Builder;
|
9 |
+
import lombok.Data;
|
10 |
+
import lombok.NoArgsConstructor;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* DTO for user management operations.
|
14 |
+
*/
|
15 |
+
@Data
|
16 |
+
@Builder
|
17 |
+
@NoArgsConstructor
|
18 |
+
@AllArgsConstructor
|
19 |
+
public class UserDTO {
|
20 |
+
|
21 |
+
private String id;
|
22 |
+
|
23 |
+
@NotBlank
|
24 |
+
private String username;
|
25 |
+
|
26 |
+
@Email
|
27 |
+
private String email;
|
28 |
+
|
29 |
+
private String firstName;
|
30 |
+
private String lastName;
|
31 |
+
private String password;
|
32 |
+
|
33 |
+
@Builder.Default
|
34 |
+
private boolean enabled = true;
|
35 |
+
|
36 |
+
private List<String> roles;
|
37 |
+
|
38 |
+
}
|
src/main/java/com/dalab/adminservice/exception/ConflictException.java
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.exception;
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Exception thrown when a conflict occurs during operations.
|
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/adminservice/exception/KeycloakAdminException.java
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.exception;
|
2 |
+
|
3 |
+
// Can be a generic runtime exception or extend a more specific one if needed
|
4 |
+
public class KeycloakAdminException extends RuntimeException {
|
5 |
+
public KeycloakAdminException(String message, Throwable cause) {
|
6 |
+
super(message, cause);
|
7 |
+
}
|
8 |
+
}
|
src/main/java/com/dalab/adminservice/exception/NotFoundException.java
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.exception;
|
2 |
+
|
3 |
+
import org.springframework.http.HttpStatus;
|
4 |
+
import org.springframework.web.bind.annotation.ResponseStatus;
|
5 |
+
|
6 |
+
@ResponseStatus(HttpStatus.NOT_FOUND)
|
7 |
+
public class NotFoundException extends RuntimeException {
|
8 |
+
public NotFoundException(String message) {
|
9 |
+
super(message);
|
10 |
+
}
|
11 |
+
}
|
src/main/java/com/dalab/adminservice/mapper/CloudConnectionMapper.java
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.mapper;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.CloudConnectionDTO;
|
4 |
+
import com.dalab.adminservice.model.CloudConnectionEntity;
|
5 |
+
import org.mapstruct.Mapper;
|
6 |
+
import org.mapstruct.Mapping;
|
7 |
+
import org.mapstruct.MappingTarget;
|
8 |
+
import org.mapstruct.NullValuePropertyMappingStrategy;
|
9 |
+
import org.mapstruct.ReportingPolicy;
|
10 |
+
import org.mapstruct.factory.Mappers;
|
11 |
+
|
12 |
+
import java.util.List;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Mapper interface for converting between CloudConnectionEntity and CloudConnectionDTO.
|
16 |
+
*/
|
17 |
+
@Mapper(
|
18 |
+
componentModel = "spring",
|
19 |
+
unmappedTargetPolicy = ReportingPolicy.IGNORE,
|
20 |
+
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE
|
21 |
+
)
|
22 |
+
public interface CloudConnectionMapper {
|
23 |
+
|
24 |
+
CloudConnectionMapper INSTANCE = Mappers.getMapper(CloudConnectionMapper.class);
|
25 |
+
|
26 |
+
// When mapping from Entity to DTO, ignore encryptedCredentials
|
27 |
+
@Mapping(target = "sensitiveCredentials", ignore = true)
|
28 |
+
CloudConnectionDTO toDto(CloudConnectionEntity entity);
|
29 |
+
|
30 |
+
List<CloudConnectionDTO> toDtoList(List<CloudConnectionEntity> entities);
|
31 |
+
|
32 |
+
// When mapping from DTO to Entity for creation/update, sensitiveCredentials from DTO will be handled by service for encryption
|
33 |
+
// The encryptedCredentials field in entity will be set by the service, not directly by mapper from DTO.sensitiveCredentials
|
34 |
+
@Mapping(target = "id", ignore = true) // ID is generated or path param
|
35 |
+
@Mapping(target = "encryptedCredentials", ignore = true) // Handled by service
|
36 |
+
@Mapping(target = "createdBy", ignore = true)
|
37 |
+
@Mapping(target = "createdDate", ignore = true)
|
38 |
+
@Mapping(target = "lastModifiedBy", ignore = true)
|
39 |
+
@Mapping(target = "lastModifiedDate", ignore = true)
|
40 |
+
@Mapping(target = "lastConnectionTestAt", ignore = true) // These are set by the system
|
41 |
+
@Mapping(target = "lastConnectionTestStatus", ignore = true)
|
42 |
+
void updateEntityFromDto(CloudConnectionDTO dto, @MappingTarget CloudConnectionEntity entity);
|
43 |
+
|
44 |
+
@Mapping(target = "id", ignore = true) // ID is generated
|
45 |
+
@Mapping(target = "encryptedCredentials", ignore = true) // Handled by service
|
46 |
+
@Mapping(target = "createdBy", ignore = true)
|
47 |
+
@Mapping(target = "createdDate", ignore = true)
|
48 |
+
@Mapping(target = "lastModifiedBy", ignore = true)
|
49 |
+
@Mapping(target = "lastModifiedDate", ignore = true)
|
50 |
+
@Mapping(target = "lastConnectionTestAt", ignore = true)
|
51 |
+
@Mapping(target = "lastConnectionTestStatus", ignore = true)
|
52 |
+
CloudConnectionEntity toEntity(CloudConnectionDTO dto);
|
53 |
+
}
|
src/main/java/com/dalab/adminservice/mapper/RoleMapper.java
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.mapper;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.RoleDTO;
|
4 |
+
import org.keycloak.representations.idm.RoleRepresentation;
|
5 |
+
import org.mapstruct.Mapper;
|
6 |
+
import org.mapstruct.ReportingPolicy;
|
7 |
+
|
8 |
+
import java.util.List;
|
9 |
+
|
10 |
+
@Mapper(
|
11 |
+
componentModel = "spring",
|
12 |
+
unmappedTargetPolicy = ReportingPolicy.IGNORE
|
13 |
+
)
|
14 |
+
public interface RoleMapper {
|
15 |
+
|
16 |
+
RoleDTO toDto(RoleRepresentation roleRepresentation);
|
17 |
+
|
18 |
+
List<RoleDTO> toDtoList(List<RoleRepresentation> roleRepresentations);
|
19 |
+
}
|
src/main/java/com/dalab/adminservice/mapper/ServiceConfigMapper.java
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.mapper;
|
2 |
+
|
3 |
+
import java.util.List;
|
4 |
+
|
5 |
+
import org.mapstruct.Mapper;
|
6 |
+
import org.mapstruct.Mapping;
|
7 |
+
import org.mapstruct.MappingTarget;
|
8 |
+
import org.mapstruct.factory.Mappers;
|
9 |
+
|
10 |
+
import com.dalab.adminservice.dto.ServiceConfigDTO;
|
11 |
+
import com.dalab.adminservice.model.ServiceConfigEntity;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Mapper interface for converting between ServiceConfigEntity and ServiceConfigDTO.
|
15 |
+
*/
|
16 |
+
@Mapper
|
17 |
+
public interface ServiceConfigMapper {
|
18 |
+
|
19 |
+
ServiceConfigMapper INSTANCE = Mappers.getMapper(ServiceConfigMapper.class);
|
20 |
+
|
21 |
+
ServiceConfigDTO toDto(ServiceConfigEntity entity);
|
22 |
+
|
23 |
+
List<ServiceConfigDTO> toDtoList(List<ServiceConfigEntity> entities);
|
24 |
+
|
25 |
+
@Mapping(target = "serviceId", ignore = true)
|
26 |
+
void updateEntityFromDto(ServiceConfigDTO dto, @MappingTarget ServiceConfigEntity entity);
|
27 |
+
|
28 |
+
ServiceConfigEntity toEntity(ServiceConfigDTO dto);
|
29 |
+
}
|
src/main/java/com/dalab/adminservice/mapper/UserMapper.java
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.mapper;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.UserDTO;
|
4 |
+
import org.keycloak.representations.idm.CredentialRepresentation;
|
5 |
+
import org.keycloak.representations.idm.UserRepresentation;
|
6 |
+
import org.mapstruct.Mapper;
|
7 |
+
import org.mapstruct.Mapping;
|
8 |
+
import org.mapstruct.Named;
|
9 |
+
import org.mapstruct.ReportingPolicy;
|
10 |
+
|
11 |
+
import java.util.Collections;
|
12 |
+
import java.util.List;
|
13 |
+
|
14 |
+
@Mapper(
|
15 |
+
componentModel = "spring",
|
16 |
+
unmappedTargetPolicy = ReportingPolicy.IGNORE
|
17 |
+
)
|
18 |
+
public interface UserMapper {
|
19 |
+
|
20 |
+
@Mapping(target = "roles", source = "realmRoles") // Assuming roles are stored in realmRoles
|
21 |
+
UserDTO toDto(UserRepresentation userRepresentation);
|
22 |
+
|
23 |
+
List<UserDTO> toDtoList(List<UserRepresentation> userRepresentations);
|
24 |
+
|
25 |
+
// For creating/updating users in Keycloak
|
26 |
+
@Mapping(target = "realmRoles", source = "roles")
|
27 |
+
@Mapping(target = "credentials", source = "password", qualifiedByName = "passwordToCredentials")
|
28 |
+
UserRepresentation toRepresentation(UserDTO userDTO);
|
29 |
+
|
30 |
+
@Named("passwordToCredentials")
|
31 |
+
default List<CredentialRepresentation> passwordToCredentials(String password) {
|
32 |
+
if (password == null || password.isEmpty()) {
|
33 |
+
return Collections.emptyList();
|
34 |
+
}
|
35 |
+
CredentialRepresentation credential = new CredentialRepresentation();
|
36 |
+
credential.setType(CredentialRepresentation.PASSWORD);
|
37 |
+
credential.setValue(password);
|
38 |
+
credential.setTemporary(false); // Set to true if password reset is required on first login
|
39 |
+
return Collections.singletonList(credential);
|
40 |
+
}
|
41 |
+
}
|
src/main/java/com/dalab/adminservice/model/AbstractAuditableEntity.java
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.model;
|
2 |
+
|
3 |
+
import jakarta.persistence.Column;
|
4 |
+
import jakarta.persistence.EntityListeners;
|
5 |
+
import jakarta.persistence.MappedSuperclass;
|
6 |
+
import lombok.Data;
|
7 |
+
import org.springframework.data.annotation.CreatedBy;
|
8 |
+
import org.springframework.data.annotation.CreatedDate;
|
9 |
+
import org.springframework.data.annotation.LastModifiedBy;
|
10 |
+
import org.springframework.data.annotation.LastModifiedDate;
|
11 |
+
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
12 |
+
|
13 |
+
import java.io.Serializable;
|
14 |
+
import java.time.Instant;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Abstract base class for auditable entities.
|
18 |
+
*/
|
19 |
+
@Data
|
20 |
+
@MappedSuperclass
|
21 |
+
@EntityListeners(AuditingEntityListener.class)
|
22 |
+
public abstract class AbstractAuditableEntity implements Serializable {
|
23 |
+
|
24 |
+
private static final long serialVersionUID = 1L;
|
25 |
+
|
26 |
+
@CreatedBy
|
27 |
+
@Column(name = "created_by", nullable = false, length = 50, updatable = false)
|
28 |
+
private String createdBy;
|
29 |
+
|
30 |
+
@CreatedDate
|
31 |
+
@Column(name = "created_date", nullable = false, updatable = false)
|
32 |
+
private Instant createdDate = Instant.now();
|
33 |
+
|
34 |
+
@LastModifiedBy
|
35 |
+
@Column(name = "last_modified_by", length = 50)
|
36 |
+
private String lastModifiedBy;
|
37 |
+
|
38 |
+
@LastModifiedDate
|
39 |
+
@Column(name = "last_modified_date")
|
40 |
+
private Instant lastModifiedDate = Instant.now();
|
41 |
+
}
|
src/main/java/com/dalab/adminservice/model/CloudConnectionEntity.java
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.model;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.model.enums.CloudProviderType;
|
4 |
+
import io.hypersistence.utils.hibernate.type.json.JsonType;
|
5 |
+
import jakarta.persistence.*;
|
6 |
+
import lombok.Getter;
|
7 |
+
import lombok.Setter;
|
8 |
+
import org.hibernate.annotations.GenericGenerator;
|
9 |
+
import org.hibernate.annotations.Type;
|
10 |
+
|
11 |
+
import java.time.LocalDateTime;
|
12 |
+
import java.util.Map;
|
13 |
+
|
14 |
+
@Getter
|
15 |
+
@Setter
|
16 |
+
@Entity
|
17 |
+
@Table(name = "dalab_cloud_connection")
|
18 |
+
public class CloudConnectionEntity extends AbstractAuditableEntity {
|
19 |
+
|
20 |
+
@Id
|
21 |
+
@GeneratedValue(generator = "uuid2")
|
22 |
+
@GenericGenerator(name = "uuid2", strategy = "uuid2")
|
23 |
+
@Column(name = "id", updatable = false, nullable = false, columnDefinition = "VARCHAR(36)")
|
24 |
+
private String id;
|
25 |
+
|
26 |
+
@Column(name = "name", nullable = false, length = 100)
|
27 |
+
private String name;
|
28 |
+
|
29 |
+
@Column(name = "description", length = 500)
|
30 |
+
private String description;
|
31 |
+
|
32 |
+
@Enumerated(EnumType.STRING)
|
33 |
+
@Column(name = "provider_type", nullable = false, length = 50)
|
34 |
+
private CloudProviderType providerType;
|
35 |
+
|
36 |
+
@Type(JsonType.class)
|
37 |
+
@Column(name = "connection_parameters", columnDefinition = "jsonb")
|
38 |
+
private Map<String, String> connectionParameters;
|
39 |
+
|
40 |
+
@Lob // Or @Column(length = large_enough_value) depending on DB and encrypted size
|
41 |
+
@Column(name = "encrypted_credentials", nullable = true) // Storing as byte[] might be better for encrypted data
|
42 |
+
private String encryptedCredentials; // Placeholder: In a real app, this would be byte[] and encrypted
|
43 |
+
|
44 |
+
@Column(name = "enabled", nullable = false)
|
45 |
+
private boolean enabled = true;
|
46 |
+
|
47 |
+
@Column(name = "last_connection_test_at")
|
48 |
+
private LocalDateTime lastConnectionTestAt;
|
49 |
+
|
50 |
+
@Column(name = "last_connection_test_status", length = 50)
|
51 |
+
private String lastConnectionTestStatus; // e.g., SUCCESS, FAILED
|
52 |
+
}
|
src/main/java/com/dalab/adminservice/model/ServiceConfigEntity.java
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.model;
|
2 |
+
|
3 |
+
import jakarta.persistence.Column;
|
4 |
+
import jakarta.persistence.Entity;
|
5 |
+
import jakarta.persistence.Id;
|
6 |
+
import jakarta.persistence.Table;
|
7 |
+
import lombok.AllArgsConstructor;
|
8 |
+
import lombok.Builder;
|
9 |
+
import lombok.Data;
|
10 |
+
import lombok.NoArgsConstructor;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Entity for service configuration management.
|
14 |
+
*/
|
15 |
+
@Entity
|
16 |
+
@Table(name = "service_configs")
|
17 |
+
@Data
|
18 |
+
@Builder
|
19 |
+
@NoArgsConstructor
|
20 |
+
@AllArgsConstructor
|
21 |
+
public class ServiceConfigEntity {
|
22 |
+
|
23 |
+
@Id
|
24 |
+
@Column(nullable = false, unique = true, length = 100)
|
25 |
+
private String serviceId;
|
26 |
+
|
27 |
+
@Column(length = 255)
|
28 |
+
private String displayName;
|
29 |
+
|
30 |
+
@Column(length = 1000)
|
31 |
+
private String description;
|
32 |
+
|
33 |
+
@Column(length = 50)
|
34 |
+
private String version;
|
35 |
+
|
36 |
+
@Column(length = 500)
|
37 |
+
private String endpoint;
|
38 |
+
|
39 |
+
@Builder.Default
|
40 |
+
private boolean enabled = true;
|
41 |
+
|
42 |
+
}
|
src/main/java/com/dalab/adminservice/model/enums/CloudProviderType.java
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.model.enums;
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Enumeration of supported cloud provider types.
|
5 |
+
*/
|
6 |
+
public enum CloudProviderType {
|
7 |
+
AWS,
|
8 |
+
AZURE,
|
9 |
+
GCP,
|
10 |
+
OTHER
|
11 |
+
}
|
src/main/java/com/dalab/adminservice/repository/CloudConnectionRepository.java
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.repository;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.model.CloudConnectionEntity;
|
4 |
+
import org.springframework.data.jpa.repository.JpaRepository;
|
5 |
+
import org.springframework.stereotype.Repository;
|
6 |
+
|
7 |
+
import java.util.Optional;
|
8 |
+
|
9 |
+
@Repository
|
10 |
+
public interface CloudConnectionRepository extends JpaRepository<CloudConnectionEntity, String> {
|
11 |
+
Optional<CloudConnectionEntity> findByName(String name);
|
12 |
+
}
|
src/main/java/com/dalab/adminservice/repository/ServiceConfigRepository.java
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.repository;
|
2 |
+
|
3 |
+
import java.util.Optional;
|
4 |
+
import java.util.UUID;
|
5 |
+
|
6 |
+
import org.springframework.data.jpa.repository.JpaRepository;
|
7 |
+
import org.springframework.stereotype.Repository;
|
8 |
+
|
9 |
+
import com.dalab.adminservice.model.ServiceConfigEntity;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Repository interface for ServiceConfig entities
|
13 |
+
*/
|
14 |
+
@Repository
|
15 |
+
public interface ServiceConfigRepository extends JpaRepository<ServiceConfigEntity, UUID> {
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Find service configuration by service ID
|
19 |
+
* @param serviceId the service identifier
|
20 |
+
* @return Optional containing the service config if found
|
21 |
+
*/
|
22 |
+
Optional<ServiceConfigEntity> findByServiceId(String serviceId);
|
23 |
+
}
|
src/main/java/com/dalab/adminservice/service/ICloudConnectionService.java
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.service;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.CloudConnectionDTO;
|
4 |
+
import com.dalab.adminservice.dto.CloudConnectionTestResultDTO;
|
5 |
+
|
6 |
+
import java.util.List;
|
7 |
+
import java.util.Optional;
|
8 |
+
|
9 |
+
public interface ICloudConnectionService {
|
10 |
+
List<CloudConnectionDTO> getAllCloudConnections();
|
11 |
+
Optional<CloudConnectionDTO> getCloudConnectionById(String id);
|
12 |
+
CloudConnectionDTO createCloudConnection(CloudConnectionDTO cloudConnectionDTO);
|
13 |
+
CloudConnectionDTO updateCloudConnection(String id, CloudConnectionDTO cloudConnectionDTO);
|
14 |
+
void deleteCloudConnection(String id);
|
15 |
+
CloudConnectionTestResultDTO testCloudConnection(String id);
|
16 |
+
// Method to get decrypted credentials - internal use, not exposed via API directly
|
17 |
+
String getDecryptedCredentials(String id);
|
18 |
+
}
|
src/main/java/com/dalab/adminservice/service/IEncryptionService.java
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.service;
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Service interface for encryption and decryption operations.
|
5 |
+
*/
|
6 |
+
public interface IEncryptionService {
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Encrypt the given data.
|
10 |
+
* @param data The data to encrypt
|
11 |
+
* @return The encrypted data as a string
|
12 |
+
*/
|
13 |
+
String encrypt(String data);
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Decrypt the given encrypted data.
|
17 |
+
* @param encryptedData The encrypted data to decrypt
|
18 |
+
* @return The decrypted data as a string
|
19 |
+
*/
|
20 |
+
String decrypt(String encryptedData);
|
21 |
+
|
22 |
+
}
|
src/main/java/com/dalab/adminservice/service/IJobStatusService.java
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.service;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.AggregatedJobStatusDTO;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Service interface for job status aggregation operations.
|
7 |
+
*/
|
8 |
+
public interface IJobStatusService {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Get aggregated job statuses from all microservices.
|
12 |
+
* @return Aggregated job status DTO
|
13 |
+
*/
|
14 |
+
AggregatedJobStatusDTO getAggregatedJobStatuses();
|
15 |
+
|
16 |
+
}
|
src/main/java/com/dalab/adminservice/service/IRoleService.java
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.service;
|
2 |
+
|
3 |
+
import java.util.List;
|
4 |
+
|
5 |
+
import com.dalab.adminservice.dto.RoleDTO;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Service interface for role management operations.
|
9 |
+
*/
|
10 |
+
public interface IRoleService {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Get all available realm roles.
|
14 |
+
* @return List of role DTOs
|
15 |
+
*/
|
16 |
+
List<RoleDTO> getAllRealmRoles();
|
17 |
+
|
18 |
+
}
|
src/main/java/com/dalab/adminservice/service/IServiceConfigService.java
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.service;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.ServiceConfigDTO;
|
4 |
+
|
5 |
+
import java.util.List;
|
6 |
+
import java.util.Optional;
|
7 |
+
|
8 |
+
public interface IServiceConfigService {
|
9 |
+
List<ServiceConfigDTO> getAllServiceConfigs();
|
10 |
+
Optional<ServiceConfigDTO> getServiceConfigById(String serviceId);
|
11 |
+
ServiceConfigDTO createServiceConfig(ServiceConfigDTO serviceConfigDTO);
|
12 |
+
ServiceConfigDTO updateServiceConfig(String serviceId, ServiceConfigDTO serviceConfigDTO);
|
13 |
+
// No delete operation defined for now, services are typically not "deleted" but maybe disabled.
|
14 |
+
}
|
src/main/java/com/dalab/adminservice/service/IUserService.java
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.service;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.UserDTO;
|
4 |
+
import org.keycloak.representations.idm.RoleRepresentation;
|
5 |
+
|
6 |
+
import java.util.List;
|
7 |
+
import java.util.Optional;
|
8 |
+
|
9 |
+
public interface IUserService {
|
10 |
+
List<UserDTO> getAllUsers(Integer firstResult, Integer maxResults);
|
11 |
+
Optional<UserDTO> getUserById(String userId);
|
12 |
+
Optional<UserDTO> getUserByUsername(String username);
|
13 |
+
UserDTO createUser(UserDTO userDTO);
|
14 |
+
UserDTO updateUser(String userId, UserDTO userDTO);
|
15 |
+
void deleteUser(String userId);
|
16 |
+
void assignRealmRolesToUser(String userId, List<String> roleNames);
|
17 |
+
List<RoleRepresentation> getAvailableRealmRoles(); // Helper to get roles for UI
|
18 |
+
List<RoleRepresentation> getUserRealmRoles(String userId);
|
19 |
+
}
|
src/main/java/com/dalab/adminservice/service/impl/BasicEncryptionServiceImpl.java
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.service.impl;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.service.IEncryptionService;
|
4 |
+
import lombok.extern.slf4j.Slf4j;
|
5 |
+
import org.springframework.beans.factory.annotation.Value;
|
6 |
+
import org.springframework.stereotype.Service;
|
7 |
+
import javax.crypto.Cipher;
|
8 |
+
import javax.crypto.spec.SecretKeySpec;
|
9 |
+
import java.nio.charset.StandardCharsets;
|
10 |
+
import java.util.Base64;
|
11 |
+
|
12 |
+
@Service
|
13 |
+
@Slf4j
|
14 |
+
public class BasicEncryptionServiceImpl implements IEncryptionService {
|
15 |
+
|
16 |
+
private static final String ALGORITHM = "AES";
|
17 |
+
private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding"; // ECB is simple but less secure for some uses.
|
18 |
+
|
19 |
+
@Value("${app.encryption.key:DefaultTestEncrKey}") // 16, 24, or 32 bytes for AES-128, AES-192, or AES-256. IMPORTANT: Store securely, not hardcoded/defaulted in prod.
|
20 |
+
private String encryptionKeyString;
|
21 |
+
|
22 |
+
private SecretKeySpec getSecretKeySpec() {
|
23 |
+
// Ensure the key is the correct length (e.g., 16 bytes for AES-128)
|
24 |
+
byte[] keyBytes = new byte[16];
|
25 |
+
byte[] providedKeyBytes = encryptionKeyString.getBytes(StandardCharsets.UTF_8);
|
26 |
+
System.arraycopy(providedKeyBytes, 0, keyBytes, 0, Math.min(providedKeyBytes.length, keyBytes.length));
|
27 |
+
return new SecretKeySpec(keyBytes, ALGORITHM);
|
28 |
+
}
|
29 |
+
|
30 |
+
@Override
|
31 |
+
public String encrypt(String data) {
|
32 |
+
if (data == null) return null;
|
33 |
+
try {
|
34 |
+
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
35 |
+
cipher.init(Cipher.ENCRYPT_MODE, getSecretKeySpec());
|
36 |
+
byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
|
37 |
+
return Base64.getEncoder().encodeToString(encryptedBytes);
|
38 |
+
} catch (Exception e) {
|
39 |
+
log.error("Error encrypting data", e);
|
40 |
+
// In a real app, throw a specific EncryptionException
|
41 |
+
throw new RuntimeException("Error encrypting data", e);
|
42 |
+
}
|
43 |
+
}
|
44 |
+
|
45 |
+
@Override
|
46 |
+
public String decrypt(String encryptedData) {
|
47 |
+
if (encryptedData == null) return null;
|
48 |
+
try {
|
49 |
+
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
50 |
+
cipher.init(Cipher.DECRYPT_MODE, getSecretKeySpec());
|
51 |
+
byte[] originalBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
|
52 |
+
return new String(originalBytes, StandardCharsets.UTF_8);
|
53 |
+
} catch (Exception e) {
|
54 |
+
log.error("Error decrypting data", e);
|
55 |
+
// In a real app, throw a specific EncryptionException or return null/empty based on policy
|
56 |
+
throw new RuntimeException("Error decrypting data", e);
|
57 |
+
}
|
58 |
+
}
|
59 |
+
}
|
src/main/java/com/dalab/adminservice/service/impl/CloudConnectionServiceImpl.java
ADDED
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.service.impl;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.CloudConnectionDTO;
|
4 |
+
import com.dalab.adminservice.dto.CloudConnectionTestResultDTO;
|
5 |
+
import com.dalab.adminservice.exception.ConflictException;
|
6 |
+
import com.dalab.adminservice.exception.NotFoundException;
|
7 |
+
import com.dalab.adminservice.mapper.CloudConnectionMapper;
|
8 |
+
import com.dalab.adminservice.model.CloudConnectionEntity;
|
9 |
+
import com.dalab.adminservice.repository.CloudConnectionRepository;
|
10 |
+
import com.dalab.adminservice.service.ICloudConnectionService;
|
11 |
+
import com.dalab.adminservice.service.IEncryptionService;
|
12 |
+
import lombok.RequiredArgsConstructor;
|
13 |
+
import lombok.extern.slf4j.Slf4j;
|
14 |
+
import org.springframework.stereotype.Service;
|
15 |
+
import org.springframework.transaction.annotation.Transactional;
|
16 |
+
import org.springframework.util.StringUtils;
|
17 |
+
|
18 |
+
import java.time.LocalDateTime;
|
19 |
+
import java.time.format.DateTimeFormatter;
|
20 |
+
import java.util.List;
|
21 |
+
import java.util.Optional;
|
22 |
+
|
23 |
+
@Service
|
24 |
+
@RequiredArgsConstructor
|
25 |
+
@Slf4j
|
26 |
+
public class CloudConnectionServiceImpl implements ICloudConnectionService {
|
27 |
+
|
28 |
+
private final CloudConnectionRepository cloudConnectionRepository;
|
29 |
+
private final CloudConnectionMapper cloudConnectionMapper;
|
30 |
+
private final IEncryptionService encryptionService; // For encrypting/decrypting sensitiveCredentials
|
31 |
+
|
32 |
+
@Override
|
33 |
+
@Transactional(readOnly = true)
|
34 |
+
public List<CloudConnectionDTO> getAllCloudConnections() {
|
35 |
+
log.debug("Fetching all cloud connections");
|
36 |
+
return cloudConnectionMapper.toDtoList(cloudConnectionRepository.findAll());
|
37 |
+
}
|
38 |
+
|
39 |
+
@Override
|
40 |
+
@Transactional(readOnly = true)
|
41 |
+
public Optional<CloudConnectionDTO> getCloudConnectionById(String id) {
|
42 |
+
log.debug("Fetching cloud connection by ID: {}", id);
|
43 |
+
return cloudConnectionRepository.findById(id)
|
44 |
+
.map(cloudConnectionMapper::toDto);
|
45 |
+
}
|
46 |
+
|
47 |
+
@Override
|
48 |
+
@Transactional
|
49 |
+
public CloudConnectionDTO createCloudConnection(CloudConnectionDTO cloudConnectionDTO) {
|
50 |
+
log.info("Creating new cloud connection with name: {}", cloudConnectionDTO.getName());
|
51 |
+
cloudConnectionRepository.findByName(cloudConnectionDTO.getName()).ifPresent(entity -> {
|
52 |
+
throw new ConflictException("Cloud connection with name '" + cloudConnectionDTO.getName() + "' already exists.");
|
53 |
+
});
|
54 |
+
|
55 |
+
CloudConnectionEntity entity = cloudConnectionMapper.toEntity(cloudConnectionDTO);
|
56 |
+
if (StringUtils.hasText(cloudConnectionDTO.getSensitiveCredentials())) {
|
57 |
+
entity.setEncryptedCredentials(encryptionService.encrypt(cloudConnectionDTO.getSensitiveCredentials()));
|
58 |
+
}
|
59 |
+
CloudConnectionEntity savedEntity = cloudConnectionRepository.save(entity);
|
60 |
+
return cloudConnectionMapper.toDto(savedEntity);
|
61 |
+
}
|
62 |
+
|
63 |
+
@Override
|
64 |
+
@Transactional
|
65 |
+
public CloudConnectionDTO updateCloudConnection(String id, CloudConnectionDTO cloudConnectionDTO) {
|
66 |
+
log.info("Updating cloud connection with ID: {}", id);
|
67 |
+
CloudConnectionEntity existingEntity = cloudConnectionRepository.findById(id)
|
68 |
+
.orElseThrow(() -> new NotFoundException("Cloud connection with ID '" + id + "' not found."));
|
69 |
+
|
70 |
+
// Check if name is being changed and if the new name already exists for another entity
|
71 |
+
if (StringUtils.hasText(cloudConnectionDTO.getName()) && !existingEntity.getName().equals(cloudConnectionDTO.getName())) {
|
72 |
+
cloudConnectionRepository.findByName(cloudConnectionDTO.getName()).ifPresent(entity -> {
|
73 |
+
if (!entity.getId().equals(id)) { // If it's not the same entity
|
74 |
+
throw new ConflictException("Cloud connection with name '" + cloudConnectionDTO.getName() + "' already exists.");
|
75 |
+
}
|
76 |
+
});
|
77 |
+
}
|
78 |
+
|
79 |
+
cloudConnectionMapper.updateEntityFromDto(cloudConnectionDTO, existingEntity);
|
80 |
+
if (StringUtils.hasText(cloudConnectionDTO.getSensitiveCredentials())) {
|
81 |
+
log.debug("Updating encrypted credentials for cloud connection ID: {}", id);
|
82 |
+
existingEntity.setEncryptedCredentials(encryptionService.encrypt(cloudConnectionDTO.getSensitiveCredentials()));
|
83 |
+
} else {
|
84 |
+
// If sensitiveCredentials is null or empty in DTO, it means user doesn't want to update them.
|
85 |
+
// If user wants to clear them, an explicit mechanism should be used.
|
86 |
+
// For now, we just don't update if it's not provided.
|
87 |
+
}
|
88 |
+
|
89 |
+
CloudConnectionEntity updatedEntity = cloudConnectionRepository.save(existingEntity);
|
90 |
+
return cloudConnectionMapper.toDto(updatedEntity);
|
91 |
+
}
|
92 |
+
|
93 |
+
@Override
|
94 |
+
@Transactional
|
95 |
+
public void deleteCloudConnection(String id) {
|
96 |
+
log.info("Deleting cloud connection with ID: {}", id);
|
97 |
+
if (!cloudConnectionRepository.existsById(id)) {
|
98 |
+
throw new NotFoundException("Cloud connection with ID '" + id + "' not found.");
|
99 |
+
}
|
100 |
+
cloudConnectionRepository.deleteById(id);
|
101 |
+
}
|
102 |
+
|
103 |
+
@Override
|
104 |
+
@Transactional
|
105 |
+
public CloudConnectionTestResultDTO testCloudConnection(String id) {
|
106 |
+
log.info("Testing cloud connection with ID: {}", id);
|
107 |
+
CloudConnectionEntity entity = cloudConnectionRepository.findById(id)
|
108 |
+
.orElseThrow(() -> new NotFoundException("Cloud connection with ID '" + id + "' not found for testing."));
|
109 |
+
|
110 |
+
// Placeholder for actual connection testing logic
|
111 |
+
// This would involve:
|
112 |
+
// 1. Decrypting entity.getEncryptedCredentials() using encryptionService.decrypt()
|
113 |
+
// 2. Using the decrypted credentials and entity.getConnectionParameters()
|
114 |
+
// to attempt a connection to the specified cloudProviderType (e.g., using GCP/AWS SDKs).
|
115 |
+
boolean testSuccess = false;
|
116 |
+
String message = "Test not implemented for provider: " + entity.getProviderType();
|
117 |
+
|
118 |
+
// Simulate a test
|
119 |
+
try {
|
120 |
+
String decryptedCreds = encryptionService.decrypt(entity.getEncryptedCredentials());
|
121 |
+
if (decryptedCreds != null && !decryptedCreds.isEmpty()) {
|
122 |
+
// Simulate success if credentials exist for the sake of example
|
123 |
+
message = "Simulated connection test successful for " + entity.getName();
|
124 |
+
testSuccess = true;
|
125 |
+
log.info("Simulated connection test successful for ID: {}. Decrypted (sample): {}", id, decryptedCreds.substring(0, Math.min(decryptedCreds.length(), 10)));
|
126 |
+
} else {
|
127 |
+
message = "Simulated connection test failed: No credentials to test for " + entity.getName();
|
128 |
+
log.warn("Simulated connection test failed for ID: {}. No credentials found.", id);
|
129 |
+
}
|
130 |
+
} catch (Exception e) {
|
131 |
+
log.error("Error during simulated connection test for ID: {}", id, e);
|
132 |
+
message = "Simulated connection test failed: " + e.getMessage();
|
133 |
+
}
|
134 |
+
|
135 |
+
entity.setLastConnectionTestAt(LocalDateTime.now());
|
136 |
+
entity.setLastConnectionTestStatus(testSuccess ? "SUCCESS" : "FAILED");
|
137 |
+
cloudConnectionRepository.save(entity);
|
138 |
+
|
139 |
+
return CloudConnectionTestResultDTO.builder()
|
140 |
+
.success(testSuccess)
|
141 |
+
.message(message)
|
142 |
+
.testedAt(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME))
|
143 |
+
.build();
|
144 |
+
}
|
145 |
+
|
146 |
+
@Override
|
147 |
+
@Transactional(readOnly = true)
|
148 |
+
public String getDecryptedCredentials(String id) {
|
149 |
+
log.debug("Retrieving decrypted credentials for cloud connection ID: {} (internal use)", id);
|
150 |
+
CloudConnectionEntity entity = cloudConnectionRepository.findById(id)
|
151 |
+
.orElseThrow(() -> new NotFoundException("Cloud connection with ID '" + id + "' not found."));
|
152 |
+
|
153 |
+
if (!StringUtils.hasText(entity.getEncryptedCredentials())) {
|
154 |
+
log.warn("No encrypted credentials found for cloud connection ID: {}", id);
|
155 |
+
return null;
|
156 |
+
}
|
157 |
+
try {
|
158 |
+
return encryptionService.decrypt(entity.getEncryptedCredentials());
|
159 |
+
} catch (Exception e) {
|
160 |
+
log.error("Failed to decrypt credentials for cloud connection ID: {}. Error: {}", id, e.getMessage());
|
161 |
+
// Depending on policy, you might re-throw, or return null, or a specific error marker.
|
162 |
+
// Throwing an exception is safer to indicate failure clearly.
|
163 |
+
throw new RuntimeException("Failed to decrypt credentials for ID: " + id, e);
|
164 |
+
}
|
165 |
+
}
|
166 |
+
}
|
src/main/java/com/dalab/adminservice/service/impl/JobStatusServiceImpl.java
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.service.impl;
|
2 |
+
|
3 |
+
import java.util.ArrayList;
|
4 |
+
import java.util.List;
|
5 |
+
|
6 |
+
import org.springframework.stereotype.Service;
|
7 |
+
|
8 |
+
import com.dalab.adminservice.client.IAutoarchivalTaskApiClient;
|
9 |
+
import com.dalab.adminservice.client.IAutocomplianceJobApiClient;
|
10 |
+
import com.dalab.adminservice.client.IAutodeleteTaskApiClient;
|
11 |
+
import com.dalab.adminservice.client.IAutolabelJobApiClient;
|
12 |
+
import com.dalab.adminservice.client.IDiscoveryJobApiClient;
|
13 |
+
import com.dalab.adminservice.dto.AggregatedJobStatusDTO;
|
14 |
+
import com.dalab.adminservice.dto.JobStatusDTO;
|
15 |
+
import com.dalab.adminservice.service.IJobStatusService;
|
16 |
+
|
17 |
+
import lombok.RequiredArgsConstructor;
|
18 |
+
import lombok.extern.slf4j.Slf4j;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Service implementation for aggregating job statuses from all DALab microservices
|
22 |
+
*/
|
23 |
+
@Service
|
24 |
+
@RequiredArgsConstructor
|
25 |
+
@Slf4j
|
26 |
+
public class JobStatusServiceImpl implements IJobStatusService {
|
27 |
+
|
28 |
+
private final IDiscoveryJobApiClient discoveryJobApiClient;
|
29 |
+
private final IAutolabelJobApiClient autolabelJobApiClient;
|
30 |
+
private final IAutoarchivalTaskApiClient autoarchivalTaskApiClient;
|
31 |
+
private final IAutodeleteTaskApiClient autodeleteTaskApiClient;
|
32 |
+
private final IAutocomplianceJobApiClient autocomplianceJobApiClient;
|
33 |
+
|
34 |
+
@Override
|
35 |
+
public AggregatedJobStatusDTO getAggregatedJobStatuses() {
|
36 |
+
log.debug("Aggregating job statuses from all DALab services");
|
37 |
+
|
38 |
+
List<JobStatusDTO> allJobs = new ArrayList<>();
|
39 |
+
|
40 |
+
// Aggregate jobs from all services with error handling
|
41 |
+
aggregateFromService("Discovery", () -> discoveryJobApiClient.getDiscoveryJobs(), allJobs);
|
42 |
+
aggregateFromService("Autolabel", () -> autolabelJobApiClient.getAutolabelJobs(), allJobs);
|
43 |
+
aggregateFromService("Autoarchival", () -> autoarchivalTaskApiClient.getArchivalTasks(), allJobs);
|
44 |
+
aggregateFromService("Autodelete", () -> autodeleteTaskApiClient.getDeletionTasks(), allJobs);
|
45 |
+
aggregateFromService("Autocompliance", () -> autocomplianceJobApiClient.getComplianceReportJobs(), allJobs);
|
46 |
+
|
47 |
+
return buildAggregatedResult(allJobs);
|
48 |
+
}
|
49 |
+
|
50 |
+
private void aggregateFromService(String serviceName, ServiceJobProvider provider, List<JobStatusDTO> allJobs) {
|
51 |
+
try {
|
52 |
+
List<JobStatusDTO> serviceJobs = provider.getJobs();
|
53 |
+
if (serviceJobs != null) {
|
54 |
+
allJobs.addAll(serviceJobs);
|
55 |
+
log.debug("Aggregated {} jobs from {} service", serviceJobs.size(), serviceName);
|
56 |
+
}
|
57 |
+
} catch (Exception e) {
|
58 |
+
log.warn("Failed to retrieve jobs from {} service: {}", serviceName, e.getMessage());
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
62 |
+
private AggregatedJobStatusDTO buildAggregatedResult(List<JobStatusDTO> allJobs) {
|
63 |
+
int totalJobs = allJobs.size();
|
64 |
+
int pendingJobs = 0;
|
65 |
+
int runningJobs = 0;
|
66 |
+
int completedSuccessJobs = 0;
|
67 |
+
int completedFailedJobs = 0;
|
68 |
+
|
69 |
+
for (JobStatusDTO job : allJobs) {
|
70 |
+
String status = job.getStatus();
|
71 |
+
if (status == null) continue;
|
72 |
+
|
73 |
+
switch (status.toUpperCase()) {
|
74 |
+
case "PENDING":
|
75 |
+
pendingJobs++;
|
76 |
+
break;
|
77 |
+
case "RUNNING":
|
78 |
+
case "IN_PROGRESS":
|
79 |
+
runningJobs++;
|
80 |
+
break;
|
81 |
+
case "COMPLETED_SUCCESS":
|
82 |
+
case "COMPLETED":
|
83 |
+
case "SUCCESS":
|
84 |
+
completedSuccessJobs++;
|
85 |
+
break;
|
86 |
+
case "COMPLETED_FAILED":
|
87 |
+
case "FAILED":
|
88 |
+
case "ERROR":
|
89 |
+
completedFailedJobs++;
|
90 |
+
break;
|
91 |
+
}
|
92 |
+
}
|
93 |
+
|
94 |
+
return AggregatedJobStatusDTO.builder()
|
95 |
+
.totalJobs(totalJobs)
|
96 |
+
.pendingJobs(pendingJobs)
|
97 |
+
.runningJobs(runningJobs)
|
98 |
+
.completedSuccessJobs(completedSuccessJobs)
|
99 |
+
.completedFailedJobs(completedFailedJobs)
|
100 |
+
.jobs(allJobs)
|
101 |
+
.build();
|
102 |
+
}
|
103 |
+
|
104 |
+
@FunctionalInterface
|
105 |
+
private interface ServiceJobProvider {
|
106 |
+
List<JobStatusDTO> getJobs();
|
107 |
+
}
|
108 |
+
}
|
src/main/java/com/dalab/adminservice/service/impl/RoleServiceImpl.java
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package com.dalab.adminservice.service.impl;
|
2 |
+
|
3 |
+
import com.dalab.adminservice.dto.RoleDTO;
|
4 |
+
import com.dalab.adminservice.exception.KeycloakAdminException;
|
5 |
+
import com.dalab.adminservice.mapper.RoleMapper;
|
6 |
+
import com.dalab.adminservice.service.IRoleService;
|
7 |
+
import jakarta.ws.rs.WebApplicationException;
|
8 |
+
import lombok.RequiredArgsConstructor;
|
9 |
+
import lombok.extern.slf4j.Slf4j;
|
10 |
+
import org.keycloak.admin.client.Keycloak;
|
11 |
+
import org.keycloak.admin.client.resource.RealmResource;
|
12 |
+
import org.keycloak.representations.idm.RoleRepresentation;
|
13 |
+
import org.springframework.beans.factory.annotation.Value;
|
14 |
+
import org.springframework.stereotype.Service;
|
15 |
+
|
16 |
+
import java.util.List;
|
17 |
+
|
18 |
+
@Service
|
19 |
+
@RequiredArgsConstructor
|
20 |
+
@Slf4j
|
21 |
+
public class RoleServiceImpl implements IRoleService {
|
22 |
+
|
23 |
+
private final Keycloak keycloakAdminClient;
|
24 |
+
private final RoleMapper roleMapper;
|
25 |
+
|
26 |
+
@Value("${keycloak.realm}")
|
27 |
+
private String realmName;
|
28 |
+
|
29 |
+
private RealmResource getRealmResource() {
|
30 |
+
return keycloakAdminClient.realm(realmName);
|
31 |
+
}
|
32 |
+
|
33 |
+
@Override
|
34 |
+
public List<RoleDTO> getAllRealmRoles() {
|
35 |
+
log.debug("Fetching all available realm roles from Keycloak realm: {}", realmName);
|
36 |
+
try {
|
37 |
+
List<RoleRepresentation> roleRepresentations = getRealmResource().roles().list();
|
38 |
+
return roleMapper.toDtoList(roleRepresentations);
|
39 |
+
} catch (WebApplicationException e) {
|
40 |
+
log.error("Keycloak admin error fetching available realm roles: {}", e.getMessage(), e);
|
41 |
+
throw new KeycloakAdminException("Error fetching available realm roles: " + e.getMessage(), e);
|
42 |
+
}
|
43 |
+
}
|
44 |
+
}
|