From 313d804b6285bda5b7f5dec51fb913a573d4e12c Mon Sep 17 00:00:00 2001 From: Mubelotix Date: Wed, 16 Jul 2025 14:30:58 +0200 Subject: [PATCH] Create structure --- crates/meilisearch-types/src/deserr/mod.rs | 3 + crates/meilisearch-types/src/error.rs | 26 +++- crates/meilisearch/src/routes/indexes/mod.rs | 2 + .../meilisearch/src/routes/indexes/render.rs | 138 ++++++++++++++++++ .../src/routes/indexes/render_analytics.rs | 0 5 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 crates/meilisearch/src/routes/indexes/render.rs create mode 100644 crates/meilisearch/src/routes/indexes/render_analytics.rs diff --git a/crates/meilisearch-types/src/deserr/mod.rs b/crates/meilisearch-types/src/deserr/mod.rs index f1470c201..ba1130c2e 100644 --- a/crates/meilisearch-types/src/deserr/mod.rs +++ b/crates/meilisearch-types/src/deserr/mod.rs @@ -213,3 +213,6 @@ merge_with_error_impl_take_error_message!(InvalidSearchSemanticRatio); merge_with_error_impl_take_error_message!(InvalidSearchRankingScoreThreshold); merge_with_error_impl_take_error_message!(InvalidSimilarRankingScoreThreshold); merge_with_error_impl_take_error_message!(InvalidSimilarId); +// merge_with_error_impl_take_error_message!(InvalidRenderTemplate); +// merge_with_error_impl_take_error_message!(InvalidRenderTemplateId); +// merge_with_error_impl_take_error_message!(InvalidRenderTemplateInline); diff --git a/crates/meilisearch-types/src/error.rs b/crates/meilisearch-types/src/error.rs index c57e2d042..9f8218a32 100644 --- a/crates/meilisearch-types/src/error.rs +++ b/crates/meilisearch-types/src/error.rs @@ -416,7 +416,13 @@ InvalidChatCompletionSystemPrompt , InvalidRequest , BAD_REQU InvalidChatCompletionSearchDescriptionPrompt , InvalidRequest , BAD_REQUEST ; InvalidChatCompletionSearchQueryParamPrompt , InvalidRequest , BAD_REQUEST ; InvalidChatCompletionSearchIndexUidParamPrompt , InvalidRequest , BAD_REQUEST ; -InvalidChatCompletionPreQueryPrompt , InvalidRequest , BAD_REQUEST +InvalidChatCompletionPreQueryPrompt , InvalidRequest , BAD_REQUEST ; +InvalidRenderTemplate , InvalidRequest , BAD_REQUEST ; +InvalidRenderTemplateId , InvalidRequest , BAD_REQUEST ; +InvalidRenderTemplateInline , InvalidRequest , BAD_REQUEST ; +InvalidRenderInput , InvalidRequest , BAD_REQUEST ; +InvalidRenderInputDocumentId , InvalidRequest , BAD_REQUEST ; +InvalidRenderInputInline , InvalidRequest , BAD_REQUEST } impl ErrorCode for JoinError { @@ -646,6 +652,24 @@ impl fmt::Display for deserr_codes::InvalidNetworkSearchApiKey { } } +impl fmt::Display for deserr_codes::InvalidRenderTemplate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "the value of `searchApiKey` is invalid, expected a string.") + } +} + +impl fmt::Display for deserr_codes::InvalidRenderTemplateId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "the value of `searchApiKey` is invalid, expected a string.") + } +} + +impl fmt::Display for deserr_codes::InvalidRenderTemplateInline { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "the value of `searchApiKey` is invalid, expected a string.") + } +} + #[macro_export] macro_rules! internal_error { ($target:ty : $($other:path), *) => { diff --git a/crates/meilisearch/src/routes/indexes/mod.rs b/crates/meilisearch/src/routes/indexes/mod.rs index 04b3e12c4..b77c7ace6 100644 --- a/crates/meilisearch/src/routes/indexes/mod.rs +++ b/crates/meilisearch/src/routes/indexes/mod.rs @@ -30,6 +30,8 @@ use crate::Opt; pub mod documents; pub mod facet_search; +pub mod render; +mod render_analytics; pub mod search; mod search_analytics; #[cfg(test)] diff --git a/crates/meilisearch/src/routes/indexes/render.rs b/crates/meilisearch/src/routes/indexes/render.rs new file mode 100644 index 000000000..fa77bdb67 --- /dev/null +++ b/crates/meilisearch/src/routes/indexes/render.rs @@ -0,0 +1,138 @@ +use actix_web::web::{self, Data}; +use actix_web::{HttpRequest, HttpResponse}; +use deserr::actix_web::{AwebJson, AwebQueryParameter}; +use deserr::Deserr; +use index_scheduler::IndexScheduler; +use itertools::structs; +use meilisearch_types::deserr::query_params::Param; +use meilisearch_types::deserr::{DeserrJsonError, DeserrQueryParamError}; +use meilisearch_types::error::deserr_codes::*; +use meilisearch_types::error::ResponseError; +use meilisearch_types::index_uid::IndexUid; +use meilisearch_types::keys::actions; +use meilisearch_types::serde_cs::vec::CS; +use serde::Serialize; +use serde_json::Value; +use tracing::debug; +use utoipa::{IntoParams, OpenApi, ToSchema}; + +use super::ActionPolicy; +use crate::analytics::Analytics; +use crate::extractors::authentication::GuardedData; +use crate::extractors::sequential_extractor::SeqHandler; +use crate::routes::indexes::similar_analytics::{SimilarAggregator, SimilarGET, SimilarPOST}; +use crate::search::{ + add_search_rules, perform_similar, RankingScoreThresholdSimilar, RetrieveVectors, Route, + SearchKind, SimilarQuery, SimilarResult, DEFAULT_SEARCH_LIMIT, DEFAULT_SEARCH_OFFSET, +}; + +#[derive(OpenApi)] +#[openapi( + paths(render), + tags( + ( + name = "Render templates", + description = "The /render route allows rendering templates used by Meilisearch.", + external_docs(url = "https://www.meilisearch.com/docs/reference/api/render"), + ), + ), +)] +pub struct RenderApi; + +pub fn configure(cfg: &mut web::ServiceConfig) { + cfg.service( + web::resource("") + .route(web::post().to(SeqHandler(render))) + ); +} + +/// Render templates with POST +#[utoipa::path( + post, + path = "{indexUid}/render", + tag = "Render templates", + security(("Bearer" = ["templates.render", "*.get", "*"])), + params(("indexUid" = String, Path, example = "movies", description = "Index Unique Identifier", nullable = false)), + request_body = RenderQuery, + responses( + (status = 200, description = "The rendered result is returned", body = RenderResult, content_type = "application/json", example = json!( + { + "rendered": "A Jack Russell called Iko" + } + )), + (status = 404, description = "Template or document not found", body = ResponseError, content_type = "application/json", example = json!( + { + "message": "Index `movies` not found.", // TODO + "code": "index_not_found", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#index_not_found" + } + )), + (status = 400, description = "Template couldn't be rendered", body = ResponseError, content_type = "application/json", example = json!( + { + "message": "The Authorization header is missing. It must use the bearer authorization method.", // TODO + "code": "missing_authorization_header", + "type": "auth", + "link": "https://docs.meilisearch.com/errors#missing_authorization_header" + } + )), + ) +)] +pub async fn render( + index_scheduler: GuardedData, Data>, + index_uid: web::Path, + params: AwebJson, + req: HttpRequest, + analytics: web::Data, +) -> Result { + let index_uid = IndexUid::try_from(index_uid.into_inner())?; + + let query = params.into_inner(); + debug!(parameters = ?query, "Render template"); + + //let mut aggregate = SimilarAggregator::::from_query(&query); + + let rendered = RenderResult { + rendered: String::from("TODO") + }; + + // if let Ok(similar) = &similar { + // aggregate.succeed(similar); + // } + // analytics.publish(aggregate, &req); + + debug!(returns = ?rendered, "Render template"); + Ok(HttpResponse::Ok().json(rendered)) +} + +#[derive(Debug, Clone, PartialEq, Deserr, ToSchema)] +#[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)] +pub struct RenderQuery { + #[deserr(error = DeserrJsonError)] + pub template: RenderQueryTemplate, + #[deserr(error = DeserrJsonError)] + pub input: RenderQueryInput, +} + +#[derive(Debug, Clone, PartialEq, Deserr, ToSchema)] +#[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)] +pub struct RenderQueryTemplate { + #[deserr(default, error = DeserrJsonError)] + id: Option, + #[deserr(default, error = DeserrJsonError)] + inline: Option, +} + +#[derive(Debug, Clone, PartialEq, Deserr, ToSchema)] +#[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)] +pub struct RenderQueryInput { + #[deserr(default, error = DeserrJsonError)] + document_id: Option, + #[deserr(default, error = DeserrJsonError)] + inline: Option, +} + +#[derive(Debug, Clone, Serialize, PartialEq, ToSchema)] +pub struct RenderResult { + rendered: String, +} diff --git a/crates/meilisearch/src/routes/indexes/render_analytics.rs b/crates/meilisearch/src/routes/indexes/render_analytics.rs new file mode 100644 index 000000000..e69de29bb