gooddata 0.6.7 → 0.6.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -1
- data/README.md +10 -2
- data/TODO.md +32 -0
- data/gooddata.gemspec +5 -0
- data/lib/gooddata.rb +4 -0
- data/lib/gooddata/app/app.rb +12 -0
- data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +4 -3
- data/lib/gooddata/bricks/middleware/restforce_middleware.rb +2 -1
- data/lib/gooddata/cli/commands/console_cmd.rb +23 -5
- data/lib/gooddata/cli/commands/domain_cmd.rb +9 -10
- data/lib/gooddata/cli/commands/process_cmd.rb +11 -9
- data/lib/gooddata/cli/commands/project_cmd.rb +25 -27
- data/lib/gooddata/cli/commands/projects_cmd.rb +2 -2
- data/lib/gooddata/cli/commands/run_ruby_cmd.rb +1 -1
- data/lib/gooddata/cli/commands/user_cmd.rb +2 -2
- data/lib/gooddata/cli/hooks.rb +4 -2
- data/lib/gooddata/cli/shared.rb +1 -1
- data/lib/gooddata/cli/terminal.rb +1 -1
- data/lib/gooddata/commands/api.rb +1 -1
- data/lib/gooddata/commands/auth.rb +4 -28
- data/lib/gooddata/commands/domain.rb +9 -4
- data/lib/gooddata/commands/process.rb +26 -23
- data/lib/gooddata/commands/project.rb +74 -50
- data/lib/gooddata/commands/projects.rb +3 -2
- data/lib/gooddata/commands/role.rb +9 -3
- data/lib/gooddata/commands/user.rb +6 -4
- data/lib/gooddata/connection.rb +11 -45
- data/lib/gooddata/core/logging.rb +0 -1
- data/lib/gooddata/core/project.rb +22 -22
- data/lib/gooddata/core/rest.rb +9 -8
- data/lib/gooddata/core/user.rb +0 -11
- data/lib/gooddata/exceptions/project_not_found.rb +1 -0
- data/lib/gooddata/extensions/enumerable.rb +10 -0
- data/lib/gooddata/extensions/hash.rb +25 -0
- data/lib/gooddata/goodzilla/goodzilla.rb +4 -4
- data/lib/gooddata/helper/class_helper.rb +1 -0
- data/lib/gooddata/helper/helpers.rb +8 -0
- data/lib/gooddata/helpers/auth_helpers.rb +41 -0
- data/lib/gooddata/mixins/author.rb +1 -1
- data/lib/gooddata/mixins/contributor.rb +1 -1
- data/lib/gooddata/mixins/data_property_reader.rb +2 -0
- data/lib/gooddata/mixins/data_property_writer.rb +2 -0
- data/lib/gooddata/mixins/inspector.rb +49 -0
- data/lib/gooddata/mixins/md_finders.rb +16 -8
- data/lib/gooddata/mixins/md_id_to_uri.rb +12 -4
- data/lib/gooddata/mixins/md_object_indexer.rb +15 -4
- data/lib/gooddata/mixins/md_object_query.rb +42 -20
- data/lib/gooddata/mixins/md_relations.rb +21 -12
- data/lib/gooddata/mixins/meta_getter.rb +2 -0
- data/lib/gooddata/mixins/meta_property_reader.rb +2 -0
- data/lib/gooddata/mixins/meta_property_writer.rb +2 -0
- data/lib/gooddata/mixins/rest_resource.rb +32 -10
- data/lib/gooddata/mixins/root_key_getter.rb +1 -1
- data/lib/gooddata/models/data_result.rb +3 -1
- data/lib/gooddata/models/domain.rb +31 -22
- data/lib/gooddata/models/empty_result.rb +22 -0
- data/lib/gooddata/models/invitation.rb +11 -9
- data/lib/gooddata/models/links.rb +5 -3
- data/lib/gooddata/models/membership.rb +23 -28
- data/lib/gooddata/models/metadata.rb +35 -35
- data/lib/gooddata/models/metadata/attribute.rb +10 -8
- data/lib/gooddata/models/metadata/dashboard.rb +1 -1
- data/lib/gooddata/models/metadata/fact.rb +3 -3
- data/lib/gooddata/models/metadata/label.rb +4 -4
- data/lib/gooddata/models/metadata/metric.rb +76 -38
- data/lib/gooddata/models/metadata/report.rb +52 -17
- data/lib/gooddata/models/metadata/report_definition.rb +178 -28
- data/lib/gooddata/models/model.rb +13 -6
- data/lib/gooddata/models/process.rb +93 -30
- data/lib/gooddata/models/profile.rb +18 -20
- data/lib/gooddata/models/project.rb +344 -127
- data/lib/gooddata/models/project_creator.rb +32 -22
- data/lib/gooddata/models/project_metadata.rb +26 -14
- data/lib/gooddata/models/project_role.rb +15 -17
- data/lib/gooddata/models/report_data_result.rb +4 -0
- data/lib/gooddata/models/schedule.rb +51 -20
- data/lib/gooddata/models/schema_blueprint.rb +9 -3
- data/lib/gooddata/rest/README.md +37 -0
- data/lib/gooddata/rest/client.rb +318 -0
- data/lib/gooddata/rest/connection.rb +235 -0
- data/lib/gooddata/rest/connections/connections.rb +8 -0
- data/lib/gooddata/rest/connections/dummy_connection.rb +52 -0
- data/lib/gooddata/rest/connections/rest_client_connection.rb +177 -0
- data/lib/gooddata/rest/object.rb +32 -0
- data/lib/gooddata/rest/object_factory.rb +67 -0
- data/lib/gooddata/rest/resource.rb +17 -0
- data/lib/gooddata/rest/rest.rb +20 -0
- data/lib/gooddata/version.rb +1 -1
- data/spec/data/cc/data/source/commits.csv +4 -0
- data/spec/data/cc/data/source/devs.csv +4 -0
- data/spec/data/cc/data/source/repos.csv +3 -0
- data/spec/data/cc/devel.prm +0 -0
- data/spec/data/cc/graph/graph.grf +11 -0
- data/spec/data/cc/workspace.prm +19 -0
- data/spec/data/hello_world_process/hello_world.rb +1 -0
- data/spec/data/hello_world_process/hello_world.zip +0 -0
- data/spec/data/users.csv +12 -12
- data/spec/helpers/connection_helper.rb +6 -0
- data/spec/helpers/process_helper.rb +12 -0
- data/spec/helpers/project_helper.rb +2 -2
- data/spec/integration/command_projects_spec.rb +11 -9
- data/spec/integration/create_from_template_spec.rb +6 -2
- data/spec/integration/full_process_schedule_spec.rb +49 -36
- data/spec/integration/full_project_spec.rb +221 -256
- data/spec/integration/partial_md_export_import_spec.rb +18 -17
- data/spec/logging_in_logging_out_spec.rb +17 -8
- data/spec/spec_helper.rb +4 -2
- data/spec/unit/cli/commands/cmd_api_spec.rb +1 -1
- data/spec/unit/cli/commands/cmd_auth_spec.rb +1 -1
- data/spec/unit/cli/commands/cmd_domain_spec.rb +29 -3
- data/spec/unit/cli/commands/cmd_process_spec.rb +1 -1
- data/spec/unit/cli/commands/cmd_project_spec.rb +1 -1
- data/spec/unit/cli/commands/cmd_role_spec.rb +13 -2
- data/spec/unit/cli/commands/cmd_run_ruby_spec.rb +1 -1
- data/spec/unit/cli/commands/cmd_scaffold_spec.rb +1 -1
- data/spec/unit/cli/commands/cmd_user_spec.rb +1 -1
- data/spec/unit/commands/command_api_spec.rb +0 -19
- data/spec/unit/commands/command_auth_spec.rb +20 -13
- data/spec/unit/commands/command_dataset_spec.rb +2 -2
- data/spec/unit/commands/command_process_spec.rb +24 -21
- data/spec/unit/commands/command_projects_spec.rb +2 -2
- data/spec/unit/commands/command_scaffold_spec.rb +2 -2
- data/spec/unit/commands/command_user_spec.rb +3 -3
- data/spec/unit/core/connection_spec.rb +9 -10
- data/spec/unit/core/project_spec.rb +8 -4
- data/spec/unit/core/rest_spec.rb +6 -6
- data/spec/unit/models/domain_spec.rb +14 -7
- data/spec/unit/models/invitation_spec.rb +2 -2
- data/spec/unit/models/membership_spec.rb +5 -5
- data/spec/unit/models/metric_spec.rb +92 -0
- data/spec/unit/models/profile_spec.rb +25 -21
- data/spec/unit/models/project_blueprint_spec.rb +6 -6
- data/spec/unit/models/project_role_spec.rb +3 -5
- data/spec/unit/models/project_spec.rb +43 -37
- data/spec/unit/models/schedule_spec.rb +58 -107
- data/spec/unit/rest/resource_spec.rb +6 -0
- metadata +87 -10
- data/lib/gooddata/cli/commands/role_cmd.rb +0 -28
- data/lib/gooddata/core/connection.rb +0 -392
- data/lib/gooddata/core/threaded.rb +0 -14
- data/lib/gooddata/models/md_object.rb +0 -25
- data/lib/gooddata/models/metadata/folder.rb +0 -24
- data/spec/unit/models/md_object_spec.rb +0 -55
- data/spec/unit/models/metric.rb +0 -92
@@ -1,6 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
require_relative '../core/connection'
|
4
3
|
require_relative '../core/rest'
|
5
4
|
|
6
5
|
require_relative 'metadata/metadata'
|
@@ -61,7 +60,15 @@ module GoodData
|
|
61
60
|
end
|
62
61
|
|
63
62
|
# Load given file into a data set described by the given schema
|
64
|
-
def upload_data(path, project_blueprint, dataset, options = {})
|
63
|
+
def upload_data(path, project_blueprint, dataset, options = { :client => GoodData.connection, :project => GoodData.project })
|
64
|
+
client = options[:client]
|
65
|
+
fail ArgumentError, 'No :client specified' if client.nil?
|
66
|
+
|
67
|
+
p = options[:project]
|
68
|
+
fail ArgumentError, 'No :project specified' if p.nil?
|
69
|
+
|
70
|
+
project = GoodData::Project[p, options]
|
71
|
+
fail ArgumentError, 'Wrong :project specified' if project.nil?
|
65
72
|
# path = if path =~ URI.regexp
|
66
73
|
# Tempfile.open('remote_file') do |temp|
|
67
74
|
# temp << open(path).read
|
@@ -99,7 +106,7 @@ module GoodData
|
|
99
106
|
end
|
100
107
|
|
101
108
|
# upload it
|
102
|
-
|
109
|
+
client.upload_to_user_webdav("#{dir}/upload.zip", :directory => File.basename(dir), :client => options[:client], :project => options[:project])
|
103
110
|
ensure
|
104
111
|
FileUtils.rm_rf dir
|
105
112
|
end
|
@@ -107,15 +114,15 @@ module GoodData
|
|
107
114
|
# kick the load
|
108
115
|
pull = { 'pullIntegration' => File.basename(dir) }
|
109
116
|
link = project.md.links('etl')['pull']
|
110
|
-
task =
|
117
|
+
task = client.post link, pull
|
111
118
|
|
112
|
-
res =
|
119
|
+
res = client.poll_on_response(task['pullTask']['uri']) do |body|
|
113
120
|
body['taskStatus'] == 'RUNNING' || body['taskStatus'] == 'PREPARED'
|
114
121
|
end
|
115
122
|
|
116
123
|
if res['taskStatus'] == 'ERROR'
|
117
124
|
s = StringIO.new
|
118
|
-
|
125
|
+
client.download_from_user_webdav(File.basename(dir) + '/upload_status.json', s)
|
119
126
|
js = MultiJson.load(s.string)
|
120
127
|
fail "Load Failed with error #{JSON.pretty_generate(js)}"
|
121
128
|
end
|
@@ -2,8 +2,10 @@
|
|
2
2
|
|
3
3
|
require 'pry'
|
4
4
|
|
5
|
+
require_relative '../rest/resource'
|
6
|
+
|
5
7
|
module GoodData
|
6
|
-
class Process
|
8
|
+
class Process < GoodData::Rest::Object
|
7
9
|
attr_reader :data
|
8
10
|
|
9
11
|
alias_method :raw_data, :data
|
@@ -12,15 +14,24 @@ module GoodData
|
|
12
14
|
|
13
15
|
class << self
|
14
16
|
def [](id, options = {})
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
project = options[:project]
|
18
|
+
c = client(options)
|
19
|
+
|
20
|
+
if id == :all && project
|
21
|
+
uri = "/gdc/projects/#{project.pid}/dataload/processes"
|
22
|
+
data = c.get(uri)
|
18
23
|
data['processes']['items'].map do |process_data|
|
19
24
|
Process.new(process_data)
|
20
25
|
end
|
26
|
+
elsif id == :all
|
27
|
+
uri = "/gdc/account/profile/#{c.user.obj_id}/dataload/processes"
|
28
|
+
data = c.get(uri)
|
29
|
+
data['processes']['items'].map do |process_data|
|
30
|
+
c.create(Process, process_data)
|
31
|
+
end
|
21
32
|
else
|
22
|
-
uri = "/gdc/projects/#{
|
23
|
-
|
33
|
+
uri = "/gdc/projects/#{project.pid}/dataload/processes/#{id}"
|
34
|
+
c.create(Process, c.get(uri), project: project)
|
24
35
|
end
|
25
36
|
end
|
26
37
|
|
@@ -30,8 +41,16 @@ module GoodData
|
|
30
41
|
|
31
42
|
# TODO: Check the params.
|
32
43
|
def with_deploy(dir, options = {}, &block)
|
33
|
-
|
34
|
-
|
44
|
+
client = options[:client]
|
45
|
+
fail ArgumentError, 'No :client specified' if client.nil?
|
46
|
+
|
47
|
+
p = options[:project]
|
48
|
+
fail ArgumentError, 'No :project specified' if p.nil?
|
49
|
+
|
50
|
+
project = GoodData::Project[p, options]
|
51
|
+
fail ArgumentError, 'Wrong :project specified' if project.nil?
|
52
|
+
|
53
|
+
GoodData.with_project(project) do
|
35
54
|
params = options[:params].nil? ? [] : [options[:params]]
|
36
55
|
if block
|
37
56
|
begin
|
@@ -46,9 +65,37 @@ module GoodData
|
|
46
65
|
end
|
47
66
|
end
|
48
67
|
|
49
|
-
def upload_package(path, files_to_exclude)
|
50
|
-
|
51
|
-
|
68
|
+
def upload_package(path, files_to_exclude, opts = { :client => GoodData.connection })
|
69
|
+
client = opts[:client]
|
70
|
+
fail ArgumentError, 'No :client specified' if client.nil?
|
71
|
+
|
72
|
+
p = opts[:project]
|
73
|
+
fail ArgumentError, 'No :project specified' if p.nil?
|
74
|
+
|
75
|
+
project = GoodData::Project[p, opts]
|
76
|
+
fail ArgumentError, 'Wrong :project specified' if project.nil?
|
77
|
+
|
78
|
+
if !path.directory? && (path.extname == '.grf' || path.extname == '.rb')
|
79
|
+
puts 'Creating package for upload'
|
80
|
+
Tempfile.open('deploy-graph-archive') do |temp|
|
81
|
+
Zip::OutputStream.open(temp.path) do |zio|
|
82
|
+
FileUtils.cd(path.parent) do
|
83
|
+
files_to_pack = [path.basename]
|
84
|
+
files_to_pack.each do |item|
|
85
|
+
puts "including #{item}"
|
86
|
+
unless File.directory?(item)
|
87
|
+
zio.put_next_entry(item)
|
88
|
+
zio.print IO.read(item)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
client.upload_to_user_webdav(temp.path, opts)
|
95
|
+
temp.path
|
96
|
+
end
|
97
|
+
elsif !path.directory?
|
98
|
+
client.upload_to_user_webdav(path, opts)
|
52
99
|
path
|
53
100
|
else
|
54
101
|
Tempfile.open('deploy-graph-archive') do |temp|
|
@@ -57,7 +104,7 @@ module GoodData
|
|
57
104
|
|
58
105
|
files_to_pack = Dir.glob('./**/*').reject { |f| files_to_exclude.include?(Pathname(path) + f) }
|
59
106
|
files_to_pack.each do |item|
|
60
|
-
|
107
|
+
puts "including #{item}"
|
61
108
|
unless File.directory?(item)
|
62
109
|
zio.put_next_entry(item)
|
63
110
|
zio.print IO.read(item)
|
@@ -65,7 +112,8 @@ module GoodData
|
|
65
112
|
end
|
66
113
|
end
|
67
114
|
end
|
68
|
-
|
115
|
+
|
116
|
+
client.upload_to_user_webdav(temp.path, opts)
|
69
117
|
temp.path
|
70
118
|
end
|
71
119
|
end
|
@@ -80,15 +128,27 @@ module GoodData
|
|
80
128
|
# @option options [String] :process_id ID of a process to be redeployed (do not set if you want to create a new process)
|
81
129
|
# @option options [Boolean] :verbose (false) Switch on verbose mode for detailed logging
|
82
130
|
def deploy(path, options = {})
|
131
|
+
client = options[:client]
|
132
|
+
fail ArgumentError, 'No :client specified' if client.nil?
|
133
|
+
|
134
|
+
p = options[:project]
|
135
|
+
fail ArgumentError, 'No :project specified' if p.nil?
|
136
|
+
|
137
|
+
project = GoodData::Project[p, options]
|
138
|
+
fail ArgumentError, 'No :project specified' if project.nil?
|
139
|
+
|
83
140
|
path = Pathname(path) || fail('Path is not specified')
|
84
|
-
files_to_exclude = options[:files_to_exclude].nil? ? [] : options[:files_to_exclude].map { |
|
141
|
+
files_to_exclude = options[:files_to_exclude].nil? ? [] : options[:files_to_exclude].map { |pname| Pathname(pname) }
|
85
142
|
process_id = options[:process_id]
|
86
143
|
|
87
144
|
type = options[:type] || 'GRAPH'
|
88
145
|
deploy_name = options[:name]
|
146
|
+
fail ArgumentError, 'options[:name] can not be nil or empty!' if deploy_name.nil? || deploy_name.empty?
|
147
|
+
|
89
148
|
verbose = options[:verbose] || false
|
90
149
|
puts HighLine.color("Deploying #{path}", HighLine::BOLD) if verbose
|
91
|
-
|
150
|
+
|
151
|
+
deployed_path = Process.upload_package(path, files_to_exclude, :client => client, :project => project)
|
92
152
|
data = {
|
93
153
|
:process => {
|
94
154
|
:name => deploy_name,
|
@@ -96,12 +156,14 @@ module GoodData
|
|
96
156
|
:type => type
|
97
157
|
}
|
98
158
|
}
|
159
|
+
|
99
160
|
res = if process_id.nil?
|
100
|
-
|
161
|
+
client.post("/gdc/projects/#{project.pid}/dataload/processes", data)
|
101
162
|
else
|
102
|
-
|
163
|
+
client.put("/gdc/projects/#{project.pid}/dataload/processes/#{process_id}", data)
|
103
164
|
end
|
104
|
-
|
165
|
+
|
166
|
+
process = client.create(Process, res, project: p)
|
105
167
|
puts HighLine.color("Deploy DONE #{path}", HighLine::GREEN) if verbose
|
106
168
|
process
|
107
169
|
end
|
@@ -112,7 +174,7 @@ module GoodData
|
|
112
174
|
end
|
113
175
|
|
114
176
|
def delete
|
115
|
-
|
177
|
+
client.delete(uri)
|
116
178
|
end
|
117
179
|
|
118
180
|
# Redeploy existing process.
|
@@ -128,7 +190,7 @@ module GoodData
|
|
128
190
|
end
|
129
191
|
|
130
192
|
def process
|
131
|
-
|
193
|
+
data['process']
|
132
194
|
end
|
133
195
|
|
134
196
|
def name
|
@@ -146,6 +208,7 @@ module GoodData
|
|
146
208
|
def link
|
147
209
|
links['self']
|
148
210
|
end
|
211
|
+
|
149
212
|
alias_method :uri, :link
|
150
213
|
|
151
214
|
def obj_id
|
@@ -167,28 +230,28 @@ module GoodData
|
|
167
230
|
end
|
168
231
|
|
169
232
|
def schedules
|
170
|
-
|
233
|
+
project.schedules.select { |schedule| schedule.process_id == obj_id }
|
171
234
|
end
|
172
235
|
|
173
236
|
def create_schedule(cron, executable, options = {})
|
174
|
-
|
237
|
+
project.create_schedule(process_id, cron, executable, options.merge(client: client, project: project))
|
175
238
|
end
|
176
239
|
|
177
240
|
def execute(executable, options = {})
|
178
241
|
params = options[:params] || {}
|
179
242
|
hidden_params = options[:hidden_params] || {}
|
180
|
-
result =
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
243
|
+
result = client.post(executions_link,
|
244
|
+
:execution => {
|
245
|
+
:graph => executable.to_s,
|
246
|
+
:params => params,
|
247
|
+
:hiddenParams => hidden_params
|
248
|
+
})
|
186
249
|
begin
|
187
|
-
|
250
|
+
client.poll_on_code(result['executionTask']['links']['poll'])
|
188
251
|
rescue RestClient::RequestFailed => e
|
189
252
|
raise(e)
|
190
253
|
ensure
|
191
|
-
result =
|
254
|
+
result = client.get(result['executionTask']['links']['detail'])
|
192
255
|
if result['executionDetail']['status'] == 'ERROR'
|
193
256
|
fail "Runing process failed. You can look at a log here #{result["executionDetail"]["logFileName"]}"
|
194
257
|
end
|
@@ -1,11 +1,14 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
+
require 'pmap'
|
4
|
+
|
5
|
+
require_relative '../rest/object'
|
6
|
+
|
3
7
|
require_relative 'project'
|
4
8
|
|
5
9
|
module GoodData
|
6
|
-
|
7
|
-
|
8
|
-
attr_reader :json
|
10
|
+
class Profile < GoodData::Rest::Object
|
11
|
+
attr_reader :user, :json
|
9
12
|
|
10
13
|
EMPTY_OBJECT = {
|
11
14
|
'accountSetting' => {
|
@@ -73,8 +76,7 @@ module GoodData
|
|
73
76
|
# Gets user currently logged in
|
74
77
|
# @return [GoodData::Profile] User currently logged-in
|
75
78
|
def current
|
76
|
-
|
77
|
-
GoodData::Profile.new(json)
|
79
|
+
GoodData.connection.user
|
78
80
|
end
|
79
81
|
|
80
82
|
# Gets hash representing diff of profiles
|
@@ -207,7 +209,7 @@ module GoodData
|
|
207
209
|
|
208
210
|
# Deletes this account settings
|
209
211
|
def delete
|
210
|
-
|
212
|
+
client.delete uri
|
211
213
|
end
|
212
214
|
|
213
215
|
# Gets hash representing diff of profiles
|
@@ -278,13 +280,6 @@ module GoodData
|
|
278
280
|
@json['accountSetting']['login'] = val
|
279
281
|
end
|
280
282
|
|
281
|
-
# Get full name
|
282
|
-
#
|
283
|
-
# @return [String] Full name
|
284
|
-
def name
|
285
|
-
"#{first_name} #{last_name}"
|
286
|
-
end
|
287
|
-
|
288
283
|
# Gets the resource identifier
|
289
284
|
#
|
290
285
|
# @return [String] Resource identifier
|
@@ -328,14 +323,10 @@ module GoodData
|
|
328
323
|
#
|
329
324
|
# @return [Array<GoodData::Project>] Array of project where account settings belongs to
|
330
325
|
def projects
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
projects['projects'].each do |project|
|
335
|
-
res << GoodData::Project.new(project)
|
326
|
+
projects = client.get @json['accountSetting']['links']['projects']
|
327
|
+
projects['projects'].map do |project|
|
328
|
+
client.create(GoodData::Project, project)
|
336
329
|
end
|
337
|
-
|
338
|
-
res
|
339
330
|
end
|
340
331
|
|
341
332
|
# Saves object if dirty, clears dirty flag
|
@@ -380,5 +371,12 @@ module GoodData
|
|
380
371
|
def uri
|
381
372
|
@json['accountSetting']['links']['self']
|
382
373
|
end
|
374
|
+
|
375
|
+
private
|
376
|
+
|
377
|
+
def initialize(json)
|
378
|
+
@json = json
|
379
|
+
@user = @json['accountSetting']['firstName'] + ' ' + @json['accountSetting']['lastName']
|
380
|
+
end
|
383
381
|
end
|
384
382
|
end
|
@@ -3,16 +3,22 @@
|
|
3
3
|
require 'csv'
|
4
4
|
require 'zip'
|
5
5
|
require 'fileutils'
|
6
|
+
require 'multi_json'
|
7
|
+
require 'pmap'
|
8
|
+
require 'zip'
|
6
9
|
|
7
|
-
require_relative 'process'
|
8
10
|
require_relative '../exceptions/no_project_error'
|
9
11
|
|
10
|
-
require_relative '../
|
12
|
+
require_relative '../rest/resource'
|
13
|
+
require_relative '../mixins/author'
|
14
|
+
require_relative '../mixins/contributor'
|
15
|
+
require_relative '../mixins/rest_resource'
|
11
16
|
|
17
|
+
require_relative 'process'
|
12
18
|
require_relative 'project_role'
|
13
19
|
|
14
20
|
module GoodData
|
15
|
-
class Project
|
21
|
+
class Project < GoodData::Rest::Resource
|
16
22
|
USERSPROJECTS_PATH = '/gdc/account/profile/%s/projects'
|
17
23
|
PROJECTS_PATH = '/gdc/projects'
|
18
24
|
PROJECT_PATH = '/gdc/projects/%s'
|
@@ -24,20 +30,19 @@ module GoodData
|
|
24
30
|
alias_method :to_json, :json
|
25
31
|
alias_method :raw_data, :json
|
26
32
|
|
27
|
-
include GoodData::Mixin::
|
28
|
-
include GoodData::Mixin::DataGetter
|
29
|
-
|
30
|
-
class << self
|
31
|
-
include GoodData::Mixin::RootKeySetter
|
33
|
+
include GoodData::Mixin::RestResource
|
32
34
|
|
33
|
-
|
35
|
+
Project.root_key :project
|
34
36
|
|
35
|
-
|
37
|
+
include GoodData::Mixin::Author
|
38
|
+
include GoodData::Mixin::Contributor
|
36
39
|
|
40
|
+
class << self
|
37
41
|
# Returns an array of all projects accessible by
|
38
42
|
# current user
|
39
|
-
def all
|
40
|
-
|
43
|
+
def all(opts = {})
|
44
|
+
c = client(opts)
|
45
|
+
c.user.projects
|
41
46
|
end
|
42
47
|
|
43
48
|
# Returns a Project object identified by given string
|
@@ -46,10 +51,11 @@ module GoodData
|
|
46
51
|
# - /gdc/projects/<id>
|
47
52
|
# - <id>
|
48
53
|
#
|
49
|
-
def [](id,
|
50
|
-
return id if id.respond_to?(:project?) && id.project?
|
54
|
+
def [](id, opts = {})
|
55
|
+
return id if id.instance_of?(GoodData::Project) || id.respond_to?(:project?) && id.project?
|
56
|
+
|
51
57
|
if id == :all
|
52
|
-
Project.all
|
58
|
+
Project.all(opts)
|
53
59
|
else
|
54
60
|
if id.to_s !~ %r{^(\/gdc\/(projects|md)\/)?[a-zA-Z\d]+$}
|
55
61
|
fail(ArgumentError, 'wrong type of argument. Should be either project ID or path')
|
@@ -57,48 +63,46 @@ module GoodData
|
|
57
63
|
|
58
64
|
id = id.match(/[a-zA-Z\d]+$/)[0] if id =~ /\//
|
59
65
|
|
60
|
-
|
61
|
-
|
66
|
+
c = client(opts)
|
67
|
+
fail ArgumentError, 'No :client specified' if c.nil?
|
68
|
+
|
69
|
+
response = c.get(PROJECT_PATH % id)
|
70
|
+
c.factory.create(Project, response)
|
62
71
|
end
|
63
72
|
end
|
64
73
|
|
65
|
-
Project.root_key :project
|
66
|
-
|
67
|
-
include GoodData::Mixin::RootKeyGetter
|
68
|
-
include GoodData::Mixin::DataGetter
|
69
|
-
include GoodData::Mixin::Author
|
70
|
-
include GoodData::Mixin::Contributor
|
71
|
-
include GoodData::Mixin::Timestamps
|
72
|
-
|
73
74
|
# Create a project from a given attributes
|
74
75
|
# Expected keys:
|
75
76
|
# - :title (mandatory)
|
76
77
|
# - :summary
|
77
78
|
# - :template (default /projects/blank)
|
78
79
|
#
|
79
|
-
def create(
|
80
|
-
GoodData.logger.info "Creating project #{
|
80
|
+
def create(opts = { :client => GoodData.connection }, &block)
|
81
|
+
GoodData.logger.info "Creating project #{opts[:title]}"
|
81
82
|
|
82
|
-
|
83
|
+
c = client(opts)
|
84
|
+
fail ArgumentError, 'No :client specified' if c.nil?
|
85
|
+
|
86
|
+
auth_token = opts[:auth_token] || GoodData.connection.auth_token
|
83
87
|
fail 'You have to provide your token for creating projects as :auth_token parameter' if auth_token.nil? || auth_token.empty?
|
84
88
|
|
85
89
|
json = {
|
86
90
|
'project' =>
|
87
91
|
{
|
88
92
|
'meta' => {
|
89
|
-
'title' =>
|
90
|
-
'summary' =>
|
93
|
+
'title' => opts[:title],
|
94
|
+
'summary' => opts[:summary] || 'No summary'
|
91
95
|
},
|
92
96
|
'content' => {
|
93
|
-
'guidedNavigation' =>
|
97
|
+
'guidedNavigation' => opts[:guided_navigation] || 1,
|
94
98
|
'authorizationToken' => auth_token,
|
95
|
-
'driver' =>
|
99
|
+
'driver' => opts[:driver] || 'Pg'
|
96
100
|
}
|
97
101
|
}
|
98
102
|
}
|
99
103
|
|
100
|
-
json['project']['meta']['projectTemplate'] =
|
101
|
-
project = Project
|
104
|
+
json['project']['meta']['projectTemplate'] = opts[:template] if opts[:template] && !opts[:template].empty?
|
105
|
+
project = c.create(Project, json)
|
102
106
|
project.save
|
103
107
|
# until it is enabled or deleted, recur. This should still end if there is a exception thrown out from RESTClient. This sometimes happens from WebApp when request is too long
|
104
108
|
while project.state.to_s != 'enabled'
|
@@ -119,6 +123,13 @@ module GoodData
|
|
119
123
|
project
|
120
124
|
end
|
121
125
|
|
126
|
+
def find(opts = {}, client = GoodData::Rest::Client.client)
|
127
|
+
user = client.user
|
128
|
+
user.projects['projects'].map do |project|
|
129
|
+
client.create(GoodData::Project, project)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
122
133
|
def create_from_blueprint(blueprint, options = {})
|
123
134
|
GoodData::Model::ProjectCreator.migrate(:spec => blueprint, :token => options[:auth_token])
|
124
135
|
end
|
@@ -141,15 +152,18 @@ module GoodData
|
|
141
152
|
end
|
142
153
|
end
|
143
154
|
|
144
|
-
def add_metric(options = {})
|
145
|
-
|
146
|
-
|
147
|
-
|
155
|
+
def add_metric(metric, options = {})
|
156
|
+
default = { client: client, project: self }
|
157
|
+
if metric.is_a?(String)
|
158
|
+
GoodData::Metric.xcreate(metric, options.merge(default))
|
159
|
+
else
|
160
|
+
GoodData::Metric.xcreate(metric.merge(default))
|
161
|
+
end
|
148
162
|
end
|
149
163
|
alias_method :create_metric, :add_metric
|
150
164
|
|
151
165
|
def add_report(options = {})
|
152
|
-
rep = GoodData::Report.create(options)
|
166
|
+
rep = GoodData::Report.create(options.merge(client: client, project: self))
|
153
167
|
rep.save
|
154
168
|
end
|
155
169
|
alias_method :create_report, :add_report
|
@@ -161,13 +175,29 @@ module GoodData
|
|
161
175
|
user_has_role?(GoodData.user, 'admin')
|
162
176
|
end
|
163
177
|
|
178
|
+
# Helper for getting attributes of a project
|
179
|
+
#
|
180
|
+
# @param [String | Number | Object] Anything that you can pass to GoodData::Attribute[id]
|
181
|
+
# @return [GoodData::Attribute | Array<GoodData::Attribute>] fact instance or list
|
182
|
+
def attributes(id = :all)
|
183
|
+
GoodData::Attribute[id, project: self, client: client]
|
184
|
+
end
|
185
|
+
|
186
|
+
def attribute_by_title(title)
|
187
|
+
GoodData::Attribute.find_first_by_title(title, project: self, client: client)
|
188
|
+
end
|
189
|
+
|
190
|
+
def attributes_by_title(title)
|
191
|
+
GoodData::Attribute.find_by_title(title, project: self, client: client)
|
192
|
+
end
|
193
|
+
|
164
194
|
# Gets project blueprint from the server
|
165
195
|
#
|
166
196
|
# @return [GoodData::ProjectRole] Project role if found
|
167
197
|
def blueprint
|
168
|
-
result =
|
198
|
+
result = client.get("/gdc/projects/#{pid}/model/view")
|
169
199
|
polling_url = result['asyncTask']['link']['poll']
|
170
|
-
model =
|
200
|
+
model = client.poll_on_code(polling_url)
|
171
201
|
GoodData::Model::FromWire.from_wire(model)
|
172
202
|
end
|
173
203
|
|
@@ -177,9 +207,9 @@ module GoodData
|
|
177
207
|
def browser_uri(options = {})
|
178
208
|
grey = options[:grey]
|
179
209
|
if grey
|
180
|
-
|
210
|
+
client.connection.url + uri
|
181
211
|
else
|
182
|
-
|
212
|
+
client.connection.url + '#s=' + uri
|
183
213
|
end
|
184
214
|
end
|
185
215
|
|
@@ -188,12 +218,12 @@ module GoodData
|
|
188
218
|
# @return [GoodData::Project] Newly created project
|
189
219
|
def clone(options = {})
|
190
220
|
# TODO: Refactor so if export or import fails the new_project will be cleaned
|
191
|
-
with_data = options[:data]
|
192
|
-
with_users = options[:users]
|
221
|
+
with_data = options[:data].nil? ? true : options[:data]
|
222
|
+
with_users = options[:users].nil? ? false : options[:users]
|
193
223
|
a_title = options[:title] || "Clone of #{title}"
|
194
224
|
|
195
225
|
# Create the project first so we know that it is passing. What most likely is wrong is the tokena and the export actaully takes majoiryt of the time
|
196
|
-
new_project = GoodData::Project.create(options.merge(:title => a_title))
|
226
|
+
new_project = GoodData::Project.create(options.merge(:title => a_title, :client => client))
|
197
227
|
|
198
228
|
export = {
|
199
229
|
:exportProject => {
|
@@ -202,11 +232,11 @@ module GoodData
|
|
202
232
|
}
|
203
233
|
}
|
204
234
|
|
205
|
-
result =
|
235
|
+
result = client.post("/gdc/md/#{obj_id}/maintenance/export", export)
|
206
236
|
export_token = result['exportArtifact']['token']
|
207
237
|
|
208
238
|
status_url = result['exportArtifact']['status']['uri']
|
209
|
-
|
239
|
+
client.poll_on_response(status_url) do |body|
|
210
240
|
body['taskState']['status'] == 'RUNNING'
|
211
241
|
end
|
212
242
|
|
@@ -216,35 +246,46 @@ module GoodData
|
|
216
246
|
}
|
217
247
|
}
|
218
248
|
|
219
|
-
result =
|
249
|
+
result = client.post("/gdc/md/#{new_project.obj_id}/maintenance/import", import)
|
220
250
|
status_url = result['uri']
|
221
|
-
|
251
|
+
client.poll_on_response(status_url) do |body|
|
222
252
|
body['taskState']['status'] == 'RUNNING'
|
223
253
|
end
|
224
254
|
|
225
255
|
new_project
|
226
256
|
end
|
227
257
|
|
228
|
-
def
|
229
|
-
|
230
|
-
datasets_uri = "#{md['data']}/sets"
|
231
|
-
response = GoodData.get datasets_uri
|
232
|
-
response['dataSetsInfo']['sets'].map do |ds|
|
233
|
-
DataSet[ds['meta']['uri']]
|
234
|
-
end
|
258
|
+
def create_schedule(process, date, executable, options = {})
|
259
|
+
GoodData::Schedule.create(process, date, executable, options.merge(:client => client, :project => self))
|
235
260
|
end
|
236
261
|
|
237
|
-
#
|
262
|
+
# Helper for getting dashboards of a project
|
238
263
|
#
|
239
|
-
# @
|
240
|
-
|
241
|
-
|
264
|
+
# @param [String | Number | Object] Anything that you can pass to GoodData::Dashboard[id]
|
265
|
+
# @return [GoodData::Dashboard | Array<GoodData::Dashboard>] dashboard instance or list
|
266
|
+
def dashboards(id = :all)
|
267
|
+
GoodData::Dashboard[id, project: self, client: client]
|
268
|
+
end
|
269
|
+
|
270
|
+
def datasets
|
271
|
+
blueprint.datasets
|
242
272
|
end
|
243
273
|
|
244
274
|
# Deletes project
|
245
275
|
def delete
|
246
276
|
fail "Project '#{title}' with id #{uri} is already deleted" if state == :deleted
|
247
|
-
|
277
|
+
client.delete(uri)
|
278
|
+
end
|
279
|
+
|
280
|
+
# Helper for getting rid of all data in the project
|
281
|
+
#
|
282
|
+
# @option options [Boolean] :force has to be added otherwise the operation is not performed
|
283
|
+
# @return [Array] Result of executing MAQLs
|
284
|
+
def delete_all_data(options = {})
|
285
|
+
return false unless options[:force]
|
286
|
+
datasets.pmap do |dataset|
|
287
|
+
execute_maql("SYNCHRONIZE {#{dataset.identifier}}")
|
288
|
+
end
|
248
289
|
end
|
249
290
|
|
250
291
|
# Deletes dashboards for project
|
@@ -252,30 +293,58 @@ module GoodData
|
|
252
293
|
Dashboard.all.map { |data| Dashboard[data['link']] }.each { |d| d.delete }
|
253
294
|
end
|
254
295
|
|
296
|
+
def deploy_process(path, options = {})
|
297
|
+
GoodData::Process.deploy(path, options.merge(client: client, project: self))
|
298
|
+
end
|
299
|
+
|
255
300
|
# Executes DML expression. See (https://developer.gooddata.com/article/deleting-records-from-datasets)
|
256
301
|
# for some examples and explanations
|
257
302
|
#
|
258
303
|
# @param dml [String] DML expression
|
304
|
+
# @return [Hash] Result of executing DML
|
259
305
|
def execute_dml(dml)
|
260
306
|
uri = "/gdc/md/#{pid}/dml/manage"
|
261
|
-
result =
|
262
|
-
manage: {
|
263
|
-
maql: dml
|
264
|
-
})
|
307
|
+
result = client.post(uri, manage: { maql: dml })
|
265
308
|
polling_uri = result['uri']
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
309
|
+
|
310
|
+
client.poll_on_response(polling_uri) do |body|
|
311
|
+
body && body['taskState'] && body['taskState']['status'] == 'WAIT'
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
# Executes MAQL expression and waits for it to be finished.
|
316
|
+
#
|
317
|
+
# @param maql [String] MAQL expression
|
318
|
+
# @return [Hash] Result of executing MAQL
|
319
|
+
def execute_maql(maql)
|
320
|
+
ldm_links = client.get(md[GoodData::Model::LDM_CTG])
|
321
|
+
ldm_uri = Links.new(ldm_links)[GoodData::Model::LDM_MANAGE_CTG]
|
322
|
+
response = client.post(ldm_uri, manage: { maql: maql })
|
323
|
+
polling_uri = response['entries'].first['link']
|
324
|
+
|
325
|
+
client.poll_on_response(polling_uri) do |body|
|
326
|
+
body && body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
|
270
327
|
end
|
271
328
|
end
|
272
329
|
|
273
330
|
# Helper for getting facts of a project
|
274
331
|
#
|
275
332
|
# @param [String | Number | Object] Anything that you can pass to GoodData::Fact[id]
|
276
|
-
# @return [GoodData::Fact] fact instance or list
|
277
|
-
def
|
278
|
-
GoodData::Fact[id, project: self]
|
333
|
+
# @return [GoodData::Fact | Array<GoodData::Fact>] fact instance or list
|
334
|
+
def facts(id = :all)
|
335
|
+
GoodData::Fact[id, project: self, client: client]
|
336
|
+
end
|
337
|
+
|
338
|
+
def fact_by_title(title)
|
339
|
+
GoodData::Fact.find_first_by_title(title, project: self, client: client)
|
340
|
+
end
|
341
|
+
|
342
|
+
def facts_by_title(title)
|
343
|
+
GoodData::Fact.find_by_title(title, project: self, client: client)
|
344
|
+
end
|
345
|
+
|
346
|
+
def find_attribute_element_value(uri)
|
347
|
+
GoodData::Attribute.find_element_value(uri, client: client, project: self)
|
279
348
|
end
|
280
349
|
|
281
350
|
# Gets project role by its identifier
|
@@ -348,6 +417,107 @@ module GoodData
|
|
348
417
|
nil
|
349
418
|
end
|
350
419
|
|
420
|
+
# Exports project users to file
|
421
|
+
def import_users(path, opts = { :header => true }, &block)
|
422
|
+
opts[:path] = path
|
423
|
+
|
424
|
+
##########################
|
425
|
+
# Caching/Cached objects
|
426
|
+
##########################
|
427
|
+
domains = {}
|
428
|
+
current_users = users
|
429
|
+
role_list = roles
|
430
|
+
|
431
|
+
##########################
|
432
|
+
# Load users from CSV
|
433
|
+
##########################
|
434
|
+
new_users = GoodData::Helpers.csv_read(opts) do |row|
|
435
|
+
json = {}
|
436
|
+
if block_given?
|
437
|
+
json = yield row
|
438
|
+
else
|
439
|
+
json = {
|
440
|
+
'user' => {
|
441
|
+
'content' => {
|
442
|
+
'email' => row[0],
|
443
|
+
'login' => row[1],
|
444
|
+
'firstname' => row[2],
|
445
|
+
'lastname' => row[3]
|
446
|
+
},
|
447
|
+
'meta' => {}
|
448
|
+
}
|
449
|
+
}
|
450
|
+
end
|
451
|
+
|
452
|
+
GoodData::User.new(json)
|
453
|
+
end
|
454
|
+
|
455
|
+
##########################
|
456
|
+
# Diff users
|
457
|
+
##########################
|
458
|
+
diff = GoodData::User.diff_list(current_users, new_users)
|
459
|
+
|
460
|
+
##########################
|
461
|
+
# Create new users
|
462
|
+
##########################
|
463
|
+
diff[:added].map do |user|
|
464
|
+
# TODO: Add user here
|
465
|
+
domain_name = user.json['user']['content']['domain']
|
466
|
+
|
467
|
+
# Lookup for domain in cache'
|
468
|
+
domain = domains[domain_name]
|
469
|
+
|
470
|
+
# Get domain info from REST, add to cache
|
471
|
+
if domain.nil?
|
472
|
+
domain = {
|
473
|
+
:domain => GoodData::Domain[domain_name],
|
474
|
+
:users => GoodData::Domain[domain_name].users
|
475
|
+
}
|
476
|
+
|
477
|
+
domain[:users_map] = Hash[domain[:users].map { |u| [u.email, u] }]
|
478
|
+
domains[domain_name] = domain
|
479
|
+
end
|
480
|
+
|
481
|
+
# Check if user exists in domain
|
482
|
+
domain_user = domain[:users_map][user.email]
|
483
|
+
|
484
|
+
# Create domain user if needed
|
485
|
+
unless domain_user
|
486
|
+
password = user.json['user']['content']['password']
|
487
|
+
|
488
|
+
# Fill necessary user data
|
489
|
+
user_data = {
|
490
|
+
:login => user.login,
|
491
|
+
:firstName => user.first_name,
|
492
|
+
:lastName => user.last_name,
|
493
|
+
:password => password,
|
494
|
+
:verifyPassword => password,
|
495
|
+
:email => user.login
|
496
|
+
}
|
497
|
+
|
498
|
+
# Add created user to cache
|
499
|
+
domain_user = domain[:domain].add_user(user_data)
|
500
|
+
domain[:users] << domain_user
|
501
|
+
domain[:users_map][user.email] = domain_user
|
502
|
+
end
|
503
|
+
|
504
|
+
# Lookup for role
|
505
|
+
role_name = user.json['user']['content']['role'] || 'readOnlyUser'
|
506
|
+
role = get_role_by_identifier(role_name, role_list)
|
507
|
+
next if role.nil?
|
508
|
+
|
509
|
+
# Assign user project role
|
510
|
+
add_user(domain_user, [role.uri])
|
511
|
+
end
|
512
|
+
|
513
|
+
##########################
|
514
|
+
# Remove old users
|
515
|
+
##########################
|
516
|
+
# diff[:removed].map do |user|
|
517
|
+
# user.disable(self)
|
518
|
+
# end
|
519
|
+
end
|
520
|
+
|
351
521
|
# Checks whether user has particular role in given proejct
|
352
522
|
#
|
353
523
|
# @param user [GoodData::Profile | GoodData::Membership | String] User in question. Can be passed by login (String), profile or membershi objects
|
@@ -365,6 +535,7 @@ module GoodData
|
|
365
535
|
#
|
366
536
|
# @param json Json used for initialization
|
367
537
|
def initialize(json)
|
538
|
+
super
|
368
539
|
@json = json
|
369
540
|
end
|
370
541
|
|
@@ -403,21 +574,17 @@ module GoodData
|
|
403
574
|
}
|
404
575
|
|
405
576
|
url = "/gdc/projects/#{pid}/invitations"
|
406
|
-
|
577
|
+
client.post(url, data)
|
407
578
|
end
|
408
579
|
|
409
580
|
# Returns invitations to project
|
410
581
|
#
|
411
582
|
# @return [Array<GoodData::Invitation>] List of invitations
|
412
583
|
def invitations
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
tmp['invitations'].each do |invitation|
|
417
|
-
res << GoodData::Invitation.new(invitation)
|
584
|
+
invitations = client.get @json['project']['links']['invitations']
|
585
|
+
invitations['invitations'].pmap do |invitation|
|
586
|
+
client.create GoodData::Invitation, invitation
|
418
587
|
end
|
419
|
-
|
420
|
-
res
|
421
588
|
end
|
422
589
|
|
423
590
|
# Returns project related links
|
@@ -427,12 +594,21 @@ module GoodData
|
|
427
594
|
data['links']
|
428
595
|
end
|
429
596
|
|
430
|
-
|
431
|
-
|
597
|
+
# Helper for getting labels of a project
|
598
|
+
#
|
599
|
+
# @param [String | Number | Object] Anything that you can pass to
|
600
|
+
# GoodData::Label[id] + it supports :all as welll
|
601
|
+
# @return [GoodData::Fact | Array<GoodData::Fact>] fact instance or list
|
602
|
+
def labels(id = :all, opts = {})
|
603
|
+
if id == :all
|
604
|
+
attributes.pmapcat { |a| a.labels }.uniq
|
605
|
+
else
|
606
|
+
GoodData::Label[id, opts.merge(project: self, client: client)]
|
607
|
+
end
|
432
608
|
end
|
433
609
|
|
434
610
|
def md
|
435
|
-
@md ||= Links
|
611
|
+
@md ||= client.create(Links, client.get(data['links']['metadata']))
|
436
612
|
end
|
437
613
|
|
438
614
|
# Gets membership for profile specified
|
@@ -449,6 +625,21 @@ module GoodData
|
|
449
625
|
list.find { |m| m.login == profile.login }
|
450
626
|
end
|
451
627
|
|
628
|
+
# Helper for getting metrics of a project
|
629
|
+
#
|
630
|
+
# @return [Array<GoodData::Metric>] matric instance or list
|
631
|
+
def metrics(id = :all, opts = { :full => true })
|
632
|
+
GoodData::Metric[id, opts.merge(project: self, client: client)]
|
633
|
+
end
|
634
|
+
|
635
|
+
def metric_by_title(title)
|
636
|
+
GoodData::Metric.find_first_by_title(title, project: self, client: client)
|
637
|
+
end
|
638
|
+
|
639
|
+
def metrics_by_title(title)
|
640
|
+
GoodData::Metric.find_by_title(title, project: self, client: client)
|
641
|
+
end
|
642
|
+
|
452
643
|
# Checks if the profile is member of project
|
453
644
|
#
|
454
645
|
# @param [GoodData::Profile] profile - Profile to be checked
|
@@ -467,26 +658,32 @@ module GoodData
|
|
467
658
|
|
468
659
|
alias_method :pid, :obj_id
|
469
660
|
|
470
|
-
|
471
|
-
|
661
|
+
# Helper for getting objects of a project
|
662
|
+
#
|
663
|
+
# @return [Array<GoodData::MdObject>] object instance or list
|
664
|
+
def objects(id, opts = {})
|
665
|
+
GoodData::MdObject[id, opts.merge(project: self, client: client)]
|
666
|
+
end
|
472
667
|
|
473
|
-
|
474
|
-
fail '
|
668
|
+
def partial_md_export(objs, options = {})
|
669
|
+
fail 'Nothing to migrate. You have to pass list of objects, ids or uris that you would like to migrate' if objs.nil?
|
670
|
+
objs = [objs] unless objs.is_a?(Array)
|
671
|
+
fail 'Nothing to migrate. The list you provided is empty' if objs.empty?
|
475
672
|
|
476
673
|
target_project = options[:project]
|
477
674
|
fail 'You have to provide a project instance or project pid to migrate to' if target_project.nil?
|
478
|
-
target_project =
|
479
|
-
|
675
|
+
target_project = client.projects(target_project)
|
676
|
+
objs = objs.pmap { |obj| objects(obj) }
|
480
677
|
export_payload = {
|
481
678
|
:partialMDExport => {
|
482
|
-
:uris =>
|
679
|
+
:uris => objs.map { |obj| obj.uri }
|
483
680
|
}
|
484
681
|
}
|
485
|
-
result =
|
682
|
+
result = client.post("#{md['maintenance']}/partialmdexport", export_payload)
|
486
683
|
polling_url = result['partialMDArtifact']['status']['uri']
|
487
684
|
token = result['partialMDArtifact']['token']
|
488
685
|
|
489
|
-
polling_result =
|
686
|
+
polling_result = client.poll_on_response(polling_url) do |body|
|
490
687
|
body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
|
491
688
|
end
|
492
689
|
|
@@ -500,10 +697,10 @@ module GoodData
|
|
500
697
|
}
|
501
698
|
}
|
502
699
|
|
503
|
-
result =
|
700
|
+
result = client.post("#{target_project.md['maintenance']}/partialmdimport", import_payload)
|
504
701
|
polling_url = result['uri']
|
505
702
|
|
506
|
-
|
703
|
+
client.poll_on_response(polling_url) do |body|
|
507
704
|
body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
|
508
705
|
end
|
509
706
|
|
@@ -512,6 +709,14 @@ module GoodData
|
|
512
709
|
|
513
710
|
alias_method :transfer_objects, :partial_md_export
|
514
711
|
|
712
|
+
# Helper for getting processes of a project
|
713
|
+
#
|
714
|
+
# @param [String | Number | Object] Anything that you can pass to GoodData::Report[id]
|
715
|
+
# @return [GoodData::Report | Array<GoodData::Report>] report instance or list
|
716
|
+
def processes(id = :all)
|
717
|
+
GoodData::Process[id, project: self, client: client]
|
718
|
+
end
|
719
|
+
|
515
720
|
# Checks if this object instance is project
|
516
721
|
#
|
517
722
|
# @return [Boolean] Return true for all instances
|
@@ -520,8 +725,8 @@ module GoodData
|
|
520
725
|
end
|
521
726
|
|
522
727
|
def info
|
523
|
-
results = blueprint.datasets.
|
524
|
-
[ds, ds.count]
|
728
|
+
results = blueprint.datasets.pmap do |ds|
|
729
|
+
[ds, ds.count(self)]
|
525
730
|
end
|
526
731
|
puts title
|
527
732
|
puts GoodData::Helpers.underline(title)
|
@@ -544,21 +749,38 @@ module GoodData
|
|
544
749
|
# Forces project to reload
|
545
750
|
def reload!
|
546
751
|
if saved?
|
547
|
-
response =
|
752
|
+
response = client.get(uri)
|
548
753
|
@json = response
|
549
754
|
end
|
550
755
|
self
|
551
756
|
end
|
552
757
|
|
758
|
+
# Helper for getting reports of a project
|
759
|
+
#
|
760
|
+
# @param [String | Number | Object] Anything that you can pass to GoodData::Report[id]
|
761
|
+
# @return [GoodData::Report | Array<GoodData::Report>] report instance or list
|
762
|
+
def reports(id = :all)
|
763
|
+
GoodData::Report[id, project: self, client: client]
|
764
|
+
end
|
765
|
+
|
766
|
+
# Helper for getting report definitions of a project
|
767
|
+
#
|
768
|
+
# @param [String | Number | Object] Anything that you can pass to GoodData::ReportDefinition[id]
|
769
|
+
# @return [GoodData::ReportDefinition | Array<GoodData::ReportDefinition>] report definition instance or list
|
770
|
+
def report_definitions(id = :all, options = {})
|
771
|
+
GoodData::ReportDefinition[id, options.merge(project: self, client: client)]
|
772
|
+
end
|
773
|
+
|
553
774
|
# Gets the list or project roles
|
554
775
|
#
|
555
776
|
# @return [Array<GoodData::ProjectRole>] List of roles
|
556
777
|
def roles
|
557
778
|
url = "/gdc/projects/#{pid}/roles"
|
558
|
-
|
559
|
-
tmp
|
560
|
-
|
561
|
-
|
779
|
+
|
780
|
+
tmp = client.get(url)
|
781
|
+
tmp['projectRoles']['roles'].pmap do |role_url|
|
782
|
+
json = client.get role_url
|
783
|
+
client.create(GoodData::ProjectRole, json)
|
562
784
|
end
|
563
785
|
end
|
564
786
|
|
@@ -569,11 +791,11 @@ module GoodData
|
|
569
791
|
data_to_send['project']['content'].delete('isPublic')
|
570
792
|
data_to_send['project']['content'].delete('state')
|
571
793
|
response = if uri
|
572
|
-
|
573
|
-
|
794
|
+
client.post(PROJECT_PATH % pid, data_to_send)
|
795
|
+
client.get uri
|
574
796
|
else
|
575
|
-
result =
|
576
|
-
|
797
|
+
result = client.post(PROJECTS_PATH, data_to_send)
|
798
|
+
client.get result['uri']
|
577
799
|
end
|
578
800
|
@json = response
|
579
801
|
self
|
@@ -587,12 +809,10 @@ module GoodData
|
|
587
809
|
!res
|
588
810
|
end
|
589
811
|
|
590
|
-
#
|
591
|
-
#
|
592
|
-
|
593
|
-
|
594
|
-
tmp = GoodData.get @json['project']['links']['schedules']
|
595
|
-
tmp['schedules']['items'].map { |schedule| GoodData::Schedule.new(schedule) }
|
812
|
+
# @param [String | Number | Object] Anything that you can pass to GoodData::Schedule[id]
|
813
|
+
# @return [GoodData::Schedule | Array<GoodData::Schedule>] schedule instance or list
|
814
|
+
def schedules(id = :all)
|
815
|
+
GoodData::Schedule[id, project: self, client: client]
|
596
816
|
end
|
597
817
|
|
598
818
|
# Gets SLIs data
|
@@ -602,7 +822,7 @@ module GoodData
|
|
602
822
|
link = "#{data['links']['metadata']}#{SLIS_PATH}"
|
603
823
|
|
604
824
|
# FIXME: Review what to do with passed extra argument
|
605
|
-
Metadata.new
|
825
|
+
Metadata.new client.get(link)
|
606
826
|
end
|
607
827
|
|
608
828
|
# Gets project state
|
@@ -637,14 +857,10 @@ module GoodData
|
|
637
857
|
#
|
638
858
|
# @return [Array<GoodData::User>] List of users
|
639
859
|
def users
|
640
|
-
|
641
|
-
|
642
|
-
tmp = GoodData.get @json['project']['links']['users']
|
860
|
+
tmp = client.get @json['project']['links']['users']
|
643
861
|
tmp['users'].map do |user|
|
644
|
-
|
862
|
+
client.factory.create(GoodData::Membership, user)
|
645
863
|
end
|
646
|
-
|
647
|
-
res
|
648
864
|
end
|
649
865
|
|
650
866
|
alias_method :members, :users
|
@@ -660,9 +876,10 @@ module GoodData
|
|
660
876
|
|
661
877
|
# Get domain info from REST, add to cache
|
662
878
|
if domain.nil?
|
879
|
+
d = GoodData::Domain[domain_name, { :client => client }]
|
663
880
|
domain = {
|
664
|
-
:domain =>
|
665
|
-
:users =>
|
881
|
+
:domain => d,
|
882
|
+
:users => d.users(:client => client)
|
666
883
|
}
|
667
884
|
|
668
885
|
domain[:users_map] = Hash[domain[:users].map { |u| [u.email, u] }]
|
@@ -735,7 +952,7 @@ module GoodData
|
|
735
952
|
#
|
736
953
|
# @param list List of users to be disabled
|
737
954
|
def users_remove(list)
|
738
|
-
list.
|
955
|
+
list.pmap do |user|
|
739
956
|
user.disable
|
740
957
|
end
|
741
958
|
end
|
@@ -772,7 +989,7 @@ module GoodData
|
|
772
989
|
}
|
773
990
|
}
|
774
991
|
|
775
|
-
|
992
|
+
client.post url, payload
|
776
993
|
end
|
777
994
|
|
778
995
|
alias_method :add_user, :set_user_roles
|
@@ -782,12 +999,12 @@ module GoodData
|
|
782
999
|
# @param list List of users to be updated
|
783
1000
|
# @param role_list Optional list of cached roles to prevent unnecessary server round-trips
|
784
1001
|
def set_users_roles(list, role_list = roles)
|
785
|
-
list.
|
1002
|
+
list.pmap do |user_hash|
|
786
1003
|
user = user_hash[:user]
|
787
1004
|
roles = user_hash[:role] || user_hash[:roles]
|
788
1005
|
{
|
789
1006
|
:user => user,
|
790
|
-
:result => set_user_roles(user, roles
|
1007
|
+
:result => set_user_roles(user, roles)
|
791
1008
|
}
|
792
1009
|
end
|
793
1010
|
end
|
@@ -800,9 +1017,9 @@ module GoodData
|
|
800
1017
|
# invalid_objects - Checks metadata for invalid/corrupted objects.
|
801
1018
|
# asyncTask response
|
802
1019
|
def validate(filters = %w(ldm pdm metric_filter invalid_objects))
|
803
|
-
response =
|
1020
|
+
response = client.post "#{md['validate-project']}", 'validateProject' => filters
|
804
1021
|
polling_link = response['asyncTask']['link']['poll']
|
805
|
-
|
1022
|
+
client.poll_on_response(polling_link) do |body|
|
806
1023
|
body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
|
807
1024
|
end
|
808
1025
|
end
|