hoist_core/resources/
knowledge_source.rs1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use super::traits::{Resource, ResourceKind};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub struct KnowledgeSource {
12 pub name: String,
13 pub index_name: String,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub description: Option<String>,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub knowledge_base_name: Option<String>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub query_type: Option<String>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub semantic_configuration: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub top: Option<i32>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub filter: Option<String>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub select_fields: Option<Vec<String>>,
28 #[serde(flatten)]
30 pub extra: std::collections::HashMap<String, Value>,
31}
32
33impl Resource for KnowledgeSource {
34 fn kind() -> ResourceKind {
35 ResourceKind::KnowledgeSource
36 }
37
38 fn name(&self) -> &str {
39 &self.name
40 }
41
42 fn volatile_fields() -> &'static [&'static str] {
43 &["@odata.etag", "@odata.context"]
44 }
45
46 fn read_only_fields() -> &'static [&'static str] {
47 &["ingestionPermissionOptions", "createdResources"]
51 }
52
53 fn dependencies(&self) -> Vec<(ResourceKind, String)> {
54 let mut deps = vec![(ResourceKind::Index, self.index_name.clone())];
55 if let Some(ref kb) = self.knowledge_base_name {
56 deps.push((ResourceKind::KnowledgeBase, kb.clone()));
57 }
58 deps
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn test_knowledge_source_kind() {
68 assert_eq!(KnowledgeSource::kind(), ResourceKind::KnowledgeSource);
69 }
70
71 #[test]
72 fn test_knowledge_source_dependencies_without_kb() {
73 let ks = KnowledgeSource {
74 name: "ks1".to_string(),
75 index_name: "idx".to_string(),
76 description: None,
77 knowledge_base_name: None,
78 query_type: None,
79 semantic_configuration: None,
80 top: None,
81 filter: None,
82 select_fields: None,
83 extra: Default::default(),
84 };
85 let deps = ks.dependencies();
86 assert_eq!(deps.len(), 1);
87 assert_eq!(deps[0], (ResourceKind::Index, "idx".to_string()));
88 }
89
90 #[test]
91 fn test_knowledge_source_volatile_fields() {
92 let fields = KnowledgeSource::volatile_fields();
93 assert!(fields.contains(&"@odata.etag"));
94 assert!(fields.contains(&"@odata.context"));
95 assert!(!fields.contains(&"ingestionPermissionOptions"));
97 assert!(!fields.contains(&"createdResources"));
98 }
99
100 #[test]
101 fn test_knowledge_source_read_only_fields() {
102 let fields = KnowledgeSource::read_only_fields();
103 assert!(
104 fields.contains(&"ingestionPermissionOptions"),
105 "ingestionPermissionOptions is read-only: kept in local, stripped before push"
106 );
107 assert!(
108 fields.contains(&"createdResources"),
109 "createdResources is read-only: kept in local, stripped before push"
110 );
111 }
112
113 #[test]
114 fn test_knowledge_source_dependencies_with_kb() {
115 let ks = KnowledgeSource {
116 name: "ks1".to_string(),
117 index_name: "idx".to_string(),
118 description: None,
119 knowledge_base_name: Some("kb1".to_string()),
120 query_type: None,
121 semantic_configuration: None,
122 top: None,
123 filter: None,
124 select_fields: None,
125 extra: Default::default(),
126 };
127 let deps = ks.dependencies();
128 assert_eq!(deps.len(), 2);
129 assert!(deps.contains(&(ResourceKind::KnowledgeBase, "kb1".to_string())));
130 }
131}