package com.sas.unravl.generators;
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.annotations.UnRAVLRequestBodyGeneratorPlugin;
import com.sas.unravl.util.Json;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map.Entry;
import org.apache.http.message.BasicHeader;
import org.apache.log4j.Logger;
/**
* Generates a <code>application/x-www-form-urlencoded</code> request body for
* this API call. The node can have one of several forms:
*
* <pre>
* { "form" : json-object }
* { "form" : "@file-or-url" }
* { "form" : "varName" }
* { "form" : "name=encoded-&name=encoded-value&name=encoded-value" }
* </pre>
* <p>
* In the first form the request body is derived from a JSON object. Each value
* must have a scalar value (String, number, boolean). It is converted to
* <code>application/x-www-form-urlencoded</code> by mapping each
* <code>"name" : value</code> pair in the JSON object into a
* "name=form-encoded-value" in the request body, separated by the
* <code>'&'</code> character.
* </p>
* <p>
* In the second form, the JSON is first read from an external file or URL.
* </p>
* <p>
* In the third form, <code>varName</code> must name a JSON object or a
* java.util.Map object in the current environment; similar conversion to the
* form body is performed.
* </p>
* <p>
* The final form may be used to supply a literal pre-formatted body, using a
* string.
* </p>
* <p>
* Environment substitution is performed on string values in the input JSON.
* </p>
* <p>
* The form body is bound in the current environment as a string named
* "requestBody".
* <p>
*
* @author David.Biesack@sas.com
*
*/
@UnRAVLRequestBodyGeneratorPlugin("form")
public class FormBodyGenerator extends BaseUnRAVLRequestBodyGenerator {
@Override
public InputStream getBody(UnRAVL script, ObjectNode bodySpec, ApiCall call)
throws IOException, UnRAVLException {
super.getBody(script, bodySpec, call);
JsonNode json = Json.firstFieldValue(bodySpec);
ObjectNode inputJson = null;
StringBuilder body = new StringBuilder();
if (json.isTextual()) {
String val = json.textValue(); // expand it? could be
// "a={val1}&b={val2}" ?
if (val.startsWith(UnRAVL.REDIRECT_PREFIX)) {
Text request = new Text(script, json);
// TODO: if can't parse, assume it is
// application/x-www-form-urlencoded text
json = Json.parse(request.text());
inputJson = (ObjectNode) Json.expand(json, script);
encode(inputJson, body);
} else {
Object ref = script.binding(val);
if (ref instanceof ObjectNode) {
inputJson = (ObjectNode) Json.expand((ObjectNode) ref,
script);
encode(inputJson, body);
} else if (val.contains("=")) {
// assume body is already application/x-www-form-urlencoded
// text
body.append(val);
} else {
throw new UnRAVLException(
String.format(
"Variable %s is not bound to a JSON value in 'body' body generator",
val));
}
}
} else if (json.isObject()) {
inputJson = (ObjectNode) Json.expand((ObjectNode) json, script);
encode(inputJson, body);
} else {
throw new UnRAVLException(String.format(
"Unrecognized value %s in 'body' body generator", json));
}
String bodyText = body.toString();
script.bind("requestBody", bodyText);
script.addRequestHeader(new BasicHeader("Content-Type",
"application/x-www-form-urlencoded"));
return new ByteArrayInputStream(Text.utf8(bodyText));
}
private static final Logger logger = Logger
.getLogger(FormBodyGenerator.class);
private void encode(ObjectNode inputJson, StringBuilder body) {
try {
String delim = "";
for (Entry<String, JsonNode> x : Json.fields(inputJson)) {
body.append(delim)
.append(x.getKey())
.append("=")
.append(URLEncoder.encode(x.getValue().asText(),
"UTF-8"));
delim = "&";
}
} catch (UnsupportedEncodingException e) {
logger.error(e);
}
}
}