1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
15- import database
15+ import json
1616import logging
17+
18+ import database
1719import flask
20+ import gcs as gcs_type
1821import httpbin
19- import simdjson
2022import utils
21- import gcs as gcs_type
22- from google .protobuf import json_format
2323from werkzeug import serving
2424from werkzeug .middleware .dispatcher import DispatcherMiddleware
25- from google . cloud . storage_v1 . proto . storage_resources_pb2 import CommonEnums
25+
2626from google .cloud .storage_v1 .proto import storage_resources_pb2 as resources_pb2
27+ from google .cloud .storage_v1 .proto .storage_resources_pb2 import CommonEnums
28+ from google .protobuf import json_format
2729
2830db = None
2931
@@ -327,11 +329,14 @@ def bucket_lock_retention_policy(bucket_name):
327329@gcs .route ("/b/<bucket_name>/o" )
328330def object_list (bucket_name ):
329331 db .insert_test_bucket (None )
330- items , prefixes = db .list_object (flask .request , bucket_name , None )
332+ items , prefixes , rest_onlys = db .list_object (flask .request , bucket_name , None )
331333 response = {
332334 "kind" : "storage#objects" ,
333335 "nextPageToken" : "" ,
334- "items" : [gcs_type .object .Object .rest (blob ) for blob in items ],
336+ "items" : [
337+ gcs_type .object .Object .rest (blob , rest_only )
338+ for blob , rest_only in zip (items , rest_onlys )
339+ ],
335340 "prefixes" : prefixes ,
336341 }
337342 fields = flask .request .args .get ("fields" , None )
@@ -372,7 +377,7 @@ def object_delete(bucket_name, object_name):
372377@gcs .route ("/b/<bucket_name>/o/<path:object_name>/compose" , methods = ["POST" ])
373378def objects_compose (bucket_name , object_name ):
374379 bucket = db .get_bucket_without_generation (bucket_name , None ).metadata
375- payload = simdjson .loads (flask .request .data )
380+ payload = json .loads (flask .request .data )
376381 source_objects = payload ["sourceObjects" ]
377382 if source_objects is None :
378383 utils .error .missing ("source component" , None )
@@ -427,8 +432,9 @@ def objects_copy(src_bucket_name, src_object_name, dst_bucket_name, dst_object_n
427432 dst_metadata .name = dst_object_name
428433 dst_media = b""
429434 dst_media += src_object .media
435+ dst_rest_only = dict (src_object .rest_only )
430436 dst_object , _ = gcs_type .object .Object .init (
431- flask .request , dst_metadata , dst_media , dst_bucket , True , None
437+ flask .request , dst_metadata , dst_media , dst_bucket , True , None , dst_rest_only
432438 )
433439 db .insert_object (flask .request , dst_bucket_name , dst_object , None )
434440 dst_object .patch (flask .request , None )
@@ -439,6 +445,70 @@ def objects_copy(src_bucket_name, src_object_name, dst_bucket_name, dst_object_n
439445 return dst_object .rest_metadata ()
440446
441447
448+ @gcs .route (
449+ "/b/<src_bucket_name>/o/<path:src_object_name>/rewriteTo/b/<dst_bucket_name>/o/<path:dst_object_name>" ,
450+ methods = ["POST" ],
451+ )
452+ def objects_rewrite (src_bucket_name , src_object_name , dst_bucket_name , dst_object_name ):
453+ db .insert_test_bucket (None )
454+ token , rewrite = flask .request .args .get ("rewriteToken" ), None
455+ src_object = None
456+ if token is None :
457+ rewrite = gcs_type .holder .DataHolder .init_rewrite_rest (
458+ flask .request ,
459+ src_bucket_name ,
460+ src_object_name ,
461+ dst_bucket_name ,
462+ dst_object_name ,
463+ )
464+ db .insert_rewrite (rewrite )
465+ else :
466+ rewrite = db .get_rewrite (token , None )
467+ src_object = db .get_object (
468+ rewrite .request , src_bucket_name , src_object_name , True , None
469+ )
470+ total_bytes_rewritten = len (rewrite .media )
471+ total_bytes_rewritten += min (
472+ rewrite .max_bytes_rewritten_per_call , len (src_object .media ) - len (rewrite .media )
473+ )
474+ rewrite .media += src_object .media [len (rewrite .media ) : total_bytes_rewritten ]
475+ done , dst_object = total_bytes_rewritten == len (src_object .media ), None
476+ response = {
477+ "kind" : "storage#rewriteResponse" ,
478+ "totalBytesRewritten" : len (rewrite .media ),
479+ "objectSize" : len (src_object .media ),
480+ "done" : done ,
481+ }
482+ if done :
483+ dst_bucket = db .get_bucket_without_generation (dst_bucket_name , None ).metadata
484+ dst_metadata = resources_pb2 .Object ()
485+ dst_metadata .CopyFrom (src_object .metadata )
486+ dst_rest_only = dict (src_object .rest_only )
487+ dst_metadata .bucket = dst_bucket_name
488+ dst_metadata .name = dst_object_name
489+ dst_media = rewrite .media
490+ dst_object , _ = gcs_type .object .Object .init (
491+ flask .request ,
492+ dst_metadata ,
493+ dst_media ,
494+ dst_bucket ,
495+ True ,
496+ None ,
497+ dst_rest_only ,
498+ )
499+ db .insert_object (flask .request , dst_bucket_name , dst_object , None )
500+ dst_object .patch (rewrite .request , None )
501+ dst_object .metadata .metageneration = 1
502+ dst_object .metadata .updated .FromDatetime (
503+ dst_object .metadata .time_created .ToDatetime ()
504+ )
505+ resources = dst_object .rest_metadata ()
506+ response ["resource" ] = resources
507+ else :
508+ response ["rewriteToken" ] = rewrite .token
509+ return response
510+
511+
442512# === OBJECT ACCESS CONTROL === #
443513
444514
@@ -564,10 +634,11 @@ def resumable_upload_chunk(bucket_name):
564634 utils .error .missing ("upload_id in resumable_upload_chunk" , None )
565635 upload = db .get_upload (upload_id , None )
566636 if upload .complete :
567- return gcs_type .object .Object .rest (upload .metadata )
637+ return gcs_type .object .Object .rest (upload .metadata , upload . rest_only )
568638 upload .transfer .add (request .environ .get ("HTTP_TRANSFER_ENCODING" , "" ))
569639 content_length = int (request .headers .get ("content-length" , 0 ))
570- if content_length != len (request .data ):
640+ data = utils .common .extract_media (request )
641+ if content_length != len (data ):
571642 utils .error .invalid ("content-length header" , None )
572643 content_range = request .headers .get ("content-range" )
573644 if content_range is not None :
@@ -576,7 +647,7 @@ def resumable_upload_chunk(bucket_name):
576647 utils .error .invalid ("content-range header" , None )
577648 if items [0 ] == "*" :
578649 if upload .complete :
579- return gcs_type .object .Object .rest (upload .metadata )
650+ return gcs_type .object .Object .rest (upload .metadata , upload . rest_only )
580651 if items [1 ] != "*" and int (items [1 ]) == len (upload .media ):
581652 upload .complete = True
582653 blob , _ = gcs_type .object .Object .init (
@@ -618,16 +689,22 @@ def resumable_upload_chunk(bucket_name):
618689 None ,
619690 rest_code = 400 ,
620691 )
621- upload .media += request . data
692+ upload .media += data
622693 upload .complete = total_object_size == len (upload .media ) or (
623694 chunk_last_byte + 1 == total_object_size
624695 )
625696 else :
626- upload .media += request . data
697+ upload .media += data
627698 upload .complete = True
628699 if upload .complete :
629700 blob , _ = gcs_type .object .Object .init (
630- upload .request , upload .metadata , upload .media , upload .bucket , False , None
701+ upload .request ,
702+ upload .metadata ,
703+ upload .media ,
704+ upload .bucket ,
705+ False ,
706+ None ,
707+ upload .rest_only ,
631708 )
632709 blob .metadata .metadata ["x_testbench_transfer_encoding" ] = ":" .join (
633710 upload .transfer
@@ -672,6 +749,12 @@ def xml_get_object(bucket_name, object_name):
672749
673750# === SERVER === #
674751
752+ # Define the WSGI application to handle HMAC key requests
753+ (PROJECTS_HANDLER_PATH , projects_app ) = gcs_type .project .get_projects_app ()
754+
755+ # Define the WSGI application to handle IAM requests
756+ (IAM_HANDLER_PATH , iam_app ) = gcs_type .iam .get_iam_app ()
757+
675758
676759server = DispatcherMiddleware (
677760 root ,
@@ -680,6 +763,8 @@ def xml_get_object(bucket_name, object_name):
680763 GCS_HANDLER_PATH : gcs ,
681764 DOWNLOAD_HANDLER_PATH : download ,
682765 UPLOAD_HANDLER_PATH : upload ,
766+ PROJECTS_HANDLER_PATH : projects_app ,
767+ IAM_HANDLER_PATH : iam_app ,
683768 },
684769)
685770
0 commit comments