vector_config/schema/parser/
component.rs

1use std::borrow::Cow;
2
3use serde_json::Value;
4use snafu::Snafu;
5use vector_config_common::{
6    attributes::CustomAttribute,
7    constants::{self, ComponentType},
8    schema::SchemaObject,
9};
10
11use super::query::{OneOrMany, QueryError, QueryableSchema, SchemaType, SimpleSchema};
12
13#[derive(Debug, Snafu)]
14pub enum SchemaError {
15    #[snafu(display("invalid component schema: {pointer}: {reason}"))]
16    InvalidComponentSchema {
17        pointer: &'static str,
18        reason: Cow<'static, str>,
19    },
20}
21
22impl SchemaError {
23    pub fn invalid_component_schema<S: Into<Cow<'static, str>>>(
24        pointer: &'static str,
25        reason: S,
26    ) -> Self {
27        Self::InvalidComponentSchema {
28            pointer,
29            reason: reason.into(),
30        }
31    }
32}
33
34/// A schema object that represents the schema of a single Vector component.
35///
36/// The schema represents the equivalent of the component's configuration type, excluding any common
37/// configuration fields that appear on a per-component type basis. This means that, for a sink
38/// component, this schema would include the configuration fields of the specific sink component,
39/// but wouldn't contain the common sink configuration fields such as `inputs` or `buffer`.
40pub struct ComponentSchema<'a> {
41    schema: &'a SchemaObject,
42    component_name: String,
43    component_type: ComponentType,
44}
45
46impl ComponentSchema<'_> {
47    /// The type of the component represented by this schema.
48    pub fn component_type(&self) -> ComponentType {
49        self.component_type
50    }
51
52    /// The name of the component represented by this schema.
53    ///
54    /// This refers to the configuration-specific identifier used to specify the component type
55    /// within the `type` field.
56    ///
57    /// For example, the AWS S3 sink would be `aws_s3`.
58    pub fn component_name(&self) -> &str {
59        &self.component_name
60    }
61}
62
63impl QueryableSchema for ComponentSchema<'_> {
64    fn schema_type(&self) -> SchemaType {
65        self.schema.schema_type()
66    }
67
68    fn description(&self) -> Option<&str> {
69        self.schema.description()
70    }
71
72    fn title(&self) -> Option<&str> {
73        self.schema.title()
74    }
75
76    fn get_attributes(&self, key: &str) -> Option<OneOrMany<CustomAttribute>> {
77        self.schema.get_attributes(key)
78    }
79
80    fn get_attribute(&self, key: &str) -> Result<Option<CustomAttribute>, QueryError> {
81        self.schema.get_attribute(key)
82    }
83
84    fn has_flag_attribute(&self, key: &str) -> Result<bool, QueryError> {
85        self.schema.has_flag_attribute(key)
86    }
87}
88
89impl<'a> TryFrom<SimpleSchema<'a>> for ComponentSchema<'a> {
90    type Error = SchemaError;
91
92    fn try_from(value: SimpleSchema<'a>) -> Result<Self, Self::Error> {
93        // Component schemas must have a component type _and_ component name defined.
94        let component_type =
95            get_component_metadata_kv_str(&value, constants::DOCS_META_COMPONENT_TYPE).and_then(
96                |s| {
97                    ComponentType::try_from(s.as_str()).map_err(|_| {
98                        SchemaError::invalid_component_schema(
99                            constants::DOCS_META_COMPONENT_TYPE,
100                            "value was not a valid component type",
101                        )
102                    })
103                },
104            )?;
105
106        let component_name =
107            get_component_metadata_kv_str(&value, constants::DOCS_META_COMPONENT_NAME)?;
108
109        Ok(Self {
110            schema: value.into_inner(),
111            component_name,
112            component_type,
113        })
114    }
115}
116
117fn get_component_metadata_kv_str<'a>(
118    schema: &'a SimpleSchema<'a>,
119    key: &'static str,
120) -> Result<String, SchemaError> {
121    schema
122        .get_attribute(key)
123        .map_err(|e| SchemaError::invalid_component_schema(key, e.to_string()))?
124        .ok_or_else(|| SchemaError::invalid_component_schema(key, "attribute must be present"))
125        .and_then(|attr| match attr {
126            CustomAttribute::Flag(_) => Err(SchemaError::invalid_component_schema(
127                key,
128                "expected key/value attribute, got flag instead",
129            )),
130            CustomAttribute::KeyValue { value, .. } => Ok(value),
131        })
132        .and_then(|v| match v {
133            Value::String(name) => Ok(name),
134            _ => Err(SchemaError::invalid_component_schema(
135                key,
136                format!("`{key}` must be a string"),
137            )),
138        })
139}