Skip to content

Commit

Permalink
Add CORS block update to Bucket#cors
Browse files Browse the repository at this point in the history
Also add missing attributes to tests, and re-order some Bucket attribute accessors alphabetically.
  • Loading branch information
quartzmo authored and blowmage committed Oct 20, 2015
1 parent fd555b1 commit 5af1bd3
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 53 deletions.
135 changes: 93 additions & 42 deletions lib/gcloud/storage/bucket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,36 +75,62 @@ def url
@gapi["selfLink"]
end

##
# The location of the bucket.
# Object data for objects in the bucket resides in physical
# storage within this region. Defaults to US.
# See the developer's guide for the authoritative list.
#
# https://cloud.google.com/storage/docs/concepts-techniques
def location
@gapi["location"]
end

##
# Creation time of the bucket.
def created_at
@gapi["timeCreated"]
end

##
# The CORS configuration for a static website served from the
# bucket. For more information, see {Cross-Origin Resource
# Returns the current CORS configuration for a static website served from
# the bucket. For more information, see {Cross-Origin Resource
# Sharing (CORS)}[https://cloud.google.com/storage/docs/cross-origin].
# Returns an array of hashes containing the attributes specified for the
# Bucket resource field
# The return value is a frozen (unmodifiable) array of hashes containing
# the attributes specified for the Bucket resource field
# {cors}[https://cloud.google.com/storage/docs/json_api/v1/buckets#cors].
#
# This method also accepts a block for updating the bucket's CORS rules.
# See Bucket::Cors for details.
#
# === Examples
#
# Retrieving the bucket's CORS rules.
#
# require "gcloud"
#
# gcloud = Gcloud.new
# storage = gcloud.storage
#
# bucket = storage.bucket "my-todo-app"
# bucket.cors #=> [{"origin"=>["http://example.org"],
# # "method"=>["GET","POST","DELETE"],
# # "responseHeader"=>["X-My-Custom-Header"],
# # "maxAgeSeconds"=>3600}]
#
# Updating the bucket's CORS rules inside a block.
#
# require "gcloud"
#
# gcloud = Gcloud.new
# storage = gcloud.storage
# bucket = storage.bucket "my-todo-app"
#
# bucket.update do |b|
# b.cors do |c|
# c.add_rule ["http://example.org", "https://example.org"],
# "*",
# response_headers: ["X-My-Custom-Header"],
# max_age: 3600
# end
# end
#
def cors
g = @gapi
g = g.to_hash if g.respond_to? :to_hash
# TODO: consider freezing the array so no updates?
# TODO: deep to_hash...
g["cors"] ||= []
if block_given?
cors_builder = Bucket::Cors.new @gapi["cors"]
yield cors_builder
self.cors = cors_builder.cors if cors_builder.changed?
end
deep_dup_and_freeze @gapi["cors"]
end

##
Expand All @@ -119,27 +145,14 @@ def cors= new_cors
end

##
# The bucket's storage class. This defines how objects in the bucket are
# stored and determines the SLA and the cost of storage. Values include
# +STANDARD+, +NEARLINE+, and +DURABLE_REDUCED_AVAILABILITY+.
def storage_class
@gapi["storageClass"]
end

##
# Whether {Object
# Versioning}[https://cloud.google.com/storage/docs/object-versioning] is
# enabled for the bucket.
def versioning?
!@gapi["versioning"].nil? && @gapi["versioning"]["enabled"]
end

##
# Updates whether {Object
# Versioning}[https://cloud.google.com/storage/docs/object-versioning] is
# enabled for the bucket. (+Boolean+)
def versioning= new_versioning
patch_gapi! versioning: new_versioning
# The location of the bucket.
# Object data for objects in the bucket resides in physical
# storage within this region. Defaults to US.
# See the developer's guide for the authoritative list.
#
# https://cloud.google.com/storage/docs/concepts-techniques
def location
@gapi["location"]
end

##
Expand Down Expand Up @@ -177,6 +190,30 @@ def logging_prefix= logging_prefix
patch_gapi! logging_prefix: logging_prefix
end

