1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
|
module Fog
module AWS
class Storage
# From https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html
ALLOWED_UPLOAD_PART_OPTIONS = %i(
x-amz-copy-source
x-amz-copy-source-if-match
x-amz-copy-source-if-modified-since
x-amz-copy-source-if-none-match
x-amz-copy-source-if-unmodified-since
x-amz-copy-source-range
x-amz-copy-source-server-side-encryption-customer-algorithm
x-amz-copy-source-server-side-encryption-customer-key
x-amz-copy-source-server-side-encryption-customer-key-MD5
x-amz-expected-bucket-owner
x-amz-request-payer
x-amz-server-side-encryption-customer-algorithm
x-amz-server-side-encryption-customer-key
x-amz-server-side-encryption-customer-key-MD5
x-amz-source-expected-bucket-owner
).freeze
class Real
require 'fog/aws/parsers/storage/upload_part_copy_object'
# Upload a part for a multipart copy
#
# @param target_bucket_name [String] Name of bucket to create copy in
# @param target_object_name [String] Name for new copy of object
# @param upload_id [String] Id of upload to add part to
# @param part_number [String] Index of part in upload
# @param options [Hash]:
# @option options [String] x-amz-metadata-directive Specifies whether to copy metadata from source or replace with data in request. Must be in ['COPY', 'REPLACE']
# @option options [String] x-amz-copy_source-if-match Copies object if its etag matches this value
# @option options [Time] x-amz-copy_source-if-modified_since Copies object it it has been modified since this time
# @option options [String] x-amz-copy_source-if-none-match Copies object if its etag does not match this value
# @option options [Time] x-amz-copy_source-if-unmodified-since Copies object it it has not been modified since this time
# @option options [Time] x-amz-copy-source-range Specifes the range of bytes to copy from the source object
#
# @return [Excon::Response]
# * body [Hash]:
# * ETag [String] - etag of new object
# * LastModified [Time] - date object was last modified
#
# @see https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html
#
def upload_part_copy(target_bucket_name, target_object_name, upload_id, part_number, options = {})
headers = options
request({
:expects => 200,
:idempotent => true,
:headers => headers,
:bucket_name => target_bucket_name,
:object_name => target_object_name,
:method => 'PUT',
:query => {'uploadId' => upload_id, 'partNumber' => part_number},
:parser => Fog::Parsers::AWS::Storage::UploadPartCopyObject.new,
})
end
end # Real
class Mock # :nodoc:all
require 'fog/aws/requests/storage/shared_mock_methods'
include Fog::AWS::Storage::SharedMockMethods
def upload_part_copy(target_bucket_name, target_object_name, upload_id, part_number, options = {})
validate_options!(options)
copy_source = options['x-amz-copy-source']
copy_range = options['x-amz-copy-source-range']
raise 'No x-amz-copy-source header provided' unless copy_source
raise 'No x-amz-copy-source-range header provided' unless copy_range
source_bucket_name, source_object_name = copy_source.split('/', 2)
verify_mock_bucket_exists(source_bucket_name)
source_bucket = self.data[:buckets][source_bucket_name]
source_object = source_bucket && source_bucket[:objects][source_object_name] && source_bucket[:objects][source_object_name].first
upload_info = get_upload_info(target_bucket_name, upload_id)
response = Excon::Response.new
if source_object
start_pos, end_pos = byte_range(copy_range, source_object[:body].length)
upload_info[:parts][part_number] = source_object[:body][start_pos..end_pos]
response.status = 200
response.body = {
# just use the part number as the ETag, for simplicity
'ETag' => part_number.to_i,
'LastModified' => Time.parse(source_object['Last-Modified'])
}
response
else
response.status = 404
raise(Excon::Errors.status_error({:expects => 200}, response))
end
end
def byte_range(range, size)
matches = range.match(/bytes=(\d*)-(\d*)/)
return nil unless matches
end_pos = [matches[2].to_i, size].min
[matches[1].to_i, end_pos]
end
def validate_options!(options)
options.keys.each do |key|
raise "Invalid UploadPart option: #{key}" unless ::Fog::AWS::Storage::ALLOWED_UPLOAD_PART_OPTIONS.include?(key.to_sym)
end
end
end # Mock
end # Storage
end # AWS
end # Fog
|