gooddata 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|