wick_config/config/components/
http_client.rs

1#![allow(missing_docs)] // delete when we move away from the `property` crate.
2use std::borrow::Cow;
3use std::collections::HashMap;
4
5use wick_interface_types::OperationSignatures;
6
7use super::{ComponentConfig, OperationConfig};
8use crate::config::bindings::BoundIdentifier;
9use crate::config::{self, Codec, HttpMethod};
10
11#[derive(
12  Debug,
13  Clone,
14  derive_builder::Builder,
15  PartialEq,
16  derive_asset_container::AssetManager,
17  property::Property,
18  serde::Serialize,
19)]
20#[property(get(public), set(public), mut(public, suffix = "_mut"))]
21#[asset(asset(config::AssetReference))]
22#[builder(setter(into))]
23#[must_use]
24/// A component made out of other components
25pub struct HttpClientComponentConfig {
26  /// The URL base to use.
27  #[asset(skip)]
28  pub(crate) resource: BoundIdentifier,
29
30  /// The configuration for the component.
31  #[asset(skip)]
32  #[builder(default)]
33  #[serde(skip_serializing_if = "Vec::is_empty")]
34  pub(crate) config: Vec<wick_interface_types::Field>,
35
36  /// The codec to use when encoding/decoding data.
37  #[asset(skip)]
38  #[builder(default)]
39  #[serde(skip_serializing_if = "Option::is_none")]
40  pub(crate) codec: Option<Codec>,
41
42  /// The proxy to use when connecting to server.
43  #[asset(skip)]
44  #[builder(default)]
45  #[serde(skip_serializing_if = "Option::is_none")]
46  pub(crate) proxy: Option<Proxy>,
47
48  /// The timeout for requests in seconds
49  #[asset(skip)]
50  #[builder(default)]
51  #[serde(skip_serializing_if = "Option::is_none")]
52  pub(crate) timeout: Option<u16>,
53
54  /// A list of operations to expose on this component.
55  #[asset(skip)]
56  #[builder(default)]
57  #[property(skip)]
58  #[serde(skip_serializing_if = "Vec::is_empty")]
59  pub(crate) operations: Vec<HttpClientOperationDefinition>,
60}
61
62impl HttpClientComponentConfig {}
63
64#[derive(Debug, Clone, derive_builder::Builder, PartialEq, property::Property, serde::Serialize)]
65#[property(get(public), set(private), mut(disable))]
66#[builder(setter(into))]
67#[must_use]
68/// A proxy to use when connecting to server.
69pub struct Proxy {
70  /// The URL base to use.
71  pub(crate) resource: BoundIdentifier,
72
73  /// username for proxy authentication
74  #[builder(default)]
75  pub(crate) username: Option<String>,
76
77  /// password for proxy authentication
78  #[builder(default)]
79  pub(crate) password: Option<String>,
80}
81
82impl Proxy {}
83
84impl OperationSignatures for HttpClientComponentConfig {
85  fn operation_signatures(&self) -> Vec<wick_interface_types::OperationSignature> {
86    let codec = self.codec;
87    self
88      .operations
89      .clone()
90      .into_iter()
91      .map(|mut op| {
92        op.codec = op.codec.or(codec);
93        op
94      })
95      .map(Into::into)
96      .collect()
97  }
98}
99
100impl ComponentConfig for HttpClientComponentConfig {
101  type Operation = HttpClientOperationDefinition;
102
103  fn operations(&self) -> &[Self::Operation] {
104    &self.operations
105  }
106
107  fn operations_mut(&mut self) -> &mut Vec<Self::Operation> {
108    &mut self.operations
109  }
110}
111
112impl OperationConfig for HttpClientOperationDefinition {
113  fn name(&self) -> &str {
114    &self.name
115  }
116
117  fn inputs(&self) -> Cow<Vec<wick_interface_types::Field>> {
118    Cow::Borrowed(&self.inputs)
119  }
120
121  fn outputs(&self) -> Cow<Vec<wick_interface_types::Field>> {
122    Cow::Owned(vec![
123      // TODO: support actual HTTP Response type.
124      wick_interface_types::Field::new("response", wick_interface_types::Type::Object),
125      wick_interface_types::Field::new(
126        "body",
127        match self.codec {
128          Some(Codec::Json) => wick_interface_types::Type::Object,
129          Some(Codec::Raw) => wick_interface_types::Type::Bytes,
130          Some(Codec::FormData) => wick_interface_types::Type::Object,
131          Some(Codec::Text) => wick_interface_types::Type::Object,
132          None => wick_interface_types::Type::Object,
133        },
134      ),
135    ])
136  }
137}
138
139impl From<HttpClientOperationDefinition> for wick_interface_types::OperationSignature {
140  fn from(operation: HttpClientOperationDefinition) -> Self {
141    Self::new(
142      operation.name,
143      operation.inputs,
144      vec![
145        // TODO: support actual HTTP Response type.
146        wick_interface_types::Field::new("response", wick_interface_types::Type::Object),
147        wick_interface_types::Field::new(
148          "body",
149          match operation.codec {
150            Some(Codec::Json) => wick_interface_types::Type::Object,
151            Some(Codec::Raw) => wick_interface_types::Type::Bytes,
152            Some(Codec::FormData) => wick_interface_types::Type::Object,
153            Some(Codec::Text) => wick_interface_types::Type::Object,
154            None => wick_interface_types::Type::Object,
155          },
156        ),
157      ],
158      operation.config,
159    )
160  }
161}
162
163#[derive(Debug, Clone, derive_builder::Builder, PartialEq, property::Property, serde::Serialize)]
164#[property(get(public), set(private), mut(disable))]
165#[builder(setter(into))]
166#[must_use]
167/// An operation whose implementation is an HTTP request.
168pub struct HttpClientOperationDefinition {
169  /// The name of the operation.
170  #[property(skip)]
171  pub(crate) name: String,
172
173  /// The configuration the operation needs.
174  #[builder(default)]
175  #[serde(skip_serializing_if = "Vec::is_empty")]
176  pub(crate) config: Vec<wick_interface_types::Field>,
177
178  /// Types of the inputs to the operation.
179  #[property(skip)]
180  #[serde(skip_serializing_if = "Vec::is_empty")]
181  pub(crate) inputs: Vec<wick_interface_types::Field>,
182
183  /// The path to append to our base URL, processed as a liquid template with each input as part of the template data.
184  pub(crate) path: String,
185
186  /// The codec to use when encoding/decoding data.
187  #[builder(default)]
188  #[serde(skip_serializing_if = "Option::is_none")]
189  pub(crate) codec: Option<Codec>,
190
191  /// The body to send with the request.
192  #[builder(default)]
193  #[serde(skip_serializing_if = "Option::is_none")]
194  pub(crate) body: Option<liquid_json::LiquidJsonValue>,
195
196  /// The headers to send with the request.
197  #[builder(default)]
198  #[serde(skip_serializing_if = "Option::is_none")]
199  pub(crate) headers: Option<HashMap<String, Vec<String>>>,
200
201  /// The HTTP method to use.
202  pub(crate) method: HttpMethod,
203}
204
205impl HttpClientOperationDefinition {
206  /// Create a new GET operation.
207  #[must_use]
208  pub fn new_get(
209    name: &str,
210    path: &str,
211    inputs: Vec<wick_interface_types::Field>,
212    headers: Option<HashMap<String, Vec<String>>>,
213  ) -> HttpClientOperationDefinitionBuilder {
214    let mut builder = HttpClientOperationDefinitionBuilder::default();
215    builder
216      .name(name)
217      .path(path)
218      .inputs(inputs)
219      .headers(headers)
220      .method(HttpMethod::Get);
221    builder
222  }
223
224  /// Create a new POST operation.
225  #[must_use]
226  pub fn new_post(
227    name: &str,
228    path: &str,
229    inputs: Vec<wick_interface_types::Field>,
230    body: Option<liquid_json::LiquidJsonValue>,
231    headers: Option<HashMap<String, Vec<String>>>,
232  ) -> HttpClientOperationDefinitionBuilder {
233    let mut builder = HttpClientOperationDefinitionBuilder::default();
234    builder
235      .name(name)
236      .path(path)
237      .inputs(inputs)
238      .body(body)
239      .headers(headers)
240      .method(HttpMethod::Post);
241    builder
242  }
243
244  /// Create a new PUT operation.
245  #[must_use]
246  pub fn new_put(
247    name: &str,
248    path: &str,
249    inputs: Vec<wick_interface_types::Field>,
250    body: Option<liquid_json::LiquidJsonValue>,
251    headers: Option<HashMap<String, Vec<String>>>,
252  ) -> HttpClientOperationDefinitionBuilder {
253    let mut builder = HttpClientOperationDefinitionBuilder::default();
254    builder
255      .name(name)
256      .path(path)
257      .inputs(inputs)
258      .body(body)
259      .headers(headers)
260      .method(HttpMethod::Put);
261    builder
262  }
263
264  /// Create a new DELETE operation.
265  #[must_use]
266  pub fn new_delete(
267    name: &str,
268    path: &str,
269    inputs: Vec<wick_interface_types::Field>,
270    body: Option<liquid_json::LiquidJsonValue>,
271    headers: Option<HashMap<String, Vec<String>>>,
272  ) -> HttpClientOperationDefinitionBuilder {
273    let mut builder = HttpClientOperationDefinitionBuilder::default();
274    builder
275      .name(name)
276      .path(path)
277      .inputs(inputs)
278      .body(body)
279      .headers(headers)
280      .method(HttpMethod::Delete);
281    builder
282  }
283}