bigquery-ruby 0.0.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: 507e892d8247fea0bab2f3c666f9c84d83dda984
4
+ data.tar.gz: ebe4581eefc54d2d82b1614ea1818cf8bcad6d6b
5
+ SHA512:
6
+ metadata.gz: 49d416632bac167cacb9e9a74ce2468d21492b95ba8df4618db7ed640774d1548f4713cffee711d62cec40c25d8ae953f02c3589f01257969c65fb4bc75de818
7
+ data.tar.gz: 550dda8e9bb3d0de904061645773596dddcb87b527999957a5eb816ad622574ce51c2932bd38cbdc6877194cfbc37d1be81362cdcddc4015b7e62af9e3eb73c3
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bigquery-ruby.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Coconala Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ # BigQuery
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/bigquery/ruby`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'bigquery-ruby'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install bigquery-ruby
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ 1. Fork it ( https://github.com/[my-github-username]/bigquery-ruby/fork )
36
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
37
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
38
+ 4. Push to the branch (`git push origin my-new-feature`)
39
+ 5. Create a new Pull Request
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ end
7
+
8
+ task :default => :test
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bigquery/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bigquery-ruby"
8
+ spec.version = BigQuery::VERSION
9
+ spec.authors = ["Satoshi Ebisawa"]
10
+ spec.email = ["satoshi.ebisawa@coconala.com"]
11
+
12
+ spec.summary = %q{A BigQuery client library for Ruby}
13
+ spec.description = %q{A BigQuery client library for Ruby}
14
+ spec.homepage = "https://github.com/coconala/bigquery-ruby"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.9"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "minitest"
25
+
26
+ spec.add_runtime_dependency 'google-api-client', "~> 0.8.6"
27
+ end
@@ -0,0 +1,108 @@
1
+ require 'bigquery/bigquery_api'
2
+ require 'bigquery/bigquery_error'
3
+ require 'bigquery/query_async'
4
+ require 'bigquery/query_result'
5
+ require 'bigquery/resource'
6
+ require 'bigquery/version'
7
+
8
+ class BigQuery
9
+ DEFAULT_KEY_PASS = 'notasecret'
10
+
11
+ attr_accessor :project_id, :dataset_id
12
+
13
+ def initialize(project_id: nil, dataset_id: nil, api: nil,
14
+ auth_email: nil, auth_key: nil, auth_pass: DEFAULT_KEY_PASS,
15
+ client_id: nil, client_secret: nil)
16
+
17
+ @project_id = project_id
18
+ @dataset_id = dataset_id
19
+
20
+ if !api.nil?
21
+ @api = api
22
+ else
23
+ auth = do_auth(auth_email, auth_key, auth_pass, client_id, client_secret)
24
+ @api = BigQueryApi.new(auth)
25
+ end
26
+ end
27
+
28
+
29
+ def projects
30
+ result = @api.projects_list
31
+ result['projects'].map {|d| BigQuery::Resource.new(d) }
32
+ end
33
+
34
+
35
+ def datasets
36
+ check_project!
37
+ result = @api.datasets_list(@project_id)
38
+ result['datasets'].map {|d| BigQuery::Resource.new(d) }
39
+ end
40
+
41
+ def jobs
42
+ check_project!
43
+ result = @api.jobs_list(@project_id)
44
+ result['jobs'].map {|d| BigQuery::Resource.new(d) }
45
+ end
46
+
47
+
48
+ def tables
49
+ check_dataset!
50
+ result = @api.tables_list(@project_id, @dataset_id)
51
+ result['tables'].map {|d| BigQuery::Resource.new(d) }
52
+ end
53
+
54
+ def create_table(table_id, schema)
55
+ check_dataset!
56
+ @api.tables_insert(@project_id, @dataset_id, table_id, schema)
57
+ end
58
+
59
+ def query(sql, dry_run: false)
60
+ check_dataset!
61
+ result = do_query(sql, dry_run)
62
+ (dry_run) ? [] : BigQuery::QueryResult.new(result)
63
+ end
64
+
65
+ def query_async(sql, dry_run: false)
66
+ check_dataset!
67
+ result = @api.jobs_insert(@project_id, @dataset_id, sql, dry_run)
68
+ job_id = result['jobReference']['jobId']
69
+ (dry_run) ? nil : BigQuery::QueryAsync.new(@api, @project_id, job_id)
70
+ end
71
+
72
+
73
+ private
74
+ def do_auth(auth_email, auth_key, auth_pass, client_id, client_secret)
75
+ if !auth_email.nil?
76
+ auth = BigQueryApi.auth_account_by_cert(auth_email, auth_key, auth_pass)
77
+ end
78
+
79
+ if !client_id.nil?
80
+ auth = BigQueryApi.auth_installed_app(client_id, client_secret)
81
+ end
82
+
83
+ raise 'not authenticated' if auth.nil?
84
+ auth
85
+ end
86
+
87
+ def do_query(sql, dry_run = false)
88
+ job_id = nil
89
+ res = @api.jobs_query(@project_id, @dataset_id, sql, dry_run)
90
+
91
+ 10.times do
92
+ return res if res['jobComplete']
93
+
94
+ job_id ||= res['jobReference']['jobId']
95
+ res = @api.jobs_get_query_results(@project_id, job_id)
96
+ end
97
+ raise 'bigquery query failed'
98
+ end
99
+
100
+ def check_project!
101
+ raise 'project is not selected. set project_id.' if @project_id.nil?
102
+ end
103
+
104
+ def check_dataset!
105
+ check_project!
106
+ raise 'dataset is not selected. set dataset_id.' if @dataset_id.nil?
107
+ end
108
+ end
@@ -0,0 +1,131 @@
1
+ require 'google/api_client'
2
+ require 'google/api_client/auth/installed_app'
3
+ require 'json'
4
+
5
+ class BigQuery
6
+ class BigQueryApi
7
+ AUTH_SCOPE = 'https://www.googleapis.com/auth/bigquery'
8
+ MAX_RESULTS = 10000
9
+
10
+ class << self
11
+ def auth_account_by_cert(auth_email, auth_key, auth_pass)
12
+ key = Google::APIClient::KeyUtils.load_from_pkcs12(auth_key, auth_pass)
13
+
14
+ asserter = Google::APIClient::JWTAsserter.new(auth_email, AUTH_SCOPE, key)
15
+ asserter.authorize
16
+ end
17
+
18
+ def auth_installed_app(client_id, client_secret)
19
+ params = {
20
+ :client_id => client_id,
21
+ :client_secret => client_secret,
22
+ :scope => AUTH_SCOPE,
23
+ }
24
+
25
+ flow = Google::APIClient::InstalledAppFlow.new(params)
26
+ flow.authorize
27
+ end
28
+ end
29
+
30
+
31
+ def initialize(auth)
32
+ @client = Google::APIClient.new(application_name: NAME, application_version: VERSION)
33
+ @client.authorization = auth
34
+
35
+ @bq = @client.discovered_api('bigquery', 'v2')
36
+ end
37
+
38
+
39
+ def projects_list
40
+ execute(@bq.projects.list)
41
+ end
42
+
43
+
44
+ def datasets_list(project_id)
45
+ execute(@bq.datasets.list, params: { projectId: project_id })
46
+ end
47
+
48
+ def datasets_get(dataset_id)
49
+ execute(@bq.datasets.get, params: { projectId: project_id, datasetId: dataset_id })
50
+ end
51
+
52
+
53
+ def tables_list(project_id, dataset_id)
54
+ execute(@bq.tables.list, params: { projectId: project_id, datasetId: dataset_id })
55
+ end
56
+
57
+ def tables_insert(project_id, dataset_id, table_id, schema)
58
+ body = {
59
+ tableReference: {
60
+ "projectId": project_id,
61
+ "datasetId": dataset_id,
62
+ "tableId": table_id,
63
+ },
64
+ schema: { fields: schema },
65
+ }
66
+
67
+ execute(@bq.tables.insert, params: { projectId: project_id, datasetId: dataset_id }, body: body)
68
+ end
69
+
70
+
71
+ def jobs_list(project_id)
72
+ execute(@bq.jobs.list, params: { projectId: project_id })
73
+ end
74
+
75
+ def jobs_get(project_id, job_id)
76
+ execute(@bq.jobs.get, params: { projectId: project_id, jobId: job_id })
77
+ end
78
+
79
+ def jobs_query(project_id, dataset_id, sql, dry_run = false)
80
+ body = {
81
+ query: sql,
82
+ defaultDataset: { datasetId: dataset_id },
83
+ maxResults: MAX_RESULTS,
84
+ timeoutMs: 10000,
85
+ dryRun: dry_run,
86
+ }
87
+
88
+ execute(@bq.jobs.query, params: { projectId: project_id }, body: body)
89
+ end
90
+
91
+ def jobs_insert(project_id, dataset_id, sql, dry_run = false)
92
+ body = {
93
+ configuration: {
94
+ query: {
95
+ query: sql,
96
+ defaultDataset: { datasetId: dataset_id },
97
+ priority: 'INTERACTIVE', # or 'BATCH'
98
+ allowLargeResults: false,
99
+ useQueryCache: true,
100
+ },
101
+ dryRun: dry_run,
102
+ },
103
+ }
104
+
105
+ execute(@bq.jobs.insert, params: { projectId: project_id }, body: body)
106
+ end
107
+
108
+ def jobs_get_query_results(project_id, job_id)
109
+ params = {
110
+ projectId: project_id,
111
+ jobId: job_id,
112
+ maxResults: MAX_RESULTS,
113
+ timeoutMs: 10000,
114
+ }
115
+
116
+ execute(@bq.jobs.get_query_results, params: params)
117
+ end
118
+
119
+
120
+ private
121
+ def execute(method, params: nil, body: nil)
122
+ args = { api_method: method }
123
+ args[:parameters] = params if !params.nil?
124
+ args[:body_object] = body if !body.nil?
125
+
126
+ res = JSON.parse(@client.execute(args).response.body)
127
+ raise BigQueryError.new(res['error']['message']) if res['error']
128
+ res
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,4 @@
1
+ class BigQueryError < StandardError
2
+
3
+
4
+ end
@@ -0,0 +1,23 @@
1
+ class BigQuery
2
+ class QueryAsync
3
+ def initialize(api, project_id, job_id)
4
+ @api = api
5
+ @project_id = project_id
6
+ @job_id = job_id
7
+ end
8
+
9
+ def done?
10
+ result = @api.jobs_get(@project_id, @job_id)
11
+ result['status']['state'] == 'DONE'
12
+ end
13
+
14
+ def job
15
+ BigQuery::Resource.new(@api.jobs_get(@project_id, @job_id))
16
+ end
17
+
18
+ def result
19
+ result = @api.jobs_get_query_results(@project_id, @job_id)
20
+ BigQuery::QueryResult.new(result)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ class BigQuery
2
+ class QueryResult < Array
3
+ attr_reader :total_bytes_processed
4
+
5
+ def initialize(result)
6
+ @total_bytes_processed = result['totalBytesProcessed'].to_i
7
+ make_rows(result)
8
+ end
9
+
10
+ private
11
+ def make_rows(res)
12
+ schema = res['schema']['fields']
13
+ if res['totalRows'].to_i > 0
14
+ res['rows'].each {|row| self.push(row_hash(row, schema)) }
15
+ end
16
+ end
17
+
18
+ def row_hash(row, schema)
19
+ rh = {}
20
+ row['f'].each_with_index do |field, index|
21
+ name = schema[index]['name']
22
+ rh[name] = parse_value(field['v'], schema[index]['type'])
23
+ end
24
+ rh
25
+ end
26
+
27
+ def parse_value(value, type)
28
+ case type
29
+ when 'STRING'
30
+ return value
31
+ when 'INTEGER'
32
+ return value.to_i
33
+ when 'FLOAT'
34
+ return value.to_f
35
+ when 'BOOLEAN'
36
+ return (value.upcase == 'TRUE') ? true : false
37
+ when 'TIMESTAMP'
38
+ t = value.to_f.to_i
39
+ return Time.at(t)
40
+ else
41
+ raise 'unknown data type: #{type}'
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
1
+ class BigQuery
2
+ class Resource
3
+ def initialize(res)
4
+ @resource = make_resource(res)
5
+ end
6
+
7
+ def to_s
8
+ @resource['id']
9
+ end
10
+
11
+ def properties
12
+ @resource.keys
13
+ end
14
+
15
+ def method_missing(method, *args)
16
+ @resource[method.to_s]
17
+ end
18
+
19
+ private
20
+ def make_resource(src)
21
+ result = {}
22
+ src.each do |k, v|
23
+ key = k.gsub(/[A-Z]/, '_\&').downcase
24
+ next if respond_to?(key)
25
+
26
+ result[key] = v.kind_of?(Hash) ? BigQuery::Resource.new(v) : v
27
+ end
28
+ result
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,4 @@
1
+ class BigQuery
2
+ NAME = 'bigquery-ruby'
3
+ VERSION = "0.0.1"
4
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bigquery-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Satoshi Ebisawa
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-02-04 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.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: google-api-client
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.8.6
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.8.6
69
+ description: A BigQuery client library for Ruby
70
+ email:
71
+ - satoshi.ebisawa@coconala.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - bigquery-ruby.gemspec
82
+ - lib/bigquery.rb
83
+ - lib/bigquery/bigquery_api.rb
84
+ - lib/bigquery/bigquery_error.rb
85
+ - lib/bigquery/query_async.rb
86
+ - lib/bigquery/query_result.rb
87
+ - lib/bigquery/resource.rb
88
+ - lib/bigquery/version.rb
89
+ homepage: https://github.com/coconala/bigquery-ruby
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.5.1
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: A BigQuery client library for Ruby
113
+ test_files: []