diff options
author | raviteja.karumuri <raviteja.karumuri@est.tech> | 2023-09-14 12:38:49 +0100 |
---|---|---|
committer | raviteja.karumuri <raviteja.karumuri@est.tech> | 2023-09-27 10:45:15 +0100 |
commit | a4687d9a257e6d95cdca6182d5a012a149f5f716 (patch) | |
tree | 6fe6288bcc45d49c3a13c9edb00470fab64f5ec2 /a1-policy-management/src | |
parent | aeb5e2ac45f459a9145a3898b8144387a1703a97 (diff) |
OpenApi-First approach
Issue-ID: CCSDK-3927
Signed-off-by: raviteja.karumuri <raviteja.karumuri@est.tech>
Change-Id: Ie88b84cce42081f1becdf186813b2e557479297b
Signed-off-by: raviteja.karumuri <raviteja.karumuri@est.tech>
Diffstat (limited to 'a1-policy-management/src')
22 files changed, 500 insertions, 1289 deletions
diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/BeanFactory.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/BeanFactory.java index 0d93eae2..71eae06f 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/BeanFactory.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/BeanFactory.java @@ -31,8 +31,9 @@ import org.onap.ccsdk.oran.a1policymanagementservice.repository.Rics; import org.onap.ccsdk.oran.a1policymanagementservice.repository.Services; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.boot.web.servlet.server.ServletWebServerFactory; +import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -78,8 +79,8 @@ public class BeanFactory { } @Bean - public ServletWebServerFactory servletContainer() { - TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); + public ReactiveWebServerFactory servletContainer() { + TomcatReactiveWebServerFactory tomcat = new TomcatReactiveWebServerFactory(); if (httpPort > 0) { tomcat.addAdditionalTomcatConnectors(getHttpConnector(httpPort)); } diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ConfigurationController.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ConfigurationController.java index 0d0919d8..6bf6fbaf 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ConfigurationController.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ConfigurationController.java @@ -22,38 +22,28 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import com.google.gson.JsonParser; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -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 java.io.IOException; -import java.util.Optional; - import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig; import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfigParser; import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ConfigurationFile; -import org.onap.ccsdk.oran.a1policymanagementservice.controllers.VoidResponse; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.api.v2.ConfigurationApi; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; +import java.io.IOException; +import java.util.Optional; @RestController("ConfigurationControllerV2") @Tag( // name = ConfigurationController.API_NAME, // description = ConfigurationController.API_DESCRIPTION // ) -public class ConfigurationController { +public class ConfigurationController implements ConfigurationApi { private static final Logger logger = LoggerFactory.getLogger(ConfigurationController.class); public static final String API_NAME = "Management of configuration"; @@ -71,58 +61,43 @@ public class ConfigurationController { private static Gson gson = new GsonBuilder() // .create(); // - @PutMapping(path = Consts.V2_API_ROOT + "/configuration", consumes = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = "Replace the current configuration file with the given configuration") - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "Configuration updated", // - content = @Content(schema = @Schema(implementation = VoidResponse.class))), // - @ApiResponse(responseCode = "400", // - description = "Invalid configuration provided", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))), // - @ApiResponse(responseCode = "500", // - description = "Something went wrong when replacing the configuration. Try again.", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) // - }) - public ResponseEntity<Object> putConfiguration(@RequestBody Object configuration) { - try { - String configAsString = gson.toJson(configuration); - JsonObject configJson = JsonParser.parseString(configAsString).getAsJsonObject(); - ApplicationConfigParser configParser = new ApplicationConfigParser(applicationConfig); - configParser.parse(configJson); - configurationFile.writeFile(configJson); - logger.info("Configuration changed through REST call."); - return new ResponseEntity<>(HttpStatus.OK); - } catch (IOException ioe) { - logger.warn("Configuration file not written, {}.", ioe.getMessage()); - return ErrorResponse.create("Internal error when writing the configuration.", - HttpStatus.INTERNAL_SERVER_ERROR); - } catch (Exception e) { - return ErrorResponse.create(e, HttpStatus.BAD_REQUEST); - } + @Override + public Mono<ResponseEntity<Object>> putConfiguration(final Mono<Object> configuration, + final ServerWebExchange exchange) { + return configuration + .flatMap(configObject -> { + try { + String configAsString = gson.toJson(configObject); + JsonObject configJson = JsonParser.parseString(configAsString).getAsJsonObject(); + ApplicationConfigParser configParser = new ApplicationConfigParser(applicationConfig); + configParser.parse(configJson); + configurationFile.writeFile(configJson); + logger.info("Configuration changed through REST call."); + return Mono.just(new ResponseEntity<>(HttpStatus.OK)); + } catch (IOException ioe) { + logger.warn("Configuration file not written, {}.", ioe.getMessage()); + return ErrorResponse.createMono("Internal error when writing the configuration.", + HttpStatus.INTERNAL_SERVER_ERROR); + } catch (Exception e) { + return ErrorResponse.createMono(e, HttpStatus.BAD_REQUEST); + } + }) + .onErrorResume(error -> { + return ErrorResponse.createMono(error.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + }); } - @GetMapping(path = Consts.V2_API_ROOT + "/configuration", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = "Returns the contents of the application configuration file") // - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "Configuration", // - content = @Content(schema = @Schema(implementation = Object.class))), // - @ApiResponse(responseCode = "404", // - description = "File is not found or readable", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) - - }) - public ResponseEntity<Object> getConfiguration() { + @Override + public Mono<ResponseEntity<Object>> getConfiguration(final ServerWebExchange exchange) { try { Optional<JsonObject> rootObject = configurationFile.readFile(); if (rootObject.isPresent()) { - return new ResponseEntity<>(rootObject.get().toString(), HttpStatus.OK); + return Mono.just(new ResponseEntity<>(rootObject.get().toString(), HttpStatus.OK)); } else { - return ErrorResponse.create("File does not exist", HttpStatus.NOT_FOUND); + return ErrorResponse.createMono("File does not exist", HttpStatus.NOT_FOUND); } } catch (Exception e) { - return ErrorResponse.create(e, HttpStatus.INTERNAL_SERVER_ERROR); + return ErrorResponse.createMono(e, HttpStatus.INTERNAL_SERVER_ERROR); } } diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyController.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyController.java index 64905f44..23dcba71 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyController.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyController.java @@ -2,7 +2,7 @@ * ========================LICENSE_START================================= * ONAP : ccsdk oran * ====================================================================== - * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved. + * Copyright (C) 2019-2023 Nordix Foundation. All rights reserved. * ====================================================================== * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,15 +20,12 @@ package org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -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 java.lang.invoke.MethodHandles; @@ -41,38 +38,28 @@ import java.util.Map; import lombok.Getter; import org.onap.ccsdk.oran.a1policymanagementservice.clients.A1ClientFactory; -import org.onap.ccsdk.oran.a1policymanagementservice.controllers.VoidResponse; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.api.v2.A1PolicyManagementApi; import org.onap.ccsdk.oran.a1policymanagementservice.controllers.authorization.AuthorizationCheck; import org.onap.ccsdk.oran.a1policymanagementservice.controllers.authorization.PolicyAuthorizationRequest.Input.AccessType; import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.EntityNotFoundException; import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException; -import org.onap.ccsdk.oran.a1policymanagementservice.repository.Lock; -import org.onap.ccsdk.oran.a1policymanagementservice.repository.Lock.LockType; -import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies; -import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy; -import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyType; -import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyTypes; -import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric; -import org.onap.ccsdk.oran.a1policymanagementservice.repository.Rics; -import org.onap.ccsdk.oran.a1policymanagementservice.repository.Service; -import org.onap.ccsdk.oran.a1policymanagementservice.repository.Services; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyTypeDefinition; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyInfo; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyTypeIdList; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyInfoList; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyIdList; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyStatusInfo; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.function.client.WebClientException; import org.springframework.web.reactive.function.client.WebClientResponseException; +import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -81,7 +68,7 @@ import reactor.core.publisher.Mono; name = PolicyController.API_NAME, // description = PolicyController.API_DESCRIPTION // ) -public class PolicyController { +public class PolicyController implements A1PolicyManagementApi { public static final String API_NAME = "A1 Policy Management"; public static final String API_DESCRIPTION = ""; @@ -108,7 +95,8 @@ public class PolicyController { private A1ClientFactory a1ClientFactory; @Autowired private Services services; - + @Autowired + private ObjectMapper objectMapper; @Autowired private AuthorizationCheck authorization; @@ -116,51 +104,17 @@ public class PolicyController { private static Gson gson = new GsonBuilder() // .create(); // - @GetMapping(path = Consts.V2_API_ROOT + "/policy-types/{policytype_id:.+}") // - @Operation(summary = "Returns a policy type definition") // - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "Policy type", // - content = @Content(schema = @Schema(implementation = PolicyTypeInfo.class))), // - @ApiResponse(responseCode = "404", // - description = "Policy type is not found", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class)))// - }) - public ResponseEntity<Object> getPolicyType( // - @PathVariable("policytype_id") String policyTypeId) throws EntityNotFoundException { + @Override + public Mono<ResponseEntity<Object>> getPolicyTypeDefinition(String policyTypeId, ServerWebExchange exchange) + throws EntityNotFoundException, JsonProcessingException { PolicyType type = policyTypes.getType(policyTypeId); - PolicyTypeInfo info = new PolicyTypeInfo(type.getSchema()); - return new ResponseEntity<>(gson.toJson(info), HttpStatus.OK); + JsonNode node = objectMapper.readTree(type.getSchema()); + PolicyTypeDefinition policyTypeDefinition = new PolicyTypeDefinition().policySchema(node); + return Mono.just(new ResponseEntity<>(policyTypeDefinition, HttpStatus.OK)); } - @GetMapping(path = Consts.V2_API_ROOT + "/policy-types", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = "Query policy type identities") - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "Policy type IDs", // - content = @Content(schema = @Schema(implementation = PolicyTypeIdList.class))), // - @ApiResponse(responseCode = "404", // - description = "Near-RT RIC is not found", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) // - }) - public ResponseEntity<Object> getPolicyTypes( // - @Parameter(name = Consts.RIC_ID_PARAM, required = false, // - description = "Select types for the given Near-RT RIC identity.") // - @RequestParam(name = Consts.RIC_ID_PARAM, required = false) String ricId, - - @Parameter(name = Consts.TYPE_NAME_PARAM, required = false, // - description = "Select types with the given type name (type identity has the format <typename_version>)") // - @RequestParam(name = Consts.TYPE_NAME_PARAM, required = false) String typeName, - - @Parameter(name = Consts.COMPATIBLE_WITH_VERSION_PARAM, required = false, // - description = "Select types that are compatible with the given version. This parameter is only applicable in conjunction with " - + Consts.TYPE_NAME_PARAM - + ". As an example version 1.9.1 is compatible with 1.0.0 but not the other way around." - + " Matching types will be returned sorted in ascending order.") // - @RequestParam(name = Consts.COMPATIBLE_WITH_VERSION_PARAM, required = false) String compatibleWithVersion - - ) throws ServiceException { - + @Override + public Mono<ResponseEntity<Object>> getPolicyTypes(String ricId, String typeName, String compatibleWithVersion, ServerWebExchange exchange) throws Exception { if (compatibleWithVersion != null && typeName == null) { throw new ServiceException("Parameter " + Consts.COMPATIBLE_WITH_VERSION_PARAM + " can only be used when " + Consts.TYPE_NAME_PARAM + " is given", HttpStatus.BAD_REQUEST); @@ -170,117 +124,82 @@ public class PolicyController { ricId != null ? rics.getRic(ricId).getSupportedPolicyTypes() : this.policyTypes.getAll(); types = PolicyTypes.filterTypes(types, typeName, compatibleWithVersion); - return new ResponseEntity<>(toPolicyTypeIdsJson(types), HttpStatus.OK); + return Mono.just(new ResponseEntity<>(toPolicyTypeIdsJson(types), HttpStatus.OK)); } - @GetMapping(path = Consts.V2_API_ROOT + "/policies/{policy_id:.+}", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = "Returns a policy") // - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "Policy found", // - content = @Content(schema = @Schema(implementation = PolicyInfo.class))), // - @ApiResponse(responseCode = "404", // - description = "Policy is not found", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) // - }) - public Mono<ResponseEntity<Object>> getPolicy( // - @PathVariable(name = Consts.POLICY_ID_PARAM, required = true) String id, - @RequestHeader Map<String, String> headers) throws EntityNotFoundException { - Policy policy = policies.getPolicy(id); - return authorization.doAccessControl(headers, policy, AccessType.READ) // - .map(x -> new ResponseEntity<>((Object) gson.toJson(toPolicyInfo(policy)), HttpStatus.OK)) // + + @Override + public Mono<ResponseEntity<Object>> getPolicy(String policyId, final ServerWebExchange exchange) + throws EntityNotFoundException { + Policy policy = policies.getPolicy(policyId); + return authorization.doAccessControl(exchange.getRequest().getHeaders().toSingleValueMap(), policy, AccessType.READ) // + .map(x -> new ResponseEntity<>((Object) toPolicyInfo(policy), HttpStatus.OK)) // .onErrorResume(this::handleException); } - @DeleteMapping(Consts.V2_API_ROOT + "/policies/{policy_id:.+}") - @Operation(summary = "Delete a policy") - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "Not used", // - content = @Content(schema = @Schema(implementation = VoidResponse.class))), // - @ApiResponse(responseCode = "204", // - description = "Policy deleted", // - content = @Content(schema = @Schema(implementation = VoidResponse.class))), // - @ApiResponse(responseCode = "404", // - description = "Policy is not found", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))), // - @ApiResponse(responseCode = "423", // - description = "Near-RT RIC is not operational", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) // - }) - public Mono<ResponseEntity<Object>> deletePolicy( // - @PathVariable(Consts.POLICY_ID_PARAM) String policyId, @RequestHeader Map<String, String> headers) - throws EntityNotFoundException { + @Override + public Mono<ResponseEntity<Object>> deletePolicy(String policyId, ServerWebExchange exchange) throws Exception { + Policy policy = policies.getPolicy(policyId); keepServiceAlive(policy.getOwnerServiceId()); - return authorization.doAccessControl(headers, policy, AccessType.WRITE) - .flatMap(x -> policy.getRic().getLock().lock(LockType.SHARED, "deletePolicy")) // - .flatMap(grant -> deletePolicy(grant, policy)) // + return authorization.doAccessControl(exchange.getRequest().getHeaders().toSingleValueMap(), policy, AccessType.WRITE) + .flatMap(x -> policy.getRic().getLock().lock(Lock.LockType.SHARED, "deletePolicy")) + .flatMap(grant -> deletePolicy(grant, policy)) .onErrorResume(this::handleException); } Mono<ResponseEntity<Object>> deletePolicy(Lock.Grant grant, Policy policy) { - return assertRicStateIdle(policy.getRic()) // - .flatMap(notUsed -> a1ClientFactory.createA1Client(policy.getRic())) // - .doOnNext(notUsed -> policies.remove(policy)) // - .doFinally(x -> grant.unlockBlocking()) // - .flatMap(client -> client.deletePolicy(policy)) // - .map(notUsed -> new ResponseEntity<>(HttpStatus.NO_CONTENT)) // + return checkRicStateIdle(policy.getRic()) // + .flatMap(notUsed -> a1ClientFactory.createA1Client(policy.getRic())) + .doOnNext(notUsed -> policies.remove(policy)) + .doFinally(x -> grant.unlockBlocking()) + .flatMap(client -> client.deletePolicy(policy)) + .map(notUsed -> new ResponseEntity<>(HttpStatus.NO_CONTENT)) .onErrorResume(this::handleException); } - @PutMapping(path = Consts.V2_API_ROOT + "/policies", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = "Create or update a policy") - @ApiResponses(value = { // - @ApiResponse(responseCode = "201", // - description = "Policy created", // - content = @Content(schema = @Schema(implementation = VoidResponse.class))), // - @ApiResponse(responseCode = "200", // - description = "Policy updated", // - content = @Content(schema = @Schema(implementation = VoidResponse.class))), // - @ApiResponse(responseCode = "423", // - description = "Near-RT RIC is not operational", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))), // - @ApiResponse(responseCode = "404", // - description = "Near-RT RIC or policy type is not found", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) // - }) - public Mono<ResponseEntity<Object>> putPolicy(@RequestBody PolicyInfo policyInfo, - @RequestHeader Map<String, String> headers) throws EntityNotFoundException { - - if (!policyInfo.validate()) { - return ErrorResponse.createMono("Missing required parameter in body", HttpStatus.BAD_REQUEST); - } - String jsonString = gson.toJson(policyInfo.policyData); - Ric ric = rics.get(policyInfo.ricId); - PolicyType type = policyTypes.get(policyInfo.policyTypeId); - keepServiceAlive(policyInfo.serviceId); - if (ric == null || type == null) { - throw new EntityNotFoundException("Near-RT RIC or policy type not found"); - } - Policy policy = Policy.builder() // - .id(policyInfo.policyId) // - .json(jsonString) // - .type(type) // - .ric(ric) // - .ownerServiceId(policyInfo.serviceId) // - .lastModified(Instant.now()) // - .isTransient(policyInfo.isTransient) // - .statusNotificationUri(policyInfo.statusNotificationUri == null ? "" : policyInfo.statusNotificationUri) // - .build(); - - return authorization.doAccessControl(headers, policy, AccessType.WRITE) // - .flatMap(x -> ric.getLock().lock(LockType.SHARED, "putPolicy")) // - .flatMap(grant -> putPolicy(grant, policy)) // - .onErrorResume(this::handleException); + @Override + public Mono<ResponseEntity<Object>> putPolicy(final Mono<PolicyInfo> policyInfo, final ServerWebExchange exchange) { + + return policyInfo.flatMap(policyInfoValue -> { + String jsonString = gson.toJson(policyInfoValue.getPolicyData()); + return Mono.zip( + Mono.justOrEmpty(rics.get(policyInfoValue.getRicId())) + .switchIfEmpty(Mono.error(new EntityNotFoundException("Near-RT RIC not found"))), + Mono.justOrEmpty(policyTypes.get(policyInfoValue.getPolicytypeId())) + .switchIfEmpty(Mono.error(new EntityNotFoundException("policy type not found"))) + ) + .flatMap(tuple -> { + Ric ric = tuple.getT1(); + PolicyType type = tuple.getT2(); + + Policy policy = Policy.builder() + .id(policyInfoValue.getPolicyId()) + .json(jsonString) + .type(type) + .ric(ric) + .ownerServiceId(policyInfoValue.getServiceId()) + .lastModified(Instant.now()) + .isTransient(policyInfoValue.getTransient()) + .statusNotificationUri(policyInfoValue.getStatusNotificationUri() == null ? "" : policyInfoValue.getStatusNotificationUri()) + .build(); + + return authorization.doAccessControl(exchange.getRequest().getHeaders().toSingleValueMap(), policy, AccessType.WRITE) + .flatMap(x -> ric.getLock().lock(Lock.LockType.SHARED, "putPolicy")) + .flatMap(grant -> putPolicy(grant, policy)); + }) + .onErrorResume(this::handleException); + }); } + + private Mono<ResponseEntity<Object>> putPolicy(Lock.Grant grant, Policy policy) { final boolean isCreate = this.policies.get(policy.getId()) == null; final Ric ric = policy.getRic(); - return assertRicStateIdle(ric) // + return checkRicStateIdle(ric) // .flatMap(notUsed -> checkSupportedType(ric, policy.getType())) // .flatMap(notUsed -> validateModifiedPolicy(policy)) // .flatMap(notUsed -> a1ClientFactory.createA1Client(ric)) // @@ -333,7 +252,7 @@ public class PolicyController { return Mono.just("{}"); } - private Mono<Object> assertRicStateIdle(Ric ric) { + private Mono<Object> checkRicStateIdle(Ric ric) { if (ric.getState() == Ric.RicState.AVAILABLE) { return Mono.just("{}"); } else { @@ -345,77 +264,27 @@ public class PolicyController { } } - static final String GET_POLICIES_QUERY_DETAILS = - "Returns a list of A1 policies matching given search criteria. <br>" // - + "If several query parameters are defined, the policies matching all conditions are returned."; - - @GetMapping(path = Consts.V2_API_ROOT + "/policy-instances", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = "Query for A1 policy instances", description = GET_POLICIES_QUERY_DETAILS) - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "Policies", // - content = @Content(schema = @Schema(implementation = PolicyInfoList.class))), // - @ApiResponse(responseCode = "404", // - description = "Near-RT RIC, policy type or service not found", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) // - }) - public Mono<ResponseEntity<Object>> getPolicyInstances( // - @Parameter(name = Consts.POLICY_TYPE_ID_PARAM, required = false, - description = "Select policies with a given type identity.") // - @RequestParam(name = Consts.POLICY_TYPE_ID_PARAM, required = false) String typeId, // - @Parameter(name = Consts.RIC_ID_PARAM, required = false, - description = "Select policies for a given Near-RT RIC identity.") // - @RequestParam(name = Consts.RIC_ID_PARAM, required = false) String ric, // - @Parameter(name = Consts.SERVICE_ID_PARAM, required = false, - description = "Select policies owned by a given service.") // - @RequestParam(name = Consts.SERVICE_ID_PARAM, required = false) String service, - @Parameter(name = Consts.TYPE_NAME_PARAM, required = false, // - description = "Select policies of a given type name (type identity has the format <typename_version>)") // - @RequestParam(name = Consts.TYPE_NAME_PARAM, required = false) String typeName, - @RequestHeader Map<String, String> headers) throws EntityNotFoundException // - { - if ((typeId != null && this.policyTypes.get(typeId) == null)) { + @Override + public Mono<ResponseEntity<Object>> getPolicyInstances(String policyTypeId, String ricId, String serviceId, String typeName, ServerWebExchange exchange) throws Exception { + if ((policyTypeId != null && this.policyTypes.get(policyTypeId) == null)) { throw new EntityNotFoundException("Policy type identity not found"); } - if ((ric != null && this.rics.get(ric) == null)) { + if ((ricId != null && this.rics.get(ricId) == null)) { throw new EntityNotFoundException("Near-RT RIC not found"); } - Collection<Policy> filtered = policies.filterPolicies(typeId, ric, service, typeName); + Collection<Policy> filtered = policies.filterPolicies(policyTypeId, ricId, serviceId, typeName); return Flux.fromIterable(filtered) // - .flatMap(policy -> authorization.doAccessControl(headers, policy, AccessType.READ)) // - .doOnError(e -> logger.debug("Unauthorized to read policy: {}", e.getMessage())) // - .onErrorResume(e -> Mono.empty()) // - .collectList() // - .map(authPolicies -> policiesToJson(authPolicies)) // - .map(str -> new ResponseEntity<>(str, HttpStatus.OK)); + .flatMap(policy -> authorization.doAccessControl(exchange.getRequest().getHeaders().toSingleValueMap(), policy, AccessType.READ)) + .doOnError(e -> logger.debug("Unauthorized to read policy: {}", e.getMessage())) + .onErrorResume(e -> Mono.empty()) + .collectList() + .map(authPolicies -> new ResponseEntity<>((Object) policiesToJson(authPolicies), HttpStatus.OK)) + .onErrorResume(this::handleException); } - @GetMapping(path = Consts.V2_API_ROOT + "/policies", produces = MediaType.APPLICATION_JSON_VALUE) // - @Operation(summary = "Query policy identities", description = GET_POLICIES_QUERY_DETAILS) // - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "Policy identities", // - content = @Content(schema = @Schema(implementation = PolicyIdList.class))), // - @ApiResponse(responseCode = "404", // - description = "Near-RT RIC or type not found", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) // - }) - public Mono<ResponseEntity<Object>> getPolicyIds( // - @Parameter(name = Consts.POLICY_TYPE_ID_PARAM, required = false, // - description = "Select policies of a given policy type identity.") // - @RequestParam(name = Consts.POLICY_TYPE_ID_PARAM, required = false) String policyTypeId, // - @Parameter(name = Consts.RIC_ID_PARAM, required = false, // - description = "Select policies of a given Near-RT RIC identity.") // - @RequestParam(name = Consts.RIC_ID_PARAM, required = false) String ricId, // - @Parameter(name = Consts.SERVICE_ID_PARAM, required = false, // - description = "Select policies owned by a given service.") // - @RequestParam(name = Consts.SERVICE_ID_PARAM, required = false) String serviceId, - @Parameter(name = Consts.TYPE_NAME_PARAM, required = false, // - description = "Select policies of types with the given type name (type identity has the format <typename_version>)") // - @RequestParam(name = Consts.TYPE_NAME_PARAM, required = false) String typeName, - @RequestHeader Map<String, String> headers) throws EntityNotFoundException // - { + @Override + public Mono<ResponseEntity<Object>> getPolicyIds(String policyTypeId, String ricId, String serviceId, String typeName, ServerWebExchange exchange) throws Exception { if ((policyTypeId != null && this.policyTypes.get(policyTypeId) == null)) { throw new EntityNotFoundException("Policy type not found"); } @@ -424,42 +293,37 @@ public class PolicyController { } Collection<Policy> filtered = policies.filterPolicies(policyTypeId, ricId, serviceId, typeName); - return Flux.fromIterable(filtered) // - .flatMap(policy -> authorization.doAccessControl(headers, policy, AccessType.READ)) // - .doOnError(e -> logger.debug("Unauthorized to read policy: {}", e.getMessage())) // - .onErrorResume(e -> Mono.empty()) // - .collectList() // - .map(authPolicies -> toPolicyIdsJson(authPolicies)) // - .map(policyIdsJson -> new ResponseEntity<>(policyIdsJson, HttpStatus.OK)); + return Flux.fromIterable(filtered) + .flatMap(policy -> authorization.doAccessControl(exchange.getRequest().getHeaders().toSingleValueMap(), policy, AccessType.READ)) + .doOnError(e -> logger.debug("Unauthorized to read policy: {}", e.getMessage())) + .onErrorResume(e -> Mono.empty()) + .collectList() + .map(authPolicies -> new ResponseEntity<>((Object)toPolicyIdsJson(authPolicies), HttpStatus.OK)) + .onErrorResume(this::handleException); } - @GetMapping(path = Consts.V2_API_ROOT + "/policies/{policy_id}/status", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = "Returns a policy status") // - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "Policy status", // - content = @Content(schema = @Schema(implementation = PolicyStatusInfo.class))), // - @ApiResponse(responseCode = "404", // - description = "Policy is not found", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) // - }) - public Mono<ResponseEntity<Object>> getPolicyStatus( // - @PathVariable(Consts.POLICY_ID_PARAM) String policyId, @RequestHeader Map<String, String> headers) - throws EntityNotFoundException { + @Override + public Mono<ResponseEntity<Object>> getPolicyStatus(String policyId, ServerWebExchange exchange) throws Exception { Policy policy = policies.getPolicy(policyId); - return authorization.doAccessControl(headers, policy, AccessType.READ) // + return authorization.doAccessControl(exchange.getRequest().getHeaders().toSingleValueMap(), policy, AccessType.READ) // .flatMap(notUsed -> a1ClientFactory.createA1Client(policy.getRic())) // .flatMap(client -> client.getPolicyStatus(policy).onErrorResume(e -> Mono.just("{}"))) // - .flatMap(status -> createPolicyStatus(policy, status)) // + .flatMap(status -> createPolicyStatus(policy, status)) .onErrorResume(this::handleException); - } private Mono<ResponseEntity<Object>> createPolicyStatus(Policy policy, String statusFromNearRic) { - PolicyStatusInfo info = new PolicyStatusInfo(policy.getLastModified(), fromJson(statusFromNearRic)); - String str = gson.toJson(info); - return Mono.just(new ResponseEntity<>(str, HttpStatus.OK)); + + try { + PolicyStatusInfo policyStatusInfo = new PolicyStatusInfo(); + policyStatusInfo.setLastModified(policy.getLastModified().toString()); + policyStatusInfo.setStatus(fromJson(statusFromNearRic)); + String policyStatusInfoAsString = objectMapper.writeValueAsString(policyStatusInfo); + return Mono.just(new ResponseEntity<>(policyStatusInfoAsString, HttpStatus.OK)); + } catch (JsonProcessingException ex) { + throw new RuntimeException(ex); + } } private void keepServiceAlive(String name) { @@ -469,52 +333,70 @@ public class PolicyController { } } - private PolicyInfo toPolicyInfo(Policy p) { - PolicyInfo policyInfo = new PolicyInfo(); - policyInfo.policyId = p.getId(); - policyInfo.policyData = fromJson(p.getJson()); - policyInfo.ricId = p.getRic().id(); - policyInfo.policyTypeId = p.getType().getId(); - policyInfo.serviceId = p.getOwnerServiceId(); - policyInfo.isTransient = p.isTransient(); - if (!p.getStatusNotificationUri().isEmpty()) { - policyInfo.statusNotificationUri = p.getStatusNotificationUri(); - } - if (!policyInfo.validate()) { - logger.error("BUG, all mandatory fields must be set"); + private PolicyInfo toPolicyInfo(Policy policy) { + PolicyInfo policyInfo = new PolicyInfo() + .policyId(policy.getId()) + .policyData(gson.fromJson(policy.getJson(), Map.class)) + .ricId(policy.getRic().id()) + .policytypeId(policy.getType().getId()) + .serviceId(policy.getOwnerServiceId()) + ._transient(policy.isTransient()); + if (!policy.getStatusNotificationUri().isEmpty()) { + policyInfo.setStatusNotificationUri(policy.getStatusNotificationUri()); } - return policyInfo; } - private String policiesToJson(Collection<Policy> policies) { - List<PolicyInfo> v = new ArrayList<>(policies.size()); - for (Policy p : policies) { - v.add(toPolicyInfo(p)); + private String toPolicyInfoString(Policy policy) { + + try { + return objectMapper.writeValueAsString(toPolicyInfo(policy)); + } catch (JsonProcessingException ex) { + throw new RuntimeException(ex); } - PolicyInfoList list = new PolicyInfoList(v); - return gson.toJson(list); + } + + private String policiesToJson(Collection<Policy> policies) { + + try { + List<PolicyInfo> policiesList = new ArrayList<>(policies.size()); + PolicyInfoList policyInfoList = new PolicyInfoList(); + for (Policy policy : policies) { + policiesList.add(toPolicyInfo(policy)); + } + policyInfoList.setPolicies(policiesList); + return objectMapper.writeValueAsString(policyInfoList); + } catch(JsonProcessingException ex) { + throw new RuntimeException(ex); + } } private Object fromJson(String jsonStr) { return gson.fromJson(jsonStr, Object.class); } - private String toPolicyTypeIdsJson(Collection<PolicyType> types) { - List<String> v = new ArrayList<>(types.size()); - for (PolicyType t : types) { - v.add(t.getId()); + private String toPolicyTypeIdsJson(Collection<PolicyType> policyTypes) throws JsonProcessingException { + + PolicyTypeIdList idList = new PolicyTypeIdList(); + for (PolicyType policyType : policyTypes) { + idList.addPolicytypeIdsItem(policyType.getId()); } - PolicyTypeIdList ids = new PolicyTypeIdList(v); - return gson.toJson(ids); + + return objectMapper.writeValueAsString(idList); } private String toPolicyIdsJson(Collection<Policy> policies) { - List<String> v = new ArrayList<>(policies.size()); - for (Policy p : policies) { - v.add(p.getId()); + + try { + List<String> policyIds = new ArrayList<>(policies.size()); + PolicyIdList idList = new PolicyIdList(); + for (Policy policy : policies) { + policyIds.add(policy.getId()); + } + idList.setPolicyIds(policyIds); + return objectMapper.writeValueAsString(idList); + } catch (JsonProcessingException ex) { + throw new RuntimeException(ex); } - return gson.toJson(new PolicyIdList(v)); } - } diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyIdList.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyIdList.java deleted file mode 100644 index 35c1c947..00000000 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyIdList.java +++ /dev/null @@ -1,41 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * ONAP : ccsdk oran - * ====================================================================== - * Copyright (C) 2020-2023 Nordix Foundation. All rights reserved. - * ====================================================================== - * 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.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.annotations.SerializedName; - -import io.swagger.v3.oas.annotations.media.Schema; - -import java.util.Collection; - -@Schema(name = "policy_id_list_v2", description = "A list of policy identities") -public class PolicyIdList { - - @Schema(description = "Policy identities") - @SerializedName("policy_ids") - @JsonProperty("policy_ids") - public final Collection<String> policyIds; - - public PolicyIdList(Collection<String> ids) { - this.policyIds = ids; - } -} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyInfo.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyInfo.java deleted file mode 100644 index 537f98c2..00000000 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyInfo.java +++ /dev/null @@ -1,81 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * ONAP : ccsdk oran - * ====================================================================== - * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved. - * ====================================================================== - * 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.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.annotations.SerializedName; - -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.media.Schema.RequiredMode; - -@Schema(name = "policy_info_v2", description = "Information for one A1-P Policy") -public class PolicyInfo { - - @Schema(name = "policy_id", description = "identity of the policy", requiredMode = RequiredMode.REQUIRED) - @JsonProperty(value = "policy_id", required = true) - @SerializedName("policy_id") - public String policyId; - - @Schema(name = "policytype_id", description = "identity of the policy type", requiredMode = RequiredMode.REQUIRED) - @JsonProperty(value = "policytype_id", required = true) - @SerializedName("policytype_id") - public String policyTypeId; - - @Schema(name = "ric_id", description = "identity of the target Near-RT RIC", requiredMode = RequiredMode.REQUIRED) - @JsonProperty(value = "ric_id", required = true) - @SerializedName("ric_id") - public String ricId; - - @Schema(name = "policy_data", description = "the configuration of the policy", requiredMode = RequiredMode.REQUIRED) - @JsonProperty(value = "policy_data", required = true) - @SerializedName("policy_data") - public Object policyData; - - private static final String SERVICE_ID_DESCRIPTION = "the identity of the service owning the policy." - + " This can be used to group the policies (it is possible to get all policies associated to a service)." - + " Note that the service does not need to be registered."; - - @Schema(name = "service_id", description = SERVICE_ID_DESCRIPTION, requiredMode = RequiredMode.NOT_REQUIRED, - defaultValue = "") - @JsonProperty(value = "service_id", required = false) - @SerializedName("service_id") - public String serviceId = ""; - - @Schema(name = "transient", - description = "if true, the policy is deleted at RIC restart. If false, its value is maintained by this service until explicitly deleted. Default false.", - requiredMode = RequiredMode.NOT_REQUIRED, defaultValue = "false", example = "false") - @JsonProperty(value = "transient", required = false, defaultValue = "false") - @SerializedName("transient") - public boolean isTransient = false; - - @Schema(name = "status_notification_uri", description = "Callback URI for policy status updates", - requiredMode = RequiredMode.NOT_REQUIRED, defaultValue = "") - @JsonProperty(value = "status_notification_uri", required = false) - @SerializedName("status_notification_uri") - public String statusNotificationUri = ""; - - PolicyInfo() {} - - public boolean validate() { - return policyId != null && policyTypeId != null && ricId != null && policyData != null && serviceId != null; - } - -} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyInfoList.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyInfoList.java deleted file mode 100644 index ae1aed95..00000000 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyInfoList.java +++ /dev/null @@ -1,42 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * ONAP : ccsdk oran - * ====================================================================== - * Copyright (C) 2020-2023 Nordix Foundation. All rights reserved. - * ====================================================================== - * 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.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.annotations.SerializedName; - -import io.swagger.v3.oas.annotations.media.Schema; - -import java.util.Collection; - -@Schema(name = "policy_info_list_v2", description = "List of policy information") -public class PolicyInfoList { - - @Schema(description = "List of policy information") - @SerializedName("policies") - @JsonProperty("policies") - public final Collection<PolicyInfo> policies; - - public PolicyInfoList(Collection<PolicyInfo> policies) { - this.policies = policies; - } - -} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyStatusInfo.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyStatusInfo.java deleted file mode 100644 index c19c4a0d..00000000 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyStatusInfo.java +++ /dev/null @@ -1,54 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * ONAP : ccsdk oran - * ====================================================================== - * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved. - * ====================================================================== - * 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.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.annotations.SerializedName; - -import io.swagger.v3.oas.annotations.media.Schema; - -import java.time.Instant; - -@Schema(name = "policy_status_info_v2", description = "Status for one A1-P Policy") -public class PolicyStatusInfo { - - @Schema(description = "timestamp, last modification time") - @SerializedName("last_modified") - @JsonProperty("last_modified") - public String lastModified; - - @Schema(description = "the Policy status") - @SerializedName("status") - @JsonProperty("status") - public Object status; - - public PolicyStatusInfo() {} - - public PolicyStatusInfo(Instant lastModified, Object statusFromNearRTRic) { - this.lastModified = lastModified.toString(); - this.status = statusFromNearRTRic; - } - - public boolean validate() { - return lastModified != null; - } - -} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyTypeIdList.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyTypeIdList.java deleted file mode 100644 index 0da2e654..00000000 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyTypeIdList.java +++ /dev/null @@ -1,41 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * ONAP : ccsdk oran - * ====================================================================== - * Copyright (C) 2020-2023 Nordix Foundation. All rights reserved. - * ====================================================================== - * 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.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.annotations.SerializedName; - -import io.swagger.v3.oas.annotations.media.Schema; - -import java.util.Collection; - -@Schema(name = "policytype_id_list_v2", description = "Information about policy types") -public class PolicyTypeIdList { - - @Schema(description = "Policy type identities") - @SerializedName("policytype_ids") - @JsonProperty("policytype_ids") - public final Collection<String> policyTypesIds; - - public PolicyTypeIdList(Collection<String> ids) { - this.policyTypesIds = ids; - } -} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyTypeInfo.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyTypeInfo.java deleted file mode 100644 index 1f0e4476..00000000 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyTypeInfo.java +++ /dev/null @@ -1,43 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * ONAP : ccsdk oran - * ====================================================================== - * Copyright (C) 2020-2023 Nordix Foundation. All rights reserved. - * ====================================================================== - * 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.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.annotations.SerializedName; - -import io.swagger.v3.oas.annotations.media.Schema; - -@Schema(name = "policytype_v2", description = "Policy type") -public class PolicyTypeInfo { - - @Schema(description = "Policy type json schema. The schema is a json object following http://json-schema.org/draft-07/schema") - @SerializedName("policy_schema") - @JsonProperty("policy_schema") - public final Object schema; - - public PolicyTypeInfo(String schemaAsString) { - JsonObject jsonObj = JsonParser.parseString(schemaAsString).getAsJsonObject(); - this.schema = jsonObj; - } - -} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/RicInfo.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/RicInfo.java deleted file mode 100644 index 81fc56cb..00000000 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/RicInfo.java +++ /dev/null @@ -1,72 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * ONAP : ccsdk oran - * ====================================================================== - * Copyright (C) 2020-2023 Nordix Foundation. All rights reserved. - * ====================================================================== - * 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.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.annotations.SerializedName; - -import io.swagger.v3.oas.annotations.media.Schema; - -import java.util.Collection; - -@Schema(name = "ric_info_v2", description = "Information for a Near-RT RIC") -public class RicInfo { - - @Schema(name = "ric_state_v2", description = "Represents the states for a Near-RT RIC") - public enum RicState { - UNAVAILABLE, AVAILABLE, SYNCHRONIZING, CONSISTENCY_CHECK - } - - private static final String STATE_DESCRIPTION = - """ - State for the Near-RT RIC, values: - UNAVAILABLE: The Near-RT RIC is not available, information may be inconsistent - AVAILABLE: The normal state. Policies can be configured. - SYNCHRONIZING: The Policy Management Service is synchronizing the view of the Near-RT RIC. Policies cannot be configured. - CONSISTENCY_CHECK: A consistency check between the Policy Management Service and the Near-RT RIC. Policies cannot be configured."""; - - @Schema(description = "identity of the Near-RT RIC") - @SerializedName("ric_id") - @JsonProperty("ric_id") - public final String ricId; - - @Schema(description = "O1 identities for managed entities") - @SerializedName("managed_element_ids") - @JsonProperty("managed_element_ids") - public final Collection<String> managedElementIds; - - @Schema(description = "supported policy types") - @SerializedName("policytype_ids") - @JsonProperty("policytype_ids") - public final Collection<String> policyTypeIds; - - @Schema(description = STATE_DESCRIPTION, name = "state") - @SerializedName("state") - @JsonProperty("state") - public final RicState state; - - RicInfo(String ricId, Collection<String> managedElementIds, Collection<String> policyTypes, RicState state) { - this.ricId = ricId; - this.managedElementIds = managedElementIds; - this.policyTypeIds = policyTypes; - this.state = state; - } -} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/RicInfoList.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/RicInfoList.java deleted file mode 100644 index 715c15b4..00000000 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/RicInfoList.java +++ /dev/null @@ -1,42 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * ONAP : ccsdk oran - * ====================================================================== - * Copyright (C) 2020-2023 Nordix Foundation. All rights reserved. - * ====================================================================== - * 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.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.annotations.SerializedName; - -import io.swagger.v3.oas.annotations.media.Schema; - -import java.util.Collection; - -@Schema(name = "ric_info_list_v2", description = "List of Near-RT RIC information") -public class RicInfoList { - - @Schema(description = "List of Near-RT RIC information") - @SerializedName("rics") - @JsonProperty("rics") - public final Collection<RicInfo> rics; - - public RicInfoList(Collection<RicInfo> rics) { - this.rics = rics; - } - -} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/RicRepositoryController.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/RicRepositoryController.java index 9e2644ad..63b0560c 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/RicRepositoryController.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/RicRepositoryController.java @@ -20,39 +20,34 @@ package org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.GsonBuilder; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -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 java.util.ArrayList; -import java.util.List; - +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.api.v2.NearRtRicRepositoryApi; import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.EntityNotFoundException; import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.InvalidRequestException; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.RicInfo; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.RicInfoList; import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyTypes; import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric; import org.onap.ccsdk.oran.a1policymanagementservice.repository.Rics; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.ArrayList; +import java.util.List; @RestController("RicRepositoryControllerV2") @Tag( // name = RicRepositoryController.API_NAME, // description = RicRepositoryController.API_DESCRIPTION // ) -public class RicRepositoryController { +public class RicRepositoryController implements NearRtRicRepositoryApi { public static final String API_NAME = "NearRT-RIC Repository"; public static final String API_DESCRIPTION = ""; @@ -63,6 +58,9 @@ public class RicRepositoryController { @Autowired PolicyTypes types; + @Autowired + ObjectMapper objectMapper; + private static Gson gson = new GsonBuilder() // .create(); // @@ -70,38 +68,18 @@ public class RicRepositoryController { private static final String GET_RIC_DETAILS = "Either a Near-RT RIC identity or a Managed Element identity can be specified.<br>" // + "The intention with Managed Element identity is the ID used in O1 for accessing the traffical element (such as the ID of CU)."; - - /** - * Example: http://localhost:8081/v2/rics/ric?managed_element_id=kista_1 - * - * @throws EntityNotFoundException - */ - @GetMapping(path = Consts.V2_API_ROOT + "/rics/ric", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = GET_RIC_BRIEF, description = GET_RIC_DETAILS) - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "Near-RT RIC is found", // - content = @Content(schema = @Schema(implementation = RicInfo.class))), // - @ApiResponse(responseCode = "404", // - description = "Near-RT RIC is not found", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) // - }) - public ResponseEntity<Object> getRic( // - @Parameter(name = Consts.MANAGED_ELEMENT_ID_PARAM, required = false, - description = "The identity of a Managed Element. If given, the Near-RT RIC managing the ME is returned.") // - @RequestParam(name = Consts.MANAGED_ELEMENT_ID_PARAM, required = false) String managedElementId, - @Parameter(name = Consts.RIC_ID_PARAM, required = false, - description = "The identity of a Near-RT RIC to get information for.") // - @RequestParam(name = Consts.RIC_ID_PARAM, required = false) String ricId) - throws EntityNotFoundException, InvalidRequestException { + @Override + public Mono<ResponseEntity<Object>> getRic( + final String managedElementId, final String ricId, final ServerWebExchange exchange) + throws Exception { if (managedElementId != null && ricId != null) { throw new InvalidRequestException("Give one query parameter"); } else if (managedElementId != null) { Ric ric = this.rics.lookupRicForManagedElement(managedElementId); - return new ResponseEntity<>(gson.toJson(toRicInfo(ric)), HttpStatus.OK); + return Mono.just(new ResponseEntity<>(objectMapper.writeValueAsString(toRicInfo(ric)), HttpStatus.OK)); } else if (ricId != null) { RicInfo info = toRicInfo(this.rics.getRic(ricId)); - return new ResponseEntity<>(gson.toJson(info), HttpStatus.OK); + return Mono.just(new ResponseEntity<>(objectMapper.writeValueAsString(info), HttpStatus.OK)); } else { throw new InvalidRequestException("Give one query parameter"); } @@ -110,25 +88,9 @@ public class RicRepositoryController { static final String QUERY_RIC_INFO_DETAILS = "The call returns all Near-RT RICs that supports a given policy type identity"; - /** - * @return a Json array of all RIC data Example: http://localhost:8081/v2/ric - * @throws EntityNotFoundException - */ - @GetMapping(path = Consts.V2_API_ROOT + "/rics", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = "Query Near-RT RIC information", description = QUERY_RIC_INFO_DETAILS) - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "OK", // - content = @Content(schema = @Schema(implementation = RicInfoList.class))), // - @ApiResponse(responseCode = "404", // - description = "Policy type is not found", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) // - }) - public ResponseEntity<Object> getRics( // - @Parameter(name = Consts.POLICY_TYPE_ID_PARAM, required = false, - description = "The identity of a policy type. If given, all Near-RT RICs supporting the policy type are returned") // - @RequestParam(name = Consts.POLICY_TYPE_ID_PARAM, required = false) String supportingPolicyType) - throws EntityNotFoundException { + @Override + public Mono<ResponseEntity<Object>> getRics(final String supportingPolicyType, final ServerWebExchange exchange) + throws Exception { if ((supportingPolicyType != null) && (this.types.get(supportingPolicyType) == null)) { throw new EntityNotFoundException("Policy type not found"); } @@ -140,26 +102,28 @@ public class RicRepositoryController { } } - return new ResponseEntity<>(gson.toJson(new RicInfoList(result)), HttpStatus.OK); + return Mono.just(new ResponseEntity<>(objectMapper.writeValueAsString(new RicInfoList().rics(result)), HttpStatus.OK)); } - private RicInfo.RicState toRicState(Ric.RicState state) { + private RicInfo.StateEnum toRicState(Ric.RicState state) { switch (state) { case AVAILABLE: - return RicInfo.RicState.AVAILABLE; + return RicInfo.StateEnum.AVAILABLE; case CONSISTENCY_CHECK: - return RicInfo.RicState.CONSISTENCY_CHECK; + return RicInfo.StateEnum.CONSISTENCY_CHECK; case SYNCHRONIZING: - return RicInfo.RicState.SYNCHRONIZING; + return RicInfo.StateEnum.SYNCHRONIZING; case UNAVAILABLE: - return RicInfo.RicState.UNAVAILABLE; + return RicInfo.StateEnum.UNAVAILABLE; default: - return RicInfo.RicState.UNAVAILABLE; + return RicInfo.StateEnum.UNAVAILABLE; } } private RicInfo toRicInfo(Ric ric) { - return new RicInfo(ric.id(), ric.getManagedElementIds(), ric.getSupportedPolicyTypeNames(), - toRicState(ric.getState())); + return new RicInfo().ricId(ric.id()) + .managedElementIds((List<String>) ric.getManagedElementIds()) + .policytypeIds((List<String>) ric.getSupportedPolicyTypeNames()) + .state(toRicState(ric.getState())); } } diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceController.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceController.java index ed97820f..07b9440c 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceController.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceController.java @@ -2,7 +2,7 @@ * ========================LICENSE_START================================= * ONAP : ccsdk oran * ====================================================================== - * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved. + * Copyright (C) 2019-2023 Nordix Foundation. All rights reserved. * ====================================================================== * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,39 +20,33 @@ package org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.GsonBuilder; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -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 java.net.MalformedURLException; -import java.net.URL; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collection; - -import org.onap.ccsdk.oran.a1policymanagementservice.controllers.VoidResponse; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.api.v2.ServiceRegistryAndSupervisionApi; import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.ServiceRegistrationInfo; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.ServiceStatus; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.ServiceStatusList; import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies; import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy; import org.onap.ccsdk.oran.a1policymanagementservice.repository.Service; import org.onap.ccsdk.oran.a1policymanagementservice.repository.Services; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.net.MalformedURLException; +import java.net.URL; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; @RestController("ServiceControllerV2") @Tag( // @@ -60,7 +54,7 @@ import org.springframework.web.bind.annotation.RestController; description = ServiceController.API_DESCRIPTION // ) -public class ServiceController { +public class ServiceController implements ServiceRegistryAndSupervisionApi { public static final String API_NAME = "Service Registry and Supervision"; public static final String API_DESCRIPTION = ""; @@ -68,6 +62,9 @@ public class ServiceController { private final Services services; private final Policies policies; + @Autowired + private ObjectMapper objectMapper; + private static Gson gson = new GsonBuilder().create(); ServiceController(Services services, Policies policies) { @@ -78,49 +75,40 @@ public class ServiceController { private static final String GET_SERVICE_DETAILS = "Either information about a registered service with given identity or all registered services are returned."; - @GetMapping(path = Consts.V2_API_ROOT + "/services", produces = MediaType.APPLICATION_JSON_VALUE) // - @Operation(summary = "Returns service information", description = GET_SERVICE_DETAILS) // - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "OK", // - content = @Content(schema = @Schema(implementation = ServiceStatusList.class))), // - @ApiResponse(responseCode = "404", // - description = "Service is not found", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class)))// - }) - public ResponseEntity<Object> getServices(// - @Parameter(name = Consts.SERVICE_ID_PARAM, required = false, description = "The identity of the service") // - @RequestParam(name = Consts.SERVICE_ID_PARAM, required = false) String name) { + @Override + public Mono<ResponseEntity<Object>> getServices(final String name, final ServerWebExchange exchange) throws Exception { if (name != null && this.services.get(name) == null) { - return ErrorResponse.create("Service not found", HttpStatus.NOT_FOUND); + return ErrorResponse.createMono("Service not found", HttpStatus.NOT_FOUND); } - Collection<ServiceStatus> servicesStatus = new ArrayList<>(); + List<ServiceStatus> servicesStatus = new ArrayList<>(); for (Service s : this.services.getAll()) { if (name == null || name.equals(s.getName())) { servicesStatus.add(toServiceStatus(s)); } } - - String res = gson.toJson(new ServiceStatusList(servicesStatus)); - return new ResponseEntity<>(res, HttpStatus.OK); + String res = objectMapper.writeValueAsString(new ServiceStatusList().serviceList(servicesStatus)); + return Mono.just(new ResponseEntity<>(res, HttpStatus.OK)); } private ServiceStatus toServiceStatus(Service s) { - return new ServiceStatus(s.getName(), s.getKeepAliveInterval().toSeconds(), s.timeSinceLastPing().toSeconds(), - s.getCallbackUrl()); + return new ServiceStatus() + .serviceId(s.getName()) + .keepAliveIntervalSeconds(s.getKeepAliveInterval().toSeconds()) + .timeSinceLastActivitySeconds(s.timeSinceLastPing().toSeconds()) + .callbackUrl(s.getCallbackUrl()); } private void validateRegistrationInfo(ServiceRegistrationInfo registrationInfo) throws ServiceException, MalformedURLException { - if (registrationInfo.serviceId.isEmpty()) { + if (registrationInfo.getServiceId().isEmpty()) { throw new ServiceException("Missing mandatory parameter 'service-id'"); } - if (registrationInfo.keepAliveIntervalSeconds < 0) { - throw new ServiceException("Keepalive interval shoul be greater or equal to 0"); + if (registrationInfo.getKeepAliveIntervalSeconds() < 0) { + throw new ServiceException("Keep alive interval should be greater or equal to 0"); } - if (!registrationInfo.callbackUrl.isEmpty()) { - new URL(registrationInfo.callbackUrl); + if (!registrationInfo.getCallbackUrl().isEmpty()) { + new URL(registrationInfo.getCallbackUrl()); } } @@ -131,67 +119,42 @@ public class ServiceController { + "</ul>" // + "Policies can be created even if the service is not registerred. This is a feature which it is optional to use."; - @PutMapping(Consts.V2_API_ROOT + "/services") - @Operation(summary = "Register a service", description = REGISTER_SERVICE_DETAILS) - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", description = "Service updated"), - @ApiResponse(responseCode = "201", description = "Service created"), // - @ApiResponse(responseCode = "400", // - description = "The ServiceRegistrationInfo is not accepted", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class)))}) - - public ResponseEntity<Object> putService(// - @RequestBody ServiceRegistrationInfo registrationInfo) { - try { - validateRegistrationInfo(registrationInfo); - final boolean isCreate = this.services.get(registrationInfo.serviceId) == null; - this.services.put(toService(registrationInfo)); - return new ResponseEntity<>(isCreate ? HttpStatus.CREATED : HttpStatus.OK); - } catch (Exception e) { - return ErrorResponse.create(e, HttpStatus.BAD_REQUEST); - } + @Override + public Mono<ResponseEntity<Object>> putService( + final Mono<ServiceRegistrationInfo> registrationInfo, final ServerWebExchange exchange) { + return registrationInfo.flatMap(info -> { + try { + validateRegistrationInfo(info); + } catch(Exception e) { + return ErrorResponse.createMono(e, HttpStatus.BAD_REQUEST); + } + final boolean isCreate = this.services.get(info.getServiceId()) == null; + this.services.put(toService(info)); + return Mono.just(new ResponseEntity<>(isCreate ? HttpStatus.CREATED : HttpStatus.OK)); + }).onErrorResume(Exception.class, e -> ErrorResponse.createMono(e, HttpStatus.BAD_REQUEST)); } - @DeleteMapping(Consts.V2_API_ROOT + "/services/{service_id:.+}") - @Operation(summary = "Unregister a service") - @ApiResponses(value = { // - @ApiResponse(responseCode = "204", description = "Service unregistered"), - @ApiResponse(responseCode = "200", description = "Not used", // - content = @Content(schema = @Schema(implementation = VoidResponse.class))), - @ApiResponse(responseCode = "404", description = "Service not found", // - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) - - }) - public ResponseEntity<Object> deleteService(// - @PathVariable("service_id") String serviceId) { + @Override + public Mono<ResponseEntity<Object>> deleteService(final String serviceId, final ServerWebExchange exchange) { try { Service service = removeService(serviceId); // Remove the policies from the repo and let the consistency monitoring // do the rest. removePolicies(service); - return new ResponseEntity<>(HttpStatus.NO_CONTENT); + return Mono.just(new ResponseEntity<>(HttpStatus.NO_CONTENT)); } catch (ServiceException e) { - return ErrorResponse.create(e, HttpStatus.NOT_FOUND); + return ErrorResponse.createMono(e, HttpStatus.NOT_FOUND); } } - @Operation(summary = "Heartbeat indicates that the service is running", - description = "A registered service should invoke this operation regularly to indicate that it is still alive. If a registered service fails to invoke this operation before the end of a timeout period the service will be deregistered and all its A1 policies wil be removed. (This timeout can be set or disabled when each service is initially registered)") - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", description = "Service supervision timer refreshed, OK"), // - @ApiResponse(responseCode = "404", description = "The service is not found, needs re-registration", - content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) - - }) - + @Override @PutMapping(Consts.V2_API_ROOT + "/services/{service_id}/keepalive") - public ResponseEntity<Object> keepAliveService(// - @PathVariable(Consts.SERVICE_ID_PARAM) String serviceId) { + public Mono<ResponseEntity<Object>> keepAliveService(final String serviceId, final ServerWebExchange exchange) { try { services.getService(serviceId).keepAlive(); - return new ResponseEntity<>(HttpStatus.OK); + return Mono.just(new ResponseEntity<>(HttpStatus.OK)); } catch (ServiceException e) { - return ErrorResponse.create(e, HttpStatus.NOT_FOUND); + return ErrorResponse.createMono(e, HttpStatus.NOT_FOUND); } } @@ -209,7 +172,7 @@ public class ServiceController { } private Service toService(ServiceRegistrationInfo s) { - return new Service(s.serviceId, Duration.ofSeconds(s.keepAliveIntervalSeconds), s.callbackUrl); + return new Service(s.getServiceId(), Duration.ofSeconds(s.getKeepAliveIntervalSeconds()), s.getCallbackUrl()); } } diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceRegistrationInfo.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceRegistrationInfo.java deleted file mode 100644 index 1bbac871..00000000 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceRegistrationInfo.java +++ /dev/null @@ -1,61 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * ONAP : ccsdk oran - * ====================================================================== - * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved. - * ====================================================================== - * 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.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.annotations.SerializedName; - -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.media.Schema.RequiredMode; - - -@Schema(name = "service_registration_info_v2", description = "Information for one service") -public class ServiceRegistrationInfo { - - @Schema(description = "identity of the service", requiredMode = RequiredMode.REQUIRED) - @SerializedName("service_id") - @JsonProperty("service_id") - public String serviceId = ""; - - @Schema(description = "keep alive interval for the service. This is used to enable optional heartbeat supervision of the service. " - + "If set (> 0) the registered service should regularly invoke a 'keepalive' REST call. " - + "When a service fails to invoke this 'keepalive' call within the configured time, the service is considered unavailable. " - + "An unavailable service will be automatically deregistered and its policies will be deleted. " - + "Value 0 means timeout supervision is disabled.") - @SerializedName("keep_alive_interval_seconds") - @JsonProperty("keep_alive_interval_seconds") - public long keepAliveIntervalSeconds = 0; - - @Schema(description = "callback for notifying of Near-RT RIC state changes", - requiredMode = RequiredMode.NOT_REQUIRED, defaultValue = "") - @SerializedName("callback_url") - @JsonProperty("callback_url") - public String callbackUrl = ""; - - public ServiceRegistrationInfo() {} - - public ServiceRegistrationInfo(String id, long keepAliveIntervalSeconds, String callbackUrl) { - this.serviceId = id; - this.keepAliveIntervalSeconds = keepAliveIntervalSeconds; - this.callbackUrl = callbackUrl; - } - -} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceStatus.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceStatus.java deleted file mode 100644 index f25aff0e..00000000 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceStatus.java +++ /dev/null @@ -1,58 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * ONAP : ccsdk oran - * ====================================================================== - * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved. - * ====================================================================== - * 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.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.annotations.SerializedName; - -import io.swagger.v3.oas.annotations.media.Schema; - -@Schema(name = "service_status_v2") -public class ServiceStatus { - - @Schema(description = "identity of the service") - @SerializedName("service_id") - @JsonProperty("service_id") - public final String serviceId; - - @Schema(description = "policy keep alive timeout") - @SerializedName("keep_alive_interval_seconds") - @JsonProperty("keep_alive_interval_seconds") - public final long keepAliveIntervalSeconds; - - @Schema(description = "time since last invocation by the service") - @SerializedName("time_since_last_activity_seconds") - @JsonProperty("time_since_last_activity_seconds") - public final long timeSinceLastActivitySeconds; - - @Schema(description = "callback for notifying of RIC synchronization") - @SerializedName("callback_url") - @JsonProperty("callback_url") - public String callbackUrl; - - ServiceStatus(String id, long keepAliveIntervalSeconds, long timeSincePingSeconds, String callbackUrl) { - this.serviceId = id; - this.keepAliveIntervalSeconds = keepAliveIntervalSeconds; - this.timeSinceLastActivitySeconds = timeSincePingSeconds; - this.callbackUrl = callbackUrl; - } - -} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceStatusList.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceStatusList.java deleted file mode 100644 index 55593894..00000000 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ServiceStatusList.java +++ /dev/null @@ -1,42 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * ONAP : ccsdk oran - * ====================================================================== - * Copyright (C) 2020-2023 Nordix Foundation. All rights reserved. - * ====================================================================== - * 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.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.annotations.SerializedName; - -import io.swagger.v3.oas.annotations.media.Schema; - -import java.util.Collection; - -@Schema(name = "service_list_v2", description = "List of service information") -public class ServiceStatusList { - - @Schema(description = "List of service information") - @SerializedName("service_list") - @JsonProperty("service_list") - public final Collection<ServiceStatus> statusList; - - public ServiceStatusList(Collection<ServiceStatus> statuses) { - this.statusList = statuses; - } - -} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/StatusController.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/StatusController.java index 57731793..22200da6 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/StatusController.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/StatusController.java @@ -2,7 +2,7 @@ * ========================LICENSE_START================================= * ONAP : ccsdk oran * ====================================================================== - * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved. + * Copyright (C) 2019-2023 Nordix Foundation. All rights reserved. * ====================================================================== * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,27 +20,20 @@ package org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -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.onap.ccsdk.oran.a1policymanagementservice.controllers.api.v2.HealthCheckApi; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; - +import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @RestController("StatusControllerV2") -@Tag( // - name = StatusController.API_NAME, // - description = StatusController.API_DESCRIPTION // +@Tag( name = StatusController.API_NAME, + description = StatusController.API_DESCRIPTION ) -public class StatusController { +public class StatusController implements HealthCheckApi{ public static final String API_NAME = "Health Check"; public static final String API_DESCRIPTION = ""; @@ -55,28 +48,15 @@ public class StatusController { } } - @GetMapping(path = Consts.V2_API_ROOT + "/status", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = "Returns status and statistics of this service") - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", // - description = "Service is living", // - content = @Content(schema = @Schema(implementation = StatusInfo.class))), // - }) - - public Mono<ResponseEntity<Object>> getStatus() { + @Override + public Mono<ResponseEntity<Object>> getStatus(final ServerWebExchange exchange) { StatusInfo info = new StatusInfo("success"); return Mono.just(new ResponseEntity<>(info, HttpStatus.OK)); } - @GetMapping("/status") - @Operation(summary = "Returns status and statistics of this service") - @ApiResponses(value = { // - @ApiResponse(responseCode = "200", description = "Service is living", - content = @Content(schema = @Schema(implementation = String.class))) // - }) - - public Mono<ResponseEntity<String>> getStatusV1() { + @Override + public Mono<ResponseEntity<String>> getStatusV1(final ServerWebExchange exchange) { return Mono.just(new ResponseEntity<>("success", HttpStatus.OK)); } -} +}
\ No newline at end of file diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/datastore/FileStore.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/datastore/FileStore.java index 565120e1..7152614d 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/datastore/FileStore.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/datastore/FileStore.java @@ -83,7 +83,7 @@ class FileStore implements DataStore { private String externalName(Path path) { String fullName = path.toString(); String externalName = fullName.substring(path().toString().length()); - if (externalName.startsWith("/")) { + if (externalName.startsWith(File.separator)) { externalName = externalName.substring(1); } return externalName; @@ -127,7 +127,7 @@ class FileStore implements DataStore { } private Path path() { - return Path.of(applicationConfig.getVardataDirectory(), "database", this.location); + return Path.of(applicationConfig.getVardataDirectory(), "database", this.location, File.separator); } @Override diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/exceptions/GlobalExceptionHandler.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/exceptions/GlobalExceptionHandler.java index 76ce6f97..be2f5b7b 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/exceptions/GlobalExceptionHandler.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/exceptions/GlobalExceptionHandler.java @@ -30,7 +30,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +import org.springframework.web.reactive.result.method.annotation.ResponseEntityExceptionHandler; @ControllerAdvice(annotations = RestController.class) public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/repository/Policies.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/repository/Policies.java index 7eddeae1..ba3dd279 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/repository/Policies.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/repository/Policies.java @@ -197,7 +197,7 @@ public class Policies { byte[] bytes = gson.toJson(toStorageObject(policy)).getBytes(); this.dataStore.writeObject(this.getPath(policy), bytes) // - .doOnError(t -> logger.error("Could not store policy in S3, reason: {}", t.getMessage())) // + .doOnError(t -> logger.error("Could not store policy in S3, reason: {}", t.getMessage())) .subscribe(); } @@ -232,29 +232,29 @@ public class Policies { } private PersistentPolicyInfo toStorageObject(Policy p) { - return PersistentPolicyInfo.builder() // - .id(p.getId()) // - .json(p.getJson()) // - .ownerServiceId(p.getOwnerServiceId()) // - .ricId(p.getRic().id()) // - .statusNotificationUri(p.getStatusNotificationUri()) // - .typeId(p.getType().getId()) // - .isTransient(p.isTransient()) // - .lastModified(p.getLastModified().toString()) // + return PersistentPolicyInfo.builder() + .id(p.getId()) + .json(p.getJson()) + .ownerServiceId(p.getOwnerServiceId()) + .ricId(p.getRic().id()) + .statusNotificationUri(p.getStatusNotificationUri()) + .typeId(p.getType().getId()) + .isTransient(p.isTransient()) + .lastModified(p.getLastModified().toString()) .build(); } private Policy toPolicy(PersistentPolicyInfo p, Ric ric, PolicyTypes types) { try { - return Policy.builder() // - .id(p.getId()) // - .isTransient(p.isTransient()) // - .json(p.getJson()) // - .lastModified(Instant.parse(p.lastModified)) // - .ownerServiceId(p.getOwnerServiceId()) // - .ric(ric) // - .statusNotificationUri(p.getStatusNotificationUri()) // - .type(types.getType(p.getTypeId())) // + return Policy.builder() + .id(p.getId()) + .isTransient(p.isTransient()) + .json(p.getJson()) + .lastModified(Instant.parse(p.lastModified)) + .ownerServiceId(p.getOwnerServiceId()) + .ric(ric) + .statusNotificationUri(p.getStatusNotificationUri()) + .type(types.getType(p.getTypeId())) .build(); } catch (EntityNotFoundException e) { logger.warn("Not found: {}", e.getMessage()); diff --git a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ApplicationTest.java b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ApplicationTest.java index a42bd65e..9afa42f6 100644 --- a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ApplicationTest.java +++ b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ApplicationTest.java @@ -2,7 +2,7 @@ * ========================LICENSE_START================================= * ONAP : ccsdk oran * ====================================================================== - * Copyright (C) 2019-2022 Nordix Foundation. All rights reserved. + * Copyright (C) 2019-2023 Nordix Foundation. All rights reserved. * ====================================================================== * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,23 +27,23 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import java.io.FileOutputStream; -import java.io.PrintStream; import java.lang.invoke.MethodHandles; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.HashMap; -import org.json.JSONObject; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -64,6 +64,16 @@ import org.onap.ccsdk.oran.a1policymanagementservice.controllers.ServiceCallback import org.onap.ccsdk.oran.a1policymanagementservice.controllers.authorization.PolicyAuthorizationRequest; import org.onap.ccsdk.oran.a1policymanagementservice.controllers.authorization.PolicyAuthorizationRequest.Input.AccessType; import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.RicInfo; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyTypeDefinition; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyTypeIdList; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyInfo; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyInfoList; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyIdList; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyStatusInfo; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.ServiceStatusList; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.ServiceStatus; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.ServiceRegistrationInfo; import org.onap.ccsdk.oran.a1policymanagementservice.repository.Lock; import org.onap.ccsdk.oran.a1policymanagementservice.repository.Lock.LockType; import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies; @@ -132,6 +142,9 @@ class ApplicationTest { MockA1ClientFactory a1ClientFactory; @Autowired + private ObjectMapper objectMapper; + + @Autowired RicSupervision supervision; @Autowired @@ -247,23 +260,6 @@ class ApplicationTest { } @Test - @DisplayName("test generate Api Doc") - void generateApiDoc() throws Exception { - String url = "https://localhost:" + this.port + "/v3/api-docs"; - ResponseEntity<String> resp = restClient("", false).getForEntity(url).block(); - assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK); - JSONObject jsonObj = new JSONObject(resp.getBody()); - assertThat(jsonObj.remove("servers")).isNotNull(); - - String indented = (jsonObj).toString(4); - String docDir = "api/"; - Files.createDirectories(Paths.get(docDir)); - try (PrintStream out = new PrintStream(new FileOutputStream(docDir + "pms-api.json"))) { - out.print(indented); - } - } - - @Test @DisplayName("test Persistency Policies") void testPersistencyPolicies() throws Exception { Ric ric = this.addRic("ric1"); @@ -405,7 +401,8 @@ class ApplicationTest { this.addPolicyType("type1", "ric1"); String url = "/rics?policytype_id=type1"; String rsp = restClient().get(url).block(); - assertThat(rsp).contains("ric1"); + String expectedResponse = "{\"rics\":[{\"managed_element_ids\":[],\"ric_id\":\"ric1\", \"state\":\"AVAILABLE\",\"policytype_ids\":[\"type1\"]}]}"; + assertEquals(objectMapper.readTree(expectedResponse), objectMapper.readTree(rsp)); // nameless type for ORAN A1 1.1 addRic("ric2"); @@ -473,13 +470,13 @@ class ApplicationTest { String url = "/rics/ric?managed_element_id=" + managedElementId; String rsp = restClient().get(url).block(); - RicInfo ricInfo = gson.fromJson(rsp, RicInfo.class); - assertThat(ricInfo.ricId).isEqualTo(ricId); + RicInfo ricInfo = objectMapper.readValue(rsp, RicInfo.class); + assertThat(ricInfo.getRicId()).isEqualTo(ricId); url = "/rics/ric?ric_id=" + ricId; rsp = restClient().get(url).block(); - ricInfo = gson.fromJson(rsp, RicInfo.class); - assertThat(ricInfo.ricId).isEqualTo(ricId); + ricInfo = objectMapper.readValue(rsp, RicInfo.class); + assertThat(ricInfo.getRicId()).isEqualTo(ricId); // test GET RIC for ManagedElement that does not exist url = "/rics/ric?managed_element_id=" + "junk"; @@ -490,22 +487,32 @@ class ApplicationTest { } private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId, - boolean isTransient, String statusNotificationUri) { - PolicyInfo info = new PolicyInfo(); - info.policyId = policyInstanceId; - info.policyTypeId = policyTypeName; - info.ricId = ricId; - info.serviceId = serviceName; - info.policyData = gson.fromJson(jsonString(), Object.class); - - if (isTransient) { - info.isTransient = isTransient; - } - info.statusNotificationUri = statusNotificationUri; - return gson.toJson(info); - } - - private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId) { + boolean isTransient, String statusNotificationUri) throws JsonProcessingException { + PolicyInfo policyInfo = new PolicyInfo(); + policyInfo.setPolicyId(policyInstanceId); + policyInfo.setPolicytypeId(policyTypeName); + policyInfo.setRicId(ricId); + policyInfo.setServiceId(serviceName); + policyInfo.setPolicyData(jsonString()); + policyInfo.setTransient(isTransient); + policyInfo.setStatusNotificationUri(statusNotificationUri); + return objectMapper.writeValueAsString(policyInfo); + } + + private String putPolicyBod(String serviceName, String ricId, String policyTypeName, String policyInstanceId, + boolean isTransient, String statusNotificationUri) throws JsonProcessingException { + PolicyInfo policyInfo = new PolicyInfo(); + policyInfo.setPolicyId(policyInstanceId); + policyInfo.setPolicytypeId(policyTypeName); + policyInfo.setRicId(ricId); + policyInfo.setServiceId(serviceName); + policyInfo.setPolicyData(jsonString()); + policyInfo.setTransient(isTransient); + policyInfo.setStatusNotificationUri(statusNotificationUri); + return objectMapper.writeValueAsString(policyInfo); + } + + private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId) throws JsonProcessingException { return putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId, false, "statusUri"); } @@ -555,6 +562,7 @@ class ApplicationTest { url = "/policies/" + policyInstanceId; rsp = restClient().get(url).block(); + assertThat(rsp).contains(policyBody); // Test of error codes @@ -582,7 +590,6 @@ class ApplicationTest { this.applicationConfig .setAuthProviderUrl(baseUrl() + OpenPolicyAgentSimulatorController.ACCESS_CONTROL_URL_REJECT); - String url = "/policy-instances"; String rsp = restClient().get(url).block(); assertThat(rsp).as("Response contains no policy instance ID.").contains("[]"); @@ -622,7 +629,6 @@ class ApplicationTest { rsp = restClient().get(url).block(); assertThat(rsp).as("Response contains no policy instance ID.").contains("[]"); } - @Test @DisplayName("test Put Policy No Service No Status Uri") void testPutPolicy_NoServiceNoStatusUri() throws Exception { @@ -653,7 +659,7 @@ class ApplicationTest { * @throws ServiceException */ @DisplayName("test Error From Ric") - void testErrorFromRic() throws ServiceException { + void testErrorFromRic() throws ServiceException, JsonProcessingException { putService("service1"); addPolicyType("type1", "ric1"); @@ -687,31 +693,33 @@ class ApplicationTest { restClient().put("/policies", body).block(); String rsp = restClient().get("/policy-instances").block(); - PolicyInfoList info = gson.fromJson(rsp, PolicyInfoList.class); - assertThat(info.policies).hasSize(1); - PolicyInfo policyInfo = info.policies.iterator().next(); - assertThat(policyInfo.policyId).isEqualTo("id1"); - assertThat(policyInfo.policyTypeId).isEmpty(); + PolicyInfoList info = objectMapper.readValue(rsp, PolicyInfoList.class); + assertThat(info.getPolicies()).hasSize(1); + PolicyInfo policyInfo = info.getPolicies().iterator().next(); + assertThat(policyInfo.getPolicyId()).isEqualTo("id1"); + assertThat(policyInfo.getPolicytypeId()).isEmpty(); } @Test @DisplayName("test Update Service") void testUpdateService() throws Exception { this.addRic("ric1"); - this.addPolicy("p", "type1", "", "ric1"); + this.addPolicy("policyId", "type1", "", "ric1"); String url = "/policies?service_id="; String resp = restClient().get(url).block(); - assertThat(resp).contains("[\"p\"]"); + String expectedResponse = "{\"policy_ids\":[\"policyId\"]}"; + assertEquals(expectedResponse, resp); - this.addPolicy("p", "type1", "service", "ric1"); + this.addPolicy("policyId", "type1", "service", "ric1"); url = "/policies?service_id="; resp = restClient().get(url).block(); - assertThat(resp).contains("[]"); + expectedResponse = "{\"policy_ids\":[]}"; + assertEquals(expectedResponse, resp); url = "/policies?service_id=service"; resp = restClient().get(url).block(); - assertThat(resp).contains("[\"p\"]"); + assertThat(resp).contains("[\"policyId\"]"); } @Test @@ -735,10 +743,10 @@ class ApplicationTest { String url = "/policies/id"; Policy policy = addPolicy("id", "typeName", "service1", "ric1"); { - String rsp = restClient().get(url).block(); - PolicyInfo info = gson.fromJson(rsp, PolicyInfo.class); - String policyStr = gson.toJson(info.policyData); - assertThat(policyStr).isEqualTo(policy.getJson()); + String response = restClient().get(url).block(); + PolicyInfo policyInfo = objectMapper.readValue(response, PolicyInfo.class); + String policyData = gson.toJson(policyInfo.getPolicyData()); + assertThat(policyData).isEqualTo(policy.getJson()); } { policies.remove(policy); @@ -763,6 +771,7 @@ class ApplicationTest { testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } + @Test @DisplayName("test Get Policy Type") void testGetPolicyType() throws Exception { @@ -773,21 +782,21 @@ class ApplicationTest { String url = "/policy-types/" + typeId; - String rsp = this.restClient().get(url).block(); + String response = this.restClient().get(url).block(); - PolicyTypeInfo info = gson.fromJson(rsp, PolicyTypeInfo.class); - assertThat(info.schema).isNotNull(); + assertEquals("{\"policy_schema\":{\"title\":\"AC.D\"}}", response); // Get non existing schema url = "/policy-types/JUNK"; testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } - String createPolicyTypesJson(String... types) { + String createPolicyTypesJson(String... types) throws JsonProcessingException { List<String> list = new ArrayList<>(); Collections.addAll(list, types); - PolicyTypeIdList ids = new PolicyTypeIdList(list); - return gson.toJson(ids); + PolicyTypeIdList ids = new PolicyTypeIdList(); + ids.setPolicytypeIds(list); + return objectMapper.writeValueAsString(ids); } @Test @@ -845,15 +854,14 @@ class ApplicationTest { addPolicy("id1", "type1", "service1"); String url = "/policy-instances"; - String rsp = restClient().get(url).block(); - logger.info(rsp); - PolicyInfoList info = gson.fromJson(rsp, PolicyInfoList.class); - assertThat(info.policies).hasSize(1); - PolicyInfo policyInfo = info.policies.iterator().next(); - assert (policyInfo.validate()); - assertThat(policyInfo.policyId).isEqualTo("id1"); - assertThat(policyInfo.policyTypeId).isEqualTo("type1"); - assertThat(policyInfo.serviceId).isEqualTo("service1"); + String response = restClient().get(url).block(); + logger.info(response); + PolicyInfoList policyInfoList = objectMapper.readValue(response, PolicyInfoList.class); + assertThat(policyInfoList.getPolicies()).hasSize(1); + PolicyInfo policyInfo = policyInfoList.getPolicies().iterator().next(); + assertThat(policyInfo.getPolicyId()).isEqualTo("id1"); + assertThat(policyInfo.getPolicytypeId()).isEqualTo("type1"); + assertThat(policyInfo.getServiceId()).isEqualTo("service1"); } @Test @@ -867,14 +875,14 @@ class ApplicationTest { String url = "/policy-instances?policytype_id=type1"; String rsp = restClient().get(url).block(); logger.info(rsp); - assertThat(rsp).contains("id1") // - .contains("id2") // + assertThat(rsp).contains("id1") + .contains("id2") .doesNotContain("id3"); url = "/policy-instances?policytype_id=type1&service_id=service2"; rsp = restClient().get(url).block(); logger.info(rsp); - assertThat(rsp).doesNotContain("id1") // + assertThat(rsp).doesNotContain("id1") .contains("id2") // .doesNotContain("id3"); @@ -882,7 +890,7 @@ class ApplicationTest { rsp = restClient().get(url).block(); assertThat(rsp).contains("id1") // .contains("id2") // - .doesNotContain("id3") // + .doesNotContain("id3") .contains("id4"); // Test get policies for non existing type @@ -905,21 +913,19 @@ class ApplicationTest { String url = "/policies?policytype_id=type1"; String rsp = restClient().get(url).block(); logger.info(rsp); - assertThat(rsp).contains("id1") // - .contains("id2") // + assertThat(rsp).contains("id1") + .contains("id2") .doesNotContain("id3"); url = "/policies?policytype_id=type1&service_id=service1&ric=ric1"; rsp = restClient().get(url).block(); - PolicyIdList respList = gson.fromJson(rsp, PolicyIdList.class); - assertThat(respList.policyIds.iterator().next()).isEqualTo("id1"); + PolicyIdList respList = objectMapper.readValue(rsp, PolicyIdList.class); + assertThat(respList.getPolicyIds().iterator().next()).isEqualTo("id1"); - url = "/policies?policytype_name=type1&service_id=service1"; + url = "/policies?type_name=type1&service_id=service1"; rsp = restClient().get(url).block(); - assertThat(rsp).contains("id1") // - .contains("id3") // - .contains("id4"); - assertThat(gson.fromJson(rsp, PolicyIdList.class).policyIds).hasSize(3); + assertThat(rsp).contains("id1").contains("id4"); + assertThat(objectMapper.readValue(rsp, PolicyIdList.class).getPolicyIds()).hasSize(2); // Test get policy ids for non existing type url = "/policies?policytype_id=type1XXX"; @@ -930,6 +936,7 @@ class ApplicationTest { testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } + @Test @DisplayName("test Put And Get Service") void testPutAndGetService() throws Exception { @@ -941,11 +948,11 @@ class ApplicationTest { // GET one service String url = "/services?service_id=" + serviceName; String rsp = restClient().get(url).block(); - ServiceStatusList info = gson.fromJson(rsp, ServiceStatusList.class); - assertThat(info.statusList).hasSize(1); - ServiceStatus status = info.statusList.iterator().next(); - assertThat(status.keepAliveIntervalSeconds).isZero(); - assertThat(status.serviceId).isEqualTo(serviceName); + ServiceStatusList info = objectMapper.readValue(rsp, ServiceStatusList.class); + assertThat(info.getServiceList()).hasSize(1); + ServiceStatus status = info.getServiceList().iterator().next(); + assertThat(status.getKeepAliveIntervalSeconds()).isZero(); + assertThat(status.getServiceId()).isEqualTo(serviceName); // GET (all) url = "/services"; @@ -982,7 +989,7 @@ class ApplicationTest { @Test @DisplayName("test Service Supervision") void testServiceSupervision() throws Exception { - putService("service1", 1, HttpStatus.CREATED); + putService("service1", 2, HttpStatus.CREATED); addPolicyType("type1", "ric1"); String policyBody = putPolicyBody("service1", "ric1", "type1", "instance1"); @@ -991,7 +998,7 @@ class ApplicationTest { assertThat(policies.size()).isEqualTo(1); assertThat(services.size()).isEqualTo(1); - // Timeout after ~1 second + // Timeout after ~2 second await().untilAsserted(() -> assertThat(policies.size()).isZero()); assertThat(services.size()).isZero(); } @@ -1003,9 +1010,9 @@ class ApplicationTest { assertThat(policies.size()).isEqualTo(1); String url = "/policies/id/status"; - String rsp = restClient().get(url).block(); - PolicyStatusInfo info = gson.fromJson(rsp, PolicyStatusInfo.class); - assertThat(info.status).isEqualTo("OK"); + String response = restClient().get(url).block(); + PolicyStatusInfo info = objectMapper.readValue(response, PolicyStatusInfo.class); + assertThat(info.getStatus()).isEqualTo("OK"); // GET non existing policy status url = "/policies/XXX/status"; @@ -1016,9 +1023,9 @@ class ApplicationTest { url = "/policies/id/status"; WebClientResponseException a1Exception = new WebClientResponseException(404, "", null, null, null); doReturn(Mono.error(a1Exception)).when(a1Client).getPolicyStatus(any()); - rsp = restClient().get(url).block(); - info = gson.fromJson(rsp, PolicyStatusInfo.class); - assertThat(info.status).hasToString("{}"); + response = restClient().get(url).block(); + info = objectMapper.readValue(response, PolicyStatusInfo.class); + assertThat(info.getStatus()).hasToString("{}"); } @Test @@ -1065,15 +1072,15 @@ class ApplicationTest { private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException { addRic(ric); - Policy policy = Policy.builder() // - .id(id) // - .json(jsonString()) // - .ownerServiceId(service) // - .ric(rics.getRic(ric)) // - .type(addPolicyType(typeName, ric)) // - .lastModified(Instant.now()) // - .isTransient(false) // - .statusNotificationUri("/policy-status?id=XXX") // + Policy policy = Policy.builder() + .id(id) + .json(gson.toJson(jsonString())) + .ownerServiceId(service) + .ric(rics.getRic(ric)) + .type(addPolicyType(typeName, ric)) + .lastModified(Instant.now()) + .isTransient(false) + .statusNotificationUri("/policy-status?id=XXX") .build(); policies.put(policy); return policy; @@ -1083,23 +1090,24 @@ class ApplicationTest { return addPolicy(id, typeName, service, "ric"); } - private String createServiceJson(String name, long keepAliveIntervalSeconds) { + private String createServiceJson(String name, long keepAliveIntervalSeconds) throws JsonProcessingException { String callbackUrl = baseUrl() + RappSimulatorController.SERVICE_CALLBACK_URL; return createServiceJson(name, keepAliveIntervalSeconds, callbackUrl); } - private String createServiceJson(String name, long keepAliveIntervalSeconds, String url) { - ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, keepAliveIntervalSeconds, url); + private String createServiceJson(String name, long keepAliveIntervalSeconds, String url) throws JsonProcessingException { + ServiceRegistrationInfo service = new ServiceRegistrationInfo(name) + .keepAliveIntervalSeconds(keepAliveIntervalSeconds) + .callbackUrl(url); - String json = gson.toJson(service); - return json; + return objectMapper.writeValueAsString(service); } - private void putService(String name) { + private void putService(String name) throws JsonProcessingException { putService(name, 0, null); } - private void putService(String name, long keepAliveIntervalSeconds, @Nullable HttpStatus expectedStatus) { + private void putService(String name, long keepAliveIntervalSeconds, @Nullable HttpStatus expectedStatus) throws JsonProcessingException { String url = "/services"; String body = createServiceJson(name, keepAliveIntervalSeconds); ResponseEntity<String> resp = restClient().putForEntity(url, body).block(); @@ -1108,8 +1116,10 @@ class ApplicationTest { } } - private String jsonString() { - return "{\"servingCellNrcgi\":\"1\"}"; + private Map<String,String> jsonString() { + Map<String,String> policyDataInMap = new HashMap<>(); + policyDataInMap.put("servingCellNrcgi","1"); + return policyDataInMap; } @Test @@ -1154,15 +1164,15 @@ class ApplicationTest { private AsyncRestClient restClient(String baseUrl, boolean useTrustValidation) { WebClientConfig config = this.applicationConfig.getWebClientConfig(); - config = WebClientConfig.builder() // - .keyStoreType(config.getKeyStoreType()) // - .keyStorePassword(config.getKeyStorePassword()) // - .keyStore(config.getKeyStore()) // - .keyPassword(config.getKeyPassword()) // - .isTrustStoreUsed(useTrustValidation) // - .trustStore(config.getTrustStore()) // - .trustStorePassword(config.getTrustStorePassword()) // - .httpProxyConfig(config.getHttpProxyConfig()) // + config = WebClientConfig.builder() + .keyStoreType(config.getKeyStoreType()) + .keyStorePassword(config.getKeyStorePassword()) + .keyStore(config.getKeyStore()) + .keyPassword(config.getKeyPassword()) + .isTrustStoreUsed(useTrustValidation) + .trustStore(config.getTrustStore()) + .trustStorePassword(config.getTrustStorePassword()) + .httpProxyConfig(config.getHttpProxyConfig()) .build(); AsyncRestClientFactory f = new AsyncRestClientFactory(config, new SecurityContext("")); @@ -1196,10 +1206,10 @@ class ApplicationTest { private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains, boolean expectApplicationProblemJsonMediaType) { - StepVerifier.create(request) // - .expectSubscription() // + StepVerifier.create(request) + .expectSubscription() .expectErrorMatches( - t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) // + t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) .verify(); } @@ -1227,9 +1237,9 @@ class ApplicationTest { } private PolicyType createPolicyType(String policyTypeName) { - return PolicyType.builder() // - .id(policyTypeName) // - .schema("{\"title\":\"" + policyTypeName + "\"}") // + return PolicyType.builder() + .id(policyTypeName) + .schema("{\"title\":\"" + policyTypeName + "\"}") .build(); } @@ -1249,10 +1259,10 @@ class ApplicationTest { if (managedElement != null) { mes.add(managedElement); } - return RicConfig.builder() // - .ricId(ricId) // - .baseUrl(ricId) // - .managedElementIds(mes) // + return RicConfig.builder() + .ricId(ricId) + .baseUrl(ricId) + .managedElementIds(mes) .build(); } diff --git a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ConcurrencyTestRunnable.java b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ConcurrencyTestRunnable.java index 0bf212a7..888325b2 100644 --- a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ConcurrencyTestRunnable.java +++ b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ConcurrencyTestRunnable.java @@ -20,14 +20,19 @@ package org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.lang.invoke.MethodHandles; import java.time.Instant; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.onap.ccsdk.oran.a1policymanagementservice.clients.AsyncRestClient; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyInfo; import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy; import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyType; import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyTypes; @@ -38,6 +43,7 @@ import org.onap.ccsdk.oran.a1policymanagementservice.utils.MockA1Client; import org.onap.ccsdk.oran.a1policymanagementservice.utils.MockA1ClientFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; /** @@ -55,6 +61,8 @@ class ConcurrencyTestRunnable implements Runnable { private final PolicyTypes types; private boolean failed = false; + private ObjectMapper objectMapper = new ObjectMapper(); + private static Gson gson = new GsonBuilder().create(); ConcurrencyTestRunnable(AsyncRestClient client, RicSupervision supervision, MockA1ClientFactory a1ClientFactory, @@ -140,25 +148,30 @@ class ConcurrencyTestRunnable implements Runnable { webClient.getForEntity(uri).block(); } - private void putPolicy(String name) { + private void putPolicy(String name) throws JsonProcessingException { String putUrl = "/policies"; String body = putPolicyBody("service1", "ric", "type1", name, false); webClient.putForEntity(putUrl, body).block(); } private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId, - boolean isTransient) { - PolicyInfo info = new PolicyInfo(); - info.policyId = policyInstanceId; - info.policyTypeId = policyTypeName; - info.ricId = ricId; - info.serviceId = serviceName; - info.policyData = gson.fromJson("{}", Object.class); - - if (isTransient) { - info.isTransient = isTransient; - } - return gson.toJson(info); + boolean isTransient) throws JsonProcessingException { + + PolicyInfo policyInfo = new PolicyInfo(); + policyInfo.setPolicyId(policyInstanceId); + policyInfo.setPolicytypeId(policyTypeName); + policyInfo.setRicId(ricId); + policyInfo.setServiceId(serviceName); + policyInfo.setPolicyData(policyData()); + policyInfo.setStatusNotificationUri("/status"); + policyInfo.setTransient(isTransient); + return objectMapper.writeValueAsString(policyInfo); + } + + private Map<String,String> policyData() { + Map<String,String> policyDataInMap = new HashMap<>(); + policyDataInMap.put("servingCellNrcgi","1"); + return policyDataInMap; } private void deletePolicy(String name) { |