use serde_json::{Value, json};
use crate::state::{IndexMeta, OpenSearchState};
pub fn create_index(state: &OpenSearchState, index_name: &str, body: &Value) -> (u16, Value) {
if state.index_exists(index_name) {
return (
400,
json!({
"error": {
"root_cause": [{"type": "resource_already_exists_exception", "reason": format!("index [{}] already exists", index_name)}],
"type": "resource_already_exists_exception",
"reason": format!("index [{}] already exists", index_name),
},
"status": 400,
}),
);
}
let mappings = body.get("mappings").cloned().unwrap_or(json!({}));
let settings = body.get("settings").cloned().unwrap_or(json!({}));
if let Err(e) = state.create_index_meta(
index_name,
IndexMeta {
mappings,
settings,
created_at: crate::util::now_iso8601(),
uuid: uuid::Uuid::new_v4().to_string(),
},
) {
return (500, storage_error(&e));
}
(
200,
json!({
"acknowledged": true,
"shards_acknowledged": true,
"index": index_name,
}),
)
}
pub fn delete_index(state: &OpenSearchState, index_name: &str) -> (u16, Value) {
match state.delete_index_meta(index_name) {
Ok(true) => (200, json!({ "acknowledged": true })),
Ok(false) => (404, index_not_found(index_name)),
Err(e) => (500, storage_error(&e)),
}
}
pub fn get_mapping(state: &OpenSearchState, index_name: &str) -> (u16, Value) {
match state.get_index_meta(index_name) {
Some(meta) => (200, json!({ index_name: { "mappings": meta.mappings } })),
None => (404, index_not_found(index_name)),
}
}
pub fn put_mapping(state: &OpenSearchState, index_name: &str, body: &Value) -> (u16, Value) {
let Some(mut meta) = state.get_index_meta(index_name) else {
return (404, index_not_found(index_name));
};
if let (Some(existing), Some(new)) = (meta.mappings.as_object_mut(), body.as_object()) {
if let Some(props) = new.get("properties").and_then(|p| p.as_object()) {
let existing_props = existing
.entry("properties".to_string())
.or_insert_with(|| json!({}));
if let Some(ep) = existing_props.as_object_mut() {
for (k, v) in props {
ep.insert(k.clone(), v.clone());
}
}
} else {
for (k, v) in new {
existing.insert(k.clone(), v.clone());
}
}
}
if let Err(e) = state.create_index_meta(index_name, meta) {
return (500, storage_error(&e));
}
(200, json!({ "acknowledged": true }))
}
pub fn refresh(state: &OpenSearchState, index_name: &str) -> (u16, Value) {
if !state.index_exists(index_name) {
return (404, index_not_found(index_name));
}
(
200,
json!({
"_shards": { "total": 1, "successful": 1, "failed": 0 },
}),
)
}
pub fn get_index(state: &OpenSearchState, index_name: &str) -> (u16, Value) {
match state.get_index_meta(index_name) {
Some(meta) => (
200,
json!({
index_name: {
"aliases": {},
"mappings": meta.mappings,
"settings": {
"index": {
"creation_date": meta.created_at,
"number_of_shards": "1",
"number_of_replicas": "1",
"uuid": meta.uuid,
}
}
}
}),
),
None => (404, index_not_found(index_name)),
}
}
pub fn index_exists(state: &OpenSearchState, index_name: &str) -> u16 {
if state.index_exists(index_name) {
200
} else {
404
}
}
pub fn cat_indices(state: &OpenSearchState) -> (u16, Value) {
let indices: Vec<Value> = state
.list_indices()
.into_iter()
.map(|(name, _meta)| {
let count = state.count_docs(&name).unwrap_or(0);
json!({
"index": name,
"health": "green",
"status": "open",
"pri": "1",
"rep": "1",
"docs.count": count.to_string(),
"store.size": "1kb",
})
})
.collect();
(200, json!(indices))
}
pub(crate) fn index_not_found(name: &str) -> Value {
json!({
"error": {
"root_cause": [{"type": "index_not_found_exception", "reason": format!("no such index [{}]", name)}],
"type": "index_not_found_exception",
"reason": format!("no such index [{}]", name),
},
"status": 404,
})
}
pub(crate) fn storage_error(e: &impl std::fmt::Display) -> Value {
json!({
"error": {
"type": "internal_server_error",
"reason": e.to_string(),
},
"status": 500,
})
}