/*
* SoapUI, Copyright (C) 2004-2016 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.impl.rest.support;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.rest.RestRequestInterface;
import com.eviware.soapui.impl.rest.RestResource;
import com.eviware.soapui.impl.rest.support.RestParamsPropertyHolder.ParameterStyle;
import com.eviware.soapui.model.ModelItem;
import com.eviware.soapui.model.propertyexpansion.DefaultPropertyExpansionContext;
import com.eviware.soapui.model.propertyexpansion.PropertyExpander;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.Tools;
import com.eviware.soapui.support.types.StringList;
import org.apache.xmlbeans.XmlBoolean;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.eviware.soapui.impl.support.HttpUtils.urlEncodeWithUtf8;
public class RestUtils {
public static enum TemplateExtractionOption {
EXTRACT_TEMPLATE_PARAMETERS, IGNORE_TEMPLATE_PARAMETERS
}
public static String[] extractTemplateParams(String path) {
if (StringUtils.isNullOrEmpty(path)) {
return new String[0];
}
StringList result = new StringList();
int ix = path.indexOf('{');
while (ix != -1) {
int endIx = path.indexOf('}', ix);
if (endIx == -1) {
break;
}
if (endIx > ix + 1 && (ix > 0 && path.charAt(ix - 1) != '$')) {
result.add(path.substring(ix + 1, endIx));
}
ix = path.indexOf('{', ix + 1);
}
return result.toStringArray();
}
public static String extractParams(String pathOrEndpoint, RestParamsPropertyHolder params, boolean keepHost) {
return extractParams(pathOrEndpoint, params, keepHost, TemplateExtractionOption.EXTRACT_TEMPLATE_PARAMETERS);
}
public static String extractParams(String pathOrEndpoint, RestParamsPropertyHolder params, boolean keepHost, TemplateExtractionOption templateExtractionOptions) {
if (StringUtils.isNullOrEmpty(pathOrEndpoint)) {
return "";
}
String path = pathOrEndpoint;
String queryString = "";
URL url = null;
try {
url = new URL(pathOrEndpoint);
path = url.getPath();
queryString = url.getQuery();
} catch (MalformedURLException e) {
int ix = path.indexOf('?');
if (ix >= 0) {
queryString = path.substring(ix + 1);
path = path.substring(0, ix);
}
}
String[] items = path.split("/");
StringBuilder resultPath = new StringBuilder();
for (String item : items) {
try {
if (templateExtractionOptions == TemplateExtractionOption.EXTRACT_TEMPLATE_PARAMETERS) {
int openCurlyIndex = item.indexOf("{");
int closingCurlyIndex = item.indexOf("}");
if (openCurlyIndex != -1 && closingCurlyIndex > openCurlyIndex) {
String name = item.substring(openCurlyIndex + 1, closingCurlyIndex);
RestParamProperty property = params.getProperty(name);
if (!params.hasProperty(name)) {
property = params.addProperty(name);
}
property.setStyle(ParameterStyle.TEMPLATE);
property.setValue(name);
property.setDefaultValue(name);
}
}
String[] matrixParams = item.split(";");
if (matrixParams.length > 0) {
item = matrixParams[0];
for (int c = 1; c < matrixParams.length; c++) {
String matrixParam = matrixParams[c];
int ix = matrixParam.indexOf('=');
if (ix == -1) {
String name = URLDecoder.decode(matrixParam, "Utf-8");
if (!params.hasProperty(name)) {
params.addProperty(name).setStyle(ParameterStyle.MATRIX);
}
} else {
String name = URLDecoder.decode(matrixParam.substring(0, ix), "Utf-8");
RestParamProperty property = params.getProperty(name);
if (property == null) {
property = params.addProperty(name);
}
property.setStyle(ParameterStyle.MATRIX);
property.setValue(URLDecoder.decode(matrixParam.substring(ix + 1), "Utf-8"));
property.setDefaultValue(URLDecoder.decode(matrixParam.substring(ix + 1), "Utf-8"));
}
}
}
} catch (Exception e) {
SoapUI.logError(e, "Couldn't parse the template/matrix parameters from URI");
}
if (StringUtils.hasContent(item)) {
resultPath.append('/').append(item);
}
}
if (StringUtils.hasContent(queryString)) {
extractParamsFromQueryString(params, queryString);
}
if (path.endsWith("/")) {
resultPath.append('/');
}
if (keepHost && url != null) {
return Tools.getEndpointFromUrl(url) + resultPath.toString();
}
return resultPath.toString();
}
public static void extractParamsFromQueryString(RestParamsPropertyHolder params, String queryString) {
String[] items;
items = queryString.split("&");
for (String item : items) {
try {
int ix = item.indexOf('=');
if (ix == -1) {
String name = URLDecoder.decode(item, "Utf-8");
if (!params.hasProperty(name)) {
params.addProperty(name).setStyle(ParameterStyle.QUERY);
}
} else {
String name = URLDecoder.decode(item.substring(0, ix), "Utf-8");
RestParamProperty property = params.getProperty(name);
if (property == null) {
property = params.addProperty(name);
}
property.setStyle(ParameterStyle.QUERY);
property.setValue(URLDecoder.decode(item.substring(ix + 1), "Utf-8"));
property.setDefaultValue(URLDecoder.decode(item.substring(ix + 1), "Utf-8"));
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
public static String expandPath(String path, RestParamsPropertyHolder params, RestRequestInterface request) {
DefaultPropertyExpansionContext context = new DefaultPropertyExpansionContext(request);
for (int c = 0; c < params.getPropertyCount(); c++) {
RestParamProperty param = params.getPropertyAt(c);
String value = PropertyExpander.expandProperties(context, param.getValue());
if (!StringUtils.hasContent(value) && !param.getRequired()) {
continue;
}
if (value != null && !param.isDisableUrlEncoding()) {
try {
String encoding = System.getProperty("soapui.request.encoding", request.getEncoding());
encoding = StringUtils.hasContent(encoding) ? encoding : Charset.defaultCharset().toString();
value = URLEncoder.encode(value, encoding);
} catch (UnsupportedEncodingException e1) {
SoapUI.logError(e1);
value = urlEncodeWithUtf8(value);
}
}
if (param.getStyle() == ParameterStyle.TEMPLATE) {
path = path.replaceAll("\\{" + param.getName() + "\\}", value);
}
}
return path + makeSuffixParameterString(request);
}
/**
* Build the parameter string to be appended to the path of an REST request
*
* @param request an object representing the REST request
* @return the full parameter string, including matrix style parameters and query string.
*/
public static String makeSuffixParameterString(RestRequestInterface request) {
return makeMatrixParameterString(request.getParams()) + getQueryParamsString(request);
}
private static String makeMatrixParameterString(RestParamsPropertyHolder params) {
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < params.getPropertyCount(); i++) {
RestParamProperty param = params.getPropertyAt(i);
String value = param.getValue();
if (param.getStyle() == ParameterStyle.MATRIX) {
if (param.getType().equals(XmlBoolean.type.getName())) {
if (value.toUpperCase().equals("TRUE") || value.equals("1")) {
buffer.append(";").append(param.getName());
}
} else {
buffer.append(";").append(param.getName());
if (StringUtils.hasContent(value)) {
buffer.append("=").append(value);
}
}
}
}
return buffer.toString();
}
public static String getQueryParamsString(RestRequestInterface request) {
if (isRequestWithoutQueryString(request)) {
return "";
}
RestParamsPropertyHolder params = request.getParams();
StringBuilder query = new StringBuilder();
for (int c = 0; c < params.getPropertyCount(); c++) {
RestParamProperty param = params.getPropertyAt(c);
String value = param.getValue();
List<String> valueParts = splitMultipleParameters(value, request.getMultiValueDelimiter());
if (param.getStyle() != ParameterStyle.QUERY || (valueParts.isEmpty() && !param.getRequired())) {
continue;
}
for (String valuePart : valueParts) {
if (query.length() > 0) {
query.append('&');
}
query.append(param.getName()).append('=');
if (StringUtils.hasContent(valuePart)) {
query.append(valuePart);
}
}
}
return (query.length() > 0 ? "?" : "") + query.toString();
}
private static boolean isRequestWithoutQueryString(RestRequestInterface request) {
return request.isPostQueryString() || "multipart/form-data".equals(request.getMediaType())
|| "multipart/mixed".equals(request.getMediaType());
}
public static List<String> splitMultipleParameters(String paramStr, String delimiter) {
StringList result = new StringList();
if (StringUtils.hasContent(paramStr)) {
if (!StringUtils.hasContent(delimiter)) {
result.add(paramStr);
} else {
result.addAll(paramStr.split(delimiter));
}
}
return result;
}
/**
* specificaly used for adding empty parameters also in the list when
* "send empty parameters" are checked in HTTP TestRequest Properties
*
* @param paramStr
* @param delimiter
* @return
*/
public static List<String> splitMultipleParametersEmptyIncluded(String paramStr, String delimiter) {
StringList result = new StringList();
if (!StringUtils.hasContent(delimiter)) {
result.add(paramStr);
} else {
result.addAll(paramStr.split(delimiter));
}
return result;
}
public static List<RestResource> extractAncestorsParentFirst(RestResource childResource) {
final List<RestResource> resources = new ArrayList<RestResource>();
for (RestResource r = childResource; r != null; r = r.getParentResource()) {
resources.add(r);
}
Collections.reverse(resources);
return resources;
}
public static String getExpandedPath(String path, RestParamsPropertyHolder params, ModelItem context) {
String expandedPath = path;
expandedPath = PropertyExpander.expandProperties(context, expandedPath);
for (String pathParam : RestUtils.extractTemplateParams(expandedPath)) {
String pathParamValue = params.getPropertyValue(pathParam);
pathParamValue = PropertyExpander.expandProperties(context, pathParamValue);
expandedPath = expandedPath.replaceAll("\\{" + pathParam + "\\}", pathParamValue == null ? "" : pathParamValue);
}
return expandedPath;
}
}