@@ -378,8 +378,12 @@ impl<T: DeserializeOwned + AsyncValidate + Send + Sync> FromRequest for AsyncVal
378378 let value: T = json:: from_slice ( & body) ?;
379379
380380 // Create validation context from request
381- // TODO: Extract validators from App State
382- let ctx = ValidationContext :: default ( ) ;
381+ // Check if validators are configured in App State
382+ let ctx = if let Some ( ctx) = req. state ( ) . get :: < ValidationContext > ( ) {
383+ ctx. clone ( )
384+ } else {
385+ ValidationContext :: default ( )
386+ } ;
383387
384388 // Perform full validation (sync + async)
385389 if let Err ( errors) = value. validate_full ( & ctx) . await {
@@ -1715,4 +1719,178 @@ mod tests {
17151719 assert_eq ! ( cookies. get( "token" ) . unwrap( ) . value( ) , "xyz789" ) ;
17161720 }
17171721 }
1722+
1723+ #[ tokio:: test]
1724+ async fn test_async_validated_json_with_state_context ( ) {
1725+ use async_trait:: async_trait;
1726+ use rustapi_validate:: prelude:: * ;
1727+ use rustapi_validate:: v2:: {
1728+ AsyncValidationRule , DatabaseValidator , ValidationContextBuilder ,
1729+ } ;
1730+ use serde:: { Deserialize , Serialize } ;
1731+
1732+ struct MockDbValidator {
1733+ unique_values : Vec < String > ,
1734+ }
1735+
1736+ #[ async_trait]
1737+ impl DatabaseValidator for MockDbValidator {
1738+ async fn exists (
1739+ & self ,
1740+ _table : & str ,
1741+ _column : & str ,
1742+ _value : & str ,
1743+ ) -> Result < bool , String > {
1744+ Ok ( true )
1745+ }
1746+ async fn is_unique (
1747+ & self ,
1748+ _table : & str ,
1749+ _column : & str ,
1750+ value : & str ,
1751+ ) -> Result < bool , String > {
1752+ Ok ( !self . unique_values . contains ( & value. to_string ( ) ) )
1753+ }
1754+ async fn is_unique_except (
1755+ & self ,
1756+ _table : & str ,
1757+ _column : & str ,
1758+ value : & str ,
1759+ _except_id : & str ,
1760+ ) -> Result < bool , String > {
1761+ Ok ( !self . unique_values . contains ( & value. to_string ( ) ) )
1762+ }
1763+ }
1764+
1765+ #[ derive( Debug , Deserialize , Serialize ) ]
1766+ struct TestUser {
1767+ email : String ,
1768+ }
1769+
1770+ impl Validate for TestUser {
1771+ fn validate_with_group (
1772+ & self ,
1773+ _group : rustapi_validate:: v2:: ValidationGroup ,
1774+ ) -> Result < ( ) , rustapi_validate:: v2:: ValidationErrors > {
1775+ Ok ( ( ) )
1776+ }
1777+ }
1778+
1779+ #[ async_trait]
1780+ impl AsyncValidate for TestUser {
1781+ async fn validate_async_with_group (
1782+ & self ,
1783+ ctx : & ValidationContext ,
1784+ _group : rustapi_validate:: v2:: ValidationGroup ,
1785+ ) -> Result < ( ) , rustapi_validate:: v2:: ValidationErrors > {
1786+ let mut errors = rustapi_validate:: v2:: ValidationErrors :: new ( ) ;
1787+
1788+ let rule = AsyncUniqueRule :: new ( "users" , "email" ) ;
1789+ if let Err ( e) = rule. validate_async ( & self . email , ctx) . await {
1790+ errors. add ( "email" , e) ;
1791+ }
1792+
1793+ errors. into_result ( )
1794+ }
1795+ }
1796+
1797+ // Test 1: Without context in state (should fail due to missing validator)
1798+ let uri: http:: Uri = "/test" . parse ( ) . unwrap ( ) ;
1799+ let user = TestUser {
1800+ email : "new@example.com" . to_string ( ) ,
1801+ } ;
1802+ let body_bytes = serde_json:: to_vec ( & user) . unwrap ( ) ;
1803+
1804+ let builder = http:: Request :: builder ( )
1805+ . method ( Method :: POST )
1806+ . uri ( uri. clone ( ) )
1807+ . header ( "content-type" , "application/json" ) ;
1808+ let req = builder. body ( ( ) ) . unwrap ( ) ;
1809+ let ( parts, _) = req. into_parts ( ) ;
1810+
1811+ // Construct Request with BodyVariant::Buffered
1812+ let mut request = Request :: new (
1813+ parts,
1814+ crate :: request:: BodyVariant :: Buffered ( Bytes :: from ( body_bytes. clone ( ) ) ) ,
1815+ Arc :: new ( Extensions :: new ( ) ) ,
1816+ PathParams :: new ( ) ,
1817+ ) ;
1818+
1819+ let result = AsyncValidatedJson :: < TestUser > :: from_request ( & mut request) . await ;
1820+
1821+ assert ! ( result. is_err( ) , "Expected error when validator is missing" ) ;
1822+ let err = result. unwrap_err ( ) ;
1823+ let err_str = format ! ( "{:?}" , err) ;
1824+ assert ! (
1825+ err_str. contains( "Database validator not configured" )
1826+ || err_str. contains( "async_unique" ) ,
1827+ "Error should mention missing configuration or rule: {:?}" ,
1828+ err_str
1829+ ) ;
1830+
1831+ // Test 2: With context in state (should succeed)
1832+ let db_validator = MockDbValidator {
1833+ unique_values : vec ! [ "taken@example.com" . to_string( ) ] ,
1834+ } ;
1835+ let ctx = ValidationContextBuilder :: new ( )
1836+ . database ( db_validator)
1837+ . build ( ) ;
1838+
1839+ let mut extensions = Extensions :: new ( ) ;
1840+ extensions. insert ( ctx) ;
1841+
1842+ let builder = http:: Request :: builder ( )
1843+ . method ( Method :: POST )
1844+ . uri ( uri. clone ( ) )
1845+ . header ( "content-type" , "application/json" ) ;
1846+ let req = builder. body ( ( ) ) . unwrap ( ) ;
1847+ let ( parts, _) = req. into_parts ( ) ;
1848+
1849+ let mut request = Request :: new (
1850+ parts,
1851+ crate :: request:: BodyVariant :: Buffered ( Bytes :: from ( body_bytes. clone ( ) ) ) ,
1852+ Arc :: new ( extensions) ,
1853+ PathParams :: new ( ) ,
1854+ ) ;
1855+
1856+ let result = AsyncValidatedJson :: < TestUser > :: from_request ( & mut request) . await ;
1857+ assert ! (
1858+ result. is_ok( ) ,
1859+ "Expected success when validator is present and value is unique. Error: {:?}" ,
1860+ result. err( )
1861+ ) ;
1862+
1863+ // Test 3: With context in state (should fail validation logic)
1864+ let user_taken = TestUser {
1865+ email : "taken@example.com" . to_string ( ) ,
1866+ } ;
1867+ let body_taken = serde_json:: to_vec ( & user_taken) . unwrap ( ) ;
1868+
1869+ let db_validator = MockDbValidator {
1870+ unique_values : vec ! [ "taken@example.com" . to_string( ) ] ,
1871+ } ;
1872+ let ctx = ValidationContextBuilder :: new ( )
1873+ . database ( db_validator)
1874+ . build ( ) ;
1875+
1876+ let mut extensions = Extensions :: new ( ) ;
1877+ extensions. insert ( ctx) ;
1878+
1879+ let builder = http:: Request :: builder ( )
1880+ . method ( Method :: POST )
1881+ . uri ( "/test" )
1882+ . header ( "content-type" , "application/json" ) ;
1883+ let req = builder. body ( ( ) ) . unwrap ( ) ;
1884+ let ( parts, _) = req. into_parts ( ) ;
1885+
1886+ let mut request = Request :: new (
1887+ parts,
1888+ crate :: request:: BodyVariant :: Buffered ( Bytes :: from ( body_taken) ) ,
1889+ Arc :: new ( extensions) ,
1890+ PathParams :: new ( ) ,
1891+ ) ;
1892+
1893+ let result = AsyncValidatedJson :: < TestUser > :: from_request ( & mut request) . await ;
1894+ assert ! ( result. is_err( ) , "Expected validation error for taken email" ) ;
1895+ }
17181896}
0 commit comments