##
# The bucket's storage class. This defines how objects in the bucket are
# stored and determines the SLA and the cost of storage. Values include
# +STANDARD+, +NEARLINE+, and +DURABLE_REDUCED_AVAILABILITY+.
def storage_class
@gapi["storageClass"]
end

##
# Whether {Object
# Versioning}[https://cloud.google.com/storage/docs/object-versioning] is
# enabled for the bucket.
def versioning?
!@gapi["versioning"].nil? && @gapi["versioning"]["enabled"]
end

##
# Updates whether {Object
# Versioning}[https://cloud.google.com/storage/docs/object-versioning] is
# enabled for the bucket. (+Boolean+)
def versioning= new_versioning
patch_gapi! versioning: new_versioning
end

##
# The index page returned from a static website served from the bucket
# when a site visitor requests the top level directory. For more
Expand Down Expand Up @@ -233,7 +270,7 @@ def website_404= website_404
# bucket.update do |b|
# b.website_main = "index.html"
# b.website_404 = "not_found.html"
# b.cors[0]["method"] = ["PUT"]
# b.cors[0]["method"] = ["GET","POST","DELETE"]
# b.cors[1]["responseHeader"] << "X-Another-Custom-Header"
# end
#
Expand Down Expand Up @@ -731,6 +768,20 @@ def verify_chunk_size! chunk_size
chunk_size
end

##
# Given nil, empty array, a gapi array of hashes, or any value, returns a
# deeply dup'd and frozen array of simple hashes or values (not gapi
# objects.)
def deep_dup_and_freeze array
return [].freeze if array.nil? || array.empty?
array = Array(array.dup)
array = array.map do |h|
h = h.to_hash if h.respond_to? :to_hash
h.dup.freeze
end
array.freeze
end

##
# Yielded to a block to accumulate changes for a patch request.
class Updater
Expand Down
46 changes: 38 additions & 8 deletions test/gcloud/storage/bucket_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,47 @@

describe Gcloud::Storage::Bucket, :mock_storage do
# Create a bucket object with the project's mocked connection object
let(:bucket_hash) { random_bucket_hash }
let(:bucket_hash) { random_bucket_hash }
let(:bucket) { Gcloud::Storage::Bucket.from_gapi bucket_hash, storage.connection }

let(:bucket_name) { "new-bucket-#{Time.now.to_i}" }
let(:bucket_url_root) { "https://www.googleapis.com/storage/v1" }
let(:bucket_url) { "#{bucket_url_root}/b/#{bucket_name}" }
let(:bucket_cors) { [{ "maxAgeSeconds" => 300,
"origin" => ["http://example.org", "https://example.org"],
"method" => ["*"],
"responseHeader" => ["X-My-Custom-Header"] }] }
let(:bucket_location) { "US" }
let(:bucket_logging_bucket) { "bucket-name-logging" }
let(:bucket_logging_prefix) { "AccessLog" }
let(:bucket_storage_class) { "STANDARD" }
let(:bucket_versioning) { true }
let(:bucket_website_main) { "index.html" }
let(:bucket_website_404) { "404.html" }
let(:bucket_hash_complete) { random_bucket_hash bucket_name, bucket_url_root,
bucket_location, bucket_storage_class, bucket_versioning,
bucket_logging_bucket, bucket_logging_prefix, bucket_website_main,
bucket_website_404, bucket_cors }
let(:bucket_complete) { Gcloud::Storage::Bucket.from_gapi bucket_hash_complete, storage.connection }

