diff --git a/scripts/test.sh b/scripts/test.sh index f51048d8..f186b847 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -13,7 +13,7 @@ elif [[ "$crate" == "utoipa-gen" ]]; then cargo test -p utoipa-gen --test schema_derive_test --features decimal_float cargo test -p utoipa-gen --test path_derive_auto_into_responses --features auto_into_responses,utoipa/uuid,uuid - cargo test -p utoipa-gen --test path_derive_actix --test path_parameter_derive_actix --features actix_extras,utoipa/uuid,uuid + cargo test -p utoipa-gen --test path_derive_actix --test path_parameter_derive_actix --features actix_extras,utoipa/uuid,uuid,utoipa/chrono,chrono,utoipa/time,time cargo test -p utoipa-gen --test path_derive_auto_into_responses_actix --features actix_extras,utoipa/auto_into_responses,utoipa/uuid,uuid cargo test -p utoipa-gen --test path_derive_rocket --features rocket_extras diff --git a/utoipa-gen/tests/path_derive.rs b/utoipa-gen/tests/path_derive.rs index 4f1aa8eb..f88ba7ba 100644 --- a/utoipa-gen/tests/path_derive.rs +++ b/utoipa-gen/tests/path_derive.rs @@ -280,6 +280,138 @@ fn derive_path_with_security_requirements() { } } +#[test] +fn derive_path_with_datetime_format_query_parameter() { + #[derive(serde::Deserialize, utoipa::ToSchema)] + struct Since { + /// Some date + #[allow(dead_code)] + date: String, + /// Some time + #[allow(dead_code)] + time: String, + } + + /// This is test operation + /// + /// This is long description for test operation + #[utoipa::path( + get, + path = "/foo/{id}/{start}", + responses( + (status = 200, description = "success response") + ), + params( + ("id" = i64, Path, description = "Foo database id"), + ("start" = String, Path, description = "Datetime since foo is updated", format = DateTime) + ) + )] + #[allow(unused)] + async fn get_foos_by_id_date() -> String { + "".to_string() + } + + let operation: Value = test_api_fn_doc! { + get_foos_by_id_date, + operation: get, + path: "/foo/{id}/{start}" + }; + + let parameters: &Value = operation.get("parameters").unwrap(); + + assert_json_eq!( + parameters, + json!([ + { + "description": "Foo database id", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer", + } + }, + { + "description": "Datetime since foo is updated", + "in": "path", + "name": "start", + "required": true, + "schema": { + "format": "date-time", + "type": "string", + } + } + ]) + ); +} + +#[test] +fn derive_path_with_datetime_format_path_parameter() { + #[derive(serde::Deserialize, utoipa::ToSchema)] + struct Since { + /// Some date + #[allow(dead_code)] + date: String, + /// Some time + #[allow(dead_code)] + time: String, + } + + /// This is test operation + /// + /// This is long description for test operation + #[utoipa::path( + get, + path = "/foo/{id}", + responses( + (status = 200, description = "success response") + ), + params( + ("id" = i64, description = "Foo database id"), + ("start" = String, Query, description = "Datetime since foo is updated", format = DateTime) + ) + )] + #[allow(unused)] + async fn get_foos_by_id_date() -> String { + "".to_string() + } + + let operation: Value = test_api_fn_doc! { + get_foos_by_id_date, + operation: get, + path: "/foo/{id}" + }; + + let parameters: &Value = operation.get("parameters").unwrap(); + + assert_json_eq!( + parameters, + json!([ + { + "description": "Foo database id", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer", + } + }, + { + "description": "Datetime since foo is updated", + "in": "query", + "name": "start", + "required": true, + "schema": { + "format": "date-time", + "type": "string", + } + } + ]) + ); +} + #[test] fn derive_path_with_parameter_schema() { #[derive(serde::Deserialize, utoipa::ToSchema)] diff --git a/utoipa-gen/tests/path_parameter_derive_actix.rs b/utoipa-gen/tests/path_parameter_derive_actix.rs index 32408427..732ddc24 100644 --- a/utoipa-gen/tests/path_parameter_derive_actix.rs +++ b/utoipa-gen/tests/path_parameter_derive_actix.rs @@ -164,3 +164,112 @@ fn derive_params_from_method_args_actix_success() { "[1].schema.format" = r#"null"#, "Parameter schema format" }; } + +#[test] +fn derive_path_with_date_params_implicit() { + mod mod_derive_path_with_date_params { + use actix_web::{get, web, HttpResponse, Responder}; + use chrono::{DateTime, Utc}; + use serde_json::json; + use time::Date; + + #[utoipa::path( + responses( + (status = 200, description = "success response") + ), + params( + ("start_date", description = "Start date filter"), + ("end_date", description = "End date filter"), + ) + )] + #[get("/visitors/v1/{start_date}/{end_date}")] + #[allow(unused)] + async fn get_foo_by_date(path: web::Path<(Date, DateTime)>) -> impl Responder { + let (start_date, end_date) = path.into_inner(); + HttpResponse::Ok() + .json(json!({ "params": &format!("{:?} {:?}", start_date, end_date) })) + } + } + + #[derive(OpenApi, Default)] + #[openapi(paths(mod_derive_path_with_date_params::get_foo_by_date))] + struct ApiDoc; + + let doc = serde_json::to_value(ApiDoc::openapi()).unwrap(); + let parameters = doc + .pointer("/paths/~1visitors~1v1~1{start_date}~1{end_date}/get/parameters") + .unwrap(); + + common::assert_json_array_len(parameters, 2); + assert_value! {parameters=> + "[0].in" = r#""path""#, "Parameter in" + "[0].name" = r#""start_date""#, "Parameter name" + "[0].description" = r#""Start date filter""#, "Parameter description" + "[0].required" = r#"true"#, "Parameter required" + "[0].deprecated" = r#"null"#, "Parameter deprecated" + "[0].schema.type" = r#""string""#, "Parameter schema type" + "[0].schema.format" = r#""date""#, "Parameter schema format" + + "[1].in" = r#""path""#, "Parameter in" + "[1].name" = r#""end_date""#, "Parameter name" + "[1].description" = r#""End date filter""#, "Parameter description" + "[1].required" = r#"true"#, "Parameter required" + "[1].deprecated" = r#"null"#, "Parameter deprecated" + "[1].schema.type" = r#""string""#, "Parameter schema type" + "[1].schema.format" = r#""date-time""#, "Parameter schema format" + }; +} + +#[test] +fn derive_path_with_date_params_explicit_ignored() { + mod mod_derive_path_with_date_params { + use actix_web::{get, web, HttpResponse, Responder}; + use serde_json::json; + use time::Date; + + #[utoipa::path( + responses( + (status = 200, description = "success response") + ), + params( + ("start_date", description = "Start date filter", format = Date), + ("end_date", description = "End date filter", format = DateTime), + ) + )] + #[get("/visitors/v1/{start_date}/{end_date}")] + #[allow(unused)] + async fn get_foo_by_date(path: web::Path<(Date, String)>) -> impl Responder { + let (start_date, end_date) = path.into_inner(); + HttpResponse::Ok() + .json(json!({ "params": &format!("{:?} {:?}", start_date, end_date) })) + } + } + + #[derive(OpenApi, Default)] + #[openapi(paths(mod_derive_path_with_date_params::get_foo_by_date))] + struct ApiDoc; + + let doc = serde_json::to_value(ApiDoc::openapi()).unwrap(); + let parameters = doc + .pointer("/paths/~1visitors~1v1~1{start_date}~1{end_date}/get/parameters") + .unwrap(); + + common::assert_json_array_len(parameters, 2); + assert_value! {parameters=> + "[0].in" = r#""path""#, "Parameter in" + "[0].name" = r#""start_date""#, "Parameter name" + "[0].description" = r#""Start date filter""#, "Parameter description" + "[0].required" = r#"true"#, "Parameter required" + "[0].deprecated" = r#"null"#, "Parameter deprecated" + "[0].schema.type" = r#""string""#, "Parameter schema type" + "[0].schema.format" = r#""date""#, "Parameter schema format" + + "[1].in" = r#""path""#, "Parameter in" + "[1].name" = r#""end_date""#, "Parameter name" + "[1].description" = r#""End date filter""#, "Parameter description" + "[1].required" = r#"true"#, "Parameter required" + "[1].deprecated" = r#"null"#, "Parameter deprecated" + "[1].schema.type" = r#""string""#, "Parameter schema type" + "[1].schema.format" = r#"null"#, "Parameter schema format" + }; +}