diff options
author | rameshiyer27 <ramesh.murugan.iyer@est.tech> | 2021-07-20 07:57:41 +0100 |
---|---|---|
committer | rameshiyer27 <ramesh.murugan.iyer@est.tech> | 2021-07-29 16:30:11 +0100 |
commit | 23e25e89619c732a687b44a5487afbb6badf613c (patch) | |
tree | 76641d98717f826bf1df96357a7db815cf899b25 | |
parent | 931424fc681b1d41afbba98a190253283f9a6eba (diff) |
Implement http participant in CLAMP
Issue-ID: POLICY-3449
Signed-off-by: zrrmmua <ramesh.murugan.iyer@est.tech>
Change-Id: Ibfe23a6e98fb760a930b6080dc2291113b3cb4fa
17 files changed, 1174 insertions, 0 deletions
diff --git a/participant/participant-impl/participant-impl-http/pom.xml b/participant/participant-impl/participant-impl-http/pom.xml new file mode 100644 index 000000000..85d65e707 --- /dev/null +++ b/participant/participant-impl/participant-impl-http/pom.xml @@ -0,0 +1,50 @@ +<!-- + ============LICENSE_START======================================================= + Copyright (C) 2021 Nordix Foundation. + ================================================================================ + 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. + + SPDX-License-Identifier: Apache-2.0 + ============LICENSE_END========================================================= +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.policy.clamp.participant</groupId> + <artifactId>policy-clamp-participant-impl</artifactId> + <version>6.1.2-SNAPSHOT</version> + </parent> + + <artifactId>policy-clamp-participant-impl-http</artifactId> + <name>${project.artifactId}</name> + <description>HTTP participant, that performs CRUD operations on Microservices through REST</description> + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + <phase>package</phase> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/Application.java b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/Application.java new file mode 100644 index 000000000..dc36392f7 --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/Application.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.participant.http; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Starter. + * + */ +@SpringBootApplication +public class Application { + /** + * Main class. + * + * @param args args + */ + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/config/ParticipantConfig.java b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/config/ParticipantConfig.java new file mode 100644 index 000000000..ce013a765 --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/config/ParticipantConfig.java @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.participant.http.config; + +import org.onap.policy.clamp.controlloop.participant.http.main.handler.ControlLoopElementHandler; +import org.onap.policy.clamp.controlloop.participant.intermediary.api.ParticipantIntermediaryApi; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ParticipantConfig { + + /** + * Register ControlLoopElementListener. + * + * @param intermediaryApi the ParticipantIntermediaryApi + * @param clElementHandler the ControlLoop Element Handler + */ + @Autowired + public void registerControlLoopElementListener(ParticipantIntermediaryApi intermediaryApi, + ControlLoopElementHandler clElementHandler) { + intermediaryApi.registerControlLoopElementListener(clElementHandler); + clElementHandler.setIntermediaryApi(intermediaryApi); + } +} diff --git a/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/exception/HttpWebClientException.java b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/exception/HttpWebClientException.java new file mode 100644 index 000000000..b10af884c --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/exception/HttpWebClientException.java @@ -0,0 +1,31 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.participant.http.main.exception; + + +import org.springframework.web.reactive.function.client.WebClientResponseException; + +public class HttpWebClientException extends WebClientResponseException { + + public HttpWebClientException(int statusCode, String statusText) { + super(statusCode, statusText, null, null, null); + } +} diff --git a/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/handler/ControlLoopElementHandler.java b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/handler/ControlLoopElementHandler.java new file mode 100644 index 000000000..67948a95c --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/handler/ControlLoopElementHandler.java @@ -0,0 +1,148 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.participant.http.main.handler; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.ValidationException; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.tuple.Pair; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopOrderedState; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState; +import org.onap.policy.clamp.controlloop.participant.http.main.models.ConfigRequest; +import org.onap.policy.clamp.controlloop.participant.http.main.webclient.ClHttpClient; +import org.onap.policy.clamp.controlloop.participant.intermediary.api.ControlLoopElementListener; +import org.onap.policy.clamp.controlloop.participant.intermediary.api.ParticipantIntermediaryApi; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.models.base.PfModelException; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; +import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * This class handles implementation of controlLoopElement updates. + */ +@Component +public class ControlLoopElementHandler implements ControlLoopElementListener, Closeable { + + private static final Coder CODER = new StandardCoder(); + + private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + + private Map<ToscaConceptIdentifier, Pair<Integer, String>> restResponseMap = new ConcurrentHashMap<>(); + + @Setter + private ParticipantIntermediaryApi intermediaryApi; + + /** + * Handle controlLoopElement statistics. + * + * @param controlLoopElementId controlloop element id + */ + @Override + public void handleStatistics(UUID controlLoopElementId) throws PfModelException { + // Implementation not needed for http participant + + } + + /** + * Handle a control loop element state change. + * + * @param controlLoopElementId the ID of the control loop element + * @param currentState the current state of the control loop element + * @param newState the state to which the control loop element is changing to + * @throws PfModelException in case of a model exception + */ + @Override + public void controlLoopElementStateChange(UUID controlLoopElementId, ControlLoopState currentState, + ControlLoopOrderedState newState) throws PfModelException { + // Implementation not needed for http participant + } + + /** + * Callback method to handle an update on a control loop element. + * + * @param element the information on the control loop element + * @param controlLoopDefinition toscaServiceTemplate + */ + @Override + public void controlLoopElementUpdate(ControlLoopElement element, ToscaServiceTemplate controlLoopDefinition) { + + for (Map.Entry<String, ToscaNodeTemplate> nodeTemplate : controlLoopDefinition.getToscaTopologyTemplate() + .getNodeTemplates().entrySet()) { + // Fetching the node template of corresponding CL element + if (element.getDefinition().getName().equals(nodeTemplate.getKey())) { + try { + var configRequest = CODER.convert(nodeTemplate.getValue().getProperties(), ConfigRequest.class); + Set<ConstraintViolation<ConfigRequest>> violations = Validation.buildDefaultValidatorFactory() + .getValidator().validate(configRequest); + if (violations.isEmpty()) { + invokeHttpClient(configRequest); + } else { + LOGGER.error("Violations found in the config request parameters: {}", violations); + throw new ValidationException("Constraint violations in the config request"); + } + } catch (CoderException | ValidationException e) { + LOGGER.error("Error invoking the http request for the config ", e); + } + } + } + } + + /** + * Invoke a runnable thread to execute http requests. + * @param configRequest ConfigRequest + */ + public void invokeHttpClient(ConfigRequest configRequest) { + // Invoke runnable thread to execute https requests of all config entities + executor.execute(new ClHttpClient(configRequest, restResponseMap)); + } + + /** + * Closes this stream and releases any system resources associated + * with it. If the stream is already closed then invoking this + * method has no effect. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void close() throws IOException { + executor.shutdown(); + } +} diff --git a/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/models/ConfigRequest.java b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/models/ConfigRequest.java new file mode 100644 index 000000000..87cab88a7 --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/models/ConfigRequest.java @@ -0,0 +1,51 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.participant.http.main.models; + +import java.util.List; +import java.util.Map; +import javax.validation.Valid; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.ws.rs.DefaultValue; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class ConfigRequest { + + @NotBlank + private String baseUrl; + + @NotNull + private Map<String, String> httpHeaders; + + @NotNull + @Valid + private List<ConfigurationEntity> configurationEntities; + + @Min(value = 1) + @DefaultValue("20") + private int uninitializedToPassiveTimeout; + +} diff --git a/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/models/ConfigurationEntity.java b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/models/ConfigurationEntity.java new file mode 100644 index 000000000..8703a9d21 --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/models/ConfigurationEntity.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.participant.http.main.models; + +import java.util.List; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; + +@Data +@AllArgsConstructor +public class ConfigurationEntity { + + @NotNull + private ToscaConceptIdentifier configurationEntityId; + + @NotNull + @Valid + private List<RestParams> restSequence; +} diff --git a/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/models/RestParams.java b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/models/RestParams.java new file mode 100644 index 000000000..fc4e02897 --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/models/RestParams.java @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.participant.http.main.models; + +import java.util.Map; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; + +@Data +@AllArgsConstructor +public class RestParams { + + @NotNull + private ToscaConceptIdentifier restRequestId; + + @NotNull + private String httpMethod; + + @NotNull + private String path; + + @Min(100) + @Max(599) + private int expectedResponse; + + private Map<String, Object> pathParams; + + private Map<String, String> queryParams; + + private String body; + +} diff --git a/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/webclient/ClHttpClient.java b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/webclient/ClHttpClient.java new file mode 100644 index 000000000..0e0ea557e --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/main/java/org/onap/policy/clamp/controlloop/participant/http/main/webclient/ClHttpClient.java @@ -0,0 +1,135 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.participant.http.main.webclient; + +import java.lang.invoke.MethodHandles; +import java.time.Duration; +import java.util.Map; +import java.util.Objects; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.onap.policy.clamp.controlloop.participant.http.main.exception.HttpWebClientException; +import org.onap.policy.clamp.controlloop.participant.http.main.models.ConfigRequest; +import org.onap.policy.clamp.controlloop.participant.http.main.models.ConfigurationEntity; +import org.onap.policy.clamp.controlloop.participant.http.main.models.RestParams; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.util.UriComponentsBuilder; +import reactor.core.publisher.Mono; + +public class ClHttpClient implements Runnable { + + private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private final ConfigRequest configRequest; + + private Map<ToscaConceptIdentifier, Pair<Integer, String>> responseMap; + + /** + * Constructor. + */ + public ClHttpClient(ConfigRequest configRequest, Map<ToscaConceptIdentifier, Pair<Integer, String>> responseMap) { + this.configRequest = configRequest; + this.responseMap = responseMap; + } + + /** + * Runnable to execute http requests. + */ + @Override + public void run() { + + var webClient = WebClient.builder() + .baseUrl(configRequest.getBaseUrl()) + .defaultHeaders(httpHeaders -> httpHeaders.addAll(createHeaders(configRequest))) + .build(); + + for (ConfigurationEntity configurationEntity : configRequest.getConfigurationEntities()) { + LOGGER.info("Executing http requests for the config entity {}", + configurationEntity.getConfigurationEntityId()); + + executeRequest(webClient, configurationEntity); + } + } + + private void executeRequest(WebClient client, ConfigurationEntity configurationEntity) { + + // Iterate the sequence of http requests + for (RestParams request: configurationEntity.getRestSequence()) { + String response = null; + try { + var httpMethod = Objects.requireNonNull(HttpMethod.resolve(request.getHttpMethod())); + var uri = createUriString(request); + LOGGER.info("Executing HTTP request: {} for the Rest request id: {}", httpMethod, + request.getRestRequestId()); + + response = client.method(httpMethod) + .uri(uri) + .body(request.getBody() == null ? BodyInserters.empty() + : BodyInserters.fromValue(request.getBody())) + .exchangeToMono(clientResponse -> + clientResponse.statusCode().value() == request.getExpectedResponse() + ? clientResponse.bodyToMono(String.class) + : Mono.error(new HttpWebClientException(clientResponse.statusCode().value(), + clientResponse.bodyToMono(String.class).toString()))) + .block(Duration.ofMillis(configRequest.getUninitializedToPassiveTimeout() * 1000L)); + + LOGGER.info("HTTP response for the {} request : {}", httpMethod, response); + responseMap.put(request.getRestRequestId(), new ImmutablePair<>(request.getExpectedResponse(), + response)); + + } catch (HttpWebClientException ex) { + LOGGER.error("Error occurred on the HTTP request ", ex); + responseMap.put(request.getRestRequestId(), new ImmutablePair<>(ex.getStatusCode().value(), + ex.getResponseBodyAsString())); + } + } + } + + private HttpHeaders createHeaders(ConfigRequest request) { + var headers = new HttpHeaders(); + for (Map.Entry<String, String> entry: request.getHttpHeaders().entrySet()) { + headers.add(entry.getKey(), entry.getValue()); + } + return headers; + } + + private String createUriString(RestParams restParams) { + var uriComponentsBuilder = UriComponentsBuilder.fromUriString(restParams.getPath()); + // Add path params if present + if (restParams.getPathParams() != null) { + uriComponentsBuilder.uriVariables(restParams.getPathParams()); + } + // Add query params if present + if (restParams.getQueryParams() != null) { + for (Map.Entry<String, String> entry : restParams.getQueryParams().entrySet()) { + uriComponentsBuilder.queryParam(entry.getKey(), entry.getValue()); + } + } + return uriComponentsBuilder.build().toUriString(); + } + +} diff --git a/participant/participant-impl/participant-impl-http/src/main/resources/config/application.yaml b/participant/participant-impl/participant-impl-http/src/main/resources/config/application.yaml new file mode 100644 index 000000000..1fd528458 --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/main/resources/config/application.yaml @@ -0,0 +1,27 @@ +participant: + intermediaryParameters: + reportingTimeInterval: 120000 + description: Participant Description + participantId: + name: HttpParticipant0 + version: 1.0.0 + participantType: + name: org.onap.k8s.controlloop.HttpControlLoopParticipant + version: 2.3.4 + clampControlLoopTopics: + topicSources: + - topic: POLICY-CLRUNTIME-PARTICIPANT + servers: + - ${topicServer:message-router} + topicCommInfrastructure: dmaap + fetchTimeout: 15000 + topicSinks: + - topic: POLICY-CLRUNTIME-PARTICIPANT + servers: + - ${topicServer:message-router} + topicCommInfrastructure: dmaap + + - topic: POLICY-NOTIFICATION + servers: + - ${topicServer:message-router} + topicCommInfrastructure: dmaap diff --git a/participant/participant-impl/participant-impl-http/src/test/java/handler/ClElementHandlerTest.java b/participant/participant-impl/participant-impl-http/src/test/java/handler/ClElementHandlerTest.java new file mode 100644 index 000000000..10b8103cb --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/test/java/handler/ClElementHandlerTest.java @@ -0,0 +1,63 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package handler; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement; +import org.onap.policy.clamp.controlloop.participant.http.main.handler.ControlLoopElementHandler; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import utils.CommonTestData; +import utils.ToscaUtils; + +@ExtendWith(SpringExtension.class) +class ClElementHandlerTest { + + @InjectMocks + @Spy + private ControlLoopElementHandler controlLoopElementHandler = new ControlLoopElementHandler(); + + private CommonTestData commonTestData = new CommonTestData(); + + private static ToscaServiceTemplate serviceTemplate; + + @BeforeAll + static void init() throws CoderException { + serviceTemplate = ToscaUtils.readControlLoopFromTosca(); + } + + @Test + void test_ControlLoopElementUpdate() { + doNothing().when(controlLoopElementHandler).invokeHttpClient(any()); + ControlLoopElement element = commonTestData.getControlLoopElement(); + assertDoesNotThrow(() -> controlLoopElementHandler + .controlLoopElementUpdate(element, serviceTemplate)); + } +} diff --git a/participant/participant-impl/participant-impl-http/src/test/java/utils/CommonTestData.java b/participant/participant-impl/participant-impl-http/src/test/java/utils/CommonTestData.java new file mode 100644 index 000000000..84957edfa --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/test/java/utils/CommonTestData.java @@ -0,0 +1,146 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package utils; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopOrderedState; +import org.onap.policy.clamp.controlloop.participant.http.main.models.ConfigurationEntity; +import org.onap.policy.clamp.controlloop.participant.http.main.models.RestParams; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; + +public class CommonTestData { + + private static final Coder CODER = new StandardCoder(); + private static final String TEST_KEY_NAME = "org.onap.domain.database.Http_PMSHMicroserviceControlLoopElement"; + + + /** + * Get a controlLoop Element. + * @return controlLoopElement object + */ + public ControlLoopElement getControlLoopElement() { + ControlLoopElement element = new ControlLoopElement(); + element.setId(UUID.randomUUID()); + element.setDefinition(new ToscaConceptIdentifier(TEST_KEY_NAME, "1.0.1")); + element.setOrderedState(ControlLoopOrderedState.PASSIVE); + return element; + } + + /** + * Get query params. + * @return Map of query params + */ + public Map<String, String> getQueryParams() { + return Map.of("name", "dummy", "version", "1.0"); + } + + /** + * Get path params. + * @return Map of path params + */ + public Map<String, Object> getPathParams() { + return Map.of("id", "123", "name", "dummy"); + } + + /** + * Rest params with GET request. + * @return RestParams obj + */ + public RestParams restParamsWithGet() { + return new RestParams( + new ToscaConceptIdentifier("getRequest", "1.0"), + "GET", + "get", + 200, + null, + getQueryParams(), + null + ); + } + + /** + * Rest params with POST request. + * @return RestParams obj + */ + public RestParams restParamsWithPost() { + return new RestParams( + new ToscaConceptIdentifier("postRequest", "1.0"), + "POST", + "post", + 200, + null, + getQueryParams(), + "Test body" + ); + } + + /** + * Rest params with POST request. + * @return RestParams obj + */ + public RestParams restParamsWithInvalidPost() { + return new RestParams( + new ToscaConceptIdentifier("postRequest", "1.0"), + "POST", + "post/{id}/{name}", + 200, + getPathParams(), + getQueryParams(), + "Test body" + ); + } + + /** + * Get invalid configuration entity. + * @return ConfigurationEntity obj + */ + public ConfigurationEntity getInvalidConfigurationEntity() { + return new ConfigurationEntity( + new ToscaConceptIdentifier("config1", "1.0.1"), + List.of(restParamsWithGet(), restParamsWithInvalidPost()) + ); + } + + /** + * Get configuration entity. + * @return ConfigurationEntity obj + */ + public ConfigurationEntity getConfigurationEntity() { + return new ConfigurationEntity( + new ToscaConceptIdentifier("config1", "1.0.1"), + List.of(restParamsWithGet(), restParamsWithPost()) + ); + } + + /** + * Get headers for config request. + * @return Map of headers + */ + public Map<String, String> getHeaders() { + return Map.of("Content-Type", "application/json", "Accept", "application/json"); + } + +} diff --git a/participant/participant-impl/participant-impl-http/src/test/java/utils/ToscaUtils.java b/participant/participant-impl/participant-impl-http/src/test/java/utils/ToscaUtils.java new file mode 100644 index 000000000..cf71248c3 --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/test/java/utils/ToscaUtils.java @@ -0,0 +1,48 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package utils; + +import org.onap.policy.common.utils.coder.YamlJsonTranslator; +import org.onap.policy.common.utils.resources.ResourceUtils; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; + +/** + * Util class for Test scope. + */ +public class ToscaUtils { + + private static final YamlJsonTranslator yamlTranslator = new YamlJsonTranslator(); + private static final String TOSCA_TEMPLATE_YAML = "src/test/resources/HttpParticipantConfig.yaml"; + + + /** + * Read a service template yaml. + * @return ToscaServiceTemplate + */ + public static ToscaServiceTemplate readControlLoopFromTosca() { + return serializeControlLoopYaml(TOSCA_TEMPLATE_YAML); + } + + private static ToscaServiceTemplate serializeControlLoopYaml(String controlLoopFilePath) { + String controlLoopString = ResourceUtils.getResourceAsString(controlLoopFilePath); + return yamlTranslator.fromYaml(controlLoopString, ToscaServiceTemplate.class); + } +} diff --git a/participant/participant-impl/participant-impl-http/src/test/java/webclient/ClHttpClientTest.java b/participant/participant-impl/participant-impl-http/src/test/java/webclient/ClHttpClientTest.java new file mode 100644 index 000000000..411974a04 --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/test/java/webclient/ClHttpClientTest.java @@ -0,0 +1,85 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package webclient; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.onap.policy.clamp.controlloop.participant.http.main.models.ConfigRequest; +import org.onap.policy.clamp.controlloop.participant.http.main.models.ConfigurationEntity; +import org.onap.policy.clamp.controlloop.participant.http.main.webclient.ClHttpClient; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import utils.CommonTestData; + +@ExtendWith(SpringExtension.class) +class ClHttpClientTest { + + private CommonTestData commonTestData = new CommonTestData(); + + private String testBaseUrl = "https://httpbin.org"; + + private Map<ToscaConceptIdentifier, Pair<Integer, String>> responseMap = new HashMap<>(); + + + @Test + void test_validRequest() { + //Add valid rest requests POST, GET + ConfigurationEntity configurationEntity = commonTestData.getConfigurationEntity(); + + Map<String, String> headers = commonTestData.getHeaders(); + ConfigRequest configRequest = new ConfigRequest(testBaseUrl, headers, + List.of(configurationEntity), 10); + + ClHttpClient client = new ClHttpClient(configRequest, responseMap); + assertDoesNotThrow(client::run); + assertThat(responseMap).hasSize(2).containsKey(commonTestData + .restParamsWithGet().getRestRequestId()); + + Pair<Integer, String> restResponseMap = responseMap.get(commonTestData + .restParamsWithGet().getRestRequestId()); + assertThat(restResponseMap.getKey()).isEqualTo(200); + } + + @Test + void test_invalidRequest() { + //Add rest requests Invalid POST, Valid GET + ConfigurationEntity configurationEntity = commonTestData.getInvalidConfigurationEntity(); + + Map<String, String> headers = commonTestData.getHeaders(); + ConfigRequest configRequest = new ConfigRequest(testBaseUrl, headers, + List.of(configurationEntity), 10); + + ClHttpClient client = new ClHttpClient(configRequest, responseMap); + assertDoesNotThrow(client::run); + assertThat(responseMap).hasSize(2).containsKey(commonTestData + .restParamsWithGet().getRestRequestId()); + Pair<Integer, String> response = responseMap + .get(commonTestData.restParamsWithInvalidPost().getRestRequestId()); + assertThat(response.getKey()).isEqualTo(404); + } +} diff --git a/participant/participant-impl/participant-impl-http/src/test/resources/HttpParticipantConfig.yaml b/participant/participant-impl/participant-impl-http/src/test/resources/HttpParticipantConfig.yaml new file mode 100644 index 000000000..43d0c269c --- /dev/null +++ b/participant/participant-impl/participant-impl-http/src/test/resources/HttpParticipantConfig.yaml @@ -0,0 +1,208 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2021 Nordix Foundation. +# ================================================================================ +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# ============LICENSE_END========================================================= +tosca_definitions_version: tosca_simple_yaml_1_3 +data_types: + onap.datatypes.ToscaConceptIdentifier: + derived_from: tosca.datatypes.Root + properties: + name: + type: string + required: true + version: + type: string + required: true + + org.onap.datatypes.policy.clamp.controlloop.httpControlLoopElement.RestRequest: + version: 1.0.0 + derived_from: tosca.datatypes.Root + properties: + restRequestId: + type: onap.datatypes.ToscaConceptIdentifier + typeVersion: 1.0.0 + required: true + description: The name and version of a REST request to be sent to a REST endpoint + httpMethod: + type: string + required: true + constraints: + - valid_values: [POST, PUT, GET, DELETE] + description: The REST method to use + path: + type: string + required: true + description: The path of the REST request relative to the base URL + body: + type: string + required: false + description: The body of the REST request for PUT and POST requests + expectedResponse: + type: integer + required: true + constraints: + - in_range: [100, 599] + description: THe expected HTTP status code for the REST request + org.onap.datatypes.policy.clamp.controlloop.httpControlLoopElement.ConfigurationEntity: + version: 1.0.0 + derived_from: tosca.datatypes.Root + properties: + configurationEntityId: + type: onap.datatypes.ToscaConceptIdentifier + typeVersion: 1.0.0 + required: true + description: The name and version of a Configuration Entity to be handled by the HTTP Control Loop Element + restSequence: + type: list + entry_schema: + type: org.onap.datatypes.policy.clamp.controlloop.httpControlLoopElement.RestRequest + typeVersion: 1.0.0 + description: A sequence of REST commands to send to the REST endpoint + + +node_types: + org.onap.policy.clamp.controlloop.Participant: + version: 1.0.1 + derived_from: tosca.nodetypes.Root + properties: + provider: + type: string + requred: false + org.onap.policy.clamp.controlloop.ControlLoopElement: + version: 1.0.1 + derived_from: tosca.nodetypes.Root + properties: + provider: + type: string + requred: false + participant_id: + type: onap.datatypes.ToscaConceptIdentifier + requred: true + org.onap.policy.clamp.controlloop.ControlLoop: + version: 1.0.1 + derived_from: tosca.nodetypes.Root + properties: + provider: + type: string + requred: false + elements: + type: list + required: true + entry_schema: + type: onap.datatypes.ToscaConceptIdentifier + org.onap.policy.clamp.controlloop.HttpControlLoopElement: + version: 1.0.1 + derived_from: org.onap.policy.clamp.controlloop.ControlLoopElement + properties: + baseUrl: + type: string + required: true + description: The base URL to be prepended to each path, identifies the host for the REST endpoints. + httpHeaders: + type: map + required: false + entry_schema: + type: string + description: HTTP headers to send on REST requests + configurationEntities: + type: map + required: true + entry_schema: + type: org.onap.datatypes.policy.clamp.controlloop.httpControlLoopElement.ConfigurationEntity + typeVersion: 1.0.0 + description: The connfiguration entities the Control Loop Element is managing and their associated REST requests +topology_template: + node_templates: + org.onap.k8s.controlloop.HttpControlLoopParticipant: + version: 2.3.4 + type: org.onap.policy.clamp.controlloop.Participant + type_version: 1.0.1 + description: Participant for Http requests + properties: + provider: ONAP + + org.onap.domain.database.Http_PMSHMicroserviceControlLoopElement: + # Http config for PMSH. + version: 1.2.3 + type: org.onap.policy.clamp.controlloop.HttpControlLoopElement + type_version: 1.0.1 + description: Control loop element for the http requests of PMSH microservice + properties: + provider: ONAP + participant_id: + name: org.onap.controlloop.HttpControlLoopParticipant + version: 2.3.4 + uninitializedToPassiveTimeout: 180 + baseUrl: https://httpbin.org + httpHeaders: + Content-Type: application/json + configurationEntities: + - configurationEntityId: + name: entity1 + version: 1.0.1 + restSequence: + - restRequestId: + name: request1 + version: 1.0.1 + httpMethod: POST + path: post + body: '{"name":"ob1","port_type"}' + expectedResponse: 200 + + - restRequestId: + name: request1 + version: 1.0.1 + httpMethod: GET + path: get + expectedResponse: 200 + + # Rest path with path params and query params + - configurationEntityId: + name: entity2 + version: 1.0.1 + restSequence: + - restRequestId: + name: request1 + version: 1.0.1 + httpMethod: POST + path: post/{id}/{name} + pathParams: + id: 123 + name: dummyName + body: this is a test body + expectedResponse: 200 + + - restRequestId: + name: request1 + version: 1.0.1 + httpMethod: GET + path: get + queryParams: + id: 123 + name: dummyName + expectedResponse: 200 + + + org.onap.domain.sample.GenericK8s_ControlLoopDefinition: + version: 1.2.3 + type: org.onap.policy.clamp.controlloop.ControlLoop + type_version: 1.0.0 + description: Control loop for HTTP request + properties: + provider: ONAP + elements: + - name: org.onap.domain.database.Http_PMSHMicroserviceControlLoopElement + version: 1.2.3 diff --git a/participant/participant-impl/pom.xml b/participant/participant-impl/pom.xml index f1f4f0eb9..439bdea90 100644 --- a/participant/participant-impl/pom.xml +++ b/participant/participant-impl/pom.xml @@ -40,6 +40,7 @@ <module>participant-impl-policy</module> <module>participant-impl-cds</module> <module>participant-impl-kubernetes</module> + <module>participant-impl-http</module> </modules> <dependencies> @@ -201,5 +201,9 @@ <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-webflux</artifactId> + </dependency> </dependencies> </project> |