///|
fn json_property(
o : Map[String, Json],
key : String,
) -> Json raise TemplateError {
match o.get(key) {
Some(v) => v
None => raise Invalid("missing request field: " + key)
}
}
///|
fn protocol_request(request : Json) -> Json raise TemplateError {
let o = match request {
Object(o) => o
_ => raise Invalid("request must be object")
}
for k, _ in o {
if !["template", "context", "now", "expression"].contains(k) {
raise Invalid("unknown request field: " + k)
}
}
let context = o.get("context").unwrap_or(Json::empty_object())
let now = match o.get("now") {
Some(String(s)) => s
Some(_) => raise Invalid("now must be string")
None => "1970-01-01T00:00:00.000Z"
}
if o.contains("expression") {
if o.contains("template") {
raise Invalid("request cannot contain expression and template")
}
let expr = match json_property(o, "expression") {
String(s) => s
_ => raise Invalid("expression must be string")
}
evaluate_expression(expr, context, now~)
} else {
render(json_property(o, "template"), context, now~)
}
}
///|
fn protocol_one(request : Json) -> Json {
try {
let value = protocol_request(request)
Json::object(Map([("ok", Json::boolean(true)), ("result", value)]))
} catch {
Invalid(message) =>
Json::object(
Map([
("ok", Json::boolean(false)),
("category", Json::string("invalid")),
("message", Json::string(message)),
]),
)
Limit(message) =>
Json::object(
Map([
("ok", Json::boolean(false)),
("category", Json::string("limit")),
("message", Json::string(message)),
]),
)
}
}
///|
/// JSON request protocol for portable command-line and embedded integrations.
/// Accepts one request or a bounded batch. Every response has an ok field.
pub fn execute(input : String) -> String {
if input.length() > 4000000 {
return "{\"ok\":false,\"category\":\"limit\",\"message\":\"request too large\"}"
}
try {
let request = @json.parse(
input[:],
max_nesting_depth=128,
reject_duplicate_keys=true,
)
let response = match request {
Array(xs) => {
if xs.length() > 1000 {
raise Limit("batch exceeds 1000 requests")
}
Json::array(xs.map(protocol_one))
}
_ => protocol_one(request)
}
response.stringify()
} catch {
_ =>
"{\"ok\":false,\"category\":\"invalid\",\"message\":\"invalid request JSON or oversized batch\"}"
}
}