gooddata 0.6.0.pre6 → 0.6.0.pre7
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/gooddata +102 -56
- data/gooddata.gemspec +3 -0
- data/lib/gooddata/bricks/base_downloader.rb +62 -0
- data/lib/gooddata/bricks/brick.rb +24 -25
- data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +38 -0
- data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +4 -2
- data/lib/gooddata/bricks/middleware/restforce_middleware.rb +9 -10
- data/lib/gooddata/client.rb +21 -1
- data/lib/gooddata/commands/auth.rb +68 -60
- data/lib/gooddata/commands/process.rb +75 -62
- data/lib/gooddata/commands/runners.rb +9 -6
- data/lib/gooddata/connection.rb +45 -20
- data/lib/gooddata/helpers.rb +31 -4
- data/lib/gooddata/model.rb +199 -72
- data/lib/gooddata/models/data_result.rb +94 -101
- data/lib/gooddata/models/metadata.rb +31 -9
- data/lib/gooddata/models/metric.rb +1 -1
- data/lib/gooddata/models/process.rb +2 -59
- data/lib/gooddata/models/project.rb +1 -1
- data/lib/gooddata/models/project_metadata.rb +10 -1
- data/lib/gooddata/models/report.rb +0 -3
- data/lib/gooddata/models/report_definition.rb +43 -8
- data/lib/gooddata/version.rb +1 -1
- data/spec/full_project_spec.rb +54 -0
- data/spec/goodzilla_spec.rb +2 -2
- data/spec/model_dsl_spec.rb +2 -1
- data/spec/test_project_model_spec.json +86 -0
- metadata +53 -2
- data/TODO.md +0 -77
@@ -28,12 +28,20 @@ module GoodData
|
|
28
28
|
self[:all].find_all {|r| r["tags"].split(",").include?(tag)}
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
32
|
-
|
33
|
-
self[
|
31
|
+
def get_by_id(id)
|
32
|
+
uri = GoodData::MdObject.id_to_uri(id)
|
33
|
+
self[uri] unless uri.nil?
|
34
34
|
end
|
35
35
|
|
36
|
-
|
36
|
+
def find_first_by_title(title)
|
37
|
+
all = self[:all]
|
38
|
+
item = if title.is_a?(Regexp)
|
39
|
+
all.find {|r| r["title"] =~ title}
|
40
|
+
else
|
41
|
+
all.find {|r| r["title"] == title}
|
42
|
+
end
|
43
|
+
self[item["link"]] unless item.nil?
|
44
|
+
end
|
37
45
|
|
38
46
|
def identifier_to_uri(id)
|
39
47
|
raise NoProjectError.new "Connect to a project before searching for an object" unless GoodData.project
|
@@ -45,6 +53,9 @@ module GoodData
|
|
45
53
|
response['identifiers'][0]['uri']
|
46
54
|
end
|
47
55
|
end
|
56
|
+
|
57
|
+
alias :id_to_uri :identifier_to_uri
|
58
|
+
|
48
59
|
end
|
49
60
|
|
50
61
|
def initialize(json)
|
@@ -52,7 +63,18 @@ module GoodData
|
|
52
63
|
end
|
53
64
|
|
54
65
|
def delete
|
55
|
-
|
66
|
+
if saved?
|
67
|
+
GoodData.delete(uri)
|
68
|
+
meta.delete("uri")
|
69
|
+
# ["uri"] = nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def refresh
|
74
|
+
if saved?
|
75
|
+
@json = GoodData.get(uri)
|
76
|
+
end
|
77
|
+
self
|
56
78
|
end
|
57
79
|
|
58
80
|
def obj_id
|
@@ -64,7 +86,7 @@ module GoodData
|
|
64
86
|
end
|
65
87
|
|
66
88
|
def uri
|
67
|
-
meta['uri']
|
89
|
+
meta && meta['uri']
|
68
90
|
end
|
69
91
|
|
70
92
|
def browser_uri
|
@@ -100,11 +122,11 @@ module GoodData
|
|
100
122
|
end
|
101
123
|
|
102
124
|
def meta
|
103
|
-
data['meta']
|
125
|
+
data && data['meta']
|
104
126
|
end
|
105
127
|
|
106
128
|
def content
|
107
|
-
data['content']
|
129
|
+
data && data['content']
|
108
130
|
end
|
109
131
|
|
110
132
|
def project
|
@@ -158,4 +180,4 @@ module GoodData
|
|
158
180
|
true
|
159
181
|
end
|
160
182
|
end
|
161
|
-
end
|
183
|
+
end
|
@@ -7,68 +7,11 @@ module GoodData
|
|
7
7
|
class << self
|
8
8
|
def [](id)
|
9
9
|
if id == :all
|
10
|
-
GoodData.get("/gdc/projects/
|
10
|
+
GoodData.get("/gdc/projects/#{GoodData.project.pid}/dataload/processes")
|
11
11
|
else
|
12
|
-
self.new(GoodData.get("/gdc/projects/
|
12
|
+
self.new(GoodData.get("/gdc/projects/#{GoodData.project.pid}/dataload/processes/#{id}"))
|
13
13
|
end
|
14
14
|
end
|
15
|
-
|
16
|
-
def deploy(dir, options={}, &block)
|
17
|
-
if block
|
18
|
-
begin
|
19
|
-
res = deploy_graph(dir, options)
|
20
|
-
block.call(res)
|
21
|
-
ensure
|
22
|
-
self_link = res["process"]["links"]["self"]
|
23
|
-
GoodData.delete(self_link)
|
24
|
-
end
|
25
|
-
else
|
26
|
-
deploy_graph(dir, options)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def deploy_graph(dir, options={})
|
31
|
-
dir = Pathname(dir)
|
32
|
-
fail "Provided path (#{dir}) is not directory." unless dir.directory?
|
33
|
-
type = options[:type] || "ETL"
|
34
|
-
|
35
|
-
deploy_name = options[:name] || options[:project_name]
|
36
|
-
verbose = options[:verbose] || false
|
37
|
-
project_pid = 'hi95siviyangv53c3vptkz1eas546pnn'
|
38
|
-
|
39
|
-
puts HighLine::color("Deploying #{dir}", HighLine::BOLD) if verbose
|
40
|
-
res = nil
|
41
|
-
|
42
|
-
Tempfile.open("deploy-graph-archive") do |temp|
|
43
|
-
Zip::OutputStream.open(temp.path) do |zio|
|
44
|
-
Dir.glob(dir + "**/*") do |item|
|
45
|
-
puts "including #{item}" if verbose
|
46
|
-
unless File.directory?(item)
|
47
|
-
zio.put_next_entry(item)
|
48
|
-
zio.print IO.read(item)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
GoodData.connection.upload(temp.path)
|
54
|
-
process_id = options[:process]
|
55
|
-
|
56
|
-
data = {
|
57
|
-
:process => {
|
58
|
-
:name => deploy_name,
|
59
|
-
:path => "/uploads/#{File.basename(temp.path)}",
|
60
|
-
:type => type
|
61
|
-
}
|
62
|
-
}
|
63
|
-
res = if process_id.nil?
|
64
|
-
GoodData.post("/gdc/projects/#{project_pid}/dataload/processes", data)
|
65
|
-
else
|
66
|
-
GoodData.put("/gdc/projects/#{project_pid}/dataload/processes/#{process_id}", data)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
puts HighLine::color("Deploy DONE #{dir}", HighLine::BOLD) if verbose
|
70
|
-
res
|
71
|
-
end
|
72
15
|
end
|
73
16
|
|
74
17
|
def initialize(data)
|
@@ -2,15 +2,24 @@ module GoodData
|
|
2
2
|
class ProjectMetadata
|
3
3
|
|
4
4
|
class << self
|
5
|
+
|
6
|
+
def keys
|
7
|
+
ProjectMetadata[:all].keys
|
8
|
+
end
|
9
|
+
|
5
10
|
def [](key)
|
6
11
|
if key == :all
|
7
|
-
GoodData.get("/gdc/projects/#{GoodData.project.pid}/dataload/metadata")
|
12
|
+
res = GoodData.get("/gdc/projects/#{GoodData.project.pid}/dataload/metadata")
|
13
|
+
res["metadataItems"]["items"].reduce({}) {|memo, i| memo[i["metadataItem"]["key"]] = i["metadataItem"]["value"]; memo}
|
8
14
|
else
|
9
15
|
res = GoodData.get("/gdc/projects/#{GoodData.project.pid}/dataload/metadata/#{key}")
|
10
16
|
res["metadataItem"]["value"]
|
11
17
|
end
|
12
18
|
end
|
13
19
|
|
20
|
+
alias_method :get, :[]
|
21
|
+
alias_method :get_key, :[]
|
22
|
+
|
14
23
|
def has_key?(key)
|
15
24
|
begin
|
16
25
|
ProjectMetadata[key]
|
@@ -57,11 +57,41 @@ module GoodData
|
|
57
57
|
if item.respond_to?(:is_attribute?)
|
58
58
|
item.display_forms.first
|
59
59
|
elsif item.is_a?(String)
|
60
|
-
GoodData::
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
60
|
+
x = GoodData::MdObject.get_by_id(item)
|
61
|
+
fail "Object given by id \"#{item}\" could not be found" if x.nil?
|
62
|
+
case x.raw_data.keys.first.to_s
|
63
|
+
when "attribute"
|
64
|
+
GoodData::Attribute.new(x.raw_data).display_forms.first
|
65
|
+
when "attributeDisplayForm"
|
66
|
+
GoodData::DisplayForm.new(x.raw_data)
|
67
|
+
when "metric"
|
68
|
+
GoodData::Metric.new(x.raw_data)
|
69
|
+
end
|
70
|
+
elsif item.is_a?(Hash) && item.keys.include?(:title)
|
71
|
+
case item[:type].to_s
|
72
|
+
when "metric"
|
73
|
+
GoodData::Metric.find_first_by_title(item[:title])
|
74
|
+
when "attribute"
|
75
|
+
GoodData::Attribute.find_first_by_title(item[:title]).display_forms.first
|
76
|
+
end
|
77
|
+
elsif item.is_a?(Hash) && (item.keys.include?(:id))
|
78
|
+
case item[:type].to_s
|
79
|
+
when "metric"
|
80
|
+
GoodData::Metric.get_by_id(item[:id])
|
81
|
+
when "attribute"
|
82
|
+
GoodData::Attribute.get_by_id(item[:id]).display_forms.first
|
83
|
+
when "label"
|
84
|
+
GoodData::DisplayForm.get_by_id(item[:id])
|
85
|
+
end
|
86
|
+
elsif item.is_a?(Hash) && (item.keys.include?(:identifier))
|
87
|
+
case item[:type].to_s
|
88
|
+
when "metric"
|
89
|
+
GoodData::Metric.get_by_id(item[:identifier])
|
90
|
+
when "attribute"
|
91
|
+
GoodData::Attribute.get_by_id(item[:identifier]).display_forms.first
|
92
|
+
when "label"
|
93
|
+
GoodData::DisplayForm.get_by_id(item[:identifier])
|
94
|
+
end
|
65
95
|
else
|
66
96
|
item
|
67
97
|
end
|
@@ -83,8 +113,8 @@ module GoodData
|
|
83
113
|
rd.save
|
84
114
|
rd.execute
|
85
115
|
ensure
|
86
|
-
rd.delete if rd.saved?
|
87
|
-
unsaved_metrics.each {|m| m.delete if m.saved?}
|
116
|
+
rd.delete if rd && rd.saved?
|
117
|
+
unsaved_metrics.each {|m| m.delete if m && m.saved?}
|
88
118
|
end
|
89
119
|
end
|
90
120
|
|
@@ -126,7 +156,12 @@ module GoodData
|
|
126
156
|
end
|
127
157
|
|
128
158
|
def execute
|
129
|
-
result =
|
159
|
+
result = if saved?
|
160
|
+
GoodData.post '/gdc/xtab2/executor3', {"report_req" => {"reportDefinition" => uri}}
|
161
|
+
else
|
162
|
+
# GoodData.post '/gdc/xtab2/executor3', {"report_req" => raw_data}
|
163
|
+
fail("this is currently unsupported. For executing unsaved report definitions please use class method execute.")
|
164
|
+
end
|
130
165
|
data_result_uri = result["execResult"]["dataResult"]
|
131
166
|
result = GoodData.get data_result_uri
|
132
167
|
while result["taskState"] && result["taskState"]["status"] == "WAIT" do
|
data/lib/gooddata/version.rb
CHANGED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'gooddata'
|
2
|
+
|
3
|
+
describe "Spin a project", :constraint => 'slow' do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
spec = JSON.parse(File.read("./spec/test_project_model_spec.json"), :symbolize_names => true)
|
7
|
+
GoodData.connect("svarovsky+gem_tester@gooddata.com", "jindrisska")
|
8
|
+
GoodData.logging_on
|
9
|
+
|
10
|
+
@project = GoodData::Model::ProjectCreator.migrate({:spec => spec, :token => "INTNA000000GDPM"})
|
11
|
+
end
|
12
|
+
|
13
|
+
after(:all) do
|
14
|
+
@project.delete unless @project.nil?
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should compute a metric" do
|
18
|
+
GoodData.with_project(@project) do |p|
|
19
|
+
f = GoodData::Fact.find_first_by_title('Lines changed')
|
20
|
+
metric = GoodData::Metric.xcreate("SELECT SUM(#\"#{f.title}\")")
|
21
|
+
metric.execute.should == 9
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should execute an anonymous metric twice and not fail" do
|
26
|
+
GoodData.with_project(@project) do |p|
|
27
|
+
f = GoodData::Fact.find_first_by_title('Lines changed')
|
28
|
+
metric = GoodData::Metric.xcreate("SELECT SUM(#\"#{f.title}\")")
|
29
|
+
metric.execute.should == 9
|
30
|
+
# Since GD platform cannot execute inline specified metric the metric has to be saved
|
31
|
+
# The code tries to resolve this as transparently as possible
|
32
|
+
metric.execute.should == 9
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should compute a report" do
|
37
|
+
GoodData.with_project(@project) do |p|
|
38
|
+
f = GoodData::Fact.find_first_by_title('Lines changed')
|
39
|
+
metric = GoodData::Metric.xcreate(:title => "My metric", :expression => "SELECT SUM(#\"#{f.title}\")")
|
40
|
+
metric.save
|
41
|
+
|
42
|
+
result = GoodData::ReportDefinition.execute(:title => "My report", :top => [metric], :left => ['label.devs.email'])
|
43
|
+
result[1][1].should == 3
|
44
|
+
result.include_row?(["jirka@gooddata.com", 5]).should == true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should contain metadata for each dataset in project metadata" do
|
49
|
+
GoodData.with_project(@project) do |p|
|
50
|
+
k = GoodData::ProjectMetadata.keys
|
51
|
+
k.should include("manifest_devs")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/spec/goodzilla_spec.rb
CHANGED
data/spec/model_dsl_spec.rb
CHANGED
@@ -6,7 +6,8 @@ describe GoodData::Model::SchemaBuilder do
|
|
6
6
|
it "should create a schema" do
|
7
7
|
builder = GoodData::Model::SchemaBuilder.new("a_title")
|
8
8
|
schema = builder.to_schema
|
9
|
-
schema.title.should == "
|
9
|
+
schema.title.should == "A title"
|
10
|
+
schema.name.should == "a_title"
|
10
11
|
end
|
11
12
|
|
12
13
|
it "should create a schema with some columns" do
|
@@ -0,0 +1,86 @@
|
|
1
|
+
{
|
2
|
+
"title": "Dev Week test",
|
3
|
+
"datasets": [
|
4
|
+
{
|
5
|
+
"name": "repos",
|
6
|
+
"columns": [
|
7
|
+
{
|
8
|
+
"type": "anchor",
|
9
|
+
"name": "id"
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"type": "label",
|
13
|
+
"name": "name",
|
14
|
+
"reference": "id"
|
15
|
+
}
|
16
|
+
]
|
17
|
+
},
|
18
|
+
{
|
19
|
+
"name": "devs",
|
20
|
+
"columns": [
|
21
|
+
{
|
22
|
+
"type": "anchor",
|
23
|
+
"name": "id"
|
24
|
+
},
|
25
|
+
{
|
26
|
+
"type": "label",
|
27
|
+
"name": "email",
|
28
|
+
"reference": "id"
|
29
|
+
}
|
30
|
+
]
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"name": "commits",
|
34
|
+
"columns": [
|
35
|
+
{
|
36
|
+
"type": "fact",
|
37
|
+
"name": "lines_changed"
|
38
|
+
},
|
39
|
+
{
|
40
|
+
"type": "date",
|
41
|
+
"name": "committed_on",
|
42
|
+
"dataset": "committed_on"
|
43
|
+
},
|
44
|
+
{
|
45
|
+
"type": "reference",
|
46
|
+
"name": "dev_id",
|
47
|
+
"dataset": "devs",
|
48
|
+
"reference": "id"
|
49
|
+
},
|
50
|
+
{
|
51
|
+
"type": "reference",
|
52
|
+
"name": "repo_id",
|
53
|
+
"dataset": "repos",
|
54
|
+
"reference": "id"
|
55
|
+
}
|
56
|
+
]
|
57
|
+
}
|
58
|
+
],
|
59
|
+
"uploads": [
|
60
|
+
{
|
61
|
+
"source":
|
62
|
+
[["lines_changed","committed_on","dev_id","repo_id"],
|
63
|
+
[1,"01/01/2014",1,1],
|
64
|
+
[3,"01/02/2014",2,2],
|
65
|
+
[5,"05/02/2014",3,1]],
|
66
|
+
"mode": "FULL",
|
67
|
+
"dataset": "commits"
|
68
|
+
},
|
69
|
+
{
|
70
|
+
"source":
|
71
|
+
[["id", "email"],
|
72
|
+
[1, "tomas@gooddata.com"],
|
73
|
+
[2, "petr@gooddata.com"],
|
74
|
+
[3, "jirka@gooddata.com"]],
|
75
|
+
"mode": "FULL",
|
76
|
+
"dataset": "devs"
|
77
|
+
}
|
78
|
+
],
|
79
|
+
"date_dimensions": [
|
80
|
+
{
|
81
|
+
"urn": null,
|
82
|
+
"name": "committed_on",
|
83
|
+
"title": null
|
84
|
+
}
|
85
|
+
]
|
86
|
+
}
|