Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.intuit.karate.ScenarioContext Maven / Gradle / Ivy
/*
* The MIT License
*
* Copyright 2017 Intuit Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.intuit.karate;
import com.intuit.karate.core.ExecutionHook;
import com.intuit.karate.core.FeatureResult;
import com.intuit.karate.cucumber.ScenarioInfo;
import com.intuit.karate.exception.KarateException;
import com.intuit.karate.exception.KarateFileNotFoundException;
import com.intuit.karate.http.Cookie;
import com.intuit.karate.http.HttpClient;
import com.intuit.karate.http.HttpConfig;
import com.intuit.karate.http.HttpRequest;
import com.intuit.karate.http.HttpRequestBuilder;
import com.intuit.karate.http.HttpResponse;
import com.intuit.karate.http.HttpUtils;
import com.intuit.karate.http.MultiPartItem;
import com.intuit.karate.validator.Validator;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
/**
*
* @author pthomas3
*/
public class ScenarioContext {
public final Logger logger;
public final ScriptBindings bindings;
public final int callDepth;
public final List tags;
public final Map> tagValues;
public final ScriptValueMap vars;
public final FeatureContext featureContext;
public final ExecutionHook executionHook;
public final boolean useLogAppenderFile;
public final ScenarioInfo scenarioInfo;
// these can get re-built or swapped, so cannot be final
private HttpClient client;
private HttpConfig config;
private HttpRequestBuilder request = new HttpRequestBuilder();
private HttpResponse response;
// the actual http request last sent on the wire
private HttpRequest prevRequest;
// pass call result to engine via this variable (hack)
private List callResults;
public List getCallResults() {
return callResults;
}
public void setCallResults(List callResults) {
this.callResults = callResults;
}
public void addCallResult(FeatureResult callResult) {
if (callResults == null) {
callResults = new ArrayList();
}
callResults.add(callResult);
}
public void setScenarioError(Throwable error) {
scenarioInfo.setErrorMessage(error.getMessage());
}
public void setPrevRequest(HttpRequest prevRequest) {
this.prevRequest = prevRequest;
}
public HttpRequestBuilder getRequest() {
return request;
}
public HttpRequest getPrevRequest() {
return prevRequest;
}
public HttpClient getHttpClient() {
return client;
}
public int getCallDepth() {
return callDepth;
}
public FeatureContext getFeatureContext() {
return featureContext;
}
public ScriptValueMap getVars() {
return vars;
}
public HttpConfig getConfig() {
return config;
}
public void updateConfigCookies(Map cookies) {
if (cookies == null) {
return;
}
if (config.getCookies().isNull()) {
config.setCookies(new ScriptValue(cookies));
} else {
Map map = config.getCookies().evalAsMap(this);
map.putAll(cookies);
config.setCookies(new ScriptValue(map));
}
}
public boolean isPrintEnabled() {
return config.isPrintEnabled();
}
public ScenarioContext(FeatureContext featureContext, CallContext call) {
this.featureContext = featureContext; // make sure references below to env.env use the updated one
logger = featureContext.logger;
callDepth = call.callDepth;
executionHook = call.executionHook;
useLogAppenderFile = call.useLogAppenderFile;
tags = call.getTags().getTags();
tagValues = call.getTags().getTagValues();
scenarioInfo = call.getScenarioInfo();
if (call.reuseParentContext) {
vars = call.parentContext.vars; // shared context !
config = call.parentContext.config;
} else if (call.parentContext != null) {
vars = call.parentContext.vars.copy();
config = new HttpConfig(call.parentContext.config);
} else {
vars = new ScriptValueMap();
config = new HttpConfig();
config.setClientClass(call.httpClientClass);
}
client = HttpClient.construct(config, this);
bindings = new ScriptBindings(this);
if (call.parentContext == null && call.evalKarateConfig) {
// base config is only looked for in the classpath
try {
Script.callAndUpdateConfigAndAlsoVarsIfMapReturned(false, ScriptBindings.READ_KARATE_CONFIG_BASE, null, this);
} catch (Exception e) {
if (e instanceof KarateFileNotFoundException) {
logger.trace("skipping 'classpath:karate-base.js': {}", e.getMessage());
} else {
throw new RuntimeException("evaluation of 'classpath:karate-base.js' failed", e);
}
}
String configDir = System.getProperty(ScriptBindings.KARATE_CONFIG_DIR);
String configScript = ScriptBindings.readKarateConfigForEnv(true, configDir, null);
try {
Script.callAndUpdateConfigAndAlsoVarsIfMapReturned(false, configScript, null, this);
} catch (Exception e) {
if (e instanceof KarateFileNotFoundException) {
logger.warn("skipping bootstrap configuration: {}", e.getMessage());
} else {
throw new RuntimeException("evaluation of '" + ScriptBindings.KARATE_CONFIG_JS + "' failed", e);
}
}
if (featureContext.env != null) {
configScript = ScriptBindings.readKarateConfigForEnv(false, configDir, featureContext.env);
try {
Script.callAndUpdateConfigAndAlsoVarsIfMapReturned(false, configScript, null, this);
} catch (Exception e) {
if (e instanceof KarateFileNotFoundException) {
logger.trace("skipping bootstrap configuration for env: {} - {}", featureContext.env, e.getMessage());
} else {
throw new RuntimeException("evaluation of 'karate-config-" + featureContext.env + ".js' failed", e);
}
}
}
}
if (call.callArg != null) { // if call.reuseParentContext is true, arg will clobber parent context
for (Map.Entry entry : call.callArg.entrySet()) {
vars.put(entry.getKey(), entry.getValue());
}
vars.put(Script.VAR_ARG, call.callArg);
vars.put(Script.VAR_LOOP, call.loopIndex);
} else if (call.parentContext != null) {
vars.put(Script.VAR_ARG, ScriptValue.NULL);
vars.put(Script.VAR_LOOP, -1);
}
logger.trace("karate context init - initial properties: {}", vars);
}
public void configure(HttpConfig config) {
this.config = config;
client = HttpClient.construct(config, this);
}
public void configure(String key, ScriptValue value) { // TODO use enum
key = StringUtils.trimToEmpty(key);
if (key.equals("headers")) {
config.setHeaders(value);
return;
}
if (key.equals("cookies")) {
config.setCookies(value);
return;
}
if (key.equals("responseHeaders")) {
config.setResponseHeaders(value);
return;
}
if (key.equals("lowerCaseResponseHeaders")) {
config.setLowerCaseResponseHeaders(value.isBooleanTrue());
return;
}
if (key.equals("cors")) {
config.setCorsEnabled(value.isBooleanTrue());
return;
}
if (key.equals("logPrettyResponse")) {
config.setLogPrettyResponse(value.isBooleanTrue());
return;
}
if (key.equals("logPrettyRequest")) {
config.setLogPrettyRequest(value.isBooleanTrue());
return;
}
if (key.equals("printEnabled")) {
config.setPrintEnabled(value.isBooleanTrue());
return;
}
if (key.equals("afterScenario")) {
config.setAfterScenario(value);
return;
}
if (key.equals("afterFeature")) {
config.setAfterFeature(value);
return;
}
if (key.equals("httpClientClass")) {
config.setClientClass(value.getAsString());
// re-construct all the things ! and we exit early
client = HttpClient.construct(config, this);
return;
}
if (key.equals("httpClientInstance")) {
config.setClientInstance(value.getValue(HttpClient.class));
// here too, re-construct client - and exit early
client = HttpClient.construct(config, this);
return;
}
if (key.equals("charset")) {
if (value.isNull()) {
config.setCharset(null);
} else {
config.setCharset(Charset.forName(value.getAsString()));
}
// here again, re-construct client - and exit early
client = HttpClient.construct(config, this);
return;
}
if (key.equals("report")) {
if (value.isMapLike()) {
Map map = value.getAsMap();
config.setLogEnabled((Boolean) map.get("logEnabled"));
config.setShowAllSteps((Boolean) map.get("showAllSteps"));
} else if (value.isBooleanTrue()) {
config.setLogEnabled(true);
config.setShowAllSteps(true);
} else {
config.setLogEnabled(false);
config.setShowAllSteps(false);
}
return;
}
// beyond this point, we don't exit early and we have to re-configure the http client
if (key.equals("ssl")) {
if (value.isString()) {
config.setSslEnabled(true);
config.setSslAlgorithm(value.getAsString());
} else if (value.isMapLike()) {
config.setSslEnabled(true);
Map map = value.getAsMap();
config.setSslKeyStore((String) map.get("keyStore"));
config.setSslKeyStorePassword((String) map.get("keyStorePassword"));
config.setSslKeyStoreType((String) map.get("keyStoreType"));
config.setSslTrustStore((String) map.get("trustStore"));
config.setSslTrustStorePassword((String) map.get("trustStorePassword"));
config.setSslTrustStoreType((String) map.get("trustStoreType"));
String trustAll = (String) map.get("trustAll");
if (trustAll != null) {
config.setSslTrustAll(Boolean.valueOf(trustAll));
}
config.setSslAlgorithm((String) map.get("algorithm"));
} else {
config.setSslEnabled(value.isBooleanTrue());
}
} else if (key.equals("followRedirects")) {
config.setFollowRedirects(value.isBooleanTrue());
} else if (key.equals("connectTimeout")) {
config.setConnectTimeout(Integer.valueOf(value.getAsString()));
} else if (key.equals("readTimeout")) {
config.setReadTimeout(Integer.valueOf(value.getAsString()));
} else if (key.equals("proxy")) {
if (value.isString()) {
config.setProxyUri(value.getAsString());
} else {
Map map = value.getAsMap();
config.setProxyUri((String) map.get("uri"));
config.setProxyUsername((String) map.get("username"));
config.setProxyPassword((String) map.get("password"));
config.setNonProxyHosts(((List) ((ScriptObjectMirror) map.get("nonProxyHosts")).values()));
}
} else if (key.equals("userDefined")) {
config.setUserDefined(value.getAsMap());
} else {
throw new RuntimeException("unexpected 'configure' key: '" + key + "'");
}
client.configure(config, this);
}
private List evalList(List values) {
List list = new ArrayList(values.size());
try {
for (String value : values) {
ScriptValue temp = Script.evalKarateExpression(value, this);
list.add(temp.getAsString());
}
} catch (Exception e) { // hack. for e.g. json with commas would land here
String joined = StringUtils.join(values, ',');
ScriptValue temp = Script.evalKarateExpression(joined, this);
if (temp.isListLike()) {
return temp.getAsList();
} else {
return Collections.singletonList(temp.getAsString());
}
}
return list;
}
private Map evalMapExpr(String expr) {
ScriptValue value = Script.evalKarateExpression(expr, this);
if (!value.isMapLike()) {
throw new KarateException("cannot convert to map: " + expr);
}
return value.getAsMap();
}
private String getVarAsString(String name) {
ScriptValue sv = vars.get(name);
if (sv == null) {
throw new RuntimeException("no variable found with name: " + name);
}
return sv.getAsString();
}
private static String asString(Map map, String key) {
Object o = map.get(key);
return o == null ? null : o.toString();
}
public void updateResponseVars(HttpResponse response) {
vars.put(ScriptValueMap.VAR_RESPONSE_STATUS, response.getStatus());
vars.put(ScriptValueMap.VAR_REQUEST_TIME_STAMP, response.getStartTime());
vars.put(ScriptValueMap.VAR_RESPONSE_TIME, response.getResponseTime());
vars.put(ScriptValueMap.VAR_RESPONSE_COOKIES, response.getCookies());
if (config.isLowerCaseResponseHeaders()) {
Object temp = new ScriptValue(response.getHeaders()).toLowerCase();
vars.put(ScriptValueMap.VAR_RESPONSE_HEADERS, temp);
} else {
vars.put(ScriptValueMap.VAR_RESPONSE_HEADERS, response.getHeaders());
}
byte[] responseBytes = response.getBody();
vars.put(ScriptValueMap.VAR_RESPONSE_BYTES, responseBytes);
String responseString = FileUtils.toString(responseBytes);
Object responseBody = responseString;
responseString = StringUtils.trimToEmpty(responseString);
if (Script.isJson(responseString)) {
try {
responseBody = JsonUtils.toJsonDoc(responseString);
} catch (Exception e) {
logger.warn("json parsing failed, response data type set to string: {}", e.getMessage());
}
} else if (Script.isXml(responseString)) {
try {
responseBody = XmlUtils.toXmlDoc(responseString);
} catch (Exception e) {
logger.warn("xml parsing failed, response data type set to string: {}", e.getMessage());
}
}
vars.put(ScriptValueMap.VAR_RESPONSE, responseBody);
}
//==========================================================================
public void configure(String key, String exp) {
configure(key, Script.evalKarateExpression(exp, this));
}
public void url(String expression) {
String temp = Script.evalKarateExpression(expression, this).getAsString();
request.setUrl(temp);
}
public void path(List paths) {
for (String path : paths) {
ScriptValue temp = Script.evalKarateExpression(path, this);
if (temp.isListLike()) {
List list = temp.getAsList();
for (Object o : list) {
if (o == null) {
continue;
}
request.addPath(o.toString());
}
} else {
request.addPath(temp.getAsString());
}
}
}
public void param(String name, List values) {
request.setParam(name, evalList(values));
}
public void params(String expr) {
Map map = evalMapExpr(expr);
for (Map.Entry entry : map.entrySet()) {
String key = entry.getKey();
Object temp = entry.getValue();
if (temp == null) {
request.removeParam(key);
} else {
if (temp instanceof List) {
request.setParam(key, (List) temp);
} else {
request.setParam(key, temp.toString());
}
}
}
}
public void cookie(String name, String value) {
ScriptValue sv = Script.evalKarateExpression(value, this);
Cookie cookie;
if (sv.isMapLike()) {
cookie = new Cookie((Map) sv.getAsMap());
cookie.put(Cookie.NAME, name);
} else {
cookie = new Cookie(name, sv.getAsString());
}
request.setCookie(cookie);
}
public void cookies(String expr) {
Map map = evalMapExpr(expr);
for (Map.Entry entry : map.entrySet()) {
String key = entry.getKey();
Object temp = entry.getValue();
if (temp == null) {
request.removeCookie(key);
} else {
request.setCookie(new Cookie(key, temp.toString()));
}
}
}
public void header(String name, List values) {
request.setHeader(name, evalList(values));
}
public void headers(String expr) {
Map map = evalMapExpr(expr);
for (Map.Entry entry : map.entrySet()) {
String key = entry.getKey();
Object temp = entry.getValue();
if (temp == null) {
request.removeHeader(key);
} else {
if (temp instanceof List) {
request.setHeader(key, (List) temp);
} else {
request.setHeader(key, temp.toString());
}
}
}
}
public void formField(String name, List values) {
request.setFormField(name, evalList(values));
}
public void formFields(String expr) {
Map map = evalMapExpr(expr);
for (Map.Entry entry : map.entrySet()) {
String key = entry.getKey();
Object temp = entry.getValue();
if (temp == null) {
request.removeFormField(key);
} else {
if (temp instanceof List) {
request.setFormField(key, (List) temp);
} else {
request.setFormField(key, temp.toString());
}
}
}
}
public void request(String requestBody) {
ScriptValue temp = Script.evalKarateExpression(requestBody, this);
request.setBody(temp);
}
public void table(String name, List> table) {
int pos = name.indexOf('='); // backward compatibility, we used to require this till v0.5.0
if (pos != -1) {
name = name.substring(0, pos);
}
List> list = Script.evalTable(table, this);
DocumentContext doc = JsonPath.parse(list);
vars.put(name.trim(), doc);
}
public void replace(String name, List> table) {
name = name.trim();
String text = getVarAsString(name);
String replaced = Script.replacePlaceholders(text, table, this);
vars.put(name, replaced);
}
public void replace(String name, String token, String value) {
name = name.trim();
String text = getVarAsString(name);
String replaced = Script.replacePlaceholderText(text, token, value, this);
vars.put(name, replaced);
}
public void assign(AssignType assignType, String name, String exp) {
Script.assign(assignType, name, exp, this, true);
}
public void assertTrue(String expression) {
AssertionResult ar = Script.assertBoolean(expression, this);
if (!ar.pass) {
logger.error("{}", ar);
throw new KarateException(ar.message);
}
}
public void method(String method) {
if (!HttpUtils.HTTP_METHODS.contains(method.toUpperCase())) { // support expressions also
method = Script.evalKarateExpression(method, this).getAsString();
}
request.setMethod(method);
try {
response = client.invoke(request, this);
} catch (Exception e) {
String message = e.getMessage();
logger.error("http request failed: {}", message);
throw new KarateException(message); // reduce log verbosity
}
updateResponseVars(response);
String prevUrl = request.getUrl();
request = new HttpRequestBuilder();
request.setUrl(prevUrl);
}
public void soapAction(String action) {
action = Script.evalKarateExpression(action, this).getAsString();
if (action == null) {
action = "";
}
request.setHeader("SOAPAction", action);
request.setHeader("Content-Type", "text/xml");
method("post");
}
public void multipartField(String name, String value) {
ScriptValue sv = Script.evalKarateExpression(value, this);
request.addMultiPartItem(name, sv);
}
public void multipartFields(String expr) {
Map map = evalMapExpr(expr);
map.forEach((k, v) -> {
ScriptValue sv = new ScriptValue(v);
request.addMultiPartItem(k, sv);
});
}
public void multipartFile(String name, String value) {
name = name.trim();
ScriptValue sv = Script.evalKarateExpression(value, this);
if (!sv.isMapLike()) {
throw new RuntimeException("mutipart file value should be json");
}
Map map = sv.getAsMap();
String read = asString(map, "read");
if (read == null) {
throw new RuntimeException("mutipart file json should have a value for 'read'");
}
ScriptValue fileValue = FileUtils.readFile(read, this);
MultiPartItem item = new MultiPartItem(name, fileValue);
String filename = asString(map, "filename");
if (filename == null) {
filename = name;
}
item.setFilename(filename);
String contentType = asString(map, "contentType");
if (contentType != null) {
item.setContentType(contentType);
}
request.addMultiPartItem(item);
}
public void multipartFiles(String expr) {
Map map = evalMapExpr(expr);
map.forEach((k, v) -> {
ScriptValue sv = new ScriptValue(v);
multipartFile(k, sv.getAsString());
});
}
public void print(List exps) {
if (isPrintEnabled()) {
String prev = ""; // handle rogue commas embedded in string literals
StringBuilder sb = new StringBuilder();
sb.append("[print]");
for (String exp : exps) {
if (!prev.isEmpty()) {
exp = prev + StringUtils.trimToNull(exp);
}
if (exp == null) {
sb.append("null");
} else {
ScriptValue sv = Script.getIfVariableReference(exp, this);
if (sv == null) {
try {
sv = Script.evalJsExpression(exp, this);
prev = ""; // evalKarateExpression success, reset rogue comma detector
} catch (Exception e) {
prev = exp + ", ";
continue;
}
}
sb.append(' ').append(sv.getAsPrettyString());
}
}
logger.info("{}", sb);
}
}
public void status(int status) {
if (status != response.getStatus()) {
String rawResponse = vars.get(ScriptValueMap.VAR_RESPONSE).getAsString();
String responseTime = vars.get(ScriptValueMap.VAR_RESPONSE_TIME).getAsString();
String message = "status code was: " + response.getStatus() + ", expected: " + status
+ ", response time: " + responseTime + ", url: " + response.getUri() + ", response: " + rawResponse;
logger.error(message);
throw new KarateException(message);
}
}
public void match(MatchType matchType, String name, String path, String expected) {
AssertionResult ar = Script.matchNamed(matchType, name, path, expected, this);
if (!ar.pass) {
logger.error("{}", ar);
throw new KarateException(ar.message);
}
}
public void set(String name, String path, String value) {
Script.setValueByPath(name, path, value, this);
}
public void set(String name, String path, List> table) {
Script.setByPathTable(name, path, table, this);
}
public void remove(String name, String path) {
Script.removeValueByPath(name, path, this);
}
public void call(boolean callonce, String name, String arg) {
Script.callAndUpdateConfigAndAlsoVarsIfMapReturned(callonce, name, arg, this);
}
public void eval(String exp) {
Script.evalJsExpression(exp, this);
}
}