1+ use std:: sync:: Arc ;
2+ use std:: time:: Duration ;
3+
14use anyhow:: { anyhow, Error } ;
25use bytes:: Bytes ;
36use graph:: futures03:: future:: BoxFuture ;
4- use graph:: {
5- derive:: CheapClone ,
6- ipfs_client:: { CidFile , IpfsClient } ,
7- prelude:: CheapClone ,
8- } ;
9- use std:: time:: Duration ;
7+ use graph:: ipfs:: ContentPath ;
8+ use graph:: ipfs:: IpfsClient ;
9+ use graph:: ipfs:: IpfsError ;
10+ use graph:: { derive:: CheapClone , prelude:: CheapClone } ;
1011use tower:: { buffer:: Buffer , ServiceBuilder , ServiceExt } ;
1112
12- const CLOUDFLARE_TIMEOUT : u16 = 524 ;
13- const GATEWAY_TIMEOUT : u16 = 504 ;
14-
15- pub type IpfsService = Buffer < CidFile , BoxFuture < ' static , Result < Option < Bytes > , Error > > > ;
13+ pub type IpfsService = Buffer < ContentPath , BoxFuture < ' static , Result < Option < Bytes > , Error > > > ;
1614
1715pub fn ipfs_service (
18- client : IpfsClient ,
16+ client : Arc < dyn IpfsClient > ,
1917 max_file_size : usize ,
2018 timeout : Duration ,
2119 rate_limit : u16 ,
2220) -> IpfsService {
2321 let ipfs = IpfsServiceInner {
2422 client,
25- max_file_size,
2623 timeout,
24+ max_file_size,
2725 } ;
2826
2927 let svc = ServiceBuilder :: new ( )
@@ -38,37 +36,30 @@ pub fn ipfs_service(
3836
3937#[ derive( Clone , CheapClone ) ]
4038struct IpfsServiceInner {
41- client : IpfsClient ,
42- max_file_size : usize ,
39+ client : Arc < dyn IpfsClient > ,
4340 timeout : Duration ,
41+ max_file_size : usize ,
4442}
4543
4644impl IpfsServiceInner {
47- async fn call_inner ( self , req : CidFile ) -> Result < Option < Bytes > , Error > {
48- let CidFile { cid, path } = req;
49- let multihash = cid. hash ( ) . code ( ) ;
45+ async fn call_inner ( self , path : ContentPath ) -> Result < Option < Bytes > , Error > {
46+ let multihash = path. cid ( ) . hash ( ) . code ( ) ;
5047 if !SAFE_MULTIHASHES . contains ( & multihash) {
5148 return Err ( anyhow ! ( "CID multihash {} is not allowed" , multihash) ) ;
5249 }
5350
54- let cid_str = match path {
55- Some ( path) => format ! ( "{}/{}" , cid, path) ,
56- None => cid. to_string ( ) ,
57- } ;
58-
5951 let res = self
6052 . client
61- . cat_all ( & cid_str , Some ( self . timeout ) , self . max_file_size )
53+ . cat ( & path , self . max_file_size , Some ( self . timeout ) )
6254 . await ;
6355
6456 match res {
6557 Ok ( file_bytes) => Ok ( Some ( file_bytes) ) ,
66- Err ( e) => match e. status ( ) . map ( |e| e. as_u16 ( ) ) {
67- // Timeouts in IPFS mean the file is not available, so we return `None`
68- Some ( GATEWAY_TIMEOUT ) | Some ( CLOUDFLARE_TIMEOUT ) => return Ok ( None ) ,
69- _ if e. is_timeout ( ) => return Ok ( None ) ,
70- _ => return Err ( e. into ( ) ) ,
71- } ,
58+ Err ( IpfsError :: RequestFailed ( err) ) if err. is_timeout ( ) => {
59+ // Timeouts in IPFS mean that the content is not available, so we return `None`.
60+ Ok ( None )
61+ }
62+ Err ( err) => Err ( err. into ( ) ) ,
7263 }
7364 }
7465}
@@ -96,48 +87,42 @@ const SAFE_MULTIHASHES: [u64; 15] = [
9687
9788#[ cfg( test) ]
9889mod test {
99- use ipfs:: IpfsApi ;
100- use ipfs_api as ipfs;
101- use std:: { fs, str:: FromStr , time:: Duration } ;
90+ use std:: time:: Duration ;
91+
92+ use graph:: components:: link_resolver:: ArweaveClient ;
93+ use graph:: components:: link_resolver:: ArweaveResolver ;
94+ use graph:: data:: value:: Word ;
95+ use graph:: ipfs:: test_utils:: add_files_to_local_ipfs_node_for_testing;
96+ use graph:: ipfs:: IpfsRpcClient ;
97+ use graph:: ipfs:: ServerAddress ;
98+ use graph:: tokio;
10299 use tower:: ServiceExt ;
103-
104- use cid:: Cid ;
105- use graph:: {
106- components:: link_resolver:: { ArweaveClient , ArweaveResolver } ,
107- data:: value:: Word ,
108- ipfs_client:: IpfsClient ,
109- tokio,
110- } ;
111-
112100 use uuid:: Uuid ;
113101
102+ use super :: * ;
103+
114104 #[ tokio:: test]
115105 async fn cat_file_in_folder ( ) {
116- let path = "./tests/fixtures/ipfs_folder" ;
117- let uid = Uuid :: new_v4 ( ) . to_string ( ) ;
118- fs:: write ( format ! ( "{}/random.txt" , path) , & uid) . unwrap ( ) ;
106+ let random_bytes = Uuid :: new_v4 ( ) . as_bytes ( ) . to_vec ( ) ;
107+ let ipfs_file = ( "dir/file.txt" , random_bytes. clone ( ) ) ;
119108
120- let cl: ipfs:: IpfsClient = ipfs:: IpfsClient :: default ( ) ;
109+ let add_resp = add_files_to_local_ipfs_node_for_testing ( [ ipfs_file] )
110+ . await
111+ . unwrap ( ) ;
121112
122- let rsp = cl . add_path ( path ) . await . unwrap ( ) ;
113+ let dir_cid = add_resp . into_iter ( ) . find ( |x| x . name == "dir" ) . unwrap ( ) . hash ;
123114
124- let ipfs_folder = rsp. iter ( ) . find ( |rsp| rsp. name == "ipfs_folder" ) . unwrap ( ) ;
115+ let client =
116+ IpfsRpcClient :: new_unchecked ( ServerAddress :: local_rpc_api ( ) , & graph:: log:: discard ( ) )
117+ . unwrap ( )
118+ . into_boxed ( ) ;
125119
126- let local = IpfsClient :: localhost ( ) ;
127- let cid = Cid :: from_str ( & ipfs_folder. hash ) . unwrap ( ) ;
128- let file = "random.txt" . to_string ( ) ;
120+ let svc = ipfs_service ( client. into ( ) , 100000 , Duration :: from_secs ( 30 ) , 10 ) ;
129121
130- let svc = super :: ipfs_service ( local, 100000 , Duration :: from_secs ( 5 ) , 10 ) ;
122+ let path = ContentPath :: new ( format ! ( "{dir_cid}/file.txt" ) ) . unwrap ( ) ;
123+ let content = svc. oneshot ( path) . await . unwrap ( ) . unwrap ( ) ;
131124
132- let content = svc
133- . oneshot ( super :: CidFile {
134- cid,
135- path : Some ( file) ,
136- } )
137- . await
138- . unwrap ( )
139- . unwrap ( ) ;
140- assert_eq ! ( content. to_vec( ) , uid. as_bytes( ) . to_vec( ) ) ;
125+ assert_eq ! ( content. to_vec( ) , random_bytes) ;
141126 }
142127
143128 #[ tokio:: test]
0 commit comments