/*-
* ============LICENSE_START=======================================================
* Copyright (C) 2016-2018 Ericsson. All rights reserved.
* Modifications Copyright (C) 2019-2021, 2023 Nordix Foundation.
* Modifications Copyright (C) 2021 AT&T Intellectual Property. 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.
*
* SPDX-License-Identifier: Apache-2.0
* ============LICENSE_END=========================================================
*/
package org.onap.policy.apex.service.parameters.carriertechnology;
import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.onap.policy.common.parameters.BeanValidationResult;
import org.onap.policy.common.parameters.ObjectValidationResult;
import org.onap.policy.common.parameters.ValidationResult;
import org.onap.policy.common.parameters.ValidationStatus;
import org.onap.policy.common.utils.validation.ParameterValidationUtils;
import org.onap.policy.models.base.Validated;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// @formatter:off
/**
* Apex plugin parameters for REST as an event carrier technology with Apex.
*
*
The parameters for this plugin are:
*
* - url: The URL that the Apex Rest client will connect to over REST for event reception or event sending. This
* parameter is mandatory.
*
- httpMethod: The HTTP method to use when sending events over REST, legal values are POST (default) and PUT. When
* receiving events, the REST client plugin always uses the HTTP GET method.
*
- httpHeaders, the HTTP headers to send on REST requests, optional parameter, defaults to none.
*
- httpCodeFilter: a regular expression filter for returned HTTP codes, if the returned HTTP code passes this
* filter, then the request is assumed to have succeeded by the plugin, optional, defaults to allowing 2xx codes
* through, that is a regular expression of "[2][0-9][0-9]"
*
*
* @author Ning Xi(ning.xi@ericsson.com)
*/
//@formatter:on
@Setter
@Getter
@NoArgsConstructor
public class RestPluginCarrierTechnologyParameters extends CarrierTechnologyParameters {
// Get a reference to the logger
private static final Logger LOGGER = LoggerFactory.getLogger(RestPluginCarrierTechnologyParameters.class);
/** The supported HTTP methods. */
// @formatter:off
public enum HttpMethod {
GET,
PUT,
POST,
DELETE
}
// @formatter:on
/** The default HTTP code filter, allows 2xx HTTP codes through. */
public static final String DEFAULT_HTTP_CODE_FILTER = "[2][0-9][0-9]";
// Commonly occurring strings
private static final String HTTP_HEADERS = "httpHeaders";
private static final String HTTP_CODE_FILTER = "httpCodeFilter";
// Regular expression patterns for finding and checking keys in URLs
private static final Pattern patternProperKey = Pattern.compile("(?<=\\{)[^}]*(?=\\})");
protected static final Pattern patternErrorKey = Pattern
.compile("(\\{[^\\{}]*.?\\{)|(\\{[^\\{}]*$)|(\\}[^\\{}]*.?\\})|(^[^\\{}]*.?\\})|\\{\\s*\\}");
// variable
protected String url = null;
protected HttpMethod httpMethod = null;
protected String[][] httpHeaders = null;
protected String httpCodeFilter = DEFAULT_HTTP_CODE_FILTER;
/**
* Check if http headers have been set for the REST request.
*
* @return true if headers have been set
*/
public boolean checkHttpHeadersSet() {
return httpHeaders != null && httpHeaders.length > 0;
}
/**
* Gets the http headers for the REST request as a multivalued map.
*
* @return the headers
*/
public MultivaluedMap getHttpHeadersAsMultivaluedMap() {
if (httpHeaders == null) {
return null;
}
// Load the HTTP headers into the map
MultivaluedMap httpHeaderMap = new MultivaluedHashMap<>();
for (String[] httpHeader : httpHeaders) {
httpHeaderMap.putSingle(httpHeader[0], httpHeader[1]);
}
return httpHeaderMap;
}
/**
* Sets the header for the REST request.
*
* @param httpHeaders the incoming HTTP headers
*/
public void setHttpHeaders(final String[][] httpHeaders) {
this.httpHeaders = httpHeaders;
}
/**
* Get the tag for the REST Producer Properties.
*
* @return set of the tags
*/
public Set getKeysFromUrl() {
Matcher matcher = patternProperKey.matcher(getUrl());
Set key = new HashSet<>();
while (matcher.find()) {
key.add(matcher.group());
}
return key;
}
/**
* {@inheritDoc}.
*/
@Override
public BeanValidationResult validate() {
BeanValidationResult result = super.validate();
result.addResult(validateUrl());
result.addResult(validateHttpHeaders());
result.addResult(validateHttpCodeFilter());
return result;
}
// @formatter:off
/**
* Validate the URL.
*
* Checks:
*
http://www.blah.com/{par1/somethingelse (Missing end tag) use {[^\\{}]*$
*
http://www.blah.com/{par1/{some}thingelse (Nested tag) use {[^}]*{
*
http://www.blah.com/{par1}/some}thingelse (Missing start tag1) use }[^{}]*.}
*
http://www.blah.com/par1}/somethingelse (Missing start tag2) use }[^{}]*}
*
http://www.blah.com/{}/somethingelse (Empty tag) use {[\s]*}
*/
// @formatter:on
public ValidationResult validateUrl() {
// The URL may be optional so existence must be checked in the plugin code
String url2 = getUrl();
if (url2 == null) {
return null;
}
var matcher = patternErrorKey.matcher(url2);
if (matcher.find()) {
final String urlInvalidMessage = "invalid URL has been set for event sending on " + getLabel();
return new ObjectValidationResult("url", url2, ValidationStatus.INVALID, urlInvalidMessage);
}
return null;
}
/**
* Validate the HTTP headers.
*
* @return the result of the validation
*/
private ValidationResult validateHttpHeaders() {
if (httpHeaders == null) {
return null;
}
var result = new BeanValidationResult(HTTP_HEADERS, httpHeaders);
var item = 0;
for (String[] httpHeader : httpHeaders) {
final String label = "entry " + (item++);
final List value = (httpHeader == null ? null : Arrays.asList(httpHeader));
var result2 = new BeanValidationResult(label, value);
if (httpHeader == null) {
// note: add to result, not result2
result.addResult(label, null, ValidationStatus.INVALID, Validated.IS_NULL);
} else if (httpHeader.length != 2) {
// note: add to result, not result2
result.addResult(label, value, ValidationStatus.INVALID, "must have one key and one value");
} else if (!ParameterValidationUtils.validateStringParameter(httpHeader[0])) {
result2.addResult("key", httpHeader[0], ValidationStatus.INVALID, Validated.IS_BLANK);
} else if (!ParameterValidationUtils.validateStringParameter(httpHeader[1])) {
result2.addResult("value", httpHeader[1], ValidationStatus.INVALID, Validated.IS_BLANK);
}
result.addResult(result2);
}
return result;
}
/**
* Validate the HTTP code filter.
*/
public ValidationResult validateHttpCodeFilter() {
if (httpCodeFilter == null) {
httpCodeFilter = DEFAULT_HTTP_CODE_FILTER;
} else if (StringUtils.isBlank(httpCodeFilter)) {
return new ObjectValidationResult(HTTP_CODE_FILTER, httpCodeFilter, ValidationStatus.INVALID,
"must be a three digit regular expression");
} else {
try {
Pattern.compile(httpCodeFilter);
} catch (PatternSyntaxException pse) {
LOGGER.debug("Invalid HTTP code filter", pse);
String message = "Invalid HTTP code filter, the filter must be specified as a three digit "
+ "regular expression: " + pse.getMessage();
return new ObjectValidationResult(HTTP_CODE_FILTER, httpCodeFilter, ValidationStatus.INVALID,
message);
}
}
return null;
}
/**
* {@inheritDoc}.
*/
@Override
public String toString() {
return getLabel() + "CarrierTechnologyParameters [url=" + url + ", httpMethod=" + httpMethod + ", httpHeaders="
+ Arrays.deepToString(httpHeaders) + ", httpCodeFilter=" + httpCodeFilter + "]";
}
}