diff --git a/Dockerfile b/Dockerfile index 5d9a1d4f60cd7487c9de71a8f0057930e7637841..945df88d49e577d182447daed8b95b3caa8074d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM ibm-semeru-runtimes:open-17.0.7_7-jdk # RUN mkdir /opt/shareclasses RUN mkdir -p /opt/openslice/lib/ -COPY target/org.etsi.osl.tmf.api-1.1.0-exec.jar /opt/openslice/lib/ -CMD ["java", "-Xshareclasses:cacheDir=/opt/shareclasses", "-jar", "/opt/openslice/lib/org.etsi.osl.tmf.api-1.1.0-exec.jar"] +COPY target/org.etsi.osl.tmf.api-1.2.0-exec.jar /opt/openslice/lib/ +CMD ["java", "-Xshareclasses:cacheDir=/opt/shareclasses", "-jar", "/opt/openslice/lib/org.etsi.osl.tmf.api-1.2.0-exec.jar"] EXPOSE 13082 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3184b3aa8216683e03259ec2967b84fd4309139e..9773329aeeb660ddebe2ed5c14938e0d2118d9b5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.etsi.osl org.etsi.osl.main - 2024Q4 + 2025Q2 ../org.etsi.osl.main @@ -305,7 +305,7 @@ com.h2database h2 - test + 2.3.232 org.apache.activemq diff --git a/src/main/java/org/etsi/osl/tmf/BootstrapRepository.java b/src/main/java/org/etsi/osl/tmf/BootstrapRepository.java index 31cc63fd735f194dbf570f77dc2ad9eb402ab9b1..a7488387094262b84dc577e2a7b55dbffcaf488c 100644 --- a/src/main/java/org/etsi/osl/tmf/BootstrapRepository.java +++ b/src/main/java/org/etsi/osl/tmf/BootstrapRepository.java @@ -113,7 +113,7 @@ public class BootstrapRepository { } else { //check if we have the latest version of GST if (ADDGST) { ServiceCategory scategory = this.categRepoService.findByName("Generic Services"); - ServiceSpecification serviceSpecificationObj = this.specRepoService.findByNameAndVersion( GST_EXAMPLE_NAME , "5.0.0"); + ServiceSpecification serviceSpecificationObj = this.specRepoService.findByNameAndVersion( GST_EXAMPLE_NAME , "10.0.0"); if ( ( scategory != null ) && ( serviceSpecificationObj == null )) { 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/GeneralMetricsApi.java b/src/main/java/org/etsi/osl/tmf/metrics/api/GeneralMetricsApi.java new file mode 100644 index 0000000000000000000000000000000000000000..4c3714c563937337897bf35da9c21f4b9b56ed8e --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/GeneralMetricsApi.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 = "GeneralMetricsApi", description = "The General Metrics API") +public interface GeneralMetricsApi { + + Logger log = LoggerFactory.getLogger(GeneralMetricsApi.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 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 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 getRegisteredResourceSpecifications(); +} diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/GeneralMetricsApiController.java b/src/main/java/org/etsi/osl/tmf/metrics/api/GeneralMetricsApiController.java new file mode 100644 index 0000000000000000000000000000000000000000..187920b39606571c595d93d3acd804d3ed7c833f --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/GeneralMetricsApiController.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.GeneralMetricsRepoService; +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 GeneralMetricsApiController implements GeneralMetricsApi { + + private static final Logger log = LoggerFactory.getLogger(GeneralMetricsApiController.class); + + private final GeneralMetricsRepoService generalMetricsRepoService; + + @Autowired + public GeneralMetricsApiController(GeneralMetricsRepoService generalMetricsRepoService) { + this.generalMetricsRepoService = generalMetricsRepoService; + } + + @Override + public ResponseEntity 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 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 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/api/ResourceMetricsApi.java b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java new file mode 100644 index 0000000000000000000000000000000000000000..8f0016979b961f47f980234faa1366f034d71988 --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.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 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; +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", description = "The Resources' Metrics API") +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 = "/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 = "/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 0000000000000000000000000000000000000000..fac650828ed2384d72b70a6f6300816a7351a4e9 --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java @@ -0,0 +1,76 @@ +package org.etsi.osl.tmf.metrics.api; + +import org.etsi.osl.tmf.metrics.*; +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.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); + TotalResources response = new 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 + }); + + // Create aggregation items + List groupByStateList = fullStateMap.entrySet().stream() + .map(entry -> new ResourcesGroupByStateItem(ResourceStatusType.valueOf(entry.getKey()), entry.getValue())) + .toList(); + + // Build response structure using models + 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); + + 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/api/ServiceMetricsApi.java b/src/main/java/org/etsi/osl/tmf/metrics/api/ServiceMetricsApi.java new file mode 100644 index 0000000000000000000000000000000000000000..b5595535132a027f88cc6c34e38b8d6f3a2350fe --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ServiceMetricsApi.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 jakarta.validation.Valid; +import org.etsi.osl.tmf.common.model.service.ServiceStateType; +import org.etsi.osl.tmf.metrics.ServicesGroupByState; +import org.etsi.osl.tmf.metrics.TotalServices; +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 = "ServiceMetricsApi", description = "The Services' Metrics API") +public interface ServiceMetricsApi { + + Logger log = LoggerFactory.getLogger(ServiceMetricsApi.class); + + @Operation(summary = "Get total number of services", operationId = "getTotalServices") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success"), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + }) + @RequestMapping(value = "/metrics/totalServices", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + ResponseEntity getTotalServices( + @Valid @RequestParam(value = "state", required = false) ServiceStateType state + ); + + @Operation(summary = "Get services grouped by state", operationId = "getServicesGroupedByState") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success"), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + }) + @RequestMapping(value = "/metrics/servicesGroupByState", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + ResponseEntity getServicesGroupedByState( + @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/ServiceMetricsApiController.java b/src/main/java/org/etsi/osl/tmf/metrics/api/ServiceMetricsApiController.java new file mode 100644 index 0000000000000000000000000000000000000000..abe7575e19556a33442e830bdca100173f0d681a --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ServiceMetricsApiController.java @@ -0,0 +1,75 @@ +package org.etsi.osl.tmf.metrics.api; + +import org.etsi.osl.tmf.common.model.service.ServiceStateType; +import org.etsi.osl.tmf.metrics.*; +import org.etsi.osl.tmf.metrics.reposervices.ServiceMetricsRepoService; +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.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Controller +public class ServiceMetricsApiController implements ServiceMetricsApi { + + private static final Logger log = LoggerFactory.getLogger(ServiceMetricsApiController.class); + private final ServiceMetricsRepoService serviceMetricsRepoService; + + @Autowired + public ServiceMetricsApiController(ServiceMetricsRepoService serviceMetricsRepoService) { + this.serviceMetricsRepoService = serviceMetricsRepoService; + } + + @Override + public ResponseEntity getTotalServices(ServiceStateType state) { + try { + int totalServices = serviceMetricsRepoService.countTotalServices(state); + TotalServices response = new TotalServices(totalServices); + return new ResponseEntity<>(response, HttpStatus.OK); + } catch (Exception e) { + log.error("Couldn't retrieve total services. ", e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + public ResponseEntity getServicesGroupedByState(OffsetDateTime starttime, OffsetDateTime endtime) { + try { + Map servicesByState = serviceMetricsRepoService.getServicesGroupedByState(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 (ServiceStateType state : ServiceStateType.values()) { + fullStateMap.put(state.name(), 0); // default to 0 + } + + // Overwrite counts with actual data + servicesByState.forEach((key, value) -> { + fullStateMap.put(key.toUpperCase(), value); // normalize case just in case + }); + + // Create aggregation items + List groupByStateList = fullStateMap.entrySet().stream() + .map(entry -> new ServicesGroupByStateItem(ServiceStateType.valueOf(entry.getKey()), entry.getValue())) + .toList(); + + // Build response structure using metrics models + ServicesGroupByStateAggregations aggregations = new ServicesGroupByStateAggregations(groupByStateList); + int total = fullStateMap.values().stream().mapToInt(Integer::intValue).sum(); + Services services = new Services(total, aggregations); + ServicesGroupByState response = new ServicesGroupByState(services); + + return new ResponseEntity<>(response, HttpStatus.OK); + + } catch (Exception e) { + log.error("Couldn't retrieve services grouped by state. ", e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/ServiceOrderMetricsApi.java b/src/main/java/org/etsi/osl/tmf/metrics/api/ServiceOrderMetricsApi.java new file mode 100644 index 0000000000000000000000000000000000000000..17afc56186a9fbbb4282a1dc17b93ef33386be0c --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ServiceOrderMetricsApi.java @@ -0,0 +1,75 @@ +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.metrics.ActiveServiceOrders; +import org.etsi.osl.tmf.metrics.ServiceOrdersGroupByDay; +import org.etsi.osl.tmf.metrics.ServiceOrdersGroupByState; +import org.etsi.osl.tmf.metrics.TotalServiceOrders; +import org.etsi.osl.tmf.so641.model.ServiceOrderStateType; +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 = "ServiceOrderMetricsApi", description = "The Service Orders' Metrics API") +public interface ServiceOrderMetricsApi { + + Logger log = LoggerFactory.getLogger(ServiceOrderMetricsApi.class); + + @Operation(summary = "Get total number of service orders", operationId = "getTotalServiceOrders") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success"), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + }) + @RequestMapping(value = "/metrics/totalServiceOrders", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + ResponseEntity getTotalServiceOrders( + @Valid @RequestParam(value = "state", required = false) ServiceOrderStateType state + ); + + + @Operation(summary = "Get total number of active service orders", operationId = "getTotalActiveServiceOrders") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success"), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + }) + @RequestMapping(value = "/metrics/activeServiceOrders", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + ResponseEntity getTotalActiveServiceOrders(); + + + @Operation(summary = "Get service orders grouped by day", operationId = "getServiceOrdersGroupedByDay") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success"), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + }) + @RequestMapping(value = "/metrics/serviceOrdersGroupByDay", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + ResponseEntity getServiceOrdersGroupedByDay( + @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 + ); + + + @Operation(summary = "Get service orders grouped by state", operationId = "getServiceOrdersGroupedByState") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success"), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + }) + @RequestMapping(value = "/metrics/serviceOrdersGroupByState", method = RequestMethod.GET, produces = "application/json;charset=utf-8") + ResponseEntity getServiceOrdersGroupedByState( + @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/ServiceOrderMetricsApiController.java b/src/main/java/org/etsi/osl/tmf/metrics/api/ServiceOrderMetricsApiController.java new file mode 100644 index 0000000000000000000000000000000000000000..6b0a1bf8efe15abe954d20c65fd20f24a65603e8 --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ServiceOrderMetricsApiController.java @@ -0,0 +1,123 @@ +package org.etsi.osl.tmf.metrics.api; + +import org.etsi.osl.tmf.metrics.*; +import org.etsi.osl.tmf.metrics.reposervices.ServiceOrderMetricsRepoService; +import org.etsi.osl.tmf.so641.model.ServiceOrderStateType; +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.time.temporal.ChronoUnit; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + + +@Controller +public class ServiceOrderMetricsApiController implements ServiceOrderMetricsApi { + + private static final Logger log = LoggerFactory.getLogger(ServiceOrderMetricsApiController.class); + private final ServiceOrderMetricsRepoService serviceOrderMetricsRepoService; + + @Autowired + public ServiceOrderMetricsApiController(ServiceOrderMetricsRepoService serviceOrderMetricsRepoService) { + this.serviceOrderMetricsRepoService = serviceOrderMetricsRepoService; + } + + @Override + public ResponseEntity getTotalServiceOrders(ServiceOrderStateType state) { + try { + int totalServiceOrders = serviceOrderMetricsRepoService.countTotalServiceOrders(state); + TotalServiceOrders response = new TotalServiceOrders(totalServiceOrders); + return new ResponseEntity<>(response, HttpStatus.OK); + } catch (Exception e) { + log.error("Couldn't retrieve total service orders. ", e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + public ResponseEntity getTotalActiveServiceOrders() { + try { + int totalActiveServiceOrders = serviceOrderMetricsRepoService.countTotalActiveServiceOrders(); + ActiveServiceOrders response = new ActiveServiceOrders(totalActiveServiceOrders); + return new ResponseEntity<>(response, HttpStatus.OK); + } catch (Exception e) { + log.error("Couldn't retrieve total active service orders. ", e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + public ResponseEntity getServiceOrdersGroupedByDay(OffsetDateTime starttime, OffsetDateTime endtime) { + try { + Map orderDatesGroupedByDate = serviceOrderMetricsRepoService.getServiceOrdersGroupedByDay(starttime, endtime); + + // Fill missing days with count 0 + Map fullDayMap = new LinkedHashMap<>(); + OffsetDateTime cursor = starttime.truncatedTo(ChronoUnit.DAYS); + OffsetDateTime endDay = endtime.truncatedTo(ChronoUnit.DAYS); + while (!cursor.isAfter(endDay)) { + String key = cursor.toInstant().toString(); + fullDayMap.put(key, orderDatesGroupedByDate.getOrDefault(key, 0)); + cursor = cursor.plusDays(1); + } + + // Convert to model list + List groupByDayList = fullDayMap.entrySet().stream() + .map(entry -> new ServiceOrdersGroupByDayItem(entry.getKey(), entry.getValue())) + .toList(); + + ServiceOrdersGroupByDayAggregations aggregations = new ServiceOrdersGroupByDayAggregations(groupByDayList); + int total = fullDayMap.values().stream().mapToInt(Integer::intValue).sum(); + ServiceOrdersGroupByDayParent wrapper = new ServiceOrdersGroupByDayParent(total, aggregations); + ServiceOrdersGroupByDay response = new ServiceOrdersGroupByDay(wrapper); + + return new ResponseEntity<>(response, HttpStatus.OK); + + } catch (Exception e) { + log.error("Couldn't retrieve services grouped by state. ", e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + public ResponseEntity getServiceOrdersGroupedByState(OffsetDateTime starttime, OffsetDateTime endtime) { + try { + Map servicesByState = serviceOrderMetricsRepoService.getServiceOrdersGroupedByState(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 (ServiceOrderStateType state : ServiceOrderStateType.values()) { + fullStateMap.put(state.name(), 0); + } + + // Overwrite counts with actual data + servicesByState.forEach((key, value) -> { + fullStateMap.put(key.toUpperCase(), value); + }); + + // Create aggregation items + List groupByStateList = fullStateMap.entrySet().stream() + .map(entry -> new ServiceOrdersGroupByStateItem(ServiceOrderStateType.valueOf(entry.getKey()), entry.getValue())) + .toList(); + + // Build response structure using models + ServiceOrdersGroupByStateAggregations aggregations = new ServiceOrdersGroupByStateAggregations(groupByStateList); + int total = fullStateMap.values().stream().mapToInt(Integer::intValue).sum(); + ServiceOrdersGroupByStateParent services = new ServiceOrdersGroupByStateParent(total, aggregations); + ServiceOrdersGroupByState response = new ServiceOrdersGroupByState(services); + + return new ResponseEntity<>(response, HttpStatus.OK); + + } catch (Exception e) { + log.error("Couldn't retrieve services grouped by state. ", e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + +} diff --git a/src/main/java/org/etsi/osl/tmf/metrics/reposervices/GeneralMetricsRepoService.java b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/GeneralMetricsRepoService.java new file mode 100644 index 0000000000000000000000000000000000000000..fb3af6a76e120fe77896d12a2d50178d7c5cfb34 --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/GeneralMetricsRepoService.java @@ -0,0 +1,74 @@ +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.ServiceCatalog; +import org.etsi.osl.tmf.scm633.model.ServiceCategory; +import org.etsi.osl.tmf.scm633.repo.CandidateRepository; +import org.etsi.osl.tmf.scm633.repo.CatalogRepository; +import org.etsi.osl.tmf.scm633.repo.CategoriesRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.time.OffsetDateTime; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Service +public class GeneralMetricsRepoService { + + @Autowired + IndividualRepository individualRepository; + + @Autowired + ResourceSpecificationRepository resourceSpecificationRepository; + + @Autowired + CatalogRepository catalogRepository; + + @Autowired + CategoriesRepository categoriesRepository; + + @Autowired + CandidateRepository candidateRepository; + + // Cached values for performance optimization + private static Integer cachedPublishedServiceSpecCount = null; + private static OffsetDateTime lastRetrieved = null; + private static final Duration CACHE_DURATION = Duration.ofMinutes(15); + + public int countRegisteredIndividuals() { + return individualRepository.countAll(); + } + + public int countPublishedServiceSpecifications() { + OffsetDateTime now = OffsetDateTime.now(); + + if (cachedPublishedServiceSpecCount == null || lastRetrieved == null || + Duration.between(lastRetrieved, now).compareTo(CACHE_DURATION) > 0) { + + int count = 0; + Set serviceCategories = new HashSet<>(); + List serviceCatalogs = catalogRepository.findByOrderByName(); + + for (ServiceCatalog serviceCatalog: serviceCatalogs) { + serviceCategories.addAll(serviceCatalog.getCategoryObj()); + } + + for (ServiceCategory serviceCategory : serviceCategories) { + count += serviceCategory.getServiceCandidateObj().size() + + serviceCategory.getServiceCandidateRefs().size(); + } + + cachedPublishedServiceSpecCount = count; + lastRetrieved = now; + } + return cachedPublishedServiceSpecCount; + } + + public int countRegisteredResourceSpecifications() { + return resourceSpecificationRepository.countLogical() + resourceSpecificationRepository.countPhysical(); + } +} 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 0000000000000000000000000000000000000000..02d26d518a6828de96b3cd36415aba2c75dac6e3 --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java @@ -0,0 +1,47 @@ +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() + .filter(row -> row[0] != null) + .collect(Collectors.toMap( + row -> row[0].toString(), + row -> ((Number) row[1]).intValue() + )); + } + +} diff --git a/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ServiceMetricsRepoService.java b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ServiceMetricsRepoService.java new file mode 100644 index 0000000000000000000000000000000000000000..542ea87c7572d05bf7d1debc0f300d750f550ff4 --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ServiceMetricsRepoService.java @@ -0,0 +1,46 @@ +package org.etsi.osl.tmf.metrics.reposervices; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.tmf.common.model.service.ServiceStateType; +import org.etsi.osl.tmf.sim638.repo.ServiceRepository; +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 ServiceMetricsRepoService { + + @Autowired + ObjectMapper objectMapper; + + @Autowired + ServiceRepository serviceRepo; + + public int countTotalServices(ServiceStateType state) { + if (state == null) { + return serviceRepo.countAll(); + } else { + return serviceRepo.countByState(state); + } + } + + public Map getServicesGroupedByState(OffsetDateTime starttime, OffsetDateTime endtime) { + if (starttime.plusDays(31).isBefore(endtime)) { + starttime = endtime.minusDays(31); + } + + List rawResults = serviceRepo.groupByStateBetweenDates(starttime, endtime); + + return rawResults.stream() + .filter(row -> row[0] != null) + .collect(Collectors.toMap( + row -> row[0].toString(), + row -> ((Number) row[1]).intValue() + )); + } + +} diff --git a/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ServiceOrderMetricsRepoService.java b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ServiceOrderMetricsRepoService.java new file mode 100644 index 0000000000000000000000000000000000000000..72730dd944cbba6e735adcbea387ffb43e347592 --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ServiceOrderMetricsRepoService.java @@ -0,0 +1,80 @@ +package org.etsi.osl.tmf.metrics.reposervices; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.tmf.so641.model.ServiceOrderStateType; +import org.etsi.osl.tmf.so641.repo.ServiceOrderRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +public class ServiceOrderMetricsRepoService { + + @Autowired + ObjectMapper objectMapper; + + @Autowired + ServiceOrderRepository serviceOrderRepository; + + public int countTotalServiceOrders(ServiceOrderStateType state) { + if (state == null) { + return serviceOrderRepository.countAll(); + } else { + return serviceOrderRepository.countByState(state); + } + } + + public int countTotalActiveServiceOrders() { + OffsetDateTime currentDate = OffsetDateTime.now(); + List activeStates = List.of( + ServiceOrderStateType.INPROGRESS, + ServiceOrderStateType.COMPLETED + ); + + return serviceOrderRepository.countAllActive(currentDate, activeStates); + } + + public Map getServiceOrdersGroupedByDay(OffsetDateTime starttime, OffsetDateTime endtime) { + if (starttime.plusDays(31).isBefore(endtime)) { + starttime = endtime.minusDays(31); + } + + List orderDates = serviceOrderRepository.getOrderDatesBetweenDates(starttime, endtime); + + // First group by day with count as Long + Map grouped = orderDates.stream() + .map(dt -> dt.truncatedTo(ChronoUnit.DAYS)) // Remove time portion + .collect(Collectors.groupingBy( + dt -> dt.toInstant().toString(), // Format as ISO string (Z) + Collectors.counting() + )); + + // Convert Map to Map + return grouped.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + e -> e.getValue().intValue() + )); + } + + public Map getServiceOrdersGroupedByState(OffsetDateTime starttime, OffsetDateTime endtime) { + if (starttime.plusDays(31).isBefore(endtime)) { + starttime = endtime.minusDays(31); + } + + List rawResults = serviceOrderRepository.groupByStateBetweenDates(starttime, endtime); + + return rawResults.stream() + .filter(row -> row[0] != null) + .collect(Collectors.toMap( + row -> row[0].toString(), + row -> ((Number) row[1]).intValue() + )); + } + +} diff --git a/src/main/java/org/etsi/osl/tmf/pcm620/api/ProductCatalogApiRouteBuilder.java b/src/main/java/org/etsi/osl/tmf/pcm620/api/ProductCatalogApiRouteBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..950a9a5404769089d9d59c56c82a2297ad33d066 --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/pcm620/api/ProductCatalogApiRouteBuilder.java @@ -0,0 +1,132 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.tmf.api + * %% + * Copyright (C) 2019 - 2020 openslice.io + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.tmf.pcm620.api; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.apache.camel.LoggingLevel; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.model.dataformat.JsonLibrary; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.etsi.osl.tmf.pcm620.reposervices.ProductCatalogRepoService; +import org.etsi.osl.tmf.pcm620.reposervices.ProductCategoryRepoService; +import org.etsi.osl.tmf.scm633.model.ServiceSpecification; +import org.etsi.osl.tmf.scm633.model.ServiceSpecificationCreate; +import org.etsi.osl.tmf.scm633.model.ServiceSpecificationUpdate; +import org.etsi.osl.tmf.scm633.reposervices.CatalogRepoService; +import org.etsi.osl.tmf.scm633.reposervices.CategoryRepoService; +import org.etsi.osl.tmf.scm633.reposervices.ServiceSpecificationRepoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +@Configuration +//@RefreshScope +@Component +public class ProductCatalogApiRouteBuilder extends RouteBuilder { + + private static final transient Log logger = LogFactory.getLog(ProductCatalogApiRouteBuilder.class.getName()); + + + + @Value("${CATALOG_GET_PRODUCTCATALOGS}") + private String CATALOG_GET_PRODUCTCATALOGS = ""; + + @Value("${CATALOG_GET_PRODUCTCATALOG_BY_ID}") + private String CATALOG_GET_PRODUCTCATALOG_BY_ID = ""; + + @Value("${CATALOG_GET_PRODUCTCATALOG_BY_NAME}") + private String CATALOG_GET_PRODUCTCATALOG_BY_NAME = ""; + + @Value("${CATALOG_GET_PRODUCTCATEGORIES}") + private String CATALOG_GET_PRODUCTCATEGORIES = ""; + + @Value("${CATALOG_GET_PRODUCTCATEGORY_BY_ID}") + private String CATALOG_GET_PRODUCTCATEGORY_BY_ID = ""; + + + @Value("${CATALOG_GET_PRODUCTOFFERINGS_BYCATEGORY_ID}") + private String CATALOG_GET_PRODUCTOFFERINGS_BYCATEGORY_ID = ""; + + + @Autowired + ProductCatalogRepoService catalogRepoService; + + + @Autowired + ProductCategoryRepoService categoryRepoService; + + + @Override + public void configure() throws Exception { + + from( CATALOG_GET_PRODUCTCATALOG_BY_ID ) + .log(LoggingLevel.INFO, log, CATALOG_GET_PRODUCTCATALOG_BY_ID + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean( catalogRepoService, "findByUuidEager(${header.catalogId})"); + + from( CATALOG_GET_PRODUCTCATALOGS ) + .log(LoggingLevel.INFO, log, CATALOG_GET_PRODUCTCATALOGS + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean( catalogRepoService, "findAllEager()"); + + from( CATALOG_GET_PRODUCTCATALOG_BY_NAME ) + .log(LoggingLevel.INFO, log, CATALOG_GET_PRODUCTCATALOG_BY_NAME + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean( catalogRepoService, "findByNameEager(${header.catalogName})") + .marshal().json( JsonLibrary.Jackson, String.class) + .convertBodyTo( String.class ); + + + from( CATALOG_GET_PRODUCTCATEGORIES ) + .log(LoggingLevel.INFO, log, CATALOG_GET_PRODUCTCATEGORIES + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean( catalogRepoService, "findAllCategoriesByCatalogName(${header.catalogName})"); + + + from( CATALOG_GET_PRODUCTCATEGORY_BY_ID ) + .log(LoggingLevel.INFO, log, CATALOG_GET_PRODUCTCATEGORY_BY_ID + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean( categoryRepoService, "findByIdEager(${header.catalogId})"); + + + from( CATALOG_GET_PRODUCTOFFERINGS_BYCATEGORY_ID ) + .log(LoggingLevel.INFO, log, CATALOG_GET_PRODUCTOFFERINGS_BYCATEGORY_ID + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean( categoryRepoService, "findAllProductOfferingsByCategId(${header.categoryId})"); + + } + + + + + + static T toJsonObj(String content, Class valueType) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.readValue( content, valueType); + } + +} diff --git a/src/main/java/org/etsi/osl/tmf/pcm620/api/ProductSpecificationApiRouteBuilder.java b/src/main/java/org/etsi/osl/tmf/pcm620/api/ProductSpecificationApiRouteBuilder.java index 07fe723d0e242df176992fc1729001098dd3427e..0c55c76983439e340b18b69c30807cfad260a407 100644 --- a/src/main/java/org/etsi/osl/tmf/pcm620/api/ProductSpecificationApiRouteBuilder.java +++ b/src/main/java/org/etsi/osl/tmf/pcm620/api/ProductSpecificationApiRouteBuilder.java @@ -1,5 +1,6 @@ package org.etsi.osl.tmf.pcm620.api; +import java.util.ArrayList; import org.apache.camel.LoggingLevel; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.model.dataformat.JsonLibrary; @@ -38,6 +39,10 @@ public class ProductSpecificationApiRouteBuilder extends RouteBuilder { @Value("${CATALOG_GET_PRODUCTOFFERING_BY_ID}") private String CATALOG_GET_PRODUCTOFFERING_BY_ID = ""; + + + @Value("${CATALOG_SEARCH_PRODUCTOFFERINGS}") + private String CATALOG_SEARCH_PRODUCTOFFERINGS = ""; @@ -90,6 +95,14 @@ public class ProductSpecificationApiRouteBuilder extends RouteBuilder { .marshal().json( JsonLibrary.Jackson, String.class) .convertBodyTo( String.class ); + + + from( CATALOG_SEARCH_PRODUCTOFFERINGS ) + .log(LoggingLevel.INFO, log, CATALOG_SEARCH_PRODUCTOFFERINGS + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .unmarshal().json( JsonLibrary.Jackson, ArrayList.class, true) + .bean( productOfferingRepoService, "searchProductOfferings( ${body} )"); + } } diff --git a/src/main/java/org/etsi/osl/tmf/pcm620/reposervices/ProductCatalogRepoService.java b/src/main/java/org/etsi/osl/tmf/pcm620/reposervices/ProductCatalogRepoService.java index 126999bd90598748e8c8d91146cb28b070f282ac..3f66f5c7686023d4734ae40376100f99e179e269 100644 --- a/src/main/java/org/etsi/osl/tmf/pcm620/reposervices/ProductCatalogRepoService.java +++ b/src/main/java/org/etsi/osl/tmf/pcm620/reposervices/ProductCatalogRepoService.java @@ -21,8 +21,12 @@ package org.etsi.osl.tmf.pcm620.reposervices; import java.time.OffsetDateTime; import java.time.ZoneOffset; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.hibernate5.jakarta.Hibernate5JakartaModule; import org.etsi.osl.tmf.common.model.ELifecycle; import org.etsi.osl.tmf.common.model.TimePeriod; import org.etsi.osl.tmf.pcm620.model.Catalog; @@ -31,11 +35,16 @@ import org.etsi.osl.tmf.pcm620.model.CatalogUpdate; import org.etsi.osl.tmf.pcm620.model.Category; import org.etsi.osl.tmf.pcm620.model.CategoryRef; import org.etsi.osl.tmf.pcm620.repo.ProductCatalogRepository; +import org.etsi.osl.tmf.scm633.model.ServiceCatalog; +import org.etsi.osl.tmf.scm633.model.ServiceCategory; +import org.etsi.osl.tmf.scm633.model.ServiceCategoryRef; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import jakarta.validation.Valid; @Service +@Transactional public class ProductCatalogRepoService { @@ -80,6 +89,65 @@ public class ProductCatalogRepoService { return null; } + + public String findByUuidEager(String id) { + + Catalog sc = this.findById(id); + + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + String res; + try { + res = mapper.writeValueAsString( sc ); + } catch (JsonProcessingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return "{}"; + } + + return res; + } + + public String findAllEager() { + + List oids = (List) this.catalogRepo.findByOrderByName(); + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + String res; + try { + res = mapper.writeValueAsString( oids ); + } catch (JsonProcessingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return "{}"; + } + + return res; + } + + public String findByNameEager(String aname) { + Catalog sc = this.findByName(aname); + + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + String res; + try { + res = mapper.writeValueAsString( sc ); + } catch (JsonProcessingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return "{}"; + } + + return res; + + } public Catalog updateCatalog(String id, CatalogUpdate Catalog) { @@ -131,5 +199,64 @@ public class ProductCatalogRepoService { public Catalog updateCatalog(Catalog scatalog) { return this.catalogRepo.save(scatalog); } + + /** + * return recursively all categories in catalog + * @param catalogName + */ + @Transactional + public String findAllCategoriesByCatalogName(String catalogName) { + String res="[]"; + + Optional scopt = this.catalogRepo.findByName(catalogName); + + if (scopt.isEmpty() ) { + return res; + } + + Catalog sc = scopt.get(); + + sc.getCategoryRefs(); + + + List allcategories = this.getCategories( sc.getCategoryRefs()); + + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + + + try { + res = mapper.writeValueAsString( allcategories ); + } catch (JsonProcessingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + return res; + + } + + + @Transactional + private List getCategories( @Valid List list) { + List categories = new ArrayList(); + + for (CategoryRef c : list ) { + Category category = this.categRepoService.findByUuid( c.getId()); + categories.add(category); + + if (category.getCategoryRefs()!=null && category.getCategoryRefs().size()>0) { + List subcategories = this.getCategories( category.getCategoryRefs() ); + categories.addAll(subcategories );//add children + } + + } + + return categories; + } + } diff --git a/src/main/java/org/etsi/osl/tmf/pcm620/reposervices/ProductCategoryRepoService.java b/src/main/java/org/etsi/osl/tmf/pcm620/reposervices/ProductCategoryRepoService.java index 60ebcca044262ec1f144f26d8f63ca2e01d46891..c7e226c03bbea2d617b95b6c1c4dae13b8820053 100644 --- a/src/main/java/org/etsi/osl/tmf/pcm620/reposervices/ProductCategoryRepoService.java +++ b/src/main/java/org/etsi/osl/tmf/pcm620/reposervices/ProductCategoryRepoService.java @@ -26,39 +26,41 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.hibernate5.jakarta.Hibernate5JakartaModule; import org.etsi.osl.tmf.common.model.ELifecycle; import org.etsi.osl.tmf.common.model.TimePeriod; +import org.etsi.osl.tmf.common.model.service.ServiceSpecificationRef; import org.etsi.osl.tmf.pcm620.model.Category; import org.etsi.osl.tmf.pcm620.model.CategoryCreate; import org.etsi.osl.tmf.pcm620.model.CategoryRef; import org.etsi.osl.tmf.pcm620.model.CategoryUpdate; import org.etsi.osl.tmf.pcm620.model.ProductOffering; import org.etsi.osl.tmf.pcm620.model.ProductOfferingRef; +import org.etsi.osl.tmf.pcm620.model.ProductSpecificationRef; import org.etsi.osl.tmf.pcm620.repo.ProductCatalogRepository; import org.etsi.osl.tmf.pcm620.repo.ProductCategoriesRepository; import org.etsi.osl.tmf.pcm620.repo.ProductOfferingRepository; +import org.etsi.osl.tmf.scm633.model.ServiceCandidate; +import org.etsi.osl.tmf.scm633.model.ServiceCategory; import org.hibernate.Hibernate; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import jakarta.persistence.EntityManagerFactory; import jakarta.validation.Valid; @Service public class ProductCategoryRepoService { - @Autowired - ProductCategoriesRepository categsRepo; - - @Autowired - ProductCatalogRepository catalogRepo; + private final ProductCategoriesRepository categsRepo; - @Autowired - ProductOfferingRepository prodsOfferingRepo; - - private SessionFactory sessionFactory; + private final ProductOfferingRepository prodsOfferingRepo; /** * from @@ -67,11 +69,11 @@ public class ProductCategoryRepoService { * @param factory */ @Autowired - public ProductCategoryRepoService(EntityManagerFactory factory) { - if (factory.unwrap(SessionFactory.class) == null) { - throw new NullPointerException("factory is not a hibernate factory"); - } - this.sessionFactory = factory.unwrap(SessionFactory.class); + public ProductCategoryRepoService( ProductCategoriesRepository categsRepo, + ProductOfferingRepository prodsOfferingRepo) { + + this.categsRepo = categsRepo; + this.prodsOfferingRepo = prodsOfferingRepo; } @@ -100,28 +102,31 @@ public class ProductCategoryRepoService { } - public Category findByIdEager(String id) { -// Optional optionalCat = this.categsRepo.findByIdEager( id ); -// return optionalCat -// .orElse(null); - - Session session = sessionFactory.openSession(); - Transaction tx = session.beginTransaction(); - Category dd = null; - try { - dd = (Category) session.get(Category.class, id); - Hibernate.initialize( dd.getCategoryObj() ); - Hibernate.initialize( dd.getProductOfferingRefs() ); - for (ProductOfferingRef sc : dd.getProductOfferingRefs()) { - Hibernate.initialize(sc ); - } - - tx.commit(); - } finally { - session.close(); - } - return dd; - } + @Transactional + public String findByIdEager(String id) { + Category sc = this.findByUuid( id ); + + String res= "{}"; + + if ( sc == null ) { + return res; + } + + + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + + try { + res = mapper.writeValueAsString( sc ); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + + return res; + } @@ -321,4 +326,35 @@ public class ProductCategoryRepoService { return optionalCat .orElse(null); } + + @Transactional + public String findAllProductOfferingsByCategId(String categoryId) { + + + String res="[]"; + List productSpecificationRefList = new ArrayList<>(); + Category category = this.findByUuid(categoryId); + + if ( category == null ) { + return res; + } + + Set proffs = category.getProductOfferingObj(); + + + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + + try { + res = mapper.writeValueAsString( proffs ); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + + return res; + + } } diff --git a/src/main/java/org/etsi/osl/tmf/pcm620/reposervices/ProductOfferingRepoService.java b/src/main/java/org/etsi/osl/tmf/pcm620/reposervices/ProductOfferingRepoService.java index 40176d7b632d3795da8fa4b598a4fdfd4d25ea4c..942d0e7d91331ea7958797bf88af6814d2996736 100644 --- a/src/main/java/org/etsi/osl/tmf/pcm620/reposervices/ProductOfferingRepoService.java +++ b/src/main/java/org/etsi/osl/tmf/pcm620/reposervices/ProductOfferingRepoService.java @@ -26,18 +26,20 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import org.etsi.osl.tmf.JsonUtils; +import java.util.StringJoiner; +import java.util.stream.Collectors; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.hibernate5.jakarta.Hibernate5JakartaModule; import org.etsi.osl.tmf.am651.model.AgreementRef; import org.etsi.osl.tmf.common.model.Any; import org.etsi.osl.tmf.common.model.AttachmentRefOrValue; import org.etsi.osl.tmf.common.model.ELifecycle; import org.etsi.osl.tmf.common.model.TimePeriod; -import org.etsi.osl.tmf.common.model.service.ServiceSpecificationRef; import org.etsi.osl.tmf.pcm620.model.BundledProductOffering; import org.etsi.osl.tmf.pcm620.model.ProductOffering; import org.etsi.osl.tmf.pcm620.model.ProductOfferingCreate; @@ -50,8 +52,6 @@ import org.etsi.osl.tmf.pcm620.model.ProductSpecificationCharacteristicValueUse; import org.etsi.osl.tmf.pcm620.model.ProductSpecificationCreate; import org.etsi.osl.tmf.pcm620.model.ProductSpecificationRef; import org.etsi.osl.tmf.pcm620.repo.ProductOfferingRepository; -import org.etsi.osl.tmf.pcm620.repo.ProductSpecificationRepository; -import org.etsi.osl.tmf.scm633.model.ServiceSpecCharacteristicValue; import org.etsi.osl.tmf.scm633.model.ServiceSpecification; import org.etsi.osl.tmf.scm633.reposervices.ServiceSpecificationRepoService; import org.hibernate.Hibernate; @@ -142,15 +142,14 @@ public class ProductOfferingRepoService { } sql += " FROM ProductOffering s"; - if (allParams.size() > 0) { - sql += " WHERE "; - for (String pname : allParams.keySet()) { - sql += " " + pname + " LIKE "; - String pval = URLDecoder.decode(allParams.get(pname), StandardCharsets.UTF_8.toString()); - sql += "'" + pval + "'"; - } - - } + if (allParams.size() > 0) { + String items = allParams.entrySet() + .stream() + .map(entry -> "s." + entry.getKey() + " LIKE '%" + URLDecoder.decode( entry.getValue(), StandardCharsets.UTF_8 )+ "%'" ) + .collect(Collectors.joining(" OR ")); + sql += " WHERE " + items; + + } sql += " ORDER BY s.name"; @@ -632,5 +631,124 @@ public class ProductOfferingRepoService { return pOffer; } + + + @Transactional + public String searchProductOfferings(List searchText) { + String res = "[]"; + + try { + + List specs= this.searchOfferingsInCategories( searchText); + + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + res = mapper.writeValueAsString( specs ); + + + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + + return res; + } + + + /** + * + * This findAll is optimized on fields. + * @param fields + * @param allParams + * @return + * @throws UnsupportedEncodingException + */ + @Transactional + public List searchOfferingsInCategories( List searchList ) + throws UnsupportedEncodingException { + + if ( searchList == null || searchList.size() ==0) { + return new ArrayList<>(); + } + + Session session = sessionFactory.openSession(); + Transaction tx = session.beginTransaction(); + + try { + String sql = "SELECT p.id as productOfferingId, p.name as productName, p.description as productDescription"; + + + + sql += " FROM ProductCategory as pcateg JOIN pcateg.productOffObj as p "; + sql += " WHERE " ; + + + // Build the name LIKE clause + StringJoiner nameJoiner = new StringJoiner(" AND "); + for (String term : searchList) { + nameJoiner.add("p.name LIKE '%" + term + "%'"); + } + + // Build the description LIKE clause + StringJoiner descriptionJoiner = new StringJoiner(" AND "); + for (String term : searchList) { + descriptionJoiner.add("p.description LIKE '%" + term + "%'"); + } + + // Combine both clauses with OR + sql += "(" + nameJoiner.toString() + ") OR (" + descriptionJoiner.toString() + ")"; + + sql += " ORDER BY p.name"; + +// List specs = session +// .createQuery( sql, ServiceSpecification.class) +// .getResultList(); + + + List mapaEntity = session + .createQuery(sql ) + .setResultTransformer( new ResultTransformer() { + + @Override + public Object transformTuple(Object[] tuple, String[] aliases) { + Map result = new LinkedHashMap(tuple.length); + for (int i = 0; i < tuple.length; i++) { + String alias = aliases[i]; + if (alias != null) { + if (alias.equals("type")) { + alias = "@type"; + } + result.put(alias, tuple[i]); + } + } + + return result; + } + + @Override + public List transformList(List collection) { + return collection; + } + } ) + .list(); + +// //this will fetch the whole object fields + + + return mapaEntity; + + + + + } finally { + tx.commit(); + session.close(); + } + + } } diff --git a/src/main/java/org/etsi/osl/tmf/pm628/api/MeasurementCollectionJobApiRouteBuilder.java b/src/main/java/org/etsi/osl/tmf/pm628/api/MeasurementCollectionJobApiRouteBuilder.java index b8fbcca3e6274e3a625dcd4ba4440b3f5dc43959..ed20d281d98f63037f95733aa1719d879cbb3a93 100644 --- a/src/main/java/org/etsi/osl/tmf/pm628/api/MeasurementCollectionJobApiRouteBuilder.java +++ b/src/main/java/org/etsi/osl/tmf/pm628/api/MeasurementCollectionJobApiRouteBuilder.java @@ -3,7 +3,6 @@ package org.etsi.osl.tmf.pm628.api; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.camel.LoggingLevel; -import org.apache.camel.ProducerTemplate; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.model.dataformat.JsonLibrary; import org.apache.commons.logging.Log; @@ -12,7 +11,6 @@ import org.etsi.osl.centrallog.client.CentralLogger; import org.etsi.osl.tmf.pm628.model.MeasurementCollectionJobFVO; import org.etsi.osl.tmf.pm628.model.MeasurementCollectionJobMVO; import org.etsi.osl.tmf.pm628.reposervices.MeasurementCollectionJobService; -import org.etsi.osl.tmf.pm628.api.MeasurementCollectionJobApiRouteBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; @@ -41,8 +39,8 @@ public class MeasurementCollectionJobApiRouteBuilder extends RouteBuilder { @Value("${PM_MEASUREMENT_COLLECTION_JOB_UPDATE}") private String PM_UPDATE_MEASUREMENT_COLLECTION_JOB; - @Autowired - private ProducerTemplate template; + @Value("${PM_MEASUREMENT_COLLECTION_JOB_GET_INPROGRESS_OR_PENDING}") + private String PM_MEASUREMENT_COLLECTION_JOB_GET_INPROGRESS_OR_PENDING; @Autowired MeasurementCollectionJobService measurementCollectionJobService; @@ -53,16 +51,17 @@ public class MeasurementCollectionJobApiRouteBuilder extends RouteBuilder { @Override public void configure() throws Exception { from(PM_GET_MEASUREMENT_COLLECTION_JOBS) - .log(LoggingLevel.INFO, log, PM_GET_MEASUREMENT_COLLECTION_JOBS + " message received!") - .to("log:DEBUG?showBody=true&showHeaders=true") - .bean(measurementCollectionJobService, "findAllMeasurementCollectionJobs") - .convertBodyTo( String.class ); + .log(LoggingLevel.INFO, log, PM_GET_MEASUREMENT_COLLECTION_JOBS + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean(measurementCollectionJobService, "findAllMeasurementCollectionJobs") + .convertBodyTo(String.class); + from(PM_MEASUREMENT_COLLECTION_GET_JOB_BY_ID) - .log(LoggingLevel.INFO, log, PM_MEASUREMENT_COLLECTION_GET_JOB_BY_ID + " message received!") - .to("log:DEBUG?showBody=true&showHeaders=true") - .bean(measurementCollectionJobService, "findMeasurementCollectionJobByUuidEagerAsString") - .convertBodyTo( String.class ); + .log(LoggingLevel.INFO, log, PM_MEASUREMENT_COLLECTION_GET_JOB_BY_ID + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean(measurementCollectionJobService, "findMeasurementCollectionJobByUuidEagerAsString") + .convertBodyTo(String.class); from(PM_ADD_MEASUREMENT_COLLECTION_JOB) .log(LoggingLevel.INFO, log, PM_ADD_MEASUREMENT_COLLECTION_JOB + " message received!") @@ -70,16 +69,29 @@ public class MeasurementCollectionJobApiRouteBuilder extends RouteBuilder { .unmarshal() .json(JsonLibrary.Jackson, MeasurementCollectionJobFVO.class, true) .bean(measurementCollectionJobService, "createMeasurementCollectionJob(${body})") - .marshal().json( JsonLibrary.Jackson) - .convertBodyTo( String.class ); + .marshal().json(JsonLibrary.Jackson) + .convertBodyTo(String.class); from(PM_UPDATE_MEASUREMENT_COLLECTION_JOB) .log(LoggingLevel.INFO, log, PM_UPDATE_MEASUREMENT_COLLECTION_JOB + " message received!") .to("log:DEBUG?showBody=true&showHeaders=true").unmarshal() .json(JsonLibrary.Jackson, MeasurementCollectionJobMVO.class, true) .bean(measurementCollectionJobService, "updateMeasurementCollectionJob(${header.mcjid}, ${body})") - .marshal().json( JsonLibrary.Jackson) - .convertBodyTo( String.class ); + .marshal().json(JsonLibrary.Jackson) + .convertBodyTo(String.class); + + from(PM_MEASUREMENT_COLLECTION_JOB_GET_INPROGRESS_OR_PENDING) + .log(LoggingLevel.INFO, log, PM_MEASUREMENT_COLLECTION_JOB_GET_INPROGRESS_OR_PENDING + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean(measurementCollectionJobService, "findPendingOrInProgressMeasurementCollectionJobsAsJson") + .process(exchange -> { + Object body = exchange.getIn().getBody(); + if (!(body instanceof String)) { + throw new IllegalArgumentException("Unexpected body type: " + body.getClass()); + } + // Body remains as a String + exchange.getIn().setBody(body); + }); } static String toJsonString(Object object) throws IOException { diff --git a/src/main/java/org/etsi/osl/tmf/pm628/reposervices/MeasurementCollectionJobService.java b/src/main/java/org/etsi/osl/tmf/pm628/reposervices/MeasurementCollectionJobService.java index 4ca0ceaeb4875242189cd57f36867ee9984a4d92..a78a6029aa0d8d95b685dd5419bea5b6f80efb54 100755 --- a/src/main/java/org/etsi/osl/tmf/pm628/reposervices/MeasurementCollectionJobService.java +++ b/src/main/java/org/etsi/osl/tmf/pm628/reposervices/MeasurementCollectionJobService.java @@ -20,6 +20,7 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.io.UnsupportedEncodingException; +import java.net.URI; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; @@ -456,4 +457,42 @@ public class MeasurementCollectionJobService { routeBuilderEvents.publishEvent(event, mcj.getUuid()); } + + /* + This method is creating the JSON array manually because of a bug in the object mapper where: + -> When the object mapper serializes 1 MeasurementCollectionJob it works fine + -> When the object mapper serializes a list of Measurement CollectionJob it ommits the "@type" field + As a result when de-serializing the list of MeasurementCollectionJob this error comes-up: + com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class org.etsi.osl.tmf.pm628.model.MeasurementCollectionJob]: missing type id property '@type' + */ + + @Transactional + public String findPendingOrInProgressMeasurementCollectionJobsAsJson() { + log.debug("findPendingOrInProgressMeasurementCollectionJobsAsJson"); + List pendingOrInProgressMeasurementCollectionJobs = findAllByExecutionState(ExecutionStateType.PENDING); + pendingOrInProgressMeasurementCollectionJobs.addAll(findAllByExecutionState(ExecutionStateType.INPROGRESS)); + + try { + ObjectMapper objectMapper = new ObjectMapper(); + String jsonArray; + + List jobJsonStrings = new ArrayList<>(); + for (MeasurementCollectionJob job : pendingOrInProgressMeasurementCollectionJobs) { + jobJsonStrings.add(objectMapper.writeValueAsString(job)); + } + StringJoiner joiner = new StringJoiner(",", "[", "]"); + for (String jobStr : jobJsonStrings) { + joiner.add(jobStr); + } + jsonArray = joiner.toString(); + log.debug("Serialized JSON Array: {}", jsonArray); + + return jsonArray; + } catch (JsonProcessingException e) { + log.error("Error serializing measurement collection jobs to JSON: {}", e.getMessage()); + throw new RuntimeException("Failed to serialize jobs to JSON", e); + } + } + + } 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, Optional 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 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/ri639/repo/ResourceRepository.java b/src/main/java/org/etsi/osl/tmf/ri639/repo/ResourceRepository.java index 446a04ca1c4193c892f0f27ece6d20126ac148cf..c324cad99e2a734d147fc881981f769e9c726244 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/main/java/org/etsi/osl/tmf/scm633/api/ServiceCatalogApiRouteBuilder.java b/src/main/java/org/etsi/osl/tmf/scm633/api/ServiceCatalogApiRouteBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..afa3892c5eaee8f732aab0b7e3a8d65b316de67d --- /dev/null +++ b/src/main/java/org/etsi/osl/tmf/scm633/api/ServiceCatalogApiRouteBuilder.java @@ -0,0 +1,135 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.tmf.api + * %% + * Copyright (C) 2019 - 2020 openslice.io + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.tmf.scm633.api; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.apache.camel.LoggingLevel; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.model.dataformat.JsonLibrary; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.etsi.osl.tmf.scm633.model.ServiceSpecification; +import org.etsi.osl.tmf.scm633.model.ServiceSpecificationCreate; +import org.etsi.osl.tmf.scm633.model.ServiceSpecificationUpdate; +import org.etsi.osl.tmf.scm633.reposervices.CatalogRepoService; +import org.etsi.osl.tmf.scm633.reposervices.CategoryRepoService; +import org.etsi.osl.tmf.scm633.reposervices.ServiceSpecificationRepoService; +import org.etsi.osl.tmf.sim638.model.ServiceCreate; +import org.etsi.osl.tmf.sim638.model.ServiceUpdate; +import org.etsi.osl.tmf.so641.api.ServiceOrderApiRouteBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +@Configuration +//@RefreshScope +@Component +public class ServiceCatalogApiRouteBuilder extends RouteBuilder { + + private static final transient Log logger = LogFactory.getLog(ServiceCatalogApiRouteBuilder.class.getName()); + + + + @Value("${CATALOG_GET_SERVICECATALOGS}") + private String CATALOG_GET_SERVICECATALOGS = ""; + + @Value("${CATALOG_GET_SERVICECATALOG_BY_ID}") + private String CATALOG_GET_SERVICECATALOG_BY_ID = ""; + + @Value("${CATALOG_GET_SERVICECATALOG_BY_NAME}") + private String CATALOG_GET_SERVICECATALOG_BY_NAME = ""; + + @Value("${CATALOG_GET_SERVICECATEGORIES}") + private String CATALOG_GET_SERVICECATEGORIES = ""; + + @Value("${CATALOG_GET_SERVICECATEGORY_BY_ID}") + private String CATALOG_GET_SERVICECATEGORY_BY_ID = ""; + + + @Value("${CATALOG_GET_SERVICESPECREFS_BYCATEGORY_ID}") + private String CATALOG_GET_SERVICESPECREFS_BYCATEGORY_ID = ""; + + + + + @Autowired + CatalogRepoService catalogRepoService; + + + @Autowired + CategoryRepoService categoryRepoService; + + + @Override + public void configure() throws Exception { + + from( CATALOG_GET_SERVICECATALOG_BY_ID ) + .log(LoggingLevel.INFO, log, CATALOG_GET_SERVICECATALOG_BY_ID + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean( catalogRepoService, "findByUuidEager(${header.catalogId})"); + + from( CATALOG_GET_SERVICECATALOGS ) + .log(LoggingLevel.INFO, log, CATALOG_GET_SERVICECATALOGS + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean( catalogRepoService, "findAllEager()"); + + from( CATALOG_GET_SERVICECATALOG_BY_NAME ) + .log(LoggingLevel.INFO, log, CATALOG_GET_SERVICECATALOG_BY_NAME + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean( catalogRepoService, "findByNameEager(${header.catalogName})") + .marshal().json( JsonLibrary.Jackson, String.class) + .convertBodyTo( String.class ); + + + from( CATALOG_GET_SERVICECATEGORIES ) + .log(LoggingLevel.INFO, log, CATALOG_GET_SERVICECATEGORIES + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean( catalogRepoService, "findAllCategoriesByCatalogName(${header.catalogName})"); + + + from( CATALOG_GET_SERVICECATEGORY_BY_ID ) + .log(LoggingLevel.INFO, log, CATALOG_GET_SERVICECATEGORY_BY_ID + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean( categoryRepoService, "findByIdEager(${header.catalogId})"); + + + from( CATALOG_GET_SERVICESPECREFS_BYCATEGORY_ID ) + .log(LoggingLevel.INFO, log, CATALOG_GET_SERVICESPECREFS_BYCATEGORY_ID + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .bean( categoryRepoService, "findAllServiceSpecRefsByCategId(${header.categoryId})"); + + } + + + + + + static T toJsonObj(String content, Class valueType) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.readValue( content, valueType); + } + +} diff --git a/src/main/java/org/etsi/osl/tmf/scm633/api/ServiceSpecificationApiRouteBuilder.java b/src/main/java/org/etsi/osl/tmf/scm633/api/ServiceSpecificationApiRouteBuilder.java index 8116a38c20479395b44ff0e2e7770f470f5c7f2f..ed5f01481ad6635cb123a3e33ffaeed08a49b6d6 100644 --- a/src/main/java/org/etsi/osl/tmf/scm633/api/ServiceSpecificationApiRouteBuilder.java +++ b/src/main/java/org/etsi/osl/tmf/scm633/api/ServiceSpecificationApiRouteBuilder.java @@ -20,7 +20,7 @@ package org.etsi.osl.tmf.scm633.api; import java.io.IOException; - +import java.util.ArrayList; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; @@ -46,7 +46,7 @@ import org.springframework.stereotype.Component; @Component public class ServiceSpecificationApiRouteBuilder extends RouteBuilder { - private static final transient Log logger = LogFactory.getLog(ServiceOrderApiRouteBuilder.class.getName()); + private static final transient Log logger = LogFactory.getLog(ServiceSpecificationApiRouteBuilder.class.getName()); @Value("${CATALOG_GET_SERVICESPEC_BY_ID}") private String CATALOG_GET_SERVICESPEC_BY_ID = ""; @@ -67,7 +67,14 @@ public class ServiceSpecificationApiRouteBuilder extends RouteBuilder { @Value("${CATALOG_UPD_EXTERNAL_SERVICESPEC}") private String CATALOG_UPD_EXTERNAL_SERVICESPEC = ""; - + + + @Value("${CATALOG_SEARCH_SERVICESPECREFS}") + private String CATALOG_SEARCH_SERVICESPECREFS = ""; + + + + @Autowired ServiceSpecificationRepoService serviceSpecificationRepoService; @@ -114,6 +121,16 @@ public class ServiceSpecificationApiRouteBuilder extends RouteBuilder { .bean( serviceSpecificationRepoService, "updateOrAddServiceSpecification(${header.serviceSpecId}, ${header.forceId}, ${body} )") .marshal().json( JsonLibrary.Jackson) .convertBodyTo( String.class ); + + + + from( CATALOG_SEARCH_SERVICESPECREFS ) + .log(LoggingLevel.INFO, log, CATALOG_SEARCH_SERVICESPECREFS + " message received!") + .to("log:DEBUG?showBody=true&showHeaders=true") + .unmarshal().json( JsonLibrary.Jackson, ArrayList.class, true) + .bean( serviceSpecificationRepoService, "searchServiceSpecRefs( ${body} )"); + + } 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 findByServiceSpecUuid(String id); + // Methods for metrics + @Query("SELECT sc FROM ServiceCandidate sc") + List findAll(); + } diff --git a/src/main/java/org/etsi/osl/tmf/scm633/reposervices/CandidateRepoService.java b/src/main/java/org/etsi/osl/tmf/scm633/reposervices/CandidateRepoService.java index 872a8229686fa2cf112d5769db4efb4f29281068..24cd95d1a87309f68ffe1e02e081d30bc747b59b 100644 --- a/src/main/java/org/etsi/osl/tmf/scm633/reposervices/CandidateRepoService.java +++ b/src/main/java/org/etsi/osl/tmf/scm633/reposervices/CandidateRepoService.java @@ -39,18 +39,26 @@ import org.springframework.transaction.annotation.Transactional; import jakarta.validation.Valid; @Service +@Transactional public class CandidateRepoService { - @Autowired - CandidateRepository candidateRepo; + private final CandidateRepository candidateRepo; - @Autowired - CategoryRepoService categsRepoService; - + private final CategoryRepoService categoryRepoService; + private final ServiceSpecificationRepository serviceSpecificationRepo; + @Autowired - ServiceSpecificationRepository serviceSpecificationRepo; + public CandidateRepoService( + CandidateRepository candidateRepo, + CategoryRepoService categoryRepoService, + ServiceSpecificationRepository serviceSpecificationRepo + ) { + this.candidateRepo = candidateRepo; + this.categoryRepoService = categoryRepoService; + this.serviceSpecificationRepo = serviceSpecificationRepo; + } public ServiceCandidate addServiceCandidate( ServiceCandidate c) { @@ -153,11 +161,11 @@ public class CandidateRepoService { if ( serviceCandidateUpd.getCategory() !=null ){ for (ServiceCategoryRef sCategD : serviceCandidateUpd.getCategory()) { - ServiceCategory catObj = this.categsRepoService.findByIdEager(sCategD.getId()); + ServiceCategory catObj = this.categoryRepoService.findByUuid(sCategD.getId()); if ( catObj!=null){ catObj.getServiceCandidateObj().add(savedCand); //add candidate ref to category - catObj = this.categsRepoService.categsRepo.save(catObj); + catObj = this.categoryRepoService.getCategsRepo().save(catObj); } } diff --git a/src/main/java/org/etsi/osl/tmf/scm633/reposervices/CatalogRepoService.java b/src/main/java/org/etsi/osl/tmf/scm633/reposervices/CatalogRepoService.java index db4c70237acaab21e585f1ce3e41a0fe10b98ec2..124f96125b32cb49a85d6e6f27c3be4e0e2bb342 100644 --- a/src/main/java/org/etsi/osl/tmf/scm633/reposervices/CatalogRepoService.java +++ b/src/main/java/org/etsi/osl/tmf/scm633/reposervices/CatalogRepoService.java @@ -21,8 +21,12 @@ package org.etsi.osl.tmf.scm633.reposervices; import java.time.OffsetDateTime; import java.time.ZoneOffset; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.hibernate5.jakarta.Hibernate5JakartaModule; import org.etsi.osl.tmf.common.model.ELifecycle; import org.etsi.osl.tmf.common.model.TimePeriod; import org.etsi.osl.tmf.scm633.model.ServiceCatalog; @@ -30,12 +34,21 @@ import org.etsi.osl.tmf.scm633.model.ServiceCatalogCreate; import org.etsi.osl.tmf.scm633.model.ServiceCatalogUpdate; import org.etsi.osl.tmf.scm633.model.ServiceCategory; import org.etsi.osl.tmf.scm633.model.ServiceCategoryRef; +import org.etsi.osl.tmf.scm633.model.ServiceSpecCharacteristic; +import org.etsi.osl.tmf.scm633.model.ServiceSpecification; import org.etsi.osl.tmf.scm633.repo.CatalogRepository; +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import jakarta.persistence.EntityManagerFactory; import jakarta.validation.Valid; @Service +@Transactional public class CatalogRepoService { @@ -46,10 +59,8 @@ public class CatalogRepoService { CategoryRepoService categRepoService; @Autowired - ServiceSpecificationRepoService specRepoService; - - @Autowired - CandidateRepoService candidateRepoService; + ServiceSpecificationRepoService specRepoService; + public ServiceCatalog addCatalog(ServiceCatalog c) { @@ -64,10 +75,75 @@ public class CatalogRepoService { return this.catalogRepo.save(sc); } + public String findAllEager() { + + List oids = (List) this.catalogRepo.findByOrderByName(); + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + String res; + try { + res = mapper.writeValueAsString( oids ); + } catch (JsonProcessingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return "{}"; + } + + return res; + } + + + public List findAll() { return (List) this.catalogRepo.findByOrderByName(); } + + public String findByUuidEager(String id) { + + ServiceCatalog sc = this.findById(id); + + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + String res; + try { + res = mapper.writeValueAsString( sc ); + } catch (JsonProcessingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return "{}"; + } + + return res; + } + + + + public String findByNameEager(String aname) { + ServiceCatalog sc = this.findByName(aname); + + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + String res; + try { + res = mapper.writeValueAsString( sc ); + } catch (JsonProcessingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return "{}"; + } + + return res; + + } + + public ServiceCatalog findById(String id) { Optional optionalCat = this.catalogRepo.findByUuid(id); return optionalCat.orElse(null); @@ -136,6 +212,65 @@ public class CatalogRepoService { public ServiceCatalog updateCatalog(ServiceCatalog scatalog) { return this.catalogRepo.save(scatalog); } + + + /** + * return recursively all categories in catalog + * @param catalogName + */ + @Transactional + public String findAllCategoriesByCatalogName(String catalogName) { + String res="[]"; + + Optional scopt = this.catalogRepo.findByName(catalogName); + + if (scopt.isEmpty() ) { + return res; + } + + ServiceCatalog sc = scopt.get(); + + sc.getCategoryRefs(); + + + List allcategories = this.getCategories( sc.getCategoryRefs()); + + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + + + try { + res = mapper.writeValueAsString( allcategories ); + } catch (JsonProcessingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + return res; + + } + + + @Transactional + private List getCategories( @Valid List list) { + List categories = new ArrayList(); + + for (ServiceCategoryRef c : list ) { + ServiceCategory category = this.categRepoService.findByUuid( c.getId()); + categories.add(category); + + if (category.getCategoryRefs()!=null && category.getCategoryRefs().size()>0) { + List subcategories = this.getCategories( category.getCategoryRefs() ); + categories.addAll(subcategories );//add children + } + + } + + return categories; + } diff --git a/src/main/java/org/etsi/osl/tmf/scm633/reposervices/CategoryRepoService.java b/src/main/java/org/etsi/osl/tmf/scm633/reposervices/CategoryRepoService.java index cb43791abb4ecb3c54a7414e80badeab7b6b20f6..2ebf74d01b9a64db3389e6f295fe90e46a60842b 100644 --- a/src/main/java/org/etsi/osl/tmf/scm633/reposervices/CategoryRepoService.java +++ b/src/main/java/org/etsi/osl/tmf/scm633/reposervices/CategoryRepoService.java @@ -23,67 +23,49 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.hibernate5.jakarta.Hibernate5JakartaModule; import org.etsi.osl.tmf.common.model.ELifecycle; import org.etsi.osl.tmf.common.model.TimePeriod; +import org.etsi.osl.tmf.common.model.service.ServiceSpecificationRef; import org.etsi.osl.tmf.scm633.model.ServiceCandidate; import org.etsi.osl.tmf.scm633.model.ServiceCandidateRef; import org.etsi.osl.tmf.scm633.model.ServiceCategory; import org.etsi.osl.tmf.scm633.model.ServiceCategoryCreate; import org.etsi.osl.tmf.scm633.model.ServiceCategoryRef; import org.etsi.osl.tmf.scm633.model.ServiceCategoryUpdate; -import org.etsi.osl.tmf.scm633.model.ServiceSpecCharRelationship; -import org.etsi.osl.tmf.scm633.model.ServiceSpecCharacteristic; -import org.etsi.osl.tmf.scm633.model.ServiceSpecCharacteristicValue; import org.etsi.osl.tmf.scm633.repo.CandidateRepository; -import org.etsi.osl.tmf.scm633.repo.CatalogRepository; import org.etsi.osl.tmf.scm633.repo.CategoriesRepository; -import org.hibernate.Hibernate; -import org.hibernate.Session; -import org.hibernate.SessionFactory; -import org.hibernate.Transaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import jakarta.persistence.EntityManagerFactory; +import org.springframework.transaction.annotation.Transactional; import jakarta.validation.Valid; @Service +@Transactional public class CategoryRepoService { - @Autowired - CategoriesRepository categsRepo; - - - @Autowired - CandidateRepository candidateRepo; - - @Autowired - CatalogRepository catalogRepo; + private final CategoriesRepository categsRepo; + private final CandidateRepository candidateRepo; - private SessionFactory sessionFactory; - /** - * from https://stackoverflow.com/questions/25063995/spring-boot-handle-to-hibernate-sessionfactory - * @param factory - */ - @Autowired - public CategoryRepoService(EntityManagerFactory factory) { - if(factory.unwrap(SessionFactory.class) == null){ - throw new NullPointerException("factory is not a hibernate factory"); - } - this.sessionFactory = factory.unwrap(SessionFactory.class); - } - + @Autowired + public CategoryRepoService(CategoriesRepository categsRepo, CandidateRepository candidateRepo) { + this.categsRepo = categsRepo; + this.candidateRepo = candidateRepo; + } public ServiceCategory addCategory(ServiceCategory c) { - return this.categsRepo.save( c ); + return this.getCategsRepo().save( c ); } public ServiceCategory addCategory(@Valid ServiceCategoryCreate serviceCategory) { @@ -91,71 +73,99 @@ public class CategoryRepoService { ServiceCategory sc = new ServiceCategory() ; sc = updateCategoryDataFromAPICall(sc, serviceCategory); - return this.categsRepo.save( sc ); + return this.getCategsRepo().save( sc ); } + @Transactional public List findAll() { - return (List) this.categsRepo.findByOrderByName(); + return (List) this.getCategsRepo().findByOrderByName(); } + @Transactional public ServiceCategory findByUuid(String id) { - Optional optionalCat = this.categsRepo.findByUuid( id ); + Optional optionalCat = this.getCategsRepo().findByUuid( id ); return optionalCat .orElse(null); } - public ServiceCategory findByIdEager(String id) { -// Optional optionalCat = this.categsRepo.findByIdEager( id ); -// return optionalCat -// .orElse(null); + @Transactional + public String findByIdEager(String id) { + ServiceCategory sc = this.findByUuid( id ); + + String res= "{}"; + + if ( sc == null ) { + return res; + } + - Session session = sessionFactory.openSession(); - Transaction tx = session.beginTransaction(); - ServiceCategory dd = null; - try { - dd = (ServiceCategory) session.get(ServiceCategory.class, id); - Hibernate.initialize( dd.getCategoryObj() ); - Hibernate.initialize( dd.getServiceCandidateObj() ); - for (ServiceCandidate sc : dd.getServiceCandidateObj()) { - Hibernate.initialize(sc ); - Hibernate.initialize(sc.getCategoryObj() ); - Hibernate.initialize(sc.getServiceSpecificationObj() ); - Hibernate.initialize(sc.getServiceSpecificationObj().getServiceSpecCharacteristic() ); - for (ServiceSpecCharacteristic ssc : sc.getServiceSpecificationObj().getServiceSpecCharacteristic() ) { - Hibernate.initialize(ssc.getServiceSpecCharRelationship() ); - for (ServiceSpecCharRelationship srel : ssc.getServiceSpecCharRelationship() ) { - Hibernate.initialize( srel ); - } - Hibernate.initialize(ssc.getServiceSpecCharacteristicValue() ); - for (ServiceSpecCharacteristicValue srel : ssc.getServiceSpecCharacteristicValue() ) { - Hibernate.initialize( srel ); - } - } - Hibernate.initialize(sc.getServiceSpecificationObj().getServiceSpecRelationship() ); - } - - tx.commit(); - } finally { - session.close(); - } - return dd; + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + + try { + res = mapper.writeValueAsString( sc ); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + + return res; } - + + + @Transactional + public String findAllServiceSpecRefsByCategId(String categoryId) { + + + String res="[]"; + List serviceSpecificationRefList = new ArrayList<>(); + ServiceCategory category = this.findByUuid(categoryId); + + if ( category == null ) { + return res; + } + + Set serviceCands = category.getServiceCandidateObj(); + + for (ServiceCandidate serviceCandidate : serviceCands) { + @Valid + ServiceSpecificationRef specRef = serviceCandidate.getServiceSpecificationRef(); + serviceSpecificationRefList.add(specRef); + } + + + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + + try { + res = mapper.writeValueAsString( serviceSpecificationRefList ); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + + return res; + + } public boolean deleteById(String id) { - Optional optionalCat = this.categsRepo.findByUuid( id ); + Optional optionalCat = this.getCategsRepo().findByUuid( id ); if ( optionalCat.get().getCategoryObj().size()>0 ) { return false; //has children } if ( optionalCat.get().getParentId() != null ) { - ServiceCategory parentCat = (this.categsRepo.findByUuid( optionalCat.get().getParentId() )).get(); + ServiceCategory parentCat = (this.getCategsRepo().findByUuid( optionalCat.get().getParentId() )).get(); //remove from parent category for (ServiceCategory ss : parentCat.getCategoryObj()) { @@ -164,24 +174,24 @@ public class CategoryRepoService { break; } } - parentCat = this.categsRepo.save(parentCat); + parentCat = this.getCategsRepo().save(parentCat); } - this.categsRepo.delete( optionalCat.get()); + this.getCategsRepo().delete( optionalCat.get()); return true; } public ServiceCategory updateCategory(String id, @Valid ServiceCategoryUpdate serviceCategory) { - Optional optionalCat = this.categsRepo.findByUuid( id ); + Optional optionalCat = this.getCategsRepo().findByUuid( id ); if ( optionalCat == null ) { return null; } ServiceCategory sc = optionalCat.get(); sc = updateCategoryDataFromAPICall(sc, serviceCategory); - return this.categsRepo.save( sc ); + return this.getCategsRepo().save( sc ); } public ServiceCategory updateCategoryDataFromAPICall( ServiceCategory sc, ServiceCategoryUpdate serviceCatUpd ) @@ -227,14 +237,14 @@ public class CategoryRepoService { } } if (!idexists) { - Optional catToAdd = this.categsRepo.findByUuid( ref.getId() ); + Optional catToAdd = this.getCategsRepo().findByUuid( ref.getId() ); if ( catToAdd.isPresent() ) { ServiceCategory scatadd = catToAdd.get(); sc.getCategoryObj().add( scatadd ); idAddedUpdated.put( ref.getId(), true); scatadd.setParentId( sc.getUuid()); - scatadd = this.categsRepo.save( scatadd ); + scatadd = this.getCategsRepo().save( scatadd ); } } } @@ -292,10 +302,15 @@ public class CategoryRepoService { public ServiceCategory findByName(String aName) { - Optional optionalCat = this.categsRepo.findByName( aName ); + Optional optionalCat = this.getCategsRepo().findByName( aName ); return optionalCat .orElse(null); } + public CategoriesRepository getCategsRepo() { + return categsRepo; + + } + } diff --git a/src/main/java/org/etsi/osl/tmf/scm633/reposervices/ServiceSpecificationRepoService.java b/src/main/java/org/etsi/osl/tmf/scm633/reposervices/ServiceSpecificationRepoService.java index eb6ca09c1cf0d752a8a84412b2b5b054b6e2b2a5..9b3f2fdf4108328e2a9cfec761d327e04fb31863 100644 --- a/src/main/java/org/etsi/osl/tmf/scm633/reposervices/ServiceSpecificationRepoService.java +++ b/src/main/java/org/etsi/osl/tmf/scm633/reposervices/ServiceSpecificationRepoService.java @@ -34,10 +34,12 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.StringJoiner; import java.util.UUID; - +import java.util.stream.Collectors; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; - +import com.fasterxml.jackson.datatype.hibernate5.jakarta.Hibernate5JakartaModule; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.etsi.osl.tmf.common.model.Any; @@ -62,6 +64,7 @@ import org.etsi.osl.tmf.scm633.api.ServiceSpecificationApiRouteBuilderNSD; import org.etsi.osl.tmf.scm633.model.ServiceCandidate; import org.etsi.osl.tmf.scm633.model.ServiceCandidateCreate; import org.etsi.osl.tmf.scm633.model.ServiceCandidateUpdate; +import org.etsi.osl.tmf.scm633.model.ServiceCategory; import org.etsi.osl.tmf.scm633.model.ServiceSpecCharacteristic; import org.etsi.osl.tmf.scm633.model.ServiceSpecCharacteristicValue; import org.etsi.osl.tmf.scm633.model.ServiceSpecRelationship; @@ -208,13 +211,12 @@ public class ServiceSpecificationRepoService { } sql += " FROM ServiceSpecification s"; - if (allParams.size() > 0) { - sql += " WHERE "; - for (String pname : allParams.keySet()) { - sql += " " + pname + " LIKE "; - String pval = URLDecoder.decode(allParams.get(pname), StandardCharsets.UTF_8.toString()); - sql += "'" + pval + "'"; - } + if (allParams.size() > 0) { + String items = allParams.entrySet() + .stream() + .map(entry -> "s." + entry.getKey() + " LIKE '%" + URLDecoder.decode( entry.getValue(), StandardCharsets.UTF_8 )+ "%'" ) + .collect(Collectors.joining(" OR ")); + sql += " WHERE " + items; } sql += " ORDER BY s.name"; @@ -230,10 +232,10 @@ public class ServiceSpecificationRepoService { Map result = new LinkedHashMap(tuple.length); for (int i = 0; i < tuple.length; i++) { String alias = aliases[i]; - if (alias.equals("type")) { - alias = "@type"; - } if (alias != null) { + if (alias.equals("type")) { + alias = "@type"; + } result.put(alias, tuple[i]); } } @@ -271,7 +273,11 @@ public class ServiceSpecificationRepoService { } -// @Transactional(propagation=Propagation.REQUIRED , readOnly=true, + + + + + // @Transactional(propagation=Propagation.REQUIRED , readOnly=true, // noRollbackFor=Exception.class) public ServiceSpecification findByUuid(String id) { Optional optionalCat = this.serviceSpecificationRepo.findByUuid(id); @@ -1464,6 +1470,124 @@ public class ServiceSpecificationRepoService { } + @Transactional + public String searchServiceSpecRefs(List searchText) { + String res = "[]"; + + try { + + List specs= this.searchSpecsInCategories( searchText); + + ObjectMapper mapper = new ObjectMapper(); + // Registering Hibernate4Module to support lazy objects + // this will fetch all lazy objects before marshaling + mapper.registerModule(new Hibernate5JakartaModule()); + res = mapper.writeValueAsString( specs ); + + + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + + return res; + } + + + /** + * + * This findAll is optimized on fields. + * @param fields + * @param allParams + * @return + * @throws UnsupportedEncodingException + */ + @Transactional + public List searchSpecsInCategories( List searchList ) + throws UnsupportedEncodingException { + + if ( searchList == null || searchList.size() ==0) { + return new ArrayList<>(); + } + + Session session = sessionFactory.openSession(); + Transaction tx = session.beginTransaction(); + + try { + String sql = "SELECT s.id as serviceSpecificationId, s.name as serviceName, s.description as serviceDescription"; + + + + sql += " FROM ServiceCategory as scateg JOIN scateg.serviceCandidateObj as scandidate JOIN scandidate.serviceSpecificationObj as s "; + sql += " WHERE " ; + + + // Build the name LIKE clause + StringJoiner nameJoiner = new StringJoiner(" AND "); + for (String term : searchList) { + nameJoiner.add("s.name LIKE '%" + term + "%'"); + } + + // Build the description LIKE clause + StringJoiner descriptionJoiner = new StringJoiner(" AND "); + for (String term : searchList) { + descriptionJoiner.add("s.description LIKE '%" + term + "%'"); + } + + // Combine both clauses with OR + sql += "(" + nameJoiner.toString() + ") OR (" + descriptionJoiner.toString() + ")"; + + sql += " ORDER BY s.name"; + +// List specs = session +// .createQuery( sql, ServiceSpecification.class) +// .getResultList(); + + + List mapaEntity = session + .createQuery(sql ) + .setResultTransformer( new ResultTransformer() { + + @Override + public Object transformTuple(Object[] tuple, String[] aliases) { + Map result = new LinkedHashMap(tuple.length); + for (int i = 0; i < tuple.length; i++) { + String alias = aliases[i]; + if (alias != null) { + if (alias.equals("type")) { + alias = "@type"; + } + result.put(alias, tuple[i]); + } + } + + return result; + } + + @Override + public List transformList(List collection) { + return collection; + } + } ) + .list(); + +// //this will fetch the whole object fields + + + return mapaEntity; + + + + + } finally { + tx.commit(); + session.close(); + } + + } + } diff --git a/src/main/java/org/etsi/osl/tmf/sim638/repo/ServiceRepository.java b/src/main/java/org/etsi/osl/tmf/sim638/repo/ServiceRepository.java index 8c920375ad11b35fcd9eb5ad794f3e029ad6f839..ee3d9c3b56894e9b87299d26465af958924dce54 100644 --- a/src/main/java/org/etsi/osl/tmf/sim638/repo/ServiceRepository.java +++ b/src/main/java/org/etsi/osl/tmf/sim638/repo/ServiceRepository.java @@ -19,8 +19,11 @@ */ package org.etsi.osl.tmf.sim638.repo; +import java.time.OffsetDateTime; import java.util.List; import java.util.Optional; + +import org.etsi.osl.tmf.common.model.service.ServiceStateType; import org.etsi.osl.tmf.sim638.model.Service; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -68,4 +71,15 @@ public interface ServiceRepository extends JpaRepository { + "WHERE sres.id = ?1 " ) List findServicesHavingThisSupportingResourceID(String resourceID); + // Methods for metrics + + @Query("SELECT COUNT(srv) FROM Service srv") + int countAll(); + + int countByState(ServiceStateType state); + + @Query("SELECT srv.state, COUNT(srv) FROM Service srv " + + "WHERE srv.startDate >= :starttime AND srv.endDate <= :endtime " + + "GROUP BY srv.state") + List groupByStateBetweenDates(OffsetDateTime starttime, OffsetDateTime endtime); } diff --git a/src/main/java/org/etsi/osl/tmf/so641/repo/ServiceOrderRepository.java b/src/main/java/org/etsi/osl/tmf/so641/repo/ServiceOrderRepository.java index c717e063b2fd3728703133654adcc4f3a58e6e04..b6ee5c83b42f86826ac670677dbbd7858dfa57d0 100644 --- a/src/main/java/org/etsi/osl/tmf/so641/repo/ServiceOrderRepository.java +++ b/src/main/java/org/etsi/osl/tmf/so641/repo/ServiceOrderRepository.java @@ -19,6 +19,7 @@ */ package org.etsi.osl.tmf.so641.repo; +import java.time.OffsetDateTime; import java.util.List; import java.util.Optional; import org.etsi.osl.tmf.common.model.UserPartRoleType; @@ -51,4 +52,27 @@ public interface ServiceOrderRepository extends JpaRepository findNotesOfServOrder(String id); + + + // Methods for metrics + + @Query("SELECT COUNT(sor) FROM ServiceOrder sor") + int countAll(); + + int countByState(ServiceOrderStateType state); + + @Query("SELECT COUNT(sor) FROM ServiceOrder sor " + + "WHERE sor.state IN :states " + + "AND sor.requestedStartDate < :currentDate AND sor.requestedCompletionDate > :currentDate") + int countAllActive(OffsetDateTime currentDate, List states); + + @Query("SELECT sor.state, COUNT(sor) FROM ServiceOrder sor " + + "WHERE sor.orderDate >= :starttime AND sor.orderDate <= :endtime " + + "GROUP BY sor.state") + List groupByStateBetweenDates(OffsetDateTime starttime, OffsetDateTime endtime); + + @Query("SELECT sor.orderDate FROM ServiceOrder sor " + + "WHERE sor.orderDate >= :starttime AND sor.orderDate <= :endtime") + List getOrderDatesBetweenDates(OffsetDateTime starttime, OffsetDateTime endtime); + } diff --git a/src/main/resources/application-testing.yml b/src/main/resources/application-testing.yml index 0ec537c55d1b5267645cb41bb7d15cba12837e4b..dc15a9c271e512228cced92f454f39da56b09f55 100644 --- a/src/main/resources/application-testing.yml +++ b/src/main/resources/application-testing.yml @@ -80,15 +80,57 @@ oauthsign: key: "XXX" #QUEUE MESSAGES -CATALOG_GET_SERVICEORDERS: "jms:queue:CATALOG.GET.SERVICEORDERS" -CATALOG_GET_SERVICEORDER_BY_ID: "jms:queue:CATALOG.GET.SERVICEORDER_BY_ID" -CATALOG_ADD_SERVICEORDER: "jms:queue:CATALOG.ADD.SERVICEORDER" -CATALOG_UPD_SERVICEORDER_BY_ID: "jms:queue:CATALOG.UPD.SERVICEORDER_BY_ID" CATALOG_GET_SERVICESPEC_BY_ID: "jms:queue:CATALOG.GET.SERVICESPEC_BY_ID" CATALOG_ADD_SERVICESPEC: "jms:queue:CATALOG.ADD.SERVICESPEC" CATALOG_UPD_SERVICESPEC: "jms:queue:CATALOG.UPD.SERVICESPEC" CATALOG_UPDADD_SERVICESPEC: "jms:queue:CATALOG.UPDADD.SERVICESPEC" +CATALOG_SERVICE_QUEUE_ITEMS_GET: "jms:queue:CATALOG.SERVICEQUEUEITEMS.GET" +CATALOG_SERVICE_QUEUE_ITEM_UPD: "jms:queue:CATALOG.SERVICEQUEUEITEM.UPDATE" +CATALOG_SERVICE_QUEUE_ITEM_DELETE: "jms:queue:CATALOG.SERVICEQUEUEITEM.DELETE" +CATALOG_GET_PARTNER_ORGANIZATON_BY_ID: "jms:queue:CATALOG.GET.PARTNER_ORGANIZATION_BY_ID" +CATALOG_UPDATE_PARTNER_ORGANIZATION: "jms:queue:CATALOG.UPD.PARTNER_ORGANIZATION" +CATALOG_SERVICES_TO_TERMINATE: "jms:queue:CATALOG.GET.SERVICETOTERMINATE" +CATALOG_SERVICES_OF_PARTNERS: "jms:queue:CATALOG.GET.SERVICESOFPARTNERS" +CATALOG_GET_EXTERNAL_SERVICE_PARTNERS: "jms:queue:CATALOG.GET.EXTERNALSERVICEPARTNERS" +CATALOG_UPD_EXTERNAL_SERVICESPEC: "jms:queue:CATALOG.UPD.EXTERNAL_SERVICESPEC" + +#SERVICE_INVENTORY +CATALOG_ADD_SERVICE: "jms:queue:CATALOG.ADD.SERVICE" +CATALOG_UPD_SERVICE: "jms:queue:CATALOG.UPD.SERVICE" +CATALOG_GET_SERVICE_BY_ID: "jms:queue:CATALOG.GET.SERVICE" +CATALOG_GET_SERVICE_BY_ORDERID: "jms:queue:CATALOG.GET.SERVICE_BY_ORDERID" + + +#SERVICE ORDERS +CATALOG_GET_SERVICEORDERS: "jms:queue:CATALOG.GET.SERVICEORDERS" +CATALOG_GET_SERVICEORDER_BY_ID: "jms:queue:CATALOG.GET.SERVICEORDER_BY_ID" +CATALOG_ADD_SERVICEORDER: "jms:queue:CATALOG.ADD.SERVICEORDER" +CATALOG_UPD_SERVICEORDER_BY_ID: "jms:queue:CATALOG.UPD.SERVICEORDER_BY_ID" +CATALOG_GET_INITIAL_SERVICEORDERS_IDS: "jms:queue:CATALOG.GET.INITIAL_SERVICEORDERS" +CATALOG_GET_SERVICEORDER_IDS_BY_STATE: "jms:queue:CATALOG.GET.ACKNOWLEDGED_SERVICEORDERS" +CATALOG_GET_PRODUCTCATALOGS: "jms:queue:CATALOG.GET.PRODUCTCATALOGS" +CATALOG_GET_PRODUCTCATALOG_BY_ID: "jms:queue:CATALOG.GET.PRODUCTCATALOG_BY_ID" +CATALOG_GET_PRODUCTCATALOG_BY_NAME: "jms:queue:CATALOG.GET.PRODUCTCATALOG_BY_NAME" +CATALOG_GET_PRODUCTCATEGORIES: "jms:queue:CATALOG.GET.PRODUCTCATEGORIES" +CATALOG_GET_PRODUCTCATEGORY_BY_ID: "jms:queue:CATALOG.GET.PRODUCTCATEGORY_BY_ID" +CATALOG_GET_PRODUCTOFFERINGS_BYCATEGORY_ID: "jms:queue:CATALOG.GET.PRODUCTOFFERINGS.CATEGORY_ID" +CATALOG_SEARCH_PRODUCTOFFERINGS: "jms:queue:CATALOG.CATALOG_SEARCH_PRODUCTOFFERINGS" + + +#SERVICE CATALOGS, CATEGORIES +CATALOG_GET_SERVICECATALOGS: "jms:queue:CATALOG.GET.SERVICECATALOGS" +CATALOG_GET_SERVICECATALOG_BY_ID: "jms:queue:CATALOG.GET.SERVICECATALOG_BY_ID" +CATALOG_GET_SERVICECATALOG_BY_NAME: "jms:queue:CATALOG.GET.SERVICECATALOG_BY_NAME" + +CATALOG_GET_SERVICECATEGORIES: "jms:queue:CATALOG.GET.SERVICECATEGORIES" +CATALOG_GET_SERVICECATEGORY_BY_ID: "jms:queue:CATALOG.GET.SERVICECATEGORY_BY_ID" + +CATALOG_GET_SERVICESPECREFS_BYCATEGORY_ID: "jms:queue:CATALOG.GETSERVICESPECREFS.SERVICECATEGORY_BY_ID" +CATALOG_SEARCH_SERVICESPECREFS: "jms:queue:CATALOG.CATALOG_SEARCH_SERVICESPECREFS" + + +#PRODUCT CATALOGS CATALOG_GET_PRODUCTSPEC_BY_ID: "jms:queue:CATALOG.GET.PRODUCTSPEC_BY_ID" CATALOG_ADD_PRODUCTSPEC: "jms:queue:CATALOG.ADD.PRODUCTSPEC" CATALOG_UPD_PRODUCTSPEC: "jms:queue:CATALOG.UPD.PRODUCTSPEC" @@ -100,29 +142,17 @@ CATALOG_ADD_PRODUCTORDER: "jms:queue:CATALOG.ADD.PRODUCTORDER" CATALOG_UPD_PRODUCTORDER_BY_ID: "jms:queue:CATALOG.UPD.PRODUCTORDER_BY_ID" CATALOG_GET_INITIAL_PRODUCTORDERS_IDS: "jms:queue:CATALOG.GET.INITIAL_PRODUCTORDERS" CATALOG_GET_PRODUCTORDER_IDS_BY_STATE: "jms:queue:CATALOG.GET.ACKNOWLEDGED_PRODUCTORDERS" -CATALOG_GET_INITIAL_SERVICEORDERS_IDS: "jms:queue:CATALOG.GET.INITIAL_SERVICEORDERS" -CATALOG_GET_SERVICEORDER_IDS_BY_STATE: "jms:queue:CATALOG.GET.ACKNOWLEDGED_SERVICEORDERS" -CATALOG_ADD_SERVICE: "jms:queue:CATALOG.ADD.SERVICE" -CATALOG_UPD_SERVICE: "jms:queue:CATALOG.UPD.SERVICE" -CATALOG_GET_SERVICE_BY_ID: "jms:queue:CATALOG.GET.SERVICE" -CATALOG_GET_SERVICE_BY_ORDERID: "jms:queue:CATALOG.GET.SERVICE_BY_ORDERID" -CATALOG_SERVICE_QUEUE_ITEMS_GET: "jms:queue:CATALOG.SERVICEQUEUEITEMS.GET" -CATALOG_SERVICE_QUEUE_ITEM_UPD: "jms:queue:CATALOG.SERVICEQUEUEITEM.UPDATE" -CATALOG_SERVICE_QUEUE_ITEM_DELETE: "jms:queue:CATALOG.SERVICEQUEUEITEM.DELETE" -CATALOG_GET_PARTNER_ORGANIZATON_BY_ID: "jms:queue:CATALOG.GET.PARTNER_ORGANIZATION_BY_ID" -CATALOG_UPDATE_PARTNER_ORGANIZATION: "jms:queue:CATALOG.UPD.PARTNER_ORGANIZATION" -CATALOG_SERVICES_TO_TERMINATE: "jms:queue:CATALOG.GET.SERVICETOTERMINATE" -CATALOG_SERVICES_OF_PARTNERS: "jms:queue:CATALOG.GET.SERVICESOFPARTNERS" -CATALOG_GET_EXTERNAL_SERVICE_PARTNERS: "jms:queue:CATALOG.GET.EXTERNALSERVICEPARTNERS" -CATALOG_UPD_EXTERNAL_SERVICESPEC: "jms:queue:CATALOG.UPD.EXTERNAL_SERVICESPEC" + + +#PM_MEASUREMENT_COLLECTION PM_MEASUREMENT_COLLECTION_GET_JOB_BY_ID: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.GET_BY_ID" PM_MEASUREMENT_COLLECTION_JOBS_GET: "jms:queue:PM.MEASUREMENTCOLLECTIONJOBS.GET" PM_MEASUREMENT_COLLECTION_JOB_ADD: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.ADD" PM_MEASUREMENT_COLLECTION_JOB_CREATED: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.CREATED" PM_MEASUREMENT_COLLECTION_JOB_UPDATE: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.UPDATE" - +PM_MEASUREMENT_COLLECTION_JOB_GET_INPROGRESS_OR_PENDING: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.GET_INPROGRESS_OR_PENDING" #ALARMS ALARMS_ADD_ALARM: "jms:queue:ALARMS.ADD.ALARM" @@ -157,7 +187,9 @@ EVENT_PRODUCT_ORDER_ATTRIBUTE_VALUE_CHANGED: "jms:topic:EVENT.PRODUCTORDER.ATTRC #QUEUE MESSSAGES WITH VNFNSD CATALOG NFV_CATALOG_GET_NSD_BY_ID: "jms:queue:NFVCATALOG.GET.NSD_BY_ID" -GET_USER_BY_USERNAME: "direct:get_user_byusername" + +#MISC +GET_USER_BY_USERNAME: "jms:queue:GET.USER_BY_USERNAME" #RESOURCES MESSAGES CATALOG_ADD_RESOURCE: "jms:queue:CATALOG.ADD.RESOURCE" @@ -181,7 +213,6 @@ CATALOG_UPD_RESOURCEACTIVATION: "jms:queue:CATALOG.UPD.RESOURCEACTIVATION" CATALOG_UPDADD_RESOURCEACTIVATION: "jms:queue:CATALOG.UPDADD.RESOURCEACTIVATION" CATALOG_GET_RESOURCEACTIVATION_BY_ID: "jms:queue:CATALOG.GET.RESOURCEACTIVATION" - #LCM MESSAGES CATALOG_GET_LCMRULE_BY_ID: "jms:queue:CATALOG.GET.LCMRULE" CATALOG_GET_LCMRULES_BY_SPECID_PHASE: "jms:queue:CATALOG.GET.LCMRULES_BY_SPECID_PHASE" @@ -200,3 +231,8 @@ NFV_CATALOG_NS_LCMCHANGED: "jms:topic:NFV_CATALOG_NS_LCMCHANGED" #RESERVATION_API RESERVATION_CREATE: "jms:queue:RESERVATION.ADD" RESERVATION_AVAILABILITY_CHECK: "jms:queue:RESERVATION.AVAILABILITYCHECK" + + +--- + + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3722fa870b9ede79c840948872329f4e8874fd5a..dc4f0ce805b2dccea5a1762bd78947b7093795be 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -111,15 +111,46 @@ oauthsign: key: "EK97Y7Y9WPGG1MEG" #QUEUE MESSAGES -CATALOG_GET_SERVICEORDERS: "jms:queue:CATALOG.GET.SERVICEORDERS" -CATALOG_GET_SERVICEORDER_BY_ID: "jms:queue:CATALOG.GET.SERVICEORDER_BY_ID" -CATALOG_ADD_SERVICEORDER: "jms:queue:CATALOG.ADD.SERVICEORDER" -CATALOG_UPD_SERVICEORDER_BY_ID: "jms:queue:CATALOG.UPD.SERVICEORDER_BY_ID" CATALOG_GET_SERVICESPEC_BY_ID: "jms:queue:CATALOG.GET.SERVICESPEC_BY_ID" CATALOG_ADD_SERVICESPEC: "jms:queue:CATALOG.ADD.SERVICESPEC" CATALOG_UPD_SERVICESPEC: "jms:queue:CATALOG.UPD.SERVICESPEC" CATALOG_UPDADD_SERVICESPEC: "jms:queue:CATALOG.UPDADD.SERVICESPEC" +CATALOG_SERVICE_QUEUE_ITEMS_GET: "jms:queue:CATALOG.SERVICEQUEUEITEMS.GET" +CATALOG_SERVICE_QUEUE_ITEM_UPD: "jms:queue:CATALOG.SERVICEQUEUEITEM.UPDATE" +CATALOG_SERVICE_QUEUE_ITEM_DELETE: "jms:queue:CATALOG.SERVICEQUEUEITEM.DELETE" +CATALOG_GET_PARTNER_ORGANIZATON_BY_ID: "jms:queue:CATALOG.GET.PARTNER_ORGANIZATION_BY_ID" +CATALOG_UPDATE_PARTNER_ORGANIZATION: "jms:queue:CATALOG.UPD.PARTNER_ORGANIZATION" +CATALOG_SERVICES_TO_TERMINATE: "jms:queue:CATALOG.GET.SERVICETOTERMINATE" +CATALOG_SERVICES_OF_PARTNERS: "jms:queue:CATALOG.GET.SERVICESOFPARTNERS" +CATALOG_GET_EXTERNAL_SERVICE_PARTNERS: "jms:queue:CATALOG.GET.EXTERNALSERVICEPARTNERS" +CATALOG_UPD_EXTERNAL_SERVICESPEC: "jms:queue:CATALOG.UPD.EXTERNAL_SERVICESPEC" + +#SERVICE_INVENTORY +CATALOG_ADD_SERVICE: "jms:queue:CATALOG.ADD.SERVICE" +CATALOG_UPD_SERVICE: "jms:queue:CATALOG.UPD.SERVICE" +CATALOG_GET_SERVICE_BY_ID: "jms:queue:CATALOG.GET.SERVICE" +CATALOG_GET_SERVICE_BY_ORDERID: "jms:queue:CATALOG.GET.SERVICE_BY_ORDERID" + + +#SERVICE ORDERS +CATALOG_GET_SERVICEORDERS: "jms:queue:CATALOG.GET.SERVICEORDERS" +CATALOG_GET_SERVICEORDER_BY_ID: "jms:queue:CATALOG.GET.SERVICEORDER_BY_ID" +CATALOG_ADD_SERVICEORDER: "jms:queue:CATALOG.ADD.SERVICEORDER" +CATALOG_UPD_SERVICEORDER_BY_ID: "jms:queue:CATALOG.UPD.SERVICEORDER_BY_ID" +CATALOG_GET_INITIAL_SERVICEORDERS_IDS: "jms:queue:CATALOG.GET.INITIAL_SERVICEORDERS" +CATALOG_GET_SERVICEORDER_IDS_BY_STATE: "jms:queue:CATALOG.GET.ACKNOWLEDGED_SERVICEORDERS" + +#SERVICE CATALOGS, CATEGORIES +CATALOG_GET_SERVICECATALOGS: "jms:queue:CATALOG.GET.SERVICECATALOGS" +CATALOG_GET_SERVICECATALOG_BY_ID: "jms:queue:CATALOG.GET.SERVICECATALOG_BY_ID" +CATALOG_GET_SERVICECATALOG_BY_NAME: "jms:queue:CATALOG.GET.SERVICECATALOG_BY_NAME" +CATALOG_GET_SERVICECATEGORIES: "jms:queue:CATALOG.GET.SERVICECATEGORIES" +CATALOG_GET_SERVICECATEGORY_BY_ID: "jms:queue:CATALOG.GET.SERVICECATEGORY_BY_ID" +CATALOG_GET_SERVICESPECREFS_BYCATEGORY_ID: "jms:queue:CATALOG.GETSERVICESPECREFS.SERVICECATEGORY_BY_ID" +CATALOG_SEARCH_SERVICESPECREFS: "jms:queue:CATALOG.CATALOG_SEARCH_SERVICESPECREFS" + +#PRODUCT CATALOGS CATALOG_GET_PRODUCTSPEC_BY_ID: "jms:queue:CATALOG.GET.PRODUCTSPEC_BY_ID" CATALOG_ADD_PRODUCTSPEC: "jms:queue:CATALOG.ADD.PRODUCTSPEC" CATALOG_UPD_PRODUCTSPEC: "jms:queue:CATALOG.UPD.PRODUCTSPEC" @@ -131,30 +162,22 @@ CATALOG_ADD_PRODUCTORDER: "jms:queue:CATALOG.ADD.PRODUCTORDER" CATALOG_UPD_PRODUCTORDER_BY_ID: "jms:queue:CATALOG.UPD.PRODUCTORDER_BY_ID" CATALOG_GET_INITIAL_PRODUCTORDERS_IDS: "jms:queue:CATALOG.GET.INITIAL_PRODUCTORDERS" CATALOG_GET_PRODUCTORDER_IDS_BY_STATE: "jms:queue:CATALOG.GET.ACKNOWLEDGED_PRODUCTORDERS" -CATALOG_GET_INITIAL_SERVICEORDERS_IDS: "jms:queue:CATALOG.GET.INITIAL_SERVICEORDERS" -CATALOG_GET_SERVICEORDER_IDS_BY_STATE: "jms:queue:CATALOG.GET.ACKNOWLEDGED_SERVICEORDERS" -CATALOG_ADD_SERVICE: "jms:queue:CATALOG.ADD.SERVICE" -CATALOG_UPD_SERVICE: "jms:queue:CATALOG.UPD.SERVICE" -CATALOG_GET_SERVICE_BY_ID: "jms:queue:CATALOG.GET.SERVICE" -CATALOG_GET_SERVICE_BY_ORDERID: "jms:queue:CATALOG.GET.SERVICE_BY_ORDERID" -CATALOG_SERVICE_QUEUE_ITEMS_GET: "jms:queue:CATALOG.SERVICEQUEUEITEMS.GET" -CATALOG_SERVICE_QUEUE_ITEM_UPD: "jms:queue:CATALOG.SERVICEQUEUEITEM.UPDATE" -CATALOG_SERVICE_QUEUE_ITEM_DELETE: "jms:queue:CATALOG.SERVICEQUEUEITEM.DELETE" -CATALOG_GET_PARTNER_ORGANIZATON_BY_ID: "jms:queue:CATALOG.GET.PARTNER_ORGANIZATION_BY_ID" -CATALOG_UPDATE_PARTNER_ORGANIZATION: "jms:queue:CATALOG.UPD.PARTNER_ORGANIZATION" -CATALOG_SERVICES_TO_TERMINATE: "jms:queue:CATALOG.GET.SERVICETOTERMINATE" -CATALOG_SERVICES_OF_PARTNERS: "jms:queue:CATALOG.GET.SERVICESOFPARTNERS" - -CATALOG_GET_EXTERNAL_SERVICE_PARTNERS: "jms:queue:CATALOG.GET.EXTERNALSERVICEPARTNERS" -CATALOG_UPD_EXTERNAL_SERVICESPEC: "jms:queue:CATALOG.UPD.EXTERNAL_SERVICESPEC" - +CATALOG_GET_PRODUCTCATALOGS: "jms:queue:CATALOG.GET.PRODUCTCATALOGS" +CATALOG_GET_PRODUCTCATALOG_BY_ID: "jms:queue:CATALOG.GET.PRODUCTCATALOG_BY_ID" +CATALOG_GET_PRODUCTCATALOG_BY_NAME: "jms:queue:CATALOG.GET.PRODUCTCATALOG_BY_NAME" +CATALOG_GET_PRODUCTCATEGORIES: "jms:queue:CATALOG.GET.PRODUCTCATEGORIES" +CATALOG_GET_PRODUCTCATEGORY_BY_ID: "jms:queue:CATALOG.GET.PRODUCTCATEGORY_BY_ID" +CATALOG_GET_PRODUCTOFFERINGS_BYCATEGORY_ID: "jms:queue:CATALOG.GET.PRODUCTOFFERINGS.CATEGORY_ID" +CATALOG_SEARCH_PRODUCTOFFERINGS: "jms:queue:CATALOG.CATALOG_SEARCH_PRODUCTOFFERINGS" +#PM_MEASUREMENT_COLLECTION PM_MEASUREMENT_COLLECTION_GET_JOB_BY_ID: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.GET_BY_ID" PM_MEASUREMENT_COLLECTION_JOBS_GET: "jms:queue:PM.MEASUREMENTCOLLECTIONJOBS.GET" PM_MEASUREMENT_COLLECTION_JOB_ADD: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.ADD" PM_MEASUREMENT_COLLECTION_JOB_CREATED: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.CREATED" PM_MEASUREMENT_COLLECTION_JOB_UPDATE: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.UPDATE" +PM_MEASUREMENT_COLLECTION_JOB_GET_INPROGRESS_OR_PENDING: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.GET_INPROGRESS_OR_PENDING" #ALARMS ALARMS_ADD_ALARM: "jms:queue:ALARMS.ADD.ALARM" @@ -234,6 +257,7 @@ NFV_CATALOG_NS_LCMCHANGED: "jms:topic:NFV_CATALOG_NS_LCMCHANGED" RESERVATION_CREATE: "jms:queue:RESERVATION.ADD" RESERVATION_AVAILABILITY_CHECK: "jms:queue:RESERVATION.AVAILABILITYCHECK" + --- diff --git a/src/main/resources/gst.json b/src/main/resources/gst.json index 246026d825a0bfe37fdebf938c1daa81c7a672df..722bc33936311725e35198c33e074b78854d051f 100644 --- a/src/main/resources/gst.json +++ b/src/main/resources/gst.json @@ -1,18 +1,14 @@ { "name": "GST External", - "description": "GST external example", - "version": "5.0.0", + "description": "The purpose of this specification template is to provide the standardised list of attributes that can characterise a type of network slice. The Service Designer is encouraged to tailor the template to its service needs", + "version": "10.0.0", "isBundle": false, - "attachment": [ - ], - "relatedParty": [ - ], - "resourceSpecification": [ - ], - "serviceLevelSpecification": [ - ], + "attachment": [], + "relatedParty": [], + "resourceSpecification": [], + "serviceLevelSpecification": [], "serviceSpecCharacteristic": [ - { + { "name": "Availability", "configurable": false, "description": "(Communication service) availability: percentage value of the amount of time the end-to-end communication service is delivered according to an agreed QoS, divided by the amount of time the system is expected to deliver the end-to-end service according to the specification in a specific area, see also 3GPP TS 22.261", @@ -23,8 +19,16 @@ "regex": null, "valueType": "FLOAT", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -37,11 +41,10 @@ "valueType": "FLOAT", "validFor": null, "value": { - "value": "95", + "value": "99", "alias": "High availability" } } - ] }, { @@ -55,14 +58,35 @@ "regex": null, "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } , + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operation", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + }, { "role": "Region specification", "name": "Area of Service: Region specification", - "relationshipType": "dependency" + "relationshipType": "aggregation" + }, + { + "role": "Minimum altitude", + "name": "Area of Service: Minimum altitude", + "relationshipType": "aggregation" } ], "serviceSpecCharacteristicValue": [ @@ -76,11 +100,10 @@ "valueType": "TEXT", "validFor": null, "value": { - "value": "Country", + "value": "GR", "alias": "ISO 3166-1 Alpha-2 country codes" } } - ] }, { @@ -94,10 +117,26 @@ "regex": null, "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operation", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -142,7 +181,44 @@ "alias": "Local Region B" } } - + ] + }, + { + "name": "Area of Service: Minimum altitude", + "configurable": false, + "description": "This parameter describes the minimum height required for the service that the network slice should support. If the NSC requires a different minimum altitude per region, this parameter needs to be provided for every region listed in Region specification parameter.", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "INTEGER", + "serviceSpecCharRelationship": [ + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } + ], + "serviceSpecCharacteristicValue": [ + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "meters", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "610" + } + } ] }, { @@ -155,11 +231,22 @@ "minCardinality": 0, "regex": null, "valueType": "BINARY", - "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -194,27 +281,39 @@ }, { "name": "Deterministic communication", - "description": "This attribute defines if the network slice supports deterministic communication for.", + "description": "This attribute defines if the network slice supports deterministic communication for periodic. UE traffic. Periodic traffic refers to the type of traffic with periodic transmissions.", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Performance", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" }, { - "role": "Availabilty", - "name": "Deterministic communication: Availabilty", - "relationshipType": "dependency" + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Performance", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "Availability", + "name": "Deterministic communication: Availability", + "relationshipType": "aggregation" }, { "role": "Periodicity", "name": "Deterministic communication: Periodicity", - "relationshipType": "dependency" + "relationshipType": "aggregation" } ] }, { - "name": "Deterministic communication: Availabilty", + "name": "Deterministic communication: Availability", "configurable": false, - "description": "Availability describes if the network slice supports deterministic communication.", + "description": "This parameter describes if the network slice supports deterministic communication.", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -222,9 +321,21 @@ "regex": null, "valueType": "BINARY", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Performance", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Performance", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -260,17 +371,34 @@ { "name": "Deterministic communication: Periodicity", "configurable": false, - "description": "Periodicity provides a list of periodicities supported by the network slice.", + "description": "This parameter provides a list of periodicities supported by the network slice. Conditional: This parameter must be present when the “Availability” is set to Supported.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional: This parameter is present when the “Availability” is set to 1.", - "valueType": "ARRAY", + "regex": null, + "valueType": "FLOAT", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Performance", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Performance", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "Availability", + "name": "Deterministic communication: Availability", + "relationshipType": "dependency" + } ], "serviceSpecCharacteristicValue": [ { @@ -283,7 +411,7 @@ "valueType": "FLOAT", "validFor": null, "value": { - "value": "0" + "value": "200" } } ] @@ -293,19 +421,14 @@ "description": "This attribute relates to the aggregated data rate in downlink for all UEs together in the network slice (this is not per UE).", "serviceSpecCharRelationship": [ { - "role": "Guaranteed downlink throughput", - "name": "Downlink throughput per network slice: Guaranteed downlink throughput", - "relationshipType": "dependency" - }, - { - "role": "Additional downlink GBR QoS flows", - "name": "Downlink throughput per network slice: Additional downlink GBR QoS flows", - "relationshipType": "dependency" + "role": "Guaranteed downlink throughput quota", + "name": "Downlink throughput per network slice: Guaranteed downlink throughput quota", + "relationshipType": "aggregation" }, { "role": "Maximum downlink throughput", "name": "Downlink throughput per network slice: Maximum downlink throughput", - "relationshipType": "dependency" + "relationshipType": "aggregation" } ] }, @@ -316,12 +439,20 @@ "extensible": null, "isUnique": true, "maxCardinality": 1, - "minCardinality": 1, + "minCardinality": 0, "regex": null, - "valueType": "FLOAT", + "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -331,7 +462,7 @@ "unitOfMeasure": "kbps", "valueFrom": null, "valueTo": null, - "valueType": "FLOAT", + "valueType": "INTEGER", "validFor": null, "value": { "value": "0" @@ -340,64 +471,86 @@ ] }, { - "name": "Downlink throughput per network slice: Additional downlink GBR QoS flows", + "name": "Downlink throughput per network slice: Maximum downlink throughput", "configurable": false, - "description": "Additional downlink GBR QoS flows.", + "description": "This parameter defines the maximum data rate supported by the network slice for all UEs together in downlink. Note: The sum of all data rates in downlink for all UEs does not exceed this value", "extensible": null, "isUnique": true, "maxCardinality": 1, - "minCardinality": 1, + "minCardinality": 0, "regex": null, - "valueType": "SET", + "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { - "isDefault": true, + "isDefault": false, "rangeInterval": null, "regex": null, - "unitOfMeasure": "N/A", + "unitOfMeasure": "kbps", "valueFrom": null, "valueTo": null, "valueType": "INTEGER", "validFor": null, "value": { - "value": "0", - "alias": "No additional downlink GBR QoS flows allowed" + "value": "100000" } + } + ] + }, + { + "name": "Downlink maximum throughput per UE", + "configurable": false, + "description": "This attribute describes the maximum data rate supported by the network slice per UE in downlink. The attribute is comprised of a list of Service Category parameters with the associated Maximum Downlink Throughput per UE value.", + "serviceSpecCharRelationship": [ + { + "role": "Maximum downlink throughput per UE value", + "name": "Downlink maximum throughput per UE: Maximum downlink throughput per UE value", + "relationshipType": "aggregation" }, { - "isDefault": false, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "1", - "alias": "Additional downlink GBR QoS flows allowed" - } + "role": "Service category", + "name": "Downlink maximum throughput per UE: Service category", + "relationshipType": "aggregation" } - ] }, { - "name": "Downlink throughput per network slice: Maximum downlink throughput", + "name": "Downlink maximum throughput per UE: Maximum downlink throughput per UE value", "configurable": false, - "description": "This parameter defines the maximum data rate supported by the network slice for all UEs together in downlink.", + "description": "This parameter defines the Maximum Downlink Throughput per UE value. This may be associated with a Service Category parameter.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional: Either maximum downlink throughput per network slice or Maximum downlink throughput per UE shall be present", + "regex": null, "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Performance", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -410,28 +563,11 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "0" + "value": "50000" } } ] }, - { - "name": "Downlink maximum throughput per UE", - "configurable": false, - "description": "This attribute describes the maximum data rate supported by the network slice per UE in downlink. The attribute is comprised of a list of Service Category parameters with the associated Maximum Downlink Throughput per UE value.", - "serviceSpecCharRelationship": [ - { - "role": "Service category", - "name": "Downlink maximum throughput per UE: Service category", - "relationshipType": "dependency" - }, - { - "role": "Maximum downlink throughput per UE value", - "name": "Downlink maximum throughput per UE: Maximum downlink throughput per UE value", - "relationshipType": "dependency" - } - ] - }, { "name": "Downlink maximum throughput per UE: Service category", "configurable": false, @@ -440,11 +576,19 @@ "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional: Attribute must be present if service is supported requiring guaranteed downlink throughput", - "valueType": "TEXT", + "regex": null, + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -457,38 +601,36 @@ "valueType": "TEXT", "validFor": null, "value": { - "value": "Service Category" + "value": "Service Category 1", + "alias": "50000 kbps" } - } - ] - }, - { - "name": "Downlink maximum throughput per UE: Maximum downlink throughput per UE value", - "configurable": false, - "description": "This parameter defines the Maximum Downlink Throughput per UE value. This may be associated with a Service Category parameter.", - "extensible": null, - "isUnique": true, - "maxCardinality": 1, - "minCardinality": 0, - "regex": "Conditional: Either maximum downlink throughput per network slice or Maximum downlink throughput per UE shall be present", - "valueType": "INTEGER", - "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Performance", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } - ], - "serviceSpecCharacteristicValue": [ + }, { "isDefault": false, "rangeInterval": null, "regex": null, - "unitOfMeasure": "kbps", + "unitOfMeasure": "N/A", "valueFrom": null, "valueTo": null, - "valueType": "INTEGER", + "valueType": "TEXT", "validFor": null, "value": { - "value": "50000" + "value": "Service Category 2", + "alias": "400000 kbps" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "TEXT", + "validFor": null, + "value": { + "value": "Service Category 3", + "alias": "1000000 kbps" } } ] @@ -498,39 +640,66 @@ "description": "This attribute describes whether the network slice supports the energy efficiency KPI. The energy efficiency is evaluated only when the network is running.", "serviceSpecCharRelationship": [ { - "role": "Network slice energy efficiency", - "name": "Energy efficiency: Network slice energy efficiency", - "relationshipType": "dependency" + "role": "Network slice energy efficiency KPI", + "name": "Energy efficiency: Network slice energy efficiency KPI", + "relationshipType": "aggregation" } ] }, { - "name": "Energy efficiency: Network slice energy efficiency", + "name": "Energy efficiency: Network slice energy efficiency KPI", "configurable": false, - "description": "", + "description": "Network slice energy efficiency KPI is defined in 3GPP Release 17 TS 28.554 Section 6.7. It is defined as a ratio between the performance of a network slice and its energy consumption.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "INTEGER", + "valueType": "BINARY", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ + { + "isDefault": true, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "BINARY", + "validFor": null, + "value": { + "value": "0", + "alias": "Not supported" + } + }, { "isDefault": false, "rangeInterval": null, "regex": null, - "unitOfMeasure": "Bit / Joule", + "unitOfMeasure": "N/A", "valueFrom": null, "valueTo": null, - "valueType": "INTEGER", + "valueType": "BINARY", "validFor": null, "value": { - "value": "40", - "alias": "(Urban area)" + "value": "1", + "alias": "Supported" } } ] @@ -538,17 +707,24 @@ { "name": "Group communication support", "configurable": false, - "description": "This parameter describes which type of group communication is provided by the network slice. ", + "description": "This attribute describes which type of group communication is provided by the network slice. This attribute applies to 3GPP access type only.", "extensible": null, "isUnique": true, "maxCardinality": 1, - "minCardinality": 1, + "minCardinality": 0, "regex": null, "valueType": "ENUM", - "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -562,7 +738,7 @@ "validFor": null, "value": { "value": "0", - "alias": "not available" + "alias": "Not supported" } }, { @@ -606,211 +782,44 @@ "value": "3", "alias": "Broadcast/Multicast + SC-PTM" } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "4", + "alias": "Unicast" + } } ] }, { - "name": "Isolation level: Isolation", + "name": "Maximum supported packet size", "configurable": false, - "description": "Isolation is one of the key expectations of network slicing. A network slice instance may be fully or partly, logically and/or physically, isolated from another network slice instance. ", + "description": "This attribute describes the maximum packet size supported by the network slice and may be important for URLLC (Ultra-Reliable Low Latency Communication) and MIoT (Massive IoT), or to indicate a supported maximum transmission unit (MTU).", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "ENUM", - "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { - "role": "Physical Isolation", - "name": "Isolation level: Physical Isolation", - "relationshipType": "dependency" - }, - { - "role": "Logical Isolation", - "name": "Isolation level: Logical Isolation", - "relationshipType": "dependency" - } - ], - "serviceSpecCharacteristicValue": [ - { - "isDefault": true, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "0", - "alias": "No Isolation" - } - }, - { - "isDefault": false, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "1", - "alias": "Physical Isolation" - } - }, - { - "isDefault": false, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "2", - "alias": "Logical Isolation" - } - } - ] - }, - { - "name": "Isolation level: Physical Isolation", - "configurable": false, - "description": "Physical network slices are physically separated (e.g. different rack, different hardware, different location, etc.). Process and threads isolation, Physical memory isolation, Physical network isolation", - "extensible": null, - "isUnique": true, - "maxCardinality": 1, - "minCardinality": 0, - "regex": "Conditional: This parameter must be present when Isolation is set to 1.", - "valueType": "ENUM", - "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } - - ], - "serviceSpecCharacteristicValue": [ - { - "isDefault": true, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "0", - "alias": "Process and threads Isolation" - } - }, - { - "isDefault": false, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "1", - "alias": "Physical memory Isolation" - } - }, - { - "isDefault": false, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "2", - "alias": "Physical network isolation" - } - } - ] - }, - { - "name": "Isolation level: Logical Isolation", - "configurable": false, - "description": "Logical network slices are logically separated. Virtual resources isolation a network slice has access to specific range of resources that do not overlap with other network slices (e.g. VM isolation). Network functions isolation NF (Network Function) is dedicated to the NSC, but virtual resources are shared. Tenant/Service Isolation NSC data are isolated from other NSCs, but virtual resources and NFs are shared", - "extensible": null, - "isUnique": true, - "maxCardinality": 1, - "minCardinality": 0, - "regex": "Conditional: ", - "valueType": "ENUM", + "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } - ], - "serviceSpecCharacteristicValue": [ - { - "isDefault": true, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "1", - "alias": "Virtual resource isolation" - } - }, { - "isDefault": false, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "2", - "alias": "Network Function isolation" - } + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" }, { - "isDefault": false, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "3", - "alias": "Tenant/Service isolation" - } + "name": "Performance", + "role": "tag", + "relationshipType": "tag" } - ] - }, - { - "name": "Maximum supported packet size", - "configurable": false, - "description": "This attribute describes the maximum packet size supported by the network slice and may be important for URLLC (Ultra-Reliable Low Latency Communication) and MIoT (Massive IoT), or to indicate a supported maximum transmission unit (MTU).", - "extensible": null, - "isUnique": true, - "maxCardinality": 1, - "minCardinality": 0, - "regex": null, - "valueType": "INTEGER", - "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Performance", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } ], "serviceSpecCharacteristicValue": [ { @@ -835,21 +844,19 @@ "extensible": null, "isUnique": true, "maxCardinality": 1, - "minCardinality": 1, + "minCardinality": 0, "regex": null, "valueType": "BINARY", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, { "role": "Mission-critical capability support", "name": "Mission critical support: Mission-critical capability support", - "relationshipType": "dependency" + "relationshipType": "aggregation" }, { "role": "Mission-critical service support", "name": "Mission critical support: Mission-critical service support", - "relationshipType": "dependency" + "relationshipType": "aggregation" } ], "serviceSpecCharacteristicValue": [ @@ -864,7 +871,7 @@ "validFor": null, "value": { "value": "0", - "alias": "non-mission-critical" + "alias": "Non-mission-critical" } }, { @@ -878,7 +885,7 @@ "validFor": null, "value": { "value": "1", - "alias": "mission-critical" + "alias": "Mission-critical" } } ] @@ -886,17 +893,29 @@ { "name": "Mission critical support: Mission-critical capability support", "configurable": false, - "description": "Mission-critical (MC) leads to a priority of the network slice relative to others, for C-plane (Control Plane) and U-plane (User Plane) decisions. This is relative to a customer provider relationship and to a PLMN (Public land Mobile Network)", + "description": "This parameter specifies what capabilities are available to support mission-critical services. More than one capability may be supported at once. Conditional: This parameter must be present if Mission critical support is set to Mission-critical.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional: Conditional: Specifies capabilities available to support mission-critical services. More than one capability may be supported at once. This parameter is optional when Mission critical support is set to 1 (mission-critical slice)", - "valueType": "ARRAY", - + "regex": null, + "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "Mission critical support", + "name": "Mission critical support", + "relationshipType": "dependency" + } ], "serviceSpecCharacteristicValue": [ { @@ -909,7 +928,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "1", + "value": "0", "alias": "Inter-user prioritization" } }, @@ -923,7 +942,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "2", + "value": "1", "alias": "Pre-emption" } }, @@ -937,7 +956,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "3", + "value": "2", "alias": "Local control" } } @@ -946,16 +965,29 @@ { "name": "Mission critical support: Mission-critical service support", "configurable": false, - "description": "This attribute specifies whether or not the network slice supports mission-critical push-to-talk (MCPTT), mission-critical data (MCData)], mission-critical video (MCVideo), Isolated E-UTRAN Operation for Public Safety (IOPS), or mission-critical interworking", + "description": "This attribute specifies whether or not the network slice supports mission-critical push-to-talk (MCPTT), mission-critical data (MCData)], mission-critical video (MCVideo), or mission-critical interworking. Conditional: This parameter must be present if Mission critical support is set to Mission-critical.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional: This attribute must be present when Mission critical support is set to 1.", - "valueType": "ARRAY", + "regex": null, + "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "Mission critical support", + "name": "Mission critical support", + "relationshipType": "dependency" + } ], "serviceSpecCharacteristicValue": [ { @@ -968,7 +1000,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "1", + "value": "0", "alias": "MCPTT" } }, @@ -982,7 +1014,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "2", + "value": "1", "alias": "MCData" } }, @@ -996,7 +1028,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "3", + "value": "2", "alias": "MCVideo" } }, @@ -1010,21 +1042,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "4", - "alias": "IOPS" - } - }, - { - "isDefault": false, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "4", + "value": "3", "alias": "MC interworking" } } @@ -1033,7 +1051,7 @@ { "name": "MMTel support", "configurable": false, - "description": "This attribute describes whether the network slice supports IP Multimedia Subsystem (IMS) and Multimedia Telephony Service MMTel. This parameter describes whether the GSMA PRD IR.92 0 compliant MMTel deployment is supported in the network slice", + "description": "This attribute describes whether the network slice supports IP Multimedia Subsystem (IMS) and Multimedia Telephony Service MMTel. This parameter describes whether the GSMA PRD NG.114 compliant MMTel deployment is supported in the network slice.", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -1041,8 +1059,16 @@ "regex": null, "valueType": "BINARY", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -1078,17 +1104,13 @@ { "name": "NB-IoT support", "configurable": false, - "description": "This parameter describes whether NB-IoTis supported in the network slice", + "description": "This attribute describes whether NB-IoT is supported in the RAN in the network slice.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, "valueType": "BINARY", - "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } - ], "serviceSpecCharacteristicValue": [ { "isDefault": true, @@ -1123,7 +1145,7 @@ { "name": "Network functions owned by Network Slice Customer", "configurable": false, - "description": "A NSC can own some network functions. This attribute provides a list of network functions to be provided by the NSC. If the list is empty, or this attribute is not included, the NSC is not providing any network function relevant for the network slice", + "description": "A NSC can own some network functions. This attribute provides a list of network functions to be provided by the NSC. If the list is empty, or this attribute is not included, the NSC is not providing any network function relevant for the network slice.", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -1131,9 +1153,21 @@ "regex": null, "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -1160,8 +1194,22 @@ "valueType": "TEXT", "validFor": null, "value": { - "value": "UDM/AUSF", - "alias": "UDM/AUSF" + "value": "UDM", + "alias": "UDM" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "TEXT", + "validFor": null, + "value": { + "value": "AUSF", + "alias": "AUSF" } }, { @@ -1197,19 +1245,33 @@ { "name": "Maximum number of PDU sessions", "configurable": false, - "description": "This attribute describes the maximum number of concurrent PDU supported by the network slice as specified by the Maximum number of PDU sessions parameter. If the network slice also requires taking into account PDN connections that can be handed over to the 5GS while the UEs are in the EPS, this is specified in the optional attribute EPS counting required. If the parameter EPS counting required is missing, then no counting happens of any PDN connections in EPS", + "description": "This attribute describes the maximum number of concurrent PDU supported by the network slice as specified by the Maximum number of PDU sessions parameter. If the network slice also requires taking into account Packet Data Network (PDN) connections that can be handed over to the 5GS while the UEs are in the EPS, this is specified in the optional attribute EPS counting required. If the parameter EPS counting required is missing, then no counting happens of any PDN connections in EPS. In roaming case (i.e. when the Area of Service attribute includes roaming partners' PMNs), the number of PDU sessions includes any Local Breakout PDU sessions if the Parameter Local Break Out (LBO) PDU Sessions Counting Required is set to Yes. Otherwise (including the case where this parameter is not present), Local Breakout PDU sessions are not counted.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional: Either Number of connections or Number of terminals shall be present", + "regex": null, "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" } + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "EPS counting required", + "name": "Maximum number of PDU sessions: EPS counting required", + "relationshipType": "aggregation" + }, + { + "role": "LBO PDU Sessions Counting required", + "name": "Maximum number of PDU sessions: LBO PDU Sessions Counting required", + "relationshipType": "aggregation" + } ], "serviceSpecCharacteristicValue": [ { - "isDefault": true, + "isDefault": false, "rangeInterval": null, "regex": null, "unitOfMeasure": "N/A", @@ -1218,8 +1280,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "10000", - "alias": "PDU sessions" + "value": "100000" } } ] @@ -1232,10 +1293,14 @@ "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "", + "regex": null, "valueType": "BINARY", "serviceSpecCharRelationship": [ - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" } + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -1249,7 +1314,7 @@ "validFor": null, "value": { "value": "0", - "alias": "no" + "alias": "No" } }, { @@ -1263,23 +1328,27 @@ "validFor": null, "value": { "value": "1", - "alias": "yes" + "alias": "Yes" } } ] }, { - "name": "Maximum number of UEs", + "name": "Maximum number of PDU sessions: LBO PDU Sessions Counting required", "configurable": false, - "description": "This attribute describes the maximum number of UEs that can use the network slice simultaneously as specified by the Maximum number of UEs parameter. If the network slice also requires taking into account UEs using PDN connections that can be handed over to the 5GS while they are in the EPS, this is specified in the optional attribute EPS counting required. If the parameter EPS counting required is missing, then no counting of UEs happens while they are in EPS", + "description": "This parameter specifies whether the LBO PDU sessions are counted. If the value is 'Yes' then LBO sessions are counted also, if the value is set to 'No', then LBO sessions are not counted. If this parameter is not present, then the LBO PDU sessions are not counted.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional: Either Number of connections or Number of terminals shall be present", - "valueType": "INTEGER", + "regex": null, + "valueType": "BINARY", "serviceSpecCharRelationship": [ - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" } + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -1289,11 +1358,68 @@ "unitOfMeasure": "N/A", "valueFrom": null, "valueTo": null, - "valueType": "INTEGER", + "valueType": "BINARY", "validFor": null, "value": { - "value": "10000", - "alias": "terminals" + "value": "0", + "alias": "No" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "BINARY", + "validFor": null, + "value": { + "value": "1", + "alias": "Yes" + } + } + ] + }, + { + "name": "Maximum number of UEs", + "configurable": false, + "description": "This attribute describes the maximum number of UEs that can use the network slice simultaneously as specified by the 'Maximum number of UEs' parameter. If the network slice also requires taking into account UEs using PDN connections that can be handed over to the 5GS while they are in the EPS, this is specified in the optional attribute 'EPS counting required'. If the parameter 'EPS counting required' is missing, then no counting of UEs happens while they are in EPS. In roaming case (i.e. when the Area of Service attribute includes roaming partners' PMNs), the number of UEs includes any roaming UEs if the parameter 'Roaming UEs Counting Required' is set to 'Yes'. Otherwise (including the case where this parameter is not present), roaming UEs are not counted.", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "INTEGER", + "serviceSpecCharRelationship": [ + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "EPS counting required", + "name": "Maximum number of UEs: EPS counting required", + "relationshipType": "aggregation" + }, + { + "role": "Roaming UEs Counting required", + "name": "Maximum number of UEs: Roaming UEs Counting required", + "relationshipType": "aggregation" + } + ], + "serviceSpecCharacteristicValue": [ + { + "isDefault": true, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "100000" } } ] @@ -1301,15 +1427,67 @@ { "name": "Maximum number of UEs: EPS counting required", "configurable": false, - "description": "If this parameter indicates that EPS counting is required, the UE counting shall also take intoaccount the UEs in the EPS with at least one PDN connection in the network slice, i.e. it is required to count the UEs that have at least one PDU session connected to an APN that maps to a DNN/S-NSSAI of the network slice", + "description": "If this parameter indicates that EPS counting is required, the UE counting shall also take into account the UEs in the EPS with at least one PDN connection in the network slice, i.e. it is required to count the UEs that have at least one PDU session connected to an APN that maps to a DNN/S-NSSAI of the network slice", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "BINARY", + "serviceSpecCharRelationship": [ + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + } + ], + "serviceSpecCharacteristicValue": [ + { + "isDefault": true, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "BINARY", + "validFor": null, + "value": { + "value": "0", + "alias": "No" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "BINARY", + "validFor": null, + "value": { + "value": "1", + "alias": "Yes" + } + } + ] + }, + { + "name": "Maximum number of UEs: Roaming UEs Counting required", + "configurable": false, + "description": "This parameter specifies whether roaming UEs are counted. If the value is 'Yes', then roaming UEs are counted also, if the value is set to 'No', then roaming UEs are not counted. If this parameter is not present, then the roaming UEs are not counted.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "", + "regex": null, "valueType": "BINARY", "serviceSpecCharRelationship": [ - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" } + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -1323,7 +1501,7 @@ "validFor": null, "value": { "value": "0", - "alias": "no" + "alias": "No" } }, { @@ -1337,7 +1515,7 @@ "validFor": null, "value": { "value": "1", - "alias": "yes" + "alias": "Yes" } } ] @@ -1345,26 +1523,26 @@ { "name": "Performance monitoring", "configurable": false, - "description": "This attribute provides the capability for NSC and NOP to monitor Key Quality Indicators (KQIs) and Key Performance Indicators (KPIs). KQIs reflect the end-to-end service performance and quality while KPIs reflect the performance of the network", + "description": "This attribute provides the capability for NSC and NOP to monitor Key Quality Indicators (KQIs) and Key Performance Indicators (KPIs). KQIs reflect the end-to-end service performance and quality while KPIs reflect the performance of the network.", "regex": null, "valueType": "BINARY", "serviceSpecCharRelationship": [ { "role": "Availability", "name": "Performance monitoring: Availability", - "relationshipType": "dependency" + "relationshipType": "aggregation" }, { "role": "Monitoring sample frequency", "name": "Performance monitoring: Monitoring sample frequency", - "relationshipType": "dependency" + "relationshipType": "aggregation" } ] }, { "name": "Performance monitoring: Availability", "configurable": false, - "description": "This parameter contains a list of KQIs and KPIs available for monitoring. If the list is empty this attribute is not available in the network slice and the other parameters might be ignored", + "description": "This parameter contains a list of KQIs and KPIs available for monitoring. If the list is empty this attribute is not available in the network slice and the other parameters might be ignored.", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -1372,13 +1550,25 @@ "regex": null, "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { - "isDefault": true, + "isDefault": false, "rangeInterval": null, "regex": null, "unitOfMeasure": "N/A", @@ -1388,28 +1578,9 @@ "validFor": null, "value": { "value": "0", - "alias": "None" + "alias": "Throughput" } - } - ] - }, - { - "name": "Performance monitoring: Monitoring sample frequency", - "configurable": false, - "description": "This parameter describes how often the KQIs and KPIs are monitored.", - "extensible": null, - "isUnique": true, - "maxCardinality": 1, - "minCardinality": 0, - "regex": null, - "valueType": "INTEGER", - "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } - ], - "serviceSpecCharacteristicValue": [ + }, { "isDefault": false, "rangeInterval": null, @@ -1421,7 +1592,7 @@ "validFor": null, "value": { "value": "1", - "alias": "per second" + "alias": "Latency" } }, { @@ -1435,7 +1606,56 @@ "validFor": null, "value": { "value": "2", - "alias": "per minute" + "alias": "Service Request Success Rate" + } + } + ] + }, + { + "name": "Performance monitoring: Monitoring sample frequency", + "configurable": false, + "description": "This parameter describes how often the KQIs and KPIs are monitored. Only the KQIs of communication services offered by the NSP can be monitored. For over-the-top services, the NSP is not able to access the KQIs.", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "ENUM", + "serviceSpecCharRelationship": [ + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } + ], + "serviceSpecCharacteristicValue": [ + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "0", + "alias": "Per second" } }, { @@ -1448,8 +1668,8 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "3", - "alias": "per hour" + "value": "1", + "alias": "Per minute" } }, { @@ -1462,8 +1682,8 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "4", - "alias": "conditional, e.g. in case a defined threshold is crossed" + "value": "2", + "alias": "Per hour" } } ] @@ -1471,37 +1691,38 @@ { "name": "Performance prediction", "configurable": false, - "description": "This attribute defines the capability to allow the mobile system to predict the network and service status. Predictive QoS (Quality of Service) can be done for various Key Quality Indicators (KQIs) and Key Performance Indicators (KPIs). KQIs reflect the end-to-end service performance and quality, while KPIs reflect the performance of the network. The prediction is done for a specific point of time in the future and for a specific geolocation.", + "description": "This attribute defines the capability to allow the mobile system to predict the network and service status. Predictive QoS (Quality of Service) can be done for various Key Quality Indicators (KQIs) and Key Performance Indicators (KPIs). KQIs reflect the end-to-end service performance and quality, while KPIs reflect the performance of the network. The prediction is done for a specific point of time in the future and for a specific geolocation. Only the KQIs of communication services offered by the NSP can be predicted. For over-the-top services, the NSP is not able to access the KQIs.", "regex": null, "valueType": "BINARY", "serviceSpecCharRelationship": [ { "role": "Availability", "name": "Performance prediction: Availability", - "relationshipType": "dependency" + "relationshipType": "aggregation" }, { "role": "Prediction frequency", "name": "Performance prediction: Prediction frequency", - "relationshipType": "dependency" + "relationshipType": "aggregation" } ] }, { "name": "Performance prediction: Availability", "configurable": false, - "description": "This parameter contains a list of KQIs and KPIs available for prediction. If the list is empty, the attribute is not available in the network slice and the other parameters might be ignored", + "description": "This parameter contains a list of KQIs and KPIs available for prediction. If the list is empty, the attribute is not available in the network slice and the other parameters might be ignored.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "ARRAY", + "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -1514,7 +1735,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "1", + "value": "0", "alias": "Throughput" } }, @@ -1528,7 +1749,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "2", + "value": "1", "alias": "Latency" } }, @@ -1542,7 +1763,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "3", + "value": "2", "alias": "Service Request Success Rate" } } @@ -1551,18 +1772,34 @@ { "name": "Performance prediction: Prediction frequency", "configurable": false, - "description": "This parameter describes how often KQIs and KPIs prediction values are provided", + "description": "This parameter describes how often KQIs and KPIs prediction values are provided.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "INTEGER", + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -1575,8 +1812,8 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "1", - "alias": "per second" + "value": "0", + "alias": "Per second" } }, { @@ -1589,8 +1826,8 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "2", - "alias": "per minute" + "value": "1", + "alias": "Per minute" } }, { @@ -1603,8 +1840,8 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "3", - "alias": "per hour" + "value": "2", + "alias": "Per hour" } } ] @@ -1614,39 +1851,51 @@ "configurable": false, "description": "This attribute describes if the network slice provides geo-localization methods or supporting methods.", "regex": null, - "valueType": "ARRAY", + "valueType": "SET", "serviceSpecCharRelationship": [ { "role": "Availability", "name": "Positioning support: Availability", - "relationshipType": "dependency" + "relationshipType": "aggregation" }, { "role": "Prediction frequency", "name": "Positioning support: Prediction frequency", - "relationshipType": "dependency" + "relationshipType": "aggregation" }, { "role": "Prediction frequency", "name": "Positioning support: Accuracy", - "relationshipType": "dependency" + "relationshipType": "aggregation" } ] }, { "name": "Positioning support: Availability", "configurable": false, - "description": "This parameter describes if this attribute is provided by the network slice and contains a list of positioning methods provided by the slice. If the list is empty this attribute is not available in the network slice and the other parameters might be ignored", + "description": "This parameter describes if this attribute is provided by the network slice and contains a list of positioning methods provided by the slice. If the list is empty this attribute is not available in the network slice and the other parameters might be ignored.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "ARRAY", + "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -1659,8 +1908,8 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "1", - "alias": "CID" + "value": "0", + "alias": "CID (LTE and NR)" } }, { @@ -1673,7 +1922,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "2", + "value": "1", "alias": "E-CID (LTE and NR)" } }, @@ -1687,7 +1936,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "3", + "value": "2", "alias": "OTDOA (LTE and NR)" } }, @@ -1701,7 +1950,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "4", + "value": "3", "alias": "RF fingerprinting" } }, @@ -1715,7 +1964,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "5", + "value": "4", "alias": "AECID" } }, @@ -1729,7 +1978,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "6", + "value": "5", "alias": "Hybrid positioning" } }, @@ -1743,7 +1992,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "7", + "value": "6", "alias": "NET-RTK" } } @@ -1758,27 +2007,25 @@ "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "INTEGER", + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } - ], - "serviceSpecCharacteristicValue": [ { - "isDefault": false, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "1", - "alias": "per second" - } + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } + ], + "serviceSpecCharacteristicValue": [ { "isDefault": false, "rangeInterval": null, @@ -1789,8 +2036,8 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "2", - "alias": "per minute)" + "value": "0", + "alias": "Per second" } }, { @@ -1803,8 +2050,8 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "3", - "alias": "per hour" + "value": "1", + "alias": "Per minute" } }, { @@ -1817,8 +2064,8 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "4", - "alias": "conditional, e.g. in case a specific area is entered or left" + "value": "2", + "alias": "Per hour" } } ] @@ -1826,7 +2073,7 @@ { "name": "Positioning support: Accuracy", "configurable": false, - "description": "This parameter describes the accuracy of the location information. Accuracy depends on the respective positioning solution applied in the network slice", + "description": "This parameter describes the accuracy (+/- 0.01m) of the location information. Accuracy depends on the respective positioning solution applied in the network slice.", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -1834,9 +2081,21 @@ "regex": null, "valueType": "FLOAT", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -1849,8 +2108,7 @@ "valueType": "FLOAT", "validFor": null, "value": { - "value": "0.01", - "alias": "+/- 0.01m" + "value": "0.01" } } ] @@ -1858,16 +2116,19 @@ { "name": "Radio spectrum", "configurable": false, - "description": "Defines the radio spectrum supported by the network slice. This is important information, as some terminals might be restricted in terms of frequencies to be used.", + "description": "Defines the radio spectrum in which the network slice should be supported. This is important information, as: 1) Some UEs of the NSC might be capable of using certain frequency bands only, 2) The NSC may only own or have access to certain frequency bands.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "ARRAY", - + "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" } + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -1881,7 +2142,7 @@ "validFor": null, "value": { "value": "n1", - "alias": "UL: 1920 MHz – 1980 MHz DL: 2110 MHz – 2170 MHz FDD" + "alias": "UL: 1920 MHz - 1980 MHz DL: 2110 MHz - 2170 MHz FDD" } }, { @@ -1895,7 +2156,7 @@ "validFor": null, "value": { "value": "n77", - "alias": "UL: 3300 MHz – 4200 MHz DL: 3300 MHz – 4200 MHz TDD" + "alias": "UL: 3300 MHz - 4200 MHz DL: 3300 MHz - 4200 MHz TDD" } }, { @@ -1909,7 +2170,7 @@ "validFor": null, "value": { "value": "n78", - "alias": "UL: 3300 MHz – 3800 MHz DL: 3300 MHz – 3800 MHz TDD" + "alias": "UL: 3300 MHz - 3800 MHz DL: 3300 MHz - 3800 MHz TDD" } }, { @@ -1923,40 +2184,56 @@ "validFor": null, "value": { "value": "n79", - "alias": "UL: 4400 MHz – 5000 MHz DL: 4400 MHz – 5000 MHz TDD" + "alias": "UL: 4400 MHz - 5000 MHz DL: 4400 MHz - 5000 MHz TDD" } } ] }, { - "name": "Root cause investigation", + "name": "Monitoring and analytics", "configurable": false, - "description": "Root cause investigation is the capability provided to NSC to understand or investigate the root cause of network service performance degradation or failure", + "description": "This attribute helps the NSC to understand or investigate the root cause of network service performance degradation or failure. The network can provide an interface to expose monitoring and analytics information (e.g. via Network Exposure Function (NEF) as defined in 3GPP TS 23.502)", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "INTEGER", + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } - ], - "serviceSpecCharacteristicValue": [ { - "isDefault": true, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } + ], + "serviceSpecCharacteristicValue": [ + { + "isDefault": true, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, "valueTo": null, "valueType": "INTEGER", "validFor": null, "value": { "value": "0", - "alias": "not available" + "alias": "Not supported" } }, { @@ -1970,7 +2247,7 @@ "validFor": null, "value": { "value": "1", - "alias": "passive investigation" + "alias": "Passive investigation" } }, { @@ -1984,7 +2261,7 @@ "validFor": null, "value": { "value": "2", - "alias": "active investigation" + "alias": "Active investigation" } } ] @@ -1992,33 +2269,31 @@ { "name": "Session and Service Continuity support", "configurable": false, - "description": "The attribute defines the continuity of a Protocol Data Unit (PDU) session. The following three Session and Service Continuity (SSC) modes are specified: SSC mode 1 - the network preserves the connectivity service provided to the UE (the IP address is preserved), SSC mode 2 - the network may release the connectivity service delivered to the UE and release the corresponding PDU Session (the network may release IP address(es) that had been allocated to the UE), SSC mode 3 - changes to the user plane can be visible to the UE, while the network ensures that the UE suffers no loss of connectivity service (the IP address is not preserved in this mode when the PDU Session Anchor changes), None – UE loses the connectivity service", + "description": "The attribute defines the continuity of a Protocol Data Unit (PDU) session. The following three Session and Service Continuity (SSC) modes are specified: SSC mode 1 - the network preserves the connectivity service provided to the UE (the IP address is preserved), SSC mode 2 - the network may release the connectivity service delivered to the UE and release the corresponding PDU Session (the release of the PDU Session induces the release of IP address(es) that had been allocated to the UE), SSC mode 3 - changes to the user plane can be visible to the UE, while the network ensures that the UE suffers no loss of connectivity service (the IP address is not preserved in this mode when the PDU Session Anchor changes)", "extensible": null, "isUnique": true, "maxCardinality": 1, - "minCardinality": 1, + "minCardinality": 0, "regex": null, - "valueType": "ARRAY", + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } - ], - "serviceSpecCharacteristicValue": [ { - "isDefault": true, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "0", - "alias": "none" - } + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } + ], + "serviceSpecCharacteristicValue": [ { "isDefault": false, "rangeInterval": null, @@ -2066,16 +2341,29 @@ { "name": "Simultaneous use of the network slice", "configurable": false, - "description": "This attribute describes whether a network slice can be simultaneously used by a UE together with other network slices and if so, with which other classes of network slices. The attribute is comprised of a list of Service Category Parameters with the associated Simultaneous Use Class parameter value.", + "description": "This attribute describes whether a network slice can be simultaneously used by a UE together with other network slices and if so, with which other classes of network slices. The attribute is comprised of a list of Service Category Parameters with the associated Simultaneous Use Class parameter value. If no Service Category parameters are present, all UEs in the network slice are associated with the same Simultaneous Use Class.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "INTEGER", + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "Service category", + "name": "Simultaneous use of the network slice: Service category", + "relationshipType": "aggregation" + } ], "serviceSpecCharacteristicValue": [ { @@ -2089,7 +2377,7 @@ "validFor": null, "value": { "value": "0", - "alias": "Can be used with any slice" + "alias": "Can be used simultaneously with any network slice" } }, { @@ -2103,7 +2391,7 @@ "validFor": null, "value": { "value": "1", - "alias": "Can be used with slices with same SST value" + "alias": "Can be used simultaneously with any network slices with same SST value but different SD values" } }, { @@ -2117,7 +2405,7 @@ "validFor": null, "value": { "value": "2", - "alias": "Can be used with any slice with same SD value" + "alias": "Can be used simultaneously with any network slice with the same SD value but different SST value" } }, { @@ -2131,7 +2419,7 @@ "validFor": null, "value": { "value": "3", - "alias": "Cannot be used with another slice" + "alias": "Cannot be used simultaneously with any another network slice" } }, { @@ -2147,20 +2435,6 @@ "value": "4", "alias": "operator defined class" } - }, - { - "isDefault": false, - "rangeInterval": null, - "regex": null, - "unitOfMeasure": "N/A", - "valueFrom": null, - "valueTo": null, - "valueType": "INTEGER", - "validFor": null, - "value": { - "value": "5", - "alias": "operator defined class" - } } ] }, @@ -2172,11 +2446,19 @@ "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional: Attribute must be present if service is supported requiring Simultaneous Use Class", - "valueType": "TEXT", + "regex": null, + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -2189,7 +2471,33 @@ "valueType": "TEXT", "validFor": null, "value": { - "value": "Service Category" + "value": "Service Category 1" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "TEXT", + "validFor": null, + "value": { + "value": "Service Category 2" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "TEXT", + "validFor": null, + "value": { + "value": "Service Category 3" } } ] @@ -2197,71 +2505,107 @@ { "name": "Slice quality of service", "configurable": false, - "description": "some of these parameters 3GPP has already defined standard values [1]. By preselecting a 5G QoS Identifier (5QI) these parameters will automatically be filled out with the standardised values", + "description": "This attribute defines all the QoS relevant parameters supported by the network slice. For the 5G QoS Identifier (5QI) parameter, 3GPP has already defined standardised values (see 3GPP TS 23.501). By preselecting a 5G QoS Identifier (5QI) these parameters should automatically be filled out with the standardised values.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "ARRAY", + "valueType": "SET", "serviceSpecCharRelationship": [ { "role": "3GPP 5QI", - "name": "Slice quality of service parameters: 3GPP 5QI", - "relationshipType": "dependency" + "name": "Slice quality of service: 3GPP 5QI", + "relationshipType": "aggregation" }, { "role": "Resource Type", - "name": "Slice quality of service parameters: Resource Type", - "relationshipType": "dependency" + "name": "Slice quality of service: Resource Type", + "relationshipType": "aggregation" }, { "role": "Priority Level", - "name": "Slice quality of service parameters: Priority Level", - "relationshipType": "dependency" + "name": "Slice quality of service: Priority Level", + "relationshipType": "aggregation" }, { "role": "Packet Delay Budget", - "name": "Slice quality of service parameters: Packet Delay Budget", - "relationshipType": "dependency" + "name": "Slice quality of service: Packet Delay Budget", + "relationshipType": "aggregation" }, { "role": "Packet Error Rate", - "name": "Slice quality of service parameters: Packet Error Rate", - "relationshipType": "dependency" + "name": "Slice quality of service: Packet Error Rate", + "relationshipType": "aggregation" }, { "role": "Averaging Window", - "name": "Slice quality of service parameters: Averaging Window", - "relationshipType": "dependency" + "name": "Slice quality of service: Averaging Window", + "relationshipType": "aggregation" }, { "role": "Maximum Data Burst Volume", - "name": "Slice quality of service parameters: Maximum Data Burst Volume", - "relationshipType": "dependency" + "name": "Slice quality of service: Maximum Data Burst Volume", + "relationshipType": "aggregation" + }, + { + "role": "Guaranteed Flow Bit Rate - Downlink", + "name": "Slice quality of service: Guaranteed Flow Bit Rate - Downlink", + "relationshipType": "aggregation" + }, + { + "role": "Guaranteed Flow Bit Rate - Uplink", + "name": "Slice quality of service: Guaranteed Flow Bit Rate - Uplink", + "relationshipType": "aggregation" + }, + { + "role": "Maximum Flow Bit Rate - Downlink", + "name": "Slice quality of service: Maximum Flow Bit Rate - Downlink", + "relationshipType": "aggregation" + }, + { + "role": "Maximum Flow Bit Rate - Uplink", + "name": "Slice quality of service: Maximum Flow Bit Rate - Uplink", + "relationshipType": "aggregation" }, { "role": "Maximum Packet Loss Rate", "name": "Slice quality of service parameters: Maximum Packet Loss Rate", - "relationshipType": "dependency" + "relationshipType": "aggregation" } ] }, { - "name": "Slice quality of service parameters: 3GPP 5QI", + "name": "Slice quality of service: 3GPP 5QI", "configurable": false, - "description": "A 5QI is a scalar used as a reference to 5G QoS characteristics defined in clause [1], i.e. access node-specific parameters that control QoS forwarding treatment for the QoS Flow (e.g. scheduling weights, admission thresholds, queue management thresholds, link layer protocol configuration, etc.). 3GPP has already defined standardized 5QI to QoS characteristics mapping. See 5QI value in the table of GNST", + "description": "A 5QI is a scalar used as a reference to 5G QoS characteristics defined in 3GPP TS 23.501, i.e. access node-specific parameters that control QoS forwarding treatment for the QoS Flow (e.g. scheduling weights, admission thresholds, queue management thresholds, link layer protocol configuration, etc.). The 5G QoS characteristics for pre-configured 5QI values are pre-configured in the Access Network (AN). The 5G QoS characteristics for QoS Flows with dynamically assigned 5QI are signalled as part of the QoS profile (see 3GPP TS 23.501).", "extensible": null, "isUnique": true, "maxCardinality": 1, - "minCardinality": 1, + "minCardinality": 0, "regex": null, - "valueType": "ARRAY", + "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -2285,7 +2629,7 @@ "valueType": "INTEGER", "value": { "value": "3", - "alias": "Real Time Gaming, V2X messages Electricity distribution – medium voltage, Process automation - monitoring" + "alias": "Real Time Gaming, V2X messages Electricity distribution - medium voltage, Process automation - monitoring" } }, { @@ -2293,7 +2637,7 @@ "valueType": "INTEGER", "value": { "value": "4", - "alias": "Non- Conversational Video (Buffered Streaming)" + "alias": "Non-Conversational Video (Buffered Streaming)" } }, { @@ -2347,20 +2691,41 @@ ] }, { - "name": "Slice quality of service parameters: Resource Type", + "name": "Slice quality of service: Resource Type", "configurable": false, - "description": "The Resource Type determines if dedicated network resources related QoS Flow-level Guaranteed Flow Bit Rate (GFBR) value are permanently allocated [1]. This value needs to be provided for each customised 5QI value selected.", + "description": "The Resource Type determines if dedicated network resources related to the QoS Flow-level Guaranteed Flow Bit Rate (GFBR) value, are permanently allocated (see clause 5.7.3.2 of 3GPP TS 23.501). This value needs to be provided for each customised 5QI value selected.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional:", - "valueType": "INTEGER", + "regex": null, + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "3GPP 5QI", + "name": "Slice quality of service: 3GPP 5QI", + "relationshipType": "dependency" + } ], "serviceSpecCharacteristicValue": [ { @@ -2390,74 +2755,153 @@ ] }, { - "name": "Slice quality of service parameters: Priority Level", + "name": "Slice quality of service: Priority Level", "configurable": false, - "description": "The Priority level associated with 5G QoS characteristics indicates a priority in scheduling resources among QoS Flows. The Priority level shall be used to differentiate between QoS Flows of the same UE, and it shall also be used to differentiate between QoS Flows from different UEs. Once all QoS requirements up to GFBR are fulfilled for all the Guaranteed Bit Rate (GBR) QoS Flows, the Priority Level may also be used to distribute resources between GBR QoS Flows (for rates above GFBR up to MFBR, Maximum Flow Bit Rate) and non- GBR QoS Flows, in an implementation specific manner. The lowest Priority level value corresponds to the highest Priority [1]. This value needs to be provided for each customised 5QI value selected..", + "description": "The Priority level associated with 5G QoS characteristics indicates a priority in scheduling resources among QoS Flows. The Priority level shall be used to differentiate between QoS Flows of the same UE, and it shall also be used to differentiate between QoS Flows from different UEs. Once all QoS requirements up to GFBR are fulfilled for all the Guaranteed Bit Rate (GBR) QoS Flows, the Priority Level may also be used to distribute resources between GBR QoS Flows (for rates above GFBR up to MFBR, Maximum Flow Bit Rate) and non- GBR QoS Flows, in an implementation specific manner. The lowest Priority level value corresponds to the highest Priority (see clause 5.7.3.3 of 3GPP TS 23.501). This value needs to be provided for each customised 5QI value selected.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional:", - "valueType": "FLOAT", + "regex": null, + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "3GPP 5QI", + "name": "Slice quality of service: 3GPP 5QI", + "relationshipType": "dependency" + } ], "serviceSpecCharacteristicValue": [ { - "unitOfMeasure": "Seconds", - "valueType": "FLOAT", + "unitOfMeasure": "N/A", + "valueType": "INTEGER", "value": { "value": "10", "alias": "IMS signalling" } + }, + { + "unitOfMeasure": "N/A", + "valueType": "INTEGER", + "value": { + "value": "30", + "alias": "Real time gaming" + } } ] }, { - "name": "Slice quality of service parameters: Packet Delay Budget", + "name": "Slice quality of service: Packet Delay Budget", "configurable": false, - "description": "The Packet Delay Budget (PDB) defines an upper bound for the time that a packet may be delayed between the UE and the UPF, that terminates the N6 interface. For a certain 5QI the value of the PDB is the same in UL (Uplink) and DL (Downlink). In the case of 3GPP access, the PDB is used to support the configuration of scheduling and link layer functions (e.g. the setting of scheduling priority weights and HARQ (Hybrid Automatic Repeat request) target operating points).", + "description": "The Packet Delay Budget (PDB) defines an upper bound for the time that a packet may be delayed between the UE and the UPF, that terminates the N6 interface. For a certain 5QI the value of the PDB is the same in UL (Uplink) and DL (Downlink). In the case of 3GPP access, the PDB is used to support the configuration of scheduling and link layer functions (e.g. the setting of scheduling priority weights and HARQ (Hybrid Automatic Repeat request) target operating points) (see clause 5.7.3.4 of 3GPP TS 23.501). If the value is set to 0, no special measures are used to bring latency down to a minimum required by low-latency use cases.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional:", - "valueType": "FLOAT", + "regex": null, + "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "3GPP 5QI", + "name": "Slice quality of service: 3GPP 5QI", + "relationshipType": "dependency" + } ], "serviceSpecCharacteristicValue": [ { - "unitOfMeasure": "Seconds", - "valueType": "FLOAT", + "unitOfMeasure": "Milliseconds", + "valueType": "INTEGER", "value": { - "value": "20000", + "value": "20", "alias": "Cooperative driving" } + }, + { + "unitOfMeasure": "Milliseconds", + "valueType": "INTEGER", + "value": { + "value": "30", + "alias": "Virtual reality" + } } ] }, { - "name": "Slice quality of service parameters: Packet Error Rate", + "name": "Slice quality of service: Packet Error Rate", "configurable": false, "description": "The Packet Error Rate (PER) defines an upper bound for the rate of PDUs (e.g. IP packets) that have been processed by the sender but that are not successfully delivered by the corresponding receiver. The purpose of the PER is to allow for the appropriate link layer, protocol configurations (e.g. RLC and HARQ in RAN of a 3GPP access). For all 5QIs the value of the PER is the same in UL and DL. For GBR QoS Flows with Delay critical GBR resource type, a packet which is delayed more than PDB (but which complies with the GFBR and MDBV (Maximum Data Burst Volume) requirements) is counted as lost, and included in the PER. This value needs to be provided for each customised 5QI value selected.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional:", + "regex": null, "valueType": "FLOAT", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "3GPP 5QI", + "name": "Slice quality of service: 3GPP 5QI", + "relationshipType": "dependency" + } ], "serviceSpecCharacteristicValue": [ { @@ -2465,42 +2909,70 @@ "valueType": "FLOAT", "value": { "value": "0.000001", - "alias": "mission critical data" + "alias": "Mission critical data" + } + }, + { + "unitOfMeasure": "N/A", + "valueType": "FLOAT", + "value": { + "value": "0.01", + "alias": "V2X messaging" } } ] }, { - "name": "Slice quality of service parameters: Averaging Window", + "name": "Slice quality of service: Averaging Window", "configurable": false, - "description": "Each GBR QoS Flow shall be associated with an Averaging window. The Averaging window represents the duration over which the GFBR and MFBR shall be calculated (e.g. in the (R)AN, UPF, UE). (see clause 5.7.3.6 of 3GPP TS 23.501 [1]).", + "description": "Each GBR QoS Flow shall be associated with an Averaging window. The Averaging window represents the duration over which the GFBR and MFBR shall be calculated (e.g. in the (R)AN, UPF, UE). (see clause 5.7.3.6 of 3GPP TS 23.501). This value needs to be provided for each Operator-specific 5QI value selected and for any selected standardised 5QI value using non default Averaging Window.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "FLOAT", + "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "3GPP 5QI", + "name": "Slice quality of service: 3GPP 5QI", + "relationshipType": "dependency" + } ], "serviceSpecCharacteristicValue": [ { - "unitOfMeasure": "millisecond", - "valueType": "FLOAT", + "unitOfMeasure": "Milliseconds", + "valueType": "INTEGER", "value": { - "value": "01", - "alias": "" + "value": "1" } } ] }, { - "name": "Slice quality of service parameters: Maximum Data Burst Volume", + "name": "Slice quality of service: Maximum Data Burst Volume", "configurable": false, - "description": "Each GBR QoS Flow with Delay-critical resource type shall be associated with a Maximum Data Burst Volume (MDBV). MDBV denotes the largest amount of data that the 5G-AN is required to serve within a period of 5G-AN PDB (i.e. 5G-AN part of the PDB) (see clause 5.7.3.7 of 3GPP TS 23.501 [1]).", + "description": "Each GBR QoS Flow with Delay-critical resource type shall be associated with a Maximum Data Burst Volume (MDBV). MDBV denotes the largest amount of data that the 5G-AN is required to serve within a period of 5G-AN PDB (i.e. 5G-AN part of the PDB) (see clause 5.7.3.7 of 3GPP TS 23.501). This value needs to be provided for each Operator-specific 5QI value selected and for any selected standardised 5QI value using non default Maximum Data Burst Volume.", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -2508,26 +2980,46 @@ "regex": null, "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "3GPP 5QI", + "name": "Slice quality of service: 3GPP 5QI", + "relationshipType": "dependency" + } ], "serviceSpecCharacteristicValue": [ { "unitOfMeasure": "Bytes", "valueType": "INTEGER", "value": { - "value": "0", - "alias": "" + "value": "0" } } ] }, { - "name": "Slice quality of service parameters: Maximum Packet Loss Rate", + "name": "Slice quality of service: Guaranteed Flow Bit Rate - Downlink", "configurable": false, - "description": "The Maximum Packet Loss Rate (UL, DL) indicates the maximum rate for lost packets of the QoS flow that can be tolerated in the uplink (UL) and downlink (DL) direction.", + "description": "Each GBR QoS Flow is associated with a Guaranteed Flow Bit Rate (GFBR) - Downlink (DL) (see clause 5.7.1.2 of 3GPP TS 23.501). A recommended value of GFBR-DL may be provided for GBR QoS Flow on a per selected 5QI value basis (5QI value that is of resource type GBR or Delay-critical GBR).", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -2535,60 +3027,256 @@ "regex": null, "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { - "unitOfMeasure": "Percentage", + "unitOfMeasure": "kbps", "valueType": "INTEGER", "value": { - "value": "0", - "alias": "" + "value": "0" } } ] }, { - "name": "Support for non-IP traffic", + "name": "Slice quality of service: Guaranteed Flow Bit Rate - Uplink", "configurable": false, - "description": "This attribute provides non-IP Session support (Ethernet session and forwarding support) of communication devices", + "description": "Each GBR QoS Flow is associated with a Guaranteed Flow Bit Rate (GFBR) - Uplink (UL) (see clause 5.7.1.2 of 3GPP TS 23.501). A recommended value of GFBR-UL may be provided for GBR QoS Flow on a per selected 5QI value basis (5QI value that is of resource type GBR or Delay-critical GBR).", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "BINARY", + "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { - "unitOfMeasure": "N/A", + "unitOfMeasure": "kbps", + "valueType": "INTEGER", + "value": { + "value": "0" + } + } + ] + }, + { + "name": "Slice quality of service: Maximum Flow Bit Rate - Downlink", + "configurable": false, + "description": "Each GBR QoS Flow is associated with a Maximum Flow Bit Rate (MFBR) - Downlink (DL) (see clause 5.7.1.2 of 3GPP TS 23.501). A recommended value of MFBR-DL may be provided for GBR QoS Flow on a per selected 5QI value basis (5QI value that is of resource type GBR or Delay-critical GBR).", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "INTEGER", + "serviceSpecCharRelationship": [ + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } + ], + "serviceSpecCharacteristicValue": [ + { + "unitOfMeasure": "kbps", + "valueType": "INTEGER", + "value": { + "value": "0" + } + } + ] + }, + { + "name": "Slice quality of service: Maximum Flow Bit Rate - Uplink", + "configurable": false, + "description": "Each GBR QoS Flow is associated with a Maximum Flow Bit Rate (MFBR) - Uplink (UL) (see clause 5.7.1.2 of 3GPP TS 23.501). A recommended value of MFBR-UL may be provided for GBR QoS Flow on a per selected 5QI value basis (5QI value that is of resource type GBR or Delay-critical GBR).", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "INTEGER", + "serviceSpecCharRelationship": [ + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } + ], + "serviceSpecCharacteristicValue": [ + { + "unitOfMeasure": "kbps", + "valueType": "INTEGER", + "value": { + "value": "0" + } + } + ] + }, + { + "name": "Slice quality of service parameters: Maximum Packet Loss Rate", + "configurable": false, + "description": "The Maximum Packet Loss Rate (UL, DL) indicates the maximum rate for lost packets of the QoS flow that can be tolerated in the uplink (UL) and downlink (DL) direction. The Maximum Packet Loss Rate (UL, DL) can only be provided for a GBR QoS flow belonging to voice media.", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "INTEGER", + "serviceSpecCharRelationship": [ + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } + ], + "serviceSpecCharacteristicValue": [ + { + "unitOfMeasure": "Percentage", + "valueType": "INTEGER", + "value": { + "value": "0" + } + } + ] + }, + { + "name": "Support for non-IP traffic", + "configurable": false, + "description": "This attribute provides non-IP Session support (Ethernet session and forwarding support) of communication devices", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "BINARY", + "serviceSpecCharRelationship": [ + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } + ], + "serviceSpecCharacteristicValue": [ + { + "isDefault": true, + "unitOfMeasure": "N/A", "valueType": "INTEGER", "value": { "value": "0", - "alias": "not supported" + "alias": "Not supported" } - },{ + }, + { "unitOfMeasure": "N/A", "valueType": "INTEGER", "value": { "value": "1", - "alias": "supported" + "alias": "Supported" } } ] }, - { "name": "Supported device velocity", "configurable": false, - "description": "Maximum speed supported by the network slice at which a defined QoS and seamless transfer between TRxPs (Transmission Reception Point(s)), which may belong to different deployment layers and/or radio access technologies (multi-layer /-RAT), can be achieved.", + "description": "Maximum speed supported by the network slice at which a defined QoS and seamless transfer between TRxPs (Transmission Reception Point(s)), which may belong to different deployment layers and/or radio access technologies (multi-layer/-RAT), can be achieved.", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -2596,16 +3284,28 @@ "regex": null, "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Performance", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Performance", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { "unitOfMeasure": "Kilometres per hour (km/h)", "valueType": "INTEGER", "value": { - "value": "1", + "value": "0", "alias": "Stationary: 0 km/h" } }, @@ -2613,24 +3313,24 @@ "unitOfMeasure": "Kilometres per hour (km/h)", "valueType": "INTEGER", "value": { - "value": "2", - "alias": "Pedestrian: 0 km/h to 10 km/h" + "value": "1", + "alias": "Pedestrian: 10 km/h" } }, { "unitOfMeasure": "Kilometres per hour (km/h)", "valueType": "INTEGER", "value": { - "value": "3", - "alias": "Vehicular: 10 km/h to 120 km/h" + "value": "2", + "alias": "Vehicular: 120 km/h" } }, { "unitOfMeasure": "Kilometres per hour (km/h)", "valueType": "INTEGER", "value": { - "value": "4", - "alias": "High speed vehicular: 120 km/h to 500 km/h" + "value": "3", + "alias": "High speed vehicular: 500 km/h" } } ] @@ -2638,7 +3338,7 @@ { "name": "Synchronicity", "configurable": false, - "description": "This attribute provides synchronicity of communication devices. Two cases are most important in this context: Synchronicity between a base station and a mobile device and Synchronicity between mobile devices.", + "description": "This attribute provides synchronicity of communication devices. Two cases are most important in this context: 1) Synchronicity between a base station and a mobile device and 2) Synchronicity between mobile devices.", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -2646,42 +3346,48 @@ "regex": null, "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, { "role": "Availability", "name": "Synchronicity: Availability", - "relationshipType": "dependency" + "relationshipType": "aggregation" }, { "role": "Accuracy", "name": "Synchronicity: Accuracy", - "relationshipType": "dependency" + "relationshipType": "aggregation" } ] }, { "name": "Synchronicity: Availability", "configurable": false, - "description": "The synchronicity between devices over PC5 in absence of the network is not in scope of this attribute.", + "description": "The synchronicity between devices over PC5 in absence of the network is not in scope of this parameter.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "INTEGER", + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } - + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { + "isDefault": true, "unitOfMeasure": "N/A", "valueType": "INTEGER", "value": { "value": "0", - "alias": "not available" + "alias": "Not supported" } }, { @@ -2689,7 +3395,7 @@ "valueType": "INTEGER", "value": { "value": "1", - "alias": "between BS and UE" + "alias": "Between BS and UE" } }, { @@ -2697,7 +3403,7 @@ "valueType": "INTEGER", "value": { "value": "2", - "alias": "between BS and UE & UE and UE" + "alias": "Between BS and UE & UE and UE" } } ] @@ -2705,7 +3411,7 @@ { "name": "Synchronicity: Accuracy", "configurable": false, - "description": "This parameter describes the accuracy of the synchronicity..", + "description": "This parameter describes the accuracy of the synchronicity.", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -2713,16 +3419,23 @@ "regex": null, "valueType": "FLOAT", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { - "unitOfMeasure": "seconds", + "unitOfMeasure": "Seconds", "valueType": "FLOAT", "value": { - "value": "0,0000001", - "alias": "" + "value": "0,000001" } } ] @@ -2738,9 +3451,11 @@ "regex": null, "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } - + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -2748,7 +3463,15 @@ "valueType": "INTEGER", "value": { "value": "10000", - "alias": "devices per km2" + "alias": "10,000 devices per km2" + } + }, + { + "unitOfMeasure": "Number per km2", + "valueType": "INTEGER", + "value": { + "value": "1000000", + "alias": "1,000,000 devices per km2" } } ] @@ -2756,7 +3479,7 @@ { "name": "Uplink throughput per network slice", "configurable": false, - "description": "The achievable data rate of the network slice instance in uplink that is available ubiquitously across the coverage area of the network slice.", + "description": "This attribute relates to the aggregated data rate in uplink for all UEs together in the network slice (this is not per UE).", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -2764,23 +3487,27 @@ "regex": null, "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" }, { - "role": "Guaranteed uplink throughput", - "name": "Uplink throughput per network slice: Guaranteed uplink throughput", - "relationshipType": "dependency" + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "Guaranteed uplink throughput quota", + "name": "Uplink throughput per network slice: Guaranteed uplink throughput quota", + "relationshipType": "aggregation" }, { "role": "Maximum uplink throughput", "name": "Uplink throughput per network slice: Maximum uplink throughput", - "relationshipType": "dependency" + "relationshipType": "aggregation" } ] }, { - "name": "Uplink throughput per network slice: Additional uplink GBR QoS flows", + "name": "Uplink throughput per network slice: Guaranteed uplink throughput quota", "configurable": false, - "description": "This attribute describes the guaranteed data rate supported by the network slice in uplink. There are services (e.g. emergency services) where guaranteed uplink throughput is required.", + "description": "This parameter describes the guaranteed throughput/data rate supported by the network slice for the aggregate of all GBR QoS flows in uplink belonging to the set of all UEs using the network slice. Not including this parameter or if the value is 0, best effort traffic is expected where no minimum throughput is guaranteed.", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -2788,15 +3515,18 @@ "regex": null, "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" } + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { "unitOfMeasure": "kbps", "valueType": "INTEGER", "value": { - "value": "0", - "alias": "not specified" + "value": "0" } } ] @@ -2804,23 +3534,26 @@ { "name": "Uplink throughput per network slice: Maximum uplink throughput", "configurable": false, - "description": "This attribute describes the guaranteed data rate supported by the network slice in uplink. There are services (e.g. emergency services) where guaranteed uplink throughput is required.", + "description": "This parameter describes the maximum data rate supported by the network slice for all UEs together in uplink.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional: Either Maximum uplink throughput per network slice or Maximum uplink throughput per slice shall be present. ", + "regex": null, "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Scalability Attribute", "role": "tag", "relationshipType": "tag" } + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { "unitOfMeasure": "kbps", "valueType": "INTEGER", "value": { - "value": "10000", - "alias": "10 Mbps" + "value": "100000" } } ] @@ -2828,17 +3561,17 @@ { "name": "Uplink maximum throughput per UE", "configurable": false, - "description": "", + "description": "This attribute describes the maximum data rate supported by the network slice per UE in uplink. The attribute is comprised of a list of Service Category parameters with the associated Maximum Uplink Throughput per UE value. If no Service Category parameter is present, all devices in the network slice experience the same Maximum Uplink Throughput per UE value. If Service Category parameters are present, then the UEs will be associated with a specific Maximum Uplink Throughput per UE parameter value depending on which Service Category the UE is associated with for the network slice.", "serviceSpecCharRelationship": [ { - "role": "Guaranteed uplink throughput", + "role": "Maximum Uplink Throughput per UE value", "name": "Uplink maximum throughput per UE: Maximum Uplink Throughput per UE value", - "relationshipType": "dependency" + "relationshipType": "aggregation" }, { - "role": "Maximum uplink throughput", + "role": "Service category", "name": "Uplink maximum throughput per UE: Service category", - "relationshipType": "dependency" + "relationshipType": "aggregation" } ] }, @@ -2850,12 +3583,24 @@ "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional:", + "regex": null, "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -2868,8 +3613,7 @@ "valueType": "INTEGER", "validFor": null, "value": { - "value": "0", - "alias": "not specified" + "value": "0" } } ] @@ -2882,12 +3626,24 @@ "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional: Either Maximum uplink throughput per network slice or Maximum uplink throughput per UE must be present", - "valueType": "TEXT", + "regex": null, + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -2900,7 +3656,36 @@ "valueType": "TEXT", "validFor": null, "value": { - "value": "Service Category" + "value": "Service Category 1", + "alias": "Service category 1 (10,000 kbps)" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "TEXT", + "validFor": null, + "value": { + "value": "Service Category 2", + "alias": "Service category 2 (100,000 kbps)" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "TEXT", + "validFor": null, + "value": { + "value": "Service Category 3", + "alias": "Service category 3 (200,000 kbps)" } } ] @@ -2908,7 +3693,7 @@ { "name": "User management openness", "configurable": false, - "description": "This attribute describes the capability for the NSC to manage their users or groups of users’ network services and corresponding requirements. For instance, if NSC Y orders a network slice which is capable to support X users of Y, then Y should be capable to decide which X users could use this network slice. Hence, Y could manage the users, in terms of add, modify or delete users to receive network services provided by the specific network slice.", + "description": "This attribute describes the capability for the NSC to manage their users or groups of users' network services and corresponding requirements. For instance, if NSC Y orders a network slice which is capable to support X users of Y, then Y should be capable to decide which X users could use this network slice. Hence, Y could manage the users, in terms of add, modify or delete users to receive network services provided by the specific network slice.", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -2916,10 +3701,26 @@ "regex": null, "valueType": "BINARY", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "Operational", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Operational", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -2933,7 +3734,7 @@ "validFor": null, "value": { "value": "0", - "alias": "not supported" + "alias": "Not supported" } }, { @@ -2947,40 +3748,53 @@ "validFor": null, "value": { "value": "1", - "alias": "supported" + "alias": "Supported" } } ] }, { "name": "Data network access", - "description": "For each Supported data network list value in the Supported data networks attribute (see clause 3.4.39). This attribute defines how the network slice supported data networks handle the user data.", + "description": "For each Supported data network list value in the Supported data networks attribute. This attribute defines how the network slice supported data networks handle the user data.", "serviceSpecCharRelationship": [ { - "role": "Data access", + "role": "Data access per data network", "name": "Data network access: Data access per data network", - "relationshipType": "dependency" + "relationshipType": "aggregation" }, { "role": "Tunnelling mechanism", "name": "Data network access: Tunnelling mechanism", - "relationshipType": "dependency" + "relationshipType": "aggregation" + }, + { + "role": "LBO Allowed", + "name": "Data network access: LBO Allowed", + "relationshipType": "aggregation" } ] }, { "name": "Data network access: Data access per data network", "configurable": false, - "description": "TThe options for a specific Supported data network are as follows: Direct access to the Internet, Termination in a private network (e.g. via tunnelling mechanism such as L2TP, VPN Virtual Private Network, tunnel, etc.), All data traffic stays local to an operator network and the devices do not have access to the Internet or private network", + "description": "The options for a specific Supported data network are as follows: Direct access to the Internet, Termination in a private network (e.g. via tunnelling mechanism such as L2TP, VPN Virtual Private Network, tunnel, etc.), All data traffic stays local to an operator network and the devices do not have access to the Internet or private network", "extensible": null, "isUnique": true, "maxCardinality": 1, - "minCardinality": 1, + "minCardinality": 0, "regex": null, - "valueType": "ARRAY", + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -3030,16 +3844,29 @@ { "name": "Data network access: Tunnelling mechanism", "configurable": false, - "description": "The attribute defines the tunnelling mechanism; how the user data can be delivered to the external private data network. 3GPP TS 29.561 [12] lists the interworking with data networks and tunnelling mechanism used", + "description": "The parameter, if present, defines the tunnelling mechanism used to connect to a private data network. 3GPP TS 29.561 lists the interworking with data networks and tunnelling mechanism used. This parameter must be present if Data access per data network is set to Termination in the private network.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "Conditional:This parameter is present if User data Access is 1(Termination in the private network).", - "valueType": "ARRAY", + "regex": null, + "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "Data access per data network", + "name": "Data network access: Data access per data network", + "relationshipType": "dependency" + } ], "serviceSpecCharacteristicValue": [ { @@ -3071,7 +3898,7 @@ } }, { - "isDefault": true, + "isDefault": false, "rangeInterval": null, "regex": null, "unitOfMeasure": "N/A", @@ -3115,19 +3942,26 @@ ] }, { - "name": "V2X communication mode", + "name": "Data network access: LBO Allowed", "configurable": false, - "description": "This parameter describes if the V2X communication mode is supported by the network slice.", + "description": "The parameter, if present, defines whether a data network is available in Local Breakout whilst roaming.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "INTEGER", - + "valueType": "BINARY", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -3137,11 +3971,11 @@ "unitOfMeasure": "N/A", "valueFrom": null, "valueTo": null, - "valueType": "INTEGER", + "valueType": "BINARY", "validFor": null, "value": { "value": "0", - "alias": "NO" + "alias": "No" } }, { @@ -3151,13 +3985,43 @@ "unitOfMeasure": "N/A", "valueFrom": null, "valueTo": null, - "valueType": "INTEGER", + "valueType": "BINARY", "validFor": null, "value": { "value": "1", - "alias": "YES-EUTRA" + "alias": "Yes" } - }, + } + ] + }, + { + "name": "V2X-PC5 Network Scheduled Mode", + "configurable": false, + "description": "This parameter describes if the network slice supports V2X-PC5 Network scheduled mode related to the V2X-PC5 interface (PC5 interface as used for V2X communication) specified in 3GPP TS 23.287 for 5GS. This attribute applies to both NR PC5 RAT and LTE PC5 RAT, see 3GPP TS 23.287.", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "ENUM", + "serviceSpecCharRelationship": [ + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "Default mode of communication for NR PC5", + "name": "V2X-PC5 Network Scheduled Mode: Default mode of communication for NR PC5", + "relationshipType": "aggregation" + } + ], + "serviceSpecCharacteristicValue": [ { "isDefault": true, "rangeInterval": null, @@ -3167,9 +4031,37 @@ "valueTo": null, "valueType": "INTEGER", "validFor": null, + "value": { + "value": "0", + "alias": "Not supported" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "1", + "alias": "Supported for LTE PC5" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, "value": { "value": "2", - "alias": "YES- NR" + "alias": "Supported for NR PC5" } }, { @@ -3183,7 +4075,79 @@ "validFor": null, "value": { "value": "3", - "alias": "YES -NR and E-UTRA" + "alias": "Supported for NR PC5 and LTE PC5" + } + } + ] + }, + { + "name": "V2X-PC5 Network Scheduled Mode: Default mode of communication for NR PC5", + "configurable": false, + "description": "This parameter specifies the default mode of communication for NR PC5. This parameter must be present when the 'Network Scheduled Mode' on PC5 is set to 'NR PC5' or 'NR PC5 and LTE PC5'.", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "ENUM", + "serviceSpecCharRelationship": [ + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "V2X-PC5 Network Scheduled Mode", + "name": "V2X-PC5 Network Scheduled Mode", + "relationshipType": "dependency" + } + ], + "serviceSpecCharacteristicValue": [ + { + "isDefault": true, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "0", + "alias": "Broadcast" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "1", + "alias": "Groupcast" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "2", + "alias": "Unicast" } } ] @@ -3191,7 +4155,7 @@ { "name": "Latency from (last) UPF to Application Server", "configurable": false, - "description": "This optional attribute specifies maximum or worst case one-way latency between UPF and application server offered by the slice. This does not include latency introduced by the application server. In the case of chained UPFs, this refers to the last UPF (in the chain) towards the application server. This attribute extends what is covered by the 3GPP QoS PDB attribute (see GST QoS attribute) which is only between UE and UPF. This is an optional attribute for network slices that offer latency objectives between UPF and application server residing within the operator network", + "description": "This optional attribute specifies maximum or worst case one-way latency between UPF and application server offered by the slice. This does not include latency introduced by the application server. In the case of chained UPFs, this refers to the last UPF (in the chain) towards the application server. This attribute extends what is covered by the 3GPP QoS PDB attribute (see GST QoS attribute) which is only between UE and UPF. This is an optional attribute for network slices that offer latency objectives between UPF and application server residing within the operator network.", "extensible": null, "isUnique": true, "maxCardinality": 1, @@ -3199,22 +4163,29 @@ "regex": null, "valueType": "INTEGER", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Performance", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Performance", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { "isDefault": false, "rangeInterval": null, "regex": null, - "unitOfMeasure": "second", + "unitOfMeasure": "Milliseconds", "valueFrom": null, "valueTo": null, "valueType": "INTEGER", "validFor": null, "value": { - "value": "1", - "alias": "" + "value": "1" } } ] @@ -3222,18 +4193,24 @@ { "name": "Network Slice Specific Authentication and Authorization (NSSAA) Required", "configurable": false, - "description": "This attribute specifies whether for the Network Slice, devices need to be also authenticated and authorized by a AAA server using additional credentials different than the ones used for the primary authentication (see Rel-16 of 3GPP TS 23.501 [1] clause 5.15.10 for a definition of the Network Slice Specific Authentication and Authorization feature).", + "description": "This attribute specifies whether for the Network Slice, devices need to be also authenticated and authorized by a AAA server using additional credentials different than the ones used for the primary authentication (see Rel-16 of 3GPP TS 23.501 clause 5.15.10 for a definition of the Network Slice Specific Authentication and Authorization feature).", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, "valueType": "BINARY", - "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -3276,11 +4253,27 @@ "minCardinality": 0, "regex": null, "valueType": "BINARY", - "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "Multimedia Priority Service capability support", + "name": "Multimedia Priority Service: Multimedia Priority Service capability support", + "relationshipType": "aggregation" + }, + { + "role": "Multimedia Priority Service support", + "name": "Multimedia Priority Service: Multimedia Priority Service support", + "relationshipType": "aggregation" + } ], "serviceSpecCharacteristicValue": [ { @@ -3322,22 +4315,33 @@ "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "BINARY", - + "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "Multimedia Priority Service", + "name": "Multimedia Priority Service", + "relationshipType": "dependency" + } ], "serviceSpecCharacteristicValue": [ { - "isDefault": true, + "isDefault": false, "rangeInterval": null, "regex": null, "unitOfMeasure": "N/A", "valueFrom": null, "valueTo": null, - "valueType": "BINARY", + "valueType": "INTEGER", "validFor": null, "value": { "value": "0", @@ -3351,7 +4355,7 @@ "unitOfMeasure": "N/A", "valueFrom": null, "valueTo": null, - "valueType": "BINARY", + "valueType": "INTEGER", "validFor": null, "value": { "value": "1", @@ -3363,18 +4367,34 @@ { "name": "Multimedia Priority Service: Multimedia Priority Service support", "configurable": false, - "description": "This parameter specifies whether or not the network slice supports MPS for MMTel voice, MPS for MMTel video, and/or MPS for Data as specified in 3GPP TS 22.153 [31].", + "description": "This parameter specifies whether or not the network slice supports MPS for MMTel voice, MPS for MMTel video, and/or MPS for Data as specified in 3GPP TS 22.153.", "extensible": null, "isUnique": true, "maxCardinality": 1, "minCardinality": 0, "regex": null, - "valueType": "ARRAY", - + "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" }, - { "name": "KPI", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "KPI", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "Multimedia Priority Service", + "name": "Multimedia Priority Service", + "relationshipType": "dependency" + } ], "serviceSpecCharacteristicValue": [ { @@ -3384,7 +4404,7 @@ "unitOfMeasure": "N/A", "valueFrom": null, "valueTo": null, - "valueType": "ARRAY", + "valueType": "INTEGER", "validFor": null, "value": { "value": "0", @@ -3398,7 +4418,7 @@ "unitOfMeasure": "N/A", "valueFrom": null, "valueTo": null, - "valueType": "ARRAY", + "valueType": "INTEGER", "validFor": null, "value": { "value": "1", @@ -3412,7 +4432,7 @@ "unitOfMeasure": "N/A", "valueFrom": null, "valueTo": null, - "valueType": "ARRAY", + "valueType": "INTEGER", "validFor": null, "value": { "value": "2", @@ -3432,8 +4452,21 @@ "regex": null, "valueType": "SET", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + }, + { + "role": "Service category", + "name": "Supported data network: Service category", + "relationshipType": "aggregation" + } ], "serviceSpecCharacteristicValue": [ { @@ -3464,7 +4497,6 @@ "alias": "Service category 2" } } - ] }, { @@ -3475,11 +4507,19 @@ "isUnique": true, "maxCardinality": 1, "minCardinality": 0, - "regex": "", - "valueType": "TEXT", + "regex": null, + "valueType": "ENUM", "serviceSpecCharRelationship": [ - { "name": "Character Attribute", "role": "tag", "relationshipType": "tag" }, - { "name": "Functional", "role": "tag", "relationshipType": "tag" } + { + "name": "Character Attribute", + "role": "tag", + "relationshipType": "tag" + }, + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } ], "serviceSpecCharacteristicValue": [ { @@ -3492,13 +4532,176 @@ "valueType": "TEXT", "validFor": null, "value": { - "value": "Service Category" + "value": "Service Category 1" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "TEXT", + "validFor": null, + "value": { + "value": "Service Category 2" + } + } + ] + }, + { + "name": "Maximum number of UEs with at least one PDU session/PDN connection", + "configurable": false, + "description": "This attribute, which applies for network slices where EPS interworking is supported describes the maximum number of UEs that can use the network slice simultaneously with at least one PDU session/PDN connection as specified by the 'Maximum number of UEs with at least one PDU session and PDN connection' parameter. The counting of UEs shall consider the UEs in 5GS with at least one PDU Session in the network slice, and the UEs in the EPS with at least one PDN connection in the network slice, i.e., it is required to count the UEs that have at least one PDN connection connected to an APN that is associated with the S-NSSAI of the network slice.", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "INTEGER", + "serviceSpecCharRelationship": [ + { + "name": "Scalability Attribute", + "role": "tag", + "relationshipType": "tag" + } + ], + "serviceSpecCharacteristicValue": [ + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "100000" + } + } + ] + }, + { + "name": "V2X-PC5 parameter provisioning", + "configurable": false, + "description": "This attribute specifies the support for parameter provisioning over the control plane for V2X-PC5 interface (PC5 interface as used for V2X communication) specified in 3GPP TS 23.287 for 5GS. This attribute applies to both NR PC5 RAT and LTE PC5 RAT, see 3GPP TS 23.287. ", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "ENUM", + "serviceSpecCharRelationship": [ + { + "name": "Functional", + "role": "tag", + "relationshipType": "tag" + } + ], + "serviceSpecCharacteristicValue": [ + { + "isDefault": true, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "0", + "alias": "Not supported" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "1", + "alias": "Provisioning for LTE PC5" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "2", + "alias": "Provisioning for NR PC5" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "INTEGER", + "validFor": null, + "value": { + "value": "3", + "alias": "Provisioning for NR PC5 and LTE PC5" + } + } + ] + }, + { + "name": "PDU Set support", + "configurable": false, + "description": "PDU Set means one or more PDUs carrying the payload of one unit of information generated at the application level (e.g., frame(s) or video slice(s) etc for eXtended Reality (XR) Services). All the PDUs of a PDU set are transmitted within the same QoS Flow. When the network handles the PDU, it needs to consider the relation between the PDUs, i.e. per PDU set granularity.", + "extensible": null, + "isUnique": true, + "maxCardinality": 1, + "minCardinality": 0, + "regex": null, + "valueType": "BINARY", + "serviceSpecCharRelationship": [], + "serviceSpecCharacteristicValue": [ + { + "isDefault": true, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "BINARY", + "validFor": null, + "value": { + "value": "0", + "alias": "Not supported" + } + }, + { + "isDefault": false, + "rangeInterval": null, + "regex": null, + "unitOfMeasure": "N/A", + "valueFrom": null, + "valueTo": null, + "valueType": "BINARY", + "validFor": null, + "value": { + "value": "1", + "alias": "Supported" } } ] } ], - "serviceSpecRelationship": [ - ], + "serviceSpecRelationship": [], "targetServiceSchema": null -} +} \ No newline at end of file diff --git a/src/test/java/org/etsi/osl/services/api/ServiceCatalogIntegrationTest.java b/src/test/java/org/etsi/osl/services/api/ServiceCatalogIntegrationTest.java index 807e356072382992b7e28cef11a50efe9ba29558..143fe256c8417ab0be4b8805b2b9175747e18c21 100644 --- a/src/test/java/org/etsi/osl/services/api/ServiceCatalogIntegrationTest.java +++ b/src/test/java/org/etsi/osl/services/api/ServiceCatalogIntegrationTest.java @@ -164,7 +164,7 @@ public class ServiceCatalogIntegrationTest { ServiceCategory categ = categRepoService.findByName( "Generic Services" ); assertThat( categ.getServiceCandidateRefs().size() ).isEqualTo( FIXED_BOOTSTRAPS_SPECS ); - ServiceCategory categ2 = categRepoService.findByIdEager( categ.getId() ); + ServiceCategory categ2 = categRepoService.findByUuid( categ.getId() ); assertThat( categ2.getServiceCandidateRefs().size() ).isEqualTo( FIXED_BOOTSTRAPS_SPECS ); boolean vinnisbFound = false; @@ -955,8 +955,8 @@ public class ServiceCatalogIntegrationTest { logger.info("Test: testBundledSpec responseSpec1 = " + responseSpec1); - assertThat( responsesSpec1.getVersion() ).isEqualTo("5.0.0"); - assertThat( responsesSpec1.getServiceSpecCharacteristic().size() ).isEqualTo(80); + assertThat( responsesSpec1.getVersion() ).isEqualTo("10.0.0"); + assertThat( responsesSpec1.getServiceSpecCharacteristic().size() ).isEqualTo(88); assertThat( responsesSpec1.getServiceSpecRelationship().size() ).isEqualTo(0); boolean userPartyRoleOwnerexists = false; for (RelatedParty r : responsesSpec1.getRelatedParty()) { @@ -1014,10 +1014,10 @@ public class ServiceCatalogIntegrationTest { logger.info("Test: testGSTUpdate " ); ServiceCategory categ = categRepoService.findByName( "Generic Services" ); - ServiceCategory categ2 = categRepoService.findByIdEager( categ.getId() ); + ServiceCategory categ2 = categRepoService.findByUuid( categ.getId() ); assertThat( categ2.getServiceCandidateRefs().size() ).isEqualTo( FIXED_BOOTSTRAPS_SPECS ); - ServiceSpecification spec = this.specRepoService.findByNameAndVersion("A GST(NEST) Service Example", "5.0.0" ); + ServiceSpecification spec = this.specRepoService.findByNameAndVersion("A GST(NEST) Service Example", "10.0.0" ); assertThat( spec ).isNotNull(); spec.setVersion("0.x.0"); diff --git a/src/test/java/org/etsi/osl/services/api/metrics/GeneralMetricsApiControllerTest.java b/src/test/java/org/etsi/osl/services/api/metrics/GeneralMetricsApiControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..20a97508f92c622e777b37d696a40478548a433b --- /dev/null +++ b/src/test/java/org/etsi/osl/services/api/metrics/GeneralMetricsApiControllerTest.java @@ -0,0 +1,138 @@ +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.ServiceCatalog; +import org.etsi.osl.tmf.scm633.model.ServiceCategory; +import org.etsi.osl.tmf.scm633.reposervices.CandidateRepoService; +import org.etsi.osl.tmf.scm633.reposervices.CatalogRepoService; +import org.etsi.osl.tmf.scm633.reposervices.CategoryRepoService; +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.HashSet; +import java.util.List; +import java.util.Set; + +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 GeneralMetricsApiControllerTest { + + @Autowired + private MockMvc mvc; + + @Autowired + private WebApplicationContext context; + + @Autowired + IndividualRepoService individualRepoService; + + @Autowired + ResourceSpecificationRepoService resourceSpecificationRepoService; + + @Autowired + CandidateRepoService candidateRepoService; + + @Autowired + CatalogRepoService catalogRepoService; + + @Autowired + CategoryRepoService categoryRepoService; + + + @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"); + + int count = 0; + Set serviceCategories = new HashSet<>(); + List serviceCatalogs = catalogRepoService.findAll(); + + for (ServiceCatalog serviceCatalog : serviceCatalogs) { + serviceCategories.addAll(serviceCatalog.getCategoryObj()); + } + + for (ServiceCategory serviceCategory : serviceCategories) { + count += serviceCategory.getServiceCandidateObj().size() + serviceCategory.getServiceCandidateRefs().size(); + } + + 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()); + } +} 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 0000000000000000000000000000000000000000..e0578d5933fac6f60c97ad564485f6a14c72af1a --- /dev/null +++ b/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java @@ -0,0 +1,237 @@ +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.service.Note; +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("/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("/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("/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; + } +} diff --git a/src/test/java/org/etsi/osl/services/api/metrics/ServiceMetricsApiControllerTest.java b/src/test/java/org/etsi/osl/services/api/metrics/ServiceMetricsApiControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e7562ec4338ad9c501a888ba82f335553943110d --- /dev/null +++ b/src/test/java/org/etsi/osl/services/api/metrics/ServiceMetricsApiControllerTest.java @@ -0,0 +1,215 @@ +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.service.*; +import org.etsi.osl.tmf.scm633.model.ServiceSpecification; +import org.etsi.osl.tmf.scm633.model.ServiceSpecificationCreate; +import org.etsi.osl.tmf.sim638.model.Service; +import org.etsi.osl.tmf.sim638.model.ServiceCreate; +import org.etsi.osl.tmf.sim638.service.ServiceRepoService; +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.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 ServiceMetricsApiControllerTest { + + @Autowired + private MockMvc mvc; + + @Autowired + ServiceRepoService serviceRepoService; + + @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 testCountTotalServices() throws Exception { + createService(ServiceStateType.ACTIVE); + + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/totalServices" ) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk() ) + .andReturn().getResponse().getContentAsString(); + + int totalServices = JsonPath.read(response, "$.totalServices"); + + + assertThat(totalServices).isEqualTo(serviceRepoService.findAll().size()); + } + + @WithMockUser(username="osadmin", roles = {"ADMIN","USER"}) + @Test + public void testCountTotalServicesWithState() throws Exception { + createService(ServiceStateType.ACTIVE); + createService(ServiceStateType.INACTIVE); + + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/totalServices" ) + .param("state", "ACTIVE") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk() ) + .andReturn().getResponse().getContentAsString(); + + int totalServices = JsonPath.read(response, "$.totalServices"); + + + List servicesList = serviceRepoService.findAll(); + int activeServices = (int) servicesList.stream().filter(service -> service.getState() == ServiceStateType.ACTIVE).count(); + + assertThat(totalServices).isEqualTo(activeServices); + assertThat(activeServices).isEqualTo(1); + } + + @WithMockUser(username = "osadmin", roles = {"ADMIN", "USER"}) + @Test + public void testGetServicesGroupedByState() throws Exception { + String startTime = OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT); + + createService(ServiceStateType.ACTIVE); + createService(ServiceStateType.ACTIVE); + createService(ServiceStateType.ACTIVE); + createService(ServiceStateType.INACTIVE); + createService(ServiceStateType.INACTIVE); + createService(ServiceStateType.TERMINATED); + + String endTime = OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT); + + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/servicesGroupByState") + .param("starttime", startTime) + .param("endtime", endTime) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + List> groupByState = JsonPath.read(response, "$.services.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("ACTIVE")).isEqualTo(3); + assertThat(stateCounts.get("INACTIVE")).isEqualTo(2); + assertThat(stateCounts.get("TERMINATED")).isEqualTo(1); + assertThat(stateCounts.get("FEASIBILITYCHECKED")).isEqualTo(0); + assertThat(stateCounts.get("RESERVED")).isEqualTo(0); + assertThat(stateCounts.get("DESIGNED")).isEqualTo(0); + } + + + + @Transactional + void createService(ServiceStateType state ) throws Exception { + int servicesCount = serviceRepoService.findAll().size(); + + File sspec = new File( "src/test/resources/testServiceSpec.json" ); + InputStream in = new FileInputStream( sspec ); + String sspectext = IOUtils.toString(in, "UTF-8"); + + ServiceSpecificationCreate sspeccr1 = JsonUtils.toJsonObj( sspectext, ServiceSpecificationCreate.class); + sspeccr1.setName("Spec1"); + ServiceSpecification responsesSpec = createServiceSpec(sspeccr1); + + ServiceCreate aService = new ServiceCreate(); + aService.setName("aNew Service"); + aService.setCategory("Test Category"); + aService.setDescription("A Test Service"); + aService.setStartDate( OffsetDateTime.now(ZoneOffset.UTC ).toString() ); + aService.setEndDate( OffsetDateTime.now(ZoneOffset.UTC ).toString() ); + aService.setState(state); + + Characteristic serviceCharacteristicItem = new Characteristic(); + + serviceCharacteristicItem.setName( "ConfigStatus" ); + serviceCharacteristicItem.setValue( new Any("NONE")); + aService.addServiceCharacteristicItem(serviceCharacteristicItem); + + ServiceSpecificationRef aServiceSpecificationRef = new ServiceSpecificationRef(); + aServiceSpecificationRef.setId(responsesSpec.getId() ); + aServiceSpecificationRef.setName(responsesSpec.getName()); + + aService.setServiceSpecificationRef(aServiceSpecificationRef ); + + String response = mvc.perform(MockMvcRequestBuilders.post("/serviceInventory/v4/service") + .with( SecurityMockMvcRequestPostProcessors.csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content( JsonUtils.toJson( aService ) )) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + Service responseService = JsonUtils.toJsonObj(response, Service.class); + + assertThat( serviceRepoService.findAll().size() ).isEqualTo( servicesCount + 1 ); + assertThat(responseService.getCategory()).isEqualTo("Test Category"); + assertThat(responseService.getDescription()).isEqualTo("A Test Service"); + + } + + private ServiceSpecification createServiceSpec(ServiceSpecificationCreate sspeccr1) throws Exception{ + String response = mvc.perform(MockMvcRequestBuilders.post("/serviceCatalogManagement/v4/serviceSpecification") + .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(); + + return JsonUtils.toJsonObj(response, ServiceSpecification.class); + } +} diff --git a/src/test/java/org/etsi/osl/services/api/metrics/ServiceOrderMetricsApiControllerTest.java b/src/test/java/org/etsi/osl/services/api/metrics/ServiceOrderMetricsApiControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..757f82cd45d6445b6427d08bf4910743fcd8cf27 --- /dev/null +++ b/src/test/java/org/etsi/osl/services/api/metrics/ServiceOrderMetricsApiControllerTest.java @@ -0,0 +1,254 @@ +package org.etsi.osl.services.api.metrics; + +import com.jayway.jsonpath.JsonPath; +import org.etsi.osl.tmf.JsonUtils; +import org.etsi.osl.tmf.OpenAPISpringBoot; +import org.etsi.osl.tmf.so641.model.*; +import org.etsi.osl.tmf.so641.reposervices.ServiceOrderRepoService; +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.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +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 ServiceOrderMetricsApiControllerTest { + + @Autowired + private MockMvc mvc; + + @Autowired + ServiceOrderRepoService serviceOrderRepoService; + + @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 testCountTotalServiceOrders() throws Exception { + createServiceOrder(ServiceOrderStateType.INPROGRESS); + + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/totalServiceOrders" ) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk() ) + .andReturn().getResponse().getContentAsString(); + + int totalServiceOrders = JsonPath.read(response, "$.totalServiceOrders"); + + + assertThat(totalServiceOrders).isEqualTo(serviceOrderRepoService.findAll().size()); + } + + @WithMockUser(username="osadmin", roles = {"ADMIN","USER"}) + @Test + public void testCountTotalServiceOrdersWithState() throws Exception { + createServiceOrder(ServiceOrderStateType.INPROGRESS); + createServiceOrder(ServiceOrderStateType.ACKNOWLEDGED); + + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/totalServiceOrders" ) + .param("state", "ACKNOWLEDGED") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk() ) + .andReturn().getResponse().getContentAsString(); + + int totalServiceOrders = JsonPath.read(response, "$.totalServiceOrders"); + + + List serviceOrdersList = serviceOrderRepoService.findAll(); + int activeServiceOrders = (int) serviceOrdersList.stream().filter(serviceOrder -> serviceOrder.getState() == ServiceOrderStateType.ACKNOWLEDGED).count(); + + assertThat(totalServiceOrders).isEqualTo(activeServiceOrders); + assertThat(activeServiceOrders).isEqualTo(1); + } + + @WithMockUser(username="osadmin", roles = {"ADMIN","USER"}) + @Test + public void testGetTotalActiveServiceOrders() throws Exception { + createServiceOrder(ServiceOrderStateType.INPROGRESS); + createServiceOrder(ServiceOrderStateType.INPROGRESS); + createServiceOrder(ServiceOrderStateType.COMPLETED); + createServiceOrder(ServiceOrderStateType.COMPLETED); + createServiceOrder(ServiceOrderStateType.ACKNOWLEDGED); + createServiceOrder(ServiceOrderStateType.REJECTED); + + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/activeServiceOrders" ) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk() ) + .andReturn().getResponse().getContentAsString(); + + int totalServiceOrders = JsonPath.read(response, "$.activeServiceOrders"); + + assertThat(totalServiceOrders).isEqualTo(4); + } + + @WithMockUser(username = "osadmin", roles = {"ADMIN", "USER"}) + @Test + public void testGetServiceOrdersGroupedByDay() throws Exception { + String startTime = OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT); + + createServiceOrder(ServiceOrderStateType.INPROGRESS); + createServiceOrder(ServiceOrderStateType.INPROGRESS); + createServiceOrder(ServiceOrderStateType.INPROGRESS); + createServiceOrder(ServiceOrderStateType.ACKNOWLEDGED); + createServiceOrder(ServiceOrderStateType.ACKNOWLEDGED); + createServiceOrder(ServiceOrderStateType.PARTIAL); + + String endTime = OffsetDateTime.now(ZoneOffset.UTC).plusDays(4).format(DateTimeFormatter.ISO_INSTANT); + + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/serviceOrdersGroupByDay") + .param("starttime", startTime) + .param("endtime", endTime) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + List> groupByDay = JsonPath.read(response, "$.serviceOrders.aggregations.groupByDay"); + + + OffsetDateTime start = OffsetDateTime.parse(startTime); + OffsetDateTime end = OffsetDateTime.parse(endTime); + + DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT; + + Map dayCounts = groupByDay.stream() + .collect(Collectors.toMap( + entry -> (String) entry.get("key"), + entry -> (Integer) entry.get("count") + )); + + int totalDays = (int) ChronoUnit.DAYS.between(start.toLocalDate(), end.toLocalDate()) + 1; + + for (int i = 0; i < totalDays; i++) { + OffsetDateTime day = start.plusDays(i).toLocalDate().atStartOfDay().atOffset(ZoneOffset.UTC); + String dayKey = formatter.format(day.toInstant()); + + if (i == 0) { + // Today: should have all 6 + assertThat(dayCounts.get(dayKey)).isEqualTo(6); + } else { + // Other days: should be 0 + assertThat(dayCounts.get(dayKey)).isEqualTo(0); + } + } + } + + @WithMockUser(username = "osadmin", roles = {"ADMIN", "USER"}) + @Test + public void testGetServiceOrdersGroupedByState() throws Exception { + String startTime = OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT); + + createServiceOrder(ServiceOrderStateType.INPROGRESS); + createServiceOrder(ServiceOrderStateType.INPROGRESS); + createServiceOrder(ServiceOrderStateType.INPROGRESS); + createServiceOrder(ServiceOrderStateType.ACKNOWLEDGED); + createServiceOrder(ServiceOrderStateType.ACKNOWLEDGED); + createServiceOrder(ServiceOrderStateType.PARTIAL); + + String endTime = OffsetDateTime.now(ZoneOffset.UTC).plusDays(4).format(DateTimeFormatter.ISO_INSTANT); + + String response = mvc.perform(MockMvcRequestBuilders.get("/metrics/serviceOrdersGroupByState") + .param("starttime", startTime) + .param("endtime", endTime) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + List> groupByState = JsonPath.read(response, "$.serviceOrders.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("INPROGRESS")).isEqualTo(3); + assertThat(stateCounts.get("ACKNOWLEDGED")).isEqualTo(2); + assertThat(stateCounts.get("PARTIAL")).isEqualTo(1); + assertThat(stateCounts.get("INITIAL")).isEqualTo(0); + assertThat(stateCounts.get("REJECTED")).isEqualTo(0); + assertThat(stateCounts.get("PENDING")).isEqualTo(0); + assertThat(stateCounts.get("HELD")).isEqualTo(0); + assertThat(stateCounts.get("CANCELLED")).isEqualTo(0); + assertThat(stateCounts.get("COMPLETED")).isEqualTo(0); + assertThat(stateCounts.get("FAILED")).isEqualTo(0); + } + + private void createServiceOrder(ServiceOrderStateType stateType) throws Exception { + + ServiceOrderCreate serviceOrder = new ServiceOrderCreate(); + serviceOrder.setCategory("Test Category"); + serviceOrder.setDescription("A Test Service Order"); + serviceOrder.setRequestedStartDate(OffsetDateTime.now(ZoneOffset.UTC).toString()); + serviceOrder.setRequestedCompletionDate(OffsetDateTime.now(ZoneOffset.UTC).plusDays(3).toString()); + + String response = mvc + .perform(MockMvcRequestBuilders.post("/serviceOrdering/v4/serviceOrder") + .with( SecurityMockMvcRequestPostProcessors.csrf()) + .contentType(MediaType.APPLICATION_JSON).content(JsonUtils.toJson(serviceOrder))) + .andExpect(status().isOk()).andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + + ServiceOrder responseSO = JsonUtils.toJsonObj(response, ServiceOrder.class); + + // Update Service Order State + String soId = responseSO.getId(); + + ServiceOrderUpdate servOrderUpd = new ServiceOrderUpdate(); + servOrderUpd.setState(stateType); + + String response2 = mvc.perform(MockMvcRequestBuilders.patch("/serviceOrdering/v4/serviceOrder/" + soId) + .with( SecurityMockMvcRequestPostProcessors.csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content( JsonUtils.toJson( servOrderUpd ) )) + .andExpect(status().isOk() ) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andReturn().getResponse().getContentAsString(); + + ServiceOrder responsesServiceOrder2 = JsonUtils.toJsonObj(response2, ServiceOrder.class); + assertThat(responsesServiceOrder2.getState().toString()).isEqualTo(stateType.toString()); + } + +} diff --git a/src/test/java/org/etsi/osl/services/api/pm628/ManagementJobMVOTest.java b/src/test/java/org/etsi/osl/services/api/pm628/ManagementJobMVOTest.java new file mode 100644 index 0000000000000000000000000000000000000000..27275dfbafea5019c3630e65e451bdfbe7afb5c1 --- /dev/null +++ b/src/test/java/org/etsi/osl/services/api/pm628/ManagementJobMVOTest.java @@ -0,0 +1,35 @@ +package org.etsi.osl.services.api.pm628; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.tmf.pm628.model.ExecutionStateType; +import org.etsi.osl.tmf.pm628.model.ManagementJobMVO; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ManagementJobMVOTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void testToStringShowsExecutionState() { + ManagementJobMVO job = new ManagementJobMVO("ManagementJob"); + job.setExecutionState(ExecutionStateType.ACKNOWLEDGED); + + String str = job.toString(); + // executionState is not included in the default toString, so this will fail unless you add it to the toString method + assertTrue(str.contains("executionState: acknowledged"), "Default toString does not show executionState"); + } + + @Test + void testSerializationDeserialization() throws Exception { + ManagementJobMVO job = new ManagementJobMVO("ManagementJob"); + job.setExecutionState(ExecutionStateType.ACKNOWLEDGED); + + String json = objectMapper.writeValueAsString(job); + ManagementJobMVO deserialized = objectMapper.readValue(json, ManagementJobMVO.class); + + assertEquals(job, deserialized); + assertEquals(ExecutionStateType.ACKNOWLEDGED, deserialized.getExecutionState()); + } +} \ No newline at end of file diff --git a/src/test/java/org/etsi/osl/services/api/pm628/MeasurementCollectionJobMVOTest.java b/src/test/java/org/etsi/osl/services/api/pm628/MeasurementCollectionJobMVOTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5b34a0c8cf85441b7e77fce9aee274350bfead35 --- /dev/null +++ b/src/test/java/org/etsi/osl/services/api/pm628/MeasurementCollectionJobMVOTest.java @@ -0,0 +1,48 @@ +package org.etsi.osl.services.api.pm628; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.tmf.pm628.model.*; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; + +class MeasurementCollectionJobMVOTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void testToString() { + MeasurementCollectionJobMVO job = new MeasurementCollectionJobMVO("MeasurementCollectionJob") + .outputFormat("JSON") + .jobOnDemand(true); + job.setExecutionState(ExecutionStateType.ACKNOWLEDGED); + + String str = job.toString(); + assertTrue(str.contains("outputFormat: JSON")); + assertTrue(str.contains("jobOnDemand: true")); + assertTrue(str.contains("executionState: acknowledged")); + assertTrue(str.contains("class MeasurementCollectionJobMVO")); + } + + @Test + void testSerializationDeserialization() throws Exception { + MeasurementCollectionJobMVO job = new MeasurementCollectionJobMVO("MeasurementCollectionJob") + .outputFormat("JSON") + .jobOnDemand(false); + + // Set a nested object for better coverage + job.setReportingPeriod(ReportingPeriod.R_1H); + job.setExecutionState(ExecutionStateType.ACKNOWLEDGED); + + String json = objectMapper.writeValueAsString(job); + MeasurementCollectionJobMVO deserialized = objectMapper.readValue(json, MeasurementCollectionJobMVO.class); + + assertEquals(job, deserialized); + assertEquals("JSON", deserialized.getOutputFormat()); + assertFalse(deserialized.getJobOnDemand()); + assertNotNull(deserialized.getReportingPeriod()); + assertEquals(ExecutionStateType.ACKNOWLEDGED, deserialized.getExecutionState()); + } +} \ No newline at end of file diff --git a/src/test/java/org/etsi/osl/services/api/pm628/MeasurementCollectionJobServiceTest.java b/src/test/java/org/etsi/osl/services/api/pm628/MeasurementCollectionJobServiceTest.java index d718e0ab2f954b6ab5510258ee4a01911b596564..65fdd391310872225a6220d3b4580dc30a2d5086 100644 --- a/src/test/java/org/etsi/osl/services/api/pm628/MeasurementCollectionJobServiceTest.java +++ b/src/test/java/org/etsi/osl/services/api/pm628/MeasurementCollectionJobServiceTest.java @@ -192,4 +192,38 @@ public class MeasurementCollectionJobServiceTest { return response; } + + private MeasurementCollectionJob createNewMeasurementCollectionJobWithExecutionState(int previousNumberOfMcjs, ExecutionStateType executionStateType) throws Exception { + assertThat(measurementCollectionJobService.findAllMeasurementCollectionJobs().size()).isEqualTo(FIXED_BOOTSTRAPS_JOBS + previousNumberOfMcjs); + + File fvo = new File("src/test/resources/testMeasurementCollectionJobFVO.json"); + InputStream in = new FileInputStream(fvo); + String mcjFVOText = IOUtils.toString(in, "UTF-8"); + + MeasurementCollectionJobFVO mcjFVO = JsonUtils.toJsonObj(mcjFVOText, MeasurementCollectionJobFVO.class); + mcjFVO.setExecutionState(executionStateType); + + MeasurementCollectionJob response = measurementCollectionJobService.createMeasurementCollectionJob(mcjFVO); + + assertThat(measurementCollectionJobService.findAllMeasurementCollectionJobs().size()).isEqualTo(FIXED_BOOTSTRAPS_JOBS + previousNumberOfMcjs + 1); + + return response; + } + +// @WithMockUser(username="osadmin", roles = {"USER","ADMIN"}) +// @Test +// public void testFindPendingOrInProgressMeasurementCollectionJobs() throws Exception { +// MeasurementCollectionJob pendingMcj = createNewMeasurementCollectionJobWithExecutionState(measurementCollectionJobService.findAllMeasurementCollectionJobs().size(), ExecutionStateType.PENDING); +// +// int currentNumOfMcjs = measurementCollectionJobService.findAllMeasurementCollectionJobs().size(); +// MeasurementCollectionJob inProgressMcj = createNewMeasurementCollectionJobWithExecutionState(currentNumOfMcjs, ExecutionStateType.INPROGRESS); +// +// +// List ackMcjList = measurementCollectionJobService.findAllByExecutionState(ExecutionStateType.ACKNOWLEDGED); +// +// List mcjList = measurementCollectionJobService.findPendingOrInProgressMeasurementCollectionJobs(); +// +// +// assertThat(mcjList.size()).isEqualTo(FIXED_BOOTSTRAPS_JOBS + 2); +// } } diff --git a/src/test/java/org/etsi/osl/services/api/pm628/SerializationTest.java b/src/test/java/org/etsi/osl/services/api/pm628/SerializationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6d7bf41356702236ddaade97737af96038fd6f0f --- /dev/null +++ b/src/test/java/org/etsi/osl/services/api/pm628/SerializationTest.java @@ -0,0 +1,51 @@ +package org.etsi.osl.services.api.pm628; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator; +import org.etsi.osl.tmf.pm628.model.MeasurementCollectionJob; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SerializationTest { + + @Test + public void testJsonArraySerialization() throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + + // Create a list of objects to serialize + MeasurementCollectionJob job1 = new MeasurementCollectionJob("JobType1").outputFormat("JSON"); + job1.setType("MeasurementCollectionJob"); + MeasurementCollectionJob job2 = new MeasurementCollectionJob("JobType2").outputFormat("JSON"); + job2.setType("MeasurementCollectionJob"); + String job1Str = objectMapper.writeValueAsString(job1); + System.out.println(job1Str); +// List jobs = List.of(job1, job2); + List jobs = new ArrayList<>(); + jobs.add(job1); + jobs.add(job2); + + // Serialize the list to JSON + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + + String jsonArray = objectMapper.writeValueAsString(jobs); + System.out.println(jsonArray); + + // Deserialize the JSON back to a list + List deserializedJobs = objectMapper.readValue( + jsonArray, + objectMapper.getTypeFactory().constructCollectionType(List.class, MeasurementCollectionJob.class) + ); + + // Assert the deserialized list matches the original + assertEquals(jobs.size(), deserializedJobs.size()); + assertEquals(jobs.get(0).getOutputFormat(), deserializedJobs.get(0).getOutputFormat()); + assertEquals(jobs.get(1).getOutputFormat(), deserializedJobs.get(1).getOutputFormat()); + } +} \ No newline at end of file diff --git a/src/test/java/org/etsi/osl/services/api/sim638/ServiceRepoServiceTest.java b/src/test/java/org/etsi/osl/services/api/sim638/ServiceRepoServiceTest.java index d433726a7f82478384f861b19c7d5644ce43283a..ded942c8a351a41ddd5ba20e18204a2070b5308a 100644 --- a/src/test/java/org/etsi/osl/services/api/sim638/ServiceRepoServiceTest.java +++ b/src/test/java/org/etsi/osl/services/api/sim638/ServiceRepoServiceTest.java @@ -524,7 +524,7 @@ public class ServiceRepoServiceTest { r1.setResourceStatus(ResourceStatusType.SUSPENDED); nstate = s.findNextStateBasedOnResourceList(rlist); - assertThat(nstate).isEqualTo( ServiceStateType.TERMINATED ); + assertThat(nstate).isEqualTo( ServiceStateType.ACTIVE ); s.setState( ServiceStateType.TERMINATED ); r1.setResourceStatus(ResourceStatusType.AVAILABLE); diff --git a/src/test/resources/testMeasurementCollectionJobFVO.json b/src/test/resources/testMeasurementCollectionJobFVO.json index 3854d4e47a8bacd2af998805f2ffdf3033c27a8e..14b324bd4337204263e82e999835a3a13891ac66 100644 --- a/src/test/resources/testMeasurementCollectionJobFVO.json +++ b/src/test/resources/testMeasurementCollectionJobFVO.json @@ -35,7 +35,6 @@ "groupCategory": "CPUGroup", "performanceIndicatorSpecification": [ { - "uuid": "5000", "href": "https://host:port/tmf-api/performanceManagement/v5/performanceIndicatorSpecification/5000", "@type": "PerformanceIndicatorSpecificationRef" } @@ -47,7 +46,6 @@ ], "performanceIndicatorSpecification": [ { - "uuid": "101", "href": "https://host:port/tmf-api/performanceManagement/v5/performanceIndicatorSpecification/101", "derivationAlgorithm": "na", "derivationMethod": "average", @@ -60,7 +58,6 @@ "indicatorType": "float", "performanceIndicatorSpecRelationship": [ { - "uuid": "321", "href": "https://host:port/tmf-api/performanceManagement/v5/performanceIndicatorSpecRelationship/321", "relationshipType": "reliesOn", "validFor": {