@@ -6,7 +6,7 @@ use std::{
6
6
7
7
use anyhow:: { Context , Error } ;
8
8
use http:: { HeaderMap , Method } ;
9
- use semver:: Version ;
9
+ use semver:: { Version , VersionReq } ;
10
10
use url:: Url ;
11
11
use webc:: metadata:: Manifest ;
12
12
@@ -60,39 +60,6 @@ impl WapmSource {
60
60
& self . registry_endpoint
61
61
}
62
62
63
- async fn lookup_package ( & self , package_name : & str ) -> Result < WapmWebQuery , Error > {
64
- if let Some ( cache) = & self . cache {
65
- match cache. lookup_cached_query ( package_name) {
66
- Ok ( Some ( cached) ) => {
67
- tracing:: debug!( "Cache hit!" ) ;
68
- return Ok ( cached) ;
69
- }
70
- Ok ( None ) => { }
71
- Err ( e) => {
72
- tracing:: warn!(
73
- package_name,
74
- error = & * e,
75
- "An unexpected error occurred while checking the local query cache" ,
76
- ) ;
77
- }
78
- }
79
- }
80
-
81
- let response = self . query_graphql ( package_name) . await ?;
82
-
83
- if let Some ( cache) = & self . cache {
84
- if let Err ( e) = cache. update ( package_name, & response) {
85
- tracing:: warn!(
86
- package_name,
87
- error = & * e,
88
- "An error occurred while caching the GraphQL response" ,
89
- ) ;
90
- }
91
- }
92
-
93
- Ok ( response)
94
- }
95
-
96
63
#[ tracing:: instrument( level = "debug" , skip_all) ]
97
64
async fn query_graphql ( & self , package_name : & str ) -> Result < WapmWebQuery , Error > {
98
65
#[ derive( serde:: Serialize ) ]
@@ -166,68 +133,104 @@ impl WapmSource {
166
133
impl Source for WapmSource {
167
134
#[ tracing:: instrument( level = "debug" , skip_all, fields( %package) ) ]
168
135
async fn query ( & self , package : & PackageSpecifier ) -> Result < Vec < PackageSummary > , QueryError > {
169
- let ( full_name , version_constraint) = match package {
136
+ let ( package_name , version_constraint) = match package {
170
137
PackageSpecifier :: Registry { full_name, version } => ( full_name, version) ,
171
138
_ => return Err ( QueryError :: Unsupported ) ,
172
139
} ;
173
140
174
- let response: WapmWebQuery = self . lookup_package ( full_name) . await ?;
175
-
176
- let mut summaries = Vec :: new ( ) ;
177
-
178
- let WapmWebQueryGetPackage {
179
- package_name,
180
- namespace,
181
- versions,
182
- } = response. data . get_package . ok_or ( QueryError :: NotFound ) ?;
183
- let mut archived_versions = Vec :: new ( ) ;
184
-
185
- for pkg_version in versions {
186
- let version = match Version :: parse ( & pkg_version. version ) {
187
- Ok ( v) => v,
141
+ if let Some ( cache) = & self . cache {
142
+ match cache. lookup_cached_query ( package_name) {
143
+ Ok ( Some ( cached) ) => {
144
+ if let Ok ( cached) = matching_package_summaries ( cached, version_constraint) {
145
+ tracing:: debug!( "Cache hit!" ) ;
146
+ return Ok ( cached) ;
147
+ }
148
+ }
149
+ Ok ( None ) => { }
188
150
Err ( e) => {
189
- tracing:: debug !(
190
- pkg . version = pkg_version . version . as_str ( ) ,
191
- error = & e as & dyn std :: error :: Error ,
192
- "Skipping a version because it doesn't have a valid version numer " ,
151
+ tracing:: warn !(
152
+ package_name ,
153
+ error = & * e ,
154
+ "An unexpected error occurred while checking the local query cache " ,
193
155
) ;
194
- continue ;
195
156
}
196
- } ;
157
+ }
158
+ }
197
159
198
- if pkg_version. is_archived {
160
+ let response = self . query_graphql ( package_name) . await ?;
161
+
162
+ if let Some ( cache) = & self . cache {
163
+ if let Err ( e) = cache. update ( package_name, & response) {
164
+ tracing:: warn!(
165
+ package_name,
166
+ error = & * e,
167
+ "An error occurred while caching the GraphQL response" ,
168
+ ) ;
169
+ }
170
+ }
171
+
172
+ matching_package_summaries ( response, version_constraint)
173
+ }
174
+ }
175
+
176
+ fn matching_package_summaries (
177
+ response : WapmWebQuery ,
178
+ version_constraint : & VersionReq ,
179
+ ) -> Result < Vec < PackageSummary > , QueryError > {
180
+ let mut summaries = Vec :: new ( ) ;
181
+
182
+ let WapmWebQueryGetPackage {
183
+ package_name,
184
+ namespace,
185
+ versions,
186
+ } = response. data . get_package . ok_or ( QueryError :: NotFound ) ?;
187
+ let mut archived_versions = Vec :: new ( ) ;
188
+
189
+ for pkg_version in versions {
190
+ let version = match Version :: parse ( & pkg_version. version ) {
191
+ Ok ( v) => v,
192
+ Err ( e) => {
199
193
tracing:: debug!(
200
- pkg. version=%version,
201
- "Skipping an archived version" ,
194
+ pkg. version = pkg_version. version. as_str( ) ,
195
+ error = & e as & dyn std:: error:: Error ,
196
+ "Skipping a version because it doesn't have a valid version numer" ,
202
197
) ;
203
- archived_versions. push ( version) ;
204
198
continue ;
205
199
}
200
+ } ;
206
201
207
- if version_constraint. matches ( & version) {
208
- match decode_summary (
209
- pkg_version,
210
- & response. data . info . default_frontend ,
211
- & namespace,
212
- & package_name,
213
- ) {
214
- Ok ( summary) => summaries. push ( summary) ,
215
- Err ( e) => {
216
- tracing:: debug!(
217
- version=%version,
218
- error=& * e,
219
- "Skipping version because its metadata couldn't be parsed"
220
- ) ;
221
- }
202
+ if pkg_version. is_archived {
203
+ tracing:: debug!(
204
+ pkg. version=%version,
205
+ "Skipping an archived version" ,
206
+ ) ;
207
+ archived_versions. push ( version) ;
208
+ continue ;
209
+ }
210
+
211
+ if version_constraint. matches ( & version) {
212
+ match decode_summary (
213
+ pkg_version,
214
+ & response. data . info . default_frontend ,
215
+ & namespace,
216
+ & package_name,
217
+ ) {
218
+ Ok ( summary) => summaries. push ( summary) ,
219
+ Err ( e) => {
220
+ tracing:: debug!(
221
+ version=%version,
222
+ error=& * e,
223
+ "Skipping version because its metadata couldn't be parsed"
224
+ ) ;
222
225
}
223
226
}
224
227
}
228
+ }
225
229
226
- if summaries. is_empty ( ) {
227
- Err ( QueryError :: NoMatches { archived_versions } )
228
- } else {
229
- Ok ( summaries)
230
- }
230
+ if summaries. is_empty ( ) {
231
+ Err ( QueryError :: NoMatches { archived_versions } )
232
+ } else {
233
+ Ok ( summaries)
231
234
}
232
235
}
233
236
@@ -719,4 +722,88 @@ mod tests {
719
722
assert_eq ! ( summaries. len( ) , 1 ) ;
720
723
assert_eq ! ( summaries[ 0 ] . pkg. version. to_string( ) , "3.12.1" ) ;
721
724
}
725
+
726
+ #[ tokio:: test]
727
+ async fn query_the_backend_again_if_cached_queries_dont_match ( ) {
728
+ let cached_value = serde_json:: from_value ( serde_json:: json! {
729
+ {
730
+ "data" : {
731
+ "getPackage" : {
732
+ "packageName" : "python" ,
733
+ "namespace" : "wasmer" ,
734
+ "versions" : [
735
+ {
736
+ "version" : "3.12.0" ,
737
+ "piritaManifest" : "{\" package\" : {\" wapm\" : {\" name\" : \" wasmer/python\" , \" version\" : \" 3.12.0\" , \" description\" : \" Python\" }}}" ,
738
+ "distribution" : {
739
+ "piritaDownloadUrl" : "https://wasmer.io/wasmer/[email protected] " ,
740
+ "piritaSha256Hash" : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
741
+ }
742
+ } ,
743
+ ]
744
+ } ,
745
+ "info" : {
746
+ "defaultFrontend" : "https://wasmer.io/" ,
747
+ } ,
748
+ }
749
+ }
750
+ } ) . unwrap ( ) ;
751
+ let body = serde_json:: json! {
752
+ {
753
+ "data" : {
754
+ "getPackage" : {
755
+ "packageName" : "python" ,
756
+ "namespace" : "wasmer" ,
757
+ "versions" : [
758
+ {
759
+ "version" : "4.0.0" ,
760
+ "piritaManifest" : "{\" package\" : {\" wapm\" : {\" name\" : \" wasmer/python\" , \" version\" : \" 4.0.0\" , \" description\" : \" Python\" }}}" ,
761
+ "distribution" : {
762
+ "piritaDownloadUrl" : "https://wasmer.io/wasmer/[email protected] " ,
763
+ "piritaSha256Hash" : "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
764
+ }
765
+ } ,
766
+ {
767
+ "version" : "3.12.0" ,
768
+ "piritaManifest" : "{\" package\" : {\" wapm\" : {\" name\" : \" wasmer/python\" , \" version\" : \" 3.12.0\" , \" description\" : \" Python\" }}}" ,
769
+ "distribution" : {
770
+ "piritaDownloadUrl" : "https://wasmer.io/wasmer/[email protected] " ,
771
+ "piritaSha256Hash" : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
772
+ }
773
+ } ,
774
+ ]
775
+ } ,
776
+ "info" : {
777
+ "defaultFrontend" : "https://wasmer.io/" ,
778
+ } ,
779
+ }
780
+ }
781
+ } ;
782
+ let response = HttpResponse {
783
+ body : Some ( serde_json:: to_vec ( & body) . unwrap ( ) ) ,
784
+ redirected : false ,
785
+ status : StatusCode :: OK ,
786
+ headers : HeaderMap :: new ( ) ,
787
+ } ;
788
+ let client = Arc :: new ( DummyClient :: new ( vec ! [ response] ) ) ;
789
+ let registry_endpoint = WapmSource :: WASMER_PROD_ENDPOINT . parse ( ) . unwrap ( ) ;
790
+ let request = PackageSpecifier :: Registry {
791
+ full_name : "wasmer/python" . to_string ( ) ,
792
+ version : "4.0.0" . parse ( ) . unwrap ( ) ,
793
+ } ;
794
+ let temp = tempfile:: tempdir ( ) . unwrap ( ) ;
795
+ let source = WapmSource :: new ( registry_endpoint, client. clone ( ) )
796
+ . with_local_cache ( temp. path ( ) , Duration :: from_secs ( 0 ) ) ;
797
+ source
798
+ . cache
799
+ . as_ref ( )
800
+ . unwrap ( )
801
+ . update ( "wasmer/python" , & cached_value)
802
+ . unwrap ( ) ;
803
+
804
+ let summaries = source. query ( & request) . await . unwrap ( ) ;
805
+
806
+ assert_eq ! ( summaries. len( ) , 1 ) ;
807
+ assert_eq ! ( summaries[ 0 ] . pkg. version. to_string( ) , "4.0.0" ) ;
808
+ }
722
809
}
0 commit comments