1
+ module Fog
2
+ module AWS
3
+ class Storage
4
+ class Real
5
+ require 'fog/aws/parsers/storage/list_objects_v2'
6
+
7
+ # List information about objects in an S3 bucket using ListObjectsV2
8
+ #
9
+ # @param bucket_name [String] name of bucket to list object keys from
10
+ # @param options [Hash] config arguments for list. Defaults to {}.
11
+ # @option options delimiter [String] causes keys with the same string between the prefix
12
+ # value and the first occurrence of delimiter to be rolled up
13
+ # @option options continuation-token [String] continuation token from a previous request
14
+ # @option options fetch-owner [Boolean] specifies whether to return owner information
15
+ # @option options max-keys [Integer] limits number of object keys returned
16
+ # @option options prefix [String] limits object keys to those beginning with its value
17
+ # @option options start-after [String] starts listing after this specified key
18
+ #
19
+ # @return [Excon::Response] response:
20
+ # * body [Hash]:
21
+ # * Delimiter [String] - Delimiter specified for query
22
+ # * IsTruncated [Boolean] - Whether or not the listing is truncated
23
+ # * ContinuationToken [String] - Token specified in the request
24
+ # * NextContinuationToken [String] - Token to use in subsequent requests
25
+ # * KeyCount [Integer] - Number of keys returned
26
+ # * MaxKeys [Integer] - Maximum number of keys specified for query
27
+ # * Name [String] - Name of the bucket
28
+ # * Prefix [String] - Prefix specified for query
29
+ # * StartAfter [String] - StartAfter specified in the request
30
+ # * CommonPrefixes [Array] - Array of strings for common prefixes
31
+ # * Contents [Array]:
32
+ # * ETag [String] - Etag of object
33
+ # * Key [String] - Name of object
34
+ # * LastModified [String] - Timestamp of last modification of object
35
+ # * Owner [Hash]:
36
+ # * DisplayName [String] - Display name of object owner
37
+ # * ID [String] - Id of object owner
38
+ # * Size [Integer] - Size of object
39
+ # * StorageClass [String] - Storage class of object
40
+ #
41
+ # @see https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html
42
+
43
+ def list_objects_v2 ( bucket_name , options = { } )
44
+ unless bucket_name
45
+ raise ArgumentError . new ( 'bucket_name is required' )
46
+ end
47
+
48
+ # Add list-type=2 to indicate ListObjectsV2
49
+ options = options . merge ( 'list-type' => '2' )
50
+
51
+ request ( {
52
+ :expects => 200 ,
53
+ :headers => { } ,
54
+ :bucket_name => bucket_name ,
55
+ :idempotent => true ,
56
+ :method => 'GET' ,
57
+ :parser => Fog ::Parsers ::AWS ::Storage ::ListObjectsV2 . new ,
58
+ :query => options
59
+ } )
60
+ end
61
+ end
62
+
63
+ class Mock # :nodoc:all
64
+ def list_objects_v2 ( bucket_name , options = { } )
65
+ prefix = options [ 'prefix' ]
66
+ continuation_token = options [ 'continuation-token' ]
67
+ delimiter = options [ 'delimiter' ]
68
+ max_keys = options [ 'max-keys' ]
69
+ start_after = options [ 'start-after' ]
70
+ fetch_owner = options [ 'fetch-owner' ]
71
+ common_prefixes = [ ]
72
+
73
+ unless bucket_name
74
+ raise ArgumentError . new ( 'bucket_name is required' )
75
+ end
76
+
77
+ response = Excon ::Response . new
78
+ if bucket = self . data [ :buckets ] [ bucket_name ]
79
+ contents = bucket [ :objects ] . values . map ( &:first ) . sort { |x , y | x [ 'Key' ] <=> y [ 'Key' ] } . reject do |object |
80
+ ( prefix && object [ 'Key' ] [ 0 ...prefix . length ] != prefix ) ||
81
+ ( start_after && object [ 'Key' ] <= start_after ) ||
82
+ ( continuation_token && object [ 'Key' ] <= continuation_token ) ||
83
+ ( delimiter && object [ 'Key' ] [ ( prefix ? prefix . length : 0 ) ..-1 ] . include? ( delimiter ) \
84
+ && common_prefixes << object [ 'Key' ] . sub ( /^(#{ prefix } [^#{ delimiter } ]+.).*/ , '\1' ) ) ||
85
+ object . key? ( :delete_marker )
86
+ end . map do |object |
87
+ data = object . reject { |key , value | ![ 'ETag' , 'Key' , 'StorageClass' ] . include? ( key ) }
88
+ data . merge! ( {
89
+ 'LastModified' => Time . parse ( object [ 'Last-Modified' ] ) ,
90
+ 'Owner' => fetch_owner ? bucket [ 'Owner' ] : nil ,
91
+ 'Size' => object [ 'Content-Length' ] . to_i
92
+ } )
93
+ data
94
+ end
95
+
96
+ max_keys = max_keys || 1000
97
+ size = [ max_keys , 1000 ] . min
98
+ truncated_contents = contents [ 0 ...size ]
99
+ next_token = truncated_contents . size != contents . size ? truncated_contents . last [ 'Key' ] : nil
100
+
101
+ response . status = 200
102
+ common_prefixes_uniq = common_prefixes . uniq
103
+ response . body = {
104
+ 'CommonPrefixes' => common_prefixes_uniq ,
105
+ 'Contents' => truncated_contents ,
106
+ 'IsTruncated' => truncated_contents . size != contents . size ,
107
+ 'ContinuationToken' => continuation_token ,
108
+ 'NextContinuationToken' => next_token ,
109
+ 'KeyCount' => truncated_contents . size + common_prefixes_uniq . size ,
110
+ 'MaxKeys' => max_keys ,
111
+ 'Name' => bucket [ 'Name' ] ,
112
+ 'Prefix' => prefix ,
113
+ 'StartAfter' => start_after
114
+ }
115
+ if max_keys && max_keys < response . body [ 'Contents' ] . length
116
+ response . body [ 'IsTruncated' ] = true
117
+ response . body [ 'Contents' ] = response . body [ 'Contents' ] [ 0 ...max_keys ]
118
+ response . body [ 'KeyCount' ] = response . body [ 'Contents' ] . size + response . body [ 'CommonPrefixes' ] . size
119
+ end
120
+ else
121
+ response . status = 404
122
+ raise ( Excon ::Errors . status_error ( { :expects => 200 } , response ) )
123
+ end
124
+ response
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
0 commit comments