package com.sas.unravl.assertions; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.sas.unravl.ApiCall; import com.sas.unravl.UnRAVL; import com.sas.unravl.UnRAVLException; import com.sas.unravl.generators.Text; import com.sas.unravl.util.Json; import java.io.IOException; import org.apache.log4j.Logger; /** * Run a script, passing the current environment. If the script returns false, * the assertion fails. Ignore non-Boolean return values. This base class for * {@link GroovyAssertion} and {@link JavaScriptAssertion}. * <p> * Usage: * * <pre> * "assert" : [ * { "lang" : "<em>expression</em>" } * { "lang" : "@file-or-URL" } * { "lang" : [ array-of-text-or-@file-or-URL" } * ] * </pre> * * where "lang" is a supported script language name such as "groovy" or * "javascript". * <p> * In the simplest form, all top level strings inside an "assert" or * "preconditions" array are interpreted as expressions in the target script * language. * <p> * If using a <code>{ "lang" : <em>value</em> }</code> object, the source * <code><em>value</em></code> may also be expressed as defined with * {@link Text}. The value may be a simple string (containing a expression), or * a string in the form <code>"@<em>file-or-URL</em>"</code> in which case the * script is ready from that location. Finally, the value may be an array of one * or more source strings or @file-or-URL references which are then concatenated * and then interpreted. * <p> * The final script value is subject to environment expansion before running. * All variables in the environment are available as local variables when the * script runs. * * @author David.Biesack@sas.com */ public class BaseScriptAssertion extends BaseUnRAVLAssertion { static final Logger logger = Logger.getLogger(BaseScriptAssertion.class); private final String language; public BaseScriptAssertion(String language) { this.language = language; } @Override public void check(UnRAVL script, ObjectNode assertion, Stage when, ApiCall call) throws UnRAVLAssertionException, UnRAVLException { super.check(script, assertion, when, call); JsonNode val = null; String expression = null; try { val = Json.firstFieldValue(assertion); // TODO: If the value is an array, such as // { "assert" : { "javascript" : [ "line1", "line2", "line3" ] } } // processing as a Text object will concatenate these into // one String representing a single script, "line1\nline2\nline3". // UnRAVL needs a new option in the assert element to treat the // array // as separate/individual assertions, for example, // { "assert" : { "javascript" : [ "line1", "line2", "line3" ], // "separate" : true } } // which will run each array element as an assertion. For now, one // must // use the more verbose form: // { "assert" : { "javascript" : "line1"} } // { "assert" : { "javascript" : "line2"} } // { "assert" : { "javascript" : "line3"} } // I need a better name than "separate" : true ... ?? Text t = new Text(script, val); expression = script.expand(t.text()); Object result = script.evalWith(expression, language); logger.info("Script " + script + ", returned " + result); if ((result instanceof Boolean) && !(((Boolean) result).booleanValue())) { throw new UnRAVLAssertionException( "Groovy script returned false: " + script); } } catch (RuntimeException e) { logger.error("Script '" + script + "' threw a runtime exception " + e.getClass().getName() + ", " + e.getMessage()); throw new UnRAVLException(e.getMessage(), e); } catch (IOException e) { logger.error("I/O exception " + e.getMessage() + " trying to load assertion '" + val); throw new UnRAVLException(e.getMessage(), e); } } }