/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you 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. */ package org.elasticsearch.test.rest.yaml; import com.carrotsearch.randomizedtesting.RandomizedTest; import org.apache.http.HttpEntity; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.BytesRef; import org.elasticsearch.Version; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Execution context passed across the REST tests. * Holds the REST client used to communicate with elasticsearch. * Caches the last obtained test response and allows to stash part of it within variables * that can be used as input values in following requests. */ public class ClientYamlTestExecutionContext { private static final Logger logger = Loggers.getLogger(ClientYamlTestExecutionContext.class); private static final XContentType[] STREAMING_CONTENT_TYPES = new XContentType[]{XContentType.JSON, XContentType.SMILE}; private final Stash stash = new Stash(); private final ClientYamlTestClient clientYamlTestClient; private ClientYamlTestResponse response; private final boolean randomizeContentType; ClientYamlTestExecutionContext(ClientYamlTestClient clientYamlTestClient, boolean randomizeContentType) { this.clientYamlTestClient = clientYamlTestClient; this.randomizeContentType = randomizeContentType; } /** * Calls an elasticsearch api with the parameters and request body provided as arguments. * Saves the obtained response in the execution context. */ public ClientYamlTestResponse callApi(String apiName, Map<String, String> params, List<Map<String, Object>> bodies, Map<String, String> headers) throws IOException { //makes a copy of the parameters before modifying them for this specific request Map<String, String> requestParams = new HashMap<>(params); requestParams.putIfAbsent("error_trace", "true"); // By default ask for error traces, this my be overridden by params for (Map.Entry<String, String> entry : requestParams.entrySet()) { if (stash.containsStashedValue(entry.getValue())) { entry.setValue(stash.getValue(entry.getValue()).toString()); } } //make a copy of the headers before modifying them for this specific request Map<String, String> requestHeaders = new HashMap<>(headers); for (Map.Entry<String, String> entry : requestHeaders.entrySet()) { if (stash.containsStashedValue(entry.getValue())) { entry.setValue(stash.getValue(entry.getValue()).toString()); } } HttpEntity entity = createEntity(bodies, requestHeaders); try { response = callApiInternal(apiName, requestParams, entity, requestHeaders); return response; } catch(ClientYamlTestResponseException e) { response = e.getRestTestResponse(); throw e; } finally { // if we hit a bad exception the response is null Object responseBody = response != null ? response.getBody() : null; //we always stash the last response body stash.stashValue("body", responseBody); } } private HttpEntity createEntity(List<Map<String, Object>> bodies, Map<String, String> headers) throws IOException { if (bodies.isEmpty()) { return null; } if (bodies.size() == 1) { XContentType xContentType = getContentType(headers, XContentType.values()); BytesRef bytesRef = bodyAsBytesRef(bodies.get(0), xContentType); return new ByteArrayEntity(bytesRef.bytes, bytesRef.offset, bytesRef.length, ContentType.create(xContentType.mediaTypeWithoutParameters(), StandardCharsets.UTF_8)); } else { XContentType xContentType = getContentType(headers, STREAMING_CONTENT_TYPES); List<BytesRef> bytesRefList = new ArrayList<>(bodies.size()); int totalBytesLength = 0; for (Map<String, Object> body : bodies) { BytesRef bytesRef = bodyAsBytesRef(body, xContentType); bytesRefList.add(bytesRef); totalBytesLength += bytesRef.length - bytesRef.offset + 1; } byte[] bytes = new byte[totalBytesLength]; int position = 0; for (BytesRef bytesRef : bytesRefList) { for (int i = bytesRef.offset; i < bytesRef.length; i++) { bytes[position++] = bytesRef.bytes[i]; } bytes[position++] = xContentType.xContent().streamSeparator(); } return new ByteArrayEntity(bytes, ContentType.create(xContentType.mediaTypeWithoutParameters(), StandardCharsets.UTF_8)); } } private XContentType getContentType(Map<String, String> headers, XContentType[] supportedContentTypes) { XContentType xContentType = null; String contentType = headers.get("Content-Type"); if (contentType != null) { xContentType = XContentType.fromMediaType(contentType); } if (xContentType != null) { return xContentType; } if (randomizeContentType) { return RandomizedTest.randomFrom(supportedContentTypes); } return XContentType.JSON; } private BytesRef bodyAsBytesRef(Map<String, Object> bodyAsMap, XContentType xContentType) throws IOException { Map<String, Object> finalBodyAsMap = stash.replaceStashedValues(bodyAsMap); try (XContentBuilder builder = XContentFactory.contentBuilder(xContentType)) { return builder.map(finalBodyAsMap).bytes().toBytesRef(); } } // pkg-private for testing ClientYamlTestResponse callApiInternal(String apiName, Map<String, String> params, HttpEntity entity, Map<String, String> headers) throws IOException { return clientYamlTestClient.callApi(apiName, params, entity, headers); } /** * Extracts a specific value from the last saved response */ public Object response(String path) throws IOException { return response.evaluate(path, stash); } /** * Clears the last obtained response and the stashed fields */ public void clear() { logger.debug("resetting client, response and stash"); response = null; stash.clear(); } public Stash stash() { return stash; } /** * Returns the current es version as a string */ public Version esVersion() { return clientYamlTestClient.getEsVersion(); } }