453 lines
15 KiB
Rust
Raw Normal View History

2020-02-13 10:25:37 +01:00
#![allow(dead_code)]
2020-04-16 11:09:47 +02:00
use serde_json::{json, Value};
use std::time::Duration;
2020-01-15 17:11:27 +01:00
2020-04-16 11:09:47 +02:00
use actix_web::{http::StatusCode, test};
2020-01-15 17:11:27 +01:00
use meilisearch_http::data::Data;
use meilisearch_http::option::Opt;
use tempdir::TempDir;
2020-04-16 11:09:47 +02:00
use tokio::time::delay_for;
2020-01-15 17:11:27 +01:00
2020-03-04 14:18:07 +01:00
pub struct Server {
uid: String,
2020-04-16 11:09:47 +02:00
data: Data,
2020-01-15 17:11:27 +01:00
}
2020-03-04 14:18:07 +01:00
impl Server {
pub fn with_uid(uid: &str) -> Server {
let tmp_dir = TempDir::new("meilisearch").unwrap();
let opt = Opt {
db_path: tmp_dir.path().to_str().unwrap().to_string(),
http_addr: "127.0.0.1:7700".to_owned(),
master_key: None,
env: "development".to_owned(),
no_analytics: true,
};
let data = Data::new(opt.clone());
Server {
uid: uid.to_string(),
2020-04-16 11:09:47 +02:00
data: data,
2020-03-04 14:18:07 +01:00
}
}
2020-04-16 11:09:47 +02:00
pub async fn wait_update_id(&mut self, update_id: u64) {
2020-03-04 14:18:07 +01:00
loop {
2020-04-16 11:09:47 +02:00
let (response, status_code) = self.get_update_status(update_id).await;
assert_eq!(status_code, 200);
if response["status"] == "processed" || response["status"] == "error" {
eprintln!("{:#?}", response);
2020-03-04 14:18:07 +01:00
return;
}
2020-04-16 11:09:47 +02:00
delay_for(Duration::from_secs(1)).await;
2020-03-04 14:18:07 +01:00
}
}
2020-02-13 10:25:37 +01:00
// Global Http request GET/POST/DELETE async or sync
2020-02-13 10:25:37 +01:00
2020-04-16 11:09:47 +02:00
pub async fn get_request(&mut self, url: &str) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
eprintln!("get_request: {}", url);
2020-04-16 11:09:47 +02:00
let mut app = test::init_service(meilisearch_http::create_app(&self.data)).await;
let req = test::TestRequest::get().uri(url).to_request();
let res = test::call_service(&mut app, req).await;
2020-03-04 14:18:07 +01:00
let status_code = res.status().clone();
2020-04-16 11:09:47 +02:00
let body = test::read_body(res).await;
let response = serde_json::from_slice(&body).unwrap_or_default();
2020-03-04 14:18:07 +01:00
(response, status_code)
}
2020-01-23 18:33:23 +01:00
2020-04-16 11:09:47 +02:00
pub async fn post_request(&mut self, url: &str, body: Value) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
eprintln!("post_request: {}", url);
2020-01-23 18:33:23 +01:00
2020-04-16 11:09:47 +02:00
let mut app = test::init_service(meilisearch_http::create_app(&self.data)).await;
let req = test::TestRequest::post()
.uri(url)
.set_json(&body)
.to_request();
let res = test::call_service(&mut app, req).await;
2020-03-04 14:18:07 +01:00
let status_code = res.status().clone();
2020-01-23 18:33:23 +01:00
2020-04-16 11:09:47 +02:00
let body = test::read_body(res).await;
let response = serde_json::from_slice(&body).unwrap_or_default();
2020-03-04 14:18:07 +01:00
(response, status_code)
}
2020-01-23 18:33:23 +01:00
2020-04-16 11:09:47 +02:00
pub async fn post_request_async(&mut self, url: &str, body: Value) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
eprintln!("post_request_async: {}", url);
2020-04-16 11:09:47 +02:00
let (response, status_code) = self.post_request(url, body).await;
2020-03-04 14:18:07 +01:00
assert_eq!(status_code, 202);
assert!(response["updateId"].as_u64().is_some());
2020-04-16 11:09:47 +02:00
self.wait_update_id(response["updateId"].as_u64().unwrap())
.await;
2020-03-04 14:18:07 +01:00
(response, status_code)
}
2020-01-23 18:33:23 +01:00
2020-04-16 11:09:47 +02:00
pub async fn put_request(&mut self, url: &str, body: Value) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
eprintln!("put_request: {}", url);
2020-01-23 18:33:23 +01:00
2020-04-16 11:09:47 +02:00
let mut app = test::init_service(meilisearch_http::create_app(&self.data)).await;
let req = test::TestRequest::put()
.uri(url)
.set_json(&body)
.to_request();
let res = test::call_service(&mut app, req).await;
2020-03-04 14:18:07 +01:00
let status_code = res.status().clone();
2020-02-13 10:25:37 +01:00
2020-04-16 11:09:47 +02:00
let body = test::read_body(res).await;
let response = serde_json::from_slice(&body).unwrap_or_default();
2020-03-04 14:18:07 +01:00
(response, status_code)
}
2020-02-13 10:25:37 +01:00
2020-04-16 11:09:47 +02:00
pub async fn put_request_async(&mut self, url: &str, body: Value) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
eprintln!("put_request_async: {}", url);
2020-04-16 11:09:47 +02:00
let (response, status_code) = self.put_request(url, body).await;
2020-03-04 14:18:07 +01:00
assert!(response["updateId"].as_u64().is_some());
assert_eq!(status_code, 202);
2020-04-16 11:09:47 +02:00
self.wait_update_id(response["updateId"].as_u64().unwrap())
.await;
2020-03-04 14:18:07 +01:00
(response, status_code)
}
2020-02-13 10:25:37 +01:00
2020-04-16 11:09:47 +02:00
pub async fn delete_request(&mut self, url: &str) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
eprintln!("delete_request: {}", url);
2020-04-16 11:09:47 +02:00
let mut app = test::init_service(meilisearch_http::create_app(&self.data)).await;
let req = test::TestRequest::delete().uri(url).to_request();
let res = test::call_service(&mut app, req).await;
2020-03-04 14:18:07 +01:00
let status_code = res.status().clone();
2020-02-13 10:25:37 +01:00
2020-04-16 11:09:47 +02:00
let body = test::read_body(res).await;
let response = serde_json::from_slice(&body).unwrap_or_default();
2020-03-04 14:18:07 +01:00
(response, status_code)
}
2020-02-13 10:25:37 +01:00
2020-04-16 11:09:47 +02:00
pub async fn delete_request_async(&mut self, url: &str) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
eprintln!("delete_request_async: {}", url);
2020-04-16 11:09:47 +02:00
let (response, status_code) = self.delete_request(url).await;
2020-03-04 14:18:07 +01:00
assert!(response["updateId"].as_u64().is_some());
assert_eq!(status_code, 202);
2020-04-16 11:09:47 +02:00
self.wait_update_id(response["updateId"].as_u64().unwrap())
.await;
2020-03-04 14:18:07 +01:00
(response, status_code)
}
// All Routes
2020-03-04 14:18:07 +01:00
2020-04-16 11:09:47 +02:00
pub async fn list_indexes(&mut self) -> (Value, StatusCode) {
self.get_request("/indexes").await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn create_index(&mut self, body: Value) -> (Value, StatusCode) {
self.post_request("/indexes", body).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn search_multi_index(&mut self, query: &str) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/search?{}", query);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_index(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn update_index(&mut self, body: Value) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}", self.uid);
2020-04-16 11:09:47 +02:00
self.put_request(&url, body).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn delete_index(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}", self.uid);
2020-04-16 11:09:47 +02:00
self.delete_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn search(&mut self, query: &str) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/search?{}", self.uid, query);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_all_updates_status(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/updates", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_update_status(&mut self, update_id: u64) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/updates/{}", self.uid, update_id);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_all_documents(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/documents", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn add_or_replace_multiple_documents(&mut self, body: Value) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/documents", self.uid);
2020-04-16 11:09:47 +02:00
self.post_request_async(&url, body).await;
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn add_or_replace_multiple_documents_sync(
&mut self,
body: Value,
) -> (Value, StatusCode) {
let url = format!("/indexes/{}/documents", self.uid);
2020-04-16 11:09:47 +02:00
self.post_request(&url, body).await
}
2020-04-16 11:09:47 +02:00
pub async fn add_or_update_multiple_documents(&mut self, body: Value) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/documents", self.uid);
2020-04-16 11:09:47 +02:00
self.put_request_async(&url, body).await;
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn clear_all_documents(&mut self) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/documents", self.uid);
2020-04-16 11:09:47 +02:00
self.delete_request_async(&url).await;
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_document(&mut self, document_id: impl ToString) -> (Value, StatusCode) {
2020-03-10 11:29:56 +01:00
let url = format!(
"/indexes/{}/documents/{}",
self.uid,
document_id.to_string()
);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn delete_document(&mut self, document_id: impl ToString) -> (Value, StatusCode) {
2020-03-10 11:29:56 +01:00
let url = format!(
"/indexes/{}/documents/{}",
self.uid,
document_id.to_string()
);
2020-04-16 11:09:47 +02:00
self.delete_request_async(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn delete_multiple_documents(&mut self, body: Value) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/documents/delete-batch", self.uid);
2020-04-16 11:09:47 +02:00
self.post_request_async(&url, body).await;
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_all_settings(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-02-13 10:25:37 +01:00
}
2020-03-04 14:18:07 +01:00
2020-04-16 11:09:47 +02:00
pub async fn update_all_settings(&mut self, body: Value) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings", self.uid);
2020-04-16 11:09:47 +02:00
self.post_request_async(&url, body).await;
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn delete_all_settings(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings", self.uid);
2020-04-16 11:09:47 +02:00
self.delete_request_async(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_ranking_rules(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/ranking-rules", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn update_ranking_rules(&mut self, body: Value) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/ranking-rules", self.uid);
2020-04-16 11:09:47 +02:00
self.post_request_async(&url, body).await;
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn update_ranking_rules_sync(&mut self, body: Value) -> (Value, StatusCode) {
let url = format!("/indexes/{}/settings/ranking-rules", self.uid);
2020-04-16 11:09:47 +02:00
self.post_request(&url, body).await
}
2020-04-16 11:09:47 +02:00
pub async fn delete_ranking_rules(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/ranking-rules", self.uid);
2020-04-16 11:09:47 +02:00
self.delete_request_async(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_distinct_attribute(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/distinct-attribute", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn update_distinct_attribute(&mut self, body: Value) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/distinct-attribute", self.uid);
2020-04-16 11:09:47 +02:00
self.post_request_async(&url, body).await;
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn delete_distinct_attribute(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/distinct-attribute", self.uid);
2020-04-16 11:09:47 +02:00
self.delete_request_async(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_primary_key(&mut self) -> (Value, StatusCode) {
let url = format!("/indexes/{}/settings/primary_key", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_searchable_attributes(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/searchable-attributes", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn update_searchable_attributes(&mut self, body: Value) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/searchable-attributes", self.uid);
2020-04-16 11:09:47 +02:00
self.post_request_async(&url, body).await;
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn delete_searchable_attributes(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/searchable-attributes", self.uid);
2020-04-16 11:09:47 +02:00
self.delete_request_async(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_displayed_attributes(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/displayed-attributes", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn update_displayed_attributes(&mut self, body: Value) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/displayed-attributes", self.uid);
2020-04-16 11:09:47 +02:00
self.post_request_async(&url, body).await;
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn delete_displayed_attributes(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/displayed-attributes", self.uid);
2020-04-16 11:09:47 +02:00
self.delete_request_async(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_accept_new_fields(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/accept-new-fields", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn update_accept_new_fields(&mut self, body: Value) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/accept-new-fields", self.uid);
2020-04-16 11:09:47 +02:00
self.post_request_async(&url, body).await;
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_synonyms(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/synonyms", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn update_synonyms(&mut self, body: Value) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/synonyms", self.uid);
2020-04-16 11:09:47 +02:00
self.post_request_async(&url, body).await;
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn delete_synonyms(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/synonyms", self.uid);
2020-04-16 11:09:47 +02:00
self.delete_request_async(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_stop_words(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/stop-words", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn update_stop_words(&mut self, body: Value) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/stop-words", self.uid);
2020-04-16 11:09:47 +02:00
self.post_request_async(&url, body).await;
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn delete_stop_words(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/settings/stop-words", self.uid);
2020-04-16 11:09:47 +02:00
self.delete_request_async(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_index_stats(&mut self) -> (Value, StatusCode) {
2020-03-04 14:18:07 +01:00
let url = format!("/indexes/{}/stats", self.uid);
2020-04-16 11:09:47 +02:00
self.get_request(&url).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn list_keys(&mut self) -> (Value, StatusCode) {
self.get_request("/keys").await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_health(&mut self) -> (Value, StatusCode) {
self.get_request("/health").await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn update_health(&mut self, body: Value) -> (Value, StatusCode) {
self.put_request("/health", body).await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_version(&mut self) -> (Value, StatusCode) {
self.get_request("/version").await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_sys_info(&mut self) -> (Value, StatusCode) {
self.get_request("/sys-info").await
2020-03-04 14:18:07 +01:00
}
2020-04-16 11:09:47 +02:00
pub async fn get_sys_info_pretty(&mut self) -> (Value, StatusCode) {
self.get_request("/sys-info/pretty").await
2020-03-04 14:18:07 +01:00
}
// Populate routes
2020-04-16 11:09:47 +02:00
pub async fn populate_movies(&mut self) {
2020-03-04 14:18:07 +01:00
let body = json!({
"uid": "movies",
"primaryKey": "id",
2020-03-04 14:18:07 +01:00
});
2020-04-16 11:09:47 +02:00
self.create_index(body).await;
2020-03-04 14:18:07 +01:00
let body = json!({
"rankingRules": [
"typo",
"words",
"proximity",
"attribute",
"wordsPosition",
"desc(popularity)",
"exactness",
"desc(vote_average)",
],
"searchableAttributes": [
"title",
"tagline",
"overview",
"cast",
"director",
"producer",
"production_companies",
"genres",
],
"displayedAttributes": [
"title",
"director",
"producer",
"tagline",
"genres",
"id",
"overview",
"vote_count",
"vote_average",
"poster_path",
"popularity",
],
"acceptNewFields": false,
});
2020-04-16 11:09:47 +02:00
self.update_all_settings(body).await;
2020-03-04 14:18:07 +01:00
let dataset = include_bytes!("assets/movies.json");
let body: Value = serde_json::from_slice(dataset).unwrap();
2020-04-16 11:09:47 +02:00
self.add_or_replace_multiple_documents(body).await;
2020-03-04 14:18:07 +01:00
}
2020-01-23 18:33:23 +01:00
}