use serde_json::Value;
#[derive(Debug, Default, Clone)]
pub struct SqlQueryRunParams {
pub format: Option<String>,
pub header: Option<bool>,
pub query_reference: Option<String>,
pub query_resource: Option<Value>,
pub parameters: Option<Value>,
pub source: Option<String>,
pub limit: Option<u32>,
}
pub fn extract_sqlquery_params_from_json(body: &Value) -> SqlQueryRunParams {
let mut out = SqlQueryRunParams::default();
if body.get("resourceType").and_then(|v| v.as_str()) != Some("Parameters") {
return out;
}
let Some(entries) = body.get("parameter").and_then(|p| p.as_array()) else {
return out;
};
for p in entries {
let Some(name) = p.get("name").and_then(|n| n.as_str()) else {
continue;
};
match name {
"_format" | "format" => {
if out.format.is_none() {
out.format = read_str(p, &["valueCode", "valueString"]);
}
}
"header" => {
if out.header.is_none() {
if let Some(b) = p.get("valueBoolean").and_then(|v| v.as_bool()) {
out.header = Some(b);
} else if let Some(s) = p.get("valueString").and_then(|v| v.as_str()) {
out.header = Some(s == "true" || s == "1");
}
}
}
"queryReference" => {
if out.query_reference.is_none() {
out.query_reference = read_reference(p);
}
}
"queryResource" => {
if out.query_resource.is_none() {
if let Some(r) = p.get("resource") {
out.query_resource = Some(r.clone());
}
}
}
"parameters" => {
if out.parameters.is_none() {
if let Some(r) = p.get("resource") {
out.parameters = Some(r.clone());
}
}
}
"source" => {
if out.source.is_none() {
out.source = read_str(p, &["valueString", "valueUri"]);
}
}
"_limit" => {
if out.limit.is_none() {
if let Some(n) = p.get("valueInteger").and_then(|v| v.as_u64()) {
out.limit = Some(n as u32);
} else if let Some(n) = p
.get("valuePositiveInt")
.or_else(|| p.get("valueUnsignedInt"))
.and_then(|v| v.as_u64())
{
out.limit = Some(n as u32);
}
}
}
_ => {}
}
}
out
}
fn read_str(p: &Value, keys: &[&str]) -> Option<String> {
for k in keys {
if let Some(s) = p.get(*k).and_then(|v| v.as_str()) {
return Some(s.to_string());
}
}
None
}
fn read_reference(p: &Value) -> Option<String> {
p.get("valueReference")
.and_then(|v| v.get("reference"))
.and_then(|v| v.as_str())
.map(str::to_string)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn extracts_format_and_header() {
let body = json!({
"resourceType": "Parameters",
"parameter": [
{"name": "_format", "valueCode": "csv"},
{"name": "header", "valueBoolean": false}
]
});
let p = extract_sqlquery_params_from_json(&body);
assert_eq!(p.format.as_deref(), Some("csv"));
assert_eq!(p.header, Some(false));
}
#[test]
fn extracts_query_reference_and_resource() {
let body = json!({
"resourceType": "Parameters",
"parameter": [
{"name": "_format", "valueCode": "json"},
{"name": "queryReference", "valueReference": {"reference": "Library/foo"}},
{"name": "queryResource", "resource": {"resourceType": "Library"}}
]
});
let p = extract_sqlquery_params_from_json(&body);
assert_eq!(p.query_reference.as_deref(), Some("Library/foo"));
assert!(p.query_resource.is_some());
}
#[test]
fn non_parameters_body_returns_default() {
let p = extract_sqlquery_params_from_json(&json!({"resourceType": "Bundle"}));
assert!(p.format.is_none());
}
#[test]
fn extracts_limit() {
let body = json!({
"resourceType": "Parameters",
"parameter": [
{"name": "_limit", "valueInteger": 50}
]
});
let p = extract_sqlquery_params_from_json(&body);
assert_eq!(p.limit, Some(50));
}
#[test]
fn query_reference_only_reads_value_reference() {
let body = json!({
"resourceType": "Parameters",
"parameter": [
{"name": "_format", "valueCode": "json"},
{"name": "queryReference", "valueString": "Library/foo"}
]
});
let p = extract_sqlquery_params_from_json(&body);
assert!(p.query_reference.is_none());
}
}