1use std::collections::BTreeMap;
10
11use serde_json::{Value, json};
12
13use crate::registry;
14use crate::resources::ResourceKind;
15
16pub fn created_resources(ks_doc: &Value) -> BTreeMap<ResourceKind, String> {
20 let mut out = BTreeMap::new();
21 walk_created(ks_doc, &mut out);
22 out
23}
24
25fn walk_created(v: &Value, out: &mut BTreeMap<ResourceKind, String>) {
26 match v {
27 Value::Object(map) => {
28 if let Some(Value::Object(created)) = map.get("createdResources") {
29 for (member, name) in created {
30 let kind = match member.as_str() {
31 "datasource" => Some(ResourceKind::DataSource),
32 "indexer" => Some(ResourceKind::Indexer),
33 "skillset" => Some(ResourceKind::Skillset),
34 "index" => Some(ResourceKind::Index),
35 _ => None, };
37 if let (Some(kind), Some(name)) = (kind, name.as_str()) {
38 out.insert(kind, name.to_string());
39 }
40 }
41 }
42 for val in map.values() {
43 walk_created(val, out);
44 }
45 }
46 Value::Array(arr) => {
47 for item in arr {
48 walk_created(item, out);
49 }
50 }
51 _ => {}
52 }
53}
54
55pub fn is_indexed_with_created(ks_doc: &Value) -> bool {
60 ks_doc.get("kind").and_then(Value::as_str) != Some("searchIndex")
61 && !created_resources(ks_doc).is_empty()
62}
63
64pub fn to_search_index_ks(ks_doc: &Value, index_name: &str) -> Value {
68 let mut out = json!({
69 "name": ks_doc.get("name").cloned().unwrap_or(Value::Null),
70 "kind": "searchIndex",
71 "searchIndexParameters": { "searchIndexName": index_name }
72 });
73 if let Some(desc) = ks_doc.get("description").filter(|d| !d.is_null()) {
74 out["description"] = desc.clone();
75 }
76 out
77}
78
79pub fn derive_names(
84 old_ks: &str,
85 new_ks: &str,
86 created: &BTreeMap<ResourceKind, String>,
87) -> BTreeMap<ResourceKind, String> {
88 created
89 .iter()
90 .map(|(kind, name)| {
91 let derived = match name.strip_prefix(old_ks) {
92 Some(rest) => format!("{new_ks}{rest}"),
93 None => {
94 let suffix = match kind {
95 ResourceKind::DataSource => "datasource",
96 ResourceKind::Index => "index",
97 ResourceKind::Skillset => "skillset",
98 ResourceKind::Indexer => "indexer",
99 other => registry::meta(*other).dir_name,
100 };
101 format!("{new_ks}-{suffix}")
102 }
103 };
104 (*kind, derived)
105 })
106 .collect()
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 fn blob_ks() -> Value {
114 json!({
115 "name": "regulatory",
116 "kind": "azureBlob",
117 "description": "Legal docs.",
118 "azureBlobParameters": {
119 "containerName": "regulatory",
120 "createdResources": {
121 "datasource": "regulatory-datasource",
122 "indexer": "regulatory-indexer",
123 "skillset": "regulatory-skillset",
124 "index": "regulatory-index",
125 "somethingFuture": "ignored-name"
126 }
127 }
128 })
129 }
130
131 #[test]
132 fn created_resources_reads_nested_map() {
133 let map = created_resources(&blob_ks());
134 assert_eq!(map.len(), 4);
135 assert_eq!(map[&ResourceKind::Index], "regulatory-index");
136 assert_eq!(map[&ResourceKind::DataSource], "regulatory-datasource");
137 assert_eq!(map[&ResourceKind::Skillset], "regulatory-skillset");
138 assert_eq!(map[&ResourceKind::Indexer], "regulatory-indexer");
139 }
140
141 #[test]
142 fn is_indexed_with_created_true_for_blob_false_otherwise() {
143 assert!(is_indexed_with_created(&blob_ks()));
144 let search_index = json!({"name": "ks", "kind": "searchIndex",
145 "searchIndexParameters": {"searchIndexName": "docs"}});
146 assert!(!is_indexed_with_created(&search_index));
147 let web = json!({"name": "ks", "kind": "web", "webParameters": {}});
148 assert!(!is_indexed_with_created(&web));
149 }
150
151 #[test]
152 fn to_search_index_ks_preserves_name_and_description() {
153 let ks = to_search_index_ks(&blob_ks(), "regulatory-index");
154 assert_eq!(
155 ks,
156 json!({
157 "name": "regulatory",
158 "kind": "searchIndex",
159 "description": "Legal docs.",
160 "searchIndexParameters": {"searchIndexName": "regulatory-index"}
161 })
162 );
163 }
164
165 #[test]
166 fn to_search_index_ks_omits_missing_description() {
167 let ks = json!({"name": "n", "kind": "azureBlob", "description": null});
168 let out = to_search_index_ks(&ks, "i");
169 assert!(out.get("description").is_none());
170 }
171
172 #[test]
173 fn derive_names_swaps_prefix() {
174 let created = created_resources(&blob_ks());
175 let names = derive_names("regulatory", "reg2", &created);
176 assert_eq!(names[&ResourceKind::Index], "reg2-index");
177 assert_eq!(names[&ResourceKind::DataSource], "reg2-datasource");
178 assert_eq!(names[&ResourceKind::Skillset], "reg2-skillset");
179 assert_eq!(names[&ResourceKind::Indexer], "reg2-indexer");
180 }
181
182 #[test]
183 fn derive_names_falls_back_to_conventional_suffix() {
184 let mut created = BTreeMap::new();
185 created.insert(ResourceKind::Index, "weird-name".to_string());
186 let names = derive_names("regulatory", "reg2", &created);
187 assert_eq!(names[&ResourceKind::Index], "reg2-index");
188 }
189}