From d36b5847c3cb8db5171a487b8e61277be5422a30 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Wed, 18 Feb 2026 18:07:53 +0300 Subject: [PATCH 1/2] feat: add endpoint to retrieve application metadata including sync schedule --- .../qubership/colly/InventoryServiceRest.java | 57 ++++++++++++++++++- .../colly/dto/ApplicationMetadataDto.java | 14 +++++ .../qubership/InventoryServiceRestTest.java | 18 ++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 envgene-inventory-service/src/main/java/org/qubership/colly/dto/ApplicationMetadataDto.java diff --git a/envgene-inventory-service/src/main/java/org/qubership/colly/InventoryServiceRest.java b/envgene-inventory-service/src/main/java/org/qubership/colly/InventoryServiceRest.java index 3d7f8913..c053bf3d 100644 --- a/envgene-inventory-service/src/main/java/org/qubership/colly/InventoryServiceRest.java +++ b/envgene-inventory-service/src/main/java/org/qubership/colly/InventoryServiceRest.java @@ -7,6 +7,7 @@ import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; +import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.media.Content; import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; @@ -32,14 +33,18 @@ public class InventoryServiceRest { private final CollyStorage collyStorage; private final SecurityIdentity securityIdentity; private final DtoMapper dtoMapper; + private final String syncCronSchedule; + @Inject public InventoryServiceRest(CollyStorage collyStorage, SecurityIdentity securityIdentity, - DtoMapper dtoMapper) { + DtoMapper dtoMapper, + @ConfigProperty(name = "colly.eis.cron.schedule") String syncCronSchedule) { this.collyStorage = collyStorage; this.securityIdentity = securityIdentity; this.dtoMapper = dtoMapper; + this.syncCronSchedule = syncCronSchedule; } @GET @@ -736,5 +741,55 @@ public Response getAuthStatus() { return Response.ok(userInfo).build(); } + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/metadata") + @Operation( + summary = "Get application metadata", + description = "Retrieves application metadata including synchronization schedule. Requires authentication." + ) + @APIResponses({ + @APIResponse( + responseCode = "200", + description = "Successfully retrieved application metadata", + content = @Content( + mediaType = MediaType.APPLICATION_JSON, + schema = @Schema(implementation = ApplicationMetadataDto.class), + examples = @ExampleObject( + name = "metadata-response", + summary = "Example metadata response", + value = """ + { + syncSchedule: "0 * * * * ?" + } + """ + ) + ) + ), + @APIResponse( + responseCode = "401", + description = "Unauthorized - authentication required", + content = @Content( + mediaType = MediaType.APPLICATION_JSON, + examples = @ExampleObject( + value = "{\"error\": \"Authentication required\"}" + ) + ) + ), + @APIResponse( + responseCode = "500", + description = "Internal server error", + content = @Content( + mediaType = MediaType.APPLICATION_JSON, + examples = @ExampleObject( + value = "{\"error\": \"Internal server error occurred\"}" + ) + ) + ) + }) + public ApplicationMetadataDto getMetadata() { + return new ApplicationMetadataDto(syncCronSchedule); + } + } diff --git a/envgene-inventory-service/src/main/java/org/qubership/colly/dto/ApplicationMetadataDto.java b/envgene-inventory-service/src/main/java/org/qubership/colly/dto/ApplicationMetadataDto.java new file mode 100644 index 00000000..246903ff --- /dev/null +++ b/envgene-inventory-service/src/main/java/org/qubership/colly/dto/ApplicationMetadataDto.java @@ -0,0 +1,14 @@ +package org.qubership.colly.dto; + +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +@Schema(description = "Application metadata including sync info") +public record ApplicationMetadataDto( + @Schema( + description = "Cron expression defining the schedule for synchronization with Projects Git", + examples = "0 * * * * ?", + required = true + ) + String syncSchedule +) { +} diff --git a/envgene-inventory-service/src/test/java/org/qubership/InventoryServiceRestTest.java b/envgene-inventory-service/src/test/java/org/qubership/InventoryServiceRestTest.java index 47660cbd..0619df92 100644 --- a/envgene-inventory-service/src/test/java/org/qubership/InventoryServiceRestTest.java +++ b/envgene-inventory-service/src/test/java/org/qubership/InventoryServiceRestTest.java @@ -621,4 +621,22 @@ void update_environment_with_empty_fields() { .body("find { it.name == 'env-metadata-test' }.owners", emptyIterable()) .body("find { it.name == 'env-metadata-test' }.expirationDate", nullValue()); } + + @Test + void get_metadata_without_auth() { + given() + .when().get("/colly/v2/inventory-service/metadata") + .then() + .statusCode(401); + } + + @Test + @TestSecurity(user = "test") + void get_metadata() { + given() + .when().get("/colly/v2/inventory-service/metadata") + .then() + .statusCode(200) + .body("syncSchedule", equalTo("0 0 0 1 1 ? 2020")); + } } From 3b1825b8aaaaa1d05a85a16a687bfaafda68bf70 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Thu, 19 Feb 2026 16:46:59 +0300 Subject: [PATCH 2/2] feat: add lastProjectSyncAt to application metadata --- .../org/qubership/colly/CollyStorage.java | 6 ++++ .../qubership/colly/InventoryServiceRest.java | 6 +++- .../colly/db/SyncInfoRepository.java | 33 +++++++++++++++++++ .../colly/dto/ApplicationMetadataDto.java | 9 ++++- .../qubership/InventoryServiceRestTest.java | 7 +++- 5 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 envgene-inventory-service/src/main/java/org/qubership/colly/db/SyncInfoRepository.java diff --git a/envgene-inventory-service/src/main/java/org/qubership/colly/CollyStorage.java b/envgene-inventory-service/src/main/java/org/qubership/colly/CollyStorage.java index 75d4a46c..77f27718 100644 --- a/envgene-inventory-service/src/main/java/org/qubership/colly/CollyStorage.java +++ b/envgene-inventory-service/src/main/java/org/qubership/colly/CollyStorage.java @@ -11,6 +11,7 @@ import org.qubership.colly.db.ClusterRepository; import org.qubership.colly.db.EnvironmentRepository; import org.qubership.colly.db.ProjectRepository; +import org.qubership.colly.db.SyncInfoRepository; import org.qubership.colly.db.data.Cluster; import org.qubership.colly.db.data.Environment; import org.qubership.colly.db.data.Namespace; @@ -18,6 +19,7 @@ import org.qubership.colly.projectrepo.Project; import org.qubership.colly.projectrepo.ProjectRepoLoader; +import java.time.Instant; import java.util.Comparator; import java.util.List; import java.util.UUID; @@ -28,6 +30,7 @@ public class CollyStorage { private final ClusterRepository clusterRepository; private final EnvironmentRepository environmentRepository; private final ProjectRepository projectRepository; + private final SyncInfoRepository syncInfoRepository; private final CloudPassportLoader cloudPassportLoader; private final UpdateEnvironmentService updateEnvironmentService; private final ProjectRepoLoader projectRepoLoader; @@ -36,10 +39,12 @@ public class CollyStorage { public CollyStorage( ClusterRepository clusterRepository, EnvironmentRepository environmentRepository, ProjectRepository projectRepository, + SyncInfoRepository syncInfoRepository, CloudPassportLoader cloudPassportLoader, UpdateEnvironmentService updateEnvironmentService, ProjectRepoLoader projectRepoLoader) { this.clusterRepository = clusterRepository; this.environmentRepository = environmentRepository; this.projectRepository = projectRepository; + this.syncInfoRepository = syncInfoRepository; this.cloudPassportLoader = cloudPassportLoader; this.updateEnvironmentService = updateEnvironmentService; this.projectRepoLoader = projectRepoLoader; @@ -50,6 +55,7 @@ void syncAll() { Log.info("Task for loading data from git has started"); List projects = projectRepoLoader.loadProjects(); projects.forEach(projectRepository::persist); + syncInfoRepository.saveLastProjectSyncAt(Instant.now()); Log.info("Projects loaded: " + projects.size()); List cloudPassports = cloudPassportLoader.loadCloudPassports(projects); Log.info("Cloud passports loaded: " + cloudPassports.size()); diff --git a/envgene-inventory-service/src/main/java/org/qubership/colly/InventoryServiceRest.java b/envgene-inventory-service/src/main/java/org/qubership/colly/InventoryServiceRest.java index c053bf3d..19fa28fa 100644 --- a/envgene-inventory-service/src/main/java/org/qubership/colly/InventoryServiceRest.java +++ b/envgene-inventory-service/src/main/java/org/qubership/colly/InventoryServiceRest.java @@ -17,6 +17,7 @@ import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; +import org.qubership.colly.db.SyncInfoRepository; import org.qubership.colly.db.data.Cluster; import org.qubership.colly.db.data.Environment; import org.qubership.colly.dto.*; @@ -33,6 +34,7 @@ public class InventoryServiceRest { private final CollyStorage collyStorage; private final SecurityIdentity securityIdentity; private final DtoMapper dtoMapper; + private final SyncInfoRepository syncInfoRepository; private final String syncCronSchedule; @@ -40,10 +42,12 @@ public class InventoryServiceRest { public InventoryServiceRest(CollyStorage collyStorage, SecurityIdentity securityIdentity, DtoMapper dtoMapper, + SyncInfoRepository syncInfoRepository, @ConfigProperty(name = "colly.eis.cron.schedule") String syncCronSchedule) { this.collyStorage = collyStorage; this.securityIdentity = securityIdentity; this.dtoMapper = dtoMapper; + this.syncInfoRepository = syncInfoRepository; this.syncCronSchedule = syncCronSchedule; } @@ -788,7 +792,7 @@ public Response getAuthStatus() { ) }) public ApplicationMetadataDto getMetadata() { - return new ApplicationMetadataDto(syncCronSchedule); + return new ApplicationMetadataDto(syncCronSchedule, syncInfoRepository.getLastProjectSyncAt()); } } diff --git a/envgene-inventory-service/src/main/java/org/qubership/colly/db/SyncInfoRepository.java b/envgene-inventory-service/src/main/java/org/qubership/colly/db/SyncInfoRepository.java new file mode 100644 index 00000000..0ae6e8f6 --- /dev/null +++ b/envgene-inventory-service/src/main/java/org/qubership/colly/db/SyncInfoRepository.java @@ -0,0 +1,33 @@ +package org.qubership.colly.db; + +import io.quarkus.redis.datasource.RedisDataSource; +import io.quarkus.redis.datasource.hash.HashCommands; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.time.Instant; + +@ApplicationScoped +public class SyncInfoRepository { + + private static final String SYNC_INFO_KEY = "inventory:sync-info"; + + @Inject + RedisDataSource redisDataSource; + + private HashCommands hashCommands() { + return redisDataSource.hash(String.class, String.class, String.class); + } + + public void saveLastProjectSyncAt(Instant instant) { + hashCommands().hset(SYNC_INFO_KEY, "lastProjectSyncAt", instant.toString()); + } + + public Instant getLastProjectSyncAt() { + String value = hashCommands().hget(SYNC_INFO_KEY, "lastProjectSyncAt"); + if (value == null) { + return null; + } + return Instant.parse(value); + } +} diff --git a/envgene-inventory-service/src/main/java/org/qubership/colly/dto/ApplicationMetadataDto.java b/envgene-inventory-service/src/main/java/org/qubership/colly/dto/ApplicationMetadataDto.java index 246903ff..0d905513 100644 --- a/envgene-inventory-service/src/main/java/org/qubership/colly/dto/ApplicationMetadataDto.java +++ b/envgene-inventory-service/src/main/java/org/qubership/colly/dto/ApplicationMetadataDto.java @@ -2,6 +2,8 @@ import org.eclipse.microprofile.openapi.annotations.media.Schema; +import java.time.Instant; + @Schema(description = "Application metadata including sync info") public record ApplicationMetadataDto( @Schema( @@ -9,6 +11,11 @@ public record ApplicationMetadataDto( examples = "0 * * * * ?", required = true ) - String syncSchedule + String syncSchedule, + @Schema( + description = "Timestamp of the last project repository synchronization", + examples = "2025-01-15T10:30:00Z" + ) + Instant lastProjectSyncAt ) { } diff --git a/envgene-inventory-service/src/test/java/org/qubership/InventoryServiceRestTest.java b/envgene-inventory-service/src/test/java/org/qubership/InventoryServiceRestTest.java index 0619df92..d4a07e8a 100644 --- a/envgene-inventory-service/src/test/java/org/qubership/InventoryServiceRestTest.java +++ b/envgene-inventory-service/src/test/java/org/qubership/InventoryServiceRestTest.java @@ -633,10 +633,15 @@ void get_metadata_without_auth() { @Test @TestSecurity(user = "test") void get_metadata() { + given() + .when().post("/colly/v2/inventory-service/manual-sync") + .then() + .statusCode(204); given() .when().get("/colly/v2/inventory-service/metadata") .then() .statusCode(200) - .body("syncSchedule", equalTo("0 0 0 1 1 ? 2020")); + .body("syncSchedule", equalTo("0 0 0 1 1 ? 2020")) + .body("lastProjectSyncAt", notNullValue()); } }