wick_config/config/app_config/triggers/http/
rest_router.rs

1use std::collections::HashMap;
2use std::path::Path;
3
4use wick_asset_reference::AssetReference;
5use wick_packet::RuntimeConfig;
6
7use super::index_to_router_id;
8use super::middleware::expand_for_middleware_components;
9use crate::config::common::HttpMethod;
10use crate::config::template_config::Renderable;
11use crate::config::{self, Binding, ComponentOperationExpression, ImportDefinition};
12use crate::error::ManifestError;
13
14#[derive(
15  Debug,
16  Clone,
17  PartialEq,
18  derive_builder::Builder,
19  derive_asset_container::AssetManager,
20  property::Property,
21  serde::Serialize,
22)]
23#[asset(asset(AssetReference))]
24#[property(get(public), set(private), mut(public, suffix = "_mut"))]
25pub struct RestRouterConfig {
26  /// The path to start serving this router from.
27  #[asset(skip)]
28  #[property(get(disable))]
29  pub(crate) path: String,
30  /// Middleware operations for this router.
31  #[property(get(disable), mut(disable))]
32  #[serde(skip_serializing_if = "Option::is_none")]
33  pub(crate) middleware: Option<super::middleware::Middleware>,
34  /// Additional tools and services to enable.
35  #[asset(skip)]
36  #[serde(skip_serializing_if = "Option::is_none")]
37  pub(crate) tools: Option<Tools>,
38  /// The routes to serve and operations that handle them.
39  #[serde(skip_serializing_if = "Vec::is_empty")]
40  pub(crate) routes: Vec<RestRoute>,
41  /// Information about the router to use when generating documentation and other tools.
42  #[asset(skip)]
43  #[serde(skip_serializing_if = "Option::is_none")]
44  pub(crate) info: Option<Info>,
45}
46
47impl Renderable for RestRouterConfig {
48  fn render_config(
49    &mut self,
50    source: Option<&Path>,
51    root_config: Option<&RuntimeConfig>,
52    env: Option<&HashMap<String, String>>,
53  ) -> Result<(), ManifestError> {
54    self.middleware.render_config(source, root_config, env)?;
55    self.routes.render_config(source, root_config, env)
56  }
57}
58
59impl Renderable for RestRoute {
60  fn render_config(
61    &mut self,
62    source: Option<&Path>,
63    root_config: Option<&RuntimeConfig>,
64    env: Option<&HashMap<String, String>>,
65  ) -> Result<(), ManifestError> {
66    self.operation.render_config(source, root_config, env)
67  }
68}
69
70impl super::WickRouter for RestRouterConfig {
71  fn middleware(&self) -> Option<&super::Middleware> {
72    self.middleware.as_ref()
73  }
74  fn middleware_mut(&mut self) -> Option<&mut super::Middleware> {
75    self.middleware.as_mut()
76  }
77
78  fn path(&self) -> &str {
79    &self.path
80  }
81}
82
83#[derive(Debug, Default, Clone, PartialEq, property::Property, serde::Serialize)]
84#[property(get(public), set(disable), mut(disable))]
85#[allow(missing_copy_implementations)]
86pub struct Tools {
87  /// Set to true to generate an OpenAPI specification and serve it at *router_path*/openapi.json
88  pub(crate) openapi: bool,
89}
90
91#[derive(Debug, Default, Clone, PartialEq, property::Property, serde::Serialize)]
92#[property(get(public), set(private), mut(disable))]
93/// Information about the router to use when generating documentation and other tools.
94pub struct Info {
95  /// The title of the API.
96  #[serde(skip_serializing_if = "Option::is_none")]
97  pub(crate) title: Option<String>,
98  /// A short description of the API.
99  #[serde(skip_serializing_if = "Option::is_none")]
100  pub(crate) description: Option<String>,
101  /// The terms of service for the API.
102  #[serde(skip_serializing_if = "Option::is_none")]
103  pub(crate) tos: Option<String>,
104  /// The contact information for the API.
105  #[serde(skip_serializing_if = "Option::is_none")]
106  pub(crate) contact: Option<Contact>,
107  /// The license information for the API.
108  #[serde(skip_serializing_if = "Option::is_none")]
109  pub(crate) license: Option<License>,
110  /// The version of the API.
111  pub(crate) version: String,
112  /// The URL to the API&#x27;s terms of service.
113  #[serde(skip_serializing_if = "Option::is_none")]
114  pub(crate) documentation: Option<Documentation>,
115}
116
117#[derive(Debug, Default, Clone, PartialEq, property::Property, serde::Serialize)]
118#[property(get(public), set(private), mut(disable))]
119/// Documentation information for the API.
120pub struct Documentation {
121  /// The URL to the API&#x27;s documentation.
122  #[serde(skip_serializing_if = "Option::is_none")]
123  pub(crate) url: Option<String>,
124  /// A short description of the documentation.
125  #[serde(skip_serializing_if = "Option::is_none")]
126  pub(crate) description: Option<String>,
127}
128
129#[derive(Debug, Default, Clone, PartialEq, property::Property, serde::Serialize)]
130#[property(get(public), set(private), mut(disable))]
131/// The license information for the API.
132pub struct License {
133  /// The name of the license.
134  pub(crate) name: String,
135  /// The URL to the license.
136  #[serde(skip_serializing_if = "Option::is_none")]
137  pub(crate) url: Option<String>,
138}
139
140#[derive(Debug, Default, Clone, PartialEq, property::Property, serde::Serialize)]
141#[property(get(public), set(private), mut(disable))]
142/// The contact information for the API.
143pub struct Contact {
144  /// The name of the contact.
145  #[serde(skip_serializing_if = "Option::is_none")]
146  pub(crate) name: Option<String>,
147  /// The URL to the contact.
148  #[serde(skip_serializing_if = "Option::is_none")]
149  pub(crate) url: Option<String>,
150  /// The email address of the contact.
151  #[serde(skip_serializing_if = "Option::is_none")]
152  pub(crate) email: Option<String>,
153}
154
155#[derive(
156  Debug,
157  Clone,
158  PartialEq,
159  derive_builder::Builder,
160  derive_asset_container::AssetManager,
161  property::Property,
162  serde::Serialize,
163)]
164#[asset(asset(AssetReference))]
165#[property(get(public), set(private), mut(public, suffix = "_mut"))]
166/// A route to serve and the operation that handles it.
167pub struct RestRoute {
168  /// The name of the route, used for documentation and tooling.
169  #[asset(skip)]
170  #[serde(skip_serializing_if = "Option::is_none")]
171  pub(crate) id: Option<String>,
172  /// The HTTP methods to serve this route for.
173  #[asset(skip)]
174  #[serde(skip_serializing_if = "Vec::is_empty")]
175  pub(crate) methods: Vec<HttpMethod>,
176  /// The path to serve this route from.
177  #[asset(skip)]
178  pub(crate) sub_path: String,
179  /// The operation that will act as the main entrypoint for this route.
180  pub(crate) operation: ComponentOperationExpression,
181  /// A short description of the route.
182  #[asset(skip)]
183  #[serde(skip_serializing_if = "Option::is_none")]
184  pub(crate) description: Option<String>,
185  /// A longer description of the route.
186  #[asset(skip)]
187  #[serde(skip_serializing_if = "Option::is_none")]
188  pub(crate) summary: Option<String>,
189}
190
191pub(crate) fn process_runtime_config(
192  trigger_index: usize,
193  index: usize,
194  router_config: &mut RestRouterConfig,
195  bindings: &mut Vec<Binding<ImportDefinition>>,
196) -> Result<(), ManifestError> {
197  expand_for_middleware_components(trigger_index, index, router_config, bindings)?;
198
199  for (i, route) in router_config.routes_mut().iter_mut().enumerate() {
200    let component_id = format!("{}_{}", index_to_router_id(trigger_index, index), i);
201    route.operation_mut().maybe_import(&component_id, bindings);
202  }
203
204  let router_component = config::ComponentDefinition::Native(config::components::NativeComponent {});
205  let router_binding = config::Binding::new(
206    index_to_router_id(trigger_index, index),
207    ImportDefinition::component(router_component),
208  );
209  bindings.push(router_binding);
210  Ok(())
211}