Skip to content

Commit 5e84bdf

Browse files
Merge #709
709: Add sharding support for Network methods r=curquiza a=kumarUjjawal # Pull Request This PR aims to bring the SDK close to the Mealisearch [Meilisearch 1.19](https://github.com/meilisearch/meilisearch/releases/tag/v1.19.0) features and add support for sharding . ## Related issue Fixes #700 ## What does this PR do? As per the tasks described in the origina issue #700 - Update the Network methods to accept sending the sharding parameter - Update the Network methods to include `remotes.[remoteName].writeApiKey` in the responses - Update the Tasks methods to include remotes objects in the tasks reponse index update method to allow renaming - Add new test cases to test implementation ## PR checklist Please check if your PR fulfills the following requirements: - [x] Does this PR fix an existing issue, or have you listed the changes applied in the PR description (and why they are needed)? - [x] Have you read the contributing guidelines? - [x] Have you made sure that the title is accurate and descriptive of the changes? Thank you so much for contributing to Meilisearch! <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Manage network configuration from the client: view/update network state, toggle sharding, and set the local remote. * Public Network module exposing remotes, self identity, and sharding configuration. * Multi-search federation options can target a specific remote. * **Improvements** * Task details (enqueued, processing, succeeded) now include remotes information when available. * **Tests** * Added tests for network updates and task deserialization with remotes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Kumar Ujjawal <ujjawalpathak6@gmail.com>
2 parents 59971fc + d27dbed commit 5e84bdf

File tree

5 files changed

+187
-2
lines changed

5 files changed

+187
-2
lines changed

