diff --git a/src/main/java/org/etsi/osl/tmf/configuration/SwaggerDocumentationConfig.java b/src/main/java/org/etsi/osl/tmf/configuration/SwaggerDocumentationConfig.java index b8e5b11ab5d362a73dc607ebe8a56453a7d04132..796d26a3a83f5b72bd160c3f6664b812b9220294 100644 --- a/src/main/java/org/etsi/osl/tmf/configuration/SwaggerDocumentationConfig.java +++ b/src/main/java/org/etsi/osl/tmf/configuration/SwaggerDocumentationConfig.java @@ -830,7 +830,7 @@ public GroupedOpenApi pim637() { SpringDocUtils.getConfig().replaceWithClass(java.time.OffsetDateTime.class, java.util.Date.class); return GroupedOpenApi.builder() - .group("OpensliceLCMRulesspecificationAPI") + .group("OpenSliceLCMRulesspecificationAPI") .addOpenApiCustomizer( this.lcmOpenAPI() ) .packagesToScan("org.etsi.osl.tmf.lcm.api") .build(); @@ -947,6 +947,39 @@ public GroupedOpenApi pim637() { .build(); } + + + /** + * Metrics + * @return + */ + @Bean + public OpenApiCustomizer metricsOpenAPI() { + return openApi -> openApi + .specVersion( SpecVersion.V30 ).addSecurityItem(new SecurityRequirement().addList("security_auth")) + .info(new Info().title("OpenSlice Metrics API") + .description("OpenAPI environment for OpenSlice Metrics") + .version("4.0.0") + .license(new License() + .name("Apache 2.0") + .url("https://osl.etsi.org"))) + .externalDocs(new ExternalDocumentation() + .description("OpenSlice Metrics") + .url("https://osl.etsi.org")); + } + + @Bean + public GroupedOpenApi metrics(){ + + SpringDocUtils.getConfig().replaceWithClass(java.time.LocalDate.class, java.sql.Date.class); + SpringDocUtils.getConfig().replaceWithClass(java.time.OffsetDateTime.class, java.util.Date.class); + return GroupedOpenApi.builder() + .group("OpenSliceMetricsAPI") + .addOpenApiCustomizer( this.metricsOpenAPI() ) + .packagesToScan("org.etsi.osl.tmf.metrics.api") + .build(); + + } // @Bean 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 new file mode 100644 index 0000000000000000000000000000000000000000..0ddf35e4b417988fd8a59fbf1e77535f7af85312 --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApi.java @@ -0,0 +1,49 @@ +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 org.etsi.osl.tmf.metrics.PublishedServiceSpecifications; +import org.etsi.osl.tmf.metrics.RegisteredIndividuals; +import org.etsi.osl.tmf.metrics.RegisteredResourceSpecifications; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.util.Map; + +@Tag(name = "MetricsApi") +public interface MetricsApi { + + Logger log = LoggerFactory.getLogger(MetricsApi.class); + + @Operation(summary = "Get total number of registered individuals", operationId = "getRegisteredIndividuals") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success"), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + }) + @RequestMapping(value = "/metrics/registeredIndividuals", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + ResponseEntity<RegisteredIndividuals> getRegisteredIndividuals(); + + @Operation(summary = "Get total number of published service specifications", operationId = "getPublishedServiceSpecifications") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success"), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + }) + @RequestMapping(value = "/metrics/publishedServiceSpecifications", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + ResponseEntity<PublishedServiceSpecifications> getPublishedServiceSpecifications(); + + @Operation(summary = "Get total number of registered resource specifications", operationId = "getRegisteredResourceSpecifications") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success"), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + }) + @RequestMapping(value = "/metrics/registeredResourceSpecifications", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + ResponseEntity<RegisteredResourceSpecifications> getRegisteredResourceSpecifications(); +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..089f7252b0fb8f983f83b996ec5b7c2fac28a29b --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/MetricsApiController.java @@ -0,0 +1,64 @@ +package org.etsi.osl.tmf.metrics.api; + +import org.etsi.osl.tmf.metrics.PublishedServiceSpecifications; +import org.etsi.osl.tmf.metrics.RegisteredIndividuals; +import org.etsi.osl.tmf.metrics.RegisteredResourceSpecifications; +import org.etsi.osl.tmf.metrics.reposervices.MetricsRepoService; +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.util.HashMap; +import java.util.Map; + +@Controller +public class MetricsApiController implements MetricsApi { + + private static final Logger log = LoggerFactory.getLogger(MetricsApiController.class); + + private final MetricsRepoService generalMetricsRepoService; + + @Autowired + public MetricsApiController(MetricsRepoService generalMetricsRepoService) { + this.generalMetricsRepoService = generalMetricsRepoService; + } + + @Override + public ResponseEntity<RegisteredIndividuals> getRegisteredIndividuals() { + try { + int totalIndividuals = generalMetricsRepoService.countRegisteredIndividuals(); + RegisteredIndividuals response = new RegisteredIndividuals(totalIndividuals); + return new ResponseEntity<>(response, HttpStatus.OK); + } catch (Exception e) { + log.error("Couldn't retrieve total registered individuals. ", e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + public ResponseEntity<PublishedServiceSpecifications> getPublishedServiceSpecifications() { + try { + int totalSpecifications = generalMetricsRepoService.countPublishedServiceSpecifications(); + PublishedServiceSpecifications response = new PublishedServiceSpecifications(totalSpecifications); + return new ResponseEntity<>(response, HttpStatus.OK); + } catch (Exception e) { + log.error("Couldn't retrieve total published service specifications. ", e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + public ResponseEntity<RegisteredResourceSpecifications> getRegisteredResourceSpecifications() { + try { + int totalResourceSpecifications = generalMetricsRepoService.countRegisteredResourceSpecifications(); + RegisteredResourceSpecifications response = new RegisteredResourceSpecifications(totalResourceSpecifications); + return new ResponseEntity<>(response, HttpStatus.OK); + } catch (Exception e) { + log.error("Couldn't retrieve total registered resource specifications. ", e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/src/main/java/org/etsi/osl/tmf/metrics/reposervices/MetricsRepoService.java b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/MetricsRepoService.java new file mode 100644 index 0000000000000000000000000000000000000000..834699b831c0dc3fd62d3583e90fa646749a6f46 --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/MetricsRepoService.java @@ -0,0 +1,49 @@ +package org.etsi.osl.tmf.metrics.reposervices; + +import org.etsi.osl.tmf.pm632.repo.IndividualRepository; +import org.etsi.osl.tmf.rcm634.repo.ResourceSpecificationRepository; +import org.etsi.osl.tmf.scm633.model.ServiceCandidate; +import org.etsi.osl.tmf.scm633.repo.CandidateRepository; +import org.etsi.osl.tmf.scm633.repo.CategoriesRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class MetricsRepoService { + + @Autowired + IndividualRepository individualRepository; + + @Autowired + ResourceSpecificationRepository resourceSpecificationRepository; + + @Autowired + CategoriesRepository categoriesRepository; + + @Autowired + CandidateRepository candidateRepository; + + public int countRegisteredIndividuals() { + return individualRepository.countAll(); + } + + public int countPublishedServiceSpecifications() { + + List<ServiceCandidate> serviceCandidates = candidateRepository.findAll(); + int count = 0; + + for (ServiceCandidate serviceCandidate : serviceCandidates) { + System.out.println("ServiceCandidate Category: " + serviceCandidate.getCategoryObj()); + if (serviceCandidate.getCategory() != null) { + count += 1; + } + } + return count; + } + + public int countRegisteredResourceSpecifications() { + return resourceSpecificationRepository.countLogical() + resourceSpecificationRepository.countPhysical(); + } +} diff --git a/src/main/java/org/etsi/osl/tmf/pm632/repo/IndividualRepository.java b/src/main/java/org/etsi/osl/tmf/pm632/repo/IndividualRepository.java index 1e16ed9f435307b98ed881fab7e0b40c58aa0fe4..214a5bfd9cde3f3aac5170968b03c5ec08641458 100644 --- a/src/main/java/org/etsi/osl/tmf/pm632/repo/IndividualRepository.java +++ b/src/main/java/org/etsi/osl/tmf/pm632/repo/IndividualRepository.java @@ -22,6 +22,7 @@ package org.etsi.osl.tmf.pm632.repo; import java.util.List; import java.util.Optional; import org.etsi.osl.tmf.pm632.model.Individual; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.stereotype.Repository; @@ -40,4 +41,10 @@ public interface IndividualRepository extends CrudRepository<Individual, Long>, Optional<Individual> findByPreferredGivenName(String username); + //Methods for metrics + + @Query("SELECT COUNT(ind) FROM Individual ind") + int countAll(); + + } diff --git a/src/main/java/org/etsi/osl/tmf/rcm634/repo/ResourceSpecificationRepository.java b/src/main/java/org/etsi/osl/tmf/rcm634/repo/ResourceSpecificationRepository.java index 456b890768270f1806cade84f393def6d17cd299..c7726f9bb4b0661244d3c8ab18f9d6c6e9636d4b 100644 --- a/src/main/java/org/etsi/osl/tmf/rcm634/repo/ResourceSpecificationRepository.java +++ b/src/main/java/org/etsi/osl/tmf/rcm634/repo/ResourceSpecificationRepository.java @@ -52,4 +52,12 @@ public interface ResourceSpecificationRepository extends CrudRepository<Resource @Query("SELECT sc FROM PhysicalRspec sc") List<PhysicalResourceSpecification> findAllPhysical(); + // Methods for metrics + + @Query("SELECT COUNT(sc) FROM LogicalRspec sc") + int countLogical(); + + @Query("SELECT COUNT(sc) FROM PhysicalRspec sc") + int countPhysical(); + } diff --git a/src/main/java/org/etsi/osl/tmf/scm633/repo/CandidateRepository.java b/src/main/java/org/etsi/osl/tmf/scm633/repo/CandidateRepository.java index df131d4b69dbc5c10a2a6e9107a8e5a0edab5ca6..85e56f4b7317c9259a789dbccf91fbb510dabb48 100644 --- a/src/main/java/org/etsi/osl/tmf/scm633/repo/CandidateRepository.java +++ b/src/main/java/org/etsi/osl/tmf/scm633/repo/CandidateRepository.java @@ -19,6 +19,7 @@ */ package org.etsi.osl.tmf.scm633.repo; +import java.util.List; import java.util.Optional; import org.etsi.osl.tmf.scm633.model.ServiceCandidate; import org.springframework.data.jpa.repository.Query; @@ -36,4 +37,8 @@ public interface CandidateRepository extends CrudRepository<ServiceCandidate, Lo @Query("SELECT sc FROM ServiceCandidate sc JOIN FETCH sc.serviceSpecificationObj spec WHERE spec.uuid = ?1") Optional<ServiceCandidate> findByServiceSpecUuid(String id); + // Methods for metrics + @Query("SELECT sc FROM ServiceCandidate sc") + List<ServiceCandidate> findAll(); + } diff --git a/src/test/java/org/etsi/osl/services/api/metrics/MetricsApiControllerTest.java b/src/test/java/org/etsi/osl/services/api/metrics/MetricsApiControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4841b8ec8fa1120b28b25f73962fa3b7607d3570 --- /dev/null +++ b/src/test/java/org/etsi/osl/services/api/metrics/MetricsApiControllerTest.java @@ -0,0 +1,124 @@ +package org.etsi.osl.services.api.metrics; + +import com.jayway.jsonpath.JsonPath; +import org.etsi.osl.tmf.OpenAPISpringBoot; +import org.etsi.osl.tmf.pm632.model.IndividualCreate; +import org.etsi.osl.tmf.pm632.reposervices.IndividualRepoService; +import org.etsi.osl.tmf.rcm634.reposervices.ResourceSpecificationRepoService; +import org.etsi.osl.tmf.scm633.model.ServiceCandidate; +import org.etsi.osl.tmf.scm633.reposervices.CandidateRepoService; +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.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.util.List; + +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.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 MetricsApiControllerTest { + + @Autowired + private MockMvc mvc; + + @Autowired + private WebApplicationContext context; + + @Autowired + IndividualRepoService individualRepoService; + + @Autowired + ResourceSpecificationRepoService resourceSpecificationRepoService; + + @Autowired + CandidateRepoService candidateRepoService; + + + @Before + public void setup() throws Exception { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .apply(springSecurity()) + .build(); + } + + @WithMockUser(username="osadmin", roles = {"ADMIN","USER"}) + @Test + public void testGetRegisteredIndividuals() throws Exception { + + IndividualCreate individualCreate = new IndividualCreate(); + individualCreate.setFullName("John Doe"); + individualRepoService.addIndividual(individualCreate); + + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/registeredIndividuals" ) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk() ) + .andReturn().getResponse().getContentAsString(); + + int totalIndividuals = JsonPath.read(response, "$.registeredIndividuals"); + + assertThat(totalIndividuals).isEqualTo(individualRepoService.findAll().size()); + } + + @WithMockUser(username="osadmin", roles = {"ADMIN","USER"}) + @Test + public void testGetPublishedServiceSpecifications() throws Exception { + + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/publishedServiceSpecifications" ) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk() ) + .andReturn().getResponse().getContentAsString(); + + int totalSpecs = JsonPath.read(response, "$.publishedServiceSpecifications"); + + List<ServiceCandidate> serviceCandidates = candidateRepoService.findAll(); + int count = 0; + + for (ServiceCandidate serviceCandidate : serviceCandidates) { + if (serviceCandidate.getCategory() != null) { + count += 1; + } + } + + assertThat(totalSpecs).isEqualTo(count); + } + + @WithMockUser(username="osadmin", roles = {"ADMIN","USER"}) + @Test + public void testGetRegisteredResourceSpecifications() throws Exception { + + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/registeredResourceSpecifications" ) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk() ) + .andReturn().getResponse().getContentAsString(); + + int totalSpecs = JsonPath.read(response, "$.registeredResourceSpecifications"); + + assertThat(totalSpecs).isEqualTo(resourceSpecificationRepoService.findAll().size()); + } +}