drasi_bootstrap_postgres/
descriptor.rs1use drasi_lib::bootstrap::BootstrapProvider;
18use drasi_plugin_sdk::prelude::*;
19use utoipa::OpenApi;
20
21use crate::config::{SslMode, TableKeyConfig};
22use crate::PostgresBootstrapConfig;
23use crate::PostgresBootstrapProvider;
24
25#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, utoipa::ToSchema)]
29#[schema(as = bootstrap::postgres::SslMode)]
30#[serde(rename_all = "lowercase")]
31#[derive(Default)]
32pub enum SslModeDto {
33 Disable,
34 #[default]
35 Prefer,
36 Require,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, utoipa::ToSchema)]
41#[schema(as = bootstrap::postgres::TableKeyConfig)]
42#[serde(rename_all = "camelCase", deny_unknown_fields)]
43pub struct TableKeyConfigDto {
44 pub table: String,
45 pub key_columns: Vec<String>,
46}
47
48fn default_host() -> ConfigValue<String> {
49 ConfigValue::Static("localhost".to_string()) }
51
52fn default_port() -> ConfigValue<u16> {
53 ConfigValue::Static(5432)
54}
55
56fn default_password() -> ConfigValue<String> {
57 ConfigValue::Static(String::new())
58}
59
60fn default_slot_name() -> String {
61 "drasi_slot".to_string()
62}
63
64fn default_publication_name() -> String {
65 "drasi_publication".to_string()
66}
67
68fn default_ssl_mode() -> ConfigValue<SslModeDto> {
69 ConfigValue::Static(SslModeDto::default())
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, utoipa::ToSchema)]
74#[schema(as = bootstrap::postgres::PostgresBootstrapConfig)]
75#[serde(rename_all = "camelCase", deny_unknown_fields)]
76pub struct PostgresBootstrapConfigDto {
77 #[serde(default = "default_host")]
78 #[schema(value_type = ConfigValueString)]
79 pub host: ConfigValue<String>,
80
81 #[serde(default = "default_port")]
82 #[schema(value_type = ConfigValueU16)]
83 pub port: ConfigValue<u16>,
84
85 #[schema(value_type = ConfigValueString)]
86 pub database: ConfigValue<String>,
87
88 #[schema(value_type = ConfigValueString)]
89 pub user: ConfigValue<String>,
90
91 #[serde(default = "default_password")]
92 #[schema(value_type = ConfigValueString)]
93 pub password: ConfigValue<String>,
94
95 #[serde(default)]
96 pub tables: Vec<String>,
97
98 #[serde(default = "default_slot_name")]
99 pub slot_name: String,
100
101 #[serde(default = "default_publication_name")]
102 pub publication_name: String,
103
104 #[serde(default = "default_ssl_mode")]
105 #[schema(value_type = ConfigValue<bootstrap::postgres::SslMode>)]
106 pub ssl_mode: ConfigValue<SslModeDto>,
107
108 #[serde(default)]
109 #[schema(value_type = Vec<bootstrap::postgres::TableKeyConfig>)]
110 pub table_keys: Vec<TableKeyConfigDto>,
111}
112
113#[derive(OpenApi)]
116#[openapi(components(schemas(PostgresBootstrapConfigDto, SslModeDto, TableKeyConfigDto,)))]
117struct PostgresBootstrapSchemas;
118
119pub struct PostgresBootstrapDescriptor;
121
122#[async_trait]
123impl BootstrapPluginDescriptor for PostgresBootstrapDescriptor {
124 fn kind(&self) -> &str {
125 "postgres"
126 }
127
128 fn config_version(&self) -> &str {
129 "1.0.0"
130 }
131
132 fn config_schema_name(&self) -> &str {
133 "bootstrap.postgres.PostgresBootstrapConfig"
134 }
135
136 fn config_schema_json(&self) -> String {
137 let api = PostgresBootstrapSchemas::openapi();
138 serde_json::to_string(
139 &api.components
140 .as_ref()
141 .expect("OpenAPI components missing")
142 .schemas,
143 )
144 .expect("Failed to serialize config schema")
145 }
146
147 async fn create_bootstrap_provider(
148 &self,
149 config_json: &serde_json::Value,
150 _source_config_json: &serde_json::Value,
151 ) -> anyhow::Result<Box<dyn BootstrapProvider>> {
152 let dto: PostgresBootstrapConfigDto = serde_json::from_value(config_json.clone())?;
153 let mapper = DtoMapper::new();
154
155 let ssl_mode = match dto.ssl_mode {
156 ConfigValue::Static(v) => match v {
157 SslModeDto::Disable => SslMode::Disable,
158 SslModeDto::Prefer => SslMode::Prefer,
159 SslModeDto::Require => SslMode::Require,
160 },
161 _ => SslMode::default(),
162 };
163
164 let table_keys = dto
165 .table_keys
166 .into_iter()
167 .map(|tk| TableKeyConfig {
168 table: tk.table,
169 key_columns: tk.key_columns,
170 })
171 .collect();
172
173 let config = PostgresBootstrapConfig {
174 host: mapper.resolve_string(&dto.host)?,
175 port: mapper.resolve_typed(&dto.port)?,
176 database: mapper.resolve_string(&dto.database)?,
177 user: mapper.resolve_string(&dto.user)?,
178 password: mapper.resolve_string(&dto.password)?,
179 tables: dto.tables,
180 slot_name: dto.slot_name,
181 publication_name: dto.publication_name,
182 ssl_mode,
183 table_keys,
184 };
185
186 config.validate()?;
187 Ok(Box::new(PostgresBootstrapProvider::new(config)))
188 }
189}