gooddata 0.6.7 → 0.6.8
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.
- 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
|