2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2024 Nordix Foundation
4 * Modifications Copyright (C) 2022 Bell Canada
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.cps.ncmp.api.impl.client;
24 import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING;
25 import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA;
26 import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR;
27 import static org.springframework.http.HttpStatus.BAD_REQUEST;
28 import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
30 import com.fasterxml.jackson.databind.JsonNode;
32 import java.net.URISyntaxException;
33 import java.util.Locale;
34 import lombok.RequiredArgsConstructor;
35 import lombok.extern.slf4j.Slf4j;
36 import org.onap.cps.ncmp.api.impl.config.DmiWebClientConfiguration.DmiProperties;
37 import org.onap.cps.ncmp.api.impl.exception.DmiClientRequestException;
38 import org.onap.cps.ncmp.api.impl.exception.InvalidDmiResourceUrlException;
39 import org.onap.cps.ncmp.api.impl.operations.OperationType;
40 import org.onap.cps.utils.JsonObjectMapper;
41 import org.springframework.http.HttpHeaders;
42 import org.springframework.http.HttpStatus;
43 import org.springframework.http.ResponseEntity;
44 import org.springframework.stereotype.Component;
45 import org.springframework.web.client.HttpServerErrorException;
46 import org.springframework.web.reactive.function.BodyInserters;
47 import org.springframework.web.reactive.function.client.WebClient;
48 import org.springframework.web.reactive.function.client.WebClientResponseException;
51 @RequiredArgsConstructor
53 public class DmiRestClient {
55 private static final String HEALTH_CHECK_URL_EXTENSION = "/actuator/health";
56 private static final String NOT_SPECIFIED = "";
57 private static final String NO_AUTHORIZATION = null;
58 private final WebClient webClient;
59 private final DmiProperties dmiProperties;
60 private final JsonObjectMapper jsonObjectMapper;
63 * Sends POST operation to DMI with json body containing module references.
65 * @param dmiResourceUrl dmi resource url
66 * @param requestBodyAsJsonString json data body
67 * @param operationType the type of operation being executed (for error reporting only)
68 * @param authorization contents of Authorization header, or null if not present
69 * @return response entity of type String
71 public ResponseEntity<Object> postOperationWithJsonData(final String dmiResourceUrl,
72 final String requestBodyAsJsonString,
73 final OperationType operationType,
74 final String authorization) {
76 return ResponseEntity.ok(webClient.post().uri(toUri(dmiResourceUrl))
77 .headers(httpHeaders -> configureHttpHeaders(httpHeaders, authorization))
78 .body(BodyInserters.fromValue(requestBodyAsJsonString))
80 .bodyToMono(Object.class)
81 .onErrorMap(httpError -> handleDmiClientException(httpError, operationType.getOperationName()))
83 } catch (final HttpServerErrorException e) {
84 throw handleDmiClientException(e, operationType.getOperationName());
89 * Get DMI plugin health status.
91 * @param dmiPluginBaseUrl the base URL of the dmi-plugin
92 * @return plugin health status ("UP" is all OK, "" (not-specified) in case of any exception)
94 public String getDmiHealthStatus(final String dmiPluginBaseUrl) {
96 final JsonNode responseHealthStatus = webClient.get()
97 .uri(toUri(dmiPluginBaseUrl + HEALTH_CHECK_URL_EXTENSION))
98 .headers(httpHeaders -> configureHttpHeaders(httpHeaders, NO_AUTHORIZATION))
100 .bodyToMono(JsonNode.class).block();
101 return responseHealthStatus == null ? NOT_SPECIFIED :
102 responseHealthStatus.get("status").asText();
103 } catch (final Exception e) {
104 log.warn("Failed to retrieve health status from {}. Error Message: {}", dmiPluginBaseUrl, e.getMessage());
105 return NOT_SPECIFIED;
109 private HttpHeaders configureHttpHeaders(final HttpHeaders httpHeaders, final String authorization) {
110 if (dmiProperties.isDmiBasicAuthEnabled()) {
111 httpHeaders.setBasicAuth(dmiProperties.getAuthUsername(), dmiProperties.getAuthPassword());
112 } else if (authorization != null && authorization.toLowerCase(Locale.getDefault()).startsWith("bearer ")) {
113 httpHeaders.add(HttpHeaders.AUTHORIZATION, authorization);
118 private static URI toUri(final String dmiResourceUrl) {
120 return new URI(dmiResourceUrl);
121 } catch (final URISyntaxException e) {
122 throw new InvalidDmiResourceUrlException(dmiResourceUrl, BAD_REQUEST.value());
126 private DmiClientRequestException handleDmiClientException(final Throwable e, final String operationType) {
127 final String exceptionMessage = "Unable to " + operationType + " resource data.";
128 if (e instanceof WebClientResponseException wcre) {
129 if (wcre.getStatusCode().isSameCodeAs(HttpStatus.REQUEST_TIMEOUT)) {
130 throw new DmiClientRequestException(wcre.getStatusCode().value(), wcre.getMessage(),
131 jsonObjectMapper.asJsonString(wcre.getResponseBodyAsString()), DMI_SERVICE_NOT_RESPONDING);
133 throw new DmiClientRequestException(wcre.getStatusCode().value(), wcre.getMessage(),
134 jsonObjectMapper.asJsonString(wcre.getResponseBodyAsString()), UNABLE_TO_READ_RESOURCE_DATA);
137 if (e instanceof HttpServerErrorException httpServerErrorException) {
138 throw new DmiClientRequestException(httpServerErrorException.getStatusCode().value(), exceptionMessage,
139 httpServerErrorException.getResponseBodyAsString(), DMI_SERVICE_NOT_RESPONDING);
141 throw new DmiClientRequestException(INTERNAL_SERVER_ERROR.value(), exceptionMessage, e.getMessage(),