src/client.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{
88
errors::*,
99
indexes::*,
1010
key::{Key, KeyBuilder, KeyUpdater, KeysQuery, KeysResults},
11+
network::{NetworkState, NetworkUpdate},
1112
request::*,
1213
search::*,
1314
task_info::TaskInfo,
@@ -1148,6 +1149,46 @@ impl<Http: HttpClient> Client<Http> {
11481149
crate::tenant_tokens::generate_tenant_token(api_key_uid, search_rules, api_key, expires_at)
11491150
}
11501151

1152+
/// Get the current network state (/network)
1153+
pub async fn get_network_state(&self) -> Result<NetworkState, Error> {
1154+
self.http_client
1155+
.request::<(), (), NetworkState>(
1156+
&format!("{}/network", self.host),
1157+
Method::Get { query: () },
1158+
200,
1159+
)
1160+
.await
1161+
}
1162+
1163+
/// Partially update the network state (/network)
1164+
pub async fn update_network_state(&self, body: &NetworkUpdate) -> Result<NetworkState, Error> {
1165+
self.http_client
1166+
.request::<(), &NetworkUpdate, NetworkState>(
1167+
&format!("{}/network", self.host),
1168+
Method::Patch { query: (), body },
1169+
200,
1170+
)
1171+
.await
1172+
}
1173+
1174+
/// Convenience: set sharding=true/false
1175+
pub async fn set_sharding(&self, enabled: bool) -> Result<NetworkState, Error> {
1176+
let update = NetworkUpdate {
1177+
sharding: Some(enabled),
1178+
..NetworkUpdate::default()
1179+
};
1180+
self.update_network_state(&update).await
1181+
}
1182+
1183+
/// Convenience: set self to a remote name
1184+
pub async fn set_self_remote(&self, name: &str) -> Result<NetworkState, Error> {
1185+
let update = NetworkUpdate {
1186+
self_name: Some(name.to_string()),
1187+
..NetworkUpdate::default()
1188+
};
1189+
self.update_network_state(&update).await
1190+
}
1191+
11511192
fn sleep_backend(&self) -> SleepBackend {
11521193
SleepBackend::infer(self.http_client.is_tokio())
11531194
}
@@ -1207,6 +1248,49 @@ pub struct Version {
12071248

12081249
#[cfg(test)]
12091250
mod tests {
1251+
use super::*;
1252+
use mockito::Matcher;
1253+
1254+
#[tokio::test]
1255+
async fn test_network_update_and_deserialize_remotes() {
1256+
let mut s = mockito::Server::new_async().await;
1257+
let base = s.url();
1258+
1259+
let response_body = serde_json::json!({
1260+
"remotes": {
1261+
"ms-00": {
1262+
"url": "http://ms-00",
1263+
"searchApiKey": "SEARCH",
1264+
"writeApiKey": "WRITE"
1265+
}
1266+
},
1267+
"self": "ms-00",
1268+
"sharding": true
1269+
})
1270+
.to_string();
1271+
1272+
let _m = s
1273+
.mock("PATCH", "/network")
1274+
.match_body(Matcher::Regex(
1275+
r#"\{.*"sharding"\s*:\s*true.*\}"#.to_string(),
1276+
))
1277+
.with_status(200)
1278+
.with_header("content-type", "application/json")
1279+
.with_body(response_body)
1280+
.create_async()
1281+
.await;
1282+
1283+
let client = Client::new(base, None::<String>).unwrap();
1284+
let updated = client
1285+
.set_sharding(true)
1286+
.await
1287+
.expect("update_network_state failed");
1288+
assert_eq!(updated.sharding, Some(true));
1289+
let remotes = updated.remotes.expect("remotes should be present");
1290+
let ms00 = remotes.get("ms-00").expect("ms-00 should exist");
1291+
assert_eq!(ms00.write_api_key.as_deref(), Some("WRITE"));
1292+
}
1293+
12101294
use big_s::S;
12111295
use time::OffsetDateTime;
12121296

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ pub mod features;
244244
pub mod indexes;
245245
/// Module containing the [`Key`](key::Key) struct.
246246
pub mod key;
247+
/// Module for Network configuration API (sharding/remotes).
248+
pub mod network;
247249
pub mod request;
248250
/// Module related to search queries and results.
249251
pub mod search;

src/network.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use serde::{Deserialize, Serialize};
2+
use std::collections::HashMap;
3+
4+
#[derive(Clone, Serialize, Deserialize)]
5+
#[serde(rename_all = "camelCase")]
6+
pub struct RemoteConfig {
7+
pub url: String,
8+
#[serde(rename = "searchApiKey")]
9+
pub search_api_key: String,
10+
#[serde(rename = "writeApiKey", skip_serializing_if = "Option::is_none")]
11+
// present in responses since 1.19
12+
pub write_api_key: Option<String>,
13+
}
14+
15+
pub type RemotesMap = HashMap<String, RemoteConfig>;
16+
17+
/// Full network state returned by GET /network
18+
#[derive(Clone, Serialize, Deserialize)]
19+
#[serde(rename_all = "camelCase")]
20+
pub struct NetworkState {
21+
pub remotes: Option<RemotesMap>,
22+
#[serde(rename = "self")]
23+
pub self_name: Option<String>,
24+
pub sharding: Option<bool>,
25+
}
26+
27+
/// Partial update body for PATCH /network
28+
#[derive(Default, Clone, Serialize, Deserialize)]
29+
#[serde(rename_all = "camelCase")]
30+
pub struct NetworkUpdate {
31+
#[serde(skip_serializing_if = "Option::is_none")]
32+
pub remotes: Option<RemotesMap>,
33+
#[serde(rename = "self", skip_serializing_if = "Option::is_none")]
34+
pub self_name: Option<String>,
35+
#[serde(skip_serializing_if = "Option::is_none")]
36+
pub sharding: Option<bool>,
37+
}

src/search.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,12 @@ pub struct SearchQuery<'a, Http: HttpClient> {
415415
#[derive(Debug, Serialize, Clone)]
416416
#[serde(rename_all = "camelCase")]
417417
pub struct QueryFederationOptions {
418+
/// Weight multiplier for this query when merging federated results
418419
#[serde(skip_serializing_if = "Option::is_none")]
419420
pub weight: Option<f32>,
421+
/// Remote instance name to target when sharding; corresponds to a key in network.remotes
422+
#[serde(skip_serializing_if = "Option::is_none")]
423+
pub remote: Option<String>,
420424
}
421425

422426
#[allow(missing_docs)]
@@ -766,6 +770,7 @@ impl<'a, 'b, Http: HttpClient> MultiSearchQuery<'a, 'b, Http> {
766770
search_query,
767771
QueryFederationOptions {
768772
weight: Some(weight),
773+
remote: None,
769774
},
770775
)
771776
}

src/tasks.rs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use serde::{Deserialize, Deserializer, Serialize};
2+
use serde_json::{Map, Value};
23
use std::time::Duration;
34
use time::OffsetDateTime;
45

@@ -157,6 +158,9 @@ pub struct SucceededTask {
157158
pub canceled_by: Option<usize>,
158159
pub index_uid: Option<String>,
159160
pub error: Option<MeilisearchError>,
161+
/// Remotes object returned by the server for this task (present since Meilisearch 1.19)
162+
#[serde(skip_serializing_if = "Option::is_none")]
163+
pub remotes: Option<Map<String, Value>>,
160164
#[serde(flatten)]
161165
pub update_type: TaskType,
162166
pub uid: u32,
@@ -174,6 +178,9 @@ pub struct EnqueuedTask {
174178
#[serde(with = "time::serde::rfc3339")]
175179
pub enqueued_at: OffsetDateTime,
176180
pub index_uid: Option<String>,
181+
/// Remotes object returned by the server for this enqueued task
182+
#[serde(skip_serializing_if = "Option::is_none")]
183+
pub remotes: Option<Map<String, Value>>,
177184
#[serde(flatten)]
178185
pub update_type: TaskType,
179186
pub uid: u32,
@@ -193,6 +200,9 @@ pub struct ProcessingTask {
193200
#[serde(with = "time::serde::rfc3339")]
194201
pub started_at: OffsetDateTime,
195202
pub index_uid: Option<String>,
203+
/// Remotes object returned by the server for this processing task
204+
#[serde(skip_serializing_if = "Option::is_none")]
205+
pub remotes: Option<Map<String, Value>>,
196206
#[serde(flatten)]
197207
pub update_type: TaskType,
198208
pub uid: u32,
@@ -738,6 +748,54 @@ impl<'a, Http: HttpClient> TasksQuery<'a, TasksPaginationFilters, Http> {
738748

739749
#[cfg(test)]
740750
mod test {
751+
752+
#[test]
753+
fn test_deserialize_enqueued_task_with_remotes() {
754+
let json = r#"{
755+
"enqueuedAt": "2022-02-03T13:02:38.369634Z",
756+
"indexUid": "movies",
757+
"status": "enqueued",
758+
"type": "indexUpdate",
759+
"uid": 12,
760+
"remotes": { "ms-00": { "status": "ok" } }
761+
}"#;
762+
let task: Task = serde_json::from_str(json).unwrap();
763+
match task {
764+
Task::Enqueued { content } => {
765+
let remotes = content.remotes.expect("remotes should be present");
766+
assert!(remotes.contains_key("ms-00"));
767+
}
768+
_ => panic!("expected enqueued task"),
769+
}
770+
}
771+
772+
#[test]
773+
fn test_deserialize_processing_task_with_remotes() {
774+
let json = r#"{
775+
"details": {
776+
"indexedDocuments": null,
777+
"receivedDocuments": 10
778+
},
779+
"duration": null,
780+
"enqueuedAt": "2022-02-03T15:17:02.801341Z",
781+
"finishedAt": null,
782+
"indexUid": "movies",
783+
"startedAt": "2022-02-03T15:17:02.812338Z",
784+
"status": "processing",
785+
"type": "documentAdditionOrUpdate",
786+
"uid": 14,
787+
"remotes": { "ms-00": { "status": "ok" } }
788+
}"#;
789+
let task: Task = serde_json::from_str(json).unwrap();
790+
match task {
791+
Task::Processing { content } => {
792+
let remotes = content.remotes.expect("remotes should be present");
793+
assert!(remotes.contains_key("ms-00"));
794+
}
795+
_ => panic!("expected processing task"),
796+
}
797+
}
798+
741799
use super::*;
742800
use crate::{
743801
client::*,
@@ -782,8 +840,7 @@ mod test {
782840
enqueued_at,
783841
index_uid: Some(index_uid),
784842
update_type: TaskType::DocumentAdditionOrUpdate { details: None },
785-
uid: 12,
786-
}
843+
uid: 12, .. }
787844
}
788845
if enqueued_at == datetime && index_uid == "meili"));
789846

0 commit comments

Comments
 (0)