gooddata 0.6.0.pre6 → 0.6.0.pre7
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/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
|
+
}
|