From c35f8cce14b1312eb9d5ae1a6e3c61c0fb05ae01 Mon Sep 17 00:00:00 2001 From: Alberto Moretti Date: Sat, 13 Jun 2026 11:15:38 +0200 Subject: [PATCH] fix: use mercxry translations api --- README.md | 2 +- src/application/pokedex.rs | 8 ++++---- src/clients/funtranslations.rs | 17 +++++++++++------ src/config.rs | 2 +- tests/app.rs | 14 +++++++------- 5 files changed, 24 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 9e675ca..b0d96d5 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Example response: | --- | --- | --- | | `BIND_ADDR` | `0.0.0.0:5000` | HTTP bind address | | `POKEAPI_BASE_URL` | `https://pokeapi.co/api/v2` | PokéAPI base URL | -| `FUN_TRANSLATIONS_BASE_URL` | `https://funtranslations.mercxry.me` | FunTranslations base URL | +| `FUN_TRANSLATIONS_BASE_URL` | `https://api.funtranslations.mercxry.me/v1` | FunTranslations base URL | | `REQUEST_TIMEOUT_SECONDS` | `5` | Upstream HTTP timeout | | `RATE_LIMIT_PER_SECOND` | `20` | Global token refill rate | | `RATE_LIMIT_BURST` | `40` | Global burst capacity | diff --git a/src/application/pokedex.rs b/src/application/pokedex.rs index 3568fda..4664a36 100644 --- a/src/application/pokedex.rs +++ b/src/application/pokedex.rs @@ -150,8 +150,8 @@ mod tests { let translations = MockServer::start().await; mount_species(&pokeapi, "mewtwo", true, "rare", "Created by science.").await; Mock::given(matchers::method("POST")) - .and(matchers::path("/translate/yoda.json")) - .and(matchers::body_string_contains("Created+by+science")) + .and(matchers::path("/translate/yoda")) + .and(matchers::body_string_contains("Created by science")) .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ "contents": { "translated": "Created by science, it was." } }))) @@ -174,7 +174,7 @@ mod tests { let translations = MockServer::start().await; mount_species(&pokeapi, "pikachu", false, "forest", "Electric cheeks.").await; Mock::given(matchers::method("POST")) - .and(matchers::path("/translate/shakespeare.json")) + .and(matchers::path("/translate/shakespeare")) .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ "contents": { "translated": "Electric cheeks, good sir." } }))) @@ -197,7 +197,7 @@ mod tests { let translations = MockServer::start().await; mount_species(&pokeapi, "zubat", false, "cave", "It has no eyes.").await; Mock::given(matchers::method("POST")) - .and(matchers::path("/translate/yoda.json")) + .and(matchers::path("/translate/yoda")) .respond_with(ResponseTemplate::new(500)) .expect(1) .mount(&translations) diff --git a/src/clients/funtranslations.rs b/src/clients/funtranslations.rs index 07d8cea..d5194fb 100644 --- a/src/clients/funtranslations.rs +++ b/src/clients/funtranslations.rs @@ -24,11 +24,11 @@ impl TranslationClient { #[instrument(skip(self, text), fields(translation.kind = ?kind))] pub async fn translate(&self, kind: TranslationKind, text: &str) -> Result { - let url = format!("{}/translate/{}.json", self.base_url, endpoint_for(kind)); + let url = format!("{}/translate/{}", self.base_url, endpoint_for(kind)); let response = self .http .post(url) - .form(&[("text", text)]) + .json(&TranslationRequest { text }) .send() .await .map_err(|error| map_reqwest_error(&error))?; @@ -51,6 +51,11 @@ impl TranslationClient { } } +#[derive(serde::Serialize)] +struct TranslationRequest<'a> { + text: &'a str, +} + #[derive(Debug, Deserialize)] struct TranslationResponse { contents: TranslationContents, @@ -97,8 +102,8 @@ mod tests { async fn posts_text_to_the_requested_translation_endpoint() { let server = MockServer::start().await; Mock::given(matchers::method("POST")) - .and(matchers::path("/translate/yoda.json")) - .and(matchers::body_string_contains("text=Created")) + .and(matchers::path("/translate/yoda")) + .and(matchers::body_string_contains("Created")) .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ "contents": { "translated": "Created, it was." } }))) @@ -119,7 +124,7 @@ mod tests { async fn rejects_empty_translations_so_callers_can_fallback() { let server = MockServer::start().await; Mock::given(matchers::method("POST")) - .and(matchers::path("/translate/yoda.json")) + .and(matchers::path("/translate/yoda")) .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ "contents": { "translated": " " } }))) @@ -139,7 +144,7 @@ mod tests { async fn maps_translation_http_failures_to_upstream_errors() { let server = MockServer::start().await; Mock::given(matchers::method("POST")) - .and(matchers::path("/translate/shakespeare.json")) + .and(matchers::path("/translate/shakespeare")) .respond_with(ResponseTemplate::new(429)) .mount(&server) .await; diff --git a/src/config.rs b/src/config.rs index 93945a6..544fbe1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,7 +17,7 @@ impl AppConfig { Self { bind_addr: default_bind_addr(), pokeapi_base_url: "https://pokeapi.co/api/v2".to_owned(), - translations_base_url: "https://funtranslations.mercxry.me".to_owned(), + translations_base_url: "https://api.funtranslations.mercxry.me/v1".to_owned(), request_timeout: Duration::from_secs(5), rate_limit_per_second: 20.0, rate_limit_burst: 40, diff --git a/tests/app.rs b/tests/app.rs index f82db53..d1ed899 100644 --- a/tests/app.rs +++ b/tests/app.rs @@ -79,8 +79,8 @@ async fn translated_endpoint_returns_translated_description() { let translations = MockServer::start().await; mount_species(&pokeapi, "pikachu", false, "forest", "Electric cheeks.").await; Mock::given(matchers::method("POST")) - .and(matchers::path("/translate/shakespeare.json")) - .and(matchers::body_string_contains("Electric+cheeks")) + .and(matchers::path("/translate/shakespeare")) + .and(matchers::body_string_contains("Electric cheeks")) .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ "contents": { "translated": "Electric cheeks, prithee." } }))) @@ -112,8 +112,8 @@ async fn translated_endpoint_uses_yoda_for_legendary_pokemon() { let translations = MockServer::start().await; mount_species(&pokeapi, "mewtwo", true, "rare", "Created by science.").await; Mock::given(matchers::method("POST")) - .and(matchers::path("/translate/yoda.json")) - .and(matchers::body_string_contains("Created+by+science")) + .and(matchers::path("/translate/yoda")) + .and(matchers::body_string_contains("Created by science")) .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ "contents": { "translated": "Created by science, it was." } }))) @@ -121,7 +121,7 @@ async fn translated_endpoint_uses_yoda_for_legendary_pokemon() { .mount(&translations) .await; Mock::given(matchers::method("POST")) - .and(matchers::path("/translate/shakespeare.json")) + .and(matchers::path("/translate/shakespeare")) .respond_with(ResponseTemplate::new(200)) .expect(0) .mount(&translations) @@ -151,7 +151,7 @@ async fn translated_endpoint_falls_back_to_original_description_when_translation let translations = MockServer::start().await; mount_species(&pokeapi, "zubat", false, "cave", "It has no eyes.").await; Mock::given(matchers::method("POST")) - .and(matchers::path("/translate/yoda.json")) + .and(matchers::path("/translate/yoda")) .respond_with(ResponseTemplate::new(500)) .mount(&translations) .await; @@ -180,7 +180,7 @@ async fn translated_endpoint_falls_back_when_translation_is_empty() { let translations = MockServer::start().await; mount_species(&pokeapi, "zubat", false, "cave", "It has no eyes.").await; Mock::given(matchers::method("POST")) - .and(matchers::path("/translate/yoda.json")) + .and(matchers::path("/translate/yoda")) .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ "contents": { "translated": " " } })))