1
1
use axum:: {
2
- extract:: { FromRequest , FromRequestParts , State } ,
3
- http:: { request :: Parts , Request } ,
2
+ extract:: { FromRequest , State } ,
3
+ http:: Request ,
4
4
response:: IntoResponse ,
5
5
Json ,
6
6
} ;
@@ -15,6 +15,7 @@ use fuel_streams_domains::{
15
15
inputs:: { queryable:: InputsQuery , InputType } ,
16
16
queryable:: { Queryable , ValidatedQuery } ,
17
17
} ;
18
+ use paste:: paste;
18
19
19
20
use super :: open_api:: TAG_INPUTS ;
20
21
use crate :: server:: {
@@ -23,71 +24,113 @@ use crate::server::{
23
24
state:: ServerState ,
24
25
} ;
25
26
26
- pub struct InputTypeVariant ( Option < InputType > ) ;
27
+ #[ macro_export]
28
+ macro_rules! generate_input_endpoints {
29
+ (
30
+ $tag: expr,
31
+ $base_path: expr,
32
+ $base_name: ident,
33
+ $(
34
+ $variant: ident => $path_suffix: literal
35
+ ) ,*
36
+ ) => {
37
+ paste! {
38
+ $(
39
+ #[ utoipa:: path(
40
+ get,
41
+ path = concat!( $base_path, $path_suffix) ,
42
+ tag = $tag,
43
+ params(
44
+ ( "txId" = Option <TxId >, Query , description = "Filter by transaction ID" ) ,
45
+ ( "txIndex" = Option <u32 >, Query , description = "Filter by transaction index" ) ,
46
+ ( "inputIndex" = Option <i32 >, Query , description = "Filter by input index" ) ,
47
+ ( "blockHeight" = Option <BlockHeight >, Query , description = "Filter by block height" ) ,
48
+ ( "ownerId" = Option <Address >, Query , description = "Filter by owner ID (for coin inputs)" ) ,
49
+ ( "assetId" = Option <AssetId >, Query , description = "Filter by asset ID (for coin inputs)" ) ,
50
+ ( "contractId" = Option <ContractId >, Query , description = "Filter by contract ID (for contract inputs)" ) ,
51
+ ( "senderAddress" = Option <Address >, Query , description = "Filter by sender address (for message inputs)" ) ,
52
+ ( "recipientAddress" = Option <Address >, Query , description = "Filter by recipient address (for message inputs)" ) ,
53
+ ( "address" = Option <Address >, Query , description = "Filter by address" ) ,
54
+ ( "after" = Option <i32 >, Query , description = "Return inputs after this height" ) ,
55
+ ( "before" = Option <i32 >, Query , description = "Return inputs before this height" ) ,
56
+ ( "first" = Option <i32 >, Query , description = "Limit results, sorted by ascending block height" , maximum = 100 ) ,
57
+ ( "last" = Option <i32 >, Query , description = "Limit results, sorted by descending block height" , maximum = 100 )
58
+ ) ,
59
+ responses(
60
+ ( status = 200 , description = "Successfully retrieved inputs" , body = GetDataResponse ) ,
61
+ ( status = 400 , description = "Invalid query parameters" , body = String ) ,
62
+ ( status = 500 , description = "Internal server error" , body = String )
63
+ ) ,
64
+ security(
65
+ ( "api_key" = [ ] )
66
+ )
67
+ ) ]
68
+ pub async fn [ <$base_name _ $variant: snake>] (
69
+ State ( state) : State <ServerState >,
70
+ req: Request <axum:: body:: Body >,
71
+ ) -> Result <impl IntoResponse , ApiError > {
72
+ let mut query = ValidatedQuery :: <InputsQuery >:: from_request( req, & state)
73
+ . await ?
74
+ . into_inner( ) ;
75
+ query. set_input_type( Some ( InputType :: $variant) ) ;
76
+ let response: GetDataResponse =
77
+ query. execute( & state. db. pool) . await ?. try_into( ) ?;
78
+ Ok ( Json ( response) )
79
+ }
80
+ ) *
27
81
28
- impl < S > FromRequestParts < S > for InputTypeVariant
29
- where
30
- S : Send + Sync ,
31
- {
32
- type Rejection = ApiError ;
33
-
34
- async fn from_request_parts (
35
- parts : & mut Parts ,
36
- _state : & S ,
37
- ) -> Result < Self , Self :: Rejection > {
38
- let path = parts. uri . path ( ) ;
39
- let variant = match path {
40
- p if p. ends_with ( "/message" ) => Some ( InputType :: Message ) ,
41
- p if p. ends_with ( "/contract" ) => Some ( InputType :: Contract ) ,
42
- p if p. ends_with ( "/coin" ) => Some ( InputType :: Coin ) ,
43
- _ => None ,
44
- } ;
45
- Ok ( InputTypeVariant ( variant) )
46
- }
82
+ // Generic handler for the base path
83
+ #[ utoipa:: path(
84
+ get,
85
+ path = $base_path,
86
+ tag = $tag,
87
+ params(
88
+ ( "txId" = Option <TxId >, Query , description = "Filter by transaction ID" ) ,
89
+ ( "txIndex" = Option <u32 >, Query , description = "Filter by transaction index" ) ,
90
+ ( "inputIndex" = Option <i32 >, Query , description = "Filter by input index" ) ,
91
+ ( "inputType" = Option <InputType >, Query , description = "Filter by input type" ) ,
92
+ ( "blockHeight" = Option <BlockHeight >, Query , description = "Filter by block height" ) ,
93
+ ( "ownerId" = Option <Address >, Query , description = "Filter by owner ID (for coin inputs)" ) ,
94
+ ( "assetId" = Option <AssetId >, Query , description = "Filter by asset ID (for coin inputs)" ) ,
95
+ ( "contractId" = Option <ContractId >, Query , description = "Filter by contract ID (for contract inputs)" ) ,
96
+ ( "senderAddress" = Option <Address >, Query , description = "Filter by sender address (for message inputs)" ) ,
97
+ ( "recipientAddress" = Option <Address >, Query , description = "Filter by recipient address (for message inputs)" ) ,
98
+ ( "address" = Option <Address >, Query , description = "Filter by address" ) ,
99
+ ( "after" = Option <i32 >, Query , description = "Return inputs after this height" ) ,
100
+ ( "before" = Option <i32 >, Query , description = "Return inputs before this height" ) ,
101
+ ( "first" = Option <i32 >, Query , description = "Limit results, sorted by ascending block height" , maximum = 100 ) ,
102
+ ( "last" = Option <i32 >, Query , description = "Limit results, sorted by descending block height" , maximum = 100 )
103
+ ) ,
104
+ responses(
105
+ ( status = 200 , description = "Successfully retrieved inputs" , body = GetDataResponse ) ,
106
+ ( status = 400 , description = "Invalid query parameters" , body = String ) ,
107
+ ( status = 500 , description = "Internal server error" , body = String )
108
+ ) ,
109
+ security(
110
+ ( "api_key" = [ ] )
111
+ )
112
+ ) ]
113
+ pub async fn $base_name(
114
+ State ( state) : State <ServerState >,
115
+ req: Request <axum:: body:: Body >,
116
+ ) -> Result <impl IntoResponse , ApiError > {
117
+ let mut query = ValidatedQuery :: <InputsQuery >:: from_request( req, & state)
118
+ . await ?
119
+ . into_inner( ) ;
120
+ query. set_input_type( None ) ;
121
+ let response: GetDataResponse =
122
+ query. execute( & state. db. pool) . await ?. try_into( ) ?;
123
+ Ok ( Json ( response) )
124
+ }
125
+ }
126
+ } ;
47
127
}
48
128
49
- #[ utoipa:: path(
50
- get,
51
- path = "/inputs" ,
52
- tag = TAG_INPUTS ,
53
- params(
54
- ( "txId" = Option <TxId >, Query , description = "Filter by transaction ID" ) ,
55
- ( "txIndex" = Option <u32 >, Query , description = "Filter by transaction index" ) ,
56
- ( "inputIndex" = Option <i32 >, Query , description = "Filter by input index" ) ,
57
- ( "inputType" = Option <InputType >, Query , description = "Filter by input type" ) ,
58
- ( "blockHeight" = Option <BlockHeight >, Query , description = "Filter by block height" ) ,
59
- ( "ownerId" = Option <Address >, Query , description = "Filter by owner ID (for coin inputs)" ) ,
60
- ( "assetId" = Option <AssetId >, Query , description = "Filter by asset ID (for coin inputs)" ) ,
61
- ( "contractId" = Option <ContractId >, Query , description = "Filter by contract ID (for contract inputs)" ) ,
62
- ( "senderAddress" = Option <Address >, Query , description = "Filter by sender address (for message inputs)" ) ,
63
- ( "recipientAddress" = Option <Address >, Query , description = "Filter by recipient address (for message inputs)" ) ,
64
- ( "address" = Option <Address >, Query , description = "Filter by address" ) ,
65
- ( "after" = Option <i32 >, Query , description = "Return inputs after this height" ) ,
66
- ( "before" = Option <i32 >, Query , description = "Return inputs before this height" ) ,
67
- ( "first" = Option <i32 >, Query , description = "Limit results, sorted by ascending block height" , maximum = 100 ) ,
68
- ( "last" = Option <i32 >, Query , description = "Limit results, sorted by descending block height" , maximum = 100 )
69
- ) ,
70
- responses(
71
- ( status = 200 , description = "Successfully retrieved inputs" , body = GetDataResponse ) ,
72
- ( status = 400 , description = "Invalid query parameters" , body = String ) ,
73
- ( status = 500 , description = "Internal server error" , body = String )
74
- ) ,
75
- security(
76
- ( "api_key" = [ ] )
77
- )
78
- ) ]
79
- pub async fn get_inputs (
80
- State ( state) : State < ServerState > ,
81
- variant : InputTypeVariant ,
82
- req : Request < axum:: body:: Body > ,
83
- ) -> Result < impl IntoResponse , ApiError > {
84
- let mut query = ValidatedQuery :: < InputsQuery > :: from_request ( req, & state)
85
- . await ?
86
- . into_inner ( ) ;
87
- if let Some ( input_type) = variant. 0 {
88
- query. set_input_type ( Some ( input_type) ) ;
89
- }
90
- let response: GetDataResponse =
91
- query. execute ( & state. db . pool ) . await ?. try_into ( ) ?;
92
- Ok ( Json ( response) )
93
- }
129
+ generate_input_endpoints ! (
130
+ TAG_INPUTS ,
131
+ "/inputs" ,
132
+ get_inputs,
133
+ Message => "/message" ,
134
+ Contract => "/contract" ,
135
+ Coin => "/coin"
136
+ ) ;
0 commit comments