Create structure

This commit is contained in:
Mubelotix
2025-07-16 14:30:58 +02:00
parent a683faa882
commit 313d804b62
5 changed files with 168 additions and 1 deletions

View File

@ -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!(InvalidSearchRankingScoreThreshold);
merge_with_error_impl_take_error_message!(InvalidSimilarRankingScoreThreshold); merge_with_error_impl_take_error_message!(InvalidSimilarRankingScoreThreshold);
merge_with_error_impl_take_error_message!(InvalidSimilarId); 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);

View File

@ -416,7 +416,13 @@ InvalidChatCompletionSystemPrompt , InvalidRequest , BAD_REQU
InvalidChatCompletionSearchDescriptionPrompt , InvalidRequest , BAD_REQUEST ; InvalidChatCompletionSearchDescriptionPrompt , InvalidRequest , BAD_REQUEST ;
InvalidChatCompletionSearchQueryParamPrompt , InvalidRequest , BAD_REQUEST ; InvalidChatCompletionSearchQueryParamPrompt , InvalidRequest , BAD_REQUEST ;
InvalidChatCompletionSearchIndexUidParamPrompt , 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 { 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_export]
macro_rules! internal_error { macro_rules! internal_error {
($target:ty : $($other:path), *) => { ($target:ty : $($other:path), *) => {

View File

@ -30,6 +30,8 @@ use crate::Opt;
pub mod documents; pub mod documents;
pub mod facet_search; pub mod facet_search;
pub mod render;
mod render_analytics;
pub mod search; pub mod search;
mod search_analytics; mod search_analytics;
#[cfg(test)] #[cfg(test)]

View File

@ -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<ActionPolicy<{ actions::SEARCH }>, Data<IndexScheduler>>,
index_uid: web::Path<String>,
params: AwebJson<RenderQuery, DeserrJsonError>,
req: HttpRequest,
analytics: web::Data<Analytics>,
) -> Result<HttpResponse, ResponseError> {
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
let query = params.into_inner();
debug!(parameters = ?query, "Render template");
//let mut aggregate = SimilarAggregator::<SimilarPOST>::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<InvalidRenderTemplate>)]
pub template: RenderQueryTemplate,
#[deserr(error = DeserrJsonError<InvalidRenderInput>)]
pub input: RenderQueryInput,
}
#[derive(Debug, Clone, PartialEq, Deserr, ToSchema)]
#[deserr(error = DeserrJsonError<InvalidRenderTemplate>, rename_all = camelCase, deny_unknown_fields)]
pub struct RenderQueryTemplate {
#[deserr(default, error = DeserrJsonError<InvalidRenderTemplateId>)]
id: Option<String>,
#[deserr(default, error = DeserrJsonError<InvalidRenderTemplateInline>)]
inline: Option<serde_json::Value>,
}
#[derive(Debug, Clone, PartialEq, Deserr, ToSchema)]
#[deserr(error = DeserrJsonError<InvalidRenderInput>, rename_all = camelCase, deny_unknown_fields)]
pub struct RenderQueryInput {
#[deserr(default, error = DeserrJsonError<InvalidRenderTemplateId>)]
document_id: Option<String>,
#[deserr(default, error = DeserrJsonError<InvalidRenderTemplateId>)]
inline: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, PartialEq, ToSchema)]
pub struct RenderResult {
rendered: String,
}