gcs 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cf9d720d07e17816a3153871d3270c62cedc6636
4
+ data.tar.gz: 14d98afb77a2604726d50af1495f8567001c8387
5
+ SHA512:
6
+ metadata.gz: a6c852593cfdbebe19960b7e2a98cefaa688c849587e58d64ab90aad7ceefff7dc587d2397672bf3dba8eb64f6f69fbebe4c78b98f0e2bc0c7662337c4311054
7
+ data.tar.gz: d70fa373f3fd6e57fa2560befab7f1e70fd57253f58ba3087820227381fb93ba6a47b4eb7078036db1029a32255a88be1ce0c6cb9bfa0036f9c306ed7c6e7ca0
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ /config/*.json
16
+ /vendor/bundle
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,8 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ - 2.3.4
6
+ before_install:
7
+ - if [ "${TRAVIS_SECURE_ENV_VARS}" = true ]; then openssl aes-256-cbc -K $encrypted_22290c477736_key -iv $encrypted_22290c477736_iv -in config/service_account.json.enc -out config/service_account.json -d; fi
8
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gcs.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2017 Groovenauts, Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,69 @@
1
+ # gcs-ruby
2
+
3
+ [![Build Status](https://travis-ci.org/groovenauts/gcs-ruby.svg?branch=master)](https://travis-ci.org/groovenauts/gcs-ruby)
4
+
5
+ Groovenauts' wrapper library for Google Cloud Storage with [google-api-ruby-client](https://github.com/google/google-api-ruby-client).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'gcs'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install gcs
22
+
23
+ ## Usage
24
+
25
+ ```
26
+ gcs = Gcs.new(email_address, private_key)
27
+
28
+ # get bucket list
29
+ gcs.bucket(project_id)
30
+
31
+ # get object list
32
+ gcs.list_objects("myBucket", prefix: "path/to/subdir", max_results: 1000)
33
+
34
+ # get object metadata
35
+ get_object "myBucket", "myObject"
36
+ get_object "gs://myBucket/myObject"
37
+
38
+ # download object content
39
+ io = StringIO.new
40
+ get_object "myBucket", "myObject", download_dest: io
41
+ get_object "gs://myBucket/myObject", download_dest: io
42
+
43
+ # delete object
44
+ gcs.delete_object("myBucket", "myObject")
45
+ gcs.delete_object("gs://myBucket/myObject")
46
+
47
+ # create new object
48
+ io = StringIO.new("Hello, World!\")
49
+ gcs.insert_object("myBucket", "myObject", io)
50
+
51
+ # copy recursively
52
+ gcs.copy_tree("gs://myBucket1/src", "gs://myBucket2/dest")
53
+
54
+ # delete recursively
55
+ gcs.remove_tree("gs://myBucket/dir")
56
+
57
+ # read object content with size limitation
58
+ gcs.read_partial("gs://myBucket/myObject", limit: 1024) # => read first part of object at least 1024 bytes.
59
+ gcs.read_partial("myBucket", "myObjet", limit: 1024)
60
+ gcs.read_partial("gs://myBucket/myObject", limit: 1024, trim_after_last_delimiter: "\n") #=> remove substr after last "\n"
61
+
62
+ # initiate resumable upload
63
+ # return session URL to upload object content by PUT method requests.
64
+ # see https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload
65
+ # origin_domain keyword arg was for CORS setting.
66
+ gcs.initiate_resumable_upload("myBucket", "myObject", content_type: "text/plain", origin_domain: "http://example.com")
67
+ gcs.initiate_resumable_upload("gs://myBucket/myObject", content_type: "text/plain", origin_domain: "http://example.com")
68
+ ```
69
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "gcs"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ service_account_json = File.expand_path("../../config/service_account.json", __FILE__)
14
+ if File.readable?(service_account_json)
15
+ json = JSON.parse(File.read(service_account_json))
16
+ @client = Gcs.new(json["client_email"], OpenSSL::PKey::RSA.new(json["private_key"]))
17
+ end
18
+
19
+ require "pry"
20
+ if @client
21
+ Pry.start(@client)
22
+ else
23
+ Pry.start
24
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'gcs/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "gcs"
8
+ spec.version = Gcs::VERSION
9
+ spec.authors = ["Groovenauts, Inc."]
10
+ spec.email = ["tech@groovenauts.jp"]
11
+
12
+ spec.summary = %q{Groovenauts' wrapper library for Google Cloud Storage with google-api-ruby-client}
13
+ spec.description = %q{Groovenauts' wrapper library for Google Cloud Storage with google-api-ruby-client}
14
+ spec.homepage = "https://github.com/groovenauts/gcs-ruby"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.13"
24
+ spec.add_development_dependency "pry"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rake-compiler"
27
+ spec.add_development_dependency "rspec", "~> 3.0"
28
+ spec.add_dependency "google-api-client"
29
+ end
@@ -0,0 +1,234 @@
1
+ # coding: utf-8
2
+
3
+ require "net/http"
4
+ require "cgi"
5
+ require "json"
6
+
7
+ require "gcs/version"
8
+ require "google/apis/storage_v1"
9
+
10
+ class Gcs
11
+ include Google::Apis::StorageV1
12
+ def initialize(email_address = nil, private_key = nil, scope: "cloud-platform")
13
+ @api = Google::Apis::StorageV1::StorageService.new
14
+ scope_url = "https://www.googleapis.com/auth/#{scope}"
15
+ if email_address and private_key
16
+ auth = Signet::OAuth2::Client.new(
17
+ token_credential_uri: "https://accounts.google.com/o/oauth2/token",
18
+ audience: "https://accounts.google.com/o/oauth2/token",
19
+ scope: scope_url,
20
+ issuer: email_address,
21
+ signing_key: private_key)
22
+ else
23
+ auth = Google::Auth.get_application_default([scope_url])
24
+ end
25
+ auth.fetch_access_token!
26
+ @api.authorization = auth
27
+ end
28
+
29
+ def buckets(project_id)
30
+ @api.list_buckets(project_id, max_results: 1000).items || []
31
+ end
32
+
33
+ def bucket(name)
34
+ @api.get_bucket(name)
35
+ rescue Google::Apis::ClientError
36
+ if $!.status_code == 404
37
+ return nil
38
+ else
39
+ raise
40
+ end
41
+ end
42
+
43
+ def insert_bucket(project_id, name, storage_class: "STANDARD", acl: nil, default_object_acl: nil, location: nil)
44
+ b = Bucket.new(
45
+ name: name,
46
+ storage_class: storage_class
47
+ )
48
+ b.location = location if location
49
+ b.acl = acl if acl
50
+ b.default_object_acl = default_object_acl if default_object_acl
51
+ @api.insert_bucket(project_id, b)
52
+ end
53
+
54
+ def delete_bucket(name)
55
+ @api.delete_bucket(name)
56
+ rescue Google::Apis::ClientError
57
+ if $!.status_code == 404
58
+ return nil
59
+ else
60
+ raise
61
+ end
62
+ end
63
+
64
+ def self.ensure_bucket_object(bucket, object=nil)
65
+ if object.nil? and bucket.start_with?("gs://")
66
+ bucket = bucket.sub(%r{\Ags://}, "")
67
+ bucket, object = bucket.split("/", 2)
68
+ end
69
+ return [bucket, object]
70
+ end
71
+
72
+ def _ensure_bucket_object(bucket, object=nil)
73
+ self.class.ensure_bucket_object(bucket, object)
74
+ end
75
+
76
+ def get_object(bucket, object=nil, download_dest: nil)
77
+ bucket, object = _ensure_bucket_object(bucket, object)
78
+ begin
79
+ obj = @api.get_object(bucket, object)
80
+ if download_dest
81
+ @api.get_object(bucket, object, generation: obj.generation, download_dest: download_dest)
82
+ end
83
+ obj
84
+ rescue Google::Apis::ClientError
85
+ if $!.status_code == 404
86
+ return nil
87
+ else
88
+ raise
89
+ end
90
+ end
91
+ end
92
+
93
+ def read_partial(bucket, object=nil, limit: 1024*1024, trim_after_last_delimiter: nil, &blk)
94
+ bucket, object = _ensure_bucket_object(bucket, object)
95
+ uri = URI("https://www.googleapis.com/download/storage/v1/b/#{CGI.escape(bucket)}/o/#{CGI.escape(object).gsub("+", "%20")}?alt=media")
96
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
97
+ req = Net::HTTP::Get.new(uri.request_uri)
98
+ req["Authorization"] = "Bearer #{@api.authorization.access_token}"
99
+ http.request(req) do |res|
100
+ case res
101
+ when Net::HTTPSuccess
102
+ if blk
103
+ res.read_body(&blk)
104
+ return res
105
+ else
106
+ total = "".force_encoding(Encoding::ASCII_8BIT)
107
+ res.read_body do |part|
108
+ total << part
109
+ if total.bytesize > limit
110
+ break
111
+ end
112
+ end
113
+ if trim_after_last_delimiter
114
+ i = total.rindex(trim_after_last_delimiter.force_encoding(Encoding::ASCII_8BIT))
115
+ if i.nil?
116
+ # If no delimiter was found, return empty string.
117
+ # This is because caller expect not to incomplete line. (ex: Newline Delimited JSON)
118
+ i = -1
119
+ end
120
+ total[(i+1)..-1] = ""
121
+ end
122
+ return total
123
+ end
124
+ when Net::HTTPNotFound
125
+ return nil
126
+ else
127
+ raise "Gcs.read_partial failed with HTTP status #{res.code}: #{res.body}"
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ def list_objects(bucket, delimiter: "/", prefix: "", page_token: nil, max_results: nil)
134
+ @api.list_objects(bucket, delimiter: delimiter, prefix: prefix, page_token: page_token, max_results: max_results)
135
+ end
136
+
137
+ def delete_object(bucket, object=nil, if_generation_match: nil)
138
+ bucket, object = _ensure_bucket_object(bucket, object)
139
+ @api.delete_object(bucket, object, if_generation_match: if_generation_match)
140
+ end
141
+
142
+ # @param [String] bucket
143
+ # @param [String] object name
144
+ # @param [String|IO] source
145
+ # @param [String] content_type
146
+ # @param [String] content_encoding
147
+ #
148
+ # @return [Google::Apis::StorageV1::Object]
149
+ def insert_object(bucket, name, source, content_type: nil, content_encoding: nil, if_generation_match: nil)
150
+ bucket, name = _ensure_bucket_object(bucket, name)
151
+ obj = Google::Apis::StorageV1::Object.new(name: name)
152
+ @api.insert_object(bucket, obj, content_encoding: content_encoding, upload_source: source, content_type: content_type,
153
+ if_generation_match: if_generation_match)
154
+ end
155
+
156
+ def rewrite(src_bucket, src_object, dest_bucket, dest_object, if_generation_match: nil)
157
+ r = @api.rewrite_object(src_bucket, src_object, dest_bucket, dest_object, if_generation_match: if_generation_match)
158
+ until r.done
159
+ r = @api.rewrite_object(src_bucket, src_object, dest_bucket, dest_object, rewrite_token: r.rewrite_token, if_generation_match: if_generation_match)
160
+ end
161
+ r
162
+ end
163
+
164
+ def copy_tree(src, dest)
165
+ src_bucket, src_path = self.class.ensure_bucket_object(src)
166
+ dest_bucket, dest_path = self.class.ensure_bucket_object(dest)
167
+ src_path = src_path + "/" unless src_path[-1] == "/"
168
+ dest_path = dest_path + "/" unless dest_path[-1] == "/"
169
+ res = list_objects(src_bucket, prefix: src_path)
170
+ (res.items || []).each do |o|
171
+ next if o.name[-1] == "/"
172
+ dest_obj_name = dest_path + o.name.sub(/\A#{Regexp.escape(src_path)}/, "")
173
+ self.rewrite(src_bucket, o.name, dest_bucket, dest_obj_name)
174
+ end
175
+ (res.prefixes || []).each do |p|
176
+ copy_tree("gs://#{src_bucket}/#{p}", "gs://#{dest_bucket}/#{dest_path}#{p.sub(/\A#{Regexp.escape(src_path)}/, "")}")
177
+ end
178
+ end
179
+
180
+ def copy_object(src, dest)
181
+ src_bucket, src_path = self.class.ensure_bucket_object(src)
182
+ dest_bucket, dest_path = self.class.ensure_bucket_object(dest)
183
+ self.rewrite(src_bucket, src_path, dest_bucket, dest_path)
184
+ end
185
+
186
+ def remove_tree(gcs_url)
187
+ bucket, path = self.class.ensure_bucket_object(gcs_url)
188
+ if path.size > 0 and path[-1] != "/"
189
+ path = path + "/"
190
+ end
191
+ next_page_token = nil
192
+ loop do
193
+ begin
194
+ res = list_objects(bucket, prefix: path, delimiter: nil, page_token: next_page_token)
195
+ rescue Google::Apis::ClientError
196
+ if $!.status_code == 404
197
+ return nil
198
+ else
199
+ raise
200
+ end
201
+ end
202
+
203
+ # batch request あたりの API 呼び出しの量は API の種類によって異なり
204
+ # Cloud Storage JSON API のドキュメントでは 100 となってるけど1000でもいけたので1000に変更
205
+ # ref. https://cloud.google.com/storage/docs/json_api/v1/how-tos/batch
206
+ (res.items || []).each_slice(1000) do |objs|
207
+ @api.batch do
208
+ objs.each do |o|
209
+ @api.delete_object(bucket, o.name) {|_, err| raise err if err and (not(err.respond_to?(:status_code)) or (err.status_code != 404))}
210
+ end
211
+ end
212
+ end
213
+ break unless res.next_page_token
214
+ next_page_token = res.next_page_token
215
+ end
216
+ end
217
+
218
+ def initiate_resumable_upload(bucket, object=nil, content_type: "application/octet-stream", origin_domain: nil)
219
+ bucket, object = self.class.ensure_bucket_object(bucket, object)
220
+ uri = URI("https://www.googleapis.com/upload/storage/v1/b/#{CGI.escape(bucket)}/o?uploadType=resumable")
221
+ http = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
222
+ req = Net::HTTP::Post.new(uri.request_uri)
223
+ req["content-type"] = "application/json; charset=UTF-8"
224
+ req["Authorization"] = "Bearer #{@api.authorization.access_token}"
225
+ req["X-Upload-Content-Type"] = content_type
226
+ if origin_domain
227
+ req["Origin"] = origin_domain
228
+ end
229
+ req.body = JSON.generate({ "name" => object })
230
+ res = http.request(req)
231
+ return res["location"]
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,3 @@
1
+ class Gcs
2
+ VERSION = "0.1.1"
3
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gcs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Groovenauts, Inc.
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-12-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake-compiler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: google-api-client
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Groovenauts' wrapper library for Google Cloud Storage with google-api-ruby-client
98
+ email:
99
+ - tech@groovenauts.jp
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".travis.yml"
107
+ - Gemfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - bin/console
112
+ - bin/setup
113
+ - config/service_account.json.enc
114
+ - gcs.gemspec
115
+ - lib/gcs.rb
116
+ - lib/gcs/version.rb
117
+ homepage: https://github.com/groovenauts/gcs-ruby
118
+ licenses: []
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.6.13
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: Groovenauts' wrapper library for Google Cloud Storage with google-api-ruby-client
140
+ test_files: []