it "knows its attributes" do
bucket.id.must_equal bucket_hash["id"]
bucket.name.must_equal bucket_hash["name"]
bucket.created_at.must_equal bucket_hash["timeCreated"]
bucket.url.must_equal bucket_hash["selfLink"]
bucket.location.must_equal bucket_hash["location"]
bucket.storage_class.must_equal bucket_hash["storageClass"]
bucket.versioning?.must_equal false
bucket_complete.id.must_equal bucket_hash_complete["id"]
bucket_complete.name.must_equal bucket_name
bucket_complete.created_at.must_equal bucket_hash_complete["timeCreated"]
bucket_complete.url.must_equal bucket_url
bucket_complete.location.must_equal bucket_location
bucket_complete.logging_bucket.must_equal bucket_logging_bucket
bucket_complete.logging_prefix.must_equal bucket_logging_prefix
bucket_complete.storage_class.must_equal bucket_storage_class
bucket_complete.versioning?.must_equal bucket_versioning
bucket_complete.website_main.must_equal bucket_website_main
bucket_complete.website_404.must_equal bucket_website_404
end

it "return frozen cors" do
bucket_complete.cors.must_equal bucket_hash_complete["cors"]
bucket_complete.cors.frozen?.must_equal true
bucket_complete.cors.first.frozen?.must_equal true
end

it "can delete itself" do
Expand Down
47 changes: 44 additions & 3 deletions test/gcloud/storage/bucket_update_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@
"method" => ["*"],
"responseHeader" => ["X-My-Custom-Header"] }] }

let(:bucket_hash) { random_bucket_hash bucket_name, bucket_url, bucket_location, bucket_storage_class }
let(:bucket_hash) { random_bucket_hash bucket_name, bucket_url_root, bucket_location, bucket_storage_class }
let(:bucket) { Gcloud::Storage::Bucket.from_gapi bucket_hash, storage.connection }

let(:bucket_with_cors_hash) { random_bucket_hash bucket_name, bucket_url, bucket_location, bucket_storage_class,
let(:bucket_with_cors_hash) { random_bucket_hash bucket_name, bucket_url_root, bucket_location, bucket_storage_class,
nil, nil, nil, nil, nil, bucket_cors }
let(:bucket_with_cors) { Gcloud::Storage::Bucket.from_gapi bucket_with_cors_hash, storage.connection }

Expand Down Expand Up @@ -163,7 +163,7 @@
end
end

it "sets cors rules in a nested block" do
it "adds cors rules in a nested block in update" do
mock_connection.patch "/storage/v1/b/#{bucket.name}" do |env|
json = JSON.parse env.body
rules = json["cors"]
Expand Down Expand Up @@ -203,4 +203,45 @@
end
end
end

it "adds cors rules in a block to cors" do
mock_connection.patch "/storage/v1/b/#{bucket.name}" do |env|
json = JSON.parse env.body
rules = json["cors"]
rules.count.must_equal 3
rules[0]["maxAgeSeconds"].must_equal 1800
rules[0]["origin"].must_equal ["http://example.org"]
rules[0]["method"].must_equal ["GET"]
rules[0]["responseHeader"].must_equal []
rules[1]["maxAgeSeconds"].must_equal 300
rules[1]["origin"].must_equal ["http://example.org", "https://example.org"]
rules[1]["method"].must_equal ["PUT", "DELETE"]
rules[1]["responseHeader"].must_equal ["X-My-Custom-Header"]
rules[2]["maxAgeSeconds"].must_equal 1800
rules[2]["origin"].must_equal ["http://example.com"]
rules[2]["method"].must_equal ["*"]
rules[2]["responseHeader"].must_equal ["X-Another-Custom-Header"]

updated_gapi = bucket.gapi.dup
updated_gapi["cors"] = json["cors"]
[200, { "Content-Type" => "application/json" },
random_bucket_hash(bucket_name, bucket_url, bucket_location,
bucket_storage_class, nil, nil, nil, nil, nil,
bucket_cors).to_json]
end


returned_cors = bucket.cors do |c|
c.add_rule "http://example.org", "GET"
c.add_rule ["http://example.org", "https://example.org"],
["PUT", "DELETE"],
headers: ["X-My-Custom-Header"],
max_age: 300
c.add_rule "http://example.com",
"*",
headers: "X-Another-Custom-Header"
end
returned_cors.frozen?.must_equal true
returned_cors.first.frozen?.must_equal true
end
end

0 comments on commit 5af1bd3

Please sign in to comment.