From 72b84e9ccd105dda07b3c5391c87eb66a6253e0b Mon Sep 17 00:00:00 2001 From: Nikos Kyriakoulis Date: Fri, 2 May 2025 16:04:18 +0300 Subject: [PATCH 1/7] Created metrics endpoints for TMP Resource related information --- .../tmf/metrics/api/ResourceMetricsApi.java | 47 ++++ .../api/ResourceMetricsApiController.java | 85 +++++++ .../ResourceMetricsRepoService.java | 46 ++++ .../tmf/ri639/repo/ResourceRepository.java | 16 ++ .../ResourceMetricsApiControllerTest.java | 240 ++++++++++++++++++ 5 files changed, 434 insertions(+) create mode 100644 src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java create mode 100644 src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java create mode 100644 src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java create mode 100644 src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java new file mode 100644 index 00000000..7cf08736 --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java @@ -0,0 +1,47 @@ +package org.etsi.osl.tmf.metrics.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.etsi.osl.tmf.ri639.model.ResourceStatusType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import java.time.OffsetDateTime; +import java.util.Map; + +@Tag(name = "ResourceMetricsApi") +public interface ResourceMetricsApi { + + Logger log = LoggerFactory.getLogger(ResourceMetricsApi.class); + + @Operation(summary = "Get total number of resources", operationId = "getTotalResources") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success"), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + }) + @RequestMapping(value = "/tmf-api/metrics/totalResources", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + ResponseEntity> getTotalResources( + @Valid @RequestParam(value = "state", required = false) ResourceStatusType state + ); + + @Operation(summary = "Get resources grouped by state", operationId = "getResourcesGroupedByState") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success"), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + }) + @RequestMapping(value = "/tmf-api/metrics/resourcesGroupByState", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + ResponseEntity> getResourcesGroupedByState( + @Valid @RequestParam(value = "starttime", required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime starttime, + @Valid @RequestParam(value = "endtime", required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime endtime + ); +} diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java new file mode 100644 index 00000000..14be245c --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java @@ -0,0 +1,85 @@ +package org.etsi.osl.tmf.metrics.api; + +import org.etsi.osl.tmf.metrics.reposervices.ResourceMetricsRepoService; +import org.etsi.osl.tmf.ri639.model.ResourceStatusType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; + +import java.time.OffsetDateTime; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Controller +public class ResourceMetricsApiController implements ResourceMetricsApi{ + + private static final Logger log = LoggerFactory.getLogger(ResourceMetricsApiController.class); + private final ResourceMetricsRepoService resourceMetricsRepoService; + + @Autowired + public ResourceMetricsApiController(ResourceMetricsRepoService resourceMetricsRepoService) { + this.resourceMetricsRepoService = resourceMetricsRepoService; + } + + @Override + public ResponseEntity> getTotalResources(ResourceStatusType state) { + try { + int totalResources = resourceMetricsRepoService.countTotalResources(state); + Map response = new HashMap<>(); + response.put("totalResources", totalResources); + return new ResponseEntity<>(response, HttpStatus.OK); + } catch (Exception e) { + log.error("Couldn't retrieve total resources. ", e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + public ResponseEntity> getResourcesGroupedByState(OffsetDateTime starttime, OffsetDateTime endtime) { + try { + Map resourcesByState = resourceMetricsRepoService.getResourcesGroupedByState(starttime, endtime); + + // Initialize with all possible states and 0. Ensures that all states are represented, even if not present in the data. + Map fullStateMap = new LinkedHashMap<>(); + for (ResourceStatusType state : ResourceStatusType.values()) { + fullStateMap.put(state.name(), 0); // default to 0 + } + + // Overwrite counts with actual data + resourcesByState.forEach((key, value) -> { + fullStateMap.put(key.toUpperCase(), value); // normalize case just in case + }); + + // Build groupByState list + List> groupByStateList = fullStateMap.entrySet().stream() + .map(entry -> { + Map map = new HashMap<>(); + map.put("key", entry.getKey()); + map.put("count", entry.getValue()); + return map; + }) + .toList(); + + + // Wrap in response structure + Map aggregations = Map.of("groupByState", groupByStateList); + Map services = Map.of( + "total", fullStateMap.values().stream().mapToInt(Integer::intValue).sum(), + "aggregations", aggregations + ); + + Map response = Map.of("resources", services); + return new ResponseEntity<>(response, HttpStatus.OK); + + } catch (Exception e) { + log.error("Couldn't retrieve resources grouped by state. ", e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + +} diff --git a/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java new file mode 100644 index 00000000..70feca73 --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java @@ -0,0 +1,46 @@ +package org.etsi.osl.tmf.metrics.reposervices; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.tmf.ri639.model.ResourceStatusType; +import org.etsi.osl.tmf.ri639.repo.ResourceRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +@Service +public class ResourceMetricsRepoService { + + @Autowired + ObjectMapper objectMapper; + + @Autowired + ResourceRepository resourceRepository; + + public int countTotalResources(ResourceStatusType state) { + if (state == null) { + return resourceRepository.countAll(); + } else { + return resourceRepository.countByResourceStatus(state); + } + } + + public Map getResourcesGroupedByState(OffsetDateTime starttime, OffsetDateTime endtime) { + if (starttime.plusDays(31).isBefore(endtime)) { + starttime = endtime.minusDays(31); + } + + List rawResults = resourceRepository.groupByStateBetweenDates(starttime, endtime); + + return rawResults.stream() + .collect(Collectors.toMap( + row -> row[0].toString(), + row -> ((Number) row[1]).intValue() + )); + } + +} diff --git a/src/main/java/org/etsi/osl/tmf/ri639/repo/ResourceRepository.java b/src/main/java/org/etsi/osl/tmf/ri639/repo/ResourceRepository.java index 446a04ca..c324cad9 100644 --- a/src/main/java/org/etsi/osl/tmf/ri639/repo/ResourceRepository.java +++ b/src/main/java/org/etsi/osl/tmf/ri639/repo/ResourceRepository.java @@ -19,9 +19,11 @@ */ package org.etsi.osl.tmf.ri639.repo; +import java.time.OffsetDateTime; import java.util.List; import java.util.Optional; import org.etsi.osl.tmf.ri639.model.Resource; +import org.etsi.osl.tmf.ri639.model.ResourceStatusType; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @@ -56,4 +58,18 @@ public interface ResourceRepository extends JpaRepository { List findByNameAndResourceVersion(String aname, String aversion); List findByNameAndCategoryAndResourceVersion(String aname, String acategory, String aversion); + + + // Methods for metrics + + @Query("SELECT COUNT(res) FROM RIResource res") + int countAll(); + + int countByResourceStatus(ResourceStatusType status); + + @Query("SELECT res.resourceStatus, COUNT(res) FROM RIResource res " + + "WHERE res.startOperatingDate >= :starttime AND res.endOperatingDate <= :endtime " + + "GROUP BY res.resourceStatus") + List groupByStateBetweenDates(OffsetDateTime starttime, OffsetDateTime endtime); + } diff --git a/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java b/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java new file mode 100644 index 00000000..406580fe --- /dev/null +++ b/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java @@ -0,0 +1,240 @@ +package org.etsi.osl.services.api.metrics; + +import com.jayway.jsonpath.JsonPath; +import org.apache.commons.io.IOUtils; +import org.etsi.osl.tmf.JsonUtils; +import org.etsi.osl.tmf.OpenAPISpringBoot; +import org.etsi.osl.tmf.common.model.Any; +import org.etsi.osl.tmf.common.model.UserPartRoleType; +import org.etsi.osl.tmf.common.model.service.Note; +import org.etsi.osl.tmf.common.model.service.ResourceRef; +import org.etsi.osl.tmf.prm669.model.RelatedParty; +import org.etsi.osl.tmf.rcm634.model.*; +import org.etsi.osl.tmf.ri639.model.*; +import org.etsi.osl.tmf.ri639.reposervices.ResourceRepoService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.URI; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@Transactional +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.MOCK, + classes = OpenAPISpringBoot.class +) +//@AutoConfigureTestDatabase //this automatically uses h2 +@AutoConfigureMockMvc +@ActiveProfiles("testing") +//@TestPropertySource( +// locations = "classpath:application-testing.yml") +public class ResourceMetricsApiControllerTest { + + @Autowired + private MockMvc mvc; + + @Autowired + ResourceRepoService resourceRepoService; + + @Autowired + private WebApplicationContext context; + + @Before + public void setup() throws Exception { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .apply(springSecurity()) + .build(); + } + + @WithMockUser(username="osadmin", roles = {"ADMIN","USER"}) + @Test + public void testCountTotalResources() throws Exception { + createResource(ResourceStatusType.AVAILABLE); + + String response = mvc.perform(MockMvcRequestBuilders.get("/tmf-api/metrics/totalResources" ) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk() ) + .andReturn().getResponse().getContentAsString(); + + int totalResources = JsonPath.read(response, "$.totalResources"); + + + assertThat(totalResources).isEqualTo(resourceRepoService.findAll().size()); + } + + @WithMockUser(username="osadmin", roles = {"ADMIN","USER"}) + @Test + public void testCountTotalServicesWithState() throws Exception { + createResource(ResourceStatusType.AVAILABLE); + createResource(ResourceStatusType.STANDBY); + + String response = mvc.perform(MockMvcRequestBuilders.get("/tmf-api/metrics/totalResources" ) + .param("state", "STANDBY") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk() ) + .andReturn().getResponse().getContentAsString(); + + int totalResources = JsonPath.read(response, "$.totalResources"); + + + List resourcesList = resourceRepoService.findAll(); + int standByResources = (int) resourcesList.stream().filter(resource -> resource.getResourceStatus() == ResourceStatusType.STANDBY).count(); + + assertThat(totalResources).isEqualTo(standByResources); + assertThat(totalResources).isEqualTo(1); + } + + @WithMockUser(username = "osadmin", roles = {"ADMIN", "USER"}) + @Test + public void testGetServicesGroupedByState() throws Exception { + String startTime = OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT); + + createResource(ResourceStatusType.AVAILABLE); + createResource(ResourceStatusType.AVAILABLE); + createResource(ResourceStatusType.AVAILABLE); + createResource(ResourceStatusType.RESERVED); + createResource(ResourceStatusType.RESERVED); + createResource(ResourceStatusType.STANDBY); + + String endTime = OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT); + + String response = mvc.perform(MockMvcRequestBuilders.get("/tmf-api/metrics/resourcesGroupByState") + .param("starttime", startTime) + .param("endtime", endTime) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + List> groupByState = JsonPath.read(response, "$.resources.aggregations.groupByState"); + + // Create a map from key -> count + Map stateCounts = groupByState.stream() + .collect(Collectors.toMap( + entry -> (String) entry.get("key"), + entry -> (Integer) entry.get("count") + )); + + assertThat(stateCounts.get("AVAILABLE")).isEqualTo(3); + assertThat(stateCounts.get("RESERVED")).isEqualTo(2); + assertThat(stateCounts.get("STANDBY")).isEqualTo(1); + assertThat(stateCounts.get("ALARM")).isEqualTo(0); + assertThat(stateCounts.get("UNKNOWN")).isEqualTo(0); + assertThat(stateCounts.get("SUSPENDED")).isEqualTo(0); + } + + private void createResource(ResourceStatusType statusType) throws Exception{ + + /** + * first add 2 specs + */ + + File sspec = new File( "src/test/resources/testResourceSpec.json" ); + InputStream in = new FileInputStream( sspec ); + String sspectext = IOUtils.toString(in, "UTF-8"); + + + ResourceSpecificationCreate sspeccr1 = JsonUtils.toJsonObj( sspectext, ResourceSpecificationCreate.class); + sspeccr1.setName("Spec1"); + ResourceSpecification responsesSpec1 = createResourceSpec( sspeccr1); + + //res 2 is an RFS + ResourceSpecificationCreate sspeccr2 = JsonUtils.toJsonObj( sspectext, ResourceSpecificationCreate.class); + sspeccr2.setName("Spec2"); + + sspeccr2.addResourceSpecificationRelationshipWith( responsesSpec1 ); + LogicalResourceSpecification responsesSpec2 = (LogicalResourceSpecification) createResourceSpec( sspeccr2 ); + /** + * add them as bundle + */ + + ResourceSpecificationCreate sspeccr3 = JsonUtils.toJsonObj( sspectext, ResourceSpecificationCreate.class); + sspeccr3.setName("BundleExampleSpec"); + sspeccr3.isBundle(true); + sspeccr3.addResourceSpecificationRelationshipWith( responsesSpec1 ); + sspeccr3.addResourceSpecificationRelationshipWith( responsesSpec2 ); + ResourceSpecification responsesSpec3 = createResourceSpec( sspeccr3); + + ResourceCreate aResource = new ResourceCreate(); + aResource.setName("aNew Resource parent"); + aResource.setCategory("Experimentation"); + aResource.setDescription("Experimentation Descr"); + aResource.setStartOperatingDate( OffsetDateTime.now(ZoneOffset.UTC ).toString() ); + aResource.setEndOperatingDate( OffsetDateTime.now(ZoneOffset.UTC ).toString() ); + aResource.setResourceStatus(statusType); + + + + Note noteItem = new Note(); + noteItem.text("test note"); + aResource.addNoteItem(noteItem); + + Characteristic resCharacteristicItem = new Characteristic(); + + resCharacteristicItem.setName( "externalPartnerServiceId" ); + resCharacteristicItem.setValue( new Any("NONE")); + aResource.addResourceCharacteristicItem(resCharacteristicItem); + + ResourceSpecificationRef aServiceSpecificationRef = new ResourceSpecificationRef(); + aServiceSpecificationRef.setId(responsesSpec3.getId() ); + aServiceSpecificationRef.setName(responsesSpec3.getName()); + + aResource.setResourceSpecification( aServiceSpecificationRef ); + + resourceRepoService.addResource(aResource); + } + + + private ResourceSpecification createResourceSpec(ResourceSpecificationUpdate sspeccr1) throws Exception{ + + URI url = new URI("/resourceCatalogManagement/v4/resourceSpecification"); + if (sspeccr1 instanceof PhysicalResourceSpecificationUpdate ) { + url = new URI("/resourceCatalogManagement/v4/resourceSpecification"); + } + String responseSpec = mvc.perform(MockMvcRequestBuilders.post( url ) + .with( SecurityMockMvcRequestPostProcessors.csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content( JsonUtils.toJson( sspeccr1 ) )) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + ResourceSpecification responsesSpec1; + if (sspeccr1 instanceof PhysicalResourceSpecificationUpdate ) { + responsesSpec1 = JsonUtils.toJsonObj(responseSpec, PhysicalResourceSpecification.class); + }else { + responsesSpec1 = JsonUtils.toJsonObj(responseSpec, LogicalResourceSpecification.class); + } + + return responsesSpec1; + } +} -- GitLab From 93b54c473c5560112aa15a6eb147b26b3d7af5fa Mon Sep 17 00:00:00 2001 From: Nikos Kyriakoulis Date: Tue, 6 May 2025 17:21:13 +0300 Subject: [PATCH 2/7] Removed duplicate 'tmf-api' prefix in metrics API endpoints --- .../java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java index 7cf08736..e02ecf42 100644 --- a/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java @@ -28,7 +28,7 @@ public interface ResourceMetricsApi { @ApiResponse(responseCode = "400", description = "Bad Request"), @ApiResponse(responseCode = "500", description = "Internal Server Error") }) - @RequestMapping(value = "/tmf-api/metrics/totalResources", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + @RequestMapping(value = "/metrics/totalResources", method = RequestMethod.GET, produces = "application/json;charset=utf-8") ResponseEntity> getTotalResources( @Valid @RequestParam(value = "state", required = false) ResourceStatusType state ); @@ -39,7 +39,7 @@ public interface ResourceMetricsApi { @ApiResponse(responseCode = "400", description = "Bad Request"), @ApiResponse(responseCode = "500", description = "Internal Server Error") }) - @RequestMapping(value = "/tmf-api/metrics/resourcesGroupByState", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + @RequestMapping(value = "/metrics/resourcesGroupByState", method = RequestMethod.GET, produces = "application/json;charset=utf-8") ResponseEntity> getResourcesGroupedByState( @Valid @RequestParam(value = "starttime", required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime starttime, @Valid @RequestParam(value = "endtime", required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime endtime -- GitLab From 47ee8a05e1f354980b2407240310c53572ce1d6f Mon Sep 17 00:00:00 2001 From: Nikos Kyriakoulis Date: Tue, 6 May 2025 17:41:17 +0300 Subject: [PATCH 3/7] Removed duplicate 'tmf-api' prefix in unit tests --- .../api/metrics/ResourceMetricsApiControllerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java b/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java index 406580fe..5d99d776 100644 --- a/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java +++ b/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java @@ -80,7 +80,7 @@ public class ResourceMetricsApiControllerTest { public void testCountTotalResources() throws Exception { createResource(ResourceStatusType.AVAILABLE); - String response = mvc.perform(MockMvcRequestBuilders.get("/tmf-api/metrics/totalResources" ) + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/totalResources" ) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk() ) .andReturn().getResponse().getContentAsString(); @@ -97,7 +97,7 @@ public class ResourceMetricsApiControllerTest { createResource(ResourceStatusType.AVAILABLE); createResource(ResourceStatusType.STANDBY); - String response = mvc.perform(MockMvcRequestBuilders.get("/tmf-api/metrics/totalResources" ) + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/totalResources" ) .param("state", "STANDBY") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk() ) @@ -127,7 +127,7 @@ public class ResourceMetricsApiControllerTest { String endTime = OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT); - String response = mvc.perform(MockMvcRequestBuilders.get("/tmf-api/metrics/resourcesGroupByState") + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/resourcesGroupByState") .param("starttime", startTime) .param("endtime", endTime) .contentType(MediaType.APPLICATION_JSON)) -- GitLab From b4e7250e877c1346706f3addb330a42c6cec2704 Mon Sep 17 00:00:00 2001 From: Nikos Kyriakoulis Date: Wed, 7 May 2025 16:20:03 +0300 Subject: [PATCH 4/7] Changed structure to gather all metrics endpoints under one controller --- .../api/{ResourceMetricsApi.java => MetricsApi.java} | 6 +++--- ...icsApiController.java => MetricsApiController.java} | 10 +++++----- ...MetricsRepoService.java => MetricsRepoService.java} | 2 +- ...ntrollerTest.java => MetricsApiControllerTest.java} | 5 +---- 4 files changed, 10 insertions(+), 13 deletions(-) rename src/main/java/org/etsi/osl/tmf/metrics/api/{ResourceMetricsApi.java => MetricsApi.java} (94%) rename src/main/java/org/etsi/osl/tmf/metrics/api/{ResourceMetricsApiController.java => MetricsApiController.java} (88%) rename src/main/java/org/etsi/osl/tmf/metrics/reposervices/{ResourceMetricsRepoService.java => MetricsRepoService.java} (96%) rename src/test/java/org/etsi/osl/services/api/metrics/{ResourceMetricsApiControllerTest.java => MetricsApiControllerTest.java} (97%) diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java b/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApi.java similarity index 94% rename from src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java rename to src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApi.java index e02ecf42..ee5dada7 100644 --- a/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApi.java @@ -17,10 +17,10 @@ import org.springframework.web.bind.annotation.RequestParam; import java.time.OffsetDateTime; import java.util.Map; -@Tag(name = "ResourceMetricsApi") -public interface ResourceMetricsApi { +@Tag(name = "MetricsApi") +public interface MetricsApi { - Logger log = LoggerFactory.getLogger(ResourceMetricsApi.class); + Logger log = LoggerFactory.getLogger(MetricsApi.class); @Operation(summary = "Get total number of resources", operationId = "getTotalResources") @ApiResponses(value = { diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java b/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java similarity index 88% rename from src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java rename to src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java index 14be245c..84c4f452 100644 --- a/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java @@ -1,6 +1,6 @@ package org.etsi.osl.tmf.metrics.api; -import org.etsi.osl.tmf.metrics.reposervices.ResourceMetricsRepoService; +import org.etsi.osl.tmf.metrics.reposervices.MetricsRepoService; import org.etsi.osl.tmf.ri639.model.ResourceStatusType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,13 +16,13 @@ import java.util.List; import java.util.Map; @Controller -public class ResourceMetricsApiController implements ResourceMetricsApi{ +public class MetricsApiController implements MetricsApi { - private static final Logger log = LoggerFactory.getLogger(ResourceMetricsApiController.class); - private final ResourceMetricsRepoService resourceMetricsRepoService; + private static final Logger log = LoggerFactory.getLogger(MetricsApiController.class); + private final MetricsRepoService resourceMetricsRepoService; @Autowired - public ResourceMetricsApiController(ResourceMetricsRepoService resourceMetricsRepoService) { + public MetricsApiController(MetricsRepoService resourceMetricsRepoService) { this.resourceMetricsRepoService = resourceMetricsRepoService; } diff --git a/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/MetricsRepoService.java similarity index 96% rename from src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java rename to src/main/java/org/etsi/osl/tmf/metrics/reposervices/MetricsRepoService.java index 70feca73..5b493149 100644 --- a/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java +++ b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/MetricsRepoService.java @@ -13,7 +13,7 @@ import java.util.stream.Collectors; @Service -public class ResourceMetricsRepoService { +public class MetricsRepoService { @Autowired ObjectMapper objectMapper; diff --git a/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java b/src/test/java/org/etsi/osl/services/api/metrics/MetricsApiControllerTest.java similarity index 97% rename from src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java rename to src/test/java/org/etsi/osl/services/api/metrics/MetricsApiControllerTest.java index 5d99d776..c497ea43 100644 --- a/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java +++ b/src/test/java/org/etsi/osl/services/api/metrics/MetricsApiControllerTest.java @@ -5,10 +5,7 @@ import org.apache.commons.io.IOUtils; import org.etsi.osl.tmf.JsonUtils; import org.etsi.osl.tmf.OpenAPISpringBoot; import org.etsi.osl.tmf.common.model.Any; -import org.etsi.osl.tmf.common.model.UserPartRoleType; import org.etsi.osl.tmf.common.model.service.Note; -import org.etsi.osl.tmf.common.model.service.ResourceRef; -import org.etsi.osl.tmf.prm669.model.RelatedParty; import org.etsi.osl.tmf.rcm634.model.*; import org.etsi.osl.tmf.ri639.model.*; import org.etsi.osl.tmf.ri639.reposervices.ResourceRepoService; @@ -56,7 +53,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @ActiveProfiles("testing") //@TestPropertySource( // locations = "classpath:application-testing.yml") -public class ResourceMetricsApiControllerTest { +public class MetricsApiControllerTest { @Autowired private MockMvc mvc; -- GitLab From cfa1768413b35224c9c70df80a8cf740e34834c4 Mon Sep 17 00:00:00 2001 From: Nikos Kyriakoulis Date: Mon, 12 May 2025 17:40:06 +0300 Subject: [PATCH 5/7] Use metrics models for API responses --- .../etsi/osl/tmf/metrics/api/MetricsApi.java | 6 ++-- .../tmf/metrics/api/MetricsApiController.java | 33 +++++++------------ 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApi.java b/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApi.java index ee5dada7..41f271e6 100644 --- a/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApi.java +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApi.java @@ -5,6 +5,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import org.etsi.osl.tmf.metrics.ResourcesGroupByState; +import org.etsi.osl.tmf.metrics.TotalResources; import org.etsi.osl.tmf.ri639.model.ResourceStatusType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +31,7 @@ public interface MetricsApi { @ApiResponse(responseCode = "500", description = "Internal Server Error") }) @RequestMapping(value = "/metrics/totalResources", method = RequestMethod.GET, produces = "application/json;charset=utf-8") - ResponseEntity> getTotalResources( + ResponseEntity getTotalResources( @Valid @RequestParam(value = "state", required = false) ResourceStatusType state ); @@ -40,7 +42,7 @@ public interface MetricsApi { @ApiResponse(responseCode = "500", description = "Internal Server Error") }) @RequestMapping(value = "/metrics/resourcesGroupByState", method = RequestMethod.GET, produces = "application/json;charset=utf-8") - ResponseEntity> getResourcesGroupedByState( + ResponseEntity getResourcesGroupedByState( @Valid @RequestParam(value = "starttime", required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime starttime, @Valid @RequestParam(value = "endtime", required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime endtime ); diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java b/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java index 84c4f452..41af1e92 100644 --- a/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java @@ -1,5 +1,6 @@ package org.etsi.osl.tmf.metrics.api; +import org.etsi.osl.tmf.metrics.*; import org.etsi.osl.tmf.metrics.reposervices.MetricsRepoService; import org.etsi.osl.tmf.ri639.model.ResourceStatusType; import org.slf4j.Logger; @@ -10,7 +11,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import java.time.OffsetDateTime; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -27,11 +27,10 @@ public class MetricsApiController implements MetricsApi { } @Override - public ResponseEntity> getTotalResources(ResourceStatusType state) { + public ResponseEntity getTotalResources(ResourceStatusType state) { try { int totalResources = resourceMetricsRepoService.countTotalResources(state); - Map response = new HashMap<>(); - response.put("totalResources", totalResources); + TotalResources response = new TotalResources(totalResources); return new ResponseEntity<>(response, HttpStatus.OK); } catch (Exception e) { log.error("Couldn't retrieve total resources. ", e); @@ -40,7 +39,7 @@ public class MetricsApiController implements MetricsApi { } @Override - public ResponseEntity> getResourcesGroupedByState(OffsetDateTime starttime, OffsetDateTime endtime) { + public ResponseEntity getResourcesGroupedByState(OffsetDateTime starttime, OffsetDateTime endtime) { try { Map resourcesByState = resourceMetricsRepoService.getResourcesGroupedByState(starttime, endtime); @@ -55,25 +54,17 @@ public class MetricsApiController implements MetricsApi { fullStateMap.put(key.toUpperCase(), value); // normalize case just in case }); - // Build groupByState list - List> groupByStateList = fullStateMap.entrySet().stream() - .map(entry -> { - Map map = new HashMap<>(); - map.put("key", entry.getKey()); - map.put("count", entry.getValue()); - return map; - }) + // Create aggregation items + List groupByStateList = fullStateMap.entrySet().stream() + .map(entry -> new GroupByItem(entry.getKey(), entry.getValue())) .toList(); + // Build response structure using models + GroupByStateAggregations aggregations = new GroupByStateAggregations(groupByStateList); + int total = fullStateMap.values().stream().mapToInt(Integer::intValue).sum(); + Resources services = new Resources(total, aggregations); + ResourcesGroupByState response = new ResourcesGroupByState(services); - // Wrap in response structure - Map aggregations = Map.of("groupByState", groupByStateList); - Map services = Map.of( - "total", fullStateMap.values().stream().mapToInt(Integer::intValue).sum(), - "aggregations", aggregations - ); - - Map response = Map.of("resources", services); return new ResponseEntity<>(response, HttpStatus.OK); } catch (Exception e) { -- GitLab From 570e8d51d08ce7a134f51b07634dfecd0fb1cc32 Mon Sep 17 00:00:00 2001 From: Nikos Kyriakoulis Date: Wed, 14 May 2025 12:15:34 +0300 Subject: [PATCH 6/7] Use new version of metrics model --- .../org/etsi/osl/tmf/metrics/api/MetricsApiController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java b/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java index 41af1e92..749ce67a 100644 --- a/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java @@ -55,12 +55,12 @@ public class MetricsApiController implements MetricsApi { }); // Create aggregation items - List groupByStateList = fullStateMap.entrySet().stream() - .map(entry -> new GroupByItem(entry.getKey(), entry.getValue())) + List groupByStateList = fullStateMap.entrySet().stream() + .map(entry -> new ResourcesGroupByStateItem(ResourceStatusType.valueOf(entry.getKey()), entry.getValue())) .toList(); // Build response structure using models - GroupByStateAggregations aggregations = new GroupByStateAggregations(groupByStateList); + ResourcesGroupByStateAggregations aggregations = new ResourcesGroupByStateAggregations(groupByStateList); int total = fullStateMap.values().stream().mapToInt(Integer::intValue).sum(); Resources services = new Resources(total, aggregations); ResourcesGroupByState response = new ResourcesGroupByState(services); -- GitLab From 2c7e1f6c7af3b271533e8bcba79b9535479e6a4a Mon Sep 17 00:00:00 2001 From: Kostis Trantzas Date: Fri, 16 May 2025 02:18:26 +0300 Subject: [PATCH 7/7] renaming files --- .../api/{MetricsApi.java => ResourceMetricsApi.java} | 6 +++--- ...ntroller.java => ResourceMetricsApiController.java} | 10 +++++----- ...epoService.java => ResourceMetricsRepoService.java} | 2 +- ...Test.java => ResourceMetricsApiControllerTest.java} | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) rename src/main/java/org/etsi/osl/tmf/metrics/api/{MetricsApi.java => ResourceMetricsApi.java} (92%) rename src/main/java/org/etsi/osl/tmf/metrics/api/{MetricsApiController.java => ResourceMetricsApiController.java} (87%) rename src/main/java/org/etsi/osl/tmf/metrics/reposervices/{MetricsRepoService.java => ResourceMetricsRepoService.java} (96%) rename src/test/java/org/etsi/osl/services/api/metrics/{MetricsApiControllerTest.java => ResourceMetricsApiControllerTest.java} (99%) diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApi.java b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java similarity index 92% rename from src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApi.java rename to src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java index 41f271e6..8f001697 100644 --- a/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApi.java +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java @@ -19,10 +19,10 @@ import org.springframework.web.bind.annotation.RequestParam; import java.time.OffsetDateTime; import java.util.Map; -@Tag(name = "MetricsApi") -public interface MetricsApi { +@Tag(name = "ResourceMetricsApi", description = "The Resources' Metrics API") +public interface ResourceMetricsApi { - Logger log = LoggerFactory.getLogger(MetricsApi.class); + Logger log = LoggerFactory.getLogger(ResourceMetricsApi.class); @Operation(summary = "Get total number of resources", operationId = "getTotalResources") @ApiResponses(value = { diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java similarity index 87% rename from src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java rename to src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java index 749ce67a..fac65082 100644 --- a/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java @@ -1,7 +1,7 @@ package org.etsi.osl.tmf.metrics.api; import org.etsi.osl.tmf.metrics.*; -import org.etsi.osl.tmf.metrics.reposervices.MetricsRepoService; +import org.etsi.osl.tmf.metrics.reposervices.ResourceMetricsRepoService; import org.etsi.osl.tmf.ri639.model.ResourceStatusType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,13 +16,13 @@ import java.util.List; import java.util.Map; @Controller -public class MetricsApiController implements MetricsApi { +public class ResourceMetricsApiController implements ResourceMetricsApi { - private static final Logger log = LoggerFactory.getLogger(MetricsApiController.class); - private final MetricsRepoService resourceMetricsRepoService; + private static final Logger log = LoggerFactory.getLogger(ResourceMetricsApiController.class); + private final ResourceMetricsRepoService resourceMetricsRepoService; @Autowired - public MetricsApiController(MetricsRepoService resourceMetricsRepoService) { + public ResourceMetricsApiController(ResourceMetricsRepoService resourceMetricsRepoService) { this.resourceMetricsRepoService = resourceMetricsRepoService; } diff --git a/src/main/java/org/etsi/osl/tmf/metrics/reposervices/MetricsRepoService.java b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java similarity index 96% rename from src/main/java/org/etsi/osl/tmf/metrics/reposervices/MetricsRepoService.java rename to src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java index 5b493149..70feca73 100644 --- a/src/main/java/org/etsi/osl/tmf/metrics/reposervices/MetricsRepoService.java +++ b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java @@ -13,7 +13,7 @@ import java.util.stream.Collectors; @Service -public class MetricsRepoService { +public class ResourceMetricsRepoService { @Autowired ObjectMapper objectMapper; diff --git a/src/test/java/org/etsi/osl/services/api/metrics/MetricsApiControllerTest.java b/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java similarity index 99% rename from src/test/java/org/etsi/osl/services/api/metrics/MetricsApiControllerTest.java rename to src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java index c497ea43..e0578d59 100644 --- a/src/test/java/org/etsi/osl/services/api/metrics/MetricsApiControllerTest.java +++ b/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java @@ -53,7 +53,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @ActiveProfiles("testing") //@TestPropertySource( // locations = "classpath:application-testing.yml") -public class MetricsApiControllerTest { +public class ResourceMetricsApiControllerTest { @Autowired private MockMvc mvc; -- GitLab