keboola-gooddata-writer 2.0.0.pre1

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: 85346de59e1ea53f6e4ba5e15629accc9ca519d5
4
+ data.tar.gz: 11eed840a3b3f0d4dcf741fb289b988f4183e853
5
+ SHA512:
6
+ metadata.gz: 58927fffa6fb30be7bc722dfd76ab0e37e544f5f81d3967170ab7ed0268c380b1750eea3b895e58b97f9df6f252eb2a5bd45d0a550afedc88ac647c204f025b4
7
+ data.tar.gz: f7409cd5e031b9d94f30e478e5a4e6d6d8bec50d35726f2bd9e315852a07813ea7cb1d860469f6d569c239e553cb0b4aa797dbaf3354bca9d534c97c75f1988c
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1
5
+ - 2.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in keboola-gooddata-writer.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Roman Sklenář
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,74 @@
1
+ # Keboola GoodData Writer API Ruby client
2
+
3
+ [![Quality](http://img.shields.io/codeclimate/github/romansklenar/gooddata-writer-ruby-client.svg?style=flat-square)](https://codeclimate.com/github/romansklenar/gooddata-writer-ruby-client)
4
+ [![Coverage](http://img.shields.io/codeclimate/coverage/github/romansklenar/gooddata-writer-ruby-client.svg?style=flat-square)](https://codeclimate.com/github/romansklenar/gooddata-writer-ruby-client)
5
+ [![Build](http://img.shields.io/travis-ci/romansklenar/gooddata-writer-ruby-client.svg?style=flat-square)](https://travis-ci.org/romansklenar/gooddata-writer-ruby-client)
6
+ [![Dependencies](http://img.shields.io/gemnasium/romansklenar/gooddata-writer-ruby-client.svg?style=flat-square)](https://gemnasium.com/romansklenar/gooddata-writer-ruby-client)
7
+ [![Downloads](http://img.shields.io/gem/dtv/gooddata-writer-ruby-client.svg?style=flat-square)](https://rubygems.org/gems/gooddata-writer-ruby-client)
8
+ [![Tags](http://img.shields.io/github/tag/romansklenar/gooddata-writer-ruby-client.svg?style=flat-square)](http://github.com/romansklenar/gooddata-writer-ruby-client/tags)
9
+ [![Releases](http://img.shields.io/github/release/romansklenar/gooddata-writer-ruby-client.svg?style=flat-square)](http://github.com/romansklenar/gooddata-writer-ruby-client/releases)
10
+ [![Issues](http://img.shields.io/github/issues/romansklenar/gooddata-writer-ruby-client.svg?style=flat-square)](http://github.com/romansklenar/gooddata-writer-ruby-client/issues)
11
+ [![License](http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](http://opensource.org/licenses/MIT)
12
+ [![Version](http://img.shields.io/gem/v/gooddata-writer-ruby-client.svg?style=flat-square)](https://rubygems.org/gems/gooddata-writer-ruby-client)
13
+
14
+
15
+ Simple Ruby wrapper library for [Keboola GoodData Writer REST API](http://docs.keboolagooddatawriter.apiary.io/).
16
+
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ ```ruby
23
+ gem 'keboola-gooddata-writer'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install keboola-gooddata-writer
33
+
34
+ ## Usage
35
+
36
+ Obtain your Keboola Storage API token and use to initialize client.
37
+
38
+ ```ruby
39
+ api = Keboola::GoodDataWriter::API.new(token: '123-abcdefghjklmnopqrstuvxyz')
40
+
41
+ # get details of specific writer
42
+ api.writer('MyWriter') # {"bucket"=>"sys.c-wr-gooddata-writer1", "writer"=>"gooddata", "writerId"=>"writer1", …, "status"=>"ready"}
43
+
44
+ # create GoodData project
45
+ api.create_project('MyWriter', optionals: { name: 'KBC - MyProject - MyWriter' }) # <Keboola::GoodDataWriter::Job url="https://syrup.keboola.com/queue/jobs/123456", id="123456">
46
+
47
+ # create user
48
+ api.create_user('MyWriter', 'john.snow@test.keboola.com', 't0pS3cr3t', 'John', 'Snow') # <Keboola::GoodDataWriter::Job url="https://syrup.keboola.com/queue/jobs/123456", id="123456">
49
+
50
+ # assign user to existing GoodData project
51
+ api.add_project_users('MyWriter', 'xjywplmhejceb6j3ezzlxiganmjavqio', 'john.snow@test.keboola.com', 'editor') #<Keboola::GoodDataWriter::Job url="https://syrup.keboola.com/queue/jobs/123456", id="123456">
52
+
53
+ # getretreive GoodData SSO link
54
+ api.sso('MyWriter', 'xjywplmhejceb6j3ezzlxiganmjavqio', 'john.snow@test.keboola.com') # "https://secure.gooddata.com/gdc/account/customerlogin?sessionId=-----BEGIN+PGP+MESSAGE-----s0m3_l0000n6_h4sh"
55
+ ```
56
+
57
+ ## Development
58
+
59
+ 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.
60
+
61
+ Tests are runned against [mock API server](https://private-anon-cf7bb7f95-keboolagooddatawriter.apiary-mock.com). If you want to run tests against production API server you must provide valid Keboola Storage API token and set it as environment variable called `KEBOOLA_STORAGE_API_TOKEN`.
62
+
63
+ 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).
64
+
65
+ ## Contributing
66
+ Get familiar with [Github Flow](https://guides.github.com/introduction/flow/index.html) and stick with it on this project.
67
+ We're using [Github Issues](https://github.com/slowpath/rails-insights/issues) as an issue tracker. All related tasks are there.
68
+
69
+ It's simple!
70
+ 1. Fork it ( https://github.com/romansklenar/keboola-gooddata-writer/fork )
71
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
72
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
73
+ 4. Push to the branch (`git push origin my-new-feature`)
74
+ 5. Create a new Pull Request
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |task|
5
+ task.libs << %w[test lib]
6
+ task.pattern = 'test/**/*_test.rb'
7
+ task.verbose = true
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "keboola/gooddata_writer"
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
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'keboola/gooddata_writer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "keboola-gooddata-writer"
8
+ spec.version = Keboola::GoodDataWriter::VERSION
9
+ spec.date = Date.today
10
+ spec.authors = ["Roman Sklenář"]
11
+ spec.email = ["mail@romansklenar.cz"]
12
+
13
+ spec.summary = "A convenient Ruby wrapper around the Keboola GoodData Writer API."
14
+ spec.description = "Use the Keboola::GoodDataWriter class to integrate Keboola GoodData Writer into your own application."
15
+ spec.homepage = "https://github.com/romansklenar/gooddata-writer-ruby-client"
16
+ spec.license = "MIT"
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.8"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "minitest", "~> 5.7"
24
+ spec.add_development_dependency "webmock"
25
+ spec.add_development_dependency "vcr", "~> 2.9"
26
+ spec.add_development_dependency "codeclimate-test-reporter"
27
+
28
+ spec.add_dependency "json"
29
+ spec.add_dependency "hurley", "~> 0.1"
30
+ end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path|
2
+ require "keboola/core_ext/#{File.basename(path, '.rb')}"
3
+ end
@@ -0,0 +1,22 @@
1
+ class Hash
2
+ # Merges the caller into +other_hash+. For example,
3
+ #
4
+ # options = options.reverse_merge(size: 25, velocity: 10)
5
+ #
6
+ # is equivalent to
7
+ #
8
+ # options = { size: 25, velocity: 10 }.merge(options)
9
+ #
10
+ # This is particularly useful for initializing an options hash
11
+ # with default values.
12
+ def reverse_merge(other_hash)
13
+ other_hash.merge(self)
14
+ end
15
+
16
+ # Destructive +reverse_merge+.
17
+ def reverse_merge!(other_hash)
18
+ # right wins if there is no left
19
+ merge!( other_hash ){|key,left,right| left }
20
+ end
21
+ alias_method :reverse_update, :reverse_merge!
22
+ end
@@ -0,0 +1,12 @@
1
+ require "keboola/gooddata_writer/version"
2
+ require "keboola/gooddata_writer/errors"
3
+ require "keboola/gooddata_writer/job"
4
+ require "keboola/gooddata_writer/parser"
5
+ require "keboola/gooddata_writer/client"
6
+ require "keboola/gooddata_writer/api"
7
+ require "keboola/core_ext"
8
+
9
+ module Keboola
10
+ module GoodDataWriter
11
+ end
12
+ end
@@ -0,0 +1,365 @@
1
+ module Keboola
2
+ module GoodDataWriter
3
+
4
+ class API
5
+ QUEUE_ENDPOINT_URL = 'https://syrup.keboola.com/queue/'
6
+ WRITER_ENDPOINT_URL = 'https://syrup.keboola.com/gooddata-writer/'
7
+ QUEUE_MOCK_ENDPOINT_URL = 'https://private-anon-c1bf53b9c-syrupqueue.apiary-mock.com/queue/'
8
+ WRITER_MOCK_ENDPOINT_URL = 'https://private-anon-df256c5fb-keboolagooddatawriter.apiary-mock.com/gooddata-writer/'
9
+
10
+ attr_accessor :token, :client, :queue, :parser
11
+
12
+
13
+ def initialize(token:, endpoint: nil, client: nil, queue: nil, parser: nil)
14
+ @token = token
15
+ @client = client || self.class.build_client(endpoint: endpoint || WRITER_ENDPOINT_URL, token: token)
16
+ @queue = queue || self.class.build_client(endpoint: QUEUE_ENDPOINT_URL, token: token)
17
+ @parser = parser || self.class.build_parser
18
+ end
19
+
20
+ def self.build_client(token:, endpoint:)
21
+ Client.factory(token: token, endpoint: endpoint)
22
+ end
23
+
24
+ def self.build_parser
25
+ Parser.new
26
+ end
27
+
28
+
29
+ # === Writer resource commands
30
+
31
+ # Returns list of available writers and their buckets
32
+ def writers
33
+ handle @client.get("writers") do |result|
34
+ result["writers"]
35
+ end
36
+ end
37
+
38
+ # Returns attributes of the writer
39
+ def writer(writer_id)
40
+ handle @client.get("writers", { writerId: writer_id }) do |result|
41
+ result["writers"].first rescue result["writer"]
42
+ end
43
+ end
44
+
45
+ # Creates new configuration bucket and either uses existing GoodData
46
+ # project or creates new one along with dedicated GoodData user.
47
+ def create_writer(writer_id, optionals: {}, async: true)
48
+ handle @client.post("writers", { writerId: writer_id }.reverse_merge(optionals).to_json) do |result|
49
+ async ? Job.new(result) : wait_for_job(result["id"])
50
+ end
51
+ end
52
+
53
+ # Sets attributes to writer's configuration
54
+ def update_writer(writer_id, attributes: {})
55
+ handle @client.post("writers/#{writer_id}", attributes.to_json) do |result|
56
+ Job.new(result).ok?
57
+ end
58
+ end
59
+
60
+ # Deletes configuration bucket and enqueues GoodData project and dedicated GoodData user for removal
61
+ def delete_writer(writer_id, async: true)
62
+ handle @client.delete("writers", { writerId: writer_id }) do |result|
63
+ async ? Job.new(result) : wait_for_job(result["id"])
64
+ end
65
+ end
66
+
67
+
68
+ # === Project resource commands
69
+
70
+ # Returns list of project clones including main project marked with main: 1 field
71
+ def projects(writer_id)
72
+ handle @client.get("projects", { writerId: writer_id }) do |result|
73
+ result["projects"]
74
+ end
75
+ end
76
+
77
+ # Creates new configuration bucket and either uses existing GoodData
78
+ # project or creates new one along with dedicated GoodData user.
79
+ def create_project(writer_id, optionals: {}, async: true)
80
+ handle @client.post("projects", { writerId: writer_id }.reverse_merge(optionals).to_json) do |result|
81
+ async ? Job.new(result) : wait_for_job(result["id"])
82
+ end
83
+ end
84
+
85
+ # Creates new GoodData project for the writer and enqueues the old for deletion
86
+ def reset_project(writer_id, optionals: {}, async: true)
87
+ handle @client.post("reset-project", { writerId: writer_id }.reverse_merge(optionals).to_json) do |result|
88
+ async ? Job.new(result) : wait_for_job(result["id"])
89
+ end
90
+ end
91
+
92
+ # Upload project to GoodData
93
+ def upload_project(writer_id, optionals: {}, async: true)
94
+ handle @client.post("upload-project", { writerId: writer_id }.reverse_merge(optionals).to_json) do |result|
95
+ async ? Job.new(result) : wait_for_job(result["id"])
96
+ end
97
+ end
98
+
99
+
100
+ # === User resource commands
101
+
102
+ # Get users list
103
+ def users(writer_id)
104
+ handle @client.get("users", { writerId: writer_id }) do |result|
105
+ result["users"]
106
+ end
107
+ end
108
+
109
+ # Creates new GoodData user in Keboola domain.
110
+ def create_user(writer_id, email, password, first_name, last_name, optionals: {}, async: true)
111
+ required = { writerId: writer_id, email: email, password: password, firstName: first_name, lastName: last_name }
112
+ handle @client.post("users", required.reverse_merge(optionals).to_json) do |result|
113
+ async ? Job.new(result) : wait_for_job(result["id"])
114
+ end
115
+ end
116
+
117
+
118
+ # === Project & User nested resource commands
119
+
120
+ # Get list of users in project
121
+ def project_users(writer_id, project_id)
122
+ handle @client.get("project-users", { writerId: writer_id, pid: project_id }) do |result|
123
+ result["users"]
124
+ end
125
+ end
126
+
127
+ # Adds GoodData user to specified project.
128
+ def add_project_users(writer_id, project_id, email, role, optionals: {}, async: true)
129
+ required = { writerId: writer_id, pid: project_id, email: email, role: role }
130
+ handle @client.post("project-users", required.reverse_merge(optionals).to_json) do |result|
131
+ async ? Job.new(result) : wait_for_job(result["id"])
132
+ end
133
+ end
134
+
135
+ # Remove user from specified project.
136
+ def remove_project_users(writer_id, project_id, email, async: true)
137
+ handle @client.delete("project-users", { writerId: writer_id, pid: project_id, email: email }) do |result|
138
+ async ? Job.new(result) : wait_for_job(result["id"])
139
+ end
140
+ end
141
+
142
+
143
+ # === GoodData project structure commands
144
+
145
+ # Get tables list
146
+ def tables(writer_id)
147
+ handle @client.get("tables", { writerId: writer_id }) do |result|
148
+ result["tables"]
149
+ end
150
+ end
151
+
152
+ # Get table detail
153
+ def table(writer_id, table_id)
154
+ handle @client.get("tables", { writerId: writer_id, tableId: table_id }) do |result|
155
+ result["tables"].first rescue result["table"]
156
+ end
157
+ end
158
+
159
+ # Update table configuration
160
+ def update_table(writer_id, table_id, optionals: {})
161
+ handle @client.post("tables", { writerId: writer_id, tableId: table_id }.reverse_merge(optionals).to_json) do |result|
162
+ Job.new(result).ok?
163
+ end
164
+ end
165
+
166
+ # Update table column configuration
167
+ def update_table_column(writer_id, table_id, column, optionals: {})
168
+ handle @client.post("tables", { writerId: writer_id, tableId: table_id, column: column }.reverse_merge(optionals).to_json) do |result|
169
+ Job.new(result).ok?
170
+ end
171
+ end
172
+
173
+ # Bulk update table column configuration
174
+ def bulk_update_table_column(writer_id, table_id, columns = [])
175
+ handle @client.post("tables", { writerId: writer_id, tableId: table_id, columns: columns }.to_json) do |result|
176
+ Job.new(result).ok?
177
+ end
178
+ end
179
+
180
+ # Upload selected table to GoodData
181
+ def upload_table(writer_id, table_id, optionals: {}, async: true)
182
+ handle @client.post("upload-table", { writerId: writer_id, tableId: table_id }.reverse_merge(optionals).to_json) do |result|
183
+ async ? Job.new(result) : wait_for_job(result["id"])
184
+ end
185
+ end
186
+
187
+ # Update model of selected table in GoodData
188
+ def update_table_model(writer_id, optionals: {}, async: true)
189
+ handle @client.post("update-model", { writerId: writer_id }.reverse_merge(optionals).to_json) do |result|
190
+ async ? Job.new(result) : wait_for_job(result["id"])
191
+ end
192
+ end
193
+ alias_method :update_model, :update_table_model
194
+
195
+ # Remove dataset in GoodData project belonging to the table and reset it's export status
196
+ def reset_table(writer_id, table_id, optionals: {}, async: true)
197
+ handle @client.post("reset-table", { writerId: writer_id, tableId: table_id }.reverse_merge(optionals).to_json) do |result|
198
+ async ? Job.new(result) : wait_for_job(result["id"])
199
+ end
200
+ end
201
+
202
+ # Selectively upload date dimension (must be already configured in Writer)
203
+ def upload_date_dimension(writer_id, name, optionals: {}, async: true)
204
+ handle @client.post("reset-table", { writerId: writer_id, name: name }.reverse_merge(optionals).to_json) do |result|
205
+ async ? Job.new(result) : wait_for_job(result["id"])
206
+ end
207
+ end
208
+
209
+
210
+ # === Commands for loading data into GoodData
211
+
212
+ # Load data to selected tables in GoodData
213
+ def load_data(writer_id, optionals: {}, async: true)
214
+ handle @client.post("load-data", { writerId: writer_id }.reverse_merge(optionals).to_json) do |result|
215
+ async ? Job.new(result) : wait_for_job(result["id"])
216
+ end
217
+ end
218
+
219
+ # Load data to selected tables in GoodData concurrently
220
+ def load_data_multi(writer_id, optionals: {}, async: true)
221
+ handle @client.post("load-data-multi", { writerId: writer_id }.reverse_merge(optionals).to_json) do |result|
222
+ async ? Job.new(result) : wait_for_job(result["id"])
223
+ end
224
+ end
225
+
226
+ # === Filter resource
227
+
228
+ def filters(writer_id, optionals: {})
229
+ handle @client.get("filters", { writerId: writer_id }.reverse_merge(optionals)) do |result|
230
+ result["filters"]
231
+ end
232
+ end
233
+
234
+ def create_filter(writer_id, project_id, name, attribute, value, operator = '=', optionals: {}, async: true)
235
+ required = { writerId: writer_id, pid: project_id, name: name, attribute: attribute, operator: operator, value: value }
236
+ handle @client.post("filters", required.reverse_merge(optionals).to_json) do |result|
237
+ async ? Job.new(result) : wait_for_job(result["id"])
238
+ end
239
+ end
240
+
241
+ def delete_filter(writer_id, name, async: true)
242
+ handle @client.delete("filters", { writerId: writer_id, name: name }) do |result|
243
+ async ? Job.new(result) : wait_for_job(result["id"])
244
+ end
245
+ end
246
+
247
+
248
+ # === Commands for manipulation with GoodData's Mandatory User Filters
249
+
250
+ # Get Filters for Projects
251
+ def filters_projects(writer_id, optionals: {})
252
+ handle @client.get("filters-projects", { writerId: writer_id }.reverse_merge(optionals)) do |result|
253
+ result["filters"]
254
+ end
255
+ end
256
+
257
+ # Get Filters for Users
258
+ def filters_users(writer_id, optionals: {})
259
+ handle @client.get("filters-users", { writerId: writer_id }.reverse_merge(optionals)) do |result|
260
+ result["filters"]
261
+ end
262
+ end
263
+
264
+ # Assign Filter to User
265
+ def assign_filters_users(writer_id, email, filters = [], optionals: {}, async: true)
266
+ required = { writerId: writer_id, email: email, filters: filters }
267
+ handle @client.post("filters-users", required.reverse_merge(optionals).to_json) do |result|
268
+ async ? Job.new(result) : wait_for_job(result["id"])
269
+ end
270
+ end
271
+
272
+ # Synchronizes filters in GoodData project according to writer's configuration
273
+ def sync_filters(writer_id, optionals: {}, async: true)
274
+ handle @client.post("sync-filters", { writerId: writer_id }.reverse_merge(optionals).to_json) do |result|
275
+ async ? Job.new(result) : wait_for_job(result["id"])
276
+ end
277
+ end
278
+
279
+
280
+ # === Various GoodData commands
281
+
282
+ # Execute selected reports in GoodData
283
+ def execute_reports(writer_id, project_id, optionals: {}, async: true)
284
+ handle @client.post("execute-reports", { writerId: writer_id, pid: project_id }.reverse_merge(optionals).to_json) do |result|
285
+ async ? Job.new(result) : wait_for_job(result["id"])
286
+ end
287
+ end
288
+
289
+ # Call to obtain an SSO link for user
290
+ def sso(writer_id, project_id, email, optionals: {})
291
+ handle @client.get("sso", { writerId: writer_id, pid: project_id, email: email }.reverse_merge(optionals)) do |result|
292
+ result["ssoLink"]
293
+ end
294
+ end
295
+
296
+ # Simple proxy for direct calls to GoodData API
297
+ def proxy(method, writer_id, query, optionals: {}, async: true)
298
+ required = { writerId: writer_id, query: query }
299
+ case method.to_sym
300
+ when :get
301
+ handle @client.get("proxy", required.reverse_merge(optionals))
302
+ when :post
303
+ handle @client.post("proxy", required.reverse_merge(optionals).to_json) do |result|
304
+ async ? Job.new(result) : wait_for_job(result["id"])
305
+ end
306
+ end
307
+ end
308
+
309
+
310
+ # === Commands for jobs handling in Syrup Queue API
311
+
312
+ # Return list of jobs for given writer
313
+ def jobs(writer_id)
314
+ handle @queue.get("jobs", { q: "+params.writerId:#{writer_id}", limit: 50 }) do |result|
315
+ result.map { |hash| Job.new(hash) }
316
+ end
317
+ end
318
+
319
+ # Return detail of given job
320
+ def job(job_id)
321
+ handle @queue.get("jobs/#{job_id}") do |result|
322
+ Job.new(result)
323
+ end
324
+ end
325
+
326
+ # Ask repeatedly for job status until it is finished
327
+ def wait_for_job(job_id)
328
+ begin
329
+ job = job(job_id)
330
+ sleep 5 unless job.finished?
331
+ end until job.finished?
332
+ job
333
+ end
334
+
335
+
336
+ private
337
+
338
+ # Properly handle response of API call request
339
+ def handle(response)
340
+ result = case response.status_type
341
+ when :success # Is this a 2xx response?
342
+ @parser.parse(response.body)
343
+
344
+ when :redirection # Is this a 3xx redirect?
345
+ @parser.parse(response.body)
346
+
347
+ when :client_error # Is this is a 4xx response?
348
+ raise ClientError.new(response)
349
+
350
+ when :server_error # Is this a 5xx response?
351
+ raise ServerError.new(response)
352
+
353
+ else
354
+ raise ResponseError.new(response)
355
+ end
356
+
357
+ block_given? ? yield(result) : result
358
+
359
+ rescue ::Hurley::Error, ::Hurley::Timeout => e
360
+ raise ResponseError.new(e)
361
+ end
362
+ end
363
+
364
+ end
365
+ end
@@ -0,0 +1,18 @@
1
+ require "hurley"
2
+
3
+ module Keboola
4
+ module GoodDataWriter
5
+
6
+ class Client < ::Hurley::Client
7
+ def self.factory(endpoint:, token:)
8
+ client = new(endpoint)
9
+ client.header[:x_storageapi_token] = token
10
+ client.header[:accept] = "application/json"
11
+ client.request_options.redirection_limit = 5
12
+ client.request_options.timeout = 5
13
+ client
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,46 @@
1
+ module Keboola
2
+ module GoodDataWriter
3
+
4
+ class Error < RuntimeError; end
5
+ class ParsingError < Error; end
6
+
7
+ class ResponseError < Error
8
+ attr_reader :response
9
+
10
+ def initialize(ex, response = nil)
11
+ @wrapped_exception, @response = nil, response
12
+
13
+ if ex.respond_to?(:backtrace)
14
+ super(ex.message)
15
+ @wrapped_exception = ex
16
+
17
+ elsif ex.respond_to?(:status_code)
18
+ begin
19
+ # try to parse response body to retreive more detailed API error message
20
+ result = Parser.parse(ex.body)
21
+ super("the server responded with status #{ex.status_code} and message '#{result['message']}'")
22
+ rescue ParsingError
23
+ super("the server responded with status #{ex.status_code}")
24
+ ensure
25
+ @response = ex
26
+ end
27
+
28
+ else
29
+ super(ex.to_s)
30
+ end
31
+ end
32
+
33
+ def backtrace
34
+ @wrapped_exception ? @wrapped_exception.backtrace : super
35
+ end
36
+
37
+ def inspect
38
+ %(#<#{self.class}: #{@wrapped_exception.class}>)
39
+ end
40
+ end
41
+
42
+ class ClientError < ResponseError; end
43
+ class ServerError < ResponseError; end
44
+
45
+ end
46
+ end
@@ -0,0 +1,25 @@
1
+ require "ostruct"
2
+
3
+ module Keboola
4
+ module GoodDataWriter
5
+
6
+ class Job < OpenStruct
7
+ def ok?
8
+ %w[ok].include?(status)
9
+ end
10
+
11
+ def success?
12
+ %w[success].include?(status)
13
+ end
14
+
15
+ def finished?
16
+ %w[cancelled success error warning terminated].include?(status)
17
+ end
18
+
19
+ def pending?
20
+ %w[waiting processing terminating].include?(status)
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ require "json"
2
+
3
+ module Keboola
4
+ module GoodDataWriter
5
+
6
+ class Parser
7
+ def self.parse(string)
8
+ begin
9
+ JSON.parse(string)
10
+ rescue JSON::ParserError, TypeError => e
11
+ raise ParsingError.new(e.message)
12
+ end
13
+ end
14
+
15
+ def parse(string)
16
+ self.class.parse(string)
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ module Keboola
2
+ module GoodDataWriter
3
+ VERSION = "2.0.0.pre1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: keboola-gooddata-writer
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0.pre1
5
+ platform: ruby
6
+ authors:
7
+ - Roman Sklenář
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-19 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.8'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
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: '5.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
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: vcr
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.9'
83
+ - !ruby/object:Gem::Dependency
84
+ name: codeclimate-test-reporter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: json
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: hurley
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.1'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.1'
125
+ description: Use the Keboola::GoodDataWriter class to integrate Keboola GoodData Writer
126
+ into your own application.
127
+ email:
128
+ - mail@romansklenar.cz
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".gitignore"
134
+ - ".travis.yml"
135
+ - Gemfile
136
+ - LICENSE.txt
137
+ - README.md
138
+ - Rakefile
139
+ - bin/console
140
+ - bin/setup
141
+ - keboola-gooddata-writer.gemspec
142
+ - lib/keboola/core_ext.rb
143
+ - lib/keboola/core_ext/hash.rb
144
+ - lib/keboola/gooddata_writer.rb
145
+ - lib/keboola/gooddata_writer/api.rb
146
+ - lib/keboola/gooddata_writer/client.rb
147
+ - lib/keboola/gooddata_writer/errors.rb
148
+ - lib/keboola/gooddata_writer/job.rb
149
+ - lib/keboola/gooddata_writer/parser.rb
150
+ - lib/keboola/gooddata_writer/version.rb
151
+ homepage: https://github.com/romansklenar/gooddata-writer-ruby-client
152
+ licenses:
153
+ - MIT
154
+ metadata: {}
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">"
167
+ - !ruby/object:Gem::Version
168
+ version: 1.3.1
169
+ requirements: []
170
+ rubyforge_project:
171
+ rubygems_version: 2.2.2
172
+ signing_key:
173
+ specification_version: 4
174
+ summary: A convenient Ruby wrapper around the Keboola GoodData Writer API.
175
+ test_files: []