slumber-python 5.1.0__tar.gz → 5.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {slumber_python-5.1.0 → slumber_python-5.1.1}/Cargo.lock +11 -11
- {slumber_python-5.1.0 → slumber_python-5.1.1}/Cargo.toml +9 -9
- {slumber_python-5.1.0 → slumber_python-5.1.1}/PKG-INFO +1 -1
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/collection/json.rs +89 -91
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/http.rs +21 -26
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/render/functions.rs +9 -12
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/render/tests.rs +31 -74
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/render.rs +109 -88
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/util.rs +21 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/error.rs +0 -10
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/lib.rs +19 -18
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/tests.rs +16 -23
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/value.rs +13 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/src/lib.rs +0 -15
- {slumber_python-5.1.0 → slumber_python-5.1.1}/README.md +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/Cargo.toml +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/cereal.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/default.yml +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/default_old.yml +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/lib.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/tui/cereal.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/tui/input.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/tui/mime.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/tui/theme.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/tui.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/Cargo.toml +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/collection/cereal.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/collection/models.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/collection/recipe_tree.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/collection/schema.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/collection.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/database/convert.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/database/migrations.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/database/tests.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/database.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/http/curl.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/http/models.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/http/tests.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/lib.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/render/util.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/test_util.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/macros/Cargo.toml +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/macros/src/lib.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/Cargo.toml +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/README.md +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/dev.py +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/mise.toml +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/slumber.pyi +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/src/lib.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/tests/slumber.yml +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/tests/test.py +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/uv.lock +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/Cargo.toml +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/proptest-regressions/parse.txt +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/cereal.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/display.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/expression.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/parse.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/test_util.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/Cargo.toml +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/src/paths.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/src/test_util.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/src/yaml/error.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/src/yaml/resolve.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/src/yaml.rs +0 -0
- {slumber_python-5.1.0 → slumber_python-5.1.1}/pyproject.toml +0 -0
|
@@ -809,7 +809,7 @@ dependencies = [
|
|
|
809
809
|
|
|
810
810
|
[[package]]
|
|
811
811
|
name = "doc_utils"
|
|
812
|
-
version = "5.1.
|
|
812
|
+
version = "5.1.1"
|
|
813
813
|
dependencies = [
|
|
814
814
|
"anyhow",
|
|
815
815
|
"clap",
|
|
@@ -3280,7 +3280,7 @@ checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
|
|
3280
3280
|
|
|
3281
3281
|
[[package]]
|
|
3282
3282
|
name = "slumber"
|
|
3283
|
-
version = "5.1.
|
|
3283
|
+
version = "5.1.1"
|
|
3284
3284
|
dependencies = [
|
|
3285
3285
|
"anyhow",
|
|
3286
3286
|
"console-subscriber",
|
|
@@ -3294,7 +3294,7 @@ dependencies = [
|
|
|
3294
3294
|
|
|
3295
3295
|
[[package]]
|
|
3296
3296
|
name = "slumber_cli"
|
|
3297
|
-
version = "5.1.
|
|
3297
|
+
version = "5.1.1"
|
|
3298
3298
|
dependencies = [
|
|
3299
3299
|
"anyhow",
|
|
3300
3300
|
"assert_cmd",
|
|
@@ -3329,7 +3329,7 @@ dependencies = [
|
|
|
3329
3329
|
|
|
3330
3330
|
[[package]]
|
|
3331
3331
|
name = "slumber_config"
|
|
3332
|
-
version = "5.1.
|
|
3332
|
+
version = "5.1.1"
|
|
3333
3333
|
dependencies = [
|
|
3334
3334
|
"derive_more",
|
|
3335
3335
|
"dirs",
|
|
@@ -3354,7 +3354,7 @@ dependencies = [
|
|
|
3354
3354
|
|
|
3355
3355
|
[[package]]
|
|
3356
3356
|
name = "slumber_core"
|
|
3357
|
-
version = "5.1.
|
|
3357
|
+
version = "5.1.1"
|
|
3358
3358
|
dependencies = [
|
|
3359
3359
|
"async-trait",
|
|
3360
3360
|
"base64 0.22.1",
|
|
@@ -3402,7 +3402,7 @@ dependencies = [
|
|
|
3402
3402
|
|
|
3403
3403
|
[[package]]
|
|
3404
3404
|
name = "slumber_import"
|
|
3405
|
-
version = "5.1.
|
|
3405
|
+
version = "5.1.1"
|
|
3406
3406
|
dependencies = [
|
|
3407
3407
|
"anyhow",
|
|
3408
3408
|
"base64 0.22.1",
|
|
@@ -3434,7 +3434,7 @@ dependencies = [
|
|
|
3434
3434
|
|
|
3435
3435
|
[[package]]
|
|
3436
3436
|
name = "slumber_macros"
|
|
3437
|
-
version = "5.1.
|
|
3437
|
+
version = "5.1.1"
|
|
3438
3438
|
dependencies = [
|
|
3439
3439
|
"proc-macro2",
|
|
3440
3440
|
"quote",
|
|
@@ -3443,7 +3443,7 @@ dependencies = [
|
|
|
3443
3443
|
|
|
3444
3444
|
[[package]]
|
|
3445
3445
|
name = "slumber_python"
|
|
3446
|
-
version = "5.1.
|
|
3446
|
+
version = "5.1.1"
|
|
3447
3447
|
dependencies = [
|
|
3448
3448
|
"async-trait",
|
|
3449
3449
|
"dialoguer",
|
|
@@ -3457,7 +3457,7 @@ dependencies = [
|
|
|
3457
3457
|
|
|
3458
3458
|
[[package]]
|
|
3459
3459
|
name = "slumber_template"
|
|
3460
|
-
version = "5.1.
|
|
3460
|
+
version = "5.1.1"
|
|
3461
3461
|
dependencies = [
|
|
3462
3462
|
"bytes",
|
|
3463
3463
|
"derive_more",
|
|
@@ -3484,7 +3484,7 @@ dependencies = [
|
|
|
3484
3484
|
|
|
3485
3485
|
[[package]]
|
|
3486
3486
|
name = "slumber_tui"
|
|
3487
|
-
version = "5.1.
|
|
3487
|
+
version = "5.1.1"
|
|
3488
3488
|
dependencies = [
|
|
3489
3489
|
"anyhow",
|
|
3490
3490
|
"async-trait",
|
|
@@ -3525,7 +3525,7 @@ dependencies = [
|
|
|
3525
3525
|
|
|
3526
3526
|
[[package]]
|
|
3527
3527
|
name = "slumber_util"
|
|
3528
|
-
version = "5.1.
|
|
3528
|
+
version = "5.1.1"
|
|
3529
3529
|
dependencies = [
|
|
3530
3530
|
"derive_more",
|
|
3531
3531
|
"dirs",
|
|
@@ -9,7 +9,7 @@ homepage = "https://slumber.lucaspickering.me"
|
|
|
9
9
|
keywords = ["rest", "http", "terminal", "tui"]
|
|
10
10
|
license = "MIT"
|
|
11
11
|
repository = "https://github.com/LucasPickering/slumber"
|
|
12
|
-
version = "5.1.
|
|
12
|
+
version = "5.1.1"
|
|
13
13
|
# Keep in sync w/ rust-toolchain.toml
|
|
14
14
|
rust-version = "1.90.0"
|
|
15
15
|
|
|
@@ -43,14 +43,14 @@ serde_json = {version = "1.0.120", default-features = false, features = ["preser
|
|
|
43
43
|
serde_json_path = "0.7.1"
|
|
44
44
|
serde_test = "1.0.176"
|
|
45
45
|
serde_yaml = {version = "0.9.0", default-features = false}
|
|
46
|
-
slumber_cli = {path = "./crates/cli", version = "5.1.
|
|
47
|
-
slumber_config = {path = "./crates/config", version = "5.1.
|
|
48
|
-
slumber_core = {path = "./crates/core", version = "5.1.
|
|
49
|
-
slumber_import = {path = "./crates/import", version = "5.1.
|
|
50
|
-
slumber_macros = {path = "./crates/macros", version = "5.1.
|
|
51
|
-
slumber_template = {path = "./crates/template", version = "5.1.
|
|
52
|
-
slumber_tui = {path = "./crates/tui", version = "5.1.
|
|
53
|
-
slumber_util = {path = "./crates/util", version = "5.1.
|
|
46
|
+
slumber_cli = {path = "./crates/cli", version = "5.1.1" }
|
|
47
|
+
slumber_config = {path = "./crates/config", version = "5.1.1", default-features = false }
|
|
48
|
+
slumber_core = {path = "./crates/core", version = "5.1.1" }
|
|
49
|
+
slumber_import = {path = "./crates/import", version = "5.1.1" }
|
|
50
|
+
slumber_macros = {path = "./crates/macros", version = "5.1.1" }
|
|
51
|
+
slumber_template = {path = "./crates/template", version = "5.1.1" }
|
|
52
|
+
slumber_tui = {path = "./crates/tui", version = "5.1.1" }
|
|
53
|
+
slumber_util = {path = "./crates/util", version = "5.1.1" }
|
|
54
54
|
strum = {version = "0.27.0", default-features = false}
|
|
55
55
|
syn = "2.0.101"
|
|
56
56
|
terminput = "0.5.3"
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
//! Utilities for working with templated JSON
|
|
2
2
|
|
|
3
|
-
use crate::render::TemplateContext;
|
|
3
|
+
use crate::{render::TemplateContext, util::value_to_json};
|
|
4
4
|
use futures::future;
|
|
5
5
|
use serde::{Serialize, Serializer, ser::SerializeMap};
|
|
6
6
|
use slumber_template::{
|
|
7
|
-
RenderError, Template, TemplateParseError,
|
|
7
|
+
RenderError, RenderedOutput, Template, TemplateParseError, Value,
|
|
8
|
+
ValueError,
|
|
8
9
|
};
|
|
9
10
|
use std::str::FromStr;
|
|
10
11
|
use thiserror::Error;
|
|
11
12
|
|
|
12
13
|
/// A JSON value like [serde_json::Value], but all strings are templates
|
|
13
|
-
#[derive(Clone, Debug, Serialize)]
|
|
14
|
-
#[cfg_attr(any(test, feature = "test"), derive(PartialEq))]
|
|
14
|
+
#[derive(Clone, Debug, PartialEq, Serialize)]
|
|
15
15
|
#[serde(untagged)]
|
|
16
16
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
|
17
17
|
pub enum JsonTemplate {
|
|
@@ -50,82 +50,64 @@ impl JsonTemplate {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
/// Render
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
53
|
+
/// Render to previewable chunks
|
|
54
|
+
///
|
|
55
|
+
/// The return value is *usually* a single chunk, but if the JSON value is
|
|
56
|
+
/// a multi-chunk template string, then its multi-chunk output will be the
|
|
57
|
+
/// output for this.
|
|
58
|
+
pub async fn render(&self, context: &TemplateContext) -> RenderedOutput {
|
|
59
|
+
match self {
|
|
60
|
+
JsonTemplate::Null => Value::Null.into(),
|
|
61
|
+
JsonTemplate::Bool(b) => Value::Boolean(*b).into(),
|
|
62
|
+
JsonTemplate::Number(n) => {
|
|
63
|
+
if let Some(i) = n.as_i64() {
|
|
64
|
+
Value::Integer(i).into()
|
|
65
|
+
} else if let Some(f) = n.as_f64() {
|
|
66
|
+
Value::Float(f).into()
|
|
67
|
+
} else {
|
|
68
|
+
// Integer is out of i64 range. Template values really
|
|
69
|
+
// should be a superset of JSON values, but right now that's
|
|
70
|
+
// not the case.
|
|
71
|
+
Err(RenderError::from(ValueError::other(
|
|
72
|
+
"JSON integer out of range",
|
|
73
|
+
)))
|
|
74
|
+
.into()
|
|
75
|
+
}
|
|
75
76
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
JsonTemplate::String(template) => template.render(context).await,
|
|
78
|
+
JsonTemplate::Array(array) => {
|
|
79
|
+
// Render each value and collection into an Array
|
|
80
|
+
future::try_join_all(array.iter().map(|value| async {
|
|
81
|
+
value.render(context).await.try_collect_value().await
|
|
82
|
+
}))
|
|
83
|
+
.await
|
|
84
|
+
.map(Value::from)
|
|
85
|
+
.into() // Wrap into RenderedOutput
|
|
82
86
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
)
|
|
93
|
-
.
|
|
94
|
-
serde_json::Value::Object(map.into_iter().collect())
|
|
87
|
+
JsonTemplate::Object(map) => {
|
|
88
|
+
// Render each key/value and collect into an Object
|
|
89
|
+
future::try_join_all(map.iter().map(|(key, value)| async {
|
|
90
|
+
let key = key.render_string(context).await?;
|
|
91
|
+
let value =
|
|
92
|
+
value.render(context).await.try_collect_value().await?;
|
|
93
|
+
Ok::<_, RenderError>((key, value))
|
|
94
|
+
}))
|
|
95
|
+
.await
|
|
96
|
+
.map(Value::from)
|
|
97
|
+
.into() // Wrap into RenderedOutput
|
|
95
98
|
}
|
|
96
|
-
}
|
|
97
|
-
Ok(rendered)
|
|
99
|
+
}
|
|
98
100
|
}
|
|
99
|
-
}
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
JsonTemplate::String(template) => {
|
|
112
|
-
serde_json::Value::String(template.display().to_string())
|
|
113
|
-
}
|
|
114
|
-
JsonTemplate::Array(array) => serde_json::Value::Array(
|
|
115
|
-
array.iter().map(serde_json::Value::from).collect(),
|
|
116
|
-
),
|
|
117
|
-
JsonTemplate::Object(object) => serde_json::Value::Object(
|
|
118
|
-
object
|
|
119
|
-
.iter()
|
|
120
|
-
.map(|(key, value)| {
|
|
121
|
-
(
|
|
122
|
-
key.display().to_string(),
|
|
123
|
-
serde_json::Value::from(value),
|
|
124
|
-
)
|
|
125
|
-
})
|
|
126
|
-
.collect(),
|
|
127
|
-
),
|
|
128
|
-
}
|
|
102
|
+
/// Render all templates to strings and return a static JSON value
|
|
103
|
+
pub async fn render_json(
|
|
104
|
+
&self,
|
|
105
|
+
context: &TemplateContext,
|
|
106
|
+
) -> Result<serde_json::Value, RenderError> {
|
|
107
|
+
// Collect render output as a single value. The renderer should always
|
|
108
|
+
// output a single chunk, so it gets unpacked back to one value.
|
|
109
|
+
let value = self.render(context).await.try_collect_value().await?;
|
|
110
|
+
Ok(value_to_json(value))
|
|
129
111
|
}
|
|
130
112
|
}
|
|
131
113
|
|
|
@@ -210,45 +192,61 @@ pub enum JsonTemplateError {
|
|
|
210
192
|
mod tests {
|
|
211
193
|
use super::*;
|
|
212
194
|
use crate::{
|
|
213
|
-
collection::Profile,
|
|
195
|
+
collection::Profile,
|
|
196
|
+
render::TemplateContext,
|
|
197
|
+
test_util::{by_id, invalid_utf8},
|
|
214
198
|
};
|
|
215
199
|
use indexmap::indexmap;
|
|
216
200
|
use rstest::rstest;
|
|
217
201
|
use serde_json::json;
|
|
218
202
|
use slumber_util::{Factory, assert_result};
|
|
219
203
|
|
|
220
|
-
///
|
|
204
|
+
/// Render JSON templates to JSON values
|
|
221
205
|
#[rstest]
|
|
222
|
-
#[case::
|
|
206
|
+
#[case::null(json!(null), Ok(json!(null)))]
|
|
207
|
+
#[case::bool(json!(true), Ok(json!(true)))]
|
|
208
|
+
#[case::integer(json!(3), Ok(json!(3)))]
|
|
209
|
+
#[case::float(json!(3.15), Ok(json!(3.15)))]
|
|
210
|
+
#[case::string(json!("{{ username }}"), Ok(json!("testuser")))]
|
|
211
|
+
#[case::array(
|
|
212
|
+
json!([1, 2, "{{ username }}"]),
|
|
213
|
+
Ok(json!([1, 2, "testuser"])),
|
|
214
|
+
)]
|
|
215
|
+
#[case::object(
|
|
223
216
|
json!({"{{ user_id }}": {"name": "{{ username }}"}}),
|
|
224
217
|
Ok(json!({"123": {"name": "testuser"}})),
|
|
225
218
|
)]
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
219
|
+
// serde_json converts the byte string to a number array. Seems reasonable
|
|
220
|
+
// enough.
|
|
221
|
+
#[case::bytes(json!("{{ invalid_utf8 }}"), Ok(json!([0xc3, 0x28])))]
|
|
222
|
+
// Once we have non-string profile values we can test:
|
|
223
|
+
// - Invalid int/float values
|
|
224
|
+
// - Unpacking strings
|
|
230
225
|
#[tokio::test]
|
|
231
|
-
async fn
|
|
226
|
+
async fn test_render_json(
|
|
232
227
|
#[case] input: serde_json::Value,
|
|
233
228
|
#[case] expected: Result<serde_json::Value, &str>,
|
|
234
229
|
) {
|
|
235
230
|
let profile = Profile {
|
|
236
231
|
data: indexmap! {
|
|
237
232
|
"user_id".into() => "123".into(),
|
|
238
|
-
"username".into() => "testuser".into()
|
|
233
|
+
"username".into() => "testuser".into(),
|
|
234
|
+
"invalid_utf8".into() => invalid_utf8(),
|
|
239
235
|
},
|
|
240
236
|
..Profile::factory(())
|
|
241
237
|
};
|
|
242
238
|
let context =
|
|
243
239
|
TemplateContext::factory((by_id([profile]), indexmap! {}));
|
|
244
240
|
|
|
245
|
-
let
|
|
246
|
-
|
|
247
|
-
// so don't check for errors during render. Saves having to
|
|
248
|
-
// consolidate the error types
|
|
249
|
-
Ok(json) => Ok(json.render(&context).await.unwrap()),
|
|
250
|
-
Err(error) => Err(error),
|
|
251
|
-
};
|
|
241
|
+
let template = JsonTemplate::try_from(input).expect("Invalid template");
|
|
242
|
+
let result = template.render_json(&context).await;
|
|
252
243
|
assert_result(result, expected);
|
|
253
244
|
}
|
|
245
|
+
|
|
246
|
+
/// Parsing a JSON value with a key that isn't a valid template is an error
|
|
247
|
+
#[test]
|
|
248
|
+
fn test_invalid_key_template() {
|
|
249
|
+
let json = json!({"{{ invalid_key": {"name": "{{ username }}"}});
|
|
250
|
+
assert_result(JsonTemplate::try_from(json), Err("invalid expression"));
|
|
251
|
+
}
|
|
254
252
|
}
|
|
@@ -568,7 +568,7 @@ impl Recipe {
|
|
|
568
568
|
) -> Result<Url, RequestBuildErrorKind> {
|
|
569
569
|
let template = options.url.as_ref().unwrap_or(&self.url);
|
|
570
570
|
let url = template
|
|
571
|
-
.render_string(
|
|
571
|
+
.render_string(context)
|
|
572
572
|
.await
|
|
573
573
|
.map_err(RequestBuildErrorKind::UrlRender)?;
|
|
574
574
|
url.parse::<Url>()
|
|
@@ -591,12 +591,12 @@ impl Recipe {
|
|
|
591
591
|
);
|
|
592
592
|
|
|
593
593
|
let iter = merged.into_iter().map(async |((param, _), template)| {
|
|
594
|
-
let value =
|
|
595
|
-
.render_string(
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
594
|
+
let value =
|
|
595
|
+
template.render_string(context).await.map_err(|error| {
|
|
596
|
+
RequestBuildErrorKind::QueryRender {
|
|
597
|
+
parameter: param.to_owned(),
|
|
598
|
+
error,
|
|
599
|
+
}
|
|
600
600
|
})?;
|
|
601
601
|
Ok((param.to_owned(), value))
|
|
602
602
|
});
|
|
@@ -637,7 +637,7 @@ impl Recipe {
|
|
|
637
637
|
value_template: &Template,
|
|
638
638
|
) -> Result<(HeaderName, HeaderValue), RequestBuildErrorKind> {
|
|
639
639
|
let mut value: Vec<u8> = value_template
|
|
640
|
-
.render_bytes(
|
|
640
|
+
.render_bytes(context)
|
|
641
641
|
.await
|
|
642
642
|
.map_err(|error| RequestBuildErrorKind::HeaderRender {
|
|
643
643
|
header: header.to_owned(),
|
|
@@ -679,17 +679,16 @@ impl Recipe {
|
|
|
679
679
|
.authentication
|
|
680
680
|
.as_ref()
|
|
681
681
|
.or(self.authentication.as_ref());
|
|
682
|
-
let context = context.streaming(false); // Auth templates never support streaming
|
|
683
682
|
match authentication {
|
|
684
683
|
Some(Authentication::Basic { username, password }) => {
|
|
685
684
|
let (username, password) =
|
|
686
685
|
try_join!(
|
|
687
686
|
username
|
|
688
|
-
.render_string(
|
|
687
|
+
.render_string(context)
|
|
689
688
|
.map_err(RequestBuildErrorKind::AuthUsernameRender),
|
|
690
689
|
async {
|
|
691
690
|
OptionFuture::from(password.as_ref().map(
|
|
692
|
-
|password| password.render_string(
|
|
691
|
+
|password| password.render_string(context),
|
|
693
692
|
))
|
|
694
693
|
.await
|
|
695
694
|
.transpose()
|
|
@@ -701,7 +700,7 @@ impl Recipe {
|
|
|
701
700
|
|
|
702
701
|
Some(Authentication::Bearer { token }) => {
|
|
703
702
|
let token = token
|
|
704
|
-
.render_string(
|
|
703
|
+
.render_string(context)
|
|
705
704
|
.await
|
|
706
705
|
.map_err(RequestBuildErrorKind::AuthTokenRender)?;
|
|
707
706
|
Ok(Some(Authentication::Bearer { token }))
|
|
@@ -735,7 +734,7 @@ impl Recipe {
|
|
|
735
734
|
Some(BodyOverride::Raw(template)),
|
|
736
735
|
) => {
|
|
737
736
|
let rendered = template
|
|
738
|
-
.render_bytes(
|
|
737
|
+
.render_bytes(context)
|
|
739
738
|
.await
|
|
740
739
|
.map_err(RequestBuildErrorKind::BodyRender)?;
|
|
741
740
|
Ok(Some(RenderedBody::Raw(rendered)))
|
|
@@ -748,7 +747,7 @@ impl Recipe {
|
|
|
748
747
|
Some(BodyOverride::Raw(template)),
|
|
749
748
|
) => {
|
|
750
749
|
// Stream body is rendered as a stream (!!)
|
|
751
|
-
let output = template.render(&context.
|
|
750
|
+
let output = template.render(&context.stream()).await;
|
|
752
751
|
let source = output.stream_source().cloned();
|
|
753
752
|
let stream = output
|
|
754
753
|
.try_into_stream()
|
|
@@ -764,7 +763,7 @@ impl Recipe {
|
|
|
764
763
|
| (Some(RecipeBody::Raw(_)), Some(BodyOverride::Json(json)))
|
|
765
764
|
| (Some(RecipeBody::Stream(_)), Some(BodyOverride::Json(json)))
|
|
766
765
|
| (Some(RecipeBody::Json(_)), Some(BodyOverride::Json(json))) => {
|
|
767
|
-
json.
|
|
766
|
+
json.render_json(context)
|
|
768
767
|
.await
|
|
769
768
|
.map(|value| Some(RenderedBody::Json(value)))
|
|
770
769
|
.map_err(RequestBuildErrorKind::BodyRender)
|
|
@@ -776,15 +775,12 @@ impl Recipe {
|
|
|
776
775
|
// handled above
|
|
777
776
|
let merged = apply_overrides(fields, &options.form_fields);
|
|
778
777
|
let iter = merged.into_iter().map(async |(field, template)| {
|
|
779
|
-
let value = template
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
error,
|
|
786
|
-
}
|
|
787
|
-
})?;
|
|
778
|
+
let value = template.render_string(context).await.map_err(
|
|
779
|
+
|error| RequestBuildErrorKind::BodyFormFieldRender {
|
|
780
|
+
field: field.clone(),
|
|
781
|
+
error,
|
|
782
|
+
},
|
|
783
|
+
)?;
|
|
788
784
|
Ok::<_, RequestBuildErrorKind>((field.clone(), value))
|
|
789
785
|
});
|
|
790
786
|
let rendered = try_join_all(iter).await?;
|
|
@@ -793,8 +789,7 @@ impl Recipe {
|
|
|
793
789
|
(Some(RecipeBody::FormMultipart(fields)), None) => {
|
|
794
790
|
let merged = apply_overrides(fields, &options.form_fields);
|
|
795
791
|
let iter = merged.into_iter().map(async |(field, template)| {
|
|
796
|
-
let output =
|
|
797
|
-
template.render(&context.streaming(true)).await;
|
|
792
|
+
let output = template.render(&context.stream()).await;
|
|
798
793
|
// If this is a single-chunk template, we might be able to
|
|
799
794
|
// load directly from the source, since we support file
|
|
800
795
|
// streams natively. In that case, the stream will be thrown
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
use crate::{
|
|
4
4
|
collection::RecipeId,
|
|
5
|
-
render::{FunctionError, Prompt, SelectOption,
|
|
5
|
+
render::{FunctionError, Prompt, SelectOption, TemplateContext},
|
|
6
6
|
};
|
|
7
7
|
use base64::{Engine, prelude::BASE64_STANDARD};
|
|
8
8
|
use bytes::Bytes;
|
|
@@ -124,7 +124,7 @@ pub fn boolean(value: Value) -> bool {
|
|
|
124
124
|
/// ```
|
|
125
125
|
#[template]
|
|
126
126
|
pub fn command(
|
|
127
|
-
#[context] context: &
|
|
127
|
+
#[context] context: &TemplateContext,
|
|
128
128
|
command: Vec<String>,
|
|
129
129
|
#[kwarg] cwd: Option<String>,
|
|
130
130
|
#[kwarg] stdin: Option<Bytes>,
|
|
@@ -304,10 +304,7 @@ pub fn env(variable: String, #[kwarg] default: String) -> String {
|
|
|
304
304
|
/// output: Contents of config.json file
|
|
305
305
|
/// ```
|
|
306
306
|
#[template]
|
|
307
|
-
pub fn file(
|
|
308
|
-
#[context] context: &SingleRenderContext<'_>,
|
|
309
|
-
path: String,
|
|
310
|
-
) -> LazyValue {
|
|
307
|
+
pub fn file(#[context] context: &TemplateContext, path: String) -> LazyValue {
|
|
311
308
|
let path = context.root_dir.join(expand_home(PathBuf::from(path)));
|
|
312
309
|
let source = StreamSource::File { path: path.clone() };
|
|
313
310
|
// Return the file as a stream. If streaming isn't available here, it will
|
|
@@ -870,7 +867,7 @@ pub fn lower(value: String) -> String {
|
|
|
870
867
|
/// ```
|
|
871
868
|
#[template]
|
|
872
869
|
pub async fn prompt(
|
|
873
|
-
#[context] context: &
|
|
870
|
+
#[context] context: &TemplateContext,
|
|
874
871
|
#[kwarg] message: Option<String>,
|
|
875
872
|
#[kwarg] default: Option<String>,
|
|
876
873
|
#[kwarg] sensitive: bool,
|
|
@@ -979,7 +976,7 @@ pub fn replace(
|
|
|
979
976
|
/// ```
|
|
980
977
|
#[template]
|
|
981
978
|
pub async fn response(
|
|
982
|
-
#[context] context: &
|
|
979
|
+
#[context] context: &TemplateContext,
|
|
983
980
|
recipe_id: RecipeId,
|
|
984
981
|
#[kwarg] trigger: RequestTrigger,
|
|
985
982
|
) -> Result<Bytes, FunctionError> {
|
|
@@ -1015,7 +1012,7 @@ pub async fn response(
|
|
|
1015
1012
|
/// ```
|
|
1016
1013
|
#[template]
|
|
1017
1014
|
pub async fn response_header(
|
|
1018
|
-
#[context] context: &
|
|
1015
|
+
#[context] context: &TemplateContext,
|
|
1019
1016
|
recipe_id: RecipeId,
|
|
1020
1017
|
header: String,
|
|
1021
1018
|
#[kwarg] trigger: RequestTrigger,
|
|
@@ -1055,7 +1052,7 @@ pub async fn response_header(
|
|
|
1055
1052
|
/// ```
|
|
1056
1053
|
#[template]
|
|
1057
1054
|
pub async fn select(
|
|
1058
|
-
#[context] context: &
|
|
1055
|
+
#[context] context: &TemplateContext,
|
|
1059
1056
|
options: Vec<SelectOption>,
|
|
1060
1057
|
#[kwarg] message: Option<String>,
|
|
1061
1058
|
) -> Result<Value, FunctionError> {
|
|
@@ -1107,7 +1104,7 @@ impl TryFromValue for SelectOption {
|
|
|
1107
1104
|
/// ```
|
|
1108
1105
|
#[template]
|
|
1109
1106
|
pub fn sensitive(
|
|
1110
|
-
#[context] context: &
|
|
1107
|
+
#[context] context: &TemplateContext,
|
|
1111
1108
|
value: String,
|
|
1112
1109
|
) -> String {
|
|
1113
1110
|
mask_sensitive(context, value)
|
|
@@ -1310,7 +1307,7 @@ pub fn upper(value: String) -> String {
|
|
|
1310
1307
|
value.to_uppercase()
|
|
1311
1308
|
}
|
|
1312
1309
|
|
|
1313
|
-
fn mask_sensitive(context: &
|
|
1310
|
+
fn mask_sensitive(context: &TemplateContext, value: String) -> String {
|
|
1314
1311
|
if context.show_sensitive {
|
|
1315
1312
|
value
|
|
1316
1313
|
} else {
|