gooddata 0.2.0
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.
- data/LICENSE +22 -0
- data/README.rdoc +97 -0
- data/VERSION +1 -0
- data/bin/gooddata +13 -0
- data/bin/igd.rb +33 -0
- data/lib/gooddata.rb +3 -0
- data/lib/gooddata/client.rb +196 -0
- data/lib/gooddata/command.rb +75 -0
- data/lib/gooddata/commands/api.rb +37 -0
- data/lib/gooddata/commands/auth.rb +74 -0
- data/lib/gooddata/commands/base.rb +80 -0
- data/lib/gooddata/commands/datasets.rb +228 -0
- data/lib/gooddata/commands/help.rb +104 -0
- data/lib/gooddata/commands/profile.rb +9 -0
- data/lib/gooddata/commands/projects.rb +51 -0
- data/lib/gooddata/commands/version.rb +7 -0
- data/lib/gooddata/connection.rb +220 -0
- data/lib/gooddata/extract.rb +13 -0
- data/lib/gooddata/helpers.rb +18 -0
- data/lib/gooddata/model.rb +558 -0
- data/lib/gooddata/models/links.rb +42 -0
- data/lib/gooddata/models/metadata.rb +82 -0
- data/lib/gooddata/models/profile.rb +32 -0
- data/lib/gooddata/models/project.rb +136 -0
- data/lib/gooddata/version.rb +3 -0
- data/test/helper.rb +10 -0
- data/test/test_commands.rb +85 -0
- data/test/test_guessing.rb +46 -0
- data/test/test_model.rb +63 -0
- data/test/test_rest_api_basic.rb +41 -0
- data/test/test_upload.rb +52 -0
- metadata +202 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
(BSD License)
|
2
|
+
|
3
|
+
Copyright (c) 2010 Thomas Watson Steen & GoodData Corporation. All rights reserved.
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided
|
6
|
+
that the following conditions are met:
|
7
|
+
|
8
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and
|
9
|
+
the following disclaimer.
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions
|
11
|
+
and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
12
|
+
* Neither the name of the GoodData Corporation nor the names of its contributors may be used to endorse
|
13
|
+
or promote products derived from this software without specific prior written permission.
|
14
|
+
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
|
16
|
+
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
17
|
+
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
18
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
19
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
20
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
21
|
+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
22
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
= GoodData Ruby wrapper and CLI
|
2
|
+
|
3
|
+
A convenient Ruby wrapper around the GoodData RESTful API.
|
4
|
+
|
5
|
+
Use the Gooddata::Client class to integrate GoodData into your own application
|
6
|
+
or use the CLI to work with GoodData directly from the command line.
|
7
|
+
|
8
|
+
The best documentation for the API can be found using these resources:
|
9
|
+
* http://developer.gooddata.com/api
|
10
|
+
* https://secure.gooddata.com/gdc
|
11
|
+
|
12
|
+
== Usage
|
13
|
+
|
14
|
+
Since this project is in its early stages, no gem have been released to rubygems.org yet.
|
15
|
+
To install the gooddata gem, you instead have to first checkout this repository. Then install
|
16
|
+
the gem using the rake task:
|
17
|
+
|
18
|
+
rake install
|
19
|
+
|
20
|
+
Now you are ready to use either the Ruby wrapper class or the CLI.
|
21
|
+
|
22
|
+
Alternatively you can run the source code directly. For details about this approach, see {the
|
23
|
+
Development Wiki page}[http://github.com/gooddata/gooddata-ruby/wiki/Development].
|
24
|
+
|
25
|
+
=== Wrapper class usage
|
26
|
+
|
27
|
+
For details of how to use the Ruby wrapper class, see the Gooddata::Client RDoc.
|
28
|
+
|
29
|
+
=== CLI Usage
|
30
|
+
|
31
|
+
After installing the gooddata gem, GoodData is available from your command line using
|
32
|
+
the <tt>gooddata</tt> command. To get a complete overview of possible options type:
|
33
|
+
|
34
|
+
gooddata help
|
35
|
+
|
36
|
+
The examples and descriptions below does not cover all the options available via the CLI.
|
37
|
+
So remember to refer back to the <tt>help</tt> command.
|
38
|
+
|
39
|
+
Before you do anything else, a good idea is to see if your account is set up correctly and
|
40
|
+
that you can log in. To do this, use the <tt>api:test</tt> command:
|
41
|
+
|
42
|
+
gooddata api:test
|
43
|
+
|
44
|
+
==== Authentication
|
45
|
+
|
46
|
+
As you saw if you ran the above test command <tt>gooddata</tt> will prompt you
|
47
|
+
for your GoodData username and password. If you don't wish to write your
|
48
|
+
credentials each time you connect to GoodData using <tt>gooddata</tt>, you can
|
49
|
+
create a simple gooddata credentials file called <tt>.gooddata</tt> in the root
|
50
|
+
of your home directory. To make it easy you can just run the credentials file
|
51
|
+
generator command which will create the file for you:
|
52
|
+
|
53
|
+
gooddata auth:store
|
54
|
+
|
55
|
+
==== List available projects
|
56
|
+
|
57
|
+
To get a list of projects available to your GoodData user account, run:
|
58
|
+
|
59
|
+
gooddata projects
|
60
|
+
|
61
|
+
The output from the above command will look similar to this:
|
62
|
+
|
63
|
+
521 Some project
|
64
|
+
3521 Some other project
|
65
|
+
3642 Some third project
|
66
|
+
|
67
|
+
The first column contains the project-key. You need this if you wan't to either
|
68
|
+
see more details about the project using the <tt>projects:show</tt> comamnd or
|
69
|
+
if you wish to delete the project using the <tt>projects:delete</tt> command.
|
70
|
+
|
71
|
+
==== Create a new project
|
72
|
+
|
73
|
+
To create a new project under on the GoodData servers, run:
|
74
|
+
|
75
|
+
gooddata projects:create
|
76
|
+
|
77
|
+
You will then be asked about the desired project name and summary before it's created.
|
78
|
+
|
79
|
+
== Note on Patches/Pull Requests
|
80
|
+
|
81
|
+
* Fork the project.
|
82
|
+
* Make your feature addition or bug fix.
|
83
|
+
* Add tests for it. This is important so I don't break it in a
|
84
|
+
future version unintentionally.
|
85
|
+
* Commit, do not mess with rakefile, version, or history.
|
86
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself we can ignore when we pull)
|
87
|
+
* Send us a pull request. Bonus points for topic branches.
|
88
|
+
|
89
|
+
== Credits
|
90
|
+
|
91
|
+
This project is developed and maintained by Pavel Kolesnikov [ mailto:pavel@gooddata.com
|
92
|
+
/ {@koles}[http:/twitter.com/koles] ] and Thomas Watson Steen [ mailto:w@tson.dk /
|
93
|
+
{@wa7son}[http://twitter.com/wa7son] ]
|
94
|
+
|
95
|
+
== Copyright
|
96
|
+
|
97
|
+
Copyright (c) 2010 - 2011 GoodData Corporation and Thomas Watson Steen. See LICENSE for details.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/bin/gooddata
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
require 'gooddata'
|
6
|
+
require 'gooddata/command'
|
7
|
+
|
8
|
+
args = ARGV.dup
|
9
|
+
ARGV.clear
|
10
|
+
command = args.shift.strip rescue 'help'
|
11
|
+
|
12
|
+
GoodData.logger = Logger.new(STDOUT) if ENV['DEBUG']
|
13
|
+
GoodData::Command.run command, args
|
data/bin/igd.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'gooddata'
|
4
|
+
require 'gooddata/command'
|
5
|
+
|
6
|
+
require 'irb'
|
7
|
+
|
8
|
+
include GoodData
|
9
|
+
|
10
|
+
module IRB
|
11
|
+
def IRB.start2(bind, ap_path)
|
12
|
+
IRB.setup(ap_path)
|
13
|
+
irb = Irb.new(WorkSpace.new(bind))
|
14
|
+
@CONF[:MAIN_CONTEXT] = irb.context
|
15
|
+
trap("SIGINT") do
|
16
|
+
irb.signal_handle
|
17
|
+
end
|
18
|
+
catch(:IRB_EXIT) do
|
19
|
+
irb.eval_input
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
param = ARGV.shift if ARGV.length
|
25
|
+
if param == '--debug' then
|
26
|
+
require 'logger'
|
27
|
+
GoodData.logger = Logger.new STDOUT
|
28
|
+
end
|
29
|
+
Command.connect
|
30
|
+
puts "Logged into GoodData as #{GoodData.profile.user} (#{GoodData.profile['login']})"
|
31
|
+
puts
|
32
|
+
IRB::start2 binding, $STDIN
|
33
|
+
puts "Logging out"
|
data/lib/gooddata.rb
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
require 'gooddata/version'
|
2
|
+
require 'gooddata/connection'
|
3
|
+
Dir[File.dirname(__FILE__) + '/models/*.rb'].each { |file| require file }
|
4
|
+
Dir[File.dirname(__FILE__) + '/collections/*.rb'].each { |file| require file }
|
5
|
+
|
6
|
+
# = GoodData API wrapper
|
7
|
+
#
|
8
|
+
# A convenient Ruby wrapper around the GoodData RESTful API.
|
9
|
+
#
|
10
|
+
# The best documentation for the API can be found using these resources:
|
11
|
+
# * http://developer.gooddata.com/api
|
12
|
+
# * https://secure.gooddata.com/gdc
|
13
|
+
#
|
14
|
+
# == Usage
|
15
|
+
#
|
16
|
+
# To communicate with the API you first need a personal GoodData account.
|
17
|
+
# {Sign up here}[https://secure.gooddata.com/registration.html] if you havent already.
|
18
|
+
#
|
19
|
+
# Now it is just a matter of initializing the GoodData connection via the connect
|
20
|
+
# method:
|
21
|
+
#
|
22
|
+
# GoodData.connect 'gooddata_user', 'gooddata_password'
|
23
|
+
#
|
24
|
+
# This GoodData object can now be utalized to retrieve your GoodData profile, the available
|
25
|
+
# projects etc.
|
26
|
+
#
|
27
|
+
# == Logging
|
28
|
+
#
|
29
|
+
# GoodData.logger = Logger.new(STDOUT)
|
30
|
+
#
|
31
|
+
# For details about the logger options and methods, see the
|
32
|
+
# {Logger module documentation}[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc].
|
33
|
+
#
|
34
|
+
module GoodData
|
35
|
+
module Threaded
|
36
|
+
# Used internally for thread safety
|
37
|
+
def threaded
|
38
|
+
Thread.current[:goooddata] ||= {}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class NilLogger
|
43
|
+
def debug(*args) ; end
|
44
|
+
alias :info :debug
|
45
|
+
alias :warn :debug
|
46
|
+
alias :error :debug
|
47
|
+
end
|
48
|
+
|
49
|
+
def project=(project)
|
50
|
+
GoodData.project = project
|
51
|
+
GoodData.project
|
52
|
+
end
|
53
|
+
alias :use :project=
|
54
|
+
|
55
|
+
class << self
|
56
|
+
include Threaded
|
57
|
+
|
58
|
+
RELEASE_INFO_PATH = '/gdc/releaseInfo'
|
59
|
+
|
60
|
+
def version
|
61
|
+
VERSION
|
62
|
+
end
|
63
|
+
|
64
|
+
def gem_version_string
|
65
|
+
"gooddata-gem/#{version}"
|
66
|
+
end
|
67
|
+
|
68
|
+
# Connect to the GoodData API
|
69
|
+
#
|
70
|
+
# === Parameters
|
71
|
+
#
|
72
|
+
# * +user+ - A GoodData username
|
73
|
+
# * +password+ - A GoodData password
|
74
|
+
#
|
75
|
+
def connect(user, password, url = nil)
|
76
|
+
threaded[:connection] = Connection.new user, password, url
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns the active GoodData connection earlier initialized via
|
80
|
+
# GoodData.connect call
|
81
|
+
#
|
82
|
+
# @see GoodData.connect
|
83
|
+
#
|
84
|
+
def connection
|
85
|
+
threaded[:connection]
|
86
|
+
end
|
87
|
+
|
88
|
+
# Sets the active project
|
89
|
+
#
|
90
|
+
# === Parameters
|
91
|
+
#
|
92
|
+
# * +project+ - a project identifier
|
93
|
+
#
|
94
|
+
# === Examples
|
95
|
+
#
|
96
|
+
# The following calls are equivalent:
|
97
|
+
# * GoodData.project = 'afawtv356b6usdfsdf34vt'
|
98
|
+
# * GoodData.use 'afawtv356b6usdfsdf34vt'
|
99
|
+
# * GoodData.use '/gdc/projects/afawtv356b6usdfsdf34vt'
|
100
|
+
# * GoodData.project = Project['afawtv356b6usdfsdf34vt']
|
101
|
+
#
|
102
|
+
def project=(project)
|
103
|
+
if project.is_a? Project
|
104
|
+
threaded[:project] = project
|
105
|
+
else
|
106
|
+
threaded[:project] = Project[project]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
alias :use :project=
|
111
|
+
|
112
|
+
# Returns the active project
|
113
|
+
#
|
114
|
+
def project
|
115
|
+
threaded[:project]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Performs a HTTP GET request.
|
119
|
+
#
|
120
|
+
# Retuns the JSON response formatted as a Hash object.
|
121
|
+
#
|
122
|
+
# === Parameters
|
123
|
+
#
|
124
|
+
# * +path+ - The HTTP path on the GoodData server (must be prefixed with a forward slash)
|
125
|
+
# === Examples
|
126
|
+
#
|
127
|
+
# GoodData.get '/gdc/projects'
|
128
|
+
def get(path)
|
129
|
+
connection.get(path)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Performs a HTTP POST request.
|
133
|
+
#
|
134
|
+
# Retuns the JSON response formatted as a Hash object.
|
135
|
+
#
|
136
|
+
# === Parameters
|
137
|
+
#
|
138
|
+
# * +path+ - The HTTP path on the GoodData server (must be prefixed with a forward slash)
|
139
|
+
# * +data+ - The payload data in the format of a Hash object
|
140
|
+
#
|
141
|
+
# === Examples
|
142
|
+
#
|
143
|
+
# GoodData.post '/gdc/projects', { ... }
|
144
|
+
def post(path, data)
|
145
|
+
connection.post path, data
|
146
|
+
end
|
147
|
+
|
148
|
+
# Performs a HTTP DELETE request.
|
149
|
+
#
|
150
|
+
# Retuns the JSON response formatted as a Hash object.
|
151
|
+
#
|
152
|
+
# === Parameters
|
153
|
+
#
|
154
|
+
# * +path+ - The HTTP path on the GoodData server (must be prefixed with a forward slash)
|
155
|
+
#
|
156
|
+
# === Examples
|
157
|
+
#
|
158
|
+
# GoodData.delete '/gdc/project/1'
|
159
|
+
def delete(path)
|
160
|
+
connection.delete path
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_login
|
164
|
+
connection.connect!
|
165
|
+
connection.logged_in?
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns the currently logged in user Profile.
|
169
|
+
def profile
|
170
|
+
threaded[:profile] ||= Profile.load
|
171
|
+
end
|
172
|
+
|
173
|
+
# Returns information about the GoodData API as a Hash (e.g. version, release time etc.)
|
174
|
+
def release_info
|
175
|
+
@release_info ||= @connection.get(RELEASE_INFO_PATH)['release']
|
176
|
+
end
|
177
|
+
|
178
|
+
# Returns the logger instance. The default implementation
|
179
|
+
# does not log anything
|
180
|
+
# For some serious logging, set the logger instance using
|
181
|
+
# the logger= method
|
182
|
+
#
|
183
|
+
# === Example
|
184
|
+
#
|
185
|
+
# require 'logger'
|
186
|
+
# GoodData.logger = Logger.new(STDOUT)
|
187
|
+
def logger
|
188
|
+
@logger ||= NilLogger.new
|
189
|
+
end
|
190
|
+
|
191
|
+
# Sets the logger instance
|
192
|
+
def logger=(logger)
|
193
|
+
@logger = logger
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'gooddata/helpers'
|
3
|
+
require 'gooddata/commands/base'
|
4
|
+
Dir[File.dirname(__FILE__) + '/commands/*.rb'].each { |file| require file unless file =~ /\/base.rb$/ }
|
5
|
+
|
6
|
+
module GoodData::Command
|
7
|
+
class InvalidCommand < RuntimeError; end
|
8
|
+
class InvalidOption < RuntimeError; end
|
9
|
+
class CommandFailed < RuntimeError; end
|
10
|
+
|
11
|
+
extend GoodData::Helpers
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def run(command, args)
|
15
|
+
begin
|
16
|
+
run_internal command, args.dup
|
17
|
+
rescue InvalidCommand
|
18
|
+
error "Unknown command. Run 'gooddata help' for usage information."
|
19
|
+
rescue InvalidOption
|
20
|
+
error "Unknown option."
|
21
|
+
rescue RestClient::Unauthorized
|
22
|
+
error "Authentication failure"
|
23
|
+
rescue RestClient::ResourceNotFound => e
|
24
|
+
error extract_not_found(e.http_body)
|
25
|
+
rescue RestClient::RequestFailed => e
|
26
|
+
error extract_error(e.http_body)
|
27
|
+
rescue RestClient::RequestTimeout
|
28
|
+
error "API request timed out. Please try again, or contact support@gooddata.com if this issue persists."
|
29
|
+
rescue CommandFailed => e
|
30
|
+
error e.message
|
31
|
+
rescue Interrupt => e
|
32
|
+
error "\n[canceled]"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def run_internal(command, args)
|
37
|
+
klass, method = parse(command)
|
38
|
+
runner = klass.new(args)
|
39
|
+
raise InvalidCommand unless runner.respond_to?(method)
|
40
|
+
runner.send(method)
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse(command)
|
44
|
+
parts = command.split(':')
|
45
|
+
|
46
|
+
parts << :index if parts.size == 1
|
47
|
+
raise InvalidCommand if parts.size > 2
|
48
|
+
|
49
|
+
begin
|
50
|
+
return GoodData::Command.const_get(parts[0].capitalize), parts[1]
|
51
|
+
rescue NameError
|
52
|
+
raise InvalidCommand
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def extract_not_found(body)
|
57
|
+
body =~ /^[\w\s]+ not found$/ ? body : "Resource not found"
|
58
|
+
end
|
59
|
+
|
60
|
+
def extract_error(body)
|
61
|
+
msg = parse_error_json(body) || 'Internal server error'
|
62
|
+
msg.split("\n").map { |line| ' ! ' + line }.join("\n")
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_error_json(body)
|
66
|
+
begin
|
67
|
+
error = JSON.parse(body.to_s)['error']
|
68
|
+
return error['message'] if !error['parameters']
|
69
|
+
return error['message'] % error['parameters'] rescue error
|
70
|
+
rescue
|
71
|
+
return body
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|