1- use std:: borrow:: Cow ;
21use std:: io;
32use std:: time:: SystemTime ;
43
5- use anyhow:: { Context , Result } ;
4+ use anyhow:: Context ;
65use axum:: body:: Body ;
7- use axum:: extract:: Path ;
86use axum:: http:: { HeaderMap , StatusCode } ;
97use axum:: response:: { IntoResponse , Response } ;
108use axum:: routing;
119use axum:: { Json , Router } ;
1210use futures_util:: { StreamExt , TryStreamExt } ;
1311use objectstore_service:: id:: { ObjectContext , ObjectId } ;
1412use objectstore_types:: Metadata ;
15- use objectstore_types:: scope:: { Scope , Scopes } ;
16- use serde:: { Deserialize , Serialize , de} ;
13+ use serde:: Serialize ;
1714
1815use crate :: auth:: AuthAwareService ;
19- use crate :: endpoints:: helpers;
2016use crate :: error:: ApiResult ;
17+ use crate :: extractors:: Xt ;
2118use crate :: state:: ServiceState ;
2219
23- /// Used in place of scopes in the URL to represent an empty set of scopes.
24- const EMPTY_SCOPES : & str = "_" ;
25-
2620pub fn router ( ) -> Router < ServiceState > {
2721 let collection_routes = routing:: post ( objects_post) ;
2822 let object_routes = routing:: get ( object_get)
@@ -45,13 +39,10 @@ pub struct InsertObjectResponse {
4539
4640async fn objects_post (
4741 service : AuthAwareService ,
48- Path ( params ) : Path < CollectionParams > ,
42+ Xt ( context ) : Xt < ObjectContext > ,
4943 headers : HeaderMap ,
5044 body : Body ,
5145) -> ApiResult < Response > {
52- let context = params. into_context ( ) ;
53- helpers:: populate_sentry_context ( & context) ;
54-
5546 let mut metadata =
5647 Metadata :: from_headers ( & headers, "" ) . context ( "extracting metadata from headers" ) ?;
5748 metadata. time_created = Some ( SystemTime :: now ( ) ) ;
@@ -67,13 +58,7 @@ async fn objects_post(
6758 Ok ( ( StatusCode :: CREATED , response) . into_response ( ) )
6859}
6960
70- async fn object_get (
71- service : AuthAwareService ,
72- Path ( params) : Path < ObjectParams > ,
73- ) -> ApiResult < Response > {
74- let id = params. into_object_id ( ) ;
75- helpers:: populate_sentry_object_id ( & id) ;
76-
61+ async fn object_get ( service : AuthAwareService , Xt ( id) : Xt < ObjectId > ) -> ApiResult < Response > {
7762 let Some ( ( metadata, stream) ) = service. get_object ( & id) . await ? else {
7863 return Ok ( StatusCode :: NOT_FOUND . into_response ( ) ) ;
7964 } ;
@@ -84,13 +69,7 @@ async fn object_get(
8469 Ok ( ( headers, Body :: from_stream ( stream) ) . into_response ( ) )
8570}
8671
87- async fn object_head (
88- service : AuthAwareService ,
89- Path ( params) : Path < ObjectParams > ,
90- ) -> ApiResult < Response > {
91- let id = params. into_object_id ( ) ;
92- helpers:: populate_sentry_object_id ( & id) ;
93-
72+ async fn object_head ( service : AuthAwareService , Xt ( id) : Xt < ObjectId > ) -> ApiResult < Response > {
9473 let Some ( ( metadata, _stream) ) = service. get_object ( & id) . await ? else {
9574 return Ok ( StatusCode :: NOT_FOUND . into_response ( ) ) ;
9675 } ;
@@ -104,13 +83,10 @@ async fn object_head(
10483
10584async fn object_put (
10685 service : AuthAwareService ,
107- Path ( params ) : Path < ObjectParams > ,
86+ Xt ( id ) : Xt < ObjectId > ,
10887 headers : HeaderMap ,
10988 body : Body ,
11089) -> ApiResult < Response > {
111- let id = params. into_object_id ( ) ;
112- helpers:: populate_sentry_object_id ( & id) ;
113-
11490 let mut metadata =
11591 Metadata :: from_headers ( & headers, "" ) . context ( "extracting metadata from headers" ) ?;
11692 metadata. time_created = Some ( SystemTime :: now ( ) ) ;
@@ -130,77 +106,8 @@ async fn object_put(
130106
131107async fn object_delete (
132108 service : AuthAwareService ,
133- Path ( params ) : Path < ObjectParams > ,
109+ Xt ( id ) : Xt < ObjectId > ,
134110) -> ApiResult < impl IntoResponse > {
135- let id = params. into_object_id ( ) ;
136- helpers:: populate_sentry_object_id ( & id) ;
137-
138111 service. delete_object ( & id) . await ?;
139-
140112 Ok ( StatusCode :: NO_CONTENT )
141113}
142-
143- /// Path parameters used for collection-level endpoints without a key.
144- ///
145- /// This is meant to be used with the axum `Path` extractor.
146- #[ derive( Clone , Debug , Deserialize ) ]
147- struct CollectionParams {
148- usecase : String ,
149- #[ serde( deserialize_with = "deserialize_scopes" ) ]
150- scopes : Scopes ,
151- }
152-
153- impl CollectionParams {
154- /// Converts the params into an [`ObjectContext`].
155- pub fn into_context ( self ) -> ObjectContext {
156- ObjectContext {
157- usecase : self . usecase ,
158- scopes : self . scopes ,
159- }
160- }
161- }
162-
163- /// Path parameters used for object-level endpoints.
164- ///
165- /// This is meant to be used with the axum `Path` extractor.
166- #[ derive( Clone , Debug , Deserialize ) ]
167- struct ObjectParams {
168- usecase : String ,
169- #[ serde( deserialize_with = "deserialize_scopes" ) ]
170- scopes : Scopes ,
171- key : String ,
172- }
173-
174- impl ObjectParams {
175- /// Converts the params into an [`ObjectId`].
176- pub fn into_object_id ( self ) -> ObjectId {
177- ObjectId :: from_parts ( self . usecase , self . scopes , self . key )
178- }
179- }
180-
181- /// Deserializes a `Scopes` instance from a string representation.
182- ///
183- /// The string representation is a semicolon-separated list of `key=value` pairs, following the
184- /// Matrix URIs proposal. An empty scopes string (`"_"`) represents no scopes.
185- fn deserialize_scopes < ' de , D > ( deserializer : D ) -> Result < Scopes , D :: Error >
186- where
187- D : de:: Deserializer < ' de > ,
188- {
189- let s = Cow :: < str > :: deserialize ( deserializer) ?;
190- if s == EMPTY_SCOPES {
191- return Ok ( Scopes :: empty ( ) ) ;
192- }
193-
194- let scopes = s
195- . split ( ';' )
196- . map ( |s| {
197- let ( key, value) = s
198- . split_once ( "=" )
199- . ok_or_else ( || de:: Error :: custom ( "scope must be 'key=value'" ) ) ?;
200-
201- Scope :: create ( key, value) . map_err ( de:: Error :: custom)
202- } )
203- . collect :: < Result < _ , _ > > ( ) ?;
204-
205- Ok ( scopes)
206- }
0 commit comments