miasma 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 70ab81fefcaa4ab8942ca15c53e1ad296a5d5938
4
- data.tar.gz: b2042bdf52407d095767388854adad5d8783526a
3
+ metadata.gz: 2d96b86dbb247d22f846177758907886638e4eec
4
+ data.tar.gz: 5c052aae2f76878d06868e1c60daa01b38921fd2
5
5
  SHA512:
6
- metadata.gz: 6cb01229d765fc2f5d66ba690848af6dc1af060641e4ce4d0d3d791d27051d0a35511077a2dcfdc7ef3cc6e74aee3a395cf61703f1a507b38ee130061b409b49
7
- data.tar.gz: f83fc149fb9b9bac850ef563a17eb133472d281a65ac6ce3db66c32da4bb4e12b5d367b761e1b040ad4f01305977067d07dc70cde63d9534752a6148f62fbd3f
6
+ metadata.gz: 13d34cdd67c7bfea1470f25ffcd0a37a225a9900c59d1c49fa69a61e302e5dfa733f51c4d85ddd04e402b43e261f847bd2ba83c8b0899302a6cae25b933df57e
7
+ data.tar.gz: e7e9a45b0ea4380ba9474b2ba6cdf26f3265bdcff1ce2ebe68a4a193bf4f931b83f347668a8e8ddd6f6d02660d7c04ba3a6813bc8f4579df5b32405c6279c854
@@ -1,3 +1,10 @@
1
+ # v0.2.2
2
+ * Add support for pre-signed AWS URLs
3
+ * Add `#url` method to `Storage::File` model
4
+ * Relax content-type checks on response for more reliable auto-parse (#2)
5
+ * Basic abstract spec coverage on storage
6
+ * Fix response body result by adding streaming detection
7
+
1
8
  # v0.2.0
2
9
  * Add initial OpenStack provider support
3
10
  * Refactor of Rackspace provider support (build off OpenStack)
@@ -156,23 +156,52 @@ module Miasma
156
156
  @service = service
157
157
  end
158
158
 
159
- # Generate the signature
159
+ # Generate the signature string for AUTH
160
160
  #
161
161
  # @param http_method [Symbol] HTTP request method
162
162
  # @param path [String] request path
163
163
  # @param opts [Hash] request options
164
164
  # @return [String] signature
165
165
  def generate(http_method, path, opts)
166
+ signature = generate_signature(http_method, path, opts)
167
+ "#{algorithm} Credential=#{access_key}/#{credential_scope}, SignedHeaders=#{signed_headers(opts[:headers])}, Signature=#{signature}"
168
+ end
169
+
170
+ # Generate URL with signed params
171
+ #
172
+ # @param http_method [Symbol] HTTP request method
173
+ # @param path [String] request path
174
+ # @param opts [Hash] request options
175
+ # @return [String] signature
176
+ def generate_url(http_method, path, opts)
177
+ opts[:params].merge!(
178
+ Smash.new(
179
+ 'X-Amz-SignedHeaders' => signed_headers(opts[:headers]),
180
+ 'X-Amz-Algorithm' => algorithm,
181
+ 'X-Amz-Credential' => "#{access_key}/#{credential_scope}"
182
+ )
183
+ )
184
+ signature = generate_signature(http_method, path, opts.merge(:body => 'UNSIGNED-PAYLOAD'))
185
+ params = opts[:params].merge('X-Amz-Signature' => signature)
186
+ "https://#{opts[:headers]['Host']}/#{path}?#{canonical_query(params)}"
187
+ end
188
+
189
+ # Generate the signature
190
+ #
191
+ # @param http_method [Symbol] HTTP request method
192
+ # @param path [String] request path
193
+ # @param opts [Hash] request options
194
+ # @return [String] signature
195
+ def generate_signature(http_method, path, opts)
166
196
  to_sign = [
167
197
  algorithm,
168
198
  AwsApiCore.time_iso8601,
169
199
  credential_scope,
170
200
  hashed_canonical_request(
171
- build_canonical_request(http_method, path, opts)
201
+ can_req = build_canonical_request(http_method, path, opts)
172
202
  )
173
203
  ].join("\n")
174
204
  signature = sign_request(to_sign)
175
- "#{algorithm} Credential=#{access_key}/#{credential_scope}, SignedHeaders=#{signed_headers(opts[:headers])}, Signature=#{signature}"
176
205
  end
177
206
 
178
207
  # Sign the request
@@ -226,6 +255,9 @@ module Miasma
226
255
  # @param opts [Hash] request options
227
256
  # @return [String] canonical request string
228
257
  def build_canonical_request(http_method, path, opts)
258
+ unless(path.start_with?('/'))
259
+ path = "/#{path}"
260
+ end
229
261
  [
230
262
  http_method.to_s.upcase,
231
263
  path,
@@ -281,7 +313,11 @@ module Miasma
281
313
  elsif(options[:form])
282
314
  body = URI.encode_www_form(options[:form])
283
315
  end
284
- hmac.hexdigest_of(body)
316
+ if(body == 'UNSIGNED-PAYLOAD')
317
+ body
318
+ else
319
+ hmac.hexdigest_of(body)
320
+ end
285
321
  end
286
322
 
287
323
  end
@@ -167,7 +167,7 @@ module Miasma
167
167
  end
168
168
  end.compact
169
169
  ]
170
- if(file.attributes[:body].is_a?(IO) && file.body.length >= 102400)
170
+ if(file.attributes[:body].is_a?(IO) && file.body.length >= Storage::MAX_BODY_SIZE_FOR_STRINGIFY)
171
171
  upload_id = request(
172
172
  args.merge(
173
173
  Smash.new(
@@ -182,7 +182,7 @@ module Miasma
182
182
  count = 1
183
183
  parts = []
184
184
  file.body.rewind
185
- while(content = file.body.read(102400))
185
+ while(content = file.body.read(Storage::READ_BODY_CHUNK_SIZE))
186
186
  parts << [
187
187
  count,
188
188
  request(
@@ -260,7 +260,7 @@ module Miasma
260
260
  :method => :delete,
261
261
  :path => file.name,
262
262
  :endpoint => bucket_endpoint(file.bucket),
263
- :expect => 204
263
+ :expects => 204
264
264
  )
265
265
  true
266
266
  else
@@ -293,6 +293,28 @@ module Miasma
293
293
  file
294
294
  end
295
295
 
296
+ # Create publicly accessible URL
297
+ #
298
+ # @param timeout_secs [Integer] seconds available
299
+ # @return [String] URL
300
+ def file_url(file, timeout_secs)
301
+ if(file.persisted?)
302
+ signer.generate_url(
303
+ :get, ::File.join(uri_escape(file.bucket.name), uri_escape(file.name)),
304
+ :headers => Smash.new(
305
+ 'Host' => aws_host
306
+ ),
307
+ :params => Smash.new(
308
+ 'X-Amz-Date' => Contrib::AwsApiCore.time_iso8601,
309
+ 'X-Amz-Expires' => timeout_secs
310
+ )
311
+
312
+ )
313
+ else
314
+ raise Error::ModelPersistError.new "#{file} has not been saved!"
315
+ end
316
+ end
317
+
296
318
  # Fetch the contents of the file
297
319
  #
298
320
  # @param file [Models::Storage::File]
@@ -220,6 +220,7 @@ module Miasma
220
220
  attribute :open_stack_tenant_name, String
221
221
  attribute :open_stack_domain, String
222
222
  attribute :open_stack_project, String
223
+
223
224
  end
224
225
  end
225
226
 
@@ -87,5 +87,8 @@ module Miasma
87
87
  # Invalid modification request
88
88
  class ImmutableError < Error; end
89
89
 
90
+ # Model has not been persisted
91
+ class ModelPersistError < Error; end
92
+
90
93
  end
91
94
  end
@@ -5,6 +5,11 @@ module Miasma
5
5
  # Abstract storage API
6
6
  class Storage < Types::Api
7
7
 
8
+ # @return [Integer] max bytes allowed for storing body in string
9
+ MAX_BODY_SIZE_FOR_STRINGIFY = 102400
10
+ # @return [Integer] chunking size for reading IO
11
+ READ_BODY_CHUNK_SIZE = 102400
12
+
8
13
  autoload :Buckets, 'miasma/models/storage/buckets'
9
14
  autoload :Bucket, 'miasma/models/storage/bucket'
10
15
  autoload :Files, 'miasma/models/storage/files'
@@ -45,7 +45,13 @@ module Miasma
45
45
  # @note object returned will provide #readpartial
46
46
  def body
47
47
  unless(attributes[:body])
48
- data[:body] ||= api.file_body(self)
48
+ begin
49
+ _body = api.file_body(self)
50
+ _body.stream!
51
+ data[:body] = api.file_body(self)
52
+ rescue HTTP::StateError
53
+ data[:body] = StringIO.new(_body.to_s)
54
+ end
49
55
  end
50
56
  attributes[:body]
51
57
  end
@@ -55,14 +61,37 @@ module Miasma
55
61
  # @param io [IO, String]
56
62
  # @return [IO]
57
63
  def body=(io)
58
- unless(io.is_a?(IO))
64
+ unless(io.respond_to?(:readpartial))
59
65
  io = StringIO.new(io)
60
66
  end
61
67
  dirty[:body] = io
62
68
  end
63
69
 
70
+ # Create accessible URL
71
+ #
72
+ # @param timeout_in_seconds [Integer] optional if private (default: 60)
73
+ # @return [String] URL
74
+ def url(timeout_in_seconds=60)
75
+ perform_file_url(timeout_in_seconds)
76
+ end
77
+
78
+ # Destroy file
79
+ #
80
+ # @return [self]
81
+ def destroy
82
+ perform_destroy
83
+ data.clear
84
+ dirty.clear
85
+ self
86
+ end
87
+
64
88
  protected
65
89
 
90
+ # Proxy URL action up to the API
91
+ def perform_file_url(secs)
92
+ api.file_url(self, secs)
93
+ end
94
+
66
95
  # Proxy reload action up to the API
67
96
  def perform_reload
68
97
  api.file_reload(self)
@@ -118,13 +118,13 @@ module Miasma
118
118
  # @return [Smash]
119
119
  def format_response(result)
120
120
  extracted_headers = Smash[result.headers.map{|k,v| [Utils.snake(k), v]}]
121
- if(extracted_headers[:content_type].to_s.end_with?('json'))
121
+ if(extracted_headers[:content_type].to_s.include?('json'))
122
122
  begin
123
123
  extracted_body = MultiJson.load(result.body.to_s).to_smash
124
124
  rescue MultiJson::ParseError
125
125
  extracted_body = result.body.to_s
126
126
  end
127
- elsif(extracted_headers[:content_type].to_s.end_with?('xml'))
127
+ elsif(extracted_headers[:content_type].to_s.include?('xml'))
128
128
  begin
129
129
  extracted_body = MultiXml.parse(result.body.to_s).to_smash
130
130
  rescue MultiXml::ParseError
@@ -1,4 +1,4 @@
1
1
  module Miasma
2
2
  # current library version
3
- VERSION = Gem::Version.new('0.2.0')
3
+ VERSION = Gem::Version.new('0.2.2')
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miasma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-19 00:00:00.000000000 Z
11
+ date: 2014-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashie