clnrest: add more valid request and response types
Changelog-Added: clnrest can now return successful responses as xml, yaml, or form-encoded in addition to json defined in the 'Accept' header. The same goes for request types defined in the 'Content-type' header.
This commit is contained in:
committed by
ShahanaFarooqui
parent
2e7181d04f
commit
bf37d41c7e
69
Cargo.lock
generated
69
Cargo.lock
generated
@@ -482,9 +482,13 @@ dependencies = [
|
||||
"hyper 1.6.0",
|
||||
"log",
|
||||
"log-panics",
|
||||
"quick-xml",
|
||||
"rcgen",
|
||||
"roxmltree_to_serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_qs",
|
||||
"serde_yml",
|
||||
"socketioxide",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@@ -1240,6 +1244,16 @@ version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "libyml"
|
||||
version = "0.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3302702afa434ffa30847a83305f0a69d6abd74293b6554c18ec85c7ef30c980"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.4"
|
||||
@@ -1604,6 +1618,16 @@ dependencies = [
|
||||
"prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.37.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
@@ -1759,6 +1783,25 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roxmltree"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||
|
||||
[[package]]
|
||||
name = "roxmltree_to_serde"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eabe602f48dfc72e56d9beefcefe457c2898b3b4853ba4aa836804e49732460"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"roxmltree",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.7.1"
|
||||
@@ -2008,6 +2051,17 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_qs"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3faaf9e727533a19351a43cc5a8de957372163c7d35cc48c90b75cdda13c352"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
@@ -2020,6 +2074,21 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yml"
|
||||
version = "0.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59e2dd588bf1597a252c3b920e0143eb99b0f76e4e082f4c92ce34fbc9e71ddd"
|
||||
dependencies = [
|
||||
"indexmap 2.9.0",
|
||||
"itoa",
|
||||
"libyml",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
|
||||
@@ -13,6 +13,10 @@ bytes = "1"
|
||||
log = { version = "0.4", features = ['std'] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_yml = "0.0.12"
|
||||
quick-xml = { version = "0.37", features = ["serialize"] }
|
||||
roxmltree_to_serde = "0.6"
|
||||
serde_qs = "0.15"
|
||||
tokio-util = { version = "0.7", features = ["codec"] }
|
||||
tokio = { version="1", features = ['io-std', 'rt-multi-thread', 'sync', 'macros', 'io-util'] }
|
||||
axum = "0.8"
|
||||
|
||||
@@ -27,6 +27,7 @@ pub enum AppError {
|
||||
Forbidden(RpcError),
|
||||
NotFound(RpcError),
|
||||
InternalServerError(RpcError),
|
||||
NotAcceptable(RpcError),
|
||||
}
|
||||
|
||||
impl IntoResponse for AppError {
|
||||
@@ -36,6 +37,7 @@ impl IntoResponse for AppError {
|
||||
AppError::Forbidden(err) => (StatusCode::FORBIDDEN, err),
|
||||
AppError::NotFound(err) => (StatusCode::NOT_FOUND, err),
|
||||
AppError::InternalServerError(err) => (StatusCode::INTERNAL_SERVER_ERROR, err),
|
||||
AppError::NotAcceptable(err) => (StatusCode::NOT_ACCEPTABLE, err),
|
||||
};
|
||||
|
||||
let body = Json(json!(error_message));
|
||||
@@ -83,19 +85,38 @@ fn process_help_response(help_response: serde_json::Value) -> String {
|
||||
processed_html_res
|
||||
}
|
||||
|
||||
/* Example for swagger ui */
|
||||
#[derive(utoipa::ToSchema)]
|
||||
#[allow(non_camel_case_types)]
|
||||
struct newaddr {
|
||||
#[schema(example = "p2tr")]
|
||||
#[allow(dead_code)]
|
||||
addresstype: String,
|
||||
}
|
||||
|
||||
/* Example for swagger ui */
|
||||
#[derive(utoipa::ToSchema)]
|
||||
#[allow(dead_code)]
|
||||
struct DynamicForm(HashMap<String, String>);
|
||||
|
||||
/* Handler for calling RPC methods */
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/v1/{rpc_method}",
|
||||
responses(
|
||||
(status = 201, description = "Call rpc method", body = serde_json::Value),
|
||||
(status = 201, description = "Call rpc method", body = serde_json::Value,
|
||||
content(("application/json"),("application/yaml"),("application/xml"),
|
||||
("application/x-www-form-urlencoded"))),
|
||||
(status = 401, description = "Unauthorized", body = serde_json::Value),
|
||||
(status = 403, description = "Forbidden", body = serde_json::Value),
|
||||
(status = 404, description = "Not Found", body = serde_json::Value),
|
||||
(status = 500, description = "Server Error", body = serde_json::Value)
|
||||
),
|
||||
request_body(content = serde_json::Value, content_type = "application/json",
|
||||
example = json!({}) ),
|
||||
request_body(description = "RPC params",
|
||||
content((newaddr = "application/json"),
|
||||
(newaddr = "application/yaml"),
|
||||
(DynamicForm = "application/x-www-form-urlencoded"),
|
||||
(newaddr = "application/xml"))),
|
||||
security(("api_key" = []))
|
||||
)]
|
||||
pub async fn call_rpc_method(
|
||||
@@ -109,7 +130,7 @@ pub async fn call_rpc_method(
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(String::from);
|
||||
|
||||
let bytes = match to_bytes(body.into_body(), usize::MAX).await {
|
||||
let request_bytes = match to_bytes(body.into_body(), usize::MAX).await {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
return Err(AppError::InternalServerError(RpcError {
|
||||
@@ -120,52 +141,207 @@ pub async fn call_rpc_method(
|
||||
}
|
||||
};
|
||||
|
||||
let mut rpc_params = match serde_json::from_slice(&bytes) {
|
||||
Ok(o) => o,
|
||||
Err(e1) => {
|
||||
/* it's not json but a form instead */
|
||||
let form_str = String::from_utf8(bytes.to_vec()).unwrap();
|
||||
let mut form_data = HashMap::new();
|
||||
for pair in form_str.split('&') {
|
||||
let mut kv = pair.split('=');
|
||||
if let (Some(key), Some(value)) = (kv.next(), kv.next()) {
|
||||
form_data.insert(key.to_string(), value.to_string());
|
||||
}
|
||||
}
|
||||
match serde_json::to_value(form_data) {
|
||||
Ok(o) => o,
|
||||
Err(e2) => {
|
||||
return Err(AppError::InternalServerError(RpcError {
|
||||
code: None,
|
||||
data: None,
|
||||
message: format!(
|
||||
"Could not parse json from form data: {}\
|
||||
Original serde_json error: {}",
|
||||
e2, e1
|
||||
),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut rpc_params = convert_request_to_json(&headers, &rpc_method, request_bytes)?;
|
||||
|
||||
filter_json(&mut rpc_params);
|
||||
|
||||
verify_rune(plugin.clone(), rune, &rpc_method, &rpc_params).await?;
|
||||
|
||||
match call_rpc(plugin, &rpc_method, rpc_params).await {
|
||||
Ok(result) => {
|
||||
let response_body = Json(result);
|
||||
let response = (StatusCode::CREATED, response_body).into_response();
|
||||
Ok(response)
|
||||
}
|
||||
let cln_result = match call_rpc(plugin, &rpc_method, rpc_params).await {
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
if let Some(code) = err.code {
|
||||
if code == -32601 {
|
||||
return Err(AppError::NotFound(err));
|
||||
}
|
||||
}
|
||||
Err(AppError::InternalServerError(err))
|
||||
return Err(AppError::InternalServerError(err));
|
||||
}
|
||||
};
|
||||
|
||||
convert_json_to_response(headers, &rpc_method, cln_result)
|
||||
}
|
||||
|
||||
fn convert_request_to_json(
|
||||
headers: &axum::http::HeaderMap,
|
||||
rpc_method: &str,
|
||||
request_bytes: axum::body::Bytes,
|
||||
) -> Result<serde_json::Value, AppError> {
|
||||
let content_type = headers
|
||||
.get("content-type")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("application/json");
|
||||
|
||||
let format = match content_type {
|
||||
a if a.contains("*/*") => "json",
|
||||
a if a.contains("application/json") => "json",
|
||||
a if a.contains("application/yaml") => "yaml",
|
||||
a if a.contains("application/xml") => "xml",
|
||||
a if a.contains("application/x-www-form-urlencoded") => "form",
|
||||
_ => {
|
||||
return Err(AppError::NotAcceptable(RpcError {
|
||||
code: None,
|
||||
data: None,
|
||||
message: format!("Unsupported content-type header: {}", content_type),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
if request_bytes.is_empty() {
|
||||
return Ok(json!({}));
|
||||
}
|
||||
|
||||
match format {
|
||||
"yaml" => serde_yml::from_slice(&request_bytes).map_err(|e| {
|
||||
AppError::InternalServerError(RpcError {
|
||||
code: None,
|
||||
data: None,
|
||||
message: format!(
|
||||
"Could not parse `{}` as YAML request: {}",
|
||||
String::from_utf8_lossy(&request_bytes),
|
||||
e
|
||||
),
|
||||
})
|
||||
}),
|
||||
"xml" => {
|
||||
let req_str = std::str::from_utf8(&request_bytes).map_err(|e| {
|
||||
AppError::InternalServerError(RpcError {
|
||||
code: None,
|
||||
data: None,
|
||||
message: format!(
|
||||
"Could not read `{}` as valid utf8: {}",
|
||||
String::from_utf8_lossy(&request_bytes),
|
||||
e
|
||||
),
|
||||
})
|
||||
})?;
|
||||
let json_with_root = roxmltree_to_serde::xml_str_to_json(
|
||||
req_str,
|
||||
&roxmltree_to_serde::Config::new_with_defaults(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
AppError::InternalServerError(RpcError {
|
||||
code: None,
|
||||
data: None,
|
||||
message: format!(
|
||||
"Could not parse `{}` as XML request: {}",
|
||||
String::from_utf8_lossy(&request_bytes),
|
||||
e
|
||||
),
|
||||
})
|
||||
})?;
|
||||
let json_without_root = json_with_root.get(rpc_method).ok_or_else(|| {
|
||||
AppError::InternalServerError(RpcError {
|
||||
code: None,
|
||||
data: None,
|
||||
message: format!("Use rpc method name as root element: `{}`", rpc_method),
|
||||
})
|
||||
})?;
|
||||
Ok(json!(json_without_root))
|
||||
}
|
||||
"form" => {
|
||||
let form_map: HashMap<String, serde_json::Value> = serde_qs::from_bytes(&request_bytes)
|
||||
.map_err(|e| {
|
||||
AppError::InternalServerError(RpcError {
|
||||
code: None,
|
||||
data: None,
|
||||
message: format!(
|
||||
"Could not parse `{}` FORM-URLENCODED request: {}",
|
||||
String::from_utf8_lossy(&request_bytes),
|
||||
e
|
||||
),
|
||||
})
|
||||
})?;
|
||||
Ok(json!(form_map))
|
||||
}
|
||||
_ => serde_json::from_slice(&request_bytes).map_err(|e| {
|
||||
AppError::InternalServerError(RpcError {
|
||||
code: None,
|
||||
data: None,
|
||||
message: format!(
|
||||
"Could not parse `{}` JSON request: {}",
|
||||
String::from_utf8_lossy(&request_bytes),
|
||||
e
|
||||
),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_json_to_response(
|
||||
headers: axum::http::HeaderMap,
|
||||
rpc_method: &str,
|
||||
cln_result: serde_json::Value,
|
||||
) -> Result<Response, AppError> {
|
||||
let accept = headers
|
||||
.get("accept")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("application/json");
|
||||
|
||||
let format = match accept {
|
||||
a if a.contains("*/*") => "json",
|
||||
a if a.contains("application/json") => "json",
|
||||
a if a.contains("application/yaml") => "yaml",
|
||||
a if a.contains("application/xml") => "xml",
|
||||
a if a.contains("application/x-www-form-urlencoded") => "form",
|
||||
_ => {
|
||||
return Err(AppError::NotAcceptable(RpcError {
|
||||
code: None,
|
||||
data: None,
|
||||
message: format!("Unsupported accept header: {}", accept),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
match format {
|
||||
"yaml" => match serde_yml::to_string(&cln_result) {
|
||||
Ok(yaml) => Ok((
|
||||
StatusCode::CREATED,
|
||||
[("Content-Type", "application/yaml")],
|
||||
yaml,
|
||||
)
|
||||
.into_response()),
|
||||
Err(e) => Err(AppError::InternalServerError(RpcError {
|
||||
code: None,
|
||||
data: None,
|
||||
message: format!("Could not serialize to YAML: {}", e),
|
||||
})),
|
||||
},
|
||||
"xml" => match quick_xml::se::to_string_with_root(rpc_method, &cln_result) {
|
||||
Ok(xml) => Ok((
|
||||
StatusCode::CREATED,
|
||||
[("Content-Type", "application/xml")],
|
||||
xml,
|
||||
)
|
||||
.into_response()),
|
||||
Err(e) => Err(AppError::InternalServerError(RpcError {
|
||||
code: None,
|
||||
data: None,
|
||||
message: format!("Could not serialize to XML: {}", e),
|
||||
})),
|
||||
},
|
||||
"form" => match serde_qs::to_string(&cln_result) {
|
||||
Ok(form) => Ok((
|
||||
StatusCode::CREATED,
|
||||
[("Content-Type", "application/x-www-form-urlencoded")],
|
||||
form,
|
||||
)
|
||||
.into_response()),
|
||||
Err(e) => Err(AppError::InternalServerError(RpcError {
|
||||
code: None,
|
||||
data: None,
|
||||
message: format!("Could not serialize to FORM-URLENCODED: {}", e),
|
||||
})),
|
||||
},
|
||||
_ => {
|
||||
let response_body = Json(cln_result);
|
||||
let response = (
|
||||
StatusCode::CREATED,
|
||||
[("Content-Type", "application/json")],
|
||||
response_body,
|
||||
)
|
||||
.into_response();
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ from urllib3.util.retry import Retry
|
||||
import socketio
|
||||
import time
|
||||
import pytest
|
||||
import json
|
||||
|
||||
|
||||
def http_session_with_retry():
|
||||
@@ -494,3 +495,146 @@ def test_websocket_upgrade_header(node_factory):
|
||||
sio.disconnect()
|
||||
|
||||
assert len(notifications) == 0
|
||||
|
||||
|
||||
def test_accept_header_types(node_factory):
|
||||
l1, base_url, ca_cert = start_node_with_clnrest(node_factory)
|
||||
http_session = http_session_with_retry()
|
||||
|
||||
rune = l1.rpc.createrune(restrictions=[])['rune']
|
||||
|
||||
response = http_session.post(base_url + '/v1/getinfo',
|
||||
headers={'Rune': rune},
|
||||
verify=ca_cert)
|
||||
response.raise_for_status()
|
||||
assert response.json()['id'] == l1.info['id']
|
||||
|
||||
response = http_session.post(base_url + '/v1/getinfo',
|
||||
headers={'Rune': rune, 'Accept': 'application/json'},
|
||||
verify=ca_cert)
|
||||
response.raise_for_status()
|
||||
assert response.json()['id'] == l1.info['id']
|
||||
|
||||
response = http_session.post(base_url + '/v1/getinfo',
|
||||
headers={'Rune': rune, 'Accept': 'application/yaml'},
|
||||
verify=ca_cert)
|
||||
response.raise_for_status()
|
||||
assert f"id: '{l1.info['id']}'" in response.text
|
||||
|
||||
response = http_session.post(base_url + '/v1/getinfo',
|
||||
headers={'Rune': rune, 'Accept': 'application/xml'},
|
||||
verify=ca_cert)
|
||||
response.raise_for_status()
|
||||
assert f"<id>{l1.info['id']}</id>" in response.text
|
||||
|
||||
response = http_session.post(base_url + '/v1/getinfo',
|
||||
headers={'Rune': rune, 'Accept': 'application/x-www-form-urlencoded'},
|
||||
verify=ca_cert)
|
||||
response.raise_for_status()
|
||||
assert f"id={l1.info['id']}" in response.text
|
||||
|
||||
|
||||
def test_content_type_header_types(node_factory):
|
||||
l1, base_url, ca_cert = start_node_with_clnrest(node_factory)
|
||||
http_session = http_session_with_retry()
|
||||
|
||||
datastore_res = l1.rpc.datastore(key=['project'], string='core lightning', mode='must-create')
|
||||
rune = l1.rpc.createrune(restrictions=[])['rune']
|
||||
|
||||
listdatastore_res = http_session.post(base_url + '/v1/listdatastore',
|
||||
headers={'Rune': rune},
|
||||
data=json.dumps({'key': ['project']}),
|
||||
verify=ca_cert)
|
||||
listdatastore_res.raise_for_status()
|
||||
datastore_key = listdatastore_res.json()["datastore"]
|
||||
assert len(datastore_key) == 1
|
||||
assert datastore_key[0]['key'] == datastore_res['key']
|
||||
assert datastore_key[0]['string'] == datastore_res['string']
|
||||
|
||||
listdatastore_res = http_session.post(base_url + '/v1/listdatastore',
|
||||
headers={'Rune': rune, 'Content-Type': 'application/json'},
|
||||
data=json.dumps({'key': ['project']}),
|
||||
verify=ca_cert)
|
||||
listdatastore_res.raise_for_status()
|
||||
datastore_key = listdatastore_res.json()["datastore"]
|
||||
assert len(datastore_key) == 1
|
||||
assert datastore_key[0]['key'] == datastore_res['key']
|
||||
assert datastore_key[0]['string'] == datastore_res['string']
|
||||
|
||||
listdatastore_res = http_session.post(base_url + '/v1/listdatastore',
|
||||
headers={'Rune': rune, 'Content-Type': 'application/yaml'},
|
||||
data=f"key: {datastore_res['key']}",
|
||||
verify=ca_cert)
|
||||
listdatastore_res.raise_for_status()
|
||||
datastore_key = listdatastore_res.json()["datastore"]
|
||||
assert len(datastore_key) == 1
|
||||
assert datastore_key[0]['key'] == datastore_res['key']
|
||||
assert datastore_key[0]['string'] == datastore_res['string']
|
||||
|
||||
listdatastore_res = http_session.post(base_url + '/v1/listdatastore',
|
||||
headers={'Rune': rune, 'Content-Type': 'application/xml'},
|
||||
data=f"<listdatastore><key>{datastore_res['key'][0]}</key></listdatastore>",
|
||||
verify=ca_cert)
|
||||
listdatastore_res.raise_for_status()
|
||||
datastore_key = listdatastore_res.json()["datastore"]
|
||||
assert len(datastore_key) == 1
|
||||
assert datastore_key[0]['key'] == datastore_res['key']
|
||||
assert datastore_key[0]['string'] == datastore_res['string']
|
||||
|
||||
listdatastore_res = http_session.post(base_url + '/v1/listdatastore',
|
||||
headers={'Rune': rune, 'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
data={'key': datastore_res['key']},
|
||||
verify=ca_cert)
|
||||
listdatastore_res.raise_for_status()
|
||||
datastore_key = listdatastore_res.json()["datastore"]
|
||||
assert len(datastore_key) == 1
|
||||
assert datastore_key[0]['key'] == datastore_res['key']
|
||||
assert datastore_key[0]['string'] == datastore_res['string']
|
||||
|
||||
|
||||
def test_matching_accept_and_content_types(node_factory):
|
||||
l1, base_url, ca_cert = start_node_with_clnrest(node_factory)
|
||||
http_session = http_session_with_retry()
|
||||
|
||||
datastore_res = l1.rpc.datastore(key=['project'], string='core lightning', mode='must-create')
|
||||
|
||||
rune = l1.rpc.createrune(restrictions=[])['rune']
|
||||
|
||||
listdatastore_res = http_session.post(base_url + '/v1/listdatastore',
|
||||
headers={'Rune': rune},
|
||||
data=json.dumps({'key': datastore_res['key']}),
|
||||
verify=ca_cert)
|
||||
listdatastore_res.raise_for_status()
|
||||
datastore_key = listdatastore_res.json()["datastore"]
|
||||
assert len(datastore_key) == 1
|
||||
assert datastore_key[0]['key'] == datastore_res['key']
|
||||
|
||||
listdatastore_res = http_session.post(base_url + '/v1/listdatastore',
|
||||
headers={'Rune': rune, 'Content-Type': 'application/json', 'Accept': 'application/json'},
|
||||
data=json.dumps({'key': datastore_res['key']}),
|
||||
verify=ca_cert)
|
||||
listdatastore_res.raise_for_status()
|
||||
datastore_key = listdatastore_res.json()["datastore"]
|
||||
assert len(datastore_key) == 1
|
||||
assert datastore_key[0]['key'] == datastore_res['key']
|
||||
|
||||
listdatastore_res = http_session.post(base_url + '/v1/listdatastore',
|
||||
headers={'Rune': rune, 'Content-Type': 'application/yaml', 'Accept': 'application/yaml'},
|
||||
data=f"key: {datastore_res['key']}",
|
||||
verify=ca_cert)
|
||||
listdatastore_res.raise_for_status()
|
||||
assert f"key:\n - {datastore_res['key'][0]}" in listdatastore_res.text
|
||||
|
||||
listdatastore_res = http_session.post(base_url + '/v1/listdatastore',
|
||||
headers={'Rune': rune, 'Content-Type': 'application/xml', 'Accept': 'application/xml'},
|
||||
data=f"<listdatastore><key>{datastore_res['key'][0]}</key></listdatastore>",
|
||||
verify=ca_cert)
|
||||
listdatastore_res.raise_for_status()
|
||||
assert f"<key>{datastore_res['key'][0]}</key>" in listdatastore_res.text
|
||||
|
||||
listdatastore_res = http_session.post(base_url + '/v1/listdatastore',
|
||||
headers={'Rune': rune, 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/x-www-form-urlencoded'},
|
||||
data={'key': datastore_res['key']},
|
||||
verify=ca_cert)
|
||||
listdatastore_res.raise_for_status()
|
||||
assert f"datastore[0][key][0]={datastore_res['key'][0]}" in listdatastore_res.text
|
||||
|
||||
Reference in New Issue
Block a user